Compare commits
3 Commits
android-21
...
android-20
Author | SHA1 | Date | |
---|---|---|---|
65f76b6af4 | |||
d431619c14 | |||
51da014e53 |
@ -1,7 +1,7 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
| [12499](https://github.com/yuzu-emu/yuzu-android//pull/12499) | [`fc30a84fc`](https://github.com/yuzu-emu/yuzu-android//pull/12499/files) | Rework time services | [Kelebek1](https://github.com/Kelebek1/) | Yes |
|
||||
| [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`e3171486d`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12769](https://github.com/yuzu-emu/yuzu-android//pull/12769) | [`ad4622da2`](https://github.com/yuzu-emu/yuzu-android//pull/12769/files) | core: hid: Reduce controller requests | [german77](https://github.com/german77/) | Yes |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
@ -303,11 +303,6 @@ object NativeLibrary {
|
||||
*/
|
||||
external fun getCpuBackend(): String
|
||||
|
||||
/**
|
||||
* Returns the current GPU Driver.
|
||||
*/
|
||||
external fun getGpuDriver(): String
|
||||
|
||||
external fun applySettings()
|
||||
|
||||
external fun logSettings()
|
||||
@ -619,11 +614,6 @@ object NativeLibrary {
|
||||
*/
|
||||
external fun clearFilesystemProvider()
|
||||
|
||||
/**
|
||||
* Checks if all necessary keys are present for decryption
|
||||
*/
|
||||
external fun areKeysPresent(): Boolean
|
||||
|
||||
/**
|
||||
* Button type for use in onTouchEvent
|
||||
*/
|
||||
|
@ -14,20 +14,15 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
* Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
|
||||
* code used in every [RecyclerView].
|
||||
* Type assigned to [Model] must inherit from [Object] in order to be compared properly.
|
||||
* @param exact Decides whether each item will be compared by reference or by their contents
|
||||
*/
|
||||
abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>>(
|
||||
exact: Boolean = true
|
||||
) : ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>(exact)).build()) {
|
||||
abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>> :
|
||||
ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>()).build()) {
|
||||
override fun onBindViewHolder(holder: Holder, position: Int) =
|
||||
holder.bind(currentList[position])
|
||||
|
||||
private class DiffCallback<Model>(val exact: Boolean) : DiffUtil.ItemCallback<Model>() {
|
||||
private class DiffCallback<Model> : DiffUtil.ItemCallback<Model>() {
|
||||
override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
|
||||
if (exact) {
|
||||
return oldItem === newItem
|
||||
}
|
||||
return oldItem == newItem
|
||||
return oldItem === newItem
|
||||
}
|
||||
|
||||
@SuppressLint("DiffUtilEquals")
|
||||
|
@ -30,7 +30,7 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
|
||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||
|
||||
class GameAdapter(private val activity: AppCompatActivity) :
|
||||
AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>(exact = false) {
|
||||
AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
|
||||
CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
.also { return GameViewHolder(it) }
|
||||
|
@ -38,6 +38,7 @@ import androidx.window.layout.WindowLayoutInfo
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.slider.Slider
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||
@ -140,9 +141,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
|
||||
// So this fragment doesn't restart on configuration changes; i.e. rotation.
|
||||
retainInstance = true
|
||||
emulationState = EmulationState(game.path) {
|
||||
return@EmulationState driverViewModel.isInteractionAllowed.value
|
||||
}
|
||||
emulationState = EmulationState(game.path)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -371,15 +370,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
driverViewModel.isInteractionAllowed.collect {
|
||||
if (it) {
|
||||
startEmulation()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
emulationViewModel.emulationStarted.collectLatest {
|
||||
@ -408,10 +398,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
driverViewModel.isInteractionAllowed.collect {
|
||||
if (it) {
|
||||
onEmulationStart()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startEmulation() {
|
||||
private fun onEmulationStart() {
|
||||
if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
|
||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||
DirectoryInitialization.start()
|
||||
@ -486,15 +485,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
val FRAMETIME = 2
|
||||
val SPEED = 3
|
||||
perfStatsUpdater = {
|
||||
if (emulationViewModel.emulationStarted.value &&
|
||||
!emulationViewModel.isEmulationStopping.value
|
||||
) {
|
||||
if (emulationViewModel.emulationStarted.value) {
|
||||
val perfStats = NativeLibrary.getPerfStats()
|
||||
val cpuBackend = NativeLibrary.getCpuBackend()
|
||||
val gpuDriver = NativeLibrary.getGpuDriver()
|
||||
if (_binding != null) {
|
||||
binding.showFpsText.text =
|
||||
String.format("FPS: %.1f\n%s/%s", perfStats[FPS], cpuBackend, gpuDriver)
|
||||
String.format("FPS: %.1f\n%s", perfStats[FPS], cpuBackend)
|
||||
}
|
||||
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
|
||||
}
|
||||
@ -811,10 +807,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
private class EmulationState(
|
||||
private val gamePath: String,
|
||||
private val emulationCanStart: () -> Boolean
|
||||
) {
|
||||
private class EmulationState(private val gamePath: String) {
|
||||
private var state: State
|
||||
private var surface: Surface? = null
|
||||
|
||||
@ -908,7 +901,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
State.PAUSED -> Log.warning(
|
||||
"[EmulationFragment] Surface cleared while emulation paused."
|
||||
)
|
||||
|
||||
else -> Log.warning(
|
||||
"[EmulationFragment] Surface cleared while emulation stopped."
|
||||
)
|
||||
@ -918,10 +910,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
|
||||
private fun runWithValidSurface() {
|
||||
NativeLibrary.surfaceChanged(surface)
|
||||
if (!emulationCanStart.invoke()) {
|
||||
return
|
||||
}
|
||||
|
||||
when (state) {
|
||||
State.STOPPED -> {
|
||||
val emulationThread = Thread({
|
||||
|
@ -26,15 +26,9 @@ class MessageDialogFragment : DialogFragment() {
|
||||
val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
|
||||
val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!!
|
||||
val helpLinkId = requireArguments().getInt(HELP_LINK)
|
||||
val dismissible = requireArguments().getBoolean(DISMISSIBLE)
|
||||
val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION)
|
||||
|
||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||
|
||||
if (clearPositiveAction) {
|
||||
messageDialogViewModel.positiveAction = null
|
||||
}
|
||||
|
||||
if (messageDialogViewModel.positiveAction == null) {
|
||||
builder.setPositiveButton(R.string.close, null)
|
||||
} else {
|
||||
@ -57,8 +51,6 @@ class MessageDialogFragment : DialogFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
isCancelable = dismissible
|
||||
|
||||
return builder.show()
|
||||
}
|
||||
|
||||
@ -75,8 +67,6 @@ class MessageDialogFragment : DialogFragment() {
|
||||
private const val DESCRIPTION_ID = "DescriptionId"
|
||||
private const val DESCRIPTION_STRING = "DescriptionString"
|
||||
private const val HELP_LINK = "Link"
|
||||
private const val DISMISSIBLE = "Dismissible"
|
||||
private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction"
|
||||
|
||||
fun newInstance(
|
||||
activity: FragmentActivity? = null,
|
||||
@ -85,28 +75,22 @@ class MessageDialogFragment : DialogFragment() {
|
||||
descriptionId: Int = 0,
|
||||
descriptionString: String = "",
|
||||
helpLinkId: Int = 0,
|
||||
dismissible: Boolean = true,
|
||||
positiveAction: (() -> Unit)? = null
|
||||
): MessageDialogFragment {
|
||||
var clearPositiveAction = false
|
||||
if (activity != null) {
|
||||
ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
|
||||
clear()
|
||||
this.positiveAction = positiveAction
|
||||
}
|
||||
} else {
|
||||
clearPositiveAction = true
|
||||
}
|
||||
|
||||
val dialog = MessageDialogFragment()
|
||||
val bundle = Bundle().apply {
|
||||
val bundle = Bundle()
|
||||
bundle.apply {
|
||||
putInt(TITLE_ID, titleId)
|
||||
putString(TITLE_STRING, titleString)
|
||||
putInt(DESCRIPTION_ID, descriptionId)
|
||||
putString(DESCRIPTION_STRING, descriptionString)
|
||||
putInt(HELP_LINK, helpLinkId)
|
||||
putBoolean(DISMISSIBLE, dismissible)
|
||||
putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction)
|
||||
}
|
||||
if (activity != null) {
|
||||
ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
|
||||
clear()
|
||||
this.positiveAction = positiveAction
|
||||
}
|
||||
}
|
||||
dialog.arguments = bundle
|
||||
return dialog
|
||||
|
@ -31,7 +31,6 @@ import androidx.preference.PreferenceManager
|
||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||
import com.google.android.material.transition.MaterialFadeThrough
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import java.io.File
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
@ -163,7 +162,7 @@ class SetupFragment : Fragment() {
|
||||
R.string.install_prod_keys_warning_help,
|
||||
{
|
||||
val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
|
||||
if (file.exists() && NativeLibrary.areKeysPresent()) {
|
||||
if (file.exists()) {
|
||||
StepState.COMPLETE
|
||||
} else {
|
||||
StepState.INCOMPLETE
|
||||
@ -348,8 +347,7 @@ class SetupFragment : Fragment() {
|
||||
val getProdKey =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
if (result != null) {
|
||||
mainActivity.processKey(result)
|
||||
if (NativeLibrary.areKeysPresent()) {
|
||||
if (mainActivity.processKey(result)) {
|
||||
keyCallback.onStepCompleted()
|
||||
}
|
||||
}
|
||||
|
@ -144,7 +144,6 @@ class DriverViewModel : ViewModel() {
|
||||
val selectedDriverFile = File(StringSetting.DRIVER_PATH.getString())
|
||||
val selectedDriverMetadata = GpuDriverHelper.customDriverSettingData
|
||||
if (GpuDriverHelper.installedCustomDriverData == selectedDriverMetadata) {
|
||||
setDriverReady()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -70,19 +70,11 @@ class Game(
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
if (other !is Game) {
|
||||
return false
|
||||
}
|
||||
|
||||
other as Game
|
||||
|
||||
if (title != other.title) return false
|
||||
if (path != other.path) return false
|
||||
if (programId != other.programId) return false
|
||||
if (developer != other.developer) return false
|
||||
if (version != other.version) return false
|
||||
if (isHomebrew != other.isHomebrew) return false
|
||||
|
||||
return true
|
||||
return hashCode() == other.hashCode()
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
@ -31,9 +31,6 @@ class HomeViewModel : ViewModel() {
|
||||
private val _reloadPropertiesList = MutableStateFlow(false)
|
||||
val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
|
||||
|
||||
private val _checkKeys = MutableStateFlow(false)
|
||||
val checkKeys = _checkKeys.asStateFlow()
|
||||
|
||||
var navigatedToSetup = false
|
||||
|
||||
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
|
||||
@ -69,8 +66,4 @@ class HomeViewModel : ViewModel() {
|
||||
fun reloadPropertiesList(reload: Boolean) {
|
||||
_reloadPropertiesList.value = reload
|
||||
}
|
||||
|
||||
fun setCheckKeys(value: Boolean) {
|
||||
_checkKeys.value = value
|
||||
}
|
||||
}
|
||||
|
@ -64,9 +64,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
|
||||
override var themeId: Int = 0
|
||||
|
||||
private val CHECKED_DECRYPTION = "CheckedDecryption"
|
||||
private var checkedDecryption = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val splashScreen = installSplashScreen()
|
||||
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
|
||||
@ -78,18 +75,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION)
|
||||
}
|
||||
if (!checkedDecryption) {
|
||||
val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext)
|
||||
.getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true)
|
||||
if (!firstTimeSetup) {
|
||||
checkKeys()
|
||||
}
|
||||
checkedDecryption = true
|
||||
}
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
|
||||
|
||||
@ -165,16 +150,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
homeViewModel.checkKeys.collect {
|
||||
if (it) {
|
||||
checkKeys()
|
||||
homeViewModel.setCheckKeys(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dismiss previous notifications (should not happen unless a crash occurred)
|
||||
@ -183,21 +158,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
setInsets()
|
||||
}
|
||||
|
||||
private fun checkKeys() {
|
||||
if (!NativeLibrary.areKeysPresent()) {
|
||||
MessageDialogFragment.newInstance(
|
||||
titleId = R.string.keys_missing,
|
||||
descriptionId = R.string.keys_missing_description,
|
||||
helpLinkId = R.string.keys_missing_help
|
||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putBoolean(CHECKED_DECRYPTION, checkedDecryption)
|
||||
}
|
||||
|
||||
fun finishSetup(navController: NavController) {
|
||||
navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment)
|
||||
(binding.navigationView as NavigationBarView).setupWithNavController(navController)
|
||||
@ -389,7 +349,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
R.string.install_keys_success,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
homeViewModel.setCheckKeys(true)
|
||||
gamesViewModel.reloadGames(true)
|
||||
return true
|
||||
} else {
|
||||
@ -440,7 +399,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
firmwarePath.deleteRecursively()
|
||||
cacheFirmwareDir.copyRecursively(firmwarePath, true)
|
||||
NativeLibrary.initializeSystem(true)
|
||||
homeViewModel.setCheckKeys(true)
|
||||
getString(R.string.save_file_imported_success)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
@ -247,7 +247,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
||||
m_system.GetCpuManager().OnGpuReady();
|
||||
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
||||
|
||||
OnEmulationStarted();
|
||||
return Core::SystemResultStatus::Success;
|
||||
}
|
||||
|
||||
@ -464,8 +463,8 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
|
||||
};
|
||||
|
||||
return static_cast<int>(
|
||||
ContentManager::InstallNSP(EmulationSession::GetInstance().System(),
|
||||
*EmulationSession::GetInstance().System().GetFilesystem(),
|
||||
ContentManager::InstallNSP(&EmulationSession::GetInstance().System(),
|
||||
EmulationSession::GetInstance().System().GetFilesystem().get(),
|
||||
GetJString(env, j_file), callback));
|
||||
}
|
||||
|
||||
@ -675,11 +674,6 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass
|
||||
return ToJString(env, "JIT");
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) {
|
||||
return ToJString(env,
|
||||
EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor());
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
|
||||
EmulationSession::GetInstance().System().ApplySettings();
|
||||
}
|
||||
@ -825,7 +819,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject job
|
||||
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);
|
||||
ContentManager::RemoveAllDLC(&EmulationSession::GetInstance().System(), program_id);
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
|
||||
@ -835,9 +829,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj,
|
||||
program_id, GetJString(env, jname));
|
||||
}
|
||||
|
||||
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env,
|
||||
jobject jobj,
|
||||
jobject jcallback) {
|
||||
jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj,
|
||||
jobject jcallback) {
|
||||
auto jlambdaClass = env->GetObjectClass(jcallback);
|
||||
auto jlambdaInvokeMethod = env->GetMethodID(
|
||||
jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
@ -849,7 +842,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEn
|
||||
|
||||
auto& session = EmulationSession::GetInstance();
|
||||
std::vector<std::string> result = ContentManager::VerifyInstalledContents(
|
||||
session.System(), *session.GetContentProvider(), callback);
|
||||
&session.System(), session.GetContentProvider(), callback);
|
||||
jobjectArray jresult =
|
||||
env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, ""));
|
||||
for (size_t i = 0; i < result.size(); ++i) {
|
||||
@ -870,7 +863,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje
|
||||
};
|
||||
auto& session = EmulationSession::GetInstance();
|
||||
return static_cast<jint>(
|
||||
ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback));
|
||||
ContentManager::VerifyGameContents(&session.System(), GetJString(env, jpath), callback));
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
||||
@ -919,10 +912,4 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env,
|
||||
EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobject jobj) {
|
||||
auto& system = EmulationSession::GetInstance().System();
|
||||
system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
|
||||
return ContentManager::AreKeysPresent();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
@ -144,9 +144,6 @@
|
||||
<string name="no_save_data_found">No save data found</string>
|
||||
<string name="verify_installed_content">Verify installed content</string>
|
||||
<string name="verify_installed_content_description">Checks all installed content for corruption</string>
|
||||
<string name="keys_missing">Encryption keys are missing</string>
|
||||
<string name="keys_missing_description">Firmware and retail games cannot be decrypted</string>
|
||||
<string name="keys_missing_help">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
|
||||
|
||||
<!-- Applet launcher strings -->
|
||||
<string name="applets">Applet launcher</string>
|
||||
|
@ -29,6 +29,10 @@ 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)};
|
||||
}
|
||||
@ -42,11 +46,11 @@ std::chrono::milliseconds NativeClock::GetTimeMS() const {
|
||||
}
|
||||
|
||||
s64 NativeClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
|
||||
return MultiplyHigh(GetUptime() - start_ticks, guest_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
|
||||
return MultiplyHigh(GetUptime() - start_ticks, gputick_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetUptime() const {
|
||||
|
@ -11,6 +11,8 @@ class NativeClock final : public WallClock {
|
||||
public:
|
||||
explicit NativeClock();
|
||||
|
||||
void Reset() override;
|
||||
|
||||
std::chrono::nanoseconds GetTimeNS() const override;
|
||||
|
||||
std::chrono::microseconds GetTimeUS() const override;
|
||||
@ -40,6 +42,7 @@ private:
|
||||
FactorType ms_cntfrq_factor;
|
||||
FactorType guest_cntfrq_factor;
|
||||
FactorType gputick_cntfrq_factor;
|
||||
s64 start_ticks;
|
||||
};
|
||||
|
||||
} // namespace Common::Arm64
|
||||
|
@ -20,6 +20,10 @@ class StandardWallClock final : public WallClock {
|
||||
public:
|
||||
explicit StandardWallClock() {}
|
||||
|
||||
void Reset() override {
|
||||
start_time = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds GetTimeNS() const override {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
@ -45,13 +49,16 @@ public:
|
||||
|
||||
s64 GetUptime() const override {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
std::chrono::system_clock::now() - start_time)
|
||||
.count();
|
||||
}
|
||||
|
||||
bool IsNative() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::chrono::system_clock::time_point start_time{};
|
||||
};
|
||||
|
||||
std::unique_ptr<WallClock> CreateOptimalClock() {
|
||||
|
@ -19,6 +19,8 @@ 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;
|
||||
|
||||
|
@ -15,6 +15,10 @@ NativeClock::NativeClock(u64 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)};
|
||||
}
|
||||
@ -28,11 +32,11 @@ std::chrono::milliseconds NativeClock::GetTimeMS() const {
|
||||
}
|
||||
|
||||
s64 NativeClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
|
||||
return MultiplyHigh(GetUptime() - start_ticks, cntpct_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
|
||||
return MultiplyHigh(GetUptime() - start_ticks, gputick_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetUptime() const {
|
||||
|
@ -11,6 +11,8 @@ 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;
|
||||
@ -26,6 +28,7 @@ public:
|
||||
bool IsNative() const override;
|
||||
|
||||
private:
|
||||
u64 start_ticks;
|
||||
u64 rdtsc_frequency;
|
||||
|
||||
u64 ns_rdtsc_factor;
|
||||
|
@ -472,8 +472,6 @@ add_library(core STATIC
|
||||
hle/service/caps/caps_types.h
|
||||
hle/service/caps/caps_u.cpp
|
||||
hle/service/caps/caps_u.h
|
||||
hle/service/cmif_serialization.h
|
||||
hle/service/cmif_types.h
|
||||
hle/service/erpt/erpt.cpp
|
||||
hle/service/erpt/erpt.h
|
||||
hle/service/es/es.cpp
|
||||
|
@ -66,6 +66,7 @@ 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));
|
||||
}
|
||||
|
@ -69,14 +69,9 @@ public:
|
||||
};
|
||||
|
||||
template <typename AddressType>
|
||||
void InvalidateInstructionCache(KernelCore& kernel, KPageTableBase* table, AddressType addr,
|
||||
u64 size) {
|
||||
void InvalidateInstructionCache(KernelCore& kernel, AddressType addr, u64 size) {
|
||||
// TODO: lock the process list
|
||||
for (auto& process : kernel.GetProcessList()) {
|
||||
if (std::addressof(process->GetPageTable().GetBasePageTable()) != table) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
auto* interface = process->GetArmInterface(i);
|
||||
if (interface) {
|
||||
@ -1307,7 +1302,7 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr
|
||||
bool reprotected_pages = false;
|
||||
SCOPE_EXIT({
|
||||
if (reprotected_pages && any_code_pages) {
|
||||
InvalidateInstructionCache(m_kernel, this, dst_address, size);
|
||||
InvalidateInstructionCache(m_kernel, dst_address, size);
|
||||
}
|
||||
});
|
||||
|
||||
@ -2041,7 +2036,7 @@ Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t s
|
||||
for (const auto& block : pg) {
|
||||
StoreDataCache(GetHeapVirtualPointer(m_kernel, block.GetAddress()), block.GetSize());
|
||||
}
|
||||
InvalidateInstructionCache(m_kernel, this, addr, size);
|
||||
InvalidateInstructionCache(m_kernel, addr, size);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
@ -3282,7 +3277,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
|
||||
R_TRY(PerformCopy());
|
||||
|
||||
// Invalidate the instruction cache, as this svc allows modifying executable pages.
|
||||
InvalidateInstructionCache(m_kernel, this, dst_address, size);
|
||||
InvalidateInstructionCache(m_kernel, dst_address, size);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
@ -1,337 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/div_ceil.h"
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
|
||||
// clang-format off
|
||||
struct RequestLayout {
|
||||
u32 copy_handle_count;
|
||||
u32 move_handle_count;
|
||||
u32 cmif_raw_data_size;
|
||||
u32 domain_interface_count;
|
||||
};
|
||||
|
||||
template <ArgumentType Type1, ArgumentType Type2, typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0>
|
||||
constexpr u32 GetArgumentRawDataSize() {
|
||||
if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
|
||||
return static_cast<u32>(DataOffset);
|
||||
} else {
|
||||
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
|
||||
|
||||
if constexpr (ArgumentTraits<ArgType>::Type == Type1 || ArgumentTraits<ArgType>::Type == Type2) {
|
||||
constexpr size_t ArgAlign = alignof(ArgType);
|
||||
constexpr size_t ArgSize = sizeof(ArgType);
|
||||
|
||||
static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");
|
||||
|
||||
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
|
||||
constexpr size_t ArgEnd = ArgOffset + ArgSize;
|
||||
|
||||
return GetArgumentRawDataSize<Type1, Type2, MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>();
|
||||
} else {
|
||||
return GetArgumentRawDataSize<Type1, Type2, MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <ArgumentType DataType, typename MethodArguments, size_t ArgCount = 0, size_t ArgIndex = 0>
|
||||
constexpr u32 GetArgumentTypeCount() {
|
||||
if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
|
||||
return static_cast<u32>(ArgCount);
|
||||
} else {
|
||||
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
|
||||
|
||||
if constexpr (ArgumentTraits<ArgType>::Type == DataType) {
|
||||
return GetArgumentTypeCount<DataType, MethodArguments, ArgCount + 1, ArgIndex + 1>();
|
||||
} else {
|
||||
return GetArgumentTypeCount<DataType, MethodArguments, ArgCount, ArgIndex + 1>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename MethodArguments>
|
||||
constexpr RequestLayout GetNonDomainReplyInLayout() {
|
||||
return RequestLayout{
|
||||
.copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
|
||||
.move_handle_count = 0,
|
||||
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(),
|
||||
.domain_interface_count = 0,
|
||||
};
|
||||
}
|
||||
|
||||
template <typename MethodArguments>
|
||||
constexpr RequestLayout GetDomainReplyInLayout() {
|
||||
return RequestLayout{
|
||||
.copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
|
||||
.move_handle_count = 0,
|
||||
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(),
|
||||
.domain_interface_count = GetArgumentTypeCount<ArgumentType::InInterface, MethodArguments>(),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename MethodArguments>
|
||||
constexpr RequestLayout GetNonDomainReplyOutLayout() {
|
||||
return RequestLayout{
|
||||
.copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
|
||||
.move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>() + GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
|
||||
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(),
|
||||
.domain_interface_count = 0,
|
||||
};
|
||||
}
|
||||
|
||||
template <typename MethodArguments>
|
||||
constexpr RequestLayout GetDomainReplyOutLayout() {
|
||||
return RequestLayout{
|
||||
.copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
|
||||
.move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>(),
|
||||
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(),
|
||||
.domain_interface_count = GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
|
||||
};
|
||||
}
|
||||
|
||||
template <bool Domain, typename MethodArguments>
|
||||
constexpr RequestLayout GetReplyInLayout() {
|
||||
return Domain ? GetDomainReplyInLayout<MethodArguments>() : GetNonDomainReplyInLayout<MethodArguments>();
|
||||
}
|
||||
|
||||
template <bool Domain, typename MethodArguments>
|
||||
constexpr RequestLayout GetReplyOutLayout() {
|
||||
return Domain ? GetDomainReplyOutLayout<MethodArguments>() : GetNonDomainReplyOutLayout<MethodArguments>();
|
||||
}
|
||||
|
||||
using OutTemporaryBuffers = std::array<Common::ScratchBuffer<u8>, 3>;
|
||||
|
||||
template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t HandleIndex = 0, size_t InBufferIndex = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
|
||||
void ReadInArgument(CallArguments& args, const u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
|
||||
if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
|
||||
return;
|
||||
} else {
|
||||
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
|
||||
|
||||
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
|
||||
constexpr size_t ArgAlign = alignof(ArgType);
|
||||
constexpr size_t ArgSize = sizeof(ArgType);
|
||||
|
||||
static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");
|
||||
static_assert(!RawDataFinished, "All input interface arguments must appear after raw data");
|
||||
|
||||
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
|
||||
constexpr size_t ArgEnd = ArgOffset + ArgSize;
|
||||
|
||||
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
|
||||
// TODO: abort parsing if PID is not provided?
|
||||
// TODO: validate against raw data value?
|
||||
std::get<ArgIndex>(args).pid = ctx.GetPID();
|
||||
} else {
|
||||
std::memcpy(&std::get<ArgIndex>(args), raw_data + ArgOffset, ArgSize);
|
||||
}
|
||||
|
||||
return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InInterface) {
|
||||
constexpr size_t ArgAlign = alignof(u32);
|
||||
constexpr size_t ArgSize = sizeof(u32);
|
||||
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
|
||||
constexpr size_t ArgEnd = ArgOffset + ArgSize;
|
||||
|
||||
static_assert(Domain);
|
||||
ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0);
|
||||
|
||||
u32 value{};
|
||||
std::memcpy(&value, raw_data + ArgOffset, ArgSize);
|
||||
std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1);
|
||||
|
||||
return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) {
|
||||
std::get<ArgIndex>(args) = std::move(ctx.GetObjectFromHandle<typename ArgType::Type>(ctx.GetCopyHandle(HandleIndex)));
|
||||
|
||||
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex + 1, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InLargeData) {
|
||||
constexpr size_t BufferSize = sizeof(ArgType);
|
||||
|
||||
// Clear the existing data.
|
||||
std::memset(&std::get<ArgIndex>(args), 0, BufferSize);
|
||||
|
||||
std::span<const u8> buffer{};
|
||||
|
||||
ASSERT(ctx.CanReadBuffer(InBufferIndex));
|
||||
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
|
||||
buffer = ctx.ReadBuffer(InBufferIndex);
|
||||
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
|
||||
buffer = ctx.ReadBufferA(InBufferIndex);
|
||||
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
|
||||
buffer = ctx.ReadBufferX(InBufferIndex);
|
||||
}
|
||||
|
||||
std::memcpy(&std::get<ArgIndex>(args), buffer.data(), std::min(BufferSize, buffer.size()));
|
||||
|
||||
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InBuffer) {
|
||||
using ElementType = typename ArgType::Type;
|
||||
|
||||
std::span<const u8> buffer{};
|
||||
|
||||
if (ctx.CanReadBuffer(InBufferIndex)) {
|
||||
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
|
||||
buffer = ctx.ReadBuffer(InBufferIndex);
|
||||
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
|
||||
buffer = ctx.ReadBufferA(InBufferIndex);
|
||||
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
|
||||
buffer = ctx.ReadBufferX(InBufferIndex);
|
||||
}
|
||||
}
|
||||
|
||||
ElementType* ptr = (ElementType*) buffer.data();
|
||||
size_t size = buffer.size() / sizeof(ElementType);
|
||||
|
||||
std::get<ArgIndex>(args) = std::span(ptr, size);
|
||||
|
||||
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
|
||||
constexpr size_t BufferSize = sizeof(ArgType);
|
||||
|
||||
// Clear the existing data.
|
||||
std::memset(&std::get<ArgIndex>(args), 0, BufferSize);
|
||||
|
||||
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
|
||||
using ElementType = typename ArgType::Type;
|
||||
|
||||
// Set up scratch buffer.
|
||||
auto& buffer = temp[OutBufferIndex];
|
||||
if (ctx.CanWriteBuffer(OutBufferIndex)) {
|
||||
buffer.resize_destructive(ctx.GetWriteBufferSize(OutBufferIndex));
|
||||
} else {
|
||||
buffer.resize_destructive(0);
|
||||
}
|
||||
|
||||
ElementType* ptr = (ElementType*) buffer.data();
|
||||
size_t size = buffer.size() / sizeof(ElementType);
|
||||
|
||||
std::get<ArgIndex>(args) = std::span(ptr, size);
|
||||
|
||||
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else {
|
||||
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
|
||||
void WriteOutArgument(CallArguments& args, u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
|
||||
if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
|
||||
return;
|
||||
} else {
|
||||
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
|
||||
|
||||
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) {
|
||||
constexpr size_t ArgAlign = alignof(ArgType);
|
||||
constexpr size_t ArgSize = sizeof(ArgType);
|
||||
|
||||
static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment");
|
||||
static_assert(!RawDataFinished, "All output interface arguments must appear after raw data");
|
||||
|
||||
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
|
||||
constexpr size_t ArgEnd = ArgOffset + ArgSize;
|
||||
|
||||
std::memcpy(raw_data + ArgOffset, &std::get<ArgIndex>(args), ArgSize);
|
||||
|
||||
return WriteOutArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutInterface) {
|
||||
if constexpr (Domain) {
|
||||
ctx.AddDomainObject(std::get<ArgIndex>(args));
|
||||
} else {
|
||||
ctx.AddMoveInterface(std::get<ArgIndex>(args));
|
||||
}
|
||||
|
||||
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutCopyHandle) {
|
||||
ctx.AddCopyObject(std::get<ArgIndex>(args).GetPointerUnsafe());
|
||||
|
||||
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutMoveHandle) {
|
||||
ctx.AddMoveObject(std::get<ArgIndex>(args).GetPointerUnsafe());
|
||||
|
||||
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
|
||||
constexpr size_t BufferSize = sizeof(ArgType);
|
||||
|
||||
ASSERT(ctx.CanWriteBuffer(OutBufferIndex));
|
||||
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
|
||||
ctx.WriteBuffer(std::get<ArgIndex>(args), OutBufferIndex);
|
||||
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
|
||||
ctx.WriteBufferB(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
|
||||
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
|
||||
ctx.WriteBufferC(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
|
||||
}
|
||||
|
||||
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
|
||||
auto& buffer = temp[OutBufferIndex];
|
||||
const size_t size = buffer.size();
|
||||
|
||||
if (ctx.CanWriteBuffer(OutBufferIndex)) {
|
||||
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
|
||||
ctx.WriteBuffer(buffer.data(), size, OutBufferIndex);
|
||||
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
|
||||
ctx.WriteBufferB(buffer.data(), size, OutBufferIndex);
|
||||
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
|
||||
ctx.WriteBufferC(buffer.data(), size, OutBufferIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>( args, raw_data, ctx, temp);
|
||||
} else {
|
||||
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Domain, typename T, typename... A>
|
||||
void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
|
||||
// Verify domain state.
|
||||
if constexpr (Domain) {
|
||||
ASSERT_MSG(ctx.GetManager()->IsDomain(), "Domain reply used on non-domain session");
|
||||
} else {
|
||||
ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Non-domain reply used on domain session");
|
||||
}
|
||||
|
||||
using MethodArguments = std::tuple<std::remove_reference_t<A>...>;
|
||||
|
||||
OutTemporaryBuffers buffers{};
|
||||
auto call_arguments = std::tuple<typename RemoveOut<A>::Type...>();
|
||||
|
||||
// Read inputs.
|
||||
const size_t offset_plus_command_id = ctx.GetDataPayloadOffset() + 2;
|
||||
ReadInArgument<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + offset_plus_command_id), ctx, buffers);
|
||||
|
||||
// Call.
|
||||
const auto Callable = [&]<typename... CallArgs>(CallArgs&... args) {
|
||||
return (t.*f)(args...);
|
||||
};
|
||||
const Result res = std::apply(Callable, call_arguments);
|
||||
|
||||
// Write result.
|
||||
constexpr RequestLayout layout = GetReplyOutLayout<Domain, MethodArguments>();
|
||||
IPC::ResponseBuilder rb{ctx, 2 + Common::DivCeil(layout.cmif_raw_data_size, sizeof(u32)), layout.copy_handle_count, layout.move_handle_count + layout.domain_interface_count};
|
||||
rb.Push(res);
|
||||
|
||||
// Write out arguments.
|
||||
WriteOutArgument<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + rb.GetCurrentOffset()), ctx, buffers);
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
template <typename Self>
|
||||
template <bool Domain, auto F>
|
||||
inline void ServiceFramework<Self>::CmifReplyWrap(HLERequestContext& ctx) {
|
||||
return CmifReplyWrapImpl<Domain>(ctx, *static_cast<Self*>(this), F);
|
||||
}
|
||||
|
||||
} // namespace Service
|
@ -1,234 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/hle_ipc.h"
|
||||
|
||||
namespace Service {
|
||||
|
||||
// clang-format off
|
||||
template <typename T>
|
||||
class Out {
|
||||
public:
|
||||
/* implicit */ Out(T& t) : raw(&t) {}
|
||||
~Out() = default;
|
||||
|
||||
T* Get() const {
|
||||
return raw;
|
||||
}
|
||||
|
||||
T& operator*() {
|
||||
return *raw;
|
||||
}
|
||||
|
||||
private:
|
||||
T* raw;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using SharedPointer = std::shared_ptr<T>;
|
||||
|
||||
struct ClientProcessId {
|
||||
explicit operator bool() const {
|
||||
return pid != 0;
|
||||
}
|
||||
|
||||
const u64& operator*() const {
|
||||
return pid;
|
||||
}
|
||||
|
||||
u64 pid;
|
||||
};
|
||||
|
||||
using ClientAppletResourceUserId = ClientProcessId;
|
||||
|
||||
template <typename T>
|
||||
class InCopyHandle : public Kernel::KScopedAutoObject<T> {
|
||||
public:
|
||||
using Type = T;
|
||||
|
||||
template <typename... Args>
|
||||
/* implicit */ InCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
|
||||
~InCopyHandle() = default;
|
||||
|
||||
InCopyHandle& operator=(InCopyHandle&& rhs) {
|
||||
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class OutCopyHandle : public Kernel::KScopedAutoObject<T> {
|
||||
public:
|
||||
using Type = T;
|
||||
|
||||
template <typename... Args>
|
||||
/* implicit */ OutCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
|
||||
~OutCopyHandle() = default;
|
||||
|
||||
OutCopyHandle& operator=(OutCopyHandle&& rhs) {
|
||||
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class OutMoveHandle : public Kernel::KScopedAutoObject<T> {
|
||||
public:
|
||||
using Type = T;
|
||||
|
||||
template <typename... Args>
|
||||
/* implicit */ OutMoveHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
|
||||
~OutMoveHandle() = default;
|
||||
|
||||
OutMoveHandle& operator=(OutMoveHandle&& rhs) {
|
||||
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
enum BufferAttr : int {
|
||||
BufferAttr_In = (1U << 0),
|
||||
BufferAttr_Out = (1U << 1),
|
||||
BufferAttr_HipcMapAlias = (1U << 2),
|
||||
BufferAttr_HipcPointer = (1U << 3),
|
||||
BufferAttr_FixedSize = (1U << 4),
|
||||
BufferAttr_HipcAutoSelect = (1U << 5),
|
||||
BufferAttr_HipcMapTransferAllowsNonSecure = (1U << 6),
|
||||
BufferAttr_HipcMapTransferAllowsNonDevice = (1U << 7),
|
||||
};
|
||||
|
||||
template <typename T, int A>
|
||||
struct Buffer : public std::span<T> {
|
||||
static_assert(std::is_trivial_v<T>, "Buffer type must be trivial");
|
||||
static_assert((A & BufferAttr_FixedSize) == 0, "Buffer attr must not contain FixedSize");
|
||||
static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "Buffer attr must be In or Out");
|
||||
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A);
|
||||
using Type = T;
|
||||
|
||||
Buffer& operator=(const std::span<T>& rhs) {
|
||||
std::span<T>::operator=(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() const {
|
||||
return *this->data();
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return this->size() > 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <BufferAttr A>
|
||||
using InBuffer = Buffer<const u8, BufferAttr_In | A>;
|
||||
|
||||
template <typename T, BufferAttr A>
|
||||
using InArray = Buffer<T, BufferAttr_In | A>;
|
||||
|
||||
template <BufferAttr A>
|
||||
using OutBuffer = Buffer<u8, BufferAttr_Out | A>;
|
||||
|
||||
template <typename T, BufferAttr A>
|
||||
using OutArray = Buffer<T, BufferAttr_Out | A>;
|
||||
|
||||
template <typename T, int A>
|
||||
struct LargeData : public T {
|
||||
static_assert(std::is_trivial_v<T>, "LargeData type must be trivial");
|
||||
static_assert((A & BufferAttr_FixedSize) != 0, "LargeData attr must contain FixedSize");
|
||||
static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "LargeData attr must be In or Out");
|
||||
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A);
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <typename T, BufferAttr A>
|
||||
using InLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_In | A>;
|
||||
|
||||
template <typename T, BufferAttr A>
|
||||
using OutLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_Out | A>;
|
||||
|
||||
template <typename T>
|
||||
struct RemoveOut {
|
||||
using Type = std::remove_reference_t<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RemoveOut<Out<T>> {
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
enum class ArgumentType {
|
||||
InProcessId,
|
||||
InData,
|
||||
InInterface,
|
||||
InCopyHandle,
|
||||
OutData,
|
||||
OutInterface,
|
||||
OutCopyHandle,
|
||||
OutMoveHandle,
|
||||
InBuffer,
|
||||
InLargeData,
|
||||
OutBuffer,
|
||||
OutLargeData,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ArgumentTraits;
|
||||
|
||||
template <>
|
||||
struct ArgumentTraits<ClientProcessId> {
|
||||
static constexpr ArgumentType Type = ArgumentType::InProcessId;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ArgumentTraits<SharedPointer<T>> {
|
||||
static constexpr ArgumentType Type = ArgumentType::InInterface;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ArgumentTraits<InCopyHandle<T>> {
|
||||
static constexpr ArgumentType Type = ArgumentType::InCopyHandle;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ArgumentTraits<Out<SharedPointer<T>>> {
|
||||
static constexpr ArgumentType Type = ArgumentType::OutInterface;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ArgumentTraits<Out<T>> {
|
||||
static constexpr ArgumentType Type = ArgumentType::OutData;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ArgumentTraits<OutCopyHandle<T>> {
|
||||
static constexpr ArgumentType Type = ArgumentType::OutCopyHandle;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ArgumentTraits<OutMoveHandle<T>> {
|
||||
static constexpr ArgumentType Type = ArgumentType::OutMoveHandle;
|
||||
};
|
||||
|
||||
template <typename T, int A>
|
||||
struct ArgumentTraits<Buffer<T, A>> {
|
||||
static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutBuffer : ArgumentType::InBuffer;
|
||||
};
|
||||
|
||||
template <typename T, int A>
|
||||
struct ArgumentTraits<LargeData<T, A>> {
|
||||
static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutLargeData : ArgumentType::InLargeData;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ArgumentTraits {
|
||||
static constexpr ArgumentType Type = ArgumentType::InData;
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
} // namespace Service
|
@ -501,22 +501,6 @@ bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const {
|
||||
}
|
||||
}
|
||||
|
||||
void HLERequestContext::AddMoveInterface(SessionRequestHandlerPtr s) {
|
||||
ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(
|
||||
Kernel::LimitableResource::SessionCountMax, 1));
|
||||
|
||||
auto* session = Kernel::KSession::Create(kernel);
|
||||
session->Initialize(nullptr, 0);
|
||||
Kernel::KSession::Register(kernel, session);
|
||||
|
||||
auto& server = manager.lock()->GetServerManager();
|
||||
auto next_manager = std::make_shared<Service::SessionRequestManager>(kernel, server);
|
||||
next_manager->SetSessionHandler(std::move(s));
|
||||
server.RegisterSession(&session->GetServerSession(), next_manager);
|
||||
|
||||
AddMoveObject(&session->GetClientSession());
|
||||
}
|
||||
|
||||
std::string HLERequestContext::Description() const {
|
||||
if (!command_header) {
|
||||
return "No command header available";
|
||||
|
@ -339,8 +339,6 @@ public:
|
||||
outgoing_move_objects.emplace_back(object);
|
||||
}
|
||||
|
||||
void AddMoveInterface(SessionRequestHandlerPtr s);
|
||||
|
||||
void AddCopyObject(Kernel::KAutoObject* object) {
|
||||
outgoing_copy_objects.emplace_back(object);
|
||||
}
|
||||
|
@ -6,12 +6,12 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/jit/jit.h"
|
||||
#include "core/hle/service/jit/jit_code_memory.h"
|
||||
#include "core/hle/service/jit/jit_context.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Service::JIT {
|
||||
@ -21,9 +21,6 @@ struct CodeRange {
|
||||
u64 size;
|
||||
};
|
||||
|
||||
using Struct32 = std::array<u64, 4>;
|
||||
static_assert(sizeof(Struct32) == 32, "Struct32 has wrong size");
|
||||
|
||||
class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
|
||||
public:
|
||||
explicit IJitEnvironment(Core::System& system_,
|
||||
@ -32,13 +29,12 @@ public:
|
||||
: ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)},
|
||||
user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)},
|
||||
context{system_.ApplicationMemory()} {
|
||||
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, C<&IJitEnvironment::GenerateCode>, "GenerateCode"},
|
||||
{1, C<&IJitEnvironment::Control>, "Control"},
|
||||
{1000, C<&IJitEnvironment::LoadPlugin>, "LoadPlugin"},
|
||||
{1001, C<&IJitEnvironment::GetCodeAddress>, "GetCodeAddress"},
|
||||
{0, &IJitEnvironment::GenerateCode, "GenerateCode"},
|
||||
{1, &IJitEnvironment::Control, "Control"},
|
||||
{1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"},
|
||||
{1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@ -54,10 +50,28 @@ public:
|
||||
configuration.sys_ro_memory = configuration.user_ro_memory;
|
||||
}
|
||||
|
||||
Result GenerateCode(Out<s32> out_return_value, Out<CodeRange> out_range0,
|
||||
Out<CodeRange> out_range1, OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
u32 data_size, u64 command, CodeRange range0, CodeRange range1,
|
||||
Struct32 data, InBuffer<BufferAttr_HipcMapAlias> buffer) {
|
||||
void GenerateCode(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_JIT, "called");
|
||||
|
||||
struct InputParameters {
|
||||
u32 data_size;
|
||||
u64 command;
|
||||
std::array<CodeRange, 2> ranges;
|
||||
Struct32 data;
|
||||
};
|
||||
|
||||
struct OutputParameters {
|
||||
s32 return_value;
|
||||
std::array<CodeRange, 2> ranges;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters{rp.PopRaw<InputParameters>()};
|
||||
|
||||
// Optional input/output buffers
|
||||
const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
|
||||
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
|
||||
|
||||
// Function call prototype:
|
||||
// void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg,
|
||||
// u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in,
|
||||
@ -69,36 +83,66 @@ public:
|
||||
// other arguments are used to transfer state between the game and the plugin.
|
||||
|
||||
const VAddr ret_ptr{context.AddHeap(0u)};
|
||||
const VAddr c0_in_ptr{context.AddHeap(range0)};
|
||||
const VAddr c1_in_ptr{context.AddHeap(range1)};
|
||||
const VAddr c0_out_ptr{context.AddHeap(ClearSize(range0))};
|
||||
const VAddr c1_out_ptr{context.AddHeap(ClearSize(range1))};
|
||||
const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])};
|
||||
const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])};
|
||||
const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))};
|
||||
const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))};
|
||||
|
||||
const VAddr input_ptr{context.AddHeap(buffer.data(), buffer.size())};
|
||||
const VAddr output_ptr{context.AddHeap(out_buffer.data(), out_buffer.size())};
|
||||
const VAddr data_ptr{context.AddHeap(data)};
|
||||
const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
|
||||
const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
|
||||
const VAddr data_ptr{context.AddHeap(parameters.data)};
|
||||
const VAddr configuration_ptr{context.AddHeap(configuration)};
|
||||
|
||||
// The callback does not directly return a value, it only writes to the output pointer
|
||||
context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr,
|
||||
configuration_ptr, command, input_ptr, buffer.size(), c0_in_ptr,
|
||||
c1_in_ptr, data_ptr, data_size, output_ptr, out_buffer.size());
|
||||
configuration_ptr, parameters.command, input_ptr, input_buffer.size(),
|
||||
c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr,
|
||||
output_buffer.size());
|
||||
|
||||
*out_return_value = context.GetHeap<s32>(ret_ptr);
|
||||
*out_range0 = context.GetHeap<CodeRange>(c0_out_ptr);
|
||||
*out_range1 = context.GetHeap<CodeRange>(c1_out_ptr);
|
||||
context.GetHeap(output_ptr, out_buffer.data(), out_buffer.size());
|
||||
const s32 return_value{context.GetHeap<s32>(ret_ptr)};
|
||||
|
||||
if (*out_return_value != 0) {
|
||||
if (return_value == 0) {
|
||||
// The callback has written to the output executable code range,
|
||||
// requiring an instruction cache invalidation
|
||||
Core::InvalidateInstructionCacheRange(process.GetPointerUnsafe(),
|
||||
configuration.user_rx_memory.offset,
|
||||
configuration.user_rx_memory.size);
|
||||
|
||||
// Write back to the IPC output buffer, if provided
|
||||
if (ctx.CanWriteBuffer()) {
|
||||
context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
|
||||
ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
|
||||
}
|
||||
|
||||
const OutputParameters out{
|
||||
.return_value = return_value,
|
||||
.ranges =
|
||||
{
|
||||
context.GetHeap<CodeRange>(c0_out_ptr),
|
||||
context.GetHeap<CodeRange>(c1_out_ptr),
|
||||
},
|
||||
};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 8};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(out);
|
||||
} else {
|
||||
LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");
|
||||
R_THROW(ResultUnknown);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
}
|
||||
};
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
void Control(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_JIT, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto command{rp.PopRaw<u64>()};
|
||||
|
||||
// Optional input/output buffers
|
||||
const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
|
||||
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
|
||||
|
||||
Result Control(Out<s32> out_return_value, InBuffer<BufferAttr_HipcMapAlias> in_data,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_data, u64 command) {
|
||||
// Function call prototype:
|
||||
// u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size,
|
||||
// u8* output_buf, size_t output_size);
|
||||
@ -108,30 +152,53 @@ public:
|
||||
|
||||
const VAddr ret_ptr{context.AddHeap(0u)};
|
||||
const VAddr configuration_ptr{context.AddHeap(configuration)};
|
||||
const VAddr input_ptr{context.AddHeap(in_data.data(), in_data.size())};
|
||||
const VAddr output_ptr{context.AddHeap(out_data.data(), out_data.size())};
|
||||
const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
|
||||
const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
|
||||
|
||||
const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr,
|
||||
command, input_ptr, in_data.size(), output_ptr,
|
||||
out_data.size())};
|
||||
command, input_ptr, input_buffer.size(),
|
||||
output_ptr, output_buffer.size())};
|
||||
|
||||
*out_return_value = context.GetHeap<s32>(ret_ptr);
|
||||
context.GetHeap(output_ptr, out_data.data(), out_data.size());
|
||||
const s32 return_value{context.GetHeap<s32>(ret_ptr)};
|
||||
|
||||
if (wrapper_value == 0 && *out_return_value == 0) {
|
||||
R_SUCCEED();
|
||||
if (wrapper_value == 0 && return_value == 0) {
|
||||
// Write back to the IPC output buffer, if provided
|
||||
if (ctx.CanWriteBuffer()) {
|
||||
context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
|
||||
ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(return_value);
|
||||
} else {
|
||||
LOG_WARNING(Service_JIT, "plugin Control callback failed");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
}
|
||||
|
||||
LOG_WARNING(Service_JIT, "plugin Control callback failed");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory>& tmem,
|
||||
InBuffer<BufferAttr_HipcMapAlias> nrr,
|
||||
InBuffer<BufferAttr_HipcMapAlias> nro) {
|
||||
void LoadPlugin(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_JIT, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto tmem_size{rp.PopRaw<u64>()};
|
||||
const auto tmem_handle{ctx.GetCopyHandle(0)};
|
||||
const auto nro_plugin{ctx.ReadBuffer(1)};
|
||||
|
||||
if (tmem_size == 0) {
|
||||
LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)};
|
||||
if (tmem.IsNull()) {
|
||||
LOG_ERROR(Service_JIT, "Invalid transfer memory handle!");
|
||||
R_THROW(ResultUnknown);
|
||||
LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up the configuration with the required TransferMemory address
|
||||
@ -139,7 +206,7 @@ public:
|
||||
configuration.transfer_memory.size = tmem_size;
|
||||
|
||||
// Gather up all the callbacks from the loaded plugin
|
||||
auto symbols{Core::Symbols::GetSymbols(nro, true)};
|
||||
auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)};
|
||||
const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }};
|
||||
|
||||
callbacks.rtld_fini = GetSymbol("_fini");
|
||||
@ -156,12 +223,16 @@ public:
|
||||
if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
|
||||
callbacks.OnPrepared == 0) {
|
||||
LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
|
||||
R_THROW(ResultUnknown);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.LoadNRO(nro)) {
|
||||
if (!context.LoadNRO(nro_plugin)) {
|
||||
LOG_ERROR(Service_JIT, "failed to load plugin");
|
||||
R_THROW(ResultUnknown);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
context.MapProcessMemory(configuration.sys_ro_memory.offset,
|
||||
@ -181,7 +252,9 @@ public:
|
||||
const auto version{context.CallFunction(callbacks.GetVersion)};
|
||||
if (version != 1) {
|
||||
LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
|
||||
R_THROW(ResultUnknown);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
// Function prototype:
|
||||
@ -207,19 +280,22 @@ public:
|
||||
const auto configuration_ptr{context.AddHeap(configuration)};
|
||||
context.CallFunction(callbacks.OnPrepared, configuration_ptr);
|
||||
|
||||
R_SUCCEED();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
Result GetCodeAddress(Out<u64> rx_offset, Out<u64> ro_offset) {
|
||||
void GetCodeAddress(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_JIT, "called");
|
||||
|
||||
*rx_offset = configuration.user_rx_memory.offset;
|
||||
*ro_offset = configuration.user_ro_memory.offset;
|
||||
|
||||
R_SUCCEED();
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(configuration.user_rx_memory.offset);
|
||||
rb.Push(configuration.user_ro_memory.offset);
|
||||
}
|
||||
|
||||
private:
|
||||
using Struct32 = std::array<u8, 32>;
|
||||
|
||||
struct GuestCallbacks {
|
||||
VAddr rtld_fini;
|
||||
VAddr rtld_init;
|
||||
@ -259,7 +335,7 @@ public:
|
||||
explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, C<&JITU::CreateJitEnvironment>, "CreateJitEnvironment"},
|
||||
{0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@ -267,33 +343,76 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment,
|
||||
u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess>& process,
|
||||
InCopyHandle<Kernel::KCodeMemory>& rx_mem,
|
||||
InCopyHandle<Kernel::KCodeMemory>& ro_mem) {
|
||||
void CreateJitEnvironment(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_JIT, "called");
|
||||
|
||||
struct Parameters {
|
||||
u64 rx_size;
|
||||
u64 ro_size;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
const auto process_handle{ctx.GetCopyHandle(0)};
|
||||
const auto rx_mem_handle{ctx.GetCopyHandle(1)};
|
||||
const auto ro_mem_handle{ctx.GetCopyHandle(2)};
|
||||
|
||||
if (parameters.rx_size == 0 || parameters.ro_size == 0) {
|
||||
LOG_ERROR(Service_JIT, "attempted to init with empty code regions");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
|
||||
if (process.IsNull()) {
|
||||
LOG_ERROR(Service_JIT, "process is null");
|
||||
R_THROW(ResultUnknown);
|
||||
LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)};
|
||||
if (rx_mem.IsNull()) {
|
||||
LOG_ERROR(Service_JIT, "rx_mem is null");
|
||||
R_THROW(ResultUnknown);
|
||||
LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
if (rx_mem.IsNull()) {
|
||||
LOG_ERROR(Service_JIT, "ro_mem is null");
|
||||
R_THROW(ResultUnknown);
|
||||
|
||||
auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)};
|
||||
if (ro_mem.IsNull()) {
|
||||
LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
CodeMemory rx, ro;
|
||||
Result res;
|
||||
|
||||
R_TRY(rx.Initialize(*process, *rx_mem, rx_size, Kernel::Svc::MemoryPermission::ReadExecute,
|
||||
generate_random));
|
||||
R_TRY(ro.Initialize(*process, *ro_mem, ro_size, Kernel::Svc::MemoryPermission::Read,
|
||||
generate_random));
|
||||
res = rx.Initialize(*process, *rx_mem, parameters.rx_size,
|
||||
Kernel::Svc::MemoryPermission::ReadExecute, generate_random);
|
||||
if (R_FAILED(res)) {
|
||||
LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
return;
|
||||
}
|
||||
|
||||
*out_jit_environment = std::make_shared<IJitEnvironment>(system, std::move(process),
|
||||
std::move(rx), std::move(ro));
|
||||
R_SUCCEED();
|
||||
res = ro.Initialize(*process, *ro_mem, parameters.ro_size,
|
||||
Kernel::Svc::MemoryPermission::Read, generate_random);
|
||||
if (R_FAILED(res)) {
|
||||
LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IJitEnvironment>(system, std::move(process), std::move(rx),
|
||||
std::move(ro));
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -112,7 +112,6 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||
|
||||
void Container::CloseSession(SessionId session_id) {
|
||||
std::scoped_lock lk(impl->session_guard);
|
||||
impl->file.UnmapAllHandles(session_id);
|
||||
auto& session = impl->sessions[session_id.id];
|
||||
auto& smmu = impl->host1x.MemoryManager();
|
||||
if (session.has_preallocated_area) {
|
||||
|
@ -326,17 +326,4 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
|
||||
return freeInfo;
|
||||
}
|
||||
|
||||
void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
|
||||
auto handles_copy = [&] {
|
||||
std::scoped_lock lk{handles_lock};
|
||||
return handles;
|
||||
}();
|
||||
|
||||
for (auto& [id, handle] : handles_copy) {
|
||||
if (handle->session_id.id == session_id.id) {
|
||||
FreeHandle(id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::NvCore
|
||||
|
@ -152,8 +152,6 @@ public:
|
||||
*/
|
||||
std::optional<FreeInfo> FreeHandle(Handle::Id handle, bool internal_session);
|
||||
|
||||
void UnmapAllHandles(NvCore::SessionId session_id);
|
||||
|
||||
private:
|
||||
std::list<std::shared_ptr<Handle>> unmap_queue{};
|
||||
std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue`
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
@ -30,7 +29,7 @@ void NVDRV::Open(HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
const auto& buffer = ctx.ReadBuffer();
|
||||
const std::string device_name(Common::StringFromBuffer(buffer));
|
||||
const std::string device_name(buffer.begin(), buffer.end());
|
||||
|
||||
if (device_name == "/dev/nvhost-prof-gpu") {
|
||||
rb.Push<DeviceFD>(0);
|
||||
|
@ -6,13 +6,13 @@
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/ro/ro.h"
|
||||
#include "core/hle/service/ro/ro_nro_utils.h"
|
||||
#include "core/hle/service/ro/ro_results.h"
|
||||
#include "core/hle/service/ro/ro_types.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::RO {
|
||||
|
||||
@ -500,65 +500,46 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class RoInterface : public ServiceFramework<RoInterface> {
|
||||
class RoInterface {
|
||||
public:
|
||||
explicit RoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro,
|
||||
NrrKind nrr_kind)
|
||||
: ServiceFramework{system_, name_}, m_ro(ro), m_context_id(InvalidContextId),
|
||||
m_nrr_kind(nrr_kind) {
|
||||
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, C<&RoInterface::MapManualLoadModuleMemory>, "MapManualLoadModuleMemory"},
|
||||
{1, C<&RoInterface::UnmapManualLoadModuleMemory>, "UnmapManualLoadModuleMemory"},
|
||||
{2, C<&RoInterface::RegisterModuleInfo>, "RegisterModuleInfo"},
|
||||
{3, C<&RoInterface::UnregisterModuleInfo>, "UnregisterModuleInfo"},
|
||||
{4, C<&RoInterface::RegisterProcessHandle>, "RegisterProcessHandle"},
|
||||
{10, C<&RoInterface::RegisterProcessModuleInfo>, "RegisterProcessModuleInfo"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
explicit RoInterface(std::shared_ptr<RoContext> ro, NrrKind nrr_kind)
|
||||
: m_ro(ro), m_context_id(InvalidContextId), m_nrr_kind(nrr_kind) {}
|
||||
~RoInterface() {
|
||||
m_ro->UnregisterProcess(m_context_id);
|
||||
}
|
||||
|
||||
Result MapManualLoadModuleMemory(Out<u64> out_load_address, ClientProcessId client_pid,
|
||||
u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
|
||||
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
|
||||
R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address,
|
||||
Result MapManualLoadModuleMemory(u64* out_load_address, u64 client_pid, u64 nro_address,
|
||||
u64 nro_size, u64 bss_address, u64 bss_size) {
|
||||
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
|
||||
R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address, m_context_id, nro_address,
|
||||
nro_size, bss_address, bss_size));
|
||||
}
|
||||
|
||||
Result UnmapManualLoadModuleMemory(ClientProcessId client_pid, u64 nro_address) {
|
||||
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
|
||||
Result UnmapManualLoadModuleMemory(u64 client_pid, u64 nro_address) {
|
||||
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
|
||||
R_RETURN(m_ro->UnmapManualLoadModuleMemory(m_context_id, nro_address));
|
||||
}
|
||||
|
||||
Result RegisterModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size) {
|
||||
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
|
||||
Result RegisterModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size) {
|
||||
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
|
||||
R_RETURN(
|
||||
m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, NrrKind::User, true));
|
||||
}
|
||||
|
||||
Result UnregisterModuleInfo(ClientProcessId client_pid, u64 nrr_address) {
|
||||
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
|
||||
Result UnregisterModuleInfo(u64 client_pid, u64 nrr_address) {
|
||||
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
|
||||
R_RETURN(m_ro->UnregisterModuleInfo(m_context_id, nrr_address));
|
||||
}
|
||||
|
||||
Result RegisterProcessHandle(ClientProcessId client_pid,
|
||||
InCopyHandle<Kernel::KProcess>& process) {
|
||||
Result RegisterProcessHandle(u64 client_pid, Kernel::KProcess* process) {
|
||||
// Register the process.
|
||||
R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.GetPointerUnsafe(),
|
||||
*client_pid));
|
||||
R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process, client_pid));
|
||||
}
|
||||
|
||||
Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size,
|
||||
InCopyHandle<Kernel::KProcess>& process) {
|
||||
Result RegisterProcessModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size,
|
||||
Kernel::KProcess* process) {
|
||||
// Validate the process.
|
||||
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
|
||||
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
|
||||
|
||||
// Register the module.
|
||||
R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind,
|
||||
@ -571,6 +552,137 @@ private:
|
||||
NrrKind m_nrr_kind{};
|
||||
};
|
||||
|
||||
class IRoInterface : public ServiceFramework<IRoInterface> {
|
||||
public:
|
||||
explicit IRoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro,
|
||||
NrrKind nrr_kind)
|
||||
: ServiceFramework{system_, name_}, interface {
|
||||
ro, nrr_kind
|
||||
} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IRoInterface::MapManualLoadModuleMemory, "MapManualLoadModuleMemory"},
|
||||
{1, &IRoInterface::UnmapManualLoadModuleMemory, "UnmapManualLoadModuleMemory"},
|
||||
{2, &IRoInterface::RegisterModuleInfo, "RegisterModuleInfo"},
|
||||
{3, &IRoInterface::UnregisterModuleInfo, "UnregisterModuleInfo"},
|
||||
{4, &IRoInterface::RegisterProcessHandle, "RegisterProcessHandle"},
|
||||
{10, &IRoInterface::RegisterProcessModuleInfo, "RegisterProcessModuleInfo"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void MapManualLoadModuleMemory(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LDR, "(called)");
|
||||
|
||||
struct InputParameters {
|
||||
u64 client_pid;
|
||||
u64 nro_address;
|
||||
u64 nro_size;
|
||||
u64 bss_address;
|
||||
u64 bss_size;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<InputParameters>();
|
||||
|
||||
u64 load_address = 0;
|
||||
auto result = interface.MapManualLoadModuleMemory(&load_address, ctx.GetPID(),
|
||||
params.nro_address, params.nro_size,
|
||||
params.bss_address, params.bss_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(result);
|
||||
rb.Push(load_address);
|
||||
}
|
||||
|
||||
void UnmapManualLoadModuleMemory(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LDR, "(called)");
|
||||
|
||||
struct InputParameters {
|
||||
u64 client_pid;
|
||||
u64 nro_address;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<InputParameters>();
|
||||
auto result = interface.UnmapManualLoadModuleMemory(ctx.GetPID(), params.nro_address);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void RegisterModuleInfo(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LDR, "(called)");
|
||||
|
||||
struct InputParameters {
|
||||
u64 client_pid;
|
||||
u64 nrr_address;
|
||||
u64 nrr_size;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<InputParameters>();
|
||||
auto result =
|
||||
interface.RegisterModuleInfo(ctx.GetPID(), params.nrr_address, params.nrr_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void UnregisterModuleInfo(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LDR, "(called)");
|
||||
|
||||
struct InputParameters {
|
||||
u64 client_pid;
|
||||
u64 nrr_address;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<InputParameters>();
|
||||
auto result = interface.UnregisterModuleInfo(ctx.GetPID(), params.nrr_address);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void RegisterProcessHandle(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LDR, "(called)");
|
||||
|
||||
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
|
||||
auto client_pid = ctx.GetPID();
|
||||
auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void RegisterProcessModuleInfo(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LDR, "(called)");
|
||||
|
||||
struct InputParameters {
|
||||
u64 client_pid;
|
||||
u64 nrr_address;
|
||||
u64 nrr_size;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<InputParameters>();
|
||||
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
|
||||
|
||||
auto client_pid = ctx.GetPID();
|
||||
auto result = interface.RegisterProcessModuleInfo(
|
||||
client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
RoInterface interface;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
@ -579,11 +691,11 @@ void LoopProcess(Core::System& system) {
|
||||
auto ro = std::make_shared<RoContext>();
|
||||
|
||||
const auto RoInterfaceFactoryForUser = [&, ro] {
|
||||
return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User);
|
||||
return std::make_shared<IRoInterface>(system, "ldr:ro", ro, NrrKind::User);
|
||||
};
|
||||
|
||||
const auto RoInterfaceFactoryForJitPlugin = [&, ro] {
|
||||
return std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin);
|
||||
return std::make_shared<IRoInterface>(system, "ro:1", ro, NrrKind::JitPlugin);
|
||||
};
|
||||
|
||||
server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser));
|
||||
|
@ -206,22 +206,6 @@ protected:
|
||||
RegisterHandlersBaseTipc(functions, n);
|
||||
}
|
||||
|
||||
protected:
|
||||
template <bool Domain, auto F>
|
||||
void CmifReplyWrap(HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* Wraps the template pointer-to-member function for use in a domain session.
|
||||
*/
|
||||
template <auto F>
|
||||
static constexpr HandlerFnP<Self> D = &Self::template CmifReplyWrap<true, F>;
|
||||
|
||||
/**
|
||||
* Wraps the template pointer-to-member function for use in a non-domain session.
|
||||
*/
|
||||
template <auto F>
|
||||
static constexpr HandlerFnP<Self> C = &Self::template CmifReplyWrap<false, F>;
|
||||
|
||||
private:
|
||||
/**
|
||||
* This function is used to allow invocation of pointers to handlers stored in the base class
|
||||
|
@ -709,12 +709,12 @@ void ISystemSettingsServer::GetSettingsItemValueSize(HLERequestContext& ctx) {
|
||||
// The category of the setting. This corresponds to the top-level keys of
|
||||
// system_settings.ini.
|
||||
const auto setting_category_buf{ctx.ReadBuffer(0)};
|
||||
const std::string setting_category{Common::StringFromBuffer(setting_category_buf)};
|
||||
const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()};
|
||||
|
||||
// The name of the setting. This corresponds to the second-level keys of
|
||||
// system_settings.ini.
|
||||
const auto setting_name_buf{ctx.ReadBuffer(1)};
|
||||
const std::string setting_name{Common::StringFromBuffer(setting_name_buf)};
|
||||
const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()};
|
||||
|
||||
auto settings{GetSettings()};
|
||||
u64 response_size{0};
|
||||
@ -732,12 +732,12 @@ void ISystemSettingsServer::GetSettingsItemValue(HLERequestContext& ctx) {
|
||||
// The category of the setting. This corresponds to the top-level keys of
|
||||
// system_settings.ini.
|
||||
const auto setting_category_buf{ctx.ReadBuffer(0)};
|
||||
const std::string setting_category{Common::StringFromBuffer(setting_category_buf)};
|
||||
const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()};
|
||||
|
||||
// The name of the setting. This corresponds to the second-level keys of
|
||||
// system_settings.ini.
|
||||
const auto setting_name_buf{ctx.ReadBuffer(1)};
|
||||
const std::string setting_name{Common::StringFromBuffer(setting_name_buf)};
|
||||
const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()};
|
||||
|
||||
std::vector<u8> value;
|
||||
auto response = GetSettingsItemValue(value, setting_category, setting_name);
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
@ -695,7 +694,9 @@ private:
|
||||
void OpenLayer(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
const std::string display_name(Common::StringFromBuffer(name_buf));
|
||||
const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
const std::string display_name(name_buf.begin(), end);
|
||||
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
const u64 aruid = rp.Pop<u64>();
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
@ -110,13 +109,6 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
|
||||
return result;
|
||||
}
|
||||
|
||||
if (nsp->IsExtractedType()) {
|
||||
system.GetFileSystemController().RegisterProcess(
|
||||
process.GetProcessId(), {},
|
||||
std::make_shared<FileSys::RomFSFactory>(*this, system.GetContentProvider(),
|
||||
system.GetFileSystemController()));
|
||||
}
|
||||
|
||||
FileSys::VirtualFile update_raw;
|
||||
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
|
||||
system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(),
|
||||
|
@ -47,14 +47,14 @@ inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_contro
|
||||
|
||||
/**
|
||||
* \brief Removes all DLC for a game
|
||||
* \param system Reference to the system instance
|
||||
* \param system Raw pointer to the system instance
|
||||
* \param program_id Program ID for the game that will have all of its DLC removed
|
||||
* \return Number of DLC removed
|
||||
*/
|
||||
inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) {
|
||||
inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) {
|
||||
size_t count{};
|
||||
const auto& fs_controller = system.GetFileSystemController();
|
||||
const auto dlc_entries = system.GetContentProvider().ListEntriesFilter(
|
||||
const auto& fs_controller = system->GetFileSystemController();
|
||||
const auto dlc_entries = system->GetContentProvider().ListEntriesFilter(
|
||||
FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||
std::vector<u64> program_dlc_entries;
|
||||
|
||||
@ -124,15 +124,15 @@ inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_contro
|
||||
|
||||
/**
|
||||
* \brief Installs an NSP
|
||||
* \param system Reference to the system instance
|
||||
* \param vfs Reference to the VfsFilesystem instance in Core::System
|
||||
* \param system Raw pointer to the system instance
|
||||
* \param vfs Raw pointer to the VfsFilesystem instance in Core::System
|
||||
* \param filename Path to the NSP file
|
||||
* \param callback Callback to report the progress of the installation. The first size_t
|
||||
* parameter is the total size of the virtual file and the second is the current progress. If you
|
||||
* return true to the callback, it will cancel the installation as soon as possible.
|
||||
* \return [InstallResult] representing how the installation finished
|
||||
*/
|
||||
inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vfs,
|
||||
inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vfs,
|
||||
const std::string& filename,
|
||||
const std::function<bool(size_t, size_t)>& callback) {
|
||||
const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
||||
@ -159,7 +159,7 @@ inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vf
|
||||
};
|
||||
|
||||
std::shared_ptr<FileSys::NSP> nsp;
|
||||
FileSys::VirtualFile file = vfs.OpenFile(filename, FileSys::Mode::Read);
|
||||
FileSys::VirtualFile file = vfs->OpenFile(filename, FileSys::Mode::Read);
|
||||
if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) {
|
||||
nsp = std::make_shared<FileSys::NSP>(file);
|
||||
if (nsp->IsExtractedType()) {
|
||||
@ -173,7 +173,7 @@ inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vf
|
||||
return InstallResult::Failure;
|
||||
}
|
||||
const auto res =
|
||||
system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy);
|
||||
system->GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy);
|
||||
switch (res) {
|
||||
case FileSys::InstallResult::Success:
|
||||
return InstallResult::Success;
|
||||
@ -188,17 +188,17 @@ inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vf
|
||||
|
||||
/**
|
||||
* \brief Installs an NCA
|
||||
* \param vfs Reference to the VfsFilesystem instance in Core::System
|
||||
* \param vfs Raw pointer to the VfsFilesystem instance in Core::System
|
||||
* \param filename Path to the NCA file
|
||||
* \param registered_cache Reference to the registered cache that the NCA will be installed to
|
||||
* \param registered_cache Raw pointer to the registered cache that the NCA will be installed to
|
||||
* \param title_type Type of NCA package to install
|
||||
* \param callback Callback to report the progress of the installation. The first size_t
|
||||
* parameter is the total size of the virtual file and the second is the current progress. If you
|
||||
* return true to the callback, it will cancel the installation as soon as possible.
|
||||
* \return [InstallResult] representing how the installation finished
|
||||
*/
|
||||
inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string& filename,
|
||||
FileSys::RegisteredCache& registered_cache,
|
||||
inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& filename,
|
||||
FileSys::RegisteredCache* registered_cache,
|
||||
const FileSys::TitleType title_type,
|
||||
const std::function<bool(size_t, size_t)>& callback) {
|
||||
const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
||||
@ -224,7 +224,7 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string&
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto nca = std::make_shared<FileSys::NCA>(vfs.OpenFile(filename, FileSys::Mode::Read));
|
||||
const auto nca = std::make_shared<FileSys::NCA>(vfs->OpenFile(filename, FileSys::Mode::Read));
|
||||
const auto id = nca->GetStatus();
|
||||
|
||||
// Game updates necessary are missing base RomFS
|
||||
@ -233,7 +233,7 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string&
|
||||
return InstallResult::Failure;
|
||||
}
|
||||
|
||||
const auto res = registered_cache.InstallEntry(*nca, title_type, true, copy);
|
||||
const auto res = registered_cache->InstallEntry(*nca, title_type, true, copy);
|
||||
if (res == FileSys::InstallResult::Success) {
|
||||
return InstallResult::Success;
|
||||
} else if (res == FileSys::InstallResult::OverwriteExisting) {
|
||||
@ -245,19 +245,19 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string&
|
||||
|
||||
/**
|
||||
* \brief Verifies the installed contents for a given ManualContentProvider
|
||||
* \param system Reference to the system instance
|
||||
* \param provider Reference to the content provider that's tracking indexed games
|
||||
* \param system Raw pointer to the system instance
|
||||
* \param provider Raw pointer to the content provider that's tracking indexed games
|
||||
* \param callback Callback to report the progress of the installation. The first size_t
|
||||
* parameter is the total size of the installed contents and the second is the current progress. If
|
||||
* you return true to the callback, it will cancel the installation as soon as possible.
|
||||
* \return A list of entries that failed to install. Returns an empty vector if successful.
|
||||
*/
|
||||
inline std::vector<std::string> VerifyInstalledContents(
|
||||
Core::System& system, FileSys::ManualContentProvider& provider,
|
||||
Core::System* system, FileSys::ManualContentProvider* provider,
|
||||
const std::function<bool(size_t, size_t)>& callback) {
|
||||
// Get content registries.
|
||||
auto bis_contents = system.GetFileSystemController().GetSystemNANDContents();
|
||||
auto user_contents = system.GetFileSystemController().GetUserNANDContents();
|
||||
auto bis_contents = system->GetFileSystemController().GetSystemNANDContents();
|
||||
auto user_contents = system->GetFileSystemController().GetUserNANDContents();
|
||||
|
||||
std::vector<FileSys::RegisteredCache*> content_providers;
|
||||
if (bis_contents) {
|
||||
@ -309,11 +309,11 @@ inline std::vector<std::string> VerifyInstalledContents(
|
||||
const auto title_id = nca.GetTitleId();
|
||||
std::string title_name = "unknown";
|
||||
|
||||
const auto control = provider.GetEntry(FileSys::GetBaseTitleID(title_id),
|
||||
FileSys::ContentRecordType::Control);
|
||||
const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id),
|
||||
FileSys::ContentRecordType::Control);
|
||||
if (control && control->GetStatus() == Loader::ResultStatus::Success) {
|
||||
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||
provider};
|
||||
const FileSys::PatchManager pm{title_id, system->GetFileSystemController(),
|
||||
*provider};
|
||||
const auto [nacp, logo] = pm.ParseControlNCA(*control);
|
||||
if (nacp) {
|
||||
title_name = nacp->GetApplicationName();
|
||||
@ -335,7 +335,7 @@ inline std::vector<std::string> VerifyInstalledContents(
|
||||
|
||||
/**
|
||||
* \brief Verifies the contents of a given game
|
||||
* \param system Reference to the system instance
|
||||
* \param system Raw pointer to the system instance
|
||||
* \param game_path Patch to the game file
|
||||
* \param callback Callback to report the progress of the installation. The first size_t
|
||||
* parameter is the total size of the installed contents and the second is the current progress. If
|
||||
@ -343,10 +343,10 @@ inline std::vector<std::string> VerifyInstalledContents(
|
||||
* \return GameVerificationResult representing how the verification process finished
|
||||
*/
|
||||
inline GameVerificationResult VerifyGameContents(
|
||||
Core::System& system, const std::string& game_path,
|
||||
Core::System* system, const std::string& game_path,
|
||||
const std::function<bool(size_t, size_t)>& callback) {
|
||||
const auto loader =
|
||||
Loader::GetLoader(system, system.GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read));
|
||||
const auto loader = Loader::GetLoader(
|
||||
*system, system->GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read));
|
||||
if (loader == nullptr) {
|
||||
return GameVerificationResult::NotImplemented;
|
||||
}
|
||||
@ -368,11 +368,4 @@ inline GameVerificationResult VerifyGameContents(
|
||||
return GameVerificationResult::Success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the keys required for decrypting firmware and games are available
|
||||
*/
|
||||
inline bool AreKeysPresent() {
|
||||
return !Core::Crypto::KeyManager::Instance().BaseDeriveNecessary();
|
||||
}
|
||||
|
||||
} // namespace ContentManager
|
||||
|
@ -110,11 +110,7 @@ void EmulatedController::ReloadFromSettings() {
|
||||
original_npad_type = npad_type;
|
||||
}
|
||||
|
||||
// Disable special features before disconnecting
|
||||
if (controller.right_polling_mode != Common::Input::PollingMode::Active) {
|
||||
SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Active);
|
||||
}
|
||||
|
||||
SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Active);
|
||||
Disconnect();
|
||||
if (player.connected) {
|
||||
Connect();
|
||||
@ -1245,12 +1241,7 @@ bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationV
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip duplicated vibrations
|
||||
if (last_vibration_value[index] == vibration) {
|
||||
return Settings::values.vibration_enabled.GetValue();
|
||||
}
|
||||
|
||||
last_vibration_value[index] = vibration;
|
||||
last_vibration_value = vibration;
|
||||
|
||||
if (!Settings::values.vibration_enabled) {
|
||||
return false;
|
||||
@ -1281,10 +1272,7 @@ bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationV
|
||||
}
|
||||
|
||||
VibrationValue EmulatedController::GetActualVibrationValue(DeviceIndex device_index) const {
|
||||
if (device_index >= DeviceIndex::MaxDeviceIndex) {
|
||||
return Core::HID::DEFAULT_VIBRATION_VALUE;
|
||||
}
|
||||
return last_vibration_value[static_cast<std::size_t>(device_index)];
|
||||
return last_vibration_value;
|
||||
}
|
||||
|
||||
bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
|
||||
|
@ -581,8 +581,7 @@ private:
|
||||
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
|
||||
u32 turbo_button_state{0};
|
||||
std::size_t nfc_handles{0};
|
||||
std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE,
|
||||
DEFAULT_VIBRATION_VALUE};
|
||||
VibrationValue last_vibration_value{DEFAULT_VIBRATION_VALUE};
|
||||
|
||||
// Temporary values to avoid doing changes while the controller is in configuring mode
|
||||
NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
|
||||
|
@ -639,15 +639,6 @@ struct VibrationValue {
|
||||
f32 low_frequency{};
|
||||
f32 high_amplitude{};
|
||||
f32 high_frequency{};
|
||||
bool operator==(const VibrationValue& b) {
|
||||
if (low_amplitude != b.low_amplitude || high_amplitude != b.high_amplitude) {
|
||||
return false;
|
||||
}
|
||||
if (low_frequency != b.low_amplitude || high_frequency != b.high_frequency) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
|
||||
|
||||
|
@ -39,10 +39,6 @@ void SortPhysicalDevicesPerVendor(std::vector<VkPhysicalDevice>& devices,
|
||||
}
|
||||
}
|
||||
|
||||
bool IsMicrosoftDozen(const char* device_name) {
|
||||
return std::strstr(device_name, "Microsoft") != nullptr;
|
||||
}
|
||||
|
||||
void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceDispatch& dld) {
|
||||
// Sort by name, this will set a base and make GPUs with higher numbers appear first
|
||||
// (e.g. GTX 1650 will intentionally be listed before a GTX 1080).
|
||||
@ -56,12 +52,6 @@ void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceD
|
||||
});
|
||||
// Prefer Nvidia over AMD, AMD over Intel, Intel over the rest.
|
||||
SortPhysicalDevicesPerVendor(devices, dld, {0x10DE, 0x1002, 0x8086});
|
||||
// Demote Microsoft's Dozen devices to the bottom.
|
||||
SortPhysicalDevices(
|
||||
devices, dld,
|
||||
[](const VkPhysicalDeviceProperties& lhs, const VkPhysicalDeviceProperties& rhs) {
|
||||
return IsMicrosoftDozen(rhs.deviceName) && !IsMicrosoftDozen(lhs.deviceName);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -423,7 +423,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
|
||||
RemoveCachedContents();
|
||||
|
||||
// Gen keys if necessary
|
||||
OnCheckFirmwareDecryption();
|
||||
OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
|
||||
|
||||
game_list->LoadCompatibilityList();
|
||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||
@ -1574,6 +1574,8 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig);
|
||||
|
||||
// Tools
|
||||
connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this,
|
||||
ReinitializeKeyBehavior::Warning));
|
||||
connect_menu(ui->action_Load_Album, &GMainWindow::OnAlbum);
|
||||
connect_menu(ui->action_Load_Cabinet_Nickname_Owner,
|
||||
[this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); });
|
||||
@ -2499,7 +2501,7 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
|
||||
}
|
||||
|
||||
void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
|
||||
const size_t count = ContentManager::RemoveAllDLC(*system, program_id);
|
||||
const size_t count = ContentManager::RemoveAllDLC(system.get(), program_id);
|
||||
if (count == 0) {
|
||||
QMessageBox::warning(this, GetGameListErrorRemoving(type),
|
||||
tr("There are no DLC installed for this title."));
|
||||
@ -2796,7 +2798,8 @@ void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
|
||||
return progress.wasCanceled();
|
||||
};
|
||||
|
||||
const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback);
|
||||
const auto result =
|
||||
ContentManager::VerifyGameContents(system.get(), game_path, QtProgressCallback);
|
||||
progress.close();
|
||||
switch (result) {
|
||||
case ContentManager::GameVerificationResult::Success:
|
||||
@ -3265,7 +3268,7 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||
return false;
|
||||
};
|
||||
future = QtConcurrent::run([this, &file, progress_callback] {
|
||||
return ContentManager::InstallNSP(*system, *vfs, file.toStdString(),
|
||||
return ContentManager::InstallNSP(system.get(), vfs.get(), file.toStdString(),
|
||||
progress_callback);
|
||||
});
|
||||
|
||||
@ -3368,7 +3371,7 @@ ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return ContentManager::InstallNCA(*vfs, filename.toStdString(), *registered_cache,
|
||||
return ContentManager::InstallNCA(vfs.get(), filename.toStdString(), registered_cache,
|
||||
static_cast<FileSys::TitleType>(index), progress_callback);
|
||||
}
|
||||
|
||||
@ -4118,7 +4121,7 @@ void GMainWindow::OnVerifyInstalledContents() {
|
||||
};
|
||||
|
||||
const std::vector<std::string> result =
|
||||
ContentManager::VerifyInstalledContents(*system, *provider, QtProgressCallback);
|
||||
ContentManager::VerifyInstalledContents(system.get(), provider.get(), QtProgressCallback);
|
||||
progress.close();
|
||||
|
||||
if (result.empty()) {
|
||||
@ -4548,20 +4551,122 @@ void GMainWindow::OnMouseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnCheckFirmwareDecryption() {
|
||||
void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
|
||||
if (behavior == ReinitializeKeyBehavior::Warning) {
|
||||
const auto res = QMessageBox::information(
|
||||
this, tr("Confirm Key Rederivation"),
|
||||
tr("You are about to force rederive all of your keys. \nIf you do not know what "
|
||||
"this "
|
||||
"means or what you are doing, \nthis is a potentially destructive action. "
|
||||
"\nPlease "
|
||||
"make sure this is what you want \nand optionally make backups.\n\nThis will "
|
||||
"delete "
|
||||
"your autogenerated key files and re-run the key derivation module."),
|
||||
QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
|
||||
|
||||
if (res == QMessageBox::Cancel)
|
||||
return;
|
||||
|
||||
const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
|
||||
|
||||
Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated");
|
||||
Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated");
|
||||
Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated");
|
||||
}
|
||||
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
bool all_keys_present{true};
|
||||
|
||||
if (keys.BaseDeriveNecessary()) {
|
||||
Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory("", FileSys::Mode::Read)};
|
||||
|
||||
const auto function = [this, &keys, &pdm] {
|
||||
keys.PopulateFromPartitionData(pdm);
|
||||
|
||||
system->GetFileSystemController().CreateFactories(*vfs);
|
||||
keys.DeriveETicket(pdm, system->GetContentProvider());
|
||||
};
|
||||
|
||||
QString errors;
|
||||
if (!pdm.HasFuses()) {
|
||||
errors += tr("Missing fuses");
|
||||
}
|
||||
if (!pdm.HasBoot0()) {
|
||||
errors += tr(" - Missing BOOT0");
|
||||
}
|
||||
if (!pdm.HasPackage2()) {
|
||||
errors += tr(" - Missing BCPKG2-1-Normal-Main");
|
||||
}
|
||||
if (!pdm.HasProdInfo()) {
|
||||
errors += tr(" - Missing PRODINFO");
|
||||
}
|
||||
if (!errors.isEmpty()) {
|
||||
all_keys_present = false;
|
||||
QMessageBox::warning(
|
||||
this, tr("Derivation Components Missing"),
|
||||
tr("Encryption keys are missing. "
|
||||
"<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
|
||||
"quickstart guide</a> to get all your keys, firmware and "
|
||||
"games.<br><br><small>(%1)</small>")
|
||||
.arg(errors));
|
||||
}
|
||||
|
||||
QProgressDialog prog(this);
|
||||
prog.setRange(0, 0);
|
||||
prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your "
|
||||
"system's performance."));
|
||||
prog.setWindowTitle(tr("Deriving Keys"));
|
||||
|
||||
prog.show();
|
||||
|
||||
auto future = QtConcurrent::run(function);
|
||||
while (!future.isFinished()) {
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
prog.close();
|
||||
}
|
||||
|
||||
system->GetFileSystemController().CreateFactories(*vfs);
|
||||
if (!ContentManager::AreKeysPresent()) {
|
||||
|
||||
if (all_keys_present && !this->CheckSystemArchiveDecryption()) {
|
||||
LOG_WARNING(Frontend, "Mii model decryption failed");
|
||||
QMessageBox::warning(
|
||||
this, tr("Derivation Components Missing"),
|
||||
tr("Encryption keys are missing. "
|
||||
this, tr("System Archive Decryption Failed"),
|
||||
tr("Encryption keys failed to decrypt firmware. "
|
||||
"<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
|
||||
"quickstart guide</a> to get all your keys, firmware and "
|
||||
"games."));
|
||||
}
|
||||
|
||||
SetFirmwareVersion();
|
||||
|
||||
if (behavior == ReinitializeKeyBehavior::Warning) {
|
||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||
}
|
||||
|
||||
UpdateMenuState();
|
||||
}
|
||||
|
||||
bool GMainWindow::CheckSystemArchiveDecryption() {
|
||||
constexpr u64 MiiModelId = 0x0100000000000802;
|
||||
|
||||
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
|
||||
if (!bis_system) {
|
||||
// Not having system BIS files is not an error.
|
||||
return true;
|
||||
}
|
||||
|
||||
auto mii_nca = bis_system->GetEntry(MiiModelId, FileSys::ContentRecordType::Data);
|
||||
if (!mii_nca) {
|
||||
// Not having the Mii model is not an error.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return whether we are able to decrypt the RomFS of the Mii model.
|
||||
return mii_nca->GetRomFS().get() != nullptr;
|
||||
}
|
||||
|
||||
bool GMainWindow::CheckFirmwarePresence() {
|
||||
constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);
|
||||
|
||||
|
@ -125,6 +125,11 @@ enum class EmulatedDirectoryTarget {
|
||||
SDMC,
|
||||
};
|
||||
|
||||
enum class ReinitializeKeyBehavior {
|
||||
NoWarning,
|
||||
Warning,
|
||||
};
|
||||
|
||||
namespace VkDeviceInfo {
|
||||
class Record;
|
||||
}
|
||||
@ -395,7 +400,7 @@ private slots:
|
||||
void OnMiiEdit();
|
||||
void OnOpenControllerMenu();
|
||||
void OnCaptureScreenshot();
|
||||
void OnCheckFirmwareDecryption();
|
||||
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
|
||||
void OnLanguageChanged(const QString& locale);
|
||||
void OnMouseActivity();
|
||||
bool OnShutdownBegin();
|
||||
@ -436,6 +441,7 @@ private:
|
||||
void LoadTranslation();
|
||||
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
|
||||
bool CheckDarkMode();
|
||||
bool CheckSystemArchiveDecryption();
|
||||
bool CheckFirmwarePresence();
|
||||
void SetFirmwareVersion();
|
||||
void ConfigureFilesystemProvider(const std::string& filepath);
|
||||
|
@ -224,6 +224,11 @@
|
||||
<string>&Stop</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Rederive">
|
||||
<property name="text">
|
||||
<string>&Reinitialize keys...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Verify_installed_contents">
|
||||
<property name="text">
|
||||
<string>&Verify Installed Contents</string>
|
||||
|
Reference in New Issue
Block a user