mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-30 10:26:14 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9985c74cea | |||
| eec3a4168f | |||
| d39f9126f7 | |||
| 4563af7527 | |||
| 64059d5d64 | |||
| d5f064380d | |||
| 06a2420ae7 | |||
| 71d43234a1 | |||
| 0c389249a0 | |||
| 5bcbf2cf91 | |||
| ed351fbd41 | |||
| a57c284ee9 | |||
| 3cd8aea0ae | |||
| 45074ba1ae | |||
| 764cbf747c | |||
| 5ec2d443c0 | |||
| c1eece6218 | |||
| 5201e6bea5 | |||
| 81162d376f | |||
| e80ec69795 | |||
| 07f8605957 | |||
| e3fc6fc6ea | |||
| 1d377180e7 | |||
| ecb1ac96e0 | |||
| daded9e507 | |||
| 80a6576c7a | |||
| 6d03182e33 | |||
| bc47304607 | |||
| f5e488db05 | |||
| c9df8409df | |||
| 7cf84f0936 | |||
| 6e5af6a3c1 | |||
| c2fbcd816a |
@@ -69,6 +69,26 @@ if (YUZU_STATIC_ROOM)
|
||||
set(fmt_FORCE_BUNDLED ON)
|
||||
endif()
|
||||
|
||||
# my unity/jumbo build
|
||||
option(ENABLE_UNITY_BUILD "Enable Unity/Jumbo build" OFF)
|
||||
|
||||
# 0 compiles all files in
|
||||
# not ideal, but if you're going gung-ho with a unity build, expect failure
|
||||
# MSVC physically can't compile that many files into one TU, so we limit it to 100.
|
||||
if (MSVC)
|
||||
set(_unity_default 100)
|
||||
else()
|
||||
set(_unity_default 0)
|
||||
endif()
|
||||
|
||||
set(UNITY_BATCH_SIZE ${_unity_default} CACHE STRING "Unity build batch size")
|
||||
|
||||
if(MSVC AND ENABLE_UNITY_BUILD)
|
||||
message(STATUS "Unity build")
|
||||
# Unity builds need big objects for MSVC...
|
||||
add_compile_options(/bigobj)
|
||||
endif()
|
||||
|
||||
# qt stuff
|
||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
|
||||
|
||||
+2
-2
@@ -340,10 +340,10 @@
|
||||
"version": "vulkan-sdk-%NUMERIC_VERSION%"
|
||||
},
|
||||
"xbyak": {
|
||||
"hash": "e0aa0a603dd3ac1a39d82213df1e73c042831aec2d6b2fe382c899651eaae4ed7e8aeb6be9c41ea0097087c9d091961ab18f313cd6a9e10d621715ffd49bfe36",
|
||||
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
|
||||
"package": "xbyak",
|
||||
"repo": "herumi/xbyak",
|
||||
"version": "v7.40.1"
|
||||
"version": "v7.35.2"
|
||||
},
|
||||
"zlib": {
|
||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||
|
||||
Vendored
-2
@@ -509,8 +509,6 @@ QWidget#contentRichDialog QLabel#label_title_rich {
|
||||
}
|
||||
|
||||
QWidget#contentDialog QLabel#label_dialog {
|
||||
background: #2E2E2E;
|
||||
|
||||
padding: 20px 65px;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ These options control dependencies.
|
||||
- This option is subject for removal.
|
||||
- `YUZU_TESTS` (ON) Compile tests - requires Catch2
|
||||
- `ENABLE_LTO` (OFF) Enable link-time optimization
|
||||
- `ENABLE_UNITY_BUILD` (OFF) Enables "Unity/Jumbo" builds
|
||||
- Not recommended on Windows
|
||||
- UNIX may be better off appending `-flto=thin` to compiler args
|
||||
- `USE_FASTER_LINKER` (OFF) Check if a faster linker is available
|
||||
|
||||
+11
-27
@@ -43,7 +43,6 @@ This guide will walk you through adding a new boolean toggle setting to Eden's c
|
||||
Firstly add your desired toggle:
|
||||
|
||||
Example: `src/common/setting.h`
|
||||
|
||||
```cpp
|
||||
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
||||
```
|
||||
@@ -68,7 +67,6 @@ Common Categories:
|
||||
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
|
||||
|
||||
Example: `src/qt_common/config/shared_translation.cpp`
|
||||
|
||||
```cpp
|
||||
INSERT(Settings,
|
||||
your_setting_name,
|
||||
@@ -93,7 +91,6 @@ INSERT(Settings,
|
||||
Add where it should be in the settings.
|
||||
|
||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
|
||||
|
||||
```kts
|
||||
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
||||
```
|
||||
@@ -109,7 +106,6 @@ RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
||||
Add the toggle to the Kotlin (Android) UI
|
||||
|
||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
|
||||
|
||||
```kts
|
||||
put(
|
||||
SwitchSetting(
|
||||
@@ -127,7 +123,6 @@ put(
|
||||
Add your setting within the right category.
|
||||
|
||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
||||
|
||||
```kts
|
||||
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
||||
```
|
||||
@@ -142,7 +137,6 @@ add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
||||
Add your setting and description in the appropriate place.
|
||||
|
||||
Example: `src/android/app/src/main/res/values/strings.xml`
|
||||
|
||||
```xml
|
||||
<string name="your_setting_name">Your Setting Display Name</string>
|
||||
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
|
||||
@@ -156,7 +150,6 @@ Now the UI part is done find a place in the code for the toggle,
|
||||
And use it to your heart's desire!
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
const bool your_value = Settings::values.your_setting_name.GetValue();
|
||||
|
||||
@@ -203,31 +196,25 @@ Common advantages recap:
|
||||
|
||||
#### Accessing Debug Knobs (dev side)
|
||||
|
||||
Use the `Settings::GetDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
||||
Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
||||
|
||||
```cpp
|
||||
//cpp side
|
||||
#include "common/settings.h"
|
||||
|
||||
//To use it as a general purpose uint var:
|
||||
unsigned int debug_knobs = Settings::values.debug_knobs.GetValue();
|
||||
|
||||
// Check if bit 0 is set
|
||||
bool feature_enabled = Settings::GetDebugKnobAt(0);
|
||||
bool feature_enabled = Settings::getDebugKnobAt(0);
|
||||
|
||||
// Check if bit 15 is set
|
||||
bool another_feature = Settings::GetDebugKnobAt(15);
|
||||
bool another_feature = Settings::getDebugKnobAt(15);
|
||||
```
|
||||
|
||||
```kts
|
||||
//kotlin side
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
|
||||
//To use it as a general purpose uint var
|
||||
val debug_knobs: Int = UShortSetting.DEBUG_KNOBS.getInt()
|
||||
|
||||
// Check if bit x is set
|
||||
bool feature_enabled = Settings.GetDebugKnobAt(x); //x as integer from 0 to 15
|
||||
bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15
|
||||
```
|
||||
|
||||
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
|
||||
@@ -260,7 +247,6 @@ There are two main confusions when talking about knobs:
|
||||
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
|
||||
|
||||
Debug knobs are **zero-based**, which means:
|
||||
|
||||
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
|
||||
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
|
||||
|
||||
@@ -273,7 +259,6 @@ Whenever you're instructing tests or reporting results, be precise about whether
|
||||
|
||||
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
|
||||
Examples:
|
||||
|
||||
- **knobs=0**: no knobs enabled
|
||||
- **knobs=1**: knob0 enabled, others disabled
|
||||
- **knobs=2**: knob1 enabled, others disabled
|
||||
@@ -285,7 +270,6 @@ Examples:
|
||||
|
||||
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
||||
Examples:
|
||||
|
||||
- **knob0**: knob 0 enabled, others disabled
|
||||
- **knob1**: knob 1 enabled, others disabled
|
||||
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
||||
@@ -298,12 +282,12 @@ Examples:
|
||||
|
||||
```cpp
|
||||
void SomeFunction() {
|
||||
if (Settings::GetDebugKnobAt(0)) {
|
||||
if (Settings::getDebugKnobAt(0)) {
|
||||
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
||||
// Additional debug code here
|
||||
}
|
||||
|
||||
if (Settings::GetDebugKnobAt(1)) {
|
||||
|
||||
if (Settings::getDebugKnobAt(1)) {
|
||||
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
||||
// Different debug behavior
|
||||
}
|
||||
@@ -315,7 +299,7 @@ void SomeFunction() {
|
||||
```cpp
|
||||
bool UseOptimizedPath() {
|
||||
// Skip optimization if debug bit 2 is set for testing
|
||||
return !Settings::GetDebugKnobAt(2);
|
||||
return !Settings::getDebugKnobAt(2);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -324,13 +308,13 @@ bool UseOptimizedPath() {
|
||||
```cpp
|
||||
void ExperimentalFeature() {
|
||||
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
||||
|
||||
if (!Settings::GetDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
||||
|
||||
if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
||||
// Fallback to stable implementation
|
||||
StableImplementation();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Experimental implementation
|
||||
ExperimentalImplementation();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
# Enable modules to include each other's files
|
||||
include_directories(.)
|
||||
|
||||
if (ENABLE_UNITY_BUILD)
|
||||
set(CMAKE_UNITY_BUILD ON)
|
||||
set(CMAKE_UNITY_BUILD_BATCH_SIZE ${UNITY_BATCH_SIZE})
|
||||
endif()
|
||||
|
||||
# Dynarmic
|
||||
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) AND NOT YUZU_STATIC_ROOM)
|
||||
add_subdirectory(dynarmic)
|
||||
|
||||
@@ -220,7 +220,7 @@ object NativeLibrary {
|
||||
|
||||
external fun refreshThreadPolicies()
|
||||
|
||||
external fun GetDebugKnobAt(index: Int): Boolean
|
||||
external fun getDebugKnobAt(index: Int): Boolean
|
||||
|
||||
/**
|
||||
* Set the current speed limit to the configured turbo speed.
|
||||
|
||||
@@ -35,8 +35,8 @@ object Settings {
|
||||
fun getPlayerString(player: Int): String =
|
||||
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
||||
|
||||
fun GetDebugKnobAt(index: Int): Boolean {
|
||||
return org.yuzu.yuzu_emu.NativeLibrary.GetDebugKnobAt(index)
|
||||
fun getDebugKnobAt(index: Int): Boolean {
|
||||
return org.yuzu.yuzu_emu.NativeLibrary.getDebugKnobAt(index)
|
||||
}
|
||||
|
||||
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
||||
|
||||
+3
-2
@@ -11,7 +11,8 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
||||
RENDERER_SPEED_LIMIT("speed_limit"),
|
||||
RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"),
|
||||
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit")
|
||||
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit"),
|
||||
DEBUG_KNOBS("debug_knobs")
|
||||
;
|
||||
|
||||
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
|
||||
@@ -28,4 +29,4 @@ enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
||||
override fun getValueAsString(needsGlobal: Boolean): String = getShort(needsGlobal).toString()
|
||||
|
||||
override fun reset() = NativeConfig.setShort(key, defaultValue)
|
||||
}
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model
|
||||
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
|
||||
enum class UShortSetting(override val key: String) : AbstractIntSetting {
|
||||
DEBUG_KNOBS("debug_knobs")
|
||||
;
|
||||
|
||||
override fun getInt(needsGlobal: Boolean): Int =
|
||||
NativeConfig.getUnsignedShort(key, needsGlobal)
|
||||
|
||||
override fun setInt(value: Int) {
|
||||
if (NativeConfig.isPerGameConfigLoaded()) {
|
||||
global = false
|
||||
}
|
||||
NativeConfig.setUnsignedShort(key, value)
|
||||
}
|
||||
|
||||
override val defaultValue: Int by lazy { NativeConfig.getDefaultToString(key).toInt() }
|
||||
|
||||
override fun getValueAsString(needsGlobal: Boolean): String = getInt(needsGlobal).toString()
|
||||
|
||||
override fun reset() = NativeConfig.setUnsignedShort(key, defaultValue)
|
||||
}
|
||||
+1
-2
@@ -20,7 +20,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
||||
import org.yuzu.yuzu_emu.network.NetDataValidators
|
||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
@@ -1035,7 +1034,7 @@ abstract class SettingsItem(
|
||||
)
|
||||
put(
|
||||
SpinBoxSetting(
|
||||
UShortSetting.DEBUG_KNOBS,
|
||||
ShortSetting.DEBUG_KNOBS,
|
||||
titleId = R.string.debug_knobs,
|
||||
descriptionId = R.string.debug_knobs_description,
|
||||
valueHint = R.string.debug_knobs_hint,
|
||||
|
||||
+1
-2
@@ -25,7 +25,6 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
|
||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||
@@ -1327,7 +1326,7 @@ class SettingsFragmentPresenter(
|
||||
|
||||
add(HeaderSetting(R.string.general))
|
||||
|
||||
add(UShortSetting.DEBUG_KNOBS.key)
|
||||
add(ShortSetting.DEBUG_KNOBS.key)
|
||||
add(StringSetting.PROGRAM_ARGS.key)
|
||||
|
||||
if (!NativeConfig.isPerGameConfigLoaded()) {
|
||||
|
||||
@@ -47,6 +47,7 @@ import info.debatty.java.stringsimilarity.Jaccard
|
||||
import info.debatty.java.stringsimilarity.JaroWinkler
|
||||
import java.util.Locale
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.view.doOnNextLayout
|
||||
|
||||
class GamesFragment : Fragment() {
|
||||
private var _binding: FragmentGamesBinding? = null
|
||||
@@ -58,6 +59,7 @@ class GamesFragment : Fragment() {
|
||||
private var originalHeaderLeftMargin: Int? = null
|
||||
|
||||
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
||||
private var fallbackBottomInset: Int = 0
|
||||
private var pendingPostReloadListSettle = false
|
||||
private var pendingPostReloadListSettleGeneration = 0
|
||||
private var gameListSubmitGeneration = 0
|
||||
@@ -225,7 +227,12 @@ class GamesFragment : Fragment() {
|
||||
}
|
||||
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
||||
}
|
||||
if (savedViewType != GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||
if (savedViewType == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||
(binding.gridGames as? View)?.let { it -> ViewCompat.requestApplyInsets(it)}
|
||||
doOnNextLayout { //Carousel: important to avoid overlap issues
|
||||
(this as? CarouselRecyclerView)?.notifyLaidOut(fallbackBottomInset)
|
||||
}
|
||||
} else {
|
||||
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
||||
}
|
||||
adapter = gameAdapter
|
||||
@@ -583,6 +590,11 @@ class GamesFragment : Fragment() {
|
||||
qlaunchButton.layoutParams = mlpQLaunch
|
||||
}
|
||||
|
||||
val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
|
||||
val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||
fallbackBottomInset = bottomInset
|
||||
(binding.gridGames as? CarouselRecyclerView)?.notifyInsetsReady(bottomInset)
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,12 +80,6 @@ object NativeConfig {
|
||||
@Synchronized
|
||||
external fun setShort(key: String, value: Short)
|
||||
|
||||
@Synchronized
|
||||
external fun getUnsignedShort(key: String, needsGlobal: Boolean): Int
|
||||
|
||||
@Synchronized
|
||||
external fun setUnsignedShort(key: String, value: Int)
|
||||
|
||||
@Synchronized
|
||||
external fun getInt(key: String, needsGlobal: Boolean): Int
|
||||
|
||||
|
||||
@@ -12,16 +12,13 @@ import androidx.recyclerview.widget.PagerSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
||||
/**
|
||||
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
||||
* It manages overlapping cards, center snapping, custom drawing order,
|
||||
@@ -35,9 +32,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
|
||||
private var overlapFactor: Float = 0f
|
||||
private var overlapPx: Int = 0
|
||||
private var bottomInset: Int = 0
|
||||
private var latestWindowInsets: WindowInsetsCompat? = null
|
||||
private var cardGeometryInitialized: Boolean = false
|
||||
private var bottomInset: Int = -1
|
||||
private var overlapDecoration: OverlappingDecoration? = null
|
||||
private var pagerSnapHelper: PagerSnapHelper? = null
|
||||
private var scalingScrollListener: OnScrollListener? = null
|
||||
@@ -96,38 +91,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
|
||||
init {
|
||||
setChildrenDrawingOrderEnabled(true)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets ->
|
||||
latestWindowInsets = insets
|
||||
updateCardGeometry()
|
||||
applyCarouselPadding()
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
ViewCompat.requestApplyInsets(this)
|
||||
}
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
if (w != oldw || h != oldh) {
|
||||
updateCardGeometry()
|
||||
applyCarouselPadding()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
if (isCarouselMode) updateChildScalesAndAlpha()
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if (hasFocus) {
|
||||
ViewCompat.requestApplyInsets(this)
|
||||
post { updateCardGeometry() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun setAdapter(adapter: Adapter<*>?) {
|
||||
@@ -140,8 +103,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
super.setAdapter(adapter)
|
||||
|
||||
(adapter as? GameAdapter)?.registerAdapterDataObserver(carouselAdapterObserver)
|
||||
updateCardGeometry()
|
||||
applyCarouselPadding()
|
||||
}
|
||||
|
||||
private fun calculateCenter(width: Int, paddingStart: Int, paddingEnd: Int): Int {
|
||||
@@ -292,71 +253,40 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveBottomInset(windowInsets: WindowInsetsCompat): Int {
|
||||
val navigationBottom = if (FullscreenHelper.isFullscreenEnabled(context)) {
|
||||
0
|
||||
} else {
|
||||
windowInsets.getInsetsIgnoringVisibility(WindowInsetsCompat.Type.navigationBars()).bottom
|
||||
fun notifyInsetsReady(newBottomInset: Int) {
|
||||
if (bottomInset != newBottomInset) {
|
||||
bottomInset = newBottomInset
|
||||
}
|
||||
|
||||
if (isCarouselMode) {
|
||||
setupCarousel(true)
|
||||
} else {
|
||||
setupCarousel(false)
|
||||
}
|
||||
val gestureInsets = windowInsets.getInsetsIgnoringVisibility(
|
||||
WindowInsetsCompat.Type.systemGestures()
|
||||
)
|
||||
val cutoutInsets = windowInsets.getInsetsIgnoringVisibility(
|
||||
WindowInsetsCompat.Type.displayCutout()
|
||||
)
|
||||
return maxOf(navigationBottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||
}
|
||||
|
||||
private fun updateCardGeometry() {
|
||||
if (!isCarouselMode || height <= 0) return
|
||||
fun notifyLaidOut(fallBackBottomInset: Int) {
|
||||
if (bottomInset < 0) bottomInset = fallBackBottomInset
|
||||
var gameAdapter = adapter as? GameAdapter ?: return
|
||||
var newCardSize = cardSize(bottomInset)
|
||||
if (gameAdapter.cardSize != newCardSize) {
|
||||
gameAdapter.setCardSize(newCardSize)
|
||||
}
|
||||
|
||||
val gameAdapter = adapter as? GameAdapter ?: return
|
||||
val windowInsets = latestWindowInsets ?: ViewCompat.getRootWindowInsets(this) ?: return
|
||||
if (isCarouselMode) {
|
||||
setupCarousel(true)
|
||||
}
|
||||
}
|
||||
|
||||
if (cardGeometryInitialized && !hasWindowFocus()) return
|
||||
|
||||
val newBottomInset = resolveBottomInset(windowInsets).coerceIn(0, height)
|
||||
fun cardSize(bottomInset: Int): Int {
|
||||
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
||||
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
val screenWidth = resources.displayMetrics.widthPixels.toFloat()
|
||||
val screenHeight = resources.displayMetrics.heightPixels.toFloat()
|
||||
val aspectFactor = ((screenWidth / screenHeight) / (20f / 9f))
|
||||
.pow(0.75f)
|
||||
.coerceIn(0.5f, 1f)
|
||||
val newCardSize = minOf(
|
||||
(height * userFactor).toInt(),
|
||||
height - newBottomInset,
|
||||
(height * aspectFactor).toInt()
|
||||
)
|
||||
if (newCardSize <= 0) return
|
||||
val insetChanged = bottomInset != newBottomInset
|
||||
val cardSizeChanged = gameAdapter.cardSize != newCardSize
|
||||
|
||||
bottomInset = newBottomInset
|
||||
cardGeometryInitialized = true
|
||||
|
||||
if (cardSizeChanged) gameAdapter.setCardSize(newCardSize)
|
||||
if (insetChanged || cardSizeChanged) setupCarousel(true)
|
||||
}
|
||||
|
||||
private fun applyCarouselPadding() {
|
||||
if (!isCarouselMode) return
|
||||
|
||||
val gameAdapter = adapter as? GameAdapter ?: return
|
||||
val cardSize = gameAdapter.cardSize
|
||||
if (cardSize <= 0 || bottomInset < 0) return
|
||||
|
||||
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0)
|
||||
val sidePadding = (width - cardSize) / 2
|
||||
if (paddingLeft != sidePadding || paddingTop != topPadding ||
|
||||
paddingRight != sidePadding || paddingBottom != 0
|
||||
) {
|
||||
setPadding(sidePadding, topPadding, sidePadding, 0)
|
||||
}
|
||||
clipToPadding = false
|
||||
val scaledHeight = height * userFactor
|
||||
val availableHeight = height - bottomInset
|
||||
return minOf(scaledHeight.toInt(), availableHeight.toInt())
|
||||
}
|
||||
|
||||
fun setupCarousel(enabled: Boolean) {
|
||||
@@ -385,6 +315,9 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
internalFlingMultiplier
|
||||
).coerceIn(1f, 5f)
|
||||
|
||||
// Detach SnapHelper during setup
|
||||
pagerSnapHelper?.attachToRecyclerView(null)
|
||||
|
||||
// Add overlap decoration if not present
|
||||
if (overlapDecoration == null) {
|
||||
overlapDecoration = OverlappingDecoration(overlapPx)
|
||||
@@ -402,7 +335,12 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
addOnScrollListener(scalingScrollListener!!)
|
||||
}
|
||||
|
||||
applyCarouselPadding()
|
||||
if (cardSize > 0) {
|
||||
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0) // Center vertically
|
||||
val sidePadding = (width - cardSize) / 2 // Center first/last card
|
||||
setPadding(sidePadding, topPadding, sidePadding, 0)
|
||||
clipToPadding = false
|
||||
}
|
||||
|
||||
if (pagerSnapHelper == null) {
|
||||
pagerSnapHelper = CenterPagerSnapHelper()
|
||||
@@ -424,7 +362,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
}
|
||||
savedItemAnimator = null
|
||||
}
|
||||
cardGeometryInitialized = false
|
||||
useCustomDrawingOrder = false
|
||||
// Reset padding and fling
|
||||
setPadding(0, 0, 0, 0)
|
||||
|
||||
@@ -1300,8 +1300,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_refreshThreadPolicies(JNIEnv* env, jo
|
||||
Common::RefreshThreadPolicies();
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDebugKnobAt(JNIEnv* env, jobject jobj, jint index) {
|
||||
return static_cast<jboolean>(Settings::GetDebugKnobAt(static_cast<u8>(index)));
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_getDebugKnobAt(JNIEnv* env, jobject jobj, jint index) {
|
||||
return static_cast<jboolean>(Settings::getDebugKnobAt(static_cast<u8>(index)));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
||||
|
||||
@@ -130,25 +130,6 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject ob
|
||||
setting->SetValue(value);
|
||||
}
|
||||
|
||||
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getUnsignedShort(JNIEnv* env, jobject obj,
|
||||
jstring jkey,
|
||||
jboolean needGlobal) {
|
||||
auto setting = getSetting<u16>(env, jkey);
|
||||
if (setting == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
return static_cast<jint>(setting->GetValue(static_cast<bool>(needGlobal)));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setUnsignedShort(JNIEnv* env, jobject obj,
|
||||
jstring jkey, jint value) {
|
||||
auto setting = getSetting<u16>(env, jkey);
|
||||
if (setting == nullptr) {
|
||||
return;
|
||||
}
|
||||
setting->SetValue(static_cast<u16>(value));
|
||||
}
|
||||
|
||||
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
|
||||
jboolean needGlobal) {
|
||||
auto setting = getSetting<int>(env, jkey);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -5,17 +8,11 @@
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace AudioCore::ADSP::OpusDecoder {
|
||||
namespace {
|
||||
bool IsValidChannelCount(u32 channel_count) {
|
||||
return channel_count == 1 || channel_count == 2;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
|
||||
if (!IsValidChannelCount(channel_count)) {
|
||||
if (channel_count == 1 || channel_count == 2)
|
||||
return 0;
|
||||
}
|
||||
return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
|
||||
return u32(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
|
||||
}
|
||||
|
||||
OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
|
||||
|
||||
@@ -22,10 +22,6 @@ namespace AudioCore::ADSP::OpusDecoder {
|
||||
namespace {
|
||||
constexpr size_t OpusStreamCountMax = 255;
|
||||
|
||||
bool IsValidChannelCount(u32 channel_count) {
|
||||
return channel_count == 1 || channel_count == 2;
|
||||
}
|
||||
|
||||
bool IsValidMultiStreamChannelCount(u32 channel_count) {
|
||||
return channel_count <= OpusStreamCountMax;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
@@ -14,14 +14,16 @@
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
|
||||
namespace AudioCore::AudioIn {
|
||||
|
||||
// See texture_cache/util.h
|
||||
template<typename T, size_t N>
|
||||
#if BOOST_VERSION >= 108100 || __GNUC__ > 12
|
||||
[[nodiscard]] boost::container::static_vector<T, N> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
|
||||
[[nodiscard]] static inline boost::container::static_vector<T, N> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
|
||||
return v;
|
||||
}
|
||||
#else
|
||||
[[nodiscard]] std::vector<T> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
|
||||
[[nodiscard]] static inline std::vector<T> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
|
||||
std::vector<T> u;
|
||||
for (auto const& e : v)
|
||||
u.push_back(e);
|
||||
@@ -29,8 +31,6 @@ template<typename T, size_t N>
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace AudioCore::AudioIn {
|
||||
|
||||
System::System(Core::System& system_, Kernel::KEvent* event_, const size_t session_id_)
|
||||
: system{system_}, buffer_event{event_},
|
||||
session_id{session_id_}, session{std::make_unique<DeviceSession>(system_)} {}
|
||||
|
||||
@@ -14,14 +14,15 @@
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
|
||||
namespace AudioCore::AudioOut {
|
||||
// See texture_cache/util.h
|
||||
template<typename T, size_t N>
|
||||
#if BOOST_VERSION >= 108100 || __GNUC__ > 12
|
||||
[[nodiscard]] boost::container::static_vector<T, N> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
|
||||
[[nodiscard]] static inline boost::container::static_vector<T, N> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
|
||||
return v;
|
||||
}
|
||||
#else
|
||||
[[nodiscard]] std::vector<T> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
|
||||
[[nodiscard]] static inline std::vector<T> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
|
||||
std::vector<T> u;
|
||||
for (auto const& e : v)
|
||||
u.push_back(e);
|
||||
@@ -29,8 +30,6 @@ template<typename T, size_t N>
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace AudioCore::AudioOut {
|
||||
|
||||
System::System(Core::System& system_, Kernel::KEvent* event_, size_t session_id_)
|
||||
: system{system_}, buffer_event{event_},
|
||||
session_id{session_id_}, session{std::make_unique<DeviceSession>(system_)} {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
@@ -16,7 +16,7 @@ namespace AudioCore::Renderer {
|
||||
* @param memory - Core memory for writing.
|
||||
* @param aux_info - Memory address pointing to the AuxInfo to reset.
|
||||
*/
|
||||
static void ResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_info) {
|
||||
static void CaptureResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_info) {
|
||||
if (aux_info == 0) {
|
||||
LOG_ERROR(Service_Audio, "Aux info is 0!");
|
||||
return;
|
||||
@@ -134,7 +134,7 @@ void CaptureCommand::Process(const AudioRenderer::CommandListProcessor& processo
|
||||
WriteAuxBufferDsp(*processor.memory, send_buffer_info, send_buffer, count_max, input_buffer,
|
||||
processor.sample_count, write_offset, update_count);
|
||||
} else {
|
||||
ResetAuxBufferDsp(*processor.memory, send_buffer_info);
|
||||
CaptureResetAuxBufferDsp(*processor.memory, send_buffer_info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2011 Google, Inc.
|
||||
// SPDX-FileContributor: Geoff Pike
|
||||
// SPDX-FileContributor: Jyrki Alakuijala
|
||||
@@ -27,8 +30,6 @@
|
||||
#define WORDS_BIGENDIAN 1
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Common {
|
||||
|
||||
static u64 unaligned_load64(const char* p) {
|
||||
@@ -135,18 +136,18 @@ static u64 HashLen17to32(const char* s, size_t len) {
|
||||
|
||||
// Return a 16-byte hash for 48 bytes. Quick and dirty.
|
||||
// Callers do best to use "random-looking" values for a and b.
|
||||
static pair<u64, u64> WeakHashLen32WithSeeds(u64 w, u64 x, u64 y, u64 z, u64 a, u64 b) {
|
||||
static std::pair<u64, u64> WeakHashLen32WithSeeds(u64 w, u64 x, u64 y, u64 z, u64 a, u64 b) {
|
||||
a += w;
|
||||
b = Rotate(b + a + z, 21);
|
||||
u64 c = a;
|
||||
a += x;
|
||||
a += y;
|
||||
b += Rotate(a, 44);
|
||||
return make_pair(a + z, b + c);
|
||||
return std::make_pair(a + z, b + c);
|
||||
}
|
||||
|
||||
// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
|
||||
static pair<u64, u64> WeakHashLen32WithSeeds(const char* s, u64 a, u64 b) {
|
||||
static std::pair<u64, u64> WeakHashLen32WithSeeds(const char* s, u64 a, u64 b) {
|
||||
return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a,
|
||||
b);
|
||||
}
|
||||
@@ -189,8 +190,8 @@ u64 CityHash64(const char* s, size_t len) {
|
||||
u64 x = Fetch64(s + len - 40);
|
||||
u64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
|
||||
u64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
|
||||
pair<u64, u64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
|
||||
pair<u64, u64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
|
||||
std::pair<u64, u64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
|
||||
std::pair<u64, u64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
|
||||
x = x * k1 + Fetch64(s);
|
||||
|
||||
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
|
||||
@@ -258,7 +259,7 @@ u128 CityHash128WithSeed(const char* s, size_t len, u128 seed) {
|
||||
|
||||
// We expect len >= 128 to be the common case. Keep 56 bytes of state:
|
||||
// v, w, x, y, and z.
|
||||
pair<u64, u64> v, w;
|
||||
std::pair<u64, u64> v, w;
|
||||
u64 x = seed[0];
|
||||
u64 y = seed[1];
|
||||
u64 z = len * k1;
|
||||
|
||||
@@ -16,3 +16,5 @@
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#undef INVALID_SOCKET
|
||||
|
||||
+15
-27
@@ -329,7 +329,7 @@ struct LogcatBackend : public Backend {
|
||||
}
|
||||
}();
|
||||
auto const df = GetDirectFormatArgs(entry);
|
||||
__android_log_print(android_log_priority, "YuzuNative", "%s %s:%u:%s: %s", df.class_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||
__android_log_print(android_log_priority, "YuzuNative", CCB_PRINTF_FMT, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||
}
|
||||
void Flush() noexcept override {}
|
||||
};
|
||||
@@ -428,33 +428,21 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, u
|
||||
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
||||
char buffer[BUFSIZ];
|
||||
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
||||
Entry e{
|
||||
.message = nullptr,
|
||||
.message_len = 0,
|
||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
.filename = TrimSourcePath(filename),
|
||||
.function = function,
|
||||
.line_num = line_num,
|
||||
};
|
||||
if (result.size <= sizeof(buffer) - 1) {
|
||||
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
||||
e.message = buffer;
|
||||
e.message_len = (std::min)(result.size, sizeof(buffer) - 1);
|
||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||
backend.Write(e);
|
||||
if (flush) backend.Flush();
|
||||
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||
backend.Write(Entry{
|
||||
.message = buffer,
|
||||
.message_len = (std::min)(result.size, sizeof(buffer) - 1),
|
||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
.filename = TrimSourcePath(filename),
|
||||
.function = function,
|
||||
.line_num = line_num,
|
||||
});
|
||||
} else {
|
||||
std::string s = fmt::vformat(format, args);
|
||||
e.message = s.c_str();
|
||||
e.message_len = s.size();
|
||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||
backend.Write(e);
|
||||
if (flush) backend.Flush();
|
||||
});
|
||||
}
|
||||
if (flush)
|
||||
backend.Flush();
|
||||
});
|
||||
}
|
||||
}
|
||||
} // namespace Common::Log
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
|
||||
namespace Common::Net {
|
||||
|
||||
struct Asset {
|
||||
typedef struct {
|
||||
std::string name;
|
||||
std::string url;
|
||||
std::string path;
|
||||
std::string filename;
|
||||
};
|
||||
} Asset;
|
||||
|
||||
struct Release {
|
||||
typedef struct Release {
|
||||
std::string title;
|
||||
std::string body;
|
||||
std::string tag;
|
||||
@@ -39,7 +39,7 @@ struct Release {
|
||||
static std::optional<Release> FromJson(const std::string_view& json, const std::string &host, const std::string& repo);
|
||||
static std::vector<Release> ListFromJson(const nlohmann::json &json, const std::string &host, const std::string &repo);
|
||||
static std::vector<Release> ListFromJson(const std::string_view &json, const std::string &host, const std::string &repo);
|
||||
};
|
||||
} Release;
|
||||
|
||||
// Make a request via httplib, and return the response body if applicable.
|
||||
std::optional<std::string> MakeRequest(const std::string &url, const std::string &path);
|
||||
|
||||
@@ -126,9 +126,9 @@ void LogSettings() {
|
||||
setting->UsingGlobal() ? '-' : 'C', TranslateCategory(category),
|
||||
setting->GetLabel());
|
||||
if (is_default)
|
||||
settings_list.push_back(fmt::format("{}: {}", name, setting->Canonicalize()));
|
||||
settings_list.push_back(fmt::format("{}: {}\n", name, setting->Canonicalize()));
|
||||
else
|
||||
settings_list.push_front(fmt::format("{}: {}", name, setting->Canonicalize()));
|
||||
settings_list.push_front(fmt::format("{}: {}\n", name, setting->Canonicalize()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,7 +146,7 @@ void LogSettings() {
|
||||
#undef LOG_PATH
|
||||
}
|
||||
|
||||
bool GetDebugKnobAt(u8 i) {
|
||||
bool getDebugKnobAt(u8 i) {
|
||||
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -904,7 +904,7 @@ struct Values {
|
||||
0,
|
||||
65535,
|
||||
"debug_knobs",
|
||||
Category::System,
|
||||
Category::Debugging,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
true};
|
||||
@@ -947,7 +947,7 @@ constexpr u32 MAX_FRAME_GEN_MULTIPLIER = 4;
|
||||
|
||||
[[nodiscard]] size_t FrameGenMaxGenerations();
|
||||
|
||||
bool GetDebugKnobAt(u8 i);
|
||||
bool getDebugKnobAt(u8 i);
|
||||
|
||||
void UpdateGPUAccuracy();
|
||||
bool IsGPULevelHigh();
|
||||
|
||||
@@ -7,19 +7,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <dynarmic/interface/halt_reason.h>
|
||||
|
||||
#include "core/arm/arm_interface.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step;
|
||||
constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort;
|
||||
constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2;
|
||||
constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3;
|
||||
constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4;
|
||||
constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6;
|
||||
inline constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step;
|
||||
inline constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort;
|
||||
inline constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2;
|
||||
inline constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3;
|
||||
inline constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4;
|
||||
inline constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6;
|
||||
|
||||
constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
|
||||
[[nodiscard]] inline constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
|
||||
static_assert(u64(HaltReason::StepThread) == u64(StepThread));
|
||||
static_assert(u64(HaltReason::DataAbort) == u64(DataAbort));
|
||||
static_assert(u64(HaltReason::BreakLoop) == u64(BreakLoop));
|
||||
|
||||
+6
-34
@@ -4,7 +4,6 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "game_settings.h"
|
||||
@@ -249,30 +248,12 @@ struct System::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyNVDECChannelOpen(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
++nvdec_active_channels[process_id];
|
||||
}
|
||||
|
||||
void NotifyNVDECChannelClose(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
const auto it = nvdec_active_channels.find(process_id);
|
||||
if (it == nvdec_active_channels.end()) {
|
||||
return;
|
||||
}
|
||||
if (--it->second == 0) {
|
||||
nvdec_active_channels.erase(it);
|
||||
}
|
||||
void SetNVDECActive(bool is_nvdec_active) {
|
||||
nvdec_active = is_nvdec_active;
|
||||
}
|
||||
|
||||
bool GetNVDECActive() {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
return !nvdec_active_channels.empty();
|
||||
}
|
||||
|
||||
bool IsNVDECActiveForProcess(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
return nvdec_active_channels.contains(process_id);
|
||||
return nvdec_active;
|
||||
}
|
||||
|
||||
void InitializeDebugger(System& system, u16 port) {
|
||||
@@ -524,8 +505,6 @@ struct System::Impl {
|
||||
|
||||
mutable std::mutex suspend_guard;
|
||||
std::mutex general_channel_mutex;
|
||||
std::mutex nvdec_active_mutex;
|
||||
std::unordered_map<u64, u32> nvdec_active_channels;
|
||||
std::atomic_bool is_paused{};
|
||||
std::atomic_bool is_shutting_down{};
|
||||
std::atomic_bool is_powered_on{};
|
||||
@@ -533,6 +512,7 @@ struct System::Impl {
|
||||
bool extended_memory_layout : 1 = false;
|
||||
bool exit_locked : 1 = false;
|
||||
bool exit_requested : 1 = false;
|
||||
bool nvdec_active : 1 = false;
|
||||
|
||||
void EnsureGeneralChannelInitialized(System& system) {
|
||||
if (!general_channel_event) {
|
||||
@@ -596,22 +576,14 @@ void System::UnstallApplication() {
|
||||
impl->UnstallApplication();
|
||||
}
|
||||
|
||||
void System::NotifyNVDECChannelOpen(u64 process_id) {
|
||||
impl->NotifyNVDECChannelOpen(process_id);
|
||||
}
|
||||
|
||||
void System::NotifyNVDECChannelClose(u64 process_id) {
|
||||
impl->NotifyNVDECChannelClose(process_id);
|
||||
void System::SetNVDECActive(bool is_nvdec_active) {
|
||||
impl->SetNVDECActive(is_nvdec_active);
|
||||
}
|
||||
|
||||
bool System::GetNVDECActive() {
|
||||
return impl->GetNVDECActive();
|
||||
}
|
||||
|
||||
bool System::IsNVDECActiveForProcess(u64 process_id) {
|
||||
return impl->IsNVDECActiveForProcess(process_id);
|
||||
}
|
||||
|
||||
void System::InitializeDebugger() {
|
||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||
}
|
||||
|
||||
+1
-3
@@ -191,10 +191,8 @@ public:
|
||||
std::unique_lock<std::mutex> StallApplication();
|
||||
void UnstallApplication();
|
||||
|
||||
void NotifyNVDECChannelOpen(u64 process_id);
|
||||
void NotifyNVDECChannelClose(u64 process_id);
|
||||
void SetNVDECActive(bool is_nvdec_active);
|
||||
[[nodiscard]] bool GetNVDECActive();
|
||||
[[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id);
|
||||
|
||||
/**
|
||||
* Initialize the debugger.
|
||||
|
||||
@@ -38,6 +38,7 @@ constexpr u32 CpuClockTargetMhz(Settings::CpuClock clock) {
|
||||
}
|
||||
}
|
||||
|
||||
#undef CreateEvent
|
||||
std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
|
||||
return std::make_shared<EventType>(std::move(callback), std::move(name));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -185,13 +185,13 @@ static_assert(sizeof(SaveDataFilter) == 0x48, "SaveDataFilter has invalid size."
|
||||
static_assert(std::is_trivially_copyable_v<SaveDataFilter>,
|
||||
"Data type must be trivially copyable.");
|
||||
|
||||
struct HashSalt {
|
||||
struct SaveDataHashSalt {
|
||||
static constexpr size_t Size = 32;
|
||||
|
||||
std::array<u8, Size> value;
|
||||
};
|
||||
static_assert(std::is_trivially_copyable_v<HashSalt>, "Data type must be trivially copyable.");
|
||||
static_assert(sizeof(HashSalt) == HashSalt::Size);
|
||||
static_assert(std::is_trivially_copyable_v<SaveDataHashSalt>, "Data type must be trivially copyable.");
|
||||
static_assert(sizeof(SaveDataHashSalt) == SaveDataHashSalt::Size);
|
||||
|
||||
struct SaveDataCreationInfo2 {
|
||||
|
||||
@@ -210,7 +210,7 @@ struct SaveDataCreationInfo2 {
|
||||
u8 reserved1;
|
||||
bool is_hash_salt_enabled;
|
||||
u8 reserved2;
|
||||
HashSalt hash_salt;
|
||||
SaveDataHashSalt hash_salt;
|
||||
SaveDataMetaType meta_type;
|
||||
u8 reserved3;
|
||||
s32 meta_size;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -11,7 +14,7 @@
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
#include "core/file_sys/vfs/vfs_vector.h"
|
||||
|
||||
namespace FileSys {
|
||||
namespace FileSys::RomFSBuilder {
|
||||
|
||||
constexpr u64 FS_MAX_PATH = 0x301;
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -9,7 +12,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
namespace FileSys::RomFSBuilder {
|
||||
|
||||
struct RomFSBuildDirectoryContext;
|
||||
struct RomFSBuildFileContext;
|
||||
|
||||
+240
-193
@@ -3,12 +3,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <span>
|
||||
#include <cctype>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging.h"
|
||||
@@ -24,30 +22,61 @@ enum class IPSFileType {
|
||||
Error,
|
||||
};
|
||||
|
||||
static IPSFileType IdentifyMagic(std::span<const u8> magic) {
|
||||
if (magic.size() >= 5) {
|
||||
if (std::memcmp(magic.data(), "PATCH", 5) == 0)
|
||||
return IPSFileType::IPS;
|
||||
if (std::memcmp(magic.data(), "IPS32", 5) == 0)
|
||||
return IPSFileType::IPS32;
|
||||
constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{{
|
||||
{"\\a", "\a"},
|
||||
{"\\b", "\b"},
|
||||
{"\\f", "\f"},
|
||||
{"\\n", "\n"},
|
||||
{"\\r", "\r"},
|
||||
{"\\t", "\t"},
|
||||
{"\\v", "\v"},
|
||||
{"\\\\", "\\"},
|
||||
{"\\\'", "\'"},
|
||||
{"\\\"", "\""},
|
||||
{"\\\?", "\?"},
|
||||
}};
|
||||
|
||||
static IPSFileType IdentifyMagic(const std::vector<u8>& magic) {
|
||||
if (magic.size() != 5) {
|
||||
return IPSFileType::Error;
|
||||
}
|
||||
|
||||
static constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}};
|
||||
if (std::equal(magic.begin(), magic.end(), patch_magic.begin())) {
|
||||
return IPSFileType::IPS;
|
||||
}
|
||||
|
||||
static constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}};
|
||||
if (std::equal(magic.begin(), magic.end(), ips32_magic.begin())) {
|
||||
return IPSFileType::IPS32;
|
||||
}
|
||||
|
||||
return IPSFileType::Error;
|
||||
}
|
||||
|
||||
static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
|
||||
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|
||||
|| (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
|
||||
static bool IsEOF(IPSFileType type, const std::vector<u8>& data) {
|
||||
static constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}};
|
||||
if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}};
|
||||
return type == IPSFileType::IPS32 && std::equal(data.begin(), data.end(), eeof.begin());
|
||||
}
|
||||
|
||||
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||
if (in == nullptr || ips == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto in_data = in->ReadAllBytes();
|
||||
auto const type = IdentifyMagic(in_data);
|
||||
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
|
||||
if (type == IPSFileType::Error)
|
||||
return nullptr;
|
||||
|
||||
auto in_data = in->ReadAllBytes();
|
||||
if (in_data.size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
||||
u64 offset = 5; // After header
|
||||
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
|
||||
@@ -56,9 +85,12 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||
break;
|
||||
}
|
||||
|
||||
u32 real_offset = (type == IPSFileType::IPS32)
|
||||
? ((temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3])
|
||||
: ((temp[0] << 16) | (temp[1] << 8) | temp[2]);
|
||||
u32 real_offset{};
|
||||
if (type == IPSFileType::IPS32)
|
||||
real_offset = (temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3];
|
||||
else
|
||||
real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2];
|
||||
|
||||
if (real_offset > in_data.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -81,35 +113,34 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||
return nullptr;
|
||||
|
||||
if (real_offset + rle_size > in_data.size())
|
||||
rle_size = u16(in_data.size() - real_offset);
|
||||
rle_size = static_cast<u16>(in_data.size() - real_offset);
|
||||
std::memset(in_data.data() + real_offset, *data, rle_size);
|
||||
} else { // Standard Patch
|
||||
auto read = data_size;
|
||||
if (real_offset + read > in_data.size())
|
||||
read = u16(in_data.size() - real_offset);
|
||||
read = static_cast<u16>(in_data.size() - real_offset);
|
||||
if (ips->Read(in_data.data() + real_offset, read, offset) != data_size)
|
||||
return nullptr;
|
||||
offset += data_size;
|
||||
}
|
||||
}
|
||||
if (IsEOF(type, temp)) {
|
||||
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), in->GetContainingDirectory());
|
||||
|
||||
if (!IsEOF(type, temp)) {
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
|
||||
in->GetContainingDirectory());
|
||||
}
|
||||
|
||||
|
||||
struct IPSwitchRecord {
|
||||
std::array<uint8_t, 256 - sizeof(size_t)> data;
|
||||
size_t count;
|
||||
};
|
||||
struct IPSwitchCompiler::IPSwitchPatch {
|
||||
ankerl::unordered_dense::map<u32, IPSwitchRecord> records;
|
||||
std::string name;
|
||||
bool enabled;
|
||||
std::map<u32, std::vector<u8>> records;
|
||||
};
|
||||
|
||||
IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) {
|
||||
Parse(patch_text->ReadAllBytes());
|
||||
Parse();
|
||||
}
|
||||
|
||||
IPSwitchCompiler::~IPSwitchCompiler() = default;
|
||||
@@ -118,185 +149,201 @@ std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
|
||||
return nso_build_id;
|
||||
}
|
||||
|
||||
static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
||||
IPSwitchRecord r{};
|
||||
for (auto it = sv.cbegin(); it != sv.cend(); ) {
|
||||
if (*it == '\\' && it + 1 < sv.cend()) {
|
||||
switch (it[1]) {
|
||||
case 'a': r.data[r.count] = '\a'; break;
|
||||
case 'b': r.data[r.count] = '\b'; break;
|
||||
case 'e': r.data[r.count] = '\e'; break;
|
||||
case 'f': r.data[r.count] = '\f'; break;
|
||||
case 'n': r.data[r.count] = '\n'; break;
|
||||
case 'r': r.data[r.count] = '\r'; break;
|
||||
case 't': r.data[r.count] = '\t'; break;
|
||||
case 'v': r.data[r.count] = '\v'; break;
|
||||
case '?': r.data[r.count] = '\?'; break;
|
||||
default: r.data[r.count] = it[1]; break;
|
||||
}
|
||||
++r.count;
|
||||
it += 2;
|
||||
} else {
|
||||
++r.count;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
bool IPSwitchCompiler::IsValid() const {
|
||||
return valid;
|
||||
}
|
||||
|
||||
void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
||||
LOG_INFO(Loader, "IPSwitchCompiler: '{}'", patch_text->GetName());
|
||||
bool is_little_endian = true;
|
||||
s64 offset_shift = 0;
|
||||
//bool print_values = false;
|
||||
auto const parse_line = [&](std::string_view const line) {
|
||||
// Keep in mind lines have trimmed spaces (at the end & start)!
|
||||
LOG_INFO(Loader, "<{}>", line);
|
||||
// IPSwitch is case insensitive
|
||||
// Yes this is how the logic goes for the main reference parsers!
|
||||
if (line.size() > 2 && line[0] == '@') {
|
||||
switch (line[1]) {
|
||||
// yes, @nsobid too -- NSO Build ID Specifier
|
||||
case 'n':
|
||||
case 'N':
|
||||
nso_build_id = Common::HexStringToArray<0x20>(fmt::format("{:0<64}", line.substr(8)));
|
||||
break;
|
||||
// @stop
|
||||
case 's':
|
||||
case 'S':
|
||||
return false;
|
||||
// @enabled
|
||||
case 'e':
|
||||
case 'E':
|
||||
patches.push_back({{}, true});
|
||||
break;
|
||||
// @disabled
|
||||
case 'd':
|
||||
case 'D':
|
||||
patches.push_back({{}, false});
|
||||
break;
|
||||
// @flag
|
||||
case 'f':
|
||||
case 'F': {
|
||||
if (line.starts_with("@flag offset_shift")) {
|
||||
offset_shift = std::strtoll(line.data() + 19, nullptr, 0); // Offset Shift Flag
|
||||
} else if (line.starts_with("@flag print_values")) {
|
||||
//print_values = true; // Force printing of applied values
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'l':
|
||||
case 'L':
|
||||
is_little_endian = true;
|
||||
break;
|
||||
// IPS parsers dont support big endian no more, we do due to backcompat
|
||||
case 'b':
|
||||
case 'B':
|
||||
is_little_endian = false;
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(Loader, "Unknown flag {}", line);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
size_t offset = size_t(std::strtoul(line.data(), nullptr, 16));
|
||||
offset += size_t(offset_shift);
|
||||
if (auto const first_quote = line.find_first_of("\"\'"); first_quote != std::string::npos) {
|
||||
// string replacement
|
||||
char quote = line[first_quote];
|
||||
auto const start = line.cbegin() + first_quote + 1;
|
||||
auto end = start;
|
||||
for (; end < line.cend() && *end != quote; )
|
||||
end += (*end == '\\') ? 2 : 1;
|
||||
if (start <= line.cend() && end <= line.cend()) {
|
||||
LOG_INFO(Loader, "[S] value @ {:#08X} ", offset);
|
||||
patches.back().records.insert_or_assign(u32(offset), EscapeStringSequences({start, end}));
|
||||
} else {
|
||||
LOG_WARNING(Loader, "invalid string");
|
||||
}
|
||||
} else if (auto const first_space = line.find_last_of(" /\t\r\n"); first_space != std::string::npos) {
|
||||
IPSwitchRecord r{}; // hex replacement
|
||||
auto const start = line.cbegin() + first_space + 1;
|
||||
auto const end = line.cend();
|
||||
if (start <= line.cend() && end <= line.cend()) {
|
||||
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
|
||||
auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
|
||||
std::memcpy(r.data.data(), hs.data(), hs.size());
|
||||
r.count = hs.size();
|
||||
LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
|
||||
patches.back().records.insert_or_assign(u32(offset), std::move(r));
|
||||
} else {
|
||||
LOG_WARNING(Loader, "invalid line");
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Loader, "unhandled line!");
|
||||
}
|
||||
}
|
||||
return true; //continue
|
||||
};
|
||||
static bool StartsWith(std::string_view base, std::string_view check) {
|
||||
return base.size() >= check.size() && base.substr(0, check.size()) == check;
|
||||
}
|
||||
|
||||
for (auto it = bytes.begin(); it < bytes.end(); ) {
|
||||
auto const start = it;
|
||||
auto end = start;
|
||||
for (; end < bytes.end() && *end != '\n' && *end != '\r'; ++end)
|
||||
;
|
||||
it = end + 1; //prepare for next line
|
||||
std::string_view const sline{
|
||||
reinterpret_cast<const char*>(bytes.data() + std::distance(bytes.begin(), start)),
|
||||
size_t(std::distance(start, end))
|
||||
};
|
||||
if (sline.size() > 0) {
|
||||
auto p = sline.cbegin();
|
||||
// skip space off line
|
||||
for (; p < sline.cend() && std::isspace(*p); ++p)
|
||||
;
|
||||
// now make a nominal preprocessed line: remove comments
|
||||
char quote = '\0';
|
||||
auto const sline_start = p;
|
||||
for (; p < sline.cend(); ) {
|
||||
// we dont check for "//", IPS checks for '/' only...
|
||||
if ((!quote && p[0] == '/')
|
||||
|| (!quote && p[0] == '#')) {
|
||||
break;
|
||||
} else if (p[0] == '\"' || p[0] == '\'') {
|
||||
quote = (p[0] == quote) ? '\0' : p[0];
|
||||
++p;
|
||||
} else if (p + 1 < sline.cend() && p[0] == '\\') {
|
||||
p += 2;
|
||||
} else {
|
||||
++p;
|
||||
}
|
||||
}
|
||||
// now we have the preprocessed string ;)
|
||||
std::string_view pp_str(sline_start, p);
|
||||
if (pp_str.size() > 0 && !parse_line(pp_str)) {
|
||||
break;
|
||||
}
|
||||
static std::string EscapeStringSequences(std::string in) {
|
||||
for (const auto& seq : ESCAPE_CHARACTER_MAP) {
|
||||
for (auto index = in.find(seq.first); index != std::string::npos;
|
||||
index = in.find(seq.first, index)) {
|
||||
in.replace(index, std::strlen(seq.first), seq.second);
|
||||
index += std::strlen(seq.second);
|
||||
}
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
void IPSwitchCompiler::ParseFlag(const std::string& line) {
|
||||
if (StartsWith(line, "@flag offset_shift ")) {
|
||||
// Offset Shift Flag
|
||||
offset_shift = std::strtoll(line.substr(19).c_str(), nullptr, 0);
|
||||
} else if (StartsWith(line, "@little-endian")) {
|
||||
// Set values to read as little endian
|
||||
is_little_endian = true;
|
||||
} else if (StartsWith(line, "@big-endian")) {
|
||||
// Set values to read as big endian
|
||||
is_little_endian = false;
|
||||
} else if (StartsWith(line, "@flag print_values")) {
|
||||
// Force printing of applied values
|
||||
print_values = true;
|
||||
}
|
||||
}
|
||||
|
||||
void IPSwitchCompiler::Parse() {
|
||||
const auto bytes = patch_text->ReadAllBytes();
|
||||
std::stringstream s;
|
||||
s.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
||||
|
||||
std::vector<std::string> lines;
|
||||
std::string stream_line;
|
||||
while (std::getline(s, stream_line)) {
|
||||
// Remove a trailing \r
|
||||
if (!stream_line.empty() && stream_line.back() == '\r')
|
||||
stream_line.pop_back();
|
||||
lines.push_back(std::move(stream_line));
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < lines.size(); ++i) {
|
||||
auto line = lines[i];
|
||||
|
||||
// Remove midline comments
|
||||
std::size_t comment_index = std::string::npos;
|
||||
bool within_string = false;
|
||||
for (std::size_t k = 0; k < line.size(); ++k) {
|
||||
if (line[k] == '\"' && (k > 0 && line[k - 1] != '\\')) {
|
||||
within_string = !within_string;
|
||||
} else if (line[k] == '\\' && (k < line.size() - 1 && line[k + 1] == '\\')) {
|
||||
comment_index = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!StartsWith(line, "//") && comment_index != std::string::npos) {
|
||||
last_comment = line.substr(comment_index + 2);
|
||||
line = line.substr(0, comment_index);
|
||||
}
|
||||
|
||||
if (StartsWith(line, "@stop")) {
|
||||
// Force stop
|
||||
break;
|
||||
} else if (StartsWith(line, "@nsobid-")) {
|
||||
// NSO Build ID Specifier
|
||||
const auto raw_build_id = fmt::format("{:0<64}", line.substr(8));
|
||||
nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
|
||||
} else if (StartsWith(line, "#")) {
|
||||
// Mandatory Comment
|
||||
LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}",
|
||||
patch_text->GetName(), line.substr(1));
|
||||
} else if (StartsWith(line, "//")) {
|
||||
// Normal Comment
|
||||
last_comment = line.substr(2);
|
||||
if (last_comment.find_first_not_of(' ') == std::string::npos)
|
||||
continue;
|
||||
if (last_comment.find_first_not_of(' ') != 0)
|
||||
last_comment = last_comment.substr(last_comment.find_first_not_of(' '));
|
||||
} else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) {
|
||||
// Start of patch
|
||||
const auto enabled = StartsWith(line, "@enabled");
|
||||
if (i == 0)
|
||||
return;
|
||||
LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Parsing patch '{}' ({})",
|
||||
patch_text->GetName(), last_comment, line.substr(1));
|
||||
|
||||
IPSwitchPatch patch{last_comment, enabled, {}};
|
||||
|
||||
// Read rest of patch
|
||||
while (true) {
|
||||
if (i + 1 >= lines.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& patch_line = lines[++i];
|
||||
|
||||
// Patch line may contain comments
|
||||
if (StartsWith(patch_line, "//") || StartsWith(patch_line, "#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start of new patch
|
||||
if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
|
||||
--i;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for a flag
|
||||
if (StartsWith(patch_line, "@")) {
|
||||
ParseFlag(patch_line);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 11 - 8 hex digit offset + space + minimum two digit overwrite val
|
||||
if (patch_line.length() < 11)
|
||||
break;
|
||||
auto offset = std::strtoul(patch_line.substr(0, 8).c_str(), nullptr, 16);
|
||||
offset += static_cast<unsigned long>(offset_shift);
|
||||
|
||||
std::vector<u8> replace;
|
||||
// 9 - first char of replacement val
|
||||
if (patch_line[9] == '\"') {
|
||||
// string replacement
|
||||
auto end_index = patch_line.find('\"', 10);
|
||||
if (end_index == std::string::npos || end_index < 10)
|
||||
return;
|
||||
while (patch_line[end_index - 1] == '\\') {
|
||||
end_index = patch_line.find('\"', end_index + 1);
|
||||
if (end_index == std::string::npos || end_index < 10)
|
||||
return;
|
||||
}
|
||||
|
||||
auto value = patch_line.substr(10, end_index - 10);
|
||||
value = EscapeStringSequences(value);
|
||||
replace.reserve(value.size());
|
||||
std::copy(value.begin(), value.end(), std::back_inserter(replace));
|
||||
} else {
|
||||
// hex replacement
|
||||
const auto value =
|
||||
patch_line.substr(9, patch_line.find_first_of(" /\r\n", 9) - 9);
|
||||
replace = Common::HexStringToVector(value, is_little_endian);
|
||||
}
|
||||
|
||||
if (print_values) {
|
||||
LOG_INFO(Loader,
|
||||
"[IPSwitchCompiler ('{}')] - Patching value at offset {:#08x} "
|
||||
"with byte string '{}'",
|
||||
patch_text->GetName(), offset, Common::HexToString(replace));
|
||||
}
|
||||
|
||||
patch.records.insert_or_assign(static_cast<u32>(offset), std::move(replace));
|
||||
}
|
||||
|
||||
patches.push_back(std::move(patch));
|
||||
} else if (StartsWith(line, "@")) {
|
||||
ParseFlag(line);
|
||||
}
|
||||
}
|
||||
|
||||
valid = true;
|
||||
}
|
||||
|
||||
VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
|
||||
if (in == nullptr)
|
||||
if (in == nullptr || !valid)
|
||||
return nullptr;
|
||||
|
||||
auto in_data = in->ReadAllBytes();
|
||||
|
||||
for (const auto& patch : patches) {
|
||||
if (patch.enabled) {
|
||||
for (const auto& record : patch.records) {
|
||||
if (record.first < in_data.size()) {
|
||||
auto replace_size = record.second.count;
|
||||
if (record.first + replace_size > in_data.size())
|
||||
replace_size = in_data.size() - record.first;
|
||||
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
|
||||
} else {
|
||||
LOG_WARNING(Loader, "record offs={:x},size={:x}", record.first, record.second.data.size());
|
||||
}
|
||||
}
|
||||
if (!patch.enabled)
|
||||
continue;
|
||||
|
||||
for (const auto& record : patch.records) {
|
||||
if (record.first >= in_data.size())
|
||||
continue;
|
||||
auto replace_size = record.second.size();
|
||||
if (record.first + replace_size > in_data.size())
|
||||
replace_size = in_data.size() - record.first;
|
||||
for (std::size_t i = 0; i < replace_size; ++i)
|
||||
in_data[i + record.first] = record.second[i];
|
||||
}
|
||||
}
|
||||
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), in->GetContainingDirectory());
|
||||
|
||||
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
|
||||
in->GetContainingDirectory());
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
@@ -23,17 +20,24 @@ public:
|
||||
~IPSwitchCompiler();
|
||||
|
||||
std::array<u8, 0x20> GetBuildID() const;
|
||||
bool IsValid() const;
|
||||
VirtualFile Apply(const VirtualFile& in) const;
|
||||
|
||||
private:
|
||||
struct IPSwitchPatch;
|
||||
|
||||
void ParseFlag(const std::string& flag);
|
||||
void Parse(std::span<u8 const> bytes);
|
||||
void Parse();
|
||||
|
||||
bool valid = false;
|
||||
|
||||
VirtualFile patch_text;
|
||||
std::vector<IPSwitchPatch> patches;
|
||||
std::array<u8, 0x20> nso_build_id{};
|
||||
bool is_little_endian = false;
|
||||
s64 offset_shift = 0;
|
||||
bool print_values = false;
|
||||
std::string last_comment = "";
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -345,7 +345,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
return exefs;
|
||||
}
|
||||
|
||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||
const std::string& build_id) const {
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||
|
||||
@@ -360,11 +361,16 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
||||
for (const auto& file : exefs_dir->GetFiles()) {
|
||||
if (file->GetExtension() == "ips") {
|
||||
auto name = file->GetName();
|
||||
const auto this_build_id = fmt::format("{:0<64}", name.substr(0, name.find('.')));
|
||||
|
||||
const auto this_build_id =
|
||||
fmt::format("{:0<64}", name.substr(0, name.find('.')));
|
||||
if (nso_build_id == this_build_id)
|
||||
out.push_back(file);
|
||||
} else if (file->GetExtension() == "pchtxt") {
|
||||
IPSwitchCompiler compiler{file};
|
||||
if (!compiler.IsValid())
|
||||
continue;
|
||||
|
||||
const auto this_build_id = Common::HexToString(compiler.GetBuildID());
|
||||
if (nso_build_id == this_build_id)
|
||||
out.push_back(file);
|
||||
@@ -372,6 +378,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -38,7 +38,7 @@ struct RomFSHeader {
|
||||
};
|
||||
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
|
||||
|
||||
struct DirectoryEntry {
|
||||
struct RomFSDirectoryEntry {
|
||||
u32_le parent;
|
||||
u32_le sibling;
|
||||
u32_le child_dir;
|
||||
@@ -46,9 +46,9 @@ struct DirectoryEntry {
|
||||
u32_le hash;
|
||||
u32_le name_length;
|
||||
};
|
||||
static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size.");
|
||||
static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size.");
|
||||
|
||||
struct FileEntry {
|
||||
struct RomFSFileEntry {
|
||||
u32_le parent;
|
||||
u32_le sibling;
|
||||
u64_le offset;
|
||||
@@ -56,7 +56,7 @@ struct FileEntry {
|
||||
u32_le hash;
|
||||
u32_le name_length;
|
||||
};
|
||||
static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
|
||||
static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size.");
|
||||
|
||||
struct RomFSTraversalContext {
|
||||
RomFSHeader header;
|
||||
@@ -84,14 +84,14 @@ std::pair<EntryType, std::string> GetEntry(const RomFSTraversalContext& ctx, siz
|
||||
return {entry, std::move(name)};
|
||||
}
|
||||
|
||||
std::pair<DirectoryEntry, std::string> GetDirectoryEntry(const RomFSTraversalContext& ctx,
|
||||
std::pair<RomFSDirectoryEntry, std::string> GetDirectoryEntry(const RomFSTraversalContext& ctx,
|
||||
size_t directory_offset) {
|
||||
return GetEntry<DirectoryEntry, &RomFSTraversalContext::directory_meta>(ctx, directory_offset);
|
||||
return GetEntry<RomFSDirectoryEntry, &RomFSTraversalContext::directory_meta>(ctx, directory_offset);
|
||||
}
|
||||
|
||||
std::pair<FileEntry, std::string> GetFileEntry(const RomFSTraversalContext& ctx,
|
||||
std::pair<RomFSFileEntry, std::string> GetFileEntry(const RomFSTraversalContext& ctx,
|
||||
size_t file_offset) {
|
||||
return GetEntry<FileEntry, &RomFSTraversalContext::file_meta>(ctx, file_offset);
|
||||
return GetEntry<RomFSFileEntry, &RomFSTraversalContext::file_meta>(ctx, file_offset);
|
||||
}
|
||||
|
||||
void ProcessFile(const RomFSTraversalContext& ctx, u32 this_file_offset,
|
||||
@@ -163,7 +163,7 @@ VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
||||
if (dir == nullptr)
|
||||
return nullptr;
|
||||
|
||||
RomFSBuildContext ctx{dir, ext};
|
||||
RomFSBuilder::RomFSBuildContext ctx{dir, ext};
|
||||
return ConcatenatedVfsFile::MakeConcatenatedFile(0, dir->GetName(), ctx.Build());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -10,6 +10,12 @@
|
||||
#include "common/fs/path_util.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
|
||||
#undef CreateFile
|
||||
#undef DeleteFile
|
||||
#undef CreateDirectory
|
||||
#undef CopyFile
|
||||
#undef MoveFile
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -99,6 +99,10 @@ private:
|
||||
std::string name;
|
||||
};
|
||||
|
||||
#undef CreateFile
|
||||
#undef DeleteFile
|
||||
#undef CreateDirectory
|
||||
|
||||
// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
|
||||
// Vector data is supplied upon construction.
|
||||
class VectorVfsDirectory : public VfsDirectory {
|
||||
|
||||
@@ -1216,7 +1216,7 @@ Result KServerSession::ReceiveRequest(KernelCore& kernel, uintptr_t server_messa
|
||||
}
|
||||
|
||||
Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||
KPhysicalAddress server_message_paddr, bool is_hle, bool session_closed) {
|
||||
KPhysicalAddress server_message_paddr, bool is_hle) {
|
||||
// Lock the session.
|
||||
KScopedLightLock lk{m_lock};
|
||||
|
||||
@@ -1248,7 +1248,7 @@ Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, u
|
||||
KEvent* event = request->GetEvent();
|
||||
|
||||
// Check whether we're closed.
|
||||
const bool closed = (client_thread == nullptr || m_parent->IsClientClosed() || session_closed);
|
||||
const bool closed = (client_thread == nullptr || m_parent->IsClientClosed());
|
||||
|
||||
Result result = ResultSuccess;
|
||||
if (!closed) {
|
||||
|
||||
@@ -54,14 +54,14 @@ public:
|
||||
|
||||
Result OnRequest(KernelCore& kernel, KSessionRequest* request);
|
||||
Result SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||
KPhysicalAddress server_message_paddr, bool is_hle = false, bool session_closed = false);
|
||||
KPhysicalAddress server_message_paddr, bool is_hle = false);
|
||||
Result ReceiveRequest(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||
KPhysicalAddress server_message_paddr,
|
||||
std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
|
||||
std::weak_ptr<Service::SessionRequestManager> manager = {});
|
||||
|
||||
Result SendReplyHLE(KernelCore& kernel, bool session_closed = false) {
|
||||
R_RETURN(this->SendReply(kernel, 0, 0, 0, true, session_closed));
|
||||
Result SendReplyHLE(KernelCore& kernel) {
|
||||
R_RETURN(this->SendReply(kernel, 0, 0, 0, true));
|
||||
}
|
||||
|
||||
Result ReceiveRequestHLE(KernelCore& kernel, std::shared_ptr<Service::HLERequestContext>* out_context,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-late
|
||||
@@ -13,6 +13,10 @@
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
|
||||
#undef OutputDebugString
|
||||
#undef GetObject
|
||||
#undef CreateProcess
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
static uint32_t GetArg32(std::span<uint64_t, 8> args, int n) {
|
||||
|
||||
@@ -17,12 +17,56 @@
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
constexpr auto MAX_MSG_TIME = std::chrono::milliseconds(250);
|
||||
const auto MAX_MSG_SIZE = 0x1000;
|
||||
|
||||
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
||||
Result OutputDebugString(Core::System& system, u64 address, u64 len) {
|
||||
static struct DebugFlusher {
|
||||
std::string msg_buffer;
|
||||
std::mutex msg_mutex;
|
||||
std::condition_variable msg_cv;
|
||||
std::chrono::steady_clock::time_point last_msg_time;
|
||||
std::optional<std::jthread> thread;
|
||||
} flusher_data;
|
||||
R_SUCCEED_IF(len == 0);
|
||||
std::string msg_buffer(len, 0);
|
||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, msg_buffer.data(), len);
|
||||
LOG_INFO(Debug_Emulated, "{}", msg_buffer);
|
||||
// Only start the thread the very first time this function is called
|
||||
if (!flusher_data.thread) {
|
||||
flusher_data.thread.emplace([](std::stop_token stop_token) {
|
||||
while (!stop_token.stop_requested()) {
|
||||
std::unique_lock lock(flusher_data.msg_mutex);
|
||||
flusher_data.msg_cv.wait(lock, [&stop_token] {
|
||||
return !flusher_data.msg_buffer.empty() || stop_token.stop_requested();
|
||||
});
|
||||
if (stop_token.stop_requested() && flusher_data.msg_buffer.empty())
|
||||
break;
|
||||
auto timeout = flusher_data.last_msg_time + MAX_MSG_TIME;
|
||||
bool woke_early = flusher_data.msg_cv.wait_until(lock, timeout, [&stop_token] {
|
||||
return flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested();
|
||||
});
|
||||
if (!woke_early || flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested()) {
|
||||
if (!flusher_data.msg_buffer.empty()) {
|
||||
// Remove trailing newline as LOG_INFO adds that anyways
|
||||
if (flusher_data.msg_buffer.back() == '\n')
|
||||
flusher_data.msg_buffer.pop_back();
|
||||
|
||||
LOG_INFO(Debug_Emulated, "\n{}", flusher_data.msg_buffer);
|
||||
flusher_data.msg_buffer.clear();
|
||||
}
|
||||
if (stop_token.stop_requested()) break;
|
||||
}
|
||||
}
|
||||
flusher_data.msg_cv.notify_all();
|
||||
});
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(flusher_data.msg_mutex);
|
||||
const auto old_size = flusher_data.msg_buffer.size();
|
||||
flusher_data.msg_buffer.resize(old_size + len);
|
||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, flusher_data.msg_buffer.data() + old_size, len);
|
||||
flusher_data.last_msg_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
flusher_data.msg_cv.notify_one();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
namespace Kernel::Svc {
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
|
||||
[[nodiscard]] inline constexpr bool IsValidSetAddressRange(u64 address, u64 size) {
|
||||
return address + size > address;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case MemoryPermission::None:
|
||||
case MemoryPermission::Read:
|
||||
@@ -22,13 +26,6 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if address + size is greater than the given address
|
||||
// This can return false if the size causes an overflow of a 64-bit type
|
||||
// or if the given size is zero.
|
||||
constexpr bool IsValidAddressRange(u64 address, u64 size) {
|
||||
return address + size > address;
|
||||
}
|
||||
|
||||
// Helper function that performs the common sanity checks for svcMapMemory
|
||||
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
|
||||
// in the same order.
|
||||
@@ -53,14 +50,17 @@ Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr
|
||||
R_THROW(ResultInvalidSize);
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(dst_addr, size)) {
|
||||
// Checks if address + size is greater than the given address
|
||||
// This can return false if the size causes an overflow of a 64-bit type
|
||||
// or if the given size is zero.
|
||||
if (!IsValidSetAddressRange(dst_addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Destination is not a valid address range, addr={:#016x}, size={:#016x}",
|
||||
dst_addr, size);
|
||||
R_THROW(ResultInvalidCurrentMemory);
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(src_addr, size)) {
|
||||
if (!IsValidSetAddressRange(src_addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr={:#016x}, size={:#016x}",
|
||||
src_addr, size);
|
||||
R_THROW(ResultInvalidCurrentMemory);
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
namespace Kernel::Svc {
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidAddressRange(u64 address, u64 size) {
|
||||
[[nodiscard]] inline constexpr bool IsValidAddressRange(u64 address, u64 size) {
|
||||
return address + size > address;
|
||||
}
|
||||
|
||||
constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
|
||||
[[nodiscard]] inline constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case Svc::MemoryPermission::None:
|
||||
case Svc::MemoryPermission::Read:
|
||||
|
||||
@@ -9,13 +9,12 @@
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::AudioIn;
|
||||
|
||||
IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
|
||||
const std::string& device_name, const AudioInParameter& in_params,
|
||||
IAudioIn::IAudioIn(Core::System& system_, AudioCore::AudioIn::Manager& manager, size_t session_id,
|
||||
const std::string& device_name, const AudioCore::AudioIn::AudioInParameter& in_params,
|
||||
Kernel::KProcess* handle, u64 applet_resource_user_id)
|
||||
: ServiceFramework{system_, "IAudioIn"}, process{handle}, service_context{system_, "IAudioIn"},
|
||||
event{service_context.CreateEvent("AudioInEvent")}, impl{std::make_shared<In>(system_,
|
||||
event{service_context.CreateEvent("AudioInEvent")}, impl{std::make_shared<AudioCore::AudioIn::In>(system_,
|
||||
manager, event,
|
||||
session_id)} {
|
||||
// clang-format off
|
||||
@@ -71,12 +70,12 @@ Result IAudioIn::Stop() {
|
||||
R_RETURN(impl->StopSystem());
|
||||
}
|
||||
|
||||
Result IAudioIn::AppendAudioInBuffer(InArray<AudioInBuffer, BufferAttr_HipcMapAlias> buffer,
|
||||
Result IAudioIn::AppendAudioInBuffer(InArray<AudioCore::AudioIn::AudioInBuffer, BufferAttr_HipcMapAlias> buffer,
|
||||
u64 buffer_client_ptr) {
|
||||
R_RETURN(this->AppendAudioInBufferAuto(buffer, buffer_client_ptr));
|
||||
}
|
||||
|
||||
Result IAudioIn::AppendAudioInBufferAuto(InArray<AudioInBuffer, BufferAttr_HipcAutoSelect> buffer,
|
||||
Result IAudioIn::AppendAudioInBufferAuto(InArray<AudioCore::AudioIn::AudioInBuffer, BufferAttr_HipcAutoSelect> buffer,
|
||||
u64 buffer_client_ptr) {
|
||||
if (buffer.empty()) {
|
||||
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!");
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -7,7 +10,6 @@
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::AudioIn;
|
||||
|
||||
IAudioInManager::IAudioInManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audin:u"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
|
||||
@@ -34,11 +36,11 @@ Result IAudioInManager::ListAudioIns(
|
||||
R_RETURN(this->ListAudioInsAutoFiltered(out_audio_ins, out_count));
|
||||
}
|
||||
|
||||
Result IAudioInManager::OpenAudioIn(Out<AudioInParameterInternal> out_parameter_internal,
|
||||
Result IAudioInManager::OpenAudioIn(Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal,
|
||||
Out<SharedPointer<IAudioIn>> out_audio_in,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name,
|
||||
AudioInParameter parameter,
|
||||
AudioCore::AudioIn::AudioInParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
ClientAppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
@@ -53,9 +55,9 @@ Result IAudioInManager::ListAudioInsAuto(
|
||||
}
|
||||
|
||||
Result IAudioInManager::OpenAudioInAuto(
|
||||
Out<AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in,
|
||||
Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioInParameter parameter,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioCore::AudioIn::AudioInParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_RETURN(this->OpenAudioInProtocolSpecified(out_parameter_internal, out_audio_in, out_name,
|
||||
@@ -70,10 +72,10 @@ Result IAudioInManager::ListAudioInsAutoFiltered(
|
||||
}
|
||||
|
||||
Result IAudioInManager::OpenAudioInProtocolSpecified(
|
||||
Out<AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in,
|
||||
Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, Protocol protocol,
|
||||
AudioInParameter parameter, InCopyHandle<Kernel::KProcess> process_handle,
|
||||
AudioCore::AudioIn::AudioInParameter parameter, InCopyHandle<Kernel::KProcess> process_handle,
|
||||
ClientAppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
@@ -104,7 +106,7 @@ Result IAudioInManager::OpenAudioInProtocolSpecified(
|
||||
|
||||
auto& out_system = impl->sessions[new_session_id]->GetSystem();
|
||||
*out_parameter_internal =
|
||||
AudioInParameterInternal{.sample_rate = out_system.GetSampleRate(),
|
||||
AudioCore::AudioIn::AudioInParameterInternal{.sample_rate = out_system.GetSampleRate(),
|
||||
.channel_count = out_system.GetChannelCount(),
|
||||
.sample_format = static_cast<u32>(out_system.GetSampleFormat()),
|
||||
.state = static_cast<u32>(out_system.GetState())};
|
||||
|
||||
@@ -13,10 +13,9 @@
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::AudioOut;
|
||||
|
||||
IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id,
|
||||
const std::string& device_name, const AudioOutParameter& in_params,
|
||||
IAudioOut::IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, size_t session_id,
|
||||
const std::string& device_name, const AudioCore::AudioOut::AudioOutParameter& in_params,
|
||||
Kernel::KProcess* handle, u64 applet_resource_user_id)
|
||||
: ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
|
||||
event{service_context.CreateEvent("AudioOutEvent")}, process{handle},
|
||||
@@ -68,12 +67,12 @@ Result IAudioOut::Stop() {
|
||||
}
|
||||
|
||||
Result IAudioOut::AppendAudioOutBuffer(
|
||||
InArray<AudioOutBuffer, BufferAttr_HipcMapAlias> audio_out_buffer, u64 buffer_client_ptr) {
|
||||
InArray<AudioCore::AudioOut::AudioOutBuffer, BufferAttr_HipcMapAlias> audio_out_buffer, u64 buffer_client_ptr) {
|
||||
R_RETURN(this->AppendAudioOutBufferAuto(audio_out_buffer, buffer_client_ptr));
|
||||
}
|
||||
|
||||
Result IAudioOut::AppendAudioOutBufferAuto(
|
||||
InArray<AudioOutBuffer, BufferAttr_HipcAutoSelect> audio_out_buffer, u64 buffer_client_ptr) {
|
||||
InArray<AudioCore::AudioOut::AudioOutBuffer, BufferAttr_HipcAutoSelect> audio_out_buffer, u64 buffer_client_ptr) {
|
||||
if (audio_out_buffer.empty()) {
|
||||
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!");
|
||||
R_THROW(Audio::ResultInsufficientBuffer);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::AudioOut;
|
||||
|
||||
IAudioOutManager::IAudioOutManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audout:u"}
|
||||
@@ -36,11 +35,11 @@ Result IAudioOutManager::ListAudioOuts(
|
||||
R_RETURN(this->ListAudioOutsAuto(out_audio_outs, out_count));
|
||||
}
|
||||
|
||||
Result IAudioOutManager::OpenAudioOut(Out<AudioOutParameterInternal> out_parameter_internal,
|
||||
Result IAudioOutManager::OpenAudioOut(Out<AudioCore::AudioOut::AudioOutParameterInternal> out_parameter_internal,
|
||||
Out<SharedPointer<IAudioOut>> out_audio_out,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name,
|
||||
AudioOutParameter parameter,
|
||||
AudioCore::AudioOut::AudioOutParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
ClientAppletResourceUserId aruid) {
|
||||
R_RETURN(this->OpenAudioOutAuto(out_parameter_internal, out_audio_out, out_name, name,
|
||||
@@ -62,10 +61,10 @@ Result IAudioOutManager::ListAudioOutsAuto(
|
||||
}
|
||||
|
||||
Result IAudioOutManager::OpenAudioOutAuto(
|
||||
Out<AudioOutParameterInternal> out_parameter_internal,
|
||||
Out<AudioCore::AudioOut::AudioOutParameterInternal> out_parameter_internal,
|
||||
Out<SharedPointer<IAudioOut>> out_audio_out,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioOutParameter parameter,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioCore::AudioOut::AudioOutParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) {
|
||||
if (!process_handle) {
|
||||
LOG_ERROR(Service_Audio, "Failed to get process handle");
|
||||
@@ -95,7 +94,7 @@ Result IAudioOutManager::OpenAudioOutAuto(
|
||||
|
||||
auto& out_system = impl->sessions[new_session_id]->GetSystem();
|
||||
*out_parameter_internal =
|
||||
AudioOutParameterInternal{.sample_rate = out_system.GetSampleRate(),
|
||||
AudioCore::AudioOut::AudioOutParameterInternal{.sample_rate = out_system.GetSampleRate(),
|
||||
.channel_count = out_system.GetChannelCount(),
|
||||
.sample_format = static_cast<u32>(out_system.GetSampleFormat()),
|
||||
.state = static_cast<u32>(out_system.GetState())};
|
||||
|
||||
@@ -4,21 +4,20 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/renderer/audio_renderer.h"
|
||||
#include "core/hle/service/audio/audio_renderer.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::Renderer;
|
||||
|
||||
IAudioRenderer::IAudioRenderer(Core::System& system_, Manager& manager_,
|
||||
IAudioRenderer::IAudioRenderer(Core::System& system_, AudioCore::Renderer::Manager& manager_,
|
||||
AudioCore::AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||
Kernel::KProcess* process_handle_, u64 applet_resource_user_id,
|
||||
s32 session_id)
|
||||
: ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"},
|
||||
rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_},
|
||||
impl{std::make_unique<Renderer>(system_, manager, rendered_event)}, process_handle{
|
||||
process_handle_} {
|
||||
impl{std::make_unique<AudioCore::Renderer::Renderer>(system_, manager, rendered_event)}, process_handle{process_handle_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioRenderer::GetSampleRate>, "GetSampleRate"},
|
||||
|
||||
@@ -14,16 +14,13 @@
|
||||
#include <cstring>
|
||||
|
||||
namespace Service::News {
|
||||
namespace {
|
||||
|
||||
std::string_view ToStringView(std::span<const char> buf) {
|
||||
[[nodiscard]] inline std::string_view ToStringViewNDS(std::span<const char> buf) {
|
||||
const std::string_view sv{buf.data(), buf.size()};
|
||||
const auto nul = sv.find('\0');
|
||||
return nul == std::string_view::npos ? sv : sv.substr(0, nul);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
INewsDataService::INewsDataService(Core::System& system_)
|
||||
: ServiceFramework{system_, "INewsDataService"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -55,7 +52,7 @@ bool INewsDataService::TryOpen(std::string_view key, std::string_view user) {
|
||||
|
||||
const auto list = NewsStorage::Instance().ListAll();
|
||||
if (!list.empty()) {
|
||||
if (auto found = NewsStorage::Instance().FindByNewsId(ToStringView(list.front().news_id))) {
|
||||
if (auto found = NewsStorage::Instance().FindByNewsId(ToStringViewNDS(list.front().news_id))) {
|
||||
opened_payload = std::move(found->payload);
|
||||
return true;
|
||||
}
|
||||
@@ -67,7 +64,7 @@ bool INewsDataService::TryOpen(std::string_view key, std::string_view user) {
|
||||
Result INewsDataService::Open(InBuffer<BufferAttr_HipcMapAlias> name) {
|
||||
EnsureBuiltinNewsLoaded();
|
||||
|
||||
const auto key = ToStringView({reinterpret_cast<const char*>(name.data()), name.size()});
|
||||
const auto key = ToStringViewNDS({reinterpret_cast<const char*>(name.data()), name.size()});
|
||||
|
||||
if (TryOpen(key, {})) {
|
||||
R_SUCCEED();
|
||||
@@ -79,8 +76,8 @@ Result INewsDataService::Open(InBuffer<BufferAttr_HipcMapAlias> name) {
|
||||
Result INewsDataService::OpenWithNewsRecordV1(NewsRecordV1 record) {
|
||||
EnsureBuiltinNewsLoaded();
|
||||
|
||||
const auto key = ToStringView(record.news_id);
|
||||
const auto user = ToStringView(record.user_id);
|
||||
const auto key = ToStringViewNDS(record.news_id);
|
||||
const auto user = ToStringViewNDS(record.user_id);
|
||||
|
||||
if (TryOpen(key, user)) {
|
||||
R_SUCCEED();
|
||||
@@ -92,8 +89,8 @@ Result INewsDataService::OpenWithNewsRecordV1(NewsRecordV1 record) {
|
||||
Result INewsDataService::OpenWithNewsRecord(NewsRecord record) {
|
||||
EnsureBuiltinNewsLoaded();
|
||||
|
||||
const auto key = ToStringView(record.news_id);
|
||||
const auto user = ToStringView(record.user_id);
|
||||
const auto key = ToStringViewNDS(record.news_id);
|
||||
const auto user = ToStringViewNDS(record.user_id);
|
||||
|
||||
if (TryOpen(key, user)) {
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -15,13 +15,13 @@
|
||||
namespace Service::News {
|
||||
namespace {
|
||||
|
||||
std::string_view ToStringView(std::span<const u8> buf) {
|
||||
[[nodiscard]] inline std::string_view ToStringView(std::span<const u8> buf) {
|
||||
if (buf.empty()) return {};
|
||||
auto data = reinterpret_cast<const char*>(buf.data());
|
||||
return {data, strnlen(data, buf.size())};
|
||||
}
|
||||
|
||||
std::string_view ToStringView(std::span<const char> buf) {
|
||||
[[nodiscard]] inline std::string_view ToStringView(std::span<const char> buf) {
|
||||
if (buf.empty()) return {};
|
||||
return {buf.data(), strnlen(buf.data(), buf.size())};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -6,6 +9,8 @@
|
||||
|
||||
namespace Service::News {
|
||||
|
||||
#undef CreateEvent
|
||||
|
||||
IOverwriteEventHolder::IOverwriteEventHolder(Core::System& system_)
|
||||
: ServiceFramework{system_, "IOverwriteEventHolder"}, service_context{system_,
|
||||
"IOverwriteEventHolder"} {
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
#undef GetCurrentTime
|
||||
|
||||
namespace Service::Capture {
|
||||
|
||||
AlbumManager::AlbumManager(Core::System& system_) : system{system_} {}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/reporter.h"
|
||||
|
||||
#undef far
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
#undef CreateFile
|
||||
#undef DeleteFile
|
||||
#undef CreateDirectory
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
||||
@@ -227,13 +231,12 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
|
||||
std::string src_path(Common::FS::SanitizePath(src_path_));
|
||||
std::string dest_path(Common::FS::SanitizePath(dest_path_));
|
||||
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
||||
if (src == nullptr)
|
||||
return FileSys::ResultPathNotFound;
|
||||
|
||||
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
|
||||
std::string full_src_path = backing->GetFullPath() + "/" + src_path;
|
||||
std::string full_dest_path = backing->GetFullPath() + "/" + dest_path;
|
||||
if (!Common::FS::RenameDir(full_src_path, full_dest_path)) {
|
||||
// Use more-optimized vfs implementation rename.
|
||||
if (src == nullptr)
|
||||
return FileSys::ResultPathNotFound;
|
||||
if (!src->Rename(Common::FS::GetFilename(dest_path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
return ResultSuccess;
|
||||
|
||||
@@ -24,7 +24,7 @@ IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGe
|
||||
{3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
|
||||
{4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
|
||||
{5, D<&IFileSystem::RenameFile>, "RenameFile"},
|
||||
{6, D<&IFileSystem::RenameDirectory>, "RenameDirectory"},
|
||||
{6, nullptr, "RenameDirectory"},
|
||||
{7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
|
||||
{8, D<&IFileSystem::OpenFile>, "OpenFile"},
|
||||
{9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
|
||||
@@ -88,14 +88,6 @@ Result IFileSystem::RenameFile(
|
||||
R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
||||
}
|
||||
|
||||
Result IFileSystem::RenameDirectory(
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path) {
|
||||
LOG_DEBUG(Service_FS, "called. directory '{}' to directory '{}'", old_path->str, new_path->str);
|
||||
|
||||
R_RETURN(backend->RenameDirectory(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
||||
}
|
||||
|
||||
Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||
u32 mode) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -39,8 +36,6 @@ public:
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||
Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
||||
Result RenameDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
||||
Result OpenFile(OutInterface<IFile> out_interface,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
|
||||
Result OpenDirectory(OutInterface<IDirectory> out_interface,
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#undef SendMessage
|
||||
|
||||
namespace Service {
|
||||
|
||||
SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_)
|
||||
|
||||
@@ -212,8 +212,9 @@ struct NifmNetworkProfileData {
|
||||
NifmWirelessSettingData wireless_setting_data{};
|
||||
IpSettingData ip_setting_data{};
|
||||
};
|
||||
static_assert(sizeof(NifmNetworkProfileData) == 0x18E,
|
||||
"NifmNetworkProfileData has incorrect size.");
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(NifmNetworkProfileData) == 0x18E, "NifmNetworkProfileData has incorrect size.");
|
||||
|
||||
struct PendingProfile {
|
||||
std::array<char, 0x21> ssid{};
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||
@@ -72,23 +71,17 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
|
||||
|
||||
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
||||
system.SetNVDECActive(true);
|
||||
sessions[fd] = session_id;
|
||||
if (const auto* session = core.GetSession(session_id);
|
||||
session != nullptr && session->process != nullptr) {
|
||||
system.NotifyNVDECChannelOpen(session->process->GetId());
|
||||
}
|
||||
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
|
||||
}
|
||||
|
||||
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
||||
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
||||
system.SetNVDECActive(false);
|
||||
auto it = sessions.find(fd);
|
||||
if (it != sessions.end()) {
|
||||
if (const auto* session = core.GetSession(it->second);
|
||||
session != nullptr && session->process != nullptr) {
|
||||
system.NotifyNVDECChannelClose(session->process->GetId());
|
||||
}
|
||||
sessions.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,6 +13,8 @@
|
||||
namespace Service::PSC::Time {
|
||||
class ContextWriter;
|
||||
|
||||
#undef GetCurrentTime
|
||||
|
||||
class SystemClockCore {
|
||||
public:
|
||||
explicit SystemClockCore(SteadyClockCore& steady_clock) : m_steady_clock{steady_clock} {}
|
||||
|
||||
@@ -19,6 +19,8 @@ class System;
|
||||
|
||||
namespace Service::PSC::Time {
|
||||
|
||||
#undef GetCurrentTime
|
||||
|
||||
class SystemClock final : public ServiceFramework<SystemClock> {
|
||||
public:
|
||||
explicit SystemClock(Core::System& system, SystemClockCore& system_clock_core, bool can_write_clock, bool can_write_uninitialized_clock);
|
||||
|
||||
@@ -393,7 +393,7 @@ Result ServerManager::CompleteSyncRequest(Session* session) {
|
||||
}
|
||||
|
||||
// Send the reply.
|
||||
res = server_session->SendReplyHLE(m_system.Kernel(), service_res == IPC::ResultSessionClosed);
|
||||
res = server_session->SendReplyHLE(m_system.Kernel());
|
||||
|
||||
// If the session has been closed, we're done.
|
||||
if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) {
|
||||
|
||||
@@ -4,16 +4,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <fmt/ranges.h>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
@@ -37,7 +33,6 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
|
||||
: SessionRequestHandler(system_.Kernel(), service_name_)
|
||||
, system{system_}
|
||||
, service_name{service_name_}
|
||||
, is_i_storage{std::string_view{service_name_} == "IStorage"}
|
||||
, handler_invoker{handler_invoker_}
|
||||
, max_sessions{max_sessions_}
|
||||
{}
|
||||
@@ -82,22 +77,13 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
||||
const auto command = ctx.GetCommand();
|
||||
auto it = handlers.find(command);
|
||||
const bool is_cmd_read = command == 0;
|
||||
auto it = handlers.find(ctx.GetCommand());
|
||||
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
||||
if (info == nullptr || info->handler_callback == nullptr)
|
||||
return ReportUnimplementedFunction(ctx, info);
|
||||
|
||||
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
||||
handler_invoker(this, info->handler_callback, ctx);
|
||||
|
||||
if (is_i_storage && is_cmd_read) {
|
||||
const auto* const process = ctx.GetThread().GetOwnerProcess();
|
||||
if (process != nullptr && system.IsNVDECActiveForProcess(process->GetId())) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{600});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
||||
|
||||
@@ -107,8 +107,6 @@ protected:
|
||||
Core::System& system;
|
||||
/// Identifier string used to connect to the service.
|
||||
const char* service_name;
|
||||
/// Whether this is the IStorage service.
|
||||
const bool is_i_storage;
|
||||
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
||||
InvokerFn* handler_invoker;
|
||||
/// Maximum number of concurrent sessions that this service can handle.
|
||||
|
||||
@@ -54,11 +54,11 @@ void PutValue(std::span<u8> buffer, const T& t) {
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
void BSD::PollWork::Execute(BSD* bsd) {
|
||||
void NetworkBSD::PollWork::Execute(NetworkBSD* bsd) {
|
||||
std::tie(ret, bsd_errno) = bsd->PollImpl(write_buffer, read_buffer, nfds, timeout);
|
||||
}
|
||||
|
||||
void BSD::PollWork::Response(HLERequestContext& ctx) {
|
||||
void NetworkBSD::PollWork::Response(HLERequestContext& ctx) {
|
||||
if (write_buffer.size() > 0) {
|
||||
ctx.WriteBuffer(write_buffer);
|
||||
}
|
||||
@@ -69,11 +69,11 @@ void BSD::PollWork::Response(HLERequestContext& ctx) {
|
||||
rb.PushEnum(bsd_errno);
|
||||
}
|
||||
|
||||
void BSD::AcceptWork::Execute(BSD* bsd) {
|
||||
void NetworkBSD::AcceptWork::Execute(NetworkBSD* bsd) {
|
||||
std::tie(ret, bsd_errno) = bsd->AcceptImpl(fd, write_buffer);
|
||||
}
|
||||
|
||||
void BSD::AcceptWork::Response(HLERequestContext& ctx) {
|
||||
void NetworkBSD::AcceptWork::Response(HLERequestContext& ctx) {
|
||||
if (write_buffer.size() > 0) {
|
||||
ctx.WriteBuffer(write_buffer);
|
||||
}
|
||||
@@ -85,22 +85,22 @@ void BSD::AcceptWork::Response(HLERequestContext& ctx) {
|
||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||
}
|
||||
|
||||
void BSD::ConnectWork::Execute(BSD* bsd) {
|
||||
void NetworkBSD::ConnectWork::Execute(NetworkBSD* bsd) {
|
||||
bsd_errno = bsd->ConnectImpl(fd, addr);
|
||||
}
|
||||
|
||||
void BSD::ConnectWork::Response(HLERequestContext& ctx) {
|
||||
void NetworkBSD::ConnectWork::Response(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
|
||||
rb.PushEnum(bsd_errno);
|
||||
}
|
||||
|
||||
void BSD::RecvWork::Execute(BSD* bsd) {
|
||||
void NetworkBSD::RecvWork::Execute(NetworkBSD* bsd) {
|
||||
std::tie(ret, bsd_errno) = bsd->RecvImpl(fd, flags, message);
|
||||
}
|
||||
|
||||
void BSD::RecvWork::Response(HLERequestContext& ctx) {
|
||||
void NetworkBSD::RecvWork::Response(HLERequestContext& ctx) {
|
||||
ctx.WriteBuffer(message);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
@@ -109,11 +109,11 @@ void BSD::RecvWork::Response(HLERequestContext& ctx) {
|
||||
rb.PushEnum(bsd_errno);
|
||||
}
|
||||
|
||||
void BSD::RecvFromWork::Execute(BSD* bsd) {
|
||||
void NetworkBSD::RecvFromWork::Execute(NetworkBSD* bsd) {
|
||||
std::tie(ret, bsd_errno) = bsd->RecvFromImpl(fd, flags, message, addr);
|
||||
}
|
||||
|
||||
void BSD::RecvFromWork::Response(HLERequestContext& ctx) {
|
||||
void NetworkBSD::RecvFromWork::Response(HLERequestContext& ctx) {
|
||||
ctx.WriteBuffer(message, 0);
|
||||
if (!addr.empty()) {
|
||||
ctx.WriteBuffer(addr, 1);
|
||||
@@ -126,29 +126,29 @@ void BSD::RecvFromWork::Response(HLERequestContext& ctx) {
|
||||
rb.Push<u32>(static_cast<u32>(addr.size()));
|
||||
}
|
||||
|
||||
void BSD::SendWork::Execute(BSD* bsd) {
|
||||
void NetworkBSD::SendWork::Execute(NetworkBSD* bsd) {
|
||||
std::tie(ret, bsd_errno) = bsd->SendImpl(fd, flags, message);
|
||||
}
|
||||
|
||||
void BSD::SendWork::Response(HLERequestContext& ctx) {
|
||||
void NetworkBSD::SendWork::Response(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<s32>(ret);
|
||||
rb.PushEnum(bsd_errno);
|
||||
}
|
||||
|
||||
void BSD::SendToWork::Execute(BSD* bsd) {
|
||||
void NetworkBSD::SendToWork::Execute(NetworkBSD* bsd) {
|
||||
std::tie(ret, bsd_errno) = bsd->SendToImpl(fd, flags, message, addr);
|
||||
}
|
||||
|
||||
void BSD::SendToWork::Response(HLERequestContext& ctx) {
|
||||
void NetworkBSD::SendToWork::Response(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<s32>(ret);
|
||||
rb.PushEnum(bsd_errno);
|
||||
}
|
||||
|
||||
void BSD::RegisterClient(HLERequestContext& ctx) {
|
||||
void NetworkBSD::RegisterClient(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -157,7 +157,7 @@ void BSD::RegisterClient(HLERequestContext& ctx) {
|
||||
rb.Push<s32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD::StartMonitoring(HLERequestContext& ctx) {
|
||||
void NetworkBSD::StartMonitoring(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -165,7 +165,7 @@ void BSD::StartMonitoring(HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void BSD::Socket(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Socket(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u32 domain = rp.Pop<u32>();
|
||||
const u32 type = rp.Pop<u32>();
|
||||
@@ -200,7 +200,7 @@ void BSD::SocketExempt(HLERequestContext& ctx) {
|
||||
rb.PushEnum(bsd_errno);
|
||||
}
|
||||
|
||||
void BSD::Select(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Select(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
@@ -210,7 +210,7 @@ void BSD::Select(HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD::Poll(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Poll(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 nfds = rp.Pop<s32>();
|
||||
const s32 timeout = rp.Pop<s32>();
|
||||
@@ -225,7 +225,7 @@ void BSD::Poll(HLERequestContext& ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
void BSD::Accept(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Accept(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
|
||||
@@ -237,7 +237,7 @@ void BSD::Accept(HLERequestContext& ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
void BSD::Bind(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Bind(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
|
||||
@@ -245,7 +245,7 @@ void BSD::Bind(HLERequestContext& ctx) {
|
||||
BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer()));
|
||||
}
|
||||
|
||||
void BSD::Connect(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Connect(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
|
||||
@@ -257,7 +257,7 @@ void BSD::Connect(HLERequestContext& ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
void BSD::GetPeerName(HLERequestContext& ctx) {
|
||||
void NetworkBSD::GetPeerName(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
|
||||
@@ -275,7 +275,7 @@ void BSD::GetPeerName(HLERequestContext& ctx) {
|
||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||
}
|
||||
|
||||
void BSD::GetSockName(HLERequestContext& ctx) {
|
||||
void NetworkBSD::GetSockName(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
|
||||
@@ -293,7 +293,7 @@ void BSD::GetSockName(HLERequestContext& ctx) {
|
||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||
}
|
||||
|
||||
void BSD::GetSockOpt(HLERequestContext& ctx) {
|
||||
void NetworkBSD::GetSockOpt(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
const u32 level = rp.Pop<u32>();
|
||||
@@ -315,7 +315,7 @@ void BSD::GetSockOpt(HLERequestContext& ctx) {
|
||||
rb.Push<u32>(static_cast<u32>(optval.size()));
|
||||
}
|
||||
|
||||
void BSD::Listen(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Listen(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
const s32 backlog = rp.Pop<s32>();
|
||||
@@ -325,7 +325,7 @@ void BSD::Listen(HLERequestContext& ctx) {
|
||||
BuildErrnoResponse(ctx, ListenImpl(fd, backlog));
|
||||
}
|
||||
|
||||
void BSD::Fcntl(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Fcntl(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
const s32 cmd = rp.Pop<s32>();
|
||||
@@ -341,7 +341,7 @@ void BSD::Fcntl(HLERequestContext& ctx) {
|
||||
rb.PushEnum(bsd_errno);
|
||||
}
|
||||
|
||||
void BSD::SetSockOpt(HLERequestContext& ctx) {
|
||||
void NetworkBSD::SetSockOpt(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
@@ -355,7 +355,7 @@ void BSD::SetSockOpt(HLERequestContext& ctx) {
|
||||
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
|
||||
}
|
||||
|
||||
void BSD::Shutdown(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Shutdown(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
@@ -366,7 +366,7 @@ void BSD::Shutdown(HLERequestContext& ctx) {
|
||||
BuildErrnoResponse(ctx, ShutdownImpl(fd, how));
|
||||
}
|
||||
|
||||
void BSD::Recv(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Recv(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
@@ -381,7 +381,7 @@ void BSD::Recv(HLERequestContext& ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
void BSD::RecvFrom(HLERequestContext& ctx) {
|
||||
void NetworkBSD::RecvFrom(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
@@ -398,7 +398,7 @@ void BSD::RecvFrom(HLERequestContext& ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
void BSD::Send(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Send(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
@@ -413,7 +413,7 @@ void BSD::Send(HLERequestContext& ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
void BSD::SendTo(HLERequestContext& ctx) {
|
||||
void NetworkBSD::SendTo(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
const u32 flags = rp.Pop<u32>();
|
||||
@@ -429,7 +429,7 @@ void BSD::SendTo(HLERequestContext& ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
void BSD::Write(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Write(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
|
||||
@@ -442,7 +442,7 @@ void BSD::Write(HLERequestContext& ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
void BSD::Read(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Read(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
|
||||
@@ -454,7 +454,7 @@ void BSD::Read(HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD::Close(HLERequestContext& ctx) {
|
||||
void NetworkBSD::Close(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
|
||||
@@ -464,7 +464,7 @@ void BSD::Close(HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
/// @brief Only bsd:s is able to dup()
|
||||
void BSD::DuplicateSocket(HLERequestContext& ctx) {
|
||||
void NetworkBSD::DuplicateSocket(HLERequestContext& ctx) {
|
||||
struct InputParameters {
|
||||
s32 fd;
|
||||
u64 reserved;
|
||||
@@ -505,7 +505,7 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
void BSD::EventFd(HLERequestContext& ctx) {
|
||||
void NetworkBSD::EventFd(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 initval = rp.Pop<u64>();
|
||||
const u32 flags = rp.Pop<u32>();
|
||||
@@ -516,12 +516,12 @@ void BSD::EventFd(HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
template <typename Work>
|
||||
void BSD::ExecuteWork(HLERequestContext& ctx, Work work) {
|
||||
void NetworkBSD::ExecuteWork(HLERequestContext& ctx, Work work) {
|
||||
work.Execute(this);
|
||||
work.Response(ctx);
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
|
||||
std::pair<s32, Errno> NetworkBSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
|
||||
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
|
||||
if (is_user && (type == Type::SEQPACKET || type == Type::RAW)) {
|
||||
if (type == Type::RAW && domain == Domain::INET && protocol == Protocol::ICMP) {
|
||||
@@ -565,7 +565,7 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
|
||||
return {fd, Errno::SUCCESS};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
|
||||
std::pair<s32, Errno> NetworkBSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
|
||||
s32 nfds, s32 timeout) {
|
||||
if (nfds <= 0) {
|
||||
// When no entries are provided, -1 is returned with errno zero
|
||||
@@ -632,7 +632,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con
|
||||
return Translate(result);
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
std::pair<s32, Errno> NetworkBSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
}
|
||||
@@ -660,7 +660,7 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
return {new_fd, Errno::SUCCESS};
|
||||
}
|
||||
|
||||
Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
|
||||
Errno NetworkBSD::BindImpl(s32 fd, std::span<const u8> addr) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
@@ -675,7 +675,7 @@ Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
|
||||
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
|
||||
}
|
||||
|
||||
Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
||||
Errno NetworkBSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
@@ -698,7 +698,7 @@ Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
Errno NetworkBSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
@@ -720,7 +720,7 @@ Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
return Translate(bsd_errno);
|
||||
}
|
||||
|
||||
Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
Errno NetworkBSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
@@ -742,7 +742,7 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
return Translate(bsd_errno);
|
||||
}
|
||||
|
||||
Errno BSD::ListenImpl(s32 fd, s32 backlog) {
|
||||
Errno NetworkBSD::ListenImpl(s32 fd, s32 backlog) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
@@ -753,7 +753,7 @@ Errno BSD::ListenImpl(s32 fd, s32 backlog) {
|
||||
return Translate(file_descriptors[fd]->socket->Listen(backlog));
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
|
||||
std::pair<s32, Errno> NetworkBSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
}
|
||||
@@ -783,7 +783,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
|
||||
}
|
||||
}
|
||||
|
||||
Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
|
||||
Errno NetworkBSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
@@ -818,7 +818,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o
|
||||
}
|
||||
}
|
||||
|
||||
Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
|
||||
Errno NetworkBSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
@@ -872,7 +872,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8
|
||||
}
|
||||
}
|
||||
|
||||
Errno BSD::ShutdownImpl(s32 fd, s32 how) {
|
||||
Errno NetworkBSD::ShutdownImpl(s32 fd, s32 how) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
@@ -884,7 +884,7 @@ Errno BSD::ShutdownImpl(s32 fd, s32 how) {
|
||||
return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
|
||||
std::pair<s32, Errno> NetworkBSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
}
|
||||
@@ -911,7 +911,7 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message)
|
||||
return {ret, bsd_errno};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
||||
std::pair<s32, Errno> NetworkBSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
||||
std::vector<u8>& addr) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
@@ -958,7 +958,7 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
|
||||
return {ret, bsd_errno};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
||||
std::pair<s32, Errno> NetworkBSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
}
|
||||
@@ -969,7 +969,7 @@ std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> messa
|
||||
return Translate(file_descriptors[fd]->socket->Send(message, flags));
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
||||
std::pair<s32, Errno> NetworkBSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
||||
std::span<const u8> addr) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
@@ -991,7 +991,7 @@ std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> mes
|
||||
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
|
||||
}
|
||||
|
||||
Errno BSD::CloseImpl(s32 fd) {
|
||||
Errno NetworkBSD::CloseImpl(s32 fd) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
@@ -1011,7 +1011,7 @@ Errno BSD::CloseImpl(s32 fd) {
|
||||
return bsd_errno;
|
||||
}
|
||||
|
||||
std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
|
||||
std::variant<s32, Errno> NetworkBSD::DuplicateSocketImpl(s32 fd) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
@@ -1030,7 +1030,7 @@ std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
|
||||
std::optional<std::shared_ptr<Network::SocketBase>> NetworkBSD::GetSocket(s32 fd) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -1041,7 +1041,7 @@ std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
|
||||
return file_descriptors[fd]->socket;
|
||||
}
|
||||
|
||||
s32 BSD::FindFreeFileDescriptorHandle() noexcept {
|
||||
s32 NetworkBSD::FindFreeFileDescriptorHandle() noexcept {
|
||||
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
|
||||
if (!file_descriptors[fd]) {
|
||||
return fd;
|
||||
@@ -1050,7 +1050,7 @@ s32 BSD::FindFreeFileDescriptorHandle() noexcept {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
|
||||
bool NetworkBSD::IsFileDescriptorValid(s32 fd) const noexcept {
|
||||
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
|
||||
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
|
||||
return false;
|
||||
@@ -1062,7 +1062,7 @@ bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
void BSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
|
||||
void NetworkBSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -1070,7 +1070,7 @@ void BSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noex
|
||||
rb.PushEnum(bsd_errno);
|
||||
}
|
||||
|
||||
void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
||||
void NetworkBSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
||||
for (auto& optional_descriptor : file_descriptors) {
|
||||
if (!optional_descriptor.has_value()) {
|
||||
continue;
|
||||
@@ -1080,43 +1080,43 @@ void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
||||
}
|
||||
}
|
||||
|
||||
BSD::BSD(Core::System& system_, const char* name, bool is_user_)
|
||||
NetworkBSD::NetworkBSD(Core::System& system_, const char* name, bool is_user_)
|
||||
: ServiceFramework{system_, name}
|
||||
, is_user{is_user_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BSD::RegisterClient, "RegisterClient"},
|
||||
{1, &BSD::StartMonitoring, "StartMonitoring"},
|
||||
{2, &BSD::Socket, "Socket"},
|
||||
{3, &BSD::SocketExempt, "SocketExempt"},
|
||||
{0, &NetworkBSD::RegisterClient, "RegisterClient"},
|
||||
{1, &NetworkBSD::StartMonitoring, "StartMonitoring"},
|
||||
{2, &NetworkBSD::Socket, "Socket"},
|
||||
{3, &NetworkBSD::SocketExempt, "SocketExempt"},
|
||||
{4, nullptr, "Open"},
|
||||
{5, &BSD::Select, "Select"},
|
||||
{6, &BSD::Poll, "Poll"},
|
||||
{5, &NetworkBSD::Select, "Select"},
|
||||
{6, &NetworkBSD::Poll, "Poll"},
|
||||
{7, nullptr, "Sysctl"},
|
||||
{8, &BSD::Recv, "Recv"},
|
||||
{9, &BSD::RecvFrom, "RecvFrom"},
|
||||
{10, &BSD::Send, "Send"},
|
||||
{11, &BSD::SendTo, "SendTo"},
|
||||
{12, &BSD::Accept, "Accept"},
|
||||
{13, &BSD::Bind, "Bind"},
|
||||
{14, &BSD::Connect, "Connect"},
|
||||
{15, &BSD::GetPeerName, "GetPeerName"},
|
||||
{16, &BSD::GetSockName, "GetSockName"},
|
||||
{17, &BSD::GetSockOpt, "GetSockOpt"},
|
||||
{18, &BSD::Listen, "Listen"},
|
||||
{8, &NetworkBSD::Recv, "Recv"},
|
||||
{9, &NetworkBSD::RecvFrom, "RecvFrom"},
|
||||
{10, &NetworkBSD::Send, "Send"},
|
||||
{11, &NetworkBSD::SendTo, "SendTo"},
|
||||
{12, &NetworkBSD::Accept, "Accept"},
|
||||
{13, &NetworkBSD::Bind, "Bind"},
|
||||
{14, &NetworkBSD::Connect, "Connect"},
|
||||
{15, &NetworkBSD::GetPeerName, "GetPeerName"},
|
||||
{16, &NetworkBSD::GetSockName, "GetSockName"},
|
||||
{17, &NetworkBSD::GetSockOpt, "GetSockOpt"},
|
||||
{18, &NetworkBSD::Listen, "Listen"},
|
||||
{19, nullptr, "Ioctl"},
|
||||
{20, &BSD::Fcntl, "Fcntl"},
|
||||
{21, &BSD::SetSockOpt, "SetSockOpt"},
|
||||
{22, &BSD::Shutdown, "Shutdown"},
|
||||
{20, &NetworkBSD::Fcntl, "Fcntl"},
|
||||
{21, &NetworkBSD::SetSockOpt, "SetSockOpt"},
|
||||
{22, &NetworkBSD::Shutdown, "Shutdown"},
|
||||
{23, nullptr, "ShutdownAllSockets"},
|
||||
{24, &BSD::Write, "Write"},
|
||||
{25, &BSD::Read, "Read"},
|
||||
{26, &BSD::Close, "Close"},
|
||||
{27, &BSD::DuplicateSocket, "DuplicateSocket"},
|
||||
{24, &NetworkBSD::Write, "Write"},
|
||||
{25, &NetworkBSD::Read, "Read"},
|
||||
{26, &NetworkBSD::Close, "Close"},
|
||||
{27, &NetworkBSD::DuplicateSocket, "DuplicateSocket"},
|
||||
{28, nullptr, "GetResourceStatistics"},
|
||||
{29, nullptr, "RecvMMsg"}, //3.0.0+
|
||||
{30, nullptr, "SendMMsg"}, //3.0.0+
|
||||
{31, &BSD::EventFd, "EventFd"}, //7.0.0+
|
||||
{31, &NetworkBSD::EventFd, "EventFd"}, //7.0.0+
|
||||
{32, nullptr, "RegisterResourceStatisticsName"}, //7.0.0+
|
||||
{33, nullptr, "RegisterClientShared"}, //10.0.0+
|
||||
{34, nullptr, "GetSocketStatistics"}, //15.0.0+
|
||||
@@ -1144,13 +1144,13 @@ BSD::BSD(Core::System& system_, const char* name, bool is_user_)
|
||||
}
|
||||
}
|
||||
|
||||
BSD::~BSD() {
|
||||
NetworkBSD::~NetworkBSD() {
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
room_member->Unbind(proxy_packet_received);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> BSD::LockService() noexcept {
|
||||
std::unique_lock<std::mutex> NetworkBSD::LockService() noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -27,10 +27,10 @@ class Socket;
|
||||
|
||||
namespace Service::Sockets {
|
||||
|
||||
class BSD final : public ServiceFramework<BSD> {
|
||||
class NetworkBSD final : public ServiceFramework<NetworkBSD> {
|
||||
public:
|
||||
explicit BSD(Core::System& system_, const char* name, bool is_user);
|
||||
~BSD() override;
|
||||
explicit NetworkBSD(Core::System& system_, const char* name, bool is_user);
|
||||
~NetworkBSD() override;
|
||||
|
||||
// These methods are called from SSL; the first two are also called from
|
||||
// this class for the corresponding IPC methods.
|
||||
@@ -50,7 +50,7 @@ private:
|
||||
};
|
||||
|
||||
struct PollWork {
|
||||
void Execute(BSD* bsd);
|
||||
void Execute(NetworkBSD* bsd);
|
||||
void Response(HLERequestContext& ctx);
|
||||
|
||||
s32 nfds;
|
||||
@@ -62,7 +62,7 @@ private:
|
||||
};
|
||||
|
||||
struct AcceptWork {
|
||||
void Execute(BSD* bsd);
|
||||
void Execute(NetworkBSD* bsd);
|
||||
void Response(HLERequestContext& ctx);
|
||||
|
||||
s32 fd;
|
||||
@@ -72,7 +72,7 @@ private:
|
||||
};
|
||||
|
||||
struct ConnectWork {
|
||||
void Execute(BSD* bsd);
|
||||
void Execute(NetworkBSD* bsd);
|
||||
void Response(HLERequestContext& ctx);
|
||||
|
||||
s32 fd;
|
||||
@@ -81,7 +81,7 @@ private:
|
||||
};
|
||||
|
||||
struct RecvWork {
|
||||
void Execute(BSD* bsd);
|
||||
void Execute(NetworkBSD* bsd);
|
||||
void Response(HLERequestContext& ctx);
|
||||
|
||||
s32 fd;
|
||||
@@ -92,7 +92,7 @@ private:
|
||||
};
|
||||
|
||||
struct RecvFromWork {
|
||||
void Execute(BSD* bsd);
|
||||
void Execute(NetworkBSD* bsd);
|
||||
void Response(HLERequestContext& ctx);
|
||||
|
||||
s32 fd;
|
||||
@@ -104,7 +104,7 @@ private:
|
||||
};
|
||||
|
||||
struct SendWork {
|
||||
void Execute(BSD* bsd);
|
||||
void Execute(NetworkBSD* bsd);
|
||||
void Response(HLERequestContext& ctx);
|
||||
|
||||
s32 fd;
|
||||
@@ -115,7 +115,7 @@ private:
|
||||
};
|
||||
|
||||
struct SendToWork {
|
||||
void Execute(BSD* bsd);
|
||||
void Execute(NetworkBSD* bsd);
|
||||
void Response(HLERequestContext& ctx);
|
||||
|
||||
s32 fd;
|
||||
|
||||
@@ -66,9 +66,9 @@ void LoopProcess(Core::System& system) {
|
||||
|
||||
server_manager->RegisterNamedService("ethc:c", std::make_shared<ETHC_C>(system));
|
||||
server_manager->RegisterNamedService("ethc:i", std::make_shared<ETHC_I>(system));
|
||||
server_manager->RegisterNamedService("bsd:s", std::make_shared<BSD>(system, "bsd:s", false));
|
||||
server_manager->RegisterNamedService("bsd:u", std::make_shared<BSD>(system, "bsd:u", true));
|
||||
server_manager->RegisterNamedService("bsd:a", std::make_shared<BSD>(system, "bsd:a", true));
|
||||
server_manager->RegisterNamedService("bsd:s", std::make_shared<NetworkBSD>(system, "bsd:s", false));
|
||||
server_manager->RegisterNamedService("bsd:u", std::make_shared<NetworkBSD>(system, "bsd:u", true));
|
||||
server_manager->RegisterNamedService("bsd:a", std::make_shared<NetworkBSD>(system, "bsd:a", true));
|
||||
server_manager->RegisterNamedService("bsd:nu", std::make_shared<BSD_NU>(system));
|
||||
server_manager->RegisterNamedService("bsdcfg", std::make_shared<BSDCFG>(system, "bsdcfg"));
|
||||
server_manager->RegisterNamedService("ifcfg", std::make_shared<BSDCFG>(system, "ifcfg"));
|
||||
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
LOG_ERROR(Service_SSL,
|
||||
"do_not_close_socket was changed after setting socket; is this right?");
|
||||
} else {
|
||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
|
||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::NetworkBSD>("bsd:u");
|
||||
if (bsd) {
|
||||
auto err = bsd->CloseImpl(fd);
|
||||
if (err != Service::Sockets::Errno::SUCCESS) {
|
||||
@@ -157,7 +157,7 @@ private:
|
||||
Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
|
||||
LOG_DEBUG(Service_SSL, "called, fd={}", fd);
|
||||
ASSERT(!did_handshake);
|
||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
|
||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::NetworkBSD>("bsd:u");
|
||||
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
|
||||
|
||||
auto const res_v = bsd->DuplicateSocketImpl(fd);
|
||||
|
||||
@@ -17,11 +17,9 @@
|
||||
|
||||
namespace Loader {
|
||||
|
||||
namespace {
|
||||
constexpr u32 PageAlignSize(u32 size) {
|
||||
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
[[nodiscard]] inline constexpr u32 PageAlignSizeKIP(u32 size) {
|
||||
return u32((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_)
|
||||
: AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {}
|
||||
@@ -76,11 +74,11 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
|
||||
kip->GetKernelCapabilities());
|
||||
|
||||
Kernel::CodeSet codeset;
|
||||
codeset.memory.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
|
||||
codeset.memory.resize(PageAlignSizeKIP(kip->GetBSSOffset()) + kip->GetBSSSize());
|
||||
const auto load_segment = [&codeset](Kernel::CodeSet::Segment& segment, std::span<const u8> data, u32 offset) {
|
||||
segment.addr = offset;
|
||||
segment.offset = offset;
|
||||
segment.size = PageAlignSize(u32(data.size()));
|
||||
segment.size = PageAlignSizeKIP(u32(data.size()));
|
||||
std::memcpy(codeset.memory.data() + offset, data.data(), data.size());
|
||||
};
|
||||
load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset());
|
||||
|
||||
@@ -148,8 +148,8 @@ bool AppLoader_NRO::IsHomebrew() {
|
||||
nro_header.magic_ext2 == Common::MakeMagic('B', 'R', 'E', 'W');
|
||||
}
|
||||
|
||||
static constexpr u32 PageAlignSize(u32 size) {
|
||||
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
[[nodiscard]] inline constexpr u32 PageAlignSizeNRO(u32 size) {
|
||||
return u32((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
}
|
||||
|
||||
static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
|
||||
@@ -166,9 +166,9 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
|
||||
}
|
||||
|
||||
// Build program image
|
||||
std::vector<u8> program_image(PageAlignSize(nro_header.file_size));
|
||||
std::vector<u8> program_image(PageAlignSizeNRO(nro_header.file_size));
|
||||
std::memcpy(program_image.data(), data.data(), program_image.size());
|
||||
if (program_image.size() != PageAlignSize(nro_header.file_size)) {
|
||||
if (program_image.size() != PageAlignSizeNRO(nro_header.file_size)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -176,11 +176,11 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
|
||||
for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
|
||||
codeset.segments[i].addr = nro_header.segments[i].offset;
|
||||
codeset.segments[i].offset = nro_header.segments[i].offset;
|
||||
codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
|
||||
codeset.segments[i].size = PageAlignSizeNRO(nro_header.segments[i].size);
|
||||
}
|
||||
|
||||
// Default .bss to NRO header bss size if MOD0 section doesn't exist
|
||||
u32 bss_size{PageAlignSize(nro_header.bss_size)};
|
||||
u32 bss_size{PageAlignSizeNRO(nro_header.bss_size)};
|
||||
|
||||
// Read MOD header
|
||||
ModHeader mod_header{};
|
||||
@@ -190,7 +190,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
|
||||
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
|
||||
if (has_mod_header) {
|
||||
// Resize program image to include .bss section and page align each section
|
||||
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
|
||||
bss_size = PageAlignSizeNRO(mod_header.bss_end_offset - mod_header.bss_start_offset);
|
||||
}
|
||||
|
||||
codeset.DataSegment().size += bss_size;
|
||||
|
||||
@@ -41,8 +41,8 @@ struct MODHeader {
|
||||
};
|
||||
static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
|
||||
|
||||
constexpr u32 PageAlignSize(u32 size) {
|
||||
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
[[nodiscard]] inline constexpr u32 PageAlignSizeNSO(u32 size) {
|
||||
return u32((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
@@ -128,11 +128,11 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
|
||||
}
|
||||
|
||||
codeset.DataSegment().size += nso_header.segments[2].bss_size;
|
||||
u32 image_size = PageAlignSize(u32(codeset.memory.size()) + nso_header.segments[2].bss_size);
|
||||
u32 image_size = PageAlignSizeNSO(u32(codeset.memory.size()) + nso_header.segments[2].bss_size);
|
||||
codeset.memory.resize(image_size);
|
||||
|
||||
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
||||
codeset.segments[i].size = PageAlignSize(codeset.segments[i].size);
|
||||
codeset.segments[i].size = PageAlignSizeNSO(codeset.segments[i].size);
|
||||
}
|
||||
|
||||
// Apply patches if necessary
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "hid_core/resource_manager.h"
|
||||
#include "hid_core/resources/npad/npad.h"
|
||||
|
||||
#undef CreateEvent
|
||||
|
||||
namespace Core::Memory {
|
||||
namespace {
|
||||
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "core/memory.h"
|
||||
#include "core/reporter.h"
|
||||
|
||||
#undef far
|
||||
|
||||
namespace {
|
||||
|
||||
std::filesystem::path GetPath(std::string_view type, u64 title_id, std::string_view timestamp) {
|
||||
|
||||
@@ -52,6 +52,8 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
#undef CreateEvent
|
||||
|
||||
Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
|
||||
: core_timing{core_timing_}, memory{memory_} {
|
||||
event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback",
|
||||
|
||||
@@ -31,7 +31,7 @@ using namespace oaknut::util;
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsOrdered(IR::AccType acctype) {
|
||||
[[nodiscard]] inline bool IsOrdered(IR::AccType acctype) {
|
||||
return acctype == IR::AccType::ORDERED || acctype == IR::AccType::ORDEREDRW || acctype == IR::AccType::LIMITEDORDERED;
|
||||
}
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ void A32EmitX64::GenTerminalHandlers() {
|
||||
calculate_location_descriptor();
|
||||
code.mov(eax, dword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_ptr)]);
|
||||
code.sub(eax, 1);
|
||||
code.and_(eax, u32(A32JitState::RSB_PTR_MASK));
|
||||
code.and_(eax, u32(A32JitState::RSBPtrMask));
|
||||
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_ptr)], eax);
|
||||
code.cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
|
||||
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
|
||||
|
||||
@@ -37,9 +37,9 @@ using namespace Backend::X64;
|
||||
|
||||
static RunCodeCallbacks GenRunCodeCallbacks(A32::UserCallbacks* cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg, const A32::UserConfig& conf) {
|
||||
return RunCodeCallbacks{
|
||||
ArgCallback(LookupBlock, reinterpret_cast<u64>(arg)),
|
||||
ArgCallback(Devirtualize<&A32::UserCallbacks::AddTicks>(cb)),
|
||||
ArgCallback(Devirtualize<&A32::UserCallbacks::GetTicksRemaining>(cb)),
|
||||
std::make_unique<ArgCallback>(LookupBlock, reinterpret_cast<u64>(arg)),
|
||||
std::make_unique<ArgCallback>(Devirtualize<&A32::UserCallbacks::AddTicks>(cb)),
|
||||
std::make_unique<ArgCallback>(Devirtualize<&A32::UserCallbacks::GetTicksRemaining>(cb)),
|
||||
conf.enable_cycle_counting,
|
||||
};
|
||||
}
|
||||
@@ -79,7 +79,7 @@ struct Jit::Impl {
|
||||
jit_interface->is_executing = true;
|
||||
const CodePtr current_codeptr = [this] {
|
||||
// RSB optimization
|
||||
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSB_PTR_MASK;
|
||||
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSBPtrMask;
|
||||
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
|
||||
jit_state.rsb_ptr = new_rsb_ptr;
|
||||
return reinterpret_cast<CodePtr>(jit_state.rsb_codeptrs[new_rsb_ptr]);
|
||||
|
||||
@@ -27,9 +27,6 @@ struct A32JitState {
|
||||
|
||||
A32JitState() { ResetRSB(); }
|
||||
|
||||
static constexpr std::size_t RSB_SIZE = 8; // MUST be a power of 2.
|
||||
static constexpr std::size_t RSB_PTR_MASK = RSB_SIZE - 1;
|
||||
|
||||
std::array<u32, 16> Reg{}; // Current register file.
|
||||
// TODO: Mode-specific register sets unimplemented.
|
||||
|
||||
@@ -39,9 +36,8 @@ struct A32JitState {
|
||||
u32 cpsr_q = 0;
|
||||
u32 cpsr_nzcv = 0;
|
||||
u32 cpsr_jaifm = 0;
|
||||
u32 fpsr_exc = 0;
|
||||
u32 fpsr_qc = 0;
|
||||
u32 fpsr_nzcv = 0;
|
||||
u32 Cpsr() const;
|
||||
void SetCpsr(u32 cpsr);
|
||||
|
||||
alignas(16) std::array<u32, 64> ExtReg{}; // Extension registers.
|
||||
|
||||
@@ -53,19 +49,21 @@ struct A32JitState {
|
||||
// Exclusive state
|
||||
u32 exclusive_state = 0;
|
||||
|
||||
static constexpr std::size_t RSBSize = 8; // MUST be a power of 2.
|
||||
static constexpr std::size_t RSBPtrMask = RSBSize - 1;
|
||||
u32 rsb_ptr = 0;
|
||||
std::array<u64, RSB_SIZE> rsb_location_descriptors;
|
||||
std::array<u64, RSB_SIZE> rsb_codeptrs;
|
||||
|
||||
u32 Cpsr() const;
|
||||
void SetCpsr(u32 cpsr);
|
||||
|
||||
std::array<u64, RSBSize> rsb_location_descriptors;
|
||||
std::array<u64, RSBSize> rsb_codeptrs;
|
||||
void ResetRSB();
|
||||
|
||||
u32 fpsr_exc = 0;
|
||||
u32 fpsr_qc = 0;
|
||||
u32 fpsr_nzcv = 0;
|
||||
u32 Fpscr() const;
|
||||
void SetFpscr(u32 FPSCR);
|
||||
|
||||
u64 GetUniqueHash() const noexcept {
|
||||
return (u64(upper_location_descriptor) << 32) | (u64(Reg[15]));
|
||||
return (static_cast<u64>(upper_location_descriptor) << 32) | (static_cast<u64>(Reg[15]));
|
||||
}
|
||||
|
||||
void TransferJitState(const A32JitState& src, bool reset_rsb) {
|
||||
|
||||
@@ -208,7 +208,7 @@ void A64EmitX64::GenTerminalHandlers() {
|
||||
calculate_location_descriptor();
|
||||
code.mov(eax, dword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_ptr)]);
|
||||
code.sub(eax, 1);
|
||||
code.and_(eax, u32(A64JitState::RSB_PTR_MASK));
|
||||
code.and_(eax, u32(A64JitState::RSBPtrMask));
|
||||
code.mov(dword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_ptr)], eax);
|
||||
code.cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
|
||||
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
|
||||
|
||||
@@ -33,9 +33,9 @@ using namespace Backend::X64;
|
||||
|
||||
static RunCodeCallbacks GenRunCodeCallbacks(A64::UserCallbacks* cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg, const A64::UserConfig& conf) {
|
||||
return RunCodeCallbacks{
|
||||
ArgCallback(LookupBlock, reinterpret_cast<u64>(arg)),
|
||||
ArgCallback(Devirtualize<&A64::UserCallbacks::AddTicks>(cb)),
|
||||
ArgCallback(Devirtualize<&A64::UserCallbacks::GetTicksRemaining>(cb)),
|
||||
std::make_unique<ArgCallback>(LookupBlock, reinterpret_cast<u64>(arg)),
|
||||
std::make_unique<ArgCallback>(Devirtualize<&A64::UserCallbacks::AddTicks>(cb)),
|
||||
std::make_unique<ArgCallback>(Devirtualize<&A64::UserCallbacks::GetTicksRemaining>(cb)),
|
||||
conf.enable_cycle_counting,
|
||||
};
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public:
|
||||
// TODO: Check code alignment
|
||||
const CodePtr current_code_ptr = [this] {
|
||||
// RSB optimization
|
||||
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSB_PTR_MASK;
|
||||
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask;
|
||||
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
|
||||
jit_state.rsb_ptr = new_rsb_ptr;
|
||||
return CodePtr(jit_state.rsb_codeptrs[new_rsb_ptr]);
|
||||
|
||||
@@ -29,19 +29,18 @@ struct A64JitState {
|
||||
|
||||
A64JitState() { ResetRSB(); }
|
||||
|
||||
// Exclusive state stuff
|
||||
static constexpr u64 RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFF0ull;
|
||||
// Return stack buffer
|
||||
static constexpr size_t RSB_SIZE = 8; // MUST be a power of 2.
|
||||
static constexpr size_t RSB_PTR_MASK = RSB_SIZE - 1;
|
||||
|
||||
std::array<u64, 31> reg{};
|
||||
u64 sp = 0;
|
||||
u64 pc = 0;
|
||||
|
||||
u32 cpsr_nzcv = 0;
|
||||
u32 fpsr_exc = 0;
|
||||
u32 fpsr_qc = 0;
|
||||
u32 fpcr = 0;
|
||||
|
||||
u32 GetPstate() const {
|
||||
return NZCV::FromX64(cpsr_nzcv);
|
||||
}
|
||||
void SetPstate(u32 new_pstate) {
|
||||
cpsr_nzcv = NZCV::ToX64(new_pstate);
|
||||
}
|
||||
|
||||
alignas(16) std::array<u64, 64> vec{}; // Extension registers.
|
||||
|
||||
@@ -51,31 +50,29 @@ struct A64JitState {
|
||||
volatile u32 halt_reason = 0;
|
||||
|
||||
// Exclusive state
|
||||
static constexpr u64 RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFF0ull;
|
||||
u8 exclusive_state = 0;
|
||||
|
||||
static constexpr size_t RSBSize = 8; // MUST be a power of 2.
|
||||
static constexpr size_t RSBPtrMask = RSBSize - 1;
|
||||
u32 rsb_ptr = 0;
|
||||
std::array<u64, RSB_SIZE> rsb_location_descriptors;
|
||||
std::array<u64, RSB_SIZE> rsb_codeptrs;
|
||||
|
||||
u32 GetPstate() const {
|
||||
return NZCV::FromX64(cpsr_nzcv);
|
||||
}
|
||||
|
||||
void SetPstate(u32 new_pstate) {
|
||||
cpsr_nzcv = NZCV::ToX64(new_pstate);
|
||||
}
|
||||
|
||||
std::array<u64, RSBSize> rsb_location_descriptors;
|
||||
std::array<u64, RSBSize> rsb_codeptrs;
|
||||
void ResetRSB() {
|
||||
rsb_location_descriptors.fill(0xFFFFFFFFFFFFFFFFull);
|
||||
rsb_codeptrs.fill(0);
|
||||
}
|
||||
|
||||
u32 fpsr_exc = 0;
|
||||
u32 fpsr_qc = 0;
|
||||
u32 fpcr = 0;
|
||||
u32 GetFpcr() const;
|
||||
u32 GetFpsr() const;
|
||||
void SetFpcr(u32 value);
|
||||
void SetFpsr(u32 value);
|
||||
|
||||
u64 GetUniqueHash() const noexcept {
|
||||
const u64 fpcr_u64 = u64(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift;
|
||||
const u64 fpcr_u64 = static_cast<u64>(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift;
|
||||
const u64 pc_u64 = pc & A64::LocationDescriptor::pc_mask;
|
||||
return pc_u64 | fpcr_u64;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,73 @@ namespace {
|
||||
constexpr size_t CONSTANT_POOL_SIZE = 2 * 1024 * 1024;
|
||||
constexpr size_t PRELUDE_COMMIT_SIZE = 16 * 1024 * 1024;
|
||||
|
||||
class CustomXbyakAllocator : public Xbyak::Allocator {
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
uint8_t* alloc(size_t size) override {
|
||||
void* p = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
if (p == nullptr) {
|
||||
using Xbyak::Error;
|
||||
XBYAK_THROW(Xbyak::ERR_CANT_ALLOC);
|
||||
}
|
||||
return static_cast<uint8_t*>(p);
|
||||
}
|
||||
|
||||
void free(uint8_t* p) override {
|
||||
VirtualFree(static_cast<void*>(p), 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
bool useProtect() const override { return false; }
|
||||
#else
|
||||
static constexpr size_t DYNARMIC_PAGE_SIZE = 4096;
|
||||
|
||||
// Can't subclass Xbyak::MmapAllocator because it is not a pure interface
|
||||
// and doesn't expose its construtor
|
||||
uint8_t* alloc(size_t size) override {
|
||||
// Waste a page to store the size
|
||||
size += DYNARMIC_PAGE_SIZE;
|
||||
|
||||
int mode = MAP_PRIVATE;
|
||||
#if defined(MAP_ANONYMOUS)
|
||||
mode |= MAP_ANONYMOUS;
|
||||
#elif defined(MAP_ANON)
|
||||
mode |= MAP_ANON;
|
||||
#else
|
||||
# error "not supported"
|
||||
#endif
|
||||
#ifdef MAP_JIT
|
||||
mode |= MAP_JIT;
|
||||
#endif
|
||||
int prot = PROT_READ | PROT_WRITE;
|
||||
#ifdef PROT_MPROTECT
|
||||
// https://man.netbsd.org/mprotect.2 specifies that an mprotect() that is LESS
|
||||
// restrictive than the original mapping MUST fail
|
||||
prot |= PROT_MPROTECT(PROT_READ) | PROT_MPROTECT(PROT_WRITE) | PROT_MPROTECT(PROT_EXEC);
|
||||
#endif
|
||||
void* p = mmap(nullptr, size, prot, mode, -1, 0);
|
||||
if (p == MAP_FAILED) {
|
||||
using Xbyak::Error;
|
||||
XBYAK_THROW(Xbyak::ERR_CANT_ALLOC);
|
||||
}
|
||||
std::memcpy(p, &size, sizeof(size_t));
|
||||
return static_cast<uint8_t*>(p) + DYNARMIC_PAGE_SIZE;
|
||||
}
|
||||
|
||||
void free(uint8_t* p) override {
|
||||
size_t size;
|
||||
std::memcpy(&size, p - DYNARMIC_PAGE_SIZE, sizeof(size_t));
|
||||
munmap(p - DYNARMIC_PAGE_SIZE, size);
|
||||
}
|
||||
|
||||
# ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
|
||||
bool useProtect() const override { return false; }
|
||||
# endif
|
||||
#endif
|
||||
};
|
||||
|
||||
// This is threadsafe as Xbyak::Allocator does not contain any state; it is a pure interface.
|
||||
CustomXbyakAllocator s_allocator;
|
||||
|
||||
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
|
||||
void ProtectMemory(const void* base, size_t size, bool is_executable) {
|
||||
# ifdef _WIN32
|
||||
@@ -78,9 +145,11 @@ void ProtectMemory(const void* base, size_t size, bool is_executable) {
|
||||
|
||||
HostFeature GetHostFeatures() {
|
||||
HostFeature features = {};
|
||||
|
||||
#ifdef DYNARMIC_ENABLE_CPU_FEATURE_DETECTION
|
||||
using Cpu = Xbyak::util::Cpu;
|
||||
Xbyak::util::Cpu cpu_info{};
|
||||
Xbyak::util::Cpu cpu_info;
|
||||
|
||||
if (cpu_info.has(Cpu::tSSSE3))
|
||||
features |= HostFeature::SSSE3;
|
||||
if (cpu_info.has(Cpu::tSSE41))
|
||||
@@ -127,6 +196,7 @@ HostFeature GetHostFeatures() {
|
||||
features |= HostFeature::GFNI;
|
||||
if (cpu_info.has(Cpu::tWAITPKG))
|
||||
features |= HostFeature::WAITPKG;
|
||||
|
||||
if (cpu_info.has(Cpu::tBMI2)) {
|
||||
// BMI2 instructions such as pdep and pext have been very slow up until Zen 3.
|
||||
// Check for Zen 3 or newer by its family (0x19).
|
||||
@@ -144,6 +214,7 @@ HostFeature GetHostFeatures() {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
@@ -162,27 +233,23 @@ bool IsUnderRosetta() {
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, std::function<void(BlockOfCode&)> rcp)
|
||||
: Xbyak::CodeGenerator(total_code_size
|
||||
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
|
||||
, Xbyak::DontSetProtectRWE
|
||||
static const auto default_cg_mode = Xbyak::DontSetProtectRWE;
|
||||
#else
|
||||
, nullptr //Allow RWE
|
||||
static const auto default_cg_mode = nullptr; //Allow RWE
|
||||
#endif
|
||||
, nullptr)
|
||||
, constant_pool(*this, CONSTANT_POOL_SIZE)
|
||||
, jsi(jsi)
|
||||
, cb(std::move(cb))
|
||||
{
|
||||
|
||||
BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, std::function<void(BlockOfCode&)> rcp)
|
||||
: Xbyak::CodeGenerator(total_code_size, default_cg_mode, &s_allocator)
|
||||
, cb(std::move(cb))
|
||||
, jsi(jsi)
|
||||
, constant_pool(*this, CONSTANT_POOL_SIZE)
|
||||
, host_features(GetHostFeatures()) {
|
||||
EnableWriting();
|
||||
EnsureMemoryCommitted(PRELUDE_COMMIT_SIZE);
|
||||
GenRunCode(rcp);
|
||||
}
|
||||
|
||||
bool BlockOfCode::HasHostFeature(HostFeature feature) const noexcept {
|
||||
return (GetHostFeatures() & feature) == feature;
|
||||
}
|
||||
|
||||
void BlockOfCode::PreludeComplete() {
|
||||
prelude_complete = true;
|
||||
code_begin = getCurr();
|
||||
@@ -274,7 +341,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||
mov(rbx, ABI_PARAM2); // save temporarily in non-volatile register
|
||||
|
||||
if (cb.enable_cycle_counting) {
|
||||
cb.GetTicksRemaining.EmitCall(*this);
|
||||
cb.GetTicksRemaining->EmitCall(*this);
|
||||
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)], ABI_RETURN);
|
||||
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], ABI_RETURN);
|
||||
}
|
||||
@@ -321,7 +388,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||
cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
|
||||
jng(return_to_caller);
|
||||
}
|
||||
cb.LookupBlock.EmitCall(*this);
|
||||
cb.LookupBlock->EmitCall(*this);
|
||||
jmp(ABI_RETURN);
|
||||
|
||||
align();
|
||||
@@ -334,7 +401,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||
jng(return_to_caller_mxcsr_already_exited);
|
||||
}
|
||||
SwitchMxcsrOnEntry();
|
||||
cb.LookupBlock.EmitCall(*this);
|
||||
cb.LookupBlock->EmitCall(*this);
|
||||
jmp(ABI_RETURN);
|
||||
|
||||
align();
|
||||
@@ -348,7 +415,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||
L(return_to_caller_mxcsr_already_exited);
|
||||
|
||||
if (cb.enable_cycle_counting) {
|
||||
cb.AddTicks.EmitCall(*this, [this](RegList param) {
|
||||
cb.AddTicks->EmitCall(*this, [this](RegList param) {
|
||||
mov(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)]);
|
||||
sub(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)]);
|
||||
});
|
||||
@@ -388,18 +455,18 @@ void BlockOfCode::UpdateTicks() {
|
||||
return;
|
||||
}
|
||||
|
||||
cb.AddTicks.EmitCall(*this, [this](RegList param) {
|
||||
cb.AddTicks->EmitCall(*this, [this](RegList param) {
|
||||
mov(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)]);
|
||||
sub(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)]);
|
||||
});
|
||||
|
||||
cb.GetTicksRemaining.EmitCall(*this);
|
||||
cb.GetTicksRemaining->EmitCall(*this);
|
||||
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)], ABI_RETURN);
|
||||
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], ABI_RETURN);
|
||||
}
|
||||
|
||||
void BlockOfCode::LookupBlock() {
|
||||
cb.LookupBlock.EmitCall(*this);
|
||||
cb.LookupBlock->EmitCall(*this);
|
||||
}
|
||||
|
||||
void BlockOfCode::LoadRequiredFlagsForCondFromRax(IR::Cond cond) {
|
||||
@@ -453,7 +520,7 @@ void BlockOfCode::LoadRequiredFlagsForCondFromRax(IR::Cond cond) {
|
||||
}
|
||||
|
||||
Xbyak::Address BlockOfCode::Const(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
|
||||
return constant_pool.GetConstant(*this, frame, lower, upper);
|
||||
return constant_pool.GetConstant(frame, lower, upper);
|
||||
}
|
||||
|
||||
CodePtr BlockOfCode::GetCodeBegin() const {
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace Dynarmic::Backend::X64 {
|
||||
using CodePtr = const void*;
|
||||
|
||||
struct RunCodeCallbacks {
|
||||
ArgCallback LookupBlock;
|
||||
ArgCallback AddTicks;
|
||||
ArgCallback GetTicksRemaining;
|
||||
std::unique_ptr<Callback> LookupBlock;
|
||||
std::unique_ptr<Callback> AddTicks;
|
||||
std::unique_ptr<Callback> GetTicksRemaining;
|
||||
bool enable_cycle_counting;
|
||||
};
|
||||
|
||||
@@ -166,24 +166,27 @@ public:
|
||||
|
||||
JitStateInfo GetJitStateInfo() const { return jsi; }
|
||||
|
||||
bool HasHostFeature(HostFeature feature) const noexcept;
|
||||
bool HasHostFeature(HostFeature feature) const {
|
||||
return (host_features & feature) == feature;
|
||||
}
|
||||
|
||||
private:
|
||||
using RunCodeFuncType = HaltReason (*)(void*, CodePtr);
|
||||
static constexpr size_t MXCSR_ALREADY_EXITED = 1 << 0;
|
||||
static constexpr size_t FORCE_RETURN = 1 << 1;
|
||||
|
||||
ConstantPool constant_pool;
|
||||
JitStateInfo jsi;
|
||||
std::array<const void*, 4> return_from_run_code;
|
||||
RunCodeFuncType run_code = nullptr;
|
||||
RunCodeFuncType step_code = nullptr;
|
||||
RunCodeCallbacks cb;
|
||||
JitStateInfo jsi;
|
||||
CodePtr code_begin = nullptr;
|
||||
#ifdef _WIN32
|
||||
size_t committed_size = 0;
|
||||
#endif
|
||||
ConstantPool constant_pool;
|
||||
RunCodeFuncType run_code = nullptr;
|
||||
RunCodeFuncType step_code = nullptr;
|
||||
std::array<const void*, 4> return_from_run_code;
|
||||
bool prelude_complete = false;
|
||||
const HostFeature host_features;
|
||||
|
||||
void GenRunCode(std::function<void(BlockOfCode&)> rcp);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
|
||||
ConstantPool::ConstantPool(BlockOfCode& code, size_t size)
|
||||
: insertion_point(0)
|
||||
: code(code)
|
||||
, insertion_point(0)
|
||||
{
|
||||
code.EnsureMemoryCommitted(align_size + size);
|
||||
code.int3();
|
||||
@@ -24,17 +25,17 @@ ConstantPool::ConstantPool(BlockOfCode& code, size_t size)
|
||||
pool = std::span<ConstantT>(reinterpret_cast<ConstantT*>(code.AllocateFromCodeSpace(size)), size / align_size);
|
||||
}
|
||||
|
||||
Xbyak::Address ConstantPool::GetConstant(BlockOfCode& code, const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
|
||||
Xbyak::Address ConstantPool::GetConstant(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
|
||||
const auto constant = ConstantT(lower, upper);
|
||||
auto it = constant_info.find(constant);
|
||||
if (it == constant_info.end()) {
|
||||
auto iter = constant_info.find(constant);
|
||||
if (iter == constant_info.end()) {
|
||||
ASSERT(insertion_point < pool.size());
|
||||
ConstantT& target_constant = pool[insertion_point];
|
||||
target_constant = constant;
|
||||
it = constant_info.insert({constant, &target_constant}).first;
|
||||
iter = constant_info.insert({constant, &target_constant}).first;
|
||||
++insertion_point;
|
||||
}
|
||||
return frame[code.rip + it->second];
|
||||
return frame[code.rip + iter->second];
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
|
||||
@@ -29,7 +29,7 @@ class ConstantPool final {
|
||||
public:
|
||||
ConstantPool(BlockOfCode& code, size_t size);
|
||||
|
||||
Xbyak::Address GetConstant(BlockOfCode& code, const Xbyak::AddressFrame& frame, u64 lower, u64 upper = 0);
|
||||
Xbyak::Address GetConstant(const Xbyak::AddressFrame& frame, u64 lower, u64 upper = 0);
|
||||
|
||||
private:
|
||||
static constexpr size_t align_size = 16; // bytes
|
||||
@@ -45,6 +45,7 @@ private:
|
||||
|
||||
ankerl::unordered_dense::map<ConstantT, void*, ConstantHash> constant_info;
|
||||
std::span<ConstantT> pool;
|
||||
BlockOfCode& code;
|
||||
std::size_t insertion_point;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
|
||||
enum class HostFeature : u32 {
|
||||
enum class HostFeature : u64 {
|
||||
SSSE3 = 1ULL << 0,
|
||||
SSE41 = 1ULL << 1,
|
||||
SSE42 = 1ULL << 2,
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -18,7 +15,7 @@ struct JitStateInfo {
|
||||
: offsetof_guest_MXCSR(offsetof(JitStateType, guest_MXCSR))
|
||||
, offsetof_asimd_MXCSR(offsetof(JitStateType, asimd_MXCSR))
|
||||
, offsetof_rsb_ptr(offsetof(JitStateType, rsb_ptr))
|
||||
, rsb_ptr_mask(JitStateType::RSB_PTR_MASK)
|
||||
, rsb_ptr_mask(JitStateType::RSBPtrMask)
|
||||
, offsetof_rsb_location_descriptors(offsetof(JitStateType, rsb_location_descriptors))
|
||||
, offsetof_rsb_codeptrs(offsetof(JitStateType, rsb_codeptrs))
|
||||
, offsetof_cpsr_nzcv(offsetof(JitStateType, cpsr_nzcv))
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -6,7 +9,6 @@
|
||||
#include "hid_core/hidbus/starlink.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr u8 DEVICE_ID = 0x28;
|
||||
|
||||
Starlink::Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
|
||||
: HidbusBase(system_, service_context_) {}
|
||||
@@ -35,7 +37,7 @@ void Starlink::OnUpdate() {
|
||||
}
|
||||
|
||||
u8 Starlink::GetDeviceId() const {
|
||||
return DEVICE_ID;
|
||||
return 0x28;
|
||||
}
|
||||
|
||||
u64 Starlink::GetReply(std::span<u8> out_data) const {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -6,7 +9,6 @@
|
||||
#include "hid_core/hidbus/stubbed.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr u8 DEVICE_ID = 0xFF;
|
||||
|
||||
HidbusStubbed::HidbusStubbed(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
|
||||
: HidbusBase(system_, service_context_) {}
|
||||
@@ -35,7 +37,7 @@ void HidbusStubbed::OnUpdate() {
|
||||
}
|
||||
|
||||
u8 HidbusStubbed::GetDeviceId() const {
|
||||
return DEVICE_ID;
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
u64 HidbusStubbed::GetReply(std::span<u8> out_data) const {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -8,9 +11,6 @@
|
||||
#include "hid_core/irsensor/moment_processor.h"
|
||||
|
||||
namespace Service::IRS {
|
||||
static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size40x30;
|
||||
static constexpr std::size_t ImageWidth = 40;
|
||||
static constexpr std::size_t ImageHeight = 30;
|
||||
|
||||
MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
|
||||
std::size_t npad_index)
|
||||
@@ -80,9 +80,9 @@ void MomentProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type)
|
||||
}
|
||||
|
||||
u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
|
||||
if ((y * ImageWidth) + x >= data.size()) {
|
||||
constexpr std::size_t ImageWidth = 40;
|
||||
if ((y * ImageWidth) + x >= data.size())
|
||||
return 0;
|
||||
}
|
||||
return data[(y * ImageWidth) + x];
|
||||
}
|
||||
|
||||
@@ -92,9 +92,12 @@ MomentProcessor::MomentStatistic MomentProcessor::GetStatistic(const std::vector
|
||||
std::size_t width,
|
||||
std::size_t height) const {
|
||||
// The actual implementation is always 320x240
|
||||
static constexpr std::size_t RealWidth = 320;
|
||||
static constexpr std::size_t RealHeight = 240;
|
||||
static constexpr std::size_t Threshold = 30;
|
||||
constexpr std::size_t RealWidth = 320;
|
||||
constexpr std::size_t RealHeight = 240;
|
||||
constexpr std::size_t Threshold = 30;
|
||||
constexpr std::size_t ImageWidth = 40;
|
||||
constexpr std::size_t ImageHeight = 30;
|
||||
|
||||
MomentStatistic statistic{};
|
||||
std::size_t active_points{};
|
||||
|
||||
@@ -143,7 +146,7 @@ void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig conf
|
||||
static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
|
||||
current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
|
||||
|
||||
npad_device->SetCameraFormat(format);
|
||||
npad_device->SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat::Size40x30);
|
||||
}
|
||||
|
||||
} // namespace Service::IRS
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -7,14 +10,14 @@
|
||||
#include "input_common/drivers/camera.h"
|
||||
|
||||
namespace InputCommon {
|
||||
constexpr PadIdentifier identifier = {
|
||||
constexpr PadIdentifier camera_identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
PreSetController(camera_identifier);
|
||||
}
|
||||
|
||||
void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data) {
|
||||
@@ -33,7 +36,7 @@ void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<cons
|
||||
}
|
||||
}
|
||||
|
||||
SetCamera(identifier, status);
|
||||
SetCamera(camera_identifier, status);
|
||||
}
|
||||
|
||||
std::size_t Camera::getImageWidth() const {
|
||||
|
||||
@@ -26,7 +26,7 @@ constexpr int mouse_axis_x = 0;
|
||||
constexpr int mouse_axis_y = 1;
|
||||
constexpr int wheel_axis_x = 2;
|
||||
constexpr int wheel_axis_y = 3;
|
||||
constexpr PadIdentifier identifier = {
|
||||
constexpr PadIdentifier mouse_identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
@@ -51,16 +51,16 @@ constexpr PadIdentifier touch_identifier = {
|
||||
};
|
||||
|
||||
Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
PreSetController(mouse_identifier);
|
||||
PreSetController(real_mouse_identifier);
|
||||
PreSetController(touch_identifier);
|
||||
PreSetController(motion_identifier);
|
||||
|
||||
// Initialize all mouse axis
|
||||
PreSetAxis(identifier, mouse_axis_x);
|
||||
PreSetAxis(identifier, mouse_axis_y);
|
||||
PreSetAxis(identifier, wheel_axis_x);
|
||||
PreSetAxis(identifier, wheel_axis_y);
|
||||
PreSetAxis(mouse_identifier, mouse_axis_x);
|
||||
PreSetAxis(mouse_identifier, mouse_axis_y);
|
||||
PreSetAxis(mouse_identifier, wheel_axis_x);
|
||||
PreSetAxis(mouse_identifier, wheel_axis_y);
|
||||
PreSetAxis(real_mouse_identifier, mouse_axis_x);
|
||||
PreSetAxis(real_mouse_identifier, mouse_axis_y);
|
||||
PreSetAxis(touch_identifier, mouse_axis_x);
|
||||
@@ -88,8 +88,8 @@ void Mouse::UpdateStickInput() {
|
||||
last_mouse_change *= maximum_stick_range;
|
||||
}
|
||||
|
||||
SetAxis(identifier, mouse_axis_x, last_mouse_change.x);
|
||||
SetAxis(identifier, mouse_axis_y, -last_mouse_change.y);
|
||||
SetAxis(mouse_identifier, mouse_axis_x, last_mouse_change.x);
|
||||
SetAxis(mouse_identifier, mouse_axis_y, -last_mouse_change.y);
|
||||
|
||||
// Decay input over time
|
||||
const float clamped_length = (std::min)(1.0f, length);
|
||||
@@ -165,8 +165,8 @@ void Mouse::Move(int x, int y, int center_x, int center_y) {
|
||||
Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity;
|
||||
const float y_sensitivity =
|
||||
Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity;
|
||||
SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * x_sensitivity);
|
||||
SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * y_sensitivity);
|
||||
SetAxis(mouse_identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * x_sensitivity);
|
||||
SetAxis(mouse_identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * y_sensitivity);
|
||||
|
||||
last_motion_change = {
|
||||
static_cast<float>(-mouse_move.y) * x_sensitivity,
|
||||
@@ -192,7 +192,7 @@ void Mouse::TouchMove(f32 touch_x, f32 touch_y) {
|
||||
}
|
||||
|
||||
void Mouse::PressButton(int x, int y, MouseButton button) {
|
||||
SetButton(identifier, static_cast<int>(button), true);
|
||||
SetButton(mouse_identifier, static_cast<int>(button), true);
|
||||
|
||||
// Set initial analog parameters
|
||||
mouse_origin = {x, y};
|
||||
@@ -211,13 +211,13 @@ void Mouse::PressTouchButton(f32 touch_x, f32 touch_y, MouseButton button) {
|
||||
}
|
||||
|
||||
void Mouse::ReleaseButton(MouseButton button) {
|
||||
SetButton(identifier, static_cast<int>(button), false);
|
||||
SetButton(mouse_identifier, static_cast<int>(button), false);
|
||||
SetButton(real_mouse_identifier, static_cast<int>(button), false);
|
||||
SetButton(touch_identifier, static_cast<int>(button), false);
|
||||
|
||||
if (!IsMousePanningEnabled()) {
|
||||
SetAxis(identifier, mouse_axis_x, 0);
|
||||
SetAxis(identifier, mouse_axis_y, 0);
|
||||
SetAxis(mouse_identifier, mouse_axis_x, 0);
|
||||
SetAxis(mouse_identifier, mouse_axis_y, 0);
|
||||
}
|
||||
|
||||
last_motion_change.x = 0;
|
||||
@@ -230,8 +230,8 @@ void Mouse::MouseWheelChange(int x, int y) {
|
||||
wheel_position.x += x;
|
||||
wheel_position.y += y;
|
||||
last_motion_change.z += static_cast<f32>(y);
|
||||
SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
|
||||
SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
|
||||
SetAxis(mouse_identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
|
||||
SetAxis(mouse_identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
|
||||
}
|
||||
|
||||
void Mouse::ReleaseAllButtons() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -6,14 +9,14 @@
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
constexpr PadIdentifier identifier = {
|
||||
constexpr PadIdentifier touch_screen_identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
PreSetController(touch_screen_identifier);
|
||||
ReleaseAllTouch();
|
||||
}
|
||||
|
||||
@@ -26,9 +29,9 @@ void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) {
|
||||
}
|
||||
const auto i = index.value();
|
||||
fingers[i].is_active = true;
|
||||
SetButton(identifier, static_cast<int>(i), true);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), x);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), y);
|
||||
SetButton(touch_screen_identifier, static_cast<int>(i), true);
|
||||
SetAxis(touch_screen_identifier, static_cast<int>(i * 2), x);
|
||||
SetAxis(touch_screen_identifier, static_cast<int>(i * 2 + 1), y);
|
||||
}
|
||||
|
||||
void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) {
|
||||
@@ -55,9 +58,9 @@ void TouchScreen::TouchReleased(std::size_t finger_id) {
|
||||
}
|
||||
const auto i = index.value();
|
||||
fingers[i].is_enabled = false;
|
||||
SetButton(identifier, static_cast<int>(i), false);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), 0.0f);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f);
|
||||
SetButton(touch_screen_identifier, static_cast<int>(i), false);
|
||||
SetAxis(touch_screen_identifier, static_cast<int>(i * 2), 0.0f);
|
||||
SetAxis(touch_screen_identifier, static_cast<int>(i * 2 + 1), 0.0f);
|
||||
}
|
||||
|
||||
std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const {
|
||||
|
||||
@@ -600,7 +600,7 @@ void TestCommunication(const std::string& host, u16 port, const std::function<vo
|
||||
}
|
||||
|
||||
CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
const std::string& host, u16 port, std::function<void(Status)> status_callback,
|
||||
const std::string& host, u16 port, std::function<void(CalibrationStatus)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback) {
|
||||
|
||||
std::thread([=, this] {
|
||||
@@ -609,13 +609,13 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
u16 max_x{};
|
||||
u16 max_y{};
|
||||
|
||||
Status current_status{Status::Initialized};
|
||||
auto current_status = CalibrationStatus::Initialized;
|
||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
|
||||
if (current_status == Status::Initialized) {
|
||||
if (current_status == CalibrationStatus::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
current_status = CalibrationStatus::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].is_active == 0) {
|
||||
@@ -624,9 +624,9 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, data.touch[0].y);
|
||||
min_x = (std::min)(min_x, u16(data.touch[0].x));
|
||||
min_y = (std::min)(min_y, u16(data.touch[0].y));
|
||||
if (current_status == Status::Ready) {
|
||||
if (current_status == CalibrationStatus::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
current_status = CalibrationStatus::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
||||
@@ -635,7 +635,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
// configuration
|
||||
max_x = data.touch[0].x;
|
||||
max_y = data.touch[0].y;
|
||||
current_status = Status::Completed;
|
||||
current_status = CalibrationStatus::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -163,7 +166,7 @@ private:
|
||||
/// An async job allowing configuration of the touchpad calibration.
|
||||
class CalibrationConfigurationJob {
|
||||
public:
|
||||
enum class Status {
|
||||
enum class CalibrationStatus {
|
||||
Initialized,
|
||||
Ready,
|
||||
Stage1Completed,
|
||||
@@ -176,7 +179,7 @@ public:
|
||||
* @param data_callback Called when calibration data is ready
|
||||
*/
|
||||
explicit CalibrationConfigurationJob(const std::string& host, u16 port,
|
||||
std::function<void(Status)> status_callback,
|
||||
std::function<void(CalibrationStatus)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback);
|
||||
~CalibrationConfigurationJob();
|
||||
void Stop();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user