Compare commits

..

9 Commits

Author SHA1 Message Date
crueter
d8656da2fa fix soversion
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-12-21 13:52:06 -05:00
crueter
5f75aceace fix dynarmic cpm cache
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-12-21 13:46:36 -05:00
crueter
7bb30a1086 fix v2
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-12-21 13:07:11 -05:00
crueter
f532611a50 fix cpm-fetch-all.sh
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-12-21 02:52:05 -05:00
crueter
d754fca70f [cmake, docs, tools] update CPMUtil
Rewrote the entire tooling scheme. That's about it, just make sure
tooling works as expected everywhere.

Signed-off-by: crueter <crueter@eden-emu.dev>
2025-12-19 23:16:17 -05:00
John
c5bd7dc047 [sdl]Mouse Panning Regression Fix (#3181)
Fix the mouse panning issues

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3181
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: John <john@eden-emu.dev>
Co-committed-by: John <john@eden-emu.dev>
2025-12-19 23:32:42 +01:00
lizzie
557876b222 [core] use memcpy instead of hand rolling aligned cases (#2639)
Hand rolling memcpy like this is always frowned upon because the compiler has more insight on whats going on (plus the code resolves to a worse version of itself on assembly). This removes some branches that are just straight up redundant. May save stuff especially for systems without fastmem enabled.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2639
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-19 03:58:20 +01:00
lizzie
22dfc560e0 [sdl] fix mouse panning delay (#3174)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3174
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-19 00:30:18 +01:00
Maufeat
1d869e8495 [hle] stubbed extra services from (parental controls and stuff) fw21 (#3175)
Adds more Firmware 20+ related service commands.
Renames existing service commands according to switchbrew docs,
Unstubs new parental service stuff.

Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3175
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: Maufeat <sahyno1996@gmail.com>
Co-committed-by: Maufeat <sahyno1996@gmail.com>
2025-12-18 21:46:00 +01:00
101 changed files with 4158 additions and 4879 deletions

View File

@@ -1,7 +1,9 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
if (MSVC OR ANDROID)
set(CPM_SOURCE_CACHE "${PROJECT_SOURCE_DIR}/.cache/cpm" CACHE STRING "" FORCE)
if(MSVC OR ANDROID)
set(BUNDLED_DEFAULT ON)
else()
set(BUNDLED_DEFAULT OFF)
@@ -19,7 +21,7 @@ include(CPM)
# cpmfile parsing
set(CPMUTIL_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json")
if (EXISTS ${CPMUTIL_JSON_FILE})
if(EXISTS ${CPMUTIL_JSON_FILE})
file(READ ${CPMUTIL_JSON_FILE} CPMFILE_CONTENT)
else()
message(WARNING "[CPMUtil] cpmfile ${CPMUTIL_JSON_FILE} does not exist, AddJsonPackage will be a no-op")
@@ -45,14 +47,14 @@ endfunction()
function(get_json_element object out member default)
string(JSON out_type ERROR_VARIABLE err TYPE "${object}" ${member})
if (err)
if(err)
set("${out}" "${default}" PARENT_SCOPE)
return()
endif()
string(JSON outvar GET "${object}" ${member})
if (out_type STREQUAL "ARRAY")
if(out_type STREQUAL "ARRAY")
string(JSON _len LENGTH "${object}" ${member})
# array_to_list("${outvar}" ${_len} outvar)
set("${out}_LENGTH" "${_len}" PARENT_SCOPE)
@@ -74,7 +76,7 @@ function(AddJsonPackage)
set(multiValueArgs OPTIONS)
cmake_parse_arguments(JSON "" "${oneValueArgs}" "${multiValueArgs}"
"${ARGN}")
"${ARGN}")
list(LENGTH ARGN argnLength)
@@ -83,18 +85,18 @@ function(AddJsonPackage)
set(JSON_NAME "${ARGV0}")
endif()
if (NOT DEFINED CPMFILE_CONTENT)
if(NOT DEFINED CPMFILE_CONTENT)
cpm_utils_message(WARNING ${name} "No cpmfile, AddJsonPackage is a no-op")
return()
endif()
if (NOT DEFINED JSON_NAME)
if(NOT DEFINED JSON_NAME)
cpm_utils_message(FATAL_ERROR "json package" "No name specified")
endif()
string(JSON object ERROR_VARIABLE err GET "${CPMFILE_CONTENT}" "${JSON_NAME}")
if (err)
if(err)
cpm_utils_message(FATAL_ERROR ${JSON_NAME} "Not found in cpmfile")
endif()
@@ -103,13 +105,13 @@ function(AddJsonPackage)
get_json_element("${object}" ci ci OFF)
get_json_element("${object}" version version "")
if (ci)
if(ci)
get_json_element("${object}" name name "${JSON_NAME}")
get_json_element("${object}" extension extension "tar.zst")
get_json_element("${object}" min_version min_version "")
get_json_element("${object}" raw_disabled disabled_platforms "")
if (raw_disabled)
if(raw_disabled)
array_to_list("${raw_disabled}" ${raw_disabled_LENGTH} disabled_platforms)
else()
set(disabled_platforms "")
@@ -154,31 +156,31 @@ function(AddJsonPackage)
# first: tag gets %VERSION% replaced if applicable, with either git_version (preferred) or version
# second: artifact gets %VERSION% and %TAG% replaced accordingly (same rules for VERSION)
if (git_version)
if(git_version)
set(version_replace ${git_version})
else()
set(version_replace ${version})
endif()
# TODO(crueter): fmt module for cmake
if (tag)
if(tag)
string(REPLACE "%VERSION%" "${version_replace}" tag ${tag})
endif()
if (artifact)
if(artifact)
string(REPLACE "%VERSION%" "${version_replace}" artifact ${artifact})
string(REPLACE "%TAG%" "${tag}" artifact ${artifact})
endif()
# format patchdir
if (raw_patches)
if(raw_patches)
math(EXPR range "${raw_patches_LENGTH} - 1")
foreach(IDX RANGE ${range})
string(JSON _patch GET "${raw_patches}" "${IDX}")
set(full_patch "${CMAKE_SOURCE_DIR}/.patch/${JSON_NAME}/${_patch}")
if (NOT EXISTS ${full_patch})
if(NOT EXISTS ${full_patch})
cpm_utils_message(FATAL_ERROR ${JSON_NAME} "specifies patch ${full_patch} which does not exist")
endif()
@@ -190,7 +192,7 @@ function(AddJsonPackage)
# options
get_json_element("${object}" raw_options options "")
if (raw_options)
if(raw_options)
array_to_list("${raw_options}" ${raw_options_LENGTH} options)
endif()
@@ -198,7 +200,7 @@ function(AddJsonPackage)
# end options
# system/bundled
if (bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE)
if(bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE)
set(bundled ${JSON_BUNDLED_PACKAGE})
endif()
@@ -284,37 +286,37 @@ function(AddPackage)
set(multiValueArgs OPTIONS PATCHES)
cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "${multiValueArgs}"
"${ARGN}")
"${ARGN}")
if (NOT DEFINED PKG_ARGS_NAME)
if(NOT DEFINED PKG_ARGS_NAME)
cpm_utils_message(FATAL_ERROR "package" "No package name defined")
endif()
option(${PKG_ARGS_NAME}_FORCE_SYSTEM "Force the system package for ${PKG_ARGS_NAME}")
option(${PKG_ARGS_NAME}_FORCE_BUNDLED "Force the bundled package for ${PKG_ARGS_NAME}")
if (NOT DEFINED PKG_ARGS_GIT_HOST)
if(NOT DEFINED PKG_ARGS_GIT_HOST)
set(git_host github.com)
else()
set(git_host ${PKG_ARGS_GIT_HOST})
endif()
if (DEFINED PKG_ARGS_URL)
if(DEFINED PKG_ARGS_URL)
set(pkg_url ${PKG_ARGS_URL})
if (DEFINED PKG_ARGS_REPO)
if(DEFINED PKG_ARGS_REPO)
set(pkg_git_url https://${git_host}/${PKG_ARGS_REPO})
else()
if (DEFINED PKG_ARGS_GIT_URL)
if(DEFINED PKG_ARGS_GIT_URL)
set(pkg_git_url ${PKG_ARGS_GIT_URL})
else()
set(pkg_git_url ${pkg_url})
endif()
endif()
elseif (DEFINED PKG_ARGS_REPO)
elseif(DEFINED PKG_ARGS_REPO)
set(pkg_git_url https://${git_host}/${PKG_ARGS_REPO})
if (DEFINED PKG_ARGS_TAG)
if(DEFINED PKG_ARGS_TAG)
set(pkg_key ${PKG_ARGS_TAG})
if(DEFINED PKG_ARGS_ARTIFACT)
@@ -324,14 +326,14 @@ function(AddPackage)
set(pkg_url
${pkg_git_url}/archive/refs/tags/${PKG_ARGS_TAG}.tar.gz)
endif()
elseif (DEFINED PKG_ARGS_SHA)
elseif(DEFINED PKG_ARGS_SHA)
set(pkg_url "${pkg_git_url}/archive/${PKG_ARGS_SHA}.tar.gz")
else()
if (DEFINED PKG_ARGS_BRANCH)
if(DEFINED PKG_ARGS_BRANCH)
set(PKG_BRANCH ${PKG_ARGS_BRANCH})
else()
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
"REPO defined but no TAG, SHA, BRANCH, or URL specified, defaulting to master")
"REPO defined but no TAG, SHA, BRANCH, or URL specified, defaulting to master")
set(PKG_BRANCH master)
endif()
@@ -343,72 +345,72 @@ function(AddPackage)
cpm_utils_message(STATUS ${PKG_ARGS_NAME} "Download URL is ${pkg_url}")
if (NOT DEFINED PKG_ARGS_KEY)
if (DEFINED PKG_ARGS_SHA)
if(NOT DEFINED PKG_ARGS_KEY)
if(DEFINED PKG_ARGS_SHA)
string(SUBSTRING ${PKG_ARGS_SHA} 0 4 pkg_key)
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
"No custom key defined, using ${pkg_key} from sha")
"No custom key defined, using ${pkg_key} from sha")
elseif(DEFINED PKG_ARGS_GIT_VERSION)
set(pkg_key ${PKG_ARGS_GIT_VERSION})
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
"No custom key defined, using ${pkg_key}")
elseif (DEFINED PKG_ARGS_TAG)
"No custom key defined, using ${pkg_key}")
elseif(DEFINED PKG_ARGS_TAG)
set(pkg_key ${PKG_ARGS_TAG})
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
"No custom key defined, using ${pkg_key}")
elseif (DEFINED PKG_ARGS_VERSION)
"No custom key defined, using ${pkg_key}")
elseif(DEFINED PKG_ARGS_VERSION)
set(pkg_key ${PKG_ARGS_VERSION})
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
"No custom key defined, using ${pkg_key}")
"No custom key defined, using ${pkg_key}")
else()
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
"Could not determine cache key, using CPM defaults")
"Could not determine cache key, using CPM defaults")
endif()
else()
set(pkg_key ${PKG_ARGS_KEY})
endif()
if (DEFINED PKG_ARGS_HASH_ALGO)
if(DEFINED PKG_ARGS_HASH_ALGO)
set(hash_algo ${PKG_ARGS_HASH_ALGO})
else()
set(hash_algo SHA512)
endif()
if (DEFINED PKG_ARGS_HASH)
if(DEFINED PKG_ARGS_HASH)
set(pkg_hash "${hash_algo}=${PKG_ARGS_HASH}")
elseif (DEFINED PKG_ARGS_HASH_SUFFIX)
elseif(DEFINED PKG_ARGS_HASH_SUFFIX)
# funny sanity check
string(TOLOWER ${hash_algo} hash_algo_lower)
string(TOLOWER ${PKG_ARGS_HASH_SUFFIX} suffix_lower)
if (NOT ${suffix_lower} MATCHES ${hash_algo_lower})
if(NOT ${suffix_lower} MATCHES ${hash_algo_lower})
cpm_utils_message(WARNING
"Hash algorithm and hash suffix do not match, errors may occur")
"Hash algorithm and hash suffix do not match, errors may occur")
endif()
set(hash_url ${pkg_url}.${PKG_ARGS_HASH_SUFFIX})
elseif (DEFINED PKG_ARGS_HASH_URL)
elseif(DEFINED PKG_ARGS_HASH_URL)
set(hash_url ${PKG_ARGS_HASH_URL})
else()
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
"No hash or hash URL found")
"No hash or hash URL found")
endif()
if (DEFINED hash_url)
if(DEFINED hash_url)
set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${PKG_ARGS_NAME}.hash)
# TODO(crueter): This is kind of a bad solution
# because "technically" the hash is invalidated each week
# but it works for now kjsdnfkjdnfjksdn
string(TOLOWER ${PKG_ARGS_NAME} lowername)
if (NOT EXISTS ${outfile} AND NOT EXISTS ${CPM_SOURCE_CACHE}/${lowername}/${pkg_key})
if(NOT EXISTS ${outfile} AND NOT EXISTS ${CPM_SOURCE_CACHE}/${lowername}/${pkg_key})
file(DOWNLOAD ${hash_url} ${outfile})
endif()
if (EXISTS ${outfile})
if(EXISTS ${outfile})
file(READ ${outfile} pkg_hash_tmp)
endif()
if (DEFINED ${pkg_hash_tmp})
if(DEFINED ${pkg_hash_tmp})
set(pkg_hash "${hash_algo}=${pkg_hash_tmp}")
endif()
endif()
@@ -426,19 +428,19 @@ function(AddPackage)
- CPMUTIL_FORCE_BUNDLED
- BUNDLED_PACKAGE
- default to allow local
]]#
if (PKG_ARGS_FORCE_BUNDLED_PACKAGE)
]] #
if(PKG_ARGS_FORCE_BUNDLED_PACKAGE)
set_precedence(OFF OFF)
elseif (${PKG_ARGS_NAME}_FORCE_SYSTEM)
elseif(${PKG_ARGS_NAME}_FORCE_SYSTEM)
set_precedence(ON ON)
elseif (${PKG_ARGS_NAME}_FORCE_BUNDLED)
elseif(${PKG_ARGS_NAME}_FORCE_BUNDLED)
set_precedence(OFF OFF)
elseif (CPMUTIL_FORCE_SYSTEM)
elseif(CPMUTIL_FORCE_SYSTEM)
set_precedence(ON ON)
elseif(CPMUTIL_FORCE_BUNDLED)
set_precedence(OFF OFF)
elseif (DEFINED PKG_ARGS_BUNDLED_PACKAGE AND NOT PKG_ARGS_BUNDLED_PACKAGE STREQUAL "unset")
if (PKG_ARGS_BUNDLED_PACKAGE)
elseif(DEFINED PKG_ARGS_BUNDLED_PACKAGE AND NOT PKG_ARGS_BUNDLED_PACKAGE STREQUAL "unset")
if(PKG_ARGS_BUNDLED_PACKAGE)
set(local OFF)
else()
set(local ON)
@@ -449,7 +451,7 @@ function(AddPackage)
set_precedence(ON OFF)
endif()
if (DEFINED PKG_ARGS_VERSION)
if(DEFINED PKG_ARGS_VERSION)
list(APPEND EXTRA_ARGS
VERSION ${PKG_ARGS_VERSION}
)
@@ -475,32 +477,32 @@ function(AddPackage)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_NAMES ${PKG_ARGS_NAME})
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS ${pkg_git_url})
if (${PKG_ARGS_NAME}_ADDED)
if (DEFINED PKG_ARGS_SHA)
if(${PKG_ARGS_NAME}_ADDED)
if(DEFINED PKG_ARGS_SHA)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_SHA})
elseif (DEFINED PKG_ARGS_GIT_VERSION)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_GIT_VERSION})
elseif (DEFINED PKG_ARGS_TAG)
${PKG_ARGS_SHA})
elseif(DEFINED PKG_ARGS_GIT_VERSION)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_TAG})
elseif(DEFINED PKG_ARGS_VERSION)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_VERSION})
${PKG_ARGS_GIT_VERSION})
elseif(DEFINED PKG_ARGS_TAG)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_TAG})
elseif(DEFINED PKG_ARGS_VERSION)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_VERSION})
else()
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
"Package has no specified sha, tag, or version")
"Package has no specified sha, tag, or version")
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS "unknown")
endif()
else()
if (DEFINED CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION AND NOT
if(DEFINED CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION AND NOT
"${CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION}" STREQUAL "")
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
"${CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION} (system)")
"${CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION} (system)")
else()
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
"unknown (system)")
"unknown (system)")
endif()
endif()
@@ -561,7 +563,7 @@ function(AddCIPackage)
message(FATAL_ERROR "[CPMUtil] PACKAGE is required")
endif()
if (NOT DEFINED PKG_ARGS_CMAKE_FILENAME)
if(NOT DEFINED PKG_ARGS_CMAKE_FILENAME)
set(ARTIFACT_CMAKE ${PKG_ARGS_NAME})
else()
set(ARTIFACT_CMAKE ${PKG_ARGS_CMAKE_FILENAME})
@@ -573,11 +575,11 @@ function(AddCIPackage)
set(ARTIFACT_EXT ${PKG_ARGS_EXTENSION})
endif()
if (DEFINED PKG_ARGS_MIN_VERSION)
if(DEFINED PKG_ARGS_MIN_VERSION)
set(ARTIFACT_MIN_VERSION ${PKG_ARGS_MIN_VERSION})
endif()
if (DEFINED PKG_ARGS_DISABLED_PLATFORMS)
if(DEFINED PKG_ARGS_DISABLED_PLATFORMS)
set(DISABLED_PLATFORMS ${PKG_ARGS_DISABLED_PLATFORMS})
endif()
@@ -587,19 +589,19 @@ function(AddCIPackage)
set(ARTIFACT_REPO ${PKG_ARGS_REPO})
set(ARTIFACT_PACKAGE ${PKG_ARGS_PACKAGE})
if ((MSVC AND ARCHITECTURE_x86_64) AND NOT "windows-amd64" IN_LIST DISABLED_PLATFORMS)
if((MSVC AND ARCHITECTURE_x86_64) AND NOT "windows-amd64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(windows-amd64)
endif()
if ((MSVC AND ARCHITECTURE_arm64) AND NOT "windows-arm64" IN_LIST DISABLED_PLATFORMS)
if((MSVC AND ARCHITECTURE_arm64) AND NOT "windows-arm64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(windows-arm64)
endif()
if ((MINGW AND ARCHITECTURE_x86_64) AND NOT "mingw-amd64" IN_LIST DISABLED_PLATFORMS)
if((MINGW AND ARCHITECTURE_x86_64) AND NOT "mingw-amd64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(mingw-amd64)
endif()
if ((MINGW AND ARCHITECTURE_arm64) AND NOT "mingw-arm64" IN_LIST DISABLED_PLATFORMS)
if((MINGW AND ARCHITECTURE_arm64) AND NOT "mingw-arm64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(mingw-arm64)
endif()
@@ -628,11 +630,11 @@ function(AddCIPackage)
endif()
# TODO(crueter): macOS amd64/aarch64 split mayhaps
if (APPLE AND NOT "macos-universal" IN_LIST DISABLED_PLATFORMS)
if(APPLE AND NOT "macos-universal" IN_LIST DISABLED_PLATFORMS)
add_ci_package(macos-universal)
endif()
if (DEFINED ARTIFACT_DIR)
if(DEFINED ARTIFACT_DIR)
set(${ARTIFACT_PACKAGE}_ADDED TRUE PARENT_SCOPE)
set(${ARTIFACT_PACKAGE}_SOURCE_DIR "${ARTIFACT_DIR}" PARENT_SCOPE)
else()

View File

@@ -1,17 +0,0 @@
# AddPackage
- `VERSION` (required): The version to get (the tag will be `v${VERSION}`)
- `NAME` (required): Name used within the artifacts
- `REPO` (required): CI repository, e.g. `crueter-ci/OpenSSL`
- `PACKAGE` (required): `find_package` package name
- `EXTENSION`: Artifact extension (default `tar.zst`)
- `MIN_VERSION`: Minimum version for `find_package`. Only used if platform does not support this package as a bundled artifact
- `DISABLED_PLATFORMS`: List of platforms that lack artifacts for this package. Options:
* `windows-amd64`
* `windows-arm64`
* `android`
* `solaris-amd64`
* `freebsd-amd64`
* `linux-amd64`
* `linux-aarch64`
* `macos-universal`

View File

@@ -10,12 +10,13 @@ Global Options:
You are highly encouraged to read AddPackage first, even if you plan to only interact with CPMUtil via `AddJsonPackage`.
<!-- TOC -->
- [AddPackage](#addpackage)
- [AddCIPackage](#addcipackage)
- [AddJsonPackage](#addjsonpackage)
- [Lists](#lists)
<!-- /TOC -->
- [For Packagers](#for-packagers)
- [Network Sandbox](#network-sandbox)
- [Unsandboxed](#unsandboxed)
## AddPackage
@@ -43,4 +44,16 @@ For an example of how this might be implemented in an application, see Eden's im
- [`dep_hashes.h.in`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/dep_hashes.h.in)
- [`GenerateDepHashes.cmake`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CMakeModules/GenerateDepHashes.cmake)
- [`deps_dialog.cpp`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/yuzu/deps_dialog.cpp)
- [`deps_dialog.cpp`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/yuzu/deps_dialog.cpp)
## For Packagers
If you are packaging a project that uses CPMUtil, read this!
### Network Sandbox
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!
### Unsandboxed
For others (AUR, MPR, etc). CPMUtil will handle everything for you, including if some of the project's dependencies are missing from your distribution's repositories. That is pretty much half the reason I created this behemoth, after all.

View File

@@ -6,15 +6,10 @@
package org.yuzu.yuzu_emu.adapters
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
import org.yuzu.yuzu_emu.model.Patch
import org.yuzu.yuzu_emu.model.PatchType
import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
@@ -32,95 +27,18 @@ class AddonAdapter(val addonViewModel: AddonViewModel) :
binding.addonSwitch.performClick()
}
binding.title.text = model.name
binding.addonSwitch.setOnCheckedChangeListener(null)
binding.version.text = model.version
binding.addonSwitch.isChecked = model.enabled
binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
model.enabled = checked
}
val isCheat = model.isCheat()
val indentPx = if (isCheat) {
binding.root.context.resources.getDimensionPixelSize(R.dimen.spacing_large)
} else {
0
}
(binding.addonCard.layoutParams as? ViewGroup.MarginLayoutParams)?.let {
it.marginStart = indentPx
binding.addonCard.layoutParams = it
}
if (isCheat) {
binding.version.visibility = View.GONE
binding.deleteCard.visibility = View.GONE
binding.buttonDelete.visibility = View.GONE
binding.addonSwitch.scaleX = 0.7f
binding.addonSwitch.scaleY = 0.7f
val compactPaddingVertical = binding.root.context.resources.getDimensionPixelSize(R.dimen.spacing_small)
binding.root.setPadding(
binding.root.paddingLeft,
compactPaddingVertical / 2,
binding.root.paddingRight,
compactPaddingVertical / 2
)
val innerLayout = binding.addonCard.getChildAt(0) as? ViewGroup
innerLayout?.let {
val leftPadding = binding.root.context.resources.getDimensionPixelSize(R.dimen.spacing_large)
val smallPadding = binding.root.context.resources.getDimensionPixelSize(R.dimen.spacing_med)
it.setPadding(leftPadding, smallPadding, smallPadding, smallPadding)
}
binding.title.textSize = 14f
binding.title.gravity = Gravity.CENTER_VERTICAL
(binding.title.layoutParams as? ConstraintLayout.LayoutParams)?.let { params ->
params.topToTop = ConstraintLayout.LayoutParams.PARENT_ID
params.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID
params.topMargin = 0
binding.title.layoutParams = params
}
} else {
binding.version.visibility = View.VISIBLE
binding.version.text = model.version
binding.deleteCard.visibility = View.VISIBLE
binding.buttonDelete.visibility = View.VISIBLE
binding.addonSwitch.scaleX = 1.0f
binding.addonSwitch.scaleY = 1.0f
val normalPadding = binding.root.context.resources.getDimensionPixelSize(R.dimen.spacing_med)
binding.root.setPadding(
binding.root.paddingLeft,
normalPadding,
binding.root.paddingRight,
normalPadding
)
val innerLayout = binding.addonCard.getChildAt(0) as? ViewGroup
innerLayout?.let {
val normalInnerPadding = binding.root.context.resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
it.setPadding(normalInnerPadding, normalInnerPadding, normalInnerPadding, normalInnerPadding)
}
binding.title.textSize = 16f
binding.title.gravity = Gravity.START
(binding.title.layoutParams as? ConstraintLayout.LayoutParams)?.let { params ->
params.topToTop = ConstraintLayout.LayoutParams.PARENT_ID
params.bottomToBottom = ConstraintLayout.LayoutParams.UNSET
params.topMargin = 0
binding.title.layoutParams = params
}
val deleteAction = {
addonViewModel.setAddonToDelete(model)
}
binding.deleteCard.setOnClickListener { deleteAction() }
binding.buttonDelete.setOnClickListener { deleteAction() }
val deleteAction = {
addonViewModel.setAddonToDelete(model)
}
binding.deleteCard.setOnClickListener { deleteAction() }
binding.buttonDelete.setOnClickListener { deleteAction() }
}
}
}

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
@@ -73,7 +73,7 @@ class AddonsFragment : Fragment() {
}
addonViewModel.addonList.collect(viewLifecycleOwner) {
(binding.listAddons.adapter as AddonAdapter).submitList(it.toList())
(binding.listAddons.adapter as AddonAdapter).submitList(it)
}
addonViewModel.showModInstallPicker.collect(
viewLifecycleOwner,
@@ -127,9 +127,7 @@ class AddonsFragment : Fragment() {
override fun onDestroy() {
super.onDestroy()
if (!requireActivity().isChangingConfigurations) {
addonViewModel.onCloseAddons()
}
addonViewModel.onCloseAddons()
}
val installAddon =

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -31,14 +28,10 @@ class AddonViewModel : ViewModel() {
val addonToDelete = _addonToDelete.asStateFlow()
var game: Game? = null
private set
private val isRefreshing = AtomicBoolean(false)
fun onOpenAddons(game: Game) {
if (this.game?.programId == game.programId && _patchList.value.isNotEmpty()) {
return
}
this.game = game
refreshAddons()
}
@@ -54,7 +47,8 @@ class AddonViewModel : ViewModel() {
NativeLibrary.getPatchesForFile(game!!.path, game!!.programId)
?: emptyArray()
).toMutableList()
_patchList.value = sortPatchesWithCheatsGrouped(patchList)
patchList.sortBy { it.name }
_patchList.value = patchList
isRefreshing.set(false)
}
}
@@ -69,9 +63,7 @@ class AddonViewModel : ViewModel() {
PatchType.Update -> NativeLibrary.removeUpdate(patch.programId)
PatchType.DLC -> NativeLibrary.removeDLC(patch.programId)
PatchType.Mod -> NativeLibrary.removeMod(patch.programId, patch.name)
PatchType.Cheat -> {}
}
_patchList.value.clear()
refreshAddons()
}
@@ -86,7 +78,7 @@ class AddonViewModel : ViewModel() {
if (it.enabled) {
null
} else {
it.getStorageKey()
it.name
}
}.toTypedArray()
)
@@ -102,28 +94,4 @@ class AddonViewModel : ViewModel() {
fun showModNoticeDialog(show: Boolean) {
_showModNoticeDialog.value = show
}
private fun sortPatchesWithCheatsGrouped(patches: MutableList<Patch>): MutableList<Patch> {
val individualCheats = patches.filter { it.isCheat() }
val nonCheats = patches.filter { !it.isCheat() }.sortedBy { it.name }
val cheatsByParent = individualCheats.groupBy { it.parentName }
val result = mutableListOf<Patch>()
for (patch in nonCheats) {
result.add(patch)
cheatsByParent[patch.name]?.sortedBy { it.name }?.let { childCheats ->
result.addAll(childCheats)
}
}
val knownParents = nonCheats.map { it.name }.toSet()
for ((parentName, orphanCheats) in cheatsByParent) {
if (parentName !in knownParents) {
result.addAll(orphanCheats.sortedBy { it.name })
}
}
return result
}
}

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -15,24 +12,5 @@ data class Patch(
val version: String,
val type: Int,
val programId: String,
val titleId: String,
val parentName: String = "" // For cheats: name of the mod folder containing them
) {
/**
* Returns the storage key used for saving enabled/disabled state.
* For cheats with a parent, returns "ParentName::CheatName".
*/
fun getStorageKey(): String {
return if (parentName.isNotEmpty()) {
"$parentName::$name"
} else {
name
}
}
/**
* Returns true if this patch is an individual cheat entry (not a cheat mod).
* Individual cheats have type=Cheat and a parent mod name.
*/
fun isCheat(): Boolean = type == PatchType.Cheat.int && parentName.isNotEmpty()
}
val titleId: String
)

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -9,8 +6,7 @@ package org.yuzu.yuzu_emu.model
enum class PatchType(val int: Int) {
Update(0),
DLC(1),
Mod(2),
Cheat(3);
Mod(2);
companion object {
fun from(int: Int): PatchType = entries.firstOrNull { it.int == int } ?: Update

View File

@@ -1298,10 +1298,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
FileSys::VirtualFile update_raw;
loader->ReadUpdateRaw(update_raw);
// Get build ID for individual cheat enumeration
const auto build_id = pm.GetBuildID(update_raw);
auto patches = pm.GetPatches(update_raw, build_id);
auto patches = pm.GetPatches(update_raw);
jobjectArray jpatchArray =
env->NewObjectArray(patches.size(), Common::Android::GetPatchClass(), nullptr);
int i = 0;
@@ -1311,8 +1308,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
Common::Android::ToJString(env, patch.name),
Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type),
Common::Android::ToJString(env, std::to_string(patch.program_id)),
Common::Android::ToJString(env, std::to_string(patch.title_id)),
Common::Android::ToJString(env, patch.parent_name));
Common::Android::ToJString(env, std::to_string(patch.title_id)));
env->SetObjectArrayElement(jpatchArray, i, jpatch);
++i;
}

View File

@@ -515,7 +515,7 @@ namespace Common::Android {
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
s_patch_constructor = env->GetMethodID(
patch_class, "<init>",
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V");
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");

View File

@@ -611,18 +611,6 @@ add_library(core STATIC
hle/service/caps/caps_u.h
hle/service/cmif_serialization.h
hle/service/cmif_types.h
hle/service/dmnt/cheat_interface.cpp
hle/service/dmnt/cheat_interface.h
hle/service/dmnt/cheat_parser.cpp
hle/service/dmnt/cheat_parser.h
hle/service/dmnt/cheat_process_manager.cpp
hle/service/dmnt/cheat_process_manager.h
hle/service/dmnt/cheat_virtual_machine.cpp
hle/service/dmnt/cheat_virtual_machine.h
hle/service/dmnt/dmnt.cpp
hle/service/dmnt/dmnt.h
hle/service/dmnt/dmnt_results.h
hle/service/dmnt/dmnt_types.h
hle/service/erpt/erpt.cpp
hle/service/erpt/erpt.h
hle/service/es/es.cpp
@@ -1157,6 +1145,11 @@ add_library(core STATIC
loader/xci.h
memory.cpp
memory.h
memory/cheat_engine.cpp
memory/cheat_engine.h
memory/dmnt_cheat_types.h
memory/dmnt_cheat_vm.cpp
memory/dmnt_cheat_vm.h
perf_stats.cpp
perf_stats.h
reporter.cpp

View File

@@ -52,12 +52,12 @@
#include "core/internal_network/network.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
#include "core/perf_stats.h"
#include "core/reporter.h"
#include "core/tools/freezer.h"
#include "core/tools/renderdoc.h"
#include "hid_core/hid_core.h"
#include "hle/service/dmnt/cheat_process_manager.h"
#include "network/network.h"
#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h"
@@ -277,18 +277,9 @@ struct System::Impl {
audio_core = std::make_unique<AudioCore::AudioCore>(system);
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
// Create cheat_manager BEFORE services, as DMNT::LoopProcess needs it
cheat_manager = std::make_unique<Service::DMNT::CheatProcessManager>(system);
services =
std::make_unique<Service::Services>(service_manager, system, stop_event.get_token());
// Apply any pending cheats that were registered before cheat_manager was initialized
if (pending_cheats.has_pending) {
ApplyPendingCheats(system);
}
is_powered_on = true;
exit_locked = false;
exit_requested = false;
@@ -352,6 +343,11 @@ struct System::Impl {
return init_result;
}
// Initialize cheat engine
if (cheat_engine) {
cheat_engine->Initialize();
}
// Register with applet manager
// All threads are started, begin main process execution, now that we're in the clear
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
@@ -419,6 +415,7 @@ struct System::Impl {
services.reset();
service_manager.reset();
fs_controller.Reset();
cheat_engine.reset();
core_timing.ClearPendingEvents();
app_loader.reset();
audio_core.reset();
@@ -494,6 +491,7 @@ struct System::Impl {
bool nvdec_active{};
Reporter reporter;
std::unique_ptr<Memory::CheatEngine> cheat_engine;
std::unique_ptr<Tools::Freezer> memory_freezer;
std::array<u8, 0x20> build_id{};
@@ -522,18 +520,6 @@ struct System::Impl {
/// Debugger
std::unique_ptr<Core::Debugger> debugger;
/// Cheat Manager (DMNT)
std::unique_ptr<Service::DMNT::CheatProcessManager> cheat_manager;
/// Pending cheats to register after cheat_manager is initialized
struct PendingCheats {
std::vector<Service::DMNT::CheatEntry> list;
std::array<u8, 32> build_id{};
u64 main_region_begin{};
u64 main_region_size{};
bool has_pending{false};
} pending_cheats;
SystemResultStatus status = SystemResultStatus::Success;
std::string status_details = "";
@@ -569,61 +555,6 @@ struct System::Impl {
general_channel_event = std::make_unique<Service::Event>(*general_channel_context);
general_channel_initialized = true;
}
void ApplyPendingCheats(System& system) {
if (!pending_cheats.has_pending || !cheat_manager) {
return;
}
LOG_DEBUG(Core, "Applying {} pending cheats", pending_cheats.list.size());
const auto result = cheat_manager->AttachToApplicationProcess(
pending_cheats.build_id, pending_cheats.main_region_begin,
pending_cheats.main_region_size);
if (result.IsError()) {
LOG_WARNING(Core, "Failed to attach cheat process: result={}", result.raw);
pending_cheats = {};
return;
}
LOG_DEBUG(Core, "Cheat process attached successfully");
for (const auto& entry : pending_cheats.list) {
if (entry.cheat_id == 0 && entry.definition.num_opcodes != 0) {
LOG_DEBUG(Core, "Setting master cheat '{}' with {} opcodes",
entry.definition.readable_name.data(), entry.definition.num_opcodes);
const auto set_result = cheat_manager->SetMasterCheat(entry.definition);
if (set_result.IsError()) {
LOG_WARNING(Core, "Failed to set master cheat: result={}", set_result.raw);
}
break;
}
}
// Add normal cheats (cheat_id != 0)
for (const auto& entry : pending_cheats.list) {
if (entry.cheat_id == 0 || entry.definition.num_opcodes == 0) {
continue;
}
u32 assigned_id = 0;
LOG_DEBUG(Core, "Adding cheat '{}' (enabled={}, {} opcodes)",
entry.definition.readable_name.data(), entry.enabled,
entry.definition.num_opcodes);
const auto add_result = cheat_manager->AddCheat(assigned_id, entry.enabled,
entry.definition);
if (add_result.IsError()) {
LOG_WARNING(Core,
"Failed to add cheat (original_id={} enabled={} name='{}'): result={}",
entry.cheat_id, entry.enabled,
entry.definition.readable_name.data(), add_result.raw);
}
}
// Clear pending cheats
pending_cheats = {};
}
};
System::System() : impl{std::make_unique<Impl>(*this)} {}
@@ -869,61 +800,11 @@ FileSys::VirtualFilesystem System::GetFilesystem() const {
return impl->virtual_filesystem;
}
void System::RegisterCheatList(const std::vector<Service::DMNT::CheatEntry>& list,
void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
const std::array<u8, 32>& build_id, u64 main_region_begin,
u64 main_region_size) {
// If cheat_manager is not yet initialized, cache the cheats for later
if (!impl->cheat_manager) {
impl->pending_cheats.list = list;
impl->pending_cheats.build_id = build_id;
impl->pending_cheats.main_region_begin = main_region_begin;
impl->pending_cheats.main_region_size = main_region_size;
impl->pending_cheats.has_pending = true;
LOG_INFO(Core, "Cached {} cheats for later registration", list.size());
return;
}
// Attach cheat process to the current application process
const auto result = impl->cheat_manager->AttachToApplicationProcess(build_id, main_region_begin,
main_region_size);
if (result.IsError()) {
LOG_WARNING(Core, "Failed to attach cheat process: result={}", result.raw);
return;
}
// Empty list: nothing more to do
if (list.empty()) {
return;
}
// Set master cheat if present (cheat_id == 0)
for (const auto& entry : list) {
if (entry.cheat_id == 0 && entry.definition.num_opcodes != 0) {
const auto set_result = impl->cheat_manager->SetMasterCheat(entry.definition);
if (set_result.IsError()) {
LOG_WARNING(Core, "Failed to set master cheat: result={}", set_result.raw);
}
// Only one master cheat allowed
break;
}
}
// Add normal cheats (cheat_id != 0)
for (const auto& entry : list) {
if (entry.cheat_id == 0 || entry.definition.num_opcodes == 0) {
continue;
}
u32 assigned_id = 0;
const auto add_result = impl->cheat_manager->AddCheat(assigned_id, entry.enabled,
entry.definition);
if (add_result.IsError()) {
LOG_WARNING(Core,
"Failed to add cheat (original_id={} enabled={} name='{}'): result={}",
entry.cheat_id, entry.enabled,
entry.definition.readable_name.data(), add_result.raw);
}
}
impl->cheat_engine = std::make_unique<Memory::CheatEngine>(*this, list, build_id);
impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size);
}
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
@@ -1067,15 +948,6 @@ Tools::RenderdocAPI& System::GetRenderdocAPI() {
return *impl->renderdoc_api;
}
Service::DMNT::CheatProcessManager& System::GetCheatManager()
{
return *impl->cheat_manager;
}
const Service::DMNT::CheatProcessManager& System::GetCheatManager() const {
return *impl->cheat_manager;
}
void System::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
return impl->kernel.RunServer(std::move(server_manager));
}

View File

@@ -17,7 +17,6 @@
#include "common/common_types.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/service/dmnt/dmnt_types.h"
#include "core/hle/service/os/event.h"
#include "core/hle/service/kernel_helpers.h"
@@ -46,14 +45,10 @@ enum class ResultStatus : u16;
} // namespace Loader
namespace Core::Memory {
struct CheatEntry;
class Memory;
} // namespace Core::Memory
namespace Service::DMNT {
class CheatProcessManager;
struct CheatEntry;
}
namespace Service {
namespace Account {
@@ -344,7 +339,7 @@ public:
[[nodiscard]] FileSys::VirtualFilesystem GetFilesystem() const;
void RegisterCheatList(const std::vector<Service::DMNT::CheatEntry>& list,
void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
const std::array<u8, 0x20>& build_id, u64 main_region_begin,
u64 main_region_size);
@@ -388,9 +383,6 @@ public:
[[nodiscard]] Tools::RenderdocAPI& GetRenderdocAPI();
[[nodiscard]] Service::DMNT::CheatProcessManager& GetCheatManager();
[[nodiscard]] const Service::DMNT::CheatProcessManager& GetCheatManager() const;
void SetExitLocked(bool locked);
bool GetExitLocked() const;

View File

@@ -27,13 +27,12 @@
#include "core/file_sys/vfs/vfs_cached.h"
#include "core/file_sys/vfs/vfs_layered.h"
#include "core/file_sys/vfs/vfs_vector.h"
#include "core/hle/service/dmnt/cheat_parser.h"
#include "core/hle/service/dmnt/dmnt_types.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/set/settings_server.h"
#include "core/loader/loader.h"
#include "core/loader/nso.h"
#include "core/memory/cheat_engine.h"
namespace FileSys {
namespace {
@@ -65,15 +64,16 @@ std::string FormatTitleVersion(u32 version,
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
}
// Returns a directory with name matching case-insensitively.
// Returns nullptr if directory doesn't contain a subdirectory with the given name.
VirtualDir FindSubdirectoryCaseless(const VirtualDir& dir, std::string_view name) {
// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
// doesn't have a directory with name.
VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) {
#ifdef _WIN32
return dir->GetSubdirectory(name);
#else
const auto target = Common::ToLower(std::string(name));
for (const auto& subdir : dir->GetSubdirectories()) {
if (Common::ToLower(subdir->GetName()) == target) {
const auto subdirs = dir->GetSubdirectories();
for (const auto& subdir : subdirs) {
std::string dir_name = Common::ToLower(subdir->GetName());
if (dir_name == name) {
return subdir;
}
}
@@ -82,35 +82,36 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir& dir, std::string_view name
#endif
}
std::optional<std::vector<Service::DMNT::CheatEntry>> ReadCheatFileFromFolder(
std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
u64 title_id, const PatchManager::BuildID& build_id_, const VirtualDir& base_path, bool upper) {
const auto build_id_raw = Common::HexToString(build_id_, upper);
const auto build_id = build_id_raw.substr(0, std::min(build_id_raw.size(), sizeof(u64) * 2));
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
if (file == nullptr) {
LOG_DEBUG(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
title_id, build_id);
LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
title_id, build_id);
return std::nullopt;
}
std::vector<u8> data(file->GetSize());
if (file->Read(data.data(), data.size()) != data.size()) {
LOG_WARNING(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
title_id, build_id);
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
title_id, build_id);
return std::nullopt;
}
const Service::DMNT::CheatParser parser;
const Core::Memory::TextCheatParser parser;
return parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
}
void AppendCommaIfNotEmpty(std::string& to, std::string_view with) {
if (!to.empty()) {
if (to.empty()) {
to += with;
} else {
to += ", ";
to += with;
}
to += with;
}
bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
@@ -315,7 +316,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
return !CollectPatches(patch_dirs, build_id).empty();
}
std::vector<Service::DMNT::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
@@ -324,71 +325,36 @@ std::vector<Service::DMNT::CheatEntry> PatchManager::CreateCheatList(const Build
const auto& disabled = Settings::values.disabled_addons[title_id];
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
std::vector<Service::DMNT::CheatEntry> out;
// Load cheats from: <mod dir>/<folder>/cheats/<build_id>.txt
// <mod dir> / <folder> / cheats / <build id>.txt
std::vector<Core::Memory::CheatEntry> out;
for (const auto& subdir : patch_dirs) {
const auto mod_name = subdir->GetName();
// Skip entirely disabled mods
if (std::find(disabled.cbegin(), disabled.cend(), mod_name) != disabled.cend()) {
continue;
}
auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats");
if (cheats_dir == nullptr) {
continue;
}
// Try uppercase build_id first, then lowercase
std::optional<std::vector<Service::DMNT::CheatEntry>> cheat_entries;
if (auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true)) {
cheat_entries = std::move(res);
} else if (auto res_lower = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false)) {
cheat_entries = std::move(res_lower);
}
if (cheat_entries) {
for (auto& entry : *cheat_entries) {
// Check if this individual cheat is disabled
const std::string cheat_name = entry.definition.readable_name.data();
const std::string cheat_key = mod_name + "::" + cheat_name;
if (std::find(disabled.cbegin(), disabled.cend(), cheat_key) != disabled.cend()) {
// Individual cheat is disabled - mark it as disabled but still include it
entry.enabled = false;
}
out.push_back(entry);
if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) == disabled.cend()) {
if (auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats"); cheats_dir != nullptr) {
if (auto const res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true))
std::copy(res->begin(), res->end(), std::back_inserter(out));
if (auto const res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false))
std::copy(res->begin(), res->end(), std::back_inserter(out));
}
}
}
// User-friendly cheat loading from: <mod dir>/cheat_*.txt
for (const auto& file : load_dir->GetFiles()) {
const auto& name = file->GetName();
if (!name.starts_with("cheat_")) {
continue;
// Uncareless user-friendly loading of patches (must start with 'cheat_')
// <mod dir> / <cheat file>.txt
auto const patch_files = load_dir->GetFiles();
for (auto const& f : patch_files) {
auto const name = f->GetName();
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
std::vector<u8> data(f->GetSize());
if (f->Read(data.data(), data.size()) == data.size()) {
const Core::Memory::TextCheatParser parser;
auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
std::copy(res.begin(), res.end(), std::back_inserter(out));
} else {
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id);
}
}
if (std::find(disabled.cbegin(), disabled.cend(), name) != disabled.cend()) {
continue;
}
std::vector<u8> data(file->GetSize());
if (file->Read(data.data(), data.size()) != static_cast<size_t>(data.size())) {
LOG_WARNING(Common_Filesystem, "Failed to read cheat file '{}' for title_id={:016X}",
name, title_id);
continue;
}
const Service::DMNT::CheatParser parser;
auto entries = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
out.insert(out.end(), entries.begin(), entries.end());
}
return out;
}
@@ -515,53 +481,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
return romfs;
}
PatchManager::BuildID PatchManager::GetBuildID(VirtualFile update_raw) const {
BuildID build_id{};
// Get the base NCA
const auto base_nca = content_provider.GetEntry(title_id, ContentRecordType::Program);
if (base_nca == nullptr) {
return build_id;
}
// Try to get ExeFS from update first, then base
VirtualDir exefs;
const auto update_tid = GetUpdateTitleID(title_id);
const auto update = content_provider.GetEntry(update_tid, ContentRecordType::Program);
if (update != nullptr && update->GetExeFS() != nullptr) {
exefs = update->GetExeFS();
} else if (update_raw != nullptr) {
const auto new_nca = std::make_shared<NCA>(update_raw, base_nca.get());
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetExeFS() != nullptr) {
exefs = new_nca->GetExeFS();
}
}
if (exefs == nullptr) {
exefs = base_nca->GetExeFS();
}
if (exefs == nullptr) {
return build_id;
}
// Try to read the main NSO header
const auto main_file = exefs->GetFile("main");
if (main_file == nullptr || main_file->GetSize() < sizeof(Loader::NSOHeader)) {
return build_id;
}
Loader::NSOHeader header{};
if (main_file->Read(reinterpret_cast<u8*>(&header), sizeof(header)) == sizeof(header)) {
build_id = header.build_id;
}
return build_id;
}
std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw, const BuildID& build_id) const {
std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
if (title_id == 0) {
return {};
}
@@ -582,8 +502,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw, const BuildI
.version = "",
.type = PatchType::Update,
.program_id = title_id,
.title_id = title_id,
.parent_name = ""};
.title_id = title_id};
if (nacp != nullptr) {
update_patch.version = nacp->GetVersionString();
@@ -603,15 +522,11 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw, const BuildI
}
}
// Check if we have a valid build_id for cheat enumeration
const bool has_build_id = std::any_of(build_id.begin(), build_id.end(), [](u8 b) { return b != 0; });
// General Mods (LayeredFS and IPS)
const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
if (mod_dir != nullptr) {
for (const auto& mod : mod_dir->GetSubdirectories()) {
std::string types;
bool has_cheats = false;
const auto exefs_dir = FindSubdirectoryCaseless(mod, "exefs");
if (IsDirValidAndNonEmpty(exefs_dir)) {
@@ -640,12 +555,8 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw, const BuildI
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfs")) ||
IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfslite")))
AppendCommaIfNotEmpty(types, "LayeredFS");
const auto cheats_dir = FindSubdirectoryCaseless(mod, "cheats");
if (IsDirValidAndNonEmpty(cheats_dir)) {
has_cheats = true;
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "cheats")))
AppendCommaIfNotEmpty(types, "Cheats");
}
if (types.empty())
continue;
@@ -657,46 +568,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw, const BuildI
.version = types,
.type = PatchType::Mod,
.program_id = title_id,
.title_id = title_id,
.parent_name = ""});
// Add individual cheats as sub-entries if we have a build_id
if (has_cheats && has_build_id && !mod_disabled) {
// Try to read cheat file (uppercase first, then lowercase)
std::optional<std::vector<Service::DMNT::CheatEntry>> cheat_entries;
if (auto res = ReadCheatFileFromFolder(title_id, build_id, cheats_dir, true)) {
cheat_entries = std::move(res);
} else if (auto res_lower = ReadCheatFileFromFolder(title_id, build_id, cheats_dir, false)) {
cheat_entries = std::move(res_lower);
}
if (cheat_entries) {
for (const auto& cheat : *cheat_entries) {
// Skip master cheat (id 0) with no readable name
if (cheat.cheat_id == 0 && cheat.definition.readable_name[0] == '\0') {
continue;
}
const std::string cheat_name = cheat.definition.readable_name.data();
if (cheat_name.empty()) {
continue;
}
// Create unique key for this cheat: "ModName::CheatName"
const std::string cheat_key = mod->GetName() + "::" + cheat_name;
const auto cheat_disabled =
std::find(disabled.begin(), disabled.end(), cheat_key) != disabled.end();
out.push_back({.enabled = !cheat_disabled,
.name = cheat_name,
.version = types,
.type = PatchType::Cheat,
.program_id = title_id,
.title_id = title_id,
.parent_name = mod->GetName()});
}
}
}
.title_id = title_id});
}
}
@@ -720,8 +592,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw, const BuildI
.version = types,
.type = PatchType::Mod,
.program_id = title_id,
.title_id = title_id,
.parent_name = ""});
.title_id = title_id});
}
}
@@ -753,8 +624,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw, const BuildI
.version = std::move(list),
.type = PatchType::DLC,
.program_id = title_id,
.title_id = dlc_match.back().title_id,
.parent_name = ""});
.title_id = dlc_match.back().title_id});
}
return out;

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -13,6 +10,7 @@
#include "common/common_types.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/memory/dmnt_cheat_types.h"
namespace Core {
class System;
@@ -22,17 +20,13 @@ namespace Service::FileSystem {
class FileSystemController;
}
namespace Service::DMNT {
struct CheatEntry;
}
namespace FileSys {
class ContentProvider;
class NCA;
class NACP;
enum class PatchType { Update, DLC, Mod, Cheat };
enum class PatchType { Update, DLC, Mod };
struct Patch {
bool enabled;
@@ -41,7 +35,6 @@ struct Patch {
PatchType type;
u64 program_id;
u64 title_id;
std::string parent_name;
};
// A centralized class to manage patches to games.
@@ -72,7 +65,7 @@ public:
[[nodiscard]] bool HasNSOPatch(const BuildID& build_id, std::string_view name) const;
// Creates a CheatList object with all
[[nodiscard]] std::vector<Service::DMNT::CheatEntry> CreateCheatList(
[[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList(
const BuildID& build_id) const;
// Currently tracked RomFS patches:
@@ -83,11 +76,8 @@ public:
VirtualFile packed_update_raw = nullptr,
bool apply_layeredfs = true) const;
// Returns a vector of patches including individual cheats
[[nodiscard]] std::vector<Patch> GetPatches(VirtualFile update_raw = nullptr,
const BuildID& build_id = {}) const;
[[nodiscard]] BuildID GetBuildID(VirtualFile update_raw = nullptr) const;
// Returns a vector of patches
[[nodiscard]] std::vector<Patch> GetPatches(VirtualFile update_raw = nullptr) const;
// If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
// it will fallback to the Meta-type NCA of the base game. If that fails, the result will be

View File

@@ -12,7 +12,7 @@
#include "common/common_types.h"
#include "common/expected.h"
// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes
// All the constants in this file come from <http://switchbrew.org/index.php?title=Error_codes>
/**
* Identifies the module which caused the error. Error codes can be propagated through a call
@@ -87,6 +87,7 @@ enum class ErrorModule : u32 {
AM = 128,
PlayReport = 129,
AHID = 130,
APPLET = 131,
Qlaunch = 132,
PCV = 133,
USBPD = 134,
@@ -113,8 +114,8 @@ enum class ErrorModule : u32 {
NPNSHTTPSTREAM = 155,
IDLE = 156,
ARP = 157,
SWKBD = 158,
BOOT = 159,
UPDATER = 158,
SWKBD = 159,
NetDiag = 160,
NFCMifare = 161,
UserlandAssert = 162,
@@ -125,7 +126,8 @@ enum class ErrorModule : u32 {
BGTC = 167,
UserlandCrash = 168,
SASBUS = 169,
PI = 170,
PL = 170,
CDMSC = 171,
AudioCtrl = 172,
LBL = 173,
JIT = 175,
@@ -137,23 +139,30 @@ enum class ErrorModule : u32 {
Dauth = 181,
STDFU = 182,
DBG = 183,
CDACM = 184,
TCAP = 185,
DHCPS = 186,
SPI = 187,
AVM = 188,
PWM = 189,
DNSServer = 190,
RTC = 191,
Regulator = 192,
LED = 193,
HTCTool = 194,
SIO = 195,
PCM = 196,
CLKRST = 197,
POWCTL = 198,
HIDDriver = 199,
DMA = 200,
AudioOld = 201,
HID = 202,
LDN = 203,
CS = 204,
Irsensor = 205,
Capture = 206,
MM = 207,
Manu = 208,
ATK = 209,
WEB = 210,
@@ -166,19 +175,24 @@ enum class ErrorModule : u32 {
MigrationLdcServ = 217,
HIDBUS = 218,
ENS = 219,
ND = 220,
NDD = 221,
ToyCon = 222,
WebSocket = 223,
SocketIO = 224,
DCDMTP = 227,
PGL = 228,
Notification = 229,
INS = 230,
LP2P = 231,
RCD = 232,
LCM40607 = 233,
ICM40607 = 233,
PRC = 235,
TMAHTC = 237,
ECTX = 238,
BridgeCtrl = 237,
ErrContext = 238,
MNPP = 239,
HSHL = 240,
RINGCON = 241,
CAPMTP = 242,
DP2HDMI = 244,
Cradle = 245,
@@ -186,6 +200,8 @@ enum class ErrorModule : u32 {
Icm42607p = 248,
NDRM = 250,
Fst2 = 251,
TS = 253,
SPLAY = 260,
Nex = 306,
NPLN = 321,
TSPM = 499,

View File

@@ -88,13 +88,13 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_
{181, nullptr, "UpgradeLaunchRequiredVersion"},
{190, nullptr, "SendServerMaintenanceOverlayNotification"},
{200, nullptr, "GetLastApplicationExitReason"},
{210, D<&IApplicationFunctions::GetLaunchRequiredVersionUpgrade>, "GetLaunchRequiredVersionUpgrade"}, // [20.0.0+]
{211, nullptr, "GetLaunchRequiredVersionUpgradeStatus"}, // [20.0.0+]
{220, nullptr, "Unknown220"}, // [20.0.0+]
{300, nullptr, "Unknown300"}, // [20.0.0+]
{310, nullptr, "Unknown310"}, // [20.0.0+]
{320, nullptr, "Unknown320"}, // [20.0.0+]
{330, nullptr, "Unknown330"}, // [20.0.0+]
{210, D<&IApplicationFunctions::GetLaunchRequiredVersionUpgrade>, "GetLaunchRequiredVersionUpgrade"}, //20.0.0+
{211, nullptr, "GetLaunchRequiredVersionUpgradeStatus"}, //20.0.0+
{220, nullptr, "Unknown220"}, //20.0.0+
{300, nullptr, "CreateMovieWriter"}, //20.0.0+
{310, nullptr, "Unknown310"}, //20.0.0+
{320, nullptr, "Unknown320"}, //20.0.0+
{330, nullptr, "Unknown330"}, //20.0.0+
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
{1000, nullptr, "CreateMovieMaker"},
{1001, D<&IApplicationFunctions::PrepareForJit>, "PrepareForJit"},

View File

@@ -18,7 +18,7 @@ IAudioController::IAudioController(Core::System& system_)
{2, D<&IAudioController::GetLibraryAppletExpectedMasterVolume>, "GetLibraryAppletExpectedMasterVolume"},
{3, D<&IAudioController::ChangeMainAppletMasterVolume>, "ChangeMainAppletMasterVolume"},
{4, D<&IAudioController::SetTransparentVolumeRate>, "SetTransparentVolumeRate"},
{5, nullptr, "Unknown5"},
{5, nullptr, "Unknown5"}, //20.0.0+
};
// clang-format on

View File

@@ -30,7 +30,7 @@ IAudioController::IAudioController(Core::System& system_)
{3, D<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"},
{4, D<&IAudioController::IsTargetMute>, "IsTargetMute"},
{5, D<&IAudioController::SetTargetMute>, "SetTargetMute"},
{6, nullptr, "IsTargetConnected"},
{6, nullptr, "IsTargetConnected"}, //20.0.0+
{7, nullptr, "SetDefaultTarget"},
{8, nullptr, "GetDefaultTarget"},
{9, D<&IAudioController::GetAudioOutputMode>, "GetAudioOutputMode"},
@@ -67,7 +67,8 @@ IAudioController::IAudioController(Core::System& system_)
{40, nullptr, "GetSystemInformationForDebug"},
{41, nullptr, "SetVolumeButtonLongPressTime"},
{42, nullptr, "SetNativeVolumeForDebug"},
{5000, D<&IAudioController::Unknown5000>, "Unknown5000"},
{43, nullptr, "Unknown43"}, //21.0.0+
{5000, D<&IAudioController::Unknown5000>, "Unknown5000"}, //19.0.0+
{10000, nullptr, "NotifyAudioOutputTargetForPlayReport"},
{10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"},
{10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"},
@@ -76,13 +77,13 @@ IAudioController::IAudioController(Core::System& system_)
{10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"},
{10103, nullptr, "GetAudioOutputTargetForPlayReport"},
{10104, nullptr, "GetAudioOutputChannelCountForPlayReport"},
{10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
{10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"},
{10200, nullptr, "Unknown10200"}, // [20.0.0+]
{50000, nullptr, "SetAnalogInputBoostGainForPrototyping"},
{50001, nullptr, "OverrideDefaultTargetForDebug"},
{50003, nullptr, "SetForceOverrideExternalDeviceNameForDebug"},
{50004, nullptr, "ClearForceOverrideExternalDeviceNameForDebug"}
{10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, //14.0.0-19.0.1
{10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"}, //14.0.0-19.0.1
{10200, nullptr, "Unknown10200"}, //20.0.0+
{50000, nullptr, "SetAnalogInputBoostGainForPrototyping"}, //15.0.0-18.1.0
{50001, nullptr, "OverrideDefaultTargetForDebug"}, //19.0.0-19.0.1
{50003, nullptr, "SetForceOverrideExternalDeviceNameForDebug"}, //19.0.0+
{50004, nullptr, "ClearForceOverrideExternalDeviceNameForDebug"} //19.0.0+
};
// clang-format on

View File

@@ -32,13 +32,13 @@ IAudioDevice::IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u
{12, D<&IAudioDevice::QueryAudioDeviceOutputEvent>, "QueryAudioDeviceOutputEvent"},
{13, D<&IAudioDevice::GetActiveAudioDeviceName>, "GetActiveAudioOutputDeviceName"},
{14, D<&IAudioDevice::ListAudioOutputDeviceName>, "ListAudioOutputDeviceName"},
{15, nullptr, "AcquireAudioInputDeviceNotification"}, // 17.0.0+
{16, nullptr, "ReleaseAudioInputDeviceNotification"}, // 17.0.0+
{17, nullptr, "AcquireAudioOutputDeviceNotification"}, // 17.0.0+
{18, nullptr, "ReleaseAudioOutputDeviceNotification"}, // 17.0.0+
{19, nullptr, "SetAudioDeviceOutputVolumeAutoTuneEnabled"}, // 18.0.0+
{20, nullptr, "IsAudioDeviceOutputVolumeAutoTuneEnabled"}, // 18.0.0+
{21, nullptr, "IsActiveOutputDeviceEstimatedLowLatency"} // 21.0.0+
{15, nullptr, "AcquireAudioInputDeviceNotification"}, //17.0.0+
{16, nullptr, "ReleaseAudioInputDeviceNotification"}, //17.0.0+
{17, nullptr, "AcquireAudioOutputDeviceNotification"}, //17.0.0+
{18, nullptr, "ReleaseAudioOutputDeviceNotification"}, //17.0.0+
{19, nullptr, "SetAudioDeviceOutputVolumeAutoTuneEnabled"}, //18.0.0+
{20, nullptr, "IsAudioDeviceOutputVolumeAutoTuneEnabled"}, //18.0.0+
{21, nullptr, "IsActiveOutputDeviceEstimatedLowLatency"} //21.0.0+
};
RegisterHandlers(functions);

View File

@@ -43,21 +43,21 @@ IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_)
{20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
{20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
{30100, D<&IBcatService::SetPassphrase>, "SetPassphrase"},
{30101, nullptr, "Unknown30101"},
{30102, nullptr, "Unknown30102"},
{30101, nullptr, "Unknown30101"}, //2.0.0-2.3.0
{30102, nullptr, "Unknown30102"}, //2.0.0-2.3.0
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
{30203, nullptr, "UnblockDeliveryTask"},
{30210, nullptr, "SetDeliveryTaskTimer"},
{30300, D<&IBcatService::RegisterSystemApplicationDeliveryTasks>, "RegisterSystemApplicationDeliveryTasks"},
{90100, nullptr, "EnumerateBackgroundDeliveryTask"},
{90101, nullptr, "Unknown90101"},
{90100, nullptr, "GetDeliveryTaskList"},
{90101, nullptr, "GetDeliveryTaskListForSystem"}, //11.0.0+
{90200, nullptr, "GetDeliveryList"},
{90201, D<&IBcatService::ClearDeliveryCacheStorage>, "ClearDeliveryCacheStorage"},
{90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"},
{90300, nullptr, "GetPushNotificationLog"},
{90301, nullptr, "Unknown90301"},
{90301, nullptr, "GetDeliveryCacheStorageUsage"}, //11.0.0+
};
// clang-format on
RegisterHandlers(functions);

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -12,19 +15,19 @@ INewsService::INewsService(Core::System& system_) : ServiceFramework{system_, "I
{10100, D<&INewsService::PostLocalNews>, "PostLocalNews"},
{20100, nullptr, "SetPassphrase"},
{30100, D<&INewsService::GetSubscriptionStatus>, "GetSubscriptionStatus"},
{30101, nullptr, "GetTopicList"},
{30110, nullptr, "Unknown30110"},
{30101, nullptr, "GetTopicList"}, //3.0.0+
{30110, nullptr, "Unknown30110"}, //6.0.0+
{30200, D<&INewsService::IsSystemUpdateRequired>, "IsSystemUpdateRequired"},
{30201, nullptr, "Unknown30201"},
{30210, nullptr, "Unknown30210"},
{30201, nullptr, "Unknown30201"}, //8.0.0+
{30210, nullptr, "Unknown30210"}, //10.0.0+
{30300, nullptr, "RequestImmediateReception"},
{30400, nullptr, "DecodeArchiveFile"},
{30500, nullptr, "Unknown30500"},
{30900, nullptr, "Unknown30900"},
{30901, nullptr, "Unknown30901"},
{30902, nullptr, "Unknown30902"},
{30400, nullptr, "DecodeArchiveFile"}, //3.0.0-18.1.0
{30500, nullptr, "Unknown30500"}, //8.0.0+
{30900, nullptr, "Unknown30900"}, //1.0.0
{30901, nullptr, "Unknown30901"}, //1.0.0
{30902, nullptr, "Unknown30902"}, //1.0.0
{40100, nullptr, "SetSubscriptionStatus"},
{40101, D<&INewsService::RequestAutoSubscription>, "RequestAutoSubscription"},
{40101, D<&INewsService::RequestAutoSubscription>, "RequestAutoSubscription"}, //3.0.0+
{40200, nullptr, "ClearStorage"},
{40201, nullptr, "ClearSubscriptionStatusAll"},
{90100, nullptr, "GetNewsDatabaseDump"},

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -77,31 +80,33 @@ public:
{57, nullptr, "RegisterAppletResourceUserId"},
{58, nullptr, "UnregisterAppletResourceUserId"},
{59, nullptr, "SetAppletResourceUserId"},
{60, nullptr, "Unknown60"},
{61, nullptr, "Unknown61"},
{62, nullptr, "Unknown62"},
{63, nullptr, "Unknown63"},
{64, nullptr, "Unknown64"},
{65, nullptr, "Unknown65"},
{66, nullptr, "Unknown66"},
{67, nullptr, "Unknown67"},
{68, nullptr, "Unknown68"},
{69, nullptr, "Unknown69"},
{70, nullptr, "Unknown70"},
{71, nullptr, "Unknown71"},
{72, nullptr, "Unknown72"},
{73, nullptr, "Unknown73"},
{74, nullptr, "Unknown74"},
{75, nullptr, "Unknown75"},
{76, nullptr, "Unknown76"},
{100, nullptr, "Unknown100"},
{101, nullptr, "Unknown101"},
{110, nullptr, "Unknown110"},
{111, nullptr, "Unknown111"},
{112, nullptr, "Unknown112"},
{113, nullptr, "Unknown113"},
{114, nullptr, "Unknown114"},
{115, nullptr, "Unknown115"},
{60, nullptr, "AcquireBleConnectionParameterUpdateEvent"}, //8.0.0+
{61, nullptr, "SetCeLength"}, //8.0.0+
{62, nullptr, "EnsureSlotExpansion"}, //9.0.0+
{63, nullptr, "IsSlotExpansionEnsured"}, //9.0.0+
{64, nullptr, "CancelConnectionTrigger"}, //10.0.0+
{65, nullptr, "GetConnectionCapacity"}, //13.0.0+
{66, nullptr, "GetWlanMode"}, //13.0.0+
{67, nullptr, "IsSlotSavingEnabled"}, //13.0.0+
{68, nullptr, "IsSlotSavingForPairingEnabled"}, //13.0.0+
{69, nullptr, "AcquireAudioDeviceConnectionEvent"}, //13.0.0+
{70, nullptr, "GetConnectedAudioDevices"}, //13.0.0+
{71, nullptr, "SetAudioSourceVolume"}, //13.0.0+
{72, nullptr, "GetAudioSourceVolume"}, //13.0.0+
{73, nullptr, "RequestAudioDeviceConnectionRejection"}, //13.0.0+
{74, nullptr, "CancelAudioDeviceConnectionRejection"}, //13.0.0+
{75, nullptr, "GetPairedAudioDevices"}, //13.0.0+
{76, nullptr, "SetWlanModeWithOption"}, //13.1.0+
{100, nullptr, "AcquireConnectionDisallowedEvent"}, //13.0.0+
{101, nullptr, "GetUsecaseViolationFactor"}, //13.0.0+
{110, nullptr, "GetShortenedDeviceInfo"}, //13.0.0+
{111, nullptr, "AcquirePairingCountUpdateEvent"},//13.0.0+
{112, nullptr, "Unknown112"}, //14.0.0-14.1.2
{113, nullptr, "Unknown113"}, //14.0.0-14.1.2
{114, nullptr, "IsFirstAudioControlConnection"}, //14.0.0+
{115, nullptr, "GetShortenedDeviceCondition"}, //14.0.0+
{116, nullptr, "SetAudioSinkVolume"}, //15.0.0+
{117, nullptr, "GetAudioSinkVolume"}, //15.0.0+
};
// clang-format on

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -17,11 +20,15 @@ IBtmDebug::IBtmDebug(Core::System& system_) : ServiceFramework{system_, "btm:dbg
{6, nullptr, "SetTsiMode"},
{7, nullptr, "GeneralTest"},
{8, nullptr, "HidConnect"},
{9, nullptr, "GeneralGet"},
{10, nullptr, "GetGattClientDisconnectionReason"},
{11, nullptr, "GetBleConnectionParameter"},
{12, nullptr, "GetBleConnectionParameterRequest"},
{13, nullptr, "Unknown13"},
{9, nullptr, "GeneralGet"}, //5.0.0+
{10, nullptr, "GetGattClientDisconnectionReason"}, //5.0.0+
{11, nullptr, "GetBleConnectionParameter"}, //5.1.0+
{12, nullptr, "GetBleConnectionParameterRequest"}, //5.1.0+
{13, nullptr, "GetDiscoveredDevice"}, //12.0.0+
{14, nullptr, "SleepAwakeLoopTest"}, //15.0.0+
{15, nullptr, "SleepTest"}, //15.0.0+
{16, nullptr, "MinimumAwakeTest"}, //15.0.0+
{17, nullptr, "ForceEnableBtm"}, //15.0.0+
};
// clang-format on

View File

@@ -1,238 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/dmnt/cheat_interface.h"
#include "core/hle/service/dmnt/cheat_process_manager.h"
#include "core/hle/service/dmnt/dmnt_results.h"
#include "core/hle/service/dmnt/dmnt_types.h"
namespace Service::DMNT {
ICheatInterface::ICheatInterface(Core::System& system_, CheatProcessManager& manager)
: ServiceFramework{system_, "dmnt:cht"}, cheat_process_manager{manager} {
// clang-format off
static const FunctionInfo functions[] = {
{65000, C<&ICheatInterface::HasCheatProcess>, "HasCheatProcess"},
{65001, C<&ICheatInterface::GetCheatProcessEvent>, "GetCheatProcessEvent"},
{65002, C<&ICheatInterface::GetCheatProcessMetadata>, "GetCheatProcessMetadata"},
{65003, C<&ICheatInterface::ForceOpenCheatProcess>, "ForceOpenCheatProcess"},
{65004, C<&ICheatInterface::PauseCheatProcess>, "PauseCheatProcess"},
{65005, C<&ICheatInterface::ResumeCheatProcess>, "ResumeCheatProcess"},
{65006, C<&ICheatInterface::ForceCloseCheatProcess>, "ForceCloseCheatProcess"},
{65100, C<&ICheatInterface::GetCheatProcessMappingCount>, "GetCheatProcessMappingCount"},
{65101, C<&ICheatInterface::GetCheatProcessMappings>, "GetCheatProcessMappings"},
{65102, C<&ICheatInterface::ReadCheatProcessMemory>, "ReadCheatProcessMemory"},
{65103, C<&ICheatInterface::WriteCheatProcessMemory>, "WriteCheatProcessMemory"},
{65104, C<&ICheatInterface::QueryCheatProcessMemory>, "QueryCheatProcessMemory"},
{65200, C<&ICheatInterface::GetCheatCount>, "GetCheatCount"},
{65201, C<&ICheatInterface::GetCheats>, "GetCheats"},
{65202, C<&ICheatInterface::GetCheatById>, "GetCheatById"},
{65203, C<&ICheatInterface::ToggleCheat>, "ToggleCheat"},
{65204, C<&ICheatInterface::AddCheat>, "AddCheat"},
{65205, C<&ICheatInterface::RemoveCheat>, "RemoveCheat"},
{65206, C<&ICheatInterface::ReadStaticRegister>, "ReadStaticRegister"},
{65207, C<&ICheatInterface::WriteStaticRegister>, "WriteStaticRegister"},
{65208, C<&ICheatInterface::ResetStaticRegisters>, "ResetStaticRegisters"},
{65209, C<&ICheatInterface::SetMasterCheat>, "SetMasterCheat"},
{65300, C<&ICheatInterface::GetFrozenAddressCount>, "GetFrozenAddressCount"},
{65301, C<&ICheatInterface::GetFrozenAddresses>, "GetFrozenAddresses"},
{65302, C<&ICheatInterface::GetFrozenAddress>, "GetFrozenAddress"},
{65303, C<&ICheatInterface::EnableFrozenAddress>, "EnableFrozenAddress"},
{65304, C<&ICheatInterface::DisableFrozenAddress>, "DisableFrozenAddress"},
};
// clang-format on
RegisterHandlers(functions);
}
ICheatInterface::~ICheatInterface() = default;
Result ICheatInterface::HasCheatProcess(Out<bool> out_has_cheat) {
LOG_INFO(CheatEngine, "called");
*out_has_cheat = cheat_process_manager.HasCheatProcess();
R_SUCCEED();
}
Result ICheatInterface::GetCheatProcessEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_INFO(CheatEngine, "called");
*out_event = &cheat_process_manager.GetCheatProcessEvent();
R_SUCCEED();
}
Result ICheatInterface::GetCheatProcessMetadata(Out<CheatProcessMetadata> out_metadata) {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.GetCheatProcessMetadata(*out_metadata));
}
Result ICheatInterface::ForceOpenCheatProcess() {
LOG_INFO(CheatEngine, "called");
R_UNLESS(R_SUCCEEDED(cheat_process_manager.ForceOpenCheatProcess()), ResultCheatNotAttached);
R_SUCCEED();
}
Result ICheatInterface::PauseCheatProcess() {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.PauseCheatProcess());
}
Result ICheatInterface::ResumeCheatProcess() {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.ResumeCheatProcess());
}
Result ICheatInterface::ForceCloseCheatProcess() {
LOG_WARNING(CheatEngine, "(STUBBED) called");
R_RETURN(cheat_process_manager.ForceCloseCheatProcess());
}
Result ICheatInterface::GetCheatProcessMappingCount(Out<u64> out_count) {
LOG_WARNING(CheatEngine, "(STUBBED) called");
R_RETURN(cheat_process_manager.GetCheatProcessMappingCount(*out_count));
}
Result ICheatInterface::GetCheatProcessMappings(
Out<u64> out_count, u64 offset,
OutArray<Kernel::Svc::MemoryInfo, BufferAttr_HipcMapAlias> out_mappings) {
LOG_INFO(CheatEngine, "called, offset={}", offset);
R_UNLESS(!out_mappings.empty(), ResultCheatNullBuffer);
R_RETURN(cheat_process_manager.GetCheatProcessMappings(*out_count, offset, out_mappings));
}
Result ICheatInterface::ReadCheatProcessMemory(u64 address, u64 size,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer) {
LOG_DEBUG(CheatEngine, "called, address={}, size={}", address, size);
R_UNLESS(!out_buffer.empty(), ResultCheatNullBuffer);
R_RETURN(cheat_process_manager.ReadCheatProcessMemory(address, size, out_buffer));
}
Result ICheatInterface::WriteCheatProcessMemory(u64 address, u64 size,
InBuffer<BufferAttr_HipcMapAlias> buffer) {
LOG_DEBUG(CheatEngine, "called, address={}, size={}", address, size);
R_UNLESS(!buffer.empty(), ResultCheatNullBuffer);
R_RETURN(cheat_process_manager.WriteCheatProcessMemory(address, size, buffer));
}
Result ICheatInterface::QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> out_mapping,
u64 address) {
LOG_WARNING(CheatEngine, "(STUBBED) called, address={}", address);
R_RETURN(cheat_process_manager.QueryCheatProcessMemory(out_mapping, address));
}
Result ICheatInterface::GetCheatCount(Out<u64> out_count) {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.GetCheatCount(*out_count));
}
Result ICheatInterface::GetCheats(Out<u64> out_count, u64 offset,
OutArray<CheatEntry, BufferAttr_HipcMapAlias> out_cheats) {
LOG_INFO(CheatEngine, "called, offset={}", offset);
R_UNLESS(!out_cheats.empty(), ResultCheatNullBuffer);
R_RETURN(cheat_process_manager.GetCheats(*out_count, offset, out_cheats));
}
Result ICheatInterface::GetCheatById(OutLargeData<CheatEntry, BufferAttr_HipcMapAlias> out_cheat,
u32 cheat_id) {
LOG_INFO(CheatEngine, "called, cheat_id={}", cheat_id);
R_RETURN(cheat_process_manager.GetCheatById(out_cheat, cheat_id));
}
Result ICheatInterface::ToggleCheat(u32 cheat_id) {
LOG_INFO(CheatEngine, "called, cheat_id={}", cheat_id);
R_RETURN(cheat_process_manager.ToggleCheat(cheat_id));
}
Result ICheatInterface::AddCheat(
Out<u32> out_cheat_id, bool is_enabled,
InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition) {
LOG_INFO(CheatEngine, "called, is_enabled={}", is_enabled);
R_RETURN(cheat_process_manager.AddCheat(*out_cheat_id, is_enabled, *cheat_definition));
}
Result ICheatInterface::RemoveCheat(u32 cheat_id) {
LOG_INFO(CheatEngine, "called, cheat_id={}", cheat_id);
R_RETURN(cheat_process_manager.RemoveCheat(cheat_id));
}
Result ICheatInterface::ReadStaticRegister(Out<u64> out_value, u8 register_index) {
LOG_DEBUG(CheatEngine, "called, register_index={}", register_index);
R_RETURN(cheat_process_manager.ReadStaticRegister(*out_value, register_index));
}
Result ICheatInterface::WriteStaticRegister(u8 register_index, u64 value) {
LOG_DEBUG(CheatEngine, "called, register_index={}, value={}", register_index, value);
R_RETURN(cheat_process_manager.WriteStaticRegister(register_index, value));
}
Result ICheatInterface::ResetStaticRegisters() {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.ResetStaticRegisters());
}
Result ICheatInterface::SetMasterCheat(
InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition) {
LOG_INFO(CheatEngine, "called, name={}, num_opcodes={}", cheat_definition->readable_name.data(),
cheat_definition->num_opcodes);
R_RETURN(cheat_process_manager.SetMasterCheat(*cheat_definition));
}
Result ICheatInterface::GetFrozenAddressCount(Out<u64> out_count) {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.GetFrozenAddressCount(*out_count));
}
Result ICheatInterface::GetFrozenAddresses(
Out<u64> out_count, u64 offset,
OutArray<FrozenAddressEntry, BufferAttr_HipcMapAlias> out_frozen_address) {
LOG_INFO(CheatEngine, "called, offset={}", offset);
R_UNLESS(!out_frozen_address.empty(), ResultCheatNullBuffer);
R_RETURN(cheat_process_manager.GetFrozenAddresses(*out_count, offset, out_frozen_address));
}
Result ICheatInterface::GetFrozenAddress(Out<FrozenAddressEntry> out_frozen_address_entry,
u64 address) {
LOG_INFO(CheatEngine, "called, address={}", address);
R_RETURN(cheat_process_manager.GetFrozenAddress(*out_frozen_address_entry, address));
}
Result ICheatInterface::EnableFrozenAddress(Out<u64> out_value, u64 address, u64 width) {
LOG_INFO(CheatEngine, "called, address={}, width={}", address, width);
R_UNLESS(width > 0, ResultFrozenAddressInvalidWidth);
R_UNLESS(width <= sizeof(u64), ResultFrozenAddressInvalidWidth);
R_UNLESS((width & (width - 1)) == 0, ResultFrozenAddressInvalidWidth);
R_RETURN(cheat_process_manager.EnableFrozenAddress(*out_value, address, width));
}
Result ICheatInterface::DisableFrozenAddress(u64 address) {
LOG_INFO(CheatEngine, "called, address={}", address);
R_RETURN(cheat_process_manager.DisableFrozenAddress(address));
}
void ICheatInterface::InitializeCheatManager() {
LOG_INFO(CheatEngine, "called");
}
Result ICheatInterface::ReadCheatProcessMemoryUnsafe(u64 process_addr, std::span<u8> out_data,
size_t size) {
LOG_DEBUG(CheatEngine, "called, process_addr={}, size={}", process_addr, size);
R_RETURN(cheat_process_manager.ReadCheatProcessMemoryUnsafe(process_addr, &out_data, size));
}
Result ICheatInterface::WriteCheatProcessMemoryUnsafe(u64 process_addr, std::span<const u8> data,
size_t size) {
LOG_DEBUG(CheatEngine, "called, process_addr={}, size={}", process_addr, size);
R_RETURN(cheat_process_manager.WriteCheatProcessMemoryUnsafe(process_addr, &data, size));
}
Result ICheatInterface::PauseCheatProcessUnsafe() {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.PauseCheatProcessUnsafe());
}
Result ICheatInterface::ResumeCheatProcessUnsafe() {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.ResumeCheatProcessUnsafe());
}
} // namespace Service::DMNT

View File

@@ -1,88 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Kernel::Svc {
struct MemoryInfo;
}
namespace Service::DMNT {
struct CheatDefinition;
struct CheatEntry;
struct CheatProcessMetadata;
struct FrozenAddressEntry;
class CheatProcessManager;
class ICheatInterface final : public ServiceFramework<ICheatInterface> {
public:
explicit ICheatInterface(Core::System& system_, CheatProcessManager& manager);
~ICheatInterface() override;
private:
Result HasCheatProcess(Out<bool> out_has_cheat);
Result GetCheatProcessEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetCheatProcessMetadata(Out<CheatProcessMetadata> out_metadata);
Result ForceOpenCheatProcess();
Result PauseCheatProcess();
Result ResumeCheatProcess();
Result ForceCloseCheatProcess();
Result GetCheatProcessMappingCount(Out<u64> out_count);
Result GetCheatProcessMappings(
Out<u64> out_count, u64 offset,
OutArray<Kernel::Svc::MemoryInfo, BufferAttr_HipcMapAlias> out_mappings);
Result ReadCheatProcessMemory(u64 address, u64 size,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer);
Result WriteCheatProcessMemory(u64 address, u64 size, InBuffer<BufferAttr_HipcMapAlias> buffer);
Result QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> out_mapping, u64 address);
Result GetCheatCount(Out<u64> out_count);
Result GetCheats(Out<u64> out_count, u64 offset,
OutArray<CheatEntry, BufferAttr_HipcMapAlias> out_cheats);
Result GetCheatById(OutLargeData<CheatEntry, BufferAttr_HipcMapAlias> out_cheat, u32 cheat_id);
Result ToggleCheat(u32 cheat_id);
Result AddCheat(Out<u32> out_cheat_id, bool enabled,
InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition);
Result RemoveCheat(u32 cheat_id);
Result ReadStaticRegister(Out<u64> out_value, u8 register_index);
Result WriteStaticRegister(u8 register_index, u64 value);
Result ResetStaticRegisters();
Result SetMasterCheat(InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition);
Result GetFrozenAddressCount(Out<u64> out_count);
Result GetFrozenAddresses(
Out<u64> out_count, u64 offset,
OutArray<FrozenAddressEntry, BufferAttr_HipcMapAlias> out_frozen_address);
Result GetFrozenAddress(Out<FrozenAddressEntry> out_frozen_address_entry, u64 address);
Result EnableFrozenAddress(Out<u64> out_value, u64 address, u64 width);
Result DisableFrozenAddress(u64 address);
private:
void InitializeCheatManager();
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, std::span<u8> out_data, size_t size);
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, std::span<const u8> data, size_t size);
Result PauseCheatProcessUnsafe();
Result ResumeCheatProcessUnsafe();
CheatProcessManager& cheat_process_manager;
};
} // namespace Service::DMNT

View File

@@ -1,121 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <cctype>
#include <cstring>
#include <optional>
#include <string>
#include "core/hle/service/dmnt/cheat_parser.h"
#include "core/hle/service/dmnt/dmnt_types.h"
namespace Service::DMNT {
CheatParser::CheatParser() {}
CheatParser::~CheatParser() = default;
std::vector<CheatEntry> CheatParser::Parse(std::string_view data) const {
std::vector<CheatEntry> out(1);
std::optional<u64> current_entry;
for (std::size_t i = 0; i < data.size(); ++i) {
if (std::isspace(data[i])) {
continue;
}
if (data[i] == '{') {
current_entry = 0;
if (out[*current_entry].definition.num_opcodes > 0) {
return {};
}
std::size_t name_size{};
const auto name = ExtractName(name_size, data, i + 1, '}');
if (name.empty()) {
return {};
}
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
name.size()));
out[*current_entry]
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
'\0';
i += name_size + 1;
} else if (data[i] == '[') {
current_entry = out.size();
out.emplace_back();
std::size_t name_size{};
const auto name = ExtractName(name_size, data, i + 1, ']');
if (name.empty()) {
return {};
}
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
name.size()));
out[*current_entry]
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
'\0';
i += name_size + 1;
} else if (std::isxdigit(data[i])) {
if (!current_entry || out[*current_entry].definition.num_opcodes >=
out[*current_entry].definition.opcodes.size()) {
return {};
}
const auto hex = std::string(data.substr(i, 8));
if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
return {};
}
const auto value = static_cast<u32>(std::strtoul(hex.c_str(), nullptr, 0x10));
out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
value;
i += 7; // 7 because the for loop will increment by 1 more
} else {
return {};
}
}
out[0].enabled = out[0].definition.num_opcodes > 0;
out[0].cheat_id = 0;
for (u32 i = 1; i < out.size(); ++i) {
out[i].enabled = out[i].definition.num_opcodes > 0;
out[i].cheat_id = i;
}
return out;
}
std::string_view CheatParser::ExtractName(std::size_t& out_name_size, std::string_view data,
std::size_t start_index, char match) const {
auto end_index = start_index;
while (data[end_index] != match) {
++end_index;
if (end_index > data.size()) {
return {};
}
}
out_name_size = end_index - start_index;
// Clamp name if it's too big
if (out_name_size > sizeof(CheatDefinition::readable_name)) {
end_index = start_index + sizeof(CheatDefinition::readable_name);
}
return data.substr(start_index, end_index - start_index);
}
} // namespace Service::DMNT

View File

@@ -1,26 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <string_view>
#include <vector>
namespace Service::DMNT {
struct CheatEntry;
class CheatParser final {
public:
CheatParser();
~CheatParser();
std::vector<CheatEntry> Parse(std::string_view data) const;
private:
std::string_view ExtractName(std::size_t& out_name_size, std::string_view data,
std::size_t start_index, char match) const;
};
} // namespace Service::DMNT

View File

@@ -1,599 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/arm/debug.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/dmnt/cheat_process_manager.h"
#include "core/hle/service/dmnt/cheat_virtual_machine.h"
#include "core/hle/service/dmnt/dmnt_results.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/resource_manager.h"
#include "hid_core/resources/npad/npad.h"
namespace Service::DMNT {
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
CheatProcessManager::CheatProcessManager(Core::System& system_)
: system{system_}, service_context{system_, "dmnt:cht"}, core_timing{system_.CoreTiming()} {
update_event = Core::Timing::CreateEvent("CheatEngine::FrameCallback",
[this](s64 time, std::chrono::nanoseconds ns_late)
-> std::optional<std::chrono::nanoseconds> {
FrameCallback(ns_late);
return std::nullopt;
});
for (size_t i = 0; i < MaxCheatCount; i++) {
ResetCheatEntry(i);
}
cheat_vm = std::make_unique<CheatVirtualMachine>(*this);
cheat_process_event = service_context.CreateEvent("CheatProcessManager::ProcessEvent");
unsafe_break_event = service_context.CreateEvent("CheatProcessManager::ProcessEvent");
}
CheatProcessManager::~CheatProcessManager() {
service_context.CloseEvent(cheat_process_event);
service_context.CloseEvent(unsafe_break_event);
core_timing.UnscheduleEvent(update_event);
}
void CheatProcessManager::SetVirtualMachine(std::unique_ptr<CheatVirtualMachine> vm) {
if (vm) {
cheat_vm = std::move(vm);
SetNeedsReloadVm(true);
}
}
bool CheatProcessManager::HasActiveCheatProcess() {
// Note: This function *MUST* be called only with the cheat lock held.
bool has_cheat_process =
cheat_process_debug_handle != InvalidHandle &&
system.ApplicationProcess()->GetProcessId() == cheat_process_metadata.process_id;
if (!has_cheat_process) {
CloseActiveCheatProcess();
}
return has_cheat_process;
}
void CheatProcessManager::CloseActiveCheatProcess() {
if (cheat_process_debug_handle != InvalidHandle) {
broken_unsafe = false;
unsafe_break_event->Signal();
core_timing.UnscheduleEvent(update_event);
// Close resources.
cheat_process_debug_handle = InvalidHandle;
// Save cheat toggles.
if (always_save_cheat_toggles || should_save_cheat_toggles) {
// TODO: save cheat toggles
should_save_cheat_toggles = false;
}
cheat_process_metadata = {};
ResetAllCheatEntries();
{
auto it = frozen_addresses_map.begin();
while (it != frozen_addresses_map.end()) {
it = frozen_addresses_map.erase(it);
}
}
cheat_process_event->Signal();
}
}
Result CheatProcessManager::EnsureCheatProcess() {
R_UNLESS(HasActiveCheatProcess(), ResultCheatNotAttached);
R_SUCCEED();
}
void CheatProcessManager::SetNeedsReloadVm(bool reload) {
needs_reload_vm = reload;
}
void CheatProcessManager::ResetCheatEntry(size_t i) {
if (i < MaxCheatCount) {
cheat_entries[i] = {};
cheat_entries[i].cheat_id = static_cast<u32>(i);
SetNeedsReloadVm(true);
}
}
void CheatProcessManager::ResetAllCheatEntries() {
for (size_t i = 0; i < MaxCheatCount; i++) {
ResetCheatEntry(i);
}
cheat_vm->ResetStaticRegisters();
}
CheatEntry* CheatProcessManager::GetCheatEntryById(size_t i) {
if (i < MaxCheatCount) {
return cheat_entries.data() + i;
}
return nullptr;
}
CheatEntry* CheatProcessManager::GetCheatEntryByReadableName(const char* readable_name) {
for (size_t i = 1; i < MaxCheatCount; i++) {
if (std::strncmp(cheat_entries[i].definition.readable_name.data(), readable_name,
sizeof(cheat_entries[i].definition.readable_name)) == 0) {
return cheat_entries.data() + i;
}
}
return nullptr;
}
CheatEntry* CheatProcessManager::GetFreeCheatEntry() {
// Check all non-master cheats for availability.
for (size_t i = 1; i < MaxCheatCount; i++) {
if (cheat_entries[i].definition.num_opcodes == 0) {
return cheat_entries.data() + i;
}
}
return nullptr;
}
bool CheatProcessManager::HasCheatProcess() {
std::scoped_lock lk(cheat_lock);
return HasActiveCheatProcess();
}
Kernel::KReadableEvent& CheatProcessManager::GetCheatProcessEvent() const {
return cheat_process_event->GetReadableEvent();
}
Result CheatProcessManager::AttachToApplicationProcess(const std::array<u8, 0x20>& build_id,
VAddr main_region_begin,
u64 main_region_size) {
std::scoped_lock lk(cheat_lock);
{
if (this->HasActiveCheatProcess()) {
this->CloseActiveCheatProcess();
}
}
cheat_process_metadata.process_id = system.ApplicationProcess()->GetProcessId();
{
const auto& page_table = system.ApplicationProcess()->GetPageTable();
cheat_process_metadata.program_id = system.GetApplicationProcessProgramID();
cheat_process_metadata.heap_extents = {
.base = GetInteger(page_table.GetHeapRegionStart()),
.size = page_table.GetHeapRegionSize(),
};
cheat_process_metadata.aslr_extents = {
.base = GetInteger(page_table.GetAliasCodeRegionStart()),
.size = page_table.GetAliasCodeRegionSize(),
};
cheat_process_metadata.alias_extents = {
.base = GetInteger(page_table.GetAliasRegionStart()),
.size = page_table.GetAliasRegionSize(),
};
}
{
cheat_process_metadata.main_nso_extents = {
.base = main_region_begin,
.size = main_region_size,
};
cheat_process_metadata.main_nso_build_id = build_id;
}
cheat_process_debug_handle = cheat_process_metadata.process_id;
broken_unsafe = false;
unsafe_break_event->Signal();
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, update_event);
LOG_INFO(CheatEngine, "Cheat engine started");
// Signal to our fans.
cheat_process_event->Signal();
R_SUCCEED();
}
Result CheatProcessManager::GetCheatProcessMetadata(CheatProcessMetadata& out_metadata) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
out_metadata = cheat_process_metadata;
R_SUCCEED();
}
Result CheatProcessManager::ForceOpenCheatProcess() {
// R_RETURN(AttachToApplicationProcess(false));
R_SUCCEED();
}
Result CheatProcessManager::PauseCheatProcess() {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_RETURN(PauseCheatProcessUnsafe());
}
Result CheatProcessManager::PauseCheatProcessUnsafe() {
broken_unsafe = true;
unsafe_break_event->Clear();
if (system.ApplicationProcess()->IsSuspended()) {
R_SUCCEED();
}
R_RETURN(system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Paused));
}
Result CheatProcessManager::ResumeCheatProcess() {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_RETURN(ResumeCheatProcessUnsafe());
}
Result CheatProcessManager::ResumeCheatProcessUnsafe() {
broken_unsafe = true;
unsafe_break_event->Clear();
if (!system.ApplicationProcess()->IsSuspended()) {
R_SUCCEED();
}
system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Runnable);
R_SUCCEED();
}
Result CheatProcessManager::ForceCloseCheatProcess() {
CloseActiveCheatProcess();
R_SUCCEED();
}
Result CheatProcessManager::GetCheatProcessMappingCount(u64& out_count) {
std::scoped_lock lk(cheat_lock);
R_TRY(this->EnsureCheatProcess());
// TODO: Call svc::QueryDebugProcessMemory
out_count = 0;
R_SUCCEED();
}
Result CheatProcessManager::GetCheatProcessMappings(
u64& out_count, u64 offset, std::span<Kernel::Svc::MemoryInfo> out_mappings) {
std::scoped_lock lk(cheat_lock);
R_TRY(this->EnsureCheatProcess());
// TODO: Call svc::QueryDebugProcessMemory
out_count = 0;
R_SUCCEED();
}
Result CheatProcessManager::ReadCheatProcessMemory(u64 process_address, u64 size,
std::span<u8> out_data) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_RETURN(ReadCheatProcessMemoryUnsafe(process_address, &out_data, size));
}
Result CheatProcessManager::ReadCheatProcessMemoryUnsafe(u64 process_address, void* out_data,
size_t size) {
if (!system.ApplicationMemory().IsValidVirtualAddress(process_address)) {
std::memset(out_data, 0, size);
R_SUCCEED();
}
system.ApplicationMemory().ReadBlock(process_address, out_data, size);
R_SUCCEED();
}
Result CheatProcessManager::WriteCheatProcessMemory(u64 process_address, u64 size,
std::span<const u8> data) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_RETURN(WriteCheatProcessMemoryUnsafe(process_address, &data, size));
}
Result CheatProcessManager::WriteCheatProcessMemoryUnsafe(u64 process_address, const void* data,
size_t size) {
if (!system.ApplicationMemory().IsValidVirtualAddress(process_address)) {
R_SUCCEED();
}
if (system.ApplicationMemory().WriteBlock(process_address, data, size)) {
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), process_address, size);
}
R_SUCCEED();
}
Result CheatProcessManager::QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> mapping,
u64 address) {
std::scoped_lock lk(cheat_lock);
R_TRY(this->EnsureCheatProcess());
// TODO: Call svc::QueryDebugProcessMemory
R_SUCCEED();
}
Result CheatProcessManager::GetCheatCount(u64& out_count) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
out_count = std::count_if(cheat_entries.begin(), cheat_entries.end(),
[](const auto& entry) { return entry.definition.num_opcodes != 0; });
R_SUCCEED();
}
Result CheatProcessManager::GetCheats(u64& out_count, u64 offset,
std::span<CheatEntry> out_cheats) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
size_t count = 0, total_count = 0;
for (size_t i = 0; i < MaxCheatCount && count < out_cheats.size(); i++) {
if (cheat_entries[i].definition.num_opcodes) {
total_count++;
if (total_count > offset) {
out_cheats[count++] = cheat_entries[i];
}
}
}
out_count = count;
R_SUCCEED();
}
Result CheatProcessManager::GetCheatById(CheatEntry* out_cheat, u32 cheat_id) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
const CheatEntry* entry = GetCheatEntryById(cheat_id);
R_UNLESS(entry != nullptr, ResultCheatUnknownId);
R_UNLESS(entry->definition.num_opcodes != 0, ResultCheatUnknownId);
*out_cheat = *entry;
R_SUCCEED();
}
Result CheatProcessManager::ToggleCheat(u32 cheat_id) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
CheatEntry* entry = GetCheatEntryById(cheat_id);
R_UNLESS(entry != nullptr, ResultCheatUnknownId);
R_UNLESS(entry->definition.num_opcodes != 0, ResultCheatUnknownId);
R_UNLESS(cheat_id != 0, ResultCheatCannotDisable);
entry->enabled = !entry->enabled;
SetNeedsReloadVm(true);
R_SUCCEED();
}
Result CheatProcessManager::AddCheat(u32& out_cheat_id, bool enabled,
const CheatDefinition& cheat_definition) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_UNLESS(cheat_definition.num_opcodes != 0, ResultCheatInvalid);
R_UNLESS(cheat_definition.num_opcodes <= cheat_definition.opcodes.size(), ResultCheatInvalid);
CheatEntry* new_entry = GetFreeCheatEntry();
R_UNLESS(new_entry != nullptr, ResultCheatOutOfResource);
new_entry->enabled = enabled;
new_entry->definition = cheat_definition;
SetNeedsReloadVm(true);
out_cheat_id = new_entry->cheat_id;
R_SUCCEED();
}
Result CheatProcessManager::RemoveCheat(u32 cheat_id) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_UNLESS(cheat_id < MaxCheatCount, ResultCheatUnknownId);
ResetCheatEntry(cheat_id);
SetNeedsReloadVm(true);
R_SUCCEED();
}
Result CheatProcessManager::ReadStaticRegister(u64& out_value, u64 register_index) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_UNLESS(register_index < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid);
out_value = cheat_vm->GetStaticRegister(register_index);
R_SUCCEED();
}
Result CheatProcessManager::WriteStaticRegister(u64 register_index, u64 value) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_UNLESS(register_index < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid);
cheat_vm->SetStaticRegister(register_index, value);
R_SUCCEED();
}
Result CheatProcessManager::ResetStaticRegisters() {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
cheat_vm->ResetStaticRegisters();
R_SUCCEED();
}
Result CheatProcessManager::SetMasterCheat(const CheatDefinition& cheat_definition) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_UNLESS(cheat_definition.num_opcodes != 0, ResultCheatInvalid);
R_UNLESS(cheat_definition.num_opcodes <= cheat_definition.opcodes.size(), ResultCheatInvalid);
cheat_entries[0] = {
.enabled = true,
.definition = cheat_definition,
};
SetNeedsReloadVm(true);
R_SUCCEED();
}
Result CheatProcessManager::GetFrozenAddressCount(u64& out_count) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
out_count = std::distance(frozen_addresses_map.begin(), frozen_addresses_map.end());
R_SUCCEED();
}
Result CheatProcessManager::GetFrozenAddresses(u64& out_count, u64 offset,
std::span<FrozenAddressEntry> out_frozen_address) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
u64 total_count = 0, written_count = 0;
for (const auto& [address, value] : frozen_addresses_map) {
if (written_count >= out_frozen_address.size()) {
break;
}
if (offset <= total_count) {
out_frozen_address[written_count].address = address;
out_frozen_address[written_count].value = value;
written_count++;
}
total_count++;
}
out_count = written_count;
R_SUCCEED();
}
Result CheatProcessManager::GetFrozenAddress(FrozenAddressEntry& out_frozen_address_entry,
u64 address) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
const auto it = frozen_addresses_map.find(address);
R_UNLESS(it != frozen_addresses_map.end(), ResultFrozenAddressNotFound);
out_frozen_address_entry = {
.address = it->first,
.value = it->second,
};
R_SUCCEED();
}
Result CheatProcessManager::EnableFrozenAddress(u64& out_value, u64 address, u64 width) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
const auto it = frozen_addresses_map.find(address);
R_UNLESS(it == frozen_addresses_map.end(), ResultFrozenAddressAlreadyExists);
FrozenAddressValue value{};
value.width = static_cast<u8>(width);
R_TRY(ReadCheatProcessMemoryUnsafe(address, &value.value, width));
frozen_addresses_map.insert({address, value});
out_value = value.value;
R_SUCCEED();
}
Result CheatProcessManager::DisableFrozenAddress(u64 address) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
const auto it = frozen_addresses_map.find(address);
R_UNLESS(it != frozen_addresses_map.end(), ResultFrozenAddressNotFound);
frozen_addresses_map.erase(it);
R_SUCCEED();
}
u64 CheatProcessManager::HidKeysDown() const {
const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid");
if (hid == nullptr) {
LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
return 0;
}
const auto applet_resource = hid->GetResourceManager();
if (applet_resource == nullptr || applet_resource->GetNpad() == nullptr) {
LOG_WARNING(CheatEngine,
"Attempted to read input state, but applet resource is not initialized!");
return 0;
}
const auto press_state = applet_resource->GetNpad()->GetAndResetPressState();
return static_cast<u64>(press_state & Core::HID::NpadButton::All);
}
void CheatProcessManager::DebugLog(u8 id, u64 value) const {
LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
}
void CheatProcessManager::CommandLog(std::string_view data) const {
LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
}
void CheatProcessManager::FrameCallback(std::chrono::nanoseconds ns_late) {
std::scoped_lock lk(cheat_lock);
if (cheat_vm == nullptr) {
LOG_DEBUG(CheatEngine, "FrameCallback: VM is null");
return;
}
if (needs_reload_vm) {
LOG_INFO(CheatEngine, "Reloading cheat VM with {} entries", cheat_entries.size());
size_t enabled_count = 0;
for (const auto& entry : cheat_entries) {
if (entry.enabled && entry.definition.num_opcodes > 0) {
enabled_count++;
LOG_INFO(CheatEngine, " Cheat '{}': {} opcodes, enabled={}",
entry.definition.readable_name.data(),
entry.definition.num_opcodes, entry.enabled);
}
}
LOG_INFO(CheatEngine, "Total enabled cheats: {}", enabled_count);
cheat_vm->LoadProgram(cheat_entries);
LOG_INFO(CheatEngine, "Cheat VM loaded, program size: {}", cheat_vm->GetProgramSize());
needs_reload_vm = false;
}
if (cheat_vm->GetProgramSize() == 0) {
return;
}
cheat_vm->Execute(cheat_process_metadata);
}
} // namespace Service::DMNT

View File

@@ -1,126 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <map>
#include <span>
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/dmnt/dmnt_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
#include "common/intrusive_red_black_tree.h"
namespace Core {
class System;
}
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Kernel::Svc {
struct MemoryInfo;
}
namespace Service::DMNT {
class CheatVirtualMachine;
class CheatProcessManager final {
public:
static constexpr size_t MaxCheatCount = 0x80;
static constexpr size_t MaxFrozenAddressCount = 0x80;
CheatProcessManager(Core::System& system_);
~CheatProcessManager();
void SetVirtualMachine(std::unique_ptr<CheatVirtualMachine> vm);
bool HasCheatProcess();
Kernel::KReadableEvent& GetCheatProcessEvent() const;
Result GetCheatProcessMetadata(CheatProcessMetadata& out_metadata);
Result AttachToApplicationProcess(const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
u64 main_region_size);
Result ForceOpenCheatProcess();
Result PauseCheatProcess();
Result PauseCheatProcessUnsafe();
Result ResumeCheatProcess();
Result ResumeCheatProcessUnsafe();
Result ForceCloseCheatProcess();
Result GetCheatProcessMappingCount(u64& out_count);
Result GetCheatProcessMappings(u64& out_count, u64 offset,
std::span<Kernel::Svc::MemoryInfo> out_mappings);
Result ReadCheatProcessMemory(u64 process_address, u64 size, std::span<u8> out_data);
Result ReadCheatProcessMemoryUnsafe(u64 process_address, void* out_data, size_t size);
Result WriteCheatProcessMemory(u64 process_address, u64 size, std::span<const u8> data);
Result WriteCheatProcessMemoryUnsafe(u64 process_address, const void* data, size_t size);
Result QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> mapping, u64 address);
Result GetCheatCount(u64& out_count);
Result GetCheats(u64& out_count, u64 offset, std::span<CheatEntry> out_cheats);
Result GetCheatById(CheatEntry* out_cheat, u32 cheat_id);
Result ToggleCheat(u32 cheat_id);
Result AddCheat(u32& out_cheat_id, bool enabled, const CheatDefinition& cheat_definition);
Result RemoveCheat(u32 cheat_id);
Result ReadStaticRegister(u64& out_value, u64 register_index);
Result WriteStaticRegister(u64 register_index, u64 value);
Result ResetStaticRegisters();
Result SetMasterCheat(const CheatDefinition& cheat_definition);
Result GetFrozenAddressCount(u64& out_count);
Result GetFrozenAddresses(u64& out_count, u64 offset,
std::span<FrozenAddressEntry> out_frozen_address);
Result GetFrozenAddress(FrozenAddressEntry& out_frozen_address_entry, u64 address);
Result EnableFrozenAddress(u64& out_value, u64 address, u64 width);
Result DisableFrozenAddress(u64 address);
u64 HidKeysDown() const;
void DebugLog(u8 id, u64 value) const;
void CommandLog(std::string_view data) const;
private:
bool HasActiveCheatProcess();
void CloseActiveCheatProcess();
Result EnsureCheatProcess();
void SetNeedsReloadVm(bool reload);
void ResetCheatEntry(size_t i);
void ResetAllCheatEntries();
CheatEntry* GetCheatEntryById(size_t i);
CheatEntry* GetCheatEntryByReadableName(const char* readable_name);
CheatEntry* GetFreeCheatEntry();
void FrameCallback(std::chrono::nanoseconds ns_late);
static constexpr u64 InvalidHandle = 0;
mutable std::mutex cheat_lock;
Kernel::KEvent* unsafe_break_event;
Kernel::KEvent* cheat_process_event;
u64 cheat_process_debug_handle = InvalidHandle;
CheatProcessMetadata cheat_process_metadata = {};
bool broken_unsafe = false;
bool needs_reload_vm = false;
std::unique_ptr<CheatVirtualMachine> cheat_vm;
bool enable_cheats_by_default = true;
bool always_save_cheat_toggles = false;
bool should_save_cheat_toggles = false;
std::array<CheatEntry, MaxCheatCount> cheat_entries = {};
// TODO: Replace with IntrusiveRedBlackTree
std::map<u64, FrozenAddressValue> frozen_addresses_map = {};
Core::System& system;
KernelHelpers::ServiceContext service_context;
std::shared_ptr<Core::Timing::EventType> update_event;
Core::Timing::CoreTiming& core_timing;
};
} // namespace Service::DMNT

File diff suppressed because it is too large Load Diff

View File

@@ -1,323 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <variant>
#include "common/common_types.h"
#include "core/hle/service/dmnt/dmnt_types.h"
namespace Service::DMNT {
class CheatProcessManager;
enum class CheatVmOpcodeType : u32 {
StoreStatic = 0,
BeginConditionalBlock = 1,
EndConditionalBlock = 2,
ControlLoop = 3,
LoadRegisterStatic = 4,
LoadRegisterMemory = 5,
StoreStaticToAddress = 6,
PerformArithmeticStatic = 7,
BeginKeypressConditionalBlock = 8,
// These are not implemented by Gateway's VM.
PerformArithmeticRegister = 9,
StoreRegisterToAddress = 10,
Reserved11 = 11,
// This is a meta entry, and not a real opcode.
// This is to facilitate multi-nybble instruction decoding.
ExtendedWidth = 12,
// Extended width opcodes.
BeginRegisterConditionalBlock = 0xC0,
SaveRestoreRegister = 0xC1,
SaveRestoreRegisterMask = 0xC2,
ReadWriteStaticRegister = 0xC3,
// This is a meta entry, and not a real opcode.
// This is to facilitate multi-nybble instruction decoding.
DoubleExtendedWidth = 0xF0,
// Double-extended width opcodes.
PauseProcess = 0xFF0,
ResumeProcess = 0xFF1,
DebugLog = 0xFFF,
};
enum class MemoryAccessType : u32 {
MainNso = 0,
Heap = 1,
Alias = 2,
Aslr = 3,
};
enum class ConditionalComparisonType : u32 {
GT = 1,
GE = 2,
LT = 3,
LE = 4,
EQ = 5,
NE = 6,
};
enum class RegisterArithmeticType : u32 {
Addition = 0,
Subtraction = 1,
Multiplication = 2,
LeftShift = 3,
RightShift = 4,
// These are not supported by Gateway's VM.
LogicalAnd = 5,
LogicalOr = 6,
LogicalNot = 7,
LogicalXor = 8,
None = 9,
};
enum class StoreRegisterOffsetType : u32 {
None = 0,
Reg = 1,
Imm = 2,
MemReg = 3,
MemImm = 4,
MemImmReg = 5,
};
enum class CompareRegisterValueType : u32 {
MemoryRelAddr = 0,
MemoryOfsReg = 1,
RegisterRelAddr = 2,
RegisterOfsReg = 3,
StaticValue = 4,
OtherRegister = 5,
};
enum class SaveRestoreRegisterOpType : u32 {
Restore = 0,
Save = 1,
ClearSaved = 2,
ClearRegs = 3,
};
enum class DebugLogValueType : u32 {
MemoryRelAddr = 0,
MemoryOfsReg = 1,
RegisterRelAddr = 2,
RegisterOfsReg = 3,
RegisterValue = 4,
};
union VmInt {
u8 bit8;
u16 bit16;
u32 bit32;
u64 bit64;
};
struct StoreStaticOpcode {
u32 bit_width{};
MemoryAccessType mem_type{};
u32 offset_register{};
u64 rel_address{};
VmInt value{};
};
struct BeginConditionalOpcode {
u32 bit_width{};
MemoryAccessType mem_type{};
ConditionalComparisonType cond_type{};
u64 rel_address{};
VmInt value{};
};
struct EndConditionalOpcode {
bool is_else;
};
struct ControlLoopOpcode {
bool start_loop{};
u32 reg_index{};
u32 num_iters{};
};
struct LoadRegisterStaticOpcode {
u32 reg_index{};
u64 value{};
};
struct LoadRegisterMemoryOpcode {
u32 bit_width{};
MemoryAccessType mem_type{};
u32 reg_index{};
bool load_from_reg{};
u64 rel_address{};
};
struct StoreStaticToAddressOpcode {
u32 bit_width{};
u32 reg_index{};
bool increment_reg{};
bool add_offset_reg{};
u32 offset_reg_index{};
u64 value{};
};
struct PerformArithmeticStaticOpcode {
u32 bit_width{};
u32 reg_index{};
RegisterArithmeticType math_type{};
u32 value{};
};
struct BeginKeypressConditionalOpcode {
u32 key_mask{};
};
struct PerformArithmeticRegisterOpcode {
u32 bit_width{};
RegisterArithmeticType math_type{};
u32 dst_reg_index{};
u32 src_reg_1_index{};
u32 src_reg_2_index{};
bool has_immediate{};
VmInt value{};
};
struct StoreRegisterToAddressOpcode {
u32 bit_width{};
u32 str_reg_index{};
u32 addr_reg_index{};
bool increment_reg{};
StoreRegisterOffsetType ofs_type{};
MemoryAccessType mem_type{};
u32 ofs_reg_index{};
u64 rel_address{};
};
struct BeginRegisterConditionalOpcode {
u32 bit_width{};
ConditionalComparisonType cond_type{};
u32 val_reg_index{};
CompareRegisterValueType comp_type{};
MemoryAccessType mem_type{};
u32 addr_reg_index{};
u32 other_reg_index{};
u32 ofs_reg_index{};
u64 rel_address{};
VmInt value{};
};
struct SaveRestoreRegisterOpcode {
u32 dst_index{};
u32 src_index{};
SaveRestoreRegisterOpType op_type{};
};
struct SaveRestoreRegisterMaskOpcode {
SaveRestoreRegisterOpType op_type{};
std::array<bool, 0x10> should_operate{};
};
struct ReadWriteStaticRegisterOpcode {
u32 static_idx{};
u32 idx{};
};
struct PauseProcessOpcode {};
struct ResumeProcessOpcode {};
struct DebugLogOpcode {
u32 bit_width{};
u32 log_id{};
DebugLogValueType val_type{};
MemoryAccessType mem_type{};
u32 addr_reg_index{};
u32 val_reg_index{};
u32 ofs_reg_index{};
u64 rel_address{};
};
struct UnrecognizedInstruction {
CheatVmOpcodeType opcode{};
};
struct CheatVmOpcode {
bool begin_conditional_block{};
std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
opcode{};
};
class CheatVirtualMachine {
public:
static constexpr std::size_t MaximumProgramOpcodeCount = 0x400;
static constexpr std::size_t NumRegisters = 0x10;
static constexpr std::size_t NumReadableStaticRegisters = 0x80;
static constexpr std::size_t NumWritableStaticRegisters = 0x80;
static constexpr std::size_t NumStaticRegisters =
NumReadableStaticRegisters + NumWritableStaticRegisters;
explicit CheatVirtualMachine(CheatProcessManager& cheat_manager);
~CheatVirtualMachine();
std::size_t GetProgramSize() const {
return this->num_opcodes;
}
bool LoadProgram(std::span<const CheatEntry> cheats);
void Execute(const CheatProcessMetadata& metadata);
u64 GetStaticRegister(std::size_t register_index) const {
return static_registers[register_index];
}
void SetStaticRegister(std::size_t register_index, u64 value) {
static_registers[register_index] = value;
}
void ResetStaticRegisters() {
static_registers = {};
}
private:
bool DecodeNextOpcode(CheatVmOpcode& out);
void SkipConditionalBlock(bool is_if);
void ResetState();
// For implementing the DebugLog opcode.
void DebugLog(u32 log_id, u64 value) const;
void LogOpcode(const CheatVmOpcode& opcode) const;
static u64 GetVmInt(VmInt value, u32 bit_width);
static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
MemoryAccessType mem_type, u64 rel_address);
CheatProcessManager& manager;
std::size_t num_opcodes = 0;
std::size_t instruction_ptr = 0;
std::size_t condition_depth = 0;
bool decode_success = false;
std::array<u32, MaximumProgramOpcodeCount> program{};
std::array<u64, NumRegisters> registers{};
std::array<u64, NumRegisters> saved_values{};
std::array<u64, NumStaticRegisters> static_registers{};
std::array<std::size_t, NumRegisters> loop_tops{};
};
}// namespace Service::DMNT

View File

@@ -1,26 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/core.h"
#include "core/hle/service/dmnt/cheat_interface.h"
#include "core/hle/service/dmnt/cheat_process_manager.h"
#include "core/hle/service/dmnt/cheat_virtual_machine.h"
#include "core/hle/service/dmnt/dmnt.h"
#include "core/hle/service/server_manager.h"
namespace Service::DMNT {
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
auto& cheat_manager = system.GetCheatManager();
auto cheat_vm = std::make_unique<CheatVirtualMachine>(cheat_manager);
cheat_manager.SetVirtualMachine(std::move(cheat_vm));
server_manager->RegisterNamedService("dmnt:cht",
std::make_shared<ICheatInterface>(system, cheat_manager));
ServerManager::RunServer(std::move(server_manager));
}
} // namespace Service::DMNT

View File

@@ -1,15 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
namespace Core {
class System;
};
namespace Service::DMNT {
void LoopProcess(Core::System& system);
} // namespace Service::DMNT

View File

@@ -1,26 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::DMNT {
constexpr Result ResultDebuggingDisabled(ErrorModule::DMNT, 2);
constexpr Result ResultCheatNotAttached(ErrorModule::DMNT, 6500);
constexpr Result ResultCheatNullBuffer(ErrorModule::DMNT, 6501);
constexpr Result ResultCheatInvalidBuffer(ErrorModule::DMNT, 6502);
constexpr Result ResultCheatUnknownId(ErrorModule::DMNT, 6503);
constexpr Result ResultCheatOutOfResource(ErrorModule::DMNT, 6504);
constexpr Result ResultCheatInvalid(ErrorModule::DMNT, 6505);
constexpr Result ResultCheatCannotDisable(ErrorModule::DMNT, 6506);
constexpr Result ResultFrozenAddressInvalidWidth(ErrorModule::DMNT, 6600);
constexpr Result ResultFrozenAddressAlreadyExists(ErrorModule::DMNT, 6601);
constexpr Result ResultFrozenAddressNotFound(ErrorModule::DMNT, 6602);
constexpr Result ResultFrozenAddressOutOfResource(ErrorModule::DMNT, 6603);
constexpr Result ResultVirtualMachineInvalidConditionDepth(ErrorModule::DMNT, 6700);
} // namespace Service::DMNT

View File

@@ -1,55 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Service::DMNT {
struct MemoryRegionExtents {
u64 base{};
u64 size{};
};
static_assert(sizeof(MemoryRegionExtents) == 0x10, "MemoryRegionExtents is an invalid size");
struct CheatProcessMetadata {
u64 process_id{};
u64 program_id{};
MemoryRegionExtents main_nso_extents{};
MemoryRegionExtents heap_extents{};
MemoryRegionExtents alias_extents{};
MemoryRegionExtents aslr_extents{};
std::array<u8, 0x20> main_nso_build_id{};
};
static_assert(sizeof(CheatProcessMetadata) == 0x70, "CheatProcessMetadata is an invalid size");
struct CheatDefinition {
std::array<char, 0x40> readable_name;
u32 num_opcodes;
std::array<u32, 0x100> opcodes;
};
static_assert(sizeof(CheatDefinition) == 0x444, "CheatDefinition is an invalid size");
struct CheatEntry {
bool enabled;
u32 cheat_id;
CheatDefinition definition;
};
static_assert(sizeof(CheatEntry) == 0x44C, "CheatEntry is an invalid size");
static_assert(std::is_trivial_v<CheatEntry>, "CheatEntry type must be trivially copyable.");
struct FrozenAddressValue {
u64 value;
u8 width;
};
static_assert(sizeof(FrozenAddressValue) == 0x10, "FrozenAddressValue is an invalid size");
struct FrozenAddressEntry {
u64 address;
FrozenAddressValue value;
};
static_assert(sizeof(FrozenAddressEntry) == 0x18, "FrozenAddressEntry is an invalid size");
} // namespace Service::DMNT

View File

@@ -60,6 +60,16 @@ public:
{38, nullptr, "OwnTicket3"},
{39, nullptr, "DeleteAllInactivePersonalizedTicket"},
{40, nullptr, "DeletePrepurchaseRecordByNintendoAccountId"},
{101, nullptr, "Unknown101"}, //18.0.0+
{102, nullptr, "Unknown102"}, //18.0.0+
{103, nullptr, "Unknown103"}, //18.0.0+
{104, nullptr, "Unknown104"}, //18.0.0+
{105, nullptr, "Unknown105"}, //20.0.0+
{201, nullptr, "Unknown201"}, //18.0.0+
{202, nullptr, "Unknown202"}, //18.0.0+
{203, nullptr, "Unknown203"}, //18.0.0+
{204, nullptr, "Unknown204"}, //18.0.0+
{205, nullptr, "Unknown205"}, //18.0.0+
{501, nullptr, "Unknown501"},
{502, nullptr, "Unknown502"},
{503, nullptr, "GetTitleKey"},

View File

@@ -125,13 +125,10 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{250, nullptr, "IsVirtual"},
{251, nullptr, "GetAnalogStickModuleParam"},
{253, nullptr, "ClearStorageForShipment"}, //19.0.0+
{254, nullptr, "Unknown254"},
{255, nullptr, "Unknown255"},
{256, nullptr, "Unknown256"},
{261, nullptr, "UpdateDesignInfo12"},
{262, nullptr, "GetUniquePadButtonCount"},
{267, nullptr, "Unknown267"},
{268, nullptr, "Unknown268"},
{261, nullptr, "UpdateDesignInfo12"}, //21.0.0+
{262, nullptr, "GetUniquePadButtonCount"}, //21.0.0+
{267, nullptr, "SetAnalogStickCalibration"}, //21.0.0+
{268, nullptr, "ResetAnalogStickCalibration"}, //21.0.0+
{301, nullptr, "GetAbstractedPadHandles"},
{302, nullptr, "GetAbstractedPadState"},
{303, nullptr, "GetAbstractedPadsState"},
@@ -148,6 +145,8 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{331, nullptr, "DetachHdlsVirtualDevice"},
{332, nullptr, "SetHdlsState"},
{350, nullptr, "AddRegisteredDevice"},
{351, nullptr, "GetRegisteredDevicesCountDebug"}, //17.0.0-18.1.0
{352, nullptr, "DeleteRegisteredDevicesDebug"}, //17.0.0-18.1.0
{400, nullptr, "DisableExternalMcuOnNxDevice"},
{401, nullptr, "DisableRailDeviceFiltering"},
{402, nullptr, "EnableWiredPairing"},
@@ -159,14 +158,30 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{551, nullptr, "GetAnalogStickModelData"},
{552, nullptr, "ResetAnalogStickModelData"},
{600, nullptr, "ConvertPadState"},
{601, nullptr, "IsButtonConfigSupported"}, //18.0.0+
{602, nullptr, "IsButtonConfigEmbeddedSupported"}, //18.0.0+
{603, nullptr, "DeleteButtonConfig"}, //18.0.0+
{604, nullptr, "DeleteButtonConfigEmbedded"}, //18.0.0+
{605, nullptr, "SetButtonConfigEnabled"}, //18.0.0+
{606, nullptr, "SetButtonConfigEmbeddedEnabled"}, //18.0.0+
{607, nullptr, "IsButtonConfigEnabled"}, //18.0.0+
{608, nullptr, "IsButtonConfigEmbeddedEnabled"}, //18.0.0+
{609, nullptr, "SetButtonConfigEmbedded"}, //18.0.0+
{610, nullptr, "SetButtonConfigFull"}, //18.0.0+
{611, nullptr, "SetButtonConfigLeft"}, //18.0.0+
{612, nullptr, "SetButtonConfigRight"}, //18.0.0+
{613, nullptr, "GetButtonConfigEmbedded"}, //18.0.0+
{614, nullptr, "GetButtonConfigFull"}, //18.0.0+
{615, nullptr, "GetButtonConfigLeft"}, //18.0.0+
{616, nullptr, "GetButtonConfigRight"}, //18.0.0+
{650, nullptr, "AddButtonPlayData"},
{651, nullptr, "StartButtonPlayData"},
{652, nullptr, "StopButtonPlayData"},
{700, nullptr, "Unknown700"},
{700, nullptr, "GetRailAttachEventCount"}, //21.0.0+
{2000, nullptr, "DeactivateDigitizer"},
{2001, nullptr, "SetDigitizerAutoPilotState"},
{2002, nullptr, "UnsetDigitizerAutoPilotState"},
{2002, nullptr, "ReloadFirmwareDebugSettings"},
{3000, nullptr, "ReloadFirmwareDebugSettings"},
};
// clang-format on

View File

@@ -70,7 +70,9 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{328, nullptr, "AttachAbstractedPadToNpad"},
{329, nullptr, "DetachAbstractedPadAll"},
{330, nullptr, "CheckAbstractedPadConnection"},
{500, nullptr, "SetAppletResourceUserId"},
{332, nullptr, "ConvertAppletDetailedUiTypeFromPlayReportType"}, //19.0.0+
{333, nullptr, "SetNpadUserSpgApplet"}, //20.0.0+
{334, nullptr, "AcquireUniquePadButtonStateChangedEventHandle"}, //20.0.0+
{501, &IHidSystemServer::RegisterAppletResourceUserId, "RegisterAppletResourceUserId"},
{502, &IHidSystemServer::UnregisterAppletResourceUserId, "UnregisterAppletResourceUserId"},
{503, &IHidSystemServer::EnableAppletToGetInput, "EnableAppletToGetInput"},
@@ -99,7 +101,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{547, nullptr, "GetAllowedBluetoothLinksCount"},
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
{549, nullptr, "GetConnectableRegisteredDevices"},
{551, nullptr, "GetRegisteredDevicesForControllerSupport"},
{551, nullptr, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+
{700, nullptr, "ActivateUniquePad"},
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
@@ -119,6 +121,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{810, nullptr, "GetUniquePadControllerNumber"},
{811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
{812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
{813, nullptr, "GetDeviceType"},
{821, nullptr, "StartAnalogStickManualCalibration"},
{822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
{823, nullptr, "CancelAnalogStickManualCalibration"},
@@ -149,6 +152,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{1009, nullptr, "AcquireAudioControlEventHandle"},
{1010, nullptr, "GetAudioControlStates"},
{1011, nullptr, "DeactivateAudioControl"},
{1012, nullptr, "GetFirmwareVersionStringForUserSupportPage"},
{1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
{1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
{1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
@@ -169,6 +173,8 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"},
{1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
{1157, nullptr, "CancelConnectionTrigger"},
{1158, nullptr, "SetConnectionLimitForSplay"}, //20.1.0+
{1159, nullptr, "ClearConnectionLimitForSplay"}, //20.1.0+
{1200, nullptr, "IsButtonConfigSupported"},
{1201, nullptr, "IsButtonConfigEmbeddedSupported"},
{1202, nullptr, "DeleteButtonConfig"},
@@ -227,16 +233,17 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{1289, nullptr, "SetButtonConfigStorageFull"},
{1290, nullptr, "DeleteButtonConfigStorageRight"},
{1291, nullptr, "DeleteButtonConfigStorageRight"},
{1308, nullptr, "SetButtonConfigVisible"}, // 18.0.0+
{1309, nullptr, "IsButtonConfigVisible"}, // 18.0.0+
{1320, nullptr, "WakeTouchScreenUp"}, // 17.0.0+
{1321, nullptr, "PutTouchScreenToSleep"}, // 17.0.0+
{1322, nullptr, "AcquireTouchScreenAsyncWakeCompletedEvent"}, // 20.0.0+
{1323, nullptr, "StartTouchScreenAutoTuneForSystemSettings"}, // 21.0.0+
{1324, nullptr, "AcquireTouchScreenAutoTuneCompletedEvent"}, // 21.0.0+
{1325, nullptr, "IsTouchScreenAutoTuneRequiredForRepairProviderReplacement"}, // 21.0.0+
{1326, nullptr, "Unknown1326"}, // 21.0.0+
{1420, nullptr, "GetAppletResourceProperty"}, // 19.0.0+
{1308, nullptr, "SetButtonConfigVisible"}, //18.0.0+
{1309, nullptr, "IsButtonConfigVisible"}, //18.0.0+
{1320, nullptr, "WakeTouchScreenUp"}, //17.0.0+
{1321, nullptr, "PutTouchScreenToSleep"}, //17.0.0+
{1322, nullptr, "AcquireTouchScreenAsyncWakeCompletedEvent"}, //20.0.0+
{1323, nullptr, "StartTouchScreenAutoTuneForSystemSettings"}, //21.0.0+
{1324, nullptr, "AcquireTouchScreenAutoTuneCompletedEvent"}, //21.0.0+
{1325, nullptr, "IsTouchScreenAutoTuneRequiredForRepairProviderReplacement"}, //21.0.0+
{1326, nullptr, "SetTouchScreenOffset"}, //21.0.0+
{1420, nullptr, "GetAppletResourceProperty"}, //19.0.0+
{12010, nullptr, "SetButtonConfigLeft"} //11.0.0-17.0.1
};
// clang-format on

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -14,17 +17,42 @@ public:
explicit MIG_USR(Core::System& system_) : ServiceFramework{system_, "mig:usr"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown0"}, //19.0.0+
{1, nullptr, "Unknown1"}, //20.0.0+
{2, nullptr, "Unknown2"}, //20.0.0+
{10, nullptr, "TryGetLastMigrationInfo"},
{100, nullptr, "CreateServer"},
{101, nullptr, "ResumeServer"},
{200, nullptr, "CreateClient"},
{201, nullptr, "ResumeClient"},
{1001, nullptr, "Unknown1001"},
{1010, nullptr, "Unknown1010"},
{1100, nullptr, "Unknown1100"},
{1101, nullptr, "Unknown1101"},
{1200, nullptr, "Unknown1200"},
{1201, nullptr, "Unknown1201"}
{11, nullptr, "Unknown11"}, //20.0.0+
{100, nullptr, "CreateUserMigrationServer"}, //7.0.0+
{101, nullptr, "ResumeUserMigrationServer"}, //7.0.0+
{200, nullptr, "CreateUserMigrationClient"}, //7.0.0+
{201, nullptr, "ResumeUserMigrationClient"}, //7.0.0+
{1001, nullptr, "GetSaveDataMigrationPolicyInfoAsync"}, //8.0.0-20.5.0
{1010, nullptr, "TryGetLastSaveDataMigrationInfo"}, //7.0.0+
{1100, nullptr, "CreateSaveDataMigrationServer"}, //7.0.0-19.0.1
{1101, nullptr, "ResumeSaveDataMigrationServer"}, //7.0.0+
{1110, nullptr, "Unknown1101"}, //17.0.0+
{1200, nullptr, "CreateSaveDataMigrationClient"}, //7.0.0+
{1201, nullptr, "ResumeSaveDataMigrationClient"}, //7.0.0+
{2001, nullptr, "Unknown2001"}, //20.0.0+
{2010, nullptr, "Unknown2010"}, //20.0.0+
{2100, nullptr, "Unknown2100"}, //20.0.0+
{2110, nullptr, "Unknown2110"}, //20.0.0+
{2200, nullptr, "Unknown2200"}, //20.0.0+
{2210, nullptr, "Unknown2210"}, //20.0.0+
{2220, nullptr, "Unknown2220"}, //20.0.0+
{2230, nullptr, "Unknown2230"}, //20.0.0+
{2231, nullptr, "Unknown2231"}, //20.0.0+
{2232, nullptr, "Unknown2232"}, //20.0.0+
{2233, nullptr, "Unknown2233"}, //20.0.0+
{2234, nullptr, "Unknown2234"}, //20.0.0+
{2250, nullptr, "Unknown2250"}, //20.0.0+
{2260, nullptr, "Unknown2260"}, //20.0.0+
{2270, nullptr, "Unknown2270"}, //20.0.0+
{2280, nullptr, "Unknown2280"}, //20.0.0+
{2300, nullptr, "Unknown2300"}, //20.0.0+
{2310, nullptr, "Unknown2310"}, //20.0.0+
{2400, nullptr, "Unknown2400"}, //20.0.0+
{2420, nullptr, "Unknown2420"}, //20.0.0+
};
// clang-format on

View File

@@ -167,57 +167,146 @@ public:
{81, nullptr, "ListLocalCommunicationSendSystemUpdateTask"},
{82, nullptr, "GetReceivedSystemDataPath"},
{83, nullptr, "CalculateApplyDeltaTaskOccupiedSize"},
{84, nullptr, "Unknown84"},
{84, nullptr, "ReloadErrorSimulation"},
{85, nullptr, "ListNetworkInstallTaskContentMetaFromInstallMeta"},
{86, nullptr, "ListNetworkInstallTaskOccupiedSize"},
{87, nullptr, "Unknown87"},
{88, nullptr, "Unknown88"},
{89, nullptr, "Unknown89"},
{90, nullptr, "Unknown90"},
{91, nullptr, "Unknown91"},
{92, nullptr, "Unknown92"},
{93, nullptr, "Unknown93"},
{94, nullptr, "Unknown94"},
{95, nullptr, "Unknown95"},
{96, nullptr, "Unknown96"},
{97, nullptr, "Unknown97"},
{98, nullptr, "Unknown98"},
{99, nullptr, "Unknown99"},
{100, nullptr, "Unknown100"},
{101, nullptr, "Unknown101"},
{102, nullptr, "Unknown102"},
{103, nullptr, "Unknown103"},
{104, nullptr, "Unknown104"},
{105, nullptr, "Unknown105"},
{106, nullptr, "Unknown106"},
{107, nullptr, "Unknown107"},
{108, nullptr, "Unknown108"},
{109, nullptr, "Unknown109"},
{110, nullptr, "Unknown110"},
{111, nullptr, "Unknown111"},
{112, nullptr, "Unknown112"},
{113, nullptr, "Unknown113"},
{114, nullptr, "Unknown114"},
{115, nullptr, "Unknown115"},
{116, nullptr, "Unknown116"},
{117, nullptr, "Unknown117"},
{118, nullptr, "Unknown118"},
{119, nullptr, "Unknown119"},
{120, nullptr, "Unknown120"},
{121, nullptr, "Unknown121"},
{122, nullptr, "Unknown122"},
{123, nullptr, "Unknown123"},
{124, nullptr, "Unknown124"},
{125, nullptr, "Unknown125"},
{126, nullptr, "Unknown126"},
{127, nullptr, "Unknown127"},
{128, nullptr, "Unknown128"},
{129, nullptr, "Unknown129"},
{130, nullptr, "Unknown130"},
{131, nullptr, "Unknown131"},
{132, nullptr, "Unknown132"},
{133, nullptr, "Unknown133"},
{134, nullptr, "Unknown134"},
{87, nullptr, "RequestQueryAvailableELicenses"},
{88, nullptr, "RequestAssignELicenses"},
{89, nullptr, "RequestExtendELicenses"},
{90, nullptr, "RequestSyncELicenses"},
{91, nullptr, "Unknown91"}, //6.0.0-14.1.2
{92, nullptr, "Unknown92"}, //21.0.0+
{93, nullptr, "RequestReportActiveELicenses"},
{94, nullptr, "RequestReportActiveELicensesPassively"},
{95, nullptr, "RequestRegisterDynamicRightsNotificationToken"},
{96, nullptr, "RequestAssignAllDeviceLinkedELicenses"},
{97, nullptr, "RequestRevokeAllELicenses"},
{98, nullptr, "RequestPrefetchForDynamicRights"},
{99, nullptr, "CreateNetworkInstallTask"},
{100, nullptr, "ListNetworkInstallTaskRightsIds"},
{101, nullptr, "RequestDownloadETickets"},
{102, nullptr, "RequestQueryDownloadableContents"},
{103, nullptr, "DeleteNetworkInstallTaskContentMeta"},
{104, nullptr, "RequestIssueEdgeTokenForDebug"},
{105, nullptr, "RequestQueryAvailableELicenses2"},
{106, nullptr, "RequestAssignELicenses2"},
{107, nullptr, "GetNetworkInstallTaskStateCounter"},
{108, nullptr, "InvalidateDynamicRightsNaIdTokenCacheForDebug"},
{109, nullptr, "ListNetworkInstallTaskPartialInstallContentMeta"},
{110, nullptr, "ListNetworkInstallTaskRightsIdsFromIndex"},
{111, nullptr, "AddNetworkInstallTaskContentMetaForUser"},
{112, nullptr, "RequestAssignELicensesAndDownloadETickets"},
{113, nullptr, "RequestQueryAvailableCommonELicenses"},
{114, nullptr, "SetNetworkInstallTaskExtendedAttribute"},
{115, nullptr, "GetNetworkInstallTaskExtendedAttribute"},
{116, nullptr, "GetAllocatorInfo"},
{117, nullptr, "RequestQueryDownloadableContentsByApplicationId"},
{118, nullptr, "MarkNoDownloadRightsErrorResolved"},
{119, nullptr, "GetApplyDeltaTaskAllAppliedContentMeta"},
{120, nullptr, "PrioritizeNetworkInstallTask"},
{121, nullptr, "RequestQueryAvailableCommonELicenses2"},
{122, nullptr, "RequestAssignCommonELicenses"},
{123, nullptr, "RequestAssignCommonELicenses2"},
{124, nullptr, "IsNetworkInstallTaskFrontOfQueue"},
{125, nullptr, "PrioritizeApplyDeltaTask"},
{126, nullptr, "RerouteDownloadingPatch"},
{127, nullptr, "UnmarkNoDownloadRightsErrorResolved"},
{128, nullptr, "RequestContentsSize"},
{129, nullptr, "RequestContentsAuthorizationToken"},
{130, nullptr, "RequestCdnVendorDiscovery"},
{131, nullptr, "RefreshDebugAvailability"},
{132, nullptr, "ClearResponseSimulationEntry"},
{133, nullptr, "RegisterResponseSimulationEntry"},
{134, nullptr, "GetProcessedCdnVendors"},
{135, nullptr, "RefreshRuntimeBehaviorsForDebug"},
{136, nullptr, "RequestOnlineSubscriptionFreeTrialAvailability"},
{137, nullptr, "GetNetworkInstallTaskContentMetaCount"},
{138, nullptr, "RequestRevokeELicenses"},
{139, nullptr, "EnableNetworkConnectionToUseApplicationCore"},
{140, nullptr, "DisableNetworkConnectionToUseApplicationCore"},
{141, nullptr, "IsNetworkConnectionEnabledToUseApplicationCore"},
{142, nullptr, "RequestCheckSafeSystemVersion"},
{143, nullptr, "RequestApplicationIcon"},
{144, nullptr, "RequestDownloadIdbeIconFile"},
{147, nullptr, "Unknown147"}, //18.0.0+
{148, nullptr, "Unknown148"}, //18.0.0+
{150, nullptr, "Unknown150"}, //19.0.0+
{151, nullptr, "Unknown151"}, //20.0.0+
{152, nullptr, "Unknown152"}, //20.0.0+
{153, nullptr, "Unknown153"}, //20.0.0+
{154, nullptr, "Unknown154"}, //20.0.0+
{155, nullptr, "Unknown155"}, //20.0.0+
{156, nullptr, "Unknown156"}, //20.0.0+
{157, nullptr, "Unknown157"}, //20.0.0+
{158, nullptr, "Unknown158"}, //20.0.0+
{159, nullptr, "Unknown159"}, //20.0.0+
{160, nullptr, "Unknown160"}, //20.0.0+
{161, nullptr, "Unknown161"}, //20.0.0+
{162, nullptr, "Unknown162"}, //20.0.0+
{163, nullptr, "Unknown163"}, //20.0.0+
{164, nullptr, "Unknown164"}, //20.0.0+
{165, nullptr, "Unknown165"}, //20.0.0+
{166, nullptr, "Unknown166"}, //20.0.0+
{167, nullptr, "Unknown167"}, //20.0.0+
{168, nullptr, "Unknown168"}, //20.0.0+
{169, nullptr, "Unknown169"}, //20.0.0+
{170, nullptr, "Unknown170"}, //20.0.0+
{171, nullptr, "Unknown171"}, //20.0.0+
{172, nullptr, "Unknown172"}, //20.0.0+
{173, nullptr, "Unknown173"}, //20.0.0+
{174, nullptr, "Unknown174"}, //20.0.0+
{175, nullptr, "Unknown175"}, //20.0.0+
{176, nullptr, "Unknown176"}, //20.0.0+
{177, nullptr, "Unknown177"}, //20.0.0+
{2000, nullptr, "Unknown2000"}, //20.0.0+
{2001, nullptr, "Unknown2001"}, //20.0.0+
{2002, nullptr, "Unknown2002"}, //20.0.0+
{2003, nullptr, "Unknown2003"}, //20.0.0+
{2004, nullptr, "Unknown2004"}, //20.0.0+
{2007, nullptr, "Unknown2007"}, //20.0.0+
{2011, nullptr, "Unknown2011"}, //20.0.0+
{2012, nullptr, "Unknown2012"}, //20.0.0+
{2013, nullptr, "Unknown2013"}, //20.0.0+
{2014, nullptr, "Unknown2014"}, //20.0.0+
{2015, nullptr, "Unknown2015"}, //20.0.0+
{2016, nullptr, "Unknown2016"}, //20.0.0+
{2017, nullptr, "Unknown2017"}, //20.0.0+
{2018, nullptr, "Unknown2018"}, //20.0.0+
{2019, nullptr, "Unknown2019"}, //20.0.0+
{2020, nullptr, "Unknown2020"}, //20.0.0+
{2021, nullptr, "Unknown2021"}, //20.0.0+
{2022, nullptr, "Unknown2022"}, //20.0.0+
{2023, nullptr, "Unknown2023"}, //20.0.0+
{2024, nullptr, "Unknown2024"}, //20.0.0+
{2025, nullptr, "Unknown2025"}, //20.0.0+
{2026, nullptr, "Unknown2026"}, //20.0.0+
{2027, nullptr, "Unknown2027"}, //20.0.0+
{2028, nullptr, "Unknown2028"}, //20.0.0+
{2029, nullptr, "Unknown2029"}, //20.0.0+
{2030, nullptr, "Unknown2030"}, //20.0.0+
{2031, nullptr, "Unknown2031"}, //20.0.0+
{2032, nullptr, "Unknown2032"}, //20.0.0+
{2033, nullptr, "Unknown2033"}, //20.0.0+
{2034, nullptr, "Unknown2034"}, //20.0.0+
{2035, nullptr, "Unknown2035"}, //20.0.0+
{2036, nullptr, "Unknown2036"}, //20.0.0+
{2037, nullptr, "Unknown2037"}, //20.0.0+
{2038, nullptr, "Unknown2038"}, //20.0.0+
{2039, nullptr, "Unknown2039"}, //20.0.0+
{2040, nullptr, "Unknown2040"}, //20.0.0+
{2041, nullptr, "Unknown2041"}, //20.0.0+
{2042, nullptr, "Unknown2042"}, //20.0.0+
{2043, nullptr, "Unknown2043"}, //20.0.0+
{2044, nullptr, "Unknown2044"}, //20.0.0+
{2045, nullptr, "Unknown2045"}, //20.0.0+
{2046, nullptr, "Unknown2046"}, //20.0.0+
{2047, nullptr, "Unknown2047"}, //20.0.0+
{2048, nullptr, "Unknown2048"}, //20.0.0+
{2049, nullptr, "Unknown2049"}, //20.0.0+
{2050, nullptr, "Unknown2050"}, //20.0.0+
{2051, nullptr, "Unknown2051"}, //20.0.0+
{3000, nullptr, "RequestLatestApplicationIcon"}, //17.0.0+
{3001, nullptr, "RequestDownloadIdbeLatestIconFile"}, //17.0.0+
};
// clang-format on

View File

@@ -134,7 +134,26 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{406, nullptr, "GetApplicationControlProperty"},
{407, nullptr, "ListApplicationTitle"},
{408, nullptr, "ListApplicationIcon"},
{411, nullptr, "Unknown411"}, //19.0.0+
{412, nullptr, "Unknown412"}, //19.0.0+
{413, nullptr, "Unknown413"}, //19.0.0+
{414, nullptr, "Unknown414"}, //19.0.0+
{415, nullptr, "Unknown415"}, //19.0.0+
{416, nullptr, "Unknown416"}, //19.0.0+
{417, nullptr, "InvalidateAllApplicationControlCacheOfTheStage"}, //19.0.0+
{418, nullptr, "InvalidateApplicationControlCacheOfTheStage"}, //19.0.0+
{419, D<&IApplicationManagerInterface::RequestDownloadApplicationControlDataInBackground>, "RequestDownloadApplicationControlDataInBackground"},
{420, nullptr, "CloneApplicationControlDataCacheForDebug"},
{421, nullptr, "Unknown421"}, //20.0.0+
{422, nullptr, "Unknown422"}, //20.0.0+
{423, nullptr, "Unknown423"}, //20.0.0+
{424, nullptr, "Unknown424"}, //20.0.0+
{425, nullptr, "Unknown425"}, //20.0.0+
{426, nullptr, "Unknown426"}, //20.0.0+
{427, nullptr, "Unknown427"}, //20.0.0+
{428, nullptr, "Unknown428"}, //21.0.0+
{429, nullptr, "Unknown429"}, //21.0.0+
{430, nullptr, "Unknown430"}, //21.0.0+
{502, nullptr, "RequestCheckGameCardRegistration"},
{503, nullptr, "RequestGameCardRegistrationGoldPoint"},
{504, nullptr, "RequestRegisterGameCard"},
@@ -146,6 +165,13 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{510, nullptr, "GetGameCardPlatformRegion"},
{511, D<&IApplicationManagerInterface::GetGameCardWakenReadyEvent>, "GetGameCardWakenReadyEvent"},
{512, D<&IApplicationManagerInterface::IsGameCardApplicationRunning>, "IsGameCardApplicationRunning"},
{513, nullptr, "Unknown513"}, //20.0.0+
{514, nullptr, "Unknown514"}, //20.0.0+
{515, nullptr, "Unknown515"}, //20.0.0+
{516, nullptr, "Unknown516"}, //21.0.0+
{517, nullptr, "Unknown517"}, //21.0.0+
{518, nullptr, "Unknown518"}, //21.0.0+
{519, nullptr, "Unknown519"}, //21.0.0+
{600, nullptr, "CountApplicationContentMeta"},
{601, nullptr, "ListApplicationContentMetaStatus"},
{602, nullptr, "ListAvailableAddOnContent"},
@@ -183,6 +209,22 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{914, nullptr, "HideApplicationRecord"},
{915, nullptr, "ShowApplicationRecord"},
{916, nullptr, "IsApplicationAutoDeleteDisabled"},
{916, nullptr, "Unknown916"}, //20.0.0+
{917, nullptr, "Unknown917"}, //20.0.0+
{918, nullptr, "Unknown918"}, //20.0.0+
{919, nullptr, "Unknown919"}, //20.0.0+
{920, nullptr, "Unknown920"}, //20.0.0+
{921, nullptr, "Unknown921"}, //20.0.0+
{922, nullptr, "Unknown922"}, //20.0.0+
{923, nullptr, "Unknown923"}, //20.0.0+
{928, nullptr, "Unknown928"}, //20.0.0+
{929, nullptr, "Unknown929"}, //20.0.0+
{930, nullptr, "Unknown930"}, //20.0.0+
{931, nullptr, "Unknown931"}, //20.0.0+
{933, nullptr, "Unknown933"}, //20.0.0+
{934, nullptr, "Unknown934"}, //20.0.0+
{935, nullptr, "Unknown935"}, //20.0.0+
{936, nullptr, "Unknown936"}, //20.0.0+
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
{1001, nullptr, "CorruptApplicationForDebug"},
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
@@ -211,6 +253,11 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{1504, nullptr, "InsertSdCard"},
{1505, nullptr, "RemoveSdCard"},
{1506, nullptr, "GetSdCardStartupStatus"},
{1508, nullptr, "Unknown1508"}, //20.0.0+
{1509, nullptr, "Unknown1509"}, //20.0.0+
{1510, nullptr, "Unknown1510"}, //20.0.0+
{1511, nullptr, "Unknown1511"}, //20.0.0+
{1512, nullptr, "Unknown1512"}, //20.0.0+
{1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
{1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
{1700, nullptr, "ListApplicationDownloadingContentMeta"},
@@ -247,8 +294,11 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{2016, nullptr, "ListNotCommittedContentMeta"},
{2017, nullptr, "CreateDownloadTask"},
{2018, nullptr, "GetApplicationDeliveryInfoHash"},
{2019, nullptr, "Unknown2019"}, //20.0.0+
{2050, D<&IApplicationManagerInterface::GetApplicationRightsOnClient>, "GetApplicationRightsOnClient"},
{2051, nullptr, "InvalidateRightsIdCache"},
{2052, nullptr, "Unknown2052"}, //20.0.0+
{2053, nullptr, "Unknown2053"}, //20.0.0+
{2100, D<&IApplicationManagerInterface::GetApplicationTerminateResult>, "GetApplicationTerminateResult"},
{2101, nullptr, "GetRawApplicationTerminateResult"},
{2150, nullptr, "CreateRightsEnvironment"},
@@ -265,6 +315,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
{2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
{2183, nullptr, "Unknown2183"}, //20.1.0+
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
{2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
@@ -281,6 +332,16 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{2357, nullptr, "EnableMultiCoreDownload"},
{2358, nullptr, "DisableMultiCoreDownload"},
{2359, nullptr, "IsMultiCoreDownloadEnabled"},
{2360, nullptr, "GetApplicationDownloadTaskCount"}, //19.0.0+
{2361, nullptr, "GetMaxApplicationDownloadTaskCount"}, //19.0.0+
{2362, nullptr, "Unknown2362"}, //20.0.0+
{2363, nullptr, "Unknown2363"}, //20.0.0+
{2364, nullptr, "Unknown2364"}, //20.0.0+
{2365, nullptr, "Unknown2365"}, //20.0.0+
{2366, nullptr, "Unknown2366"}, //20.0.0+
{2367, nullptr, "Unknown2367"}, //20.0.0+
{2368, nullptr, "Unknown2368"}, //20.0.0+
{2369, nullptr, "Unknown2369"}, //21.0.0+
{2400, nullptr, "GetPromotionInfo"},
{2401, nullptr, "CountPromotionInfo"},
{2402, nullptr, "ListPromotionInfo"},
@@ -299,6 +360,9 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{2520, nullptr, "IsQualificationTransitionSupportedByProcessId"},
{2521, nullptr, "GetRightsUserChangedEvent"},
{2522, nullptr, "IsRomRedirectionAvailable"},
{2523, nullptr, "GetProgramId"}, //17.0.0+
{2524, nullptr, "Unknown2524"}, //19.0.0+
{2525, nullptr, "Unknown2525"}, //20.0.0+
{2800, nullptr, "GetApplicationIdOfPreomia"},
{3000, nullptr, "RegisterDeviceLockKey"},
{3001, nullptr, "UnregisterDeviceLockKey"},
@@ -316,11 +380,99 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{3013, nullptr, "IsGameCardEnabled"},
{3014, nullptr, "IsLocalContentShareEnabled"},
{3050, nullptr, "ListAssignELicenseTaskResult"},
{4022, D<&IApplicationManagerInterface::Unknown4022>, "Unknown4022"},
{4023, D<&IApplicationManagerInterface::Unknown4023>, "Unknown4023"},
{4088, D<&IApplicationManagerInterface::Unknown4022>, "Unknown4088"},
{4053, D<&IApplicationManagerInterface::Unknown4053>, "Unknown4053"},
{9999, nullptr, "GetApplicationCertificate"},
{3104, nullptr, "GetApplicationNintendoLogo"}, //18.0.0+
{3105, nullptr, "GetApplicationStartupMovie"}, //18.0.0+
{4000, nullptr, "Unknown4000"}, //20.0.0+
{4004, nullptr, "Unknown4004"}, //20.0.0+
{4006, nullptr, "Unknown4006"}, //20.0.0+
{4007, nullptr, "Unknown4007"}, //20.0.0+
{4008, nullptr, "Unknown4008"}, //20.0.0+
{4009, nullptr, "Unknown4009"}, //20.0.0+
{4010, nullptr, "Unknown4010"}, //20.0.0+
{4011, nullptr, "Unknown4011"}, //20.0.0+
{4012, nullptr, "Unknown4012"}, //20.0.0+
{4013, nullptr, "Unknown4013"}, //20.0.0+
{4015, nullptr, "Unknown4015"}, //20.0.0+
{4017, nullptr, "Unknown4017"}, //20.0.0+
{4019, nullptr, "Unknown4019"}, //20.0.0+
{4020, nullptr, "Unknown4020"}, //20.0.0+
{4021, nullptr, "Unknown4021"}, //20.0.0+
{4022, D<&IApplicationManagerInterface::Unknown4022>, "Unknown4022"}, //20.0.0+
{4023, D<&IApplicationManagerInterface::Unknown4023>, "Unknown4023"}, //20.0.0+
{4024, nullptr, "Unknown4024"}, //20.0.0+
{4025, nullptr, "Unknown4025"}, //20.0.0+
{4026, nullptr, "Unknown4026"}, //20.0.0+
{4027, nullptr, "Unknown4027"}, //20.0.0+
{4028, nullptr, "Unknown4028"}, //20.0.0+
{4029, nullptr, "Unknown4029"}, //20.0.0+
{4030, nullptr, "Unknown4030"}, //20.0.0+
{4031, nullptr, "Unknown4031"}, //20.0.0+
{4032, nullptr, "Unknown4032"}, //20.0.0+
{4033, nullptr, "Unknown4033"}, //20.0.0+
{4034, nullptr, "Unknown4034"}, //20.0.0+
{4035, nullptr, "Unknown4035"}, //20.0.0+
{4037, nullptr, "Unknown4037"}, //20.0.0+
{4038, nullptr, "Unknown4038"}, //20.0.0+
{4039, nullptr, "Unknown4039"}, //20.0.0+
{4040, nullptr, "Unknown4040"}, //20.0.0+
{4041, nullptr, "Unknown4041"}, //20.0.0+
{4042, nullptr, "Unknown4042"}, //20.0.0+
{4043, nullptr, "Unknown4043"}, //20.0.0+
{4044, nullptr, "Unknown4044"}, //20.0.0+
{4045, nullptr, "Unknown4045"}, //20.0.0+
{4046, nullptr, "Unknown4046"}, //20.0.0+
{4049, nullptr, "Unknown4049"}, //20.0.0+
{4050, nullptr, "Unknown4050"}, //20.0.0+
{4051, nullptr, "Unknown4051"}, //20.0.0+
{4052, nullptr, "Unknown4052"}, //20.0.0+
{4053, D<&IApplicationManagerInterface::Unknown4053>, "Unknown4053"}, //20.0.0+
{4054, nullptr, "Unknown4054"}, //20.0.0+
{4055, nullptr, "Unknown4055"}, //20.0.0+
{4056, nullptr, "Unknown4056"}, //20.0.0+
{4057, nullptr, "Unknown4057"}, //20.0.0+
{4058, nullptr, "Unknown4058"}, //20.0.0+
{4059, nullptr, "Unknown4059"}, //20.0.0+
{4060, nullptr, "Unknown4060"}, //20.0.0+
{4061, nullptr, "Unknown4061"}, //20.0.0+
{4062, nullptr, "Unknown4062"}, //20.0.0+
{4063, nullptr, "Unknown4063"}, //20.0.0+
{4064, nullptr, "Unknown4064"}, //20.0.0+
{4065, nullptr, "Unknown4065"}, //20.0.0+
{4066, nullptr, "Unknown4066"}, //20.0.0+
{4067, nullptr, "Unknown4067"}, //20.0.0+
{4068, nullptr, "Unknown4068"}, //20.0.0+
{4069, nullptr, "Unknown4069"}, //20.0.0+
{4070, nullptr, "Unknown4070"}, //20.0.0+
{4071, nullptr, "Unknown4071"}, //20.0.0+
{4072, nullptr, "Unknown4072"}, //20.0.0+
{4073, nullptr, "Unknown4073"}, //20.0.0+
{4074, nullptr, "Unknown4074"}, //20.0.0+
{4075, nullptr, "Unknown4075"}, //20.0.0+
{4076, nullptr, "Unknown4076"}, //20.0.0+
{4077, nullptr, "Unknown4077"}, //20.0.0+
{4078, nullptr, "Unknown4078"}, //20.0.0+
{4079, nullptr, "Unknown4079"}, //20.0.0+
{4080, nullptr, "Unknown4080"}, //20.0.0+
{4081, nullptr, "Unknown4081"}, //20.0.0+
{4083, nullptr, "Unknown4083"}, //20.0.0+
{4084, nullptr, "Unknown4084"}, //20.0.0+
{4085, nullptr, "Unknown4085"}, //20.0.0+
{4086, nullptr, "Unknown4086"}, //20.0.0+
{4087, nullptr, "Unknown4087"}, //20.0.0+
{4088, D<&IApplicationManagerInterface::Unknown4022>, "Unknown4088"}, //20.0.0+
{4089, nullptr, "Unknown4089"}, //20.0.0+
{4090, nullptr, "Unknown4090"}, //20.0.0+
{4091, nullptr, "Unknown4091"}, //20.0.0+
{4092, nullptr, "Unknown4092"}, //20.0.0+
{4093, nullptr, "Unknown4093"}, //20.0.0+
{4094, nullptr, "Unknown4094"}, //20.0.0+
{4095, nullptr, "Unknown4095"}, //20.0.0+
{4096, nullptr, "Unknown4096"}, //20.0.0+
{4097, nullptr, "Unknown4097"}, //20.0.0+
{4099, nullptr, "Unknown4099"}, //21.0.0+
{5000, nullptr, "Unknown5000"}, //18.0.0+
{5001, nullptr, "Unknown5001"}, //18.0.0+
{9999, nullptr, "GetApplicationCertificate"}, //10.0.0-10.2.0
};
// clang-format on

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -22,6 +25,7 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_)
{601, nullptr, "ListApplicationContentMetaStatus"},
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
{607, nullptr, "IsAnyApplicationRunning"},
{608, nullptr, "Unknown608"}, //21.0.0+
};
// clang-format on

View File

@@ -37,7 +37,11 @@ IDynamicRightsInterface::IDynamicRightsInterface(Core::System& system_)
{23, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
{24, nullptr, "NotifyLimitedApplicationLicenseUpgradableEventForDebug"},
{25, nullptr, "RequestProceedDynamicRightsState"},
{26, D<&IDynamicRightsInterface::HasAccountRestrictedRightsInRunningApplications>, "HasAccountRestrictedRightsInRunningApplications"}
{26, D<&IDynamicRightsInterface::HasAccountRestrictedRightsInRunningApplications>, "HasAccountRestrictedRightsInRunningApplications"},
{27, nullptr, "Unknown27"}, //20.0.0+
{28, nullptr, "Unknown28"}, //20.0.0+
{29, nullptr, "Unknown29"}, //21.0.0+
{30, nullptr, "Unknown30"}, //21.0.0+
};
// clang-format on

View File

@@ -16,64 +16,67 @@ IOlscServiceForSystemService::IOlscServiceForSystemService(Core::System& system_
: ServiceFramework{system_, "olsc:s"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&IOlscServiceForSystemService::OpenTransferTaskListController>, "OpenTransferTaskListController"},
{1, D<&IOlscServiceForSystemService::OpenRemoteStorageController>, "OpenRemoteStorageController"},
{2, D<&IOlscServiceForSystemService::OpenDaemonController>, "OpenDaemonController"},
{10, nullptr, "Unknown10"},
{11, nullptr, "Unknown11"},
{12, nullptr, "Unknown12"},
{13, nullptr, "Unknown13"},
{0, D<&IOlscServiceForSystemService::GetTransferTaskListController>, "GetTransferTaskListController"},
{1, D<&IOlscServiceForSystemService::GetRemoteStorageController>, "GetRemoteStorageController"},
{2, D<&IOlscServiceForSystemService::GetDaemonController>, "GetDaemonController"},
{10, nullptr, "PrepareDeleteUserProperty"},
{11, nullptr, "DeleteUserSaveDataProperty"},
{12, nullptr, "InvalidateMountCache"},
{13, nullptr, "DeleteDeviceSaveDataProperty"},
{100, nullptr, "ListLastTransferTaskErrorInfo"},
{101, nullptr, "GetLastErrorInfoCount"},
{102, nullptr, "RemoveLastErrorInfoOld"},
{103, nullptr, "GetLastErrorInfo"},
{104, nullptr, "GetLastErrorEventHolder"},
{105, nullptr, "GetLastTransferTaskErrorInfo"},
{200, D<&IOlscServiceForSystemService::GetDataTransferPolicyInfo>, "GetDataTransferPolicyInfo"},
{201, nullptr, "RemoveDataTransferPolicyInfo"},
{202, nullptr, "UpdateDataTransferPolicyOld"},
{203, nullptr, "UpdateDataTransferPolicy"},
{204, nullptr, "CleanupDataTransferPolicyInfo"},
{205, nullptr, "RequestDataTransferPolicy"},
{300, nullptr, "GetAutoTransferSeriesInfo"},
{301, nullptr, "UpdateAutoTransferSeriesInfo"},
{400, nullptr, "CleanupSaveDataArchiveInfoType1"},
{900, nullptr, "CleanupTransferTask"},
{902, nullptr, "CleanupSeriesInfoType0"},
{903, nullptr, "CleanupSaveDataArchiveInfoType0"},
{904, nullptr, "CleanupApplicationAutoTransferSetting"},
{905, nullptr, "CleanupErrorHistory"},
{906, nullptr, "SetLastError"},
{907, nullptr, "AddSaveDataArchiveInfoType0"},
{908, nullptr, "RemoveSeriesInfoType0"},
{909, nullptr, "GetSeriesInfoType0"},
{910, nullptr, "RemoveLastErrorInfo"},
{911, nullptr, "CleanupSeriesInfoType1"},
{912, nullptr, "RemoveSeriesInfoType1"},
{913, nullptr, "GetSeriesInfoType1"},
{200, D<&IOlscServiceForSystemService::GetDataTransferPolicy>, "GetDataTransferPolicy"},
{201, nullptr, "DeleteDataTransferPolicyCache"},
{202, nullptr, "Unknown202"},
{203, nullptr, "RequestUpdateDataTransferPolicyCacheAsync"},
{204, nullptr, "ClearDataTransferPolicyCache"},
{205, nullptr, "RequestGetDataTransferPolicyAsync"},
{206, nullptr, "Unknown206"}, //21.0.0+
{300, nullptr, "GetUserSaveDataProperty"},
{301, nullptr, "SetUserSaveDataProperty"},
{302, nullptr, "Unknown302"}, //21.0.0+
{400, nullptr, "CleanupSaveDataBackupContextForSpecificApplications"},
{900, nullptr, "DeleteAllTransferTask"},
{902, nullptr, "DeleteAllSeriesInfo"},
{903, nullptr, "DeleteAllSdaInfoCache"},
{904, nullptr, "DeleteAllApplicationSetting"},
{905, nullptr, "DeleteAllTransferTaskErrorInfo"},
{906, nullptr, "RegisterTransferTaskErrorInfo"},
{907, nullptr, "AddSaveDataArchiveInfoCache"},
{908, nullptr, "DeleteSeriesInfo"},
{909, nullptr, "GetSeriesInfo"},
{910, nullptr, "RemoveTransferTaskErrorInfo"},
{911, nullptr, "DeleteAllSeriesInfoForSaveDataBackup"},
{912, nullptr, "DeleteSeriesInfoForSaveDataBackup"},
{913, nullptr, "GetSeriesInfoForSaveDataBackup"},
{914, nullptr, "Unknown914"}, //20.2.0+
{1000, nullptr, "UpdateIssueOld"},
{1010, nullptr, "Unknown1010"},
{1011, nullptr, "ListIssueInfoOld"},
{1012, nullptr, "GetIssueOld"},
{1013, nullptr, "GetIssue2Old"},
{1014, nullptr, "GetIssue3Old"},
{1020, nullptr, "RepairIssueOld"},
{1021, nullptr, "RepairIssueWithUserIdOld"},
{1022, nullptr, "RepairIssue2Old"},
{1023, nullptr, "RepairIssue3Old"},
{1011, nullptr, "Unknown1011"},
{1012, nullptr, "Unknown1012"},
{1013, nullptr, "Unkown1013"},
{1014, nullptr, "Unknown1014"},
{1020, nullptr, "Unknown1020"},
{1021, nullptr, "Unknown1021"},
{1022, nullptr, "Unknown1022"},
{1023, nullptr, "Unknown1023"},
{1024, nullptr, "Unknown1024"},
{1100, nullptr, "UpdateIssue"},
{1110, nullptr, "Unknown1110"},
{1111, nullptr, "ListIssueInfo"},
{1112, nullptr, "GetIssue"},
{1113, nullptr, "GetIssue2"},
{1114, nullptr, "GetIssue3"},
{1120, nullptr, "RepairIssue"},
{1121, nullptr, "RepairIssueWithUserId"},
{1122, nullptr, "RepairIssue2"},
{1123, nullptr, "RepairIssue3"},
{1124, nullptr, "Unknown1124"},
{10000, D<&IOlscServiceForSystemService::CloneService>, "CloneService"},
{1100, nullptr, "RepairUpdateIssueInfoCacheAync"},
{1110, nullptr, "RepairGetIssueInfo"},
{1111, nullptr, "RepairListIssueInfo"},
{1112, nullptr, "RepairListOperationPermissionInfo"},
{1113, nullptr, "RepairListDataInfoForRepairedSaveDataDownload"},
{1114, nullptr, "RepairListDataInfoForOriginalSaveDataDownload"},
{1120, nullptr, "RepairUploadSaveDataAsync"},
{1121, nullptr, "RepairUploadSaveDataAsync1"},
{1122, nullptr, "RepairDownloadRepairedSaveDataAsync"},
{1123, nullptr, "RepairDownloadOriginalSaveDataAsync"},
{1124, nullptr, "RepairGetOperationProgressInfo"},
{10000, D<&IOlscServiceForSystemService::GetOlscServiceForSystemService>, "GetOlscServiceForSystemService"},
};
// clang-format on
@@ -82,38 +85,38 @@ IOlscServiceForSystemService::IOlscServiceForSystemService(Core::System& system_
IOlscServiceForSystemService::~IOlscServiceForSystemService() = default;
Result IOlscServiceForSystemService::OpenTransferTaskListController(
Result IOlscServiceForSystemService::GetTransferTaskListController(
Out<SharedPointer<ITransferTaskListController>> out_interface) {
LOG_INFO(Service_OLSC, "called");
*out_interface = std::make_shared<ITransferTaskListController>(system);
R_SUCCEED();
}
Result IOlscServiceForSystemService::OpenRemoteStorageController(
Result IOlscServiceForSystemService::GetRemoteStorageController(
Out<SharedPointer<IRemoteStorageController>> out_interface) {
LOG_INFO(Service_OLSC, "called");
*out_interface = std::make_shared<IRemoteStorageController>(system);
R_SUCCEED();
}
Result IOlscServiceForSystemService::OpenDaemonController(
Result IOlscServiceForSystemService::GetDaemonController(
Out<SharedPointer<IDaemonController>> out_interface) {
LOG_INFO(Service_OLSC, "called");
*out_interface = std::make_shared<IDaemonController>(system);
R_SUCCEED();
}
Result IOlscServiceForSystemService::GetDataTransferPolicyInfo(
Out<DataTransferPolicy> out_policy_info, u64 application_id) {
Result IOlscServiceForSystemService::GetDataTransferPolicy(
Out<DataTransferPolicy> out_policy, u64 application_id) {
LOG_WARNING(Service_OLSC, "(STUBBED) called");
DataTransferPolicy policy{};
policy.upload_policy = 0;
policy.download_policy = 0;
*out_policy_info = policy;
*out_policy = policy;
R_SUCCEED();
}
Result IOlscServiceForSystemService::CloneService(
Result IOlscServiceForSystemService::GetOlscServiceForSystemService(
Out<SharedPointer<IOlscServiceForSystemService>> out_interface) {
LOG_INFO(Service_OLSC, "called");
*out_interface = std::static_pointer_cast<IOlscServiceForSystemService>(shared_from_this());

View File

@@ -24,12 +24,12 @@ public:
~IOlscServiceForSystemService() override;
private:
Result OpenTransferTaskListController(
Result GetTransferTaskListController(
Out<SharedPointer<ITransferTaskListController>> out_interface);
Result OpenRemoteStorageController(Out<SharedPointer<IRemoteStorageController>> out_interface);
Result OpenDaemonController(Out<SharedPointer<IDaemonController>> out_interface);
Result GetDataTransferPolicyInfo(Out<DataTransferPolicy> out_policy_info, u64 application_id);
Result CloneService(Out<SharedPointer<IOlscServiceForSystemService>> out_interface);
Result GetRemoteStorageController(Out<SharedPointer<IRemoteStorageController>> out_interface);
Result GetDaemonController(Out<SharedPointer<IDaemonController>> out_interface);
Result GetDataTransferPolicy(Out<DataTransferPolicy> out_policy, u64 application_id);
Result GetOlscServiceForSystemService(Out<SharedPointer<IOlscServiceForSystemService>> out_interface);
};
} // namespace Service::OLSC

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -11,32 +14,37 @@ ITransferTaskListController::ITransferTaskListController(Core::System& system_)
: ServiceFramework{system_, "ITransferTaskListController"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown0"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Unknown3"},
{4, nullptr, "Unknown4"},
{5, D<&ITransferTaskListController::GetNativeHandleHolder>, "GetNativeHandleHolder"},
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
{8, nullptr, "GetRemoteStorageController"},
{9, D<&ITransferTaskListController::GetNativeHandleHolder>, "GetNativeHandleHolder2"},
{10, nullptr, "Unknown10"},
{11, nullptr, "Unknown11"},
{12, nullptr, "Unknown12"},
{13, nullptr, "Unknown13"},
{14, nullptr, "Unknown14"},
{15, nullptr, "Unknown15"},
{16, nullptr, "Unknown16"},
{17, nullptr, "Unknown17"},
{18, nullptr, "Unknown18"},
{19, nullptr, "Unknown19"},
{20, nullptr, "Unknown20"},
{21, nullptr, "Unknown21"},
{22, nullptr, "Unknown22"},
{23, nullptr, "Unknown23"},
{24, nullptr, "Unknown24"},
{25, nullptr, "Unknown25"},
{0, nullptr, "GetTransferTaskCountForOcean"},
{1, nullptr, "GetTransferTaskInfoForOcean"},
{2, nullptr, "ListTransferTaskInfoForOcean"},
{3, nullptr, "DeleteTransferTaskForOcean"},
{4, nullptr, "RaiseTransferTaskPriorityForOcean"},
{5, D<&ITransferTaskListController::GetTransferTaskEndEventNativeHandleHolder>, "GetTransferTaskEndEventNativeHandleHolder"},
{6, nullptr, "GetTransferTaskProgressForOcean"},
{7, nullptr, "GetTransferTaskLastResultForOcean"},
{8, nullptr, "StopNextTransferTaskExecution"},
{9, D<&ITransferTaskListController::GetTransferTaskStartEventNativeHandleHolder>, "GetTransferTaskStartEventNativeHandleHolder"},
{10, nullptr, "SuspendTransferTaskForOcean"},
{11, nullptr, "GetCurrentTransferTaskInfoForOcean"},
{12, nullptr, "FindTransferTaskInfoForOcean"},
{13, nullptr, "CancelCurrentRepairTransferTask"},
{14, nullptr, "GetRepairTransferTaskProgress"},
{15, nullptr, "EnsureExecutableForRepairTransferTask"},
{16, nullptr, "GetTransferTaskCount"},
{17, nullptr, "GetTransferTaskInfo"},
{18, nullptr, "ListTransferTaskInfo"},
{19, nullptr, "DeleteTransferTask"},
{20, nullptr, "RaiseTransferTaskPriority"},
{21, nullptr, "GetTransferTaskProgress"},
{22, nullptr, "GetTransferTaskLastResult"},
{23, nullptr, "SuspendTransferTask"},
{24, nullptr, "GetCurrentTransferTaskInfo"},
{25, nullptr, "Unknown25"}, //20.1.0+
{26, nullptr, "Unknown26"}, //20.1.0+
{27, nullptr, "Unknown27"}, //20.1.0+
{28, nullptr, "Unknown28"}, //20.1.0+
{29, nullptr, "Unknown29"}, //20.1.0+
{30, nullptr, "Unknown30"}, //20.1.0+
};
// clang-format on
@@ -45,7 +53,14 @@ ITransferTaskListController::ITransferTaskListController(Core::System& system_)
ITransferTaskListController::~ITransferTaskListController() = default;
Result ITransferTaskListController::GetNativeHandleHolder(
Result ITransferTaskListController::GetTransferTaskEndEventNativeHandleHolder(
Out<SharedPointer<INativeHandleHolder>> out_holder) {
LOG_WARNING(Service_OLSC, "(STUBBED) called");
*out_holder = std::make_shared<INativeHandleHolder>(system);
R_SUCCEED();
}
Result ITransferTaskListController::GetTransferTaskStartEventNativeHandleHolder(
Out<SharedPointer<INativeHandleHolder>> out_holder) {
LOG_WARNING(Service_OLSC, "(STUBBED) called");
*out_holder = std::make_shared<INativeHandleHolder>(system);

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -14,7 +17,8 @@ public:
~ITransferTaskListController() override;
private:
Result GetNativeHandleHolder(Out<SharedPointer<INativeHandleHolder>> out_holder);
Result GetTransferTaskEndEventNativeHandleHolder(Out<SharedPointer<INativeHandleHolder>> out_holder);
Result GetTransferTaskStartEventNativeHandleHolder(Out<SharedPointer<INativeHandleHolder>> out_holder);
};
} // namespace Service::OLSC

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -34,10 +37,10 @@ public:
{17, nullptr, "AcquireIrq"},
{18, nullptr, "ReleaseIrq"},
{19, nullptr, "SetIrqEnable"},
{20, nullptr, "SetAspmEnable"},
{21, nullptr, "SetResetUponResumeEnable"},
{22, nullptr, "ResetFunction"},
{23, nullptr, "Unknown23"},
{20, nullptr, "GetIrqEvent"},
{21, nullptr, "SetAspmEnable"},
{22, nullptr, "SetResetUponResumeEnable"},
{23, nullptr, "ResetFunction"},
};
// clang-format on

View File

@@ -39,6 +39,9 @@ IParentalControlService::IParentalControlService(Core::System& system_, Capabili
{1017, D<&IParentalControlService::EndFreeCommunication>, "EndFreeCommunication"},
{1018, D<&IParentalControlService::IsFreeCommunicationAvailable>, "IsFreeCommunicationAvailable"},
{1019, D<&IParentalControlService::ConfirmLaunchApplicationPermission>, "ConfirmLaunchApplicationPermission"},
{1020, nullptr, "ConfirmLaunchSharedApplicationPermission"}, //20.0.0+
{1021, nullptr, "TryBeginFreeCommunicationForStreamPlay"}, //21.0.0+
{1022, nullptr, "EndFreeCommunicationForStreamPlay"}, //21.0.0+
{1031, D<&IParentalControlService::IsRestrictionEnabled>, "IsRestrictionEnabled"},
{1032, D<&IParentalControlService::GetSafetyLevel>, "GetSafetyLevel"},
{1033, nullptr, "SetSafetyLevel"},
@@ -56,6 +59,8 @@ IParentalControlService::IParentalControlService(Core::System& system_, Capabili
{1047, nullptr, "NotifyApplicationDownloadStarted"},
{1048, nullptr, "NotifyNetworkProfileCreated"},
{1049, nullptr, "ResetFreeCommunicationApplicationList"},
{1050, nullptr, "AddToFreeCommunicationApplicationList"}, //20.0.0+
{1051, nullptr, "NotifyApplicationDownloadStarted"}, //20.0.0+
{1061, D<&IParentalControlService::ConfirmStereoVisionRestrictionConfigurable>, "ConfirmStereoVisionRestrictionConfigurable"},
{1062, D<&IParentalControlService::GetStereoVisionRestriction>, "GetStereoVisionRestriction"},
{1063, D<&IParentalControlService::SetStereoVisionRestriction>, "SetStereoVisionRestriction"},
@@ -126,8 +131,17 @@ IParentalControlService::IParentalControlService(Core::System& system_, Capabili
{2013, nullptr, "SynchronizeParentalControlSettingsAsync"},
{2014, nullptr, "FinishSynchronizeParentalControlSettings"},
{2015, nullptr, "FinishSynchronizeParentalControlSettingsWithLastUpdated"},
{2016, nullptr, "RequestUpdateExemptionListAsync"},
{145601, D<&IParentalControlService::GetPlayTimerSettings>, "GetPlayTimerSettings"} // 18.0.0+
{2016, nullptr, "RequestUpdateExemptionListAsync"}, //5.0.0+
{145601, D<&IParentalControlService::GetPlayTimerSettings>, "GetPlayTimerSettings"}, // 18.0.0+
{2017, nullptr, "AuthorizePairingAsync"}, //19.0.0+
{2019, nullptr, "RequestUpdateDeviceUsersBackground"}, //19.0.0+
{2021, nullptr, "RequestCopyPairingAsync"}, //20.0.0+
{2022, nullptr, "FinishRequestCopyPairing"}, //20.0.0+
{2023, nullptr, "IsFromPairingActiveDevice"}, //20.0.0+
{2024, nullptr, "RollbackCopyPairing"}, //21.0.0+
{3001, nullptr, "GetErrorContextChangedEvent"}, //20.0.0+
{145601, D<&IParentalControlService::GetPlayTimerSettings>, "GetPlayTimerSettings"}, // 18.0.0+
{195101, D<&IParentalControlService::SetPlayTimerSettings>, "SetPlayTimerSettingsForDebug"}, //18.0.0+
};
// clang-format on
RegisterHandlers(functions);
@@ -393,16 +407,21 @@ Result IParentalControlService::IsRestrictedByPlayTimer(Out<bool> out_is_restric
}
Result IParentalControlService::GetPlayTimerSettingsOld(
Out<PlayTimerSettings> out_play_timer_settings) {
Out<PlayTimerSettingsOld> out_play_timer_settings) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");
*out_play_timer_settings = {};
R_SUCCEED();
}
Result IParentalControlService::GetPlayTimerSettings(
Out<PlayTimerSettings> out_play_timer_settings) {
Result IParentalControlService::GetPlayTimerSettings(Out<PlayTimerSettings> out_play_timer_settings) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");
*out_play_timer_settings = {};
*out_play_timer_settings = raw_play_timer_settings;
R_SUCCEED();
}
Result IParentalControlService::SetPlayTimerSettings(PlayTimerSettings play_timer_settings) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");
raw_play_timer_settings = play_timer_settings;
R_SUCCEED();
}

View File

@@ -51,7 +51,7 @@ private:
Result IsPlayTimerEnabled(Out<bool> out_is_play_timer_enabled);
Result GetPlayTimerRemainingTime(Out<s32> out_remaining_time);
Result IsRestrictedByPlayTimer(Out<bool> out_is_restricted_by_play_timer);
Result GetPlayTimerSettingsOld(Out<PlayTimerSettings> out_play_timer_settings);
Result GetPlayTimerSettingsOld(Out<PlayTimerSettingsOld> out_play_timer_settings);
Result GetPlayTimerEventToRequestSuspension(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result IsPlayTimerAlarmDisabled(Out<bool> out_play_timer_alarm_disabled);
Result GetPlayTimerRemainingTimeDisplayInfo();
@@ -60,6 +60,7 @@ private:
Result SetStereoVisionRestriction(bool stereo_vision_restriction);
Result ResetConfirmedStereoVisionPermission();
Result GetPlayTimerSettings(Out<PlayTimerSettings> out_play_timer_settings);
Result SetPlayTimerSettings(PlayTimerSettings out_play_timer_settings);
struct States {
u64 current_tid{};
@@ -83,6 +84,8 @@ private:
RestrictionSettings restriction_settings{};
std::array<char, 8> pin_code{};
Capability capability{};
// TODO: this is RAW as fuck
PlayTimerSettings raw_play_timer_settings{};
KernelHelpers::ServiceContext service_context;
Event synchronization_event;

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -34,10 +37,16 @@ struct RestrictionSettings {
};
static_assert(sizeof(RestrictionSettings) == 0x3, "RestrictionSettings has incorrect size.");
// This is nn::pctl::PlayTimerSettings
struct PlayTimerSettings {
// This is nn::pctl::PlayTimerSettingsOld
struct PlayTimerSettingsOld {
std::array<u32, 13> settings;
};
static_assert(sizeof(PlayTimerSettings) == 0x34, "PlayTimerSettings has incorrect size.");
static_assert(sizeof(PlayTimerSettingsOld) == 0x34, "PlayTimerSettingsOld has incorrect size.");
// This is nn::pctl::PlayTimerSettings
struct PlayTimerSettings {
std::array<u32, 17> settings; //21.0.0+ now takes 0x44
};
static_assert(sizeof(PlayTimerSettings) == 0x44, "PlayTimerSettings has incorrect size.");
} // namespace Service::PCTL

View File

@@ -34,9 +34,9 @@ public:
{10300, &PlayReport::GetTransmissionStatus, "GetTransmissionStatus"},
{10400, &PlayReport::GetSystemSessionId, "GetSystemSessionId"},
{20100, &PlayReport::SaveSystemReportOld, "SaveSystemReport"},
{20101, &PlayReport::SaveSystemReportWithUserOld, "SaveSystemReportWithUser"},
{20102, &PlayReport::SaveSystemReport, "SaveSystemReport"},
{20103, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"},
{20101, &PlayReport::SaveSystemReportWithUserOld, "SaveSystemReportWithUser"},
{20102, &PlayReport::SaveSystemReport, "SaveSystemReport"},
{20103, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"},
{20200, nullptr, "SetOperationMode"},
{30100, nullptr, "ClearStorage"},
{30200, nullptr, "ClearStatistics"},

View File

@@ -18,49 +18,43 @@
namespace Service {
/**
* Creates a function string for logging, complete with the name (or header code, depending
* on what's passed in) the port name, and all the cmd_buff arguments.
*/
[[maybe_unused]] static std::string MakeFunctionString(std::string_view name,
std::string_view port_name,
const u32* cmd_buff) {
/// @brief Creates a function string for logging, complete with the name (or header code, depending
/// on what's passed in) the port name, and all the cmd_buff arguments.
[[maybe_unused]] static std::string MakeFunctionString(std::string_view name, std::string_view port_name, const u32* cmd_buff) {
// Number of params == bits 0-5 + bits 6-11
int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
std::string function_string = fmt::format("function '{}': port={}", name, port_name);
for (int i = 1; i <= num_params; ++i) {
for (int i = 1; i <= num_params; ++i)
function_string += fmt::format(", cmd_buff[{}]={:#X}", i, cmd_buff[i]);
}
return function_string;
}
ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_,
u32 max_sessions_, InvokerFn* handler_invoker_)
: SessionRequestHandler(system_.Kernel(), service_name_), system{system_},
service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {}
service_name{service_name_}, handler_invoker{handler_invoker_}, max_sessions{max_sessions_} {}
ServiceFrameworkBase::~ServiceFrameworkBase() {
// Wait for other threads to release access before destroying
const auto guard = LockService();
const auto guard = ServiceFrameworkBase::LockService();
}
void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
// Usually this array is sorted by id already, so hint to insert at the end
handlers.reserve(handlers.size() + n);
for (std::size_t i = 0; i < n; ++i) {
// Usually this array is sorted by id already, so hint to insert at the end
for (std::size_t i = 0; i < n; ++i)
handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]);
}
}
void ServiceFrameworkBase::RegisterHandlersBaseTipc(const FunctionInfoBase* functions,
std::size_t n) {
void ServiceFrameworkBase::RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n) {
// Usually this array is sorted by id already, so hint to insert at the end
handlers_tipc.reserve(handlers_tipc.size() + n);
for (std::size_t i = 0; i < n; ++i) {
// Usually this array is sorted by id already, so hint to insert at the end
for (std::size_t i = 0; i < n; ++i)
handlers_tipc.emplace_hint(handlers_tipc.cend(), functions[i].expected_header,
functions[i]);
}
}
void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
@@ -69,15 +63,12 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
std::string function_name = info == nullptr ? "<unknown>" : info->name;
fmt::memory_buffer buf;
fmt::format_to(std::back_inserter(buf), "function '{}({})': port='{}' cmd_buf={{[0]={:#X}",
ctx.GetCommand(), function_name, service_name, cmd_buf[0]);
for (int i = 1; i <= 8; ++i) {
fmt::format_to(std::back_inserter(buf), "function '{}({})': port='{}' cmd_buf={{[0]={:#X}", ctx.GetCommand(), function_name, service_name, cmd_buf[0]);
for (int i = 1; i <= 8; ++i)
fmt::format_to(std::back_inserter(buf), ", [{}]={:#X}", i, cmd_buf[i]);
}
buf.push_back('}');
system.GetReporter().SaveUnimplementedFunctionReport(ctx, ctx.GetCommand(), function_name,
service_name);
system.GetReporter().SaveUnimplementedFunctionReport(ctx, ctx.GetCommand(), function_name, service_name);
UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf));
if (Settings::values.use_auto_stub) {
LOG_WARNING(Service, "Using auto stub fallback!");
@@ -87,25 +78,20 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
}
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
auto itr = handlers.find(ctx.GetCommand());
const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second;
if (info == nullptr || info->handler_callback == nullptr) {
auto it = handlers.find(ctx.GetCommand());
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
if (info == nullptr || info->handler_callback == nullptr)
return ReportUnimplementedFunction(ctx, info);
}
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
handler_invoker(this, info->handler_callback, ctx);
}
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
boost::container::flat_map<u32, FunctionInfoBase>::iterator itr;
itr = handlers_tipc.find(ctx.GetCommand());
const FunctionInfoBase* info = itr == handlers_tipc.end() ? nullptr : &itr->second;
if (info == nullptr || info->handler_callback == nullptr) {
auto it = handlers_tipc.find(ctx.GetCommand());
FunctionInfoBase const* info = it == handlers_tipc.end() ? nullptr : &it->second;
if (info == nullptr || info->handler_callback == nullptr)
return ReportUnimplementedFunction(ctx, info);
}
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
handler_invoker(this, info->handler_callback, ctx);

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -47,25 +50,23 @@ static_assert(ServerSessionCountMax == 0x40,
class ServiceFrameworkBase : public SessionRequestHandler {
public:
/// Returns the string identifier used to connect to the service.
std::string GetServiceName() const {
[[nodiscard]] std::string_view GetServiceName() const noexcept {
return service_name;
}
/**
* Returns the maximum number of sessions that can be connected to this service at the same
* time.
*/
u32 GetMaxSessions() const {
/// @brief Returns the maximum number of sessions that can be connected to this service at the same
/// time.
u32 GetMaxSessions() const noexcept {
return max_sessions;
}
/// Invokes a service request routine using the HIPC protocol.
/// @brief Invokes a service request routine using the HIPC protocol.
void InvokeRequest(HLERequestContext& ctx);
/// Invokes a service request routine using the HIPC protocol.
/// @brief Invokes a service request routine using the HIPC protocol.
void InvokeRequestTipc(HLERequestContext& ctx);
/// Handles a synchronization request for the service.
/// @brief Handles a synchronization request for the service.
Result HandleSyncRequest(Kernel::KServerSession& session, HLERequestContext& context) override;
protected:
@@ -74,7 +75,7 @@ protected:
using HandlerFnP = void (Self::*)(HLERequestContext&);
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
[[nodiscard]] virtual std::unique_lock<std::mutex> LockService() {
[[nodiscard]] virtual std::unique_lock<std::mutex> LockService() noexcept {
return std::unique_lock{lock_service};
}
@@ -105,20 +106,19 @@ private:
void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n);
void ReportUnimplementedFunction(HLERequestContext& ctx, const FunctionInfoBase* info);
boost::container::flat_map<u32, FunctionInfoBase> handlers;
boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
std::mutex lock_service;
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
InvokerFn* handler_invoker;
/// Maximum number of concurrent sessions that this service can handle.
u32 max_sessions;
/// Flag to store if a port was already create/installed to detect multiple install attempts,
/// which is not supported.
bool service_registered = false;
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
InvokerFn* handler_invoker;
boost::container::flat_map<u32, FunctionInfoBase> handlers;
boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
std::mutex lock_service;
};
/**
@@ -142,20 +142,12 @@ protected:
// TODO(yuriks): This function could be constexpr, but clang is the only compiler that
// doesn't emit an ICE or a wrong diagnostic because of the static_cast.
/**
* Constructs a FunctionInfo for a function.
*
* @param expected_header_ request header in the command buffer which will trigger dispatch
* to this handler
* @param handler_callback_ member function in this service which will be called to handle
* the request
* @param name_ human-friendly name for the request. Used mostly for logging purposes.
*/
FunctionInfoTyped(u32 expected_header_, HandlerFnP<T> handler_callback_, const char* name_)
: FunctionInfoBase{
expected_header_,
// Type-erase member function pointer by casting it down to the base class.
static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback_), name_} {}
/// @brief Constructs a FunctionInfo for a function.
/// @param expected_header_ request header in the command buffer which will trigger dispatch to this handler
/// @param handler_callback_ member function in this service which will be called to handle the request
/// @param name_ human-friendly name for the request. Used mostly for logging purposes.
constexpr FunctionInfoTyped(u32 expected_header_, HandlerFnP<T> handler_callback_, const char* name_)
: FunctionInfoBase{expected_header_, HandlerFnP<ServiceFrameworkBase>(handler_callback_), name_} {}
};
using FunctionInfo = FunctionInfoTyped<Self>;

View File

@@ -16,7 +16,6 @@
#include "core/hle/service/btdrv/btdrv.h"
#include "core/hle/service/btm/btm.h"
#include "core/hle/service/caps/caps.h"
#include "core/hle/service/dmnt/dmnt.h"
#include "core/hle/service/erpt/erpt.h"
#include "core/hle/service/es/es.h"
#include "core/hle/service/eupld/eupld.h"
@@ -108,7 +107,6 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"btdrv", &BtDrv::LoopProcess},
{"btm", &BTM::LoopProcess},
{"capsrv", &Capture::LoopProcess},
{"dmnt", &DMNT::LoopProcess},
{"erpt", &ERPT::LoopProcess},
{"es", &ES::LoopProcess},
{"eupld", &EUPLD::LoopProcess},

View File

@@ -1082,8 +1082,7 @@ BSD::~BSD() {
}
}
std::unique_lock<std::mutex> BSD::LockService() {
// Do not lock socket IClient instances.
std::unique_lock<std::mutex> BSD::LockService() noexcept {
return {};
}

View File

@@ -188,7 +188,7 @@ private:
Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
protected:
virtual std::unique_lock<std::mutex> LockService() override;
std::unique_lock<std::mutex> LockService() noexcept override;
};
class BSDCFG final : public ServiceFramework<BSDCFG> {

View File

@@ -10,6 +10,7 @@
#include <mutex>
#include <span>
#include <thread>
#include <type_traits>
#include <vector>
#include "common/assert.h"
@@ -681,22 +682,17 @@ struct Memory::Impl {
}
}
[[nodiscard]] u8* GetPointerImpl(u64 vaddr, auto on_unmapped, auto on_rasterizer) const {
template<typename F, typename G>
[[nodiscard]] u8* GetPointerImpl(u64 vaddr, F&& on_unmapped, G&& on_rasterizer) const {
// AARCH64 masks the upper 16 bit of all memory accesses
vaddr = vaddr & 0xffffffffffffULL;
if (!AddressSpaceContains(*current_page_table, vaddr, 1)) [[unlikely]] {
on_unmapped();
return nullptr;
} else {
vaddr &= 0xffffffffffffULL;
if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] {
// Avoid adding any extra logic to this fast-path block
const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw();
if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) [[likely]] {
return reinterpret_cast<u8*>(pointer + vaddr);
} else {
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
case Common::PageType::Unmapped:
on_unmapped();
return nullptr;
case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr);
return nullptr;
@@ -707,11 +703,18 @@ struct Memory::Impl {
on_rasterizer();
return host_ptr;
}
case Common::PageType::Unmapped: [[unlikely]] {
on_unmapped();
return nullptr;
}
default:
UNREACHABLE();
}
return nullptr;
}
} else {
on_unmapped();
return nullptr;
}
}
@@ -729,172 +732,38 @@ struct Memory::Impl {
GetInteger(vaddr), []() {}, []() {});
}
/**
* Reads a particular data type out of memory at the given virtual address.
*
* @param vaddr The virtual address to read the data type from.
*
* @tparam T The data type to read out of memory. This type *must* be
* trivially copyable, otherwise the behavior of this function
* is undefined.
*
* @returns The instance of T read from the specified virtual address.
*/
/// @brief Reads a particular data type out of memory at the given virtual address.
/// @param vaddr The virtual address to read the data type from.
/// @tparam T The data type to read out of memory.
/// @returns The instance of T read from the specified virtual address.
template <typename T>
T Read(Common::ProcessAddress vaddr) {
// Fast path for aligned reads of common sizes
inline T Read(Common::ProcessAddress vaddr) noexcept requires(std::is_trivially_copyable_v<T>) {
const u64 addr = GetInteger(vaddr);
if constexpr (std::is_same_v<T, u8> || std::is_same_v<T, s8>) {
// 8-bit reads are always aligned
const u8* const ptr = GetPointerImpl(
addr,
[addr]() {
LOG_ERROR(HW_Memory, "Unmapped Read8 @ 0x{:016X}", addr);
},
[&]() { HandleRasterizerDownload(addr, sizeof(T)); });
if (ptr) {
return static_cast<T>(*ptr);
}
return 0;
} else if constexpr (std::is_same_v<T, u16_le> || std::is_same_v<T, s16_le>) {
// Check alignment for 16-bit reads
if ((addr & 1) == 0) {
const u8* const ptr = GetPointerImpl(
addr,
[addr]() {
LOG_ERROR(HW_Memory, "Unmapped Read16 @ 0x{:016X}", addr);
},
[&]() { HandleRasterizerDownload(addr, sizeof(T)); });
if (ptr) {
return static_cast<T>(*reinterpret_cast<const u16*>(ptr));
}
}
} else if constexpr (std::is_same_v<T, u32_le> || std::is_same_v<T, s32_le>) {
// Check alignment for 32-bit reads
if ((addr & 3) == 0) {
const u8* const ptr = GetPointerImpl(
addr,
[addr]() {
LOG_ERROR(HW_Memory, "Unmapped Read32 @ 0x{:016X}", addr);
},
[&]() { HandleRasterizerDownload(addr, sizeof(T)); });
if (ptr) {
return static_cast<T>(*reinterpret_cast<const u32*>(ptr));
}
}
} else if constexpr (std::is_same_v<T, u64_le> || std::is_same_v<T, s64_le>) {
// Check alignment for 64-bit reads
if ((addr & 7) == 0) {
const u8* const ptr = GetPointerImpl(
addr,
[addr]() {
LOG_ERROR(HW_Memory, "Unmapped Read64 @ 0x{:016X}", addr);
},
[&]() { HandleRasterizerDownload(addr, sizeof(T)); });
if (ptr) {
return static_cast<T>(*reinterpret_cast<const u64*>(ptr));
}
}
}
// Fall back to the general case for other types or unaligned access
T result = 0;
const u8* const ptr = GetPointerImpl(
addr,
[addr]() {
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr);
},
[&]() { HandleRasterizerDownload(addr, sizeof(T)); });
if (ptr) {
if (auto const ptr = GetPointerImpl(addr, [addr]() {
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr);
}, [&]() {
HandleRasterizerDownload(addr, sizeof(T));
}); ptr) [[likely]] {
// It may be tempting to rewrite this particular section to use "reinterpret_cast";
// afterall, it's trivially copyable so surely it can be copied ov- Alignment.
// Remember, alignment. memcpy() will deal with all the alignment extremely fast.
T result{};
std::memcpy(&result, ptr, sizeof(T));
return result;
}
return result;
return T{};
}
/**
* Writes a particular data type to memory at the given virtual address.
*
* @param vaddr The virtual address to write the data type to.
*
* @tparam T The data type to write to memory. This type *must* be
* trivially copyable, otherwise the behavior of this function
* is undefined.
*/
/// @brief Writes a particular data type to memory at the given virtual address.
/// @param vaddr The virtual address to write the data type to.
/// @tparam T The data type to write to memory.
template <typename T>
void Write(Common::ProcessAddress vaddr, const T data) {
// Fast path for aligned writes of common sizes
inline void Write(Common::ProcessAddress vaddr, const T data) noexcept requires(std::is_trivially_copyable_v<T>) {
const u64 addr = GetInteger(vaddr);
if constexpr (std::is_same_v<T, u8> || std::is_same_v<T, s8>) {
// 8-bit writes are always aligned
u8* const ptr = GetPointerImpl(
addr,
[addr, data]() {
LOG_ERROR(HW_Memory, "Unmapped Write8 @ 0x{:016X} = 0x{:02X}", addr,
static_cast<u8>(data));
},
[&]() { HandleRasterizerWrite(addr, sizeof(T)); });
if (ptr) {
*ptr = static_cast<u8>(data);
}
return;
} else if constexpr (std::is_same_v<T, u16_le> || std::is_same_v<T, s16_le>) {
// Check alignment for 16-bit writes
if ((addr & 1) == 0) {
u8* const ptr = GetPointerImpl(
addr,
[addr, data]() {
LOG_ERROR(HW_Memory, "Unmapped Write16 @ 0x{:016X} = 0x{:04X}", addr,
static_cast<u16>(data));
},
[&]() { HandleRasterizerWrite(addr, sizeof(T)); });
if (ptr) {
*reinterpret_cast<u16*>(ptr) = static_cast<u16>(data);
return;
}
}
} else if constexpr (std::is_same_v<T, u32_le> || std::is_same_v<T, s32_le>) {
// Check alignment for 32-bit writes
if ((addr & 3) == 0) {
u8* const ptr = GetPointerImpl(
addr,
[addr, data]() {
LOG_ERROR(HW_Memory, "Unmapped Write32 @ 0x{:016X} = 0x{:08X}", addr,
static_cast<u32>(data));
},
[&]() { HandleRasterizerWrite(addr, sizeof(T)); });
if (ptr) {
*reinterpret_cast<u32*>(ptr) = static_cast<u32>(data);
return;
}
}
} else if constexpr (std::is_same_v<T, u64_le> || std::is_same_v<T, s64_le>) {
// Check alignment for 64-bit writes
if ((addr & 7) == 0) {
u8* const ptr = GetPointerImpl(
addr,
[addr, data]() {
LOG_ERROR(HW_Memory, "Unmapped Write64 @ 0x{:016X} = 0x{:016X}", addr,
static_cast<u64>(data));
},
[&]() { HandleRasterizerWrite(addr, sizeof(T)); });
if (ptr) {
*reinterpret_cast<u64*>(ptr) = static_cast<u64>(data);
return;
}
}
}
// Fall back to the general case for other types or unaligned access
u8* const ptr = GetPointerImpl(
addr,
[addr, data]() {
LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8,
addr, static_cast<u64>(data));
},
[&]() { HandleRasterizerWrite(addr, sizeof(T)); });
if (ptr) {
if (auto const ptr = GetPointerImpl(addr, [addr, data]() {
LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, addr, u64(data));
}, [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); ptr) [[likely]]
std::memcpy(ptr, &data, sizeof(T));
}
}
template <typename T>

View File

@@ -0,0 +1,286 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <locale>
#include "common/hex_util.h"
#include "common/swap.h"
#include "core/arm/debug.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
#include "hid_core/resource_manager.h"
#include "hid_core/resources/npad/npad.h"
namespace Core::Memory {
namespace {
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
std::string_view ExtractName(std::size_t& out_name_size, std::string_view data,
std::size_t start_index, char match) {
auto end_index = start_index;
while (data[end_index] != match) {
++end_index;
if (end_index > data.size()) {
return {};
}
}
out_name_size = end_index - start_index;
// Clamp name if it's too big
if (out_name_size > sizeof(CheatDefinition::readable_name)) {
end_index = start_index + sizeof(CheatDefinition::readable_name);
}
return data.substr(start_index, end_index - start_index);
}
} // Anonymous namespace
StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_)
: metadata{metadata_}, system{system_} {}
StandardVmCallbacks::~StandardVmCallbacks() = default;
void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
// Return zero on invalid address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
std::memset(data, 0, size);
return;
}
system.ApplicationMemory().ReadBlock(address, data, size);
}
void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
// Skip invalid memory write address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
return;
}
if (system.ApplicationMemory().WriteBlock(address, data, size)) {
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size);
}
}
u64 StandardVmCallbacks::HidKeysDown() {
const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid");
if (hid == nullptr) {
LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
return 0;
}
const auto applet_resource = hid->GetResourceManager();
if (applet_resource == nullptr || applet_resource->GetNpad() == nullptr) {
LOG_WARNING(CheatEngine,
"Attempted to read input state, but applet resource is not initialized!");
return 0;
}
const auto press_state = applet_resource->GetNpad()->GetAndResetPressState();
return static_cast<u64>(press_state & HID::NpadButton::All);
}
void StandardVmCallbacks::PauseProcess() {
if (system.ApplicationProcess()->IsSuspended()) {
return;
}
system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Paused);
}
void StandardVmCallbacks::ResumeProcess() {
if (!system.ApplicationProcess()->IsSuspended()) {
return;
}
system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Runnable);
}
void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
}
void StandardVmCallbacks::CommandLog(std::string_view data) {
LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
}
bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
if ((in < metadata.main_nso_extents.base ||
in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
(in < metadata.heap_extents.base ||
in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
(in < metadata.alias_extents.base ||
in >= metadata.alias_extents.base + metadata.alias_extents.size) &&
(in < metadata.aslr_extents.base ||
in >= metadata.aslr_extents.base + metadata.aslr_extents.size)) {
LOG_DEBUG(CheatEngine,
"Cheat attempting to access memory at invalid address={:016X}, if this "
"persists, "
"the cheat may be incorrect. However, this may be normal early in execution if "
"the game has not properly set up yet.",
in);
return false; ///< Invalid addresses will hard crash
}
return true;
}
CheatParser::~CheatParser() = default;
TextCheatParser::~TextCheatParser() = default;
std::vector<CheatEntry> TextCheatParser::Parse(std::string_view data) const {
std::vector<CheatEntry> out(1);
std::optional<u64> current_entry;
for (std::size_t i = 0; i < data.size(); ++i) {
if (::isspace(data[i])) {
continue;
}
if (data[i] == '{') {
current_entry = 0;
if (out[*current_entry].definition.num_opcodes > 0) {
return {};
}
std::size_t name_size{};
const auto name = ExtractName(name_size, data, i + 1, '}');
if (name.empty()) {
return {};
}
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
name.size()));
out[*current_entry]
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
'\0';
i += name_size + 1;
} else if (data[i] == '[') {
current_entry = out.size();
out.emplace_back();
std::size_t name_size{};
const auto name = ExtractName(name_size, data, i + 1, ']');
if (name.empty()) {
return {};
}
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
name.size()));
out[*current_entry]
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
'\0';
i += name_size + 1;
} else if (::isxdigit(data[i])) {
if (!current_entry || out[*current_entry].definition.num_opcodes >=
out[*current_entry].definition.opcodes.size()) {
return {};
}
const auto hex = std::string(data.substr(i, 8));
if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
return {};
}
const auto value = static_cast<u32>(std::strtoul(hex.c_str(), nullptr, 0x10));
out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
value;
i += 8;
} else {
return {};
}
}
out[0].enabled = out[0].definition.num_opcodes > 0;
out[0].cheat_id = 0;
for (u32 i = 1; i < out.size(); ++i) {
out[i].enabled = out[i].definition.num_opcodes > 0;
out[i].cheat_id = i;
}
return out;
}
CheatEngine::CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
const std::array<u8, 0x20>& build_id_)
: vm{std::make_unique<StandardVmCallbacks>(system_, metadata)},
cheats(std::move(cheats_)), core_timing{system_.CoreTiming()}, system{system_} {
metadata.main_nso_build_id = build_id_;
}
CheatEngine::~CheatEngine() {
core_timing.UnscheduleEvent(event);
}
void CheatEngine::Initialize() {
event = Core::Timing::CreateEvent(
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
FrameCallback(ns_late);
return std::nullopt;
});
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
metadata.process_id = system.ApplicationProcess()->GetProcessId();
metadata.title_id = system.GetApplicationProcessProgramID();
const auto& page_table = system.ApplicationProcess()->GetPageTable();
metadata.heap_extents = {
.base = GetInteger(page_table.GetHeapRegionStart()),
.size = page_table.GetHeapRegionSize(),
};
metadata.aslr_extents = {
.base = GetInteger(page_table.GetAliasCodeRegionStart()),
.size = page_table.GetAliasCodeRegionSize(),
};
metadata.alias_extents = {
.base = GetInteger(page_table.GetAliasRegionStart()),
.size = page_table.GetAliasRegionSize(),
};
is_pending_reload.exchange(true);
}
void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
metadata.main_nso_extents = {
.base = main_region_begin,
.size = main_region_size,
};
}
void CheatEngine::Reload(std::vector<CheatEntry> reload_cheats) {
cheats = std::move(reload_cheats);
is_pending_reload.exchange(true);
}
void CheatEngine::FrameCallback(std::chrono::nanoseconds ns_late) {
if (is_pending_reload.exchange(false)) {
vm.LoadProgram(cheats);
}
if (vm.GetProgramSize() == 0) {
return;
}
vm.Execute(metadata);
}
} // namespace Core::Memory

View File

@@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <chrono>
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "core/memory/dmnt_cheat_types.h"
#include "core/memory/dmnt_cheat_vm.h"
namespace Core {
class System;
}
namespace Core::Timing {
class CoreTiming;
struct EventType;
} // namespace Core::Timing
namespace Core::Memory {
class StandardVmCallbacks : public DmntCheatVm::Callbacks {
public:
StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_);
~StandardVmCallbacks() override;
void MemoryReadUnsafe(VAddr address, void* data, u64 size) override;
void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override;
u64 HidKeysDown() override;
void PauseProcess() override;
void ResumeProcess() override;
void DebugLog(u8 id, u64 value) override;
void CommandLog(std::string_view data) override;
private:
bool IsAddressInRange(VAddr address) const;
const CheatProcessMetadata& metadata;
Core::System& system;
};
// Intermediary class that parses a text file or other disk format for storing cheats into a
// CheatList object, that can be used for execution.
class CheatParser {
public:
virtual ~CheatParser();
[[nodiscard]] virtual std::vector<CheatEntry> Parse(std::string_view data) const = 0;
};
// CheatParser implementation that parses text files
class TextCheatParser final : public CheatParser {
public:
~TextCheatParser() override;
[[nodiscard]] std::vector<CheatEntry> Parse(std::string_view data) const override;
};
// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
class CheatEngine final {
public:
CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
const std::array<u8, 0x20>& build_id_);
~CheatEngine();
void Initialize();
void SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size);
void Reload(std::vector<CheatEntry> reload_cheats);
private:
void FrameCallback(std::chrono::nanoseconds ns_late);
DmntCheatVm vm;
CheatProcessMetadata metadata;
std::vector<CheatEntry> cheats;
std::atomic_bool is_pending_reload{false};
std::shared_ptr<Core::Timing::EventType> event;
Core::Timing::CoreTiming& core_timing;
Core::System& system;
};
} // namespace Core::Memory

View File

@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Core::Memory {
struct MemoryRegionExtents {
u64 base{};
u64 size{};
};
struct CheatProcessMetadata {
u64 process_id{};
u64 title_id{};
MemoryRegionExtents main_nso_extents{};
MemoryRegionExtents heap_extents{};
MemoryRegionExtents alias_extents{};
MemoryRegionExtents aslr_extents{};
std::array<u8, 0x20> main_nso_build_id{};
};
struct CheatDefinition {
std::array<char, 0x40> readable_name{};
u32 num_opcodes{};
std::array<u32, 0x100> opcodes{};
};
struct CheatEntry {
bool enabled{};
u32 cheat_id{};
CheatDefinition definition{};
};
} // namespace Core::Memory

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,330 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <variant>
#include <vector>
#include <memory>
#include <fmt/printf.h>
#include "common/common_types.h"
#include "core/memory/dmnt_cheat_types.h"
namespace Core::Memory {
enum class CheatVmOpcodeType : u32 {
StoreStatic = 0,
BeginConditionalBlock = 1,
EndConditionalBlock = 2,
ControlLoop = 3,
LoadRegisterStatic = 4,
LoadRegisterMemory = 5,
StoreStaticToAddress = 6,
PerformArithmeticStatic = 7,
BeginKeypressConditionalBlock = 8,
// These are not implemented by Gateway's VM.
PerformArithmeticRegister = 9,
StoreRegisterToAddress = 10,
Reserved11 = 11,
// This is a meta entry, and not a real opcode.
// This is to facilitate multi-nybble instruction decoding.
ExtendedWidth = 12,
// Extended width opcodes.
BeginRegisterConditionalBlock = 0xC0,
SaveRestoreRegister = 0xC1,
SaveRestoreRegisterMask = 0xC2,
ReadWriteStaticRegister = 0xC3,
// This is a meta entry, and not a real opcode.
// This is to facilitate multi-nybble instruction decoding.
DoubleExtendedWidth = 0xF0,
// Double-extended width opcodes.
PauseProcess = 0xFF0,
ResumeProcess = 0xFF1,
DebugLog = 0xFFF,
};
enum class MemoryAccessType : u32 {
MainNso = 0,
Heap = 1,
Alias = 2,
Aslr = 3,
};
enum class ConditionalComparisonType : u32 {
GT = 1,
GE = 2,
LT = 3,
LE = 4,
EQ = 5,
NE = 6,
};
enum class RegisterArithmeticType : u32 {
Addition = 0,
Subtraction = 1,
Multiplication = 2,
LeftShift = 3,
RightShift = 4,
// These are not supported by Gateway's VM.
LogicalAnd = 5,
LogicalOr = 6,
LogicalNot = 7,
LogicalXor = 8,
None = 9,
};
enum class StoreRegisterOffsetType : u32 {
None = 0,
Reg = 1,
Imm = 2,
MemReg = 3,
MemImm = 4,
MemImmReg = 5,
};
enum class CompareRegisterValueType : u32 {
MemoryRelAddr = 0,
MemoryOfsReg = 1,
RegisterRelAddr = 2,
RegisterOfsReg = 3,
StaticValue = 4,
OtherRegister = 5,
};
enum class SaveRestoreRegisterOpType : u32 {
Restore = 0,
Save = 1,
ClearSaved = 2,
ClearRegs = 3,
};
enum class DebugLogValueType : u32 {
MemoryRelAddr = 0,
MemoryOfsReg = 1,
RegisterRelAddr = 2,
RegisterOfsReg = 3,
RegisterValue = 4,
};
union VmInt {
u8 bit8;
u16 bit16;
u32 bit32;
u64 bit64;
};
struct StoreStaticOpcode {
u32 bit_width{};
MemoryAccessType mem_type{};
u32 offset_register{};
u64 rel_address{};
VmInt value{};
};
struct BeginConditionalOpcode {
u32 bit_width{};
MemoryAccessType mem_type{};
ConditionalComparisonType cond_type{};
u64 rel_address{};
VmInt value{};
};
struct EndConditionalOpcode {
bool is_else;
};
struct ControlLoopOpcode {
bool start_loop{};
u32 reg_index{};
u32 num_iters{};
};
struct LoadRegisterStaticOpcode {
u32 reg_index{};
u64 value{};
};
struct LoadRegisterMemoryOpcode {
u32 bit_width{};
MemoryAccessType mem_type{};
u32 reg_index{};
bool load_from_reg{};
u64 rel_address{};
};
struct StoreStaticToAddressOpcode {
u32 bit_width{};
u32 reg_index{};
bool increment_reg{};
bool add_offset_reg{};
u32 offset_reg_index{};
u64 value{};
};
struct PerformArithmeticStaticOpcode {
u32 bit_width{};
u32 reg_index{};
RegisterArithmeticType math_type{};
u32 value{};
};
struct BeginKeypressConditionalOpcode {
u32 key_mask{};
};
struct PerformArithmeticRegisterOpcode {
u32 bit_width{};
RegisterArithmeticType math_type{};
u32 dst_reg_index{};
u32 src_reg_1_index{};
u32 src_reg_2_index{};
bool has_immediate{};
VmInt value{};
};
struct StoreRegisterToAddressOpcode {
u32 bit_width{};
u32 str_reg_index{};
u32 addr_reg_index{};
bool increment_reg{};
StoreRegisterOffsetType ofs_type{};
MemoryAccessType mem_type{};
u32 ofs_reg_index{};
u64 rel_address{};
};
struct BeginRegisterConditionalOpcode {
u32 bit_width{};
ConditionalComparisonType cond_type{};
u32 val_reg_index{};
CompareRegisterValueType comp_type{};
MemoryAccessType mem_type{};
u32 addr_reg_index{};
u32 other_reg_index{};
u32 ofs_reg_index{};
u64 rel_address{};
VmInt value{};
};
struct SaveRestoreRegisterOpcode {
u32 dst_index{};
u32 src_index{};
SaveRestoreRegisterOpType op_type{};
};
struct SaveRestoreRegisterMaskOpcode {
SaveRestoreRegisterOpType op_type{};
std::array<bool, 0x10> should_operate{};
};
struct ReadWriteStaticRegisterOpcode {
u32 static_idx{};
u32 idx{};
};
struct PauseProcessOpcode {};
struct ResumeProcessOpcode {};
struct DebugLogOpcode {
u32 bit_width{};
u32 log_id{};
DebugLogValueType val_type{};
MemoryAccessType mem_type{};
u32 addr_reg_index{};
u32 val_reg_index{};
u32 ofs_reg_index{};
u64 rel_address{};
};
struct UnrecognizedInstruction {
CheatVmOpcodeType opcode{};
};
struct CheatVmOpcode {
bool begin_conditional_block{};
std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
opcode{};
};
class DmntCheatVm {
public:
/// Helper Type for DmntCheatVm <=> yuzu Interface
class Callbacks {
public:
virtual ~Callbacks();
virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0;
virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0;
virtual u64 HidKeysDown() = 0;
virtual void PauseProcess() = 0;
virtual void ResumeProcess() = 0;
virtual void DebugLog(u8 id, u64 value) = 0;
virtual void CommandLog(std::string_view data) = 0;
};
static constexpr std::size_t MaximumProgramOpcodeCount = 0x400;
static constexpr std::size_t NumRegisters = 0x10;
static constexpr std::size_t NumReadableStaticRegisters = 0x80;
static constexpr std::size_t NumWritableStaticRegisters = 0x80;
static constexpr std::size_t NumStaticRegisters =
NumReadableStaticRegisters + NumWritableStaticRegisters;
explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks_);
~DmntCheatVm();
std::size_t GetProgramSize() const {
return this->num_opcodes;
}
bool LoadProgram(const std::vector<CheatEntry>& cheats);
void Execute(const CheatProcessMetadata& metadata);
private:
std::unique_ptr<Callbacks> callbacks;
std::size_t num_opcodes = 0;
std::size_t instruction_ptr = 0;
std::size_t condition_depth = 0;
bool decode_success = false;
std::array<u32, MaximumProgramOpcodeCount> program{};
std::array<u64, NumRegisters> registers{};
std::array<u64, NumRegisters> saved_values{};
std::array<u64, NumStaticRegisters> static_registers{};
std::array<std::size_t, NumRegisters> loop_tops{};
bool DecodeNextOpcode(CheatVmOpcode& out);
void SkipConditionalBlock(bool is_if);
void ResetState();
// For implementing the DebugLog opcode.
void DebugLog(u32 log_id, u64 value);
void LogOpcode(const CheatVmOpcode& opcode);
static u64 GetVmInt(VmInt value, u32 bit_width);
static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
MemoryAccessType mem_type, u64 rel_address);
};
}; // namespace Core::Memory

View File

@@ -2,7 +2,12 @@
# SPDX-License-Identifier: GPL-3.0-or-later
cmake_minimum_required(VERSION 3.12)
project(dynarmic LANGUAGES C CXX ASM VERSION 6.7.0)
set(dynarmic_VERSION_MAJOR 7)
set(dynarmic_VERSION_MINOR 0)
set(dynarmic_VERSION_PATCH 0)
set(dynarmic_VERSION ${dynarmic_VERSION_MAJOR}.${dynarmic_VERSION_MINOR}.${dynarmic_VERSION_PATCH})
# Determine if we're built as a subproject (using add_subdirectory)
# or if this is the master project.
@@ -46,7 +51,7 @@ if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
endif()
# Add the module directory to the list of paths
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
# Compiler flags
if (MSVC)

View File

@@ -14,7 +14,7 @@
#include "input_common/drivers/mouse.h"
namespace InputCommon {
constexpr int update_time = 250; // 4 TPS
constexpr int update_time = 10;
constexpr float default_panning_sensitivity = 0.0010f;
constexpr float default_stick_sensitivity = 0.0006f;
constexpr float default_deadzone_counterweight = 0.01f;

View File

@@ -79,7 +79,9 @@ namespace {
#endif
if (enable_validation && AreExtensionsSupported(dld, *properties, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME}))
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
// VK_EXT_surface_maintenance1 is required for VK_EXT_swapchain_maintenance1
if (window_type != Core::Frontend::WindowSystemType::Headless && AreExtensionsSupported(dld, *properties, std::array{VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME}))
extensions.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
}
return extensions;
}

View File

@@ -5,8 +5,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <utility>
@@ -75,32 +73,10 @@ ConfigurePerGameAddons::~ConfigurePerGameAddons() = default;
void ConfigurePerGameAddons::ApplyConfiguration() {
std::vector<std::string> disabled_addons;
// Helper function to recursively collect disabled items
std::function<void(QStandardItem*)> collect_disabled = [&](QStandardItem* item) {
if (item == nullptr) {
return;
}
// Check if this item is disabled
if (item->isCheckable() && item->checkState() == Qt::Unchecked) {
// Use the stored key from UserRole, falling back to text
const auto key = item->data(Qt::UserRole).toString();
if (!key.isEmpty()) {
disabled_addons.push_back(key.toStdString());
} else {
disabled_addons.push_back(item->text().toStdString());
}
}
// Process children (for cheats under mods)
for (int row = 0; row < item->rowCount(); ++row) {
collect_disabled(item->child(row, 0));
}
};
// Process all root items
for (int row = 0; row < item_model->rowCount(); ++row) {
collect_disabled(item_model->item(row, 0));
for (const auto& item : list_items) {
const auto disabled = item.front()->checkState() == Qt::Unchecked;
if (disabled)
disabled_addons.push_back(item.front()->text().toStdString());
}
auto current = Settings::values.disabled_addons[title_id];
@@ -147,61 +123,24 @@ void ConfigurePerGameAddons::LoadConfiguration() {
FileSys::VirtualFile update_raw;
loader->ReadUpdateRaw(update_raw);
// Get the build ID from the main executable for cheat enumeration
const auto build_id = pm.GetBuildID(update_raw);
const auto& disabled = Settings::values.disabled_addons[title_id];
// Map to store parent items for mods (for adding cheat children)
std::map<std::string, QStandardItem*> mod_items;
for (const auto& patch : pm.GetPatches(update_raw, build_id)) {
for (const auto& patch : pm.GetPatches(update_raw)) {
const auto name = QString::fromStdString(patch.name);
// For cheats, we need to use the full key (parent::name) for storage
std::string storage_key;
if (patch.type == FileSys::PatchType::Cheat && !patch.parent_name.empty()) {
storage_key = patch.parent_name + "::" + patch.name;
} else {
storage_key = patch.name;
}
auto* const first_item = new QStandardItem;
first_item->setText(name);
first_item->setCheckable(true);
// Store the storage key as user data for later retrieval
first_item->setData(QString::fromStdString(storage_key), Qt::UserRole);
const auto patch_disabled =
std::find(disabled.begin(), disabled.end(), storage_key) != disabled.end();
std::find(disabled.begin(), disabled.end(), name.toStdString()) != disabled.end();
first_item->setCheckState(patch_disabled ? Qt::Unchecked : Qt::Checked);
auto* const version_item = new QStandardItem{QString::fromStdString(patch.version)};
if (patch.type == FileSys::PatchType::Cheat && !patch.parent_name.empty()) {
// This is a cheat - add as child of its parent mod
auto parent_it = mod_items.find(patch.parent_name);
if (parent_it != mod_items.end()) {
parent_it->second->appendRow(QList<QStandardItem*>{first_item, version_item});
} else {
// Parent not found (shouldn't happen), add to root
list_items.push_back(QList<QStandardItem*>{first_item, version_item});
item_model->appendRow(list_items.back());
}
} else {
// This is a top-level item (Update, Mod, DLC)
list_items.push_back(QList<QStandardItem*>{first_item, version_item});
item_model->appendRow(list_items.back());
// Store mod items for later cheat attachment
if (patch.type == FileSys::PatchType::Mod) {
mod_items[patch.name] = first_item;
}
}
list_items.push_back(QList<QStandardItem*>{
first_item, new QStandardItem{QString::fromStdString(patch.version)}});
item_model->appendRow(list_items.back());
}
tree_view->expandAll();
tree_view->resizeColumnToContents(1);
}

View File

@@ -8,10 +8,7 @@
# provided for workflow compat
# shellcheck disable=SC1091
. tools/cpm/common.sh
chmod +x tools/cpm/fetch.sh
find tools/ -name '*.sh' -type f -exec chmod +x {} \;
# shellcheck disable=SC2086
tools/cpm/fetch.sh $LIBS
tools/cpmutil.sh package fetch -a

View File

@@ -1,79 +1,3 @@
# CPMUtil Tools
# CPMUtil Tooling
These are supplemental shell scripts for CPMUtil aiming to ease maintenance burden for sanity checking, updates, prefetching, formatting, and standard operations done by these shell scripts, all in one common place.
All scripts are POSIX-compliant. If something doesn't work on your shell, ensure it's POSIX-compliant.
* If your shell doesn't support `$(...)` syntax, you've got bigger problems to worry about.
<!-- TOC -->
- [Meta](#meta)
- [Simple Utilities](#simple-utilities)
- [Functional Utilities](#functional-utilities)
<!-- /TOC -->
## Meta
These scripts are generally reserved for internal use.
- `common.sh`: Grabs all available cpmfiles and aggregates them together.
* Outputs:
- `PACKAGES`: The aggregated cpmfile
- `LIBS`: The list of individual libraries contained within each cpmfile
- `value`: A function that grabs a key from the `JSON` variable (typically the package key)
- `download.sh`: Utility script to handle downloading of regular and CI packages.
* Generally only used by the fetch scripts.
- `package.sh`: The actual package parser.
* Inputs:
- `PACKAGE`: The package key
* Outputs:
- Basically everything. You're best off reading the code rather than me poorly explaining it.
- `which.sh`: Find which cpmfile a package is located in.
* Inputs:
- The package key
- `replace.sh`: Replace a package's cpmfile definition.
* Inputs:
- `PACKAGE`: The package key
- `NEW_JSON`: All keys to replace/add
* Keys not found in the new json are not touched. Keys cannot currently be deleted.
## Simple Utilities
These scripts don't really have any functionality, they just help you out a bit yknow?
- `format.sh`: Format all cpmfiles (4-space indent is enforced)
* In the future, these scripts will have options for spacing
- `hash.sh`: Determine the hash of a specific package.
* Inputs:
- The repository (e.g. fmtlib/fmt)
- The sha or tag (e.g. v1.0.1)
- `-g <GIT_HOST>` or `--host <GIT_HOST>`: What git host to use (default github.com)
- `-a <ARTIFACT>` or `--artifact <ARTIFACT>`: The artifact to download. Set to null or empty to use a source archive instead
* Output: the SHA512 sum of the package
- `url-hash.sh`: Determine the hash of a URL
* Input: the URL
* Output: the SHA512 sum of the URL
## Functional Utilities
These modify the CPM cache or cpmfiles. Each allows you to input all the packages to act on, as well as a `<scriptname>-all.sh` that acts upon all available packages.
Beware: if a hash is `cf83e1357...` that means you got a 404 error!
- `fetch.sh`: Prefetch a package according to its cpmfile definition
* Packages are fetched to the `.cache/cpm` directory by default, following the CPMUtil default.
* Already-fetched packages will be skipped. You can invalidate the entire cache with `rm -rf .cache/cpm`, or invalidate a specific package with e.g. `rm -rf .cache/cpm/packagename` to force a refetch.
* In the future, a force option will be added
* Note that full prefetching will take a long time depending on your internet, the amount of dependencies, and the size of each dependency.
- `check-updates.sh`: Check a package for available updates
* This only applies to packages that utilize tags.
* If the tag is a format string, the `git_version` is acted upon instead.
* Specifying `-f` or `--force` will forcefully update the package and its hash, even if it's on on the latest version.
* Alternatively, only specify `-u` or `--update` to update packages that have new versions available.
* This script generally runs fast.
* Packages that should skip updates (e.g. older versions, OR packages with poorly-made tag structures... looking at you mbedtls) may specify `"skip_updates": true` in their cpmfile definition. This is unnecessary for untagged (e.g. sha or bare URL) packages.
- `check-hashes.sh`: Check a package's hash
* Specifying `-f` or `--force` will update the package's hash even if it's not mismatched.
* Alternatively, specify `-u` or `--update` to only fix mismatched hashes.
* This only applies to packages with hardcoded hashes, NOT ones that use hash URLs.
* This script will take a long time. This is operationally equivalent to a prefetch, and thus checking all hashes will take a while--but it's worth it! Just make sure you're not using dial-up.
You are recommended to run sanity hash checking for every pull request and commit, and weekly update checks.
CPMUtil's tooling entirely revolves around the `cpmutil.sh` script. It contains various functions to aid with package maintenance, such as sanity checks, updates, formatting, prefetching, adding/removing packages, and much more. These are now self-documenting, so view the scripts yourself or run the cpmutil script for help.

View File

@@ -1,10 +0,0 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# shellcheck disable=SC1091
. tools/cpm/common.sh
# shellcheck disable=SC2086
tools/cpm/check-hash.sh "$@" $LIBS

View File

@@ -1,79 +0,0 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# shellcheck disable=SC1091
. tools/cpm/common.sh
RETURN=0
usage() {
cat << EOF
Usage: $0 [uf] [PACKAGE]...
Check the hash of a specific package or packages.
If a hash mismatch occurs, this script will print the corrected hash of the package.
Options:
-u, --update Correct the package's hash if it's a mismatch
-f, --force Update the package's hash anyways (implies -u)
Note that this procedure will usually take a long time
depending on the number and size of dependencies.
This project has defined the following as valid cpmfiles:
EOF
for file in $CPMFILES; do
echo "- $file"
done
exit $RETURN
}
while true; do
case "$1" in
(-uf|-f|--force) UPDATE=true; FORCE=true; shift; continue ;;
(-u|--update) UPDATE=true; shift; continue ;;
(-h) usage ;;
("$0") break ;;
("") break ;;
esac
PACKAGE="$1"
shift
export PACKAGE
. tools/cpm/package.sh
if [ "$CI" != null ]; then
continue
fi
[ "$HASH_URL" != null ] && continue
[ "$HASH_SUFFIX" != null ] && continue
echo "-- Package $PACKAGE"
[ "$HASH" = null ] && echo "-- * Warning: no hash specified" && continue
export USE_TAG=true
ACTUAL=$(tools/cpm/url-hash.sh "$DOWNLOAD")
if [ "$ACTUAL" != "$HASH" ]; then
echo "-- * Expected $HASH"
echo "-- * Got $ACTUAL"
[ "$UPDATE" != "true" ] && RETURN=1
fi
if { [ "$UPDATE" = "true" ] && [ "$ACTUAL" != "$HASH" ]; } || [ "$FORCE" = "true" ]; then
NEW_JSON=$(echo "$JSON" | jq ".hash = \"$ACTUAL\"")
export NEW_JSON
tools/cpm/replace.sh
fi
done
exit $RETURN

View File

@@ -1,10 +0,0 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# shellcheck disable=SC1091
. tools/cpm/common.sh
# shellcheck disable=SC2086
tools/cpm/check-updates.sh "$@" $LIBS

View File

@@ -1,22 +1,24 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
##################################
# CHANGE THESE FOR YOUR PROJECT! #
##################################
# TODO: cache cpmfile defs
# How many levels to go (3 is 2 subdirs max)
MAXDEPTH=3
# For your project you'll want to change this to define what dirs you have cpmfiles in
# Remember to account for the MAXDEPTH variable!
# Adding ./ before each will help to remove duplicates
[ -z "$CPMFILES" ] && CPMFILES=$(find . ./src -maxdepth "$MAXDEPTH" -name cpmfile.json | sort | uniq)
CPMFILES=$(find . -maxdepth "$MAXDEPTH" -name cpmfile.json | sort | uniq)
# shellcheck disable=SC2016
[ -z "$PACKAGES" ] && PACKAGES=$(echo "$CPMFILES" | xargs jq -s 'reduce .[] as $item ({}; . * $item)')
PACKAGES=$(echo "$CPMFILES" | xargs jq -s 'reduce .[] as $item ({}; . * $item)')
LIBS=$(echo "$PACKAGES" | jq -j 'keys_unsorted | join(" ")')
@@ -25,7 +27,3 @@ export CPMFILES
export LIBS
export DIRS
export MAXDEPTH
value() {
echo "$JSON" | jq -r ".$1"
}

View File

@@ -1,110 +0,0 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# shellcheck disable=SC1091
. tools/cpm/common.sh
download_package() {
FILENAME=$(basename "$DOWNLOAD")
OUTFILE="$TMP/$FILENAME"
LOWER_PACKAGE=$(echo "$PACKAGE_NAME" | tr '[:upper:]' '[:lower:]')
OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
[ -d "$OUTDIR" ] && return
curl "$DOWNLOAD" -sS -L -o "$OUTFILE"
ACTUAL_HASH=$("${HASH_ALGO}"sum "$OUTFILE" | cut -d" " -f1)
[ "$ACTUAL_HASH" != "$HASH" ] && echo "!! $FILENAME did not match expected hash; expected $HASH but got $ACTUAL_HASH" && exit 1
TMPDIR="$TMP/extracted"
mkdir -p "$OUTDIR"
mkdir -p "$TMPDIR"
PREVDIR="$PWD"
mkdir -p "$TMPDIR"
cd "$TMPDIR"
case "$FILENAME" in
(*.7z)
7z x "$OUTFILE" > /dev/null
;;
(*.tar*)
tar xf "$OUTFILE" > /dev/null
;;
(*.zip)
unzip "$OUTFILE" > /dev/null
;;
esac
# basically if only one real item exists at the top we just move everything from there
# since github and some vendors hate me
DIRS=$(find . -maxdepth 1 -type d -o -type f)
# thanks gnu
if [ "$(echo "$DIRS" | wc -l)" -eq 2 ]; then
SUBDIR=$(find . -maxdepth 1 -type d -not -name ".")
mv "$SUBDIR"/* "$OUTDIR"
mv "$SUBDIR"/.* "$OUTDIR" 2>/dev/null || true
rmdir "$SUBDIR"
else
mv ./* "$OUTDIR"
mv ./.* "$OUTDIR" 2>/dev/null || true
fi
cd "$OUTDIR"
if echo "$JSON" | grep -e "patches" > /dev/null; then
PATCHES=$(echo "$JSON" | jq -r '.patches | join(" ")')
for patch in $PATCHES; do
# shellcheck disable=SC2154
patch --binary -p1 < "$ROOTDIR/.patch/$PACKAGE/$patch"
done
fi
cd "$PREVDIR"
}
ci_package() {
[ "$REPO" = null ] && echo "-- ! No repo defined" && return
echo "-- CI package $PACKAGE_NAME"
for platform in windows-amd64 windows-arm64 \
mingw-amd64 mingw-arm64 \
android-aarch64 android-x86_64 \
solaris-amd64 freebsd-amd64 openbsd-amd64 \
linux-amd64 linux-aarch64 \
macos-universal; do
echo "-- * platform $platform"
case $DISABLED in
(*"$platform"*)
echo "-- * -- disabled"
continue
;;
(*) ;;
esac
FILENAME="${NAME}-${platform}-${VERSION}.${EXT}"
DOWNLOAD="https://$GIT_HOST/${REPO}/releases/download/v${VERSION}/${FILENAME}"
KEY=$platform
LOWER_PACKAGE=$(echo "$PACKAGE_NAME" | tr '[:upper:]' '[:lower:]')
OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
[ -d "$OUTDIR" ] && continue
HASH_ALGO=$(value "hash_algo")
[ "$HASH_ALGO" = null ] && HASH_ALGO=sha512
HASH_SUFFIX="${HASH_ALGO}sum"
HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}"
HASH=$(curl "$HASH_URL" -sS -q -L -o -)
download_package
done
}

View File

@@ -1,10 +0,0 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# shellcheck disable=SC1091
. tools/cpm/common.sh
# shellcheck disable=SC2086
tools/cpm/fetch.sh "$@" $LIBS

View File

@@ -1,60 +0,0 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
[ -z "$CPM_SOURCE_CACHE" ] && CPM_SOURCE_CACHE=$PWD/.cache/cpm
mkdir -p "$CPM_SOURCE_CACHE"
# shellcheck disable=SC1091
. tools/cpm/common.sh
# shellcheck disable=SC1091
. tools/cpm/download.sh
# shellcheck disable=SC2034
ROOTDIR="$PWD"
TMP=$(mktemp -d)
usage() {
cat << EOF
Usage: $0 [PACKAGE]...
Fetch the specified package or packages from their defined download locations.
If the package is already cached, it will not be re-fetched.
This project has defined the following as valid cpmfiles:
EOF
for file in $CPMFILES; do
echo "- $file"
done
exit 0
}
while true; do
case "$1" in
(-h) usage ;;
("$0") break ;;
("") break ;;
esac
PACKAGE="$1"
shift
export PACKAGE
# shellcheck disable=SC1091
. tools/cpm/package.sh
if [ "$CI" = "true" ]; then
ci_package
else
echo "-- Downloading regular package $PACKAGE, with key $KEY, from $DOWNLOAD"
download_package
fi
done
rm -rf "$TMP"

View File

@@ -1,12 +1,9 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# shellcheck disable=SC1091
. tools/cpm/common.sh
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
for file in $CPMFILES; do
jq --indent 4 < "$file" > "$file".new
mv "$file".new "$file"
jq --indent 4 <"$file" >"$file".new
mv "$file".new "$file"
done

View File

@@ -1,99 +0,0 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# usage: hash.sh repo tag-or-sha
# env vars: GIT_HOST, USE_TAG (use tag instead of sha), ARTIFACT (download artifact with that name instead of src archive)
RETURN=0
usage() {
cat <<EOF
Usage: $0 [-a|--artifact ARTIFACT] [-g|--host GIT_HOST] [REPO] [REF]
Get the hash of a package.
REPO must be in the form of OWNER/REPO, and REF must be a commit sha, branch, or tag.
Options:
-g, --host <GIT_HOST> What Git host to use (defaults to github.com)
-a, --artifact <ARTIFACT> The artifact to download (implies -t)
If -t is specified but not -a, fetches a tag archive.
If ARTIFACT is specified but is null,
EOF
exit "$RETURN"
}
die() {
echo "$@" >&2
RETURN=1 usage
}
artifact() {
if [ $# -lt 2 ]; then
die "You must specify a valid artifact."
fi
shift
ARTIFACT="$1"
}
host() {
if [ $# -lt 2 ]; then
die "You must specify a valid Git host."
fi
shift
GIT_HOST="$1"
}
# this is a semi-hacky way to handle long/shortforms
while true; do
case "$1" in
-[a-z]*)
opt=$(echo "$1" | sed 's/^-//')
while [ -n "$opt" ]; do
# cut out first char from the optstring
char=$(echo "$opt" | cut -c1)
opt=$(echo "$opt" | cut -c2-)
case "$char" in
a) artifact "$@" ;;
g) host "$@" ;;
h) usage ;;
*) die "Invalid option -$char" ;;
esac
done
;;
--artifact) artifact "$@" ;;
--host) host "$@" ;;
--help) usage ;;
--*) die "Invalid option $1" ;;
"$0" | "") break ;;
*)
{ [ -z "$REPO" ] && REPO="$1"; } || REF="$1"
;;
esac
shift
done
[ -z "$REPO" ] && die "A valid repository must be provided."
[ -z "$REF" ] && die "A valid reference must be provided."
GIT_HOST=${GIT_HOST:-github.com}
GIT_URL="https://$GIT_HOST/$REPO"
if [ -z "$ARTIFACT" ] || [ "$ARTIFACT" = "null" ]; then
URL="${GIT_URL}/archive/$REF.tar.gz"
else
URL="${GIT_URL}/releases/download/$REF/$ARTIFACT"
fi
SUM=$(wget -q "$URL" -O - | sha512sum)
echo "$SUM" | cut -d " " -f1

47
tools/cpm/migrate.sh Executable file
View File

@@ -0,0 +1,47 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
SUBMODULES="$(git submodule status --recursive | cut -c2-)"
[ -z "$SUBMODULES" ] && echo "No submodules defined!" && exit 0
tmp=$(mktemp)
printf '{}' >"$tmp"
IFS="
"
for i in $SUBMODULES; do
sha=$(echo "$i" | cut -d" " -f1 | cut -c1-10)
ver=$(echo "$i" | cut -d" " -f3 | tr -d '()')
path=$(echo "$i" | cut -d" " -f2)
name=$(echo "$path" | awk -F/ '{print $NF}')
remote=$(git -C "$path" remote get-url origin)
host=$(echo "$remote" | cut -d"/" -f3)
[ "$host" = github.com ] && host=
repo=$(echo "$remote" | cut -d"/" -f4-5 | cut -d'.' -f1)
entry=$(jq -n --arg name "$name" \
--arg sha "$sha" \
--arg ver "$ver" \
--arg repo "$repo" \
--arg host "$host" \
'{
($name): {
sha: $sha,
git_version: $ver,
repo: $repo
} + (if $host != "" then {git_host: $host} else {} end)
}')
jq --argjson new "$entry" '. + $new' "$tmp" >"${tmp}.new"
mv "$tmp.new" "$tmp"
done
jq '.' "$tmp" >cpmfile.json
rm -f "$tmp"

View File

@@ -1,200 +1,80 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
# shellcheck disable=SC1091
. tools/cpm/common.sh
RETURN=0
[ -z "$PACKAGE" ] && echo "Package was not specified" && exit 0
usage() {
cat <<EOF
Usage: cpmutil.sh package [command]
# shellcheck disable=SC2153
JSON=$(echo "$PACKAGES" | jq -r ".\"$PACKAGE\" | select( . != null )")
Operate on a package or packages.
[ -z "$JSON" ] && echo "!! No cpmfile definition for $PACKAGE" >&2 && exit 1
Commands:
hash Verify the hash of a package, and update it if needed
update Check for updates for a package
fetch Fetch a package and place it in the cache
add Add a new package
rm Remove a package
version Change the version of a package
which Find which cpmfile a package is defined in
download Get the download URL for a package
# unset stuff
export PACKAGE_NAME="null"
export REPO="null"
export CI="null"
export GIT_HOST="null"
export EXT="null"
export NAME="null"
export DISABLED="null"
export TAG="null"
export ARTIFACT="null"
export SHA="null"
export VERSION="null"
export GIT_VERSION="null"
export DOWNLOAD="null"
export URL="null"
export KEY="null"
export HASH="null"
export ORIGINAL_TAG="null"
export HAS_REPLACE="null"
export VERSION_REPLACE="null"
export HASH_URL="null"
export HASH_SUFFIX="null"
export HASH_ALGO="null"
EOF
########
# Meta #
########
exit $RETURN
}
REPO=$(value "repo")
CI=$(value "ci")
SCRIPTS=$(CDPATH='' cd -- "$(dirname -- "$0")/package" && pwd)
export SCRIPTS
PACKAGE_NAME=$(value "package")
[ "$PACKAGE_NAME" = null ] && PACKAGE_NAME="$PACKAGE"
while :; do
case "$1" in
hash)
shift
"$SCRIPTS"/hash.sh "$@"
break
;;
update)
shift
"$SCRIPTS"/update.sh "$@"
break
;;
fetch)
shift
"$SCRIPTS"/fetch.sh "$@"
break
;;
add)
shift
"$SCRIPTS"/add.sh "$@"
break
;;
rm)
shift
"$SCRIPTS"/rm.sh "$@"
break
;;
version)
shift
"$SCRIPTS"/version.sh "$@"
break
;;
which)
shift
"$SCRIPTS"/which.sh "$@"
break
;;
download)
shift
"$SCRIPTS"/download.sh "$@"
break
;;
-h | --help) usage ;;
"") usage ;;
*) usage ;;
esac
GIT_HOST=$(value "git_host")
[ "$GIT_HOST" = null ] && GIT_HOST=github.com
export PACKAGE_NAME
export REPO
export CI
export GIT_HOST
######################
# CI Package Parsing #
######################
VERSION=$(value "version")
if [ "$CI" = "true" ]; then
EXT=$(value "extension")
[ "$EXT" = null ] && EXT="tar.zst"
NAME=$(value "name")
DISABLED=$(echo "$JSON" | jq -j '.disabled_platforms')
[ "$NAME" = null ] && NAME="$PACKAGE_NAME"
export EXT
export NAME
export DISABLED
export VERSION
return 0
fi
##############
# Versioning #
##############
TAG=$(value "tag")
ARTIFACT=$(value "artifact")
SHA=$(value "sha")
GIT_VERSION=$(value "git_version")
[ "$GIT_VERSION" = null ] && GIT_VERSION="$VERSION"
if [ "$GIT_VERSION" != null ]; then
VERSION_REPLACE="$GIT_VERSION"
else
VERSION_REPLACE="$VERSION"
fi
echo "$TAG" | grep -e "%VERSION%" > /dev/null && HAS_REPLACE=true || HAS_REPLACE=false
ORIGINAL_TAG="$TAG"
TAG=$(echo "$TAG" | sed "s/%VERSION%/$VERSION_REPLACE/g")
ARTIFACT=$(echo "$ARTIFACT" | sed "s/%VERSION%/$VERSION_REPLACE/g")
ARTIFACT=$(echo "$ARTIFACT" | sed "s/%TAG%/$TAG/g")
export TAG
export ARTIFACT
export SHA
export VERSION
export GIT_VERSION
export ORIGINAL_TAG
export HAS_REPLACE
export VERSION_REPLACE
###############
# URL Parsing #
###############
URL=$(value "url")
if [ "$URL" != "null" ]; then
DOWNLOAD="$URL"
elif [ "$REPO" != "null" ]; then
GIT_URL="https://$GIT_HOST/$REPO"
BRANCH=$(value "branch")
if [ "$TAG" != "null" ]; then
if [ "$ARTIFACT" != "null" ]; then
DOWNLOAD="${GIT_URL}/releases/download/${TAG}/${ARTIFACT}"
else
DOWNLOAD="${GIT_URL}/archive/refs/tags/${TAG}.tar.gz"
fi
elif [ "$SHA" != "null" ]; then
DOWNLOAD="${GIT_URL}/archive/${SHA}.tar.gz"
else
if [ "$BRANCH" = null ]; then
BRANCH=master
fi
DOWNLOAD="${GIT_URL}/archive/refs/heads/${BRANCH}.tar.gz"
fi
else
echo "!! No repo or URL defined for $PACKAGE"
exit 1
fi
export DOWNLOAD
export URL
###############
# Key Parsing #
###############
KEY=$(value "key")
if [ "$KEY" = null ]; then
if [ "$SHA" != null ]; then
KEY=$(echo "$SHA" | cut -c1-4)
elif [ "$GIT_VERSION" != null ]; then
KEY="$GIT_VERSION"
elif [ "$TAG" != null ]; then
KEY="$TAG"
elif [ "$VERSION" != null ]; then
KEY="$VERSION"
else
echo "!! No valid key could be determined for $PACKAGE. Must define one of: key, sha, tag, version, git_version"
exit 1
fi
fi
export KEY
################
# Hash Parsing #
################
HASH_ALGO=$(value "hash_algo")
[ "$HASH_ALGO" = null ] && HASH_ALGO=sha512
HASH=$(value "hash")
if [ "$HASH" = null ]; then
HASH_SUFFIX="${HASH_ALGO}sum"
HASH_URL=$(value "hash_url")
if [ "$HASH_URL" = null ]; then
HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}"
fi
HASH=$(curl "$HASH_URL" -Ss -L -o -)
else
HASH_URL=null
HASH_SUFFIX=null
fi
export HASH_URL
export HASH_SUFFIX
export HASH
export HASH_ALGO
export JSON
shift
done

79
tools/cpm/package/add.sh Executable file
View File

@@ -0,0 +1,79 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
RETURN=0
usage() {
cat <<EOF
Usage: cpmutil.sh package add [-s|--sha] [-t|--tag]
[-c|--cpmfile CPMFILE] [package name]
Add a new package to a cpmfile.
Options:
-t, --tag Use tag versioning, instead of the default,
commit sha versioning.
-c, --cpmfile <CPMFILE> Use the specified cpmfile instead of the root cpmfile
Note that you are still responsible for integrating this into your CMake.
EOF
exit $RETURN
}
die() {
echo "-- $*" >&2
exit 1
}
_cpmfile() {
[ -z "$1" ] && die "You must specify a valid cpmfile."
CPMFILE="$1"
}
while :; do
case "$1" in
-[a-z]*)
opt=$(printf '%s' "$1" | sed 's/^-//')
while [ -n "$opt" ]; do
# cut out first char from the optstring
char=$(echo "$opt" | cut -c1)
opt=$(echo "$opt" | cut -c2-)
case "$char" in
t) TAG=1 ;;
c)
_cpmfile "$2"
shift
;;
h) usage ;;
*) die "Invalid option -$char" ;;
esac
done
;;
--tag) TAG=1 ;;
--cpmfile)
_cpmfile "$2"
shift
;;
--help) usage ;;
"$0") break ;;
"") break ;;
*) PKG="$1" ;;
esac
shift
done
: "${CPMFILE:=$PWD/cpmfile.json}"
[ -z "$PKG" ] && die "You must specify a package name."
export PKG
export CPMFILE
export TAG
"$SCRIPTS"/util/interactive.sh

46
tools/cpm/package/download.sh Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
usage() {
cat <<EOF
Usage: cpmutil.sh package download [-a|--all] [PACKAGE]...
Get the download URL for the specified packages.
Options:
-a, --all Operate on all packages in this project.
EOF
exit 0
}
while :; do
case "$1" in
-a | --all) ALL=1 ;;
-h | --help) usage ;;
"$0") break ;;
"") break ;;
*) packages="$packages $1" ;;
esac
shift
done
[ "$ALL" = 1 ] && packages="${LIBS:-$packages}"
[ -z "$packages" ] && usage
for pkg in $packages; do
PACKAGE="$pkg"
export PACKAGE
# shellcheck disable=SC1091
. "$SCRIPTS"/vars.sh
if [ "$CI" = "true" ]; then
echo "-- $PACKAGE: https://$GIT_HOST/$REPO"
else
echo -- "$PACKAGE: $DOWNLOAD"
fi
done

156
tools/cpm/package/fetch.sh Executable file
View File

@@ -0,0 +1,156 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
: "${CPM_SOURCE_CACHE:=$PWD/.cache/cpm}"
mkdir -p "$CPM_SOURCE_CACHE"
ROOTDIR="$PWD"
TMP=$(mktemp -d)
download_package() {
FILENAME=$(basename "$DOWNLOAD")
OUTFILE="$TMP/$FILENAME"
LOWER_PACKAGE=$(echo "$PACKAGE_NAME" | tr '[:upper:]' '[:lower:]')
OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
[ -d "$OUTDIR" ] && return
curl "$DOWNLOAD" -sS -L -o "$OUTFILE"
ACTUAL_HASH=$("${HASH_ALGO}"sum "$OUTFILE" | cut -d" " -f1)
[ "$ACTUAL_HASH" != "$HASH" ] && echo "!! $FILENAME did not match expected hash; expected $HASH but got $ACTUAL_HASH" && exit 1
TMPDIR="$TMP/extracted"
mkdir -p "$OUTDIR"
mkdir -p "$TMPDIR"
PREVDIR="$PWD"
mkdir -p "$TMPDIR"
cd "$TMPDIR"
case "$FILENAME" in
*.7z)
7z x "$OUTFILE" >/dev/null
;;
*.tar*)
tar xf "$OUTFILE" >/dev/null
;;
*.zip)
unzip "$OUTFILE" >/dev/null
;;
esac
# basically if only one real item exists at the top we just move everything from there
# since github and some vendors hate me
DIRS=$(find . -maxdepth 1 -type d -o -type f)
# thanks gnu
if [ "$(echo "$DIRS" | wc -l)" -eq 2 ]; then
SUBDIR=$(find . -maxdepth 1 -type d -not -name ".")
mv "$SUBDIR"/* "$OUTDIR"
mv "$SUBDIR"/.* "$OUTDIR" 2>/dev/null || true
rmdir "$SUBDIR"
else
mv ./* "$OUTDIR"
mv ./.* "$OUTDIR" 2>/dev/null || true
fi
cd "$OUTDIR"
if echo "$JSON" | grep -e "patches" >/dev/null; then
PATCHES=$(echo "$JSON" | jq -r '.patches | join(" ")')
for patch in $PATCHES; do
patch --binary -p1 <"$ROOTDIR/.patch/$PACKAGE/$patch"
done
fi
cd "$PREVDIR"
}
ci_package() {
[ "$REPO" = null ] && echo "-- ! No repo defined" && return
echo "-- CI package $PACKAGE_NAME"
for platform in windows-amd64 windows-arm64 \
mingw-amd64 mingw-arm64 \
android-aarch64 android-x86_64 \
solaris-amd64 freebsd-amd64 openbsd-amd64 \
linux-amd64 linux-aarch64 \
macos-universal; do
echo "-- * platform $platform"
case $DISABLED in
*"$platform"*)
echo "-- * -- disabled"
continue
;;
*) ;;
esac
FILENAME="${NAME}-${platform}-${VERSION}.${EXT}"
DOWNLOAD="https://$GIT_HOST/${REPO}/releases/download/v${VERSION}/${FILENAME}"
KEY="$platform-$VERSION"
LOWER_PACKAGE=$(echo "$PACKAGE_NAME" | tr '[:upper:]' '[:lower:]')
OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}"
[ -d "$OUTDIR" ] && continue
HASH_ALGO=$(echo "$JSON" | jq -r ".hash_algo")
[ "$HASH_ALGO" = null ] && HASH_ALGO=sha512
HASH_SUFFIX="${HASH_ALGO}sum"
HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}"
HASH=$(curl "$HASH_URL" -sS -q -L -o -)
download_package
done
}
usage() {
cat <<EOF
Usage: cpmutil.sh package fetch [a|--all] [PACKAGE]...
Fetch the specified package or packages from their defined download locations.
If the package is already cached, it will not be re-fetched.
EOF
exit 0
}
while :; do
case "$1" in
-h | --help) usage ;;
-a | --all) ALL=1 ;;
"$0") break ;;
"") break ;;
*) packages="$packages $1" ;;
esac
shift
done
[ "$ALL" = 1 ] && packages="${LIBS:-$packages}"
[ -z "$packages" ] && usage
for PACKAGE in $packages; do
export PACKAGE
# shellcheck disable=SC1091
. "$SCRIPTS"/vars.sh
if [ "$CI" = "true" ]; then
ci_package
else
echo "-- Downloading regular package $PACKAGE, with key $KEY, from $DOWNLOAD"
download_package
fi
done
rm -rf "$TMP"

66
tools/cpm/package/hash.sh Executable file
View File

@@ -0,0 +1,66 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
RETURN=0
usage() {
cat <<EOF
Usage: cpmutil.sh package hash [-n|--dry-run] [-a|--all] [PACKAGE]...
Check the hash of a specific package or packages.
If a hash mismatch occurs, this script will update the package's hash.
Options:
-n, --dry-run Don't update the package's hash if it's a mismatch
-a, --all Operate on all packages in this project.
Note that this procedure will usually take a long time
depending on the number and size of dependencies.
EOF
exit $RETURN
}
while :; do
case "$1" in
-[a-z]*)
opt=$(printf '%s' "$1" | sed 's/^-//')
while [ -n "$opt" ]; do
# cut out first char from the optstring
char=$(echo "$opt" | cut -c1)
opt=$(echo "$opt" | cut -c2-)
case "$char" in
a) ALL=1 ;;
n) DRY=1 ;;
h) usage ;;
*) die "Invalid option -$char" ;;
esac
done
;;
--dry-run) DRY=1 ;;
--all) ALL=1 ;;
--help) usage ;;
"$0") break ;;
"") break ;;
*) packages="$packages $1" ;;
esac
shift
done
[ "$ALL" = 1 ] && packages="${LIBS:-$packages}"
[ "$DRY" = 1 ] && UPDATE=false || UPDATE=true
[ -z "$packages" ] && usage
export UPDATE
for pkg in $packages; do
echo "-- Package $pkg"
"$SCRIPTS"/util/fix-hash.sh "$pkg" || RETURN=1
done
exit $RETURN

30
tools/cpm/package/rm.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
RETURN=0
usage() {
cat <<EOF
Usage: cpmutil.sh package rm [PACKAGE]...
Delete a package or packages' cpmfile definition(s).
EOF
exit $RETURN
}
[ $# -lt 1 ] && usage
for pkg in "$@"; do
JSON=$("$SCRIPTS"/which.sh "$pkg") || {
echo "!! No cpmfile definition for $pkg"
continue
}
jq --indent 4 "del(.\"$pkg\")" "$JSON" >"$JSON".tmp
mv "$JSON".tmp "$JSON"
echo "-- Removed $pkg from $JSON" || :
done

View File

@@ -1,12 +1,7 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# shellcheck disable=SC1091
. tools/cpm/common.sh
RETURN=0
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
filter_out() {
TAGS=$(echo "$TAGS" | jq "[.[] | select(.name | test(\"$1\"; \"i\") | not)]")
@@ -17,53 +12,57 @@ filter_in() {
}
usage() {
cat << EOF
Usage: $0 [uf] [PACKAGE]...
cat <<EOF
Usage: cpmutil.sh package update [-n|--dry-run] [-a|--all] [PACKAGE]...
Check a specific package or packages for updates.
Options:
-u, --update Update the package if a new version is available.
This will also update the hash if provided.
-n, --dry-run Do not update the package if it has an update available
-a, --all Operate on all packages in this project.
-f, --force Forcefully update the package version (implies -u)
This is seldom useful, and should only be used in cases of
severe corruption.
This project has defined the following as valid cpmfiles:
EOF
for file in $CPMFILES; do
echo "- $file"
done
exit $RETURN
exit 0
}
while true; do
while :; do
case "$1" in
-f | --force)
UPDATE=true
FORCE=true
shift
continue
-[a-z]*)
opt=$(printf '%s' "$1" | sed 's/^-//')
while [ -n "$opt" ]; do
# cut out first char from the optstring
char=$(echo "$opt" | cut -c1)
opt=$(echo "$opt" | cut -c2-)
case "$char" in
a) ALL=1 ;;
n) DRY=1 ;;
h) usage ;;
*) die "Invalid option -$char" ;;
esac
done
;;
-u | --update)
UPDATE=true
shift
continue
;;
-h) usage ;;
--dry-run) DRY=1 ;;
--all) ALL=1 ;;
--help) usage ;;
"$0") break ;;
"") break ;;
*) packages="$packages $1" ;;
esac
PACKAGE="$1"
shift
done
[ "$ALL" = 1 ] && packages="${LIBS:-$packages}"
[ "$DRY" = 1 ] && UPDATE=false || UPDATE=true
[ -z "$packages" ] && usage
for pkg in $packages; do
PACKAGE="$pkg"
export PACKAGE
# shellcheck disable=SC1091
. tools/cpm/package.sh
. "$SCRIPTS"/vars.sh
SKIP=$(value "skip_updates")
@@ -72,10 +71,9 @@ while true; do
[ "$REPO" = null ] && continue
[ "$GIT_HOST" != "github.com" ] && continue # TODO
if [ "$CI" = "true" ]; then
TAG="v$VERSION"
fi
[ "$CI" = "true" ] && continue
# shellcheck disable=SC2153
[ "$TAG" = null ] && continue
echo "-- Package $PACKAGE"
@@ -93,8 +91,6 @@ while true; do
filter_out vulkan-sdk
fi
[ "$CI" = "true" ] && filter_in "-"
filter_out yotta # mbedtls
# ignore betas/alphas (remove if needed)
@@ -110,8 +106,6 @@ while true; do
[ "$LATEST" = "null" ] && echo "-- * Up-to-date" && continue
[ "$LATEST" = "$TAG" ] && [ "$FORCE" != "true" ] && echo "-- * Up-to-date" && continue
RETURN=1
if [ "$HAS_REPLACE" = "true" ]; then
# this just extracts the tag prefix
VERSION_PREFIX=$(echo "$ORIGINAL_TAG" | cut -d"%" -f1)
@@ -126,23 +120,15 @@ while true; do
echo "-- * Version $LATEST available, current is $TAG"
HASH=$(tools/cpm/hash.sh "$REPO" "$LATEST")
echo "-- * New hash: $HASH"
if [ "$UPDATE" = "true" ]; then
RETURN=0
if [ "$HAS_REPLACE" = "true" ]; then
NEW_JSON=$(echo "$JSON" | jq ".hash = \"$HASH\" | .git_version = \"$NEW_GIT_VERSION\"")
NEW_JSON=$(echo "$JSON" | jq ".git_version = \"$NEW_GIT_VERSION\"")
else
NEW_JSON=$(echo "$JSON" | jq ".hash = \"$HASH\" | .tag = \"$LATEST\"")
NEW_JSON=$(echo "$JSON" | jq ".tag = \"$LATEST\"")
fi
export NEW_JSON
"$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON"
tools/cpm/replace.sh
QUIET=true "$SCRIPTS"/util/fix-hash.sh
fi
done
exit $RETURN

View File

@@ -0,0 +1,30 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
: "${PACKAGE:=$1}"
# shellcheck disable=SC1091
. "$SCRIPTS"/vars.sh
[ "$CI" = null ] || exit 0
[ "$HASH_URL" = null ] || exit 0
[ "$HASH_SUFFIX" = null ] || exit 0
[ "$HASH" = null ] && echo "-- * Package has no hash specified" && exit 0
ACTUAL=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD")
if [ "$ACTUAL" != "$HASH" ] && [ "$QUIET" != true ]; then
echo "-- * Expected $HASH"
echo "-- * Got $ACTUAL"
[ "$UPDATE" != "true" ] && exit 1
fi
if [ "$UPDATE" = "true" ] && [ "$ACTUAL" != "$HASH" ]; then
NEW_JSON=$(echo "$JSON" | jq ".hash = \"$ACTUAL\"")
"$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON"
fi

View File

@@ -0,0 +1,217 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
# This reads a single-line input from the user and also gives them
# help if needed.
# $1: The prompt itself, without any trailing spaces or whatever
# $2: The help text that gets shown when the user types a question mark
# $3: This is set to "required" if it's necessary,
# otherwise it can continue without input.
# Stores its output in the "reply" variable
read_single() {
while :; do
printf -- "-- %s" "$1"
[ -n "$2" ] && printf " (? for help, %s)" "$3"
printf ": "
if ! IFS= read -r reply; then
echo
[ "$3" = "required" ] && continue || reply=""
fi
case "$reply" in
"?") echo "$2" ;;
"") [ "$3" = "required" ] && continue || return 0 ;;
*) return 0 ;;
esac
done
}
# read_single, but optional
optional() {
read_single "$1" "$2" "optional"
}
# a
required() {
read_single "$1" "$2" "required"
}
# Basically the same as the single line function except multiline,
# also it's never "required" so we don't need that handling.
multi() {
echo "-- $1"
if [ -n "$2" ]; then
echo "-- (? on first line for help, Ctrl-D to finish)"
else
echo "-- (Ctrl-D to finish)"
fi
while :; do
reply=$(cat)
if [ "$(echo "$reply" | head -n 1)" = "?" ] && [ -n "$2" ]; then
echo "$2"
continue
fi
# removes trailing EOF and empty lines
reply=$(printf '%s\n' "$reply" |
sed 's/\x04$//' |
sed '/^[[:space:]]*$/d')
break
done
}
# the actual inputs :)
required "Package repository (owner/repo)" \
"The remote repository this is stored on.
You shouldn't include the host, just owner/repo is enough."
REPO="$reply"
optional "Package name for find_package" \
"When searching for system packages, this argument will be passed to find_package.
For example, using \"Boost\" here will result in CPMUtil internally calling find_package(Boost)."
PACKAGE="$reply"
optional "Minimum required version" \
"The minimum required version for this package if it's pulled in by the system."
MIN_VERSION="$reply"
optional "Additional find_package arguments, space-separated" \
"Extra arguments passed to find_package(), (e.g. CONFIG)"
FIND_ARGS="$reply"
optional "Is this a CI package? [y/N]" \
"Yes if the package is a prebuilt binary distribution (e.g. crueter-ci),
no if the package is built from source if it's bundled."
case "$reply" in
[Yy]*) CI=true ;;
*) CI=false ;;
esac
if [ "$CI" = "false" ]; then
optional "Git host (default: github.com)" \
"The hostname of the Git server, if not GitHub (e.g. codeberg.org, git.crueter.xyz)"
GIT_HOST="$reply"
if [ "$TAG" = "1" ]; then
required "Numeric version of the bundled package" \
"The semantic version of the bundled package. This is only used for package identification,
and if you use tag/artifact fetching. Do not input the entire tag here; for example, if you're using
tag v1.3.0, then set this to 1.3.0 and set the tag to v%VERSION%."
GIT_VERSION="$reply"
optional "Name of the upstream tag. %VERSION% is replaced by the numeric version (default: %VERSION%)" \
"Most commonly this will be something like v%VERSION% or release-%VERSION%, or just %VERSION%."
TAGNAME="$reply"
[ -z "$TAGNAME" ] && TAGNAME="%VERSION%"
optional "Name of the release artifact to download, if applicable.
-- %VERSION% is replaced by the numeric version and %TAG% is replaced by the tag name" \
"Download the specified artifact from the release with the previously specified tag.
If unspecified, the source code at the specified tag will be used instead."
ARTIFACT="$reply"
else
required "Commit sha" \
"The short Git commit sha to use. You're recommended to keep this short, e.g. 10 characters."
SHA="$reply"
fi
multi "Fixed options, one per line (e.g. OPUS_BUILD_TESTING OFF)" \
"Fixed options passed to the project's CMakeLists.txt. Variadic options
should be set in CMake with AddJsonPackage's OPTIONS parameter."
OPTIONS="$reply"
else
required "Version of the CI package (e.g. 3.6.0-9eff87adb1)" \
"CI artifacts are stored as <name>-<platform>-<version>.tar.zst. This option controls the version."
VERSION="$reply"
required "Name of the CI artifact" \
"CI artifacts are stored as <name>-<platform>-<version>.tar.zst. This option controls the name."
ARTIFACT="$reply"
multi "Platforms without a package (one per line)" \
"Valid platforms:
windows-amd64 windows-arm64
mingw-amd64 mingw-arm64
android-aarch64 android-x86_64
solaris-amd64 freebsd-amd64 openbsd-amd64
linux-amd64 linux-aarch64
macos-universal"
DISABLED_PLATFORMS="$reply"
fi
# now time to construct the actual json
jq_input='{repo: "'"$REPO"'"}'
# common trivial fields
[ -n "$PACKAGE" ] && jq_input="$jq_input + {package: \"$PACKAGE\"}"
[ -n "$MIN_VERSION" ] && jq_input="$jq_input + {min_version: \"$MIN_VERSION\"}"
[ -n "$FIND_ARGS" ] && jq_input="$jq_input + {find_args: \"$FIND_ARGS\"}"
if [ "$CI" = "true" ]; then
jq_input="$jq_input + {
ci: true,
version: \"$VERSION\",
artifact: \"$ARTIFACT\"
}"
# disabled platforms
if [ -n "$DISABLED_PLATFORMS" ] && [ -n "$(printf '%s' "$DISABLED_PLATFORMS" | tr -d ' \t\n\r')" ]; then
disabled_json=$(printf '%s\n' "$DISABLED_PLATFORMS" | jq -R . | jq -s .)
jq_input="$jq_input + {disabled_platforms: $disabled_json}"
fi
else
[ -n "$MIN_VERSION" ] && jq_input="$jq_input + {version: \"$MIN_VERSION\"}"
jq_input="$jq_input + {hash: \"\"}"
# options
if [ -n "$OPTIONS" ] && [ -n "$(printf '%s' "$OPTIONS" | tr -d ' \t\n\r')" ]; then
options_json=$(printf '%s\n' "$OPTIONS" | jq -R . | jq -s .)
jq_input="$jq_input + {options: $options_json}"
fi
# Git host
if [ -n "$GIT_HOST" ] && [ "$GIT_HOST" != "github.com" ]; then
jq_input="$jq_input + {git_host: \"$GIT_HOST\"}"
fi
# versioning stuff
if [ -n "$GIT_VERSION" ]; then
jq_input="$jq_input + {git_version: \"$GIT_VERSION\"}"
[ -n "$TAGNAME" ] && jq_input="$jq_input + {tag: \"$TAGNAME\"}"
[ -n "$ARTIFACT" ] && jq_input="$jq_input + {artifact: \"$ARTIFACT\"}"
else
jq_input="$jq_input + {sha: \"$SHA\"}"
fi
fi
new_json=$(jq -n "$jq_input")
jq --arg key "$PKG" --argjson new "$new_json" \
'.[$key] = $new' "$CPMFILE" --indent 4 >"${CPMFILE}.tmp" &&
mv "${CPMFILE}.tmp" "$CPMFILE"
# now correct the hash
if [ "$CI" != true ]; then
# shellcheck disable=SC1091
. "$ROOTDIR"/common.sh
QUIET=true UPDATE=true "$SCRIPTS"/util/fix-hash.sh "$PKG"
fi
echo "Added package $PKG to $CPMFILE. Include it in your project with AddJsonPackage($PKG)"

View File

@@ -0,0 +1,13 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
# Replace a specified package with a modified json.
FILE=$(echo "$CPMFILES" | xargs grep -l "\"$1\"")
jq --indent 4 --argjson repl "$2" ".\"$1\" *= \$repl" "$FILE" >"$FILE".new
mv "$FILE".new "$FILE"
echo "-- * -- Updated $FILE"

View File

@@ -0,0 +1,7 @@
#!/bin/sh
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
SUM=$(curl -Ls "$1" -o - | sha512sum)
echo "$SUM" | cut -d " " -f1

170
tools/cpm/package/vars.sh Executable file
View File

@@ -0,0 +1,170 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
# shellcheck disable=SC1091
value() {
echo "$JSON" | jq -r ".$1"
}
[ -z "$PACKAGE" ] && echo "Package was not specified" && exit 0
# shellcheck disable=SC2153
JSON=$(echo "$PACKAGES" | jq -r ".\"$PACKAGE\" | select( . != null )")
[ -z "$JSON" ] && echo "!! No cpmfile definition for $PACKAGE" >&2 && exit 1
# unset stuff
export PACKAGE_NAME="null"
export REPO="null"
export CI="null"
export GIT_HOST="null"
export EXT="null"
export NAME="null"
export DISABLED="null"
export TAG="null"
export ARTIFACT="null"
export SHA="null"
export VERSION="null"
export GIT_VERSION="null"
export DOWNLOAD="null"
export URL="null"
export KEY="null"
export HASH="null"
export ORIGINAL_TAG="null"
export HAS_REPLACE="null"
export VERSION_REPLACE="null"
export HASH_URL="null"
export HASH_SUFFIX="null"
export HASH_ALGO="null"
########
# Meta #
########
REPO=$(value "repo")
CI=$(value "ci")
PACKAGE_NAME=$(value "package")
[ "$PACKAGE_NAME" = null ] && PACKAGE_NAME="$PACKAGE"
GIT_HOST=$(value "git_host")
[ "$GIT_HOST" = null ] && GIT_HOST=github.com
export PACKAGE_NAME
export REPO
export CI
export GIT_HOST
######################
# CI Package Parsing #
######################
VERSION=$(value "version")
if [ "$CI" = "true" ]; then
EXT=$(value "extension")
[ "$EXT" = null ] && EXT="tar.zst"
NAME=$(value "name")
DISABLED=$(echo "$JSON" | jq -j '.disabled_platforms')
[ "$NAME" = null ] && NAME="$PACKAGE_NAME"
export EXT
export NAME
export DISABLED
export VERSION
return 0
fi
##############
# Versioning #
##############
TAG=$(value "tag")
ARTIFACT=$(value "artifact")
SHA=$(value "sha")
GIT_VERSION=$(value "git_version")
[ "$GIT_VERSION" = null ] && GIT_VERSION="$VERSION"
if [ "$GIT_VERSION" != null ]; then
VERSION_REPLACE="$GIT_VERSION"
else
VERSION_REPLACE="$VERSION"
fi
echo "$TAG" | grep -e "%VERSION%" >/dev/null &&
HAS_REPLACE=true || HAS_REPLACE=false
ORIGINAL_TAG="$TAG"
TAG=$(echo "$TAG" | sed "s/%VERSION%/$VERSION_REPLACE/g")
ARTIFACT=$(echo "$ARTIFACT" | sed "s/%VERSION%/$VERSION_REPLACE/g")
ARTIFACT=$(echo "$ARTIFACT" | sed "s/%TAG%/$TAG/g")
export TAG
export ARTIFACT
export SHA
export VERSION
export GIT_VERSION
export ORIGINAL_TAG
export HAS_REPLACE
export VERSION_REPLACE
###############
# URL Parsing #
###############
URL=$(value "url")
BRANCH=$(value "branch")
export BRANCH
export URL
. "$SCRIPTS"/vars/url.sh
export DOWNLOAD
###############
# Key Parsing #
###############
KEY=$(value "key")
. "$SCRIPTS"/vars/key.sh
export KEY
################
# Hash Parsing #
################
HASH_ALGO=$(value "hash_algo")
[ "$HASH_ALGO" = null ] && HASH_ALGO=sha512
HASH=$(value "hash")
if [ "$HASH" = null ]; then
HASH_SUFFIX="${HASH_ALGO}sum"
HASH_URL=$(value "hash_url")
if [ "$HASH_URL" = null ]; then
HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}"
fi
HASH=$(curl "$HASH_URL" -Ss -L -o -)
else
HASH_URL=null
HASH_SUFFIX=null
fi
export HASH_URL
export HASH_SUFFIX
export HASH
export HASH_ALGO
export JSON

21
tools/cpm/package/vars/key.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
if [ "$KEY" = null ]; then
if [ "$SHA" != null ]; then
KEY=$(echo "$SHA" | cut -c1-4)
elif [ "$GIT_VERSION" != null ]; then
KEY="$GIT_VERSION"
elif [ "$TAG" != null ]; then
KEY="$TAG"
elif [ "$VERSION" != null ]; then
KEY="$VERSION"
else
echo "!! No valid key could be determined for $PACKAGE_NAME. Must define one of: key, sha, tag, version, git_version"
exit 1
fi
fi
export KEY

32
tools/cpm/package/vars/url.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/sh
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
# Required vars: URL, GIT_HOST, REPO, TAG, ARTIFACT, BRANCH, SHA
if [ "$URL" != "null" ]; then
DOWNLOAD="$URL"
elif [ "$REPO" != "null" ]; then
GIT_URL="https://$GIT_HOST/$REPO"
if [ "$TAG" != "null" ]; then
if [ "$ARTIFACT" != "null" ]; then
DOWNLOAD="${GIT_URL}/releases/download/${TAG}/${ARTIFACT}"
else
DOWNLOAD="${GIT_URL}/archive/refs/tags/${TAG}.tar.gz"
fi
elif [ "$SHA" != "null" ]; then
DOWNLOAD="${GIT_URL}/archive/${SHA}.tar.gz"
else
if [ "$BRANCH" = null ]; then
BRANCH=master
fi
DOWNLOAD="${GIT_URL}/archive/refs/heads/${BRANCH}.tar.gz"
fi
else
echo "!! No repo or URL defined for $PACKAGE_NAME"
exit 1
fi
export DOWNLOAD

60
tools/cpm/package/version.sh Executable file
View File

@@ -0,0 +1,60 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
# shellcheck disable=SC1091
usage() {
cat <<EOF
Usage: cpmutil.sh package version [PACKAGE] [VERSION]
Update a package's version. If the package uses a sha, you must provide a sha,
and if the package uses a tag, you must provide the fully qualified tag.
EOF
exit 0
}
PACKAGE="$1"
NEW_VERSION="$2"
[ -z "$PACKAGE" ] && usage
[ -z "$NEW_VERSION" ] && usage
export PACKAGE
. "$SCRIPTS"/vars.sh
[ "$REPO" = null ] && exit 0
if [ "$HAS_REPLACE" = "true" ]; then
# this just extracts the tag prefix
VERSION_PREFIX=$(echo "$ORIGINAL_TAG" | cut -d"%" -f1)
# then we strip out the prefix from the new tag, and make that our new git_version
if [ -z "$VERSION_PREFIX" ]; then
NEW_GIT_VERSION="$NEW_VERSION"
else
NEW_GIT_VERSION=$(echo "$NEW_VERSION" | sed "s/$VERSION_PREFIX//g")
fi
fi
if [ "$SHA" != null ]; then
NEW_JSON=$(echo "$JSON" | jq ".sha = \"$NEW_VERSION\"")
elif [ "$CI" = "true" ]; then
NEW_JSON=$(echo "$JSON" | jq ".version = \"$NEW_VERSION\"")
elif [ "$HAS_REPLACE" = "true" ]; then
NEW_JSON=$(echo "$JSON" | jq ".git_version = \"$NEW_GIT_VERSION\"")
else
NEW_JSON=$(echo "$JSON" | jq ".tag = \"$NEW_VERSION\"")
fi
echo "-- * -- Updating $PACKAGE to version $NEW_VERSION"
"$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON"
[ "$CI" = "true" ] && exit 0
echo "-- * -- Fixing hash"
. "$ROOTDIR"/common.sh
UPDATE=true QUIET=true "$SCRIPTS"/util/fix-hash.sh

12
tools/cpm/package/which.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
# check which file a package is in
JSON=$(echo "$CPMFILES" | xargs grep -l "\"$1\"")
[ -z "$JSON" ] && echo "!! No cpmfile definition for $1" >&2 && exit 1
echo "$JSON"

View File

@@ -1,20 +0,0 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# Replace a specified package with a modified json.
# env vars:
# - PACKAGE: The package key to act on
# - NEW_JSON: The new json to use
[ -z "$PACKAGE" ] && echo "You must provide the PACKAGE environment variable." && return 1
[ -z "$NEW_JSON" ] && echo "You must provide the NEW_JSON environment variable." && return 1
FILE=$(tools/cpm/which.sh "$PACKAGE")
jq --indent 4 --argjson repl "$NEW_JSON" ".\"$PACKAGE\" *= \$repl" "$FILE" > "$FILE".new
mv "$FILE".new "$FILE"
echo "-- * -- Updated $FILE"

View File

@@ -1,14 +1,14 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-License-Identifier: LGPL-3.0-or-later
# updates CPMUtil, its docs, and related tools from the latest release
if command -v zstd > /dev/null; then
EXT=tar.zst
if command -v zstd >/dev/null; then
EXT=tar.zst
else
EXT=tar.gz
EXT=tar.gz
fi
wget "https://git.crueter.xyz/CMake/CPMUtil/releases/download/continuous/CPMUtil.$EXT"

View File

@@ -1,7 +0,0 @@
#!/bin/sh
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
SUM=$(wget -q "$1" -O - | sha512sum)
echo "$SUM" | cut -d " " -f1

View File

@@ -1,15 +0,0 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
# check which file a package is in
# shellcheck disable=SC1091
. tools/cpm/common.sh
# shellcheck disable=SC2086
JSON=$(echo "$CPMFILES" | xargs grep -l "$1")
[ -z "$JSON" ] && echo "!! No cpmfile definition for $1" >&2 && exit 1
echo "$JSON"

Some files were not shown because too many files have changed in this diff Show More