mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-30 10:26:14 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4706687a11 | |||
| aa54f36783 | |||
| 844cbfaa19 | |||
| d254d24dc2 | |||
| c025e69700 | |||
| 02fef4418d | |||
| 064ab33c08 | |||
| 68e1048194 | |||
| 024f7d4918 | |||
| 96fc3378a4 | |||
| 4088d44789 | |||
| 8be2e5dfd8 | |||
| a042cedc6b | |||
| c65694e3ec | |||
| b94ec40989 | |||
| 357e2e7109 | |||
| 930989ab82 | |||
| 8cbe8d1ce9 | |||
| ff596b6e8a | |||
| 8d2d61bd8c | |||
| d0b135afb5 | |||
| 159841a6fc | |||
| 2ea449f71a | |||
| 602756e990 | |||
| 34f9c0fdec |
@@ -0,0 +1,26 @@
|
||||
From b3622608433c183ba868a1dc8dd9cf285eb3b916 Mon Sep 17 00:00:00 2001
|
||||
From: Dario Petrillo <dario.pk1@gmail.com>
|
||||
Date: Thu, 27 Nov 2025 23:12:38 +0100
|
||||
Subject: [PATCH] avoid extra memset when clearing an empty table
|
||||
|
||||
---
|
||||
include/ankerl/unordered_dense.h | 6 ++++--
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/include/ankerl/unordered_dense.h b/include/ankerl/unordered_dense.h
|
||||
index 0835342..4938212 100644
|
||||
--- a/include/ankerl/unordered_dense.h
|
||||
+++ b/include/ankerl/unordered_dense.h
|
||||
@@ -1490,8 +1490,10 @@ class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, bas
|
||||
// modifiers //////////////////////////////////////////////////////////////
|
||||
|
||||
void clear() {
|
||||
- m_values.clear();
|
||||
- clear_buckets();
|
||||
+ if (!empty()) {
|
||||
+ m_values.clear();
|
||||
+ clear_buckets();
|
||||
+ }
|
||||
}
|
||||
|
||||
auto insert(value_type const& value) -> std::pair<iterator, bool> {
|
||||
@@ -540,6 +540,7 @@ add_subdirectory(externals)
|
||||
# pass targets from externals
|
||||
# TODO(crueter): CPMUtil Propagate func?
|
||||
find_package(enet)
|
||||
find_package(unordered_dense REQUIRED)
|
||||
|
||||
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||
find_package(xbyak)
|
||||
|
||||
+13
-2
@@ -301,6 +301,17 @@
|
||||
"repo": "eden-emu/tzdb_to_nx",
|
||||
"version": "230326"
|
||||
},
|
||||
"unordered-dense": {
|
||||
"bundled": true,
|
||||
"find_args": "CONFIG",
|
||||
"hash": "d2106f6640f6bfb81755e4b8bfb64982e46ec4a507cacdb38f940123212ccf35a20b43c70c6f01d7bfb8c246d1a16f7845d8052971949cea9def1475e3fa02c8",
|
||||
"package": "unordered_dense",
|
||||
"patches": [
|
||||
"0001-avoid-memset-when-clearing-an-empty-table.patch"
|
||||
],
|
||||
"repo": "martinus/unordered_dense",
|
||||
"version": "7b55cab841"
|
||||
},
|
||||
"vulkan-headers": {
|
||||
"hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b",
|
||||
"min_version": "1.4.317",
|
||||
@@ -329,10 +340,10 @@
|
||||
"version": "vulkan-sdk-%NUMERIC_VERSION%"
|
||||
},
|
||||
"xbyak": {
|
||||
"hash": "e0aa0a603dd3ac1a39d82213df1e73c042831aec2d6b2fe382c899651eaae4ed7e8aeb6be9c41ea0097087c9d091961ab18f313cd6a9e10d621715ffd49bfe36",
|
||||
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
|
||||
"package": "xbyak",
|
||||
"repo": "herumi/xbyak",
|
||||
"version": "v7.40.1"
|
||||
"version": "v7.35.2"
|
||||
},
|
||||
"zlib": {
|
||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||
|
||||
Vendored
-2
@@ -509,8 +509,6 @@ QWidget#contentRichDialog QLabel#label_title_rich {
|
||||
}
|
||||
|
||||
QWidget#contentDialog QLabel#label_dialog {
|
||||
background: #2E2E2E;
|
||||
|
||||
padding: 20px 65px;
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -80,6 +80,7 @@ Certain other dependencies will be fetched by CPM regardless. System packages *c
|
||||
* [httplib](https://github.com/yhirose/cpp-httplib) - if `ENABLE_UPDATE_CHECKER` or `ENABLE_WEB_SERVICE` are on
|
||||
* This package is known to be broken on the AUR.
|
||||
* [cpp-jwt](https://github.com/arun11299/cpp-jwt) 1.4+ - if `ENABLE_WEB_SERVICE` is on
|
||||
* [unordered-dense](https://github.com/martinus/unordered_dense)
|
||||
|
||||
On amd64:
|
||||
|
||||
@@ -118,7 +119,7 @@ Now, install all deps:
|
||||
sudo emerge -a \
|
||||
app-arch/lz4 app-arch/zstd app-arch/unzip \
|
||||
dev-libs/libfmt dev-libs/libusb dev-libs/mcl dev-libs/sirit \
|
||||
dev-libs/boost dev-libs/openssl dev-libs/discord-rpc \
|
||||
dev-libs/unordered_dense dev-libs/boost dev-libs/openssl dev-libs/discord-rpc \
|
||||
dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \
|
||||
dev-util/vulkan-utility-libraries dev-util/glslang \
|
||||
media-gfx/renderdoc media-libs/libva media-libs/opus media-video/ffmpeg \
|
||||
@@ -259,7 +260,7 @@ brew install molten-vk
|
||||
<details>
|
||||
<summary>FreeBSD</summary>
|
||||
|
||||
As root run: `pkg install devel/cmake sdl3 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/qt6-charts devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib vulkan-headers quazip-qt6`
|
||||
As root run: `pkg install devel/cmake sdl3 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/qt6-charts devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/unordered-dense vulkan-headers quazip-qt6`
|
||||
|
||||
If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
|
||||
|
||||
@@ -293,7 +294,7 @@ pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gm
|
||||
<summary>DragonFlyBSD</summary>
|
||||
|
||||
```sh
|
||||
pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmake jq nlohmann-json enet spirv-tools sdl3 vulkan-utility-libraries vulkan-headers catch2 libfmt openssl liblz4 boost-libs cpp-httplib qt6-base qt6-charts quazip-qt6 libva-vdpau-driver libva-utils libva-intel-driver
|
||||
pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmake jq nlohmann-json enet spirv-tools sdl3 vulkan-utility-libraries vulkan-headers catch2 libfmt openssl liblz4 boost-libs cpp-httplib qt6-base qt6-charts quazip-qt6 unordered-dense libva-vdpau-driver libva-utils libva-intel-driver
|
||||
```
|
||||
|
||||
[Caveats](./Caveats.md#dragonflybsd).
|
||||
@@ -327,7 +328,7 @@ sudo pkgin install git cmake autoconf build-essential libusb-1 nasm gcc13
|
||||
|
||||
```sh
|
||||
BASE="git make autoconf libtool automake-wrapper jq patch"
|
||||
MINGW="qt6-base qt6-charts qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus libusb openssl SDL3"
|
||||
MINGW="qt6-base qt6-charts qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus libusb unordered_dense openssl SDL3"
|
||||
# Either x86_64 or clang-aarch64 (Windows on ARM)
|
||||
packages="$BASE"
|
||||
for pkg in $MINGW; do
|
||||
|
||||
+11
-27
@@ -43,7 +43,6 @@ This guide will walk you through adding a new boolean toggle setting to Eden's c
|
||||
Firstly add your desired toggle:
|
||||
|
||||
Example: `src/common/setting.h`
|
||||
|
||||
```cpp
|
||||
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
||||
```
|
||||
@@ -68,7 +67,6 @@ Common Categories:
|
||||
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
|
||||
|
||||
Example: `src/qt_common/config/shared_translation.cpp`
|
||||
|
||||
```cpp
|
||||
INSERT(Settings,
|
||||
your_setting_name,
|
||||
@@ -93,7 +91,6 @@ INSERT(Settings,
|
||||
Add where it should be in the settings.
|
||||
|
||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
|
||||
|
||||
```kts
|
||||
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
||||
```
|
||||
@@ -109,7 +106,6 @@ RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
||||
Add the toggle to the Kotlin (Android) UI
|
||||
|
||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
|
||||
|
||||
```kts
|
||||
put(
|
||||
SwitchSetting(
|
||||
@@ -127,7 +123,6 @@ put(
|
||||
Add your setting within the right category.
|
||||
|
||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
||||
|
||||
```kts
|
||||
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
||||
```
|
||||
@@ -142,7 +137,6 @@ add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
||||
Add your setting and description in the appropriate place.
|
||||
|
||||
Example: `src/android/app/src/main/res/values/strings.xml`
|
||||
|
||||
```xml
|
||||
<string name="your_setting_name">Your Setting Display Name</string>
|
||||
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
|
||||
@@ -156,7 +150,6 @@ Now the UI part is done find a place in the code for the toggle,
|
||||
And use it to your heart's desire!
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
const bool your_value = Settings::values.your_setting_name.GetValue();
|
||||
|
||||
@@ -203,31 +196,25 @@ Common advantages recap:
|
||||
|
||||
#### Accessing Debug Knobs (dev side)
|
||||
|
||||
Use the `Settings::GetDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
||||
Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
||||
|
||||
```cpp
|
||||
//cpp side
|
||||
#include "common/settings.h"
|
||||
|
||||
//To use it as a general purpose uint var:
|
||||
unsigned int debug_knobs = Settings::values.debug_knobs.GetValue();
|
||||
|
||||
// Check if bit 0 is set
|
||||
bool feature_enabled = Settings::GetDebugKnobAt(0);
|
||||
bool feature_enabled = Settings::getDebugKnobAt(0);
|
||||
|
||||
// Check if bit 15 is set
|
||||
bool another_feature = Settings::GetDebugKnobAt(15);
|
||||
bool another_feature = Settings::getDebugKnobAt(15);
|
||||
```
|
||||
|
||||
```kts
|
||||
//kotlin side
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
|
||||
//To use it as a general purpose uint var
|
||||
val debug_knobs: Int = UShortSetting.DEBUG_KNOBS.getInt()
|
||||
|
||||
// Check if bit x is set
|
||||
bool feature_enabled = Settings.GetDebugKnobAt(x); //x as integer from 0 to 15
|
||||
bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15
|
||||
```
|
||||
|
||||
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
|
||||
@@ -260,7 +247,6 @@ There are two main confusions when talking about knobs:
|
||||
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
|
||||
|
||||
Debug knobs are **zero-based**, which means:
|
||||
|
||||
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
|
||||
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
|
||||
|
||||
@@ -273,7 +259,6 @@ Whenever you're instructing tests or reporting results, be precise about whether
|
||||
|
||||
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
|
||||
Examples:
|
||||
|
||||
- **knobs=0**: no knobs enabled
|
||||
- **knobs=1**: knob0 enabled, others disabled
|
||||
- **knobs=2**: knob1 enabled, others disabled
|
||||
@@ -285,7 +270,6 @@ Examples:
|
||||
|
||||
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
||||
Examples:
|
||||
|
||||
- **knob0**: knob 0 enabled, others disabled
|
||||
- **knob1**: knob 1 enabled, others disabled
|
||||
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
||||
@@ -298,12 +282,12 @@ Examples:
|
||||
|
||||
```cpp
|
||||
void SomeFunction() {
|
||||
if (Settings::GetDebugKnobAt(0)) {
|
||||
if (Settings::getDebugKnobAt(0)) {
|
||||
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
||||
// Additional debug code here
|
||||
}
|
||||
|
||||
if (Settings::GetDebugKnobAt(1)) {
|
||||
|
||||
if (Settings::getDebugKnobAt(1)) {
|
||||
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
||||
// Different debug behavior
|
||||
}
|
||||
@@ -315,7 +299,7 @@ void SomeFunction() {
|
||||
```cpp
|
||||
bool UseOptimizedPath() {
|
||||
// Skip optimization if debug bit 2 is set for testing
|
||||
return !Settings::GetDebugKnobAt(2);
|
||||
return !Settings::getDebugKnobAt(2);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -324,13 +308,13 @@ bool UseOptimizedPath() {
|
||||
```cpp
|
||||
void ExperimentalFeature() {
|
||||
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
||||
|
||||
if (!Settings::GetDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
||||
|
||||
if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
||||
// Fallback to stable implementation
|
||||
StableImplementation();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Experimental implementation
|
||||
ExperimentalImplementation();
|
||||
}
|
||||
|
||||
@@ -308,6 +308,31 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
### unordered_dense
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Martin Leitner-Ankerl
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
### xbyak
|
||||
|
||||
|
||||
Vendored
+3
@@ -58,6 +58,9 @@ if (WIN32 AND NOT TARGET LLVM::Demangle)
|
||||
add_library(LLVM::Demangle ALIAS demangle)
|
||||
endif()
|
||||
|
||||
# unordered_dense
|
||||
AddJsonPackage(unordered-dense)
|
||||
|
||||
# httplib
|
||||
if (IOS)
|
||||
set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF)
|
||||
|
||||
@@ -220,7 +220,7 @@ object NativeLibrary {
|
||||
|
||||
external fun refreshThreadPolicies()
|
||||
|
||||
external fun GetDebugKnobAt(index: Int): Boolean
|
||||
external fun getDebugKnobAt(index: Int): Boolean
|
||||
|
||||
/**
|
||||
* Set the current speed limit to the configured turbo speed.
|
||||
|
||||
@@ -35,8 +35,8 @@ object Settings {
|
||||
fun getPlayerString(player: Int): String =
|
||||
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
||||
|
||||
fun GetDebugKnobAt(index: Int): Boolean {
|
||||
return org.yuzu.yuzu_emu.NativeLibrary.GetDebugKnobAt(index)
|
||||
fun getDebugKnobAt(index: Int): Boolean {
|
||||
return org.yuzu.yuzu_emu.NativeLibrary.getDebugKnobAt(index)
|
||||
}
|
||||
|
||||
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
||||
|
||||
+3
-2
@@ -11,7 +11,8 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
||||
RENDERER_SPEED_LIMIT("speed_limit"),
|
||||
RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"),
|
||||
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit")
|
||||
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit"),
|
||||
DEBUG_KNOBS("debug_knobs")
|
||||
;
|
||||
|
||||
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
|
||||
@@ -28,4 +29,4 @@ enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
||||
override fun getValueAsString(needsGlobal: Boolean): String = getShort(needsGlobal).toString()
|
||||
|
||||
override fun reset() = NativeConfig.setShort(key, defaultValue)
|
||||
}
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model
|
||||
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
|
||||
enum class UShortSetting(override val key: String) : AbstractIntSetting {
|
||||
DEBUG_KNOBS("debug_knobs")
|
||||
;
|
||||
|
||||
override fun getInt(needsGlobal: Boolean): Int =
|
||||
NativeConfig.getUnsignedShort(key, needsGlobal)
|
||||
|
||||
override fun setInt(value: Int) {
|
||||
if (NativeConfig.isPerGameConfigLoaded()) {
|
||||
global = false
|
||||
}
|
||||
NativeConfig.setUnsignedShort(key, value)
|
||||
}
|
||||
|
||||
override val defaultValue: Int by lazy { NativeConfig.getDefaultToString(key).toInt() }
|
||||
|
||||
override fun getValueAsString(needsGlobal: Boolean): String = getInt(needsGlobal).toString()
|
||||
|
||||
override fun reset() = NativeConfig.setUnsignedShort(key, defaultValue)
|
||||
}
|
||||
+1
-2
@@ -20,7 +20,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
||||
import org.yuzu.yuzu_emu.network.NetDataValidators
|
||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
@@ -1035,7 +1034,7 @@ abstract class SettingsItem(
|
||||
)
|
||||
put(
|
||||
SpinBoxSetting(
|
||||
UShortSetting.DEBUG_KNOBS,
|
||||
ShortSetting.DEBUG_KNOBS,
|
||||
titleId = R.string.debug_knobs,
|
||||
descriptionId = R.string.debug_knobs_description,
|
||||
valueHint = R.string.debug_knobs_hint,
|
||||
|
||||
+1
-2
@@ -25,7 +25,6 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
|
||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||
@@ -1327,7 +1326,7 @@ class SettingsFragmentPresenter(
|
||||
|
||||
add(HeaderSetting(R.string.general))
|
||||
|
||||
add(UShortSetting.DEBUG_KNOBS.key)
|
||||
add(ShortSetting.DEBUG_KNOBS.key)
|
||||
add(StringSetting.PROGRAM_ARGS.key)
|
||||
|
||||
if (!NativeConfig.isPerGameConfigLoaded()) {
|
||||
|
||||
@@ -47,6 +47,7 @@ import info.debatty.java.stringsimilarity.Jaccard
|
||||
import info.debatty.java.stringsimilarity.JaroWinkler
|
||||
import java.util.Locale
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.view.doOnNextLayout
|
||||
|
||||
class GamesFragment : Fragment() {
|
||||
private var _binding: FragmentGamesBinding? = null
|
||||
@@ -58,6 +59,7 @@ class GamesFragment : Fragment() {
|
||||
private var originalHeaderLeftMargin: Int? = null
|
||||
|
||||
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
||||
private var fallbackBottomInset: Int = 0
|
||||
private var pendingPostReloadListSettle = false
|
||||
private var pendingPostReloadListSettleGeneration = 0
|
||||
private var gameListSubmitGeneration = 0
|
||||
@@ -225,7 +227,12 @@ class GamesFragment : Fragment() {
|
||||
}
|
||||
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
||||
}
|
||||
if (savedViewType != GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||
if (savedViewType == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||
(binding.gridGames as? View)?.let { it -> ViewCompat.requestApplyInsets(it)}
|
||||
doOnNextLayout { //Carousel: important to avoid overlap issues
|
||||
(this as? CarouselRecyclerView)?.notifyLaidOut(fallbackBottomInset)
|
||||
}
|
||||
} else {
|
||||
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
||||
}
|
||||
adapter = gameAdapter
|
||||
@@ -583,6 +590,11 @@ class GamesFragment : Fragment() {
|
||||
qlaunchButton.layoutParams = mlpQLaunch
|
||||
}
|
||||
|
||||
val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
|
||||
val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||
fallbackBottomInset = bottomInset
|
||||
(binding.gridGames as? CarouselRecyclerView)?.notifyInsetsReady(bottomInset)
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,12 +80,6 @@ object NativeConfig {
|
||||
@Synchronized
|
||||
external fun setShort(key: String, value: Short)
|
||||
|
||||
@Synchronized
|
||||
external fun getUnsignedShort(key: String, needsGlobal: Boolean): Int
|
||||
|
||||
@Synchronized
|
||||
external fun setUnsignedShort(key: String, value: Int)
|
||||
|
||||
@Synchronized
|
||||
external fun getInt(key: String, needsGlobal: Boolean): Int
|
||||
|
||||
|
||||
@@ -12,16 +12,13 @@ import androidx.recyclerview.widget.PagerSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
||||
/**
|
||||
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
||||
* It manages overlapping cards, center snapping, custom drawing order,
|
||||
@@ -35,9 +32,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
|
||||
private var overlapFactor: Float = 0f
|
||||
private var overlapPx: Int = 0
|
||||
private var bottomInset: Int = 0
|
||||
private var latestWindowInsets: WindowInsetsCompat? = null
|
||||
private var cardGeometryInitialized: Boolean = false
|
||||
private var bottomInset: Int = -1
|
||||
private var overlapDecoration: OverlappingDecoration? = null
|
||||
private var pagerSnapHelper: PagerSnapHelper? = null
|
||||
private var scalingScrollListener: OnScrollListener? = null
|
||||
@@ -96,38 +91,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
|
||||
init {
|
||||
setChildrenDrawingOrderEnabled(true)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets ->
|
||||
latestWindowInsets = insets
|
||||
updateCardGeometry()
|
||||
applyCarouselPadding()
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
ViewCompat.requestApplyInsets(this)
|
||||
}
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
if (w != oldw || h != oldh) {
|
||||
updateCardGeometry()
|
||||
applyCarouselPadding()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
if (isCarouselMode) updateChildScalesAndAlpha()
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if (hasFocus) {
|
||||
ViewCompat.requestApplyInsets(this)
|
||||
post { updateCardGeometry() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun setAdapter(adapter: Adapter<*>?) {
|
||||
@@ -140,8 +103,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
super.setAdapter(adapter)
|
||||
|
||||
(adapter as? GameAdapter)?.registerAdapterDataObserver(carouselAdapterObserver)
|
||||
updateCardGeometry()
|
||||
applyCarouselPadding()
|
||||
}
|
||||
|
||||
private fun calculateCenter(width: Int, paddingStart: Int, paddingEnd: Int): Int {
|
||||
@@ -292,71 +253,40 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveBottomInset(windowInsets: WindowInsetsCompat): Int {
|
||||
val navigationBottom = if (FullscreenHelper.isFullscreenEnabled(context)) {
|
||||
0
|
||||
} else {
|
||||
windowInsets.getInsetsIgnoringVisibility(WindowInsetsCompat.Type.navigationBars()).bottom
|
||||
fun notifyInsetsReady(newBottomInset: Int) {
|
||||
if (bottomInset != newBottomInset) {
|
||||
bottomInset = newBottomInset
|
||||
}
|
||||
|
||||
if (isCarouselMode) {
|
||||
setupCarousel(true)
|
||||
} else {
|
||||
setupCarousel(false)
|
||||
}
|
||||
val gestureInsets = windowInsets.getInsetsIgnoringVisibility(
|
||||
WindowInsetsCompat.Type.systemGestures()
|
||||
)
|
||||
val cutoutInsets = windowInsets.getInsetsIgnoringVisibility(
|
||||
WindowInsetsCompat.Type.displayCutout()
|
||||
)
|
||||
return maxOf(navigationBottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||
}
|
||||
|
||||
private fun updateCardGeometry() {
|
||||
if (!isCarouselMode || height <= 0) return
|
||||
fun notifyLaidOut(fallBackBottomInset: Int) {
|
||||
if (bottomInset < 0) bottomInset = fallBackBottomInset
|
||||
var gameAdapter = adapter as? GameAdapter ?: return
|
||||
var newCardSize = cardSize(bottomInset)
|
||||
if (gameAdapter.cardSize != newCardSize) {
|
||||
gameAdapter.setCardSize(newCardSize)
|
||||
}
|
||||
|
||||
val gameAdapter = adapter as? GameAdapter ?: return
|
||||
val windowInsets = latestWindowInsets ?: ViewCompat.getRootWindowInsets(this) ?: return
|
||||
if (isCarouselMode) {
|
||||
setupCarousel(true)
|
||||
}
|
||||
}
|
||||
|
||||
if (cardGeometryInitialized && !hasWindowFocus()) return
|
||||
|
||||
val newBottomInset = resolveBottomInset(windowInsets).coerceIn(0, height)
|
||||
fun cardSize(bottomInset: Int): Int {
|
||||
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
||||
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
val screenWidth = resources.displayMetrics.widthPixels.toFloat()
|
||||
val screenHeight = resources.displayMetrics.heightPixels.toFloat()
|
||||
val aspectFactor = ((screenWidth / screenHeight) / (20f / 9f))
|
||||
.pow(0.75f)
|
||||
.coerceIn(0.5f, 1f)
|
||||
val newCardSize = minOf(
|
||||
(height * userFactor).toInt(),
|
||||
height - newBottomInset,
|
||||
(height * aspectFactor).toInt()
|
||||
)
|
||||
if (newCardSize <= 0) return
|
||||
val insetChanged = bottomInset != newBottomInset
|
||||
val cardSizeChanged = gameAdapter.cardSize != newCardSize
|
||||
|
||||
bottomInset = newBottomInset
|
||||
cardGeometryInitialized = true
|
||||
|
||||
if (cardSizeChanged) gameAdapter.setCardSize(newCardSize)
|
||||
if (insetChanged || cardSizeChanged) setupCarousel(true)
|
||||
}
|
||||
|
||||
private fun applyCarouselPadding() {
|
||||
if (!isCarouselMode) return
|
||||
|
||||
val gameAdapter = adapter as? GameAdapter ?: return
|
||||
val cardSize = gameAdapter.cardSize
|
||||
if (cardSize <= 0 || bottomInset < 0) return
|
||||
|
||||
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0)
|
||||
val sidePadding = (width - cardSize) / 2
|
||||
if (paddingLeft != sidePadding || paddingTop != topPadding ||
|
||||
paddingRight != sidePadding || paddingBottom != 0
|
||||
) {
|
||||
setPadding(sidePadding, topPadding, sidePadding, 0)
|
||||
}
|
||||
clipToPadding = false
|
||||
val scaledHeight = height * userFactor
|
||||
val availableHeight = height - bottomInset
|
||||
return minOf(scaledHeight.toInt(), availableHeight.toInt())
|
||||
}
|
||||
|
||||
fun setupCarousel(enabled: Boolean) {
|
||||
@@ -385,6 +315,9 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
internalFlingMultiplier
|
||||
).coerceIn(1f, 5f)
|
||||
|
||||
// Detach SnapHelper during setup
|
||||
pagerSnapHelper?.attachToRecyclerView(null)
|
||||
|
||||
// Add overlap decoration if not present
|
||||
if (overlapDecoration == null) {
|
||||
overlapDecoration = OverlappingDecoration(overlapPx)
|
||||
@@ -402,7 +335,12 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
addOnScrollListener(scalingScrollListener!!)
|
||||
}
|
||||
|
||||
applyCarouselPadding()
|
||||
if (cardSize > 0) {
|
||||
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0) // Center vertically
|
||||
val sidePadding = (width - cardSize) / 2 // Center first/last card
|
||||
setPadding(sidePadding, topPadding, sidePadding, 0)
|
||||
clipToPadding = false
|
||||
}
|
||||
|
||||
if (pagerSnapHelper == null) {
|
||||
pagerSnapHelper = CenterPagerSnapHelper()
|
||||
@@ -424,7 +362,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
}
|
||||
savedItemAnimator = null
|
||||
}
|
||||
cardGeometryInitialized = false
|
||||
useCustomDrawingOrder = false
|
||||
// Reset padding and fling
|
||||
setPadding(0, 0, 0, 0)
|
||||
|
||||
@@ -20,7 +20,7 @@ struct RomMetadata {
|
||||
std::vector<u8> icon;
|
||||
bool isHomebrew;
|
||||
};
|
||||
static boost::unordered::unordered_flat_map<std::string, RomMetadata> m_rom_metadata_cache;
|
||||
static ankerl::unordered_dense::map<std::string, RomMetadata> m_rom_metadata_cache;
|
||||
|
||||
static RomMetadata CacheRomMetadata(const std::string& path) {
|
||||
auto& instance = EmulationSession::GetInstance();
|
||||
|
||||
@@ -76,7 +76,6 @@ extern "C" {
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/frontend/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/frontend/applets.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
@@ -312,23 +311,11 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
||||
ConfigureFilesystemProvider(filepath);
|
||||
|
||||
// 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{
|
||||
.program_id = previous_program_id,
|
||||
.applet_id = static_cast<Service::AM::AppletId>(m_applet_id),
|
||||
.launch_type = frontend_initiated ? Service::AM::LaunchType::FrontendInitiated
|
||||
: Service::AM::LaunchType::ApplicationInitiated,
|
||||
.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);
|
||||
@@ -341,12 +328,8 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
||||
m_system.GetCpuManager().OnGpuReady();
|
||||
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
|
||||
m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
|
||||
m_next_program_id = m_system.GetApplicationProcessProgramID();
|
||||
m_next_program_index = program_index_;
|
||||
EmulationSession::GetInstance().HaltEmulation();
|
||||
});
|
||||
@@ -424,58 +407,20 @@ void EmulationSession::RunEmulation() {
|
||||
}
|
||||
|
||||
while (true) {
|
||||
std::optional<u64> reload_title;
|
||||
{
|
||||
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
||||
if (m_cv.wait_for(lock, std::chrono::milliseconds(800), [&]() {
|
||||
return !m_is_running || m_pending_shader_cache_title.has_value();
|
||||
})) {
|
||||
if (!m_is_running) {
|
||||
break;
|
||||
}
|
||||
reload_title = std::exchange(m_pending_shader_cache_title, std::nullopt);
|
||||
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
||||
[&]() { return !m_is_running; })) {
|
||||
// Emulation halted.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reload_title.has_value())
|
||||
ReloadDiskShaderCache(*reload_title);
|
||||
}
|
||||
|
||||
// Reset current applet ID.
|
||||
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() {
|
||||
return m_software_keyboard;
|
||||
}
|
||||
@@ -1300,8 +1245,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_refreshThreadPolicies(JNIEnv* env, jo
|
||||
Common::RefreshThreadPolicies();
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDebugKnobAt(JNIEnv* env, jobject jobj, jint index) {
|
||||
return static_cast<jboolean>(Settings::GetDebugKnobAt(static_cast<u8>(index)));
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_getDebugKnobAt(JNIEnv* env, jobject jobj, jint index) {
|
||||
return static_cast<jboolean>(Settings::getDebugKnobAt(static_cast<u8>(index)));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <android/native_window_jni.h>
|
||||
#include "common/android/applets/software_keyboard.h"
|
||||
#include "core/core.h"
|
||||
@@ -46,7 +44,6 @@ public:
|
||||
void HaltEmulation();
|
||||
void RunEmulation();
|
||||
void ShutdownEmulation();
|
||||
void RequestDiskShaderCacheReload(u64 program_id);
|
||||
|
||||
const Core::PerfStatsResults& PerfStats();
|
||||
int ShadersBuilding();
|
||||
@@ -68,7 +65,6 @@ private:
|
||||
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
||||
static void OnEmulationStopped(Core::SystemResultStatus result);
|
||||
static void ChangeProgram(std::size_t program_index);
|
||||
void ReloadDiskShaderCache(u64 program_id);
|
||||
|
||||
private:
|
||||
// Window management
|
||||
@@ -87,7 +83,6 @@ private:
|
||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||
int m_applet_id{1};
|
||||
std::optional<u64> m_pending_shader_cache_title;
|
||||
|
||||
// GPU driver parameters
|
||||
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
||||
@@ -98,5 +93,4 @@ private:
|
||||
|
||||
// Program index for next boot
|
||||
std::atomic<s32> m_next_program_index = -1;
|
||||
std::atomic<u64> m_next_program_id = 0;
|
||||
};
|
||||
|
||||
@@ -130,25 +130,6 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject ob
|
||||
setting->SetValue(value);
|
||||
}
|
||||
|
||||
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getUnsignedShort(JNIEnv* env, jobject obj,
|
||||
jstring jkey,
|
||||
jboolean needGlobal) {
|
||||
auto setting = getSetting<u16>(env, jkey);
|
||||
if (setting == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
return static_cast<jint>(setting->GetValue(static_cast<bool>(needGlobal)));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setUnsignedShort(JNIEnv* env, jobject obj,
|
||||
jstring jkey, jint value) {
|
||||
auto setting = getSetting<u16>(env, jkey);
|
||||
if (setting == nullptr) {
|
||||
return;
|
||||
}
|
||||
setting->SetValue(static_cast<u16>(value));
|
||||
}
|
||||
|
||||
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
|
||||
jboolean needGlobal) {
|
||||
auto setting = getSetting<int>(env, jkey);
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "input_common/drivers/virtual_gamepad.h"
|
||||
#include "native.h"
|
||||
|
||||
boost::unordered::unordered_flat_map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
|
||||
ankerl::unordered_dense::map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
|
||||
|
||||
bool IsHandheldOnly() {
|
||||
const auto npad_style_set =
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,12 +7,13 @@
|
||||
namespace AudioCore::ADSP::OpusDecoder {
|
||||
|
||||
namespace {
|
||||
constexpr u32 OpusStreamCountMax = 255;
|
||||
bool IsValidChannelCount(u32 channel_count) {
|
||||
return channel_count == 1 || channel_count == 2;
|
||||
}
|
||||
|
||||
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
|
||||
return total_stream_count > 0 && total_stream_count <= OpusStreamCountMax &&
|
||||
static_cast<s32>(stereo_stream_count) >= 0 &&
|
||||
stereo_stream_count <= total_stream_count;
|
||||
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
|
||||
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -108,7 +105,7 @@ Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 chan
|
||||
shared_memory.host_send_data[4] = total_stream_count;
|
||||
shared_memory.host_send_data[5] = stereo_stream_count;
|
||||
|
||||
ASSERT(channel_count <= shared_memory.channel_mapping.size());
|
||||
ASSERT(channel_count <= MaxChannels);
|
||||
std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
|
||||
|
||||
opus_decoder.Send(ADSP::Direction::DSP,
|
||||
|
||||
@@ -19,23 +19,6 @@
|
||||
namespace AudioCore::Sink {
|
||||
|
||||
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) {
|
||||
int device_count = 0;
|
||||
SDL_AudioDeviceID* devices = capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||
@@ -221,14 +204,20 @@ private:
|
||||
};
|
||||
|
||||
SDLSink::SDLSink(std::string_view target_device_name) {
|
||||
if (InitializeAudio()) {
|
||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||
output_device = target_device_name;
|
||||
} else {
|
||||
output_device.clear();
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
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;
|
||||
@@ -276,10 +265,15 @@ void SDLSink::SetSystemVolume(f32 volume) {
|
||||
}
|
||||
|
||||
std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
||||
if (!InitializeAudio())
|
||||
return {}; //no devices
|
||||
|
||||
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;
|
||||
SDL_AudioDeviceID* devices =
|
||||
capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||
@@ -310,8 +304,13 @@ bool IsSDLSuitable() {
|
||||
return false;
|
||||
#else
|
||||
// Check SDL can init
|
||||
if (!InitializeAudio()!
|
||||
return false;
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
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.
|
||||
|
||||
|
||||
@@ -241,7 +241,7 @@ if (lz4_ADDED)
|
||||
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
||||
endif()
|
||||
|
||||
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads)
|
||||
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads unordered_dense::unordered_dense)
|
||||
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd)
|
||||
|
||||
# Please refer to src/common/demangle.cpp
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/fs.h"
|
||||
@@ -197,8 +196,8 @@ private:
|
||||
SetLegacyPathImpl(legacy_path, new_path);
|
||||
}
|
||||
|
||||
boost::unordered::unordered_flat_map<EdenPath, fs::path> eden_paths;
|
||||
boost::unordered::unordered_flat_map<EmuPath, fs::path> legacy_paths;
|
||||
ankerl::unordered_dense::map<EdenPath, fs::path> eden_paths;
|
||||
ankerl::unordered_dense::map<EmuPath, fs::path> legacy_paths;
|
||||
};
|
||||
|
||||
bool ValidatePath(const fs::path& path) {
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <iterator>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/icl/separate_interval_set.hpp>
|
||||
#include <windows.h>
|
||||
#include "common/dynamic_library.h"
|
||||
@@ -392,7 +391,7 @@ private:
|
||||
|
||||
std::mutex placeholder_mutex; ///< Mutex for placeholders
|
||||
boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
|
||||
boost::unordered::unordered_flat_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
||||
ankerl::unordered_dense::map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
||||
};
|
||||
|
||||
#elif defined(__OPENORBIS__) || defined(__managarm__)
|
||||
|
||||
+2
-3
@@ -9,8 +9,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "common/logging.h"
|
||||
@@ -413,7 +412,7 @@ public:
|
||||
namespace Impl {
|
||||
|
||||
template <typename InputDeviceType>
|
||||
using FactoryListType = boost::unordered::unordered_flat_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
|
||||
using FactoryListType = ankerl::unordered_dense::map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
|
||||
|
||||
template <typename InputDeviceType>
|
||||
struct FactoryList {
|
||||
|
||||
+15
-27
@@ -329,7 +329,7 @@ struct LogcatBackend : public Backend {
|
||||
}
|
||||
}();
|
||||
auto const df = GetDirectFormatArgs(entry);
|
||||
__android_log_print(android_log_priority, "YuzuNative", "%s %s:%u:%s: %s", df.class_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||
__android_log_print(android_log_priority, "YuzuNative", CCB_PRINTF_FMT, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||
}
|
||||
void Flush() noexcept override {}
|
||||
};
|
||||
@@ -428,33 +428,21 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, u
|
||||
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
||||
char buffer[BUFSIZ];
|
||||
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
||||
Entry e{
|
||||
.message = nullptr,
|
||||
.message_len = 0,
|
||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
.filename = TrimSourcePath(filename),
|
||||
.function = function,
|
||||
.line_num = line_num,
|
||||
};
|
||||
if (result.size <= sizeof(buffer) - 1) {
|
||||
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
||||
e.message = buffer;
|
||||
e.message_len = (std::min)(result.size, sizeof(buffer) - 1);
|
||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||
backend.Write(e);
|
||||
if (flush) backend.Flush();
|
||||
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||
backend.Write(Entry{
|
||||
.message = buffer,
|
||||
.message_len = (std::min)(result.size, sizeof(buffer) - 1),
|
||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
.filename = TrimSourcePath(filename),
|
||||
.function = function,
|
||||
.line_num = line_num,
|
||||
});
|
||||
} else {
|
||||
std::string s = fmt::vformat(format, args);
|
||||
e.message = s.c_str();
|
||||
e.message_len = s.size();
|
||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||
backend.Write(e);
|
||||
if (flush) backend.Flush();
|
||||
});
|
||||
}
|
||||
if (flush)
|
||||
backend.Flush();
|
||||
});
|
||||
}
|
||||
}
|
||||
} // namespace Common::Log
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
|
||||
namespace Common::Net {
|
||||
|
||||
struct Asset {
|
||||
typedef struct {
|
||||
std::string name;
|
||||
std::string url;
|
||||
std::string path;
|
||||
std::string filename;
|
||||
};
|
||||
} Asset;
|
||||
|
||||
struct Release {
|
||||
typedef struct Release {
|
||||
std::string title;
|
||||
std::string body;
|
||||
std::string tag;
|
||||
@@ -39,7 +39,7 @@ struct Release {
|
||||
static std::optional<Release> FromJson(const std::string_view& json, const std::string &host, const std::string& repo);
|
||||
static std::vector<Release> ListFromJson(const nlohmann::json &json, const std::string &host, const std::string &repo);
|
||||
static std::vector<Release> ListFromJson(const std::string_view &json, const std::string &host, const std::string &repo);
|
||||
};
|
||||
} Release;
|
||||
|
||||
// Make a request via httplib, and return the response body if applicable.
|
||||
std::optional<std::string> MakeRequest(const std::string &url, const std::string &path);
|
||||
|
||||
@@ -8,15 +8,14 @@
|
||||
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// A string-based key-value container supporting serializing to and deserializing from a string
|
||||
class ParamPackage {
|
||||
public:
|
||||
using DataType = boost::unordered::unordered_flat_map<std::string, std::string>;
|
||||
using DataType = ankerl::unordered_dense::map<std::string, std::string>;
|
||||
|
||||
ParamPackage() = default;
|
||||
explicit ParamPackage(const std::string& serialized);
|
||||
|
||||
@@ -126,9 +126,9 @@ void LogSettings() {
|
||||
setting->UsingGlobal() ? '-' : 'C', TranslateCategory(category),
|
||||
setting->GetLabel());
|
||||
if (is_default)
|
||||
settings_list.push_back(fmt::format("{}: {}", name, setting->Canonicalize()));
|
||||
settings_list.push_back(fmt::format("{}: {}\n", name, setting->Canonicalize()));
|
||||
else
|
||||
settings_list.push_front(fmt::format("{}: {}", name, setting->Canonicalize()));
|
||||
settings_list.push_front(fmt::format("{}: {}\n", name, setting->Canonicalize()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,7 +146,7 @@ void LogSettings() {
|
||||
#undef LOG_PATH
|
||||
}
|
||||
|
||||
bool GetDebugKnobAt(u8 i) {
|
||||
bool getDebugKnobAt(u8 i) {
|
||||
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -904,7 +904,7 @@ struct Values {
|
||||
0,
|
||||
65535,
|
||||
"debug_knobs",
|
||||
Category::System,
|
||||
Category::Debugging,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
true};
|
||||
@@ -947,7 +947,7 @@ constexpr u32 MAX_FRAME_GEN_MULTIPLIER = 4;
|
||||
|
||||
[[nodiscard]] size_t FrameGenMaxGenerations();
|
||||
|
||||
bool GetDebugKnobAt(u8 i);
|
||||
bool getDebugKnobAt(u8 i);
|
||||
|
||||
void UpdateGPUAccuracy();
|
||||
bool IsGPULevelHigh();
|
||||
|
||||
@@ -7,4 +7,6 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION 1
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION 1
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION 1
|
||||
#define STBI_ONLY_JPEG 1
|
||||
|
||||
#include "common/stb.h"
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#pragma once
|
||||
|
||||
#define STBI_ONLY_JPEG 1
|
||||
#define STBI_WRITE_NO_STDIO 1
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
@@ -22,11 +22,10 @@
|
||||
#endif
|
||||
|
||||
// You must ensure this matches with src/common/x64/xbyak.h on root dir
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#define XBYAK_STD_UNORDERED_SET boost::unordered::unordered_flat_set
|
||||
#define XBYAK_STD_UNORDERED_MAP boost::unordered::unordered_flat_map
|
||||
#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
|
||||
#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
|
||||
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <dynarmic/interface/A64/a64.h>
|
||||
#include <dynarmic/interface/code_page.h>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
#include <oaknut/code_block.hpp>
|
||||
#include <oaknut/oaknut.hpp>
|
||||
@@ -46,7 +46,7 @@ enum class PatchMode : u32 {
|
||||
|
||||
using ModuleTextAddress = u64;
|
||||
using PatchTextAddress = u64;
|
||||
using EntryTrampolines = boost::container::flat_map<ModuleTextAddress, PatchTextAddress>;
|
||||
using EntryTrampolines = ankerl::unordered_dense::map<ModuleTextAddress, PatchTextAddress>;
|
||||
|
||||
class Patcher {
|
||||
public:
|
||||
|
||||
+8
-77
@@ -4,7 +4,6 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "game_settings.h"
|
||||
@@ -249,30 +248,12 @@ struct System::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyNVDECChannelOpen(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
++nvdec_active_channels[process_id];
|
||||
}
|
||||
|
||||
void NotifyNVDECChannelClose(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
const auto it = nvdec_active_channels.find(process_id);
|
||||
if (it == nvdec_active_channels.end()) {
|
||||
return;
|
||||
}
|
||||
if (--it->second == 0) {
|
||||
nvdec_active_channels.erase(it);
|
||||
}
|
||||
void SetNVDECActive(bool is_nvdec_active) {
|
||||
nvdec_active = is_nvdec_active;
|
||||
}
|
||||
|
||||
bool GetNVDECActive() {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
return !nvdec_active_channels.empty();
|
||||
}
|
||||
|
||||
bool IsNVDECActiveForProcess(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
return nvdec_active_channels.contains(process_id);
|
||||
return nvdec_active;
|
||||
}
|
||||
|
||||
void InitializeDebugger(System& system, u16 port) {
|
||||
@@ -314,10 +295,6 @@ struct System::Impl {
|
||||
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) {
|
||||
InitializeKernel(system);
|
||||
|
||||
if (params.applet_type == Service::AM::AppletType::Application) {
|
||||
current_application_filepath = filepath;
|
||||
}
|
||||
|
||||
const auto file = GetGameFileFromPath(virtual_filesystem, filepath);
|
||||
|
||||
// Create the application process
|
||||
@@ -353,7 +330,7 @@ struct System::Impl {
|
||||
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
|
||||
|
||||
// Make the process created be the application
|
||||
kernel.SetApplicationProcess(process->GetHandle());
|
||||
kernel.MakeApplicationProcess(process->GetHandle());
|
||||
|
||||
// Set up the rest of the system.
|
||||
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
|
||||
@@ -490,7 +467,6 @@ struct System::Impl {
|
||||
Core::SpeedLimiter speed_limiter;
|
||||
ExecuteProgramCallback execute_program_callback;
|
||||
ExitCallback exit_callback;
|
||||
ApplicationChangedCallback application_changed_callback;
|
||||
|
||||
std::optional<Service::Services> services;
|
||||
std::optional<Core::Debugger> debugger;
|
||||
@@ -512,8 +488,6 @@ struct System::Impl {
|
||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
||||
std::array<u8, 0x20> build_id{};
|
||||
|
||||
std::string current_application_filepath;
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
/// ContentProviderUnion instance
|
||||
@@ -524,8 +498,6 @@ struct System::Impl {
|
||||
|
||||
mutable std::mutex suspend_guard;
|
||||
std::mutex general_channel_mutex;
|
||||
std::mutex nvdec_active_mutex;
|
||||
std::unordered_map<u64, u32> nvdec_active_channels;
|
||||
std::atomic_bool is_paused{};
|
||||
std::atomic_bool is_shutting_down{};
|
||||
std::atomic_bool is_powered_on{};
|
||||
@@ -533,6 +505,7 @@ struct System::Impl {
|
||||
bool extended_memory_layout : 1 = false;
|
||||
bool exit_locked : 1 = false;
|
||||
bool exit_requested : 1 = false;
|
||||
bool nvdec_active : 1 = false;
|
||||
|
||||
void EnsureGeneralChannelInitialized(System& system) {
|
||||
if (!general_channel_event) {
|
||||
@@ -596,22 +569,14 @@ void System::UnstallApplication() {
|
||||
impl->UnstallApplication();
|
||||
}
|
||||
|
||||
void System::NotifyNVDECChannelOpen(u64 process_id) {
|
||||
impl->NotifyNVDECChannelOpen(process_id);
|
||||
}
|
||||
|
||||
void System::NotifyNVDECChannelClose(u64 process_id) {
|
||||
impl->NotifyNVDECChannelClose(process_id);
|
||||
void System::SetNVDECActive(bool is_nvdec_active) {
|
||||
impl->SetNVDECActive(is_nvdec_active);
|
||||
}
|
||||
|
||||
bool System::GetNVDECActive() {
|
||||
return impl->GetNVDECActive();
|
||||
}
|
||||
|
||||
bool System::IsNVDECActiveForProcess(u64 process_id) {
|
||||
return impl->IsNVDECActiveForProcess(process_id);
|
||||
}
|
||||
|
||||
void System::InitializeDebugger() {
|
||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||
}
|
||||
@@ -762,25 +727,7 @@ const Core::SpeedLimiter& System::SpeedLimiter() const {
|
||||
}
|
||||
|
||||
u64 System::GetApplicationProcessProgramID() const {
|
||||
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;
|
||||
return impl->kernel.ApplicationProcess()->GetProgramId();
|
||||
}
|
||||
|
||||
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
||||
@@ -963,10 +910,6 @@ 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.
|
||||
/// It is used to transfer data between programs.
|
||||
std::vector<std::vector<u8>>& System::GetUserChannel() {
|
||||
@@ -1018,18 +961,6 @@ 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() {
|
||||
impl->RefreshTime(*this);
|
||||
|
||||
|
||||
+1
-12
@@ -191,10 +191,8 @@ public:
|
||||
std::unique_lock<std::mutex> StallApplication();
|
||||
void UnstallApplication();
|
||||
|
||||
void NotifyNVDECChannelOpen(u64 process_id);
|
||||
void NotifyNVDECChannelClose(u64 process_id);
|
||||
void SetNVDECActive(bool is_nvdec_active);
|
||||
[[nodiscard]] bool GetNVDECActive();
|
||||
[[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id);
|
||||
|
||||
/**
|
||||
* Initialize the debugger.
|
||||
@@ -324,10 +322,6 @@ public:
|
||||
|
||||
[[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
|
||||
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
||||
|
||||
@@ -428,7 +422,6 @@ public:
|
||||
void PushGeneralChannelData(std::vector<u8>&& data);
|
||||
bool TryPopGeneralChannel(std::vector<u8>& out_data);
|
||||
[[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.
|
||||
using ExitCallback = std::function<void()>;
|
||||
@@ -442,10 +435,6 @@ public:
|
||||
/// Instructs the frontend to exit the application.
|
||||
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.
|
||||
void ApplySettings();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ static u8 MasterKeyIdForKeyGeneration(u8 key_generation) {
|
||||
return std::max<u8>(key_generation, 1) - 1;
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_, const NCA* base_nca, bool allow_missing_base)
|
||||
NCA::NCA(VirtualFile file_, const NCA* base_nca)
|
||||
: file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
@@ -110,7 +110,7 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca, bool allow_missing_base)
|
||||
}
|
||||
}
|
||||
|
||||
if (is_update && base_nca == nullptr && !allow_missing_base) {
|
||||
if (is_update && base_nca == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
|
||||
} else {
|
||||
status = Loader::ResultStatus::Success;
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -66,7 +63,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) {
|
||||
// After construction, use GetStatus to determine if the file is valid and ready to be used.
|
||||
class NCA : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit NCA(VirtualFile file, const NCA* base_nca = nullptr, bool allow_missing_base = false);
|
||||
explicit NCA(VirtualFile file, const NCA* base_nca = nullptr);
|
||||
~NCA() override;
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/fssystem/fssystem_indirect_storage.h"
|
||||
|
||||
@@ -100,18 +99,6 @@ Result IndirectStorage::GetEntryList(Entry* out_entries, s32* out_entry_count, s
|
||||
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 {
|
||||
// Validate pre-conditions.
|
||||
ASSERT(this->IsInitialized());
|
||||
|
||||
@@ -85,9 +85,6 @@ public:
|
||||
void SetStorage(s32 idx, VirtualFile storage) {
|
||||
ASSERT(0 <= idx && idx < StorageCount);
|
||||
m_data_storage[idx] = storage;
|
||||
if (idx == 0) {
|
||||
m_original_missing = storage == nullptr || storage->GetSize() == 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -133,9 +130,6 @@ protected:
|
||||
|
||||
template <bool ContinuousCheck, bool RangeCheck, typename F>
|
||||
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:
|
||||
struct ContinuousReadingEntry {
|
||||
@@ -160,8 +154,6 @@ private:
|
||||
private:
|
||||
mutable BucketTree m_table;
|
||||
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>
|
||||
@@ -280,10 +272,6 @@ Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) {
|
||||
if (needs_operate) {
|
||||
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) {
|
||||
// Get the current data storage's size.
|
||||
s64 cur_data_storage_size = m_data_storage[cur_entry.storage_index]->GetSize();
|
||||
|
||||
+233
-161
@@ -3,13 +3,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <span>
|
||||
#include <cctype>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging.h"
|
||||
@@ -25,30 +22,61 @@ enum class IPSFileType {
|
||||
Error,
|
||||
};
|
||||
|
||||
static IPSFileType IdentifyMagic(std::span<const u8> magic) {
|
||||
if (magic.size() >= 5) {
|
||||
if (std::memcmp(magic.data(), "PATCH", 5) == 0)
|
||||
return IPSFileType::IPS;
|
||||
if (std::memcmp(magic.data(), "IPS32", 5) == 0)
|
||||
return IPSFileType::IPS32;
|
||||
constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{{
|
||||
{"\\a", "\a"},
|
||||
{"\\b", "\b"},
|
||||
{"\\f", "\f"},
|
||||
{"\\n", "\n"},
|
||||
{"\\r", "\r"},
|
||||
{"\\t", "\t"},
|
||||
{"\\v", "\v"},
|
||||
{"\\\\", "\\"},
|
||||
{"\\\'", "\'"},
|
||||
{"\\\"", "\""},
|
||||
{"\\\?", "\?"},
|
||||
}};
|
||||
|
||||
static IPSFileType IdentifyMagic(const std::vector<u8>& magic) {
|
||||
if (magic.size() != 5) {
|
||||
return IPSFileType::Error;
|
||||
}
|
||||
|
||||
static constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}};
|
||||
if (std::equal(magic.begin(), magic.end(), patch_magic.begin())) {
|
||||
return IPSFileType::IPS;
|
||||
}
|
||||
|
||||
static constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}};
|
||||
if (std::equal(magic.begin(), magic.end(), ips32_magic.begin())) {
|
||||
return IPSFileType::IPS32;
|
||||
}
|
||||
|
||||
return IPSFileType::Error;
|
||||
}
|
||||
|
||||
static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
|
||||
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|
||||
|| (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
|
||||
static bool IsEOF(IPSFileType type, const std::vector<u8>& data) {
|
||||
static constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}};
|
||||
if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}};
|
||||
return type == IPSFileType::IPS32 && std::equal(data.begin(), data.end(), eeof.begin());
|
||||
}
|
||||
|
||||
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||
if (in == nullptr || ips == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto in_data = in->ReadAllBytes();
|
||||
auto const type = IdentifyMagic(in_data);
|
||||
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
|
||||
if (type == IPSFileType::Error)
|
||||
return nullptr;
|
||||
|
||||
auto in_data = in->ReadAllBytes();
|
||||
if (in_data.size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
||||
u64 offset = 5; // After header
|
||||
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
|
||||
@@ -57,9 +85,12 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||
break;
|
||||
}
|
||||
|
||||
u32 real_offset = (type == IPSFileType::IPS32)
|
||||
? ((temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3])
|
||||
: ((temp[0] << 16) | (temp[1] << 8) | temp[2]);
|
||||
u32 real_offset{};
|
||||
if (type == IPSFileType::IPS32)
|
||||
real_offset = (temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3];
|
||||
else
|
||||
real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2];
|
||||
|
||||
if (real_offset > in_data.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -82,35 +113,34 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||
return nullptr;
|
||||
|
||||
if (real_offset + rle_size > in_data.size())
|
||||
rle_size = u16(in_data.size() - real_offset);
|
||||
rle_size = static_cast<u16>(in_data.size() - real_offset);
|
||||
std::memset(in_data.data() + real_offset, *data, rle_size);
|
||||
} else { // Standard Patch
|
||||
auto read = data_size;
|
||||
if (real_offset + read > in_data.size())
|
||||
read = u16(in_data.size() - real_offset);
|
||||
read = static_cast<u16>(in_data.size() - real_offset);
|
||||
if (ips->Read(in_data.data() + real_offset, read, offset) != data_size)
|
||||
return nullptr;
|
||||
offset += data_size;
|
||||
}
|
||||
}
|
||||
if (IsEOF(type, temp)) {
|
||||
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), in->GetContainingDirectory());
|
||||
|
||||
if (!IsEOF(type, temp)) {
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
|
||||
in->GetContainingDirectory());
|
||||
}
|
||||
|
||||
|
||||
struct IPSwitchRecord {
|
||||
std::array<uint8_t, 256 - sizeof(size_t)> data;
|
||||
size_t count;
|
||||
};
|
||||
struct IPSwitchCompiler::IPSwitchPatch {
|
||||
boost::unordered::unordered_flat_map<u32, IPSwitchRecord> records;
|
||||
std::string name;
|
||||
bool enabled;
|
||||
std::map<u32, std::vector<u8>> records;
|
||||
};
|
||||
|
||||
IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) {
|
||||
Parse(patch_text->ReadAllBytes());
|
||||
Parse();
|
||||
}
|
||||
|
||||
IPSwitchCompiler::~IPSwitchCompiler() = default;
|
||||
@@ -119,159 +149,201 @@ std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
|
||||
return nso_build_id;
|
||||
}
|
||||
|
||||
static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
||||
IPSwitchRecord r{};
|
||||
for (auto it = sv.cbegin(); it != sv.cend(); ) {
|
||||
if (*it == '\\' && it + 1 < sv.cend()) {
|
||||
switch (it[1]) {
|
||||
case 'n': r.data[r.count] = '\n'; break;
|
||||
case 't': r.data[r.count] = '\t'; break;
|
||||
case 'b': r.data[r.count] = '\b'; break;
|
||||
case 'r': r.data[r.count] = '\r'; break;
|
||||
case 'e': r.data[r.count] = '\e'; break;
|
||||
case 'v': r.data[r.count] = '\v'; break;
|
||||
case '?': r.data[r.count] = '\?'; break;
|
||||
default: r.data[r.count] = it[1]; break;
|
||||
}
|
||||
++r.count;
|
||||
it += 2;
|
||||
} else {
|
||||
++r.count;
|
||||
++it;
|
||||
bool IPSwitchCompiler::IsValid() const {
|
||||
return valid;
|
||||
}
|
||||
|
||||
static bool StartsWith(std::string_view base, std::string_view check) {
|
||||
return base.size() >= check.size() && base.substr(0, check.size()) == check;
|
||||
}
|
||||
|
||||
static std::string EscapeStringSequences(std::string in) {
|
||||
for (const auto& seq : ESCAPE_CHARACTER_MAP) {
|
||||
for (auto index = in.find(seq.first); index != std::string::npos;
|
||||
index = in.find(seq.first, index)) {
|
||||
in.replace(index, std::strlen(seq.first), seq.second);
|
||||
index += std::strlen(seq.second);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline std::array<u8, 32> ReadNSOBuildId(std::string_view const s) {
|
||||
std::array<u8, 32> r{};
|
||||
for (std::size_t i = 0; i < s.size(); ++i)
|
||||
r[i / 2] |= u8(u8(Common::ToHexNibble(s[i])) << u8((i % 2) * 4));
|
||||
return r;
|
||||
void IPSwitchCompiler::ParseFlag(const std::string& line) {
|
||||
if (StartsWith(line, "@flag offset_shift ")) {
|
||||
// Offset Shift Flag
|
||||
offset_shift = std::strtoll(line.substr(19).c_str(), nullptr, 0);
|
||||
} else if (StartsWith(line, "@little-endian")) {
|
||||
// Set values to read as little endian
|
||||
is_little_endian = true;
|
||||
} else if (StartsWith(line, "@big-endian")) {
|
||||
// Set values to read as big endian
|
||||
is_little_endian = false;
|
||||
} else if (StartsWith(line, "@flag print_values")) {
|
||||
// Force printing of applied values
|
||||
print_values = true;
|
||||
}
|
||||
}
|
||||
|
||||
void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
||||
LOG_INFO(Loader, "IPSwitchCompiler: '{}'", patch_text->GetName());
|
||||
bool is_little_endian = false;
|
||||
s64 offset_shift = 0;
|
||||
//bool print_values = false;
|
||||
void IPSwitchCompiler::Parse() {
|
||||
const auto bytes = patch_text->ReadAllBytes();
|
||||
std::stringstream s;
|
||||
s.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
||||
|
||||
auto const parse_line = [&](std::string_view const line) {
|
||||
// Keep in mind lines have trimmed spaces (at the end & start)!
|
||||
LOG_INFO(Loader, "<{}>", line);
|
||||
if (line.starts_with("@stop")) {
|
||||
return false; // Force stop
|
||||
} else if (line.starts_with("@nsobid-")) { // NSO Build ID Specifier
|
||||
nso_build_id = ReadNSOBuildId(line.substr(8));
|
||||
} else if (line.starts_with("@enabled")) {
|
||||
patches.push_back({{}, true}); //enabled patch
|
||||
} else if (line.starts_with("@disabled")) {
|
||||
patches.push_back({{}, false}); //disabled patch
|
||||
} else if (line.starts_with("@flag offset_shift ")) {
|
||||
offset_shift = std::strtoll(line.data() + 19, nullptr, 0); // Offset Shift Flag
|
||||
} else if (line.starts_with("@little-endian")) {
|
||||
is_little_endian = true; // Set values to read as little endian
|
||||
} else if (line.starts_with("@big-endian")) {
|
||||
is_little_endian = false; // Set values to read as big endian
|
||||
} else if (line.starts_with("@flag print_values")) {
|
||||
//print_values = true; // Force printing of applied values
|
||||
} else if (line.starts_with("@")) {
|
||||
LOG_WARNING(Loader, "Unknown flag {}", line);
|
||||
} 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()) {
|
||||
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
|
||||
};
|
||||
std::vector<std::string> lines;
|
||||
std::string stream_line;
|
||||
while (std::getline(s, stream_line)) {
|
||||
// Remove a trailing \r
|
||||
if (!stream_line.empty() && stream_line.back() == '\r')
|
||||
stream_line.pop_back();
|
||||
lines.push_back(std::move(stream_line));
|
||||
}
|
||||
|
||||
for (auto it = bytes.begin(); it < bytes.end(); ) {
|
||||
auto const start = it;
|
||||
auto end = start;
|
||||
for (; end < bytes.end() && *end != '\n' && *end != '\r'; ++end)
|
||||
;
|
||||
it = end + 1; //prepare for next line
|
||||
std::string_view const sline{
|
||||
reinterpret_cast<const char*>(bytes.data() + std::distance(bytes.begin(), start)),
|
||||
size_t(std::distance(start, end))
|
||||
};
|
||||
if (sline.size() > 0) {
|
||||
auto p = sline.cbegin();
|
||||
// skip space off line
|
||||
for (; p < sline.cend() && std::isspace(*p); ++p)
|
||||
;
|
||||
// now make a nominal preprocessed line: remove comments
|
||||
char quote = '\0';
|
||||
auto const sline_start = p;
|
||||
for (; p < sline.cend(); ) {
|
||||
if ((!quote && p + 1 < sline.cend() && p[0] == '/' && p[1] == '/')
|
||||
|| (!quote && p[0] == '#')) {
|
||||
break;
|
||||
} else if (p[0] == '\"' || p[0] == '\'') {
|
||||
quote = (p[0] == quote) ? '\0' : p[0];
|
||||
++p;
|
||||
} else if (p + 1 < sline.cend() && p[0] == '\\') {
|
||||
p += 2;
|
||||
} else {
|
||||
++p;
|
||||
}
|
||||
}
|
||||
// now we have the preprocessed string ;)
|
||||
std::string_view pp_str(sline_start, p);
|
||||
if (pp_str.size() > 0 && !parse_line(pp_str)) {
|
||||
for (std::size_t i = 0; i < lines.size(); ++i) {
|
||||
auto line = lines[i];
|
||||
|
||||
// Remove midline comments
|
||||
std::size_t comment_index = std::string::npos;
|
||||
bool within_string = false;
|
||||
for (std::size_t k = 0; k < line.size(); ++k) {
|
||||
if (line[k] == '\"' && (k > 0 && line[k - 1] != '\\')) {
|
||||
within_string = !within_string;
|
||||
} else if (line[k] == '\\' && (k < line.size() - 1 && line[k + 1] == '\\')) {
|
||||
comment_index = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!StartsWith(line, "//") && comment_index != std::string::npos) {
|
||||
last_comment = line.substr(comment_index + 2);
|
||||
line = line.substr(0, comment_index);
|
||||
}
|
||||
|
||||
if (StartsWith(line, "@stop")) {
|
||||
// Force stop
|
||||
break;
|
||||
} else if (StartsWith(line, "@nsobid-")) {
|
||||
// NSO Build ID Specifier
|
||||
const auto raw_build_id = fmt::format("{:0<64}", line.substr(8));
|
||||
nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
|
||||
} else if (StartsWith(line, "#")) {
|
||||
// Mandatory Comment
|
||||
LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}",
|
||||
patch_text->GetName(), line.substr(1));
|
||||
} else if (StartsWith(line, "//")) {
|
||||
// Normal Comment
|
||||
last_comment = line.substr(2);
|
||||
if (last_comment.find_first_not_of(' ') == std::string::npos)
|
||||
continue;
|
||||
if (last_comment.find_first_not_of(' ') != 0)
|
||||
last_comment = last_comment.substr(last_comment.find_first_not_of(' '));
|
||||
} else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) {
|
||||
// Start of patch
|
||||
const auto enabled = StartsWith(line, "@enabled");
|
||||
if (i == 0)
|
||||
return;
|
||||
LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Parsing patch '{}' ({})",
|
||||
patch_text->GetName(), last_comment, line.substr(1));
|
||||
|
||||
IPSwitchPatch patch{last_comment, enabled, {}};
|
||||
|
||||
// Read rest of patch
|
||||
while (true) {
|
||||
if (i + 1 >= lines.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& patch_line = lines[++i];
|
||||
|
||||
// Patch line may contain comments
|
||||
if (StartsWith(patch_line, "//") || StartsWith(patch_line, "#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start of new patch
|
||||
if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
|
||||
--i;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for a flag
|
||||
if (StartsWith(patch_line, "@")) {
|
||||
ParseFlag(patch_line);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 11 - 8 hex digit offset + space + minimum two digit overwrite val
|
||||
if (patch_line.length() < 11)
|
||||
break;
|
||||
auto offset = std::strtoul(patch_line.substr(0, 8).c_str(), nullptr, 16);
|
||||
offset += static_cast<unsigned long>(offset_shift);
|
||||
|
||||
std::vector<u8> replace;
|
||||
// 9 - first char of replacement val
|
||||
if (patch_line[9] == '\"') {
|
||||
// string replacement
|
||||
auto end_index = patch_line.find('\"', 10);
|
||||
if (end_index == std::string::npos || end_index < 10)
|
||||
return;
|
||||
while (patch_line[end_index - 1] == '\\') {
|
||||
end_index = patch_line.find('\"', end_index + 1);
|
||||
if (end_index == std::string::npos || end_index < 10)
|
||||
return;
|
||||
}
|
||||
|
||||
auto value = patch_line.substr(10, end_index - 10);
|
||||
value = EscapeStringSequences(value);
|
||||
replace.reserve(value.size());
|
||||
std::copy(value.begin(), value.end(), std::back_inserter(replace));
|
||||
} else {
|
||||
// hex replacement
|
||||
const auto value =
|
||||
patch_line.substr(9, patch_line.find_first_of(" /\r\n", 9) - 9);
|
||||
replace = Common::HexStringToVector(value, is_little_endian);
|
||||
}
|
||||
|
||||
if (print_values) {
|
||||
LOG_INFO(Loader,
|
||||
"[IPSwitchCompiler ('{}')] - Patching value at offset {:#08x} "
|
||||
"with byte string '{}'",
|
||||
patch_text->GetName(), offset, Common::HexToString(replace));
|
||||
}
|
||||
|
||||
patch.records.insert_or_assign(static_cast<u32>(offset), std::move(replace));
|
||||
}
|
||||
|
||||
patches.push_back(std::move(patch));
|
||||
} else if (StartsWith(line, "@")) {
|
||||
ParseFlag(line);
|
||||
}
|
||||
}
|
||||
|
||||
valid = true;
|
||||
}
|
||||
|
||||
VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
|
||||
if (in == nullptr)
|
||||
if (in == nullptr || !valid)
|
||||
return nullptr;
|
||||
|
||||
auto in_data = in->ReadAllBytes();
|
||||
|
||||
for (const auto& patch : patches) {
|
||||
if (patch.enabled) {
|
||||
for (const auto& record : patch.records) {
|
||||
if (record.first < in_data.size()) {
|
||||
auto replace_size = record.second.count;
|
||||
if (record.first + replace_size > in_data.size())
|
||||
replace_size = in_data.size() - record.first;
|
||||
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
|
||||
}
|
||||
}
|
||||
if (!patch.enabled)
|
||||
continue;
|
||||
|
||||
for (const auto& record : patch.records) {
|
||||
if (record.first >= in_data.size())
|
||||
continue;
|
||||
auto replace_size = record.second.size();
|
||||
if (record.first + replace_size > in_data.size())
|
||||
replace_size = in_data.size() - record.first;
|
||||
for (std::size_t i = 0; i < replace_size; ++i)
|
||||
in_data[i + record.first] = record.second[i];
|
||||
}
|
||||
}
|
||||
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), in->GetContainingDirectory());
|
||||
|
||||
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
|
||||
in->GetContainingDirectory());
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
@@ -23,17 +20,24 @@ public:
|
||||
~IPSwitchCompiler();
|
||||
|
||||
std::array<u8, 0x20> GetBuildID() const;
|
||||
bool IsValid() const;
|
||||
VirtualFile Apply(const VirtualFile& in) const;
|
||||
|
||||
private:
|
||||
struct IPSwitchPatch;
|
||||
|
||||
void ParseFlag(const std::string& flag);
|
||||
void Parse(std::span<u8 const> bytes);
|
||||
void Parse();
|
||||
|
||||
bool valid = false;
|
||||
|
||||
VirtualFile patch_text;
|
||||
std::vector<IPSwitchPatch> patches;
|
||||
std::array<u8, 0x20> nso_build_id{};
|
||||
bool is_little_endian = false;
|
||||
s64 offset_shift = 0;
|
||||
bool print_values = false;
|
||||
std::string last_comment = "";
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -48,11 +48,11 @@ struct ContentRecord {
|
||||
std::array<u8, 0x10> nca_id;
|
||||
std::array<u8, 0x6> size;
|
||||
ContentRecordType type;
|
||||
u8 id_offset;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(ContentRecord) == 0x38, "ContentRecord has incorrect size.");
|
||||
|
||||
constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, 0};
|
||||
constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, {}};
|
||||
|
||||
struct MetaRecord {
|
||||
u64_le title_id;
|
||||
|
||||
@@ -345,7 +345,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
return exefs;
|
||||
}
|
||||
|
||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||
const std::string& build_id) const {
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||
|
||||
@@ -360,11 +361,16 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
||||
for (const auto& file : exefs_dir->GetFiles()) {
|
||||
if (file->GetExtension() == "ips") {
|
||||
auto name = file->GetName();
|
||||
const auto this_build_id = fmt::format("{:0<64}", name.substr(0, name.find('.')));
|
||||
|
||||
const auto this_build_id =
|
||||
fmt::format("{:0<64}", name.substr(0, name.find('.')));
|
||||
if (nso_build_id == this_build_id)
|
||||
out.push_back(file);
|
||||
} else if (file->GetExtension() == "pchtxt") {
|
||||
IPSwitchCompiler compiler{file};
|
||||
if (!compiler.IsValid())
|
||||
continue;
|
||||
|
||||
const auto this_build_id = Common::HexToString(compiler.GetBuildID());
|
||||
if (nso_build_id == this_build_id)
|
||||
out.push_back(file);
|
||||
@@ -372,6 +378,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -566,46 +566,21 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
||||
return file;
|
||||
}
|
||||
|
||||
static std::optional<NcaID> CheckMapForContentRecord(const boost::unordered::unordered_flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
|
||||
auto cmnt_iter = map.find(title_id);
|
||||
u8 id_offset = 0;
|
||||
|
||||
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);
|
||||
if (cmnt_iter == map.cend()) {
|
||||
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);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto& cnmt = cmnt_iter->second;
|
||||
const auto& content_records = cnmt.GetContentRecords();
|
||||
const auto iter = std::find_if(content_records.cbegin(), content_records.cend(),
|
||||
[type, id_offset](const ContentRecord& rec) {
|
||||
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) {
|
||||
[type](const ContentRecord& rec) { return rec.type == type; });
|
||||
if (iter == content_records.cend()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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);
|
||||
return std::make_optional(iter->nca_id);
|
||||
}
|
||||
|
||||
std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
@@ -208,11 +209,11 @@ private:
|
||||
ContentProviderParsingFunction parser;
|
||||
|
||||
// maps tid -> NcaID of meta
|
||||
boost::container::flat_map<u64, NcaID> meta_id;
|
||||
ankerl::unordered_dense::map<u64, NcaID> meta_id;
|
||||
// maps tid -> meta
|
||||
boost::container::flat_map<u64, CNMT> meta;
|
||||
ankerl::unordered_dense::map<u64, CNMT> meta;
|
||||
// maps tid -> meta for CNMT in yuzu_meta
|
||||
boost::container::flat_map<u64, CNMT> yuzu_meta;
|
||||
ankerl::unordered_dense::map<u64, CNMT> yuzu_meta;
|
||||
};
|
||||
|
||||
enum class ContentProviderUnionSlot {
|
||||
@@ -312,8 +313,8 @@ private:
|
||||
void ProcessXCI(const VirtualFile& file);
|
||||
|
||||
std::vector<VirtualDir> load_dirs;
|
||||
boost::container::flat_map<std::tuple<u64, ContentRecordType, TitleType>, VirtualFile> entries;
|
||||
boost::container::flat_map<u64, u32> versions;
|
||||
ankerl::unordered_dense::map<std::tuple<u64, ContentRecordType, TitleType>, VirtualFile> entries;
|
||||
ankerl::unordered_dense::map<u64, u32> versions;
|
||||
std::vector<ExternalUpdateEntry> multi_version_entries;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging.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/nca_metadata.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
@@ -66,12 +65,10 @@ u64 NSP::GetProgramTitleID() const {
|
||||
}
|
||||
|
||||
auto program_id = expected_program_id;
|
||||
if (program_id == 0 && !program_status.empty()) {
|
||||
program_id = std::min_element(program_status.cbegin(), program_status.cend(),
|
||||
[](const auto& lhs, const auto& rhs) {
|
||||
return lhs.first < rhs.first;
|
||||
})
|
||||
->first;
|
||||
if (program_id == 0) {
|
||||
if (!program_status.empty()) {
|
||||
program_id = program_status.begin()->first;
|
||||
}
|
||||
}
|
||||
|
||||
program_id = program_id + program_index;
|
||||
@@ -276,7 +273,8 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
||||
// 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
|
||||
// update NCA.
|
||||
if ((next_nca->GetTitleId() & AOC_TITLE_ID_MASK) != 0) {
|
||||
if ((next_nca->GetTitleId() & 0x7FF) != 0 &&
|
||||
(next_nca->GetTitleId() & 0x800) == 0) {
|
||||
ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] =
|
||||
std::move(next_nca);
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <utility>
|
||||
#include "core/file_sys/vfs/vfs_layered.h"
|
||||
|
||||
@@ -61,7 +63,7 @@ std::string LayeredVfsDirectory::GetFullPath() const {
|
||||
|
||||
std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
||||
std::vector<VirtualFile> out;
|
||||
boost::container::flat_set<std::string> out_names;
|
||||
ankerl::unordered_dense::set<std::string> out_names;
|
||||
|
||||
for (const auto& layer : dirs) {
|
||||
for (auto& file : layer->GetFiles()) {
|
||||
@@ -77,7 +79,8 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
||||
|
||||
std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
|
||||
std::vector<VirtualDir> out;
|
||||
boost::container::flat_set<std::string> out_names;
|
||||
ankerl::unordered_dense::set<std::string> out_names;
|
||||
|
||||
for (const auto& layer : dirs) {
|
||||
for (const auto& sd : layer->GetSubdirectories()) {
|
||||
out_names.emplace(sd->GetName());
|
||||
|
||||
@@ -78,7 +78,7 @@ private:
|
||||
std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{};
|
||||
std::map<KProcessAddress, u64> m_debug_page_refcounts{};
|
||||
#ifdef HAS_NCE
|
||||
boost::unordered::unordered_flat_map<u64, u64> m_post_handlers{};
|
||||
ankerl::unordered_dense::map<u64, u64> m_post_handlers{};
|
||||
#endif
|
||||
std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor;
|
||||
Core::Memory::Memory m_memory;
|
||||
@@ -494,7 +494,7 @@ public:
|
||||
static void Switch(KernelCore& kernel, KProcess* cur_process, KProcess* next_process);
|
||||
|
||||
#ifdef HAS_NCE
|
||||
boost::unordered::unordered_flat_map<u64, u64>& GetPostHandlers() noexcept {
|
||||
ankerl::unordered_dense::map<u64, u64>& GetPostHandlers() noexcept {
|
||||
return m_post_handlers;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1216,7 +1216,7 @@ Result KServerSession::ReceiveRequest(KernelCore& kernel, uintptr_t server_messa
|
||||
}
|
||||
|
||||
Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||
KPhysicalAddress server_message_paddr, bool is_hle, bool session_closed) {
|
||||
KPhysicalAddress server_message_paddr, bool is_hle) {
|
||||
// Lock the session.
|
||||
KScopedLightLock lk{m_lock};
|
||||
|
||||
@@ -1248,7 +1248,7 @@ Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, u
|
||||
KEvent* event = request->GetEvent();
|
||||
|
||||
// Check whether we're closed.
|
||||
const bool closed = (client_thread == nullptr || m_parent->IsClientClosed() || session_closed);
|
||||
const bool closed = (client_thread == nullptr || m_parent->IsClientClosed());
|
||||
|
||||
Result result = ResultSuccess;
|
||||
if (!closed) {
|
||||
|
||||
@@ -54,14 +54,14 @@ public:
|
||||
|
||||
Result OnRequest(KernelCore& kernel, KSessionRequest* request);
|
||||
Result SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||
KPhysicalAddress server_message_paddr, bool is_hle = false, bool session_closed = false);
|
||||
KPhysicalAddress server_message_paddr, bool is_hle = false);
|
||||
Result ReceiveRequest(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||
KPhysicalAddress server_message_paddr,
|
||||
std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
|
||||
std::weak_ptr<Service::SessionRequestManager> manager = {});
|
||||
|
||||
Result SendReplyHLE(KernelCore& kernel, bool session_closed = false) {
|
||||
R_RETURN(this->SendReply(kernel, 0, 0, 0, true, session_closed));
|
||||
Result SendReplyHLE(KernelCore& kernel) {
|
||||
R_RETURN(this->SendReply(kernel, 0, 0, 0, true));
|
||||
}
|
||||
|
||||
Result ReceiveRequestHLE(KernelCore& kernel, std::shared_ptr<Service::HLERequestContext>* out_context,
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -350,18 +349,9 @@ struct KernelCore::Impl {
|
||||
object_name_global_data.emplace(kernel);
|
||||
}
|
||||
|
||||
void SetApplicationProcess(KernelCore& kernel, KProcess* process) {
|
||||
if (application_process == process)
|
||||
return;
|
||||
|
||||
KProcess* const previous = application_process;
|
||||
void MakeApplicationProcess(KernelCore& kernel, KProcess* process) {
|
||||
application_process = process;
|
||||
|
||||
if (application_process != nullptr)
|
||||
application_process->Open(kernel);
|
||||
|
||||
if (previous != nullptr)
|
||||
previous->Close(kernel);
|
||||
application_process->Open(kernel);
|
||||
}
|
||||
|
||||
/// Sets the host thread ID for the caller.
|
||||
@@ -793,8 +783,8 @@ struct KernelCore::Impl {
|
||||
|
||||
std::optional<KObjectNameGlobalData> object_name_global_data;
|
||||
|
||||
boost::unordered::unordered_flat_set<KAutoObject*> registered_objects;
|
||||
boost::unordered::unordered_flat_set<KAutoObject*> registered_in_use_objects;
|
||||
ankerl::unordered_dense::set<KAutoObject*> registered_objects;
|
||||
ankerl::unordered_dense::set<KAutoObject*> registered_in_use_objects;
|
||||
|
||||
std::mutex server_lock;
|
||||
std::vector<std::unique_ptr<Service::ServerManager>> server_managers;
|
||||
@@ -889,8 +879,8 @@ void KernelCore::RemoveProcess(KProcess* process) {
|
||||
}
|
||||
}
|
||||
|
||||
void KernelCore::SetApplicationProcess(KProcess* process) {
|
||||
impl->SetApplicationProcess(*this, process);
|
||||
void KernelCore::MakeApplicationProcess(KProcess* process) {
|
||||
impl->MakeApplicationProcess(*this, process);
|
||||
}
|
||||
|
||||
KProcess* KernelCore::ApplicationProcess() {
|
||||
@@ -901,14 +891,6 @@ const KProcess* KernelCore::ApplicationProcess() const {
|
||||
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>> processes;
|
||||
std::scoped_lock lk{impl->process_list_lock};
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/polyfill_thread.h"
|
||||
@@ -125,8 +124,8 @@ public:
|
||||
void AppendNewProcess(KProcess* process);
|
||||
void RemoveProcess(KProcess* process);
|
||||
|
||||
/// Makes the given process the current application process.
|
||||
void SetApplicationProcess(KProcess* process);
|
||||
/// Makes the given process the new application process.
|
||||
void MakeApplicationProcess(KProcess* process);
|
||||
|
||||
/// Retrieves a pointer to the application process.
|
||||
KProcess* ApplicationProcess();
|
||||
@@ -134,9 +133,6 @@ public:
|
||||
/// Retrieves a const pointer to the application process.
|
||||
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.
|
||||
std::list<KScopedAutoObject<KProcess>> GetProcessList();
|
||||
|
||||
|
||||
@@ -17,12 +17,56 @@
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
constexpr auto MAX_MSG_TIME = std::chrono::milliseconds(250);
|
||||
const auto MAX_MSG_SIZE = 0x1000;
|
||||
|
||||
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
||||
Result OutputDebugString(Core::System& system, u64 address, u64 len) {
|
||||
static struct DebugFlusher {
|
||||
std::string msg_buffer;
|
||||
std::mutex msg_mutex;
|
||||
std::condition_variable msg_cv;
|
||||
std::chrono::steady_clock::time_point last_msg_time;
|
||||
std::optional<std::jthread> thread;
|
||||
} flusher_data;
|
||||
R_SUCCEED_IF(len == 0);
|
||||
std::string msg_buffer(len, 0);
|
||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, msg_buffer.data(), len);
|
||||
LOG_INFO(Debug_Emulated, "{}", msg_buffer);
|
||||
// Only start the thread the very first time this function is called
|
||||
if (!flusher_data.thread) {
|
||||
flusher_data.thread.emplace([](std::stop_token stop_token) {
|
||||
while (!stop_token.stop_requested()) {
|
||||
std::unique_lock lock(flusher_data.msg_mutex);
|
||||
flusher_data.msg_cv.wait(lock, [&stop_token] {
|
||||
return !flusher_data.msg_buffer.empty() || stop_token.stop_requested();
|
||||
});
|
||||
if (stop_token.stop_requested() && flusher_data.msg_buffer.empty())
|
||||
break;
|
||||
auto timeout = flusher_data.last_msg_time + MAX_MSG_TIME;
|
||||
bool woke_early = flusher_data.msg_cv.wait_until(lock, timeout, [&stop_token] {
|
||||
return flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested();
|
||||
});
|
||||
if (!woke_early || flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested()) {
|
||||
if (!flusher_data.msg_buffer.empty()) {
|
||||
// Remove trailing newline as LOG_INFO adds that anyways
|
||||
if (flusher_data.msg_buffer.back() == '\n')
|
||||
flusher_data.msg_buffer.pop_back();
|
||||
|
||||
LOG_INFO(Debug_Emulated, "\n{}", flusher_data.msg_buffer);
|
||||
flusher_data.msg_buffer.clear();
|
||||
}
|
||||
if (stop_token.stop_requested()) break;
|
||||
}
|
||||
}
|
||||
flusher_data.msg_cv.notify_all();
|
||||
});
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(flusher_data.msg_mutex);
|
||||
const auto old_size = flusher_data.msg_buffer.size();
|
||||
flusher_data.msg_buffer.resize(old_size + len);
|
||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, flusher_data.msg_buffer.data() + old_size, len);
|
||||
flusher_data.last_msg_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
flusher_data.msg_cv.notify_one();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -13,7 +10,6 @@ namespace Service::AM {
|
||||
constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
|
||||
constexpr Result ResultNoMessages{ErrorModule::AM, 3};
|
||||
constexpr Result ResultLibraryAppletTerminated{ErrorModule::AM, 22};
|
||||
constexpr Result ResultApplicationRecordNotFound{ErrorModule::AM, 37};
|
||||
constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
|
||||
constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511};
|
||||
constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -201,14 +201,6 @@ enum class ProgramSpecifyKind : u32 {
|
||||
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 {
|
||||
CommonArgumentVersion arguments_version;
|
||||
CommonArgumentSize size;
|
||||
|
||||
@@ -56,22 +56,22 @@ void Applet::UpdateSuspensionStateLocked(bool force_message) {
|
||||
}
|
||||
}
|
||||
|
||||
void Applet::SetInteractibleLocked(bool pad_interactible, bool touch_interactible) {
|
||||
if (is_pad_interactible == pad_interactible && is_touch_interactible == touch_interactible) {
|
||||
void Applet::SetInteractibleLocked(bool interactible) {
|
||||
if (is_interactible == interactible) {
|
||||
return;
|
||||
}
|
||||
|
||||
is_pad_interactible = pad_interactible;
|
||||
is_touch_interactible = touch_interactible;
|
||||
is_interactible = interactible;
|
||||
|
||||
const bool exit_requested = lifecycle_manager.GetExitRequested();
|
||||
const bool pad_enabled = pad_interactible && !exit_requested;
|
||||
const bool touch_enabled = touch_interactible && !exit_requested;
|
||||
const bool input_enabled = interactible && !exit_requested;
|
||||
|
||||
LOG_DEBUG(Service_AM, "applet={} pad={} touch={} exit_requested={}",
|
||||
static_cast<u32>(applet_id), pad_enabled, touch_enabled, exit_requested);
|
||||
if (applet_id == AppletId::OverlayDisplay || applet_id == AppletId::Application) {
|
||||
LOG_DEBUG(Service_AM, "called, applet={} interactible={} exit_requested={} input_enabled={} overlay_in_foreground={}",
|
||||
static_cast<u32>(applet_id), interactible, exit_requested, input_enabled, overlay_in_foreground);
|
||||
}
|
||||
|
||||
hid_registration.EnableAppletToGetInput(pad_enabled, touch_enabled);
|
||||
hid_registration.EnableAppletToGetInput(input_enabled);
|
||||
}
|
||||
|
||||
void Applet::OnProcessTerminatedLocked() {
|
||||
|
||||
@@ -125,11 +125,9 @@ struct Applet {
|
||||
bool album_image_taken_notification_enabled{};
|
||||
bool record_volume_muted{};
|
||||
bool is_activity_runnable{};
|
||||
bool is_pad_interactible{true};
|
||||
bool is_touch_interactible{true};
|
||||
bool is_interactible{true};
|
||||
bool window_visible{true};
|
||||
bool overlay_watching_short_home_button{false};
|
||||
bool overlay_handling_touch_input{false};
|
||||
bool overlay_in_foreground{false};
|
||||
|
||||
// Events
|
||||
Event overlay_event;
|
||||
@@ -150,7 +148,7 @@ struct Applet {
|
||||
|
||||
// Process state management
|
||||
void UpdateSuspensionStateLocked(bool force_message);
|
||||
void SetInteractibleLocked(bool pad_interactible, bool touch_interactible);
|
||||
void SetInteractibleLocked(bool interactible);
|
||||
void OnProcessTerminatedLocked();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "core/core.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/vi/application_display_service.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
@@ -34,7 +33,6 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro
|
||||
m_system_shared_buffer_id = 0;
|
||||
m_system_shared_layer_id = 0;
|
||||
m_applet_id = applet_id;
|
||||
m_library_applet_mode = mode;
|
||||
m_buffer_sharing_enabled = false;
|
||||
m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
|
||||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
|
||||
@@ -74,16 +72,14 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||
|
||||
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) {
|
||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
(void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id);
|
||||
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
||||
} else {
|
||||
(void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id);
|
||||
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,12 +122,10 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
||||
|
||||
// Ensure the overlay layer is visible
|
||||
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);
|
||||
s32 initial_z = Foreground;
|
||||
s32 initial_z = 1;
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
initial_z = Overlay;
|
||||
initial_z = -1;
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
||||
}
|
||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||
@@ -148,36 +142,6 @@ Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_bu
|
||||
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) {
|
||||
if (m_visible == visible) {
|
||||
return;
|
||||
@@ -221,17 +185,10 @@ void DisplayLayerManager::SetOverlayZIndex(s32 z_index) {
|
||||
}
|
||||
|
||||
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
||||
s32* out_fbshare_layer_index,
|
||||
VI::CaptureKind kind) {
|
||||
s32* out_fbshare_layer_index) {
|
||||
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
|
||||
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));
|
||||
out_was_written, out_fbshare_layer_index));
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -23,7 +23,6 @@ class KProcess;
|
||||
namespace Service::VI {
|
||||
class IApplicationDisplayService;
|
||||
class IManagerDisplayService;
|
||||
enum class CaptureKind : u32;
|
||||
} // namespace Service::VI
|
||||
|
||||
namespace Service::AM {
|
||||
@@ -49,13 +48,9 @@ public:
|
||||
|
||||
void SetOverlayZIndex(s32 z_index);
|
||||
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index,
|
||||
VI::CaptureKind kind);
|
||||
Result ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color);
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
|
||||
|
||||
private:
|
||||
u32 GetLayerStackMask() const;
|
||||
|
||||
Kernel::KProcess* m_process{};
|
||||
std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
|
||||
std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
|
||||
@@ -64,7 +59,6 @@ private:
|
||||
u64 m_system_shared_buffer_id{};
|
||||
u64 m_system_shared_layer_id{};
|
||||
AppletId m_applet_id{};
|
||||
LibraryAppletMode m_library_applet_mode{};
|
||||
bool m_buffer_sharing_enabled{};
|
||||
bool m_blending_enabled{};
|
||||
bool m_visible{true};
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
@@ -177,6 +176,6 @@ struct WebCommonReturnValue {
|
||||
};
|
||||
static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size.");
|
||||
|
||||
using WebArgInputTLVMap = boost::unordered::unordered_flat_map<WebArgInputTLVType, std::vector<u8>>;
|
||||
using WebArgInputTLVMap = ankerl::unordered_dense::map<WebArgInputTLVType, std::vector<u8>>;
|
||||
|
||||
} // namespace Service::AM::Frontend
|
||||
|
||||
@@ -36,17 +36,12 @@ HidRegistration::~HidRegistration() {
|
||||
}
|
||||
}
|
||||
|
||||
void HidRegistration::EnableAppletToGetInput(bool enable_pad, bool enable_touch) {
|
||||
if (!m_process.IsInitialized())
|
||||
return;
|
||||
|
||||
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);
|
||||
void HidRegistration::EnableAppletToGetInput(bool enable) {
|
||||
if (m_process.IsInitialized()) {
|
||||
m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(),
|
||||
enable);
|
||||
m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
~HidRegistration();
|
||||
|
||||
void RegisterCurrentProcess();
|
||||
void EnableAppletToGetInput(bool enable_pad, bool enable_touch);
|
||||
void EnableAppletToGetInput(bool enable);
|
||||
|
||||
private:
|
||||
Process& m_process;
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// 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/service/applet_common_functions.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
@@ -80,7 +78,7 @@ Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) {
|
||||
|
||||
Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_id) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_application_id = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||
*out_application_id = system.GetApplicationProcessProgramID() & ~0xFFFULL;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,8 @@
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "common/hex_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
@@ -30,27 +28,6 @@
|
||||
|
||||
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)
|
||||
: ServiceFramework{system_, "IApplicationFunctions"}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
@@ -179,7 +156,7 @@ Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
|
||||
// Default to 0 (all languages supported)
|
||||
u32 supported_languages = 0;
|
||||
|
||||
const auto res = GetApplicationMetadata(system, m_applet->program_id);
|
||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
||||
if (res.first != nullptr) {
|
||||
supported_languages = res.first->GetSupportedLanguages();
|
||||
}
|
||||
@@ -217,7 +194,7 @@ Result IApplicationFunctions::SetTerminateResult(Result terminate_result) {
|
||||
Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_version) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
const auto res = GetApplicationMetadata(system, m_applet->program_id);
|
||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
||||
if (res.first != nullptr) {
|
||||
const auto& version = res.first->GetVersionString();
|
||||
std::memcpy(out_display_version->string.data(), version.data(),
|
||||
@@ -347,7 +324,6 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
|
||||
|
||||
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
||||
LOG_WARNING(Service_AM, "(stubbed)");
|
||||
R_UNLESS(out_pseudo_device_id, ResultUnknown);
|
||||
|
||||
// This should be hashed with the device specific hash
|
||||
// for now this will do
|
||||
@@ -454,21 +430,15 @@ Result IApplicationFunctions::ExecuteProgram(ProgramSpecifyKind kind, u64 value)
|
||||
|
||||
// https://switchbrew.org/wiki/Applet_Manager_services#CreateApplicationAndRequestToStart
|
||||
Result IApplicationFunctions::CreateApplicationAndRequestToStart(u64 application_id) {
|
||||
LOG_INFO(Service_AM, "called, application_id={:016X} current_program_id={:016X}", application_id, m_applet->program_id);
|
||||
LOG_INFO(Service_AM, "called, application_id={:016X}", application_id);
|
||||
|
||||
if (application_id == 0 ||
|
||||
FileSys::GetBaseTitleID(application_id) == FileSys::GetBaseTitleID(m_applet->program_id)) {
|
||||
const auto program_index = application_id == 0
|
||||
? 0
|
||||
: application_id - FileSys::GetBaseTitleID(application_id);
|
||||
// If application_id is 0, relaunch the current application
|
||||
const u64 target_application_id =
|
||||
(application_id == 0) ? m_applet->program_id : application_id;
|
||||
|
||||
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
|
||||
system.ExecuteProgram(program_index);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_AM, "Launching a different application ({:016X}) is not implemented!", application_id);
|
||||
R_THROW(ResultUnknown);
|
||||
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
|
||||
system.ExecuteProgram(target_application_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationFunctions::ClearUserChannel() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/service/display_controller.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/vi/shared_buffer_manager.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
@@ -72,16 +71,16 @@ Result IDisplayController::TakeScreenShotOfOwnLayer(bool unknown0, s32 fbshare_l
|
||||
}
|
||||
|
||||
Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color) {
|
||||
LOG_DEBUG(Service_AM, "called, unknown0={} fbshare_layer_index={} color={:#x}", unknown0,
|
||||
fbshare_layer_index, color);
|
||||
R_RETURN(applet->display_layer_manager.ClearAppletCaptureBuffer(fbshare_layer_index, color));
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, unknown0={} fbshare_layer_index={} color={:#x}",
|
||||
unknown0, fbshare_layer_index, color);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer(
|
||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastForeground));
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
||||
out_fbshare_layer_index));
|
||||
}
|
||||
|
||||
Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
||||
@@ -91,9 +90,9 @@ Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
||||
|
||||
Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer(
|
||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index, VI::CaptureKind::CallerApplet));
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
||||
out_fbshare_layer_index));
|
||||
}
|
||||
|
||||
Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
||||
@@ -103,9 +102,9 @@ Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
||||
|
||||
Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer(
|
||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastApplication));
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
||||
out_fbshare_layer_index));
|
||||
}
|
||||
|
||||
Result IDisplayController::ReleaseLastApplicationCaptureSharedBuffer() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/am/applet.h"
|
||||
@@ -22,7 +22,7 @@ namespace Service::AM {
|
||||
{10, nullptr, "StartShutdownSequenceForOverlay"},
|
||||
{11, nullptr, "StartRebootSequenceForOverlay"},
|
||||
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{21, D<&IOverlayFunctions::SetHandlingTouchScreenInputEnabled>, "SetHandlingTouchScreenInputEnabled"},
|
||||
{21, nullptr, "SetHandlingTouchScreenInputEnabled"},
|
||||
{30, nullptr, "SetHealthWarningShowingState"},
|
||||
{31, D<&IOverlayFunctions::IsHealthWarningRequired>, "IsHealthWarningRequired"},
|
||||
{40, nullptr, "GetApplicationNintendoLogo"},
|
||||
@@ -43,12 +43,10 @@ namespace Service::AM {
|
||||
Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->overlay_watching_short_home_button = true;
|
||||
}
|
||||
m_applet->overlay_in_foreground = true;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
|
||||
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
@@ -58,26 +56,16 @@ namespace Service::AM {
|
||||
Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->overlay_watching_short_home_button = false;
|
||||
}
|
||||
m_applet->overlay_in_foreground = false;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
|
||||
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
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) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
@@ -20,7 +20,6 @@ namespace Service::AM {
|
||||
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
|
||||
Result IsHealthWarningRequired(Out<bool> is_required);
|
||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||
Result SetHandlingTouchScreenInputEnabled(bool enabled);
|
||||
Result Unknown70();
|
||||
|
||||
private:
|
||||
|
||||
@@ -4,11 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/am/am_results.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
@@ -36,82 +32,46 @@ void WindowSystem::RequestUpdate() {
|
||||
}
|
||||
|
||||
void WindowSystem::Update() {
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
||||
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
||||
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
||||
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
||||
|
||||
// Loop through all applets and remove terminated applets.
|
||||
this->PruneTerminatedAppletsLocked();
|
||||
// Loop through all applets and remove terminated applets.
|
||||
this->PruneTerminatedAppletsLocked();
|
||||
|
||||
// If the home menu is being locked into the foreground, handle that.
|
||||
if (!this->LockHomeMenuIntoForegroundLocked()) {
|
||||
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);
|
||||
}
|
||||
// If the home menu is being locked into the foreground, handle that.
|
||||
if (this->LockHomeMenuIntoForegroundLocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->NotifyApplicationChangedIfNeeded();
|
||||
bool overlay_blocks_input = false;
|
||||
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) {
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
ASSERT(m_home_menu == nullptr);
|
||||
m_home_menu = applet.get();
|
||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
m_overlay_display = applet.get();
|
||||
} else if (is_application) {
|
||||
ASSERT(m_application == nullptr);
|
||||
m_application = applet.get();
|
||||
}
|
||||
|
||||
this->UpdateCurrentApplicationLocked();
|
||||
|
||||
m_event_observer->TrackAppletProcess(*applet);
|
||||
m_applets.emplace(applet->aruid.pid, std::move(applet));
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
ASSERT(m_home_menu == nullptr);
|
||||
m_home_menu = applet.get();
|
||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
m_overlay_display = applet.get();
|
||||
} else if (is_application) {
|
||||
ASSERT(m_application == nullptr);
|
||||
m_application = applet.get();
|
||||
}
|
||||
|
||||
this->NotifyApplicationChangedIfNeeded();
|
||||
}
|
||||
|
||||
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);
|
||||
m_event_observer->TrackAppletProcess(*applet);
|
||||
m_applets.emplace(applet->aruid.pid, std::move(applet));
|
||||
}
|
||||
|
||||
std::shared_ptr<Applet> WindowSystem::GetByAppletResourceUserId(u64 aruid) {
|
||||
@@ -209,40 +169,18 @@ void WindowSystem::OnExitRequested() {
|
||||
}
|
||||
|
||||
void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) {
|
||||
const auto is_blocked = [message](const Applet& applet) {
|
||||
if (message == AppletMessage::DetectShortPressingHomeButton &&
|
||||
applet.applet_id == AppletId::OverlayDisplay &&
|
||||
!applet.overlay_watching_short_home_button) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (message) {
|
||||
case AppletMessage::DetectShortPressingHomeButton:
|
||||
return applet.home_button_short_pressed_blocked;
|
||||
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_home_menu) {
|
||||
std::scoped_lock lk_home{m_home_menu->lock};
|
||||
m_home_menu->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||
}
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
m_overlay_display->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||
}
|
||||
if (m_application) {
|
||||
std::scoped_lock lk_application{m_application->lock};
|
||||
m_application->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||
}
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
@@ -254,9 +192,19 @@ void WindowSystem::OnSystemButtonPress(SystemButtonType type) {
|
||||
case SystemButtonType::HomeButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton);
|
||||
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);
|
||||
break;
|
||||
// Force a state update after toggling overlay
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
break; }
|
||||
case SystemButtonType::CaptureButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton);
|
||||
break;
|
||||
@@ -369,8 +317,6 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
m_overlay_display = nullptr;
|
||||
}
|
||||
|
||||
this->UpdateCurrentApplicationLocked();
|
||||
|
||||
// Finalize applet.
|
||||
applet->OnProcessTerminatedLocked();
|
||||
|
||||
@@ -443,20 +389,7 @@ void WindowSystem::TerminateChildAppletsLocked(Applet* applet) {
|
||||
applet->lock.lock();
|
||||
}
|
||||
|
||||
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) {
|
||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking) {
|
||||
// With no applet, we don't have anything to do.
|
||||
if (!applet) {
|
||||
return;
|
||||
@@ -487,18 +420,24 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
||||
return false;
|
||||
}();
|
||||
|
||||
const bool is_overlay = applet->applet_id == AppletId::OverlayDisplay;
|
||||
|
||||
// Update visibility state.
|
||||
const bool should_be_visible =
|
||||
is_overlay ? applet->window_visible : (is_foreground && applet->window_visible);
|
||||
// Overlay applets should always be visible when window_visible is true, regardless of foreground state
|
||||
const bool should_be_visible = (applet->applet_id == AppletId::OverlayDisplay)
|
||||
? applet->window_visible
|
||||
: (is_foreground && applet->window_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);
|
||||
|
||||
applet->SetInteractibleLocked(needs_hid_input, needs_hid_input);
|
||||
const bool should_be_interactible = (applet->applet_id == AppletId::OverlayDisplay)
|
||||
? 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.
|
||||
const bool is_obscured = has_obscuring_child_applets || !applet->window_visible;
|
||||
@@ -514,18 +453,23 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
||||
applet->UpdateSuspensionStateLocked(true);
|
||||
}
|
||||
|
||||
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
||||
s32 z_index = Background;
|
||||
if (is_overlay) {
|
||||
z_index = Overlay;
|
||||
} else if (inherited_foreground) {
|
||||
z_index = is_obscured ? Foreground : ForegroundVisible;
|
||||
// Z-index logic like in reference C# implementation (tuned for overlay extremes)
|
||||
s32 z_index = 0;
|
||||
const bool now_foreground = inherited_foreground;
|
||||
if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
z_index = applet->overlay_in_foreground ? 100000 : -1;
|
||||
} else if (now_foreground && !is_obscured) {
|
||||
z_index = 2;
|
||||
} else if (now_foreground) {
|
||||
z_index = 1;
|
||||
} else {
|
||||
z_index = 0;
|
||||
}
|
||||
applet->display_layer_manager.SetOverlayZIndex(z_index);
|
||||
|
||||
// Recurse into child applets.
|
||||
for (const auto& child_applet : applet->child_applets) {
|
||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_takes_input);
|
||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_blocking);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
@@ -61,16 +60,11 @@ public:
|
||||
void OnPowerButtonPressed(ButtonPressDuration type) {}
|
||||
|
||||
private:
|
||||
void UpdateCurrentApplicationLocked();
|
||||
void NotifyApplicationChangedIfNeeded();
|
||||
void PruneTerminatedAppletsLocked();
|
||||
bool RestartAppletProcessLocked(Applet* applet);
|
||||
bool LockHomeMenuIntoForegroundLocked();
|
||||
void TerminateChildAppletsLocked(Applet* applet);
|
||||
bool IsOverlayOpenLocked(const Applet& overlay) const;
|
||||
bool DoesOverlayTakeInputLocked() const;
|
||||
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
|
||||
void SendButtonAppletMessageLocked(AppletMessage message);
|
||||
|
||||
private:
|
||||
@@ -94,7 +88,6 @@ private:
|
||||
|
||||
// Applet map by aruid.
|
||||
std::map<u64, std::shared_ptr<Applet>> m_applets{};
|
||||
std::optional<u64> m_pending_application_notification{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/am/am_results.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
@@ -32,10 +31,6 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 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) {
|
||||
std::vector<u64> add_on_content;
|
||||
const auto& rcu = system.GetContentProvider();
|
||||
@@ -96,7 +91,7 @@ IAddOnContentManager::~IAddOnContentManager() {
|
||||
Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||
|
||||
const auto current = GetCallerBaseTitleID(system, process_id);
|
||||
const auto current = system.GetApplicationProcessProgramID();
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||
@@ -117,7 +112,7 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id.pid);
|
||||
|
||||
const auto current = GetCallerBaseTitleID(system, process_id);
|
||||
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||
|
||||
std::vector<u32> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
@@ -131,7 +126,8 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
||||
}
|
||||
}
|
||||
|
||||
R_UNLESS(out.size() >= offset, AM::ResultApplicationRecordNotFound);
|
||||
// TODO(DarkLordZach): Find the correct error code.
|
||||
R_UNLESS(out.size() >= offset, ResultUnknown);
|
||||
|
||||
*out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||
@@ -145,7 +141,7 @@ Result IAddOnContentManager::GetAddOnContentBaseId(Out<u64> out_title_id,
|
||||
ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||
|
||||
const auto title_id = system.ResolveCallerProgramId(*process_id);
|
||||
const auto title_id = system.GetApplicationProcessProgramID();
|
||||
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ static u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
|
||||
return out;
|
||||
}
|
||||
|
||||
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_)
|
||||
: ServiceFramework{system_, "IBcatService"}, backend{backend_}, program_id{program_id_},
|
||||
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_)
|
||||
: ServiceFramework{system_, "IBcatService"}, backend{backend_},
|
||||
progress{{
|
||||
ProgressServiceBackend{system_, "Normal"},
|
||||
ProgressServiceBackend{system_, "Directory"},
|
||||
@@ -70,7 +70,8 @@ Result IBcatService::RequestSyncDeliveryCache(
|
||||
LOG_DEBUG(Service_BCAT, "called");
|
||||
|
||||
auto& progress_backend{GetProgressBackend(SyncType::Normal)};
|
||||
backend.Synchronize(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
backend.Synchronize(system.Kernel(), {system.GetApplicationProcessProgramID(),
|
||||
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
GetProgressBackend(SyncType::Normal));
|
||||
|
||||
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
||||
@@ -85,8 +86,9 @@ Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName(
|
||||
LOG_DEBUG(Service_BCAT, "called, name={}", name);
|
||||
|
||||
auto& progress_backend{GetProgressBackend(SyncType::Directory)};
|
||||
backend.SynchronizeDirectory(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
name, progress_backend);
|
||||
backend.SynchronizeDirectory(system.Kernel(), {system.GetApplicationProcessProgramID(),
|
||||
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
name, progress_backend);
|
||||
|
||||
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
||||
system, progress_backend.GetEvent(), progress_backend.GetImpl());
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -22,7 +19,7 @@ class IDeliveryCacheProgressService;
|
||||
|
||||
class IBcatService final : public ServiceFramework<IBcatService> {
|
||||
public:
|
||||
explicit IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_);
|
||||
explicit IBcatService(Core::System& system_, BcatBackend& backend_);
|
||||
~IBcatService() override;
|
||||
|
||||
private:
|
||||
@@ -42,7 +39,6 @@ private:
|
||||
const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
|
||||
|
||||
BcatBackend& backend;
|
||||
u64 program_id;
|
||||
std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
|
||||
};
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ std::vector<u8> default_logo_small;
|
||||
std::vector<u8> default_logo_large;
|
||||
bool default_logos_loaded = false;
|
||||
|
||||
boost::unordered::unordered_flat_map<std::string, std::vector<u8>> news_images_small;
|
||||
boost::unordered::unordered_flat_map<std::string, std::vector<u8>> news_images_large;
|
||||
ankerl::unordered_dense::map<std::string, std::vector<u8>> news_images_small;
|
||||
ankerl::unordered_dense::map<std::string, std::vector<u8>> news_images_large;
|
||||
std::mutex images_mutex;
|
||||
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
@@ -94,7 +93,7 @@ private:
|
||||
static s64 Now();
|
||||
|
||||
mutable std::mutex mtx;
|
||||
boost::unordered::unordered_flat_map<std::string, StoredNews> items;
|
||||
ankerl::unordered_dense::map<std::string, StoredNews> items;
|
||||
size_t open_counter{};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/bcat/bcat_service.h"
|
||||
#include "core/hle/service/bcat/delivery_cache_storage_service.h"
|
||||
#include "core/hle/service/bcat/service_creator.h"
|
||||
@@ -41,8 +37,7 @@ IServiceCreator::~IServiceCreator() = default;
|
||||
Result IServiceCreator::CreateBcatService(ClientProcessId process_id,
|
||||
OutInterface<IBcatService> out_interface) {
|
||||
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
||||
*out_interface =
|
||||
std::make_shared<IBcatService>(system, *backend, system.ResolveCallerProgramId(*process_id));
|
||||
*out_interface = std::make_shared<IBcatService>(system, *backend);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -50,7 +45,7 @@ Result IServiceCreator::CreateDeliveryCacheStorageService(
|
||||
ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
|
||||
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
||||
|
||||
const auto title_id = system.ResolveCallerProgramId(*process_id);
|
||||
const auto title_id = system.GetApplicationProcessProgramID();
|
||||
*out_interface =
|
||||
std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -243,15 +243,10 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
|
||||
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: ???
|
||||
|
||||
const u64 title_id = system.ResolveCallerProgramId(aruid);
|
||||
|
||||
const u64 title_id = system.GetApplicationProcessProgramID();
|
||||
auto static_service =
|
||||
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/fs/fs.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -89,7 +88,7 @@ private:
|
||||
AlbumFileDateTime ConvertToAlbumDateTime(u64 posix_time) const;
|
||||
|
||||
bool is_mounted{};
|
||||
boost::unordered::unordered_flat_map<AlbumFileId, std::filesystem::path> album_files;
|
||||
ankerl::unordered_dense::map<AlbumFileId, std::filesystem::path> album_files;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
@@ -95,8 +95,7 @@ void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption r
|
||||
manager->FlipVerticallyOnWrite(invert_y);
|
||||
manager->SaveScreenShot(entry, attribute, report_option, image_data, {});
|
||||
},
|
||||
layout,
|
||||
Nvnflinger::LayerStackId::Screenshot);
|
||||
layout);
|
||||
}
|
||||
|
||||
} // namespace Service::Capture
|
||||
|
||||
@@ -227,13 +227,12 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
|
||||
std::string src_path(Common::FS::SanitizePath(src_path_));
|
||||
std::string dest_path(Common::FS::SanitizePath(dest_path_));
|
||||
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
||||
if (src == nullptr)
|
||||
return FileSys::ResultPathNotFound;
|
||||
|
||||
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
|
||||
std::string full_src_path = backing->GetFullPath() + "/" + src_path;
|
||||
std::string full_dest_path = backing->GetFullPath() + "/" + dest_path;
|
||||
if (!Common::FS::RenameDir(full_src_path, full_dest_path)) {
|
||||
// Use more-optimized vfs implementation rename.
|
||||
if (src == nullptr)
|
||||
return FileSys::ResultPathNotFound;
|
||||
if (!src->Rename(Common::FS::GetFilename(dest_path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
return ResultSuccess;
|
||||
|
||||
@@ -24,7 +24,7 @@ IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGe
|
||||
{3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
|
||||
{4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
|
||||
{5, D<&IFileSystem::RenameFile>, "RenameFile"},
|
||||
{6, D<&IFileSystem::RenameDirectory>, "RenameDirectory"},
|
||||
{6, nullptr, "RenameDirectory"},
|
||||
{7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
|
||||
{8, D<&IFileSystem::OpenFile>, "OpenFile"},
|
||||
{9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
|
||||
@@ -88,14 +88,6 @@ Result IFileSystem::RenameFile(
|
||||
R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
||||
}
|
||||
|
||||
Result IFileSystem::RenameDirectory(
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path) {
|
||||
LOG_DEBUG(Service_FS, "called. directory '{}' to directory '{}'", old_path->str, new_path->str);
|
||||
|
||||
R_RETURN(backend->RenameDirectory(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
||||
}
|
||||
|
||||
Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||
u32 mode) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -39,8 +36,6 @@ public:
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||
Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
||||
Result RenameDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
||||
Result OpenFile(OutInterface<IFile> out_interface,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
|
||||
Result OpenDirectory(OutInterface<IDirectory> out_interface,
|
||||
|
||||
@@ -1164,7 +1164,7 @@ Result IHidServer::InitializeSevenSixAxisSensor(ClientAppletResourceUserId aruid
|
||||
GetResourceManager()->GetConsoleSixAxis()->Activate();
|
||||
GetResourceManager()->GetSevenSixAxis()->Activate();
|
||||
|
||||
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress(), t_mem_1->GetOwner());
|
||||
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode pollin
|
||||
|
||||
auto& device = devices[device_index.value()].device;
|
||||
device->SetPollingMode(polling_mode);
|
||||
device->SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
|
||||
device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -150,7 +147,7 @@ Result IRS::RunImageTransferProcessor(
|
||||
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
||||
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
||||
image_transfer_processor.SetConfig(processor_config);
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
||||
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
||||
Common::Input::PollingMode::IR);
|
||||
|
||||
@@ -298,8 +295,7 @@ Result IRS::RunImageTransferExProcessor(
|
||||
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
||||
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
||||
image_transfer_processor.SetConfig(processor_config);
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(),
|
||||
t_mem->GetOwner());
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
||||
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
||||
Common::Input::PollingMode::IR);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
, process{kernel, process_}
|
||||
, user_rx{std::move(user_rx_)}
|
||||
, user_ro{std::move(user_ro_)}
|
||||
, context{process_->GetMemory()}
|
||||
, context{system_.ApplicationMemory()}
|
||||
{
|
||||
|
||||
// clang-format off
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
#include <random>
|
||||
#include <span>
|
||||
#include <thread>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/socket_types.h"
|
||||
@@ -120,7 +119,7 @@ protected:
|
||||
std::array<LanStation, StationCountMax> stations;
|
||||
std::array<NodeLatestUpdate, NodeCountMax> node_changes{};
|
||||
std::array<u8, NodeCountMax> node_last_states{};
|
||||
boost::unordered::unordered_flat_map<MacAddress, NetworkInfo, MACAddressHash> scan_results{};
|
||||
ankerl::unordered_dense::map<MacAddress, NetworkInfo, MACAddressHash> scan_results{};
|
||||
NodeInfo node_info{};
|
||||
NetworkInfo network_info{};
|
||||
State state{State::None};
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
#include <string>
|
||||
|
||||
#include <optional>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include "common/logging.h"
|
||||
#include "core/core.h"
|
||||
@@ -332,7 +331,7 @@ private:
|
||||
};
|
||||
static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size");
|
||||
|
||||
boost::unordered::unordered_flat_map<LogPacketHeaderEntry, std::vector<u8>> entries{};
|
||||
ankerl::unordered_dense::map<LogPacketHeaderEntry, std::vector<u8>> entries{};
|
||||
LogDestination destination{LogDestination::All};
|
||||
};
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <common/settings.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -9,12 +9,15 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "common/stb.h"
|
||||
#define STBI_ONLY_JPEG 1
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.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/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ns/language.h"
|
||||
@@ -333,8 +336,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
||||
|
||||
constexpr s32 data_offset = 0;
|
||||
|
||||
if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) {
|
||||
auto& memory = t_mem->GetOwner()->GetMemory();
|
||||
if (t_mem != nullptr && app_count > 0) {
|
||||
auto& memory = system.ApplicationMemory();
|
||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "core/device_memory_manager.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
@@ -162,7 +161,7 @@ private:
|
||||
std::list<std::shared_ptr<Handle>> unmap_queue{};
|
||||
std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue`
|
||||
|
||||
boost::unordered::unordered_flat_map<Handle::Id, std::shared_ptr<Handle>>
|
||||
ankerl::unordered_dense::map<Handle::Id, std::shared_ptr<Handle>>
|
||||
handles{}; //!< Main owning map of handles
|
||||
std::mutex handles_lock; //!< Protects access to `handles`
|
||||
|
||||
|
||||
@@ -77,7 +77,6 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
|
||||
.transform_flags = layer.transform,
|
||||
.crop_rect = layer.crop_rect,
|
||||
.blending = ConvertBlending(layer.blending),
|
||||
.layer_stack_mask = layer.layer_stack_mask,
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/address_space.h"
|
||||
@@ -114,7 +113,7 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
|
||||
|
||||
boost::unordered::unordered_flat_set<s64_le> map_buffer_offsets{};
|
||||
ankerl::unordered_dense::set<s64_le> map_buffer_offsets{};
|
||||
|
||||
struct IoctlMapBufferEx {
|
||||
MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable
|
||||
|
||||
@@ -69,7 +69,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
||||
case 0x3:
|
||||
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output);
|
||||
case 0x8:
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, false);
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, false);
|
||||
case 0x9:
|
||||
return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output);
|
||||
case 0xb:
|
||||
@@ -83,7 +83,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
||||
case 0x1a:
|
||||
return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output, fd);
|
||||
case 0x1b:
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, true);
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, true);
|
||||
case 0x1d:
|
||||
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output);
|
||||
default:
|
||||
@@ -387,19 +387,8 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL
|
||||
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,
|
||||
std::span<Tegra::CommandListHeader> commands, DeviceFD fd,
|
||||
bool kickoff) {
|
||||
std::span<Tegra::CommandListHeader> commands, bool kickoff) {
|
||||
if (params.num_entries > commands.size()) {
|
||||
UNIMPLEMENTED();
|
||||
return NvResult::InvalidSize;
|
||||
@@ -407,7 +396,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
||||
|
||||
Tegra::CommandList entries(params.num_entries);
|
||||
if (kickoff) {
|
||||
this->GetSessionMemory(fd).ReadBlock(params.address, entries.command_lists.data(),
|
||||
system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(),
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
} else {
|
||||
std::memcpy(entries.command_lists.data(), commands.data(),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user