mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-14 04:24:31 +00:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ee7b981a6 | |||
| 2e1f4cb84b | |||
| baaf9a628c | |||
| bcaeece578 | |||
| 7a3ab68439 | |||
| c784f06704 | |||
| e662108584 | |||
| 29f712d3b5 | |||
| ae75ba4883 | |||
| 7b750bf5bb | |||
| 94e9c2bd74 | |||
| 9677522e6c | |||
| f423471cc0 | |||
| ce043aceb9 | |||
| 1643d8766f | |||
| fe1b8ff0ab | |||
| f22fd1714b | |||
| 6119e8fad2 | |||
| bfc0e60333 | |||
| 072be286d1 | |||
| 66b073ab8d | |||
| 4c65780f11 | |||
| a9c4c8aefd | |||
| a769505a45 | |||
| 54c3d10b86 | |||
| bdf60d79a0 | |||
| 2068b5d452 | |||
| 1b482fa99b | |||
| b6ee847947 | |||
| 6c16440996 | |||
| 09c583506b | |||
| 894add43f3 | |||
| d142b5dd6a | |||
| 0c2894eabf | |||
| d8a8169eb2 | |||
| 81c6e56713 | |||
| b4b41ee62c | |||
| 0d6a2158f0 | |||
| 09b6b3b71e |
@@ -1,20 +0,0 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 16c6092..9e75548 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -8,7 +8,14 @@ project(adrenotools LANGUAGES CXX C)
|
||||
|
||||
set(GEN_INSTALL_TARGET OFF CACHE BOOL "")
|
||||
|
||||
-add_subdirectory(lib/linkernsbypass)
|
||||
+include(CPM)
|
||||
+set(CPM_USE_LOCAL_PACKAGES OFF)
|
||||
+
|
||||
+CPMAddPackage(
|
||||
+ NAME linkernsbypass
|
||||
+ URL "https://github.com/bylaws/liblinkernsbypass/archive/aa3975893d.zip"
|
||||
+ URL_HASH SHA512=43d3d146facb7ec99d066a9b8990369ab7b9eec0d5f9a67131b0a0744fde0af27d884ca1f2a272cd113718a23356530ed97703c8c0659c4c25948d50c106119e
|
||||
+)
|
||||
|
||||
set(LIB_SOURCES src/bcenabler.cpp
|
||||
src/driver.cpp
|
||||
@@ -0,0 +1,26 @@
|
||||
From 52bbc5af6523daa22ad62fe4b84bc8d623d11a53 Mon Sep 17 00:00:00 2001
|
||||
From: crueter <crueter@eden-emu.dev>
|
||||
Date: Fri, 26 Jun 2026 01:09:40 -0400
|
||||
Subject: [PATCH] use cpmfile def for linkernsbypass
|
||||
|
||||
---
|
||||
CMakeLists.txt | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 16c6092..85b242c 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -8,7 +8,8 @@ project(adrenotools LANGUAGES CXX C)
|
||||
|
||||
set(GEN_INSTALL_TARGET OFF CACHE BOOL "")
|
||||
|
||||
-add_subdirectory(lib/linkernsbypass)
|
||||
+include(CPMUtil)
|
||||
+AddJsonPackage(linkernsbypass)
|
||||
|
||||
set(LIB_SOURCES src/bcenabler.cpp
|
||||
src/driver.cpp
|
||||
--
|
||||
2.54.0
|
||||
|
||||
+1
-1
@@ -270,7 +270,7 @@ if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
|
||||
set(abi ${CMAKE_ANDROID_ARCH_ABI})
|
||||
|
||||
set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/${abi}/")
|
||||
file(COPY "${VVL_SOURCE_DIR}/${abi}/libVkLayer_khronos_validation.so"
|
||||
file(COPY "${vulkan-validation-layers_SOURCE_DIR}/${abi}/libVkLayer_khronos_validation.so"
|
||||
DESTINATION "${vvl_lib_path}")
|
||||
endif()
|
||||
|
||||
|
||||
+143
-1181
File diff suppressed because it is too large
Load Diff
+639
-232
File diff suppressed because it is too large
Load Diff
+45
-79
@@ -3,11 +3,10 @@
|
||||
"hash": "1229f345b014f7ca544dedb4edb3311e41ba736f9aa9a67f88b5f26f3c983288c6bb6cdedcfb0b8a02c63088a37e6a0d7ba97d9c2a4d721b213916327cffe28a",
|
||||
"min_version": "0.9.1",
|
||||
"repo": "lioncash/biscuit",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "0.19.0"
|
||||
"version": "v0.19.0"
|
||||
},
|
||||
"boost": {
|
||||
"artifact": "%TAG%-cmake.tar.xz",
|
||||
"artifact": "%VERSION%-cmake.tar.xz",
|
||||
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
|
||||
"hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
|
||||
"min_version": "1.57",
|
||||
@@ -16,15 +15,13 @@
|
||||
"0001-clang-cl.patch"
|
||||
],
|
||||
"repo": "boostorg/boost",
|
||||
"tag": "boost-%VERSION%",
|
||||
"version": "1.90.0"
|
||||
"version": "boost-1.90.0"
|
||||
},
|
||||
"boost_headers": {
|
||||
"bundled": true,
|
||||
"hash": "4ef845775e2277a8104ded6ddf749aa262ce52cf8438042869a048f9a0156dd772fbbcfa74efa1378fecef339b7286f6fe4b4feb5c45d49966b35d08e3e83507",
|
||||
"repo": "boostorg/headers",
|
||||
"tag": "boost-%VERSION%",
|
||||
"version": "1.90.0"
|
||||
"version": "boost-1.90.0"
|
||||
},
|
||||
"catch2": {
|
||||
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
|
||||
@@ -34,8 +31,7 @@
|
||||
"0001-solaris-isnan-fix.patch"
|
||||
],
|
||||
"repo": "catchorg/Catch2",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "3.13.0"
|
||||
"version": "v3.13.0"
|
||||
},
|
||||
"cpp-jwt": {
|
||||
"find_args": "CONFIG",
|
||||
@@ -48,8 +44,7 @@
|
||||
"0001-fix-missing-decl.patch"
|
||||
],
|
||||
"repo": "arun11299/cpp-jwt",
|
||||
"sha": "7f24eb4c32",
|
||||
"version": "1.5.1"
|
||||
"version": "7f24eb4c32"
|
||||
},
|
||||
"cubeb": {
|
||||
"find_args": "CONFIG",
|
||||
@@ -61,31 +56,26 @@
|
||||
"BUNDLE_SPEEX ON"
|
||||
],
|
||||
"repo": "mozilla/cubeb",
|
||||
"sha": "fa02160712",
|
||||
"version": "0.0.0"
|
||||
"version": "fa02160712"
|
||||
},
|
||||
"discord-rpc": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "8213c43dcb0f7d479f5861091d111ed12fbdec1e62e6d729d65a4bc181d82f48a35d5fd3cd5c291f2393ac7c9681eabc6b76609755f55376284c8a8d67e148f3",
|
||||
"package": "DiscordRPC",
|
||||
"repo": "eden-emulator/discord-rpc",
|
||||
"sha": "0d8b2d6a37",
|
||||
"version": "3.4.1"
|
||||
"version": "0d8b2d6a37"
|
||||
},
|
||||
"enet": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "a0d2fa8c957704dd49e00a726284ac5ca034b50b00d2b20a94fa1bbfbb80841467834bfdc84aa0ed0d6aab894608fd6c86c3b94eee46343f0e6d9c22e391dbf9",
|
||||
"min_version": "1.3",
|
||||
"repo": "lsalzman/enet",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.3.18"
|
||||
"version": "v1.3.18"
|
||||
},
|
||||
"ffmpeg": {
|
||||
"bundled": true,
|
||||
"hash": "ed177621176b3961bdcaa339187d3a7688c1c8b060b79c4bb0257cbc67ad7021ae5d5adca5303b45625abbbe3d9aafdd87ce777b8690ac295290d744c875489a",
|
||||
"repo": "FFmpeg/FFmpeg",
|
||||
"sha": "c7b5f1537d",
|
||||
"version": "8.0.1"
|
||||
"version": "c7b5f1537d"
|
||||
},
|
||||
"ffmpeg-ci": {
|
||||
"ci": true,
|
||||
@@ -99,23 +89,20 @@
|
||||
"hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2",
|
||||
"min_version": "8",
|
||||
"repo": "fmtlib/fmt",
|
||||
"tag": "%VERSION%",
|
||||
"version": "12.1.0"
|
||||
},
|
||||
"frozen": {
|
||||
"hash": "b8dfe741c82bc178dfc9749d4ab5a130cee718d9ee7b71d9b547cf5f7f23027ed0152ad250012a8546399fcc1e12187efc68d89d6731256c4d2df7d04eef8d5c",
|
||||
"package": "frozen",
|
||||
"repo": "serge-sans-paille/frozen",
|
||||
"sha": "61dce5ae18",
|
||||
"version": "1.2.0"
|
||||
"version": "61dce5ae18"
|
||||
},
|
||||
"gamemode": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "e87ec14ed3e826d578ebf095c41580069dda603792ba91efa84f45f4571a28f4d91889675055fd6f042d7dc25b0b9443daf70963ae463e38b11bcba95f4c65a9",
|
||||
"min_version": "1.7",
|
||||
"repo": "FeralInteractive/gamemode",
|
||||
"sha": "ce6fe122f3",
|
||||
"version": "1.8.2"
|
||||
"version": "ce6fe122f3"
|
||||
},
|
||||
"httplib": {
|
||||
"find_args": "MODULE GLOBAL",
|
||||
@@ -128,23 +115,20 @@
|
||||
"0001-mingw.patch"
|
||||
],
|
||||
"repo": "yhirose/cpp-httplib",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "0.46.0"
|
||||
"version": "v0.46.0"
|
||||
},
|
||||
"lagoon": {
|
||||
"hash": "b9380f99c6effaeccc6d8f81d4942e852c11ad28613df637e155451556ae5826f93765bee57a5c87a9740d2bd1db463ad0f55a947772fe9d57eeabae3efa373e",
|
||||
"repo": "loongson-community/lagoon",
|
||||
"tag": "%VERSION%",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"libadrenotools": {
|
||||
"hash": "f6526620cb752876edc5ed4c0925d57b873a8218ee09ad10859ee476e9333259784f61c1dcc55a2bcba597352d18aff22cd2e4c1925ec2ae94074e09d7da2265",
|
||||
"patches": [
|
||||
"0001-linkerns-cpm.patch"
|
||||
"0001-use-cpmfile-def-for-linkernsbypass.patch"
|
||||
],
|
||||
"repo": "eden-emulator/libadrenotools",
|
||||
"sha": "8ba23b42d7",
|
||||
"version": "1.0.0"
|
||||
"version": "8ba23b42d7"
|
||||
},
|
||||
"libusb": {
|
||||
"find_args": "MODULE",
|
||||
@@ -153,53 +137,52 @@
|
||||
"0001-netbsd-gettime.patch"
|
||||
],
|
||||
"repo": "libusb/libusb",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.0.29"
|
||||
"version": "v1.0.29"
|
||||
},
|
||||
"linkernsbypass": {
|
||||
"bundled": "true",
|
||||
"hash": "bbe3f1f08e2bc7172b36e8f052912cc374289fc9a8e5a39ae7547a0c232de8f57ba24883451896b7a9a5d1be0e4de5c5b0f70c2022eda18d7d5a754847521800",
|
||||
"repo": "bylaws/liblinkernsbypass",
|
||||
"version": "aa3975893d"
|
||||
},
|
||||
"llvm-mingw": {
|
||||
"artifact": "clang-rt-builtins.tar.zst",
|
||||
"git_host": "git.eden-emu.dev",
|
||||
"hash": "d902392caf94e84f223766e2cc51ca5fab6cae36ab8dc6ef9ef6a683ab1c483bfcfe291ef0bd38ab16a4ecc4078344fa8af72da2f225ab4c378dee23f6186181",
|
||||
"repo": "eden-emu/llvm-mingw",
|
||||
"tag": "%VERSION%",
|
||||
"version": "20250828"
|
||||
},
|
||||
"lz4": {
|
||||
"hash": "35c21a5d9cfb5bbf314a5321d02b36819491d2ee3cf8007030ca09d13ca4dae672247b7aeab553e973093604fc48221cb03dc92197c6efe8fc3746891363fdab",
|
||||
"name": "lz4",
|
||||
"repo": "lz4/lz4",
|
||||
"sha": "ebb370ca83",
|
||||
"source_subdir": "build/cmake",
|
||||
"version": "1.10.0"
|
||||
"version": "ebb370ca83"
|
||||
},
|
||||
"moltenvk": {
|
||||
"artifact": "MoltenVK-macOS.tar",
|
||||
"bundled": true,
|
||||
"hash": "5695b36ca5775819a71791557fcb40a4a5ee4495be6b8442e0b666d0c436bec02aae68cc6210183f7a5c986bdbec0e117aecfad5396e496e9c2fd5c89133a347",
|
||||
"repo": "V380-Ori/Ryujinx.MoltenVK",
|
||||
"tag": "v%VERSION%-ryujinx",
|
||||
"version": "1.4.1"
|
||||
"version": "v1.4.1-ryujinx"
|
||||
},
|
||||
"nlohmann": {
|
||||
"hash": "6cc1e86261f8fac21cc17a33da3b6b3c3cd5c116755651642af3c9e99bb3538fd42c1bd50397a77c8fb6821bc62d90e6b91bcdde77a78f58f2416c62fc53b97d",
|
||||
"min_version": "3.8",
|
||||
"package": "nlohmann_json",
|
||||
"repo": "nlohmann/json",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "3.12.0"
|
||||
"version": "v3.12.0"
|
||||
},
|
||||
"oaknut": {
|
||||
"hash": "9697e80a7d5d9bcb3ce51051a9a24962fb90ca79d215f1f03ae6b58da8ba13a63b5dda1b4dde3d26ac6445029696b8ef2883f4e5a777b342bba01283ed293856",
|
||||
"min_version": "2.0.1",
|
||||
"repo": "eden-emulator/oaknut",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "2.0.3"
|
||||
"version": "v2.0.3"
|
||||
},
|
||||
"oboe": {
|
||||
"bundled": true,
|
||||
"hash": "ce4011afe7345370d4ead3b891cd69a5ef224b129535783586c0ca75051d303ed446e6c7f10bde8da31fff58d6e307f1732a3ffd03b249f9ef1fd48fd4132715",
|
||||
"repo": "google/oboe",
|
||||
"tag": "%VERSION%",
|
||||
"version": "1.10.0"
|
||||
},
|
||||
"openssl": {
|
||||
@@ -210,8 +193,7 @@
|
||||
"0001-add-bundled-cert.patch"
|
||||
],
|
||||
"repo": "openssl/openssl",
|
||||
"tag": "openssl-%VERSION%",
|
||||
"version": "3.6.2"
|
||||
"version": "openssl-3.6.2"
|
||||
},
|
||||
"openssl-ci": {
|
||||
"ci": true,
|
||||
@@ -234,7 +216,6 @@
|
||||
"0004-use-shell-wrapper.patch"
|
||||
],
|
||||
"repo": "jimmy-park/openssl-cmake",
|
||||
"tag": "%VERSION%",
|
||||
"version": "3.6.2"
|
||||
},
|
||||
"opus": {
|
||||
@@ -250,8 +231,7 @@
|
||||
"0002-no-install.patch"
|
||||
],
|
||||
"repo": "xiph/opus",
|
||||
"sha": "a3f0ec02b3",
|
||||
"version": "1.5.2"
|
||||
"version": "a3f0ec02b3"
|
||||
},
|
||||
"quazip": {
|
||||
"hash": "609c240c7f029ac26a37d8fbab51bc16284e05e128b78b9b9c0e95d083538c36047a67d682759ac990e4adb0eeb90f04f1ea7fe2253bbda7e7e3bcce32e53dd8",
|
||||
@@ -264,16 +244,14 @@
|
||||
],
|
||||
"package": "QuaZip-Qt6",
|
||||
"repo": "stachenov/quazip",
|
||||
"sha": "2e95c9001b",
|
||||
"version": "1.5"
|
||||
"version": "2e95c9001b"
|
||||
},
|
||||
"sdl3": {
|
||||
"hash": "df5a323af7ac366661a3c0e887969c72584d232f3cc211419d59b0487b620b6b2859d4549c9e8df002ee489290062e466fcfddf7edc0872a37b1f2845e81c0f3",
|
||||
"min_version": "3.2.10",
|
||||
"package": "SDL3",
|
||||
"repo": "libsdl-org/SDL",
|
||||
"tag": "release-%VERSION%",
|
||||
"version": "3.4.8"
|
||||
"version": "release-3.4.8"
|
||||
},
|
||||
"sdl3-ci": {
|
||||
"ci": true,
|
||||
@@ -288,19 +266,16 @@
|
||||
"hash": "b937c18a7b6277d77ca7ebfb216af4984810f77af4c32d101b7685369a4bd5eb61406223f82698e167e6311a728d07415ab59639fdf19eff71ad6dc2abfda989",
|
||||
"package": "SimpleIni",
|
||||
"repo": "brofield/simpleini",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "4.25"
|
||||
"version": "v4.25"
|
||||
},
|
||||
"sirit": {
|
||||
"artifact": "sirit-source-%VERSION%.tar.zst",
|
||||
"find_args": "CONFIG",
|
||||
"hash": "b7cd6885acae3fc8698288d19febba0dd45e4a02a2b6563d2eb995a988d0847046e8d16a5dea7e0bf832b4321bcec134cdbafc553f6ede039e4cf34525c5dce5",
|
||||
"options": [
|
||||
"SIRIT_USE_SYSTEM_SPIRV_HEADERS ON"
|
||||
],
|
||||
"repo": "eden-emulator/sirit",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.0.5",
|
||||
"hash": "10b3ff60bdcad428bb4f54360ff749212333a1d24c0b3ed99e466b1bfcf99d2db6cf596c0f965854a2095dfef9b7ce4e045edb070fa9f76eb3b295ab03a4a293"
|
||||
"version": "v1.0.5"
|
||||
},
|
||||
"sirit-ci": {
|
||||
"ci": true,
|
||||
@@ -310,13 +285,13 @@
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"spirv-headers": {
|
||||
"hash": "cae8cd179c9013068876908fecc1d158168310ad6ac250398a41f0f5206ceff6469e2aaeab9c820bce9d1b08950c725c89c46e94b89a692be9805432cf749396",
|
||||
"hash": "d624371dd455c66a300344c89812598ffe11b5eedba555779f789e85c29dc67317741858c60e0744a1e6755cc0d2759b8659f0674f4cc31479c4cb6fc25ed23b",
|
||||
"options": [
|
||||
"SPIRV_WERROR OFF"
|
||||
],
|
||||
"package": "SPIRV-Headers",
|
||||
"repo": "KhronosGroup/SPIRV-Headers",
|
||||
"sha": "04f10f650d"
|
||||
"version": "vulkan-sdk-1.4.341.0"
|
||||
},
|
||||
"tzdb": {
|
||||
"artifact": "%VERSION%.tar.gz",
|
||||
@@ -324,7 +299,6 @@
|
||||
"hash": "cce65a12bf90f4ead43b24a0b95dfad77ac3d9bfbaaf66c55e6701346e7a1e44ca5d2f23f47ee35ee02271eb1082bf1762af207aad9fb236f1c8476812d008ed",
|
||||
"package": "nx_tzdb",
|
||||
"repo": "eden-emu/tzdb_to_nx",
|
||||
"tag": "%VERSION%",
|
||||
"version": "230326"
|
||||
},
|
||||
"unordered-dense": {
|
||||
@@ -336,46 +310,40 @@
|
||||
"0001-avoid-memset-when-clearing-an-empty-table.patch"
|
||||
],
|
||||
"repo": "martinus/unordered_dense",
|
||||
"sha": "7b55cab841",
|
||||
"version": "4.8.1"
|
||||
"version": "7b55cab841"
|
||||
},
|
||||
"vulkan-headers": {
|
||||
"hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b",
|
||||
"min_version": "1.4.317",
|
||||
"package": "VulkanHeaders",
|
||||
"repo": "KhronosGroup/Vulkan-Headers",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.4.345"
|
||||
"version": "v1.4.345"
|
||||
},
|
||||
"vulkan-memory-allocator": {
|
||||
"find_args": "CONFIG",
|
||||
"hash": "deb5902ef8db0e329fbd5f3f4385eb0e26bdd9f14f3a2334823fb3fe18f36bc5d235d620d6e5f6fe3551ec3ea7038638899db8778c09f6d5c278f5ff95c3344b",
|
||||
"package": "VulkanMemoryAllocator",
|
||||
"repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "3.3.0"
|
||||
"version": "v3.3.0"
|
||||
},
|
||||
"vulkan-utility-libraries": {
|
||||
"hash": "114f6b237a6dcba923ccc576befb5dea3f1c9b3a30de7dc741f234a831d1c2d52d8a224afb37dd57dffca67ac0df461eaaab6a5ab5e503b393f91c166680c3e1",
|
||||
"package": "VulkanUtilityLibraries",
|
||||
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.4.345"
|
||||
"version": "v1.4.345"
|
||||
},
|
||||
"vulkan-validation-layers": {
|
||||
"artifact": "android-binaries-%VERSION%.zip",
|
||||
"artifact": "android-binaries-%NUMERIC_VERSION%.zip",
|
||||
"hash": "8812ae84cbe49e6a3418ade9c458d3be6d74a3dffd319d4502007b564d580998056e8190414368ec11b27bc83993c7a0dad713c31bcc3d9553b51243efee3753",
|
||||
"package": "VVL",
|
||||
"numeric_version": "1.4.341.0",
|
||||
"repo": "KhronosGroup/Vulkan-ValidationLayers",
|
||||
"tag": "vulkan-sdk-%VERSION%",
|
||||
"version": "1.4.341.0"
|
||||
"version": "vulkan-sdk-%NUMERIC_VERSION%"
|
||||
},
|
||||
"xbyak": {
|
||||
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
|
||||
"package": "xbyak",
|
||||
"repo": "herumi/xbyak",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "7.35.2"
|
||||
"version": "v7.35.2"
|
||||
},
|
||||
"zlib": {
|
||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||
@@ -386,8 +354,7 @@
|
||||
],
|
||||
"package": "ZLIB",
|
||||
"repo": "madler/zlib",
|
||||
"tag": "v%VERSION%",
|
||||
"version": "1.3.2"
|
||||
"version": "v1.3.2"
|
||||
},
|
||||
"zstd": {
|
||||
"find_args": "MODULE",
|
||||
@@ -397,8 +364,7 @@
|
||||
"ZSTD_BUILD_SHARED OFF"
|
||||
],
|
||||
"repo": "facebook/zstd",
|
||||
"sha": "b8d6101fba",
|
||||
"source_subdir": "build/cmake",
|
||||
"version": "1.5.7"
|
||||
"version": "b8d6101fba"
|
||||
}
|
||||
}
|
||||
|
||||
+103
-51
@@ -8,6 +8,7 @@ CPMUtil is a wrapper around CPM that aims to reduce boilerplate and add useful u
|
||||
- [Common Properties](#common-properties)
|
||||
- [Standard Packages](#standard-packages)
|
||||
- [Versioning](#versioning)
|
||||
- [Artifact Naming Errata](#artifact-naming-errata)
|
||||
- [Patches](#patches)
|
||||
- [Pre-built CI Packages](#pre-built-ci-packages)
|
||||
- [Usage](#usage)
|
||||
@@ -22,12 +23,18 @@ CPMUtil is a wrapper around CPM that aims to reduce boilerplate and add useful u
|
||||
- [Addendum: Module Path Packages](#addendum-module-path-packages)
|
||||
- [Example: OpenSSL](#example-openssl)
|
||||
- [Addendum: Adding Qt](#addendum-adding-qt)
|
||||
- [Addendum: Package-Specific Overrides](#addendum-package-specific-overrides)
|
||||
- [Addendum: Supply-Chain Security](#addendum-supply-chain-security)
|
||||
- [Checksumming](#checksumming)
|
||||
- [Caching](#caching)
|
||||
- [Immutable Commit Hashes](#immutable-commit-hashes)
|
||||
|
||||
## Global Options
|
||||
|
||||
- `CPMUTIL_FORCE_SYSTEM` (default `OFF`): Require all CPM dependencies to use system packages. NOT RECOMMENDED!
|
||||
- You may optionally override this (section)
|
||||
- `CPMUTIL_FORCE_SYSTEM` (default `OFF`): Require all CPM dependencies to use system packages.
|
||||
- You may optionally override this for each package: [Package-Specific Overrides](#addendum-package-specific-overrides)
|
||||
- `CPMUTIL_FORCE_BUNDLED` (default `ON` on MSVC and Android, `OFF` elsewhere): Require all CPM dependencies to use bundled packages.
|
||||
- You may optionally override this for each package: [Package-Specific Overrides](#addendum-package-specific-overrides)
|
||||
- `CPMUTIL_PATCH_DIR` (default `${PROJECT_SOURCE_DIR}/.patch`): Path to patches used in packages. Stored as `<PATCH DIR>/json-package-name/0001-patch-name.patch`, etc.
|
||||
- `CPM_SOURCE_CACHE` (default `${PROJECT_SOURCE_DIR}/.cache/cpm`): Where downloaded dependencies get stored.
|
||||
|
||||
@@ -50,23 +57,25 @@ And may optionally define other properties like:
|
||||
For instance:
|
||||
|
||||
```json
|
||||
"fmt": {
|
||||
"repo": "fmtlib/fmt",
|
||||
"tag": "12.1.0",
|
||||
"hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2",
|
||||
"min_version": "8",
|
||||
"options": [
|
||||
"FMT_TEST ON"
|
||||
],
|
||||
"patches": [
|
||||
"0001-disable-reference-copy.patch"
|
||||
]
|
||||
{
|
||||
"fmt": {
|
||||
"repo": "fmtlib/fmt",
|
||||
"version": "12.1.0",
|
||||
"hash": "f0da8...23f2",
|
||||
"min_version": "8",
|
||||
"options": [
|
||||
"FMT_TEST ON"
|
||||
],
|
||||
"patches": [
|
||||
"0001-disable-reference-copy.patch"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Calling `AddJsonPackage(fmt)`:
|
||||
|
||||
- Searches for a system package named `fmt` of version 8 or higher
|
||||
- Searches for a system package named `fmt` of version 8 or higher (`find_package(fmt 8)`)
|
||||
- If found, uses the system package and caches it for future use
|
||||
- If not found:
|
||||
- Downloads fmt 12.1.0 from the GitHub Archive into `.cache/cpm/fmt/12.1.0`
|
||||
@@ -85,63 +94,82 @@ These JSON properties are used by standard and CI packages alike.
|
||||
- `package`: The package name used by `find_package` to check for the existence of a system package.
|
||||
- If unset, defaults to the JSON key
|
||||
- `repo`: The Git repository the package is stored in, if applicable.
|
||||
- `version`: The version of the package to download. This is required.
|
||||
- `min_version`: The minimum required version of the package, if a system package is desired.
|
||||
- `version`: The version of the package. This is required.
|
||||
- Generally, this should be a 10-wide Git commit hash or Git tag.
|
||||
- Tags must be fully qualified and include prefixes and suffixes, e.g. `boost-1.88.0`
|
||||
- `git_host`: The Git host the package is stored in, if applicable. Defaults to `github.com`.
|
||||
|
||||
## Standard Packages
|
||||
|
||||
Normal packages, like the prior `fmt` example, *must* also define:
|
||||
|
||||
- `hash`: The SHA512 hash of the downloaded artifact. CPMUtil generally computes this for you.
|
||||
- A valid version/URL identifier:
|
||||
- `url`: Download from a raw URL.
|
||||
- `sha`: A short or fully-qualified Git commit sha. CPMUtil recommends using 10-character wide shas.
|
||||
- `tag`: A Git tag. See [Versioning](#versioning) for its relation to `version`.
|
||||
- `artifact`: A GitHub/Forgejo/Gitea release artifact (requires `tag`). See [Versioning](#versioning) for its relation to `tag` and `version`.
|
||||
- `hash`: The SHA512 hash of the downloaded artifact. CPMUtil generally computes this for you--if not, use `tools/cpmutil.sh package hash <JSON key>`
|
||||
- `min_version`: The minimum required version of the package, if a system package is desired.
|
||||
|
||||
And may optionally define:
|
||||
|
||||
- `url`: Download from a raw URL.
|
||||
- `artifact`: A GitHub/Forgejo/Gitea release artifact. Requires `repo` to be set and valid.
|
||||
- `numeric_version`: Replaces `%NUMERIC_VERSION%` in artifact and version definitions; see [Artifact Naming Errata](#artifact-naming-errata).
|
||||
|
||||
See [Versioning](#versioning) for version/artifact information.
|
||||
|
||||
The following are optional to define:
|
||||
|
||||
- `source_subdir`: A subdirectory containing the `CMakeLists.txt` to configure a project. Useful for projects like `zstd`.
|
||||
- `bundled`: Force the usage of a bundled package. Useful for packages where the system package is broken or nonexistent; e.g. including external fragment shaders.
|
||||
- `bundled`: Force the usage of a bundled package. Useful for packages where the system package is broken or nonexistent; e.g. external fragment shaders or data archives.
|
||||
- Note that this will conflict with `CPMUTIL_FORCE_SYSTEM`; for this reason, when using non-library archives, it may be best to allow the user to download and extract the archive manually and specify a local directory to it.
|
||||
- `find_args`: Additional arguments passed to `find_package`, e.g. `MODULE`
|
||||
- `patches`: Array of in-tree patches to apply to the downloaded source code. See [#Patches](TODO).
|
||||
- `options`: Array of CMake options to apply before configuring the package, e.g. `"FMT_TEST ON"`.
|
||||
|
||||
### Versioning
|
||||
|
||||
When using tags or artifacts, it may be cumbersome to repeat the version multiple times; especially if it's constantly changing. For this purpose, `tag` and `artifact` both support basic version text replacement.
|
||||
When fetching from a Git repository, there are generally three methods of versioning:
|
||||
|
||||
`tag` can use `%VERSION%` to have its version replaced with the `version` defined for the package, e.g. for OpenSSL; when downloading, `tag` will evaluate to `openssl-3.6.2`:
|
||||
- Commit hashes
|
||||
- Git tags
|
||||
- Release artifacts
|
||||
|
||||
```json
|
||||
"openssl": {
|
||||
"repo": "openssl/openssl",
|
||||
"version": "3.6.2",
|
||||
"tag": "openssl-%VERSION%"
|
||||
}
|
||||
```
|
||||
When `repo` is set, `version` field can be set to any commitish value, including commit hashes or Git tags. In the case of artifacts, `version` must be a Git tag, and `artifact` must be set to a release artifact attached to that tag. Many repositories intentionally version the filenames of their release artifacts; for this purpose, CPMUtil allows you to implant the version number into the artifact name. To do so, add `%VERSION%` to the artifact name; CPMUtil will then automatically replace `%VERSION%` with the `version` field. This means that when changing versions, you only need to update the version, not the artifact!
|
||||
|
||||
`artifact` also supports `%VERSION%` replacement, and can also use `%TAG%` to be replaced by the computed tag. Take this Boost definition:
|
||||
Take Boost as an example. The artifact for Boost 1.90.0 is `boost-1.90.0-cmake.tar.xz`, and the tag is `boost-1.90.0`. Thus, we can set the artifact to `%VERSION%-cmake.tar.xz`:
|
||||
|
||||
```json
|
||||
"boost": {
|
||||
"repo": "boostorg/boost",
|
||||
"tag": "boost-%VERSION%",
|
||||
"version": "1.90.0"
|
||||
"version": "boost-1.90.0",
|
||||
"artifact": "%VERSION%-cmake.tar.xz"
|
||||
}
|
||||
```
|
||||
|
||||
Boost's artifact for this version is stored in `boost-1.90.0-cmake.tar.xz`. Notice that the computed tag,`boost-1.90.0`, is in the name of the artifact! Thus, `artifact` can be either:
|
||||
The artifact will then evaluate as `boost-1.90.0-cmake.tar.xz`.
|
||||
|
||||
- `boost-%VERSION%-cmake.tar.xz`
|
||||
- Or, even simpler: `%TAG%-cmake.tar.xz`
|
||||
### Artifact Naming Errata
|
||||
|
||||
Future updates need only change the `version` identifier, and the artifact and tag will automatically be updated!
|
||||
While `%VERSION%` replacement is generally good enough for well-packaged projects, occasionally there may be some problematic packages. Take, for instance, Vulkan Validation Layers:
|
||||
|
||||
- Tag (`version`): `vulkan-sdk-1.4.341.0`
|
||||
- Artifact: `android-binaries-1.4.341.0.zip`
|
||||
|
||||
Attempting to add version replacement to the artifact definition **would not work here!** In this case, you must utilize the `numeric_version` field described earlier; we would set `numeric_version` to `1.4.341.0` and add `%NUMERIC_VERSION%` replacements into our artifact and version fields:
|
||||
|
||||
```json
|
||||
"vulkan-validation-layers": {
|
||||
"artifact": "android-binaries-%NUMERIC_VERSION%.zip",
|
||||
"repo": "KhronosGroup/Vulkan-ValidationLayers",
|
||||
"version": "vulkan-sdk-%NUMERIC_VERSION%",
|
||||
"numeric_version": "1.4.341.0"
|
||||
},
|
||||
```
|
||||
|
||||
`artifact` will thus evaluate to `android-binaries-1.4.341.0.zip`, and `version` to `vulkan-sdk-1.4.341.0`. CPMUtil's auto-updater will also account for this and only update `numeric_version`!
|
||||
|
||||
### Patches
|
||||
|
||||
CPMUtil is able to apply in-place source tree patches to downloaded packages. These are defined in JSON as an array of names, preferably using `git-format-patch`'s scheme of `<4 digit number>-patch-name.patch`. These are stored in `<CPMUTIL_PATCH_DIR>/<json-key>` (remember that `CPMUTIL_PATCH_DIR` defaults to `$ROOT/.patch`); e.g. `boost` patches would be in `.patch/boost`. Let's say we've made three patches and want to add them; in the Boost JSON definition, we would add:
|
||||
CPMUtil is able to apply in-place source tree patches to downloaded packages. These are defined in JSON as an array of names, preferably using `git-format-patch`'s scheme of `<4 digit number>-patch-name.patch`.
|
||||
|
||||
They are stored in `<CPMUTIL_PATCH_DIR>/<json-key>` (remember that `CPMUTIL_PATCH_DIR` defaults to `$ROOT/.patch`); e.g. `boost` patches would be in `.patch/boost`. Let's say we've made three patches and want to add them; in the Boost JSON definition, we would add:
|
||||
|
||||
```json
|
||||
"patches": [
|
||||
@@ -151,7 +179,7 @@ CPMUtil is able to apply in-place source tree patches to downloaded packages. Th
|
||||
]
|
||||
```
|
||||
|
||||
Then, when Boost is downloaded, it will apply these patches in order to the source tree.
|
||||
Then, when Boost is downloaded, it will apply these patches to the source tree in the order they are defined (compound/dependent patches are okay!). Note that when you add, remove, or modify patches, CPMUtil will invalidate your downloaded cache and re-fetch the source.
|
||||
|
||||
To learn how to make patches, see [Addendum: Making Patches](#addendum-making-patches).
|
||||
|
||||
@@ -199,12 +227,9 @@ If you're only concerned with basic usage, you can stop reading. For more advanc
|
||||
|
||||
CPMUtil stores downloaded packages within `.cache/cpm` by default (see `CPM_SOURCE_CACHE`). Subdirectories stored within are lowercase representations of the `find_package` name for the package; for instance, a `vulkan-headers` definition with `package: "VulkanHeaders"` would be stored in `.cache/cpm/vulkanheaders`.
|
||||
|
||||
Within these subdirectories, additional directories are created for each individual version:
|
||||
Within these subdirectories, additional directories are created for each individual version, corresponding directly to their `version` field. CI packages use `<platform>-<architecture>-<version>` unconditionally.
|
||||
|
||||
- A four-character shorthand of `sha`, if defined
|
||||
- If `sha` is not defined, the fully qualified `version` is used
|
||||
|
||||
CI packages use `<platform>-<architecture>-<version>` unconditionally.
|
||||
To see the cache directory for a given package, use `tools/cpmutil.sh package dir <JSON key>`.
|
||||
|
||||
## Addendum: Making Patches
|
||||
|
||||
@@ -242,7 +267,7 @@ If you are packaging a project that uses CPMUtil, read this!
|
||||
|
||||
For sandboxed environments (e.g. Gentoo, nixOS) you must install all dependencies to the system beforehand and set `-DCPMUTIL_FORCE_SYSTEM=ON`. If a dependency is missing, get creating!
|
||||
|
||||
Alternatively, if CPMUtil pulls in a package that has no suitable way to install or use a system version, download it separately and pass `-DPackageName_DIR=/path/to/downloaded/dir` (e.g. shaders)
|
||||
Alternatively, if CPMUtil pulls in a package that has no suitable way to install or use a system version, download it separately and pass `-D<PackageName>_CUSTOM_DIR=/path/to/downloaded/dir`.
|
||||
|
||||
### Unsandboxed
|
||||
|
||||
@@ -263,14 +288,12 @@ Using the prior Vulkan example:
|
||||
"repo": "KhronosGroup/Vulkan-Headers",
|
||||
"package": "VulkanHeaders",
|
||||
"min_version": "1.4.317",
|
||||
"version": "1.4.342",
|
||||
"tag": "v%VERSION%"
|
||||
"version": "v1.4.342"
|
||||
},
|
||||
"vulkan-utility-libraries": {
|
||||
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
|
||||
"package": "VulkanUtilityLibraries",
|
||||
"version": "1.4.342",
|
||||
"tag": "v%VERSION%"
|
||||
"version": "v1.4.342"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -318,3 +341,32 @@ AddQt(QDash-CI/Qt 6.11.1)
|
||||
```
|
||||
|
||||
Then, call `find_package(Qt6 ...)` and it will pull Qt from your downloaded source.
|
||||
|
||||
## Addendum: Package-Specific Overrides
|
||||
|
||||
There are three variables that CPMUtil defines for each package; these can be overriden by the user or in your CMake. `package` refers either to the `package` value in the JSON, or the package's JSON key if unset (see `package` in [Common Properties](#common-properties)):
|
||||
|
||||
- `<package>_FORCE_BUNDLED`: Forcefully bundle the package. This has the same effect as `CPMUTIL_FORCE_BUNDLED`, but only for this package.
|
||||
- `<package>_FORCE_BUNDLED`: Forcefully use the system package, failing if it can't be found. This has the same effect as `SYSTEM`, but only for this package.
|
||||
- `<package>_CUSTOM_DIR`: Path to an extracted copy of the package. CPMUtil will not attempt to download the package and will instead use the custom directory.
|
||||
- For an example, see [CPMUtil's test case](https://git.crueter.xyz/CMake/CPMUtil/src/branch/master/tests/dir/CMakeLists.txt)
|
||||
|
||||
Additionally, in CMake, you can add `FORCE_BUNDLED_PACKAGE ON` to your `AddJsonPackage` command--note that you will have to use the `NAME <key>` syntax as described in [Module Path Packages](#addendum-module-path-packages). This will overrule *all* other overrides, including `CPMUTIL_FORCE_SYSTEM` and `<package>_FORCE_SYSTEM`--use with caution!
|
||||
|
||||
## Addendum: Supply-Chain Security
|
||||
|
||||
Many package managers suffer from the issue of supply chain security, specifically in regards to silent overwrites of existing packages or archives, e.g. tag sliding and artifact overwriting. CPMUtil has three methods to protect against this.
|
||||
|
||||
### Checksumming
|
||||
|
||||
CPMUtil *requires* SHA512 checksums for standard packages, and soon will for CI packages as well. If an attacker or compromised account slides a tag, overwrites a release artifact, or otherwise attempts to compromise anything that CPMUtil may fetch, **CPMUtil will not allow the download to continue!** This means that consumers of your build system can **only** download an artifact if the contents of the artifact are *exactly* indentical to what it was when it was configured--any changes at all will be rejected by CPMUtil.
|
||||
|
||||
### Caching
|
||||
|
||||
CPMUtil uses a mutable cache system, stored by default in `.cache/cpm`. Dependencies are downloaded and extracted here, and can be reused infinitely. This means that, for instance, if a package is compromised but you already have a cached local copy, you won't have to worry at all!
|
||||
|
||||
### Immutable Commit Hashes
|
||||
|
||||
CPMUtil is capable of using immutable Git commit hashes for its artifacts. These are (barring SHA1 collisions) completely immune to supply chain attacks--that is, unless the entire root server gets compromised to serve infected artifacts/source code; at which point there are much larger issues to worry about. This means that once you set a package to use a Git commit hash for its version, **it will stay the same forever**. This is useful if you want to ensure that consumers are never faced with download failures stemming from hash mismatches in case of compromised artifacts.
|
||||
|
||||
Do note, however, that this will render the package incompatible with CPMUtil's built-in auto-updater, so you will have to manually update the package.
|
||||
|
||||
@@ -311,8 +311,9 @@ dispatcher, which will return control to the host.
|
||||
SetTerm(IR::Term::LinkBlockFast{next})
|
||||
```
|
||||
|
||||
This terminal instruction jumps to the basic block described by `next` unconditionally.
|
||||
This promises guarantees that must be held at runtime - i.e that the program wont hang,
|
||||
This terminal instruction jumps to the basic block described by `next` unconditionally. This promises guarantees that must be held at runtime - i.e that the program wont hang,
|
||||
|
||||
This is a valid expression if contained within another block (say a `Terminal::If`) and the semantics allow it to be so.
|
||||
|
||||
### Terminal: PopRSBHint
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Use this guide whenever you want to modify the Date or Time that Eden reports to
|
||||
|
||||
## Steps
|
||||
|
||||
1. Navigate to *Emulation → Configure*.
|
||||
1. Navigate to *Emulation > Configure*.
|
||||
2. Click on the **System** item on the left-hand side navigation, then check the *Custom RTC Date* box.
|
||||
3. The Date/Time option now becomes editable. Set it to the value you want and hit **OK**.
|
||||
4. GREAT SCOTT! We have time traveled! You can of course go forward or backward in time (as long as it is not before the year 1970) and your game should update accordingly (e.g. certain *Super Mario Odyssey* moons that take time for flowers to grow will now be fully grown.).
|
||||
@@ -30,7 +30,7 @@ Use this guide for when you want to configure specific controller settings to be
|
||||
|
||||
#### Steps
|
||||
1. Launch Eden and wait for it to load.
|
||||
2. Navigate to *Emulation > Configure…*
|
||||
2. Navigate to *Emulation > Configure...*
|
||||
3. Select **Controls** from the left-hand menu and configure your controller for the way you want it to be in game.
|
||||
4. Select **New** and enter a name for the profile in the box that appears. Press **OK** to save the profile settings.
|
||||
5. Select **OK** to close the settings menu.
|
||||
|
||||
@@ -16,14 +16,14 @@ Use this guide when you want to use the Steam Deck's native gyro functionality f
|
||||
### Steps
|
||||
|
||||
1. Go into Steam Deck's Desktop Mode, and use the shortcut to launch EmuDeck.
|
||||
2. Install [SteamDeckGyroDSU](https://github.com/kmicki/SteamDeckGyroDSU/releases) by going to *3rd Party Tools > Gyroscope* and clicking **Install.**
|
||||
2. Install [SteamDeckGyroDSU](https://github.com/kmicki/SteamDeckGyroDSU/releases) by going to *3rd Party Tools > Gyroscope* and clicking **Install.**
|
||||
a. Alternatively you can install [SteamDeckGyroDSU](https://github.com/kmicki/SteamDeckGyroDSU/releases) manually following the GitHub page instructions.
|
||||
3. Upon completion of the installation. You will need to reboot your Steam Deck. Do so before continuing on.
|
||||
4. Go back into the Steam Deck Desktop Mode and open the Dolphin File Explorer.
|
||||
5. Navigate to the following directory to see you controller configuration: `/home/deck/.config/Eden`
|
||||
6. *Right-Click* the **qt-config.ini** file and open it with ***Kate***
|
||||
7. Look for the following line: `player_0_motionleft=[empty]`.
|
||||
8. Change the line to now say: `player_0_motionleft="motion:0,pad:0,port:26760,guid:0000000000000000000000007f000001,engine:cemuhookudp"`
|
||||
5. Navigate to the following directory to see you controller configuration: `/home/deck/.config/Eden`
|
||||
6. *Right-Click* the **qt-config.ini** file and open it with ***Kate***
|
||||
7. Look for the following line: `player_0_motionleft=[empty]`.
|
||||
8. Change the line to now say: `player_0_motionleft="motion:0,pad:0,port:26760,guid:0000000000000000000000007f000001,engine:cemuhookudp"`
|
||||
9. Save the file and open Eden.
|
||||
10. Launch a compatible title, like *The Legend of Zelda: Breath of the Wild*.
|
||||
11. Test the gyro capabilities, for the above mentioned title, it is accessed by holding down the **R Trigger** and moving the Steam Deck around.
|
||||
10. Launch a compatible title, like *The Legend of Zelda: Breath of the Wild*.
|
||||
11. Test the gyro capabilities, for the above mentioned title, it is accessed by holding down the **R Trigger** and moving the Steam Deck around.
|
||||
@@ -4,7 +4,7 @@ Use this guide when you want to install Updates or DLC for your games in Eden.
|
||||
|
||||
<aside>
|
||||
|
||||
***NOTE***: This applies to separate Update/DLC files, not “merged” NSP/XCI’s which include the base game and Updates/DLC applied on top of them in a single file. These files work in Eden, but would not require the following steps.
|
||||
***NOTE***: This applies to separate Update/DLC files, not "merged" NSP/XCI's which include the base game and Updates/DLC applied on top of them in a single file. These files work in Eden, but would not require the following steps.
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ This FAQ will serve as a general quick question and answer simple questions.
|
||||
No - The only emulator that has this kind of functionality is *Ryujinx* and it's forks. This solution requires loading a custom module on a modded switch console to work.
|
||||
|
||||
### Can I Play Online Games?
|
||||
No - This would require hijacking requests to Nintendo's official servers to a custom server infrastructure built to emulate that functionality. This is how services like [*Pretendo*](https://pretendo.network/) operate. As such, you would not be able to play “Online”, you can however play multiplayer games.
|
||||
No - This would require hijacking requests to Nintendo's official servers to a custom server infrastructure built to emulate that functionality. This is how services like [*Pretendo*](https://pretendo.network/) operate. As such, you would not be able to play "Online", you can however play multiplayer games.
|
||||
|
||||
### What's the Difference Between Online and Multiplayer?
|
||||
I have chosen the wording carefully here for a reason.
|
||||
@@ -21,7 +21,7 @@ I have chosen the wording carefully here for a reason.
|
||||
The rule of thumb here is simple: If a game supports the ability to communicate without a server (Local Wireless, LAN, etc.) you will be able to play with other users. If it requires a server to function - it will not. You will need to look up if your title support Local Wireless/LAN play as an option.
|
||||
|
||||
### How Does Multiplayer Work on Eden Exactly?
|
||||
Eden's multiplayer works by emulating the Switch's local wireless (LDN) system, then tunneling that traffic over the internet through “rooms” that act like lobbies. Each player runs their own instance of the emulator, and as long as everyone joins the same room and the game supports local wireless multiplayer, the emulated consoles see each other as if they were on the same local network. This design avoids typical one-save netplay issues because every user keeps an independent save and console state while only the in-game wireless packets are forwarded through the room server. In practice, you pick or host a room, configure your network interface/port forwarding if needed, then launch any LDN-capable game; from the game's perspective it is just doing standard local wireless, while the emulator handles discovery and communication over the internet or LAN.
|
||||
Eden's multiplayer works by emulating the Switch's local wireless (LDN) system, then tunneling that traffic over the internet through "rooms" that act like lobbies. Each player runs their own instance of the emulator, and as long as everyone joins the same room and the game supports local wireless multiplayer, the emulated consoles see each other as if they were on the same local network. This design avoids typical one-save netplay issues because every user keeps an independent save and console state while only the in-game wireless packets are forwarded through the room server. In practice, you pick or host a room, configure your network interface/port forwarding if needed, then launch any LDN-capable game; from the game's perspective it is just doing standard local wireless, while the emulator handles discovery and communication over the internet or LAN.
|
||||
|
||||
### What Do I Need to Do?
|
||||
That depends entirely on what your goal is and your level of technical ability, you have a 2 options on how to proceed.
|
||||
@@ -71,7 +71,7 @@ There are 2 primary methods that you can use to connect to an existing room, dep
|
||||
</aside>
|
||||
|
||||
## Joining a Public Lobby
|
||||
1. Open Eden and navigate to *Multiplayer → Browse Public Game Lobby*.
|
||||
1. Open Eden and navigate to *Multiplayer > Browse Public Game Lobby*.
|
||||
2. The **Public Room Browser** will now open and display a list of publicly accessible rooms. Find one you want to connect to and double click it.
|
||||
|
||||
<aside>
|
||||
@@ -84,7 +84,7 @@ There are 2 primary methods that you can use to connect to an existing room, dep
|
||||
### Direct Connecting to a Room
|
||||
If the hoster has not made the lobby public, or you don't want to find it in the public game browser - use this option to connect.
|
||||
|
||||
1. Open Eden and navigate to *Multiplayer → Direct Connect*.
|
||||
1. Open Eden and navigate to *Multiplayer > Direct Connect*.
|
||||
2. Enter the *Server Address, Port*, *Nickname* (what your user will be called in the room), and a *Password* (if the hoster set one, otherwise leave it blank) and hit **Connect.**
|
||||
3. You will now see a window showing everyone on the lobby, or an error message.
|
||||
|
||||
@@ -101,7 +101,7 @@ Use this guide for when you want to host a multiplayer lobby to play with others
|
||||
- Ability to allow programs through the firewall on your device.
|
||||
|
||||
### Steps
|
||||
1. Open Eden and navigate to *Emulation → Multiplayer → Create Room.*
|
||||
1. Open Eden and navigate to *Emulation > Multiplayer > Create Room.*
|
||||
2. Fill out the following information in the popup dialog box.
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ Quite often the person with whom you want to play is located off of your interna
|
||||
|
||||
Port forwarding is a networking technique that directs incoming traffic arriving at a specific port on a router or firewall to a designated device and port inside a private local network. When an external client contacts the public IP address of the router on that port, the router rewrites the packet's destination information (IP address and sometimes port number) and forwards it to the internal host that is listening on the corresponding service. This allows services such as web servers, game servers, or remote desktop sessions hosted behind NAT (Network Address Translation) to be reachable from the wider Internet despite the devices themselves having non-routable private addresses.
|
||||
|
||||
The process works by creating a static mapping—often called a “port-forward rule”—in the router's configuration. The rule specifies three pieces of data: the external (public) port, the internal (private) IP address of the target machine, and the internal port on which that machine expects the traffic. When a packet arrives, the router checks its NAT table, matches the external port to a rule, and then translates the packet's destination to the internal address before sending it onward. Responses from the internal host are similarly rewritten so they appear to come from the router's public IP, completing the bidirectional communication loop. This mechanism enables seamless access to services inside a protected LAN without exposing the entire network.
|
||||
The process works by creating a static mapping-often called a "port-forward rule"-in the router's configuration. The rule specifies three pieces of data: the external (public) port, the internal (private) IP address of the target machine, and the internal port on which that machine expects the traffic. When a packet arrives, the router checks its NAT table, matches the external port to a rule, and then translates the packet's destination to the internal address before sending it onward. Responses from the internal host are similarly rewritten so they appear to come from the router's public IP, completing the bidirectional communication loop. This mechanism enables seamless access to services inside a protected LAN without exposing the entire network.
|
||||
|
||||
For our purposes we would pick the port we want to expose (*e.g. 24872*) and we would access our router's configuration and create a port-forward rule to send the traffic from an external connection to your local machine over our specified port (*24872)*. The exact way to do so, varies greatly by router manufacturer - and sometimes require contacting your ISP to do so depending on your agreement. You can look up your router on [*portforward.com*](https://portforward.com/router.htm) which may have instructions on how to do so for your specific equipment. If it is not there, you will have to use Google/ChatGPT to determine the steps for your equipment.
|
||||
|
||||
@@ -160,7 +160,7 @@ Remember you can't have one port open for multiple devices at the same time - yo
|
||||
</aside>
|
||||
|
||||
|
||||
Using a Tunnelling service may be the solution to avoid port forward, but also avoid worrying about your users setup. A tunnelling service works by having a lightweight client run on the machine that hosts the game server. That client immediately opens an **outbound** encrypted connection (typically over TLS/QUIC) to a relay node operated by the tunnel provider's cloud infrastructure. Because outbound traffic is almost always allowed through NAT routers and ISP firewalls, the tunnel can be established even when the host sits behind carrier-grade NAT or a strict firewall. The tunnel provider then assigns a public address (e.g., `mygame.playit.gg:12345`). When a remote player connects to that address, the traffic reaches the the tunnel provider relay, which forwards it through the already-established tunnel back to the client on the private network, and finally onto the local game server's port. In effect, the server appears to the Internet as if it were listening on the public address, while the host never needs to configure port-forwarding rules or expose its own IP directly.
|
||||
Using a Tunnelling service may be the solution to avoid port forward, but also avoid worrying about your users setup. A tunnelling service works by having a lightweight client run on the machine that hosts the game server. That client immediately opens an **outbound** encrypted connection (typically over TLS/QUIC) to a relay node operated by the tunnel provider's cloud infrastructure. Because outbound traffic is almost always allowed through NAT routers and ISP firewalls, the tunnel can be established even when the host sits behind carrier-grade NAT or a strict firewall. The tunnel provider then assigns a public address (e.g., `mygame.playit.gg:12345`). When a remote player connects to that address, the traffic reaches the the tunnel provider relay, which forwards it through the already-established tunnel back to the client on the private network, and finally onto the local game server's port. In effect, the server appears to the Internet as if it were listening on the public address, while the host never needs to configure port-forwarding rules or expose its own IP directly.
|
||||
|
||||
For our purposes we would spawn the listener for the port that way chose when hosting our room. The user would connect to our assigned public address/port combination, and it would be routed to our machine. The tunnel must remain active for as long as you want the connection to remain open. Closing the terminal will kill the tunnel and disconnect the users.
|
||||
|
||||
@@ -204,7 +204,7 @@ Use this guide when you need to determine the connection information for the Pub
|
||||
### Method 1: Grabbing the Address from the Log File
|
||||
1. Open Eden and Connect to the room you want to identify.
|
||||
1. See the *Joining a Multiplayer Room* section for instructions on how to do so if you need them.
|
||||
2. Go to *File → Open Eden Folder*, then open the **config** folder.
|
||||
2. Go to *File > Open Eden Folder*, then open the **config** folder.
|
||||
3. Open the the **qt-config.ini** file in a text editor.
|
||||
4. Search for the following keys:
|
||||
1. `Multiplayer\ip=`
|
||||
@@ -286,7 +286,7 @@ This guide will assume you are the one hosting the game and go over things *Pars
|
||||
2. If you are joining a game, you will have to send a connection request the host will have to accept.
|
||||
3. Verify that the remote player can see the screen and that there is no issues with the connection.
|
||||
4. Launch Eden.
|
||||
5. Navigate to *Emulation → Configure*.
|
||||
5. Navigate to *Emulation > Configure*.
|
||||
6. Select the **Controls** tab.
|
||||
7. Set up your controller, if necessary.
|
||||
8. Select the **Player 2** tab and select the **Connect Controller** checkbox. This enables inputs from another device to be seen as a second controller.
|
||||
|
||||
@@ -33,12 +33,12 @@ Use this guide to get starting using the Eden emulator.
|
||||
|
||||
<aside>
|
||||
|
||||
***INFO***: You may get a “*Windows protected your PC”* SmartScreen message that appears. This is just Windows Defender saying it did not recognize the application and did not run it - Eden is completely safe. Click **More info** and then **Run anyway** to dismiss this message.
|
||||
***INFO***: You may get a "*Windows protected your PC"* SmartScreen message that appears. This is just Windows Defender saying it did not recognize the application and did not run it - Eden is completely safe. Click **More info** and then **Run anyway** to dismiss this message.
|
||||
|
||||
</aside>
|
||||
4. Eden will now launch and notify you about missing Decryption keys. Close the dialog box by hitting **OK**.
|
||||
5. Navigate to **Tools → Install Decryption Keys**, navigate to the folder containing your key files and select the file, you should only be able to select one.
|
||||
6. Navigate to **Tools → Install Firmware**, *Select **From Folder*** or ***From ZIP*** - depending on how your firmware is stored, navigate to where it is stored and select it.
|
||||
5. Navigate to **Tools > Install Decryption Keys**, navigate to the folder containing your key files and select the file, you should only be able to select one.
|
||||
6. Navigate to **Tools > Install Firmware**, *Select **From Folder*** or ***From ZIP*** - depending on how your firmware is stored, navigate to where it is stored and select it.
|
||||
7. Double-Click the main window to add the folder containing your games.
|
||||
8. Go to *Emulation > Configure > Input* and set up your controller of choice. Click **OK** to close the dialog window.
|
||||
9. Double-Click a game to run it.
|
||||
@@ -72,8 +72,8 @@ Use this guide to get starting using the Eden emulator.
|
||||
|
||||
4. If you have had a different Switch emulator installed, it will detect and ask if you want to import those settings. Make your selection to close the screen.
|
||||
5. Eden will now launch and notify you about missing Encryption keys. Close the dialog box by hitting **OK**.
|
||||
6. Navigate to **Tools → Install Decryption Keys**, navigate to the folder containing your ***prod.keys*** file and select the file and hit **Open**.
|
||||
7. Navigate to **Tools → Install Firmware →** *Select **From Folder*** or ***From ZIP*** - depending on how your firmware is stored, navigate to where it is stored and select it.
|
||||
6. Navigate to **Tools > Install Decryption Keys**, navigate to the folder containing your ***prod.keys*** file and select the file and hit **Open**.
|
||||
7. Navigate to **Tools > Install Firmware >** *Select **From Folder*** or ***From ZIP*** - depending on how your firmware is stored, navigate to where it is stored and select it.
|
||||
8. Double-Click the main window to add the folder containing your games.
|
||||
9. Go to *Emulation > Configure > Input* and set up your controller. Click **OK** to close the dialog window.
|
||||
10. Double-Click a game to run it.
|
||||
|
||||
@@ -22,7 +22,7 @@ Use this guide when you need to allow Eden to run on a Mac system, but are being
|
||||
|
||||
### Why am I Seeing This?
|
||||
|
||||
Recent versions of MacOS (Catalina & newer) introduced the **Gatekeeper** security functionality, requiring software to be signed by Apple or a trusted (aka - paying) developer. If the signature isn’t on the list of trusted ones, it will stop the program from executing and display the message above.
|
||||
Recent versions of MacOS (Catalina & newer) introduced the **Gatekeeper** security functionality, requiring software to be signed by Apple or a trusted (aka - paying) developer. If the signature isn't on the list of trusted ones, it will stop the program from executing and display the message above.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Use this when you want to import the Eden AppImage into your Steam Library along
|
||||
|
||||
#### Initial Setup
|
||||
|
||||
1. Press the **STEAM** button and then go to *Power → Switch to Desktop* to enter the Desktop mode.
|
||||
1. Press the **STEAM** button and then go to *Power > Switch to Desktop* to enter the Desktop mode.
|
||||
|
||||
2. Install ***Steam ROM Manager*** (if needed), there are 2 ways you can accomplish this, either manually or through [*EmuDeck*](https://www.emudeck.com/#downloads).
|
||||
|
||||
@@ -53,8 +53,8 @@ Use this when you want to import the Eden AppImage into your Steam Library along
|
||||
|
||||
EmuDeck will automatically create an *Emulators - Emulators* parser for ***Steam ROM Manager*** that uses shell scripts to launch them. We will follow this convention.
|
||||
|
||||
1. In the file explorer go to your **EmuDeck installation folder → tools → launchers**
|
||||
2. Right-Click some empty space and hit **Create New → Text File,** call this new file ***eden.sh*** instead of ***Text File.txt***
|
||||
1. In the file explorer go to your **EmuDeck installation folder > tools > launchers**
|
||||
2. Right-Click some empty space and hit **Create New > Text File,** call this new file ***eden.sh*** instead of ***Text File.txt***
|
||||
3. Right-Click the ***eden.sh*** file you created and hit ***Open with Kate***.
|
||||
4. Paste the following code into the contents of the file, save and close the file.
|
||||
|
||||
@@ -103,7 +103,7 @@ We will need to create a new parser for the Emulators. Unlike with the EmuDeck
|
||||
|
||||
<aside>
|
||||
|
||||
***TIP***: You may need to go to **Settings → Theme** and set it to *Classic* to view this option.
|
||||
***TIP***: You may need to go to **Settings > Theme** and set it to *Classic* to view this option.
|
||||
|
||||
</aside>
|
||||
|
||||
@@ -119,7 +119,7 @@ We will need to create a new parser for the Emulators. Unlike with the EmuDeck
|
||||
2. Parser Specific Configuration
|
||||
1. **Search Glob**: *${title}@(.AppImage|.APPIMAGE|.appimage)*
|
||||
3. Executable Configuration
|
||||
1. **Executable Modifier**: *"${exePath}”*
|
||||
1. **Executable Modifier**: *"${exePath}"*
|
||||
4. Title Modification Configuration
|
||||
1. **Title Modifier**: *${fuzzyTitle}*
|
||||
|
||||
@@ -181,7 +181,7 @@ Use this when you want to import your games inside Eden into Steam to launch wit
|
||||
|
||||
### Steps
|
||||
|
||||
1. Press the **STEAM** button and then go to *Power → Switch to Desktop* to enter the Desktop mode.
|
||||
1. Press the **STEAM** button and then go to *Power > Switch to Desktop* to enter the Desktop mode.
|
||||
|
||||
1. Install ***Steam ROM Manager***, there are 2 ways you can accomplish this, either manually or through [*EmuDeck*](https://www.emudeck.com/#downloads).
|
||||
|
||||
@@ -215,7 +215,7 @@ Use this when you want to import your games inside Eden into Steam to launch wit
|
||||
|
||||
<aside>
|
||||
|
||||
***TIP***: Your layout may look different depending on how you installed *Steam ROM Manager*. You may need to go to **Settings → Theme** and change it to *Classic* to follow along.
|
||||
***TIP***: Your layout may look different depending on how you installed *Steam ROM Manager*. You may need to go to **Settings > Theme** and change it to *Classic* to follow along.
|
||||
|
||||
</aside>
|
||||
|
||||
@@ -229,13 +229,13 @@ Use this when you want to import your games inside Eden into Steam to launch wit
|
||||
2. Change the **Parser title** from *Nintendo Switch - Yuzu* to *Nintendo Switch - Eden.*
|
||||
3. Hit the **Browse** option under the *ROMs directory* section. Select the directory containing your Switch ROMs.
|
||||
4. Under *Steam collections*, you can add a Steam category name. This just organizes the games under a common category in your Steam Library, this is optional but recommended.
|
||||
5. Scroll down slightly to the **Executable Configuration → Executable**, select **Browse** and select the Eden AppImage.
|
||||
5. Scroll down slightly to the **Executable Configuration > Executable**, select **Browse** and select the Eden AppImage.
|
||||
6. Leave everything else the same and hit **Save** to save the parser.
|
||||
---
|
||||
|
||||
4. Click the Eden parser to view the options on the right, select **Test** at the bottom of the screen to ensure that *Steam ROM Manager* detects your games correctly.
|
||||
1. *Steam ROM Manager* will start to scan the specified ROMs directory and match them to games. Look over the results to ensure they are accurate. If you do not see any entries - check your parsers ROMs directory field.
|
||||
1. When you are happy with the results, click the **Add Games** → **Parse** to start the actual Parsing.
|
||||
1. When you are happy with the results, click the **Add Games** > **Parse** to start the actual Parsing.
|
||||
1. The program will now identify the games and pull artwork from [*SteamGridDB*](https://www.steamgriddb.com/).
|
||||
2. Review the game matches and ensure everything is there.
|
||||
|
||||
|
||||
+10
-10
@@ -30,15 +30,15 @@ Use this guide for when you want to configure automated backup/syncing of your E
|
||||
|
||||
## Overview
|
||||
|
||||
Rather than giving a breakdown of all the platforms and configurations, those will be in the platform’s specific guides - this will serve as a general overview of Syncthing.
|
||||
Rather than giving a breakdown of all the platforms and configurations, those will be in the platform's specific guides - this will serve as a general overview of Syncthing.
|
||||
|
||||
### What is Syncthing Anyway?
|
||||
|
||||
Syncthing is a continuous file synchronization program (in the layman’s - make sure 2 or more systems with the same files are always up to date). This is perfect for game saves where we would want to play on 1 device, save our game, and then continue playing it on another device. This technology is what Epic/Steam/etc. use to allow you to do this on games run through their respective services. Syncthing is an open source implementation of this technology that you control, rather than relying on a 3rd party. This has a few key benefits, most notably - better security, privacy, and speed (when on your LAN).
|
||||
Syncthing is a continuous file synchronization program (in the layman's - make sure 2 or more systems with the same files are always up to date). This is perfect for game saves where we would want to play on 1 device, save our game, and then continue playing it on another device. This technology is what Epic/Steam/etc. use to allow you to do this on games run through their respective services. Syncthing is an open source implementation of this technology that you control, rather than relying on a 3rd party. This has a few key benefits, most notably - better security, privacy, and speed (when on your LAN).
|
||||
|
||||
### What are some common issues?
|
||||
|
||||
Syncthing is fairly robust and doesn’t have many issues luckily, but there are some things you should watch out for (almost all of them a user issue).
|
||||
Syncthing is fairly robust and doesn't have many issues luckily, but there are some things you should watch out for (almost all of them a user issue).
|
||||
|
||||
- Sync conflicts
|
||||
- If for whatever reason you update the same file on 2 different machines, the system does not know which updated file is considered the one to sync across. This results in a ***sync conflict*** where it may not sync the files as you would expect. Worst case scenario, this can result in your save progress being lost if you are not careful. When one of these occurs, it will create a copy of the file and store it with a specific name, like this example, *Paper Mario.sync-conflict-20251102-072925-TZBBN6S.srm.* To resolve this, you must remove the other files and remove the *.sync-conflict-<TIMESTAMP>-<Syncthing Device ID>* from the file name of the file you want to keep.
|
||||
@@ -86,7 +86,7 @@ Use this when you want to set this machine as the initial source of truth (push
|
||||
|
||||
1. Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing.**
|
||||
2. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
|
||||
3. We’ll start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
|
||||
3. We'll start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
|
||||
4. A pop-up window will appear, fill in the Folder label field with whatever you want to call it, like Switch Saves.
|
||||
5. Enter the Full folder path to where your save files are stored on this machine.
|
||||
|
||||
@@ -105,7 +105,7 @@ Use this when you want to set this machine as the initial source of truth (push
|
||||
|
||||
Use this when you want to set this machine up as a child (pull files from the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
|
||||
|
||||
1. Install Syncthing Tray on the client device following the section above. Copy the child’s ID and store it so it is accessible to the Parent.
|
||||
1. Install Syncthing Tray on the client device following the section above. Copy the child's ID and store it so it is accessible to the Parent.
|
||||
2. ***ON THE PARENT***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already**.**
|
||||
3. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
|
||||
4. Navigate down to **+ Add Remote Device**, we are going to add our Child device, so I hope you have its ID handy. If not, go back and get it.
|
||||
@@ -126,7 +126,7 @@ Use this when you want to set this machine up as a child (pull files from the ot
|
||||
|
||||
</aside>
|
||||
|
||||
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered “*Parents*” for other devices you want to add. Repeat these steps for as many devices you want.
|
||||
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered "*Parents*" for other devices you want to add. Repeat these steps for as many devices you want.
|
||||
|
||||
## Linux
|
||||
|
||||
@@ -166,9 +166,9 @@ Use this when you want to set this machine up as a child (pull files from the ot
|
||||
Use this when you want to set this machine as the initial source of truth (push files out to all the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
|
||||
|
||||
1. Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing.**
|
||||
1. If you don’t have a taskbar in your distro, you can also reach it directly by opening a web browser to: *http://127.0.0.1:8384/.*
|
||||
1. If you don't have a taskbar in your distro, you can also reach it directly by opening a web browser to: *http://127.0.0.1:8384/.*
|
||||
2. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
|
||||
3. We’ll start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
|
||||
3. We'll start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
|
||||
4. A pop-up window will appear, fill in the Folder label field with whatever you want to call it, like Switch Saves.
|
||||
5. Enter the Full folder path to where your save files are stored on this machine.
|
||||
|
||||
@@ -187,7 +187,7 @@ Use this when you want to set this machine as the initial source of truth (push
|
||||
|
||||
Use this when you want to set this machine up as a child (pull files from the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
|
||||
|
||||
1. Install Syncthing Tray on the client device following the section above. Copy the child’s ID and store it so it is accessible to the Parent.
|
||||
1. Install Syncthing Tray on the client device following the section above. Copy the child's ID and store it so it is accessible to the Parent.
|
||||
2. ***ON THE PARENT***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already**.**
|
||||
3. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
|
||||
4. Navigate down to **+ Add Remote Device**, we are going to add our Child device, so I hope you have its ID handy. If not, go back and get it.
|
||||
@@ -208,4 +208,4 @@ Use this when you want to set this machine up as a child (pull files from the ot
|
||||
|
||||
</aside>
|
||||
|
||||
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered “*Parents*” for other devices you want to add. Repeat these steps for as many devices you want.
|
||||
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered "*Parents*" for other devices you want to add. Repeat these steps for as many devices you want.
|
||||
|
||||
@@ -68,7 +68,7 @@ Faulting package-relative application ID:
|
||||
2. Confirm there are no logs created in the log directory.
|
||||
1. See the [How to Access Logs](./HowToAccessLogs.md) page for the log location if you need it.
|
||||
2. If there are any entries in here since you tried step 1, this is likely not your issue.
|
||||
3. Navigate to your *Windows Event Viewer* (Start Menu → **eventvwr.msc)**.
|
||||
3. Navigate to your *Windows Event Viewer* (Start Menu > **eventvwr.msc)**.
|
||||
4. Expand **Windows Logs** and select **Application.**
|
||||
|
||||
5. Look for an entry with the Level of Error, and look for a message similar to the following
|
||||
|
||||
@@ -30,14 +30,14 @@ TBD
|
||||
|
||||
</aside>
|
||||
|
||||
1. Navigate to the Amiibo section of the game. The method for initiating the scanning varies from game to game, for *Captain Toad’s Treasure Tracker*, you need to go to the press the **+** button when on the level select. You will need to look up how to do so with your specific game.
|
||||
1. Navigate to the Amiibo section of the game. The method for initiating the scanning varies from game to game, for *Captain Toad's Treasure Tracker*, you need to go to the press the **+** button when on the level select. You will need to look up how to do so with your specific game.
|
||||
2. Upon activating the Amiibo scan functionality, you should get a Scan page. Eden is now looking for an Amiibo file to be loaded, which emulates scanning an Amiibo on actual hardware.
|
||||
3. Navigate to **File > Load/Remove Amiibo…**, or press the hotkey to do the same (**F2** on keyboard by default).
|
||||
3. Navigate to **File > Load/Remove Amiibo...**, or press the hotkey to do the same (**F2** on keyboard by default).
|
||||
4. In the file explorer that opens, navigate to the amiibo file you want to use.
|
||||
|
||||
<aside>
|
||||
|
||||
***NOTE***: It seems the scanning functionality is spotty and will sometimes throw a "*The current game is not looking for amiibos*” message, even though it is. Usually you just need to try loading it again or restarting the scanning from the game. In some situations it was only resolved by restarting the game.
|
||||
***NOTE***: It seems the scanning functionality is spotty and will sometimes throw a "*The current game is not looking for amiibos*" message, even though it is. Usually you just need to try loading it again or restarting the scanning from the game. In some situations it was only resolved by restarting the game.
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
+10
-10
@@ -58,24 +58,24 @@ Community Member [Ninjistix](https://github.com/Ninjistix) created a utility (Wi
|
||||
```
|
||||
[Super Mario Bros. Wonder - Various] <- Optional
|
||||
|
||||
[♯ 1. Always Star Power]
|
||||
[# 1. Always Star Power]
|
||||
040E0000 00880580 52800035
|
||||
|
||||
[♯ 2. Star Power + Bubble Mode (Invincible)]
|
||||
[# 2. Star Power + Bubble Mode (Invincible)]
|
||||
040E0000 00880580 52800075
|
||||
|
||||
[♯ 3. Can Fast Travel to Any Course and World]
|
||||
[# 3. Can Fast Travel to Any Course and World]
|
||||
040E0000 00935E10 52800036
|
||||
040E0000 0048A528 52800028
|
||||
040E0000 005D9F58 52800028
|
||||
|
||||
[♯ 4. Got All Top of Flag Poles]
|
||||
[# 4. Got All Top of Flag Poles]
|
||||
040E0000 0048A818 52800028
|
||||
```
|
||||
|
||||
### Step 3: Enabling/Disabling Cheats
|
||||
|
||||
Cheats are enabled by default, but can be disabled so they don’t affect gameplay fairly easily using the game properties.
|
||||
Cheats are enabled by default, but can be disabled so they don't affect gameplay fairly easily using the game properties.
|
||||
|
||||
1. Open Eden and press and hold the game you want to apply the cheat to.
|
||||
2. Scroll down on the properties until you see **Add-ons**, select this option.
|
||||
@@ -130,24 +130,24 @@ Community Member [Ninjistix](https://github.com/Ninjistix) created a utility (Wi
|
||||
```
|
||||
[Super Mario Bros. Wonder - Various] <- Optional
|
||||
|
||||
[♯ 1. Always Star Power]
|
||||
[# 1. Always Star Power]
|
||||
040E0000 00880580 52800035
|
||||
|
||||
[♯ 2. Star Power + Bubble Mode (Invincible)]
|
||||
[# 2. Star Power + Bubble Mode (Invincible)]
|
||||
040E0000 00880580 52800075
|
||||
|
||||
[♯ 3. Can Fast Travel to Any Course and World]
|
||||
[# 3. Can Fast Travel to Any Course and World]
|
||||
040E0000 00935E10 52800036
|
||||
040E0000 0048A528 52800028
|
||||
040E0000 005D9F58 52800028
|
||||
|
||||
[♯ 4. Got All Top of Flag Poles]
|
||||
[# 4. Got All Top of Flag Poles]
|
||||
040E0000 0048A818 52800028
|
||||
```
|
||||
|
||||
### Step 3: Enabling/Disabling Cheats
|
||||
|
||||
Cheats are enabled by default, but can be disabled so they don’t affect gameplay fairly easily using the game properties.
|
||||
Cheats are enabled by default, but can be disabled so they don't affect gameplay fairly easily using the game properties.
|
||||
|
||||
1. *Right-Click* the game and select *Configure Game*.
|
||||
2. In the **Add-Ons** section, locate the cheat you wish to enable.
|
||||
|
||||
+3
-1
@@ -31,6 +31,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
|
||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||
RENDERER_DEBUG("debug"),
|
||||
@@ -83,9 +84,10 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
ENABLE_OVERLAY("enable_overlay"),
|
||||
|
||||
// GPU Logging
|
||||
GPU_LOGGING_ENABLED("gpu_logging_enabled"),
|
||||
GPU_LOG_VULKAN_CALLS("gpu_log_vulkan_calls"),
|
||||
GPU_LOG_SHADER_DUMPS("gpu_log_shader_dumps"),
|
||||
DUMP_GUEST_SHADERS("dump_guest_shaders"),
|
||||
DUMP_MACROS("dump_macros"),
|
||||
GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"),
|
||||
GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"),
|
||||
|
||||
|
||||
+21
-7
@@ -806,6 +806,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.enable_buffer_history_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
|
||||
titleId = R.string.enable_gpu_buffer_readback,
|
||||
descriptionId = R.string.enable_gpu_buffer_readback_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
||||
@@ -931,13 +938,6 @@ abstract class SettingsItem(
|
||||
)
|
||||
|
||||
// GPU Logging settings
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.GPU_LOGGING_ENABLED,
|
||||
titleId = R.string.gpu_logging_enabled,
|
||||
descriptionId = R.string.gpu_logging_enabled_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
ByteSetting.GPU_LOG_LEVEL,
|
||||
@@ -954,6 +954,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.gpu_log_vulkan_calls_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.DUMP_GUEST_SHADERS,
|
||||
titleId = R.string.dump_guest_shaders,
|
||||
descriptionId = R.string.dump_guest_shaders_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.GPU_LOG_SHADER_DUMPS,
|
||||
@@ -961,6 +968,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.gpu_log_shader_dumps_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.DUMP_MACROS,
|
||||
titleId = R.string.dump_macros,
|
||||
descriptionId = R.string.dump_macros_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.GPU_LOG_MEMORY_TRACKING,
|
||||
|
||||
+12
-8
@@ -292,6 +292,7 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||
|
||||
add(HeaderSetting(R.string.hacks))
|
||||
@@ -1288,14 +1289,17 @@ class SettingsFragmentPresenter(
|
||||
add(ShortSetting.DEBUG_KNOBS.key)
|
||||
add(StringSetting.PROGRAM_ARGS.key)
|
||||
|
||||
add(HeaderSetting(R.string.gpu_logging_header))
|
||||
add(BooleanSetting.GPU_LOGGING_ENABLED.key)
|
||||
add(ByteSetting.GPU_LOG_LEVEL.key)
|
||||
add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key)
|
||||
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
|
||||
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
|
||||
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
|
||||
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
|
||||
if (!NativeConfig.isPerGameConfigLoaded()) {
|
||||
add(HeaderSetting(R.string.gpu_logging_header))
|
||||
add(ByteSetting.GPU_LOG_LEVEL.key)
|
||||
add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key)
|
||||
add(BooleanSetting.DUMP_GUEST_SHADERS.key)
|
||||
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
|
||||
add(BooleanSetting.DUMP_MACROS.key)
|
||||
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
|
||||
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
|
||||
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -569,8 +569,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">تسجيل وحدة معالجة الرسومات</string>
|
||||
<string name="gpu_logging_enabled">تمكين تسجيل وحدة معالجة الرسومات</string>
|
||||
<string name="gpu_logging_enabled_description">تسجيل عمليات وحدة معالجة الرسومات في ملف eden_gpu.log لتصحيح أخطاء برامج تشغيل Adreno</string>
|
||||
<string name="gpu_log_level">مستوى السجل</string>
|
||||
<string name="gpu_log_level_description">مستوى التفاصيل لسجلات وحدة معالجة الرسومات (كلما زاد المستوى، زادت التفاصيل وزادت التكاليف الإضافية)</string>
|
||||
<string name="gpu_log_vulkan_calls">تسجيل استدعاءات واجهة برمجة تطبيقات Vulkan</string>
|
||||
|
||||
@@ -563,8 +563,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Registros de la GPU</string>
|
||||
<string name="gpu_logging_enabled">Activar los registros de la GPU</string>
|
||||
<string name="gpu_logging_enabled_description">Registra las operaciones de la GPU en eden_gpu.log para la depuración de los controladores de Adreno</string>
|
||||
<string name="gpu_log_level">Nivel de registros</string>
|
||||
<string name="gpu_log_level_description">Nivel de detalle de los registros de la GPU (más alto = más detalles, más sobrecarga)</string>
|
||||
<string name="gpu_log_vulkan_calls">Registros de llamadas del API de Vulkan</string>
|
||||
|
||||
@@ -525,7 +525,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Journalisation GPU</string>
|
||||
<string name="gpu_logging_enabled">Activer la journalisation GPU</string>
|
||||
<string name="gpu_log_level">Niveau de journalisation</string>
|
||||
<string name="gpu_log_vulkan_calls">Journaliser les appels API Vulkan</string>
|
||||
<string name="gpu_log_shader_dumps">Extraire les shaders</string>
|
||||
|
||||
@@ -562,8 +562,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Ведение журнала ГПУ</string>
|
||||
<string name="gpu_logging_enabled">Включить ведение журнала ГПУ</string>
|
||||
<string name="gpu_logging_enabled_description">Записывать операции ГПУ в файл eden_gpu.log для отладки драйверов Adreno</string>
|
||||
<string name="gpu_log_level">Уровень журналирования</string>
|
||||
<string name="gpu_log_level_description">Уровень детализации логов ГПУ (больше значение = больше деталей, выше нагрузка)</string>
|
||||
<string name="gpu_log_vulkan_calls">Записывать вызовы Vulkan API</string>
|
||||
|
||||
@@ -565,8 +565,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Журналювання ГП</string>
|
||||
<string name="gpu_logging_enabled">Увімкнути журналювання ГП</string>
|
||||
<string name="gpu_logging_enabled_description">Журналювати операції ГП до eden_gpu.log для зневадження драйверів Adreno</string>
|
||||
<string name="gpu_log_level">Рівень журналювання</string>
|
||||
<string name="gpu_log_level_description">Рівень подробиць у журналі ГП (вищий = більше подробиць, більший вплив на швидкодію)</string>
|
||||
<string name="gpu_log_vulkan_calls">Записувати виклики API Vulkan</string>
|
||||
|
||||
@@ -559,8 +559,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">GPU 日志</string>
|
||||
<string name="gpu_logging_enabled">启用 GPU 日志</string>
|
||||
<string name="gpu_logging_enabled_description">将 GPU 操作记录至 eden_gpu.log 以供调试 Adreno 驱动</string>
|
||||
<string name="gpu_log_level">日志等级</string>
|
||||
<string name="gpu_log_level_description">GPU 日志的详细级别(数值越高 = 细节越多,开销越大)</string>
|
||||
<string name="gpu_log_vulkan_calls">记录 Vulkan API 调用</string>
|
||||
|
||||
@@ -503,6 +503,8 @@
|
||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||
<string name="enable_buffer_history">Enable buffer history</string>
|
||||
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
||||
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
|
||||
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
||||
|
||||
@@ -575,14 +577,16 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">GPU Logging</string>
|
||||
<string name="gpu_logging_enabled">Enable GPU Logging</string>
|
||||
<string name="gpu_logging_enabled_description">Log GPU operations to eden_gpu.log for debugging Adreno drivers</string>
|
||||
<string name="gpu_log_level">Log Level</string>
|
||||
<string name="gpu_log_level_description">Detail level for GPU logs (higher = more detail, more overhead)</string>
|
||||
<string name="gpu_log_vulkan_calls">Log Vulkan API Calls</string>
|
||||
<string name="gpu_log_vulkan_calls_description">Track all Vulkan API calls in ring buffer</string>
|
||||
<string name="gpu_log_shader_dumps">Dump Shaders</string>
|
||||
<string name="gpu_log_shader_dumps_description">Save compiled shader SPIR-V to files</string>
|
||||
<string name="gpu_log_shader_dumps">Dump SPIR-V Shaders</string>
|
||||
<string name="gpu_log_shader_dumps_description">Save recompiled SPIR-V binaries (.spv) to dump folder. Inspect with spirv-dis/spirv-cross/spirv-val.</string>
|
||||
<string name="dump_guest_shaders">Dump Guest (Maxwell) Shaders</string>
|
||||
<string name="dump_guest_shaders_description">Save Maxwell guest shader bytecode files (*.ash) to dump folder. Inspect with nvdisasm.</string>
|
||||
<string name="dump_macros">Dump Maxwell Macros</string>
|
||||
<string name="dump_macros_description">Save Maxwell macro program files (*.macro) to dump folder. Inspect with envydis.</string>
|
||||
<string name="gpu_log_memory_tracking">Track GPU Memory</string>
|
||||
<string name="gpu_log_memory_tracking_description">Monitor GPU memory allocations and deallocations</string>
|
||||
<string name="gpu_log_driver_debug">Driver Debug Info</string>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -181,8 +184,9 @@ MemoryPoolInfo::ResultState PoolMapper::Update(MemoryPoolInfo& pool,
|
||||
return MemoryPoolInfo::ResultState::Success;
|
||||
}
|
||||
|
||||
if (in_params.address == 0 || in_params.size == 0 || !Common::Is4KBAligned(in_params.address) ||
|
||||
!Common::Is4KBAligned(in_params.size)) {
|
||||
if (in_params.address == 0 || in_params.size == 0
|
||||
|| !Common::IsAligned(in_params.address, Core::Memory::YUZU_PAGESIZE)
|
||||
|| !Common::IsAligned(in_params.size, Core::Memory::YUZU_PAGESIZE)) {
|
||||
return MemoryPoolInfo::ResultState::BadParam;
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n
|
||||
std::memcpy(&last_frame[0], &input_buffer[(frames_written - 1) * frame_size], frame_size_bytes);
|
||||
}
|
||||
|
||||
// update sample counts für audio-ins
|
||||
// update sample counts fuer audio-ins
|
||||
{
|
||||
std::scoped_lock lk{sample_count_lock};
|
||||
last_sample_count_update_time = system.CoreTiming().GetGlobalTimeNs();
|
||||
|
||||
@@ -50,7 +50,6 @@ add_library(
|
||||
elf.h
|
||||
error.cpp
|
||||
error.h
|
||||
expected.h
|
||||
fiber.cpp
|
||||
fiber.h
|
||||
fixed_point.h
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2014 Jannik Vogel <email@jannikvogel.de>
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
@@ -34,12 +37,6 @@ template <typename T>
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>
|
||||
[[nodiscard]] constexpr bool Is4KBAligned(T value) {
|
||||
return (value & 0xFFF) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>
|
||||
[[nodiscard]] constexpr bool IsWordAligned(T value) {
|
||||
@@ -48,9 +45,9 @@ template <typename T>
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
|
||||
[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) noexcept {
|
||||
using U = typename std::make_unsigned_t<T>;
|
||||
const U mask = static_cast<U>(alignment - 1);
|
||||
const U mask = U(alignment - 1);
|
||||
return (value & mask) == 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,986 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// This is based on the proposed implementation of std::expected (P0323)
|
||||
// https://github.com/TartanLlama/expected/blob/master/include/tl/expected.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T, typename E>
|
||||
class Expected;
|
||||
|
||||
template <typename E>
|
||||
class Unexpected {
|
||||
public:
|
||||
Unexpected() = delete;
|
||||
|
||||
constexpr explicit Unexpected(const E& e) : m_val{e} {}
|
||||
|
||||
constexpr explicit Unexpected(E&& e) : m_val{std::move(e)} {}
|
||||
|
||||
constexpr E& value() & {
|
||||
return m_val;
|
||||
}
|
||||
|
||||
constexpr const E& value() const& {
|
||||
return m_val;
|
||||
}
|
||||
|
||||
constexpr E&& value() && {
|
||||
return std::move(m_val);
|
||||
}
|
||||
|
||||
constexpr const E&& value() const&& {
|
||||
return std::move(m_val);
|
||||
}
|
||||
|
||||
private:
|
||||
E m_val;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
constexpr auto operator<=>(const Unexpected<E>& lhs, const Unexpected<E>& rhs) {
|
||||
return lhs.value() <=> rhs.value();
|
||||
}
|
||||
|
||||
struct unexpect_t {
|
||||
constexpr explicit unexpect_t() = default;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct no_init_t {
|
||||
constexpr explicit no_init_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially destructible,
|
||||
* so the destructor must be called on destruction of `expected'
|
||||
* Additionally, this requires E to be trivially destructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_destructible_v<T>>
|
||||
requires std::is_trivially_destructible_v<E>
|
||||
struct expected_storage_base {
|
||||
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
|
||||
|
||||
constexpr expected_storage_base(no_init_t) : m_has_val{false} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, Args&&... args)
|
||||
: m_val{std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: m_val{il, std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
|
||||
: m_unexpect{std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, std::initializer_list<U> il,
|
||||
Args&&... args)
|
||||
: m_unexpect{il, std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
~expected_storage_base() {
|
||||
if (m_has_val) {
|
||||
m_val.~T();
|
||||
}
|
||||
}
|
||||
|
||||
union {
|
||||
T m_val;
|
||||
Unexpected<E> m_unexpect;
|
||||
};
|
||||
|
||||
bool m_has_val;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is trivially destructible,
|
||||
* so the destructor of `expected` can be trivial
|
||||
* Additionally, this requires E to be trivially destructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_destructible_v<E>
|
||||
struct expected_storage_base<T, E, true> {
|
||||
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
|
||||
|
||||
constexpr expected_storage_base(no_init_t) : m_has_val{false} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, Args&&... args)
|
||||
: m_val{std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: m_val{il, std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
|
||||
: m_unexpect{std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, std::initializer_list<U> il,
|
||||
Args&&... args)
|
||||
: m_unexpect{il, std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
~expected_storage_base() = default;
|
||||
|
||||
union {
|
||||
T m_val;
|
||||
Unexpected<E> m_unexpect;
|
||||
};
|
||||
|
||||
bool m_has_val;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
struct expected_operations_base : expected_storage_base<T, E> {
|
||||
using expected_storage_base<T, E>::expected_storage_base;
|
||||
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args) noexcept {
|
||||
new (std::addressof(this->m_val)) T{std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
template <typename Rhs>
|
||||
void construct_with(Rhs&& rhs) noexcept {
|
||||
new (std::addressof(this->m_val)) T{std::forward<Rhs>(rhs).get()};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void construct_error(Args&&... args) noexcept {
|
||||
new (std::addressof(this->m_unexpect)) Unexpected<E>{std::forward<Args>(args)...};
|
||||
this->m_has_val = false;
|
||||
}
|
||||
|
||||
void assign(const expected_operations_base& rhs) noexcept {
|
||||
if (!this->m_has_val && rhs.m_has_val) {
|
||||
geterr().~Unexpected<E>();
|
||||
construct(rhs.get());
|
||||
} else {
|
||||
assign_common(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
void assign(expected_operations_base&& rhs) noexcept {
|
||||
if (!this->m_has_val && rhs.m_has_val) {
|
||||
geterr().~Unexpected<E>();
|
||||
construct(std::move(rhs).get());
|
||||
} else {
|
||||
assign_common(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Rhs>
|
||||
void assign_common(Rhs&& rhs) {
|
||||
if (this->m_has_val) {
|
||||
if (rhs.m_has_val) {
|
||||
get() = std::forward<Rhs>(rhs).get();
|
||||
} else {
|
||||
destroy_val();
|
||||
construct_error(std::forward<Rhs>(rhs).geterr());
|
||||
}
|
||||
} else {
|
||||
if (!rhs.m_has_val) {
|
||||
geterr() = std::forward<Rhs>(rhs).geterr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_value() const {
|
||||
return this->m_has_val;
|
||||
}
|
||||
|
||||
constexpr T& get() & {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
constexpr const T& get() const& {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
constexpr T&& get() && {
|
||||
return std::move(this->m_val);
|
||||
}
|
||||
|
||||
constexpr const T&& get() const&& {
|
||||
return std::move(this->m_val);
|
||||
}
|
||||
|
||||
constexpr Unexpected<E>& geterr() & {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
constexpr const Unexpected<E>& geterr() const& {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
constexpr Unexpected<E>&& geterr() && {
|
||||
return std::move(this->m_unexpect);
|
||||
}
|
||||
|
||||
constexpr const Unexpected<E>&& geterr() const&& {
|
||||
return std::move(this->m_unexpect);
|
||||
}
|
||||
|
||||
constexpr void destroy_val() {
|
||||
get().~T();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial copy constructor
|
||||
* This specialization is for when T is trivially copy constructible
|
||||
* Additionally, this requires E to be trivially copy constructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>>
|
||||
requires std::is_trivially_copy_constructible_v<E>
|
||||
struct expected_copy_base : expected_operations_base<T, E> {
|
||||
using expected_operations_base<T, E>::expected_operations_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially copy constructible
|
||||
* Additionally, this requires E to be trivially copy constructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_copy_constructible_v<E>
|
||||
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
|
||||
using expected_operations_base<T, E>::expected_operations_base;
|
||||
|
||||
expected_copy_base() = default;
|
||||
|
||||
expected_copy_base(const expected_copy_base& rhs)
|
||||
: expected_operations_base<T, E>{no_init_t{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct_with(rhs);
|
||||
} else {
|
||||
this->construct_error(rhs.geterr());
|
||||
}
|
||||
}
|
||||
|
||||
expected_copy_base(expected_copy_base&&) = default;
|
||||
|
||||
expected_copy_base& operator=(const expected_copy_base&) = default;
|
||||
|
||||
expected_copy_base& operator=(expected_copy_base&&) = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial move constructor
|
||||
* This specialization is for when T is trivially move constructible
|
||||
* Additionally, this requires E to be trivially move constructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>>
|
||||
requires std::is_trivially_move_constructible_v<E>
|
||||
struct expected_move_base : expected_copy_base<T, E> {
|
||||
using expected_copy_base<T, E>::expected_copy_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially move constructible
|
||||
* Additionally, this requires E to be trivially move constructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_move_constructible_v<E>
|
||||
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
|
||||
using expected_copy_base<T, E>::expected_copy_base;
|
||||
|
||||
expected_move_base() = default;
|
||||
|
||||
expected_move_base(const expected_move_base&) = default;
|
||||
|
||||
expected_move_base(expected_move_base&& rhs) noexcept(std::is_nothrow_move_constructible_v<T>)
|
||||
: expected_copy_base<T, E>{no_init_t{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct_with(std::move(rhs));
|
||||
} else {
|
||||
this->construct_error(std::move(rhs.geterr()));
|
||||
}
|
||||
}
|
||||
|
||||
expected_move_base& operator=(const expected_move_base&) = default;
|
||||
|
||||
expected_move_base& operator=(expected_move_base&&) = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial copy assignment operator
|
||||
* This specialization is for when T is trivially copy assignable
|
||||
* Additionally, this requires E to be trivially copy assignable
|
||||
*/
|
||||
template <typename T, typename E,
|
||||
bool = std::conjunction_v<std::is_trivially_copy_assignable<T>,
|
||||
std::is_trivially_copy_constructible<T>,
|
||||
std::is_trivially_destructible<T>>>
|
||||
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
|
||||
std::is_trivially_copy_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_copy_assign_base : expected_move_base<T, E> {
|
||||
using expected_move_base<T, E>::expected_move_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially copy assignable
|
||||
* Additionally, this requires E to be trivially copy assignable
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
|
||||
std::is_trivially_copy_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
|
||||
using expected_move_base<T, E>::expected_move_base;
|
||||
|
||||
expected_copy_assign_base() = default;
|
||||
|
||||
expected_copy_assign_base(const expected_copy_assign_base&) = default;
|
||||
|
||||
expected_copy_assign_base(expected_copy_assign_base&&) = default;
|
||||
|
||||
expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
|
||||
this->assign(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
expected_copy_assign_base& operator=(expected_copy_assign_base&&) = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial move assignment operator
|
||||
* This specialization is for when T is trivially move assignable
|
||||
* Additionally, this requires E to be trivially move assignable
|
||||
*/
|
||||
template <typename T, typename E,
|
||||
bool = std::conjunction_v<std::is_trivially_move_assignable<T>,
|
||||
std::is_trivially_move_constructible<T>,
|
||||
std::is_trivially_destructible<T>>>
|
||||
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
|
||||
std::is_trivially_move_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
|
||||
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially move assignable
|
||||
* Additionally, this requires E to be trivially move assignable
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
|
||||
std::is_trivially_move_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> {
|
||||
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
|
||||
|
||||
expected_move_assign_base() = default;
|
||||
|
||||
expected_move_assign_base(const expected_move_assign_base&) = default;
|
||||
|
||||
expected_move_assign_base(expected_move_assign_base&&) = default;
|
||||
|
||||
expected_move_assign_base& operator=(const expected_move_assign_base&) = default;
|
||||
|
||||
expected_move_assign_base& operator=(expected_move_assign_base&& rhs) noexcept(
|
||||
std::conjunction_v<std::is_nothrow_move_constructible<T>,
|
||||
std::is_nothrow_move_assignable<T>>) {
|
||||
this->assign(std::move(rhs));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* expected_delete_ctor_base will conditionally delete copy and move constructors
|
||||
* depending on whether T is copy/move constructible
|
||||
* Additionally, this requires E to be copy/move constructible
|
||||
*/
|
||||
template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>,
|
||||
bool EnableMove = std::is_move_constructible_v<T>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, true, false> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, false, true> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, false, false> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* expected_delete_assign_base will conditionally delete copy and move assignment operators
|
||||
* depending on whether T is copy/move constructible + assignable
|
||||
* Additionally, this requires E to be copy/move constructible + assignable
|
||||
*/
|
||||
template <
|
||||
typename T, typename E,
|
||||
bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>,
|
||||
bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, true, false> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, false, true> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, false, false> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is needed to be able to construct the expected_default_ctor_base which follows,
|
||||
* while still conditionally deleting the default constructor.
|
||||
*/
|
||||
struct default_constructor_tag {
|
||||
constexpr explicit default_constructor_tag() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* expected_default_ctor_base will ensure that expected
|
||||
* has a deleted default constructor if T is not default constructible
|
||||
* This specialization is for when T is default constructible
|
||||
*/
|
||||
template <typename T, typename E, bool Enable = std::is_default_constructible_v<T>>
|
||||
struct expected_default_ctor_base {
|
||||
constexpr expected_default_ctor_base() noexcept = default;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default;
|
||||
|
||||
constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
struct expected_default_ctor_base<T, E, false> {
|
||||
constexpr expected_default_ctor_base() noexcept = delete;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default;
|
||||
|
||||
constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
|
||||
};
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
using expected_enable_forward_value =
|
||||
std::enable_if_t<std::is_constructible_v<T, U&&> &&
|
||||
!std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
|
||||
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
|
||||
!std::is_same_v<Unexpected<E>, std::remove_cvref_t<U>>>;
|
||||
|
||||
template <typename T, typename E, typename U, typename G, typename UR, typename GR>
|
||||
using expected_enable_from_other = std::enable_if_t<
|
||||
std::is_constructible_v<T, UR> && std::is_constructible_v<E, GR> &&
|
||||
!std::is_constructible_v<T, Expected<U, G>&> && !std::is_constructible_v<T, Expected<U, G>&&> &&
|
||||
!std::is_constructible_v<T, const Expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, const Expected<U, G>&&> &&
|
||||
!std::is_convertible_v<Expected<U, G>&, T> && !std::is_convertible_v<Expected<U, G>&&, T> &&
|
||||
!std::is_convertible_v<const Expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<const Expected<U, G>&&, T>>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename E>
|
||||
class Expected : private detail::expected_move_assign_base<T, E>,
|
||||
private detail::expected_delete_ctor_base<T, E>,
|
||||
private detail::expected_delete_assign_base<T, E>,
|
||||
private detail::expected_default_ctor_base<T, E> {
|
||||
public:
|
||||
using value_type = T;
|
||||
using error_type = E;
|
||||
using unexpected_type = Unexpected<E>;
|
||||
|
||||
constexpr Expected() = default;
|
||||
constexpr Expected(const Expected&) = default;
|
||||
constexpr Expected(Expected&&) = default;
|
||||
Expected& operator=(const Expected&) = default;
|
||||
Expected& operator=(Expected&&) = default;
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
|
||||
constexpr Expected(std::in_place_t, Args&&... args)
|
||||
: impl_base{std::in_place, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr Expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: impl_base{std::in_place, il, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr,
|
||||
std::enable_if_t<!std::is_convertible_v<const G&, E>>* = nullptr>
|
||||
constexpr explicit Expected(const Unexpected<G>& e)
|
||||
: impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr,
|
||||
std::enable_if_t<std::is_convertible_v<const G&, E>>* = nullptr>
|
||||
constexpr Expected(Unexpected<G> const& e)
|
||||
: impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, G&&>>* = nullptr,
|
||||
std::enable_if_t<!std::is_convertible_v<G&&, E>>* = nullptr>
|
||||
constexpr explicit Expected(Unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G&&>)
|
||||
: impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{
|
||||
detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, G&&>>* = nullptr,
|
||||
std::enable_if_t<std::is_convertible_v<G&&, E>>* = nullptr>
|
||||
constexpr Expected(Unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G&&>)
|
||||
: impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{
|
||||
detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
|
||||
constexpr explicit Expected(unexpect_t, Args&&... args)
|
||||
: impl_base{unexpect_t{}, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr explicit Expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
|
||||
: impl_base{unexpect_t{}, il, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<!(std::is_convertible_v<U const&, T> &&
|
||||
std::is_convertible_v<G const&, E>)>* = nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* = nullptr>
|
||||
constexpr explicit Expected(const Expected<U, G>& rhs)
|
||||
: ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(*rhs);
|
||||
} else {
|
||||
this->construct_error(rhs.error());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<(std::is_convertible_v<U const&, T> &&
|
||||
std::is_convertible_v<G const&, E>)>* = nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* = nullptr>
|
||||
constexpr Expected(const Expected<U, G>& rhs) : ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(*rhs);
|
||||
} else {
|
||||
this->construct_error(rhs.error());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<!(std::is_convertible_v<U&&, T> && std::is_convertible_v<G&&, E>)>* =
|
||||
nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
|
||||
constexpr explicit Expected(Expected<U, G>&& rhs)
|
||||
: ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(std::move(*rhs));
|
||||
} else {
|
||||
this->construct_error(std::move(rhs.error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<(std::is_convertible_v<U&&, T> && std::is_convertible_v<G&&, E>)>* =
|
||||
nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
|
||||
constexpr Expected(Expected<U, G>&& rhs) : ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(std::move(*rhs));
|
||||
} else {
|
||||
this->construct_error(std::move(rhs.error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U = T, std::enable_if_t<!std::is_convertible_v<U&&, T>>* = nullptr,
|
||||
detail::expected_enable_forward_value<T, E, U>* = nullptr>
|
||||
constexpr explicit Expected(U&& v) : Expected{std::in_place, std::forward<U>(v)} {}
|
||||
|
||||
template <typename U = T, std::enable_if_t<std::is_convertible_v<U&&, T>>* = nullptr,
|
||||
detail::expected_enable_forward_value<T, E, U>* = nullptr>
|
||||
constexpr Expected(U&& v) : Expected{std::in_place, std::forward<U>(v)} {}
|
||||
|
||||
template <typename U = T, typename G = T,
|
||||
std::enable_if_t<std::is_nothrow_constructible_v<T, U&&>>* = nullptr,
|
||||
std::enable_if_t<(
|
||||
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
|
||||
!std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::remove_cvref_t<U>>> &&
|
||||
std::is_constructible_v<T, U> && std::is_assignable_v<G&, U> &&
|
||||
std::is_nothrow_move_constructible_v<E>)>* = nullptr>
|
||||
Expected& operator=(U&& v) {
|
||||
if (has_value()) {
|
||||
val() = std::forward<U>(v);
|
||||
} else {
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<U>(v)};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T, typename G = T,
|
||||
std::enable_if_t<!std::is_nothrow_constructible_v<T, U&&>>* = nullptr,
|
||||
std::enable_if_t<(
|
||||
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
|
||||
!std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::remove_cvref_t<U>>> &&
|
||||
std::is_constructible_v<T, U> && std::is_assignable_v<G&, U> &&
|
||||
std::is_nothrow_move_constructible_v<E>)>* = nullptr>
|
||||
Expected& operator=(U&& v) {
|
||||
if (has_value()) {
|
||||
val() = std::forward<U>(v);
|
||||
} else {
|
||||
auto tmp = std::move(err());
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<U>(v)};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_nothrow_copy_constructible_v<G> &&
|
||||
std::is_assignable_v<G&, G>>* = nullptr>
|
||||
Expected& operator=(const Unexpected<G>& rhs) {
|
||||
if (!has_value()) {
|
||||
err() = rhs;
|
||||
} else {
|
||||
this->destroy_val();
|
||||
new (errptr()) Unexpected<E>{rhs};
|
||||
this->m_has_val = false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_nothrow_move_constructible_v<G> &&
|
||||
std::is_move_assignable_v<G>>* = nullptr>
|
||||
Expected& operator=(Unexpected<G>&& rhs) noexcept {
|
||||
if (!has_value()) {
|
||||
err() = std::move(rhs);
|
||||
} else {
|
||||
this->destroy_val();
|
||||
new (errptr()) Unexpected<E>{std::move(rhs)};
|
||||
this->m_has_val = false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Args,
|
||||
std::enable_if_t<std::is_nothrow_constructible_v<T, Args&&...>>* = nullptr>
|
||||
void emplace(Args&&... args) {
|
||||
if (has_value()) {
|
||||
val() = T{std::forward<Args>(args)...};
|
||||
} else {
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args,
|
||||
std::enable_if_t<!std::is_nothrow_constructible_v<T, Args&&...>>* = nullptr>
|
||||
void emplace(Args&&... args) {
|
||||
if (has_value()) {
|
||||
val() = T{std::forward<Args>(args)...};
|
||||
} else {
|
||||
auto tmp = std::move(err());
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_nothrow_constructible_v<T, std::initializer_list<U>&,
|
||||
Args&&...>>* = nullptr>
|
||||
void emplace(std::initializer_list<U> il, Args&&... args) {
|
||||
if (has_value()) {
|
||||
T t{il, std::forward<Args>(args)...};
|
||||
val() = std::move(t);
|
||||
} else {
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{il, std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<!std::is_nothrow_constructible_v<T, std::initializer_list<U>&,
|
||||
Args&&...>>* = nullptr>
|
||||
void emplace(std::initializer_list<U> il, Args&&... args) {
|
||||
if (has_value()) {
|
||||
T t{il, std::forward<Args>(args)...};
|
||||
val() = std::move(t);
|
||||
} else {
|
||||
auto tmp = std::move(err());
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{il, std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T* operator->() {
|
||||
return valptr();
|
||||
}
|
||||
|
||||
constexpr const T* operator->() const {
|
||||
return valptr();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U& operator*() & {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U& operator*() const& {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U&& operator*() && {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U&& operator*() const&& {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
constexpr bool has_value() const noexcept {
|
||||
return this->m_has_val;
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const noexcept {
|
||||
return this->m_has_val;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U& value() & {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U& value() const& {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U&& value() && {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U&& value() const&& {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
constexpr E& error() & {
|
||||
return err().value();
|
||||
}
|
||||
|
||||
constexpr const E& error() const& {
|
||||
return err().value();
|
||||
}
|
||||
|
||||
constexpr E&& error() && {
|
||||
return std::move(err().value());
|
||||
}
|
||||
|
||||
constexpr const E&& error() const&& {
|
||||
return std::move(err().value());
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr T value_or(U&& v) const& {
|
||||
static_assert(std::is_copy_constructible_v<T> && std::is_convertible_v<U&&, T>,
|
||||
"T must be copy-constructible and convertible from U&&");
|
||||
return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr T value_or(U&& v) && {
|
||||
static_assert(std::is_move_constructible_v<T> && std::is_convertible_v<U&&, T>,
|
||||
"T must be move-constructible and convertible from U&&");
|
||||
return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
|
||||
}
|
||||
|
||||
private:
|
||||
static_assert(!std::is_reference_v<T>, "T must not be a reference");
|
||||
static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
|
||||
"T must not be std::in_place_t");
|
||||
static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>, "T must not be unexpect_t");
|
||||
static_assert(!std::is_same_v<T, std::remove_cv_t<Unexpected<E>>>,
|
||||
"T must not be Unexpected<E>");
|
||||
static_assert(!std::is_reference_v<E>, "E must not be a reference");
|
||||
|
||||
T* valptr() {
|
||||
return std::addressof(this->m_val);
|
||||
}
|
||||
|
||||
const T* valptr() const {
|
||||
return std::addressof(this->m_val);
|
||||
}
|
||||
|
||||
Unexpected<E>* errptr() {
|
||||
return std::addressof(this->m_unexpect);
|
||||
}
|
||||
|
||||
const Unexpected<E>* errptr() const {
|
||||
return std::addressof(this->m_unexpect);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U& val() {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U& val() const {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
constexpr Unexpected<E>& err() {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
constexpr const Unexpected<E>& err() const {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
using impl_base = detail::expected_move_assign_base<T, E>;
|
||||
using ctor_base = detail::expected_default_ctor_base<T, E>;
|
||||
};
|
||||
|
||||
template <typename T, typename E, typename U, typename F>
|
||||
constexpr bool operator==(const Expected<T, E>& lhs, const Expected<U, F>& rhs) {
|
||||
return (lhs.has_value() != rhs.has_value())
|
||||
? false
|
||||
: (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U, typename F>
|
||||
constexpr bool operator!=(const Expected<T, E>& lhs, const Expected<U, F>& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator==(const Expected<T, E>& x, const U& v) {
|
||||
return x.has_value() ? *x == v : false;
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator==(const U& v, const Expected<T, E>& x) {
|
||||
return x.has_value() ? *x == v : false;
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator!=(const Expected<T, E>& x, const U& v) {
|
||||
return !operator==(x, v);
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator!=(const U& v, const Expected<T, E>& x) {
|
||||
return !operator==(v, x);
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator==(const Expected<T, E>& x, const Unexpected<E>& e) {
|
||||
return x.has_value() ? false : x.error() == e.value();
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator==(const Unexpected<E>& e, const Expected<T, E>& x) {
|
||||
return x.has_value() ? false : x.error() == e.value();
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator!=(const Expected<T, E>& x, const Unexpected<E>& e) {
|
||||
return !operator==(x, e);
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator!=(const Unexpected<E>& e, const Expected<T, E>& x) {
|
||||
return !operator==(e, x);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -455,23 +455,17 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path, const DirEn
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
|
||||
std::error_code ec;
|
||||
bool callback_error = false;
|
||||
for (const auto& entry : fs::directory_iterator(path, ec)) {
|
||||
// MSVC should now be fixed... right... right?!?!?!
|
||||
for (const auto& entry : fs::recursive_directory_iterator(path, ec)) {
|
||||
if ((True(filter & DirEntryFilter::File) && entry.status().type() == fs::file_type::regular)
|
||||
|| (True(filter & DirEntryFilter::Directory) && entry.status().type() == fs::file_type::directory)) {
|
||||
if (!callback(entry)) {
|
||||
callback_error = true;
|
||||
}
|
||||
}
|
||||
// TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
|
||||
// recursive_directory_iterator throws an exception despite passing in a std::error_code.
|
||||
if (entry.status().type() == fs::file_type::directory) {
|
||||
IterateDirEntriesRecursively(entry.path(), callback, filter);
|
||||
}
|
||||
}
|
||||
|
||||
if (callback_error || ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to visit all the directory entries of path={}, ec_message={}, callback_error={}", PathToUTF8String(path), ec.message(), callback_error);
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
@@ -13,8 +13,6 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr float PI = 3.1415926535f;
|
||||
|
||||
template <class T>
|
||||
struct Rectangle {
|
||||
T left{};
|
||||
|
||||
+10
-1
@@ -54,7 +54,6 @@ SWITCHABLE(CpuBackend, true);
|
||||
SWITCHABLE(CpuAccuracy, true);
|
||||
SWITCHABLE(FullscreenMode, true);
|
||||
SWITCHABLE(GpuAccuracy, true);
|
||||
SWITCHABLE(GpuLogLevel, true);
|
||||
SWITCHABLE(Language, true);
|
||||
SWITCHABLE(MemoryLayout, true);
|
||||
SWITCHABLE(NvdecEmulation, false);
|
||||
@@ -213,6 +212,16 @@ bool IsNceEnabled() {
|
||||
return is_nce_enabled;
|
||||
}
|
||||
|
||||
static u64 current_program_id = 0;
|
||||
|
||||
void SetCurrentProgramID(u64 program_id) {
|
||||
current_program_id = program_id;
|
||||
}
|
||||
|
||||
u64 GetCurrentProgramID() {
|
||||
return current_program_id;
|
||||
}
|
||||
|
||||
bool IsDockedMode() {
|
||||
return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
|
||||
}
|
||||
|
||||
+16
-7
@@ -573,6 +573,13 @@ struct Values {
|
||||
false,
|
||||
#endif
|
||||
"rescale_hack", Category::RendererHacks};
|
||||
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
||||
false,
|
||||
"enable_gpu_buffer_readback",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||
Category::RendererHacks};
|
||||
@@ -784,12 +791,12 @@ struct Values {
|
||||
"program_args",
|
||||
Category::System,
|
||||
Specialization::Default,
|
||||
true, // save_ — persist in config file
|
||||
false}; // runtime_modifiable_ — startup-only
|
||||
true, // save_ - persist in config file
|
||||
false}; // runtime_modifiable_ - startup-only
|
||||
Setting<bool> dump_exefs{linkage, false, "dump_exefs", Category::Debugging};
|
||||
Setting<bool> dump_nso{linkage, false, "dump_nso", Category::Debugging};
|
||||
Setting<bool> dump_shaders{
|
||||
linkage, false, "dump_shaders", Category::DebuggingGraphics, Specialization::Default,
|
||||
Setting<bool> dump_guest_shaders{
|
||||
linkage, false, "dump_guest_shaders", Category::DebuggingGraphics, Specialization::Default,
|
||||
false};
|
||||
Setting<bool> dump_macros{
|
||||
linkage, false, "dump_macros", Category::DebuggingGraphics, Specialization::Default, false};
|
||||
@@ -813,9 +820,8 @@ struct Values {
|
||||
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Debugging};
|
||||
|
||||
// GPU Logging
|
||||
Setting<bool> gpu_logging_enabled{linkage, false, "gpu_logging_enabled", Category::Debugging};
|
||||
SwitchableSetting<GpuLogLevel> gpu_log_level{linkage, GpuLogLevel::Standard, "gpu_log_level",
|
||||
Category::Debugging};
|
||||
Setting<GpuLogLevel> gpu_log_level{linkage, GpuLogLevel::Off, "gpu_log_level",
|
||||
Category::Debugging};
|
||||
Setting<bool> gpu_log_vulkan_calls{linkage, true, "gpu_log_vulkan_calls", Category::Debugging};
|
||||
Setting<bool> gpu_log_shader_dumps{linkage, false, "gpu_log_shader_dumps", Category::Debugging};
|
||||
Setting<bool> gpu_log_memory_tracking{linkage, true, "gpu_log_memory_tracking",
|
||||
@@ -876,6 +882,9 @@ bool IsFastmemEnabled();
|
||||
void SetNceEnabled(bool is_64bit);
|
||||
bool IsNceEnabled();
|
||||
|
||||
void SetCurrentProgramID(u64 program_id);
|
||||
u64 GetCurrentProgramID();
|
||||
|
||||
bool IsOpenGL();
|
||||
|
||||
bool IsDockedMode();
|
||||
|
||||
+7
-3
@@ -1,8 +1,12 @@
|
||||
// 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
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#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"
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#define STBI_ONLY_JPEG 1
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -48,22 +48,10 @@ static s64 GetSystemTimeNS() {
|
||||
|
||||
SteadyClock::time_point SteadyClock::Now() noexcept {
|
||||
#if defined(_WIN32)
|
||||
static const auto freq = WindowsQueryPerformanceFrequency();
|
||||
static const auto timer_freq = WindowsQueryPerformanceFrequency();
|
||||
const auto counter = WindowsQueryPerformanceCounter();
|
||||
|
||||
// 10 MHz is a very common QPC frequency on modern PCs.
|
||||
// Optimizing for this specific frequency can double the performance of
|
||||
// this function by avoiding the expensive frequency conversion path.
|
||||
static constexpr s64 TenMHz = 10'000'000;
|
||||
|
||||
if (freq == TenMHz) [[likely]] {
|
||||
static_assert(period::den % TenMHz == 0);
|
||||
static constexpr s64 Multiplier = period::den / TenMHz;
|
||||
return time_point{duration{counter * Multiplier}};
|
||||
}
|
||||
|
||||
const auto whole = (counter / freq) * period::den;
|
||||
const auto part = (counter % freq) * period::den / freq;
|
||||
const auto whole = (counter / timer_freq) * period::den;
|
||||
const auto part = (counter % timer_freq) * period::den / timer_freq;
|
||||
return time_point{duration{whole + part}};
|
||||
#elif defined(__APPLE__)
|
||||
return time_point{duration{clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW)}};
|
||||
|
||||
+5
-2
@@ -108,7 +108,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system)
|
||||
: kernel{system}, fs_controller{system}, hid_core{system.Kernel()}, cpu_manager{system},
|
||||
: kernel{system}, fs_controller{system}, hid_core{kernel}, cpu_manager{system},
|
||||
reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
|
||||
|
||||
u64 program_id;
|
||||
@@ -326,6 +326,9 @@ struct System::Impl {
|
||||
|
||||
LOG_INFO(Core, "Loading {} ({:016X}) ...", name, params.program_id);
|
||||
|
||||
// Expose program id to dump sites and other global readers.
|
||||
Settings::SetCurrentProgramID(params.program_id);
|
||||
|
||||
// Track launch time for frontend launches
|
||||
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
|
||||
|
||||
@@ -347,7 +350,7 @@ struct System::Impl {
|
||||
|
||||
// Register with applet manager
|
||||
// All threads are started, begin main process execution, now that we're in the clear
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::make_unique<Service::Process>(*std::move(process)), params);
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
||||
|
||||
if (Settings::values.gamecard_inserted) {
|
||||
if (Settings::values.gamecard_current_game) {
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/container/deque.hpp>
|
||||
|
||||
#include "common/address_space.h"
|
||||
#include "common/address_space.inc"
|
||||
#include "common/alignment.h"
|
||||
@@ -33,21 +35,23 @@ public:
|
||||
|
||||
void GatherValues(u32 start_entry, Common::ScratchBuffer<u32>& buffer) {
|
||||
buffer.resize(8);
|
||||
buffer.resize(0);
|
||||
size_t index = 0;
|
||||
const auto add_value = [&](u32 value) {
|
||||
buffer.resize(index + 1);
|
||||
const auto add_value = [&buffer](u32 value, size_t index) {
|
||||
if (buffer.size() < index + 1)
|
||||
buffer.resize(index + 8);
|
||||
buffer[index++] = value;
|
||||
return index;
|
||||
};
|
||||
|
||||
size_t index = 0;
|
||||
u32 iter_entry = start_entry;
|
||||
Entry* current = &storage[iter_entry - 1];
|
||||
add_value(current->value);
|
||||
index = add_value(current->value, index);
|
||||
while (current->next_entry != 0) {
|
||||
iter_entry = current->next_entry;
|
||||
current = &storage[iter_entry - 1];
|
||||
add_value(current->value);
|
||||
index = add_value(current->value, index);
|
||||
}
|
||||
buffer.resize(index);
|
||||
}
|
||||
|
||||
u32 Register(u32 value) {
|
||||
@@ -124,8 +128,8 @@ private:
|
||||
u32 value{};
|
||||
};
|
||||
|
||||
std::deque<Entry> storage;
|
||||
std::deque<u32> free_entries;
|
||||
std::vector<Entry> storage;
|
||||
boost::container::deque<u32> free_entries;
|
||||
};
|
||||
|
||||
struct EmptyAllocator {
|
||||
@@ -619,7 +623,7 @@ template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) {
|
||||
Common::ScopedRangeLock lk(counter_guard, addr, size);
|
||||
UpdatePagesCachedCountNoLock(addr, size, delta);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::UpdatePagesCachedBatch(std::span<const std::pair<DAddr, size_t>> ranges, s32 delta) {
|
||||
|
||||
@@ -106,7 +106,7 @@ void PhysicalCore::RunThread(KernelCore& kernel, Kernel::KThread* thread) {
|
||||
}
|
||||
|
||||
// Determine why we stopped.
|
||||
// If a step completed successfully, skip other halt reason handlers —
|
||||
// If a step completed successfully, skip other halt reason handlers
|
||||
// the step takes priority (e.g. step may also set InstructionBreakpoint
|
||||
// if the next instruction happens to be a breakpoint).
|
||||
const bool step_completed = True(hr & Core::HaltReason::StepThread)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -32,14 +32,13 @@ constexpr bool IsValidAddressRange(u64 address, u64 size) {
|
||||
// Helper function that performs the common sanity checks for svcMapMemory
|
||||
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
|
||||
// in the same order.
|
||||
Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr, u64 src_addr,
|
||||
u64 size) {
|
||||
if (!Common::Is4KBAligned(dst_addr)) {
|
||||
Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr, u64 src_addr, u64 size) {
|
||||
if (!Common::IsAligned(dst_addr, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
|
||||
R_THROW(ResultInvalidAddress);
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(src_addr)) {
|
||||
if (!Common::IsAligned(src_addr, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
|
||||
R_THROW(ResultInvalidSize);
|
||||
}
|
||||
@@ -49,7 +48,7 @@ Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr
|
||||
R_THROW(ResultInvalidSize);
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(size)) {
|
||||
if (!Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
|
||||
R_THROW(ResultInvalidSize);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -33,12 +33,12 @@ Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
|
||||
Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size={:#X}", addr, size);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
if (!Common::IsAligned(addr, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
|
||||
R_THROW(ResultInvalidAddress);
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(size)) {
|
||||
if (!Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, {:#X}", size);
|
||||
R_THROW(ResultInvalidSize);
|
||||
}
|
||||
@@ -82,12 +82,12 @@ Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
|
||||
Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size={:#X}", addr, size);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
if (!Common::IsAligned(addr, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
|
||||
R_THROW(ResultInvalidAddress);
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(size)) {
|
||||
if (!Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, {:#X}", size);
|
||||
R_THROW(ResultInvalidSize);
|
||||
}
|
||||
|
||||
@@ -140,19 +140,19 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst
|
||||
"src_address=0x{:016X}, size=0x{:016X}",
|
||||
process_handle, dst_address, src_address, size);
|
||||
|
||||
if (!Common::Is4KBAligned(src_address)) {
|
||||
if (!Common::IsAligned(src_address, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
|
||||
src_address);
|
||||
R_THROW(ResultInvalidAddress);
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(dst_address)) {
|
||||
if (!Common::IsAligned(dst_address, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
|
||||
dst_address);
|
||||
R_THROW(ResultInvalidAddress);
|
||||
}
|
||||
|
||||
if (size == 0 || !Common::Is4KBAligned(size)) {
|
||||
if (size == 0 || !Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
|
||||
R_THROW(ResultInvalidSize);
|
||||
}
|
||||
@@ -190,6 +190,7 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst
|
||||
R_THROW(ResultInvalidCurrentMemory);
|
||||
}
|
||||
|
||||
R_UNLESS(page_table.CanContain(dst_address, size, KMemoryState::AliasCode), ResultInvalidCurrentMemory);
|
||||
R_RETURN(page_table.MapCodeMemory(dst_address, src_address, size));
|
||||
}
|
||||
|
||||
@@ -200,19 +201,19 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
|
||||
"size=0x{:016X}",
|
||||
process_handle, dst_address, src_address, size);
|
||||
|
||||
if (!Common::Is4KBAligned(dst_address)) {
|
||||
if (!Common::IsAligned(dst_address, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
|
||||
dst_address);
|
||||
R_THROW(ResultInvalidAddress);
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(src_address)) {
|
||||
if (!Common::IsAligned(src_address, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
|
||||
src_address);
|
||||
R_THROW(ResultInvalidAddress);
|
||||
}
|
||||
|
||||
if (size == 0 || !Common::Is4KBAligned(size)) {
|
||||
if (size == 0 || !Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
|
||||
R_THROW(ResultInvalidSize);
|
||||
}
|
||||
@@ -250,6 +251,7 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
|
||||
R_THROW(ResultInvalidCurrentMemory);
|
||||
}
|
||||
|
||||
R_UNLESS(page_table.CanContain(dst_address, size, KMemoryState::AliasCode), ResultInvalidCurrentMemory);
|
||||
R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size));
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/expected.h"
|
||||
|
||||
// All the constants in this file come from <https://switchbrew.org/wiki/Error_codes>
|
||||
|
||||
|
||||
@@ -729,7 +729,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface(ensure_token_id);
|
||||
rb.PushIpcInterface(ctx, ensure_token_id);
|
||||
}
|
||||
|
||||
void LoadIdTokenCacheDeprecated(HLERequestContext& ctx) {
|
||||
@@ -921,7 +921,7 @@ void Module::Interface::GetProfile(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IProfile>(system, user_id, *profile_manager);
|
||||
rb.PushIpcInterface<IProfile>(ctx, system, user_id, *profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::IsUserRegistrationRequestPermitted(HLERequestContext& ctx) {
|
||||
@@ -993,7 +993,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(HLERequestContext& c
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IManagerForApplication>(system, profile_manager);
|
||||
rb.PushIpcInterface<IManagerForApplication>(ctx, system, profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::IsUserAccountSwitchLocked(HLERequestContext& ctx) {
|
||||
@@ -1089,7 +1089,7 @@ void Module::Interface::GetProfileEditor(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IProfileEditor>(system, user_id, *profile_manager);
|
||||
rb.PushIpcInterface<IProfileEditor>(ctx, system, user_id, *profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
|
||||
@@ -1100,7 +1100,7 @@ void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAdministrator>(system, uuid);
|
||||
rb.PushIpcInterface<IAdministrator>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::ListQualifiedUsers(HLERequestContext& ctx) {
|
||||
@@ -1143,7 +1143,7 @@ void Module::Interface::GetBaasAccountManagerForSystemService(HLERequestContext&
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IManagerForSystemService>(system, uuid);
|
||||
rb.PushIpcInterface<IManagerForSystemService>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) {
|
||||
|
||||
@@ -35,7 +35,7 @@ void IAsyncContext::GetSystemEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(completion_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, completion_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IAsyncContext::Cancel(HLERequestContext& ctx) {
|
||||
|
||||
@@ -109,6 +109,10 @@ struct Applet {
|
||||
std::list<std::shared_ptr<Applet>> child_applets{};
|
||||
bool is_completed{};
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet{};
|
||||
bool unwind_after_reserved{};
|
||||
bool is_winding{};
|
||||
|
||||
// Self state
|
||||
bool exit_locked{};
|
||||
s32 fatal_section_count{};
|
||||
|
||||
@@ -25,6 +25,13 @@ void AppletStorageChannel::Push(Kernel::KernelCore& kernel, std::shared_ptr<ISto
|
||||
m_event.Signal(kernel);
|
||||
}
|
||||
|
||||
void AppletStorageChannel::Unpop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
m_data.emplace_front(std::move(storage));
|
||||
m_event.Signal(kernel);
|
||||
}
|
||||
|
||||
Result AppletStorageChannel::Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
~AppletStorageChannel();
|
||||
|
||||
void Push(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage);
|
||||
void Unpop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage);
|
||||
Result Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage);
|
||||
Kernel::KReadableEvent* GetEvent();
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
|
||||
if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) {
|
||||
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::make_unique<Service::Process>(*std::move(overlay_process)), false);
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::move(overlay_process), false);
|
||||
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
|
||||
overlay_applet->applet_id = AppletId::OverlayDisplay;
|
||||
overlay_applet->type = AppletType::OverlayApplet;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "applet_web_browser_types.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
@@ -370,16 +371,13 @@ void WebBrowser::ExtractOfflineRomFS() {
|
||||
|
||||
void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) {
|
||||
const bool use_tlv_output =
|
||||
(web_arg_header.shim_kind == ShimKind::Share &&
|
||||
web_applet_version >= WebAppletVersion::Version196608) ||
|
||||
(web_arg_header.shim_kind == ShimKind::Web &&
|
||||
web_applet_version >= WebAppletVersion::Version524288) ||
|
||||
(web_arg_header.shim_kind == ShimKind::Lhub);
|
||||
(web_arg_header.shim_kind == ShimKind::Share && web_applet_version >= WebAppletVersion::Version196608)
|
||||
|| (web_arg_header.shim_kind == ShimKind::Web && web_applet_version >= WebAppletVersion::Version524288)
|
||||
|| (web_arg_header.shim_kind == ShimKind::Lhub);
|
||||
|
||||
// https://switchbrew.org/wiki/Internet_Browser#TLVs
|
||||
if (use_tlv_output) {
|
||||
LOG_DEBUG(Service_AM, "Using TLV output: exit_reason={}, last_url={}, last_url_size={}",
|
||||
exit_reason, last_url, last_url.size());
|
||||
LOG_DEBUG(Service_AM, "Using TLV output: exit_reason={}, last_url={}, last_url_size={}", exit_reason, last_url, last_url.size());
|
||||
|
||||
// storage size for TLVs is 0x2000 bytes (as per switchbrew documentation)
|
||||
constexpr size_t TLV_STORAGE_SIZE = 0x2000;
|
||||
@@ -600,11 +598,14 @@ void WebBrowser::ExecuteShare() {
|
||||
|
||||
void WebBrowser::ExecuteWeb() {
|
||||
LOG_INFO(Service_AM, "Opening external URL at {}", external_url);
|
||||
|
||||
frontend.OpenExternalWebPage(external_url,
|
||||
[this](WebExitReason exit_reason, std::string last_url) {
|
||||
WebBrowserExit(exit_reason, last_url);
|
||||
});
|
||||
frontend.OpenExternalWebPage(external_url, [this](WebExitReason exit_reason, std::string last_url) {
|
||||
// Offline and web applets must be explicitly exited from because they respect exit state
|
||||
// Unlike the other web stuffs
|
||||
if (exit_reason == WebExitReason::ExitRequested || exit_reason == WebExitReason::EndButtonPressed)
|
||||
exit_reason = (web_arg_header.shim_kind == ShimKind::Web || web_arg_header.shim_kind == ShimKind::Offline)
|
||||
? WebExitReason::ExitRequested : WebExitReason::EndButtonPressed;
|
||||
WebBrowserExit(exit_reason, last_url);
|
||||
});
|
||||
}
|
||||
|
||||
void WebBrowser::ExecuteWifi() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -12,7 +15,10 @@ namespace Service::AM {
|
||||
|
||||
HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) {
|
||||
m_hid_server = system.ServiceManager().GetService<HID::IHidServer>("hid", true);
|
||||
this->RegisterCurrentProcess();
|
||||
}
|
||||
|
||||
void HidRegistration::RegisterCurrentProcess() {
|
||||
if (m_process.IsInitialized()) {
|
||||
m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(),
|
||||
true);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -24,6 +27,7 @@ public:
|
||||
explicit HidRegistration(Core::System& system, Process& process);
|
||||
~HidRegistration();
|
||||
|
||||
void RegisterCurrentProcess();
|
||||
void EnableAppletToGetInput(bool enable);
|
||||
|
||||
private:
|
||||
|
||||
@@ -171,6 +171,18 @@ void LifecycleManager::SignalSystemEventIfNeeded(Kernel::KernelCore& kernel) {
|
||||
}
|
||||
}
|
||||
|
||||
void LifecycleManager::ResetForRelaunch() {
|
||||
m_unordered_messages.clear();
|
||||
|
||||
m_activity_state = ActivityState::BackgroundVisible;
|
||||
m_requested_focus_state = FocusState{};
|
||||
m_acknowledged_focus_state = FocusState{};
|
||||
m_has_focus_state_changed = true;
|
||||
|
||||
m_suspend_mode = SuspendMode::NoOverride;
|
||||
m_forced_suspend = false;
|
||||
}
|
||||
|
||||
bool LifecycleManager::PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message) {
|
||||
const auto message = this->PopMessageInOrderOfPriority();
|
||||
this->SignalSystemEventIfNeeded(kernel);
|
||||
|
||||
@@ -138,6 +138,8 @@ public:
|
||||
void PushUnorderedMessage(Kernel::KernelCore& kernel, AppletMessage message);
|
||||
bool PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message);
|
||||
|
||||
void ResetForRelaunch();
|
||||
|
||||
private:
|
||||
FocusState GetFocusStateWhileForegroundObscured() const;
|
||||
FocusState GetFocusStateWhileBackground(bool is_obscured) const;
|
||||
|
||||
@@ -40,23 +40,23 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::optional<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
[[nodiscard]] inline std::unique_ptr<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
// Get the appropriate loader to parse this NCA.
|
||||
out_loader = Loader::GetLoader(system, file, program_id, program_index);
|
||||
// Ensure we have a loader which can parse the NCA.
|
||||
if (out_loader) {
|
||||
// Try to load the process.
|
||||
auto process = std::make_optional<Process>(system);
|
||||
auto process = std::make_unique<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
// Attempt to load program NCA.
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
@@ -66,7 +66,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
|
||||
// Ensure we retrieved a program NCA.
|
||||
if (!nca_raw) {
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure we have a suitable version.
|
||||
@@ -76,7 +76,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
|
||||
}
|
||||
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) {
|
||||
FileSys::NACP nacp;
|
||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
@@ -110,7 +110,23 @@ std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, st
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
return process;
|
||||
}
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id) {
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
const auto nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
|
||||
if (!nca_raw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto loader = Loader::GetLoader(system, nca_raw, program_id, 0);
|
||||
if (!loader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Loader::ResultStatus status{};
|
||||
return process.Initialize(*loader, status);
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -27,7 +27,9 @@ class Process;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
|
||||
|
||||
bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id);
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -35,9 +35,9 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
Loader::ResultStatus result;
|
||||
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = AppletId::Application;
|
||||
applet->type = AppletType::Application;
|
||||
@@ -88,9 +88,9 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
auto process = CreateProcess(system, application_id, 1, 22);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = application_id;
|
||||
applet->applet_id = AppletId::Starter;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
|
||||
@@ -75,7 +75,7 @@ ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
|
||||
{105, D<&ILibraryAppletAccessor::GetPopOutDataEvent>, "GetPopOutDataEvent"},
|
||||
{106, D<&ILibraryAppletAccessor::GetPopInteractiveOutDataEvent>, "GetPopInteractiveOutDataEvent"},
|
||||
{110, nullptr, "NeedsToExitProcess"},
|
||||
{120, nullptr, "GetLibraryAppletInfo"},
|
||||
{120, D<&ILibraryAppletAccessor::GetLibraryAppletInfo>, "GetLibraryAppletInfo"},
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
{160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+
|
||||
{170, D<&ILibraryAppletAccessor::Unknown170>, "Unknown170"}, //22.0.0+
|
||||
@@ -218,6 +218,16 @@ Result ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(Out<u64> out_handl
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletAccessor::GetLibraryAppletInfo(
|
||||
Out<LibraryAppletInfo> out_library_applet_info) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
*out_library_applet_info = {
|
||||
.applet_id = m_applet->applet_id,
|
||||
.library_applet_mode = m_applet->library_applet_mode,
|
||||
};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletAccessor::Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_event = m_applet->unknown_event.GetHandle();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/am/service/library_applet_self_accessor.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -21,6 +22,10 @@ public:
|
||||
std::shared_ptr<Applet> applet);
|
||||
~ILibraryAppletAccessor();
|
||||
|
||||
std::shared_ptr<Applet> GetApplet() const {
|
||||
return m_applet;
|
||||
}
|
||||
|
||||
private:
|
||||
Result GetAppletStateChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result IsCompleted(Out<bool> out_is_completed);
|
||||
@@ -37,6 +42,7 @@ private:
|
||||
Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetIndirectLayerConsumerHandle(Out<u64> out_handle);
|
||||
Result GetLibraryAppletInfo(Out<LibraryAppletInfo> out_library_applet_info);
|
||||
Result Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
|
||||
void FrontendExecute();
|
||||
|
||||
@@ -123,7 +123,7 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
|
||||
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200);
|
||||
if (process) {
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
|
||||
@@ -233,8 +233,9 @@ Result ILibraryAppletSelfAccessor::ReportVisibleErrorWithErrorContext(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletSelfAccessor::UnpopInData() {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
Result ILibraryAppletSelfAccessor::UnpopInData(SharedPointer<IStorage> storage) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
m_broker->GetInData().Unpop(system.Kernel(), storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ private:
|
||||
Result ReportVisibleError(ErrorCode error_code);
|
||||
Result ReportVisibleErrorWithErrorContext(
|
||||
ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context);
|
||||
Result UnpopInData();
|
||||
Result UnpopInData(SharedPointer<IStorage> storage);
|
||||
Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language);
|
||||
Result GetCurrentApplicationId(Out<u64> out_application_id);
|
||||
Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/frontend/applets.h"
|
||||
#include "core/hle/service/am/service/library_applet_accessor.h"
|
||||
#include "core/hle/service/am/service/process_winding_controller.h"
|
||||
@@ -43,6 +45,18 @@ Result IProcessWindingController::OpenCallingLibraryApplet(
|
||||
Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
reserved_applet = std::move(m_applet->reserved_applet);
|
||||
}
|
||||
|
||||
if (reserved_applet != nullptr) {
|
||||
*out_calling_library_applet = std::make_shared<ILibraryAppletAccessor>(
|
||||
system, reserved_applet->caller_applet_broker, reserved_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
const auto caller_applet = m_applet->caller_applet.lock();
|
||||
if (caller_applet == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No caller applet available");
|
||||
@@ -74,22 +88,82 @@ Result IProcessWindingController::PopContext(Out<SharedPointer<IStorage>> out_co
|
||||
}
|
||||
|
||||
Result IProcessWindingController::CancelWindingReservation() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet.reset();
|
||||
m_applet->unwind_after_reserved = false;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::WindAndDoReserved() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
|
||||
reserved_applet = m_applet->reserved_applet;
|
||||
m_applet->display_layer_manager.SetWindowVisibility(false);
|
||||
m_applet->exit_locked = false;
|
||||
system.SetExitLocked(false);
|
||||
}
|
||||
|
||||
if (reserved_applet) {
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->is_winding = true;
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock lk{reserved_applet->lock};
|
||||
reserved_applet->window_visible = true;
|
||||
reserved_applet->process->Run();
|
||||
}
|
||||
|
||||
if (reserved_applet->frontend) {
|
||||
reserved_applet->frontend->Initialize();
|
||||
reserved_applet->frontend->Execute();
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Service_AM, "called without a reserved applet to start");
|
||||
}
|
||||
|
||||
m_applet->process->Terminate();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
if (reserved_applet_accessor == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No applet accessor provided");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet = reserved_applet_accessor->GetApplet();
|
||||
m_applet->unwind_after_reserved = true;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::ReserveToStartAndWait() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
Result IProcessWindingController::ReserveToStartAndWait(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
if (reserved_applet_accessor == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No applet accessor provided");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet = reserved_applet_accessor->GetApplet();
|
||||
m_applet->unwind_after_reserved = false;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -29,8 +29,9 @@ private:
|
||||
Result PopContext(Out<SharedPointer<IStorage>> out_context);
|
||||
Result CancelWindingReservation();
|
||||
Result WindAndDoReserved();
|
||||
Result ReserveToStartAndWaitAndUnwindThis();
|
||||
Result ReserveToStartAndWait();
|
||||
Result ReserveToStartAndWaitAndUnwindThis(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor);
|
||||
Result ReserveToStartAndWait(SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor);
|
||||
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/event_observer.h"
|
||||
#include "core/hle/service/am/process_creation.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
|
||||
namespace Service::AM {
|
||||
@@ -240,6 +241,37 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// A winding applet has had its own process killed but is kept alive as a transparent slot
|
||||
// while the reserved applet running in its place finishes (see WindAndDoReserved()).
|
||||
if (applet->is_winding) {
|
||||
if (!applet->child_applets.empty()) {
|
||||
it = std::next(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool unwind = applet->unwind_after_reserved;
|
||||
applet->is_winding = false;
|
||||
applet->unwind_after_reserved = false;
|
||||
|
||||
if (unwind && this->RestartAppletProcessLocked(applet.get())) {
|
||||
const u64 new_aruid = applet->aruid.pid;
|
||||
const auto next = std::next(it);
|
||||
|
||||
if (new_aruid != aruid) {
|
||||
auto node = m_applets.extract(it);
|
||||
node.key() = new_aruid;
|
||||
m_applets.insert(std::move(node));
|
||||
}
|
||||
|
||||
applet->process->Run();
|
||||
m_event_observer->RequestUpdate();
|
||||
it = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
applet->reserved_applet.reset();
|
||||
}
|
||||
|
||||
// Terminated, so ensure all child applets are terminated.
|
||||
if (!applet->child_applets.empty()) {
|
||||
this->TerminateChildAppletsLocked(applet.get());
|
||||
@@ -301,6 +333,28 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowSystem::RestartAppletProcessLocked(Applet* applet) {
|
||||
if (!ReinitializeProcess(m_system, *applet->process, applet->program_id)) {
|
||||
LOG_ERROR(Service_AM, "Failed to restart winding applet_id={}",
|
||||
static_cast<u32>(applet->applet_id));
|
||||
return false;
|
||||
}
|
||||
|
||||
applet->aruid.pid = applet->process->GetProcessId();
|
||||
applet->is_process_running = false;
|
||||
applet->is_completed = false;
|
||||
|
||||
applet->hid_registration.RegisterCurrentProcess();
|
||||
|
||||
applet->lifecycle_manager.ResetForRelaunch();
|
||||
applet->is_activity_runnable = false;
|
||||
|
||||
applet->launch_reason.flag = 1;
|
||||
|
||||
m_event_observer->TrackAppletProcess(*applet);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowSystem::LockHomeMenuIntoForegroundLocked() {
|
||||
// If the home menu is not locked into foreground, then there's nothing to do.
|
||||
if (m_home_menu == nullptr || !m_home_menu_foreground_locked) {
|
||||
@@ -352,6 +406,9 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
||||
const bool has_obscuring_child_applets = [&] {
|
||||
for (const auto& child_applet : applet->child_applets) {
|
||||
std::scoped_lock lk2{child_applet->lock};
|
||||
if (child_applet->is_winding) {
|
||||
return true;
|
||||
}
|
||||
const auto mode = child_applet->library_applet_mode;
|
||||
if (child_applet->is_process_running && child_applet->window_visible &&
|
||||
(mode == LibraryAppletMode::AllForeground ||
|
||||
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
|
||||
private:
|
||||
void PruneTerminatedAppletsLocked();
|
||||
bool RestartAppletProcessLocked(Applet* applet);
|
||||
bool LockHomeMenuIntoForegroundLocked();
|
||||
void TerminateChildAppletsLocked(Applet* applet);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
|
||||
|
||||
@@ -82,7 +82,7 @@ void APM::OpenSession(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISession>(system, controller);
|
||||
rb.PushIpcInterface<ISession>(ctx, system, controller);
|
||||
}
|
||||
|
||||
void APM::GetPerformanceMode(HLERequestContext& ctx) {
|
||||
@@ -125,7 +125,7 @@ void APM_Sys::GetPerformanceEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISession>(system, controller);
|
||||
rb.PushIpcInterface<ISession>(ctx, system, controller);
|
||||
}
|
||||
|
||||
void APM_Sys::SetCpuBoostMode(HLERequestContext& ctx) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -45,7 +48,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IRequest>(system);
|
||||
rb.PushIpcInterface<IRequest>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(readable_event.Signal(system.Kernel()));
|
||||
rb.PushCopyObjects(readable_event);
|
||||
rb.PushCopyObjects(ctx, readable_event);
|
||||
}
|
||||
|
||||
void Cancel(HLERequestContext& ctx) {
|
||||
@@ -400,7 +400,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(notification_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, notification_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void Clear(HLERequestContext& ctx) {
|
||||
@@ -476,7 +476,7 @@ private:
|
||||
void Module::Interface::CreateFriendService(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IFriendService>(system);
|
||||
rb.PushIpcInterface<IFriendService>(ctx, system);
|
||||
LOG_DEBUG(Service_Friend, "called");
|
||||
}
|
||||
|
||||
@@ -488,12 +488,12 @@ void Module::Interface::CreateNotificationService(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INotificationService>(system, uuid);
|
||||
rb.PushIpcInterface<INotificationService>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
|
||||
const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)} {}
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)}
|
||||
{}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ void ARP_W::AcquireRegistrar(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface(registrar);
|
||||
rb.PushIpcInterface(ctx, registrar);
|
||||
}
|
||||
|
||||
void ARP_W::UnregisterApplicationInstance(HLERequestContext& ctx) {
|
||||
|
||||
@@ -28,7 +28,7 @@ void BGTC_T::OpenTaskService(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ITaskService>(system);
|
||||
rb.PushIpcInterface<ITaskService>(ctx, system);
|
||||
}
|
||||
|
||||
ITaskService::ITaskService(Core::System& system_) : ServiceFramework{system_, "ITaskService"} {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -56,7 +59,7 @@ ECTX_AW::~ECTX_AW() = default;
|
||||
void ECTX_AW::CreateContextRegistrar(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IContextRegistrar>(std::make_shared<IContextRegistrar>(system));
|
||||
rb.PushIpcInterface<IContextRegistrar>(ctx, system);
|
||||
}
|
||||
|
||||
} // namespace Service::Glue
|
||||
|
||||
@@ -23,11 +23,13 @@ TimeZoneService::TimeZoneService(
|
||||
Core::System& system_, FileTimestampWorker& file_timestamp_worker,
|
||||
bool can_write_timezone_device_location, TimeZoneBinary& time_zone_binary,
|
||||
std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service)
|
||||
: ServiceFramework{system_, "ITimeZoneService"}, m_system{system},
|
||||
m_can_write_timezone_device_location{can_write_timezone_device_location},
|
||||
m_file_timestamp_worker{file_timestamp_worker}, m_wrapped_service{std::move(
|
||||
time_zone_service)},
|
||||
m_operation_event{m_system}, m_time_zone_binary{time_zone_binary} {
|
||||
: ServiceFramework{system_, "ITimeZoneService"}
|
||||
, m_can_write_timezone_device_location{can_write_timezone_device_location}
|
||||
, m_file_timestamp_worker{file_timestamp_worker}
|
||||
, m_wrapped_service{std::move(time_zone_service)}
|
||||
, m_operation_event{system}
|
||||
, m_time_zone_binary{time_zone_binary}
|
||||
{
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&TimeZoneService::GetDeviceLocationName>, "GetDeviceLocationName"},
|
||||
@@ -48,8 +50,7 @@ TimeZoneService::TimeZoneService(
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
m_set_sys =
|
||||
m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
|
||||
m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
|
||||
}
|
||||
|
||||
TimeZoneService::~TimeZoneService() = default;
|
||||
@@ -89,7 +90,7 @@ Result TimeZoneService::SetDeviceLocationName(
|
||||
|
||||
std::scoped_lock m{m_list_mutex};
|
||||
for (auto& operation_event : m_list_nodes) {
|
||||
operation_event.m_event->Signal(m_system.Kernel());
|
||||
operation_event.m_event->Signal(system.Kernel());
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -77,9 +80,7 @@ public:
|
||||
const Service::PSC::Time::CalendarTime& calendar_time);
|
||||
|
||||
private:
|
||||
Core::System& m_system;
|
||||
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
||||
|
||||
bool m_can_write_timezone_device_location;
|
||||
FileTimestampWorker& m_file_timestamp_worker;
|
||||
std::shared_ptr<Service::PSC::Time::TimeZoneService> m_wrapped_service;
|
||||
|
||||
@@ -726,7 +726,7 @@ void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& c
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
|
||||
@@ -734,7 +734,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
|
||||
@@ -759,7 +759,7 @@ void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext&
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, unique_pad_connection_event->GetReadableEvent());
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
@@ -776,7 +776,7 @@ void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestConte
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, joy_detach_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
|
||||
|
||||
@@ -31,7 +31,7 @@ class Memory;
|
||||
}
|
||||
|
||||
namespace IPC {
|
||||
class ResponseBuilder;
|
||||
struct ResponseBuilder;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
@@ -392,7 +392,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
friend class IPC::ResponseBuilder;
|
||||
friend struct IPC::ResponseBuilder;
|
||||
|
||||
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
|
||||
@@ -24,45 +24,7 @@ namespace IPC {
|
||||
|
||||
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
|
||||
|
||||
class RequestHelperBase {
|
||||
protected:
|
||||
Service::HLERequestContext* context = nullptr;
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
|
||||
public:
|
||||
explicit RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
|
||||
|
||||
explicit RequestHelperBase(Service::HLERequestContext& ctx)
|
||||
: context(&ctx), cmdbuf(ctx.CommandBuffer()) {}
|
||||
|
||||
void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) {
|
||||
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
}
|
||||
index += size_in_words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
*/
|
||||
void AlignWithPadding() {
|
||||
if (index & 3) {
|
||||
Skip(static_cast<u32>(4 - (index & 3)), true);
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetCurrentOffset() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
void SetCurrentOffset(u32 offset) {
|
||||
index = offset;
|
||||
}
|
||||
};
|
||||
|
||||
class ResponseBuilder : public RequestHelperBase {
|
||||
public:
|
||||
struct ResponseBuilder {
|
||||
/// Flags used for customizing the behavior of ResponseBuilder
|
||||
enum class Flags : u32 {
|
||||
None = 0,
|
||||
@@ -71,14 +33,13 @@ public:
|
||||
AlwaysMoveHandles = 1,
|
||||
};
|
||||
|
||||
explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_,
|
||||
u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0,
|
||||
Flags flags = Flags::None)
|
||||
: RequestHelperBase(ctx), normal_params_size(normal_params_size_),
|
||||
num_handles_to_copy(num_handles_to_copy_),
|
||||
num_objects_to_move(num_objects_to_move_), kernel{ctx.kernel} {
|
||||
|
||||
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
|
||||
inline explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_, u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0, Flags flags = Flags::None)
|
||||
: cmdbuf(ctx.CommandBuffer())
|
||||
, normal_params_size(normal_params_size_)
|
||||
, num_handles_to_copy(num_handles_to_copy_)
|
||||
, num_objects_to_move(num_objects_to_move_)
|
||||
{
|
||||
std::memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
|
||||
|
||||
IPC::CommandHeader header{};
|
||||
auto const mgr = ctx.GetManager().get();
|
||||
@@ -117,9 +78,7 @@ public:
|
||||
handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy_);
|
||||
handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move);
|
||||
PushRaw(handle_descriptor_header);
|
||||
|
||||
ctx.handles_offset = index;
|
||||
|
||||
Skip(num_handles_to_copy + num_handles_to_move, true);
|
||||
}
|
||||
|
||||
@@ -131,7 +90,6 @@ public:
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
}
|
||||
|
||||
IPC::DataPayloadHeader data_payload_header{};
|
||||
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
|
||||
PushRaw(data_payload_header);
|
||||
@@ -141,34 +99,39 @@ public:
|
||||
|
||||
ctx.data_payload_offset = index;
|
||||
ctx.write_size += index;
|
||||
ctx.domain_offset = static_cast<u32>(index + raw_data_size / sizeof(u32));
|
||||
ctx.domain_offset = u32(index + raw_data_size / sizeof(u32));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void PushIpcInterface(std::shared_ptr<T> iface) {
|
||||
auto manager{context->GetManager()};
|
||||
inline void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
index += size_in_words;
|
||||
}
|
||||
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
|
||||
inline u32 GetCurrentOffset() const { return index; }
|
||||
inline void SetCurrentOffset(u32 offset) { index = offset; }
|
||||
|
||||
template <class T> inline void PushIpcInterface(Service::HLERequestContext& ctx, std::shared_ptr<T> iface) {
|
||||
auto manager = ctx.GetManager();
|
||||
if (manager->IsDomain()) {
|
||||
context->AddDomainObject(std::move(iface));
|
||||
ctx.AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(kernel, Kernel::LimitableResource::SessionCountMax, 1));
|
||||
ASSERT(Kernel::GetCurrentProcess(ctx.kernel).GetResourceLimit()->Reserve(ctx.kernel, Kernel::LimitableResource::SessionCountMax, 1));
|
||||
|
||||
auto* session = Kernel::KSession::Create(kernel);
|
||||
session->Initialize(kernel, nullptr, 0);
|
||||
Kernel::KSession::Register(kernel, session);
|
||||
auto* session = Kernel::KSession::Create(ctx.kernel);
|
||||
session->Initialize(ctx.kernel, nullptr, 0);
|
||||
Kernel::KSession::Register(ctx.kernel, session);
|
||||
|
||||
auto next_manager = std::make_shared<Service::SessionRequestManager>(
|
||||
kernel, manager->GetServerManager());
|
||||
auto next_manager = std::make_shared<Service::SessionRequestManager>(ctx.kernel, manager->GetServerManager());
|
||||
next_manager->SetSessionHandler(iface);
|
||||
manager->GetServerManager().RegisterSession(&session->GetServerSession(), next_manager);
|
||||
|
||||
context->AddMoveObject(&session->GetClientSession());
|
||||
ctx.AddMoveObject(&session->GetClientSession());
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class... Args>
|
||||
void PushIpcInterface(Args&&... args) {
|
||||
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
|
||||
template <class T, class... Args> inline void PushIpcInterface(Service::HLERequestContext& ctx, Args&&... args) {
|
||||
PushIpcInterface<T>(ctx, std::make_shared<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
void PushImpl(s8 value);
|
||||
@@ -184,59 +147,38 @@ public:
|
||||
void PushImpl(bool value);
|
||||
void PushImpl(Result value);
|
||||
|
||||
template <typename T>
|
||||
void Push(T value) {
|
||||
template <typename T> inline void Push(T value) {
|
||||
return PushImpl(value);
|
||||
}
|
||||
|
||||
template <typename First, typename... Other>
|
||||
void Push(const First& first_value, const Other&... other_values);
|
||||
|
||||
/**
|
||||
* Helper function for pushing strongly-typed enumeration values.
|
||||
*
|
||||
* @tparam Enum The enumeration type to be pushed
|
||||
*
|
||||
* @param value The value to push.
|
||||
*
|
||||
* @note The underlying size of the enumeration type is the size of the
|
||||
* data that gets pushed. e.g. "enum class SomeEnum : u16" will
|
||||
* push a u16-sized amount of data.
|
||||
*/
|
||||
template <typename Enum>
|
||||
void PushEnum(Enum value) {
|
||||
/// @brief Helper function for pushing strongly-typed enumeration values.
|
||||
/// @tparam Enum The enumeration type to be pushed
|
||||
/// @param value The value to push.
|
||||
/// @note The underlying size of the enumeration type is the size of the data that gets pushed.
|
||||
/// e.g. "enum class SomeEnum : u16" will push a u16-sized amount of data.
|
||||
template <typename Enum> inline void PushEnum(Enum value) {
|
||||
static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call.");
|
||||
static_assert(!std::is_convertible_v<Enum, int>,
|
||||
"enum type in PushEnum must be a strongly typed enum.");
|
||||
static_assert(!std::is_convertible_v<Enum, int>, "enum type in PushEnum must be a strongly typed enum.");
|
||||
Push(static_cast<std::underlying_type_t<Enum>>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copies the content of the given trivially copyable class to the buffer as a normal
|
||||
* param
|
||||
* @note: The input class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
void PushRaw(const T& value);
|
||||
/// @brief Copies the content of the given trivially copyable class to the buffer as a normal param
|
||||
/// @note: The input class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> void PushRaw(const T& value);
|
||||
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers);
|
||||
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers);
|
||||
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers);
|
||||
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushMoveObjects(O*... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushMoveObjects(O&... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushCopyObjects(O*... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushCopyObjects(O&... pointers);
|
||||
|
||||
private:
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
u32 normal_params_size{};
|
||||
u32 num_handles_to_copy{};
|
||||
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
|
||||
u32 data_payload_index{};
|
||||
Kernel::KernelCore& kernel;
|
||||
};
|
||||
|
||||
/// Push ///
|
||||
@@ -251,8 +193,7 @@ inline void ResponseBuilder::PushImpl(u32 value) {
|
||||
|
||||
template <typename T>
|
||||
void ResponseBuilder::PushRaw(const T& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
std::memcpy(cmdbuf + index, &value, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
@@ -272,8 +213,8 @@ inline void ResponseBuilder::PushImpl(s16 value) {
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(s64 value) {
|
||||
PushImpl(static_cast<u32>(value));
|
||||
PushImpl(static_cast<u32>(value >> 32));
|
||||
PushImpl(u32(value));
|
||||
PushImpl(u32(value >> 32));
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(u8 value) {
|
||||
@@ -285,8 +226,8 @@ inline void ResponseBuilder::PushImpl(u16 value) {
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(u64 value) {
|
||||
PushImpl(static_cast<u32>(value));
|
||||
PushImpl(static_cast<u32>(value >> 32));
|
||||
PushImpl(u32(value));
|
||||
PushImpl(u32(value >> 32));
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(float value) {
|
||||
@@ -312,90 +253,88 @@ void ResponseBuilder::Push(const First& first_value, const Other&... other_value
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushCopyObjects(O*... pointers) {
|
||||
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddCopyObject(object);
|
||||
ctx.AddCopyObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushCopyObjects(O&... pointers) {
|
||||
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers) {
|
||||
auto objects = {&pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddCopyObject(object);
|
||||
ctx.AddCopyObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushMoveObjects(O*... pointers) {
|
||||
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddMoveObject(object);
|
||||
ctx.AddMoveObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushMoveObjects(O&... pointers) {
|
||||
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers) {
|
||||
auto objects = {&pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddMoveObject(object);
|
||||
ctx.AddMoveObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
class RequestParser : public RequestHelperBase {
|
||||
public:
|
||||
explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
|
||||
|
||||
explicit RequestParser(Service::HLERequestContext& ctx) : RequestHelperBase(ctx) {
|
||||
struct RequestParser {
|
||||
inline explicit RequestParser(u32* command_buffer) : cmdbuf(command_buffer) {}
|
||||
inline explicit RequestParser(Service::HLERequestContext& ctx)
|
||||
: cmdbuf(ctx.CommandBuffer())
|
||||
{
|
||||
// TIPC does not have data payload offset
|
||||
if (!ctx.IsTipc()) {
|
||||
ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete");
|
||||
Skip(ctx.GetDataPayloadOffset(), false);
|
||||
}
|
||||
|
||||
// Skip the u64 command id, it's already stored in the context
|
||||
static constexpr u32 CommandIdSize = 2;
|
||||
Skip(CommandIdSize, false);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Pop();
|
||||
inline void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
index += size_in_words;
|
||||
}
|
||||
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
|
||||
inline u32 GetCurrentOffset() const { return index; }
|
||||
inline void SetCurrentOffset(u32 offset) { index = offset; }
|
||||
|
||||
template <typename T>
|
||||
void Pop(T& value);
|
||||
|
||||
template <typename First, typename... Other>
|
||||
void Pop(First& first_value, Other&... other_values);
|
||||
template <typename T> T Pop();
|
||||
template <typename T> void Pop(T& value);
|
||||
template <typename First, typename... Other> void Pop(First& first_value, Other&... other_values);
|
||||
|
||||
template <typename T>
|
||||
T PopEnum() {
|
||||
static_assert(std::is_enum_v<T>, "T must be an enum type within a PopEnum call.");
|
||||
static_assert(!std::is_convertible_v<T, int>,
|
||||
"enum type in PopEnum must be a strongly typed enum.");
|
||||
return static_cast<T>(Pop<std::underlying_type_t<T>>());
|
||||
static_assert(!std::is_convertible_v<T, int>, "enum type in PopEnum must be a strongly typed enum.");
|
||||
return T(Pop<std::underlying_type_t<T>>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads the next normal parameters as a struct, by copying it
|
||||
* @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
void PopRaw(T& value);
|
||||
/// @brief Reads the next normal parameters as a struct, by copying it
|
||||
/// @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> void PopRaw(T& value);
|
||||
|
||||
/**
|
||||
* @brief Reads the next normal parameters as a struct, by copying it into a new value
|
||||
* @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
T PopRaw();
|
||||
/// @brief Reads the next normal parameters as a struct, by copying it into a new value
|
||||
/// @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> T PopRaw();
|
||||
|
||||
template <class T>
|
||||
std::weak_ptr<T> PopIpcInterface() {
|
||||
ASSERT(context->GetManager()->IsDomain());
|
||||
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
|
||||
return context->GetDomainHandler<T>(Pop<u32>() - 1);
|
||||
template <class T> [[nodiscard]] std::weak_ptr<T> PopIpcInterface(Service::HLERequestContext& ctx) {
|
||||
ASSERT(ctx.GetManager()->IsDomain());
|
||||
ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0);
|
||||
return ctx.GetDomainHandler<T>(Pop<u32>() - 1);
|
||||
}
|
||||
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
};
|
||||
|
||||
/// Pop ///
|
||||
@@ -407,7 +346,7 @@ inline u32 RequestParser::Pop() {
|
||||
|
||||
template <>
|
||||
inline s32 RequestParser::Pop() {
|
||||
return static_cast<s32>(Pop<u32>());
|
||||
return s32(Pop<u32>());
|
||||
}
|
||||
|
||||
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
|
||||
@@ -417,8 +356,7 @@ inline s32 RequestParser::Pop() {
|
||||
#endif
|
||||
template <typename T>
|
||||
void RequestParser::PopRaw(T& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
std::memcpy(&value, cmdbuf + index, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ILogger>(system);
|
||||
rb.PushIpcInterface<ILogger>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAm>(system);
|
||||
rb.PushIpcInterface<IAm>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -173,7 +173,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<MFIUser>(system);
|
||||
rb.PushIpcInterface<MFIUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -195,7 +195,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IUser>(system);
|
||||
rb.PushIpcInterface<IUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -217,7 +217,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISystem>(system);
|
||||
rb.PushIpcInterface<ISystem>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent());
|
||||
rb.PushCopyObjects(ctx, GetManager()->AttachAvailabilityChangeEvent());
|
||||
}
|
||||
|
||||
void NfcInterface::StartDetection(HLERequestContext& ctx) {
|
||||
@@ -203,7 +203,7 @@ void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(result);
|
||||
rb.PushCopyObjects(out_event);
|
||||
rb.PushCopyObjects(ctx, out_event);
|
||||
}
|
||||
|
||||
void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
|
||||
@@ -217,7 +217,7 @@ void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(result);
|
||||
rb.PushCopyObjects(out_event);
|
||||
rb.PushCopyObjects(ctx, out_event);
|
||||
}
|
||||
|
||||
void NfcInterface::SetNfcEnabled(HLERequestContext& ctx) {
|
||||
|
||||
@@ -157,7 +157,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IUser>(system);
|
||||
rb.PushIpcInterface<IUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -179,7 +179,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISystem>(system);
|
||||
rb.PushIpcInterface<ISystem>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -201,7 +201,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IDebug>(system);
|
||||
rb.PushIpcInterface<IDebug>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ private:
|
||||
void GetSystemEventReadableHandle(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(evt_scan_complete->GetReadableEvent(),
|
||||
rb.PushCopyObjects(ctx, evt_scan_complete->GetReadableEvent(),
|
||||
evt_processing->GetReadableEvent());
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(event1->GetReadableEvent(), event2->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, event1->GetReadableEvent(), event2->GetReadableEvent());
|
||||
}
|
||||
|
||||
void Cancel(HLERequestContext& ctx) {
|
||||
@@ -528,7 +528,7 @@ void IGeneralService::CreateScanRequest(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IScanRequest>(system);
|
||||
rb.PushIpcInterface<IScanRequest>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::CreateRequest(HLERequestContext& ctx) {
|
||||
@@ -537,7 +537,7 @@ void IGeneralService::CreateRequest(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IRequest>(system);
|
||||
rb.PushIpcInterface<IRequest>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) {
|
||||
@@ -716,7 +716,7 @@ void IGeneralService::GetNetworkProfile(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INetworkProfile>(system);
|
||||
rb.PushIpcInterface<INetworkProfile>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::SetNetworkProfile(HLERequestContext& ctx) {
|
||||
@@ -869,7 +869,7 @@ void IGeneralService::CreateTemporaryNetworkProfile(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INetworkProfile>(system);
|
||||
rb.PushIpcInterface<INetworkProfile>(ctx, system);
|
||||
rb.PushRaw<u128>(uuid);
|
||||
}
|
||||
|
||||
@@ -1124,7 +1124,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IGeneralService>(system);
|
||||
rb.PushIpcInterface<IGeneralService>(ctx, system);
|
||||
}
|
||||
|
||||
void CreateGeneralService(HLERequestContext& ctx) {
|
||||
@@ -1132,7 +1132,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IGeneralService>(system);
|
||||
rb.PushIpcInterface<IGeneralService>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ private:
|
||||
LOG_WARNING(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAsync>(system);
|
||||
rb.PushIpcInterface<IShopServiceAsync>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
LOG_WARNING(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessor>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessor>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -336,7 +336,7 @@ private:
|
||||
LOG_DEBUG(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system);
|
||||
}
|
||||
|
||||
void IsLargeResourceAvailable(HLERequestContext& ctx) {
|
||||
@@ -356,7 +356,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -439,7 +439,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(finished_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, finished_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void GetResult(HLERequestContext& ctx) {
|
||||
@@ -500,7 +500,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system);
|
||||
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(ctx, system);
|
||||
}
|
||||
|
||||
// TODO(ogniK): Do we need these?
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#define STBI_ONLY_JPEG 1
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize.h>
|
||||
#include <stb_image_write.h>
|
||||
#include <string>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
@@ -359,8 +361,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(async_value->ReadableEvent());
|
||||
rb.PushIpcInterface(std::move(async_value));
|
||||
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
|
||||
rb.PushIpcInterface(ctx, std::move(async_value));
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
|
||||
|
||||
@@ -199,7 +199,7 @@ void NVDRV::QueryEvent(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
auto& readable_event = event->GetReadableEvent();
|
||||
rb.PushCopyObjects(readable_event);
|
||||
rb.PushCopyObjects(ctx, readable_event);
|
||||
rb.PushEnum(NvResult::Success);
|
||||
} else {
|
||||
LOG_ERROR(Service_NVDRV, "Invalid event request!");
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -28,6 +30,15 @@ public:
|
||||
inline explicit Process(Core::System& system) noexcept : m_system(system) {}
|
||||
inline ~Process() { this->Finalize(); }
|
||||
|
||||
Process(const Process&) = delete;
|
||||
Process& operator=(const Process&) = delete;
|
||||
Process& operator=(Process&&) = delete;
|
||||
inline Process(Process&& other) noexcept
|
||||
: m_system(other.m_system), m_process(std::exchange(other.m_process, nullptr)),
|
||||
m_main_thread_stack_size(std::exchange(other.m_main_thread_stack_size, 0)),
|
||||
m_main_thread_priority(std::exchange(other.m_main_thread_priority, 0)),
|
||||
m_process_started(std::exchange(other.m_process_started, false)) {}
|
||||
|
||||
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
|
||||
void Finalize();
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -124,7 +127,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IClkrstSession>(system, device_code);
|
||||
rb.PushIpcInterface<IClkrstSession>(ctx, system, device_code);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(*process);
|
||||
rb.PushCopyObjects(ctx, *process);
|
||||
rb.PushRaw(program_location);
|
||||
rb.PushRaw(override_status);
|
||||
}
|
||||
|
||||
@@ -32,11 +32,13 @@ Alarm::~Alarm() {
|
||||
m_ctx.CloseEvent(m_event);
|
||||
}
|
||||
|
||||
Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock,
|
||||
PowerStateRequestManager& power_state_request_manager)
|
||||
: m_system{system}, m_ctx{system, "Psc:Alarms"}, m_steady_clock{steady_clock},
|
||||
m_power_state_request_manager{power_state_request_manager}, m_event{m_ctx.CreateEvent(
|
||||
"Psc:Alarms:Event")} {}
|
||||
Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, PowerStateRequestManager& power_state_request_manager)
|
||||
: m_system{system}
|
||||
, m_ctx{system, "Psc:Alarms"}
|
||||
, m_steady_clock{steady_clock}
|
||||
, m_power_state_request_manager{power_state_request_manager}
|
||||
, m_event{m_ctx.CreateEvent("Psc:Alarms:Event")}
|
||||
{}
|
||||
|
||||
Alarms::~Alarms() {
|
||||
m_ctx.CloseEvent(m_event);
|
||||
@@ -132,7 +134,9 @@ Result Alarms::UpdateClosestAndSignal(Kernel::KernelCore& kernel) {
|
||||
}
|
||||
|
||||
IAlarmService::IAlarmService(Core::System& system_, std::shared_ptr<TimeManager> manager)
|
||||
: ServiceFramework{system_, "time:al"}, m_system{system}, m_alarms{manager->m_alarms} {
|
||||
: ServiceFramework{system_, "time:al"}
|
||||
, m_alarms{manager->m_alarms}
|
||||
{
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAlarmService::CreateWakeupAlarm, "CreateWakeupAlarm"},
|
||||
@@ -147,7 +151,7 @@ void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::WakeupAlarm);
|
||||
}
|
||||
|
||||
void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
|
||||
@@ -155,7 +159,7 @@ void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::BackgroundTaskAlarm);
|
||||
}
|
||||
|
||||
ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type)
|
||||
@@ -179,7 +183,7 @@ void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(m_alarm.GetEventHandle());
|
||||
rb.PushCopyObjects(ctx, m_alarm.GetEventHandle());
|
||||
}
|
||||
|
||||
void ISteadyClockAlarm::Enable(HLERequestContext& ctx) {
|
||||
|
||||
@@ -74,8 +74,7 @@ struct Alarm : public Common::IntrusiveListBaseNode<Alarm> {
|
||||
|
||||
class Alarms {
|
||||
public:
|
||||
explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock,
|
||||
PowerStateRequestManager& power_state_request_manager);
|
||||
explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, PowerStateRequestManager& power_state_request_manager);
|
||||
~Alarms();
|
||||
|
||||
Kernel::KEvent& GetEvent() {
|
||||
@@ -110,21 +109,17 @@ private:
|
||||
class IAlarmService final : public ServiceFramework<IAlarmService> {
|
||||
public:
|
||||
explicit IAlarmService(Core::System& system, std::shared_ptr<TimeManager> manager);
|
||||
|
||||
~IAlarmService() override = default;
|
||||
|
||||
private:
|
||||
void CreateWakeupAlarm(HLERequestContext& ctx);
|
||||
void CreateBackgroundTaskAlarm(HLERequestContext& ctx);
|
||||
|
||||
Core::System& m_system;
|
||||
Alarms& m_alarms;
|
||||
};
|
||||
|
||||
class ISteadyClockAlarm final : public ServiceFramework<ISteadyClockAlarm> {
|
||||
public:
|
||||
explicit ISteadyClockAlarm(Core::System& system, Alarms& alarms, AlarmType type);
|
||||
|
||||
~ISteadyClockAlarm() override = default;
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -8,8 +11,9 @@ namespace Service::PSC::Time {
|
||||
|
||||
IPowerStateRequestHandler::IPowerStateRequestHandler(
|
||||
Core::System& system_, PowerStateRequestManager& power_state_request_manager)
|
||||
: ServiceFramework{system_, "time:p"}, m_system{system}, m_power_state_request_manager{
|
||||
power_state_request_manager} {
|
||||
: ServiceFramework{system_, "time:p"}
|
||||
, m_power_state_request_manager{power_state_request_manager}
|
||||
{
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle>, "GetPowerStateRequestEventReadableHandle"},
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -17,16 +20,13 @@ namespace Service::PSC::Time {
|
||||
|
||||
class IPowerStateRequestHandler final : public ServiceFramework<IPowerStateRequestHandler> {
|
||||
public:
|
||||
explicit IPowerStateRequestHandler(Core::System& system,
|
||||
PowerStateRequestManager& power_state_request_manager);
|
||||
|
||||
explicit IPowerStateRequestHandler(Core::System& system, PowerStateRequestManager& power_state_request_manager);
|
||||
~IPowerStateRequestHandler() override = default;
|
||||
|
||||
Result GetPowerStateRequestEventReadableHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetAndClearPowerStateRequest(Out<bool> out_cleared, Out<u32> out_priority);
|
||||
|
||||
private:
|
||||
Core::System& m_system;
|
||||
PowerStateRequestManager& m_power_state_request_manager;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Service::PSC::Time {
|
||||
|
||||
ServiceManager::ServiceManager(Core::System& system_, std::shared_ptr<TimeManager> time,
|
||||
ServerManager* server_manager)
|
||||
: ServiceFramework{system_, "time:m"}, m_system{system}, m_time{std::move(time)},
|
||||
: ServiceFramework{system_, "time:m"}, m_time{std::move(time)},
|
||||
m_server_manager{*server_manager},
|
||||
m_local_system_clock{m_time->m_standard_local_system_clock},
|
||||
m_user_system_clock{m_time->m_standard_user_system_clock},
|
||||
@@ -26,7 +26,7 @@ ServiceManager::ServiceManager(Core::System& system_, std::shared_ptr<TimeManage
|
||||
m_local_system_context_writer{m_time->m_local_system_clock_context_writer},
|
||||
m_network_system_context_writer{m_time->m_network_system_clock_context_writer},
|
||||
m_ephemeral_system_context_writer{m_time->m_ephemeral_network_clock_context_writer},
|
||||
m_local_operation{m_system}, m_network_operation{m_system}, m_ephemeral_operation{m_system} {
|
||||
m_local_operation{system_}, m_network_operation{system_}, m_ephemeral_operation{system_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&ServiceManager::GetStaticServiceAsUser>, "GetStaticServiceAsUser"},
|
||||
@@ -93,7 +93,7 @@ Result ServiceManager::SetupStandardSteadyClockCore(bool is_rtc_reset_detected,
|
||||
m_steady_clock.Initialize(clock_source_id, rtc_offset, internal_offset, test_offset,
|
||||
is_rtc_reset_detected);
|
||||
auto time = m_steady_clock.GetRawTime();
|
||||
auto ticks = m_system.CoreTiming().GetClockTicks();
|
||||
auto ticks = system.CoreTiming().GetClockTicks();
|
||||
auto boot_time = time - ConvertToTimeSpan(ticks).count();
|
||||
m_shared_memory.SetSteadyClockTimePoint(clock_source_id, boot_time);
|
||||
m_steady_clock.SetContinuousAdjustment(clock_source_id, boot_time);
|
||||
@@ -219,7 +219,7 @@ Result ServiceManager::SetStandardSteadyClockBaseTime(s64 base_time) {
|
||||
|
||||
m_steady_clock.SetRtcOffset(base_time);
|
||||
auto time = m_steady_clock.GetRawTime();
|
||||
auto ticks = m_system.CoreTiming().GetClockTicks();
|
||||
auto ticks = system.CoreTiming().GetClockTicks();
|
||||
auto diff = time - ConvertToTimeSpan(ticks).count();
|
||||
m_shared_memory.UpdateBaseTime(diff);
|
||||
m_steady_clock.UpdateContinuousAdjustmentTime(diff);
|
||||
@@ -241,7 +241,7 @@ Result ServiceManager::GetClosestAlarmUpdatedEvent(
|
||||
Result ServiceManager::CheckAndSignalAlarms() {
|
||||
LOG_DEBUG(Service_Time, "called.");
|
||||
|
||||
m_alarms.CheckAndSignal(m_system.Kernel());
|
||||
m_alarms.CheckAndSignal(system.Kernel());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -277,16 +277,16 @@ void ServiceManager::SetupSAndP() {
|
||||
m_is_s_and_p_setup = true;
|
||||
m_server_manager.RegisterNamedService(
|
||||
"time:s", std::make_shared<StaticService>(
|
||||
m_system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s"));
|
||||
system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s"));
|
||||
m_server_manager.RegisterNamedService("time:p",
|
||||
std::make_shared<IPowerStateRequestHandler>(
|
||||
m_system, m_time->m_power_state_request_manager));
|
||||
system, m_time->m_power_state_request_manager));
|
||||
}
|
||||
}
|
||||
|
||||
Result ServiceManager::GetStaticService(OutInterface<StaticService> out_service,
|
||||
StaticServiceSetupInfo setup_info, const char* name) {
|
||||
*out_service = std::make_shared<StaticService>(m_system, setup_info, m_time, name);
|
||||
*out_service = std::make_shared<StaticService>(system, setup_info, m_time, name);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -60,10 +63,8 @@ public:
|
||||
private:
|
||||
void CheckAndSetupServicesSAndP();
|
||||
void SetupSAndP();
|
||||
Result GetStaticService(OutInterface<StaticService> out_service,
|
||||
StaticServiceSetupInfo setup_info, const char* name);
|
||||
Result GetStaticService(OutInterface<StaticService> out_service, StaticServiceSetupInfo setup_info, const char* name);
|
||||
|
||||
Core::System& m_system;
|
||||
std::shared_ptr<TimeManager> m_time;
|
||||
ServerManager& m_server_manager;
|
||||
bool m_is_s_and_p_setup{};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user