Compare commits
7 Commits
android-20
...
android-19
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff6499872a | ||
|
|
3f1995ac14 | ||
|
|
a0ebeb20b9 | ||
|
|
bffc72f0bf | ||
|
|
a6e7d36f7b | ||
|
|
e80c938e46 | ||
|
|
7798494d1e |
@@ -32,6 +32,3 @@ if [ ! -z "$DIFF" ]; then
|
||||
echo "$DIFF"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd src/android
|
||||
./gradlew ktlintCheck
|
||||
|
||||
@@ -8,7 +8,17 @@ variables:
|
||||
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
|
||||
|
||||
stages:
|
||||
- stage: format
|
||||
displayName: 'format'
|
||||
jobs:
|
||||
- job: format
|
||||
displayName: 'clang'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
steps:
|
||||
- template: ./templates/format-check.yml
|
||||
- stage: build
|
||||
dependsOn: format
|
||||
displayName: 'build'
|
||||
jobs:
|
||||
- job: build
|
||||
@@ -33,6 +43,7 @@ stages:
|
||||
cache: 'true'
|
||||
version: $(DisplayVersion)
|
||||
- stage: build_win
|
||||
dependsOn: format
|
||||
displayName: 'build-windows'
|
||||
jobs:
|
||||
- job: build
|
||||
|
||||
8
.github/workflows/verify.yml
vendored
8
.github/workflows/verify.yml
vendored
@@ -13,15 +13,13 @@ jobs:
|
||||
format:
|
||||
name: 'verify format'
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: yuzuemu/build-environments:linux-clang-format
|
||||
options: -u 1001
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: false
|
||||
- name: set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: 'Verify Formatting'
|
||||
run: bash -ex ./.ci/scripts/format/script.sh
|
||||
build:
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
| [12499](https://github.com/yuzu-emu/yuzu-android//pull/12499) | [`fab260fc2`](https://github.com/yuzu-emu/yuzu-android//pull/12499/files) | Rework time services | [Kelebek1](https://github.com/Kelebek1/) | 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 |
|
||||
| [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 |
|
||||
| [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 |
|
||||
| [12611](https://github.com/yuzu-emu/yuzu-android//pull/12611) | [`2f0b57ca1`](https://github.com/yuzu-emu/yuzu-android//pull/12611/files) | kernel: fix resource management issues | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12612](https://github.com/yuzu-emu/yuzu-android//pull/12612) | [`76880b84f`](https://github.com/yuzu-emu/yuzu-android//pull/12612/files) | fsp-srv: use program registry for SetCurrentProcess | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12652](https://github.com/yuzu-emu/yuzu-android//pull/12652) | [`2a0d707ce`](https://github.com/yuzu-emu/yuzu-android//pull/12652/files) | shader_recompiler: emulate 8-bit and 16-bit storage writes with cas loop | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12659](https://github.com/yuzu-emu/yuzu-android//pull/12659) | [`d94097478`](https://github.com/yuzu-emu/yuzu-android//pull/12659/files) | audio: fetch process object from handle table | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
||||
3
externals/CMakeLists.txt
vendored
3
externals/CMakeLists.txt
vendored
@@ -178,9 +178,6 @@ if (NOT TARGET stb::headers)
|
||||
add_library(stb::headers ALIAS stb)
|
||||
endif()
|
||||
|
||||
add_library(tz tz/tz/tz.cpp)
|
||||
target_include_directories(tz PUBLIC ./tz)
|
||||
|
||||
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
||||
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
||||
|
||||
|
||||
1636
externals/tz/tz/tz.cpp
vendored
1636
externals/tz/tz/tz.cpp
vendored
File diff suppressed because it is too large
Load Diff
81
externals/tz/tz/tz.h
vendored
81
externals/tz/tz/tz.h
vendored
@@ -1,81 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: 1996 Arthur David Olson
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
#include <array>
|
||||
#include <time.h>
|
||||
|
||||
namespace Tz {
|
||||
using u8 = uint8_t;
|
||||
using s8 = int8_t;
|
||||
using u16 = uint16_t;
|
||||
using s16 = int16_t;
|
||||
using u32 = uint32_t;
|
||||
using s32 = int32_t;
|
||||
using u64 = uint64_t;
|
||||
using s64 = int64_t;
|
||||
|
||||
constexpr size_t TZ_MAX_TIMES = 1000;
|
||||
constexpr size_t TZ_MAX_TYPES = 128;
|
||||
constexpr size_t TZ_MAX_CHARS = 50;
|
||||
constexpr size_t MY_TZNAME_MAX = 255;
|
||||
constexpr size_t TZNAME_MAXIMUM = 255;
|
||||
constexpr size_t TZ_MAX_LEAPS = 50;
|
||||
constexpr s64 TIME_T_MAX = std::numeric_limits<s64>::max();
|
||||
constexpr s64 TIME_T_MIN = std::numeric_limits<s64>::min();
|
||||
constexpr size_t CHARS_EXTRA = 3;
|
||||
constexpr size_t MAX_ZONE_CHARS = std::max(TZ_MAX_CHARS + CHARS_EXTRA, sizeof("UTC"));
|
||||
constexpr size_t MAX_TZNAME_CHARS = 2 * (MY_TZNAME_MAX + 1);
|
||||
|
||||
struct ttinfo {
|
||||
s32 tt_utoff;
|
||||
bool tt_isdst;
|
||||
s32 tt_desigidx;
|
||||
bool tt_ttisstd;
|
||||
bool tt_ttisut;
|
||||
};
|
||||
static_assert(sizeof(ttinfo) == 0x10, "ttinfo has the wrong size!");
|
||||
|
||||
struct Rule {
|
||||
s32 timecnt;
|
||||
s32 typecnt;
|
||||
s32 charcnt;
|
||||
bool goback;
|
||||
bool goahead;
|
||||
std::array <u8, 0x2> padding0;
|
||||
std::array<s64, TZ_MAX_TIMES> ats;
|
||||
std::array<u8, TZ_MAX_TIMES> types;
|
||||
std::array<ttinfo, TZ_MAX_TYPES> ttis;
|
||||
std::array<char, std::max(MAX_ZONE_CHARS, MAX_TZNAME_CHARS)> chars;
|
||||
s32 defaulttype;
|
||||
std::array <u8, 0x12C4> padding1;
|
||||
};
|
||||
static_assert(sizeof(Rule) == 0x4000, "Rule has the wrong size!");
|
||||
|
||||
struct CalendarTimeInternal {
|
||||
s32 tm_sec;
|
||||
s32 tm_min;
|
||||
s32 tm_hour;
|
||||
s32 tm_mday;
|
||||
s32 tm_mon;
|
||||
s32 tm_year;
|
||||
s32 tm_wday;
|
||||
s32 tm_yday;
|
||||
s32 tm_isdst;
|
||||
std::array<char, 16> tm_zone;
|
||||
s32 tm_utoff;
|
||||
s32 time_index;
|
||||
};
|
||||
static_assert(sizeof(CalendarTimeInternal) == 0x3C, "CalendarTimeInternal has the wrong size!");
|
||||
|
||||
s32 ParseTimeZoneBinary(Rule& out_rule, std::span<const u8> binary);
|
||||
|
||||
bool localtime_rz(CalendarTimeInternal* tmp, Rule* sp, time_t* timep);
|
||||
u32 mktime_tzname(time_t* out_time, Rule* sp, CalendarTimeInternal* tmp);
|
||||
|
||||
} // namespace Tz
|
||||
@@ -188,15 +188,8 @@ tasks.create<Delete>("ktlintReset") {
|
||||
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("preBuild").dependsOn("ktlintCheck")
|
||||
|
||||
ktlint {
|
||||
version.set("0.47.1")
|
||||
@@ -235,33 +228,71 @@ dependencies {
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
||||
}
|
||||
|
||||
fun runGitCommand(command: List<String>): String {
|
||||
return try {
|
||||
ProcessBuilder(command)
|
||||
fun getGitVersion(): String {
|
||||
var versionName = "0.0"
|
||||
|
||||
try {
|
||||
versionName = ProcessBuilder("git", "describe", "--always", "--long")
|
||||
.directory(project.rootDir)
|
||||
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||
.redirectError(ProcessBuilder.Redirect.PIPE)
|
||||
.start().inputStream.bufferedReader().use { it.readText() }
|
||||
.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)?-[^-]+$"), "")
|
||||
} 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 =
|
||||
runGitCommand(listOf("git", "rev-parse", "--short", "HEAD")).ifEmpty { "dummy-hash" }
|
||||
fun getGitHash(): String {
|
||||
try {
|
||||
val processBuilder = ProcessBuilder("git", "rev-parse", "--short", "HEAD")
|
||||
processBuilder.directory(project.rootDir)
|
||||
val process = processBuilder.start()
|
||||
val inputStream = process.inputStream
|
||||
val errorStream = process.errorStream
|
||||
process.waitFor()
|
||||
|
||||
fun getBranch(): String =
|
||||
runGitCommand(listOf("git", "rev-parse", "--abbrev-ref", "HEAD")).ifEmpty { "dummy-hash" }
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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,8 +21,6 @@ import org.yuzu.yuzu_emu.utils.DocumentsTree
|
||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||
import org.yuzu.yuzu_emu.utils.Log
|
||||
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
|
||||
@@ -237,12 +235,9 @@ object NativeLibrary {
|
||||
/**
|
||||
* Installs a nsp or xci file to nand
|
||||
* @param filename String representation of file uri
|
||||
* @return int representation of [InstallResult]
|
||||
* @param extension Lowercase string representation of file extension without "."
|
||||
*/
|
||||
external fun installFileToNand(
|
||||
filename: String,
|
||||
callback: (max: Long, progress: Long) -> Boolean
|
||||
): Int
|
||||
external fun installFileToNand(filename: String, extension: String): Int
|
||||
|
||||
external fun doesUpdateMatchProgram(programId: String, updatePath: String): Boolean
|
||||
|
||||
@@ -540,29 +535,9 @@ object NativeLibrary {
|
||||
*
|
||||
* @param path Path to game file. Can be a [Uri].
|
||||
* @param programId String representation of a game's program ID
|
||||
* @return Array of available patches
|
||||
* @return Array of pairs where the first value is the name of an addon and the second is the version
|
||||
*/
|
||||
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)
|
||||
external fun getAddonsForFile(path: String, programId: String): Array<Pair<String, String>>?
|
||||
|
||||
/**
|
||||
* Gets the save location for a specific game
|
||||
@@ -634,4 +609,15 @@ object NativeLibrary {
|
||||
const val RELEASED = 0
|
||||
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,6 +49,7 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
|
||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||
import org.yuzu.yuzu_emu.utils.Log
|
||||
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.ThemeHelper
|
||||
import java.text.NumberFormat
|
||||
@@ -170,6 +171,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
stopMotionSensorListener()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
NativeConfig.saveGlobalConfig()
|
||||
}
|
||||
|
||||
override fun onUserLeaveHint() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
|
||||
|
||||
@@ -6,32 +6,27 @@ package org.yuzu.yuzu_emu.adapters
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
|
||||
import org.yuzu.yuzu_emu.model.Patch
|
||||
import org.yuzu.yuzu_emu.model.AddonViewModel
|
||||
import org.yuzu.yuzu_emu.model.Addon
|
||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||
|
||||
class AddonAdapter(val addonViewModel: AddonViewModel) :
|
||||
AbstractDiffAdapter<Patch, AddonAdapter.AddonViewHolder>() {
|
||||
class AddonAdapter : AbstractDiffAdapter<Addon, AddonAdapter.AddonViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder {
|
||||
ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
.also { return AddonViewHolder(it) }
|
||||
}
|
||||
|
||||
inner class AddonViewHolder(val binding: ListItemAddonBinding) :
|
||||
AbstractViewHolder<Patch>(binding) {
|
||||
override fun bind(model: Patch) {
|
||||
AbstractViewHolder<Addon>(binding) {
|
||||
override fun bind(model: Addon) {
|
||||
binding.root.setOnClickListener {
|
||||
binding.addonCheckbox.isChecked = !binding.addonCheckbox.isChecked
|
||||
binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked
|
||||
}
|
||||
binding.title.text = model.name
|
||||
binding.title.text = model.title
|
||||
binding.version.text = model.version
|
||||
binding.addonCheckbox.setOnCheckedChangeListener { _, checked ->
|
||||
binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
|
||||
model.enabled = checked
|
||||
}
|
||||
binding.addonCheckbox.isChecked = model.enabled
|
||||
binding.buttonDelete.setOnClickListener {
|
||||
addonViewModel.setAddonToDelete(model)
|
||||
}
|
||||
binding.addonSwitch.isChecked = model.enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ class AboutFragment : Fragment() {
|
||||
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
|
||||
}
|
||||
|
||||
binding.textVersionName.text = BuildConfig.VERSION_NAME
|
||||
binding.textVersionName.setOnClickListener {
|
||||
binding.textBuildHash.text = BuildConfig.GIT_HASH
|
||||
binding.buttonBuildHash.setOnClickListener {
|
||||
val clipBoard =
|
||||
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH)
|
||||
|
||||
@@ -74,7 +74,7 @@ class AddonsFragment : Fragment() {
|
||||
|
||||
binding.listAddons.apply {
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = AddonAdapter(addonViewModel)
|
||||
adapter = AddonAdapter()
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.apply {
|
||||
@@ -110,21 +110,6 @@ 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 {
|
||||
@@ -171,22 +156,22 @@ class AddonsFragment : Fragment() {
|
||||
descriptionId = R.string.invalid_directory_description
|
||||
)
|
||||
if (isValid) {
|
||||
ProgressDialogFragment.newInstance(
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
R.string.installing_game_content,
|
||||
false
|
||||
) { progressCallback, _ ->
|
||||
) {
|
||||
val parentDirectoryName = externalAddonDirectory.name
|
||||
val internalAddonDirectory =
|
||||
File(args.game.addonDir + parentDirectoryName)
|
||||
try {
|
||||
externalAddonDirectory.copyFilesTo(internalAddonDirectory, progressCallback)
|
||||
externalAddonDirectory.copyFilesTo(internalAddonDirectory)
|
||||
} catch (_: Exception) {
|
||||
return@newInstance errorMessage
|
||||
}
|
||||
addonViewModel.refreshAddons()
|
||||
return@newInstance getString(R.string.addon_installed_successfully)
|
||||
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
} else {
|
||||
errorMessage.show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class DriverManagerFragment : Fragment() {
|
||||
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
|
||||
binding.toolbarDrivers.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_driver_use_global -> {
|
||||
R.id.menu_driver_clear -> {
|
||||
StringSetting.DRIVER_PATH.global = true
|
||||
driverViewModel.updateDriverList()
|
||||
(binding.listDrivers.adapter as DriverAdapter)
|
||||
@@ -93,7 +93,7 @@ class DriverManagerFragment : Fragment() {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
driverViewModel.showClearButton.collect {
|
||||
binding.toolbarDrivers.menu
|
||||
.findItem(R.id.menu_driver_use_global).isVisible = it
|
||||
.findItem(R.id.menu_driver_clear).isVisible = it
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,11 +173,11 @@ class DriverManagerFragment : Fragment() {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
R.string.installing_driver,
|
||||
false
|
||||
) { _, _ ->
|
||||
) {
|
||||
val driverPath =
|
||||
"${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}"
|
||||
val driverFile = File(driverPath)
|
||||
@@ -213,6 +213,6 @@ class DriverManagerFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
return@newInstance Any()
|
||||
}.show(childFragmentManager, ProgressDialogFragment.TAG)
|
||||
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,7 +554,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean()
|
||||
}
|
||||
|
||||
popup.setOnDismissListener { NativeConfig.saveGlobalConfig() }
|
||||
popup.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_toggle_fps -> {
|
||||
@@ -721,9 +720,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.emulation_control_adjust)
|
||||
.setView(adjustBinding.root)
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||
NativeConfig.saveGlobalConfig()
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setNeutralButton(R.string.slider_default) { _: DialogInterface?, _: Int ->
|
||||
setControlScale(50)
|
||||
setControlOpacity(100)
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
|
||||
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
|
||||
@@ -356,17 +357,27 @@ class GamePropertiesFragment : Fragment() {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
val inputZip = requireContext().contentResolver.openInputStream(result)
|
||||
val savesFolder = File(args.game.saveDir)
|
||||
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
||||
cacheSaveDir.mkdir()
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
if (inputZip == null) {
|
||||
Toast.makeText(
|
||||
YuzuApplication.appContext,
|
||||
getString(R.string.fatal_error),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
R.string.save_files_importing,
|
||||
false
|
||||
) { _, _ ->
|
||||
) {
|
||||
try {
|
||||
FileUtil.unzipToInternalStorage(result.toString(), cacheSaveDir)
|
||||
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
|
||||
val files = cacheSaveDir.listFiles()
|
||||
var savesFolderFile: File? = null
|
||||
if (files != null) {
|
||||
@@ -411,7 +422,7 @@ class GamePropertiesFragment : Fragment() {
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -425,11 +436,11 @@ class GamePropertiesFragment : Fragment() {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
R.string.save_files_exporting,
|
||||
false
|
||||
) { _, _ ->
|
||||
) {
|
||||
val saveLocation = args.game.saveDir
|
||||
val zipResult = FileUtil.zipFromInternalStorage(
|
||||
File(saveLocation),
|
||||
@@ -441,6 +452,6 @@ class GamePropertiesFragment : Fragment() {
|
||||
TaskState.Completed -> getString(R.string.export_success)
|
||||
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
|
||||
}
|
||||
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,11 @@ import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||
|
||||
class ProgressDialogFragment : DialogFragment() {
|
||||
class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||
private val taskViewModel: TaskViewModel by activityViewModels()
|
||||
|
||||
private lateinit var binding: DialogProgressBarBinding
|
||||
|
||||
private val PROGRESS_BAR_RESOLUTION = 1000
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val titleId = requireArguments().getInt(TITLE)
|
||||
val cancellable = requireArguments().getBoolean(CANCELLABLE)
|
||||
@@ -63,7 +61,6 @@ class ProgressDialogFragment : DialogFragment() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.message.isSelected = true
|
||||
viewLifecycleOwner.lifecycleScope.apply {
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
@@ -100,35 +97,6 @@ class ProgressDialogFragment : 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +108,6 @@ class ProgressDialogFragment : DialogFragment() {
|
||||
val negativeButton = alertDialog.getButton(Dialog.BUTTON_NEGATIVE)
|
||||
negativeButton.setOnClickListener {
|
||||
alertDialog.setTitle(getString(R.string.cancelling))
|
||||
binding.progressBar.isIndeterminate = true
|
||||
taskViewModel.setCancelled(true)
|
||||
}
|
||||
}
|
||||
@@ -155,12 +122,9 @@ class ProgressDialogFragment : DialogFragment() {
|
||||
activity: FragmentActivity,
|
||||
titleId: Int,
|
||||
cancellable: Boolean = false,
|
||||
task: suspend (
|
||||
progressCallback: (max: Long, progress: Long) -> Boolean,
|
||||
messageCallback: (message: String) -> Unit
|
||||
) -> Any
|
||||
): ProgressDialogFragment {
|
||||
val dialog = ProgressDialogFragment()
|
||||
task: suspend () -> Any
|
||||
): IndeterminateProgressDialogFragment {
|
||||
val dialog = IndeterminateProgressDialogFragment()
|
||||
val args = Bundle()
|
||||
ViewModelProvider(activity)[TaskViewModel::class.java].task = task
|
||||
args.putInt(TITLE, titleId)
|
||||
@@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.model.TaskState
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.math.BigInteger
|
||||
@@ -194,20 +195,26 @@ class InstallableFragment : Fragment() {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
val inputZip = requireContext().contentResolver.openInputStream(result)
|
||||
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
||||
cacheSaveDir.mkdir()
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
if (inputZip == null) {
|
||||
Toast.makeText(
|
||||
YuzuApplication.appContext,
|
||||
getString(R.string.fatal_error),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
R.string.save_files_importing,
|
||||
false
|
||||
) { progressCallback, _ ->
|
||||
) {
|
||||
try {
|
||||
FileUtil.unzipToInternalStorage(
|
||||
result.toString(),
|
||||
cacheSaveDir,
|
||||
progressCallback
|
||||
)
|
||||
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
|
||||
val files = cacheSaveDir.listFiles()
|
||||
var successfulImports = 0
|
||||
var failedImports = 0
|
||||
@@ -280,7 +287,7 @@ class InstallableFragment : Fragment() {
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
private val exportSaves = registerForActivityResult(
|
||||
@@ -290,11 +297,11 @@ class InstallableFragment : Fragment() {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
R.string.save_files_exporting,
|
||||
false
|
||||
) { _, _ ->
|
||||
) {
|
||||
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
||||
cacheSaveDir.mkdir()
|
||||
|
||||
@@ -331,6 +338,6 @@ class InstallableFragment : Fragment() {
|
||||
TaskState.Completed -> getString(R.string.export_success)
|
||||
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
|
||||
}
|
||||
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,14 +136,14 @@ class SearchFragment : Fragment() {
|
||||
baseList.filter {
|
||||
val lastPlayedTime = preferences.getLong(it.keyLastPlayedTime, 0L)
|
||||
lastPlayedTime > (System.currentTimeMillis() - 24 * 60 * 60 * 1000)
|
||||
}.sortedByDescending { preferences.getLong(it.keyLastPlayedTime, 0L) }
|
||||
}
|
||||
}
|
||||
|
||||
R.id.chip_recently_added -> {
|
||||
baseList.filter {
|
||||
val addedTime = preferences.getLong(it.keyAddedToLibraryTime, 0L)
|
||||
addedTime > (System.currentTimeMillis() - 24 * 60 * 60 * 1000)
|
||||
}.sortedByDescending { preferences.getLong(it.keyAddedToLibraryTime, 0L) }
|
||||
}
|
||||
}
|
||||
|
||||
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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
|
||||
|
||||
class AddonViewModel : ViewModel() {
|
||||
private val _patchList = MutableStateFlow(mutableListOf<Patch>())
|
||||
val addonList get() = _patchList.asStateFlow()
|
||||
private val _addonList = MutableStateFlow(mutableListOf<Addon>())
|
||||
val addonList get() = _addonList.asStateFlow()
|
||||
|
||||
private val _showModInstallPicker = MutableStateFlow(false)
|
||||
val showModInstallPicker get() = _showModInstallPicker.asStateFlow()
|
||||
@@ -24,9 +24,6 @@ class AddonViewModel : ViewModel() {
|
||||
private val _showModNoticeDialog = MutableStateFlow(false)
|
||||
val showModNoticeDialog get() = _showModNoticeDialog.asStateFlow()
|
||||
|
||||
private val _addonToDelete = MutableStateFlow<Patch?>(null)
|
||||
val addonToDelete = _addonToDelete.asStateFlow()
|
||||
|
||||
var game: Game? = null
|
||||
|
||||
private val isRefreshing = AtomicBoolean(false)
|
||||
@@ -43,47 +40,36 @@ class AddonViewModel : ViewModel() {
|
||||
isRefreshing.set(true)
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val patchList = (
|
||||
NativeLibrary.getPatchesForFile(game!!.path, game!!.programId)
|
||||
?: emptyArray()
|
||||
).toMutableList()
|
||||
patchList.sortBy { it.name }
|
||||
_patchList.value = patchList
|
||||
val addonList = mutableListOf<Addon>()
|
||||
val disabledAddons = NativeConfig.getDisabledAddons(game!!.programId)
|
||||
NativeLibrary.getAddonsForFile(game!!.path, game!!.programId)?.forEach {
|
||||
val name = it.first.replace("[D] ", "")
|
||||
addonList.add(Addon(!disabledAddons.contains(name), name, it.second))
|
||||
}
|
||||
addonList.sortBy { it.title }
|
||||
_addonList.value = addonList
|
||||
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() {
|
||||
if (_patchList.value.isEmpty()) {
|
||||
if (_addonList.value.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
NativeConfig.setDisabledAddons(
|
||||
game!!.programId,
|
||||
_patchList.value.mapNotNull {
|
||||
_addonList.value.mapNotNull {
|
||||
if (it.enabled) {
|
||||
null
|
||||
} else {
|
||||
it.name
|
||||
it.title
|
||||
}
|
||||
}.toTypedArray()
|
||||
)
|
||||
NativeConfig.saveGlobalConfig()
|
||||
_patchList.value.clear()
|
||||
_addonList.value.clear()
|
||||
game = null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// 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
|
||||
)
|
||||
@@ -1,14 +0,0 @@
|
||||
// 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,7 +8,6 @@ import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class TaskViewModel : ViewModel() {
|
||||
@@ -24,28 +23,13 @@ class TaskViewModel : ViewModel() {
|
||||
val cancelled: StateFlow<Boolean> get() = _cancelled
|
||||
private val _cancelled = MutableStateFlow(false)
|
||||
|
||||
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
|
||||
lateinit var task: suspend () -> Any
|
||||
|
||||
fun clear() {
|
||||
_result.value = Any()
|
||||
_isComplete.value = false
|
||||
_isRunning.value = false
|
||||
_cancelled.value = false
|
||||
_progress.value = 0.0
|
||||
_maxProgress.value = 0.0
|
||||
_message.value = ""
|
||||
}
|
||||
|
||||
fun setCancelled(value: Boolean) {
|
||||
@@ -59,16 +43,7 @@ class TaskViewModel : ViewModel() {
|
||||
_isRunning.value = true
|
||||
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val res = task(
|
||||
{ max, progress ->
|
||||
_maxProgress.value = max.toDouble()
|
||||
_progress.value = progress.toDouble()
|
||||
return@task cancelled.value
|
||||
},
|
||||
{ message ->
|
||||
_message.value = message
|
||||
}
|
||||
)
|
||||
val res = task()
|
||||
_result.value = res
|
||||
_isComplete.value = true
|
||||
_isRunning.value = false
|
||||
|
||||
@@ -38,13 +38,12 @@ import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
|
||||
import org.yuzu.yuzu_emu.fragments.ProgressDialogFragment
|
||||
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
||||
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
||||
import org.yuzu.yuzu_emu.model.AddonViewModel
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.model.InstallResult
|
||||
import org.yuzu.yuzu_emu.model.TaskState
|
||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||
import org.yuzu.yuzu_emu.utils.*
|
||||
@@ -370,23 +369,26 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
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 firmwarePath =
|
||||
File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
|
||||
val cacheFirmwareDir = File("${cacheDir.path}/registered/")
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
this,
|
||||
R.string.firmware_installing
|
||||
) { progressCallback, _ ->
|
||||
val task: () -> Any = {
|
||||
var messageToShow: Any
|
||||
try {
|
||||
FileUtil.unzipToInternalStorage(
|
||||
result.toString(),
|
||||
cacheFirmwareDir,
|
||||
progressCallback
|
||||
)
|
||||
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheFirmwareDir)
|
||||
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
|
||||
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
|
||||
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
|
||||
@@ -402,13 +404,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
getString(R.string.save_file_imported_success)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.error("[MainActivity] Firmware install failed - ${e.message}")
|
||||
messageToShow = getString(R.string.fatal_error)
|
||||
} finally {
|
||||
cacheFirmwareDir.deleteRecursively()
|
||||
}
|
||||
messageToShow
|
||||
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
this,
|
||||
R.string.firmware_installing,
|
||||
task = task
|
||||
).show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
val getAmiiboKey =
|
||||
@@ -467,11 +474,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
this@MainActivity,
|
||||
R.string.verifying_content,
|
||||
false
|
||||
) { _, _ ->
|
||||
) {
|
||||
var updatesMatchProgram = true
|
||||
for (document in documents) {
|
||||
val valid = NativeLibrary.doesUpdateMatchProgram(
|
||||
@@ -494,42 +501,44 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
positiveAction = { homeViewModel.setContentToInstall(documents) }
|
||||
)
|
||||
}
|
||||
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
private fun installContent(documents: List<Uri>) {
|
||||
ProgressDialogFragment.newInstance(
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
this@MainActivity,
|
||||
R.string.installing_game_content
|
||||
) { progressCallback, messageCallback ->
|
||||
) {
|
||||
var installSuccess = 0
|
||||
var installOverwrite = 0
|
||||
var errorBaseGame = 0
|
||||
var error = 0
|
||||
var errorExtension = 0
|
||||
var errorOther = 0
|
||||
documents.forEach {
|
||||
messageCallback.invoke(FileUtil.getFilename(it))
|
||||
when (
|
||||
InstallResult.from(
|
||||
NativeLibrary.installFileToNand(
|
||||
it.toString(),
|
||||
progressCallback
|
||||
)
|
||||
NativeLibrary.installFileToNand(
|
||||
it.toString(),
|
||||
FileUtil.getExtension(it)
|
||||
)
|
||||
) {
|
||||
InstallResult.Success -> {
|
||||
NativeLibrary.InstallFileToNandResult.Success -> {
|
||||
installSuccess += 1
|
||||
}
|
||||
|
||||
InstallResult.Overwrite -> {
|
||||
NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
|
||||
installOverwrite += 1
|
||||
}
|
||||
|
||||
InstallResult.BaseInstallAttempted -> {
|
||||
NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
|
||||
errorBaseGame += 1
|
||||
}
|
||||
|
||||
InstallResult.Failure -> {
|
||||
error += 1
|
||||
NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
|
||||
errorExtension += 1
|
||||
}
|
||||
|
||||
else -> {
|
||||
errorOther += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -556,7 +565,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
)
|
||||
installResult.append(separator)
|
||||
}
|
||||
val errorTotal: Int = errorBaseGame + error
|
||||
val errorTotal: Int = errorBaseGame + errorExtension + errorOther
|
||||
if (errorTotal > 0) {
|
||||
installResult.append(separator)
|
||||
installResult.append(
|
||||
@@ -573,7 +582,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
)
|
||||
installResult.append(separator)
|
||||
}
|
||||
if (error > 0) {
|
||||
if (errorExtension > 0) {
|
||||
installResult.append(separator)
|
||||
installResult.append(
|
||||
getString(R.string.install_game_content_failure_file_extension)
|
||||
)
|
||||
installResult.append(separator)
|
||||
}
|
||||
if (errorOther > 0) {
|
||||
installResult.append(
|
||||
getString(R.string.install_game_content_failure_description)
|
||||
)
|
||||
@@ -592,7 +608,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
descriptionString = installResult.toString().trim()
|
||||
)
|
||||
}
|
||||
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
val exportUserData = registerForActivityResult(
|
||||
@@ -602,16 +618,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
this,
|
||||
R.string.exporting_user_data,
|
||||
true
|
||||
) { progressCallback, _ ->
|
||||
) {
|
||||
val zipResult = FileUtil.zipFromInternalStorage(
|
||||
File(DirectoryInitialization.userDirectory!!),
|
||||
DirectoryInitialization.userDirectory!!,
|
||||
BufferedOutputStream(contentResolver.openOutputStream(result)),
|
||||
progressCallback,
|
||||
taskViewModel.cancelled,
|
||||
compression = false
|
||||
)
|
||||
return@newInstance when (zipResult) {
|
||||
@@ -619,7 +635,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
TaskState.Failed -> R.string.export_failed
|
||||
TaskState.Cancelled -> R.string.user_data_export_cancelled
|
||||
}
|
||||
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
val importUserData =
|
||||
@@ -628,10 +644,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
this,
|
||||
R.string.importing_user_data
|
||||
) { progressCallback, _ ->
|
||||
) {
|
||||
val checkStream =
|
||||
ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
|
||||
var isYuzuBackup = false
|
||||
@@ -660,9 +676,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
// Copy archive to internal storage
|
||||
try {
|
||||
FileUtil.unzipToInternalStorage(
|
||||
result.toString(),
|
||||
File(DirectoryInitialization.userDirectory!!),
|
||||
progressCallback
|
||||
BufferedInputStream(contentResolver.openInputStream(result)),
|
||||
File(DirectoryInitialization.userDirectory!!)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
return@newInstance MessageDialogFragment.newInstance(
|
||||
@@ -679,6 +694,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
driverViewModel.reloadDriverData()
|
||||
|
||||
return@newInstance getString(R.string.user_data_import_success)
|
||||
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.provider.DocumentsContract
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
@@ -18,7 +19,6 @@ import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||
import org.yuzu.yuzu_emu.model.TaskState
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.OutputStream
|
||||
import java.lang.NullPointerException
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.zip.Deflater
|
||||
@@ -104,7 +104,7 @@ object FileUtil {
|
||||
|
||||
/**
|
||||
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
|
||||
* This function will be faster than DocumentFile.listFiles
|
||||
* This function will be faster than DoucmentFile.listFiles
|
||||
* @param uri Directory uri.
|
||||
* @return CheapDocument lists.
|
||||
*/
|
||||
@@ -283,34 +283,12 @@ object FileUtil {
|
||||
|
||||
/**
|
||||
* 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)
|
||||
fun unzipToInternalStorage(
|
||||
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 ->
|
||||
fun unzipToInternalStorage(zipStream: BufferedInputStream, destDir: File) {
|
||||
ZipInputStream(zipStream).use { zis ->
|
||||
var entry: ZipEntry? = zis.nextEntry
|
||||
while (entry != null) {
|
||||
if (progressCallback.invoke(totalEntries, progress)) {
|
||||
return@use
|
||||
}
|
||||
|
||||
val newFile = File(destDir, entry.name)
|
||||
val destinationDirectory = if (entry.isDirectory) newFile else newFile.parentFile
|
||||
|
||||
@@ -326,7 +304,6 @@ object FileUtil {
|
||||
newFile.outputStream().use { fos -> zis.copyTo(fos) }
|
||||
}
|
||||
entry = zis.nextEntry
|
||||
progress++
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,15 +313,14 @@ object FileUtil {
|
||||
* @param inputFile File representation of the item that will be zipped
|
||||
* @param rootDir Directory containing the inputFile
|
||||
* @param outputStream Stream where the zip file will be output
|
||||
* @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 cancelled [StateFlow] that reports whether this process has been cancelled
|
||||
* @param compression Disables compression if true
|
||||
*/
|
||||
fun zipFromInternalStorage(
|
||||
inputFile: File,
|
||||
rootDir: String,
|
||||
outputStream: BufferedOutputStream,
|
||||
progressCallback: (max: Long, progress: Long) -> Boolean = { _, _ -> false },
|
||||
cancelled: StateFlow<Boolean>? = null,
|
||||
compression: Boolean = true
|
||||
): TaskState {
|
||||
try {
|
||||
@@ -354,10 +330,8 @@ object FileUtil {
|
||||
zos.setLevel(Deflater.NO_COMPRESSION)
|
||||
}
|
||||
|
||||
var count = 0L
|
||||
val totalFiles = inputFile.walkTopDown().count().toLong()
|
||||
inputFile.walkTopDown().forEach { file ->
|
||||
if (progressCallback.invoke(totalFiles, count)) {
|
||||
if (cancelled?.value == true) {
|
||||
return TaskState.Cancelled
|
||||
}
|
||||
|
||||
@@ -369,7 +343,6 @@ object FileUtil {
|
||||
if (file.isFile) {
|
||||
file.inputStream().use { fis -> fis.copyTo(zos) }
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -383,14 +356,9 @@ object FileUtil {
|
||||
/**
|
||||
* Helper function that copies the contents of a DocumentFile folder into a [File]
|
||||
* @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
|
||||
*/
|
||||
fun DocumentFile.copyFilesTo(
|
||||
file: File,
|
||||
progressCallback: (max: Long, progress: Long) -> Boolean = { _, _ -> false }
|
||||
) {
|
||||
fun DocumentFile.copyFilesTo(file: File) {
|
||||
file.mkdirs()
|
||||
if (!this.isDirectory || !file.isDirectory) {
|
||||
throw IllegalStateException(
|
||||
@@ -398,13 +366,7 @@ object FileUtil {
|
||||
)
|
||||
}
|
||||
|
||||
var count = 0L
|
||||
val totalFiles = this.listFiles().size.toLong()
|
||||
this.listFiles().forEach {
|
||||
if (progressCallback.invoke(totalFiles, count)) {
|
||||
return
|
||||
}
|
||||
|
||||
val newFile = File(file, it.name!!)
|
||||
if (it.isDirectory) {
|
||||
newFile.mkdirs()
|
||||
@@ -419,7 +381,6 @@ object FileUtil {
|
||||
newFile.outputStream().use { os -> bos.copyTo(os) }
|
||||
}
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,18 +427,6 @@ 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)
|
||||
fun getStringFromFile(file: File): String =
|
||||
String(file.readBytes(), StandardCharsets.UTF_8)
|
||||
@@ -485,19 +434,4 @@ object FileUtil {
|
||||
@Throws(IOException::class)
|
||||
fun getStringFromInputStream(stream: InputStream): String =
|
||||
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,6 +5,7 @@ package org.yuzu.yuzu_emu.utils
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
@@ -122,7 +123,7 @@ object GpuDriverHelper {
|
||||
// Unzip the driver.
|
||||
try {
|
||||
FileUtil.unzipToInternalStorage(
|
||||
copiedFile.path,
|
||||
BufferedInputStream(copiedFile.inputStream()),
|
||||
File(driverInstallationPath!!)
|
||||
)
|
||||
} catch (e: SecurityException) {
|
||||
@@ -155,7 +156,7 @@ object GpuDriverHelper {
|
||||
// Unzip the driver to the private installation directory
|
||||
try {
|
||||
FileUtil.unzipToInternalStorage(
|
||||
driver.path,
|
||||
BufferedInputStream(driver.inputStream()),
|
||||
File(driverInstallationPath!!)
|
||||
)
|
||||
} catch (e: SecurityException) {
|
||||
|
||||
@@ -42,19 +42,3 @@ double GetJDouble(JNIEnv* env, jobject jdouble) {
|
||||
jobject ToJDouble(JNIEnv* env, double 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,7 +6,6 @@
|
||||
#include <string>
|
||||
|
||||
#include <jni.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
std::string GetJString(JNIEnv* env, jstring jstr);
|
||||
jstring ToJString(JNIEnv* env, std::string_view str);
|
||||
@@ -14,9 +13,3 @@ jstring ToJString(JNIEnv* env, std::u16string_view str);
|
||||
|
||||
double GetJDouble(JNIEnv* env, jobject jdouble);
|
||||
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() {
|
||||
SaveValues();
|
||||
Save();
|
||||
SaveAndroidValues();
|
||||
}
|
||||
|
||||
|
||||
@@ -43,27 +43,10 @@ static jfieldID s_overlay_control_data_landscape_position_field;
|
||||
static jfieldID s_overlay_control_data_portrait_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 jmethodID s_double_constructor;
|
||||
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;
|
||||
|
||||
namespace IDCache {
|
||||
@@ -203,38 +186,6 @@ jfieldID GetOverlayControlDataFoldablePositionField() {
|
||||
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() {
|
||||
return s_double_class;
|
||||
}
|
||||
@@ -247,30 +198,6 @@ jfieldID GetDoubleValueField() {
|
||||
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
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -351,37 +278,12 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
||||
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");
|
||||
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
||||
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
||||
s_double_value_field = env->GetFieldID(double_class, "value", "D");
|
||||
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
|
||||
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
||||
|
||||
@@ -407,10 +309,7 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||
env->DeleteGlobalRef(s_string_class);
|
||||
env->DeleteGlobalRef(s_pair_class);
|
||||
env->DeleteGlobalRef(s_overlay_control_data_class);
|
||||
env->DeleteGlobalRef(s_patch_class);
|
||||
env->DeleteGlobalRef(s_double_class);
|
||||
env->DeleteGlobalRef(s_integer_class);
|
||||
env->DeleteGlobalRef(s_boolean_class);
|
||||
|
||||
// UnInitialize applets
|
||||
SoftwareKeyboard::CleanupJNI(env);
|
||||
|
||||
@@ -43,25 +43,8 @@ jfieldID GetOverlayControlDataLandscapePositionField();
|
||||
jfieldID GetOverlayControlDataPortraitPositionField();
|
||||
jfieldID GetOverlayControlDataFoldablePositionField();
|
||||
|
||||
jclass GetPatchClass();
|
||||
jmethodID GetPatchConstructor();
|
||||
jfieldID GetPatchEnabledField();
|
||||
jfieldID GetPatchNameField();
|
||||
jfieldID GetPatchVersionField();
|
||||
jfieldID GetPatchTypeField();
|
||||
jfieldID GetPatchProgramIdField();
|
||||
jfieldID GetPatchTitleIdField();
|
||||
|
||||
jclass GetDoubleClass();
|
||||
jmethodID GetDoubleConstructor();
|
||||
jfieldID GetDoubleValueField();
|
||||
|
||||
jclass GetIntegerClass();
|
||||
jmethodID GetIntegerConstructor();
|
||||
jfieldID GetIntegerValueField();
|
||||
|
||||
jclass GetBooleanClass();
|
||||
jmethodID GetBooleanConstructor();
|
||||
jfieldID GetBooleanValueField();
|
||||
|
||||
} // namespace IDCache
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <core/file_sys/patch_manager.h>
|
||||
#include <core/file_sys/savedata_factory.h>
|
||||
#include <core/loader/nro.h>
|
||||
#include <frontend_common/content_manager.h>
|
||||
#include <jni.h>
|
||||
|
||||
#include "common/detached_tasks.h"
|
||||
@@ -101,6 +100,67 @@ void EmulationSession::SetNativeWindow(ANativeWindow* 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,
|
||||
const std::string& custom_driver_dir,
|
||||
const std::string& custom_driver_name,
|
||||
@@ -452,20 +512,10 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject
|
||||
}
|
||||
|
||||
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
|
||||
jstring j_file, jobject jcallback) {
|
||||
auto jlambdaClass = env->GetObjectClass(jcallback);
|
||||
auto jlambdaInvokeMethod = env->GetMethodID(
|
||||
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));
|
||||
jstring j_file,
|
||||
jstring j_file_extension) {
|
||||
return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file),
|
||||
GetJString(env, j_file_extension));
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
||||
@@ -774,9 +824,9 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env,
|
||||
return true;
|
||||
}
|
||||
|
||||
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj,
|
||||
jstring jpath,
|
||||
jstring jprogramId) {
|
||||
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env, jobject jobj,
|
||||
jstring jpath,
|
||||
jstring jprogramId) {
|
||||
const auto path = GetJString(env, jpath);
|
||||
const auto vFile =
|
||||
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
|
||||
@@ -793,40 +843,20 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
|
||||
FileSys::VirtualFile update_raw;
|
||||
loader->ReadUpdateRaw(update_raw);
|
||||
|
||||
auto patches = pm.GetPatches(update_raw);
|
||||
jobjectArray jpatchArray =
|
||||
env->NewObjectArray(patches.size(), IDCache::GetPatchClass(), nullptr);
|
||||
auto addons = pm.GetPatchVersionNames(update_raw);
|
||||
auto jemptyString = ToJString(env, "");
|
||||
auto jemptyStringPair = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
|
||||
jemptyString, jemptyString);
|
||||
jobjectArray jaddonsArray =
|
||||
env->NewObjectArray(addons.size(), IDCache::GetPairClass(), jemptyStringPair);
|
||||
int i = 0;
|
||||
for (const auto& patch : patches) {
|
||||
jobject jpatch = env->NewObject(
|
||||
IDCache::GetPatchClass(), IDCache::GetPatchConstructor(), patch.enabled,
|
||||
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);
|
||||
for (const auto& addon : addons) {
|
||||
jobject jaddon = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
|
||||
ToJString(env, addon.first), ToJString(env, addon.second));
|
||||
env->SetObjectArrayElement(jaddonsArray, i, jaddon);
|
||||
++i;
|
||||
}
|
||||
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));
|
||||
return jaddonsArray;
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "frontend_common/content_manager.h"
|
||||
#include "jni/applets/software_keyboard.h"
|
||||
#include "jni/emu_window/emu_window.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
@@ -30,6 +29,7 @@ public:
|
||||
void SetNativeWindow(ANativeWindow* native_window);
|
||||
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,
|
||||
const std::string& custom_driver_name,
|
||||
const std::string& file_redirect_dir);
|
||||
|
||||
@@ -205,7 +205,7 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEn
|
||||
jstring jkey) {
|
||||
auto setting = getSetting<std::string>(env, jkey);
|
||||
if (setting != nullptr) {
|
||||
return setting->RuntimeModifiable();
|
||||
return setting->RuntimeModfiable();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
android:layout_marginHorizontal="20dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_version_name"
|
||||
android:id="@+id/button_build_hash"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
@@ -164,7 +164,7 @@
|
||||
android:textAlignment="viewStart" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/text_version_name"
|
||||
android:id="@+id/text_build_hash"
|
||||
style="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
style="?attr/materialCardViewElevatedStyle"
|
||||
style="?attr/materialCardViewFilledStyle"
|
||||
android:id="@+id/option_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:backgroundTint="?attr/colorSurfaceVariant"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:cardElevation="4dp">
|
||||
android:focusable="true">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/option_layout"
|
||||
|
||||
@@ -1,30 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<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>
|
||||
android:padding="24dp"
|
||||
app:trackCornerRadius="4dp" />
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
android:layout_marginHorizontal="20dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_version_name"
|
||||
android:id="@+id/button_build_hash"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="16dp"
|
||||
@@ -165,7 +165,7 @@
|
||||
android:text="@string/build" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/text_version_name"
|
||||
android:id="@+id/text_build_hash"
|
||||
style="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
android:id="@+id/text_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/addon_checkbox"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/addon_switch"
|
||||
app:layout_constraintEnd_toStartOf="@+id/addon_switch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
app:layout_constraintTop_toTopOf="@+id/addon_switch">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/title"
|
||||
@@ -41,29 +42,16 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/addon_checkbox"
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/addon_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:gravity="center"
|
||||
android:layout_marginEnd="8dp"
|
||||
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"
|
||||
android:nextFocusLeft="@id/addon_container"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/addon_checkbox"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/addon_checkbox" />
|
||||
app:layout_constraintStart_toEndOf="@id/text_container"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:visibility="gone"
|
||||
android:text="@string/use_global_setting"
|
||||
android:text="@string/clear"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/use_global_setting"
|
||||
android:text="@string/clear"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_driver_use_global"
|
||||
android:title="@string/use_global_setting" />
|
||||
android:id="@+id/menu_driver_clear"
|
||||
android:icon="@drawable/ic_clear"
|
||||
android:title="@string/clear"
|
||||
app:showAsAction="always" />
|
||||
|
||||
</menu>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<item>@string/language_dutch</item>
|
||||
<item>@string/language_english</item>
|
||||
<item>@string/language_french</item>
|
||||
<item>@string/language_german</item>
|
||||
<item>@string/langauge_german</item>
|
||||
<item>@string/language_italian</item>
|
||||
<item>@string/language_japanese</item>
|
||||
<item>@string/language_korean</item>
|
||||
@@ -228,10 +228,10 @@
|
||||
<item>R</item>
|
||||
<item>ZL</item>
|
||||
<item>ZR</item>
|
||||
<item>L3</item>
|
||||
<item>R3</item>
|
||||
<item>@string/gamepad_left_stick</item>
|
||||
<item>@string/gamepad_right_stick</item>
|
||||
<item>L3</item>
|
||||
<item>R3</item>
|
||||
<item>@string/gamepad_d_pad</item>
|
||||
</string-array>
|
||||
|
||||
|
||||
@@ -286,8 +286,6 @@
|
||||
<string name="custom">Custom</string>
|
||||
<string name="notice">Notice</string>
|
||||
<string name="import_complete">Import complete</string>
|
||||
<string name="more_options">More options</string>
|
||||
<string name="use_global_setting">Use global setting</string>
|
||||
|
||||
<!-- GPU driver installation -->
|
||||
<string name="select_gpu_driver">Select GPU driver</string>
|
||||
@@ -350,8 +348,6 @@
|
||||
<string name="verifying_content">Verifying content…</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="confirm_uninstall">Confirm uninstall</string>
|
||||
<string name="confirm_uninstall_description">Are you sure you want to uninstall this addon?</string>
|
||||
|
||||
<!-- ROM loading errors -->
|
||||
<string name="loader_error_encrypted">Your ROM is encrypted</string>
|
||||
@@ -414,7 +410,7 @@
|
||||
<string name="language_japanese" translatable="false">日本語</string>
|
||||
<string name="language_english" translatable="false">English</string>
|
||||
<string name="language_french" translatable="false">Français</string>
|
||||
<string name="language_german" translatable="false">Deutsch</string>
|
||||
<string name="langauge_german" translatable="false">Deutsch</string>
|
||||
<string name="language_italian" translatable="false">Italiano</string>
|
||||
<string name="language_spanish" translatable="false">Español</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->Send(Direction::DSP, OpusDecoder::Message::Start);
|
||||
if (opus_decoder->Receive(Direction::Host) != OpusDecoder::Message::StartOK) {
|
||||
LOG_ERROR(Service_Audio, "OpusDecoder failed to initialize.");
|
||||
LOG_ERROR(Service_Audio, "OpusDeocder failed to initialize.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ void CommandGenerator::GenerateDataSourceCommand(VoiceInfo& voice_info,
|
||||
const VoiceState& voice_state, const s8 channel) {
|
||||
if (voice_info.mix_id == UnusedMixId) {
|
||||
if (voice_info.splitter_id != UnusedSplitterId) {
|
||||
auto destination{splitter_context.GetDestinationData(voice_info.splitter_id, 0)};
|
||||
auto destination{splitter_context.GetDesintationData(voice_info.splitter_id, 0)};
|
||||
u32 dest_id{0};
|
||||
while (destination != nullptr) {
|
||||
if (destination->IsConfigured()) {
|
||||
@@ -55,7 +55,7 @@ void CommandGenerator::GenerateDataSourceCommand(VoiceInfo& voice_info,
|
||||
}
|
||||
}
|
||||
dest_id++;
|
||||
destination = splitter_context.GetDestinationData(voice_info.splitter_id, dest_id);
|
||||
destination = splitter_context.GetDesintationData(voice_info.splitter_id, dest_id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -234,7 +234,7 @@ void CommandGenerator::GenerateVoiceCommand(VoiceInfo& voice_info) {
|
||||
if (voice_info.mix_id == UnusedMixId) {
|
||||
if (voice_info.splitter_id != UnusedSplitterId) {
|
||||
auto i{channel};
|
||||
auto destination{splitter_context.GetDestinationData(voice_info.splitter_id, i)};
|
||||
auto destination{splitter_context.GetDesintationData(voice_info.splitter_id, i)};
|
||||
while (destination != nullptr) {
|
||||
if (destination->IsConfigured()) {
|
||||
const auto mix_id{destination->GetMixId()};
|
||||
@@ -249,7 +249,7 @@ void CommandGenerator::GenerateVoiceCommand(VoiceInfo& voice_info) {
|
||||
}
|
||||
}
|
||||
i += voice_info.channel_count;
|
||||
destination = splitter_context.GetDestinationData(voice_info.splitter_id, i);
|
||||
destination = splitter_context.GetDesintationData(voice_info.splitter_id, i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -591,7 +591,7 @@ void CommandGenerator::GenerateMixCommands(MixInfo& mix_info) {
|
||||
if (mix_info.dst_splitter_id != UnusedSplitterId) {
|
||||
s16 dest_id{0};
|
||||
auto destination{
|
||||
splitter_context.GetDestinationData(mix_info.dst_splitter_id, dest_id)};
|
||||
splitter_context.GetDesintationData(mix_info.dst_splitter_id, dest_id)};
|
||||
while (destination != nullptr) {
|
||||
if (destination->IsConfigured()) {
|
||||
auto splitter_mix_id{destination->GetMixId()};
|
||||
@@ -612,7 +612,7 @@ void CommandGenerator::GenerateMixCommands(MixInfo& mix_info) {
|
||||
}
|
||||
dest_id++;
|
||||
destination =
|
||||
splitter_context.GetDestinationData(mix_info.dst_splitter_id, dest_id);
|
||||
splitter_context.GetDesintationData(mix_info.dst_splitter_id, dest_id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -93,7 +93,7 @@ bool MixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const InParameter& in_pa
|
||||
|
||||
for (u32 i = 0; i < destination_count; i++) {
|
||||
auto destination{
|
||||
splitter_context.GetDestinationData(in_params.dest_splitter_id, i)};
|
||||
splitter_context.GetDesintationData(in_params.dest_splitter_id, i)};
|
||||
|
||||
if (destination) {
|
||||
const auto destination_id{destination->GetMixId()};
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace AudioCore::Renderer {
|
||||
|
||||
SplitterDestinationData* SplitterContext::GetDestinationData(const s32 splitter_id,
|
||||
SplitterDestinationData* SplitterContext::GetDesintationData(const s32 splitter_id,
|
||||
const s32 destination_id) {
|
||||
return splitter_infos[splitter_id].GetData(destination_id);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
* @param destination_id - Destination index within the splitter.
|
||||
* @return Pointer to the found destination. May be nullptr.
|
||||
*/
|
||||
SplitterDestinationData* GetDestinationData(s32 splitter_id, s32 destination_id);
|
||||
SplitterDestinationData* GetDesintationData(s32 splitter_id, s32 destination_id);
|
||||
|
||||
/**
|
||||
* Get a splitter from the given index.
|
||||
|
||||
@@ -29,32 +29,28 @@ NativeClock::NativeClock() {
|
||||
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
|
||||
}
|
||||
|
||||
void NativeClock::Reset() {
|
||||
start_ticks = GetUptime();
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_cntfrq_factor)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds NativeClock::GetTimeUS() const {
|
||||
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)};
|
||||
return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_cntfrq_factor)};
|
||||
}
|
||||
|
||||
std::chrono::milliseconds NativeClock::GetTimeMS() const {
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)};
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_cntfrq_factor)};
|
||||
}
|
||||
|
||||
s64 NativeClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetUptime() - start_ticks, guest_cntfrq_factor);
|
||||
u64 NativeClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetHostTicksElapsed(), guest_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetUptime() - start_ticks, gputick_cntfrq_factor);
|
||||
u64 NativeClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetHostTicksElapsed(), gputick_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetUptime() const {
|
||||
s64 cntvct_el0 = 0;
|
||||
u64 NativeClock::GetHostTicksNow() const {
|
||||
u64 cntvct_el0 = 0;
|
||||
asm volatile("dsb ish\n\t"
|
||||
"mrs %[cntvct_el0], cntvct_el0\n\t"
|
||||
"dsb ish\n\t"
|
||||
@@ -62,11 +58,15 @@ s64 NativeClock::GetUptime() const {
|
||||
return cntvct_el0;
|
||||
}
|
||||
|
||||
u64 NativeClock::GetHostTicksElapsed() const {
|
||||
return GetHostTicksNow();
|
||||
}
|
||||
|
||||
bool NativeClock::IsNative() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
s64 NativeClock::GetHostCNTFRQ() {
|
||||
u64 NativeClock::GetHostCNTFRQ() {
|
||||
u64 cntfrq_el0 = 0;
|
||||
std::string_view board{""};
|
||||
#ifdef ANDROID
|
||||
|
||||
@@ -11,23 +11,23 @@ class NativeClock final : public WallClock {
|
||||
public:
|
||||
explicit NativeClock();
|
||||
|
||||
void Reset() override;
|
||||
|
||||
std::chrono::nanoseconds GetTimeNS() const override;
|
||||
|
||||
std::chrono::microseconds GetTimeUS() const override;
|
||||
|
||||
std::chrono::milliseconds GetTimeMS() const override;
|
||||
|
||||
s64 GetCNTPCT() const override;
|
||||
u64 GetCNTPCT() const override;
|
||||
|
||||
s64 GetGPUTick() const override;
|
||||
u64 GetGPUTick() const override;
|
||||
|
||||
s64 GetUptime() const override;
|
||||
u64 GetHostTicksNow() const override;
|
||||
|
||||
u64 GetHostTicksElapsed() const override;
|
||||
|
||||
bool IsNative() const override;
|
||||
|
||||
static s64 GetHostCNTFRQ();
|
||||
static u64 GetHostCNTFRQ();
|
||||
|
||||
public:
|
||||
using FactorType = unsigned __int128;
|
||||
@@ -42,7 +42,6 @@ private:
|
||||
FactorType ms_cntfrq_factor;
|
||||
FactorType guest_cntfrq_factor;
|
||||
FactorType gputick_cntfrq_factor;
|
||||
s64 start_ticks;
|
||||
};
|
||||
|
||||
} // namespace Common::Arm64
|
||||
|
||||
@@ -37,7 +37,7 @@ void OpenFileStream(FileStream& file_stream, const std::filesystem::path& path,
|
||||
template <typename FileStream, typename Path>
|
||||
void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::openmode open_mode) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
file_stream.open(std::filesystem::path{ToU8String(path)}, open_mode);
|
||||
file_stream.open(ToU8String(path), open_mode);
|
||||
} else {
|
||||
file_stream.open(std::filesystem::path{path}, open_mode);
|
||||
}
|
||||
|
||||
@@ -18,4 +18,4 @@ struct MemoryInfo {
|
||||
*/
|
||||
[[nodiscard]] const MemoryInfo& GetMemInfo();
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Common
|
||||
@@ -419,9 +419,7 @@ struct Values {
|
||||
linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true};
|
||||
SwitchableSetting<s64> custom_rtc{
|
||||
linkage, 0, "custom_rtc", Category::System, Specialization::Time,
|
||||
false, true, &custom_rtc_enabled};
|
||||
SwitchableSetting<s64, false> custom_rtc_offset{
|
||||
linkage, 0, "custom_rtc_offset", Category::System, Specialization::Countable, true, true};
|
||||
true, true, &custom_rtc_enabled};
|
||||
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
|
||||
s64 custom_rtc_differential;
|
||||
SwitchableSetting<bool> rng_seed_enabled{
|
||||
|
||||
@@ -35,7 +35,7 @@ bool BasicSetting::Save() const {
|
||||
return save;
|
||||
}
|
||||
|
||||
bool BasicSetting::RuntimeModifiable() const {
|
||||
bool BasicSetting::RuntimeModfiable() const {
|
||||
return runtime_modifiable;
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ public:
|
||||
/**
|
||||
* @returns true if the current setting can be changed while the guest is running.
|
||||
*/
|
||||
[[nodiscard]] bool RuntimeModifiable() const;
|
||||
[[nodiscard]] bool RuntimeModfiable() const;
|
||||
|
||||
/**
|
||||
* @returns A unique number corresponding to the setting.
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
namespace Common {
|
||||
|
||||
struct UUID {
|
||||
std::array<u8, 0x10> uuid;
|
||||
std::array<u8, 0x10> uuid{};
|
||||
|
||||
/// Constructs an invalid UUID.
|
||||
constexpr UUID() = default;
|
||||
|
||||
/// Constructs a UUID from a reference to a 128 bit array.
|
||||
@@ -33,6 +34,14 @@ struct UUID {
|
||||
*/
|
||||
explicit UUID(std::string_view uuid_string);
|
||||
|
||||
~UUID() = default;
|
||||
|
||||
constexpr UUID(const UUID&) noexcept = default;
|
||||
constexpr UUID(UUID&&) noexcept = default;
|
||||
|
||||
constexpr UUID& operator=(const UUID&) noexcept = default;
|
||||
constexpr UUID& operator=(UUID&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* Returns whether the stored UUID is valid or not.
|
||||
*
|
||||
@@ -112,7 +121,6 @@ struct UUID {
|
||||
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
|
||||
};
|
||||
static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size.");
|
||||
static_assert(std::is_trivial_v<UUID>);
|
||||
|
||||
/// An invalid UUID. This UUID has all its bytes set to 0.
|
||||
constexpr UUID InvalidUUID = {};
|
||||
|
||||
@@ -18,39 +18,34 @@ namespace Common {
|
||||
|
||||
class StandardWallClock final : public WallClock {
|
||||
public:
|
||||
explicit StandardWallClock() {}
|
||||
|
||||
void Reset() override {
|
||||
start_time = std::chrono::system_clock::now();
|
||||
}
|
||||
explicit StandardWallClock() : start_time{SteadyClock::Now()} {}
|
||||
|
||||
std::chrono::nanoseconds GetTimeNS() const override {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
return SteadyClock::Now() - start_time;
|
||||
}
|
||||
|
||||
std::chrono::microseconds GetTimeUS() const override {
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
return static_cast<std::chrono::microseconds>(GetHostTicksElapsed() / NsToUsRatio::den);
|
||||
}
|
||||
|
||||
std::chrono::milliseconds GetTimeMS() const override {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
return static_cast<std::chrono::milliseconds>(GetHostTicksElapsed() / NsToMsRatio::den);
|
||||
}
|
||||
|
||||
s64 GetCNTPCT() const override {
|
||||
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||
u64 GetCNTPCT() const override {
|
||||
return GetHostTicksElapsed() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||
}
|
||||
|
||||
s64 GetGPUTick() const override {
|
||||
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
|
||||
u64 GetGPUTick() const override {
|
||||
return GetHostTicksElapsed() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
|
||||
}
|
||||
|
||||
s64 GetUptime() const override {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now() - start_time)
|
||||
.count();
|
||||
u64 GetHostTicksNow() const override {
|
||||
return static_cast<u64>(SteadyClock::Now().time_since_epoch().count());
|
||||
}
|
||||
|
||||
u64 GetHostTicksElapsed() const override {
|
||||
return static_cast<u64>(GetTimeNS().count());
|
||||
}
|
||||
|
||||
bool IsNative() const override {
|
||||
@@ -58,7 +53,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::chrono::system_clock::time_point start_time{};
|
||||
SteadyClock::time_point start_time;
|
||||
};
|
||||
|
||||
std::unique_ptr<WallClock> CreateOptimalClock() {
|
||||
|
||||
@@ -19,8 +19,6 @@ public:
|
||||
|
||||
virtual ~WallClock() = default;
|
||||
|
||||
virtual void Reset() = 0;
|
||||
|
||||
/// @returns The time in nanoseconds since the construction of this clock.
|
||||
virtual std::chrono::nanoseconds GetTimeNS() const = 0;
|
||||
|
||||
@@ -31,13 +29,16 @@ public:
|
||||
virtual std::chrono::milliseconds GetTimeMS() const = 0;
|
||||
|
||||
/// @returns The guest CNTPCT ticks since the construction of this clock.
|
||||
virtual s64 GetCNTPCT() const = 0;
|
||||
virtual u64 GetCNTPCT() const = 0;
|
||||
|
||||
/// @returns The guest GPU ticks since the construction of this clock.
|
||||
virtual s64 GetGPUTick() const = 0;
|
||||
virtual u64 GetGPUTick() const = 0;
|
||||
|
||||
/// @returns The raw host timer ticks since an indeterminate epoch.
|
||||
virtual s64 GetUptime() const = 0;
|
||||
virtual u64 GetHostTicksNow() const = 0;
|
||||
|
||||
/// @returns The raw host timer ticks since the construction of this clock.
|
||||
virtual u64 GetHostTicksElapsed() const = 0;
|
||||
|
||||
/// @returns Whether the clock directly uses the host's hardware clock.
|
||||
virtual bool IsNative() const = 0;
|
||||
|
||||
@@ -8,39 +8,39 @@
|
||||
namespace Common::X64 {
|
||||
|
||||
NativeClock::NativeClock(u64 rdtsc_frequency_)
|
||||
: rdtsc_frequency{rdtsc_frequency_}, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den,
|
||||
rdtsc_frequency)},
|
||||
: start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_},
|
||||
ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)},
|
||||
us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)},
|
||||
ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)},
|
||||
cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)},
|
||||
gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {}
|
||||
|
||||
void NativeClock::Reset() {
|
||||
start_ticks = FencedRDTSC();
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds NativeClock::GetTimeUS() const {
|
||||
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)};
|
||||
return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::milliseconds NativeClock::GetTimeMS() const {
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)};
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)};
|
||||
}
|
||||
|
||||
s64 NativeClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetUptime() - start_ticks, cntpct_rdtsc_factor);
|
||||
u64 NativeClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetUptime() - start_ticks, gputick_rdtsc_factor);
|
||||
u64 NativeClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetUptime() const {
|
||||
return static_cast<s64>(FencedRDTSC());
|
||||
u64 NativeClock::GetHostTicksNow() const {
|
||||
return FencedRDTSC();
|
||||
}
|
||||
|
||||
u64 NativeClock::GetHostTicksElapsed() const {
|
||||
return FencedRDTSC() - start_ticks;
|
||||
}
|
||||
|
||||
bool NativeClock::IsNative() const {
|
||||
|
||||
@@ -11,19 +11,19 @@ class NativeClock final : public WallClock {
|
||||
public:
|
||||
explicit NativeClock(u64 rdtsc_frequency_);
|
||||
|
||||
void Reset() override;
|
||||
|
||||
std::chrono::nanoseconds GetTimeNS() const override;
|
||||
|
||||
std::chrono::microseconds GetTimeUS() const override;
|
||||
|
||||
std::chrono::milliseconds GetTimeMS() const override;
|
||||
|
||||
s64 GetCNTPCT() const override;
|
||||
u64 GetCNTPCT() const override;
|
||||
|
||||
s64 GetGPUTick() const override;
|
||||
u64 GetGPUTick() const override;
|
||||
|
||||
s64 GetUptime() const override;
|
||||
u64 GetHostTicksNow() const override;
|
||||
|
||||
u64 GetHostTicksElapsed() const override;
|
||||
|
||||
bool IsNative() const override;
|
||||
|
||||
|
||||
@@ -515,24 +515,6 @@ add_library(core STATIC
|
||||
hle/service/glue/glue_manager.h
|
||||
hle/service/glue/notif.cpp
|
||||
hle/service/glue/notif.h
|
||||
hle/service/glue/time/alarm_worker.cpp
|
||||
hle/service/glue/time/alarm_worker.h
|
||||
hle/service/glue/time/file_timestamp_worker.cpp
|
||||
hle/service/glue/time/file_timestamp_worker.h
|
||||
hle/service/glue/time/manager.cpp
|
||||
hle/service/glue/time/manager.h
|
||||
hle/service/glue/time/pm_state_change_handler.cpp
|
||||
hle/service/glue/time/pm_state_change_handler.h
|
||||
hle/service/glue/time/standard_steady_clock_resource.cpp
|
||||
hle/service/glue/time/standard_steady_clock_resource.h
|
||||
hle/service/glue/time/static.cpp
|
||||
hle/service/glue/time/static.h
|
||||
hle/service/glue/time/time_zone.cpp
|
||||
hle/service/glue/time/time_zone.h
|
||||
hle/service/glue/time/time_zone_binary.cpp
|
||||
hle/service/glue/time/time_zone_binary.h
|
||||
hle/service/glue/time/worker.cpp
|
||||
hle/service/glue/time/worker.h
|
||||
hle/service/grc/grc.cpp
|
||||
hle/service/grc/grc.h
|
||||
hle/service/hid/hid.cpp
|
||||
@@ -711,46 +693,6 @@ add_library(core STATIC
|
||||
hle/service/prepo/prepo.h
|
||||
hle/service/psc/psc.cpp
|
||||
hle/service/psc/psc.h
|
||||
hle/service/psc/time/alarms.cpp
|
||||
hle/service/psc/time/alarms.h
|
||||
hle/service/psc/time/clocks/context_writers.cpp
|
||||
hle/service/psc/time/clocks/context_writers.h
|
||||
hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h
|
||||
hle/service/psc/time/clocks/standard_local_system_clock_core.cpp
|
||||
hle/service/psc/time/clocks/standard_local_system_clock_core.h
|
||||
hle/service/psc/time/clocks/standard_network_system_clock_core.cpp
|
||||
hle/service/psc/time/clocks/standard_network_system_clock_core.h
|
||||
hle/service/psc/time/clocks/standard_steady_clock_core.cpp
|
||||
hle/service/psc/time/clocks/standard_steady_clock_core.h
|
||||
hle/service/psc/time/clocks/standard_user_system_clock_core.cpp
|
||||
hle/service/psc/time/clocks/standard_user_system_clock_core.h
|
||||
hle/service/psc/time/clocks/steady_clock_core.h
|
||||
hle/service/psc/time/clocks/system_clock_core.cpp
|
||||
hle/service/psc/time/clocks/system_clock_core.h
|
||||
hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp
|
||||
hle/service/psc/time/clocks/tick_based_steady_clock_core.h
|
||||
hle/service/psc/time/common.cpp
|
||||
hle/service/psc/time/common.h
|
||||
hle/service/psc/time/errors.h
|
||||
hle/service/psc/time/shared_memory.cpp
|
||||
hle/service/psc/time/shared_memory.h
|
||||
hle/service/psc/time/static.cpp
|
||||
hle/service/psc/time/static.h
|
||||
hle/service/psc/time/manager.h
|
||||
hle/service/psc/time/power_state_service.cpp
|
||||
hle/service/psc/time/power_state_service.h
|
||||
hle/service/psc/time/service_manager.cpp
|
||||
hle/service/psc/time/service_manager.h
|
||||
hle/service/psc/time/steady_clock.cpp
|
||||
hle/service/psc/time/steady_clock.h
|
||||
hle/service/psc/time/system_clock.cpp
|
||||
hle/service/psc/time/system_clock.h
|
||||
hle/service/psc/time/time_zone.cpp
|
||||
hle/service/psc/time/time_zone.h
|
||||
hle/service/psc/time/time_zone_service.cpp
|
||||
hle/service/psc/time/time_zone_service.h
|
||||
hle/service/psc/time/power_state_request_manager.cpp
|
||||
hle/service/psc/time/power_state_request_manager.h
|
||||
hle/service/ptm/psm.cpp
|
||||
hle/service/ptm/psm.h
|
||||
hle/service/ptm/ptm.cpp
|
||||
@@ -774,23 +716,22 @@ add_library(core STATIC
|
||||
hle/service/server_manager.h
|
||||
hle/service/service.cpp
|
||||
hle/service/service.h
|
||||
hle/service/set/setting_formats/appln_settings.cpp
|
||||
hle/service/set/setting_formats/appln_settings.h
|
||||
hle/service/set/setting_formats/device_settings.cpp
|
||||
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/appln_settings.cpp
|
||||
hle/service/set/appln_settings.h
|
||||
hle/service/set/device_settings.cpp
|
||||
hle/service/set/device_settings.h
|
||||
hle/service/set/factory_settings_server.cpp
|
||||
hle/service/set/factory_settings_server.h
|
||||
hle/service/set/firmware_debug_settings_server.cpp
|
||||
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.h
|
||||
hle/service/set/settings_server.cpp
|
||||
hle/service/set/settings_server.h
|
||||
hle/service/set/settings_types.h
|
||||
hle/service/set/system_settings.cpp
|
||||
hle/service/set/system_settings.h
|
||||
hle/service/set/system_settings_server.cpp
|
||||
hle/service/set/system_settings_server.h
|
||||
hle/service/sm/sm.cpp
|
||||
@@ -818,6 +759,40 @@ add_library(core STATIC
|
||||
hle/service/ssl/ssl.cpp
|
||||
hle/service/ssl/ssl.h
|
||||
hle/service/ssl/ssl_backend.h
|
||||
hle/service/time/clock_types.h
|
||||
hle/service/time/ephemeral_network_system_clock_context_writer.h
|
||||
hle/service/time/ephemeral_network_system_clock_core.h
|
||||
hle/service/time/errors.h
|
||||
hle/service/time/local_system_clock_context_writer.h
|
||||
hle/service/time/network_system_clock_context_writer.h
|
||||
hle/service/time/standard_local_system_clock_core.h
|
||||
hle/service/time/standard_network_system_clock_core.h
|
||||
hle/service/time/standard_steady_clock_core.cpp
|
||||
hle/service/time/standard_steady_clock_core.h
|
||||
hle/service/time/standard_user_system_clock_core.cpp
|
||||
hle/service/time/standard_user_system_clock_core.h
|
||||
hle/service/time/steady_clock_core.h
|
||||
hle/service/time/system_clock_context_update_callback.cpp
|
||||
hle/service/time/system_clock_context_update_callback.h
|
||||
hle/service/time/system_clock_core.cpp
|
||||
hle/service/time/system_clock_core.h
|
||||
hle/service/time/tick_based_steady_clock_core.cpp
|
||||
hle/service/time/tick_based_steady_clock_core.h
|
||||
hle/service/time/time.cpp
|
||||
hle/service/time/time.h
|
||||
hle/service/time/time_interface.cpp
|
||||
hle/service/time/time_interface.h
|
||||
hle/service/time/time_manager.cpp
|
||||
hle/service/time/time_manager.h
|
||||
hle/service/time/time_sharedmemory.cpp
|
||||
hle/service/time/time_sharedmemory.h
|
||||
hle/service/time/time_zone_content_manager.cpp
|
||||
hle/service/time/time_zone_content_manager.h
|
||||
hle/service/time/time_zone_manager.cpp
|
||||
hle/service/time/time_zone_manager.h
|
||||
hle/service/time/time_zone_service.cpp
|
||||
hle/service/time/time_zone_service.h
|
||||
hle/service/time/time_zone_types.h
|
||||
hle/service/usb/usb.cpp
|
||||
hle/service/usb/usb.h
|
||||
hle/service/vi/display/vi_display.cpp
|
||||
@@ -898,7 +873,7 @@ endif()
|
||||
|
||||
create_target_directory_groups(core)
|
||||
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb)
|
||||
target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API)
|
||||
if (MINGW)
|
||||
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
|
||||
|
||||
@@ -22,10 +22,14 @@ using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
|
||||
constexpr size_t MaxRelativeBranch = 128_MiB;
|
||||
constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32);
|
||||
|
||||
Patcher::Patcher() : c(m_patch_instructions) {
|
||||
// The first word of the patch section is always a branch to the first instruction of the
|
||||
// module.
|
||||
c.dw(0);
|
||||
Patcher::Patcher() : c(m_patch_instructions) {}
|
||||
|
||||
Patcher::~Patcher() = default;
|
||||
|
||||
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.
|
||||
c.l(m_save_context);
|
||||
@@ -34,25 +38,6 @@ Patcher::Patcher() : c(m_patch_instructions) {
|
||||
// Write load context helper function.
|
||||
c.l(m_load_context);
|
||||
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.
|
||||
const auto text = std::span{program_image}.subspan(code.offset, code.size);
|
||||
@@ -109,17 +94,16 @@ bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
|
||||
}
|
||||
|
||||
if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
|
||||
curr_patch->m_exclusives.push_back(i);
|
||||
m_exclusives.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine patching mode for the final relocation step
|
||||
total_program_size += image_size;
|
||||
const size_t image_size = program_image.size();
|
||||
this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
||||
void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
||||
const Kernel::CodeSet::Segment& code,
|
||||
Kernel::PhysicalMemory& program_image,
|
||||
EntryTrampolines* out_trampolines) {
|
||||
@@ -136,7 +120,7 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
||||
if (mode == PatchMode::PreText) {
|
||||
rc.B(rel.patch_offset - patch_size - rel.module_offset);
|
||||
} else {
|
||||
rc.B(total_program_size - rel.module_offset + rel.patch_offset);
|
||||
rc.B(image_size - rel.module_offset + rel.patch_offset);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,7 +129,7 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
||||
if (mode == PatchMode::PreText) {
|
||||
rc.B(patch_size - rel.patch_offset + rel.module_offset);
|
||||
} else {
|
||||
rc.B(rel.module_offset - total_program_size - rel.patch_offset);
|
||||
rc.B(rel.module_offset - image_size - rel.patch_offset);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -153,7 +137,7 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
||||
if (mode == PatchMode::PreText) {
|
||||
return GetInteger(load_base) + patch_offset;
|
||||
} else {
|
||||
return GetInteger(load_base) + total_program_size + patch_offset;
|
||||
return GetInteger(load_base) + image_size + patch_offset;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -166,50 +150,39 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
||||
};
|
||||
|
||||
// We are now ready to relocate!
|
||||
auto& patch = modules[m_relocate_module_index++];
|
||||
for (const Relocation& rel : patch.m_branch_to_patch_relocations) {
|
||||
for (const Relocation& rel : m_branch_to_patch_relocations) {
|
||||
ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel);
|
||||
}
|
||||
for (const Relocation& rel : patch.m_branch_to_module_relocations) {
|
||||
for (const Relocation& rel : m_branch_to_module_relocations) {
|
||||
ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32),
|
||||
rel);
|
||||
}
|
||||
|
||||
// Rewrite PC constants and record post trampolines
|
||||
for (const Relocation& rel : patch.m_write_module_pc_relocations) {
|
||||
for (const Relocation& rel : m_write_module_pc_relocations) {
|
||||
oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
|
||||
rc.dx(RebasePc(rel.module_offset));
|
||||
}
|
||||
for (const Trampoline& rel : patch.m_trampolines) {
|
||||
for (const Trampoline& rel : m_trampolines) {
|
||||
out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)});
|
||||
}
|
||||
|
||||
// Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not.
|
||||
// Convert to ordered to preserve this assumption.
|
||||
for (const ModuleTextAddress i : patch.m_exclusives) {
|
||||
for (const ModuleTextAddress i : m_exclusives) {
|
||||
auto exclusive = Exclusive{text_words[i]};
|
||||
text_words[i] = exclusive.AsOrdered();
|
||||
}
|
||||
|
||||
// Remove the patched module size from the total. This is done so total_program_size
|
||||
// always represents the distance from the currently patched module to the patch section.
|
||||
total_program_size -= image_size;
|
||||
|
||||
// Only copy to the program image of the last module
|
||||
if (m_relocate_module_index == modules.size()) {
|
||||
if (this->mode == PatchMode::PreText) {
|
||||
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;
|
||||
// Copy to program image
|
||||
if (this->mode == PatchMode::PreText) {
|
||||
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 false;
|
||||
}
|
||||
|
||||
size_t Patcher::GetSectionSize() const noexcept {
|
||||
@@ -349,7 +322,7 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) {
|
||||
|
||||
// Write the post-SVC trampoline address, which will jump back to the guest after restoring its
|
||||
// state.
|
||||
curr_patch->m_trampolines.push_back({c.offset(), module_dest});
|
||||
m_trampolines.push_back({c.offset(), module_dest});
|
||||
|
||||
// Host called this location. Save the return address so we can
|
||||
// unwind the stack properly when jumping back.
|
||||
|
||||
@@ -31,9 +31,9 @@ public:
|
||||
explicit Patcher();
|
||||
~Patcher();
|
||||
|
||||
bool PatchText(const Kernel::PhysicalMemory& program_image,
|
||||
void PatchText(const Kernel::PhysicalMemory& program_image,
|
||||
const Kernel::CodeSet::Segment& code);
|
||||
bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
|
||||
void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
|
||||
Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines);
|
||||
size_t GetSectionSize() const noexcept;
|
||||
|
||||
@@ -61,16 +61,16 @@ private:
|
||||
|
||||
private:
|
||||
void BranchToPatch(uintptr_t module_dest) {
|
||||
curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
|
||||
m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
|
||||
}
|
||||
|
||||
void BranchToModule(uintptr_t module_dest) {
|
||||
curr_patch->m_branch_to_module_relocations.push_back({c.offset(), module_dest});
|
||||
m_branch_to_module_relocations.push_back({c.offset(), module_dest});
|
||||
c.dw(0);
|
||||
}
|
||||
|
||||
void WriteModulePc(uintptr_t module_dest) {
|
||||
curr_patch->m_write_module_pc_relocations.push_back({c.offset(), module_dest});
|
||||
m_write_module_pc_relocations.push_back({c.offset(), module_dest});
|
||||
c.dx(0);
|
||||
}
|
||||
|
||||
@@ -84,22 +84,15 @@ private:
|
||||
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;
|
||||
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_load_context{};
|
||||
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
|
||||
|
||||
@@ -40,14 +40,9 @@
|
||||
#include "core/hle/service/apm/apm_controller.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/glue/glue_manager.h"
|
||||
#include "core/hle/service/glue/time/static.h"
|
||||
#include "core/hle/service/psc/time/static.h"
|
||||
#include "core/hle/service/psc/time/steady_clock.h"
|
||||
#include "core/hle/service/psc/time/system_clock.h"
|
||||
#include "core/hle/service/psc/time/time_zone_service.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/set/system_settings_server.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
#include "core/internal_network/network.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
@@ -135,8 +130,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system)
|
||||
: kernel{system}, fs_controller{system}, hid_core{}, room_network{},
|
||||
cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{} {}
|
||||
: kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system},
|
||||
reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {}
|
||||
|
||||
void Initialize(System& system) {
|
||||
device_memory = std::make_unique<Core::DeviceMemory>();
|
||||
@@ -148,6 +143,8 @@ struct System::Impl {
|
||||
core_timing.SetMulticore(is_multicore);
|
||||
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
||||
|
||||
RefreshTime();
|
||||
|
||||
// Create a default fs if one doesn't already exist.
|
||||
if (virtual_filesystem == nullptr) {
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
@@ -185,57 +182,14 @@ struct System::Impl {
|
||||
Initialize(system);
|
||||
}
|
||||
|
||||
void RefreshTime(System& system) {
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto settings_service =
|
||||
system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys",
|
||||
true);
|
||||
auto static_service_a =
|
||||
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:a", true);
|
||||
|
||||
auto static_service_s =
|
||||
system.ServiceManager().GetService<Service::PSC::Time::StaticService>("time:s", true);
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> user_clock;
|
||||
static_service_a->GetStandardUserSystemClock(user_clock);
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> local_clock;
|
||||
static_service_a->GetStandardLocalSystemClock(local_clock);
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> network_clock;
|
||||
static_service_s->GetStandardNetworkSystemClock(network_clock);
|
||||
|
||||
std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service;
|
||||
static_service_a->GetTimeZoneService(timezone_service);
|
||||
|
||||
Service::PSC::Time::LocationName name{};
|
||||
auto new_name = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
|
||||
std::memcpy(name.name.data(), new_name.data(), std::min(name.name.size(), new_name.size()));
|
||||
|
||||
timezone_service->SetDeviceLocation(name);
|
||||
|
||||
u64 time_offset = 0;
|
||||
if (Settings::values.custom_rtc_enabled) {
|
||||
time_offset = Settings::values.custom_rtc_offset.GetValue();
|
||||
}
|
||||
|
||||
void RefreshTime() {
|
||||
const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
|
||||
const u64 current_time =
|
||||
+std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
|
||||
const u64 new_time = current_time + time_offset;
|
||||
|
||||
Service::PSC::Time::SystemClockContext context{};
|
||||
settings_service->SetUserSystemClockContext(context);
|
||||
user_clock->SetCurrentTime(new_time);
|
||||
|
||||
local_clock->SetCurrentTime(new_time);
|
||||
|
||||
network_clock->GetSystemClockContext(context);
|
||||
settings_service->SetNetworkSystemClockContext(context);
|
||||
network_clock->SetCurrentTime(new_time);
|
||||
const auto current_time =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
|
||||
Settings::values.custom_rtc_differential =
|
||||
(Settings::values.custom_rtc_enabled ? Settings::values.custom_rtc.GetValue()
|
||||
: current_time) -
|
||||
current_time;
|
||||
}
|
||||
|
||||
void Run() {
|
||||
@@ -311,6 +265,9 @@ struct System::Impl {
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
|
||||
services = std::make_unique<Service::Services>(service_manager, system);
|
||||
|
||||
// Initialize time manager, which must happen after kernel is created
|
||||
time_manager.Initialize();
|
||||
|
||||
is_powered_on = true;
|
||||
exit_locked = false;
|
||||
exit_requested = false;
|
||||
@@ -460,6 +417,7 @@ struct System::Impl {
|
||||
fs_controller.Reset();
|
||||
cheat_engine.reset();
|
||||
telemetry_session.reset();
|
||||
time_manager.Shutdown();
|
||||
core_timing.ClearPendingEvents();
|
||||
app_loader.reset();
|
||||
audio_core.reset();
|
||||
@@ -575,6 +533,7 @@ struct System::Impl {
|
||||
/// Service State
|
||||
Service::Glue::ARPManager arp_manager;
|
||||
Service::Account::ProfileManager profile_manager;
|
||||
Service::Time::TimeManager time_manager;
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
@@ -952,6 +911,14 @@ const Service::Account::ProfileManager& System::GetProfileManager() const {
|
||||
return impl->profile_manager;
|
||||
}
|
||||
|
||||
Service::Time::TimeManager& System::GetTimeManager() {
|
||||
return impl->time_manager;
|
||||
}
|
||||
|
||||
const Service::Time::TimeManager& System::GetTimeManager() const {
|
||||
return impl->time_manager;
|
||||
}
|
||||
|
||||
void System::SetExitLocked(bool locked) {
|
||||
impl->exit_locked = locked;
|
||||
}
|
||||
@@ -1063,9 +1030,13 @@ void System::Exit() {
|
||||
}
|
||||
|
||||
void System::ApplySettings() {
|
||||
impl->RefreshTime(*this);
|
||||
impl->RefreshTime();
|
||||
|
||||
if (IsPoweredOn()) {
|
||||
if (Settings::values.custom_rtc_enabled) {
|
||||
const s64 posix_time{Settings::values.custom_rtc.GetValue()};
|
||||
GetTimeManager().UpdateLocalSystemClockTime(posix_time);
|
||||
}
|
||||
Renderer().RefreshBaseSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,10 @@ namespace SM {
|
||||
class ServiceManager;
|
||||
} // namespace SM
|
||||
|
||||
namespace Time {
|
||||
class TimeManager;
|
||||
} // namespace Time
|
||||
|
||||
} // namespace Service
|
||||
|
||||
namespace Tegra {
|
||||
@@ -377,6 +381,9 @@ public:
|
||||
[[nodiscard]] Service::Account::ProfileManager& GetProfileManager();
|
||||
[[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const;
|
||||
|
||||
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
|
||||
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
|
||||
|
||||
[[nodiscard]] Core::Debugger& GetDebugger();
|
||||
[[nodiscard]] const Core::Debugger& GetDebugger() const;
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
event_fifo_id = 0;
|
||||
shutting_down = false;
|
||||
cpu_ticks = 0;
|
||||
clock->Reset();
|
||||
if (is_multicore) {
|
||||
timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
|
||||
}
|
||||
@@ -158,7 +157,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& h : to_remove) {
|
||||
for (auto h : to_remove) {
|
||||
event_queue.erase(h);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,6 @@ class Memory;
|
||||
template <typename DTraits>
|
||||
struct DeviceMemoryManagerAllocator;
|
||||
|
||||
struct Asid {
|
||||
size_t id;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
class DeviceMemoryManager {
|
||||
using DeviceInterface = typename Traits::DeviceInterface;
|
||||
@@ -47,14 +43,15 @@ public:
|
||||
void AllocateFixed(DAddr start, size_t size);
|
||||
void Free(DAddr start, size_t size);
|
||||
|
||||
void Map(DAddr address, VAddr virtual_address, size_t size, Asid asid, bool track = false);
|
||||
void Map(DAddr address, VAddr virtual_address, size_t size, size_t process_id,
|
||||
bool track = false);
|
||||
|
||||
void Unmap(DAddr address, size_t size);
|
||||
|
||||
void TrackContinuityImpl(DAddr address, VAddr virtual_address, size_t size, Asid asid);
|
||||
void TrackContinuity(DAddr address, VAddr virtual_address, size_t size, Asid asid) {
|
||||
void TrackContinuityImpl(DAddr address, VAddr virtual_address, size_t size, size_t process_id);
|
||||
void TrackContinuity(DAddr address, VAddr virtual_address, size_t size, size_t process_id) {
|
||||
std::scoped_lock lk(mapping_guard);
|
||||
TrackContinuityImpl(address, virtual_address, size, asid);
|
||||
TrackContinuityImpl(address, virtual_address, size, process_id);
|
||||
}
|
||||
|
||||
// Write / Read
|
||||
@@ -108,8 +105,8 @@ public:
|
||||
void WriteBlock(DAddr address, const void* src_pointer, size_t size);
|
||||
void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size);
|
||||
|
||||
Asid RegisterProcess(Memory::Memory* memory);
|
||||
void UnregisterProcess(Asid id);
|
||||
size_t RegisterProcess(Memory::Memory* memory);
|
||||
void UnregisterProcess(size_t id);
|
||||
|
||||
void UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta);
|
||||
|
||||
@@ -166,17 +163,17 @@ private:
|
||||
static constexpr size_t guest_max_as_bits = 39;
|
||||
static constexpr size_t guest_as_size = 1ULL << guest_max_as_bits;
|
||||
static constexpr size_t guest_mask = guest_as_size - 1ULL;
|
||||
static constexpr size_t asid_start_bit = guest_max_as_bits;
|
||||
static constexpr size_t process_id_start_bit = guest_max_as_bits;
|
||||
|
||||
std::pair<Asid, VAddr> ExtractCPUBacking(size_t page_index) {
|
||||
std::pair<size_t, VAddr> ExtractCPUBacking(size_t page_index) {
|
||||
auto content = cpu_backing_address[page_index];
|
||||
const VAddr address = content & guest_mask;
|
||||
const Asid asid{static_cast<size_t>(content >> asid_start_bit)};
|
||||
return std::make_pair(asid, address);
|
||||
const size_t process_id = static_cast<size_t>(content >> process_id_start_bit);
|
||||
return std::make_pair(process_id, address);
|
||||
}
|
||||
|
||||
void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
||||
cpu_backing_address[page_index] = address | (asid.id << asid_start_bit);
|
||||
void InsertCPUBacking(size_t page_index, VAddr address, size_t process_id) {
|
||||
cpu_backing_address[page_index] = address | (process_id << process_id_start_bit);
|
||||
}
|
||||
|
||||
Common::VirtualBuffer<VAddr> cpu_backing_address;
|
||||
@@ -208,4 +205,4 @@ private:
|
||||
std::mutex mapping_guard;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
} // namespace Core
|
||||
@@ -215,8 +215,8 @@ void DeviceMemoryManager<Traits>::Free(DAddr start, size_t size) {
|
||||
|
||||
template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size_t size,
|
||||
Asid asid, bool track) {
|
||||
Core::Memory::Memory* process_memory = registered_processes[asid.id];
|
||||
size_t process_id, bool track) {
|
||||
Core::Memory::Memory* process_memory = registered_processes[process_id];
|
||||
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
|
||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||
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;
|
||||
compressed_physical_ptr[start_page_d + i] = phys_addr;
|
||||
InsertCPUBacking(start_page_d + i, new_vaddress, asid);
|
||||
InsertCPUBacking(start_page_d + i, new_vaddress, process_id);
|
||||
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
||||
const u32 new_dev = static_cast<u32>(start_page_d + i);
|
||||
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);
|
||||
}
|
||||
if (track) {
|
||||
TrackContinuityImpl(address, virtual_address, size, asid);
|
||||
TrackContinuityImpl(address, virtual_address, size, process_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,8 +277,8 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
|
||||
}
|
||||
template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtual_address,
|
||||
size_t size, Asid asid) {
|
||||
Core::Memory::Memory* process_memory = registered_processes[asid.id];
|
||||
size_t size, size_t process_id) {
|
||||
Core::Memory::Memory* process_memory = registered_processes[process_id];
|
||||
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
|
||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||
uintptr_t last_ptr = 0;
|
||||
@@ -488,8 +488,8 @@ void DeviceMemoryManager<Traits>::WriteBlockUnsafe(DAddr address, const void* sr
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
Asid DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_device_inter) {
|
||||
size_t new_id{};
|
||||
size_t DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_device_inter) {
|
||||
size_t new_id;
|
||||
if (!id_pool.empty()) {
|
||||
new_id = id_pool.front();
|
||||
id_pool.pop_front();
|
||||
@@ -498,23 +498,29 @@ Asid DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_device_
|
||||
registered_processes.emplace_back(memory_device_inter);
|
||||
new_id = registered_processes.size() - 1U;
|
||||
}
|
||||
return Asid{new_id};
|
||||
return new_id;
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::UnregisterProcess(Asid asid) {
|
||||
registered_processes[asid.id] = nullptr;
|
||||
id_pool.push_front(asid.id);
|
||||
void DeviceMemoryManager<Traits>::UnregisterProcess(size_t id) {
|
||||
registered_processes[id] = nullptr;
|
||||
id_pool.push_front(id);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) {
|
||||
std::unique_lock<std::mutex> lk(counter_guard, std::defer_lock);
|
||||
const auto Lock = [&] {
|
||||
if (!lk) {
|
||||
lk.lock();
|
||||
bool locked = false;
|
||||
auto lock = [&] {
|
||||
if (!locked) {
|
||||
counter_guard.lock();
|
||||
locked = true;
|
||||
}
|
||||
};
|
||||
SCOPE_EXIT({
|
||||
if (locked) {
|
||||
counter_guard.unlock();
|
||||
}
|
||||
});
|
||||
u64 uncache_begin = 0;
|
||||
u64 cache_begin = 0;
|
||||
u64 uncache_bytes = 0;
|
||||
@@ -524,9 +530,9 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
|
||||
size_t page = addr >> Memory::YUZU_PAGEBITS;
|
||||
auto [asid, base_vaddress] = ExtractCPUBacking(page);
|
||||
auto [process_id, base_vaddress] = ExtractCPUBacking(page);
|
||||
size_t vpage = base_vaddress >> Memory::YUZU_PAGEBITS;
|
||||
auto* memory_device_inter = registered_processes[asid.id];
|
||||
auto* memory_device_inter = registered_processes[process_id];
|
||||
for (; page != page_end; ++page) {
|
||||
std::atomic_uint8_t& count = cached_pages->at(page >> 3).Count(page);
|
||||
|
||||
@@ -549,7 +555,7 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
|
||||
}
|
||||
uncache_bytes += Memory::YUZU_PAGESIZE;
|
||||
} else if (uncache_bytes > 0) {
|
||||
Lock();
|
||||
lock();
|
||||
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
|
||||
uncache_bytes, false);
|
||||
uncache_bytes = 0;
|
||||
@@ -560,7 +566,7 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
|
||||
}
|
||||
cache_bytes += Memory::YUZU_PAGESIZE;
|
||||
} else if (cache_bytes > 0) {
|
||||
Lock();
|
||||
lock();
|
||||
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
|
||||
true);
|
||||
cache_bytes = 0;
|
||||
@@ -568,12 +574,12 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
|
||||
vpage++;
|
||||
}
|
||||
if (uncache_bytes > 0) {
|
||||
Lock();
|
||||
lock();
|
||||
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes,
|
||||
false);
|
||||
}
|
||||
if (cache_bytes > 0) {
|
||||
Lock();
|
||||
lock();
|
||||
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
|
||||
true);
|
||||
}
|
||||
|
||||
@@ -466,12 +466,12 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
||||
return romfs;
|
||||
}
|
||||
|
||||
std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const {
|
||||
if (title_id == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Patch> out;
|
||||
std::map<std::string, std::string, std::less<>> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
|
||||
// Game Updates
|
||||
@@ -482,28 +482,20 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
|
||||
const auto update_disabled =
|
||||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
||||
Patch update_patch = {.enabled = !update_disabled,
|
||||
.name = "Update",
|
||||
.version = "",
|
||||
.type = PatchType::Update,
|
||||
.program_id = title_id,
|
||||
.title_id = title_id};
|
||||
const auto update_label = update_disabled ? "[D] Update" : "Update";
|
||||
|
||||
if (nacp != nullptr) {
|
||||
update_patch.version = nacp->GetVersionString();
|
||||
out.push_back(update_patch);
|
||||
out.insert_or_assign(update_label, nacp->GetVersionString());
|
||||
} else {
|
||||
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
||||
const auto meta_ver = content_provider.GetEntryVersion(update_tid);
|
||||
if (meta_ver.value_or(0) == 0) {
|
||||
out.push_back(update_patch);
|
||||
out.insert_or_assign(update_label, "");
|
||||
} else {
|
||||
update_patch.version = FormatTitleVersion(*meta_ver);
|
||||
out.push_back(update_patch);
|
||||
out.insert_or_assign(update_label, FormatTitleVersion(*meta_ver));
|
||||
}
|
||||
} else if (update_raw != nullptr) {
|
||||
update_patch.version = "PACKED";
|
||||
out.push_back(update_patch);
|
||||
out.insert_or_assign(update_label, "PACKED");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,12 +539,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
|
||||
const auto mod_disabled =
|
||||
std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end();
|
||||
out.push_back({.enabled = !mod_disabled,
|
||||
.name = mod->GetName(),
|
||||
.version = types,
|
||||
.type = PatchType::Mod,
|
||||
.program_id = title_id,
|
||||
.title_id = title_id});
|
||||
out.insert_or_assign(mod_disabled ? "[D] " + mod->GetName() : mod->GetName(), types);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,12 +557,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
if (!types.empty()) {
|
||||
const auto mod_disabled =
|
||||
std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
|
||||
out.push_back({.enabled = !mod_disabled,
|
||||
.name = "SDMC",
|
||||
.version = types,
|
||||
.type = PatchType::Mod,
|
||||
.program_id = title_id,
|
||||
.title_id = title_id});
|
||||
out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,12 +584,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
|
||||
const auto dlc_disabled =
|
||||
std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end();
|
||||
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});
|
||||
out.insert_or_assign(dlc_disabled ? "[D] DLC" : "DLC", std::move(list));
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
@@ -26,22 +26,12 @@ class ContentProvider;
|
||||
class NCA;
|
||||
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.
|
||||
class PatchManager {
|
||||
public:
|
||||
using BuildID = std::array<u8, 0x20>;
|
||||
using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>;
|
||||
using PatchVersionNames = std::map<std::string, std::string, std::less<>>;
|
||||
|
||||
explicit PatchManager(u64 title_id_,
|
||||
const Service::FileSystem::FileSystemController& fs_controller_,
|
||||
@@ -76,8 +66,9 @@ public:
|
||||
VirtualFile packed_update_raw = nullptr,
|
||||
bool apply_layeredfs = true) const;
|
||||
|
||||
// Returns a vector of patches
|
||||
[[nodiscard]] std::vector<Patch> GetPatches(VirtualFile update_raw = nullptr) const;
|
||||
// Returns a vector of pairs between patch names and patch versions.
|
||||
// i.e. Update 3.2.2 will return {"Update", "3.2.2"}
|
||||
[[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,
|
||||
// it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/system_archive/time_zone_binary.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
#include "core/hle/service/time/time_zone_types.h"
|
||||
|
||||
#include "nx_tzdb.h"
|
||||
|
||||
|
||||
@@ -1239,10 +1239,10 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
|
||||
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
|
||||
|
||||
#ifdef HAS_NCE
|
||||
const auto& patch = code_set.PatchSegment();
|
||||
if (this->IsApplication() && Settings::IsNceEnabled() && patch.size != 0) {
|
||||
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
||||
auto& buffer = m_kernel.System().DeviceMemory().buffer;
|
||||
const auto& code = code_set.CodeSegment();
|
||||
const auto& patch = code_set.PatchSegment();
|
||||
buffer.Protect(GetInteger(base_addr + code.addr), code.size,
|
||||
Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
|
||||
buffer.Protect(GetInteger(base_addr + patch.addr), patch.size,
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/polyfill_ranges.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
|
||||
namespace Service::Account {
|
||||
@@ -62,7 +61,9 @@ ProfileManager::ProfileManager() {
|
||||
OpenUser(*GetUser(current));
|
||||
}
|
||||
|
||||
ProfileManager::~ProfileManager() = default;
|
||||
ProfileManager::~ProfileManager() {
|
||||
WriteUserSaveFile();
|
||||
}
|
||||
|
||||
/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
|
||||
/// internal management of the users profiles
|
||||
@@ -112,8 +113,6 @@ Result ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username)
|
||||
return ERROR_USER_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
is_save_needed = true;
|
||||
|
||||
return AddUser({
|
||||
.user_uuid = uuid,
|
||||
.username = username,
|
||||
@@ -165,22 +164,6 @@ std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user)
|
||||
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:*
|
||||
bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
|
||||
if (!index || index >= MAX_USERS) {
|
||||
@@ -343,9 +326,6 @@ bool ProfileManager::RemoveUser(UUID uuid) {
|
||||
profiles[*index] = ProfileInfo{};
|
||||
std::stable_partition(profiles.begin(), profiles.end(),
|
||||
[](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
|
||||
|
||||
is_save_needed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -360,8 +340,6 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
|
||||
profile.username = profile_new.username;
|
||||
profile.creation_time = profile_new.timestamp;
|
||||
|
||||
is_save_needed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -370,7 +348,6 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase&
|
||||
const auto index = GetUserIndex(uuid);
|
||||
if (index.has_value() && SetProfileBase(uuid, profile_new)) {
|
||||
profiles[*index].data = data_new;
|
||||
is_save_needed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -414,10 +391,6 @@ void ProfileManager::ParseUserSaveFile() {
|
||||
}
|
||||
|
||||
void ProfileManager::WriteUserSaveFile() {
|
||||
if (!is_save_needed) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileDataRaw raw{};
|
||||
|
||||
for (std::size_t i = 0; i < MAX_USERS; ++i) {
|
||||
@@ -450,10 +423,7 @@ void ProfileManager::WriteUserSaveFile() {
|
||||
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 "
|
||||
"made in current session will be saved.");
|
||||
return;
|
||||
}
|
||||
|
||||
is_save_needed = false;
|
||||
}
|
||||
|
||||
}; // namespace Service::Account
|
||||
|
||||
@@ -70,7 +70,6 @@ public:
|
||||
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 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(Common::UUID uuid, ProfileBase& profile) const;
|
||||
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
|
||||
@@ -104,7 +103,6 @@ private:
|
||||
std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
|
||||
bool RemoveProfileAtIndex(std::size_t index);
|
||||
|
||||
bool is_save_needed{};
|
||||
std::array<ProfileInfo, MAX_USERS> profiles{};
|
||||
std::array<ProfileInfo, MAX_USERS> stored_opened_profiles{};
|
||||
std::size_t user_count{};
|
||||
|
||||
@@ -10,10 +10,8 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/caps/caps_manager.h"
|
||||
#include "core/hle/service/caps/caps_result.h"
|
||||
#include "core/hle/service/glue/time/static.h"
|
||||
#include "core/hle/service/psc/time/system_clock.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
#include "core/hle/service/time/time_zone_content_manager.h"
|
||||
|
||||
namespace Service::Capture {
|
||||
|
||||
@@ -241,15 +239,10 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
|
||||
const ApplicationData& app_data, std::span<const u8> image_data,
|
||||
u64 aruid) {
|
||||
const u64 title_id = system.GetApplicationProcessProgramID();
|
||||
|
||||
auto static_service =
|
||||
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{};
|
||||
static_service->GetStandardUserSystemClock(user_clock);
|
||||
const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore();
|
||||
|
||||
s64 posix_time{};
|
||||
auto result = user_clock->GetCurrentTime(posix_time);
|
||||
Result result = user_clock.GetCurrentTime(system, posix_time);
|
||||
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
@@ -264,14 +257,10 @@ Result AlbumManager::SaveEditedScreenShot(ApplicationAlbumEntry& out_entry,
|
||||
const ScreenShotAttribute& attribute,
|
||||
const AlbumFileId& file_id,
|
||||
std::span<const u8> image_data) {
|
||||
auto static_service =
|
||||
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{};
|
||||
static_service->GetStandardUserSystemClock(user_clock);
|
||||
const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore();
|
||||
|
||||
s64 posix_time{};
|
||||
auto result = user_clock->GetCurrentTime(posix_time);
|
||||
Result result = user_clock.GetCurrentTime(system, posix_time);
|
||||
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
@@ -466,23 +455,19 @@ Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const
|
||||
}
|
||||
|
||||
AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const {
|
||||
auto static_service =
|
||||
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
|
||||
Time::TimeZone::CalendarInfo calendar_date{};
|
||||
const auto& time_zone_manager =
|
||||
system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager();
|
||||
|
||||
std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{};
|
||||
static_service->GetTimeZoneService(timezone_service);
|
||||
|
||||
Service::PSC::Time::CalendarTime calendar_time{};
|
||||
Service::PSC::Time::CalendarAdditionalInfo additional_info{};
|
||||
timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time);
|
||||
time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date);
|
||||
|
||||
return {
|
||||
.year = calendar_time.year,
|
||||
.month = calendar_time.month,
|
||||
.day = calendar_time.day,
|
||||
.hour = calendar_time.hour,
|
||||
.minute = calendar_time.minute,
|
||||
.second = calendar_time.second,
|
||||
.year = calendar_date.time.year,
|
||||
.month = calendar_date.time.month,
|
||||
.day = calendar_date.time.day,
|
||||
.hour = calendar_date.time.hour,
|
||||
.minute = calendar_date.time.minute,
|
||||
.second = calendar_date.time.second,
|
||||
.unique_id = 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
#include "core/hle/service/glue/ectx.h"
|
||||
#include "core/hle/service/glue/glue.h"
|
||||
#include "core/hle/service/glue/notif.h"
|
||||
#include "core/hle/service/glue/time/manager.h"
|
||||
#include "core/hle/service/glue/time/static.h"
|
||||
#include "core/hle/service/psc/time/common.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
|
||||
namespace Service::Glue {
|
||||
@@ -34,22 +31,6 @@ void LoopProcess(Core::System& system) {
|
||||
// Notification Services for application
|
||||
server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system));
|
||||
|
||||
// Time
|
||||
auto time = std::make_shared<Time::TimeManager>(system);
|
||||
|
||||
server_manager->RegisterNamedService(
|
||||
"time:u",
|
||||
std::make_shared<Time::StaticService>(
|
||||
system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, time, "time:u"));
|
||||
server_manager->RegisterNamedService(
|
||||
"time:a",
|
||||
std::make_shared<Time::StaticService>(
|
||||
system, Service::PSC::Time::StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, time, "time:a"));
|
||||
server_manager->RegisterNamedService(
|
||||
"time:r",
|
||||
std::make_shared<Time::StaticService>(
|
||||
system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, time, "time:r"));
|
||||
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/service/glue/time/alarm_worker.h"
|
||||
#include "core/hle/service/psc/time/service_manager.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
|
||||
AlarmWorker::AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource)
|
||||
: m_system{system}, m_ctx{system, "Glue:AlarmWorker"}, m_steady_clock_resource{
|
||||
steady_clock_resource} {}
|
||||
|
||||
AlarmWorker::~AlarmWorker() {
|
||||
m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
|
||||
|
||||
m_ctx.CloseEvent(m_timer_event);
|
||||
}
|
||||
|
||||
void AlarmWorker::Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m) {
|
||||
m_time_m = std::move(time_m);
|
||||
|
||||
m_timer_event = m_ctx.CreateEvent("Glue:AlarmWorker:TimerEvent");
|
||||
m_timer_timing_event = Core::Timing::CreateEvent(
|
||||
"Glue:AlarmWorker::AlarmTimer",
|
||||
[this](s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
m_timer_event->Signal();
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
AttachToClosestAlarmEvent();
|
||||
}
|
||||
|
||||
bool AlarmWorker::GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info,
|
||||
s64& out_time) {
|
||||
bool is_valid{};
|
||||
Service::PSC::Time::AlarmInfo alarm_info{};
|
||||
s64 closest_time{};
|
||||
|
||||
auto res = m_time_m->GetClosestAlarmInfo(is_valid, alarm_info, closest_time);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
if (is_valid) {
|
||||
out_alarm_info = alarm_info;
|
||||
out_time = closest_time;
|
||||
}
|
||||
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
void AlarmWorker::OnPowerStateChanged() {
|
||||
Service::PSC::Time::AlarmInfo closest_alarm_info{};
|
||||
s64 closest_time{};
|
||||
if (!GetClosestAlarmInfo(closest_alarm_info, closest_time)) {
|
||||
m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
|
||||
m_timer_event->Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (closest_alarm_info.alert_time <= closest_time) {
|
||||
m_time_m->CheckAndSignalAlarms();
|
||||
} else {
|
||||
auto next_time{closest_alarm_info.alert_time - closest_time};
|
||||
|
||||
m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
|
||||
m_timer_event->Clear();
|
||||
|
||||
m_system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds(next_time),
|
||||
m_timer_timing_event);
|
||||
}
|
||||
}
|
||||
|
||||
Result AlarmWorker::AttachToClosestAlarmEvent() {
|
||||
m_time_m->GetClosestAlarmUpdatedEvent(&m_event);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,53 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/psc/time/common.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::PSC::Time {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
class StandardSteadyClockResource;
|
||||
|
||||
class AlarmWorker {
|
||||
public:
|
||||
explicit AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource);
|
||||
~AlarmWorker();
|
||||
|
||||
void Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m);
|
||||
|
||||
Kernel::KEvent& GetEvent() {
|
||||
return *m_event;
|
||||
}
|
||||
|
||||
Kernel::KEvent& GetTimerEvent() {
|
||||
return *m_timer_event;
|
||||
}
|
||||
|
||||
void OnPowerStateChanged();
|
||||
|
||||
private:
|
||||
bool GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info, s64& out_time);
|
||||
Result AttachToClosestAlarmEvent();
|
||||
|
||||
Core::System& m_system;
|
||||
KernelHelpers::ServiceContext m_ctx;
|
||||
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
|
||||
|
||||
Kernel::KEvent* m_event{};
|
||||
Kernel::KEvent* m_timer_event{};
|
||||
std::shared_ptr<Core::Timing::EventType> m_timer_timing_event;
|
||||
StandardSteadyClockResource& m_steady_clock_resource;
|
||||
};
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,23 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/glue/time/file_timestamp_worker.h"
|
||||
#include "core/hle/service/psc/time/common.h"
|
||||
#include "core/hle/service/psc/time/system_clock.h"
|
||||
#include "core/hle/service/psc/time/time_zone_service.h"
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
|
||||
void FileTimestampWorker::SetFilesystemPosixTime() {
|
||||
s64 time{};
|
||||
Service::PSC::Time::CalendarTime calendar_time{};
|
||||
Service::PSC::Time::CalendarAdditionalInfo additional_info{};
|
||||
|
||||
if (m_initialized && m_system_clock->GetCurrentTime(time) == ResultSuccess &&
|
||||
m_time_zone->ToCalendarTimeWithMyRule(calendar_time, additional_info, time) ==
|
||||
ResultSuccess) {
|
||||
// TODO IFileSystemProxy::SetCurrentPosixTime
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,28 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::PSC::Time {
|
||||
class SystemClock;
|
||||
class TimeZoneService;
|
||||
} // namespace Service::PSC::Time
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
|
||||
class FileTimestampWorker {
|
||||
public:
|
||||
FileTimestampWorker() = default;
|
||||
|
||||
void SetFilesystemPosixTime();
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> m_system_clock{};
|
||||
std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone{};
|
||||
bool m_initialized{};
|
||||
};
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,258 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/service/glue/time/manager.h"
|
||||
#include "core/hle/service/glue/time/time_zone_binary.h"
|
||||
#include "core/hle/service/psc/time/service_manager.h"
|
||||
#include "core/hle/service/psc/time/static.h"
|
||||
#include "core/hle/service/psc/time/system_clock.h"
|
||||
#include "core/hle/service/psc/time/time_zone_service.h"
|
||||
#include "core/hle/service/set/system_settings_server.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
|
||||
const char* category, const char* name) {
|
||||
std::vector<u8> interval_buf;
|
||||
auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
T v{};
|
||||
std::memcpy(&v, interval_buf.data(), sizeof(T));
|
||||
return v;
|
||||
}
|
||||
|
||||
s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) {
|
||||
constexpr auto is_leap = [](s32 year) -> bool {
|
||||
return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
|
||||
};
|
||||
constexpr std::array<s32, 12> MonthStartDayOfYear{
|
||||
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
|
||||
};
|
||||
|
||||
s16 month_s16{calendar.month};
|
||||
s8 month{static_cast<s8>(((month_s16 * 43) & ~std::numeric_limits<s16>::max()) +
|
||||
((month_s16 * 43) >> 9))};
|
||||
s8 month_index{static_cast<s8>(calendar.month - 12 * month)};
|
||||
if (month_index == 0) {
|
||||
month_index = 12;
|
||||
}
|
||||
s32 year{(month + calendar.year) - !month_index};
|
||||
s32 v8{year >= 0 ? year : year + 3};
|
||||
|
||||
s64 days_since_epoch = calendar.day + MonthStartDayOfYear[month_index - 1];
|
||||
days_since_epoch += (year * 365) + (v8 / 4) - (year / 100) + (year / 400) - 365;
|
||||
|
||||
if (month_index <= 2 && is_leap(year)) {
|
||||
days_since_epoch--;
|
||||
}
|
||||
auto epoch_s{((24ll * days_since_epoch + calendar.hour) * 60ll + calendar.minute) * 60ll +
|
||||
calendar.second};
|
||||
return epoch_s - 62135683200ll;
|
||||
}
|
||||
|
||||
s64 GetEpochTimeFromInitialYear(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys) {
|
||||
Service::PSC::Time::CalendarTime calendar{
|
||||
.year = GetSettingsItemValue<s16>(set_sys, "time", "standard_user_clock_initial_year"),
|
||||
.month = 1,
|
||||
.day = 1,
|
||||
.hour = 0,
|
||||
.minute = 0,
|
||||
.second = 0,
|
||||
};
|
||||
return CalendarTimeToEpoch(calendar);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TimeManager::TimeManager(Core::System& system)
|
||||
: m_steady_clock_resource{system}, m_worker{system, m_steady_clock_resource,
|
||||
m_file_timestamp_worker} {
|
||||
m_time_m =
|
||||
system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
|
||||
|
||||
auto res = m_time_m->GetStaticServiceAsServiceManager(m_time_sm);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
m_set_sys =
|
||||
system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
|
||||
|
||||
ResetTimeZoneBinary();
|
||||
res = MountTimeZoneBinary(system);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
m_worker.Initialize(m_time_sm, m_set_sys);
|
||||
|
||||
res = m_time_sm->GetStandardUserSystemClock(m_file_timestamp_worker.m_system_clock);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
res = m_time_sm->GetTimeZoneService(m_file_timestamp_worker.m_time_zone);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
res = SetupStandardSteadyClockCore();
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
Service::PSC::Time::SystemClockContext user_clock_context{};
|
||||
res = m_set_sys->GetUserSystemClockContext(user_clock_context);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
// TODO the local clock should initialise with this epoch time, and be updated somewhere else on
|
||||
// first boot to update it, but I haven't been able to find that point (likely via ntc's auto
|
||||
// correct as it's defaulted to be enabled). So to get a time that isn't stuck in the past for
|
||||
// first boot, grab the current real seconds.
|
||||
auto epoch_time{GetEpochTimeFromInitialYear(m_set_sys)};
|
||||
if (user_clock_context == Service::PSC::Time::SystemClockContext{}) {
|
||||
m_steady_clock_resource.GetRtcTimeInSeconds(epoch_time);
|
||||
}
|
||||
|
||||
res = m_time_m->SetupStandardLocalSystemClockCore(user_clock_context, epoch_time);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
Service::PSC::Time::SystemClockContext network_clock_context{};
|
||||
res = m_set_sys->GetNetworkSystemClockContext(network_clock_context);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
auto network_accuracy_m{GetSettingsItemValue<s32>(
|
||||
m_set_sys, "time", "standard_network_clock_sufficient_accuracy_minutes")};
|
||||
auto one_minute_ns{
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
|
||||
s64 network_accuracy_ns{network_accuracy_m * one_minute_ns};
|
||||
|
||||
res = m_time_m->SetupStandardNetworkSystemClockCore(network_clock_context, network_accuracy_ns);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
bool is_automatic_correction_enabled{};
|
||||
res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(is_automatic_correction_enabled);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
Service::PSC::Time::SteadyClockTimePoint automatic_correction_time_point{};
|
||||
res = m_set_sys->GetUserSystemClockAutomaticCorrectionUpdatedTime(
|
||||
automatic_correction_time_point);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
res = m_time_m->SetupStandardUserSystemClockCore(automatic_correction_time_point,
|
||||
is_automatic_correction_enabled);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
res = m_time_m->SetupEphemeralNetworkSystemClockCore();
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
res = SetupTimeZoneServiceCore();
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
s64 rtc_time_s{};
|
||||
res = m_steady_clock_resource.GetRtcTimeInSeconds(rtc_time_s);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
// TODO system report "launch"
|
||||
// "rtc_reset" = m_steady_clock_resource.m_rtc_reset
|
||||
// "rtc_value" = rtc_time_s
|
||||
|
||||
m_worker.StartThread();
|
||||
|
||||
m_file_timestamp_worker.m_initialized = true;
|
||||
|
||||
s64 system_clock_time{};
|
||||
if (m_file_timestamp_worker.m_system_clock->GetCurrentTime(system_clock_time) ==
|
||||
ResultSuccess) {
|
||||
Service::PSC::Time::CalendarTime calendar_time{};
|
||||
Service::PSC::Time::CalendarAdditionalInfo calendar_additional{};
|
||||
if (m_file_timestamp_worker.m_time_zone->ToCalendarTimeWithMyRule(
|
||||
calendar_time, calendar_additional, system_clock_time) == ResultSuccess) {
|
||||
// TODO IFileSystemProxy::SetCurrentPosixTime(system_clock_time,
|
||||
// calendar_additional.ut_offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result TimeManager::SetupStandardSteadyClockCore() {
|
||||
Common::UUID external_clock_source_id{};
|
||||
auto res = m_set_sys->GetExternalSteadyClockSourceId(external_clock_source_id);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
s64 external_steady_clock_internal_offset_s{};
|
||||
res = m_set_sys->GetExternalSteadyClockInternalOffset(external_steady_clock_internal_offset_s);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
auto one_second_ns{
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
|
||||
s64 external_steady_clock_internal_offset_ns{external_steady_clock_internal_offset_s *
|
||||
one_second_ns};
|
||||
|
||||
s32 standard_steady_clock_test_offset_m{
|
||||
GetSettingsItemValue<s32>(m_set_sys, "time", "standard_steady_clock_test_offset_minutes")};
|
||||
auto one_minute_ns{
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
|
||||
s64 standard_steady_clock_test_offset_ns{standard_steady_clock_test_offset_m * one_minute_ns};
|
||||
|
||||
auto reset_detected = m_steady_clock_resource.GetResetDetected();
|
||||
if (reset_detected) {
|
||||
external_clock_source_id = {};
|
||||
}
|
||||
|
||||
Common::UUID clock_source_id{};
|
||||
m_steady_clock_resource.Initialize(&clock_source_id, &external_clock_source_id);
|
||||
|
||||
if (clock_source_id != external_clock_source_id) {
|
||||
m_set_sys->SetExternalSteadyClockSourceId(clock_source_id);
|
||||
}
|
||||
|
||||
res = m_time_m->SetupStandardSteadyClockCore(clock_source_id, m_steady_clock_resource.GetTime(),
|
||||
external_steady_clock_internal_offset_ns,
|
||||
standard_steady_clock_test_offset_ns,
|
||||
reset_detected);
|
||||
ASSERT(res == ResultSuccess);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result TimeManager::SetupTimeZoneServiceCore() {
|
||||
Service::PSC::Time::LocationName name{};
|
||||
auto res = m_set_sys->GetDeviceTimeZoneLocationName(name);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
auto configured_zone = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
|
||||
Service::PSC::Time::LocationName new_name{};
|
||||
std::memcpy(new_name.name.data(), configured_zone.data(),
|
||||
std::min(new_name.name.size(), configured_zone.size()));
|
||||
if (new_name.name != name.name) {
|
||||
m_set_sys->SetDeviceTimeZoneLocationName(new_name);
|
||||
name = new_name;
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> local_clock;
|
||||
m_time_sm->GetStandardLocalSystemClock(local_clock);
|
||||
Service::PSC::Time::SystemClockContext context{};
|
||||
local_clock->GetSystemClockContext(context);
|
||||
m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(context.steady_time_point);
|
||||
}
|
||||
|
||||
Service::PSC::Time::SteadyClockTimePoint time_point{};
|
||||
res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(time_point);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
auto location_count = GetTimeZoneCount();
|
||||
Service::PSC::Time::RuleVersion rule_version{};
|
||||
GetTimeZoneVersion(rule_version);
|
||||
|
||||
std::span<const u8> rule_buffer{};
|
||||
size_t rule_size{};
|
||||
res = GetTimeZoneRule(rule_buffer, rule_size, name);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
res = m_time_m->SetupTimeZoneServiceCore(name, time_point, rule_version, location_count,
|
||||
rule_buffer);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,42 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/hle/service/glue/time/file_timestamp_worker.h"
|
||||
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
|
||||
#include "core/hle/service/glue/time/worker.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::PSC::Time {
|
||||
class ServiceManager;
|
||||
class StaticService;
|
||||
} // namespace Service::PSC::Time
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
class TimeManager {
|
||||
public:
|
||||
explicit TimeManager(Core::System& system);
|
||||
|
||||
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m{};
|
||||
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm{};
|
||||
StandardSteadyClockResource m_steady_clock_resource;
|
||||
FileTimestampWorker m_file_timestamp_worker;
|
||||
TimeWorker m_worker;
|
||||
|
||||
private:
|
||||
Result SetupStandardSteadyClockCore();
|
||||
Result SetupTimeZoneServiceCore();
|
||||
};
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,13 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/glue/time/pm_state_change_handler.h"
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
|
||||
PmStateChangeHandler::PmStateChangeHandler(AlarmWorker& alarm_worker)
|
||||
: m_alarm_worker{alarm_worker} {
|
||||
// TODO Initialize IPmModule, dependent on Rtc and Fs
|
||||
}
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,18 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
class AlarmWorker;
|
||||
|
||||
class PmStateChangeHandler {
|
||||
public:
|
||||
explicit PmStateChangeHandler(AlarmWorker& alarm_worker);
|
||||
|
||||
AlarmWorker& m_alarm_worker;
|
||||
s32 m_priority{};
|
||||
};
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,123 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
|
||||
#include "core/hle/service/psc/time/errors.h"
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
namespace {
|
||||
[[maybe_unused]] constexpr u32 Max77620PmicSession = 0x3A000001;
|
||||
[[maybe_unused]] constexpr u32 Max77620RtcSession = 0x3B000001;
|
||||
|
||||
Result GetTimeInSeconds(Core::System& system, s64& out_time_s) {
|
||||
out_time_s = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
|
||||
if (Settings::values.custom_rtc_enabled) {
|
||||
out_time_s += Settings::values.custom_rtc_offset.GetValue();
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
StandardSteadyClockResource::StandardSteadyClockResource(Core::System& system) : m_system{system} {}
|
||||
|
||||
void StandardSteadyClockResource::Initialize(Common::UUID* out_source_id,
|
||||
Common::UUID* external_source_id) {
|
||||
constexpr size_t NUM_TRIES{20};
|
||||
|
||||
size_t i{0};
|
||||
Result res{ResultSuccess};
|
||||
for (; i < NUM_TRIES; i++) {
|
||||
res = SetCurrentTime();
|
||||
if (res == ResultSuccess) {
|
||||
break;
|
||||
}
|
||||
Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::milliseconds(1))
|
||||
.count());
|
||||
}
|
||||
|
||||
if (i < NUM_TRIES) {
|
||||
m_set_time_result = ResultSuccess;
|
||||
if (*external_source_id != Service::PSC::Time::ClockSourceId{}) {
|
||||
m_clock_source_id = *external_source_id;
|
||||
} else {
|
||||
m_clock_source_id = Common::UUID::MakeRandom();
|
||||
}
|
||||
} else {
|
||||
m_set_time_result = res;
|
||||
auto ticks{m_system.CoreTiming().GetClockTicks()};
|
||||
m_time = -Service::PSC::Time::ConvertToTimeSpan(ticks).count();
|
||||
m_clock_source_id = Common::UUID::MakeRandom();
|
||||
}
|
||||
|
||||
if (out_source_id) {
|
||||
*out_source_id = m_clock_source_id;
|
||||
}
|
||||
}
|
||||
|
||||
bool StandardSteadyClockResource::GetResetDetected() {
|
||||
// TODO:
|
||||
// call Rtc::GetRtcResetDetected(Max77620RtcSession)
|
||||
// if detected:
|
||||
// SetSys::SetExternalSteadyClockSourceId(invalid_id)
|
||||
// Rtc::ClearRtcResetDetected(Max77620RtcSession)
|
||||
// set m_rtc_reset to result
|
||||
// Instead, only set reset to true if we're booting for the first time.
|
||||
m_rtc_reset = false;
|
||||
return m_rtc_reset;
|
||||
}
|
||||
|
||||
Result StandardSteadyClockResource::SetCurrentTime() {
|
||||
auto start_tick{m_system.CoreTiming().GetClockTicks()};
|
||||
|
||||
s64 rtc_time_s{};
|
||||
// TODO R_TRY(Rtc::GetTimeInSeconds(rtc_time_s, Max77620RtcSession))
|
||||
R_TRY(GetTimeInSeconds(m_system, rtc_time_s));
|
||||
|
||||
auto end_tick{m_system.CoreTiming().GetClockTicks()};
|
||||
auto diff{Service::PSC::Time::ConvertToTimeSpan(end_tick - start_tick)};
|
||||
// Why is this here?
|
||||
R_UNLESS(diff < std::chrono::milliseconds(101), Service::PSC::Time::ResultRtcTimeout);
|
||||
|
||||
auto one_second_ns{
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
|
||||
s64 boot_time{rtc_time_s * one_second_ns -
|
||||
Service::PSC::Time::ConvertToTimeSpan(end_tick).count()};
|
||||
|
||||
std::scoped_lock l{m_mutex};
|
||||
m_time = boot_time;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result StandardSteadyClockResource::GetRtcTimeInSeconds(s64& out_time) {
|
||||
// TODO
|
||||
// R_TRY(Rtc::GetTimeInSeconds(time_s, Max77620RtcSession)
|
||||
R_RETURN(GetTimeInSeconds(m_system, out_time));
|
||||
}
|
||||
|
||||
void StandardSteadyClockResource::UpdateTime() {
|
||||
constexpr size_t NUM_TRIES{3};
|
||||
|
||||
size_t i{0};
|
||||
Result res{ResultSuccess};
|
||||
for (; i < NUM_TRIES; i++) {
|
||||
res = SetCurrentTime();
|
||||
if (res == ResultSuccess) {
|
||||
break;
|
||||
}
|
||||
Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::milliseconds(1))
|
||||
.count());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,41 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/psc/time/common.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
class StandardSteadyClockResource {
|
||||
public:
|
||||
StandardSteadyClockResource(Core::System& system);
|
||||
|
||||
void Initialize(Common::UUID* out_source_id, Common::UUID* external_source_id);
|
||||
|
||||
s64 GetTime() const {
|
||||
return m_time;
|
||||
}
|
||||
|
||||
bool GetResetDetected();
|
||||
Result SetCurrentTime();
|
||||
Result GetRtcTimeInSeconds(s64& out_time);
|
||||
void UpdateTime();
|
||||
|
||||
private:
|
||||
Core::System& m_system;
|
||||
|
||||
std::mutex m_mutex;
|
||||
Service::PSC::Time::ClockSourceId m_clock_source_id{};
|
||||
s64 m_time{};
|
||||
Result m_set_time_result;
|
||||
bool m_rtc_reset;
|
||||
};
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,447 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/service/glue/time/file_timestamp_worker.h"
|
||||
#include "core/hle/service/glue/time/static.h"
|
||||
#include "core/hle/service/psc/time/errors.h"
|
||||
#include "core/hle/service/psc/time/service_manager.h"
|
||||
#include "core/hle/service/psc/time/static.h"
|
||||
#include "core/hle/service/psc/time/steady_clock.h"
|
||||
#include "core/hle/service/psc/time/system_clock.h"
|
||||
#include "core/hle/service/psc/time/time_zone_service.h"
|
||||
#include "core/hle/service/set/system_settings_server.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
namespace {
|
||||
template <typename T>
|
||||
T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
|
||||
const char* category, const char* name) {
|
||||
std::vector<u8> interval_buf;
|
||||
auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
T v{};
|
||||
std::memcpy(&v, interval_buf.data(), sizeof(T));
|
||||
return v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
StaticService::StaticService(Core::System& system_,
|
||||
Service::PSC::Time::StaticServiceSetupInfo setup_info,
|
||||
std::shared_ptr<TimeManager> time, const char* name)
|
||||
: ServiceFramework{system_, name}, m_system{system_}, m_time_m{time->m_time_m},
|
||||
m_setup_info{setup_info}, m_time_sm{time->m_time_sm},
|
||||
m_file_timestamp_worker{time->m_file_timestamp_worker}, m_standard_steady_clock_resource{
|
||||
time->m_steady_clock_resource} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"},
|
||||
{1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
|
||||
{2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"},
|
||||
{3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"},
|
||||
{4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
|
||||
{5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"},
|
||||
{20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
|
||||
{50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"},
|
||||
{51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"},
|
||||
{100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"},
|
||||
{200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
|
||||
{201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
|
||||
{300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
|
||||
{400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"},
|
||||
{401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
|
||||
{500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
|
||||
{501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
m_set_sys =
|
||||
m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
|
||||
|
||||
if (m_setup_info.can_write_local_clock && m_setup_info.can_write_user_clock &&
|
||||
!m_setup_info.can_write_network_clock && m_setup_info.can_write_timezone_device_location &&
|
||||
!m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
|
||||
m_time_m->GetStaticServiceAsAdmin(m_wrapped_service);
|
||||
} else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
|
||||
!m_setup_info.can_write_network_clock &&
|
||||
!m_setup_info.can_write_timezone_device_location &&
|
||||
!m_setup_info.can_write_steady_clock &&
|
||||
!m_setup_info.can_write_uninitialized_clock) {
|
||||
m_time_m->GetStaticServiceAsUser(m_wrapped_service);
|
||||
} else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
|
||||
!m_setup_info.can_write_network_clock &&
|
||||
!m_setup_info.can_write_timezone_device_location &&
|
||||
m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
|
||||
m_time_m->GetStaticServiceAsRepair(m_wrapped_service);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
auto res = m_wrapped_service->GetTimeZoneService(m_time_zone);
|
||||
ASSERT(res == ResultSuccess);
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> service{};
|
||||
auto res = GetStandardUserSystemClock(service);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(res);
|
||||
rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> service{};
|
||||
auto res = GetStandardNetworkSystemClock(service);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(res);
|
||||
rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SteadyClock> service{};
|
||||
auto res = GetStandardSteadyClock(service);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(res);
|
||||
rb.PushIpcInterface(std::move(service));
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
std::shared_ptr<TimeZoneService> service{};
|
||||
auto res = GetTimeZoneService(service);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(res);
|
||||
rb.PushIpcInterface(std::move(service));
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> service{};
|
||||
auto res = GetStandardLocalSystemClock(service);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(res);
|
||||
rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> service{};
|
||||
auto res = GetEphemeralNetworkSystemClock(service);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(res);
|
||||
rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
Kernel::KSharedMemory* shared_memory{};
|
||||
auto res = GetSharedMemoryNativeHandle(&shared_memory);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(res);
|
||||
rb.PushCopyObjects(shared_memory);
|
||||
}
|
||||
|
||||
void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto offset_ns{rp.Pop<s64>()};
|
||||
|
||||
auto res = SetStandardSteadyClockInternalOffset(offset_ns);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
s64 rtc_value{};
|
||||
auto res = GetStandardSteadyClockRtcValue(rtc_value);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(res);
|
||||
rb.Push(rtc_value);
|
||||
}
|
||||
|
||||
void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(
|
||||
HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
bool is_enabled{};
|
||||
auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push<bool>(is_enabled);
|
||||
}
|
||||
|
||||
void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(
|
||||
HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto automatic_correction{rp.Pop<bool>()};
|
||||
|
||||
auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
s32 initial_year{};
|
||||
auto res = GetStandardUserSystemClockInitialYear(initial_year);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push(initial_year);
|
||||
}
|
||||
|
||||
void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
bool is_sufficient{};
|
||||
auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push<bool>(is_sufficient);
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
|
||||
HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
Service::PSC::Time::SteadyClockTimePoint time_point{};
|
||||
auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx,
|
||||
2 + sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32)};
|
||||
rb.Push(res);
|
||||
rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
|
||||
}
|
||||
|
||||
void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
|
||||
|
||||
s64 time{};
|
||||
auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(res);
|
||||
rb.Push<s64>(time);
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto type{rp.PopEnum<Service::PSC::Time::TimeType>()};
|
||||
|
||||
Service::PSC::Time::ClockSnapshot snapshot{};
|
||||
auto res = GetClockSnapshot(snapshot, type);
|
||||
|
||||
ctx.WriteBuffer(snapshot);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
}
|
||||
|
||||
void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto user_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
|
||||
auto network_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
|
||||
auto clock_type{rp.PopEnum<Service::PSC::Time::TimeType>()};
|
||||
|
||||
Service::PSC::Time::ClockSnapshot snapshot{};
|
||||
auto res =
|
||||
GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type);
|
||||
|
||||
ctx.WriteBuffer(snapshot);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
}
|
||||
|
||||
void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser(
|
||||
HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
Service::PSC::Time::ClockSnapshot a{};
|
||||
Service::PSC::Time::ClockSnapshot b{};
|
||||
|
||||
auto a_buffer{ctx.ReadBuffer(0)};
|
||||
auto b_buffer{ctx.ReadBuffer(1)};
|
||||
|
||||
std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
|
||||
std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
|
||||
|
||||
s64 difference{};
|
||||
auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(res);
|
||||
rb.Push(difference);
|
||||
}
|
||||
|
||||
void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
Service::PSC::Time::ClockSnapshot a{};
|
||||
Service::PSC::Time::ClockSnapshot b{};
|
||||
|
||||
auto a_buffer{ctx.ReadBuffer(0)};
|
||||
auto b_buffer{ctx.ReadBuffer(1)};
|
||||
|
||||
std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
|
||||
std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
|
||||
|
||||
s64 time{};
|
||||
auto res = CalculateSpanBetween(time, a, b);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(res);
|
||||
rb.Push(time);
|
||||
}
|
||||
|
||||
// =============================== Implementations ===========================
|
||||
|
||||
Result StaticService::GetStandardUserSystemClock(
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
|
||||
R_RETURN(m_wrapped_service->GetStandardUserSystemClock(out_service));
|
||||
}
|
||||
|
||||
Result StaticService::GetStandardNetworkSystemClock(
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
|
||||
R_RETURN(m_wrapped_service->GetStandardNetworkSystemClock(out_service));
|
||||
}
|
||||
|
||||
Result StaticService::GetStandardSteadyClock(
|
||||
std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service) {
|
||||
R_RETURN(m_wrapped_service->GetStandardSteadyClock(out_service));
|
||||
}
|
||||
|
||||
Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) {
|
||||
out_service = std::make_shared<TimeZoneService>(m_system, m_file_timestamp_worker,
|
||||
m_setup_info.can_write_timezone_device_location,
|
||||
m_time_zone);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result StaticService::GetStandardLocalSystemClock(
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
|
||||
R_RETURN(m_wrapped_service->GetStandardLocalSystemClock(out_service));
|
||||
}
|
||||
|
||||
Result StaticService::GetEphemeralNetworkSystemClock(
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
|
||||
R_RETURN(m_wrapped_service->GetEphemeralNetworkSystemClock(out_service));
|
||||
}
|
||||
|
||||
Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) {
|
||||
R_RETURN(m_wrapped_service->GetSharedMemoryNativeHandle(out_shared_memory));
|
||||
}
|
||||
|
||||
Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) {
|
||||
R_UNLESS(m_setup_info.can_write_steady_clock, Service::PSC::Time::ResultPermissionDenied);
|
||||
|
||||
R_RETURN(m_set_sys->SetExternalSteadyClockInternalOffset(
|
||||
offset_ns /
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()));
|
||||
}
|
||||
|
||||
Result StaticService::GetStandardSteadyClockRtcValue(s64& out_rtc_value) {
|
||||
R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(out_rtc_value));
|
||||
}
|
||||
|
||||
Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(
|
||||
bool& out_automatic_correction) {
|
||||
R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled(
|
||||
out_automatic_correction));
|
||||
}
|
||||
|
||||
Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(
|
||||
bool automatic_correction) {
|
||||
R_RETURN(m_wrapped_service->SetStandardUserSystemClockAutomaticCorrectionEnabled(
|
||||
automatic_correction));
|
||||
}
|
||||
|
||||
Result StaticService::GetStandardUserSystemClockInitialYear(s32& out_year) {
|
||||
out_year = GetSettingsItemValue<s32>(m_set_sys, "time", "standard_user_clock_initial_year");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) {
|
||||
R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient));
|
||||
}
|
||||
|
||||
Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
|
||||
Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
|
||||
R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
|
||||
out_time_point));
|
||||
}
|
||||
|
||||
Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
|
||||
s64& out_time, Service::PSC::Time::SystemClockContext& context) {
|
||||
R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context));
|
||||
}
|
||||
|
||||
Result StaticService::GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot,
|
||||
Service::PSC::Time::TimeType type) {
|
||||
R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type));
|
||||
}
|
||||
|
||||
Result StaticService::GetClockSnapshotFromSystemClockContext(
|
||||
Service::PSC::Time::ClockSnapshot& out_snapshot,
|
||||
Service::PSC::Time::SystemClockContext& user_context,
|
||||
Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type) {
|
||||
R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext(out_snapshot, user_context,
|
||||
network_context, type));
|
||||
}
|
||||
|
||||
Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(
|
||||
s64& out_time, Service::PSC::Time::ClockSnapshot& a, Service::PSC::Time::ClockSnapshot& b) {
|
||||
R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b));
|
||||
}
|
||||
|
||||
Result StaticService::CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a,
|
||||
Service::PSC::Time::ClockSnapshot& b) {
|
||||
R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b));
|
||||
}
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,110 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/glue/time/manager.h"
|
||||
#include "core/hle/service/glue/time/time_zone.h"
|
||||
#include "core/hle/service/psc/time/common.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Set {
|
||||
class ISystemSettingsServer;
|
||||
}
|
||||
|
||||
namespace Service::PSC::Time {
|
||||
class StaticService;
|
||||
class SystemClock;
|
||||
class SteadyClock;
|
||||
class TimeZoneService;
|
||||
class ServiceManager;
|
||||
} // namespace Service::PSC::Time
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
class FileTimestampWorker;
|
||||
class StandardSteadyClockResource;
|
||||
|
||||
class StaticService final : public ServiceFramework<StaticService> {
|
||||
public:
|
||||
explicit StaticService(Core::System& system,
|
||||
Service::PSC::Time::StaticServiceSetupInfo setup_info,
|
||||
std::shared_ptr<TimeManager> time, const char* name);
|
||||
|
||||
~StaticService() override = default;
|
||||
|
||||
Result GetStandardUserSystemClock(
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
|
||||
Result GetStandardNetworkSystemClock(
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
|
||||
Result GetStandardSteadyClock(std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service);
|
||||
Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service);
|
||||
Result GetStandardLocalSystemClock(
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
|
||||
Result GetEphemeralNetworkSystemClock(
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
|
||||
Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory);
|
||||
Result SetStandardSteadyClockInternalOffset(s64 offset);
|
||||
Result GetStandardSteadyClockRtcValue(s64& out_rtc_value);
|
||||
Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_automatic_correction);
|
||||
Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction);
|
||||
Result GetStandardUserSystemClockInitialYear(s32& out_year);
|
||||
Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient);
|
||||
Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
|
||||
Service::PSC::Time::SteadyClockTimePoint& out_time_point);
|
||||
Result CalculateMonotonicSystemClockBaseTimePoint(
|
||||
s64& out_time, Service::PSC::Time::SystemClockContext& context);
|
||||
Result GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot,
|
||||
Service::PSC::Time::TimeType type);
|
||||
Result GetClockSnapshotFromSystemClockContext(
|
||||
Service::PSC::Time::ClockSnapshot& out_snapshot,
|
||||
Service::PSC::Time::SystemClockContext& user_context,
|
||||
Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type);
|
||||
Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time,
|
||||
Service::PSC::Time::ClockSnapshot& a,
|
||||
Service::PSC::Time::ClockSnapshot& b);
|
||||
Result CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a,
|
||||
Service::PSC::Time::ClockSnapshot& b);
|
||||
|
||||
private:
|
||||
Result GetClockSnapshotImpl(Service::PSC::Time::ClockSnapshot& out_snapshot,
|
||||
Service::PSC::Time::SystemClockContext& user_context,
|
||||
Service::PSC::Time::SystemClockContext& network_context,
|
||||
Service::PSC::Time::TimeType type);
|
||||
|
||||
void Handle_GetStandardUserSystemClock(HLERequestContext& ctx);
|
||||
void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx);
|
||||
void Handle_GetStandardSteadyClock(HLERequestContext& ctx);
|
||||
void Handle_GetTimeZoneService(HLERequestContext& ctx);
|
||||
void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx);
|
||||
void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx);
|
||||
void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx);
|
||||
void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx);
|
||||
void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx);
|
||||
void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
|
||||
void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
|
||||
void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx);
|
||||
void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx);
|
||||
void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
|
||||
void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx);
|
||||
void Handle_GetClockSnapshot(HLERequestContext& ctx);
|
||||
void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx);
|
||||
void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx);
|
||||
void Handle_CalculateSpanBetween(HLERequestContext& ctx);
|
||||
|
||||
Core::System& m_system;
|
||||
|
||||
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
||||
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
|
||||
std::shared_ptr<Service::PSC::Time::StaticService> m_wrapped_service;
|
||||
|
||||
Service::PSC::Time::StaticServiceSetupInfo m_setup_info;
|
||||
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
|
||||
std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone;
|
||||
FileTimestampWorker& m_file_timestamp_worker;
|
||||
StandardSteadyClockResource& m_standard_steady_clock_resource;
|
||||
};
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,377 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/service/glue/time/file_timestamp_worker.h"
|
||||
#include "core/hle/service/glue/time/time_zone.h"
|
||||
#include "core/hle/service/glue/time/time_zone_binary.h"
|
||||
#include "core/hle/service/psc/time/time_zone_service.h"
|
||||
#include "core/hle/service/set/system_settings_server.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
namespace {
|
||||
static std::mutex g_list_mutex;
|
||||
static Common::IntrusiveListBaseTraits<Service::PSC::Time::OperationEvent>::ListType g_list_nodes{};
|
||||
} // namespace
|
||||
|
||||
TimeZoneService::TimeZoneService(
|
||||
Core::System& system_, FileTimestampWorker& file_timestamp_worker,
|
||||
bool can_write_timezone_device_location,
|
||||
std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service)
|
||||
: ServiceFramework{system_, "ITimeZoneService"}, m_system{system},
|
||||
m_can_write_timezone_device_location{can_write_timezone_device_location},
|
||||
m_file_timestamp_worker{file_timestamp_worker},
|
||||
m_wrapped_service{std::move(time_zone_service)}, m_operation_event{m_system} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"},
|
||||
{1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"},
|
||||
{2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"},
|
||||
{3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"},
|
||||
{4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"},
|
||||
{5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"},
|
||||
{6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"},
|
||||
{7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"},
|
||||
{8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"},
|
||||
{20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"},
|
||||
{100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"},
|
||||
{101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
|
||||
{201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"},
|
||||
{202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
g_list_nodes.clear();
|
||||
m_set_sys =
|
||||
m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
|
||||
}
|
||||
|
||||
TimeZoneService::~TimeZoneService() = default;
|
||||
|
||||
void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
Service::PSC::Time::LocationName name{};
|
||||
auto res = GetDeviceLocationName(name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)};
|
||||
rb.Push(res);
|
||||
rb.PushRaw<Service::PSC::Time::LocationName>(name);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
|
||||
|
||||
auto res = SetDeviceLocation(name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
u32 count{};
|
||||
auto res = GetTotalLocationNameCount(count);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push(count);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto index{rp.Pop<u32>()};
|
||||
|
||||
auto max_names{ctx.GetWriteBufferSize() / sizeof(Service::PSC::Time::LocationName)};
|
||||
|
||||
std::vector<Service::PSC::Time::LocationName> names{};
|
||||
u32 count{};
|
||||
auto res = LoadLocationNameList(count, names, max_names, index);
|
||||
|
||||
ctx.WriteBuffer(names);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push(count);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
|
||||
|
||||
Tz::Rule rule{};
|
||||
auto res = LoadTimeZoneRule(rule, name);
|
||||
|
||||
ctx.WriteBuffer(rule);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
Service::PSC::Time::RuleVersion rule_version{};
|
||||
auto res = GetTimeZoneRuleVersion(rule_version);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::RuleVersion) / sizeof(u32)};
|
||||
rb.Push(res);
|
||||
rb.PushRaw<Service::PSC::Time::RuleVersion>(rule_version);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
Service::PSC::Time::LocationName name{};
|
||||
Service::PSC::Time::SteadyClockTimePoint time_point{};
|
||||
auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx,
|
||||
2 + (sizeof(Service::PSC::Time::LocationName) / sizeof(u32)) +
|
||||
(sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32))};
|
||||
rb.Push(res);
|
||||
rb.PushRaw<Service::PSC::Time::LocationName>(name);
|
||||
rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
auto res = SetDeviceLocationNameWithTimeZoneRule();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(Service::PSC::Time::ResultNotImplemented);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle(
|
||||
HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
Kernel::KEvent* event{};
|
||||
auto res = GetDeviceLocationNameOperationEventReadableHandle(&event);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(res);
|
||||
rb.PushCopyObjects(event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto time{rp.Pop<s64>()};
|
||||
|
||||
auto rule_buffer{ctx.ReadBuffer()};
|
||||
Tz::Rule rule{};
|
||||
std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule));
|
||||
|
||||
Service::PSC::Time::CalendarTime calendar_time{};
|
||||
Service::PSC::Time::CalendarAdditionalInfo additional_info{};
|
||||
auto res = ToCalendarTime(calendar_time, additional_info, time, rule);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx,
|
||||
2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) +
|
||||
(sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))};
|
||||
rb.Push(res);
|
||||
rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time);
|
||||
rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto time{rp.Pop<s64>()};
|
||||
|
||||
LOG_DEBUG(Service_Time, "called. time={}", time);
|
||||
|
||||
Service::PSC::Time::CalendarTime calendar_time{};
|
||||
Service::PSC::Time::CalendarAdditionalInfo additional_info{};
|
||||
auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx,
|
||||
2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) +
|
||||
(sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))};
|
||||
rb.Push(res);
|
||||
rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time);
|
||||
rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()};
|
||||
|
||||
LOG_DEBUG(Service_Time, "called. calendar year {} month {} day {} hour {} minute {} second {}",
|
||||
calendar.year, calendar.month, calendar.day, calendar.hour, calendar.minute,
|
||||
calendar.second);
|
||||
|
||||
auto binary{ctx.ReadBuffer()};
|
||||
|
||||
Tz::Rule rule{};
|
||||
std::memcpy(&rule, binary.data(), sizeof(Tz::Rule));
|
||||
|
||||
u32 count{};
|
||||
std::array<s64, 2> times{};
|
||||
u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
|
||||
|
||||
auto res = ToPosixTime(count, times, times_count, calendar, rule);
|
||||
|
||||
ctx.WriteBuffer(times);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push(count);
|
||||
}
|
||||
|
||||
void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()};
|
||||
|
||||
u32 count{};
|
||||
std::array<s64, 2> times{};
|
||||
u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
|
||||
|
||||
auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar);
|
||||
|
||||
ctx.WriteBuffer(times);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push(count);
|
||||
}
|
||||
|
||||
// =============================== Implementations ===========================
|
||||
|
||||
Result TimeZoneService::GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name) {
|
||||
R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name));
|
||||
}
|
||||
|
||||
Result TimeZoneService::SetDeviceLocation(Service::PSC::Time::LocationName& location_name) {
|
||||
R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
|
||||
R_UNLESS(IsTimeZoneBinaryValid(location_name), Service::PSC::Time::ResultTimeZoneNotFound);
|
||||
|
||||
std::scoped_lock l{m_mutex};
|
||||
|
||||
std::span<const u8> binary{};
|
||||
size_t binary_size{};
|
||||
R_TRY(GetTimeZoneRule(binary, binary_size, location_name))
|
||||
|
||||
R_TRY(m_wrapped_service->SetDeviceLocationNameWithTimeZoneRule(location_name, binary));
|
||||
|
||||
m_file_timestamp_worker.SetFilesystemPosixTime();
|
||||
|
||||
Service::PSC::Time::SteadyClockTimePoint time_point{};
|
||||
Service::PSC::Time::LocationName name{};
|
||||
R_TRY(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(time_point, name));
|
||||
|
||||
m_set_sys->SetDeviceTimeZoneLocationName(name);
|
||||
m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(time_point);
|
||||
|
||||
std::scoped_lock m{g_list_mutex};
|
||||
for (auto& operation_event : g_list_nodes) {
|
||||
operation_event.m_event->Signal();
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) {
|
||||
R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count));
|
||||
}
|
||||
|
||||
Result TimeZoneService::LoadLocationNameList(
|
||||
u32& out_count, std::vector<Service::PSC::Time::LocationName>& out_names, size_t max_names,
|
||||
u32 index) {
|
||||
std::scoped_lock l{m_mutex};
|
||||
R_RETURN(GetTimeZoneLocationList(out_count, out_names, max_names, index));
|
||||
}
|
||||
|
||||
Result TimeZoneService::LoadTimeZoneRule(Tz::Rule& out_rule,
|
||||
Service::PSC::Time::LocationName& name) {
|
||||
std::scoped_lock l{m_mutex};
|
||||
std::span<const u8> binary{};
|
||||
size_t binary_size{};
|
||||
R_TRY(GetTimeZoneRule(binary, binary_size, name))
|
||||
R_RETURN(m_wrapped_service->ParseTimeZoneBinary(out_rule, binary));
|
||||
}
|
||||
|
||||
Result TimeZoneService::GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
|
||||
R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version));
|
||||
}
|
||||
|
||||
Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
|
||||
Service::PSC::Time::SteadyClockTimePoint& out_time_point,
|
||||
Service::PSC::Time::LocationName& location_name) {
|
||||
R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(out_time_point, location_name));
|
||||
}
|
||||
|
||||
Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule() {
|
||||
R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
|
||||
R_RETURN(Service::PSC::Time::ResultNotImplemented);
|
||||
}
|
||||
|
||||
Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle(
|
||||
Kernel::KEvent** out_event) {
|
||||
if (!operation_event_initialized) {
|
||||
operation_event_initialized = false;
|
||||
|
||||
m_operation_event.m_ctx.CloseEvent(m_operation_event.m_event);
|
||||
m_operation_event.m_event =
|
||||
m_operation_event.m_ctx.CreateEvent("Psc:TimeZoneService:OperationEvent");
|
||||
operation_event_initialized = true;
|
||||
std::scoped_lock l{m_mutex};
|
||||
g_list_nodes.push_back(m_operation_event);
|
||||
}
|
||||
|
||||
*out_event = m_operation_event.m_event;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result TimeZoneService::ToCalendarTime(
|
||||
Service::PSC::Time::CalendarTime& out_calendar_time,
|
||||
Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule) {
|
||||
R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule));
|
||||
}
|
||||
|
||||
Result TimeZoneService::ToCalendarTimeWithMyRule(
|
||||
Service::PSC::Time::CalendarTime& out_calendar_time,
|
||||
Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time) {
|
||||
R_RETURN(
|
||||
m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time));
|
||||
}
|
||||
|
||||
Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times,
|
||||
u32 out_times_count,
|
||||
Service::PSC::Time::CalendarTime& calendar_time,
|
||||
Tz::Rule& rule) {
|
||||
R_RETURN(
|
||||
m_wrapped_service->ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule));
|
||||
}
|
||||
|
||||
Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times,
|
||||
u32 out_times_count,
|
||||
Service::PSC::Time::CalendarTime& calendar_time) {
|
||||
R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, out_times_count,
|
||||
calendar_time));
|
||||
}
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,95 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/psc/time/common.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Tz {
|
||||
struct Rule;
|
||||
}
|
||||
|
||||
namespace Service::Set {
|
||||
class ISystemSettingsServer;
|
||||
}
|
||||
|
||||
namespace Service::PSC::Time {
|
||||
class TimeZoneService;
|
||||
}
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
class FileTimestampWorker;
|
||||
|
||||
class TimeZoneService final : public ServiceFramework<TimeZoneService> {
|
||||
public:
|
||||
explicit TimeZoneService(
|
||||
Core::System& system, FileTimestampWorker& file_timestamp_worker,
|
||||
bool can_write_timezone_device_location,
|
||||
std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service);
|
||||
|
||||
~TimeZoneService() override;
|
||||
|
||||
Result GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name);
|
||||
Result SetDeviceLocation(Service::PSC::Time::LocationName& location_name);
|
||||
Result GetTotalLocationNameCount(u32& out_count);
|
||||
Result LoadLocationNameList(u32& out_count,
|
||||
std::vector<Service::PSC::Time::LocationName>& out_names,
|
||||
size_t max_names, u32 index);
|
||||
Result LoadTimeZoneRule(Tz::Rule& out_rule, Service::PSC::Time::LocationName& name);
|
||||
Result GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version);
|
||||
Result GetDeviceLocationNameAndUpdatedTime(
|
||||
Service::PSC::Time::SteadyClockTimePoint& out_time_point,
|
||||
Service::PSC::Time::LocationName& location_name);
|
||||
Result SetDeviceLocationNameWithTimeZoneRule();
|
||||
Result GetDeviceLocationNameOperationEventReadableHandle(Kernel::KEvent** out_event);
|
||||
Result ToCalendarTime(Service::PSC::Time::CalendarTime& out_calendar_time,
|
||||
Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time,
|
||||
Tz::Rule& rule);
|
||||
Result ToCalendarTimeWithMyRule(Service::PSC::Time::CalendarTime& out_calendar_time,
|
||||
Service::PSC::Time::CalendarAdditionalInfo& out_additional_info,
|
||||
s64 time);
|
||||
Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
|
||||
Service::PSC::Time::CalendarTime& calendar_time, Tz::Rule& rule);
|
||||
Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
|
||||
Service::PSC::Time::CalendarTime& calendar_time);
|
||||
|
||||
private:
|
||||
void Handle_GetDeviceLocationName(HLERequestContext& ctx);
|
||||
void Handle_SetDeviceLocationName(HLERequestContext& ctx);
|
||||
void Handle_GetTotalLocationNameCount(HLERequestContext& ctx);
|
||||
void Handle_LoadLocationNameList(HLERequestContext& ctx);
|
||||
void Handle_LoadTimeZoneRule(HLERequestContext& ctx);
|
||||
void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx);
|
||||
void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx);
|
||||
void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx);
|
||||
void Handle_ParseTimeZoneBinary(HLERequestContext& ctx);
|
||||
void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx);
|
||||
void Handle_ToCalendarTime(HLERequestContext& ctx);
|
||||
void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx);
|
||||
void Handle_ToPosixTime(HLERequestContext& ctx);
|
||||
void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx);
|
||||
|
||||
Core::System& m_system;
|
||||
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
||||
|
||||
bool m_can_write_timezone_device_location;
|
||||
FileTimestampWorker& m_file_timestamp_worker;
|
||||
std::shared_ptr<Service::PSC::Time::TimeZoneService> m_wrapped_service;
|
||||
std::mutex m_mutex;
|
||||
bool operation_event_initialized{};
|
||||
Service::PSC::Time::OperationEvent m_operation_event;
|
||||
};
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,205 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/system_archive/system_archive.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/glue/time/time_zone_binary.h"
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
namespace {
|
||||
constexpr u64 TimeZoneBinaryId = 0x10000000000080E;
|
||||
|
||||
static FileSys::VirtualDir g_time_zone_binary_romfs{};
|
||||
static Result g_time_zone_binary_mount_result{ResultUnknown};
|
||||
static std::vector<u8> g_time_zone_scratch_space(0x2800, 0);
|
||||
|
||||
Result TimeZoneReadBinary(size_t& out_read_size, std::span<u8> out_buffer, size_t out_buffer_size,
|
||||
std::string_view path) {
|
||||
R_UNLESS(g_time_zone_binary_mount_result == ResultSuccess, g_time_zone_binary_mount_result);
|
||||
|
||||
auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
|
||||
R_UNLESS(vfs_file, ResultUnknown);
|
||||
|
||||
auto file_size{vfs_file->GetSize()};
|
||||
R_UNLESS(file_size > 0, ResultUnknown);
|
||||
|
||||
R_UNLESS(file_size <= out_buffer_size, Service::PSC::Time::ResultFailed);
|
||||
|
||||
out_read_size = vfs_file->Read(out_buffer.data(), file_size);
|
||||
R_UNLESS(out_read_size > 0, ResultUnknown);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ResetTimeZoneBinary() {
|
||||
g_time_zone_binary_romfs = {};
|
||||
g_time_zone_binary_mount_result = ResultUnknown;
|
||||
g_time_zone_scratch_space.clear();
|
||||
g_time_zone_scratch_space.resize(0x2800, 0);
|
||||
}
|
||||
|
||||
Result MountTimeZoneBinary(Core::System& system) {
|
||||
auto& fsc{system.GetFileSystemController()};
|
||||
std::unique_ptr<FileSys::NCA> nca{};
|
||||
|
||||
auto* bis_system = fsc.GetSystemNANDContents();
|
||||
|
||||
R_UNLESS(bis_system, ResultUnknown);
|
||||
|
||||
nca = bis_system->GetEntry(TimeZoneBinaryId, FileSys::ContentRecordType::Data);
|
||||
|
||||
if (nca) {
|
||||
g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS());
|
||||
}
|
||||
|
||||
if (!g_time_zone_binary_romfs) {
|
||||
g_time_zone_binary_romfs = FileSys::ExtractRomFS(
|
||||
FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId));
|
||||
}
|
||||
|
||||
R_UNLESS(g_time_zone_binary_romfs, ResultUnknown);
|
||||
|
||||
g_time_zone_binary_mount_result = ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void GetTimeZoneBinaryListPath(std::string& out_path) {
|
||||
if (g_time_zone_binary_mount_result != ResultSuccess) {
|
||||
return;
|
||||
}
|
||||
// out_path = fmt::format("{}:/binaryList.txt", "TimeZoneBinary");
|
||||
out_path = "/binaryList.txt";
|
||||
}
|
||||
|
||||
void GetTimeZoneBinaryVersionPath(std::string& out_path) {
|
||||
if (g_time_zone_binary_mount_result != ResultSuccess) {
|
||||
return;
|
||||
}
|
||||
// out_path = fmt::format("{}:/version.txt", "TimeZoneBinary");
|
||||
out_path = "/version.txt";
|
||||
}
|
||||
|
||||
void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name) {
|
||||
if (g_time_zone_binary_mount_result != ResultSuccess) {
|
||||
return;
|
||||
}
|
||||
// out_path = fmt::format("{}:/zoneinfo/{}", "TimeZoneBinary", name);
|
||||
out_path = fmt::format("/zoneinfo/{}", name.name.data());
|
||||
}
|
||||
|
||||
bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) {
|
||||
std::string path{};
|
||||
GetTimeZoneZonePath(path, name);
|
||||
|
||||
auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
|
||||
return vfs_file->GetSize() != 0;
|
||||
}
|
||||
|
||||
u32 GetTimeZoneCount() {
|
||||
std::string path{};
|
||||
GetTimeZoneBinaryListPath(path);
|
||||
|
||||
size_t bytes_read{};
|
||||
if (TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, 0x2800, path) != ResultSuccess) {
|
||||
return 0;
|
||||
}
|
||||
if (bytes_read == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto chars = std::span(reinterpret_cast<char*>(g_time_zone_scratch_space.data()), bytes_read);
|
||||
u32 count{};
|
||||
for (auto chr : chars) {
|
||||
if (chr == '\n') {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
|
||||
std::string path{};
|
||||
GetTimeZoneBinaryVersionPath(path);
|
||||
|
||||
auto rule_version_buffer{std::span(reinterpret_cast<u8*>(&out_rule_version),
|
||||
sizeof(Service::PSC::Time::RuleVersion))};
|
||||
size_t bytes_read{};
|
||||
R_TRY(TimeZoneReadBinary(bytes_read, rule_version_buffer, rule_version_buffer.size_bytes(),
|
||||
path));
|
||||
|
||||
rule_version_buffer[bytes_read] = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
|
||||
Service::PSC::Time::LocationName& name) {
|
||||
std::string path{};
|
||||
GetTimeZoneZonePath(path, name);
|
||||
|
||||
size_t bytes_read{};
|
||||
R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
|
||||
g_time_zone_scratch_space.size(), path));
|
||||
|
||||
out_rule = std::span(g_time_zone_scratch_space.data(), bytes_read);
|
||||
out_rule_size = bytes_read;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetTimeZoneLocationList(u32& out_count,
|
||||
std::vector<Service::PSC::Time::LocationName>& out_names,
|
||||
size_t max_names, u32 index) {
|
||||
std::string path{};
|
||||
GetTimeZoneBinaryListPath(path);
|
||||
|
||||
size_t bytes_read{};
|
||||
R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
|
||||
g_time_zone_scratch_space.size(), path));
|
||||
|
||||
out_count = 0;
|
||||
R_SUCCEED_IF(bytes_read == 0);
|
||||
|
||||
Service::PSC::Time::LocationName current_name{};
|
||||
size_t current_name_len{};
|
||||
std::span<const u8> chars{g_time_zone_scratch_space};
|
||||
u32 name_count{};
|
||||
|
||||
for (auto chr : chars) {
|
||||
if (chr == '\r') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chr == '\n') {
|
||||
if (name_count >= index) {
|
||||
out_names.push_back(current_name);
|
||||
out_count++;
|
||||
if (out_count >= max_names) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
name_count++;
|
||||
current_name_len = 0;
|
||||
current_name = {};
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chr == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
R_UNLESS(current_name_len <= current_name.name.size() - 2,
|
||||
Service::PSC::Time::ResultFailed);
|
||||
|
||||
current_name.name[current_name_len++] = chr;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,32 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "core/hle/service/psc/time/common.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
|
||||
void ResetTimeZoneBinary();
|
||||
Result MountTimeZoneBinary(Core::System& system);
|
||||
void GetTimeZoneBinaryListPath(std::string& out_path);
|
||||
void GetTimeZoneBinaryVersionPath(std::string& out_path);
|
||||
void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name);
|
||||
bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name);
|
||||
u32 GetTimeZoneCount();
|
||||
Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version);
|
||||
Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
|
||||
Service::PSC::Time::LocationName& name);
|
||||
Result GetTimeZoneLocationList(u32& out_count,
|
||||
std::vector<Service::PSC::Time::LocationName>& out_names,
|
||||
size_t max_names, u32 index);
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,338 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/glue/time/file_timestamp_worker.h"
|
||||
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
|
||||
#include "core/hle/service/glue/time/worker.h"
|
||||
#include "core/hle/service/psc/time/common.h"
|
||||
#include "core/hle/service/psc/time/service_manager.h"
|
||||
#include "core/hle/service/psc/time/static.h"
|
||||
#include "core/hle/service/psc/time/system_clock.h"
|
||||
#include "core/hle/service/set/system_settings_server.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
namespace {
|
||||
|
||||
bool g_ig_report_network_clock_context_set{};
|
||||
Service::PSC::Time::SystemClockContext g_report_network_clock_context{};
|
||||
bool g_ig_report_ephemeral_clock_context_set{};
|
||||
Service::PSC::Time::SystemClockContext g_report_ephemeral_clock_context{};
|
||||
|
||||
template <typename T>
|
||||
T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
|
||||
const char* category, const char* name) {
|
||||
std::vector<u8> interval_buf;
|
||||
auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
T v{};
|
||||
std::memcpy(&v, interval_buf.data(), sizeof(T));
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
|
||||
FileTimestampWorker& file_timestamp_worker)
|
||||
: m_system{system}, m_ctx{m_system, "Glue:58"}, m_event{m_ctx.CreateEvent("Glue:58:Event")},
|
||||
m_steady_clock_resource{steady_clock_resource},
|
||||
m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
|
||||
"Glue:58:SteadyClockTimerEvent")},
|
||||
m_timer_file_system{m_ctx.CreateEvent("Glue:58:FileTimeTimerEvent")},
|
||||
m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
|
||||
g_ig_report_network_clock_context_set = false;
|
||||
g_report_network_clock_context = {};
|
||||
g_ig_report_ephemeral_clock_context_set = false;
|
||||
g_report_ephemeral_clock_context = {};
|
||||
|
||||
m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
|
||||
"Time::SteadyClockEvent",
|
||||
[this](s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
m_timer_steady_clock->Signal();
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
m_timer_file_system_timing_event = Core::Timing::CreateEvent(
|
||||
"Time::SteadyClockEvent",
|
||||
[this](s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
m_timer_file_system->Signal();
|
||||
return std::nullopt;
|
||||
});
|
||||
}
|
||||
|
||||
TimeWorker::~TimeWorker() {
|
||||
m_local_clock_event->Signal();
|
||||
m_network_clock_event->Signal();
|
||||
m_ephemeral_clock_event->Signal();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||
|
||||
m_thread.request_stop();
|
||||
m_event->Signal();
|
||||
m_thread.join();
|
||||
|
||||
m_ctx.CloseEvent(m_event);
|
||||
m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
|
||||
m_ctx.CloseEvent(m_timer_steady_clock);
|
||||
m_system.CoreTiming().UnscheduleEvent(m_timer_file_system_timing_event);
|
||||
m_ctx.CloseEvent(m_timer_file_system);
|
||||
}
|
||||
|
||||
void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
|
||||
std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys) {
|
||||
m_set_sys = std::move(set_sys);
|
||||
m_time_m =
|
||||
m_system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
|
||||
m_time_sm = std::move(time_sm);
|
||||
|
||||
m_alarm_worker.Initialize(m_time_m);
|
||||
|
||||
auto steady_clock_interval_m = GetSettingsItemValue<s32>(
|
||||
m_set_sys, "time", "standard_steady_clock_rtc_update_interval_minutes");
|
||||
|
||||
auto one_minute_ns{
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
|
||||
s64 steady_clock_interval_ns{steady_clock_interval_m * one_minute_ns};
|
||||
|
||||
m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
|
||||
std::chrono::nanoseconds(steady_clock_interval_ns),
|
||||
m_timer_steady_clock_timing_event);
|
||||
|
||||
auto fs_notify_time_s =
|
||||
GetSettingsItemValue<s32>(m_set_sys, "time", "notify_time_to_fs_interval_seconds");
|
||||
auto one_second_ns{
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
|
||||
s64 fs_notify_time_ns{fs_notify_time_s * one_second_ns};
|
||||
|
||||
m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
|
||||
std::chrono::nanoseconds(fs_notify_time_ns),
|
||||
m_timer_file_system_timing_event);
|
||||
|
||||
auto res = m_time_sm->GetStandardLocalSystemClock(m_local_clock);
|
||||
ASSERT(res == ResultSuccess);
|
||||
res = m_time_m->GetStandardLocalClockOperationEvent(&m_local_clock_event);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
res = m_time_sm->GetStandardNetworkSystemClock(m_network_clock);
|
||||
ASSERT(res == ResultSuccess);
|
||||
res = m_time_m->GetStandardNetworkClockOperationEventForServiceManager(&m_network_clock_event);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
res = m_time_sm->GetEphemeralNetworkSystemClock(m_ephemeral_clock);
|
||||
ASSERT(res == ResultSuccess);
|
||||
res =
|
||||
m_time_m->GetEphemeralNetworkClockOperationEventForServiceManager(&m_ephemeral_clock_event);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
res = m_time_m->GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
|
||||
&m_standard_user_auto_correct_clock_event);
|
||||
ASSERT(res == ResultSuccess);
|
||||
}
|
||||
|
||||
void TimeWorker::StartThread() {
|
||||
m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this));
|
||||
}
|
||||
|
||||
void TimeWorker::ThreadFunc(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("TimeWorker");
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
|
||||
|
||||
enum class EventType {
|
||||
Exit = 0,
|
||||
IpmModuleService_GetEvent = 1,
|
||||
PowerStateChange = 2,
|
||||
SignalAlarms = 3,
|
||||
UpdateLocalSystemClock = 4,
|
||||
UpdateNetworkSystemClock = 5,
|
||||
UpdateEphemeralSystemClock = 6,
|
||||
UpdateSteadyClock = 7,
|
||||
UpdateFileTimestamp = 8,
|
||||
AutoCorrect = 9,
|
||||
Max = 10,
|
||||
};
|
||||
|
||||
s32 num_objs{};
|
||||
std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{};
|
||||
std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{};
|
||||
|
||||
const auto AddWaiter{
|
||||
[&](Kernel::KSynchronizationObject* synchronization_object, EventType type) {
|
||||
// Open a new reference to the object.
|
||||
synchronization_object->Open();
|
||||
|
||||
// Insert into the list.
|
||||
wait_indices[num_objs] = type;
|
||||
wait_objs[num_objs++] = synchronization_object;
|
||||
}};
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
SCOPE_EXIT({
|
||||
for (s32 i = 0; i < num_objs; i++) {
|
||||
wait_objs[i]->Close();
|
||||
}
|
||||
});
|
||||
|
||||
num_objs = {};
|
||||
wait_objs = {};
|
||||
if (m_pm_state_change_handler.m_priority != 0) {
|
||||
AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
|
||||
// TODO
|
||||
// AddWaiter(gIPmModuleService::GetEvent(), 1);
|
||||
AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange);
|
||||
} else {
|
||||
AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
|
||||
// TODO
|
||||
// AddWaiter(gIPmModuleService::GetEvent(), 1);
|
||||
AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange);
|
||||
AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms);
|
||||
AddWaiter(&m_local_clock_event->GetReadableEvent(), EventType::UpdateLocalSystemClock);
|
||||
AddWaiter(&m_network_clock_event->GetReadableEvent(),
|
||||
EventType::UpdateNetworkSystemClock);
|
||||
AddWaiter(&m_ephemeral_clock_event->GetReadableEvent(),
|
||||
EventType::UpdateEphemeralSystemClock);
|
||||
AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock);
|
||||
AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp);
|
||||
AddWaiter(&m_standard_user_auto_correct_clock_event->GetReadableEvent(),
|
||||
EventType::AutoCorrect);
|
||||
}
|
||||
|
||||
s32 out_index{-1};
|
||||
Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(),
|
||||
num_objs, -1);
|
||||
ASSERT(out_index >= 0 && out_index < num_objs);
|
||||
|
||||
if (stop_token.stop_requested()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (wait_indices[out_index]) {
|
||||
case EventType::Exit:
|
||||
return;
|
||||
|
||||
case EventType::IpmModuleService_GetEvent:
|
||||
// TODO
|
||||
// IPmModuleService::GetEvent()
|
||||
// clear the event
|
||||
// Handle power state change event
|
||||
break;
|
||||
|
||||
case EventType::PowerStateChange:
|
||||
m_alarm_worker.GetEvent().Clear();
|
||||
if (m_pm_state_change_handler.m_priority <= 1) {
|
||||
m_alarm_worker.OnPowerStateChanged();
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType::SignalAlarms:
|
||||
m_alarm_worker.GetTimerEvent().Clear();
|
||||
m_time_m->CheckAndSignalAlarms();
|
||||
break;
|
||||
|
||||
case EventType::UpdateLocalSystemClock: {
|
||||
m_local_clock_event->Clear();
|
||||
|
||||
Service::PSC::Time::SystemClockContext context{};
|
||||
auto res = m_local_clock->GetSystemClockContext(context);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
m_set_sys->SetUserSystemClockContext(context);
|
||||
|
||||
m_file_timestamp_worker.SetFilesystemPosixTime();
|
||||
} break;
|
||||
|
||||
case EventType::UpdateNetworkSystemClock: {
|
||||
m_network_clock_event->Clear();
|
||||
Service::PSC::Time::SystemClockContext context{};
|
||||
auto res = m_network_clock->GetSystemClockContext(context);
|
||||
ASSERT(res == ResultSuccess);
|
||||
m_set_sys->SetNetworkSystemClockContext(context);
|
||||
|
||||
s64 time{};
|
||||
if (m_network_clock->GetCurrentTime(time) != ResultSuccess) {
|
||||
break;
|
||||
}
|
||||
|
||||
[[maybe_unused]] auto offset_before{
|
||||
g_ig_report_network_clock_context_set ? g_report_network_clock_context.offset : 0};
|
||||
// TODO system report "standard_netclock_operation"
|
||||
// "clock_time" = time
|
||||
// "context_offset_before" = offset_before
|
||||
// "context_offset_after" = context.offset
|
||||
g_report_network_clock_context = context;
|
||||
if (!g_ig_report_network_clock_context_set) {
|
||||
g_ig_report_network_clock_context_set = true;
|
||||
}
|
||||
|
||||
m_file_timestamp_worker.SetFilesystemPosixTime();
|
||||
} break;
|
||||
|
||||
case EventType::UpdateEphemeralSystemClock: {
|
||||
m_ephemeral_clock_event->Clear();
|
||||
|
||||
Service::PSC::Time::SystemClockContext context{};
|
||||
auto res = m_ephemeral_clock->GetSystemClockContext(context);
|
||||
if (res != ResultSuccess) {
|
||||
break;
|
||||
}
|
||||
|
||||
s64 time{};
|
||||
res = m_ephemeral_clock->GetCurrentTime(time);
|
||||
if (res != ResultSuccess) {
|
||||
break;
|
||||
}
|
||||
|
||||
[[maybe_unused]] auto offset_before{g_ig_report_ephemeral_clock_context_set
|
||||
? g_report_ephemeral_clock_context.offset
|
||||
: 0};
|
||||
// TODO system report "ephemeral_netclock_operation"
|
||||
// "clock_time" = time
|
||||
// "context_offset_before" = offset_before
|
||||
// "context_offset_after" = context.offset
|
||||
g_report_ephemeral_clock_context = context;
|
||||
if (!g_ig_report_ephemeral_clock_context_set) {
|
||||
g_ig_report_ephemeral_clock_context_set = true;
|
||||
}
|
||||
} break;
|
||||
|
||||
case EventType::UpdateSteadyClock:
|
||||
m_timer_steady_clock->Clear();
|
||||
|
||||
m_steady_clock_resource.UpdateTime();
|
||||
m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
|
||||
break;
|
||||
|
||||
case EventType::UpdateFileTimestamp:
|
||||
m_timer_file_system->Clear();
|
||||
|
||||
m_file_timestamp_worker.SetFilesystemPosixTime();
|
||||
break;
|
||||
|
||||
case EventType::AutoCorrect: {
|
||||
m_standard_user_auto_correct_clock_event->Clear();
|
||||
|
||||
bool automatic_correction{};
|
||||
auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
|
||||
automatic_correction);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
Service::PSC::Time::SteadyClockTimePoint time_point{};
|
||||
res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
|
||||
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
|
||||
} break;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -1,64 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/glue/time/alarm_worker.h"
|
||||
#include "core/hle/service/glue/time/pm_state_change_handler.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Service::Set {
|
||||
class ISystemSettingsServer;
|
||||
}
|
||||
|
||||
namespace Service::PSC::Time {
|
||||
class StaticService;
|
||||
class SystemClock;
|
||||
} // namespace Service::PSC::Time
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
class FileTimestampWorker;
|
||||
class StandardSteadyClockResource;
|
||||
|
||||
class TimeWorker {
|
||||
public:
|
||||
explicit TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
|
||||
FileTimestampWorker& file_timestamp_worker);
|
||||
~TimeWorker();
|
||||
|
||||
void Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
|
||||
std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys);
|
||||
|
||||
void StartThread();
|
||||
|
||||
private:
|
||||
void ThreadFunc(std::stop_token stop_token);
|
||||
|
||||
Core::System& m_system;
|
||||
KernelHelpers::ServiceContext m_ctx;
|
||||
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
||||
|
||||
std::jthread m_thread;
|
||||
Kernel::KEvent* m_event{};
|
||||
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
|
||||
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> m_network_clock;
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> m_local_clock;
|
||||
std::shared_ptr<Service::PSC::Time::SystemClock> m_ephemeral_clock;
|
||||
StandardSteadyClockResource& m_steady_clock_resource;
|
||||
FileTimestampWorker& m_file_timestamp_worker;
|
||||
Kernel::KEvent* m_local_clock_event{};
|
||||
Kernel::KEvent* m_network_clock_event{};
|
||||
Kernel::KEvent* m_ephemeral_clock_event{};
|
||||
Kernel::KEvent* m_standard_user_auto_correct_clock_event{};
|
||||
Kernel::KEvent* m_timer_steady_clock{};
|
||||
std::shared_ptr<Core::Timing::EventType> m_timer_steady_clock_timing_event;
|
||||
Kernel::KEvent* m_timer_file_system{};
|
||||
std::shared_ptr<Core::Timing::EventType> m_timer_file_system_timing_event;
|
||||
AlarmWorker m_alarm_worker;
|
||||
PmStateChangeHandler m_pm_state_change_handler;
|
||||
};
|
||||
|
||||
} // namespace Service::Glue::Time
|
||||
@@ -20,13 +20,12 @@ void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system);
|
||||
std::shared_ptr<HidFirmwareSettings> firmware_settings =
|
||||
std::make_shared<HidFirmwareSettings>(system);
|
||||
std::make_shared<HidFirmwareSettings>();
|
||||
|
||||
// TODO: Remove this hack when am is emulated properly.
|
||||
resource_manager->Initialize();
|
||||
resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(),
|
||||
true);
|
||||
resource_manager->SetAruidValidForVibration(system.ApplicationProcess()->GetProcessId(), true);
|
||||
|
||||
server_manager->RegisterNamedService(
|
||||
"hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));
|
||||
|
||||
@@ -22,16 +22,12 @@
|
||||
#include "hid_core/resources/mouse/mouse.h"
|
||||
#include "hid_core/resources/npad/npad.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/six_axis/console_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/touch_screen/gesture.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 {
|
||||
|
||||
@@ -42,7 +38,7 @@ public:
|
||||
: ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"},
|
||||
{0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -50,49 +46,22 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void ActivateVibrationDevice(HLERequestContext& ctx) {
|
||||
void InitializeVibrationDevice(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
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={}",
|
||||
vibration_device_handle.npad_type, vibration_device_handle.npad_id,
|
||||
vibration_device_handle.device_index);
|
||||
|
||||
const auto result = ActivateVibrationDeviceImpl(vibration_device_handle);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -184,7 +153,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
|
||||
{209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
|
||||
{210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
|
||||
{211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
|
||||
{212, &IHidServer::SendVibrationValueInBool, "SendVibrationValueInBool"},
|
||||
{212, nullptr, "SendVibrationValueInBool"},
|
||||
{300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
|
||||
{301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
|
||||
{302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
|
||||
@@ -1523,13 +1492,59 @@ void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) {
|
||||
void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
|
||||
const auto controller = GetResourceManager()->GetNpad();
|
||||
|
||||
Core::HID::VibrationDeviceInfo vibration_device_info{};
|
||||
const auto result = GetResourceManager()->GetVibrationDeviceInfo(vibration_device_info,
|
||||
vibration_device_handle);
|
||||
Core::HID::VibrationDeviceInfo vibration_device_info;
|
||||
bool check_device_index = false;
|
||||
|
||||
switch (vibration_device_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::Fullkey:
|
||||
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};
|
||||
rb.Push(result);
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(vibration_device_info);
|
||||
}
|
||||
|
||||
@@ -1545,16 +1560,16 @@ void IHidServer::SendVibrationValue(HLERequestContext& ctx) {
|
||||
|
||||
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,
|
||||
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||
parameters.vibration_device_handle.npad_type,
|
||||
parameters.vibration_device_handle.npad_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};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
@@ -1576,28 +1591,10 @@ void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) {
|
||||
parameters.vibration_device_handle.npad_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};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(vibration_value);
|
||||
rb.PushRaw(GetResourceManager()->GetNpad()->GetLastVibration(
|
||||
parameters.applet_resource_user_id, parameters.vibration_device_handle));
|
||||
}
|
||||
|
||||
void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
|
||||
@@ -1612,27 +1609,25 @@ void IHidServer::PermitVibration(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
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);
|
||||
|
||||
const auto result =
|
||||
GetResourceManager()->GetNpad()->GetVibrationHandler()->SetVibrationMasterVolume(
|
||||
can_vibrate ? 1.0f : 0.0f);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_HID, "called");
|
||||
|
||||
f32 master_volume{};
|
||||
const auto result =
|
||||
GetResourceManager()->GetNpad()->GetVibrationHandler()->GetVibrationMasterVolume(
|
||||
master_volume);
|
||||
// nnSDK checks if a float is greater than zero. We return the bool we stored earlier
|
||||
const auto is_enabled = Settings::values.vibration_enabled.GetValue();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(result);
|
||||
rb.Push(master_volume > 0.0f);
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(is_enabled);
|
||||
}
|
||||
|
||||
void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
|
||||
@@ -1650,22 +1645,13 @@ void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
|
||||
auto vibration_values = std::span(
|
||||
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);
|
||||
|
||||
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};
|
||||
rb.Push(result);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
|
||||
@@ -1680,6 +1666,43 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
|
||||
|
||||
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,
|
||||
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
|
||||
"gc_erm_command={}",
|
||||
@@ -1688,23 +1711,8 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
|
||||
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
|
||||
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};
|
||||
rb.Push(result);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
|
||||
@@ -1717,31 +1725,33 @@ void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
|
||||
|
||||
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,
|
||||
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||
parameters.vibration_device_handle.npad_type,
|
||||
parameters.vibration_device_handle.npad_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};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushEnum(gc_erm_command);
|
||||
@@ -1751,24 +1761,21 @@ void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
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);
|
||||
|
||||
const auto result =
|
||||
GetResourceManager()->GetNpad()->GetVibrationHandler()->BeginPermitVibrationSession(
|
||||
applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) {
|
||||
GetResourceManager()->GetNpad()->SetPermitVibrationSession(false);
|
||||
|
||||
LOG_DEBUG(Service_HID, "called");
|
||||
|
||||
const auto result =
|
||||
GetResourceManager()->GetNpad()->GetVibrationHandler()->EndPermitVibrationSession();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
|
||||
@@ -1788,61 +1795,10 @@ void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
|
||||
parameters.vibration_device_handle.npad_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};
|
||||
rb.Push(result);
|
||||
rb.Push(is_mounted);
|
||||
}
|
||||
|
||||
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);
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted(
|
||||
parameters.applet_resource_user_id, parameters.vibration_device_handle));
|
||||
}
|
||||
|
||||
void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
|
||||
|
||||
@@ -97,7 +97,6 @@ private:
|
||||
void BeginPermitVibrationSession(HLERequestContext& ctx);
|
||||
void EndPermitVibrationSession(HLERequestContext& ctx);
|
||||
void IsVibrationDeviceMounted(HLERequestContext& ctx);
|
||||
void SendVibrationValueInBool(HLERequestContext& ctx);
|
||||
void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||
void StartConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||
void StopConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user