mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-31 02:35:39 +00:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6869ff75bf | |||
| 5395b2a87b | |||
| f55b8c796d | |||
| 16a955761b | |||
| 77ba06e2d9 | |||
| 06209ab415 | |||
| 5298745390 | |||
| 060ecd5e77 | |||
| 0d3c4074d0 | |||
| 0ba471a202 | |||
| 9ae3fc2560 | |||
| 4a3be2713e | |||
| 3889cb2620 | |||
| 735f0c33a1 | |||
| f37e00e4d2 | |||
| 93ce2ba3a4 | |||
| 3cc1bd83d8 | |||
| 61048eb0a5 | |||
| e3293372ca | |||
| 847dae355d | |||
| b2608b59b1 | |||
| 2330404fcb | |||
| e4eb216933 | |||
| 3d09b5a4e5 | |||
| c49f7a9640 | |||
| d3dc77b288 | |||
| f56e83fcf5 | |||
| 1d179de0e9 | |||
| 96f948a73a | |||
| 8681631324 | |||
| cca9d8dfbb | |||
| ffdf230d63 | |||
| f1fb639eca | |||
| 06f826ebb8 | |||
| 91f2d71b74 | |||
| 5ffdffaee2 | |||
| 85e4413e2d | |||
| d262c26677 | |||
| fc0c581983 | |||
| 102d2cab1f | |||
| 9129158851 | |||
| fbb25dd375 | |||
| bad0829899 | |||
| 1cb160e632 | |||
| 25ccd37d8f | |||
| 74543447f9 | |||
| 50baf56e3d | |||
| 07303cf62f | |||
| d497ede880 | |||
| 653db76c25 | |||
| 16ce2caa42 | |||
| 71b77c3025 | |||
| 106a61c943 | |||
| e5b656e372 | |||
| 297e797a32 | |||
| 48a95da874 | |||
| 2fd1dc0ff9 | |||
| 45b6ecff05 | |||
| 672bcbae01 | |||
| 28ab4a1a01 | |||
| ccf3de02cf | |||
| 1f03dee126 | |||
| 3df41c1e7a | |||
| f4a8f9421a | |||
| c0a85d0e53 | |||
| 54cd5fb8eb | |||
| 119291dc77 | |||
| f7dbff2157 | |||
| b859d7fdf4 | |||
| f635827fa6 | |||
| 243453c172 | |||
| 39158b67a7 | |||
| 50cb8fd1c9 |
+2
-2
@@ -340,10 +340,10 @@
|
|||||||
"version": "vulkan-sdk-%NUMERIC_VERSION%"
|
"version": "vulkan-sdk-%NUMERIC_VERSION%"
|
||||||
},
|
},
|
||||||
"xbyak": {
|
"xbyak": {
|
||||||
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
|
"hash": "e0aa0a603dd3ac1a39d82213df1e73c042831aec2d6b2fe382c899651eaae4ed7e8aeb6be9c41ea0097087c9d091961ab18f313cd6a9e10d621715ffd49bfe36",
|
||||||
"package": "xbyak",
|
"package": "xbyak",
|
||||||
"repo": "herumi/xbyak",
|
"repo": "herumi/xbyak",
|
||||||
"version": "v7.35.2"
|
"version": "v7.40.1"
|
||||||
},
|
},
|
||||||
"zlib": {
|
"zlib": {
|
||||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||||
|
|||||||
Vendored
+2
@@ -509,6 +509,8 @@ QWidget#contentRichDialog QLabel#label_title_rich {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QWidget#contentDialog QLabel#label_dialog {
|
QWidget#contentDialog QLabel#label_dialog {
|
||||||
|
background: #2E2E2E;
|
||||||
|
|
||||||
padding: 20px 65px;
|
padding: 20px 65px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-8
@@ -43,6 +43,7 @@ This guide will walk you through adding a new boolean toggle setting to Eden's c
|
|||||||
Firstly add your desired toggle:
|
Firstly add your desired toggle:
|
||||||
|
|
||||||
Example: `src/common/setting.h`
|
Example: `src/common/setting.h`
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
||||||
```
|
```
|
||||||
@@ -67,6 +68,7 @@ Common Categories:
|
|||||||
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
|
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`
|
Example: `src/qt_common/config/shared_translation.cpp`
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
INSERT(Settings,
|
INSERT(Settings,
|
||||||
your_setting_name,
|
your_setting_name,
|
||||||
@@ -91,6 +93,7 @@ INSERT(Settings,
|
|||||||
Add where it should be in the 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`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
||||||
```
|
```
|
||||||
@@ -106,6 +109,7 @@ RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
|||||||
Add the toggle to the Kotlin (Android) UI
|
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`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
@@ -123,6 +127,7 @@ put(
|
|||||||
Add your setting within the right category.
|
Add your setting within the right category.
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
||||||
```
|
```
|
||||||
@@ -137,6 +142,7 @@ add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
|||||||
Add your setting and description in the appropriate place.
|
Add your setting and description in the appropriate place.
|
||||||
|
|
||||||
Example: `src/android/app/src/main/res/values/strings.xml`
|
Example: `src/android/app/src/main/res/values/strings.xml`
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<string name="your_setting_name">Your Setting Display Name</string>
|
<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>
|
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
|
||||||
@@ -150,6 +156,7 @@ Now the UI part is done find a place in the code for the toggle,
|
|||||||
And use it to your heart's desire!
|
And use it to your heart's desire!
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const bool your_value = Settings::values.your_setting_name.GetValue();
|
const bool your_value = Settings::values.your_setting_name.GetValue();
|
||||||
|
|
||||||
@@ -196,25 +203,31 @@ Common advantages recap:
|
|||||||
|
|
||||||
#### Accessing Debug Knobs (dev side)
|
#### 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
|
||||||
//cpp side
|
//cpp side
|
||||||
#include "common/settings.h"
|
#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
|
// Check if bit 0 is set
|
||||||
bool feature_enabled = Settings::getDebugKnobAt(0);
|
bool feature_enabled = Settings::GetDebugKnobAt(0);
|
||||||
|
|
||||||
// Check if bit 15 is set
|
// Check if bit 15 is set
|
||||||
bool another_feature = Settings::getDebugKnobAt(15);
|
bool another_feature = Settings::GetDebugKnobAt(15);
|
||||||
```
|
```
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
//kotlin side
|
//kotlin side
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
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
|
// 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.
|
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
|
||||||
@@ -247,6 +260,7 @@ 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).
|
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:
|
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)
|
* 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.
|
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
|
||||||
|
|
||||||
@@ -259,6 +273,7 @@ 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:
|
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- **knobs=0**: no knobs enabled
|
- **knobs=0**: no knobs enabled
|
||||||
- **knobs=1**: knob0 enabled, others disabled
|
- **knobs=1**: knob0 enabled, others disabled
|
||||||
- **knobs=2**: knob1 enabled, others disabled
|
- **knobs=2**: knob1 enabled, others disabled
|
||||||
@@ -270,6 +285,7 @@ Examples:
|
|||||||
|
|
||||||
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- **knob0**: knob 0 enabled, others disabled
|
- **knob0**: knob 0 enabled, others disabled
|
||||||
- **knob1**: knob 1 enabled, others disabled
|
- **knob1**: knob 1 enabled, others disabled
|
||||||
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
||||||
@@ -282,12 +298,12 @@ Examples:
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void SomeFunction() {
|
void SomeFunction() {
|
||||||
if (Settings::getDebugKnobAt(0)) {
|
if (Settings::GetDebugKnobAt(0)) {
|
||||||
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
||||||
// Additional debug code here
|
// Additional debug code here
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::getDebugKnobAt(1)) {
|
if (Settings::GetDebugKnobAt(1)) {
|
||||||
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
||||||
// Different debug behavior
|
// Different debug behavior
|
||||||
}
|
}
|
||||||
@@ -299,7 +315,7 @@ void SomeFunction() {
|
|||||||
```cpp
|
```cpp
|
||||||
bool UseOptimizedPath() {
|
bool UseOptimizedPath() {
|
||||||
// Skip optimization if debug bit 2 is set for testing
|
// Skip optimization if debug bit 2 is set for testing
|
||||||
return !Settings::getDebugKnobAt(2);
|
return !Settings::GetDebugKnobAt(2);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -309,7 +325,7 @@ bool UseOptimizedPath() {
|
|||||||
void ExperimentalFeature() {
|
void ExperimentalFeature() {
|
||||||
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
||||||
|
|
||||||
if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
if (!Settings::GetDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
||||||
// Fallback to stable implementation
|
// Fallback to stable implementation
|
||||||
StableImplementation();
|
StableImplementation();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ object NativeLibrary {
|
|||||||
|
|
||||||
external fun refreshThreadPolicies()
|
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.
|
* Set the current speed limit to the configured turbo speed.
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ object Settings {
|
|||||||
fun getPlayerString(player: Int): String =
|
fun getPlayerString(player: Int): String =
|
||||||
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
||||||
|
|
||||||
fun getDebugKnobAt(index: Int): Boolean {
|
fun GetDebugKnobAt(index: Int): Boolean {
|
||||||
return org.yuzu.yuzu_emu.NativeLibrary.getDebugKnobAt(index)
|
return org.yuzu.yuzu_emu.NativeLibrary.GetDebugKnobAt(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
||||||
|
|||||||
+1
-2
@@ -11,8 +11,7 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
|||||||
enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
||||||
RENDERER_SPEED_LIMIT("speed_limit"),
|
RENDERER_SPEED_LIMIT("speed_limit"),
|
||||||
RENDERER_TURBO_SPEED_LIMIT("turbo_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)
|
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
|
||||||
|
|||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
||||||
+2
-1
@@ -20,6 +20,7 @@ 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.LongSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
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.StringSetting
|
||||||
|
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
||||||
import org.yuzu.yuzu_emu.network.NetDataValidators
|
import org.yuzu.yuzu_emu.network.NetDataValidators
|
||||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
@@ -1034,7 +1035,7 @@ abstract class SettingsItem(
|
|||||||
)
|
)
|
||||||
put(
|
put(
|
||||||
SpinBoxSetting(
|
SpinBoxSetting(
|
||||||
ShortSetting.DEBUG_KNOBS,
|
UShortSetting.DEBUG_KNOBS,
|
||||||
titleId = R.string.debug_knobs,
|
titleId = R.string.debug_knobs,
|
||||||
descriptionId = R.string.debug_knobs_description,
|
descriptionId = R.string.debug_knobs_description,
|
||||||
valueHint = R.string.debug_knobs_hint,
|
valueHint = R.string.debug_knobs_hint,
|
||||||
|
|||||||
+2
-1
@@ -25,6 +25,7 @@ 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.Settings.MenuTag
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
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.StringSetting
|
||||||
|
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
||||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||||
@@ -1326,7 +1327,7 @@ class SettingsFragmentPresenter(
|
|||||||
|
|
||||||
add(HeaderSetting(R.string.general))
|
add(HeaderSetting(R.string.general))
|
||||||
|
|
||||||
add(ShortSetting.DEBUG_KNOBS.key)
|
add(UShortSetting.DEBUG_KNOBS.key)
|
||||||
add(StringSetting.PROGRAM_ARGS.key)
|
add(StringSetting.PROGRAM_ARGS.key)
|
||||||
|
|
||||||
if (!NativeConfig.isPerGameConfigLoaded()) {
|
if (!NativeConfig.isPerGameConfigLoaded()) {
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ import info.debatty.java.stringsimilarity.Jaccard
|
|||||||
import info.debatty.java.stringsimilarity.JaroWinkler
|
import info.debatty.java.stringsimilarity.JaroWinkler
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.core.view.doOnNextLayout
|
|
||||||
|
|
||||||
class GamesFragment : Fragment() {
|
class GamesFragment : Fragment() {
|
||||||
private var _binding: FragmentGamesBinding? = null
|
private var _binding: FragmentGamesBinding? = null
|
||||||
@@ -59,7 +58,6 @@ class GamesFragment : Fragment() {
|
|||||||
private var originalHeaderLeftMargin: Int? = null
|
private var originalHeaderLeftMargin: Int? = null
|
||||||
|
|
||||||
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
||||||
private var fallbackBottomInset: Int = 0
|
|
||||||
private var pendingPostReloadListSettle = false
|
private var pendingPostReloadListSettle = false
|
||||||
private var pendingPostReloadListSettleGeneration = 0
|
private var pendingPostReloadListSettleGeneration = 0
|
||||||
private var gameListSubmitGeneration = 0
|
private var gameListSubmitGeneration = 0
|
||||||
@@ -227,12 +225,7 @@ class GamesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
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)
|
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
||||||
}
|
}
|
||||||
adapter = gameAdapter
|
adapter = gameAdapter
|
||||||
@@ -590,11 +583,6 @@ class GamesFragment : Fragment() {
|
|||||||
qlaunchButton.layoutParams = mlpQLaunch
|
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
|
windowInsets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ object NativeConfig {
|
|||||||
@Synchronized
|
@Synchronized
|
||||||
external fun setShort(key: String, value: Short)
|
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
|
@Synchronized
|
||||||
external fun getInt(key: String, needsGlobal: Boolean): Int
|
external fun getInt(key: String, needsGlobal: Boolean): Int
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,16 @@ import androidx.recyclerview.widget.PagerSnapHelper
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.pow
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
||||||
import androidx.core.view.doOnNextLayout
|
import androidx.core.view.doOnNextLayout
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
||||||
/**
|
/**
|
||||||
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
||||||
* It manages overlapping cards, center snapping, custom drawing order,
|
* It manages overlapping cards, center snapping, custom drawing order,
|
||||||
@@ -32,7 +35,9 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
|
|
||||||
private var overlapFactor: Float = 0f
|
private var overlapFactor: Float = 0f
|
||||||
private var overlapPx: Int = 0
|
private var overlapPx: Int = 0
|
||||||
private var bottomInset: Int = -1
|
private var bottomInset: Int = 0
|
||||||
|
private var latestWindowInsets: WindowInsetsCompat? = null
|
||||||
|
private var cardGeometryInitialized: Boolean = false
|
||||||
private var overlapDecoration: OverlappingDecoration? = null
|
private var overlapDecoration: OverlappingDecoration? = null
|
||||||
private var pagerSnapHelper: PagerSnapHelper? = null
|
private var pagerSnapHelper: PagerSnapHelper? = null
|
||||||
private var scalingScrollListener: OnScrollListener? = null
|
private var scalingScrollListener: OnScrollListener? = null
|
||||||
@@ -91,6 +96,38 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
setChildrenDrawingOrderEnabled(true)
|
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<*>?) {
|
override fun setAdapter(adapter: Adapter<*>?) {
|
||||||
@@ -103,6 +140,8 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
super.setAdapter(adapter)
|
super.setAdapter(adapter)
|
||||||
|
|
||||||
(adapter as? GameAdapter)?.registerAdapterDataObserver(carouselAdapterObserver)
|
(adapter as? GameAdapter)?.registerAdapterDataObserver(carouselAdapterObserver)
|
||||||
|
updateCardGeometry()
|
||||||
|
applyCarouselPadding()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateCenter(width: Int, paddingStart: Int, paddingEnd: Int): Int {
|
private fun calculateCenter(width: Int, paddingStart: Int, paddingEnd: Int): Int {
|
||||||
@@ -253,40 +292,71 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyInsetsReady(newBottomInset: Int) {
|
private fun resolveBottomInset(windowInsets: WindowInsetsCompat): Int {
|
||||||
if (bottomInset != newBottomInset) {
|
val navigationBottom = if (FullscreenHelper.isFullscreenEnabled(context)) {
|
||||||
bottomInset = newBottomInset
|
0
|
||||||
}
|
|
||||||
|
|
||||||
if (isCarouselMode) {
|
|
||||||
setupCarousel(true)
|
|
||||||
} else {
|
} else {
|
||||||
setupCarousel(false)
|
windowInsets.getInsetsIgnoringVisibility(WindowInsetsCompat.Type.navigationBars()).bottom
|
||||||
}
|
}
|
||||||
|
val gestureInsets = windowInsets.getInsetsIgnoringVisibility(
|
||||||
|
WindowInsetsCompat.Type.systemGestures()
|
||||||
|
)
|
||||||
|
val cutoutInsets = windowInsets.getInsetsIgnoringVisibility(
|
||||||
|
WindowInsetsCompat.Type.displayCutout()
|
||||||
|
)
|
||||||
|
return maxOf(navigationBottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyLaidOut(fallBackBottomInset: Int) {
|
private fun updateCardGeometry() {
|
||||||
if (bottomInset < 0) bottomInset = fallBackBottomInset
|
if (!isCarouselMode || height <= 0) return
|
||||||
var gameAdapter = adapter as? GameAdapter ?: return
|
|
||||||
var newCardSize = cardSize(bottomInset)
|
|
||||||
if (gameAdapter.cardSize != newCardSize) {
|
|
||||||
gameAdapter.setCardSize(newCardSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCarouselMode) {
|
val gameAdapter = adapter as? GameAdapter ?: return
|
||||||
setupCarousel(true)
|
val windowInsets = latestWindowInsets ?: ViewCompat.getRootWindowInsets(this) ?: return
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cardSize(bottomInset: Int): Int {
|
if (cardGeometryInitialized && !hasWindowFocus()) return
|
||||||
|
|
||||||
|
val newBottomInset = resolveBottomInset(windowInsets).coerceIn(0, height)
|
||||||
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
||||||
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
||||||
0f,
|
0f,
|
||||||
1f
|
1f
|
||||||
)
|
)
|
||||||
val scaledHeight = height * userFactor
|
val screenWidth = resources.displayMetrics.widthPixels.toFloat()
|
||||||
val availableHeight = height - bottomInset
|
val screenHeight = resources.displayMetrics.heightPixels.toFloat()
|
||||||
return minOf(scaledHeight.toInt(), availableHeight.toInt())
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setupCarousel(enabled: Boolean) {
|
fun setupCarousel(enabled: Boolean) {
|
||||||
@@ -315,9 +385,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
internalFlingMultiplier
|
internalFlingMultiplier
|
||||||
).coerceIn(1f, 5f)
|
).coerceIn(1f, 5f)
|
||||||
|
|
||||||
// Detach SnapHelper during setup
|
|
||||||
pagerSnapHelper?.attachToRecyclerView(null)
|
|
||||||
|
|
||||||
// Add overlap decoration if not present
|
// Add overlap decoration if not present
|
||||||
if (overlapDecoration == null) {
|
if (overlapDecoration == null) {
|
||||||
overlapDecoration = OverlappingDecoration(overlapPx)
|
overlapDecoration = OverlappingDecoration(overlapPx)
|
||||||
@@ -335,12 +402,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
addOnScrollListener(scalingScrollListener!!)
|
addOnScrollListener(scalingScrollListener!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardSize > 0) {
|
applyCarouselPadding()
|
||||||
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) {
|
if (pagerSnapHelper == null) {
|
||||||
pagerSnapHelper = CenterPagerSnapHelper()
|
pagerSnapHelper = CenterPagerSnapHelper()
|
||||||
@@ -362,6 +424,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
savedItemAnimator = null
|
savedItemAnimator = null
|
||||||
}
|
}
|
||||||
|
cardGeometryInitialized = false
|
||||||
useCustomDrawingOrder = false
|
useCustomDrawingOrder = false
|
||||||
// Reset padding and fling
|
// Reset padding and fling
|
||||||
setPadding(0, 0, 0, 0)
|
setPadding(0, 0, 0, 0)
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ extern "C" {
|
|||||||
#include "core/frontend/applets/software_keyboard.h"
|
#include "core/frontend/applets/software_keyboard.h"
|
||||||
#include "core/frontend/applets/web_browser.h"
|
#include "core/frontend/applets/web_browser.h"
|
||||||
#include "common/android/applets/web_browser.h"
|
#include "common/android/applets/web_browser.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/hle/service/am/applet_manager.h"
|
#include "core/hle/service/am/applet_manager.h"
|
||||||
#include "core/hle/service/am/frontend/applets.h"
|
#include "core/hle/service/am/frontend/applets.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
@@ -311,11 +312,23 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
|||||||
ConfigureFilesystemProvider(filepath);
|
ConfigureFilesystemProvider(filepath);
|
||||||
|
|
||||||
// Load the ROM.
|
// Load the ROM.
|
||||||
|
const u64 previous_program_id =
|
||||||
|
program_index != 0 && m_next_program_id.load() >
|
||||||
|
static_cast<u64>(Service::AM::AppletProgramId::MaxProgramId)
|
||||||
|
? m_next_program_id.load()
|
||||||
|
: 0;
|
||||||
|
|
||||||
Service::AM::FrontendAppletParameters params{
|
Service::AM::FrontendAppletParameters params{
|
||||||
|
.program_id = previous_program_id,
|
||||||
.applet_id = static_cast<Service::AM::AppletId>(m_applet_id),
|
.applet_id = static_cast<Service::AM::AppletId>(m_applet_id),
|
||||||
.launch_type = frontend_initiated ? Service::AM::LaunchType::FrontendInitiated
|
.launch_type = frontend_initiated ? Service::AM::LaunchType::FrontendInitiated
|
||||||
: Service::AM::LaunchType::ApplicationInitiated,
|
: Service::AM::LaunchType::ApplicationInitiated,
|
||||||
.program_index = static_cast<s32>(program_index),
|
.program_index = static_cast<s32>(program_index),
|
||||||
|
.previous_program_index =
|
||||||
|
previous_program_id != 0
|
||||||
|
? static_cast<s32>(previous_program_id -
|
||||||
|
FileSys::GetBaseTitleID(previous_program_id))
|
||||||
|
: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params);
|
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params);
|
||||||
@@ -328,8 +341,12 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
|||||||
m_system.GetCpuManager().OnGpuReady();
|
m_system.GetCpuManager().OnGpuReady();
|
||||||
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
||||||
|
|
||||||
|
m_system.RegisterApplicationChangedCallback(
|
||||||
|
[&](u64 changed_program_id) { RequestDiskShaderCacheReload(changed_program_id); });
|
||||||
|
|
||||||
// Register an ExecuteProgram callback such that Core can execute a sub-program
|
// Register an ExecuteProgram callback such that Core can execute a sub-program
|
||||||
m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
|
m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
|
||||||
|
m_next_program_id = m_system.GetApplicationProcessProgramID();
|
||||||
m_next_program_index = program_index_;
|
m_next_program_index = program_index_;
|
||||||
EmulationSession::GetInstance().HaltEmulation();
|
EmulationSession::GetInstance().HaltEmulation();
|
||||||
});
|
});
|
||||||
@@ -407,20 +424,58 @@ void EmulationSession::RunEmulation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
std::optional<u64> reload_title;
|
||||||
{
|
{
|
||||||
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
||||||
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
if (m_cv.wait_for(lock, std::chrono::milliseconds(800), [&]() {
|
||||||
[&]() { return !m_is_running; })) {
|
return !m_is_running || m_pending_shader_cache_title.has_value();
|
||||||
// Emulation halted.
|
})) {
|
||||||
break;
|
if (!m_is_running) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reload_title = std::exchange(m_pending_shader_cache_title, std::nullopt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reload_title.has_value())
|
||||||
|
ReloadDiskShaderCache(*reload_title);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset current applet ID.
|
// Reset current applet ID.
|
||||||
m_applet_id = static_cast<int>(Service::AM::AppletId::Application);
|
m_applet_id = static_cast<int>(Service::AM::AppletId::Application);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmulationSession::RequestDiskShaderCacheReload(u64 program_id) {
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
m_pending_shader_cache_title = program_id;
|
||||||
|
}
|
||||||
|
m_cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::ReloadDiskShaderCache(u64 program_id) {
|
||||||
|
if (!Settings::values.use_disk_shader_cache.GetValue())
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG_INFO(Frontend, "Reloading disk shader cache for {:016X}", program_id);
|
||||||
|
|
||||||
|
const bool was_paused = m_is_paused;
|
||||||
|
|
||||||
|
m_system.Pause();
|
||||||
|
m_system.GPU().WaitForIdle();
|
||||||
|
m_system.GPU().ObtainContext();
|
||||||
|
|
||||||
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||||
|
m_system.Renderer().ReadRasterizer()->LoadDiskResources(program_id, std::stop_token{},
|
||||||
|
LoadDiskCacheProgress);
|
||||||
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||||
|
|
||||||
|
m_system.GPU().ReleaseContext();
|
||||||
|
|
||||||
|
if (!was_paused)
|
||||||
|
m_system.Run();
|
||||||
|
}
|
||||||
|
|
||||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
|
Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
|
||||||
return m_software_keyboard;
|
return m_software_keyboard;
|
||||||
}
|
}
|
||||||
@@ -1245,8 +1300,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_refreshThreadPolicies(JNIEnv* env, jo
|
|||||||
Common::RefreshThreadPolicies();
|
Common::RefreshThreadPolicies();
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_getDebugKnobAt(JNIEnv* env, jobject jobj, jint 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)));
|
return static_cast<jboolean>(Settings::GetDebugKnobAt(static_cast<u8>(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <android/native_window_jni.h>
|
#include <android/native_window_jni.h>
|
||||||
#include "common/android/applets/software_keyboard.h"
|
#include "common/android/applets/software_keyboard.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
@@ -44,6 +46,7 @@ public:
|
|||||||
void HaltEmulation();
|
void HaltEmulation();
|
||||||
void RunEmulation();
|
void RunEmulation();
|
||||||
void ShutdownEmulation();
|
void ShutdownEmulation();
|
||||||
|
void RequestDiskShaderCacheReload(u64 program_id);
|
||||||
|
|
||||||
const Core::PerfStatsResults& PerfStats();
|
const Core::PerfStatsResults& PerfStats();
|
||||||
int ShadersBuilding();
|
int ShadersBuilding();
|
||||||
@@ -65,6 +68,7 @@ private:
|
|||||||
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
||||||
static void OnEmulationStopped(Core::SystemResultStatus result);
|
static void OnEmulationStopped(Core::SystemResultStatus result);
|
||||||
static void ChangeProgram(std::size_t program_index);
|
static void ChangeProgram(std::size_t program_index);
|
||||||
|
void ReloadDiskShaderCache(u64 program_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Window management
|
// Window management
|
||||||
@@ -83,6 +87,7 @@ private:
|
|||||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||||
int m_applet_id{1};
|
int m_applet_id{1};
|
||||||
|
std::optional<u64> m_pending_shader_cache_title;
|
||||||
|
|
||||||
// GPU driver parameters
|
// GPU driver parameters
|
||||||
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
||||||
@@ -93,4 +98,5 @@ private:
|
|||||||
|
|
||||||
// Program index for next boot
|
// Program index for next boot
|
||||||
std::atomic<s32> m_next_program_index = -1;
|
std::atomic<s32> m_next_program_index = -1;
|
||||||
|
std::atomic<u64> m_next_program_id = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -130,6 +130,25 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject ob
|
|||||||
setting->SetValue(value);
|
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,
|
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
|
||||||
jboolean needGlobal) {
|
jboolean needGlobal) {
|
||||||
auto setting = getSetting<int>(env, jkey);
|
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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -7,13 +10,12 @@
|
|||||||
namespace AudioCore::ADSP::OpusDecoder {
|
namespace AudioCore::ADSP::OpusDecoder {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
bool IsValidChannelCount(u32 channel_count) {
|
constexpr u32 OpusStreamCountMax = 255;
|
||||||
return channel_count == 1 || channel_count == 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
|
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
|
||||||
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
|
return total_stream_count > 0 && total_stream_count <= OpusStreamCountMax &&
|
||||||
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
|
static_cast<s32>(stereo_stream_count) >= 0 &&
|
||||||
|
stereo_stream_count <= total_stream_count;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -105,7 +108,7 @@ Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 chan
|
|||||||
shared_memory.host_send_data[4] = total_stream_count;
|
shared_memory.host_send_data[4] = total_stream_count;
|
||||||
shared_memory.host_send_data[5] = stereo_stream_count;
|
shared_memory.host_send_data[5] = stereo_stream_count;
|
||||||
|
|
||||||
ASSERT(channel_count <= MaxChannels);
|
ASSERT(channel_count <= shared_memory.channel_mapping.size());
|
||||||
std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
|
std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
|
||||||
|
|
||||||
opus_decoder.Send(ADSP::Direction::DSP,
|
opus_decoder.Send(ADSP::Direction::DSP,
|
||||||
|
|||||||
@@ -19,6 +19,23 @@
|
|||||||
namespace AudioCore::Sink {
|
namespace AudioCore::Sink {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] bool InitializeAudio() {
|
||||||
|
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||||
|
// See https://github.com/PCSX2/pcsx2/pull/12312
|
||||||
|
// "SDL and cubeb backends previously resulted in different names for the output which
|
||||||
|
// caused them be identified as different applications by the OS."
|
||||||
|
//
|
||||||
|
// Keep in sync with cubeb_sink.cpp name.
|
||||||
|
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter");
|
||||||
|
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_AudioDeviceID FindAudioDeviceByName(const std::string& device_name, bool capture) {
|
SDL_AudioDeviceID FindAudioDeviceByName(const std::string& device_name, bool capture) {
|
||||||
int device_count = 0;
|
int device_count = 0;
|
||||||
SDL_AudioDeviceID* devices = capture ? SDL_GetAudioRecordingDevices(&device_count)
|
SDL_AudioDeviceID* devices = capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||||
@@ -204,20 +221,14 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
SDLSink::SDLSink(std::string_view target_device_name) {
|
SDLSink::SDLSink(std::string_view target_device_name) {
|
||||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
if (InitializeAudio()) {
|
||||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
output_device = target_device_name;
|
||||||
return;
|
} else {
|
||||||
|
output_device.clear();
|
||||||
}
|
}
|
||||||
|
device_channels = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
|
||||||
output_device = target_device_name;
|
|
||||||
} else {
|
|
||||||
output_device.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
device_channels = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SDLSink::~SDLSink() = default;
|
SDLSink::~SDLSink() = default;
|
||||||
@@ -265,15 +276,10 @@ void SDLSink::SetSystemVolume(f32 volume) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
||||||
|
if (!InitializeAudio())
|
||||||
|
return {}; //no devices
|
||||||
|
|
||||||
std::vector<std::string> device_list;
|
std::vector<std::string> device_list;
|
||||||
|
|
||||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
|
||||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
|
||||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int device_count = 0;
|
int device_count = 0;
|
||||||
SDL_AudioDeviceID* devices =
|
SDL_AudioDeviceID* devices =
|
||||||
capture ? SDL_GetAudioRecordingDevices(&device_count)
|
capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||||
@@ -304,13 +310,8 @@ bool IsSDLSuitable() {
|
|||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
// Check SDL can init
|
// Check SDL can init
|
||||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
if (!InitializeAudio()!
|
||||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
return false;
|
||||||
LOG_ERROR(Audio_Sink, "SDL failed to init, it is not suitable. Error: {}",
|
|
||||||
SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can set any latency frequency we want with SDL, so no need to check that.
|
// We can set any latency frequency we want with SDL, so no need to check that.
|
||||||
|
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ add_library(
|
|||||||
settings_input.h
|
settings_input.h
|
||||||
settings_setting.h
|
settings_setting.h
|
||||||
slot_vector.h
|
slot_vector.h
|
||||||
socket_types.h
|
|
||||||
spin_lock.h
|
spin_lock.h
|
||||||
stb.cpp
|
stb.cpp
|
||||||
stb.h
|
stb.h
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "web_service/web_result.h"
|
#include "web_service/web_result.h"
|
||||||
|
|
||||||
namespace AnnounceMultiplayerRoom {
|
namespace AnnounceMultiplayerRoom {
|
||||||
|
|||||||
+27
-15
@@ -329,7 +329,7 @@ struct LogcatBackend : public Backend {
|
|||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
auto const df = GetDirectFormatArgs(entry);
|
auto const df = GetDirectFormatArgs(entry);
|
||||||
__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);
|
__android_log_print(android_log_priority, "YuzuNative", "%s %s:%u:%s: %s", df.class_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||||
}
|
}
|
||||||
void Flush() noexcept override {}
|
void Flush() noexcept override {}
|
||||||
};
|
};
|
||||||
@@ -428,21 +428,33 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, u
|
|||||||
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
||||||
char buffer[BUFSIZ];
|
char buffer[BUFSIZ];
|
||||||
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
||||||
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
Entry e{
|
||||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
.message = nullptr,
|
||||||
backend.Write(Entry{
|
.message_len = 0,
|
||||||
.message = buffer,
|
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
||||||
.message_len = (std::min)(result.size, sizeof(buffer) - 1),
|
.log_class = log_class,
|
||||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
.log_level = log_level,
|
||||||
.log_class = log_class,
|
.filename = TrimSourcePath(filename),
|
||||||
.log_level = log_level,
|
.function = function,
|
||||||
.filename = TrimSourcePath(filename),
|
.line_num = line_num,
|
||||||
.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();
|
||||||
});
|
});
|
||||||
if (flush)
|
} else {
|
||||||
backend.Flush();
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace Common::Log
|
} // namespace Common::Log
|
||||||
|
|||||||
@@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
namespace Common::Net {
|
namespace Common::Net {
|
||||||
|
|
||||||
typedef struct {
|
struct Asset {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string url;
|
std::string url;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
} Asset;
|
};
|
||||||
|
|
||||||
typedef struct Release {
|
struct Release {
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string body;
|
std::string body;
|
||||||
std::string tag;
|
std::string tag;
|
||||||
@@ -39,7 +39,7 @@ typedef struct Release {
|
|||||||
static std::optional<Release> FromJson(const std::string_view& json, const std::string &host, const std::string& repo);
|
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 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);
|
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.
|
// 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);
|
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->UsingGlobal() ? '-' : 'C', TranslateCategory(category),
|
||||||
setting->GetLabel());
|
setting->GetLabel());
|
||||||
if (is_default)
|
if (is_default)
|
||||||
settings_list.push_back(fmt::format("{}: {}\n", name, setting->Canonicalize()));
|
settings_list.push_back(fmt::format("{}: {}", name, setting->Canonicalize()));
|
||||||
else
|
else
|
||||||
settings_list.push_front(fmt::format("{}: {}\n", name, setting->Canonicalize()));
|
settings_list.push_front(fmt::format("{}: {}", name, setting->Canonicalize()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ void LogSettings() {
|
|||||||
#undef LOG_PATH
|
#undef LOG_PATH
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getDebugKnobAt(u8 i) {
|
bool GetDebugKnobAt(u8 i) {
|
||||||
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
|
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -904,7 +904,7 @@ struct Values {
|
|||||||
0,
|
0,
|
||||||
65535,
|
65535,
|
||||||
"debug_knobs",
|
"debug_knobs",
|
||||||
Category::Debugging,
|
Category::System,
|
||||||
Specialization::Countable,
|
Specialization::Countable,
|
||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
@@ -947,7 +947,7 @@ constexpr u32 MAX_FRAME_GEN_MULTIPLIER = 4;
|
|||||||
|
|
||||||
[[nodiscard]] size_t FrameGenMaxGenerations();
|
[[nodiscard]] size_t FrameGenMaxGenerations();
|
||||||
|
|
||||||
bool getDebugKnobAt(u8 i);
|
bool GetDebugKnobAt(u8 i);
|
||||||
|
|
||||||
void UpdateGPUAccuracy();
|
void UpdateGPUAccuracy();
|
||||||
bool IsGPULevelHigh();
|
bool IsGPULevelHigh();
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
// 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
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace Network {
|
|
||||||
|
|
||||||
/// Address families
|
|
||||||
enum class Domain : u8 {
|
|
||||||
Unspecified, ///< Represents 0, used in getaddrinfo hints
|
|
||||||
INET, ///< Address family for IPv4
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Socket types
|
|
||||||
enum class Type {
|
|
||||||
Unspecified, ///< Represents 0, used in getaddrinfo hints
|
|
||||||
STREAM,
|
|
||||||
DGRAM,
|
|
||||||
RAW,
|
|
||||||
SEQPACKET,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Protocol values for sockets
|
|
||||||
enum class Protocol : u8 {
|
|
||||||
Unspecified, ///< Represents 0, usable in various places
|
|
||||||
IP,
|
|
||||||
ICMP,
|
|
||||||
TCP,
|
|
||||||
UDP,
|
|
||||||
IPV6,
|
|
||||||
RAW,
|
|
||||||
IGMP,
|
|
||||||
GGP,
|
|
||||||
IPV4,
|
|
||||||
ST,
|
|
||||||
EGP,
|
|
||||||
PIGP,
|
|
||||||
RCCMON,
|
|
||||||
NVPII,
|
|
||||||
PUP,
|
|
||||||
ARGUS,
|
|
||||||
EMCON,
|
|
||||||
XNET,
|
|
||||||
CHAOS,
|
|
||||||
MUX,
|
|
||||||
MEAS,
|
|
||||||
HMP,
|
|
||||||
PRM,
|
|
||||||
IDP,
|
|
||||||
TRUNK1,
|
|
||||||
TRUNK2,
|
|
||||||
LEAF1,
|
|
||||||
LEAF2,
|
|
||||||
RDP,
|
|
||||||
IRTP,
|
|
||||||
TP,
|
|
||||||
BLT,
|
|
||||||
NSP,
|
|
||||||
INP,
|
|
||||||
DCCP,
|
|
||||||
//TODO: 3PC,
|
|
||||||
IDPR,
|
|
||||||
XTP,
|
|
||||||
DDP,
|
|
||||||
CMTP,
|
|
||||||
TPXX,
|
|
||||||
IL,
|
|
||||||
SDRP,
|
|
||||||
ROUTING,
|
|
||||||
FRAGMENT,
|
|
||||||
IDRP,
|
|
||||||
RSVP,
|
|
||||||
GRE,
|
|
||||||
MHRP,
|
|
||||||
BHA,
|
|
||||||
ESP,
|
|
||||||
AH,
|
|
||||||
INLSP,
|
|
||||||
SWIPE,
|
|
||||||
NHRP,
|
|
||||||
MOBILE,
|
|
||||||
TLSP,
|
|
||||||
SKIP,
|
|
||||||
ICMPV6,
|
|
||||||
NONE,
|
|
||||||
DSTOPTS,
|
|
||||||
AHIP,
|
|
||||||
CFTP,
|
|
||||||
HELLO,
|
|
||||||
SATEXPAK,
|
|
||||||
KRYPTOLAN,
|
|
||||||
RVD,
|
|
||||||
IPPC,
|
|
||||||
ADFS,
|
|
||||||
SATMON,
|
|
||||||
VISA,
|
|
||||||
IPCV,
|
|
||||||
CPNX,
|
|
||||||
CPHB,
|
|
||||||
WSN,
|
|
||||||
PVP,
|
|
||||||
BRSATMON,
|
|
||||||
ND,
|
|
||||||
WBMON,
|
|
||||||
WBEXPAK,
|
|
||||||
EON,
|
|
||||||
VMTP,
|
|
||||||
SVMTP,
|
|
||||||
VINES,
|
|
||||||
TTP,
|
|
||||||
IGP,
|
|
||||||
DGP,
|
|
||||||
TCF,
|
|
||||||
IGRP,
|
|
||||||
OSPFIGP,
|
|
||||||
SRPC,
|
|
||||||
LARP,
|
|
||||||
MTP,
|
|
||||||
AX25,
|
|
||||||
IPEIP,
|
|
||||||
MICP,
|
|
||||||
SCCSP,
|
|
||||||
ETHERIP,
|
|
||||||
ENCAP,
|
|
||||||
APES,
|
|
||||||
GMTP,
|
|
||||||
IPCOMP,
|
|
||||||
SCTP,
|
|
||||||
MH,
|
|
||||||
UDPLITE,
|
|
||||||
HIP,
|
|
||||||
SHIM6,
|
|
||||||
PIM,
|
|
||||||
CARP,
|
|
||||||
PGM,
|
|
||||||
MPLS,
|
|
||||||
PFSYNC
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Shutdown mode
|
|
||||||
enum class ShutdownHow {
|
|
||||||
RD,
|
|
||||||
WR,
|
|
||||||
RDWR,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Array of IPv4 address
|
|
||||||
using IPv4Address = std::array<u8, 4>;
|
|
||||||
|
|
||||||
/// Cross-platform sockaddr structure
|
|
||||||
struct SockAddrIn {
|
|
||||||
Domain family;
|
|
||||||
IPv4Address ip;
|
|
||||||
u16 portno;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr u32 FLAG_MSG_PEEK = 0x2;
|
|
||||||
constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
|
|
||||||
constexpr u32 FLAG_O_NONBLOCK = 0x800;
|
|
||||||
|
|
||||||
/// Cross-platform addrinfo structure
|
|
||||||
struct AddrInfo {
|
|
||||||
Domain family;
|
|
||||||
Type socket_type;
|
|
||||||
Protocol protocol;
|
|
||||||
SockAddrIn addr;
|
|
||||||
std::optional<std::string> canon_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Network
|
|
||||||
@@ -7,6 +7,4 @@
|
|||||||
#define STB_IMAGE_IMPLEMENTATION 1
|
#define STB_IMAGE_IMPLEMENTATION 1
|
||||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION 1
|
#define STB_IMAGE_RESIZE_IMPLEMENTATION 1
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION 1
|
#define STB_IMAGE_WRITE_IMPLEMENTATION 1
|
||||||
#define STBI_ONLY_JPEG 1
|
|
||||||
|
|
||||||
#include "common/stb.h"
|
#include "common/stb.h"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define STBI_ONLY_JPEG 1
|
#define STBI_ONLY_JPEG 1
|
||||||
|
#define STBI_WRITE_NO_STDIO 1
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
#include <stb_image_resize.h>
|
#include <stb_image_resize.h>
|
||||||
#include <stb_image_write.h>
|
#include <stb_image_write.h>
|
||||||
|
|||||||
@@ -1129,6 +1129,7 @@ add_library(core STATIC
|
|||||||
internal_network/network_interface.h
|
internal_network/network_interface.h
|
||||||
internal_network/socket_proxy.cpp
|
internal_network/socket_proxy.cpp
|
||||||
internal_network/socket_proxy.h
|
internal_network/socket_proxy.h
|
||||||
|
internal_network/socket_types.h
|
||||||
internal_network/sockets.h
|
internal_network/sockets.h
|
||||||
internal_network/wifi_scanner.h
|
internal_network/wifi_scanner.h
|
||||||
launch_timestamp_cache.cpp
|
launch_timestamp_cache.cpp
|
||||||
@@ -1167,6 +1168,12 @@ add_library(core STATIC
|
|||||||
tools/renderdoc.cpp
|
tools/renderdoc.cpp
|
||||||
tools/renderdoc.h)
|
tools/renderdoc.h)
|
||||||
|
|
||||||
|
if (UNIX AND NOT APPLE)
|
||||||
|
target_sources(core PRIVATE
|
||||||
|
internal_network/socket_icmp.cpp
|
||||||
|
internal_network/socket_icmp.h)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (ENABLE_WIFI_SCAN)
|
if (ENABLE_WIFI_SCAN)
|
||||||
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
|
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
|
||||||
if (LINUX)
|
if (LINUX)
|
||||||
|
|||||||
+77
-8
@@ -4,6 +4,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "game_settings.h"
|
#include "game_settings.h"
|
||||||
@@ -248,12 +249,30 @@ struct System::Impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetNVDECActive(bool is_nvdec_active) {
|
void NotifyNVDECChannelOpen(u64 process_id) {
|
||||||
nvdec_active = is_nvdec_active;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetNVDECActive() {
|
bool GetNVDECActive() {
|
||||||
return nvdec_active;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeDebugger(System& system, u16 port) {
|
void InitializeDebugger(System& system, u16 port) {
|
||||||
@@ -295,6 +314,10 @@ struct System::Impl {
|
|||||||
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) {
|
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) {
|
||||||
InitializeKernel(system);
|
InitializeKernel(system);
|
||||||
|
|
||||||
|
if (params.applet_type == Service::AM::AppletType::Application) {
|
||||||
|
current_application_filepath = filepath;
|
||||||
|
}
|
||||||
|
|
||||||
const auto file = GetGameFileFromPath(virtual_filesystem, filepath);
|
const auto file = GetGameFileFromPath(virtual_filesystem, filepath);
|
||||||
|
|
||||||
// Create the application process
|
// Create the application process
|
||||||
@@ -330,7 +353,7 @@ struct System::Impl {
|
|||||||
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
|
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
|
||||||
|
|
||||||
// Make the process created be the application
|
// Make the process created be the application
|
||||||
kernel.MakeApplicationProcess(process->GetHandle());
|
kernel.SetApplicationProcess(process->GetHandle());
|
||||||
|
|
||||||
// Set up the rest of the system.
|
// Set up the rest of the system.
|
||||||
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
|
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
|
||||||
@@ -467,6 +490,7 @@ struct System::Impl {
|
|||||||
Core::SpeedLimiter speed_limiter;
|
Core::SpeedLimiter speed_limiter;
|
||||||
ExecuteProgramCallback execute_program_callback;
|
ExecuteProgramCallback execute_program_callback;
|
||||||
ExitCallback exit_callback;
|
ExitCallback exit_callback;
|
||||||
|
ApplicationChangedCallback application_changed_callback;
|
||||||
|
|
||||||
std::optional<Service::Services> services;
|
std::optional<Service::Services> services;
|
||||||
std::optional<Core::Debugger> debugger;
|
std::optional<Core::Debugger> debugger;
|
||||||
@@ -488,6 +512,8 @@ struct System::Impl {
|
|||||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
||||||
std::array<u8, 0x20> build_id{};
|
std::array<u8, 0x20> build_id{};
|
||||||
|
|
||||||
|
std::string current_application_filepath;
|
||||||
|
|
||||||
/// Service manager
|
/// Service manager
|
||||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||||
/// ContentProviderUnion instance
|
/// ContentProviderUnion instance
|
||||||
@@ -498,6 +524,8 @@ struct System::Impl {
|
|||||||
|
|
||||||
mutable std::mutex suspend_guard;
|
mutable std::mutex suspend_guard;
|
||||||
std::mutex general_channel_mutex;
|
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_paused{};
|
||||||
std::atomic_bool is_shutting_down{};
|
std::atomic_bool is_shutting_down{};
|
||||||
std::atomic_bool is_powered_on{};
|
std::atomic_bool is_powered_on{};
|
||||||
@@ -505,7 +533,6 @@ struct System::Impl {
|
|||||||
bool extended_memory_layout : 1 = false;
|
bool extended_memory_layout : 1 = false;
|
||||||
bool exit_locked : 1 = false;
|
bool exit_locked : 1 = false;
|
||||||
bool exit_requested : 1 = false;
|
bool exit_requested : 1 = false;
|
||||||
bool nvdec_active : 1 = false;
|
|
||||||
|
|
||||||
void EnsureGeneralChannelInitialized(System& system) {
|
void EnsureGeneralChannelInitialized(System& system) {
|
||||||
if (!general_channel_event) {
|
if (!general_channel_event) {
|
||||||
@@ -569,14 +596,22 @@ void System::UnstallApplication() {
|
|||||||
impl->UnstallApplication();
|
impl->UnstallApplication();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SetNVDECActive(bool is_nvdec_active) {
|
void System::NotifyNVDECChannelOpen(u64 process_id) {
|
||||||
impl->SetNVDECActive(is_nvdec_active);
|
impl->NotifyNVDECChannelOpen(process_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::NotifyNVDECChannelClose(u64 process_id) {
|
||||||
|
impl->NotifyNVDECChannelClose(process_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::GetNVDECActive() {
|
bool System::GetNVDECActive() {
|
||||||
return impl->GetNVDECActive();
|
return impl->GetNVDECActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool System::IsNVDECActiveForProcess(u64 process_id) {
|
||||||
|
return impl->IsNVDECActiveForProcess(process_id);
|
||||||
|
}
|
||||||
|
|
||||||
void System::InitializeDebugger() {
|
void System::InitializeDebugger() {
|
||||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||||
}
|
}
|
||||||
@@ -727,7 +762,25 @@ const Core::SpeedLimiter& System::SpeedLimiter() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 System::GetApplicationProcessProgramID() const {
|
u64 System::GetApplicationProcessProgramID() const {
|
||||||
return impl->kernel.ApplicationProcess()->GetProgramId();
|
const auto* const process = impl->kernel.ApplicationProcess();
|
||||||
|
return process != nullptr ? process->GetProgramId() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 System::GetProgramIdForProcessId(u64 process_id) const {
|
||||||
|
auto process = impl->kernel.GetProcessByProcessId(process_id);
|
||||||
|
return process.IsNull() ? 0 : process->GetProgramId();
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 System::ResolveCallerProgramId(u64 process_id) const {
|
||||||
|
if (const auto program_id = this->GetProgramIdForProcessId(process_id); program_id != 0) {
|
||||||
|
return program_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fallback = this->GetApplicationProcessProgramID();
|
||||||
|
LOG_WARNING(Core,
|
||||||
|
"Could not resolve caller process_id={}, falling back to application {:016X}",
|
||||||
|
process_id, fallback);
|
||||||
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
||||||
@@ -910,6 +963,10 @@ void System::ExecuteProgram(std::size_t program_index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& System::GetCurrentApplicationFilePath() const {
|
||||||
|
return impl->current_application_filepath;
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Gets a reference to the user channel stack.
|
/// @brief Gets a reference to the user channel stack.
|
||||||
/// It is used to transfer data between programs.
|
/// It is used to transfer data between programs.
|
||||||
std::vector<std::vector<u8>>& System::GetUserChannel() {
|
std::vector<std::vector<u8>>& System::GetUserChannel() {
|
||||||
@@ -961,6 +1018,18 @@ void System::Exit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback) {
|
||||||
|
impl->application_changed_callback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::NotifyApplicationChanged(u64 program_id) {
|
||||||
|
//LOG_DEBUG(Core, "Running application changed to {:016X}", program_id);
|
||||||
|
|
||||||
|
if (impl->application_changed_callback) {
|
||||||
|
impl->application_changed_callback(program_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void System::ApplySettings() {
|
void System::ApplySettings() {
|
||||||
impl->RefreshTime(*this);
|
impl->RefreshTime(*this);
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -191,8 +191,10 @@ public:
|
|||||||
std::unique_lock<std::mutex> StallApplication();
|
std::unique_lock<std::mutex> StallApplication();
|
||||||
void UnstallApplication();
|
void UnstallApplication();
|
||||||
|
|
||||||
void SetNVDECActive(bool is_nvdec_active);
|
void NotifyNVDECChannelOpen(u64 process_id);
|
||||||
|
void NotifyNVDECChannelClose(u64 process_id);
|
||||||
[[nodiscard]] bool GetNVDECActive();
|
[[nodiscard]] bool GetNVDECActive();
|
||||||
|
[[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the debugger.
|
* Initialize the debugger.
|
||||||
@@ -322,6 +324,10 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] u64 GetApplicationProcessProgramID() const;
|
[[nodiscard]] u64 GetApplicationProcessProgramID() const;
|
||||||
|
|
||||||
|
[[nodiscard]] u64 GetProgramIdForProcessId(u64 process_id) const;
|
||||||
|
|
||||||
|
[[nodiscard]] u64 ResolveCallerProgramId(u64 process_id) const;
|
||||||
|
|
||||||
/// Gets the name of the current game
|
/// Gets the name of the current game
|
||||||
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
||||||
|
|
||||||
@@ -422,6 +428,7 @@ public:
|
|||||||
void PushGeneralChannelData(std::vector<u8>&& data);
|
void PushGeneralChannelData(std::vector<u8>&& data);
|
||||||
bool TryPopGeneralChannel(std::vector<u8>& out_data);
|
bool TryPopGeneralChannel(std::vector<u8>& out_data);
|
||||||
[[nodiscard]] Service::Event& GetGeneralChannelEvent();
|
[[nodiscard]] Service::Event& GetGeneralChannelEvent();
|
||||||
|
[[nodiscard]] const std::string& GetCurrentApplicationFilePath() const;
|
||||||
|
|
||||||
/// Type used for the frontend to designate a callback for System to exit the application.
|
/// Type used for the frontend to designate a callback for System to exit the application.
|
||||||
using ExitCallback = std::function<void()>;
|
using ExitCallback = std::function<void()>;
|
||||||
@@ -435,6 +442,10 @@ public:
|
|||||||
/// Instructs the frontend to exit the application.
|
/// Instructs the frontend to exit the application.
|
||||||
void Exit();
|
void Exit();
|
||||||
|
|
||||||
|
using ApplicationChangedCallback = std::function<void(u64 program_id)>;
|
||||||
|
void RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback);
|
||||||
|
void NotifyApplicationChanged(u64 program_id);
|
||||||
|
|
||||||
/// Applies any changes to settings to this core instance.
|
/// Applies any changes to settings to this core instance.
|
||||||
void ApplySettings();
|
void ApplySettings();
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ static u8 MasterKeyIdForKeyGeneration(u8 key_generation) {
|
|||||||
return std::max<u8>(key_generation, 1) - 1;
|
return std::max<u8>(key_generation, 1) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
NCA::NCA(VirtualFile file_, const NCA* base_nca)
|
NCA::NCA(VirtualFile file_, const NCA* base_nca, bool allow_missing_base)
|
||||||
: file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
|
: file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
status = Loader::ResultStatus::ErrorNullFile;
|
status = Loader::ResultStatus::ErrorNullFile;
|
||||||
@@ -110,7 +110,7 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_update && base_nca == nullptr) {
|
if (is_update && base_nca == nullptr && !allow_missing_base) {
|
||||||
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
|
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
|
||||||
} else {
|
} else {
|
||||||
status = Loader::ResultStatus::Success;
|
status = Loader::ResultStatus::Success;
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -63,7 +66,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) {
|
|||||||
// After construction, use GetStatus to determine if the file is valid and ready to be used.
|
// After construction, use GetStatus to determine if the file is valid and ready to be used.
|
||||||
class NCA : public ReadOnlyVfsDirectory {
|
class NCA : public ReadOnlyVfsDirectory {
|
||||||
public:
|
public:
|
||||||
explicit NCA(VirtualFile file, const NCA* base_nca = nullptr);
|
explicit NCA(VirtualFile file, const NCA* base_nca = nullptr, bool allow_missing_base = false);
|
||||||
~NCA() override;
|
~NCA() override;
|
||||||
|
|
||||||
Loader::ResultStatus GetStatus() const;
|
Loader::ResultStatus GetStatus() const;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/logging.h"
|
||||||
#include "core/file_sys/errors.h"
|
#include "core/file_sys/errors.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_indirect_storage.h"
|
#include "core/file_sys/fssystem/fssystem_indirect_storage.h"
|
||||||
|
|
||||||
@@ -99,6 +100,18 @@ Result IndirectStorage::GetEntryList(Entry* out_entries, s32* out_entry_count, s
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IndirectStorage::ReportMissingOriginal(s64 offset, s64 size) {
|
||||||
|
if (m_reported_missing_original) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_reported_missing_original = true;
|
||||||
|
|
||||||
|
LOG_ERROR(Common_Filesystem,
|
||||||
|
"Patch storage requests {:#x} bytes at {:#x} from a base storage that is not "
|
||||||
|
"present; this data will read as garbage",
|
||||||
|
size, offset);
|
||||||
|
}
|
||||||
|
|
||||||
size_t IndirectStorage::Read(u8* buffer, size_t size, size_t offset) const {
|
size_t IndirectStorage::Read(u8* buffer, size_t size, size_t offset) const {
|
||||||
// Validate pre-conditions.
|
// Validate pre-conditions.
|
||||||
ASSERT(this->IsInitialized());
|
ASSERT(this->IsInitialized());
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ public:
|
|||||||
void SetStorage(s32 idx, VirtualFile storage) {
|
void SetStorage(s32 idx, VirtualFile storage) {
|
||||||
ASSERT(0 <= idx && idx < StorageCount);
|
ASSERT(0 <= idx && idx < StorageCount);
|
||||||
m_data_storage[idx] = storage;
|
m_data_storage[idx] = storage;
|
||||||
|
if (idx == 0) {
|
||||||
|
m_original_missing = storage == nullptr || storage->GetSize() == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -130,6 +133,9 @@ protected:
|
|||||||
|
|
||||||
template <bool ContinuousCheck, bool RangeCheck, typename F>
|
template <bool ContinuousCheck, bool RangeCheck, typename F>
|
||||||
Result OperatePerEntry(s64 offset, s64 size, F func);
|
Result OperatePerEntry(s64 offset, s64 size, F func);
|
||||||
|
// Launching another game makes the original storage inaccessable.
|
||||||
|
// This is a helper for multi-nca games.
|
||||||
|
void ReportMissingOriginal(s64 offset, s64 size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ContinuousReadingEntry {
|
struct ContinuousReadingEntry {
|
||||||
@@ -154,6 +160,8 @@ private:
|
|||||||
private:
|
private:
|
||||||
mutable BucketTree m_table;
|
mutable BucketTree m_table;
|
||||||
std::array<VirtualFile, StorageCount> m_data_storage;
|
std::array<VirtualFile, StorageCount> m_data_storage;
|
||||||
|
bool m_original_missing{false};
|
||||||
|
bool m_reported_missing_original{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <bool ContinuousCheck, bool RangeCheck, typename F>
|
template <bool ContinuousCheck, bool RangeCheck, typename F>
|
||||||
@@ -272,6 +280,10 @@ Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) {
|
|||||||
if (needs_operate) {
|
if (needs_operate) {
|
||||||
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
|
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
|
||||||
|
|
||||||
|
if (cur_entry.storage_index == 0 && m_original_missing) {
|
||||||
|
this->ReportMissingOriginal(cur_offset, cur_size);
|
||||||
|
}
|
||||||
|
|
||||||
if constexpr (RangeCheck) {
|
if constexpr (RangeCheck) {
|
||||||
// Get the current data storage's size.
|
// Get the current data storage's size.
|
||||||
s64 cur_data_storage_size = m_data_storage[cur_entry.storage_index]->GetSize();
|
s64 cur_data_storage_size = m_data_storage[cur_entry.storage_index]->GetSize();
|
||||||
|
|||||||
+189
-236
@@ -3,10 +3,12 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <map>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <span>
|
||||||
|
#include <cctype>
|
||||||
|
#include <ankerl/unordered_dense.h>
|
||||||
|
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
@@ -22,61 +24,30 @@ enum class IPSFileType {
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{{
|
static IPSFileType IdentifyMagic(std::span<const u8> magic) {
|
||||||
{"\\a", "\a"},
|
if (magic.size() >= 5) {
|
||||||
{"\\b", "\b"},
|
if (std::memcmp(magic.data(), "PATCH", 5) == 0)
|
||||||
{"\\f", "\f"},
|
return IPSFileType::IPS;
|
||||||
{"\\n", "\n"},
|
if (std::memcmp(magic.data(), "IPS32", 5) == 0)
|
||||||
{"\\r", "\r"},
|
return IPSFileType::IPS32;
|
||||||
{"\\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;
|
return IPSFileType::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsEOF(IPSFileType type, const std::vector<u8>& data) {
|
static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
|
||||||
static constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}};
|
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|
||||||
if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) {
|
|| (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
|
||||||
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) {
|
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||||
if (in == nullptr || ips == nullptr)
|
if (in == nullptr || ips == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
|
auto in_data = in->ReadAllBytes();
|
||||||
|
auto const type = IdentifyMagic(in_data);
|
||||||
if (type == IPSFileType::Error)
|
if (type == IPSFileType::Error)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto in_data = in->ReadAllBytes();
|
|
||||||
if (in_data.size() == 0) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
||||||
u64 offset = 5; // After header
|
u64 offset = 5; // After header
|
||||||
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
|
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
|
||||||
@@ -85,12 +56,9 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 real_offset{};
|
u32 real_offset = (type == IPSFileType::IPS32)
|
||||||
if (type == IPSFileType::IPS32)
|
? ((temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3])
|
||||||
real_offset = (temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3];
|
: ((temp[0] << 16) | (temp[1] << 8) | temp[2]);
|
||||||
else
|
|
||||||
real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2];
|
|
||||||
|
|
||||||
if (real_offset > in_data.size()) {
|
if (real_offset > in_data.size()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -113,34 +81,35 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (real_offset + rle_size > in_data.size())
|
if (real_offset + rle_size > in_data.size())
|
||||||
rle_size = static_cast<u16>(in_data.size() - real_offset);
|
rle_size = u16(in_data.size() - real_offset);
|
||||||
std::memset(in_data.data() + real_offset, *data, rle_size);
|
std::memset(in_data.data() + real_offset, *data, rle_size);
|
||||||
} else { // Standard Patch
|
} else { // Standard Patch
|
||||||
auto read = data_size;
|
auto read = data_size;
|
||||||
if (real_offset + read > in_data.size())
|
if (real_offset + read > in_data.size())
|
||||||
read = static_cast<u16>(in_data.size() - real_offset);
|
read = u16(in_data.size() - real_offset);
|
||||||
if (ips->Read(in_data.data() + real_offset, read, offset) != data_size)
|
if (ips->Read(in_data.data() + real_offset, read, offset) != data_size)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
offset += data_size;
|
offset += data_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (IsEOF(type, temp)) {
|
||||||
if (!IsEOF(type, temp)) {
|
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), in->GetContainingDirectory());
|
||||||
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 {
|
struct IPSwitchCompiler::IPSwitchPatch {
|
||||||
std::string name;
|
ankerl::unordered_dense::map<u32, IPSwitchRecord> records;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
std::map<u32, std::vector<u8>> records;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) {
|
IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) {
|
||||||
Parse();
|
Parse(patch_text->ReadAllBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
IPSwitchCompiler::~IPSwitchCompiler() = default;
|
IPSwitchCompiler::~IPSwitchCompiler() = default;
|
||||||
@@ -149,201 +118,185 @@ std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
|
|||||||
return nso_build_id;
|
return nso_build_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IPSwitchCompiler::IsValid() const {
|
static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
||||||
return valid;
|
IPSwitchRecord r{};
|
||||||
}
|
for (auto it = sv.cbegin(); it != sv.cend(); ) {
|
||||||
|
if (*it == '\\' && it + 1 < sv.cend()) {
|
||||||
static bool StartsWith(std::string_view base, std::string_view check) {
|
switch (it[1]) {
|
||||||
return base.size() >= check.size() && base.substr(0, check.size()) == check;
|
case 'a': r.data[r.count] = '\a'; break;
|
||||||
}
|
case 'b': r.data[r.count] = '\b'; break;
|
||||||
|
case 'e': r.data[r.count] = '\e'; break;
|
||||||
static std::string EscapeStringSequences(std::string in) {
|
case 'f': r.data[r.count] = '\f'; break;
|
||||||
for (const auto& seq : ESCAPE_CHARACTER_MAP) {
|
case 'n': r.data[r.count] = '\n'; break;
|
||||||
for (auto index = in.find(seq.first); index != std::string::npos;
|
case 'r': r.data[r.count] = '\r'; break;
|
||||||
index = in.find(seq.first, index)) {
|
case 't': r.data[r.count] = '\t'; break;
|
||||||
in.replace(index, std::strlen(seq.first), seq.second);
|
case 'v': r.data[r.count] = '\v'; break;
|
||||||
index += std::strlen(seq.second);
|
case '?': r.data[r.count] = '\?'; break;
|
||||||
|
default: r.data[r.count] = it[1]; break;
|
||||||
|
}
|
||||||
|
++r.count;
|
||||||
|
it += 2;
|
||||||
|
} else {
|
||||||
|
++r.count;
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return r;
|
||||||
return in;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IPSwitchCompiler::ParseFlag(const std::string& line) {
|
void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
||||||
if (StartsWith(line, "@flag offset_shift ")) {
|
LOG_INFO(Loader, "IPSwitchCompiler: '{}'", patch_text->GetName());
|
||||||
// Offset Shift Flag
|
bool is_little_endian = true;
|
||||||
offset_shift = std::strtoll(line.substr(19).c_str(), nullptr, 0);
|
s64 offset_shift = 0;
|
||||||
} else if (StartsWith(line, "@little-endian")) {
|
//bool print_values = false;
|
||||||
// Set values to read as little endian
|
auto const parse_line = [&](std::string_view const line) {
|
||||||
is_little_endian = true;
|
// Keep in mind lines have trimmed spaces (at the end & start)!
|
||||||
} else if (StartsWith(line, "@big-endian")) {
|
LOG_INFO(Loader, "<{}>", line);
|
||||||
// Set values to read as big endian
|
// IPSwitch is case insensitive
|
||||||
is_little_endian = false;
|
// Yes this is how the logic goes for the main reference parsers!
|
||||||
} else if (StartsWith(line, "@flag print_values")) {
|
if (line.size() > 2 && line[0] == '@') {
|
||||||
// Force printing of applied values
|
switch (line[1]) {
|
||||||
print_values = true;
|
// 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
|
||||||
|
};
|
||||||
|
|
||||||
void IPSwitchCompiler::Parse() {
|
for (auto it = bytes.begin(); it < bytes.end(); ) {
|
||||||
const auto bytes = patch_text->ReadAllBytes();
|
auto const start = it;
|
||||||
std::stringstream s;
|
auto end = start;
|
||||||
s.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
for (; end < bytes.end() && *end != '\n' && *end != '\r'; ++end)
|
||||||
|
;
|
||||||
std::vector<std::string> lines;
|
it = end + 1; //prepare for next line
|
||||||
std::string stream_line;
|
std::string_view const sline{
|
||||||
while (std::getline(s, stream_line)) {
|
reinterpret_cast<const char*>(bytes.data() + std::distance(bytes.begin(), start)),
|
||||||
// Remove a trailing \r
|
size_t(std::distance(start, end))
|
||||||
if (!stream_line.empty() && stream_line.back() == '\r')
|
};
|
||||||
stream_line.pop_back();
|
if (sline.size() > 0) {
|
||||||
lines.push_back(std::move(stream_line));
|
auto p = sline.cbegin();
|
||||||
}
|
// skip space off line
|
||||||
|
for (; p < sline.cend() && std::isspace(*p); ++p)
|
||||||
for (std::size_t i = 0; i < lines.size(); ++i) {
|
;
|
||||||
auto line = lines[i];
|
// now make a nominal preprocessed line: remove comments
|
||||||
|
char quote = '\0';
|
||||||
// Remove midline comments
|
auto const sline_start = p;
|
||||||
std::size_t comment_index = std::string::npos;
|
for (; p < sline.cend(); ) {
|
||||||
bool within_string = false;
|
// we dont check for "//", IPS checks for '/' only...
|
||||||
for (std::size_t k = 0; k < line.size(); ++k) {
|
if ((!quote && p[0] == '/')
|
||||||
if (line[k] == '\"' && (k > 0 && line[k - 1] != '\\')) {
|
|| (!quote && p[0] == '#')) {
|
||||||
within_string = !within_string;
|
break;
|
||||||
} else if (line[k] == '\\' && (k < line.size() - 1 && line[k + 1] == '\\')) {
|
} else if (p[0] == '\"' || p[0] == '\'') {
|
||||||
comment_index = k;
|
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;
|
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 {
|
VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
|
||||||
if (in == nullptr || !valid)
|
if (in == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto in_data = in->ReadAllBytes();
|
auto in_data = in->ReadAllBytes();
|
||||||
|
|
||||||
for (const auto& patch : patches) {
|
for (const auto& patch : patches) {
|
||||||
if (!patch.enabled)
|
if (patch.enabled) {
|
||||||
continue;
|
for (const auto& record : patch.records) {
|
||||||
|
if (record.first < in_data.size()) {
|
||||||
for (const auto& record : patch.records) {
|
auto replace_size = record.second.count;
|
||||||
if (record.first >= in_data.size())
|
if (record.first + replace_size > in_data.size())
|
||||||
continue;
|
replace_size = in_data.size() - record.first;
|
||||||
auto replace_size = record.second.size();
|
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
|
||||||
if (record.first + replace_size > in_data.size())
|
} else {
|
||||||
replace_size = in_data.size() - record.first;
|
LOG_WARNING(Loader, "record offs={:x},size={:x}", record.first, record.second.data.size());
|
||||||
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
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
@@ -20,24 +23,17 @@ public:
|
|||||||
~IPSwitchCompiler();
|
~IPSwitchCompiler();
|
||||||
|
|
||||||
std::array<u8, 0x20> GetBuildID() const;
|
std::array<u8, 0x20> GetBuildID() const;
|
||||||
bool IsValid() const;
|
|
||||||
VirtualFile Apply(const VirtualFile& in) const;
|
VirtualFile Apply(const VirtualFile& in) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct IPSwitchPatch;
|
struct IPSwitchPatch;
|
||||||
|
|
||||||
void ParseFlag(const std::string& flag);
|
void ParseFlag(const std::string& flag);
|
||||||
void Parse();
|
void Parse(std::span<u8 const> bytes);
|
||||||
|
|
||||||
bool valid = false;
|
|
||||||
|
|
||||||
VirtualFile patch_text;
|
VirtualFile patch_text;
|
||||||
std::vector<IPSwitchPatch> patches;
|
std::vector<IPSwitchPatch> patches;
|
||||||
std::array<u8, 0x20> nso_build_id{};
|
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
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -48,11 +48,11 @@ struct ContentRecord {
|
|||||||
std::array<u8, 0x10> nca_id;
|
std::array<u8, 0x10> nca_id;
|
||||||
std::array<u8, 0x6> size;
|
std::array<u8, 0x6> size;
|
||||||
ContentRecordType type;
|
ContentRecordType type;
|
||||||
INSERT_PADDING_BYTES(1);
|
u8 id_offset;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(ContentRecord) == 0x38, "ContentRecord has incorrect size.");
|
static_assert(sizeof(ContentRecord) == 0x38, "ContentRecord has incorrect size.");
|
||||||
|
|
||||||
constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, {}};
|
constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, 0};
|
||||||
|
|
||||||
struct MetaRecord {
|
struct MetaRecord {
|
||||||
u64_le title_id;
|
u64_le title_id;
|
||||||
|
|||||||
@@ -345,8 +345,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
return exefs;
|
return exefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
||||||
const std::string& build_id) const {
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||||
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||||
|
|
||||||
@@ -361,16 +360,11 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
|||||||
for (const auto& file : exefs_dir->GetFiles()) {
|
for (const auto& file : exefs_dir->GetFiles()) {
|
||||||
if (file->GetExtension() == "ips") {
|
if (file->GetExtension() == "ips") {
|
||||||
auto name = file->GetName();
|
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)
|
if (nso_build_id == this_build_id)
|
||||||
out.push_back(file);
|
out.push_back(file);
|
||||||
} else if (file->GetExtension() == "pchtxt") {
|
} else if (file->GetExtension() == "pchtxt") {
|
||||||
IPSwitchCompiler compiler{file};
|
IPSwitchCompiler compiler{file};
|
||||||
if (!compiler.IsValid())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto this_build_id = Common::HexToString(compiler.GetBuildID());
|
const auto this_build_id = Common::HexToString(compiler.GetBuildID());
|
||||||
if (nso_build_id == this_build_id)
|
if (nso_build_id == this_build_id)
|
||||||
out.push_back(file);
|
out.push_back(file);
|
||||||
@@ -378,7 +372,6 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -567,20 +567,45 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<NcaID> CheckMapForContentRecord(const ankerl::unordered_dense::map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
|
static std::optional<NcaID> CheckMapForContentRecord(const ankerl::unordered_dense::map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
|
||||||
const auto cmnt_iter = map.find(title_id);
|
auto cmnt_iter = map.find(title_id);
|
||||||
|
u8 id_offset = 0;
|
||||||
|
|
||||||
if (cmnt_iter == map.cend()) {
|
if (cmnt_iter == map.cend()) {
|
||||||
return std::nullopt;
|
const auto program_index = title_id & AOC_TITLE_ID_MASK;
|
||||||
|
if (program_index == 0) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmnt_iter = map.find(title_id & ~AOC_TITLE_ID_MASK);
|
||||||
|
if (cmnt_iter == map.cend()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
id_offset = static_cast<u8>(program_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& cnmt = cmnt_iter->second;
|
const auto& cnmt = cmnt_iter->second;
|
||||||
const auto& content_records = cnmt.GetContentRecords();
|
const auto& content_records = cnmt.GetContentRecords();
|
||||||
const auto iter = std::find_if(content_records.cbegin(), content_records.cend(),
|
const auto iter = std::find_if(content_records.cbegin(), content_records.cend(),
|
||||||
[type](const ContentRecord& rec) { return rec.type == type; });
|
[type, id_offset](const ContentRecord& rec) {
|
||||||
if (iter == content_records.cend()) {
|
return rec.type == type && rec.id_offset == id_offset;
|
||||||
|
});
|
||||||
|
if (iter != content_records.cend()) {
|
||||||
|
return std::make_optional(iter->nca_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id_offset != 0) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_optional(iter->nca_id);
|
const auto fallback_iter =
|
||||||
|
std::find_if(content_records.cbegin(), content_records.cend(),
|
||||||
|
[type](const ContentRecord& rec) { return rec.type == type; });
|
||||||
|
if (fallback_iter == content_records.cend()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_optional(fallback_iter->nca_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
|
std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
#include "core/file_sys/partition_filesystem.h"
|
#include "core/file_sys/partition_filesystem.h"
|
||||||
@@ -65,10 +66,12 @@ u64 NSP::GetProgramTitleID() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto program_id = expected_program_id;
|
auto program_id = expected_program_id;
|
||||||
if (program_id == 0) {
|
if (program_id == 0 && !program_status.empty()) {
|
||||||
if (!program_status.empty()) {
|
program_id = std::min_element(program_status.cbegin(), program_status.cend(),
|
||||||
program_id = program_status.begin()->first;
|
[](const auto& lhs, const auto& rhs) {
|
||||||
}
|
return lhs.first < rhs.first;
|
||||||
|
})
|
||||||
|
->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
program_id = program_id + program_index;
|
program_id = program_id + program_index;
|
||||||
@@ -273,8 +276,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
|||||||
// If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and
|
// If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and
|
||||||
// 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular
|
// 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular
|
||||||
// update NCA.
|
// update NCA.
|
||||||
if ((next_nca->GetTitleId() & 0x7FF) != 0 &&
|
if ((next_nca->GetTitleId() & AOC_TITLE_ID_MASK) != 0) {
|
||||||
(next_nca->GetTitleId() & 0x800) == 0) {
|
|
||||||
ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] =
|
ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] =
|
||||||
std::move(next_nca);
|
std::move(next_nca);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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,
|
Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||||
KPhysicalAddress server_message_paddr, bool is_hle) {
|
KPhysicalAddress server_message_paddr, bool is_hle, bool session_closed) {
|
||||||
// Lock the session.
|
// Lock the session.
|
||||||
KScopedLightLock lk{m_lock};
|
KScopedLightLock lk{m_lock};
|
||||||
|
|
||||||
@@ -1248,7 +1248,7 @@ Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, u
|
|||||||
KEvent* event = request->GetEvent();
|
KEvent* event = request->GetEvent();
|
||||||
|
|
||||||
// Check whether we're closed.
|
// Check whether we're closed.
|
||||||
const bool closed = (client_thread == nullptr || m_parent->IsClientClosed());
|
const bool closed = (client_thread == nullptr || m_parent->IsClientClosed() || session_closed);
|
||||||
|
|
||||||
Result result = ResultSuccess;
|
Result result = ResultSuccess;
|
||||||
if (!closed) {
|
if (!closed) {
|
||||||
|
|||||||
@@ -54,14 +54,14 @@ public:
|
|||||||
|
|
||||||
Result OnRequest(KernelCore& kernel, KSessionRequest* request);
|
Result OnRequest(KernelCore& kernel, KSessionRequest* request);
|
||||||
Result SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
Result SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||||
KPhysicalAddress server_message_paddr, bool is_hle = false);
|
KPhysicalAddress server_message_paddr, bool is_hle = false, bool session_closed = false);
|
||||||
Result ReceiveRequest(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
Result ReceiveRequest(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||||
KPhysicalAddress server_message_paddr,
|
KPhysicalAddress server_message_paddr,
|
||||||
std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
|
std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
|
||||||
std::weak_ptr<Service::SessionRequestManager> manager = {});
|
std::weak_ptr<Service::SessionRequestManager> manager = {});
|
||||||
|
|
||||||
Result SendReplyHLE(KernelCore& kernel) {
|
Result SendReplyHLE(KernelCore& kernel, bool session_closed = false) {
|
||||||
R_RETURN(this->SendReply(kernel, 0, 0, 0, true));
|
R_RETURN(this->SendReply(kernel, 0, 0, 0, true, session_closed));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ReceiveRequestHLE(KernelCore& kernel, std::shared_ptr<Service::HLERequestContext>* out_context,
|
Result ReceiveRequestHLE(KernelCore& kernel, std::shared_ptr<Service::HLERequestContext>* out_context,
|
||||||
|
|||||||
@@ -349,9 +349,18 @@ struct KernelCore::Impl {
|
|||||||
object_name_global_data.emplace(kernel);
|
object_name_global_data.emplace(kernel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MakeApplicationProcess(KernelCore& kernel, KProcess* process) {
|
void SetApplicationProcess(KernelCore& kernel, KProcess* process) {
|
||||||
|
if (application_process == process)
|
||||||
|
return;
|
||||||
|
|
||||||
|
KProcess* const previous = application_process;
|
||||||
application_process = process;
|
application_process = process;
|
||||||
application_process->Open(kernel);
|
|
||||||
|
if (application_process != nullptr)
|
||||||
|
application_process->Open(kernel);
|
||||||
|
|
||||||
|
if (previous != nullptr)
|
||||||
|
previous->Close(kernel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the host thread ID for the caller.
|
/// Sets the host thread ID for the caller.
|
||||||
@@ -879,8 +888,8 @@ void KernelCore::RemoveProcess(KProcess* process) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::MakeApplicationProcess(KProcess* process) {
|
void KernelCore::SetApplicationProcess(KProcess* process) {
|
||||||
impl->MakeApplicationProcess(*this, process);
|
impl->SetApplicationProcess(*this, process);
|
||||||
}
|
}
|
||||||
|
|
||||||
KProcess* KernelCore::ApplicationProcess() {
|
KProcess* KernelCore::ApplicationProcess() {
|
||||||
@@ -891,6 +900,14 @@ const KProcess* KernelCore::ApplicationProcess() const {
|
|||||||
return impl->application_process;
|
return impl->application_process;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KScopedAutoObject<KProcess> KernelCore::GetProcessByProcessId(u64 process_id) {
|
||||||
|
std::scoped_lock lk{impl->process_list_lock};
|
||||||
|
for (auto* const process : impl->process_list)
|
||||||
|
if (process != nullptr && process->GetProcessId() == process_id)
|
||||||
|
return {*this, process};
|
||||||
|
return {*this, nullptr};
|
||||||
|
}
|
||||||
|
|
||||||
std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() {
|
std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() {
|
||||||
std::list<KScopedAutoObject<KProcess>> processes;
|
std::list<KScopedAutoObject<KProcess>> processes;
|
||||||
std::scoped_lock lk{impl->process_list_lock};
|
std::scoped_lock lk{impl->process_list_lock};
|
||||||
|
|||||||
@@ -124,8 +124,8 @@ public:
|
|||||||
void AppendNewProcess(KProcess* process);
|
void AppendNewProcess(KProcess* process);
|
||||||
void RemoveProcess(KProcess* process);
|
void RemoveProcess(KProcess* process);
|
||||||
|
|
||||||
/// Makes the given process the new application process.
|
/// Makes the given process the current application process.
|
||||||
void MakeApplicationProcess(KProcess* process);
|
void SetApplicationProcess(KProcess* process);
|
||||||
|
|
||||||
/// Retrieves a pointer to the application process.
|
/// Retrieves a pointer to the application process.
|
||||||
KProcess* ApplicationProcess();
|
KProcess* ApplicationProcess();
|
||||||
@@ -133,6 +133,9 @@ public:
|
|||||||
/// Retrieves a const pointer to the application process.
|
/// Retrieves a const pointer to the application process.
|
||||||
const KProcess* ApplicationProcess() const;
|
const KProcess* ApplicationProcess() const;
|
||||||
|
|
||||||
|
/// Retrieves the process with the given process ID, or a null object.
|
||||||
|
KScopedAutoObject<KProcess> GetProcessByProcessId(u64 process_id);
|
||||||
|
|
||||||
/// Retrieves the list of processes.
|
/// Retrieves the list of processes.
|
||||||
std::list<KScopedAutoObject<KProcess>> GetProcessList();
|
std::list<KScopedAutoObject<KProcess>> GetProcessList();
|
||||||
|
|
||||||
|
|||||||
@@ -17,56 +17,12 @@
|
|||||||
|
|
||||||
namespace Kernel::Svc {
|
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
|
/// 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) {
|
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);
|
R_SUCCEED_IF(len == 0);
|
||||||
// Only start the thread the very first time this function is called
|
std::string msg_buffer(len, 0);
|
||||||
if (!flusher_data.thread) {
|
GetCurrentMemory(system.Kernel()).ReadBlock(address, msg_buffer.data(), len);
|
||||||
flusher_data.thread.emplace([](std::stop_token stop_token) {
|
LOG_INFO(Debug_Emulated, "{}", msg_buffer);
|
||||||
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();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -10,6 +13,7 @@ namespace Service::AM {
|
|||||||
constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
|
constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
|
||||||
constexpr Result ResultNoMessages{ErrorModule::AM, 3};
|
constexpr Result ResultNoMessages{ErrorModule::AM, 3};
|
||||||
constexpr Result ResultLibraryAppletTerminated{ErrorModule::AM, 22};
|
constexpr Result ResultLibraryAppletTerminated{ErrorModule::AM, 22};
|
||||||
|
constexpr Result ResultApplicationRecordNotFound{ErrorModule::AM, 37};
|
||||||
constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
|
constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
|
||||||
constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511};
|
constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511};
|
||||||
constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
|
constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -201,6 +201,14 @@ enum class ProgramSpecifyKind : u32 {
|
|||||||
RestartProgram = 2,
|
RestartProgram = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Maufeat: Use enums for zindex instead of using random zindex numbers
|
||||||
|
enum AppletZIndex : s32 {
|
||||||
|
Background = 0,
|
||||||
|
Foreground = 1,
|
||||||
|
ForegroundVisible = 2,
|
||||||
|
Overlay = 3,
|
||||||
|
};
|
||||||
|
|
||||||
struct CommonArguments {
|
struct CommonArguments {
|
||||||
CommonArgumentVersion arguments_version;
|
CommonArgumentVersion arguments_version;
|
||||||
CommonArgumentSize size;
|
CommonArgumentSize size;
|
||||||
|
|||||||
@@ -56,22 +56,22 @@ void Applet::UpdateSuspensionStateLocked(bool force_message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Applet::SetInteractibleLocked(bool interactible) {
|
void Applet::SetInteractibleLocked(bool pad_interactible, bool touch_interactible) {
|
||||||
if (is_interactible == interactible) {
|
if (is_pad_interactible == pad_interactible && is_touch_interactible == touch_interactible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_interactible = interactible;
|
is_pad_interactible = pad_interactible;
|
||||||
|
is_touch_interactible = touch_interactible;
|
||||||
|
|
||||||
const bool exit_requested = lifecycle_manager.GetExitRequested();
|
const bool exit_requested = lifecycle_manager.GetExitRequested();
|
||||||
const bool input_enabled = interactible && !exit_requested;
|
const bool pad_enabled = pad_interactible && !exit_requested;
|
||||||
|
const bool touch_enabled = touch_interactible && !exit_requested;
|
||||||
|
|
||||||
if (applet_id == AppletId::OverlayDisplay || applet_id == AppletId::Application) {
|
LOG_DEBUG(Service_AM, "applet={} pad={} touch={} exit_requested={}",
|
||||||
LOG_DEBUG(Service_AM, "called, applet={} interactible={} exit_requested={} input_enabled={} overlay_in_foreground={}",
|
static_cast<u32>(applet_id), pad_enabled, touch_enabled, exit_requested);
|
||||||
static_cast<u32>(applet_id), interactible, exit_requested, input_enabled, overlay_in_foreground);
|
|
||||||
}
|
|
||||||
|
|
||||||
hid_registration.EnableAppletToGetInput(input_enabled);
|
hid_registration.EnableAppletToGetInput(pad_enabled, touch_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Applet::OnProcessTerminatedLocked() {
|
void Applet::OnProcessTerminatedLocked() {
|
||||||
|
|||||||
@@ -125,9 +125,11 @@ struct Applet {
|
|||||||
bool album_image_taken_notification_enabled{};
|
bool album_image_taken_notification_enabled{};
|
||||||
bool record_volume_muted{};
|
bool record_volume_muted{};
|
||||||
bool is_activity_runnable{};
|
bool is_activity_runnable{};
|
||||||
bool is_interactible{true};
|
bool is_pad_interactible{true};
|
||||||
|
bool is_touch_interactible{true};
|
||||||
bool window_visible{true};
|
bool window_visible{true};
|
||||||
bool overlay_in_foreground{false};
|
bool overlay_watching_short_home_button{false};
|
||||||
|
bool overlay_handling_touch_input{false};
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
Event overlay_event;
|
Event overlay_event;
|
||||||
@@ -148,7 +150,7 @@ struct Applet {
|
|||||||
|
|
||||||
// Process state management
|
// Process state management
|
||||||
void UpdateSuspensionStateLocked(bool force_message);
|
void UpdateSuspensionStateLocked(bool force_message);
|
||||||
void SetInteractibleLocked(bool interactible);
|
void SetInteractibleLocked(bool pad_interactible, bool touch_interactible);
|
||||||
void OnProcessTerminatedLocked();
|
void OnProcessTerminatedLocked();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/service/am/display_layer_manager.h"
|
#include "core/hle/service/am/display_layer_manager.h"
|
||||||
|
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
#include "core/hle/service/vi/application_display_service.h"
|
#include "core/hle/service/vi/application_display_service.h"
|
||||||
#include "core/hle/service/vi/container.h"
|
#include "core/hle/service/vi/container.h"
|
||||||
@@ -33,6 +34,7 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro
|
|||||||
m_system_shared_buffer_id = 0;
|
m_system_shared_buffer_id = 0;
|
||||||
m_system_shared_layer_id = 0;
|
m_system_shared_layer_id = 0;
|
||||||
m_applet_id = applet_id;
|
m_applet_id = applet_id;
|
||||||
|
m_library_applet_mode = mode;
|
||||||
m_buffer_sharing_enabled = false;
|
m_buffer_sharing_enabled = false;
|
||||||
m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
|
m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
|
||||||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
|
mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
|
||||||
@@ -72,14 +74,16 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
|||||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||||
|
|
||||||
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
||||||
|
(void)m_display_service->GetContainer()->SetLayerStackMask(*out_layer_id,
|
||||||
|
this->GetLayerStackMask());
|
||||||
|
|
||||||
if (m_applet_id != AppletId::Application) {
|
if (m_applet_id != AppletId::Application) {
|
||||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||||
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
|
(void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id);
|
||||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
||||||
} else {
|
} else {
|
||||||
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
|
(void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,10 +126,12 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
|||||||
|
|
||||||
// Ensure the overlay layer is visible
|
// Ensure the overlay layer is visible
|
||||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||||
|
(void)m_display_service->GetContainer()->SetLayerStackMask(m_system_shared_layer_id,
|
||||||
|
this->GetLayerStackMask());
|
||||||
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||||
s32 initial_z = 1;
|
s32 initial_z = Foreground;
|
||||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||||
initial_z = -1;
|
initial_z = Overlay;
|
||||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
||||||
}
|
}
|
||||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||||
@@ -142,6 +148,36 @@ Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_bu
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 DisplayLayerManager::GetLayerStackMask() const {
|
||||||
|
using Nvnflinger::LayerStackBit;
|
||||||
|
using Nvnflinger::LayerStackId;
|
||||||
|
|
||||||
|
constexpr u32 Displayed = LayerStackBit(LayerStackId::Default);
|
||||||
|
constexpr u32 Screenshot = LayerStackBit(LayerStackId::Screenshot);
|
||||||
|
constexpr u32 Recording = LayerStackBit(LayerStackId::Recording);
|
||||||
|
constexpr u32 LastFrame = LayerStackBit(LayerStackId::LastFrame);
|
||||||
|
constexpr u32 Debug = LayerStackBit(LayerStackId::ApplicationForDebug);
|
||||||
|
|
||||||
|
switch (m_applet_id) {
|
||||||
|
case AppletId::Application:
|
||||||
|
return Displayed | Screenshot | Recording | LastFrame | Debug;
|
||||||
|
case AppletId::OverlayDisplay:
|
||||||
|
return Displayed;
|
||||||
|
case AppletId::QLaunch:
|
||||||
|
return Displayed;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_library_applet_mode) {
|
||||||
|
case LibraryAppletMode::AllForeground:
|
||||||
|
case LibraryAppletMode::AllForegroundInitiallyHidden:
|
||||||
|
return Displayed | Screenshot | LastFrame;
|
||||||
|
default:
|
||||||
|
return Displayed | Screenshot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DisplayLayerManager::SetWindowVisibility(bool visible) {
|
void DisplayLayerManager::SetWindowVisibility(bool visible) {
|
||||||
if (m_visible == visible) {
|
if (m_visible == visible) {
|
||||||
return;
|
return;
|
||||||
@@ -185,10 +221,17 @@ void DisplayLayerManager::SetOverlayZIndex(s32 z_index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
||||||
s32* out_fbshare_layer_index) {
|
s32* out_fbshare_layer_index,
|
||||||
|
VI::CaptureKind kind) {
|
||||||
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||||
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
|
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
|
||||||
out_was_written, out_fbshare_layer_index));
|
out_was_written, out_fbshare_layer_index, kind));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DisplayLayerManager::ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color) {
|
||||||
|
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||||
|
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->ClearAppletCaptureBuffer(
|
||||||
|
fbshare_layer_index, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -23,6 +23,7 @@ class KProcess;
|
|||||||
namespace Service::VI {
|
namespace Service::VI {
|
||||||
class IApplicationDisplayService;
|
class IApplicationDisplayService;
|
||||||
class IManagerDisplayService;
|
class IManagerDisplayService;
|
||||||
|
enum class CaptureKind : u32;
|
||||||
} // namespace Service::VI
|
} // namespace Service::VI
|
||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
@@ -48,9 +49,13 @@ public:
|
|||||||
|
|
||||||
void SetOverlayZIndex(s32 z_index);
|
void SetOverlayZIndex(s32 z_index);
|
||||||
|
|
||||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
|
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index,
|
||||||
|
VI::CaptureKind kind);
|
||||||
|
Result ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
u32 GetLayerStackMask() const;
|
||||||
|
|
||||||
Kernel::KProcess* m_process{};
|
Kernel::KProcess* m_process{};
|
||||||
std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
|
std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
|
||||||
std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
|
std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
|
||||||
@@ -59,6 +64,7 @@ private:
|
|||||||
u64 m_system_shared_buffer_id{};
|
u64 m_system_shared_buffer_id{};
|
||||||
u64 m_system_shared_layer_id{};
|
u64 m_system_shared_layer_id{};
|
||||||
AppletId m_applet_id{};
|
AppletId m_applet_id{};
|
||||||
|
LibraryAppletMode m_library_applet_mode{};
|
||||||
bool m_buffer_sharing_enabled{};
|
bool m_buffer_sharing_enabled{};
|
||||||
bool m_blending_enabled{};
|
bool m_blending_enabled{};
|
||||||
bool m_visible{true};
|
bool m_visible{true};
|
||||||
|
|||||||
@@ -36,12 +36,17 @@ HidRegistration::~HidRegistration() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HidRegistration::EnableAppletToGetInput(bool enable) {
|
void HidRegistration::EnableAppletToGetInput(bool enable_pad, bool enable_touch) {
|
||||||
if (m_process.IsInitialized()) {
|
if (!m_process.IsInitialized())
|
||||||
m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(),
|
return;
|
||||||
enable);
|
|
||||||
m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable);
|
const auto resource_manager = m_hid_server->GetResourceManager();
|
||||||
}
|
const u64 aruid = m_process.GetProcessId();
|
||||||
|
|
||||||
|
resource_manager->EnablePadInput(aruid, enable_pad);
|
||||||
|
resource_manager->EnableTouchScreen(aruid, enable_touch);
|
||||||
|
|
||||||
|
resource_manager->SetAruidValidForVibration(aruid, enable_pad);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public:
|
|||||||
~HidRegistration();
|
~HidRegistration();
|
||||||
|
|
||||||
void RegisterCurrentProcess();
|
void RegisterCurrentProcess();
|
||||||
void EnableAppletToGetInput(bool enable);
|
void EnableAppletToGetInput(bool enable_pad, bool enable_touch);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Process& m_process;
|
Process& m_process;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/hle/service/am/applet.h"
|
#include "core/hle/service/am/applet.h"
|
||||||
#include "core/hle/service/am/service/applet_common_functions.h"
|
#include "core/hle/service/am/service/applet_common_functions.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
@@ -78,7 +80,7 @@ Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) {
|
|||||||
|
|
||||||
Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_id) {
|
Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_id) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
*out_application_id = system.GetApplicationProcessProgramID() & ~0xFFFULL;
|
*out_application_id = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
#include "common/hex_util.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/uuid.h"
|
#include "common/uuid.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/control_metadata.h"
|
#include "core/file_sys/control_metadata.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
@@ -28,6 +30,27 @@
|
|||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
FileSys::PatchManager::Metadata GetApplicationMetadata(Core::System& system, u64 program_id) {
|
||||||
|
auto metadata = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, program_id);
|
||||||
|
if (metadata.first != nullptr) {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto application_id = FileSys::GetBaseTitleID(program_id);
|
||||||
|
if (application_id == program_id) {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_AM,
|
||||||
|
"no metadata for program_id={:016X}, falling back to application_id={:016X}",
|
||||||
|
program_id, application_id);
|
||||||
|
return FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, application_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet)
|
IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet)
|
||||||
: ServiceFramework{system_, "IApplicationFunctions"}, m_applet{std::move(applet)} {
|
: ServiceFramework{system_, "IApplicationFunctions"}, m_applet{std::move(applet)} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@@ -156,7 +179,7 @@ Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
|
|||||||
// Default to 0 (all languages supported)
|
// Default to 0 (all languages supported)
|
||||||
u32 supported_languages = 0;
|
u32 supported_languages = 0;
|
||||||
|
|
||||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
const auto res = GetApplicationMetadata(system, m_applet->program_id);
|
||||||
if (res.first != nullptr) {
|
if (res.first != nullptr) {
|
||||||
supported_languages = res.first->GetSupportedLanguages();
|
supported_languages = res.first->GetSupportedLanguages();
|
||||||
}
|
}
|
||||||
@@ -194,7 +217,7 @@ Result IApplicationFunctions::SetTerminateResult(Result terminate_result) {
|
|||||||
Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_version) {
|
Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_version) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
const auto res = GetApplicationMetadata(system, m_applet->program_id);
|
||||||
if (res.first != nullptr) {
|
if (res.first != nullptr) {
|
||||||
const auto& version = res.first->GetVersionString();
|
const auto& version = res.first->GetVersionString();
|
||||||
std::memcpy(out_display_version->string.data(), version.data(),
|
std::memcpy(out_display_version->string.data(), version.data(),
|
||||||
@@ -324,6 +347,7 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
|
|||||||
|
|
||||||
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
||||||
LOG_WARNING(Service_AM, "(stubbed)");
|
LOG_WARNING(Service_AM, "(stubbed)");
|
||||||
|
R_UNLESS(out_pseudo_device_id, ResultUnknown);
|
||||||
|
|
||||||
// This should be hashed with the device specific hash
|
// This should be hashed with the device specific hash
|
||||||
// for now this will do
|
// for now this will do
|
||||||
@@ -430,15 +454,21 @@ Result IApplicationFunctions::ExecuteProgram(ProgramSpecifyKind kind, u64 value)
|
|||||||
|
|
||||||
// https://switchbrew.org/wiki/Applet_Manager_services#CreateApplicationAndRequestToStart
|
// https://switchbrew.org/wiki/Applet_Manager_services#CreateApplicationAndRequestToStart
|
||||||
Result IApplicationFunctions::CreateApplicationAndRequestToStart(u64 application_id) {
|
Result IApplicationFunctions::CreateApplicationAndRequestToStart(u64 application_id) {
|
||||||
LOG_INFO(Service_AM, "called, application_id={:016X}", application_id);
|
LOG_INFO(Service_AM, "called, application_id={:016X} current_program_id={:016X}", application_id, m_applet->program_id);
|
||||||
|
|
||||||
// If application_id is 0, relaunch the current application
|
if (application_id == 0 ||
|
||||||
const u64 target_application_id =
|
FileSys::GetBaseTitleID(application_id) == FileSys::GetBaseTitleID(m_applet->program_id)) {
|
||||||
(application_id == 0) ? m_applet->program_id : application_id;
|
const auto program_index = application_id == 0
|
||||||
|
? 0
|
||||||
|
: application_id - FileSys::GetBaseTitleID(application_id);
|
||||||
|
|
||||||
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
|
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
|
||||||
system.ExecuteProgram(target_application_id);
|
system.ExecuteProgram(program_index);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERROR(Service_AM, "Launching a different application ({:016X}) is not implemented!", application_id);
|
||||||
|
R_THROW(ResultUnknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IApplicationFunctions::ClearUserChannel() {
|
Result IApplicationFunctions::ClearUserChannel() {
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "core/hle/service/am/applet.h"
|
#include "core/hle/service/am/applet.h"
|
||||||
#include "core/hle/service/am/service/display_controller.h"
|
#include "core/hle/service/am/service/display_controller.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
|
#include "core/hle/service/vi/shared_buffer_manager.h"
|
||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
|
|
||||||
@@ -71,16 +72,16 @@ Result IDisplayController::TakeScreenShotOfOwnLayer(bool unknown0, s32 fbshare_l
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color) {
|
Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called, unknown0={} fbshare_layer_index={} color={:#x}",
|
LOG_DEBUG(Service_AM, "called, unknown0={} fbshare_layer_index={} color={:#x}", unknown0,
|
||||||
unknown0, fbshare_layer_index, color);
|
fbshare_layer_index, color);
|
||||||
R_SUCCEED();
|
R_RETURN(applet->display_layer_manager.ClearAppletCaptureBuffer(fbshare_layer_index, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer(
|
Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer(
|
||||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||||
out_fbshare_layer_index));
|
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastForeground));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
||||||
@@ -90,9 +91,9 @@ Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
|||||||
|
|
||||||
Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer(
|
Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer(
|
||||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||||
out_fbshare_layer_index));
|
out_was_written, out_fbshare_layer_index, VI::CaptureKind::CallerApplet));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
||||||
@@ -102,9 +103,9 @@ Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
|||||||
|
|
||||||
Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer(
|
Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer(
|
||||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||||
out_fbshare_layer_index));
|
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastApplication));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IDisplayController::ReleaseLastApplicationCaptureSharedBuffer() {
|
Result IDisplayController::ReleaseLastApplicationCaptureSharedBuffer() {
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "core/hle/service/am/applet.h"
|
#include "core/hle/service/am/applet.h"
|
||||||
@@ -22,7 +22,7 @@ namespace Service::AM {
|
|||||||
{10, nullptr, "StartShutdownSequenceForOverlay"},
|
{10, nullptr, "StartShutdownSequenceForOverlay"},
|
||||||
{11, nullptr, "StartRebootSequenceForOverlay"},
|
{11, nullptr, "StartRebootSequenceForOverlay"},
|
||||||
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||||
{21, nullptr, "SetHandlingTouchScreenInputEnabled"},
|
{21, D<&IOverlayFunctions::SetHandlingTouchScreenInputEnabled>, "SetHandlingTouchScreenInputEnabled"},
|
||||||
{30, nullptr, "SetHealthWarningShowingState"},
|
{30, nullptr, "SetHealthWarningShowingState"},
|
||||||
{31, D<&IOverlayFunctions::IsHealthWarningRequired>, "IsHealthWarningRequired"},
|
{31, D<&IOverlayFunctions::IsHealthWarningRequired>, "IsHealthWarningRequired"},
|
||||||
{40, nullptr, "GetApplicationNintendoLogo"},
|
{40, nullptr, "GetApplicationNintendoLogo"},
|
||||||
@@ -43,10 +43,12 @@ namespace Service::AM {
|
|||||||
Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() {
|
Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
m_applet->overlay_in_foreground = true;
|
{
|
||||||
m_applet->home_button_short_pressed_blocked = false;
|
std::scoped_lock lk{m_applet->lock};
|
||||||
|
m_applet->overlay_watching_short_home_button = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||||
window_system->RequestUpdate();
|
window_system->RequestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,16 +58,26 @@ namespace Service::AM {
|
|||||||
Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() {
|
Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
m_applet->overlay_in_foreground = false;
|
{
|
||||||
m_applet->home_button_short_pressed_blocked = false;
|
std::scoped_lock lk{m_applet->lock};
|
||||||
|
m_applet->overlay_watching_short_home_button = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||||
window_system->RequestUpdate();
|
window_system->RequestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IOverlayFunctions::SetHandlingTouchScreenInputEnabled(bool enabled) {
|
||||||
|
LOG_DEBUG(Service_AM, "called, enabled={}", enabled);
|
||||||
|
|
||||||
|
std::scoped_lock lk{m_applet->lock};
|
||||||
|
m_applet->overlay_handling_touch_input = enabled;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) {
|
Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -20,6 +20,7 @@ namespace Service::AM {
|
|||||||
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
|
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
|
||||||
Result IsHealthWarningRequired(Out<bool> is_required);
|
Result IsHealthWarningRequired(Out<bool> is_required);
|
||||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||||
|
Result SetHandlingTouchScreenInputEnabled(bool enabled);
|
||||||
Result Unknown70();
|
Result Unknown70();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -4,7 +4,11 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/service/am/am_results.h"
|
#include "core/hle/service/am/am_results.h"
|
||||||
#include "core/hle/service/am/applet.h"
|
#include "core/hle/service/am/applet.h"
|
||||||
#include "core/hle/service/am/applet_manager.h"
|
#include "core/hle/service/am/applet_manager.h"
|
||||||
@@ -32,46 +36,82 @@ void WindowSystem::RequestUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WindowSystem::Update() {
|
void WindowSystem::Update() {
|
||||||
std::scoped_lock lk{m_lock};
|
{
|
||||||
|
std::scoped_lock lk{m_lock};
|
||||||
|
|
||||||
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
||||||
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
||||||
|
|
||||||
// Loop through all applets and remove terminated applets.
|
// Loop through all applets and remove terminated applets.
|
||||||
this->PruneTerminatedAppletsLocked();
|
this->PruneTerminatedAppletsLocked();
|
||||||
|
|
||||||
// If the home menu is being locked into the foreground, handle that.
|
// If the home menu is being locked into the foreground, handle that.
|
||||||
if (this->LockHomeMenuIntoForegroundLocked()) {
|
if (!this->LockHomeMenuIntoForegroundLocked()) {
|
||||||
return;
|
const bool overlay_takes_input = this->DoesOverlayTakeInputLocked();
|
||||||
|
|
||||||
|
this->UpdateAppletStateLocked(m_overlay_display, true, overlay_takes_input);
|
||||||
|
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_takes_input);
|
||||||
|
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_takes_input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool overlay_blocks_input = false;
|
this->NotifyApplicationChangedIfNeeded();
|
||||||
if (m_overlay_display) {
|
|
||||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
|
||||||
overlay_blocks_input = m_overlay_display->overlay_in_foreground;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively update each applet root.
|
|
||||||
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_blocks_input);
|
|
||||||
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_blocks_input);
|
|
||||||
this->UpdateAppletStateLocked(m_overlay_display, true, false); // overlay is always updated, never blocked
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_application) {
|
void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_application) {
|
||||||
std::scoped_lock lk{m_lock};
|
{
|
||||||
|
std::scoped_lock lk{m_lock};
|
||||||
|
|
||||||
if (applet->applet_id == AppletId::QLaunch) {
|
if (applet->applet_id == AppletId::QLaunch) {
|
||||||
ASSERT(m_home_menu == nullptr);
|
ASSERT(m_home_menu == nullptr);
|
||||||
m_home_menu = applet.get();
|
m_home_menu = applet.get();
|
||||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||||
m_overlay_display = applet.get();
|
m_overlay_display = applet.get();
|
||||||
} else if (is_application) {
|
} else if (is_application) {
|
||||||
ASSERT(m_application == nullptr);
|
ASSERT(m_application == nullptr);
|
||||||
m_application = applet.get();
|
m_application = applet.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->UpdateCurrentApplicationLocked();
|
||||||
|
|
||||||
|
m_event_observer->TrackAppletProcess(*applet);
|
||||||
|
m_applets.emplace(applet->aruid.pid, std::move(applet));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_event_observer->TrackAppletProcess(*applet);
|
this->NotifyApplicationChangedIfNeeded();
|
||||||
m_applets.emplace(applet->aruid.pid, std::move(applet));
|
}
|
||||||
|
|
||||||
|
void WindowSystem::UpdateCurrentApplicationLocked() {
|
||||||
|
const Applet* const candidate = m_application != nullptr ? m_application : m_home_menu;
|
||||||
|
if (candidate == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* const process = candidate->process->GetHandle();
|
||||||
|
if (process == nullptr || process == m_system.Kernel().ApplicationProcess()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO(Service_AM, "Current application is now {:016X}", candidate->program_id);
|
||||||
|
|
||||||
|
m_system.Kernel().SetApplicationProcess(process);
|
||||||
|
Settings::SetCurrentProgramID(candidate->program_id);
|
||||||
|
|
||||||
|
m_pending_application_notification = candidate->program_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowSystem::NotifyApplicationChangedIfNeeded() {
|
||||||
|
std::optional<u64> program_id;
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{m_lock};
|
||||||
|
program_id = std::exchange(m_pending_application_notification, std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!program_id.has_value()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_system.NotifyApplicationChanged(*program_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Applet> WindowSystem::GetByAppletResourceUserId(u64 aruid) {
|
std::shared_ptr<Applet> WindowSystem::GetByAppletResourceUserId(u64 aruid) {
|
||||||
@@ -169,18 +209,40 @@ void WindowSystem::OnExitRequested() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) {
|
void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) {
|
||||||
if (m_home_menu) {
|
const auto is_blocked = [message](const Applet& applet) {
|
||||||
std::scoped_lock lk_home{m_home_menu->lock};
|
if (message == AppletMessage::DetectShortPressingHomeButton &&
|
||||||
m_home_menu->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
applet.applet_id == AppletId::OverlayDisplay &&
|
||||||
}
|
!applet.overlay_watching_short_home_button) {
|
||||||
if (m_overlay_display) {
|
return true;
|
||||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
}
|
||||||
m_overlay_display->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
|
||||||
}
|
switch (message) {
|
||||||
if (m_application) {
|
case AppletMessage::DetectShortPressingHomeButton:
|
||||||
std::scoped_lock lk_application{m_application->lock};
|
return applet.home_button_short_pressed_blocked;
|
||||||
m_application->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
case AppletMessage::DetectLongPressingHomeButton:
|
||||||
}
|
return applet.home_button_long_pressed_blocked;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto send_to = [&](Applet* applet) {
|
||||||
|
if (!applet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::scoped_lock lk{applet->lock};
|
||||||
|
if (is_blocked(*applet)) {
|
||||||
|
LOG_DEBUG(Service_AM, "Applet {} is blocking message {}",
|
||||||
|
static_cast<u32>(applet->applet_id), static_cast<u32>(message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
applet->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||||
|
};
|
||||||
|
|
||||||
|
send_to(m_home_menu);
|
||||||
|
send_to(m_overlay_display);
|
||||||
|
send_to(m_application);
|
||||||
|
|
||||||
if (m_event_observer) {
|
if (m_event_observer) {
|
||||||
m_event_observer->RequestUpdate();
|
m_event_observer->RequestUpdate();
|
||||||
}
|
}
|
||||||
@@ -192,19 +254,9 @@ void WindowSystem::OnSystemButtonPress(SystemButtonType type) {
|
|||||||
case SystemButtonType::HomeButtonShortPressing:
|
case SystemButtonType::HomeButtonShortPressing:
|
||||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton);
|
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton);
|
||||||
break;
|
break;
|
||||||
case SystemButtonType::HomeButtonLongPressing: {
|
case SystemButtonType::HomeButtonLongPressing:
|
||||||
// Toggle overlay foreground visibility on long home press
|
|
||||||
if (m_overlay_display) {
|
|
||||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
|
||||||
m_overlay_display->overlay_in_foreground = !m_overlay_display->overlay_in_foreground;
|
|
||||||
LOG_INFO(Service_AM, "Overlay long-press toggle: overlay_in_foreground={} window_visible={}", m_overlay_display->overlay_in_foreground, m_overlay_display->window_visible);
|
|
||||||
}
|
|
||||||
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton);
|
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton);
|
||||||
// Force a state update after toggling overlay
|
break;
|
||||||
if (m_event_observer) {
|
|
||||||
m_event_observer->RequestUpdate();
|
|
||||||
}
|
|
||||||
break; }
|
|
||||||
case SystemButtonType::CaptureButtonShortPressing:
|
case SystemButtonType::CaptureButtonShortPressing:
|
||||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton);
|
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton);
|
||||||
break;
|
break;
|
||||||
@@ -317,6 +369,8 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
|||||||
m_overlay_display = nullptr;
|
m_overlay_display = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->UpdateCurrentApplicationLocked();
|
||||||
|
|
||||||
// Finalize applet.
|
// Finalize applet.
|
||||||
applet->OnProcessTerminatedLocked();
|
applet->OnProcessTerminatedLocked();
|
||||||
|
|
||||||
@@ -389,7 +443,20 @@ void WindowSystem::TerminateChildAppletsLocked(Applet* applet) {
|
|||||||
applet->lock.lock();
|
applet->lock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking) {
|
bool WindowSystem::IsOverlayOpenLocked(const Applet& overlay) const {
|
||||||
|
return overlay.window_visible && overlay.overlay_watching_short_home_button;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowSystem::DoesOverlayTakeInputLocked() const {
|
||||||
|
if (m_overlay_display == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::scoped_lock lk{m_overlay_display->lock};
|
||||||
|
return this->IsOverlayOpenLocked(*m_overlay_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input) {
|
||||||
// With no applet, we don't have anything to do.
|
// With no applet, we don't have anything to do.
|
||||||
if (!applet) {
|
if (!applet) {
|
||||||
return;
|
return;
|
||||||
@@ -420,24 +487,18 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
|||||||
return false;
|
return false;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
const bool is_overlay = applet->applet_id == AppletId::OverlayDisplay;
|
||||||
|
|
||||||
// Update visibility state.
|
// Update visibility state.
|
||||||
// Overlay applets should always be visible when window_visible is true, regardless of foreground state
|
const bool should_be_visible =
|
||||||
const bool should_be_visible = (applet->applet_id == AppletId::OverlayDisplay)
|
is_overlay ? applet->window_visible : (is_foreground && applet->window_visible);
|
||||||
? applet->window_visible
|
|
||||||
: (is_foreground && applet->window_visible);
|
|
||||||
applet->display_layer_manager.SetWindowVisibility(should_be_visible);
|
applet->display_layer_manager.SetWindowVisibility(should_be_visible);
|
||||||
|
|
||||||
|
const bool needs_hid_input =
|
||||||
|
is_overlay ? this->IsOverlayOpenLocked(*applet)
|
||||||
|
: (is_foreground && applet->window_visible && !overlay_takes_input);
|
||||||
|
|
||||||
const bool should_be_interactible = (applet->applet_id == AppletId::OverlayDisplay)
|
applet->SetInteractibleLocked(needs_hid_input, needs_hid_input);
|
||||||
? applet->overlay_in_foreground
|
|
||||||
: (is_foreground && applet->window_visible && !overlay_blocking);
|
|
||||||
|
|
||||||
if (applet->applet_id == AppletId::OverlayDisplay || applet->applet_id == AppletId::Application) {
|
|
||||||
LOG_DEBUG(Service_AM, "UpdateAppletStateLocked: applet={} overlay_in_foreground={} is_foreground={} window_visible={} overlay_blocking={} should_be_interactible={}",
|
|
||||||
static_cast<u32>(applet->applet_id), applet->overlay_in_foreground, is_foreground, applet->window_visible, overlay_blocking, should_be_interactible);
|
|
||||||
}
|
|
||||||
|
|
||||||
applet->SetInteractibleLocked(should_be_interactible);
|
|
||||||
|
|
||||||
// Update focus state and suspension.
|
// Update focus state and suspension.
|
||||||
const bool is_obscured = has_obscuring_child_applets || !applet->window_visible;
|
const bool is_obscured = has_obscuring_child_applets || !applet->window_visible;
|
||||||
@@ -453,23 +514,18 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
|||||||
applet->UpdateSuspensionStateLocked(true);
|
applet->UpdateSuspensionStateLocked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Z-index logic like in reference C# implementation (tuned for overlay extremes)
|
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
||||||
s32 z_index = 0;
|
s32 z_index = Background;
|
||||||
const bool now_foreground = inherited_foreground;
|
if (is_overlay) {
|
||||||
if (applet->applet_id == AppletId::OverlayDisplay) {
|
z_index = Overlay;
|
||||||
z_index = applet->overlay_in_foreground ? 100000 : -1;
|
} else if (inherited_foreground) {
|
||||||
} else if (now_foreground && !is_obscured) {
|
z_index = is_obscured ? Foreground : ForegroundVisible;
|
||||||
z_index = 2;
|
|
||||||
} else if (now_foreground) {
|
|
||||||
z_index = 1;
|
|
||||||
} else {
|
|
||||||
z_index = 0;
|
|
||||||
}
|
}
|
||||||
applet->display_layer_manager.SetOverlayZIndex(z_index);
|
applet->display_layer_manager.SetOverlayZIndex(z_index);
|
||||||
|
|
||||||
// Recurse into child applets.
|
// Recurse into child applets.
|
||||||
for (const auto& child_applet : applet->child_applets) {
|
for (const auto& child_applet : applet->child_applets) {
|
||||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_blocking);
|
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_takes_input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/service/am/am_types.h"
|
#include "core/hle/service/am/am_types.h"
|
||||||
@@ -60,11 +61,16 @@ public:
|
|||||||
void OnPowerButtonPressed(ButtonPressDuration type) {}
|
void OnPowerButtonPressed(ButtonPressDuration type) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void UpdateCurrentApplicationLocked();
|
||||||
|
void NotifyApplicationChangedIfNeeded();
|
||||||
void PruneTerminatedAppletsLocked();
|
void PruneTerminatedAppletsLocked();
|
||||||
bool RestartAppletProcessLocked(Applet* applet);
|
bool RestartAppletProcessLocked(Applet* applet);
|
||||||
bool LockHomeMenuIntoForegroundLocked();
|
bool LockHomeMenuIntoForegroundLocked();
|
||||||
void TerminateChildAppletsLocked(Applet* applet);
|
void TerminateChildAppletsLocked(Applet* applet);
|
||||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
|
bool IsOverlayOpenLocked(const Applet& overlay) const;
|
||||||
|
bool DoesOverlayTakeInputLocked() const;
|
||||||
|
|
||||||
|
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input);
|
||||||
void SendButtonAppletMessageLocked(AppletMessage message);
|
void SendButtonAppletMessageLocked(AppletMessage message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -88,6 +94,7 @@ private:
|
|||||||
|
|
||||||
// Applet map by aruid.
|
// Applet map by aruid.
|
||||||
std::map<u64, std::shared_ptr<Applet>> m_applets{};
|
std::map<u64, std::shared_ptr<Applet>> m_applets{};
|
||||||
|
std::optional<u64> m_pending_application_notification{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/server_manager.h"
|
#include "core/hle/service/server_manager.h"
|
||||||
|
#include "core/hle/service/am/am_results.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
namespace Service::AOC {
|
namespace Service::AOC {
|
||||||
@@ -31,6 +32,10 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
|
|||||||
return FileSys::GetBaseTitleID(title_id) == base;
|
return FileSys::GetBaseTitleID(title_id) == base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u64 GetCallerBaseTitleID(Core::System& system, const ClientProcessId& process_id) {
|
||||||
|
return FileSys::GetBaseTitleID(system.ResolveCallerProgramId(*process_id));
|
||||||
|
}
|
||||||
|
|
||||||
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
||||||
std::vector<u64> add_on_content;
|
std::vector<u64> add_on_content;
|
||||||
const auto& rcu = system.GetContentProvider();
|
const auto& rcu = system.GetContentProvider();
|
||||||
@@ -91,7 +96,7 @@ IAddOnContentManager::~IAddOnContentManager() {
|
|||||||
Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcessId process_id) {
|
Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcessId process_id) {
|
||||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||||
|
|
||||||
const auto current = system.GetApplicationProcessProgramID();
|
const auto current = GetCallerBaseTitleID(system, process_id);
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[current];
|
const auto& disabled = Settings::values.disabled_addons[current];
|
||||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||||
@@ -112,7 +117,7 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
|||||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||||
process_id.pid);
|
process_id.pid);
|
||||||
|
|
||||||
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
const auto current = GetCallerBaseTitleID(system, process_id);
|
||||||
|
|
||||||
std::vector<u32> out;
|
std::vector<u32> out;
|
||||||
const auto& disabled = Settings::values.disabled_addons[current];
|
const auto& disabled = Settings::values.disabled_addons[current];
|
||||||
@@ -126,8 +131,7 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(DarkLordZach): Find the correct error code.
|
R_UNLESS(out.size() >= offset, AM::ResultApplicationRecordNotFound);
|
||||||
R_UNLESS(out.size() >= offset, ResultUnknown);
|
|
||||||
|
|
||||||
*out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
*out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||||
@@ -141,7 +145,7 @@ Result IAddOnContentManager::GetAddOnContentBaseId(Out<u64> out_title_id,
|
|||||||
ClientProcessId process_id) {
|
ClientProcessId process_id) {
|
||||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||||
|
|
||||||
const auto title_id = system.GetApplicationProcessProgramID();
|
const auto title_id = system.ResolveCallerProgramId(*process_id);
|
||||||
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||||
system.GetContentProvider()};
|
system.GetContentProvider()};
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ static u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_)
|
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_)
|
||||||
: ServiceFramework{system_, "IBcatService"}, backend{backend_},
|
: ServiceFramework{system_, "IBcatService"}, backend{backend_}, program_id{program_id_},
|
||||||
progress{{
|
progress{{
|
||||||
ProgressServiceBackend{system_, "Normal"},
|
ProgressServiceBackend{system_, "Normal"},
|
||||||
ProgressServiceBackend{system_, "Directory"},
|
ProgressServiceBackend{system_, "Directory"},
|
||||||
@@ -70,8 +70,7 @@ Result IBcatService::RequestSyncDeliveryCache(
|
|||||||
LOG_DEBUG(Service_BCAT, "called");
|
LOG_DEBUG(Service_BCAT, "called");
|
||||||
|
|
||||||
auto& progress_backend{GetProgressBackend(SyncType::Normal)};
|
auto& progress_backend{GetProgressBackend(SyncType::Normal)};
|
||||||
backend.Synchronize(system.Kernel(), {system.GetApplicationProcessProgramID(),
|
backend.Synchronize(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||||
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
|
||||||
GetProgressBackend(SyncType::Normal));
|
GetProgressBackend(SyncType::Normal));
|
||||||
|
|
||||||
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
||||||
@@ -86,9 +85,8 @@ Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName(
|
|||||||
LOG_DEBUG(Service_BCAT, "called, name={}", name);
|
LOG_DEBUG(Service_BCAT, "called, name={}", name);
|
||||||
|
|
||||||
auto& progress_backend{GetProgressBackend(SyncType::Directory)};
|
auto& progress_backend{GetProgressBackend(SyncType::Directory)};
|
||||||
backend.SynchronizeDirectory(system.Kernel(), {system.GetApplicationProcessProgramID(),
|
backend.SynchronizeDirectory(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||||
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
name, progress_backend);
|
||||||
name, progress_backend);
|
|
||||||
|
|
||||||
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
||||||
system, progress_backend.GetEvent(), progress_backend.GetImpl());
|
system, progress_backend.GetEvent(), progress_backend.GetImpl());
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
@@ -19,7 +22,7 @@ class IDeliveryCacheProgressService;
|
|||||||
|
|
||||||
class IBcatService final : public ServiceFramework<IBcatService> {
|
class IBcatService final : public ServiceFramework<IBcatService> {
|
||||||
public:
|
public:
|
||||||
explicit IBcatService(Core::System& system_, BcatBackend& backend_);
|
explicit IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_);
|
||||||
~IBcatService() override;
|
~IBcatService() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -39,6 +42,7 @@ private:
|
|||||||
const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
|
const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
|
||||||
|
|
||||||
BcatBackend& backend;
|
BcatBackend& backend;
|
||||||
|
u64 program_id;
|
||||||
std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
|
std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/hle/service/bcat/bcat_service.h"
|
#include "core/hle/service/bcat/bcat_service.h"
|
||||||
#include "core/hle/service/bcat/delivery_cache_storage_service.h"
|
#include "core/hle/service/bcat/delivery_cache_storage_service.h"
|
||||||
#include "core/hle/service/bcat/service_creator.h"
|
#include "core/hle/service/bcat/service_creator.h"
|
||||||
@@ -37,7 +41,8 @@ IServiceCreator::~IServiceCreator() = default;
|
|||||||
Result IServiceCreator::CreateBcatService(ClientProcessId process_id,
|
Result IServiceCreator::CreateBcatService(ClientProcessId process_id,
|
||||||
OutInterface<IBcatService> out_interface) {
|
OutInterface<IBcatService> out_interface) {
|
||||||
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
||||||
*out_interface = std::make_shared<IBcatService>(system, *backend);
|
*out_interface =
|
||||||
|
std::make_shared<IBcatService>(system, *backend, system.ResolveCallerProgramId(*process_id));
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +50,7 @@ Result IServiceCreator::CreateDeliveryCacheStorageService(
|
|||||||
ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
|
ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
|
||||||
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
||||||
|
|
||||||
const auto title_id = system.GetApplicationProcessProgramID();
|
const auto title_id = system.ResolveCallerProgramId(*process_id);
|
||||||
*out_interface =
|
*out_interface =
|
||||||
std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
|
std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|||||||
@@ -243,10 +243,15 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
|
|||||||
return SaveScreenShot(out_entry, attribute, report_option, {}, image_data, aruid);
|
return SaveScreenShot(out_entry, attribute, report_option, {}, image_data, aruid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, AlbumReportOption report_option, const ApplicationData& app_data, std::span<const u8> image_data, u64 aruid) {
|
Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
|
||||||
|
const ScreenShotAttribute& attribute,
|
||||||
|
AlbumReportOption report_option,
|
||||||
|
const ApplicationData& app_data, std::span<const u8> image_data,
|
||||||
|
u64 aruid) {
|
||||||
R_UNLESS(!image_data.empty(), ResultUnknown); //TODO: ???
|
R_UNLESS(!image_data.empty(), ResultUnknown); //TODO: ???
|
||||||
|
|
||||||
const u64 title_id = system.GetApplicationProcessProgramID();
|
const u64 title_id = system.ResolveCallerProgramId(aruid);
|
||||||
|
|
||||||
auto static_service =
|
auto static_service =
|
||||||
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
|
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,8 @@ void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption r
|
|||||||
manager->FlipVerticallyOnWrite(invert_y);
|
manager->FlipVerticallyOnWrite(invert_y);
|
||||||
manager->SaveScreenShot(entry, attribute, report_option, image_data, {});
|
manager->SaveScreenShot(entry, attribute, report_option, image_data, {});
|
||||||
},
|
},
|
||||||
layout);
|
layout,
|
||||||
|
Nvnflinger::LayerStackId::Screenshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::Capture
|
} // namespace Service::Capture
|
||||||
|
|||||||
@@ -227,12 +227,13 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
|
|||||||
std::string src_path(Common::FS::SanitizePath(src_path_));
|
std::string src_path(Common::FS::SanitizePath(src_path_));
|
||||||
std::string dest_path(Common::FS::SanitizePath(dest_path_));
|
std::string dest_path(Common::FS::SanitizePath(dest_path_));
|
||||||
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
||||||
|
if (src == nullptr)
|
||||||
|
return FileSys::ResultPathNotFound;
|
||||||
|
|
||||||
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
|
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
|
||||||
// Use more-optimized vfs implementation rename.
|
std::string full_src_path = backing->GetFullPath() + "/" + src_path;
|
||||||
if (src == nullptr)
|
std::string full_dest_path = backing->GetFullPath() + "/" + dest_path;
|
||||||
return FileSys::ResultPathNotFound;
|
if (!Common::FS::RenameDir(full_src_path, full_dest_path)) {
|
||||||
if (!src->Rename(Common::FS::GetFilename(dest_path))) {
|
|
||||||
// TODO(DarkLordZach): Find a better error code for this
|
|
||||||
return ResultUnknown;
|
return ResultUnknown;
|
||||||
}
|
}
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGe
|
|||||||
{3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
|
{3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
|
||||||
{4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
|
{4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
|
||||||
{5, D<&IFileSystem::RenameFile>, "RenameFile"},
|
{5, D<&IFileSystem::RenameFile>, "RenameFile"},
|
||||||
{6, nullptr, "RenameDirectory"},
|
{6, D<&IFileSystem::RenameDirectory>, "RenameDirectory"},
|
||||||
{7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
|
{7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
|
||||||
{8, D<&IFileSystem::OpenFile>, "OpenFile"},
|
{8, D<&IFileSystem::OpenFile>, "OpenFile"},
|
||||||
{9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
|
{9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
|
||||||
@@ -88,6 +88,14 @@ Result IFileSystem::RenameFile(
|
|||||||
R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
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,
|
Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||||
u32 mode) {
|
u32 mode) {
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -36,6 +39,8 @@ public:
|
|||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||||
Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_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,
|
Result OpenFile(OutInterface<IFile> out_interface,
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
|
||||||
Result OpenDirectory(OutInterface<IDirectory> out_interface,
|
Result OpenDirectory(OutInterface<IDirectory> out_interface,
|
||||||
|
|||||||
@@ -1164,7 +1164,7 @@ Result IHidServer::InitializeSevenSixAxisSensor(ClientAppletResourceUserId aruid
|
|||||||
GetResourceManager()->GetConsoleSixAxis()->Activate();
|
GetResourceManager()->GetConsoleSixAxis()->Activate();
|
||||||
GetResourceManager()->GetSevenSixAxis()->Activate();
|
GetResourceManager()->GetSevenSixAxis()->Activate();
|
||||||
|
|
||||||
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
|
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress(), t_mem_1->GetOwner());
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode pollin
|
|||||||
|
|
||||||
auto& device = devices[device_index.value()].device;
|
auto& device = devices[device_index.value()].device;
|
||||||
device->SetPollingMode(polling_mode);
|
device->SetPollingMode(polling_mode);
|
||||||
device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
device->SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -147,7 +150,7 @@ Result IRS::RunImageTransferProcessor(
|
|||||||
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
||||||
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
||||||
image_transfer_processor.SetConfig(processor_config);
|
image_transfer_processor.SetConfig(processor_config);
|
||||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
|
||||||
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
||||||
Common::Input::PollingMode::IR);
|
Common::Input::PollingMode::IR);
|
||||||
|
|
||||||
@@ -295,7 +298,8 @@ Result IRS::RunImageTransferExProcessor(
|
|||||||
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
||||||
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
||||||
image_transfer_processor.SetConfig(processor_config);
|
image_transfer_processor.SetConfig(processor_config);
|
||||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(),
|
||||||
|
t_mem->GetOwner());
|
||||||
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
||||||
Common::Input::PollingMode::IR);
|
Common::Input::PollingMode::IR);
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public:
|
|||||||
, process{kernel, process_}
|
, process{kernel, process_}
|
||||||
, user_rx{std::move(user_rx_)}
|
, user_rx{std::move(user_rx_)}
|
||||||
, user_ro{std::move(user_ro_)}
|
, user_ro{std::move(user_ro_)}
|
||||||
, context{system_.ApplicationMemory()}
|
, context{process_->GetMemory()}
|
||||||
{
|
{
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#include <ankerl/unordered_dense.h>
|
#include <ankerl/unordered_dense.h>
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/ldn/ldn_results.h"
|
#include "core/hle/service/ldn/ldn_results.h"
|
||||||
#include "core/hle/service/ldn/ldn_types.h"
|
#include "core/hle/service/ldn/ldn_types.h"
|
||||||
|
|||||||
@@ -9,15 +9,12 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#define STBI_ONLY_JPEG 1
|
#include "common/stb.h"
|
||||||
#include <stb_image.h>
|
|
||||||
#include <stb_image_resize.h>
|
|
||||||
#include <stb_image_write.h>
|
|
||||||
|
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/file_sys/control_metadata.h"
|
#include "core/file_sys/control_metadata.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/vfs/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/k_transfer_memory.h"
|
#include "core/hle/kernel/k_transfer_memory.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
#include "core/hle/service/ns/language.h"
|
#include "core/hle/service/ns/language.h"
|
||||||
@@ -336,8 +333,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
|||||||
|
|
||||||
constexpr s32 data_offset = 0;
|
constexpr s32 data_offset = 0;
|
||||||
|
|
||||||
if (t_mem != nullptr && app_count > 0) {
|
if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) {
|
||||||
auto& memory = system.ApplicationMemory();
|
auto& memory = t_mem->GetOwner()->GetMemory();
|
||||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||||
|
|
||||||
for (size_t i = 0; i < app_count; ++i) {
|
for (size_t i = 0; i < app_count; ++i) {
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
|
|||||||
.transform_flags = layer.transform,
|
.transform_flags = layer.transform,
|
||||||
.crop_rect = layer.crop_rect,
|
.crop_rect = layer.crop_rect,
|
||||||
.blending = ConvertBlending(layer.blending),
|
.blending = ConvertBlending(layer.blending),
|
||||||
|
.layer_stack_mask = layer.layer_stack_mask,
|
||||||
});
|
});
|
||||||
|
|
||||||
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
|
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
|||||||
case 0x3:
|
case 0x3:
|
||||||
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output);
|
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output);
|
||||||
case 0x8:
|
case 0x8:
|
||||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, false);
|
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, false);
|
||||||
case 0x9:
|
case 0x9:
|
||||||
return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output);
|
return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output);
|
||||||
case 0xb:
|
case 0xb:
|
||||||
@@ -83,7 +83,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
|||||||
case 0x1a:
|
case 0x1a:
|
||||||
return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output, fd);
|
return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output, fd);
|
||||||
case 0x1b:
|
case 0x1b:
|
||||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, true);
|
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, true);
|
||||||
case 0x1d:
|
case 0x1d:
|
||||||
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output);
|
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output);
|
||||||
default:
|
default:
|
||||||
@@ -387,8 +387,19 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL
|
|||||||
return NvResult::Success;
|
return NvResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Core::Memory::Memory& nvhost_gpu::GetSessionMemory(DeviceFD fd) {
|
||||||
|
if (const auto it = sessions.find(fd); it != sessions.end())
|
||||||
|
if (auto* const session = core.GetSession(it->second);
|
||||||
|
session != nullptr && session->process != nullptr)
|
||||||
|
return session->process->GetMemory();
|
||||||
|
|
||||||
|
LOG_ERROR(Service_NVDRV, "No session for fd={}, falling back to application memory", fd);
|
||||||
|
return system.ApplicationMemory();
|
||||||
|
}
|
||||||
|
|
||||||
NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
||||||
std::span<Tegra::CommandListHeader> commands, bool kickoff) {
|
std::span<Tegra::CommandListHeader> commands, DeviceFD fd,
|
||||||
|
bool kickoff) {
|
||||||
if (params.num_entries > commands.size()) {
|
if (params.num_entries > commands.size()) {
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
return NvResult::InvalidSize;
|
return NvResult::InvalidSize;
|
||||||
@@ -396,7 +407,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
|||||||
|
|
||||||
Tegra::CommandList entries(params.num_entries);
|
Tegra::CommandList entries(params.num_entries);
|
||||||
if (kickoff) {
|
if (kickoff) {
|
||||||
system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(),
|
this->GetSessionMemory(fd).ReadBlock(params.address, entries.command_lists.data(),
|
||||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||||
} else {
|
} else {
|
||||||
std::memcpy(entries.command_lists.data(), commands.data(),
|
std::memcpy(entries.command_lists.data(), commands.data(),
|
||||||
|
|||||||
@@ -16,6 +16,10 @@
|
|||||||
#include "core/hle/service/nvdrv/nvdata.h"
|
#include "core/hle/service/nvdrv/nvdata.h"
|
||||||
#include "video_core/dma_pusher.h"
|
#include "video_core/dma_pusher.h"
|
||||||
|
|
||||||
|
namespace Core::Memory {
|
||||||
|
class Memory;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
namespace Control {
|
namespace Control {
|
||||||
struct ChannelState;
|
struct ChannelState;
|
||||||
@@ -196,8 +200,11 @@ private:
|
|||||||
|
|
||||||
NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries);
|
NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries);
|
||||||
|
|
||||||
|
Core::Memory::Memory& GetSessionMemory(DeviceFD fd);
|
||||||
|
|
||||||
NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
||||||
std::span<Tegra::CommandListHeader> commands, bool kickoff = false);
|
std::span<Tegra::CommandListHeader> commands, DeviceFD fd,
|
||||||
|
bool kickoff = false);
|
||||||
NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
|
NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
|
||||||
std::span<const Tegra::CommandListHeader> commands);
|
std::span<const Tegra::CommandListHeader> commands);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "core/core.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/core/container.h"
|
||||||
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
||||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||||
@@ -71,17 +72,23 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
|
|||||||
|
|
||||||
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
||||||
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
||||||
system.SetNVDECActive(true);
|
|
||||||
sessions[fd] = session_id;
|
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);
|
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
||||||
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
||||||
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
||||||
system.SetNVDECActive(false);
|
|
||||||
auto it = sessions.find(fd);
|
auto it = sessions.find(fd);
|
||||||
if (it != sessions.end()) {
|
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);
|
sessions.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -15,7 +15,8 @@ struct Layer {
|
|||||||
explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_,
|
explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_,
|
||||||
s32 consumer_id_)
|
s32 consumer_id_)
|
||||||
: buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_),
|
: buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_),
|
||||||
blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false) {}
|
blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false),
|
||||||
|
layer_stack_mask(DefaultLayerStackMask) {}
|
||||||
~Layer() {
|
~Layer() {
|
||||||
buffer_item_consumer->Abandon();
|
buffer_item_consumer->Abandon();
|
||||||
}
|
}
|
||||||
@@ -26,6 +27,7 @@ struct Layer {
|
|||||||
bool visible;
|
bool visible;
|
||||||
s32 z_index;
|
s32 z_index;
|
||||||
bool is_overlay;
|
bool is_overlay;
|
||||||
|
u32 layer_stack_mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LayerStack {
|
struct LayerStack {
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
|
|||||||
.transform = static_cast<android::BufferTransformFlags>(item.transform),
|
.transform = static_cast<android::BufferTransformFlags>(item.transform),
|
||||||
.crop_rect = item.crop,
|
.crop_rect = item.crop,
|
||||||
.acquire_fence = item.fence,
|
.acquire_fence = item.fence,
|
||||||
|
.layer_stack_mask = layer->layer_stack_mask,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
@@ -23,6 +26,25 @@ enum class LayerBlending : u32 {
|
|||||||
Coverage = 0x405,
|
Coverage = 0x405,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class LayerStackId : u32 {
|
||||||
|
Default = 0,
|
||||||
|
Lcd = 1,
|
||||||
|
Screenshot = 2,
|
||||||
|
Recording = 3,
|
||||||
|
LastFrame = 4,
|
||||||
|
Arbitrary = 5,
|
||||||
|
ApplicationForDebug = 6,
|
||||||
|
Null = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr u32 LayerStackBit(LayerStackId id) {
|
||||||
|
return 1U << static_cast<u32>(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 DefaultLayerStackMask =
|
||||||
|
LayerStackBit(LayerStackId::Default) | LayerStackBit(LayerStackId::Screenshot) |
|
||||||
|
LayerStackBit(LayerStackId::Recording) | LayerStackBit(LayerStackId::LastFrame);
|
||||||
|
|
||||||
struct HwcLayer {
|
struct HwcLayer {
|
||||||
u32 buffer_handle;
|
u32 buffer_handle;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
@@ -35,6 +57,7 @@ struct HwcLayer {
|
|||||||
android::BufferTransformFlags transform;
|
android::BufferTransformFlags transform;
|
||||||
Common::Rectangle<int> crop_rect;
|
Common::Rectangle<int> crop_rect;
|
||||||
android::Fence acquire_fence;
|
android::Fence acquire_fence;
|
||||||
|
u32 layer_stack_mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::Nvnflinger
|
} // namespace Service::Nvnflinger
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -104,6 +104,14 @@ void SurfaceFlinger::SetLayerBlending(s32 consumer_binder_id, LayerBlending blen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SurfaceFlinger::SetLayerStackMask(s32 consumer_binder_id, u32 layer_stack_mask) {
|
||||||
|
if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
|
||||||
|
layer->layer_stack_mask = layer_stack_mask;
|
||||||
|
LOG_DEBUG(Service_VI, "Layer {} stack mask set to {:#x}", consumer_binder_id,
|
||||||
|
layer_stack_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SurfaceFlinger::SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay) {
|
void SurfaceFlinger::SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay) {
|
||||||
if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
|
if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
|
||||||
layer->is_overlay = is_overlay;
|
layer->is_overlay = is_overlay;
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -48,6 +48,7 @@ public:
|
|||||||
void SetLayerVisibility(s32 consumer_binder_id, bool visible);
|
void SetLayerVisibility(s32 consumer_binder_id, bool visible);
|
||||||
void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending);
|
void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending);
|
||||||
void SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay);
|
void SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay);
|
||||||
|
void SetLayerStackMask(s32 consumer_binder_id, u32 layer_stack_mask);
|
||||||
|
|
||||||
std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);
|
std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);
|
||||||
|
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -13,8 +13,10 @@
|
|||||||
|
|
||||||
namespace Service::PCTL {
|
namespace Service::PCTL {
|
||||||
|
|
||||||
IParentalControlService::IParentalControlService(Core::System& system_, Capability capability_)
|
IParentalControlService::IParentalControlService(Core::System& system_, Capability capability_,
|
||||||
|
u64 program_id_)
|
||||||
: ServiceFramework{system_, "IParentalControlService"}, capability{capability_},
|
: ServiceFramework{system_, "IParentalControlService"}, capability{capability_},
|
||||||
|
program_id{program_id_},
|
||||||
service_context{system_, "IParentalControlService"}, synchronization_event{service_context},
|
service_context{system_, "IParentalControlService"}, synchronization_event{service_context},
|
||||||
unlinked_event{service_context}, request_suspension_event{service_context} {
|
unlinked_event{service_context}, request_suspension_event{service_context} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@@ -202,7 +204,6 @@ Result IParentalControlService::Initialize() {
|
|||||||
|
|
||||||
// TODO(ogniK): Recovery flag initialization for pctl:r
|
// TODO(ogniK): Recovery flag initialization for pctl:r
|
||||||
|
|
||||||
const auto program_id = system.GetApplicationProcessProgramID();
|
|
||||||
if (program_id != 0) {
|
if (program_id != 0) {
|
||||||
const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
|
const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
|
||||||
system.GetContentProvider()};
|
system.GetContentProvider()};
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ namespace Service::PCTL {
|
|||||||
|
|
||||||
class IParentalControlService final : public ServiceFramework<IParentalControlService> {
|
class IParentalControlService final : public ServiceFramework<IParentalControlService> {
|
||||||
public:
|
public:
|
||||||
explicit IParentalControlService(Core::System& system_, Capability capability_);
|
explicit IParentalControlService(Core::System& system_, Capability capability_,
|
||||||
|
u64 program_id_);
|
||||||
~IParentalControlService() override;
|
~IParentalControlService() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -84,6 +85,7 @@ private:
|
|||||||
RestrictionSettings restriction_settings{};
|
RestrictionSettings restriction_settings{};
|
||||||
std::array<char, 8> pin_code{};
|
std::array<char, 8> pin_code{};
|
||||||
Capability capability{};
|
Capability capability{};
|
||||||
|
u64 program_id{};
|
||||||
// TODO: this is raw
|
// TODO: this is raw
|
||||||
PlayTimerSettings raw_play_timer_settings{};
|
PlayTimerSettings raw_play_timer_settings{};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
#include "core/hle/service/pctl/parental_control_service.h"
|
#include "core/hle/service/pctl/parental_control_service.h"
|
||||||
#include "core/hle/service/pctl/parental_control_service_factory.h"
|
#include "core/hle/service/pctl/parental_control_service_factory.h"
|
||||||
@@ -23,17 +27,17 @@ IParentalControlServiceFactory::~IParentalControlServiceFactory() = default;
|
|||||||
|
|
||||||
Result IParentalControlServiceFactory::CreateService(
|
Result IParentalControlServiceFactory::CreateService(
|
||||||
Out<SharedPointer<IParentalControlService>> out_service, ClientProcessId process_id) {
|
Out<SharedPointer<IParentalControlService>> out_service, ClientProcessId process_id) {
|
||||||
LOG_DEBUG(Service_PCTL, "called");
|
LOG_DEBUG(Service_PCTL, "called, process_id={}", process_id.pid);
|
||||||
// TODO(ogniK): Get application id from process
|
*out_service = std::make_shared<IParentalControlService>(
|
||||||
*out_service = std::make_shared<IParentalControlService>(system, capability);
|
system, capability, system.ResolveCallerProgramId(*process_id));
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IParentalControlServiceFactory::CreateServiceWithoutInitialize(
|
Result IParentalControlServiceFactory::CreateServiceWithoutInitialize(
|
||||||
Out<SharedPointer<IParentalControlService>> out_service, ClientProcessId process_id) {
|
Out<SharedPointer<IParentalControlService>> out_service, ClientProcessId process_id) {
|
||||||
LOG_DEBUG(Service_PCTL, "called");
|
LOG_DEBUG(Service_PCTL, "called, process_id={}", process_id.pid);
|
||||||
// TODO(ogniK): Get application id from process
|
*out_service = std::make_shared<IParentalControlService>(
|
||||||
*out_service = std::make_shared<IParentalControlService>(system, capability);
|
system, capability, system.ResolveCallerProgramId(*process_id));
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,8 +132,7 @@ private:
|
|||||||
|
|
||||||
LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid);
|
LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid);
|
||||||
|
|
||||||
auto list = kernel.GetProcessList();
|
auto process = kernel.GetProcessByProcessId(pid);
|
||||||
auto process = SearchProcessList(system.Kernel(), list, [pid](auto& p) { return p->GetProcessId() == pid; });
|
|
||||||
|
|
||||||
if (process.IsNull()) {
|
if (process.IsNull()) {
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
@@ -186,9 +185,7 @@ private:
|
|||||||
|
|
||||||
LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id);
|
LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id);
|
||||||
|
|
||||||
auto list = kernel.GetProcessList();
|
auto process = kernel.GetProcessByProcessId(process_id);
|
||||||
auto process = SearchProcessList(system.Kernel(),
|
|
||||||
list, [process_id](auto& p) { return p->GetProcessId() == process_id; });
|
|
||||||
|
|
||||||
if (process.IsNull()) {
|
if (process.IsNull()) {
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ private:
|
|||||||
Type, process_id, data1.size(), data2.size());
|
Type, process_id, data1.size(), data2.size());
|
||||||
|
|
||||||
const auto& reporter{system.GetReporter()};
|
const auto& reporter{system.GetReporter()};
|
||||||
reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2},
|
reporter.SavePlayReport(Type, system.ResolveCallerProgramId(process_id), {data1, data2},
|
||||||
process_id);
|
process_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
@@ -98,7 +98,7 @@ private:
|
|||||||
Type, user_id[1], user_id[0], process_id, data1.size(), data2.size());
|
Type, user_id[1], user_id[0], process_id, data1.size(), data2.size());
|
||||||
|
|
||||||
const auto& reporter{system.GetReporter()};
|
const auto& reporter{system.GetReporter()};
|
||||||
reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2},
|
reporter.SavePlayReport(Type, system.ResolveCallerProgramId(process_id), {data1, data2},
|
||||||
process_id, user_id);
|
process_id, user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
|||||||
@@ -129,6 +129,13 @@ ServerManager::~ServerManager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
||||||
|
for (size_t i = 0; i < num_threads; i++) {
|
||||||
|
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
||||||
|
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(std::move(thread_name), [&] { this->LoopProcessImpl(); }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
|
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
|
||||||
server_manager->m_system.RunServer(std::move(server_manager));
|
server_manager->m_system.RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
@@ -245,14 +252,6 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
|
||||||
for (size_t i = 0; i < num_threads; i++) {
|
|
||||||
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
|
||||||
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(
|
|
||||||
std::move(thread_name), [&] { this->LoopProcessImpl(); }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ServerManager::LoopProcess() {
|
Result ServerManager::LoopProcess() {
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
m_stopped.Set();
|
m_stopped.Set();
|
||||||
@@ -393,7 +392,7 @@ Result ServerManager::CompleteSyncRequest(Session* session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send the reply.
|
// Send the reply.
|
||||||
res = server_session->SendReplyHLE(m_system.Kernel());
|
res = server_session->SendReplyHLE(m_system.Kernel(), service_res == IPC::ResultSessionClosed);
|
||||||
|
|
||||||
// If the session has been closed, we're done.
|
// If the session has been closed, we're done.
|
||||||
if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) {
|
if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) {
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -48,8 +51,8 @@ public:
|
|||||||
Result ManageDeferral(Kernel::KEvent** out_event);
|
Result ManageDeferral(Kernel::KEvent** out_event);
|
||||||
|
|
||||||
Result LoopProcess();
|
Result LoopProcess();
|
||||||
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
|
||||||
|
|
||||||
|
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
||||||
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -4,12 +4,16 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
#include <string_view>
|
||||||
|
#include <thread>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/ipc.h"
|
#include "core/hle/ipc.h"
|
||||||
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
@@ -33,6 +37,7 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
|
|||||||
: SessionRequestHandler(system_.Kernel(), service_name_)
|
: SessionRequestHandler(system_.Kernel(), service_name_)
|
||||||
, system{system_}
|
, system{system_}
|
||||||
, service_name{service_name_}
|
, service_name{service_name_}
|
||||||
|
, is_i_storage{std::string_view{service_name_} == "IStorage"}
|
||||||
, handler_invoker{handler_invoker_}
|
, handler_invoker{handler_invoker_}
|
||||||
, max_sessions{max_sessions_}
|
, max_sessions{max_sessions_}
|
||||||
{}
|
{}
|
||||||
@@ -77,13 +82,22 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
||||||
auto it = handlers.find(ctx.GetCommand());
|
const auto command = ctx.GetCommand();
|
||||||
|
auto it = handlers.find(command);
|
||||||
|
const bool is_cmd_read = command == 0;
|
||||||
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
||||||
if (info == nullptr || info->handler_callback == nullptr)
|
if (info == nullptr || info->handler_callback == nullptr)
|
||||||
return ReportUnimplementedFunction(ctx, info);
|
return ReportUnimplementedFunction(ctx, info);
|
||||||
|
|
||||||
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
||||||
handler_invoker(this, info->handler_callback, ctx);
|
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) {
|
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ protected:
|
|||||||
Core::System& system;
|
Core::System& system;
|
||||||
/// Identifier string used to connect to the service.
|
/// Identifier string used to connect to the service.
|
||||||
const char* service_name;
|
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.
|
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
||||||
InvokerFn* handler_invoker;
|
InvokerFn* handler_invoker;
|
||||||
/// Maximum number of concurrent sessions that this service can handle.
|
/// Maximum number of concurrent sessions that this service can handle.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
@@ -20,6 +20,9 @@
|
|||||||
#include "core/hle/service/sockets/sockets_translate.h"
|
#include "core/hle/service/sockets/sockets_translate.h"
|
||||||
#include "core/internal_network/network.h"
|
#include "core/internal_network/network.h"
|
||||||
#include "core/internal_network/socket_proxy.h"
|
#include "core/internal_network/socket_proxy.h"
|
||||||
|
#if defined(__unix__) && !defined(__APPLE__)
|
||||||
|
#include "core/internal_network/socket_icmp.h"
|
||||||
|
#endif
|
||||||
#include "core/internal_network/sockets.h"
|
#include "core/internal_network/sockets.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
#include <common/settings.h>
|
#include <common/settings.h>
|
||||||
@@ -28,15 +31,18 @@ namespace Service::Sockets {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool IsConnectionBased(Type type) {
|
[[nodiscard]] bool IsConnectionBased(Network::Type type) noexcept {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type::STREAM:
|
case Network::Type::STREAM:
|
||||||
|
case Network::Type::SEQPACKET:
|
||||||
return true;
|
return true;
|
||||||
case Type::DGRAM:
|
case Network::Type::RAW:
|
||||||
|
case Network::Type::DGRAM:
|
||||||
|
case Network::Type::RDM:
|
||||||
|
case Network::Type::Unspecified:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
UNREACHABLE();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +98,7 @@ void BSD::ConnectWork::Execute(BSD* bsd) {
|
|||||||
void BSD::ConnectWork::Response(HLERequestContext& ctx) {
|
void BSD::ConnectWork::Response(HLERequestContext& ctx) {
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
|
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,10 +176,9 @@ void BSD::Socket(HLERequestContext& ctx) {
|
|||||||
const u32 domain = rp.Pop<u32>();
|
const u32 domain = rp.Pop<u32>();
|
||||||
const u32 type = rp.Pop<u32>();
|
const u32 type = rp.Pop<u32>();
|
||||||
const u32 protocol = rp.Pop<u32>();
|
const u32 protocol = rp.Pop<u32>();
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
||||||
|
|
||||||
const auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
|
const auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
@@ -189,8 +194,8 @@ void BSD::SocketExempt(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
||||||
|
|
||||||
auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
|
auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
|
||||||
if (bsd_errno == Errno::SUCCESS) {
|
if (bsd_errno == Network::Errno::E_SUCCESS) {
|
||||||
bsd_errno = ShutdownImpl(fd, 0);
|
bsd_errno = ShutdownImpl(fd, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,13 +269,13 @@ void BSD::GetPeerName(HLERequestContext& ctx) {
|
|||||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||||
|
|
||||||
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
||||||
const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
|
const Network::Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
|
||||||
|
|
||||||
ctx.WriteBuffer(write_buffer);
|
ctx.WriteBuffer(write_buffer);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
|
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||||
}
|
}
|
||||||
@@ -282,13 +287,13 @@ void BSD::GetSockName(HLERequestContext& ctx) {
|
|||||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||||
|
|
||||||
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
||||||
const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
|
const Network::Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
|
||||||
|
|
||||||
ctx.WriteBuffer(write_buffer);
|
ctx.WriteBuffer(write_buffer);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
|
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||||
}
|
}
|
||||||
@@ -296,21 +301,19 @@ void BSD::GetSockName(HLERequestContext& ctx) {
|
|||||||
void BSD::GetSockOpt(HLERequestContext& ctx) {
|
void BSD::GetSockOpt(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const s32 fd = rp.Pop<s32>();
|
const s32 fd = rp.Pop<s32>();
|
||||||
const u32 level = rp.Pop<u32>();
|
const auto level = Network::SocketLevel(rp.Pop<u32>());
|
||||||
const auto optname = static_cast<OptName>(rp.Pop<u32>());
|
const auto optname = Network::OptName(rp.Pop<u32>());
|
||||||
|
|
||||||
std::vector<u8> optval(ctx.GetWriteBufferSize());
|
std::vector<u8> optval(ctx.GetWriteBufferSize());
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname,
|
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname, optval.size());
|
||||||
optval.size());
|
const Network::Errno err = GetSockOptImpl(fd, level, optname, optval);
|
||||||
|
|
||||||
const Errno err = GetSockOptImpl(fd, level, optname, optval);
|
|
||||||
|
|
||||||
ctx.WriteBuffer(optval);
|
ctx.WriteBuffer(optval);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
|
rb.Push<s32>(err == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(err);
|
rb.PushEnum(err);
|
||||||
rb.Push<u32>(static_cast<u32>(optval.size()));
|
rb.Push<u32>(static_cast<u32>(optval.size()));
|
||||||
}
|
}
|
||||||
@@ -333,7 +336,7 @@ void BSD::Fcntl(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
|
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
|
||||||
|
|
||||||
const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
|
const auto [ret, bsd_errno] = FcntlImpl(fd, Network::FcntlCmd(cmd), arg);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
@@ -345,13 +348,11 @@ void BSD::SetSockOpt(HLERequestContext& ctx) {
|
|||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
const s32 fd = rp.Pop<s32>();
|
const s32 fd = rp.Pop<s32>();
|
||||||
const u32 level = rp.Pop<u32>();
|
const Network::SocketLevel level = Network::SocketLevel(rp.Pop<u32>());
|
||||||
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
|
const Network::OptName optname = Network::OptName(rp.Pop<u32>());
|
||||||
const auto optval = ctx.ReadBuffer();
|
const auto optval = ctx.ReadBuffer();
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level,
|
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level, u32(optname), optval.size());
|
||||||
static_cast<u32>(optname), optval.size());
|
|
||||||
|
|
||||||
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
|
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,7 +474,7 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
s32 ret;
|
s32 ret;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0x8);
|
static_assert(sizeof(OutputParameters) == 0x8);
|
||||||
|
|
||||||
@@ -485,7 +486,7 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
if (is_user) {
|
if (is_user) {
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = 0,
|
.ret = 0,
|
||||||
.bsd_errno = Errno::INVAL,
|
.bsd_errno = Network::Errno::E_INVAL,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -494,10 +495,10 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
if (auto* res = std::get_if<s32>(&res_v)) {
|
if (auto* res = std::get_if<s32>(&res_v)) {
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = *res,
|
.ret = *res,
|
||||||
.bsd_errno = Errno::SUCCESS,
|
.bsd_errno = Network::Errno::E_SUCCESS,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
auto* err = std::get_if<Errno>(&res_v);
|
auto* err = std::get_if<Network::Errno>(&res_v);
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = 0,
|
.ret = 0,
|
||||||
.bsd_errno = *err,
|
.bsd_errno = *err,
|
||||||
@@ -512,7 +513,7 @@ void BSD::EventFd(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
|
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
|
||||||
|
|
||||||
BuildErrnoResponse(ctx, Errno::SUCCESS);
|
BuildErrnoResponse(ctx, Network::Errno::E_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Work>
|
template <typename Work>
|
||||||
@@ -521,132 +522,143 @@ void BSD::ExecuteWork(HLERequestContext& ctx, Work work) {
|
|||||||
work.Response(ctx);
|
work.Response(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
|
std::pair<s32, Network::Errno> BSD::SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol) {
|
||||||
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
|
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
|
||||||
if (is_user && (type == Type::SEQPACKET || type == Type::RAW)) {
|
LOG_DEBUG(Network, "domain={},type={},protocol={}", u32(domain), u32(type), u32(protocol));
|
||||||
if (type == Type::RAW && domain == Domain::INET && protocol == Protocol::ICMP) {
|
if (is_user && (type == Network::Type::SEQPACKET || type == Network::Type::RAW)) {
|
||||||
|
if (type == Network::Type::RAW && domain == Network::Domain::INET && protocol == Network::Protocol::ICMP) {
|
||||||
// fine, can use on bsd:s and bsd:u
|
// fine, can use on bsd:s and bsd:u
|
||||||
} else {
|
} else {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
|
[[maybe_unused]] const bool unk_flag = (u32(type) & 0x20000000) != 0;
|
||||||
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
|
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
|
||||||
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
|
type = Network::Type(u32(type) & ~0x20000000);
|
||||||
|
|
||||||
const s32 fd = FindFreeFileDescriptorHandle();
|
const s32 fd = FindFreeFileDescriptorHandle();
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return {-1, Errno::MFILE};
|
return {-1, Network::Errno::E_MFILE};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::values.airplane_mode.GetValue() && IsConnectionBased(type)) {
|
||||||
|
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
|
||||||
|
file_descriptors[fd].reset();
|
||||||
|
return {-1, Network::Errno::E_NOTCONN};
|
||||||
}
|
}
|
||||||
|
|
||||||
file_descriptors[fd] = FileDescriptor{};
|
file_descriptors[fd] = FileDescriptor{};
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
// ENONMEM might be thrown here
|
// ENONMEM might be thrown here
|
||||||
|
LOG_INFO(Service, "New socket fd={},domain={},type={},prot={}", fd, domain, type, protocol);
|
||||||
|
|
||||||
LOG_INFO(Service, "New socket fd={}", fd);
|
// While room is important -- we need to remember ICMP takes priority over **everything else**
|
||||||
|
// TODO: rework this so proxy sockets can be done transparently? -- like what if i need
|
||||||
|
// to browse the internet while playing LDN or something stupid like that?
|
||||||
auto room_member = Network::GetRoomMember().lock();
|
auto room_member = Network::GetRoomMember().lock();
|
||||||
if (room_member && room_member->IsConnected()) {
|
if ((protocol != Network::Protocol::ICMP && protocol != Network::Protocol::ICMPV6)
|
||||||
|
&& (room_member && room_member->IsConnected())) {
|
||||||
descriptor.socket = std::make_shared<Network::ProxySocket>();
|
descriptor.socket = std::make_shared<Network::ProxySocket>();
|
||||||
|
descriptor.socket->fd = fd;
|
||||||
} else {
|
} else {
|
||||||
descriptor.socket = std::make_shared<Network::Socket>();
|
descriptor.socket = std::make_shared<Network::Socket>();
|
||||||
}
|
}
|
||||||
|
auto const bsd_errno = descriptor.socket->Initialize(domain, type, protocol);
|
||||||
|
|
||||||
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
|
#if defined(__unix__) && !defined(__APPLE__)
|
||||||
descriptor.is_connection_based = IsConnectionBased(type);
|
// ...only unix has this issue it seems, ICMP works otherwise fine on win
|
||||||
|
if ((protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6)
|
||||||
if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) {
|
&& bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
|
LOG_WARNING(Network, "Using ICMP emulated socket");
|
||||||
return {-1, Errno::NOTCONN};
|
descriptor.socket = std::make_shared<Network::IcmpSocket>();
|
||||||
|
descriptor.socket->fd = fd;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return {fd, Errno::SUCCESS};
|
descriptor.is_connection_based = IsConnectionBased(type);
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (descriptor.is_connection_based && descriptor.socket->fd == INVALID_SOCKET) {
|
||||||
|
#else
|
||||||
|
if (descriptor.is_connection_based && descriptor.socket->fd == Network::Socket::INVALID_SOCKET) {
|
||||||
|
#endif
|
||||||
|
file_descriptors[fd].reset();
|
||||||
|
return {-1, bsd_errno};
|
||||||
|
}
|
||||||
|
return {fd, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
|
std::pair<s32, Network::Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout) {
|
||||||
s32 nfds, s32 timeout) {
|
LOG_DEBUG(Network, "nfds={},timeout={}", nfds, timeout);
|
||||||
if (nfds <= 0) {
|
if (nfds <= 0) {
|
||||||
// When no entries are provided, -1 is returned with errno zero
|
// When no entries are provided, -1 is returned with errno zero
|
||||||
return {-1, Errno::SUCCESS};
|
return {-1, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
if (read_buffer.size() < nfds * sizeof(PollFD)) {
|
if (read_buffer.size() < nfds * sizeof(Network::PollFD)) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
if (write_buffer.size() < nfds * sizeof(PollFD)) {
|
if (write_buffer.size() < nfds * sizeof(Network::PollFD)) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<PollFD> fds(nfds);
|
std::span<const Network::PollFD> in_fds(reinterpret_cast<const Network::PollFD*>(read_buffer.data()), nfds);
|
||||||
std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
|
std::span<Network::PollFD> out_fds(reinterpret_cast<Network::PollFD*>(write_buffer.data()), nfds);
|
||||||
|
std::copy(in_fds.begin(), in_fds.end(), out_fds.begin());
|
||||||
|
|
||||||
if (timeout >= 0) {
|
if (timeout >= 0) {
|
||||||
const s64 seconds = timeout / 1000;
|
const s64 seconds = timeout / 1000;
|
||||||
const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
|
const u64 nanoseconds = 1'000'000 * (u64(timeout) % 1000);
|
||||||
|
|
||||||
if (seconds < 0) {
|
if (seconds < 0) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
if (nanoseconds > 999'999'999) {
|
if (nanoseconds > 999'999'999) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
} else if (timeout != -1) {
|
} else if (timeout != -1) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PollFD& pollfd : fds) {
|
for (size_t i = 0; i < in_fds.size(); ++i) {
|
||||||
ASSERT(False(pollfd.revents));
|
ASSERT(out_fds[i].fd == in_fds[i].fd && False(in_fds[i].revents));
|
||||||
|
if (!IsFileDescriptorValid(in_fds[i].fd)) {
|
||||||
if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
|
out_fds[i].revents = {};
|
||||||
LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
|
if (!file_descriptors[in_fds[i].fd])
|
||||||
pollfd.revents = PollEvents{};
|
out_fds[i].revents = Network::PollEvents::NVAL;
|
||||||
return {0, Errno::SUCCESS};
|
return {0, Network::Errno::E_SUCCESS};
|
||||||
}
|
|
||||||
|
|
||||||
const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
|
|
||||||
if (!descriptor) {
|
|
||||||
LOG_TRACE(Service, "File descriptor handle={} is not allocated", pollfd.fd);
|
|
||||||
pollfd.revents = PollEvents::Nval;
|
|
||||||
return {0, Errno::SUCCESS};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Network::PollFD> host_pollfds(fds.size());
|
std::vector<Network::HostPollFD> host_pollfds(in_fds.size());
|
||||||
std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [](PollFD pollfd) {
|
std::transform(in_fds.begin(), in_fds.end(), host_pollfds.begin(), [](auto const e) {
|
||||||
Network::PollFD result;
|
Network::HostPollFD result{};
|
||||||
result.socket = file_descriptors[pollfd.fd]->socket.get();
|
result.socket = file_descriptors[e.fd]->socket.get();
|
||||||
result.events = Translate(pollfd.events);
|
result.events = e.events;
|
||||||
result.revents = Network::PollEvents{};
|
result.revents = {};
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
auto const res = Network::Poll(host_pollfds, timeout);
|
||||||
const auto result = Network::Poll(host_pollfds, timeout);
|
for (size_t i = 0; i < in_fds.size(); ++i)
|
||||||
|
out_fds[i].revents = host_pollfds[i].revents;
|
||||||
const size_t num = host_pollfds.size();
|
return res;
|
||||||
for (size_t i = 0; i < num; ++i) {
|
|
||||||
fds[i].revents = Translate(host_pollfds[i].revents);
|
|
||||||
}
|
|
||||||
std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));
|
|
||||||
|
|
||||||
return Translate(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
std::pair<s32, Network::Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
const s32 new_fd = FindFreeFileDescriptorHandle();
|
const s32 new_fd = FindFreeFileDescriptorHandle();
|
||||||
if (new_fd < 0) {
|
if (new_fd < 0) {
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return {-1, Errno::MFILE};
|
return {-1, Network::Errno::E_MFILE};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
auto [result, bsd_errno] = descriptor.socket->Accept();
|
auto [result, bsd_errno] = descriptor.socket->Accept();
|
||||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return {-1, Translate(bsd_errno)};
|
return {-1, bsd_errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
file_descriptors[new_fd] = FileDescriptor{};
|
file_descriptors[new_fd] = FileDescriptor{};
|
||||||
@@ -654,267 +666,218 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
|||||||
new_descriptor.socket = std::move(result.socket);
|
new_descriptor.socket = std::move(result.socket);
|
||||||
new_descriptor.is_connection_based = descriptor.is_connection_based;
|
new_descriptor.is_connection_based = descriptor.is_connection_based;
|
||||||
|
|
||||||
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
|
PutValue(write_buffer, result.sockaddr_in);
|
||||||
PutValue(write_buffer, guest_addr_in);
|
return {new_fd, Network::Errno::E_SUCCESS};
|
||||||
|
|
||||||
return {new_fd, Errno::SUCCESS};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
|
Network::Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addr_in = GetValue<SockAddrIn>(addr);
|
auto addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||||
|
return file_descriptors[fd]->socket->Bind(addr_in);
|
||||||
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
Network::Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addr_in = GetValue<SockAddrIn>(addr);
|
auto addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||||
|
const Network::Errno result = file_descriptors[fd]->socket->Connect(addr_in);
|
||||||
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
|
if (result == Network::Errno::E_ISCONN) {
|
||||||
|
|
||||||
if (result == Errno::ISCONN) {
|
|
||||||
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
|
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
Network::Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
|
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
|
||||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return Translate(bsd_errno);
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
const SockAddrIn guest_addrin = Translate(addr_in);
|
ASSERT(write_buffer.size() >= addr_in.len);
|
||||||
|
write_buffer.resize(addr_in.len);
|
||||||
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
PutValue(write_buffer, addr_in);
|
||||||
write_buffer.resize(sizeof(guest_addrin));
|
return bsd_errno;
|
||||||
PutValue(write_buffer, guest_addrin);
|
|
||||||
return Translate(bsd_errno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
Network::Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
|
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
|
||||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return Translate(bsd_errno);
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
const SockAddrIn guest_addrin = Translate(addr_in);
|
ASSERT(write_buffer.size() >= addr_in.len);
|
||||||
|
write_buffer.resize(addr_in.len);
|
||||||
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
PutValue(write_buffer, addr_in);
|
||||||
write_buffer.resize(sizeof(guest_addrin));
|
return bsd_errno;
|
||||||
PutValue(write_buffer, guest_addrin);
|
|
||||||
return Translate(bsd_errno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD::ListenImpl(s32 fd, s32 backlog) {
|
Network::Errno BSD::ListenImpl(s32 fd, s32 backlog) {
|
||||||
|
LOG_DEBUG(Network, "fd={},backlog={}", fd, backlog);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
return Translate(file_descriptors[fd]->socket->Listen(backlog));
|
return file_descriptors[fd]->socket->Listen(backlog);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
|
std::pair<s32, Network::Errno> BSD::FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg) {
|
||||||
|
LOG_DEBUG(Network, "fd={},cmd={},arg={}", fd, u32(cmd), arg);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case FcntlCmd::GETFL:
|
case Network::FcntlCmd::GETFL:
|
||||||
ASSERT(arg == 0);
|
ASSERT(arg == 0);
|
||||||
return {descriptor.flags, Errno::SUCCESS};
|
return {descriptor.flags, Network::Errno::E_SUCCESS};
|
||||||
case FcntlCmd::SETFL: {
|
case Network::FcntlCmd::SETFL: {
|
||||||
const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
|
const bool enable = (arg & u32(Network::FcntlFlags::NONBLOCK_NX)) != 0;
|
||||||
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
|
const Network::Errno bsd_errno = descriptor.socket->SetNonBlock(enable);
|
||||||
if (bsd_errno != Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return {-1, bsd_errno};
|
return {-1, bsd_errno};
|
||||||
}
|
}
|
||||||
descriptor.flags = arg;
|
descriptor.flags = arg;
|
||||||
return {0, Errno::SUCCESS};
|
return {0, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
|
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
|
||||||
return {-1, Errno::SUCCESS};
|
return {-1, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
|
Network::Errno BSD::GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval) {
|
||||||
|
LOG_DEBUG(Network, "fd={},level={},optname={}", fd, u32(level), u32(optname));
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
|
if (level != Network::SocketLevel::SOCKET) {
|
||||||
UNIMPLEMENTED_MSG("Unknown getsockopt level");
|
LOG_WARNING(Service, "(stubbed) level fd={}, level={}, optname={}", fd, level, optname);
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
||||||
|
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
case OptName::ERROR_: {
|
case Network::OptName::ERROR_: {
|
||||||
auto [pending_err, getsockopt_err] = socket->GetPendingError();
|
auto [pending_err, getsockopt_err] = socket->GetPendingError();
|
||||||
if (getsockopt_err == Network::Errno::SUCCESS) {
|
if (getsockopt_err == Network::Errno::E_SUCCESS) {
|
||||||
Errno translated_pending_err = Translate(pending_err);
|
|
||||||
ASSERT_OR_EXECUTE_MSG(
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
optval.size() == sizeof(Errno), { return Errno::INVAL; },
|
optval.size() == sizeof(Network::Errno), { return Network::Errno::E_INVAL; },
|
||||||
"Incorrect getsockopt option size");
|
"Incorrect getsockopt option size");
|
||||||
optval.resize(sizeof(Errno));
|
optval.resize(sizeof(Network::Errno));
|
||||||
PutValue(optval, translated_pending_err);
|
PutValue(optval, pending_err);
|
||||||
}
|
}
|
||||||
return Translate(getsockopt_err);
|
return getsockopt_err;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
|
Network::Errno BSD::SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
||||||
|
LOG_DEBUG(Service, "fd={},level={},optname={}", fd, level, optname);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
|
||||||
|
|
||||||
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
|
|
||||||
LOG_WARNING(Service, "(STUBBED) setsockopt with level={}, optname={}", level, optname);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
||||||
|
return socket->SetSockOpt(level, optname, optval);
|
||||||
if (optname == OptName::LINGER) {
|
|
||||||
ASSERT(optval.size() == sizeof(Linger));
|
|
||||||
auto linger = GetValue<Linger>(optval);
|
|
||||||
ASSERT(linger.onoff == 0 || linger.onoff == 1);
|
|
||||||
|
|
||||||
return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(optval.size() == sizeof(u32));
|
|
||||||
auto value = GetValue<u32>(optval);
|
|
||||||
|
|
||||||
switch (optname) {
|
|
||||||
case OptName::REUSEADDR:
|
|
||||||
ASSERT(value == 0 || value == 1);
|
|
||||||
return Translate(socket->SetReuseAddr(value != 0));
|
|
||||||
case OptName::KEEPALIVE:
|
|
||||||
ASSERT(value == 0 || value == 1);
|
|
||||||
return Translate(socket->SetKeepAlive(value != 0));
|
|
||||||
case OptName::BROADCAST:
|
|
||||||
ASSERT(value == 0 || value == 1);
|
|
||||||
return Translate(socket->SetBroadcast(value != 0));
|
|
||||||
case OptName::SNDBUF:
|
|
||||||
return Translate(socket->SetSndBuf(value));
|
|
||||||
case OptName::RCVBUF:
|
|
||||||
return Translate(socket->SetRcvBuf(value));
|
|
||||||
case OptName::SNDTIMEO:
|
|
||||||
return Translate(socket->SetSndTimeo(value));
|
|
||||||
case OptName::RCVTIMEO:
|
|
||||||
return Translate(socket->SetRcvTimeo(value));
|
|
||||||
case OptName::NOSIGPIPE:
|
|
||||||
LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD::ShutdownImpl(s32 fd, s32 how) {
|
Network::Errno BSD::ShutdownImpl(s32 fd, s32 how) {
|
||||||
|
LOG_DEBUG(Network, "fd={},how={}", fd, how);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
|
return file_descriptors[fd]->socket->Shutdown(Network::ShutdownHow(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, Network::Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
|
||||||
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
// Apply flags
|
// Apply flags
|
||||||
using Network::FLAG_MSG_DONTWAIT;
|
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
|
||||||
using Network::FLAG_O_NONBLOCK;
|
flags &= ~u32(Network::MsgOpt::DONTWAIT);
|
||||||
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||||
flags &= ~FLAG_MSG_DONTWAIT;
|
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
|
||||||
descriptor.socket->SetNonBlock(true);
|
descriptor.socket->SetNonBlock(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
|
const auto [ret, bsd_errno] = descriptor.socket->Recv(flags, message);
|
||||||
|
|
||||||
// Restore original state
|
// Restore original state
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0)
|
||||||
descriptor.socket->SetNonBlock(false);
|
descriptor.socket->SetNonBlock(false);
|
||||||
}
|
|
||||||
|
|
||||||
return {ret, bsd_errno};
|
return {ret, bsd_errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
std::pair<s32, Network::Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::vector<u8>& addr) {
|
||||||
std::vector<u8>& addr) {
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
@@ -929,19 +892,17 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply flags
|
// Apply flags
|
||||||
using Network::FLAG_MSG_DONTWAIT;
|
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
|
||||||
using Network::FLAG_O_NONBLOCK;
|
flags &= ~u32(Network::MsgOpt::DONTWAIT);
|
||||||
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||||
flags &= ~FLAG_MSG_DONTWAIT;
|
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
|
||||||
descriptor.socket->SetNonBlock(true);
|
descriptor.socket->SetNonBlock(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
|
const auto [ret, bsd_errno] = descriptor.socket->RecvFrom(flags, message, p_addr_in);
|
||||||
|
|
||||||
// Restore original state
|
// Restore original state
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||||
descriptor.socket->SetNonBlock(false);
|
descriptor.socket->SetNonBlock(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -950,58 +911,59 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
|
|||||||
addr.clear();
|
addr.clear();
|
||||||
} else {
|
} else {
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
const SockAddrIn result = Translate(addr_in);
|
PutValue(addr, addr_in);
|
||||||
PutValue(addr, result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {ret, bsd_errno};
|
return {ret, bsd_errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
std::pair<s32, Network::Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
||||||
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
return Translate(file_descriptors[fd]->socket->Send(message, flags));
|
return 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, Network::Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::span<const u8> addr) {
|
||||||
std::span<const u8> addr) {
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SockAddrIn addr_in;
|
Network::SockAddrIn addr_in{};
|
||||||
Network::SockAddrIn* p_addr_in = nullptr;
|
Network::SockAddrIn* p_addr_in = nullptr;
|
||||||
if (!addr.empty()) {
|
if (!addr.empty()) {
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
auto guest_addr_in = GetValue<SockAddrIn>(addr);
|
auto guest_addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||||
addr_in = Translate(guest_addr_in);
|
addr_in = guest_addr_in;
|
||||||
p_addr_in = &addr_in;
|
p_addr_in = &addr_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
|
return file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD::CloseImpl(s32 fd) {
|
Network::Errno BSD::CloseImpl(s32 fd) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
|
auto const bsd_errno = file_descriptors[fd]->socket->Close();
|
||||||
if (bsd_errno != Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return bsd_errno;
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1011,15 +973,16 @@ Errno BSD::CloseImpl(s32 fd) {
|
|||||||
return bsd_errno;
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
|
std::variant<s32, Network::Errno> BSD::DuplicateSocketImpl(s32 fd) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const s32 new_fd = FindFreeFileDescriptorHandle();
|
const s32 new_fd = FindFreeFileDescriptorHandle();
|
||||||
if (new_fd < 0) {
|
if (!IsFileDescriptorValid(new_fd)) {
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return Errno::MFILE;
|
return Network::Errno::E_MFILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_descriptors[new_fd] = FileDescriptor{
|
file_descriptors[new_fd] = FileDescriptor{
|
||||||
@@ -1031,6 +994,7 @@ std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
|
std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -1042,41 +1006,40 @@ std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s32 BSD::FindFreeFileDescriptorHandle() noexcept {
|
s32 BSD::FindFreeFileDescriptorHandle() noexcept {
|
||||||
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
|
// first three file descriptors are reserved for:
|
||||||
if (!file_descriptors[fd]) {
|
// STDOUT_FILENO, STDIN_FILENO and STDERR_FILENO
|
||||||
|
for (s32 fd = 0; fd < s32(file_descriptors.size()); ++fd)
|
||||||
|
if (!file_descriptors[fd])
|
||||||
return fd;
|
return fd;
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
|
bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
|
||||||
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
|
if (fd < 0 || fd >= s32(file_descriptors.size())) {
|
||||||
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
|
LOG_ERROR(Service, "Invalid handle={}", fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]) {
|
if (!file_descriptors[fd]) {
|
||||||
LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
|
LOG_ERROR(Service, "handle={} is not allocated", fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
|
void BSD::BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept {
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
|
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
||||||
for (auto& optional_descriptor : file_descriptors) {
|
for (auto& optional_descriptor : file_descriptors) {
|
||||||
if (!optional_descriptor.has_value()) {
|
if (optional_descriptor.has_value()) {
|
||||||
continue;
|
FileDescriptor& descriptor = *optional_descriptor;
|
||||||
|
descriptor.socket.get()->HandleProxyPacket(packet);
|
||||||
}
|
}
|
||||||
FileDescriptor& descriptor = *optional_descriptor;
|
|
||||||
descriptor.socket.get()->HandleProxyPacket(packet);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
#include "core/hle/service/sockets/sockets.h"
|
#include "core/hle/service/sockets/sockets.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
@@ -35,8 +35,8 @@ public:
|
|||||||
// These methods are called from SSL; the first two are also called from
|
// These methods are called from SSL; the first two are also called from
|
||||||
// this class for the corresponding IPC methods.
|
// this class for the corresponding IPC methods.
|
||||||
// On the real device, the SSL service makes IPC calls to this service.
|
// On the real device, the SSL service makes IPC calls to this service.
|
||||||
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd);
|
std::variant<s32, Network::Errno> DuplicateSocketImpl(s32 fd);
|
||||||
Errno CloseImpl(s32 fd);
|
Network::Errno CloseImpl(s32 fd);
|
||||||
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
|
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -58,7 +58,7 @@ private:
|
|||||||
std::span<const u8> read_buffer;
|
std::span<const u8> read_buffer;
|
||||||
std::vector<u8> write_buffer;
|
std::vector<u8> write_buffer;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AcceptWork {
|
struct AcceptWork {
|
||||||
@@ -68,7 +68,7 @@ private:
|
|||||||
s32 fd;
|
s32 fd;
|
||||||
std::vector<u8> write_buffer;
|
std::vector<u8> write_buffer;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConnectWork {
|
struct ConnectWork {
|
||||||
@@ -77,7 +77,7 @@ private:
|
|||||||
|
|
||||||
s32 fd;
|
s32 fd;
|
||||||
std::span<const u8> addr;
|
std::span<const u8> addr;
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RecvWork {
|
struct RecvWork {
|
||||||
@@ -88,7 +88,7 @@ private:
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
std::vector<u8> message;
|
std::vector<u8> message;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RecvFromWork {
|
struct RecvFromWork {
|
||||||
@@ -100,7 +100,7 @@ private:
|
|||||||
std::vector<u8> message;
|
std::vector<u8> message;
|
||||||
std::vector<u8> addr;
|
std::vector<u8> addr;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SendWork {
|
struct SendWork {
|
||||||
@@ -111,7 +111,7 @@ private:
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
std::span<const u8> message;
|
std::span<const u8> message;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SendToWork {
|
struct SendToWork {
|
||||||
@@ -123,7 +123,7 @@ private:
|
|||||||
std::span<const u8> message;
|
std::span<const u8> message;
|
||||||
std::span<const u8> addr;
|
std::span<const u8> addr;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
void RegisterClient(HLERequestContext& ctx);
|
void RegisterClient(HLERequestContext& ctx);
|
||||||
@@ -155,29 +155,29 @@ private:
|
|||||||
template <typename Work>
|
template <typename Work>
|
||||||
void ExecuteWork(HLERequestContext& ctx, Work work);
|
void ExecuteWork(HLERequestContext& ctx, Work work);
|
||||||
|
|
||||||
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
|
std::pair<s32, Network::Errno> SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol);
|
||||||
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
|
std::pair<s32, Network::Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
|
||||||
std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
|
std::pair<s32, Network::Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Errno BindImpl(s32 fd, std::span<const u8> addr);
|
Network::Errno BindImpl(s32 fd, std::span<const u8> addr);
|
||||||
Errno ConnectImpl(s32 fd, std::span<const u8> addr);
|
Network::Errno ConnectImpl(s32 fd, std::span<const u8> addr);
|
||||||
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
Network::Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
Network::Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Errno ListenImpl(s32 fd, s32 backlog);
|
Network::Errno ListenImpl(s32 fd, s32 backlog);
|
||||||
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
|
std::pair<s32, Network::Errno> FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg);
|
||||||
Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
|
Network::Errno GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval);
|
||||||
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
|
Network::Errno SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval);
|
||||||
Errno ShutdownImpl(s32 fd, s32 how);
|
Network::Errno ShutdownImpl(s32 fd, s32 how);
|
||||||
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
|
std::pair<s32, Network::Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
|
||||||
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
std::pair<s32, Network::Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
||||||
std::vector<u8>& addr);
|
std::vector<u8>& addr);
|
||||||
std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
|
std::pair<s32, Network::Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
|
||||||
std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
std::pair<s32, Network::Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
||||||
std::span<const u8> addr);
|
std::span<const u8> addr);
|
||||||
|
|
||||||
s32 FindFreeFileDescriptorHandle() noexcept;
|
s32 FindFreeFileDescriptorHandle() noexcept;
|
||||||
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
||||||
|
|
||||||
void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept;
|
void BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept;
|
||||||
|
|
||||||
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
|
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
|
||||||
|
|
||||||
|
|||||||
@@ -102,39 +102,39 @@ static bool IsBlockedHost(const std::string& host) {
|
|||||||
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
|
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
|
||||||
}
|
}
|
||||||
|
|
||||||
static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
|
static NetDbError GetAddrInfoErrorToNetDbError(Network::GetAddrInfoError result) {
|
||||||
// These combinations have been verified on console (but are not
|
// These combinations have been verified on console (but are not
|
||||||
// exhaustive).
|
// exhaustive).
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case GetAddrInfoError::SUCCESS:
|
case Network::GetAddrInfoError::SUCCESS:
|
||||||
return NetDbError::Success;
|
return NetDbError::Success;
|
||||||
case GetAddrInfoError::AGAIN:
|
case Network::GetAddrInfoError::AGAIN:
|
||||||
return NetDbError::TryAgain;
|
return NetDbError::TryAgain;
|
||||||
case GetAddrInfoError::NODATA:
|
case Network::GetAddrInfoError::NODATA:
|
||||||
return NetDbError::HostNotFound;
|
return NetDbError::HostNotFound;
|
||||||
case GetAddrInfoError::SERVICE:
|
case Network::GetAddrInfoError::SERVICE:
|
||||||
return NetDbError::Success;
|
return NetDbError::Success;
|
||||||
default:
|
default:
|
||||||
return NetDbError::HostNotFound;
|
return NetDbError::HostNotFound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
|
static Network::Errno GetAddrInfoErrorToErrno(Network::GetAddrInfoError result) {
|
||||||
// These combinations have been verified on console (but are not
|
// These combinations have been verified on console (but are not
|
||||||
// exhaustive).
|
// exhaustive).
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case GetAddrInfoError::SUCCESS:
|
case Network::GetAddrInfoError::SUCCESS:
|
||||||
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
|
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
|
||||||
// some reason, but that doesn't seem useful to implement.
|
// some reason, but that doesn't seem useful to implement.
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
case GetAddrInfoError::AGAIN:
|
case Network::GetAddrInfoError::AGAIN:
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
case GetAddrInfoError::NODATA:
|
case Network::GetAddrInfoError::NODATA:
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
case GetAddrInfoError::SERVICE:
|
case Network::GetAddrInfoError::SERVICE:
|
||||||
return Errno::INVAL;
|
return Network::Errno::E_INVAL;
|
||||||
default:
|
default:
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,9 +155,7 @@ static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
|
|||||||
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
|
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
|
||||||
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
|
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
|
||||||
// doesn't implement h_errno.
|
// doesn't implement h_errno.
|
||||||
static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
|
static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrInfo> vec, std::string_view host) {
|
||||||
std::string_view host) {
|
|
||||||
|
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
// h_name: use the input hostname (append nul-terminated)
|
// h_name: use the input hostname (append nul-terminated)
|
||||||
AppendNulTerminated(data, host);
|
AppendNulTerminated(data, host);
|
||||||
@@ -165,12 +163,12 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
|
|||||||
|
|
||||||
Append<u32_be>(data, 0); // count of h_aliases
|
Append<u32_be>(data, 0); // count of h_aliases
|
||||||
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
|
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
|
||||||
Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
|
Append<u16_be>(data, u16(Network::Domain::INET)); // h_addrtype
|
||||||
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
|
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
|
||||||
// h_addr_list:
|
// h_addr_list:
|
||||||
size_t count = vec.size();
|
size_t count = vec.size();
|
||||||
ASSERT(count <= UINT32_MAX);
|
ASSERT(count <= UINT32_MAX);
|
||||||
Append<u32_be>(data, static_cast<uint32_t>(count));
|
Append<u32_be>(data, u32(count));
|
||||||
for (const Network::AddrInfo& addrinfo : vec) {
|
for (const Network::AddrInfo& addrinfo : vec) {
|
||||||
// On the Switch, this is passed through htonl despite already being
|
// On the Switch, this is passed through htonl despite already being
|
||||||
// big-endian, so it ends up as little-endian.
|
// big-endian, so it ends up as little-endian.
|
||||||
@@ -182,7 +180,7 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
|
static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
u8 use_nsd_resolve;
|
u8 use_nsd_resolve;
|
||||||
u32 cancel_handle;
|
u32 cancel_handle;
|
||||||
@@ -205,7 +203,7 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
|
|||||||
// Prevent resolution of Nintendo servers
|
// Prevent resolution of Nintendo servers
|
||||||
if (IsBlockedHost(host)) {
|
if (IsBlockedHost(host)) {
|
||||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||||
return {0, GetAddrInfoError::AGAIN};
|
return {0, Network::GetAddrInfoError::AGAIN};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
||||||
@@ -213,10 +211,10 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
|
|||||||
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
|
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
|
||||||
const u32 data_size = u32(data.size());
|
const u32 data_size = u32(data.size());
|
||||||
ctx.WriteBuffer(data, 0);
|
ctx.WriteBuffer(data, 0);
|
||||||
return {data_size, GetAddrInfoError::SUCCESS};
|
return {data_size, Network::GetAddrInfoError::SUCCESS};
|
||||||
}
|
}
|
||||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||||
return {0, Translate(*err)};
|
return {0, *err};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
||||||
@@ -224,7 +222,7 @@ void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
@@ -244,7 +242,7 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
|
|
||||||
@@ -257,24 +255,23 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
|
static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec, std::string_view host) {
|
||||||
std::string_view host) {
|
|
||||||
// Adapted from
|
// Adapted from
|
||||||
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
|
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
|
|
||||||
for (const Network::AddrInfo& addrinfo : vec) {
|
for (const Network::AddrInfo& addrinfo : vec) {
|
||||||
// serialized addrinfo:
|
// serialized addrinfo:
|
||||||
Append<u32_be>(data, 0xBEEFCAFE); // magic
|
Append<u32_be>(data, 0xBEEFCAFE); // magic
|
||||||
Append<u32_be>(data, 0); // ai_flags
|
Append<u32_be>(data, 0); // ai_flags
|
||||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
|
Append<u32_be>(data, u32(addrinfo.family)); // ai_family
|
||||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
|
Append<u32_be>(data, u32(addrinfo.socket_type)); // ai_socktype
|
||||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
|
Append<u32_be>(data, u32(addrinfo.protocol)); // ai_protocol
|
||||||
Append<u32_be>(data, 16); // ai_addrlen
|
Append<u32_be>(data, 16); // ai_addrlen
|
||||||
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
|
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
|
||||||
|
|
||||||
// ai_addr:
|
// ai_addr:
|
||||||
Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
|
Append<u16_be>(data, u16(addrinfo.addr.family)); // sin_family
|
||||||
// On the Switch, the following fields are passed through htonl despite
|
// On the Switch, the following fields are passed through htonl despite
|
||||||
// already being big-endian, so they end up as little-endian.
|
// already being big-endian, so they end up as little-endian.
|
||||||
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
|
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
|
||||||
@@ -296,7 +293,7 @@ static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& v
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
|
static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
u8 use_nsd_resolve;
|
u8 use_nsd_resolve;
|
||||||
u32 cancel_handle;
|
u32 cancel_handle;
|
||||||
@@ -321,7 +318,7 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
|
|||||||
// Prevent resolution of Nintendo servers
|
// Prevent resolution of Nintendo servers
|
||||||
if (IsBlockedHost(host)) {
|
if (IsBlockedHost(host)) {
|
||||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||||
return {0, GetAddrInfoError::AGAIN};
|
return {0, Network::GetAddrInfoError::AGAIN};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> service = std::nullopt;
|
std::optional<std::string> service = std::nullopt;
|
||||||
@@ -331,24 +328,23 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serialized hints are also passed in a buffer, but are ignored for now.
|
// Serialized hints are also passed in a buffer, but are ignored for now.
|
||||||
|
|
||||||
auto res_v = Network::GetAddressInfo(host, service);
|
auto res_v = Network::GetAddressInfo(host, service);
|
||||||
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
|
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
|
||||||
const std::vector<u8> data = SerializeAddrInfo(*res, host);
|
const std::vector<u8> data = SerializeAddrInfo(*res, host);
|
||||||
const u32 data_size = u32(data.size());
|
const u32 data_size = u32(data.size());
|
||||||
ctx.WriteBuffer(data, 0);
|
ctx.WriteBuffer(data, 0);
|
||||||
return {data_size, GetAddrInfoError::SUCCESS};
|
return {data_size, Network::GetAddrInfoError::SUCCESS};
|
||||||
}
|
}
|
||||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||||
return {0, Translate(*err)};
|
return {0, *err};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
||||||
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
|
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
|
||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
GetAddrInfoError gai_error;
|
Network::GetAddrInfoError gai_error;
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
@@ -364,7 +360,7 @@ void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
GetAddrInfoError gai_errno;
|
Network::GetAddrInfoError gai_errno;
|
||||||
};
|
};
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto input = rp.PopRaw<InputParameters>();
|
auto input = rp.PopRaw<InputParameters>();
|
||||||
@@ -382,9 +378,9 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
GetAddrInfoError gai_error;
|
Network::GetAddrInfoError gai_error;
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0x10);
|
static_assert(sizeof(OutputParameters) == 0x10);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "core/internal_network/socket_types.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
@@ -15,242 +16,6 @@ class System;
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
enum class Errno : u32 {
|
|
||||||
SUCCESS = 0,
|
|
||||||
BADF = 9,
|
|
||||||
AGAIN = 11,
|
|
||||||
INVAL = 22,
|
|
||||||
MFILE = 24,
|
|
||||||
PIPE = 32,
|
|
||||||
MSGSIZE = 90,
|
|
||||||
CONNABORTED = 103,
|
|
||||||
CONNRESET = 104,
|
|
||||||
NOTCONN = 107,
|
|
||||||
TIMEDOUT = 110,
|
|
||||||
CONNREFUSED = 111,
|
|
||||||
INPROGRESS = 115,
|
|
||||||
ISCONN = 106,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class GetAddrInfoError : s32 {
|
|
||||||
SUCCESS = 0,
|
|
||||||
ADDRFAMILY = 1,
|
|
||||||
AGAIN = 2,
|
|
||||||
BADFLAGS = 3,
|
|
||||||
FAIL = 4,
|
|
||||||
FAMILY = 5,
|
|
||||||
MEMORY = 6,
|
|
||||||
NODATA = 7,
|
|
||||||
NONAME = 8,
|
|
||||||
SERVICE = 9,
|
|
||||||
SOCKTYPE = 10,
|
|
||||||
SYSTEM = 11,
|
|
||||||
BADHINTS = 12,
|
|
||||||
PROTOCOL = 13,
|
|
||||||
OVERFLOW_ = 14, // avoid name collision with Windows macro
|
|
||||||
OTHER = 15,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Domain : u32 {
|
|
||||||
Unspecified = 0,
|
|
||||||
INET = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Type : u32 {
|
|
||||||
Unspecified = 0,
|
|
||||||
STREAM = 1,
|
|
||||||
DGRAM = 2,
|
|
||||||
RAW = 3,
|
|
||||||
SEQPACKET = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Protocol : u32 {
|
|
||||||
IP = 0,
|
|
||||||
ICMP = 1,
|
|
||||||
TCP = 6,
|
|
||||||
UDP = 17,
|
|
||||||
//
|
|
||||||
IPV6 = 41,
|
|
||||||
RAW = 255,
|
|
||||||
//
|
|
||||||
HOPOPTS = 0,
|
|
||||||
IGMP = 2,
|
|
||||||
GGP = 3,
|
|
||||||
IPV4 = 4,
|
|
||||||
ST = 7,
|
|
||||||
EGP = 8,
|
|
||||||
PIGP = 9,
|
|
||||||
RCCMON = 10,
|
|
||||||
NVPII = 11,
|
|
||||||
PUP = 12,
|
|
||||||
ARGUS = 13,
|
|
||||||
EMCON = 14,
|
|
||||||
XNET = 15,
|
|
||||||
CHAOS = 16,
|
|
||||||
MUX = 18,
|
|
||||||
MEAS = 19,
|
|
||||||
HMP = 20,
|
|
||||||
PRM = 21,
|
|
||||||
IDP = 22,
|
|
||||||
TRUNK1 = 23,
|
|
||||||
TRUNK2 = 24,
|
|
||||||
LEAF1 = 25,
|
|
||||||
LEAF2 = 26,
|
|
||||||
RDP = 27,
|
|
||||||
IRTP = 28,
|
|
||||||
TP = 29,
|
|
||||||
BLT = 30,
|
|
||||||
NSP = 31,
|
|
||||||
INP = 32,
|
|
||||||
DCCP = 33,
|
|
||||||
//3PC = 34,
|
|
||||||
IDPR = 35,
|
|
||||||
XTP = 36,
|
|
||||||
DDP = 37,
|
|
||||||
CMTP = 38,
|
|
||||||
TPXX = 39,
|
|
||||||
IL = 40,
|
|
||||||
SDRP = 42,
|
|
||||||
ROUTING = 43,
|
|
||||||
FRAGMENT = 44,
|
|
||||||
IDRP = 45,
|
|
||||||
RSVP = 46,
|
|
||||||
GRE = 47,
|
|
||||||
MHRP = 48,
|
|
||||||
BHA = 49,
|
|
||||||
ESP = 50,
|
|
||||||
AH = 51,
|
|
||||||
INLSP = 52,
|
|
||||||
SWIPE = 53,
|
|
||||||
NHRP = 54,
|
|
||||||
MOBILE = 55,
|
|
||||||
TLSP = 56,
|
|
||||||
SKIP = 57,
|
|
||||||
ICMPV6 = 58,
|
|
||||||
NONE = 59,
|
|
||||||
DSTOPTS = 60,
|
|
||||||
AHIP = 61,
|
|
||||||
CFTP = 62,
|
|
||||||
HELLO = 63,
|
|
||||||
SATEXPAK = 64,
|
|
||||||
KRYPTOLAN = 65,
|
|
||||||
RVD = 66,
|
|
||||||
IPPC = 67,
|
|
||||||
ADFS = 68,
|
|
||||||
SATMON = 69,
|
|
||||||
VISA = 70,
|
|
||||||
IPCV = 71,
|
|
||||||
CPNX = 72,
|
|
||||||
CPHB = 73,
|
|
||||||
WSN = 74,
|
|
||||||
PVP = 75,
|
|
||||||
BRSATMON = 76,
|
|
||||||
ND = 77,
|
|
||||||
WBMON = 78,
|
|
||||||
WBEXPAK = 79,
|
|
||||||
EON = 80,
|
|
||||||
VMTP = 81,
|
|
||||||
SVMTP = 82,
|
|
||||||
VINES = 83,
|
|
||||||
TTP = 84,
|
|
||||||
IGP = 85,
|
|
||||||
DGP = 86,
|
|
||||||
TCF = 87,
|
|
||||||
IGRP = 88,
|
|
||||||
OSPFIGP = 89,
|
|
||||||
SRPC = 90,
|
|
||||||
LARP = 91,
|
|
||||||
MTP = 92,
|
|
||||||
AX25 = 93,
|
|
||||||
IPEIP = 94,
|
|
||||||
MICP = 95,
|
|
||||||
SCCSP = 96,
|
|
||||||
ETHERIP = 97,
|
|
||||||
ENCAP = 98,
|
|
||||||
APES = 99,
|
|
||||||
GMTP = 100,
|
|
||||||
IPCOMP = 108,
|
|
||||||
SCTP = 132,
|
|
||||||
MH = 135,
|
|
||||||
UDPLITE = 136,
|
|
||||||
HIP = 139,
|
|
||||||
SHIM6 = 140,
|
|
||||||
PIM = 103,
|
|
||||||
CARP = 112,
|
|
||||||
PGM = 113,
|
|
||||||
MPLS = 137,
|
|
||||||
PFSYNC = 240,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class SocketLevel : u32 {
|
|
||||||
IP = 0,
|
|
||||||
TCP = 6,
|
|
||||||
SOCKET = 0xffff, // i.e. SOL_SOCKET
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class OptName : u32 {
|
|
||||||
REUSEADDR = 0x4,
|
|
||||||
KEEPALIVE = 0x8,
|
|
||||||
BROADCAST = 0x20,
|
|
||||||
LINGER = 0x80,
|
|
||||||
SNDBUF = 0x1001,
|
|
||||||
RCVBUF = 0x1002,
|
|
||||||
SNDTIMEO = 0x1005,
|
|
||||||
RCVTIMEO = 0x1006,
|
|
||||||
ERROR_ = 0x1007, // avoid name collision with Windows macro
|
|
||||||
NOSIGPIPE = 0x800, // at least according to libnx
|
|
||||||
ACCEPTFILTER = 0x1000,
|
|
||||||
BINTIME = 0x2000,
|
|
||||||
NO_OFFLOAD = 0x4000,
|
|
||||||
NO_DDP = 0x8000,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ShutdownHow : s32 {
|
|
||||||
RD = 0,
|
|
||||||
WR = 1,
|
|
||||||
RDWR = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class FcntlCmd : s32 {
|
|
||||||
GETFL = 3,
|
|
||||||
SETFL = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SockAddrIn {
|
|
||||||
u8 len;
|
|
||||||
u8 family;
|
|
||||||
u16 portno;
|
|
||||||
std::array<u8, 4> ip;
|
|
||||||
std::array<u8, 248> zeroes;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(SockAddrIn) == 0x100);
|
|
||||||
|
|
||||||
enum class PollEvents : u16 {
|
|
||||||
// Using Pascal case because IN is a macro on Windows.
|
|
||||||
In = 1 << 0,
|
|
||||||
Pri = 1 << 1,
|
|
||||||
Out = 1 << 2,
|
|
||||||
Err = 1 << 3,
|
|
||||||
Hup = 1 << 4,
|
|
||||||
Nval = 1 << 5,
|
|
||||||
RdNorm = 1 << 6,
|
|
||||||
RdBand = 1 << 7,
|
|
||||||
WrBand = 1 << 8,
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
|
||||||
|
|
||||||
struct PollFD {
|
|
||||||
s32 fd;
|
|
||||||
PollEvents events;
|
|
||||||
PollEvents revents;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Linger {
|
|
||||||
u32 onoff;
|
|
||||||
u32 linger;
|
|
||||||
};
|
|
||||||
|
|
||||||
void LoopProcess(Core::System& system);
|
void LoopProcess(Core::System& system);
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
@@ -15,388 +15,42 @@
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
Errno Translate(Network::Errno value) {
|
const char* Translate(Network::GetAddrInfoError error) {
|
||||||
switch (value) {
|
|
||||||
case Network::Errno::SUCCESS:
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
case Network::Errno::BADF:
|
|
||||||
return Errno::BADF;
|
|
||||||
case Network::Errno::AGAIN:
|
|
||||||
return Errno::AGAIN;
|
|
||||||
case Network::Errno::INVAL:
|
|
||||||
return Errno::INVAL;
|
|
||||||
case Network::Errno::MFILE:
|
|
||||||
return Errno::MFILE;
|
|
||||||
case Network::Errno::PIPE:
|
|
||||||
return Errno::PIPE;
|
|
||||||
case Network::Errno::CONNREFUSED:
|
|
||||||
return Errno::CONNREFUSED;
|
|
||||||
case Network::Errno::NOTCONN:
|
|
||||||
return Errno::NOTCONN;
|
|
||||||
case Network::Errno::TIMEDOUT:
|
|
||||||
return Errno::TIMEDOUT;
|
|
||||||
case Network::Errno::CONNABORTED:
|
|
||||||
return Errno::CONNABORTED;
|
|
||||||
case Network::Errno::CONNRESET:
|
|
||||||
return Errno::CONNRESET;
|
|
||||||
case Network::Errno::INPROGRESS:
|
|
||||||
return Errno::INPROGRESS;
|
|
||||||
case Network::Errno::ISCONN:
|
|
||||||
return Errno::ISCONN;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
|
|
||||||
return {value.first, Translate(value.second)};
|
|
||||||
}
|
|
||||||
|
|
||||||
GetAddrInfoError Translate(Network::GetAddrInfoError error) {
|
|
||||||
switch (error) {
|
|
||||||
case Network::GetAddrInfoError::SUCCESS:
|
|
||||||
return GetAddrInfoError::SUCCESS;
|
|
||||||
case Network::GetAddrInfoError::ADDRFAMILY:
|
|
||||||
return GetAddrInfoError::ADDRFAMILY;
|
|
||||||
case Network::GetAddrInfoError::AGAIN:
|
|
||||||
return GetAddrInfoError::AGAIN;
|
|
||||||
case Network::GetAddrInfoError::BADFLAGS:
|
|
||||||
return GetAddrInfoError::BADFLAGS;
|
|
||||||
case Network::GetAddrInfoError::FAIL:
|
|
||||||
return GetAddrInfoError::FAIL;
|
|
||||||
case Network::GetAddrInfoError::FAMILY:
|
|
||||||
return GetAddrInfoError::FAMILY;
|
|
||||||
case Network::GetAddrInfoError::MEMORY:
|
|
||||||
return GetAddrInfoError::MEMORY;
|
|
||||||
case Network::GetAddrInfoError::NODATA:
|
|
||||||
return GetAddrInfoError::NODATA;
|
|
||||||
case Network::GetAddrInfoError::NONAME:
|
|
||||||
return GetAddrInfoError::NONAME;
|
|
||||||
case Network::GetAddrInfoError::SERVICE:
|
|
||||||
return GetAddrInfoError::SERVICE;
|
|
||||||
case Network::GetAddrInfoError::SOCKTYPE:
|
|
||||||
return GetAddrInfoError::SOCKTYPE;
|
|
||||||
case Network::GetAddrInfoError::SYSTEM:
|
|
||||||
return GetAddrInfoError::SYSTEM;
|
|
||||||
case Network::GetAddrInfoError::BADHINTS:
|
|
||||||
return GetAddrInfoError::BADHINTS;
|
|
||||||
case Network::GetAddrInfoError::PROTOCOL:
|
|
||||||
return GetAddrInfoError::PROTOCOL;
|
|
||||||
case Network::GetAddrInfoError::OVERFLOW_:
|
|
||||||
return GetAddrInfoError::OVERFLOW_;
|
|
||||||
case Network::GetAddrInfoError::OTHER:
|
|
||||||
return GetAddrInfoError::OTHER;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error);
|
|
||||||
return GetAddrInfoError::OTHER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* Translate(GetAddrInfoError error) {
|
|
||||||
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
|
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case GetAddrInfoError::SUCCESS:
|
case Network::GetAddrInfoError::SUCCESS:
|
||||||
return "Success";
|
return "Success";
|
||||||
case GetAddrInfoError::ADDRFAMILY:
|
case Network::GetAddrInfoError::ADDRFAMILY:
|
||||||
return "Address family for hostname not supported";
|
return "Address family for hostname not supported";
|
||||||
case GetAddrInfoError::AGAIN:
|
case Network::GetAddrInfoError::AGAIN:
|
||||||
return "Temporary failure in name resolution";
|
return "Temporary failure in name resolution";
|
||||||
case GetAddrInfoError::BADFLAGS:
|
case Network::GetAddrInfoError::BADFLAGS:
|
||||||
return "Invalid value for ai_flags";
|
return "Invalid value for ai_flags";
|
||||||
case GetAddrInfoError::FAIL:
|
case Network::GetAddrInfoError::FAIL:
|
||||||
return "Non-recoverable failure in name resolution";
|
return "Non-recoverable failure in name resolution";
|
||||||
case GetAddrInfoError::FAMILY:
|
case Network::GetAddrInfoError::FAMILY:
|
||||||
return "ai_family not supported";
|
return "ai_family not supported";
|
||||||
case GetAddrInfoError::MEMORY:
|
case Network::GetAddrInfoError::MEMORY:
|
||||||
return "Memory allocation failure";
|
return "Memory allocation failure";
|
||||||
case GetAddrInfoError::NODATA:
|
case Network::GetAddrInfoError::NODATA:
|
||||||
return "No address associated with hostname";
|
return "No address associated with hostname";
|
||||||
case GetAddrInfoError::NONAME:
|
case Network::GetAddrInfoError::NONAME:
|
||||||
return "hostname nor servname provided, or not known";
|
return "hostname nor servname provided, or not known";
|
||||||
case GetAddrInfoError::SERVICE:
|
case Network::GetAddrInfoError::SERVICE:
|
||||||
return "servname not supported for ai_socktype";
|
return "servname not supported for ai_socktype";
|
||||||
case GetAddrInfoError::SOCKTYPE:
|
case Network::GetAddrInfoError::SOCKTYPE:
|
||||||
return "ai_socktype not supported";
|
return "ai_socktype not supported";
|
||||||
case GetAddrInfoError::SYSTEM:
|
case Network::GetAddrInfoError::SYSTEM:
|
||||||
return "System error returned in errno";
|
return "System error returned in errno";
|
||||||
case GetAddrInfoError::BADHINTS:
|
case Network::GetAddrInfoError::BADHINTS:
|
||||||
return "Invalid value for hints";
|
return "Invalid value for hints";
|
||||||
case GetAddrInfoError::PROTOCOL:
|
case Network::GetAddrInfoError::PROTOCOL:
|
||||||
return "Resolved protocol is unknown";
|
return "Resolved protocol is unknown";
|
||||||
case GetAddrInfoError::OVERFLOW_:
|
case Network::GetAddrInfoError::OVERFLOW_:
|
||||||
return "Argument buffer overflow";
|
return "Argument buffer overflow";
|
||||||
default:
|
default:
|
||||||
return "Unknown error";
|
return "Unknown error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Domain Translate(Domain domain) {
|
|
||||||
switch (domain) {
|
|
||||||
case Domain::Unspecified:
|
|
||||||
return Network::Domain::Unspecified;
|
|
||||||
case Domain::INET:
|
|
||||||
return Network::Domain::INET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Domain Translate(Network::Domain domain) {
|
|
||||||
switch (domain) {
|
|
||||||
case Network::Domain::Unspecified:
|
|
||||||
return Domain::Unspecified;
|
|
||||||
case Network::Domain::INET:
|
|
||||||
return Domain::INET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Network::Type Translate(Type type) {
|
|
||||||
switch (type) {
|
|
||||||
case Type::Unspecified:
|
|
||||||
return Network::Type::Unspecified;
|
|
||||||
case Type::STREAM:
|
|
||||||
return Network::Type::STREAM;
|
|
||||||
case Type::DGRAM:
|
|
||||||
return Network::Type::DGRAM;
|
|
||||||
case Type::RAW:
|
|
||||||
return Network::Type::RAW;
|
|
||||||
case Type::SEQPACKET:
|
|
||||||
return Network::Type::SEQPACKET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
|
||||||
return Network::Type{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Type Translate(Network::Type type) {
|
|
||||||
switch (type) {
|
|
||||||
case Network::Type::Unspecified: return Type::Unspecified;
|
|
||||||
case Network::Type::STREAM: return Type::STREAM;
|
|
||||||
case Network::Type::DGRAM: return Type::DGRAM;
|
|
||||||
case Network::Type::RAW: return Type::RAW;
|
|
||||||
case Network::Type::SEQPACKET: return Type::SEQPACKET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
|
||||||
return Type{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RAW) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV4) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ST) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RCCMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NVPII) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ARGUS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EMCON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(XNET) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CHAOS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MUX) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MEAS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PRM) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK1) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK2) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF1) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF2) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IRTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BLT) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(INP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DCCP) \
|
|
||||||
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(3PC)*/ \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDPR) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(XTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TPXX) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IL) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SDRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RSVP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GRE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MHRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BHA) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(INLSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SWIPE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NHRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MOBILE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TLSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SKIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AHIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CFTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HELLO) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATEXPAK) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(KRYPTOLAN) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RVD) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPPC) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ADFS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VISA) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCV) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPNX) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPHB) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WSN) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PVP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BRSATMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ND) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBEXPAK) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SVMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VINES) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCF) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(OSPFIGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SRPC) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LARP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AX25) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPEIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MICP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCCSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ETHERIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ENCAP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(APES) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCOMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MH) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SHIM6) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CARP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PGM) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC)
|
|
||||||
[[nodiscard]] Network::Protocol Translate(Protocol protocol) {
|
|
||||||
switch (protocol) {
|
|
||||||
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Protocol::name: return Network::Protocol::name;
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_LIST
|
|
||||||
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[[nodiscard]] Protocol Translate(Network::Protocol protocol) {
|
|
||||||
switch (protocol) {
|
|
||||||
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Network::Protocol::name: return Protocol::name;
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_LIST
|
|
||||||
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#undef NETWORK_PROTOCOL_TRANSLATE_LIST
|
|
||||||
|
|
||||||
Network::PollEvents Translate(PollEvents flags) {
|
|
||||||
Network::PollEvents result{};
|
|
||||||
const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) {
|
|
||||||
if (True(flags & from)) {
|
|
||||||
flags &= ~from;
|
|
||||||
result |= to;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
translate(PollEvents::In, Network::PollEvents::In);
|
|
||||||
translate(PollEvents::Pri, Network::PollEvents::Pri);
|
|
||||||
translate(PollEvents::Out, Network::PollEvents::Out);
|
|
||||||
translate(PollEvents::Err, Network::PollEvents::Err);
|
|
||||||
translate(PollEvents::Hup, Network::PollEvents::Hup);
|
|
||||||
translate(PollEvents::Nval, Network::PollEvents::Nval);
|
|
||||||
translate(PollEvents::RdNorm, Network::PollEvents::RdNorm);
|
|
||||||
translate(PollEvents::RdBand, Network::PollEvents::RdBand);
|
|
||||||
translate(PollEvents::WrBand, Network::PollEvents::WrBand);
|
|
||||||
|
|
||||||
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
PollEvents Translate(Network::PollEvents flags) {
|
|
||||||
PollEvents result{};
|
|
||||||
const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) {
|
|
||||||
if (True(flags & from)) {
|
|
||||||
flags &= ~from;
|
|
||||||
result |= to;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
translate(Network::PollEvents::In, PollEvents::In);
|
|
||||||
translate(Network::PollEvents::Pri, PollEvents::Pri);
|
|
||||||
translate(Network::PollEvents::Out, PollEvents::Out);
|
|
||||||
translate(Network::PollEvents::Err, PollEvents::Err);
|
|
||||||
translate(Network::PollEvents::Hup, PollEvents::Hup);
|
|
||||||
translate(Network::PollEvents::Nval, PollEvents::Nval);
|
|
||||||
translate(Network::PollEvents::RdNorm, PollEvents::RdNorm);
|
|
||||||
translate(Network::PollEvents::RdBand, PollEvents::RdBand);
|
|
||||||
translate(Network::PollEvents::WrBand, PollEvents::WrBand);
|
|
||||||
|
|
||||||
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Network::SockAddrIn Translate(SockAddrIn value) {
|
|
||||||
// All lengths are valid, from [0 upto 256]
|
|
||||||
return {
|
|
||||||
.family = Translate(Domain(value.family)),
|
|
||||||
.ip = value.ip,
|
|
||||||
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
SockAddrIn Translate(Network::SockAddrIn value) {
|
|
||||||
return {
|
|
||||||
.len = 16,
|
|
||||||
.family = static_cast<u8>(Translate(value.family)),
|
|
||||||
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
|
||||||
.ip = value.ip,
|
|
||||||
.zeroes = {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Network::ShutdownHow Translate(ShutdownHow how) {
|
|
||||||
switch (how) {
|
|
||||||
case ShutdownHow::RD:
|
|
||||||
return Network::ShutdownHow::RD;
|
|
||||||
case ShutdownHow::WR:
|
|
||||||
return Network::ShutdownHow::WR;
|
|
||||||
case ShutdownHow::RDWR:
|
|
||||||
return Network::ShutdownHow::RDWR;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented how={}", how);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -11,49 +14,7 @@
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
/// Translate abstract errno to guest errno
|
|
||||||
Errno Translate(Network::Errno value);
|
|
||||||
|
|
||||||
/// Translate abstract return value errno pair to guest return value errno pair
|
|
||||||
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
|
|
||||||
|
|
||||||
/// Translate abstract getaddrinfo error to guest getaddrinfo error
|
|
||||||
GetAddrInfoError Translate(Network::GetAddrInfoError value);
|
|
||||||
|
|
||||||
/// Translate guest error to string
|
/// Translate guest error to string
|
||||||
const char* Translate(GetAddrInfoError value);
|
const char* Translate(Network::GetAddrInfoError value);
|
||||||
|
|
||||||
/// Translate guest domain to abstract domain
|
|
||||||
Network::Domain Translate(Domain domain);
|
|
||||||
|
|
||||||
/// Translate abstract domain to guest domain
|
|
||||||
Domain Translate(Network::Domain domain);
|
|
||||||
|
|
||||||
/// Translate guest type to abstract type
|
|
||||||
Network::Type Translate(Type type);
|
|
||||||
|
|
||||||
/// Translate abstract type to guest type
|
|
||||||
Type Translate(Network::Type type);
|
|
||||||
|
|
||||||
/// Translate guest protocol to abstract protocol
|
|
||||||
Network::Protocol Translate(Protocol protocol);
|
|
||||||
|
|
||||||
/// Translate abstract protocol to guest protocol
|
|
||||||
Protocol Translate(Network::Protocol protocol);
|
|
||||||
|
|
||||||
/// Translate guest poll event flags to abstract poll event flags
|
|
||||||
Network::PollEvents Translate(PollEvents flags);
|
|
||||||
|
|
||||||
/// Translate abstract poll event flags to guest poll event flags
|
|
||||||
PollEvents Translate(Network::PollEvents flags);
|
|
||||||
|
|
||||||
/// Translate guest socket address structure to abstract socket address structure
|
|
||||||
Network::SockAddrIn Translate(SockAddrIn value);
|
|
||||||
|
|
||||||
/// Translate abstract socket address structure to guest socket address structure
|
|
||||||
SockAddrIn Translate(Network::SockAddrIn value);
|
|
||||||
|
|
||||||
/// Translate guest shutdown mode to abstract shutdown mode
|
|
||||||
Network::ShutdownHow Translate(ShutdownHow how);
|
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ public:
|
|||||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
|
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
|
||||||
if (bsd) {
|
if (bsd) {
|
||||||
auto err = bsd->CloseImpl(fd);
|
auto err = bsd->CloseImpl(fd);
|
||||||
if (err != Service::Sockets::Errno::SUCCESS) {
|
if (err != Network::Errno::E_SUCCESS) {
|
||||||
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
|
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ private:
|
|||||||
|
|
||||||
const bool non_block = mode == IoMode::NonBlocking;
|
const bool non_block = mode == IoMode::NonBlocking;
|
||||||
const Network::Errno error = socket->SetNonBlock(non_block);
|
const Network::Errno error = socket->SetNonBlock(non_block);
|
||||||
if (error != Network::Errno::SUCCESS) {
|
if (error != Network::Errno::E_SUCCESS) {
|
||||||
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
||||||
}
|
}
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user