Compare commits
9 Commits
dmnt2
...
update-cpm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8656da2fa | ||
|
|
5f75aceace | ||
|
|
7bb30a1086 | ||
|
|
f532611a50 | ||
|
|
d754fca70f | ||
|
|
c5bd7dc047 | ||
|
|
557876b222 | ||
|
|
22dfc560e0 | ||
|
|
1d869e8495 |
@@ -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()
|
||||
|
||||
@@ -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`
|
||||
@@ -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.
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"},
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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>
|
||||
|
||||
286
src/core/memory/cheat_engine.cpp
Normal file
286
src/core/memory/cheat_engine.cpp
Normal 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
|
||||
88
src/core/memory/cheat_engine.h
Normal file
88
src/core/memory/cheat_engine.h
Normal 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
|
||||
37
src/core/memory/dmnt_cheat_types.h
Normal file
37
src/core/memory/dmnt_cheat_types.h
Normal 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
|
||||
1268
src/core/memory/dmnt_cheat_vm.cpp
Normal file
1268
src/core/memory/dmnt_cheat_vm.cpp
Normal file
File diff suppressed because it is too large
Load Diff
330
src/core/memory/dmnt_cheat_vm.h
Normal file
330
src/core/memory/dmnt_cheat_vm.h
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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
47
tools/cpm/migrate.sh
Executable 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"
|
||||
@@ -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
79
tools/cpm/package/add.sh
Executable 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
46
tools/cpm/package/download.sh
Executable 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
156
tools/cpm/package/fetch.sh
Executable 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
66
tools/cpm/package/hash.sh
Executable 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
30
tools/cpm/package/rm.sh
Executable 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
|
||||
@@ -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
|
||||
30
tools/cpm/package/util/fix-hash.sh
Executable file
30
tools/cpm/package/util/fix-hash.sh
Executable 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
|
||||
217
tools/cpm/package/util/interactive.sh
Executable file
217
tools/cpm/package/util/interactive.sh
Executable 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)"
|
||||
13
tools/cpm/package/util/replace.sh
Executable file
13
tools/cpm/package/util/replace.sh
Executable 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"
|
||||
7
tools/cpm/package/util/url-hash.sh
Executable file
7
tools/cpm/package/util/url-hash.sh
Executable 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
170
tools/cpm/package/vars.sh
Executable 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
21
tools/cpm/package/vars/key.sh
Executable 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
32
tools/cpm/package/vars/url.sh
Executable 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
60
tools/cpm/package/version.sh
Executable 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
12
tools/cpm/package/which.sh
Executable 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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user