Compare commits

..

3 Commits

Author SHA1 Message Date
fb36f6d5e4 Android #138 2023-11-22 00:57:40 +00:00
bc5af008ff Merge PR 12074 2023-11-22 00:57:40 +00:00
14a1598a34 Merge PR 11535 2023-11-22 00:57:40 +00:00
196 changed files with 5342 additions and 6475 deletions

View File

@ -68,25 +68,6 @@ jobs:
with: with:
name: ${{ matrix.type }} name: ${{ matrix.type }}
path: artifacts/ path: artifacts/
build-mac:
name: 'test build (macos)'
needs: format
runs-on: macos-13
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
fetch-depth: 0
- name: Install dependencies
run: |
brew install autoconf automake boost@1.83 ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@5 sdl2 speexdsp zlib zlib zstd
- name: Build
run: |
mkdir build
cd build
export Qt5_DIR="/usr/local/opt/qt@5/lib/cmake"
cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_USE_BUNDLED_VCPKG=OFF -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_LIBUSB=OFF
ninja
build-msvc: build-msvc:
name: 'test build (windows, msvc)' name: 'test build (windows, msvc)'
needs: format needs: format

6
.gitmodules vendored
View File

@ -4,6 +4,9 @@
[submodule "enet"] [submodule "enet"]
path = externals/enet path = externals/enet
url = https://github.com/lsalzman/enet.git url = https://github.com/lsalzman/enet.git
[submodule "inih"]
path = externals/inih/inih
url = https://github.com/benhoyt/inih.git
[submodule "cubeb"] [submodule "cubeb"]
path = externals/cubeb path = externals/cubeb
url = https://github.com/mozilla/cubeb.git url = https://github.com/mozilla/cubeb.git
@ -58,9 +61,6 @@
[submodule "breakpad"] [submodule "breakpad"]
path = externals/breakpad path = externals/breakpad
url = https://github.com/yuzu-emu/breakpad.git url = https://github.com/yuzu-emu/breakpad.git
[submodule "simpleini"]
path = externals/simpleini
url = https://github.com/brofield/simpleini.git
[submodule "oaknut"] [submodule "oaknut"]
path = externals/oaknut path = externals/oaknut
url = https://github.com/merryhime/oaknut url = https://github.com/merryhime/oaknut

View File

@ -151,7 +151,3 @@ License: GPL-3.0-or-later
Files: externals/stb/* Files: externals/stb/*
Copyright: Sean Barrett Copyright: Sean Barrett
License: MIT License: MIT
Files: externals/gamemode/*
Copyright: Copyright 2017-2019 Feral Interactive
License: BSD-3-Clause

View File

@ -260,11 +260,6 @@ if (UNIX)
add_definitions(-DYUZU_UNIX=1) add_definitions(-DYUZU_UNIX=1)
endif() endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux"))
set(HAS_NCE 1)
add_definitions(-DHAS_NCE=1)
endif()
# Configure C++ standard # Configure C++ standard
# =========================== # ===========================
@ -290,12 +285,12 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
find_package(Boost 1.79.0 REQUIRED context) find_package(Boost 1.79.0 REQUIRED context)
find_package(enet 1.3 MODULE) find_package(enet 1.3 MODULE)
find_package(fmt 9 REQUIRED) find_package(fmt 9 REQUIRED)
find_package(inih 52 MODULE COMPONENTS INIReader)
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle) find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
find_package(lz4 REQUIRED) find_package(lz4 REQUIRED)
find_package(nlohmann_json 3.8 REQUIRED) find_package(nlohmann_json 3.8 REQUIRED)
find_package(Opus 1.3 MODULE) find_package(Opus 1.3 MODULE)
find_package(RenderDoc MODULE) find_package(RenderDoc MODULE)
find_package(SimpleIni MODULE)
find_package(stb MODULE) find_package(stb MODULE)
find_package(VulkanMemoryAllocator CONFIG) find_package(VulkanMemoryAllocator CONFIG)
find_package(ZLIB 1.2 REQUIRED) find_package(ZLIB 1.2 REQUIRED)
@ -343,10 +338,6 @@ if(ENABLE_OPENSSL)
find_package(OpenSSL 1.1.1 REQUIRED) find_package(OpenSSL 1.1.1 REQUIRED)
endif() endif()
if (UNIX AND NOT APPLE)
find_package(gamemode 1.7 MODULE)
endif()
# Please consider this as a stub # Please consider this as a stub
if(ENABLE_QT6 AND Qt6_LOCATION) if(ENABLE_QT6 AND Qt6_LOCATION)
list(APPEND CMAKE_PREFIX_PATH "${Qt6_LOCATION}") list(APPEND CMAKE_PREFIX_PATH "${Qt6_LOCATION}")

View File

@ -1,19 +0,0 @@
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
find_path(SimpleIni_INCLUDE_DIR SimpleIni.h)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SimpleIni
REQUIRED_VARS SimpleIni_INCLUDE_DIR
)
if (SimpleIni_FOUND AND NOT TARGET SimpleIni::SimpleIni)
add_library(SimpleIni::SimpleIni INTERFACE IMPORTED)
set_target_properties(SimpleIni::SimpleIni PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${SimpleIni_INCLUDE_DIR}"
)
endif()
mark_as_advanced(SimpleIni_INCLUDE_DIR)

View File

@ -1,15 +0,0 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
find_package(PkgConfig QUIET)
pkg_search_module(GAMEMODE QUIET IMPORTED_TARGET gamemode)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(gamemode
REQUIRED_VARS GAMEMODE_INCLUDEDIR
VERSION_VAR GAMEMODE_VERSION
)
if (gamemode_FOUND AND NOT TARGET gamemode::headers)
add_library(gamemode::headers ALIAS PkgConfig::GAMEMODE)
endif()

View File

@ -0,0 +1,27 @@
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
find_package(PkgConfig QUIET)
pkg_search_module(INIH QUIET IMPORTED_TARGET inih)
if (INIReader IN_LIST inih_FIND_COMPONENTS)
pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader)
if (INIREADER_FOUND)
set(inih_INIReader_FOUND TRUE)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(inih
REQUIRED_VARS INIH_LINK_LIBRARIES
VERSION_VAR INIH_VERSION
HANDLE_COMPONENTS
)
if (inih_FOUND AND NOT TARGET inih::inih)
add_library(inih::inih ALIAS PkgConfig::INIH)
endif()
if (inih_FOUND AND inih_INIReader_FOUND AND NOT TARGET inih::INIReader)
add_library(inih::INIReader ALIAS PkgConfig::INIREADER)
endif()

View File

@ -1,6 +1,7 @@
| Pull Request | Commit | Title | Author | Merged? | | Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----| |----|----|----|----|----|
| [12235](https://github.com/yuzu-emu/yuzu//pull/12235) | [`e7dd968ac`](https://github.com/yuzu-emu/yuzu//pull/12235/files) | renderer_vulkan: adjust window origin and swizzle independently | [liamwhite](https://github.com/liamwhite/) | Yes | | [11535](https://github.com/yuzu-emu/yuzu//pull/11535) | [`50bcfa5fb`](https://github.com/yuzu-emu/yuzu//pull/11535/files) | renderer_vulkan: Introduce separate cmd buffer for uploads | [GPUCode](https://github.com/GPUCode/) | Yes |
| [12074](https://github.com/yuzu-emu/yuzu//pull/12074) | [`36fccd7cc`](https://github.com/yuzu-emu/yuzu//pull/12074/files) | Implement Native Code Execution (NCE) | [GPUCode](https://github.com/GPUCode/) | Yes |
End of merge log. You can find the original README.md below the break. End of merge log. You can find the original README.md below the break.

View File

@ -38,6 +38,11 @@ endif()
# Glad # Glad
add_subdirectory(glad) add_subdirectory(glad)
# inih
if (NOT TARGET inih::INIReader)
add_subdirectory(inih)
endif()
# mbedtls # mbedtls
add_subdirectory(mbedtls) add_subdirectory(mbedtls)
target_include_directories(mbedtls PUBLIC ./mbedtls/include) target_include_directories(mbedtls PUBLIC ./mbedtls/include)
@ -193,12 +198,6 @@ if (ANDROID)
endif() endif()
endif() endif()
if (UNIX AND NOT APPLE AND NOT TARGET gamemode::headers)
add_library(gamemode INTERFACE)
target_include_directories(gamemode INTERFACE gamemode)
add_library(gamemode::headers ALIAS gamemode)
endif()
# Breakpad # Breakpad
# https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt # https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt
if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client) if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
@ -300,8 +299,3 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB) target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
endif() endif()
endif() endif()
# SimpleIni
if (NOT TARGET SimpleIni::SimpleIni)
add_subdirectory(simpleini)
endif()

View File

@ -1,376 +0,0 @@
/*
Copyright (c) 2017-2019, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CLIENT_GAMEMODE_H
#define CLIENT_GAMEMODE_H
/*
* GameMode supports the following client functions
* Requests are refcounted in the daemon
*
* int gamemode_request_start() - Request gamemode starts
* 0 if the request was sent successfully
* -1 if the request failed
*
* int gamemode_request_end() - Request gamemode ends
* 0 if the request was sent successfully
* -1 if the request failed
*
* GAMEMODE_AUTO can be defined to make the above two functions apply during static init and
* destruction, as appropriate. In this configuration, errors will be printed to stderr
*
* int gamemode_query_status() - Query the current status of gamemode
* 0 if gamemode is inactive
* 1 if gamemode is active
* 2 if gamemode is active and this client is registered
* -1 if the query failed
*
* int gamemode_request_start_for(pid_t pid) - Request gamemode starts for another process
* 0 if the request was sent successfully
* -1 if the request failed
* -2 if the request was rejected
*
* int gamemode_request_end_for(pid_t pid) - Request gamemode ends for another process
* 0 if the request was sent successfully
* -1 if the request failed
* -2 if the request was rejected
*
* int gamemode_query_status_for(pid_t pid) - Query status of gamemode for another process
* 0 if gamemode is inactive
* 1 if gamemode is active
* 2 if gamemode is active and this client is registered
* -1 if the query failed
*
* const char* gamemode_error_string() - Get an error string
* returns a string describing any of the above errors
*
* Note: All the above requests can be blocking - dbus requests can and will block while the daemon
* handles the request. It is not recommended to make these calls in performance critical code
*/
#include <stdbool.h>
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
static char internal_gamemode_client_error_string[512] = { 0 };
/**
* Load libgamemode dynamically to dislodge us from most dependencies.
* This allows clients to link and/or use this regardless of runtime.
* See SDL2 for an example of the reasoning behind this in terms of
* dynamic versioning as well.
*/
static volatile int internal_libgamemode_loaded = 1;
/* Typedefs for the functions to load */
typedef int (*api_call_return_int)(void);
typedef const char *(*api_call_return_cstring)(void);
typedef int (*api_call_pid_return_int)(pid_t);
/* Storage for functors */
static api_call_return_int REAL_internal_gamemode_request_start = NULL;
static api_call_return_int REAL_internal_gamemode_request_end = NULL;
static api_call_return_int REAL_internal_gamemode_query_status = NULL;
static api_call_return_cstring REAL_internal_gamemode_error_string = NULL;
static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL;
static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL;
static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL;
/**
* Internal helper to perform the symbol binding safely.
*
* Returns 0 on success and -1 on failure
*/
__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol(
void *handle, const char *name, void **out_func, size_t func_size, bool required)
{
void *symbol_lookup = NULL;
char *dl_error = NULL;
/* Safely look up the symbol */
symbol_lookup = dlsym(handle, name);
dl_error = dlerror();
if (required && (dl_error || !symbol_lookup)) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"dlsym failed - %s",
dl_error);
return -1;
}
/* Have the symbol correctly, copy it to make it usable */
memcpy(out_func, &symbol_lookup, func_size);
return 0;
}
/**
* Loads libgamemode and needed functions
*
* Returns 0 on success and -1 on failure
*/
__attribute__((always_inline)) static inline int internal_load_libgamemode(void)
{
/* We start at 1, 0 is a success and -1 is a fail */
if (internal_libgamemode_loaded != 1) {
return internal_libgamemode_loaded;
}
/* Anonymous struct type to define our bindings */
struct binding {
const char *name;
void **functor;
size_t func_size;
bool required;
} bindings[] = {
{ "real_gamemode_request_start",
(void **)&REAL_internal_gamemode_request_start,
sizeof(REAL_internal_gamemode_request_start),
true },
{ "real_gamemode_request_end",
(void **)&REAL_internal_gamemode_request_end,
sizeof(REAL_internal_gamemode_request_end),
true },
{ "real_gamemode_query_status",
(void **)&REAL_internal_gamemode_query_status,
sizeof(REAL_internal_gamemode_query_status),
false },
{ "real_gamemode_error_string",
(void **)&REAL_internal_gamemode_error_string,
sizeof(REAL_internal_gamemode_error_string),
true },
{ "real_gamemode_request_start_for",
(void **)&REAL_internal_gamemode_request_start_for,
sizeof(REAL_internal_gamemode_request_start_for),
false },
{ "real_gamemode_request_end_for",
(void **)&REAL_internal_gamemode_request_end_for,
sizeof(REAL_internal_gamemode_request_end_for),
false },
{ "real_gamemode_query_status_for",
(void **)&REAL_internal_gamemode_query_status_for,
sizeof(REAL_internal_gamemode_query_status_for),
false },
};
void *libgamemode = NULL;
/* Try and load libgamemode */
libgamemode = dlopen("libgamemode.so.0", RTLD_NOW);
if (!libgamemode) {
/* Attempt to load unversioned library for compatibility with older
* versions (as of writing, there are no ABI changes between the two -
* this may need to change if ever ABI-breaking changes are made) */
libgamemode = dlopen("libgamemode.so", RTLD_NOW);
if (!libgamemode) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"dlopen failed - %s",
dlerror());
internal_libgamemode_loaded = -1;
return -1;
}
}
/* Attempt to bind all symbols */
for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) {
struct binding *binder = &bindings[i];
if (internal_bind_libgamemode_symbol(libgamemode,
binder->name,
binder->functor,
binder->func_size,
binder->required)) {
internal_libgamemode_loaded = -1;
return -1;
};
}
/* Success */
internal_libgamemode_loaded = 0;
return 0;
}
/**
* Redirect to the real libgamemode
*/
__attribute__((always_inline)) static inline const char *gamemode_error_string(void)
{
/* If we fail to load the system gamemode, or we have an error string already, return our error
* string instead of diverting to the system version */
if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') {
return internal_gamemode_client_error_string;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_error_string != NULL);
return REAL_internal_gamemode_error_string();
}
/**
* Redirect to the real libgamemode
* Allow automatically requesting game mode
* Also prints errors as they happen.
*/
#ifdef GAMEMODE_AUTO
__attribute__((constructor))
#else
__attribute__((always_inline)) static inline
#endif
int gamemode_request_start(void)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_request_start != NULL);
if (REAL_internal_gamemode_request_start() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
return 0;
}
/* Redirect to the real libgamemode */
#ifdef GAMEMODE_AUTO
__attribute__((destructor))
#else
__attribute__((always_inline)) static inline
#endif
int gamemode_request_end(void)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_request_end != NULL);
if (REAL_internal_gamemode_request_end() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
return 0;
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_query_status(void)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_query_status == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_query_status missing (older host?)");
return -1;
}
return REAL_internal_gamemode_query_status();
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_request_start_for == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_request_start_for missing (older host?)");
return -1;
}
return REAL_internal_gamemode_request_start_for(pid);
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_request_end_for == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_request_end_for missing (older host?)");
return -1;
}
return REAL_internal_gamemode_request_end_for(pid);
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_query_status_for == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_query_status_for missing (older host?)");
return -1;
}
return REAL_internal_gamemode_query_status_for(pid);
}
#endif // CLIENT_GAMEMODE_H

13
externals/inih/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2014 Gui Andrade <admin@archshift.com>
# SPDX-License-Identifier: GPL-2.0-or-later
add_library(inih
inih/ini.c
inih/ini.h
inih/cpp/INIReader.cpp
inih/cpp/INIReader.h
)
create_target_directory_groups(inih)
target_include_directories(inih INTERFACE inih/cpp)
add_library(inih::INIReader ALIAS inih)

1
externals/inih/inih vendored Submodule

Submodule externals/inih/inih added at 9cecf0643d

1
externals/simpleini vendored

Submodule externals/simpleini deleted from 382ddbb4b9

View File

@ -187,7 +187,6 @@ add_subdirectory(audio_core)
add_subdirectory(video_core) add_subdirectory(video_core)
add_subdirectory(network) add_subdirectory(network)
add_subdirectory(input_common) add_subdirectory(input_common)
add_subdirectory(frontend_common)
add_subdirectory(shader_recompiler) add_subdirectory(shader_recompiler)
if (YUZU_ROOM) if (YUZU_ROOM)

View File

@ -219,6 +219,7 @@ dependencies {
implementation("io.coil-kt:coil:2.2.2") implementation("io.coil-kt:coil:2.2.2")
implementation("androidx.core:core-splashscreen:1.0.1") implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.window:window:1.2.0-beta03") implementation("androidx.window:window:1.2.0-beta03")
implementation("org.ini4j:ini4j:0.5.4")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4") implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")

View File

@ -230,6 +230,8 @@ object NativeLibrary {
*/ */
external fun onTouchReleased(finger_id: Int) external fun onTouchReleased(finger_id: Int)
external fun reloadSettings()
external fun initGameIni(gameID: String?) external fun initGameIni(gameID: String?)
external fun setAppDirectory(directory: String) external fun setAppDirectory(directory: String)

View File

@ -1,76 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.net.Uri
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.CardFolderBinding
import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel
class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
ListAdapter<GameDir, FolderAdapter.FolderViewHolder>(
AsyncDifferConfig.Builder(DiffCallback()).build()
) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): FolderAdapter.FolderViewHolder {
CardFolderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return FolderViewHolder(it) }
}
override fun onBindViewHolder(holder: FolderAdapter.FolderViewHolder, position: Int) =
holder.bind(currentList[position])
inner class FolderViewHolder(val binding: CardFolderBinding) :
RecyclerView.ViewHolder(binding.root) {
private lateinit var gameDir: GameDir
fun bind(gameDir: GameDir) {
this.gameDir = gameDir
binding.apply {
path.text = Uri.parse(gameDir.uriString).path
path.postDelayed(
{
path.isSelected = true
path.ellipsize = TextUtils.TruncateAt.MARQUEE
},
3000
)
buttonEdit.setOnClickListener {
GameFolderPropertiesDialogFragment.newInstance(this@FolderViewHolder.gameDir)
.show(
activity.supportFragmentManager,
GameFolderPropertiesDialogFragment.TAG
)
}
buttonDelete.setOnClickListener {
gamesViewModel.removeFolder(this@FolderViewHolder.gameDir)
}
}
}
}
private class DiffCallback : DiffUtil.ItemCallback<GameDir>() {
override fun areItemsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
return oldItem == newItem
}
}
}

View File

@ -3,9 +3,33 @@
package org.yuzu.yuzu_emu.features.settings.model package org.yuzu.yuzu_emu.features.settings.model
import android.text.TextUtils
import android.widget.Toast
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
object Settings { object Settings {
private val context get() = YuzuApplication.appContext
fun saveSettings(gameId: String = "") {
if (TextUtils.isEmpty(gameId)) {
Toast.makeText(
context,
context.getString(R.string.ini_saved),
Toast.LENGTH_SHORT
).show()
SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG)
} else {
// TODO: Save custom game settings
Toast.makeText(
context,
context.getString(R.string.gameid_saved, gameId),
Toast.LENGTH_SHORT
).show()
}
}
enum class Category { enum class Category {
Android, Android,
Audio, Audio,

View File

@ -82,8 +82,8 @@ abstract class SettingsItem(
IntSetting.CPU_BACKEND, IntSetting.CPU_BACKEND,
R.string.cpu_backend, R.string.cpu_backend,
0, 0,
R.array.cpuBackendArm64Names, R.array.cpuBackendNames,
R.array.cpuBackendArm64Values R.array.cpuBackendValues
) )
) )
put( put(

View File

@ -19,13 +19,13 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.navArgs import androidx.navigation.navArgs
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException import java.io.IOException
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
import org.yuzu.yuzu_emu.model.SettingsViewModel import org.yuzu.yuzu_emu.model.SettingsViewModel
@ -54,6 +54,10 @@ class SettingsActivity : AppCompatActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
if (savedInstanceState != null) {
settingsViewModel.shouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE)
}
if (InsetsHelper.getSystemGestureType(applicationContext) != if (InsetsHelper.getSystemGestureType(applicationContext) !=
InsetsHelper.GESTURE_NAVIGATION InsetsHelper.GESTURE_NAVIGATION
) { ) {
@ -124,6 +128,12 @@ class SettingsActivity : AppCompatActivity() {
} }
} }
override fun onSaveInstanceState(outState: Bundle) {
// Critical: If super method is not called, rotations will be busted.
super.onSaveInstanceState(outState)
outState.putBoolean(KEY_SHOULD_SAVE, settingsViewModel.shouldSave)
}
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
// TODO: Load custom settings contextually // TODO: Load custom settings contextually
@ -132,10 +142,16 @@ class SettingsActivity : AppCompatActivity() {
} }
} }
/**
* If this is called, the user has left the settings screen (potentially through the
* home button) and will expect their changes to be persisted. So we kick off an
* IntentService which will do so on a background thread.
*/
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
CoroutineScope(Dispatchers.IO).launch { if (isFinishing && settingsViewModel.shouldSave) {
NativeConfig.saveSettings() Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
Settings.saveSettings()
} }
} }
@ -145,13 +161,15 @@ class SettingsActivity : AppCompatActivity() {
} }
fun onSettingsReset() { fun onSettingsReset() {
// Prevents saving to a non-existent settings file
settingsViewModel.shouldSave = false
// Delete settings file because the user may have changed values that do not exist in the UI // Delete settings file because the user may have changed values that do not exist in the UI
NativeConfig.unloadConfig()
val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
if (!settingsFile.delete()) { if (!settingsFile.delete()) {
throw IOException("Failed to delete $settingsFile") throw IOException("Failed to delete $settingsFile")
} }
NativeConfig.initializeConfig() NativeLibrary.reloadSettings()
Toast.makeText( Toast.makeText(
applicationContext, applicationContext,
@ -176,4 +194,8 @@ class SettingsActivity : AppCompatActivity() {
windowInsets windowInsets
} }
} }
companion object {
private const val KEY_SHOULD_SAVE = "should_save"
}
} }

View File

@ -105,6 +105,7 @@ class SettingsAdapter(
fun onBooleanClick(item: SwitchSetting, checked: Boolean) { fun onBooleanClick(item: SwitchSetting, checked: Boolean) {
item.checked = checked item.checked = checked
settingsViewModel.setShouldReloadSettingsList(true) settingsViewModel.setShouldReloadSettingsList(true)
settingsViewModel.shouldSave = true
} }
fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) { fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) {
@ -160,6 +161,7 @@ class SettingsAdapter(
epochTime += timePicker.hour.toLong() * 60 * 60 epochTime += timePicker.hour.toLong() * 60 * 60
epochTime += timePicker.minute.toLong() * 60 epochTime += timePicker.minute.toLong() * 60
if (item.value != epochTime) { if (item.value != epochTime) {
settingsViewModel.shouldSave = true
notifyItemChanged(position) notifyItemChanged(position)
item.value = epochTime item.value = epochTime
} }

View File

@ -3,8 +3,15 @@
package org.yuzu.yuzu_emu.features.settings.utils package org.yuzu.yuzu_emu.features.settings.utils
import android.widget.Toast
import java.io.* import java.io.*
import org.ini4j.Wini
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.*
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.NativeConfig
/** /**
* Contains static methods for interacting with .ini files in which settings are stored. * Contains static methods for interacting with .ini files in which settings are stored.
@ -12,6 +19,41 @@ import org.yuzu.yuzu_emu.utils.DirectoryInitialization
object SettingsFile { object SettingsFile {
const val FILE_NAME_CONFIG = "config" const val FILE_NAME_CONFIG = "config"
/**
* Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
* telling why it failed.
*
* @param fileName The target filename without a path or extension.
*/
fun saveFile(fileName: String) {
val ini = getSettingsFile(fileName)
try {
val wini = Wini(ini)
for (specificCategory in Settings.Category.values()) {
val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal)
for (setting in Settings.settingsList) {
if (setting.key!!.isEmpty()) continue
val settingCategoryHeader =
NativeConfig.getConfigHeader(setting.category.ordinal)
val iniSetting: String? = wini.get(categoryHeader, setting.key)
if (iniSetting != null || settingCategoryHeader == categoryHeader) {
wini.put(settingCategoryHeader, setting.key, setting.valueAsString)
}
}
}
wini.store()
} catch (e: IOException) {
Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message)
val context = YuzuApplication.appContext
Toast.makeText(
context,
context.getString(R.string.error_saving, fileName, e.message),
Toast.LENGTH_SHORT
).show()
}
}
fun getSettingsFile(fileName: String): File = fun getSettingsFile(fileName: String): File =
File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini") File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
} }

View File

@ -1,53 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.content.DialogInterface
import android.net.Uri
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogAddFolderBinding
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel
class AddGameFolderDialogFragment : DialogFragment() {
private val gamesViewModel: GamesViewModel by activityViewModels()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val binding = DialogAddFolderBinding.inflate(layoutInflater)
val folderUriString = requireArguments().getString(FOLDER_URI_STRING)
if (folderUriString == null) {
dismiss()
}
binding.path.text = Uri.parse(folderUriString).path
return MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.add_game_folder)
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
val newGameDir = GameDir(folderUriString!!, binding.deepScanSwitch.isChecked)
gamesViewModel.addFolder(newGameDir)
}
.setNegativeButton(android.R.string.cancel, null)
.setView(binding.root)
.show()
}
companion object {
const val TAG = "AddGameFolderDialogFragment"
private const val FOLDER_URI_STRING = "FolderUriString"
fun newInstance(folderUriString: String): AddGameFolderDialogFragment {
val args = Bundle()
args.putString(FOLDER_URI_STRING, folderUriString)
val fragment = AddGameFolderDialogFragment()
fragment.arguments = args
return fragment
}
}
}

View File

@ -1,72 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogFolderPropertiesBinding
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
class GameFolderPropertiesDialogFragment : DialogFragment() {
private val gamesViewModel: GamesViewModel by activityViewModels()
private var deepScan = false
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val binding = DialogFolderPropertiesBinding.inflate(layoutInflater)
val gameDir = requireArguments().parcelable<GameDir>(GAME_DIR)!!
// Restore checkbox state
binding.deepScanSwitch.isChecked =
savedInstanceState?.getBoolean(DEEP_SCAN) ?: gameDir.deepScan
// Ensure that we can get the checkbox state even if the view is destroyed
deepScan = binding.deepScanSwitch.isChecked
binding.deepScanSwitch.setOnClickListener {
deepScan = binding.deepScanSwitch.isChecked
}
return MaterialAlertDialogBuilder(requireContext())
.setView(binding.root)
.setTitle(R.string.game_folder_properties)
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
val folderIndex = gamesViewModel.folders.value.indexOf(gameDir)
if (folderIndex != -1) {
gamesViewModel.folders.value[folderIndex].deepScan =
binding.deepScanSwitch.isChecked
gamesViewModel.updateGameDirs()
}
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(DEEP_SCAN, deepScan)
}
companion object {
const val TAG = "GameFolderPropertiesDialogFragment"
private const val GAME_DIR = "GameDir"
private const val DEEP_SCAN = "DeepScan"
fun newInstance(gameDir: GameDir): GameFolderPropertiesDialogFragment {
val args = Bundle()
args.putParcelable(GAME_DIR, gameDir)
val fragment = GameFolderPropertiesDialogFragment()
fragment.arguments = args
return fragment
}
}
}

View File

@ -1,128 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.FolderAdapter
import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
class GameFoldersFragment : Fragment() {
private var _binding: FragmentFoldersBinding? = null
private val binding get() = _binding!!
private val homeViewModel: HomeViewModel by activityViewModels()
private val gamesViewModel: GamesViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
gamesViewModel.onOpenGameFoldersFragment()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentFoldersBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true)
homeViewModel.setStatusBarShadeVisibility(visible = false)
binding.toolbarFolders.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
}
binding.listFolders.apply {
layoutManager = GridLayoutManager(
requireContext(),
resources.getInteger(R.integer.grid_columns)
)
adapter = FolderAdapter(requireActivity(), gamesViewModel)
}
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
gamesViewModel.folders.collect {
(binding.listFolders.adapter as FolderAdapter).submitList(it)
}
}
}
val mainActivity = requireActivity() as MainActivity
binding.buttonAdd.setOnClickListener {
mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
}
setInsets()
}
override fun onStop() {
super.onStop()
gamesViewModel.onCloseGameFoldersFragment()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarFolders.layoutParams as ViewGroup.MarginLayoutParams
mlpToolbar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets
binding.toolbarFolders.layoutParams = mlpToolbar
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonAdd.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonAdd.layoutParams = mlpFab
val mlpListFolders = binding.listFolders.layoutParams as ViewGroup.MarginLayoutParams
mlpListFolders.leftMargin = leftInsets
mlpListFolders.rightMargin = rightInsets
binding.listFolders.layoutParams = mlpListFolders
binding.listFolders.updatePadding(
bottom = barInsets.bottom +
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
)
windowInsets
}
}

View File

@ -127,13 +127,18 @@ class HomeSettingsFragment : Fragment() {
) )
add( add(
HomeSetting( HomeSetting(
R.string.manage_game_folders, R.string.select_games_folder,
R.string.select_games_folder_description, R.string.select_games_folder_description,
R.drawable.ic_add, R.drawable.ic_add,
{ {
binding.root.findNavController() mainActivity.getGamesDirectory.launch(
.navigate(R.id.action_homeSettingsFragment_to_gameFoldersFragment) Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
} )
},
{ true },
0,
0,
homeViewModel.gamesDir
) )
) )
add( add(

View File

@ -52,6 +52,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
settingsViewModel.clickedItem!!.setting.reset() settingsViewModel.clickedItem!!.setting.reset()
settingsViewModel.setAdapterItemChanged(position) settingsViewModel.setAdapterItemChanged(position)
settingsViewModel.shouldSave = true
} }
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.create() .create()
@ -136,17 +137,24 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
is SingleChoiceSetting -> { is SingleChoiceSetting -> {
val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting
val value = getValueForSingleChoiceSelection(scSetting, which) val value = getValueForSingleChoiceSelection(scSetting, which)
if (scSetting.selectedValue != value) {
settingsViewModel.shouldSave = true
}
scSetting.selectedValue = value scSetting.selectedValue = value
} }
is StringSingleChoiceSetting -> { is StringSingleChoiceSetting -> {
val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting
val value = scSetting.getValueAt(which) val value = scSetting.getValueAt(which)
if (scSetting.selectedValue != value) settingsViewModel.shouldSave = true
scSetting.selectedValue = value scSetting.selectedValue = value
} }
is SliderSetting -> { is SliderSetting -> {
val sliderSetting = settingsViewModel.clickedItem as SliderSetting val sliderSetting = settingsViewModel.clickedItem as SliderSetting
if (sliderSetting.selectedValue != settingsViewModel.sliderProgress.value) {
settingsViewModel.shouldSave = true
}
sliderSetting.selectedValue = settingsViewModel.sliderProgress.value sliderSetting.selectedValue = settingsViewModel.sliderProgress.value
} }
} }

View File

@ -42,7 +42,7 @@ import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.GameHelper
import org.yuzu.yuzu_emu.utils.ViewUtils import org.yuzu.yuzu_emu.utils.ViewUtils
class SetupFragment : Fragment() { class SetupFragment : Fragment() {
@ -184,7 +184,11 @@ class SetupFragment : Fragment() {
R.string.add_games_warning_description, R.string.add_games_warning_description,
R.string.add_games_warning_help, R.string.add_games_warning_help,
{ {
if (NativeConfig.getGameDirs().isNotEmpty()) { val preferences =
PreferenceManager.getDefaultSharedPreferences(
YuzuApplication.appContext
)
if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) {
StepState.COMPLETE StepState.COMPLETE
} else { } else {
StepState.INCOMPLETE StepState.INCOMPLETE

View File

@ -1,13 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class GameDir(
val uriString: String,
var deepScan: Boolean
) : Parcelable

View File

@ -12,7 +12,6 @@ import java.util.Locale
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
@ -21,7 +20,6 @@ import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.GameHelper import org.yuzu.yuzu_emu.utils.GameHelper
import org.yuzu.yuzu_emu.utils.GameMetadata import org.yuzu.yuzu_emu.utils.GameMetadata
import org.yuzu.yuzu_emu.utils.NativeConfig
class GamesViewModel : ViewModel() { class GamesViewModel : ViewModel() {
val games: StateFlow<List<Game>> get() = _games val games: StateFlow<List<Game>> get() = _games
@ -42,9 +40,6 @@ class GamesViewModel : ViewModel() {
val searchFocused: StateFlow<Boolean> get() = _searchFocused val searchFocused: StateFlow<Boolean> get() = _searchFocused
private val _searchFocused = MutableStateFlow(false) private val _searchFocused = MutableStateFlow(false)
private val _folders = MutableStateFlow(mutableListOf<GameDir>())
val folders = _folders.asStateFlow()
init { init {
// Ensure keys are loaded so that ROM metadata can be decrypted. // Ensure keys are loaded so that ROM metadata can be decrypted.
NativeLibrary.reloadKeys() NativeLibrary.reloadKeys()
@ -55,7 +50,6 @@ class GamesViewModel : ViewModel() {
viewModelScope.launch { viewModelScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
getGameDirs()
if (storedGames!!.isNotEmpty()) { if (storedGames!!.isNotEmpty()) {
val deserializedGames = mutableSetOf<Game>() val deserializedGames = mutableSetOf<Game>()
storedGames.forEach { storedGames.forEach {
@ -110,7 +104,7 @@ class GamesViewModel : ViewModel() {
_searchFocused.value = searchFocused _searchFocused.value = searchFocused
} }
fun reloadGames(directoriesChanged: Boolean) { fun reloadGames(directoryChanged: Boolean) {
if (isReloading.value) { if (isReloading.value) {
return return
} }
@ -122,61 +116,10 @@ class GamesViewModel : ViewModel() {
setGames(GameHelper.getGames()) setGames(GameHelper.getGames())
_isReloading.value = false _isReloading.value = false
if (directoriesChanged) { if (directoryChanged) {
setShouldSwapData(true) setShouldSwapData(true)
} }
} }
} }
} }
fun addFolder(gameDir: GameDir) =
viewModelScope.launch {
withContext(Dispatchers.IO) {
NativeConfig.addGameDir(gameDir)
getGameDirs()
}
}
fun removeFolder(gameDir: GameDir) =
viewModelScope.launch {
withContext(Dispatchers.IO) {
val gameDirs = _folders.value.toMutableList()
val removedDirIndex = gameDirs.indexOf(gameDir)
if (removedDirIndex != -1) {
gameDirs.removeAt(removedDirIndex)
NativeConfig.setGameDirs(gameDirs.toTypedArray())
getGameDirs()
}
}
}
fun updateGameDirs() =
viewModelScope.launch {
withContext(Dispatchers.IO) {
NativeConfig.setGameDirs(_folders.value.toTypedArray())
getGameDirs()
}
}
fun onOpenGameFoldersFragment() =
viewModelScope.launch {
withContext(Dispatchers.IO) {
getGameDirs()
}
}
fun onCloseGameFoldersFragment() =
viewModelScope.launch {
withContext(Dispatchers.IO) {
getGameDirs(true)
}
}
private fun getGameDirs(reloadList: Boolean = false) {
val gameDirs = NativeConfig.getGameDirs()
_folders.value = gameDirs.toMutableList()
if (reloadList) {
reloadGames(true)
}
}
} }

View File

@ -3,9 +3,15 @@
package org.yuzu.yuzu_emu.model package org.yuzu.yuzu_emu.model
import android.net.Uri
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.GameHelper
class HomeViewModel : ViewModel() { class HomeViewModel : ViewModel() {
val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible
@ -17,6 +23,14 @@ class HomeViewModel : ViewModel() {
val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward
private val _shouldPageForward = MutableStateFlow(false) private val _shouldPageForward = MutableStateFlow(false)
val gamesDir: StateFlow<String> get() = _gamesDir
private val _gamesDir = MutableStateFlow(
Uri.parse(
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
.getString(GameHelper.KEY_GAME_PATH, "")
).path ?: ""
)
var navigatedToSetup = false var navigatedToSetup = false
fun setNavigationVisibility(visible: Boolean, animated: Boolean) { fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
@ -36,4 +50,9 @@ class HomeViewModel : ViewModel() {
fun setShouldPageForward(pageForward: Boolean) { fun setShouldPageForward(pageForward: Boolean) {
_shouldPageForward.value = pageForward _shouldPageForward.value = pageForward
} }
fun setGamesDir(activity: FragmentActivity, dir: String) {
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
_gamesDir.value = dir
}
} }

View File

@ -13,6 +13,8 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
class SettingsViewModel : ViewModel() { class SettingsViewModel : ViewModel() {
var game: Game? = null var game: Game? = null
var shouldSave = false
var clickedItem: SettingsItem? = null var clickedItem: SettingsItem? = null
val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate
@ -71,5 +73,6 @@ class SettingsViewModel : ViewModel() {
fun clear() { fun clear() {
game = null game = null
shouldSave = false
} }
} }

View File

@ -40,7 +40,6 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.getPublicFilesDir import org.yuzu.yuzu_emu.getPublicFilesDir
@ -253,13 +252,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
super.onResume() super.onResume()
} }
override fun onStop() {
super.onStop()
CoroutineScope(Dispatchers.IO).launch {
NativeConfig.saveSettings()
}
}
override fun onDestroy() { override fun onDestroy() {
EmulationActivity.stopForegroundService(this) EmulationActivity.stopForegroundService(this)
super.onDestroy() super.onDestroy()
@ -301,19 +293,20 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
Intent.FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION
) )
val uriString = result.toString() // When a new directory is picked, we currently will reset the existing games
val folder = gamesViewModel.folders.value.firstOrNull { it.uriString == uriString } // database. This effectively means that only one game directory is supported.
if (folder != null) { PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
Toast.makeText( .putString(GameHelper.KEY_GAME_PATH, result.toString())
applicationContext, .apply()
R.string.folder_already_added,
Toast.LENGTH_SHORT
).show()
return
}
AddGameFolderDialogFragment.newInstance(uriString) Toast.makeText(
.show(supportFragmentManager, AddGameFolderDialogFragment.TAG) applicationContext,
R.string.games_dir_selected,
Toast.LENGTH_LONG
).show()
gamesViewModel.reloadGames(true)
homeViewModel.setGamesDir(this, result.path!!)
} }
val getProdKey = val getProdKey =
@ -632,7 +625,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} }
// Clear existing user data // Clear existing user data
NativeConfig.unloadConfig()
File(DirectoryInitialization.userDirectory!!).deleteRecursively() File(DirectoryInitialization.userDirectory!!).deleteRecursively()
// Copy archive to internal storage // Copy archive to internal storage
@ -651,7 +643,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
// Reinitialize relevant data // Reinitialize relevant data
NativeLibrary.initializeSystem(true) NativeLibrary.initializeSystem(true)
NativeConfig.initializeConfig()
gamesViewModel.reloadGames(false) gamesViewModel.reloadGames(false)
return@newInstance getString(R.string.user_data_import_success) return@newInstance getString(R.string.user_data_import_success)

View File

@ -16,7 +16,6 @@ object DirectoryInitialization {
if (!areDirectoriesReady) { if (!areDirectoriesReady) {
initializeInternalStorage() initializeInternalStorage()
NativeLibrary.initializeSystem(false) NativeLibrary.initializeSystem(false)
NativeConfig.initializeConfig()
areDirectoriesReady = true areDirectoriesReady = true
} }
} }

View File

@ -364,27 +364,6 @@ object FileUtil {
.lowercase() .lowercase()
} }
fun isTreeUriValid(uri: Uri): Boolean {
val resolver = context.contentResolver
val columns = arrayOf(
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_MIME_TYPE
)
return try {
val docId: String = if (isRootTreeUri(uri)) {
DocumentsContract.getTreeDocumentId(uri)
} else {
DocumentsContract.getDocumentId(uri)
}
val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, docId)
resolver.query(childrenUri, columns, null, null, null)
true
} catch (_: Exception) {
false
}
}
@Throws(IOException::class) @Throws(IOException::class)
fun getStringFromFile(file: File): String = fun getStringFromFile(file: File): String =
String(file.readBytes(), StandardCharsets.UTF_8) String(file.readBytes(), StandardCharsets.UTF_8)

View File

@ -11,11 +11,10 @@ import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.MinimalDocumentFile import org.yuzu.yuzu_emu.model.MinimalDocumentFile
object GameHelper { object GameHelper {
private const val KEY_OLD_GAME_PATH = "game_path" const val KEY_GAME_PATH = "game_path"
const val KEY_GAMES = "Games" const val KEY_GAMES = "Games"
private lateinit var preferences: SharedPreferences private lateinit var preferences: SharedPreferences
@ -23,43 +22,15 @@ object GameHelper {
fun getGames(): List<Game> { fun getGames(): List<Game> {
val games = mutableListOf<Game>() val games = mutableListOf<Game>()
val context = YuzuApplication.appContext val context = YuzuApplication.appContext
val gamesDir =
PreferenceManager.getDefaultSharedPreferences(context).getString(KEY_GAME_PATH, "")
val gamesUri = Uri.parse(gamesDir)
preferences = PreferenceManager.getDefaultSharedPreferences(context) preferences = PreferenceManager.getDefaultSharedPreferences(context)
val gameDirs = mutableListOf<GameDir>()
val oldGamesDir = preferences.getString(KEY_OLD_GAME_PATH, "") ?: ""
if (oldGamesDir.isNotEmpty()) {
gameDirs.add(GameDir(oldGamesDir, true))
preferences.edit().remove(KEY_OLD_GAME_PATH).apply()
}
gameDirs.addAll(NativeConfig.getGameDirs())
// Ensure keys are loaded so that ROM metadata can be decrypted. // Ensure keys are loaded so that ROM metadata can be decrypted.
NativeLibrary.reloadKeys() NativeLibrary.reloadKeys()
val badDirs = mutableListOf<Int>() addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3)
gameDirs.forEachIndexed { index: Int, gameDir: GameDir ->
val gameDirUri = Uri.parse(gameDir.uriString)
val isValid = FileUtil.isTreeUriValid(gameDirUri)
if (isValid) {
addGamesRecursive(
games,
FileUtil.listFiles(gameDirUri),
if (gameDir.deepScan) 3 else 1
)
} else {
badDirs.add(index)
}
}
// Remove all game dirs with insufficient permissions from config
if (badDirs.isNotEmpty()) {
var offset = 0
badDirs.forEach {
gameDirs.removeAt(it - offset)
offset++
}
}
NativeConfig.setGameDirs(gameDirs.toTypedArray())
// Cache list of games found on disk // Cache list of games found on disk
val serializedGames = mutableSetOf<String>() val serializedGames = mutableSetOf<String>()

View File

@ -27,8 +27,6 @@ object InputHandler {
0x054C -> getInputDS5ButtonKey(event.keyCode) 0x054C -> getInputDS5ButtonKey(event.keyCode)
0x057E -> getInputJoyconButtonKey(event.keyCode) 0x057E -> getInputJoyconButtonKey(event.keyCode)
0x1532 -> getInputRazerButtonKey(event.keyCode) 0x1532 -> getInputRazerButtonKey(event.keyCode)
0x3537 -> getInputRedmagicButtonKey(event.keyCode)
0x358A -> getInputBackboneLabsButtonKey(event.keyCode)
else -> getInputGenericButtonKey(event.keyCode) else -> getInputGenericButtonKey(event.keyCode)
} }
@ -229,42 +227,6 @@ object InputHandler {
} }
} }
private fun getInputRedmagicButtonKey(key: Int): Int {
return when (key) {
KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
else -> -1
}
}
private fun getInputBackboneLabsButtonKey(key: Int): Int {
return when (key) {
KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
else -> -1
}
}
private fun getInputGenericButtonKey(key: Int): Int { private fun getInputGenericButtonKey(key: Int): Int {
return when (key) { return when (key) {
KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A

View File

@ -3,33 +3,7 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import org.yuzu.yuzu_emu.model.GameDir
object NativeConfig { object NativeConfig {
/**
* Creates a Config object and opens the emulation config.
*/
@Synchronized
external fun initializeConfig()
/**
* Destroys the stored config object. This automatically saves the existing config.
*/
@Synchronized
external fun unloadConfig()
/**
* Reads values saved to the config file and saves them.
*/
@Synchronized
external fun reloadSettings()
/**
* Saves settings values in memory to disk.
*/
@Synchronized
external fun saveSettings()
external fun getBoolean(key: String, getDefault: Boolean): Boolean external fun getBoolean(key: String, getDefault: Boolean): Boolean
external fun setBoolean(key: String, value: Boolean) external fun setBoolean(key: String, value: Boolean)
@ -56,22 +30,4 @@ object NativeConfig {
external fun getConfigHeader(category: Int): String external fun getConfigHeader(category: Int): String
external fun getPairedSettingKey(key: String): String external fun getPairedSettingKey(key: String): String
/**
* Gets every [GameDir] in AndroidSettings::values.game_dirs
*/
@Synchronized
external fun getGameDirs(): Array<GameDir>
/**
* Clears the AndroidSettings::values.game_dirs array and replaces them with the provided array
*/
@Synchronized
external fun setGameDirs(dirs: Array<GameDir>)
/**
* Adds a single [GameDir] to the AndroidSettings::values.game_dirs array
*/
@Synchronized
external fun addGameDir(dir: GameDir)
} }

View File

@ -6,6 +6,9 @@ add_library(yuzu-android SHARED
android_common/android_common.h android_common/android_common.h
applets/software_keyboard.cpp applets/software_keyboard.cpp
applets/software_keyboard.h applets/software_keyboard.h
config.cpp
config.h
default_ini.h
emu_window/emu_window.cpp emu_window/emu_window.cpp
emu_window/emu_window.h emu_window/emu_window.h
id_cache.cpp id_cache.cpp
@ -13,17 +16,15 @@ add_library(yuzu-android SHARED
native.cpp native.cpp
native.h native.h
native_config.cpp native_config.cpp
android_settings.cpp uisettings.cpp
game_metadata.cpp game_metadata.cpp
native_log.cpp native_log.cpp
android_config.cpp
android_config.h
) )
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common) target_link_libraries(yuzu-android PRIVATE audio_core common core input_common)
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log) target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log)
if (ARCHITECTURE_arm64) if (ARCHITECTURE_arm64)
target_link_libraries(yuzu-android PRIVATE adrenotools) target_link_libraries(yuzu-android PRIVATE adrenotools)
endif() endif()

View File

@ -1,120 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "android_config.h"
#include "android_settings.h"
#include "common/settings_setting.h"
AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_type)
: Config(config_type) {
Initialize(config_name);
if (config_type != ConfigType::InputProfile) {
ReadAndroidValues();
SaveAndroidValues();
}
}
AndroidConfig::~AndroidConfig() {
if (global) {
AndroidConfig::SaveAllValues();
}
}
void AndroidConfig::ReloadAllValues() {
Reload();
ReadAndroidValues();
SaveAndroidValues();
}
void AndroidConfig::SaveAllValues() {
Save();
SaveAndroidValues();
}
void AndroidConfig::ReadAndroidValues() {
if (global) {
ReadAndroidUIValues();
ReadUIValues();
}
}
void AndroidConfig::ReadAndroidUIValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
ReadCategory(Settings::Category::Android);
EndGroup();
}
void AndroidConfig::ReadUIValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
ReadPathValues();
EndGroup();
}
void AndroidConfig::ReadPathValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
const int gamedirs_size = BeginArray(std::string("gamedirs"));
for (int i = 0; i < gamedirs_size; ++i) {
SetArrayIndex(i);
AndroidSettings::GameDir game_dir;
game_dir.path = ReadStringSetting(std::string("path"));
game_dir.deep_scan =
ReadBooleanSetting(std::string("deep_scan"), std::make_optional(false));
AndroidSettings::values.game_dirs.push_back(game_dir);
}
EndArray();
EndGroup();
}
void AndroidConfig::SaveAndroidValues() {
if (global) {
SaveAndroidUIValues();
SaveUIValues();
}
WriteToIni();
}
void AndroidConfig::SaveAndroidUIValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
WriteCategory(Settings::Category::Android);
EndGroup();
}
void AndroidConfig::SaveUIValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
SavePathValues();
EndGroup();
}
void AndroidConfig::SavePathValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
BeginArray(std::string("gamedirs"));
for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
SetArrayIndex(i);
const auto& game_dir = AndroidSettings::values.game_dirs[i];
WriteSetting(std::string("path"), game_dir.path);
WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false));
}
EndArray();
EndGroup();
}
std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
auto& map = Settings::values.linkage.by_category;
if (map.contains(category)) {
return Settings::values.linkage.by_category[category];
}
return AndroidSettings::values.linkage.by_category[category];
}

View File

@ -1,41 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "frontend_common/config.h"
class AndroidConfig final : public Config {
public:
explicit AndroidConfig(const std::string& config_name = "config",
ConfigType config_type = ConfigType::GlobalConfig);
~AndroidConfig() override;
void ReloadAllValues() override;
void SaveAllValues() override;
protected:
void ReadAndroidValues();
void ReadAndroidUIValues();
void ReadHidbusValues() override {}
void ReadDebugControlValues() override {}
void ReadPathValues() override;
void ReadShortcutValues() override {}
void ReadUIValues() override;
void ReadUIGamelistValues() override {}
void ReadUILayoutValues() override {}
void ReadMultiplayerValues() override {}
void SaveAndroidValues();
void SaveAndroidUIValues();
void SaveHidbusValues() override {}
void SaveDebugControlValues() override {}
void SavePathValues() override;
void SaveShortcutValues() override {}
void SaveUIValues() override;
void SaveUIGamelistValues() override {}
void SaveUILayoutValues() override {}
void SaveMultiplayerValues() override {}
std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
};

View File

@ -0,0 +1,331 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include <optional>
#include <sstream>
#include <INIReader.h>
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "core/hle/service/acc/profile_manager.h"
#include "input_common/main.h"
#include "jni/config.h"
#include "jni/default_ini.h"
#include "uisettings.h"
namespace FS = Common::FS;
Config::Config(const std::string& config_name, ConfigType config_type)
: type(config_type), global{config_type == ConfigType::GlobalConfig} {
Initialize(config_name);
}
Config::~Config() = default;
bool Config::LoadINI(const std::string& default_contents, bool retry) {
void(FS::CreateParentDir(config_loc));
config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc));
const auto config_loc_str = FS::PathToUTF8String(config_loc);
if (config->ParseError() < 0) {
if (retry) {
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...",
config_loc_str);
void(FS::CreateParentDir(config_loc));
void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents));
config = std::make_unique<INIReader>(config_loc_str);
return LoadINI(default_contents, false);
}
LOG_ERROR(Config, "Failed.");
return false;
}
LOG_INFO(Config, "Successfully loaded {}", config_loc_str);
return true;
}
template <>
void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault());
if (setting_value.empty()) {
setting_value = setting.GetDefault();
}
setting = std::move(setting_value);
}
template <>
void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {
setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
}
template <typename Type, bool ranged>
void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
setting = static_cast<Type>(
config->GetInteger(group, setting.GetLabel(), static_cast<long>(setting.GetDefault())));
}
void Config::ReadValues() {
ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
ReadSetting("ControlsGeneral", Settings::values.touch_device);
ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
Settings::values.touchscreen.enabled =
config->GetBoolean("ControlsGeneral", "touch_enabled", true);
Settings::values.touchscreen.rotation_angle =
config->GetInteger("ControlsGeneral", "touch_angle", 0);
Settings::values.touchscreen.diameter_x =
config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
Settings::values.touchscreen.diameter_y =
config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
int num_touch_from_button_maps =
config->GetInteger("ControlsGeneral", "touch_from_button_map", 0);
if (num_touch_from_button_maps > 0) {
for (int i = 0; i < num_touch_from_button_maps; ++i) {
Settings::TouchFromButtonMap map;
map.name = config->Get("ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) +
std::string("_name"),
"default");
const int num_touch_maps = config->GetInteger(
"ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"),
0);
map.buttons.reserve(num_touch_maps);
for (int j = 0; j < num_touch_maps; ++j) {
std::string touch_mapping =
config->Get("ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) +
std::string("_bind_") + std::to_string(j),
"");
map.buttons.emplace_back(std::move(touch_mapping));
}
Settings::values.touch_from_button_maps.emplace_back(std::move(map));
}
} else {
Settings::values.touch_from_button_maps.emplace_back(
Settings::TouchFromButtonMap{"default", {}});
num_touch_from_button_maps = 1;
}
Settings::values.touch_from_button_map_index = std::clamp(
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
ReadSetting("ControlsGeneral", Settings::values.udp_input_servers);
// Data Storage
ReadSetting("Data Storage", Settings::values.use_virtual_sd);
FS::SetYuzuPath(FS::YuzuPath::NANDDir,
config->Get("Data Storage", "nand_directory",
FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
FS::SetYuzuPath(FS::YuzuPath::SDMCDir,
config->Get("Data Storage", "sdmc_directory",
FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
FS::SetYuzuPath(FS::YuzuPath::LoadDir,
config->Get("Data Storage", "load_directory",
FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
FS::SetYuzuPath(FS::YuzuPath::DumpDir,
config->Get("Data Storage", "dump_directory",
FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
ReadSetting("Data Storage", Settings::values.gamecard_inserted);
ReadSetting("Data Storage", Settings::values.gamecard_current_game);
ReadSetting("Data Storage", Settings::values.gamecard_path);
// System
ReadSetting("System", Settings::values.current_user);
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
Service::Account::MAX_USERS - 1);
// Disable docked mode by default on Android
Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false)
? Settings::ConsoleMode::Docked
: Settings::ConsoleMode::Handheld);
const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false);
if (rng_seed_enabled) {
Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0));
} else {
Settings::values.rng_seed.SetValue(0);
}
Settings::values.rng_seed_enabled.SetValue(rng_seed_enabled);
const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false);
if (custom_rtc_enabled) {
Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0);
} else {
Settings::values.custom_rtc = 0;
}
Settings::values.custom_rtc_enabled = custom_rtc_enabled;
ReadSetting("System", Settings::values.language_index);
ReadSetting("System", Settings::values.region_index);
ReadSetting("System", Settings::values.time_zone_index);
ReadSetting("System", Settings::values.sound_index);
// Core
ReadSetting("Core", Settings::values.use_multi_core);
ReadSetting("Core", Settings::values.memory_layout_mode);
// Cpu
ReadSetting("Cpu", Settings::values.cpu_backend);
ReadSetting("Cpu", Settings::values.cpu_accuracy);
ReadSetting("Cpu", Settings::values.cpu_debug_mode);
ReadSetting("Cpu", Settings::values.cpuopt_page_tables);
ReadSetting("Cpu", Settings::values.cpuopt_block_linking);
ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer);
ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher);
ReadSetting("Cpu", Settings::values.cpuopt_context_elimination);
ReadSetting("Cpu", Settings::values.cpuopt_const_prop);
ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);
ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);
ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor);
// Renderer
ReadSetting("Renderer", Settings::values.renderer_backend);
ReadSetting("Renderer", Settings::values.renderer_debug);
ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
ReadSetting("Renderer", Settings::values.vulkan_device);
ReadSetting("Renderer", Settings::values.resolution_setup);
ReadSetting("Renderer", Settings::values.scaling_filter);
ReadSetting("Renderer", Settings::values.fsr_sharpening_slider);
ReadSetting("Renderer", Settings::values.anti_aliasing);
ReadSetting("Renderer", Settings::values.fullscreen_mode);
ReadSetting("Renderer", Settings::values.aspect_ratio);
ReadSetting("Renderer", Settings::values.max_anisotropy);
ReadSetting("Renderer", Settings::values.use_speed_limit);
ReadSetting("Renderer", Settings::values.speed_limit);
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
ReadSetting("Renderer", Settings::values.vsync_mode);
ReadSetting("Renderer", Settings::values.shader_backend);
ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
ReadSetting("Renderer", Settings::values.nvdec_emulation);
ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
ReadSetting("Renderer", Settings::values.bg_red);
ReadSetting("Renderer", Settings::values.bg_green);
ReadSetting("Renderer", Settings::values.bg_blue);
// Use GPU accuracy normal by default on Android
Settings::values.gpu_accuracy = static_cast<Settings::GpuAccuracy>(config->GetInteger(
"Renderer", "gpu_accuracy", static_cast<u32>(Settings::GpuAccuracy::Normal)));
// Use GPU default anisotropic filtering on Android
Settings::values.max_anisotropy =
static_cast<Settings::AnisotropyMode>(config->GetInteger("Renderer", "max_anisotropy", 1));
// Disable ASTC compute by default on Android
Settings::values.accelerate_astc.SetValue(
config->GetBoolean("Renderer", "accelerate_astc", false) ? Settings::AstcDecodeMode::Gpu
: Settings::AstcDecodeMode::Cpu);
// Enable asynchronous presentation by default on Android
Settings::values.async_presentation =
config->GetBoolean("Renderer", "async_presentation", true);
// Disable force_max_clock by default on Android
Settings::values.renderer_force_max_clock =
config->GetBoolean("Renderer", "force_max_clock", false);
// Disable use_reactive_flushing by default on Android
Settings::values.use_reactive_flushing =
config->GetBoolean("Renderer", "use_reactive_flushing", false);
// Audio
ReadSetting("Audio", Settings::values.sink_id);
ReadSetting("Audio", Settings::values.audio_output_device_id);
ReadSetting("Audio", Settings::values.volume);
// Miscellaneous
// log_filter has a different default here than from common
Settings::values.log_filter = "*:Info";
ReadSetting("Miscellaneous", Settings::values.use_dev_keys);
// Debugging
Settings::values.record_frame_times =
config->GetBoolean("Debugging", "record_frame_times", false);
ReadSetting("Debugging", Settings::values.dump_exefs);
ReadSetting("Debugging", Settings::values.dump_nso);
ReadSetting("Debugging", Settings::values.enable_fs_access_log);
ReadSetting("Debugging", Settings::values.reporting_services);
ReadSetting("Debugging", Settings::values.quest_flag);
ReadSetting("Debugging", Settings::values.use_debug_asserts);
ReadSetting("Debugging", Settings::values.use_auto_stub);
ReadSetting("Debugging", Settings::values.disable_macro_jit);
ReadSetting("Debugging", Settings::values.disable_macro_hle);
ReadSetting("Debugging", Settings::values.use_gdbstub);
ReadSetting("Debugging", Settings::values.gdbstub_port);
const auto title_list = config->Get("AddOns", "title_ids", "");
std::stringstream ss(title_list);
std::string line;
while (std::getline(ss, line, '|')) {
const auto title_id = std::strtoul(line.c_str(), nullptr, 16);
const auto disabled_list = config->Get("AddOns", "disabled_" + line, "");
std::stringstream inner_ss(disabled_list);
std::string inner_line;
std::vector<std::string> out;
while (std::getline(inner_ss, inner_line, '|')) {
out.push_back(inner_line);
}
Settings::values.disabled_addons.insert_or_assign(title_id, out);
}
// Web Service
ReadSetting("WebService", Settings::values.enable_telemetry);
ReadSetting("WebService", Settings::values.web_api_url);
ReadSetting("WebService", Settings::values.yuzu_username);
ReadSetting("WebService", Settings::values.yuzu_token);
// Network
ReadSetting("Network", Settings::values.network_interface);
// Android
ReadSetting("Android", AndroidSettings::values.picture_in_picture);
ReadSetting("Android", AndroidSettings::values.screen_layout);
}
void Config::Initialize(const std::string& config_name) {
const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
const auto config_file = fmt::format("{}.ini", config_name);
switch (type) {
case ConfigType::GlobalConfig:
config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
break;
case ConfigType::PerGameConfig:
config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
break;
case ConfigType::InputProfile:
config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
LoadINI(DefaultINI::android_config_file);
return;
}
LoadINI(DefaultINI::android_config_file);
ReadValues();
}

View File

@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
#include "common/settings.h"
class INIReader;
class Config {
bool LoadINI(const std::string& default_contents = "", bool retry = true);
public:
enum class ConfigType {
GlobalConfig,
PerGameConfig,
InputProfile,
};
explicit Config(const std::string& config_name = "config",
ConfigType config_type = ConfigType::GlobalConfig);
~Config();
void Initialize(const std::string& config_name);
private:
/**
* Applies a value read from the config to a Setting.
*
* @param group The name of the INI group
* @param setting The yuzu setting to modify
*/
template <typename Type, bool ranged>
void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
void ReadValues();
const ConfigType type;
std::unique_ptr<INIReader> config;
std::string config_loc;
const bool global;
};

View File

@ -0,0 +1,515 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace DefaultINI {
const char* android_config_file = R"(
[ControlsP0]
# The input devices and parameters for each Switch native input
# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
# Indicates if this player should be connected at boot
connected=
# for button input, the following devices are available:
# - "keyboard" (default) for keyboard input. Required parameters:
# - "code": the code of the key to bind
# - "sdl" for joystick input using SDL. Required parameters:
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "button"(optional): the index of the button to bind
# - "hat"(optional): the index of the hat to bind as direction buttons
# - "axis"(optional): the index of the axis to bind
# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
# triggered if the axis value crosses
# - "direction"(only used for axis): "+" means the button is triggered when the axis value
# is greater than the threshold; "-" means the button is triggered when the axis value
# is smaller than the threshold
button_a=
button_b=
button_x=
button_y=
button_lstick=
button_rstick=
button_l=
button_r=
button_zl=
button_zr=
button_plus=
button_minus=
button_dleft=
button_dup=
button_dright=
button_ddown=
button_lstick_left=
button_lstick_up=
button_lstick_right=
button_lstick_down=
button_sl=
button_sr=
button_home=
button_screenshot=
# for analog input, the following devices are available:
# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
# - "up", "down", "left", "right": sub-devices for each direction.
# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
# - "modifier": sub-devices as a modifier.
# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
# Must be in range of 0.0-1.0. Defaults to 0.5
# - "sdl" for joystick input using SDL. Required parameters:
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "axis_x": the index of the axis to bind as x-axis (default to 0)
# - "axis_y": the index of the axis to bind as y-axis (default to 1)
lstick=
rstick=
# for motion input, the following devices are available:
# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
# - "code": the code of the key to bind
# - "sdl" for motion input using SDL. Required parameters:
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "motion": the index of the motion sensor to bind
# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
# - "port": the port of the cemu hook server
# - "pad": the index of the joystick
# - "motion": the index of the motion sensor of the joystick to bind
motionleft=
motionright=
[ControlsGeneral]
# To use the debug_pad, prepend `debug_pad_` before each button setting above.
# i.e. debug_pad_button_a=
# Enable debug pad inputs to the guest
# 0 (default): Disabled, 1: Enabled
debug_pad_enabled =
# Whether to enable or disable vibration
# 0: Disabled, 1 (default): Enabled
vibration_enabled=
# Whether to enable or disable accurate vibrations
# 0 (default): Disabled, 1: Enabled
enable_accurate_vibrations=
# Enables controller motion inputs
# 0: Disabled, 1 (default): Enabled
motion_enabled =
# Defines the udp device's touch screen coordinate system for cemuhookudp devices
# - "min_x", "min_y", "max_x", "max_y"
touch_device=
# for mapping buttons to touch inputs.
#touch_from_button_map=1
#touch_from_button_maps_0_name=default
#touch_from_button_maps_0_count=2
#touch_from_button_maps_0_bind_0=foo
#touch_from_button_maps_0_bind_1=bar
# etc.
# List of Cemuhook UDP servers, delimited by ','.
# Default: 127.0.0.1:26760
# Example: 127.0.0.1:26760,123.4.5.67:26761
udp_input_servers =
# Enable controlling an axis via a mouse input.
# 0 (default): Off, 1: On
mouse_panning =
# Set mouse sensitivity.
# Default: 1.0
mouse_panning_sensitivity =
# Emulate an analog control stick from keyboard inputs.
# 0 (default): Disabled, 1: Enabled
emulate_analog_keyboard =
# Enable mouse inputs to the guest
# 0 (default): Disabled, 1: Enabled
mouse_enabled =
# Enable keyboard inputs to the guest
# 0 (default): Disabled, 1: Enabled
keyboard_enabled =
[Core]
# Whether to use multi-core for CPU emulation
# 0: Disabled, 1 (default): Enabled
use_multi_core =
# Enable unsafe extended guest system memory layout (8GB DRAM)
# 0 (default): Disabled, 1: Enabled
use_unsafe_extended_memory_layout =
[Cpu]
Selects the preferred CPU backend for executing ARM instructions
# 0 (default): Dynarmic, 1: NCE
cpu_backend =
# Adjusts various optimizations.
# Auto-select mode enables choice unsafe optimizations.
# Accurate enables only safe optimizations.
# Unsafe allows any unsafe optimizations.
# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations
cpu_accuracy =
# Allow disabling safe optimizations.
# 0 (default): Disabled, 1: Enabled
cpu_debug_mode =
# Enable inline page tables optimization (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_page_tables =
# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
# 0: Disabled, 1 (default): Enabled
cpuopt_block_linking =
# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
# 0: Disabled, 1 (default): Enabled
cpuopt_return_stack_buffer =
# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
# 0: Disabled, 1 (default): Enabled
cpuopt_fast_dispatcher =
# Enable context elimination CPU Optimization (reduce host memory use for guest context)
# 0: Disabled, 1 (default): Enabled
cpuopt_context_elimination =
# Enable constant propagation CPU optimization (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_const_prop =
# Enable miscellaneous CPU optimizations (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_misc_ir =
# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
# 0: Disabled, 1 (default): Enabled
cpuopt_reduce_misalign_checks =
# Enable Host MMU Emulation (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_fastmem =
# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_fastmem_exclusives =
# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_recompile_exclusives =
# Enable optimization to ignore invalid memory accesses (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_ignore_memory_aborts =
# Enable unfuse FMA (improve performance on CPUs without FMA)
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_unfuse_fma =
# Enable faster FRSQRTE and FRECPE
# Only enabled if cpu_accuracy is set to Unsafe.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_reduce_fp_error =
# Enable faster ASIMD instructions (32 bits only)
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_ignore_standard_fpcr =
# Enable inaccurate NaN handling
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_inaccurate_nan =
# Disable address space checks (64 bits only)
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_fastmem_check =
# Enable faster exclusive instructions
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_ignore_global_monitor =
[Renderer]
# Which backend API to use.
# 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null
backend =
# Whether to enable asynchronous presentation (Vulkan only)
# 0: Off, 1 (default): On
async_presentation =
# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).
# 0 (default): Disabled, 1: Enabled
force_max_clock =
# Enable graphics API debugging mode.
# 0 (default): Disabled, 1: Enabled
debug =
# Enable shader feedback.
# 0 (default): Disabled, 1: Enabled
renderer_shader_feedback =
# Enable Nsight Aftermath crash dumps
# 0 (default): Disabled, 1: Enabled
nsight_aftermath =
# Disable shader loop safety checks, executing the shader without loop logic changes
# 0 (default): Disabled, 1: Enabled
disable_shader_loop_safety_checks =
# Which Vulkan physical device to use (defaults to 0)
vulkan_device =
# 0: 0.5x (360p/540p) [EXPERIMENTAL]
# 1: 0.75x (540p/810p) [EXPERIMENTAL]
# 2 (default): 1x (720p/1080p)
# 3: 2x (1440p/2160p)
# 4: 3x (2160p/3240p)
# 5: 4x (2880p/4320p)
# 6: 5x (3600p/5400p)
# 7: 6x (4320p/6480p)
resolution_setup =
# Pixel filter to use when up- or down-sampling rendered frames.
# 0: Nearest Neighbor
# 1 (default): Bilinear
# 2: Bicubic
# 3: Gaussian
# 4: ScaleForce
# 5: AMD FidelityFX™ Super Resolution [Vulkan Only]
scaling_filter =
# Anti-Aliasing (AA)
# 0 (default): None, 1: FXAA
anti_aliasing =
# Whether to use fullscreen or borderless window mode
# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen
fullscreen_mode =
# Aspect ratio
# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window
aspect_ratio =
# Anisotropic filtering
# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
max_anisotropy =
# Whether to enable VSync or not.
# OpenGL: Values other than 0 enable VSync
# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
# 0: Immediate (Off), 1 (Default): Mailbox (On), 2: FIFO, 3: FIFO Relaxed
use_vsync =
# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is
# not available and GLASM is selected, GLSL will be used.
# 0: GLSL, 1 (default): GLASM, 2: SPIR-V
shader_backend =
# Whether to allow asynchronous shader building.
# 0 (default): Off, 1: On
use_asynchronous_shaders =
# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.
# 0 (default): Off, 1: On
use_reactive_flushing =
# NVDEC emulation.
# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
nvdec_emulation =
# Accelerate ASTC texture decoding.
# 0 (default): Off, 1: On
accelerate_astc =
# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
# 0: Off, 1: On (default)
use_speed_limit =
# Limits the speed of the game to run no faster than this value as a percentage of target speed
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
speed_limit =
# Whether to use disk based shader cache
# 0: Off, 1 (default): On
use_disk_shader_cache =
# Which gpu accuracy level to use
# 0 (default): Normal, 1: High, 2: Extreme (Very slow)
gpu_accuracy =
# Whether to use asynchronous GPU emulation
# 0 : Off (slow), 1 (default): On (fast)
use_asynchronous_gpu_emulation =
# Inform the guest that GPU operations completed more quickly than they did.
# 0: Off, 1 (default): On
use_fast_gpu_time =
# Force unmodified buffers to be flushed, which can cost performance.
# 0: Off (default), 1: On
use_pessimistic_flushes =
# Whether to use garbage collection or not for GPU caches.
# 0 (default): Off, 1: On
use_caches_gc =
# The clear color for the renderer. What shows up on the sides of the bottom screen.
# Must be in range of 0-255. Defaults to 0 for all.
bg_red =
bg_blue =
bg_green =
[Audio]
# Which audio output engine to use.
# auto (default): Auto-select
# cubeb: Cubeb audio engine (if available)
# sdl2: SDL2 audio engine (if available)
# null: No audio output
output_engine =
# Which audio device to use.
# auto (default): Auto-select
output_device =
# Output volume.
# 100 (default): 100%, 0; mute
volume =
[Data Storage]
# Whether to create a virtual SD card.
# 1: Yes, 0 (default): No
use_virtual_sd =
# Whether or not to enable gamecard emulation
# 1: Yes, 0 (default): No
gamecard_inserted =
# Whether or not the gamecard should be emulated as the current game
# If 'gamecard_inserted' is 0 this setting is irrelevant
# 1: Yes, 0 (default): No
gamecard_current_game =
# Path to an XCI file to use as the gamecard
# If 'gamecard_inserted' is 0 this setting is irrelevant
# If 'gamecard_current_game' is 1 this setting is irrelevant
gamecard_path =
[System]
# Whether the system is docked
# 1 (default): Yes, 0: No
use_docked_mode =
# Sets the seed for the RNG generator built into the switch
# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
rng_seed_enabled =
rng_seed =
# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
# This will auto-increment, with the time set being the time the game is started
# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
custom_rtc_enabled =
custom_rtc =
# Sets the systems language index
# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
language_index =
# The system region that yuzu will use during emulation
# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
region_index =
# The system time zone that yuzu will use during emulation
# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
time_zone_index =
# Sets the sound output mode.
# 0: Mono, 1 (default): Stereo, 2: Surround
sound_index =
[Miscellaneous]
# A filter which removes logs below a certain logging level.
# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
log_filter = *:Trace
# Use developer keys
# 0 (default): Disabled, 1: Enabled
use_dev_keys =
[Debugging]
# Record frame time data, can be found in the log directory. Boolean value
record_frame_times =
# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
dump_exefs=false
# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
dump_nso=false
# Determines whether or not yuzu will save the filesystem access log.
enable_fs_access_log=false
# Enables verbose reporting services
reporting_services =
# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
# false: Retail/Normal Mode (default), true: Kiosk Mode
quest_flag =
# Determines whether debug asserts should be enabled, which will throw an exception on asserts.
# false: Disabled (default), true: Enabled
use_debug_asserts =
# Determines whether unimplemented HLE service calls should be automatically stubbed.
# false: Disabled (default), true: Enabled
use_auto_stub =
# Enables/Disables the macro JIT compiler
disable_macro_jit=false
# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
# false: Disabled (default), true: Enabled
use_gdbstub=false
# The port to use for the GDB server, if it is enabled.
gdbstub_port=6543
[WebService]
# Whether or not to enable telemetry
# 0: No, 1 (default): Yes
enable_telemetry =
# URL for Web API
web_api_url = https://api.yuzu-emu.org
# Username and token for yuzu Web Service
# See https://profile.yuzu-emu.org/ for more info
yuzu_username =
yuzu_token =
[Network]
# Name of the network interface device to use with yuzu LAN play.
# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo'
# e.g. On Windows: 'Ethernet', 'Wi-Fi'
network_interface =
[AddOns]
# Used to disable add-ons
# List of title IDs of games that will have add-ons disabled (separated by '|'):
title_ids =
# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
)";
} // namespace DefaultINI

View File

@ -13,8 +13,6 @@ static JavaVM* s_java_vm;
static jclass s_native_library_class; static jclass s_native_library_class;
static jclass s_disk_cache_progress_class; static jclass s_disk_cache_progress_class;
static jclass s_load_callback_stage_class; static jclass s_load_callback_stage_class;
static jclass s_game_dir_class;
static jmethodID s_game_dir_constructor;
static jmethodID s_exit_emulation_activity; static jmethodID s_exit_emulation_activity;
static jmethodID s_disk_cache_load_progress; static jmethodID s_disk_cache_load_progress;
static jmethodID s_on_emulation_started; static jmethodID s_on_emulation_started;
@ -55,14 +53,6 @@ jclass GetDiskCacheLoadCallbackStageClass() {
return s_load_callback_stage_class; return s_load_callback_stage_class;
} }
jclass GetGameDirClass() {
return s_game_dir_class;
}
jmethodID GetGameDirConstructor() {
return s_game_dir_constructor;
}
jmethodID GetExitEmulationActivity() { jmethodID GetExitEmulationActivity() {
return s_exit_emulation_activity; return s_exit_emulation_activity;
} }
@ -100,11 +90,6 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass( s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>", "(Ljava/lang/String;Z)V");
env->DeleteLocalRef(game_dir_class);
// Initialize methods // Initialize methods
s_exit_emulation_activity = s_exit_emulation_activity =
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
@ -135,7 +120,6 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
env->DeleteGlobalRef(s_native_library_class); env->DeleteGlobalRef(s_native_library_class);
env->DeleteGlobalRef(s_disk_cache_progress_class); env->DeleteGlobalRef(s_disk_cache_progress_class);
env->DeleteGlobalRef(s_load_callback_stage_class); env->DeleteGlobalRef(s_load_callback_stage_class);
env->DeleteGlobalRef(s_game_dir_class);
// UnInitialize applets // UnInitialize applets
SoftwareKeyboard::CleanupJNI(env); SoftwareKeyboard::CleanupJNI(env);

View File

@ -13,8 +13,6 @@ JNIEnv* GetEnvForThread();
jclass GetNativeLibraryClass(); jclass GetNativeLibraryClass();
jclass GetDiskCacheProgressClass(); jclass GetDiskCacheProgressClass();
jclass GetDiskCacheLoadCallbackStageClass(); jclass GetDiskCacheLoadCallbackStageClass();
jclass GetGameDirClass();
jmethodID GetGameDirConstructor();
jmethodID GetExitEmulationActivity(); jmethodID GetExitEmulationActivity();
jmethodID GetDiskCacheLoadProgress(); jmethodID GetDiskCacheLoadProgress();
jmethodID GetOnEmulationStarted(); jmethodID GetOnEmulationStarted();

View File

@ -52,8 +52,8 @@
#include "core/hle/service/am/applets/applets.h" #include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "frontend_common/config.h"
#include "jni/android_common/android_common.h" #include "jni/android_common/android_common.h"
#include "jni/config.h"
#include "jni/id_cache.h" #include "jni/id_cache.h"
#include "jni/native.h" #include "jni/native.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
@ -664,6 +664,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
jboolean reload) { jboolean reload) {
// Create the default config.ini.
Config{};
// Initialize the emulated system. // Initialize the emulated system.
if (!reload) { if (!reload) {
EmulationSession::GetInstance().System().Initialize(); EmulationSession::GetInstance().System().Initialize();
@ -678,6 +680,17 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
Config{};
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
jstring j_game_id) {
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
env->ReleaseStringUTFChars(j_game_id, game_id.data());
}
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
jdoubleArray j_stats = env->NewDoubleArray(4); jdoubleArray j_stats = env->NewDoubleArray(4);

View File

@ -5,15 +5,11 @@
#include <jni.h> #include <jni.h>
#include "android_config.h"
#include "android_settings.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h" #include "common/settings.h"
#include "frontend_common/config.h"
#include "jni/android_common/android_common.h" #include "jni/android_common/android_common.h"
#include "jni/id_cache.h" #include "jni/config.h"
#include "uisettings.h"
std::unique_ptr<AndroidConfig> config;
template <typename T> template <typename T>
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
@ -32,22 +28,6 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
extern "C" { extern "C" {
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) {
config = std::make_unique<AndroidConfig>();
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) {
config.reset();
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) {
config->AndroidConfig::ReloadAllValues();
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) {
config->AndroidConfig::SaveAllValues();
}
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
jstring jkey, jboolean getDefault) { jstring jkey, jboolean getDefault) {
auto setting = getSetting<bool>(env, jkey); auto setting = getSetting<bool>(env, jkey);
@ -254,55 +234,4 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* e
return ToJString(env, setting->PairedSetting()->GetLabel()); return ToJString(env, setting->PairedSetting()->GetLabel());
} }
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) {
jclass gameDirClass = IDCache::GetGameDirClass();
jmethodID gameDirConstructor = IDCache::GetGameDirConstructor();
jobjectArray jgameDirArray =
env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr);
for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
jobject jgameDir =
env->NewObject(gameDirClass, gameDirConstructor,
ToJString(env, AndroidSettings::values.game_dirs[i].path),
static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan));
env->SetObjectArrayElement(jgameDirArray, i, jgameDir);
}
return jgameDirArray;
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject obj,
jobjectArray gameDirs) {
AndroidSettings::values.game_dirs.clear();
int size = env->GetArrayLength(gameDirs);
if (size == 0) {
return;
}
jobject dir = env->GetObjectArrayElement(gameDirs, 0);
jclass gameDirClass = IDCache::GetGameDirClass();
jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
for (int i = 0; i < size; ++i) {
dir = env->GetObjectArrayElement(gameDirs, i);
jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField));
jboolean jdeepScanBoolean = env->GetBooleanField(dir, deepScanBooleanField);
std::string uriString = GetJString(env, juriString);
AndroidSettings::values.game_dirs.push_back(
AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)});
}
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject obj,
jobject gameDir) {
jclass gameDirClass = IDCache::GetGameDirClass();
jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField));
jboolean jdeepScanBoolean = env->GetBooleanField(gameDir, deepScanBooleanField);
std::string uriString = GetJString(env, juriString);
AndroidSettings::values.game_dirs.push_back(
AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)});
}
} // extern "C" } // extern "C"

View File

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "android_settings.h" #include "uisettings.h"
namespace AndroidSettings { namespace AndroidSettings {

View File

@ -9,17 +9,9 @@
namespace AndroidSettings { namespace AndroidSettings {
struct GameDir {
std::string path;
bool deep_scan = false;
};
struct Values { struct Values {
Settings::Linkage linkage; Settings::Linkage linkage;
// Path settings
std::vector<GameDir> game_dirs;
// Android // Android
Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture", Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture",
Settings::Category::Android}; Settings::Category::Android};

View File

@ -1,70 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="12dp"
android:focusable="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
android:layout_gravity="center_vertical"
android:animateLayoutChanges="true">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/path"
style="@style/TextAppearance.Material3.BodyLarge"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/button_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/select_gpu_driver_default" />
<LinearLayout
android:id="@+id/button_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/button_edit"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/delete"
android:tooltipText="@string/edit"
app:icon="@drawable/ic_edit"
app:iconTint="?attr/colorControlNormal" />
<Button
android:id="@+id/button_delete"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/delete"
android:tooltipText="@string/delete"
app:icon="@drawable/ic_delete"
app:iconTint="?attr/colorControlNormal" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="24dp"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/path"
style="@style/TextAppearance.Material3.BodyLarge"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="center_vertical|start"
android:layout_weight="1"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="folder/folder/folder/folder" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="8dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:layout_weight="1"
android:text="@string/deep_scan"
android:textAlignment="viewStart" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/deep_scan_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="24dp"
android:orientation="vertical">
<LinearLayout
android:id="@+id/deep_scan_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:layout_weight="1"
android:text="@string/deep_scan"
android:textAlignment="viewStart" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/deep_scan_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>

View File

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_folders"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_folders"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:liftOnScrollTargetViewId="@id/list_folders">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_folders"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_back"
app:title="@string/game_folders" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_folders"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/button_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:contentDescription="@string/add_games"
app:srcCompat="@drawable/ic_add"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -28,9 +28,6 @@
<action <action
android:id="@+id/action_homeSettingsFragment_to_appletLauncherFragment" android:id="@+id/action_homeSettingsFragment_to_appletLauncherFragment"
app:destination="@id/appletLauncherFragment" /> app:destination="@id/appletLauncherFragment" />
<action
android:id="@+id/action_homeSettingsFragment_to_gameFoldersFragment"
app:destination="@id/gameFoldersFragment" />
</fragment> </fragment>
<fragment <fragment
@ -120,9 +117,5 @@
android:id="@+id/cabinetLauncherDialogFragment" android:id="@+id/cabinetLauncherDialogFragment"
android:name="org.yuzu.yuzu_emu.fragments.CabinetLauncherDialogFragment" android:name="org.yuzu.yuzu_emu.fragments.CabinetLauncherDialogFragment"
android:label="CabinetLauncherDialogFragment" /> android:label="CabinetLauncherDialogFragment" />
<fragment
android:id="@+id/gameFoldersFragment"
android:name="org.yuzu.yuzu_emu.fragments.GameFoldersFragment"
android:label="GameFoldersFragment" />
</navigation> </navigation>

View File

@ -175,24 +175,16 @@
<item>2</item> <item>2</item>
</integer-array> </integer-array>
<string-array name="cpuBackendArm64Names"> <string-array name="cpuBackendNames">
<item>@string/cpu_backend_dynarmic</item> <item>@string/cpu_backend_dynarmic</item>
<item>@string/cpu_backend_nce</item> <item>@string/cpu_backend_nce</item>
</string-array> </string-array>
<integer-array name="cpuBackendArm64Values"> <integer-array name="cpuBackendValues">
<item>0</item> <item>0</item>
<item>1</item> <item>1</item>
</integer-array> </integer-array>
<string-array name="cpuBackendX86Names">
<item>@string/cpu_backend_dynarmic</item>
</string-array>
<integer-array name="cpuBackendX86Values">
<item>0</item>
</integer-array>
<string-array name="cpuAccuracyNames"> <string-array name="cpuAccuracyNames">
<item>@string/auto</item> <item>@string/auto</item>
<item>@string/cpu_accuracy_accurate</item> <item>@string/cpu_accuracy_accurate</item>

View File

@ -13,7 +13,7 @@
<dimen name="menu_width">256dp</dimen> <dimen name="menu_width">256dp</dimen>
<dimen name="card_width">165dp</dimen> <dimen name="card_width">165dp</dimen>
<dimen name="icon_inset">24dp</dimen> <dimen name="icon_inset">24dp</dimen>
<dimen name="spacing_bottom_list_fab">76dp</dimen> <dimen name="spacing_bottom_list_fab">72dp</dimen>
<dimen name="spacing_fab">24dp</dimen> <dimen name="spacing_fab">24dp</dimen>
<dimen name="dialog_margin">20dp</dimen> <dimen name="dialog_margin">20dp</dimen>

View File

@ -38,7 +38,6 @@
<string name="empty_gamelist">No files were found or no game directory has been selected yet.</string> <string name="empty_gamelist">No files were found or no game directory has been selected yet.</string>
<string name="search_and_filter_games">Search and filter games</string> <string name="search_and_filter_games">Search and filter games</string>
<string name="select_games_folder">Select games folder</string> <string name="select_games_folder">Select games folder</string>
<string name="manage_game_folders">Manage game folders</string>
<string name="select_games_folder_description">Allows yuzu to populate the games list</string> <string name="select_games_folder_description">Allows yuzu to populate the games list</string>
<string name="add_games_warning">Skip selecting games folder?</string> <string name="add_games_warning">Skip selecting games folder?</string>
<string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string> <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string>
@ -125,11 +124,6 @@
<string name="manage_yuzu_data_description">Import/export firmware, keys, user data, and more!</string> <string name="manage_yuzu_data_description">Import/export firmware, keys, user data, and more!</string>
<string name="share_save_file">Share save file</string> <string name="share_save_file">Share save file</string>
<string name="export_save_failed">Failed to export save</string> <string name="export_save_failed">Failed to export save</string>
<string name="game_folders">Game folders</string>
<string name="deep_scan">Deep scan</string>
<string name="add_game_folder">Add game folder</string>
<string name="folder_already_added">This folder was already added!</string>
<string name="game_folder_properties">Game folder properties</string>
<!-- Applet launcher strings --> <!-- Applet launcher strings -->
<string name="applets">Applet launcher</string> <string name="applets">Applet launcher</string>
@ -191,7 +185,7 @@
<string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string> <string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string>
<string name="frame_limit_slider">Limit speed percent</string> <string name="frame_limit_slider">Limit speed percent</string>
<string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string> <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string>
<string name="cpu_backend">CPU backend</string> <string name="cpu_backend">CPU Backend</string>
<string name="cpu_accuracy">CPU accuracy</string> <string name="cpu_accuracy">CPU accuracy</string>
<string name="value_with_units">%1$s%2$s</string> <string name="value_with_units">%1$s%2$s</string>
@ -264,7 +258,6 @@
<string name="cancelling">Cancelling</string> <string name="cancelling">Cancelling</string>
<string name="install">Install</string> <string name="install">Install</string>
<string name="delete">Delete</string> <string name="delete">Delete</string>
<string name="edit">Edit</string>
<string name="export_success">Exported successfully</string> <string name="export_success">Exported successfully</string>
<!-- GPU driver installation --> <!-- GPU driver installation -->

View File

@ -182,15 +182,6 @@ if(ANDROID)
) )
endif() endif()
if (UNIX AND NOT APPLE)
target_sources(common PRIVATE
linux/gamemode.cpp
linux/gamemode.h
)
target_link_libraries(common PRIVATE gamemode::headers)
endif()
if(ARCHITECTURE_x86_64) if(ARCHITECTURE_x86_64)
target_sources(common target_sources(common
PRIVATE PRIVATE
@ -208,7 +199,7 @@ if(ARCHITECTURE_x86_64)
target_link_libraries(common PRIVATE xbyak::xbyak) target_link_libraries(common PRIVATE xbyak::xbyak)
endif() endif()
if (HAS_NCE) if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX))
target_sources(common target_sources(common
PRIVATE PRIVATE
arm64/native_clock.cpp arm64/native_clock.cpp

View File

@ -363,12 +363,21 @@ private:
#ifdef ARCHITECTURE_arm64 #ifdef ARCHITECTURE_arm64
static uint64_t GetRandomU64() {
uint64_t ret;
ASSERT(getrandom(&ret, sizeof(ret), 0) == 0);
return ret;
}
static void* ChooseVirtualBase(size_t virtual_size) { static void* ChooseVirtualBase(size_t virtual_size) {
constexpr uintptr_t Map39BitSize = (1ULL << 39); constexpr uintptr_t Map39BitSize = (1ULL << 39);
constexpr uintptr_t Map36BitSize = (1ULL << 36); constexpr uintptr_t Map36BitSize = (1ULL << 36);
// This is not a cryptographic application, we just want something random. // Seed the MT with some initial strong randomness.
std::mt19937_64 rng; //
// This is not a cryptographic application, we just want something more
// random than the current time.
std::mt19937_64 rng(GetRandomU64());
// We want to ensure we are allocating at an address aligned to the L2 block size. // We want to ensure we are allocating at an address aligned to the L2 block size.
// For Qualcomm devices, we must also allocate memory above 36 bits. // For Qualcomm devices, we must also allocate memory above 36 bits.
@ -541,7 +550,7 @@ public:
if (write) { if (write) {
flags |= PROT_WRITE; flags |= PROT_WRITE;
} }
#ifdef HAS_NCE #ifdef ARCHITECTURE_arm64
if (execute) { if (execute) {
flags |= PROT_EXEC; flags |= PROT_EXEC;
} }
@ -621,8 +630,6 @@ public:
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {} void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {}
void EnableDirectMappedAddress() {}
u8* backing_base{nullptr}; u8* backing_base{nullptr};
u8* virtual_base{nullptr}; u8* virtual_base{nullptr};
}; };

View File

@ -1,40 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <gamemode_client.h>
#include "common/linux/gamemode.h"
#include "common/logging/log.h"
#include "common/settings.h"
namespace Common::Linux {
void StartGamemode() {
if (Settings::values.enable_gamemode) {
if (gamemode_request_start() < 0) {
LOG_WARNING(Frontend, "Failed to start gamemode: {}", gamemode_error_string());
} else {
LOG_INFO(Frontend, "Started gamemode");
}
}
}
void StopGamemode() {
if (Settings::values.enable_gamemode) {
if (gamemode_request_end() < 0) {
LOG_WARNING(Frontend, "Failed to stop gamemode: {}", gamemode_error_string());
} else {
LOG_INFO(Frontend, "Stopped gamemode");
}
}
}
void SetGamemodeState(bool state) {
if (state) {
StartGamemode();
} else {
StopGamemode();
}
}
} // namespace Common::Linux

View File

@ -1,24 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Common::Linux {
/**
* Start the (Feral Interactive) Linux gamemode if it is installed and it is activated
*/
void StartGamemode();
/**
* Stop the (Feral Interactive) Linux gamemode if it is installed and it is activated
*/
void StopGamemode();
/**
* Start or stop the (Feral Interactive) Linux gamemode if it is installed and it is activated
* @param state The new state the gamemode should have
*/
void SetGamemodeState(bool state);
} // namespace Common::Linux

View File

@ -223,9 +223,9 @@ const char* TranslateCategory(Category category) {
case Category::UiAudio: case Category::UiAudio:
return "UiAudio"; return "UiAudio";
case Category::UiLayout: case Category::UiLayout:
return "UILayout"; return "UiLayout";
case Category::UiGameList: case Category::UiGameList:
return "UIGameList"; return "UiGameList";
case Category::Screenshots: case Category::Screenshots:
return "Screenshots"; return "Screenshots";
case Category::Shortcuts: case Category::Shortcuts:
@ -236,8 +236,6 @@ const char* TranslateCategory(Category category) {
return "Services"; return "Services";
case Category::Paths: case Category::Paths:
return "Paths"; return "Paths";
case Category::Linux:
return "Linux";
case Category::MaxEnum: case Category::MaxEnum:
break; break;
} }

View File

@ -182,7 +182,7 @@ struct Values {
// Cpu // Cpu
SwitchableSetting<CpuBackend, true> cpu_backend{ SwitchableSetting<CpuBackend, true> cpu_backend{
linkage, CpuBackend::Dynarmic, CpuBackend::Dynarmic, linkage, CpuBackend::Dynarmic, CpuBackend::Dynarmic,
#ifdef HAS_NCE #ifdef ARCHITECTURE_arm64
CpuBackend::Nce, CpuBackend::Nce,
#else #else
CpuBackend::Dynarmic, CpuBackend::Dynarmic,
@ -241,11 +241,7 @@ struct Values {
SwitchableSetting<bool> use_asynchronous_gpu_emulation{ SwitchableSetting<bool> use_asynchronous_gpu_emulation{
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage, SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
#ifdef ANDROID
AstcDecodeMode::Cpu,
#else
AstcDecodeMode::Gpu, AstcDecodeMode::Gpu,
#endif
AstcDecodeMode::Cpu, AstcDecodeMode::Cpu,
AstcDecodeMode::CpuAsynchronous, AstcDecodeMode::CpuAsynchronous,
"accelerate_astc", "accelerate_astc",
@ -317,11 +313,7 @@ struct Values {
linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true}; linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage, SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
#ifdef ANDROID
GpuAccuracy::Normal,
#else
GpuAccuracy::High, GpuAccuracy::High,
#endif
GpuAccuracy::Normal, GpuAccuracy::Normal,
GpuAccuracy::Extreme, GpuAccuracy::Extreme,
"gpu_accuracy", "gpu_accuracy",
@ -330,38 +322,20 @@ struct Values {
true, true,
true}; true};
GpuAccuracy current_gpu_accuracy{GpuAccuracy::High}; GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage, SwitchableSetting<AnisotropyMode, true> max_anisotropy{
#ifdef ANDROID linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16,
AnisotropyMode::Default, "max_anisotropy", Category::RendererAdvanced};
#else
AnisotropyMode::Automatic,
#endif
AnisotropyMode::Automatic,
AnisotropyMode::X16,
"max_anisotropy",
Category::RendererAdvanced};
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage, SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
AstcRecompression::Uncompressed, AstcRecompression::Uncompressed,
AstcRecompression::Uncompressed, AstcRecompression::Uncompressed,
AstcRecompression::Bc3, AstcRecompression::Bc3,
"astc_recompression", "astc_recompression",
Category::RendererAdvanced}; Category::RendererAdvanced};
SwitchableSetting<bool> async_presentation{linkage, SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation",
#ifdef ANDROID Category::RendererAdvanced};
true,
#else
false,
#endif
"async_presentation", Category::RendererAdvanced};
SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock", SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
Category::RendererAdvanced}; Category::RendererAdvanced};
SwitchableSetting<bool> use_reactive_flushing{linkage, SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing",
#ifdef ANDROID
false,
#else
true,
#endif
"use_reactive_flushing",
Category::RendererAdvanced}; Category::RendererAdvanced};
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererAdvanced}; Category::RendererAdvanced};
@ -427,20 +401,13 @@ struct Values {
Setting<s32> current_user{linkage, 0, "current_user", Category::System}; Setting<s32> current_user{linkage, 0, "current_user", Category::System};
SwitchableSetting<ConsoleMode> use_docked_mode{linkage, SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
#ifdef ANDROID
ConsoleMode::Handheld,
#else
ConsoleMode::Docked, ConsoleMode::Docked,
#endif
"use_docked_mode", "use_docked_mode",
Category::System, Category::System,
Specialization::Radio, Specialization::Radio,
true, true,
true}; true};
// Linux
SwitchableSetting<bool> enable_gamemode{linkage, true, "enable_gamemode", Category::Linux};
// Controls // Controls
InputSetting<std::array<PlayerInput, 10>> players; InputSetting<std::array<PlayerInput, 10>> players;

View File

@ -41,7 +41,6 @@ enum class Category : u32 {
Multiplayer, Multiplayer,
Services, Services,
Paths, Paths,
Linux,
MaxEnum, MaxEnum,
}; };

View File

@ -10,7 +10,7 @@
#include "common/x64/rdtsc.h" #include "common/x64/rdtsc.h"
#endif #endif
#ifdef HAS_NCE #if defined(ARCHITECTURE_arm64) && defined(__linux__)
#include "common/arm64/native_clock.h" #include "common/arm64/native_clock.h"
#endif #endif
@ -68,7 +68,7 @@ std::unique_ptr<WallClock> CreateOptimalClock() {
// - Is not more precise than 1 GHz (1ns resolution) // - Is not more precise than 1 GHz (1ns resolution)
return std::make_unique<StandardWallClock>(); return std::make_unique<StandardWallClock>();
} }
#elif defined(HAS_NCE) #elif defined(ARCHITECTURE_arm64) && defined(__linux__)
return std::make_unique<Arm64::NativeClock>(); return std::make_unique<Arm64::NativeClock>();
#else #else
return std::make_unique<StandardWallClock>(); return std::make_unique<StandardWallClock>();

View File

@ -529,7 +529,6 @@ add_library(core STATIC
hle/service/hid/hid_server.h hle/service/hid/hid_server.h
hle/service/hid/hid_system_server.cpp hle/service/hid/hid_system_server.cpp
hle/service/hid/hid_system_server.h hle/service/hid/hid_system_server.h
hle/service/hid/hid_util.h
hle/service/hid/hidbus.cpp hle/service/hid/hidbus.cpp
hle/service/hid/hidbus.h hle/service/hid/hidbus.h
hle/service/hid/irs.cpp hle/service/hid/irs.cpp
@ -541,8 +540,8 @@ add_library(core STATIC
hle/service/hid/xcd.cpp hle/service/hid/xcd.cpp
hle/service/hid/xcd.h hle/service/hid/xcd.h
hle/service/hid/errors.h hle/service/hid/errors.h
hle/service/hid/controllers/console_six_axis.cpp hle/service/hid/controllers/console_sixaxis.cpp
hle/service/hid/controllers/console_six_axis.h hle/service/hid/controllers/console_sixaxis.h
hle/service/hid/controllers/controller_base.cpp hle/service/hid/controllers/controller_base.cpp
hle/service/hid/controllers/controller_base.h hle/service/hid/controllers/controller_base.h
hle/service/hid/controllers/debug_pad.cpp hle/service/hid/controllers/debug_pad.cpp
@ -557,10 +556,6 @@ add_library(core STATIC
hle/service/hid/controllers/npad.h hle/service/hid/controllers/npad.h
hle/service/hid/controllers/palma.cpp hle/service/hid/controllers/palma.cpp
hle/service/hid/controllers/palma.h hle/service/hid/controllers/palma.h
hle/service/hid/controllers/seven_six_axis.cpp
hle/service/hid/controllers/seven_six_axis.h
hle/service/hid/controllers/six_axis.cpp
hle/service/hid/controllers/six_axis.h
hle/service/hid/controllers/stubbed.cpp hle/service/hid/controllers/stubbed.cpp
hle/service/hid/controllers/stubbed.h hle/service/hid/controllers/stubbed.h
hle/service/hid/controllers/touchscreen.cpp hle/service/hid/controllers/touchscreen.cpp
@ -926,7 +921,8 @@ if (ENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service) target_link_libraries(core PRIVATE web_service)
endif() endif()
if (HAS_NCE) if (ARCHITECTURE_arm64 AND (ANDROID OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux"))
target_compile_definitions(core PRIVATE -DHAS_NCE)
enable_language(C ASM) enable_language(C ASM)
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp") set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
@ -935,8 +931,8 @@ if (HAS_NCE)
arm/nce/arm_nce.h arm/nce/arm_nce.h
arm/nce/arm_nce.s arm/nce/arm_nce.s
arm/nce/guest_context.h arm/nce/guest_context.h
arm/nce/patcher.cpp arm/nce/patch.cpp
arm/nce/patcher.h arm/nce/patch.h
arm/nce/instructions.h arm/nce/instructions.h
) )
target_link_libraries(core PRIVATE merry::oaknut) target_link_libraries(core PRIVATE merry::oaknut)

View File

@ -6,7 +6,7 @@
#include "common/signal_chain.h" #include "common/signal_chain.h"
#include "core/arm/nce/arm_nce.h" #include "core/arm/nce/arm_nce.h"
#include "core/arm/nce/patcher.h" #include "core/arm/nce/patch.h"
#include "core/core.h" #include "core/core.h"
#include "core/memory.h" #include "core/memory.h"
@ -116,18 +116,10 @@ bool ARM_NCE::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* ra
return true; return true;
} }
// We can't handle the access, so determine why we crashed. // We can't handle the access, so trigger an exception.
const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr); const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
guest_ctx->esr_el1.fetch_or(
// For data aborts, skip the instruction and return to guest code. static_cast<u64>(is_prefetch_abort ? HaltReason::PrefetchAbort : HaltReason::DataAbort));
// This will allow games to continue in many scenarios where they would otherwise crash.
if (!is_prefetch_abort) {
host_ctx.pc += 4;
return true;
}
// This is a prefetch abort.
guest_ctx->esr_el1.fetch_or(static_cast<u64>(HaltReason::PrefetchAbort));
// Forcibly mark the context as locked. We are still running. // Forcibly mark the context as locked. We are still running.
// We may race with SignalInterrupt here: // We may race with SignalInterrupt here:

View File

@ -7,7 +7,7 @@
#include "core/arm/nce/arm_nce.h" #include "core/arm/nce/arm_nce.h"
#include "core/arm/nce/guest_context.h" #include "core/arm/nce/guest_context.h"
#include "core/arm/nce/instructions.h" #include "core/arm/nce/instructions.h"
#include "core/arm/nce/patcher.h" #include "core/arm/nce/patch.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc.h"
@ -90,10 +90,6 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
WriteMsrHandler(AddRelocations(), oaknut::XReg{static_cast<int>(msr.GetRt())}); WriteMsrHandler(AddRelocations(), oaknut::XReg{static_cast<int>(msr.GetRt())});
continue; continue;
} }
if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
m_exclusives.push_back(i);
}
} }
// Determine patching mode for the final relocation step // Determine patching mode for the final relocation step
@ -167,9 +163,11 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
// Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not. // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not.
// Convert to ordered to preserve this assumption. // Convert to ordered to preserve this assumption.
for (const ModuleTextAddress i : m_exclusives) { for (u32 i = ModuleCodeIndex; i < static_cast<u32>(text_words.size()); i++) {
auto exclusive = Exclusive{text_words[i]}; const u32 inst = text_words[i];
text_words[i] = exclusive.AsOrdered(); if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
text_words[i] = exclusive.AsOrdered();
}
} }
// Copy to program image // Copy to program image

View File

@ -6,8 +6,12 @@
#include <span> #include <span>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
#include <oaknut/code_block.hpp> #include <oaknut/code_block.hpp>
#include <oaknut/oaknut.hpp> #include <oaknut/oaknut.hpp>
#pragma GCC diagnostic pop
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/code_set.h"
@ -89,7 +93,6 @@ private:
std::vector<Relocation> m_branch_to_patch_relocations{}; std::vector<Relocation> m_branch_to_patch_relocations{};
std::vector<Relocation> m_branch_to_module_relocations{}; std::vector<Relocation> m_branch_to_module_relocations{};
std::vector<Relocation> m_write_module_pc_relocations{}; std::vector<Relocation> m_write_module_pc_relocations{};
std::vector<ModuleTextAddress> m_exclusives{};
oaknut::Label m_save_context{}; oaknut::Label m_save_context{};
oaknut::Label m_load_context{}; oaknut::Label m_load_context{};
PatchMode mode{PatchMode::None}; PatchMode mode{PatchMode::None};

View File

@ -167,11 +167,6 @@ protected:
*/ */
std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const; std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const;
/**
* Clip the provided coordinates to be inside the touchscreen area.
*/
std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const;
WindowSystemInfo window_info; WindowSystemInfo window_info;
bool strict_context_required = false; bool strict_context_required = false;
@ -186,6 +181,11 @@ private:
// By default, ignore this request and do nothing. // By default, ignore this request and do nothing.
} }
/**
* Clip the provided coordinates to be inside the touchscreen area.
*/
std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const;
Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
u32 client_area_width; ///< Current client width, should be set by window impl. u32 client_area_width; ///< Current client width, should be set by window impl.

View File

@ -8,7 +8,6 @@
#include "common/thread.h" #include "common/thread.h"
#include "core/hid/emulated_controller.h" #include "core/hid/emulated_controller.h"
#include "core/hid/input_converter.h" #include "core/hid/input_converter.h"
#include "core/hle/service/hid/hid_util.h"
namespace Core::HID { namespace Core::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff; constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
@ -83,7 +82,7 @@ Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleInde
} }
void EmulatedController::ReloadFromSettings() { void EmulatedController::ReloadFromSettings() {
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto player_index = NpadIdTypeToIndex(npad_id_type);
const auto& player = Settings::values.players.GetValue()[player_index]; const auto& player = Settings::values.players.GetValue()[player_index];
for (std::size_t index = 0; index < player.buttons.size(); ++index) { for (std::size_t index = 0; index < player.buttons.size(); ++index) {
@ -119,7 +118,7 @@ void EmulatedController::ReloadFromSettings() {
} }
void EmulatedController::ReloadColorsFromSettings() { void EmulatedController::ReloadColorsFromSettings() {
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto player_index = NpadIdTypeToIndex(npad_id_type);
const auto& player = Settings::values.players.GetValue()[player_index]; const auto& player = Settings::values.players.GetValue()[player_index];
// Avoid updating colors if overridden by physical controller // Avoid updating colors if overridden by physical controller
@ -216,7 +215,7 @@ void EmulatedController::LoadDevices() {
} }
void EmulatedController::LoadTASParams() { void EmulatedController::LoadTASParams() {
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto player_index = NpadIdTypeToIndex(npad_id_type);
Common::ParamPackage common_params{}; Common::ParamPackage common_params{};
common_params.Set("engine", "tas"); common_params.Set("engine", "tas");
common_params.Set("port", static_cast<int>(player_index)); common_params.Set("port", static_cast<int>(player_index));
@ -265,7 +264,7 @@ void EmulatedController::LoadTASParams() {
} }
void EmulatedController::LoadVirtualGamepadParams() { void EmulatedController::LoadVirtualGamepadParams() {
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto player_index = NpadIdTypeToIndex(npad_id_type);
Common::ParamPackage common_params{}; Common::ParamPackage common_params{};
common_params.Set("engine", "virtual_gamepad"); common_params.Set("engine", "virtual_gamepad");
common_params.Set("port", static_cast<int>(player_index)); common_params.Set("port", static_cast<int>(player_index));
@ -509,11 +508,9 @@ void EmulatedController::ReloadInput() {
}); });
} }
turbo_button_state = 0; turbo_button_state = 0;
is_initalized = true;
} }
void EmulatedController::UnloadInput() { void EmulatedController::UnloadInput() {
is_initalized = false;
for (auto& button : button_devices) { for (auto& button : button_devices) {
button.reset(); button.reset();
} }
@ -618,7 +615,7 @@ bool EmulatedController::IsConfiguring() const {
} }
void EmulatedController::SaveCurrentConfig() { void EmulatedController::SaveCurrentConfig() {
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto player_index = NpadIdTypeToIndex(npad_id_type);
auto& player = Settings::values.players.GetValue()[player_index]; auto& player = Settings::values.players.GetValue()[player_index];
player.connected = is_connected; player.connected = is_connected;
player.controller_type = MapNPadToSettingsType(npad_type); player.controller_type = MapNPadToSettingsType(npad_type);
@ -1209,16 +1206,13 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
} }
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
if (!is_initalized) {
return false;
}
if (device_index >= output_devices.size()) { if (device_index >= output_devices.size()) {
return false; return false;
} }
if (!output_devices[device_index]) { if (!output_devices[device_index]) {
return false; return false;
} }
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto player_index = NpadIdTypeToIndex(npad_id_type);
const auto& player = Settings::values.players.GetValue()[player_index]; const auto& player = Settings::values.players.GetValue()[player_index];
const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
@ -1244,13 +1238,9 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
} }
bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto player_index = NpadIdTypeToIndex(npad_id_type);
const auto& player = Settings::values.players.GetValue()[player_index]; const auto& player = Settings::values.players.GetValue()[player_index];
if (!is_initalized) {
return false;
}
if (!player.vibration_enabled) { if (!player.vibration_enabled) {
return false; return false;
} }
@ -1270,10 +1260,6 @@ Common::Input::DriverResult EmulatedController::SetPollingMode(
EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index); LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index);
if (!is_initalized) {
return Common::Input::DriverResult::InvalidHandle;
}
auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)]; auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)];
auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& nfc_output_device = output_devices[3]; auto& nfc_output_device = output_devices[3];
@ -1319,10 +1305,6 @@ bool EmulatedController::SetCameraFormat(
Core::IrSensor::ImageTransferProcessorFormat camera_format) { Core::IrSensor::ImageTransferProcessorFormat camera_format) {
LOG_INFO(Service_HID, "Set camera format {}", camera_format); LOG_INFO(Service_HID, "Set camera format {}", camera_format);
if (!is_initalized) {
return false;
}
auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& camera_output_device = output_devices[2]; auto& camera_output_device = output_devices[2];
@ -1346,11 +1328,6 @@ void EmulatedController::SetRingParam(Common::ParamPackage param) {
} }
bool EmulatedController::HasNfc() const { bool EmulatedController::HasNfc() const {
if (!is_initalized) {
return false;
}
const auto& nfc_output_device = output_devices[3]; const auto& nfc_output_device = output_devices[3];
switch (npad_type) { switch (npad_type) {
@ -1388,10 +1365,6 @@ bool EmulatedController::RemoveNfcHandle() {
} }
bool EmulatedController::StartNfcPolling() { bool EmulatedController::StartNfcPolling() {
if (!is_initalized) {
return false;
}
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& nfc_virtual_output_device = output_devices[3]; auto& nfc_virtual_output_device = output_devices[3];
@ -1403,10 +1376,6 @@ bool EmulatedController::StartNfcPolling() {
} }
bool EmulatedController::StopNfcPolling() { bool EmulatedController::StopNfcPolling() {
if (!is_initalized) {
return false;
}
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& nfc_virtual_output_device = output_devices[3]; auto& nfc_virtual_output_device = output_devices[3];
@ -1418,10 +1387,6 @@ bool EmulatedController::StopNfcPolling() {
} }
bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
if (!is_initalized) {
return false;
}
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& nfc_virtual_output_device = output_devices[3]; auto& nfc_virtual_output_device = output_devices[3];
@ -1434,10 +1399,6 @@ bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request,
Common::Input::MifareRequest& out_data) { Common::Input::MifareRequest& out_data) {
if (!is_initalized) {
return false;
}
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& nfc_virtual_output_device = output_devices[3]; auto& nfc_virtual_output_device = output_devices[3];
@ -1450,10 +1411,6 @@ bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& requ
} }
bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) {
if (!is_initalized) {
return false;
}
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& nfc_virtual_output_device = output_devices[3]; auto& nfc_virtual_output_device = output_devices[3];
@ -1465,10 +1422,6 @@ bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& req
} }
bool EmulatedController::WriteNfc(const std::vector<u8>& data) { bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
if (!is_initalized) {
return false;
}
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& nfc_virtual_output_device = output_devices[3]; auto& nfc_virtual_output_device = output_devices[3];
@ -1480,10 +1433,6 @@ bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
} }
void EmulatedController::SetLedPattern() { void EmulatedController::SetLedPattern() {
if (!is_initalized) {
return;
}
for (auto& device : output_devices) { for (auto& device : output_devices) {
if (!device) { if (!device) {
continue; continue;
@ -1699,7 +1648,7 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
} }
if (is_connected) { if (is_connected) {
LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
Service::HID::NpadIdTypeToIndex(npad_id_type)); NpadIdTypeToIndex(npad_id_type));
} }
npad_type = npad_type_; npad_type = npad_type_;
} }

View File

@ -559,7 +559,6 @@ private:
NpadStyleTag supported_style_tag{NpadStyleSet::All}; NpadStyleTag supported_style_tag{NpadStyleSet::All};
bool is_connected{false}; bool is_connected{false};
bool is_configuring{false}; bool is_configuring{false};
bool is_initalized{false};
bool system_buttons_enabled{true}; bool system_buttons_enabled{true};
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
u32 turbo_button_state{0}; u32 turbo_button_state{0};

View File

@ -6,7 +6,6 @@
#include "core/hid/emulated_controller.h" #include "core/hid/emulated_controller.h"
#include "core/hid/emulated_devices.h" #include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h" #include "core/hid/hid_core.h"
#include "core/hle/service/hid/hid_util.h"
namespace Core::HID { namespace Core::HID {
@ -99,11 +98,11 @@ const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
} }
EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) { EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); return GetEmulatedController(IndexToNpadIdType(index));
} }
const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const { const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); return GetEmulatedController(IndexToNpadIdType(index));
} }
void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) { void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {

View File

@ -8,7 +8,6 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/point.h" #include "common/point.h"
#include "common/uuid.h" #include "common/uuid.h"
#include "common/vector_math.h"
namespace Core::HID { namespace Core::HID {
@ -599,29 +598,6 @@ struct SixAxisSensorIcInformation {
static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
"SixAxisSensorIcInformation is an invalid size"); "SixAxisSensorIcInformation is an invalid size");
// This is nn::hid::SixAxisSensorAttribute
struct SixAxisSensorAttribute {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_interpolated;
};
};
static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
// This is nn::hid::SixAxisSensorState
struct SixAxisSensorState {
s64 delta_time{};
s64 sampling_number{};
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
SixAxisSensorAttribute attribute{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
// This is nn::hid::VibrationDeviceHandle // This is nn::hid::VibrationDeviceHandle
struct VibrationDeviceHandle { struct VibrationDeviceHandle {
NpadStyleIndex npad_type{NpadStyleIndex::None}; NpadStyleIndex npad_type{NpadStyleIndex::None};
@ -732,4 +708,60 @@ struct UniquePadId {
}; };
static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
/// Converts a NpadIdType to an array index.
constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
switch (npad_id_type) {
case NpadIdType::Player1:
return 0;
case NpadIdType::Player2:
return 1;
case NpadIdType::Player3:
return 2;
case NpadIdType::Player4:
return 3;
case NpadIdType::Player5:
return 4;
case NpadIdType::Player6:
return 5;
case NpadIdType::Player7:
return 6;
case NpadIdType::Player8:
return 7;
case NpadIdType::Handheld:
return 8;
case NpadIdType::Other:
return 9;
default:
return 0;
}
}
/// Converts an array index to a NpadIdType
constexpr NpadIdType IndexToNpadIdType(size_t index) {
switch (index) {
case 0:
return NpadIdType::Player1;
case 1:
return NpadIdType::Player2;
case 2:
return NpadIdType::Player3;
case 3:
return NpadIdType::Player4;
case 4:
return NpadIdType::Player5;
case 5:
return NpadIdType::Player6;
case 6:
return NpadIdType::Player7;
case 7:
return NpadIdType::Player8;
case 8:
return NpadIdType::Handheld;
case 9:
return NpadIdType::Other;
default:
return NpadIdType::Invalid;
}
}
} // namespace Core::HID } // namespace Core::HID

View File

@ -13,14 +13,14 @@ InputInterpreter::InputInterpreter(Core::System& system)
: npad{system.ServiceManager() : npad{system.ServiceManager()
.GetService<Service::HID::IHidServer>("hid") .GetService<Service::HID::IHidServer>("hid")
->GetResourceManager() ->GetResourceManager()
->GetNpad()} { ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {
ResetButtonStates(); ResetButtonStates();
} }
InputInterpreter::~InputInterpreter() = default; InputInterpreter::~InputInterpreter() = default;
void InputInterpreter::PollInput() { void InputInterpreter::PollInput() {
const auto button_state = npad->GetAndResetPressState(); const auto button_state = npad.GetAndResetPressState();
previous_index = current_index; previous_index = current_index;
current_index = (current_index + 1) % button_states.size(); current_index = (current_index + 1) % button_states.size();

View File

@ -16,7 +16,7 @@ enum class NpadButton : u64;
} }
namespace Service::HID { namespace Service::HID {
class NPad; class Controller_NPad;
} }
/** /**
@ -101,7 +101,7 @@ public:
} }
private: private:
std::shared_ptr<Service::HID::NPad> npad; Service::HID::Controller_NPad& npad;
/// Stores 9 consecutive button states polled from HID. /// Stores 9 consecutive button states polled from HID.
std::array<Core::HID::NpadButton, 9> button_states{}; std::array<Core::HID::NpadButton, 9> button_states{};

View File

@ -5678,8 +5678,15 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
case OperationType::ChangePermissions: case OperationType::ChangePermissions:
case OperationType::ChangePermissionsAndRefresh: case OperationType::ChangePermissionsAndRefresh:
case OperationType::ChangePermissionsAndRefreshAndFlush: { case OperationType::ChangePermissionsAndRefreshAndFlush: {
m_memory->ProtectRegion(*m_impl, virt_addr, num_pages * PageSize, const bool read = True(properties.perm & Kernel::KMemoryPermission::UserRead);
ConvertToMemoryPermission(properties.perm)); const bool write = True(properties.perm & Kernel::KMemoryPermission::UserWrite);
// todo: this doesn't really belong here and should go into m_memory to handle rasterizer
// access todo: ignore exec on non-direct-mapped case
const bool exec = True(properties.perm & Kernel::KMemoryPermission::UserExecute);
if (Settings::IsFastmemEnabled()) {
m_system.DeviceMemory().buffer.Protect(GetInteger(virt_addr), num_pages * PageSize,
read, write, exec);
}
R_SUCCEED(); R_SUCCEED();
} }
default: default:

View File

@ -1215,7 +1215,7 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read); ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read);
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
#ifdef HAS_NCE #ifdef ARCHITECTURE_arm64
if (Settings::IsNceEnabled()) { if (Settings::IsNceEnabled()) {
auto& buffer = m_kernel.System().DeviceMemory().buffer; auto& buffer = m_kernel.System().DeviceMemory().buffer;
const auto& code = code_set.CodeSegment(); const auto& code = code_set.CodeSegment();

View File

@ -122,8 +122,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
Service::NFP::RegisterInfoPrivate register_info{}; Service::NFP::RegisterInfoPrivate register_info{};
std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(), std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(),
std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1)); std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1));
register_info.mii_store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
register_info.mii_store_data.SetNickname({u'y', u'u', u'z', u'u'});
nfp_device->SetRegisterInfoPrivate(register_info); nfp_device->SetRegisterInfoPrivate(register_info);
break; break;
} }
@ -131,7 +130,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
nfp_device->DeleteApplicationArea(); nfp_device->DeleteApplicationArea();
break; break;
case Service::NFP::CabinetMode::StartRestorer: case Service::NFP::CabinetMode::StartRestorer:
nfp_device->Restore(); nfp_device->RestoreAmiibo();
break; break;
case Service::NFP::CabinetMode::StartFormatter: case Service::NFP::CabinetMode::StartFormatter:
nfp_device->Format(); nfp_device->Format();

View File

@ -1,42 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/console_six_axis.h"
#include "core/memory.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
console = hid_core.GetEmulatedConsole();
static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
"ConsoleSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
}
ConsoleSixAxis::~ConsoleSixAxis() = default;
void ConsoleSixAxis::OnInit() {}
void ConsoleSixAxis::OnRelease() {}
void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
return;
}
const auto motion_status = console->GetMotion();
shared_memory->sampling_number++;
shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
shared_memory->verticalization_error = motion_status.verticalization_error;
shared_memory->gyro_bias = motion_status.gyro_bias;
}
} // namespace Service::HID

View File

@ -1,43 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/vector_math.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Core::HID {
class EmulatedConsole;
} // namespace Core::HID
namespace Service::HID {
class ConsoleSixAxis final : public ControllerBase {
public:
explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~ConsoleSixAxis() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
struct ConsoleSharedMemory {
u64 sampling_number{};
bool is_seven_six_axis_sensor_at_rest{};
INSERT_PADDING_BYTES(3); // padding
f32 verticalization_error{};
Common::Vec3f gyro_bias{};
INSERT_PADDING_BYTES(4); // padding
};
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
ConsoleSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedConsole* console = nullptr;
};
} // namespace Service::HID

View File

@ -1,29 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_console.h" #include "core/hid/emulated_console.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h" #include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/seven_six_axis.h" #include "core/hle/service/hid/controllers/console_sixaxis.h"
#include "core/memory.h" #include "core/memory.h"
namespace Service::HID { namespace Service::HID {
SevenSixAxis::SevenSixAxis(Core::System& system_) constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_)
: ControllerBase{system_.HIDCore()}, system{system_} { : ControllerBase{system_.HIDCore()}, system{system_} {
console = hid_core.GetEmulatedConsole(); console = hid_core.GetEmulatedConsole();
static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
"ConsoleSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
} }
SevenSixAxis::~SevenSixAxis() = default; Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
void SevenSixAxis::OnInit() {} void Controller_ConsoleSixAxis::OnInit() {}
void SevenSixAxis::OnRelease() {}
void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { void Controller_ConsoleSixAxis::OnRelease() {}
void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated() || transfer_memory == 0) { if (!IsControllerActivated() || transfer_memory == 0) {
seven_sixaxis_lifo.buffer_count = 0; seven_sixaxis_lifo.buffer_count = 0;
seven_sixaxis_lifo.buffer_tail = 0; seven_sixaxis_lifo.buffer_tail = 0;
@ -50,17 +53,22 @@ void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
-motion_status.quaternion.xyz.z, -motion_status.quaternion.xyz.z,
}; };
shared_memory->sampling_number++;
shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
shared_memory->verticalization_error = motion_status.verticalization_error;
shared_memory->gyro_bias = motion_status.gyro_bias;
// Update seven six axis transfer memory
seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
sizeof(seven_sixaxis_lifo)); sizeof(seven_sixaxis_lifo));
} }
void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { void Controller_ConsoleSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
transfer_memory = t_mem; transfer_memory = t_mem;
} }
void SevenSixAxis::ResetTimestamp() { void Controller_ConsoleSixAxis::ResetTimestamp() {
last_saved_timestamp = last_global_timestamp; last_saved_timestamp = last_global_timestamp;
} }
} // namespace Service::HID } // namespace Service::HID

View File

@ -1,9 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include "common/common_types.h" #include <array>
#include "common/quaternion.h" #include "common/quaternion.h"
#include "common/typed_address.h" #include "common/typed_address.h"
#include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/controllers/controller_base.h"
@ -18,10 +19,10 @@ class EmulatedConsole;
} // namespace Core::HID } // namespace Core::HID
namespace Service::HID { namespace Service::HID {
class SevenSixAxis final : public ControllerBase { class Controller_ConsoleSixAxis final : public ControllerBase {
public: public:
explicit SevenSixAxis(Core::System& system_); explicit Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_);
~SevenSixAxis() override; ~Controller_ConsoleSixAxis() override;
// Called when the controller is initialized // Called when the controller is initialized
void OnInit() override; void OnInit() override;
@ -50,16 +51,28 @@ private:
}; };
static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size"); static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
struct ConsoleSharedMemory {
u64 sampling_number{};
bool is_seven_six_axis_sensor_at_rest{};
INSERT_PADDING_BYTES(3); // padding
f32 verticalization_error{};
Common::Vec3f gyro_bias{};
INSERT_PADDING_BYTES(4); // padding
};
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
SevenSixAxisState next_seven_sixaxis_state{};
Common::ProcessAddress transfer_memory{};
ConsoleSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedConsole* console = nullptr;
u64 last_saved_timestamp{}; u64 last_saved_timestamp{};
u64 last_global_timestamp{}; u64 last_global_timestamp{};
SevenSixAxisState next_seven_sixaxis_state{};
Common::ProcessAddress transfer_memory{};
Core::HID::EmulatedConsole* console = nullptr;
Core::System& system; Core::System& system;
}; };
} // namespace Service::HID } // namespace Service::HID

View File

@ -13,7 +13,7 @@
namespace Service::HID { namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} { : ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size, static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
"DebugPadSharedMemory is bigger than the shared memory"); "DebugPadSharedMemory is bigger than the shared memory");
@ -22,13 +22,13 @@ DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
} }
DebugPad::~DebugPad() = default; Controller_DebugPad::~Controller_DebugPad() = default;
void DebugPad::OnInit() {} void Controller_DebugPad::OnInit() {}
void DebugPad::OnRelease() {} void Controller_DebugPad::OnRelease() {}
void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) { if (!IsControllerActivated()) {
shared_memory->debug_pad_lifo.buffer_count = 0; shared_memory->debug_pad_lifo.buffer_count = 0;
shared_memory->debug_pad_lifo.buffer_tail = 0; shared_memory->debug_pad_lifo.buffer_tail = 0;

View File

@ -15,10 +15,10 @@ struct AnalogStickState;
} // namespace Core::HID } // namespace Core::HID
namespace Service::HID { namespace Service::HID {
class DebugPad final : public ControllerBase { class Controller_DebugPad final : public ControllerBase {
public: public:
explicit DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~DebugPad() override; ~Controller_DebugPad() override;
// Called when the controller is initialized // Called when the controller is initialized
void OnInit() override; void OnInit() override;

View File

@ -23,7 +23,7 @@ constexpr f32 Square(s32 num) {
return static_cast<f32>(num * num); return static_cast<f32>(num * num);
} }
Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase(hid_core_) { : ControllerBase(hid_core_) {
static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size, static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
"GestureSharedMemory is bigger than the shared memory"); "GestureSharedMemory is bigger than the shared memory");
@ -31,17 +31,17 @@ Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
console = hid_core.GetEmulatedConsole(); console = hid_core.GetEmulatedConsole();
} }
Gesture::~Gesture() = default; Controller_Gesture::~Controller_Gesture() = default;
void Gesture::OnInit() { void Controller_Gesture::OnInit() {
shared_memory->gesture_lifo.buffer_count = 0; shared_memory->gesture_lifo.buffer_count = 0;
shared_memory->gesture_lifo.buffer_tail = 0; shared_memory->gesture_lifo.buffer_tail = 0;
force_update = true; force_update = true;
} }
void Gesture::OnRelease() {} void Controller_Gesture::OnRelease() {}
void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) { if (!IsControllerActivated()) {
shared_memory->gesture_lifo.buffer_count = 0; shared_memory->gesture_lifo.buffer_count = 0;
shared_memory->gesture_lifo.buffer_tail = 0; shared_memory->gesture_lifo.buffer_tail = 0;
@ -64,7 +64,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
UpdateGestureSharedMemory(gesture, time_difference); UpdateGestureSharedMemory(gesture, time_difference);
} }
void Gesture::ReadTouchInput() { void Controller_Gesture::ReadTouchInput() {
if (!Settings::values.touchscreen.enabled) { if (!Settings::values.touchscreen.enabled) {
fingers = {}; fingers = {};
return; return;
@ -76,7 +76,8 @@ void Gesture::ReadTouchInput() {
} }
} }
bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) { bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry(); const auto& last_entry = GetLastGestureEntry();
if (force_update) { if (force_update) {
force_update = false; force_update = false;
@ -99,7 +100,8 @@ bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_dif
return false; return false;
} }
void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
f32 time_difference) {
GestureType type = GestureType::Idle; GestureType type = GestureType::Idle;
GestureAttribute attributes{}; GestureAttribute attributes{};
@ -136,8 +138,8 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
shared_memory->gesture_lifo.WriteNextEntry(next_state); shared_memory->gesture_lifo.WriteNextEntry(next_state);
} }
void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
GestureAttribute& attributes) { GestureAttribute& attributes) {
const auto& last_entry = GetLastGestureEntry(); const auto& last_entry = GetLastGestureEntry();
gesture.detection_count++; gesture.detection_count++;
@ -150,8 +152,8 @@ void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
} }
} }
void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
f32 time_difference) { f32 time_difference) {
const auto& last_entry = GetLastGestureEntry(); const auto& last_entry = GetLastGestureEntry();
// Promote to pan type if touch moved // Promote to pan type if touch moved
@ -184,8 +186,9 @@ void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& typ
} }
} }
void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, void Controller_Gesture::EndGesture(GestureProperties& gesture,
GestureType& type, GestureAttribute& attributes, f32 time_difference) { GestureProperties& last_gesture_props, GestureType& type,
GestureAttribute& attributes, f32 time_difference) {
const auto& last_entry = GetLastGestureEntry(); const auto& last_entry = GetLastGestureEntry();
if (last_gesture_props.active_points != 0) { if (last_gesture_props.active_points != 0) {
@ -219,8 +222,9 @@ void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_ges
} }
} }
void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
GestureType& type, GestureAttribute& attributes) { GestureProperties& last_gesture_props, GestureType& type,
GestureAttribute& attributes) {
type = GestureType::Tap; type = GestureType::Tap;
gesture = last_gesture_props; gesture = last_gesture_props;
force_update = true; force_update = true;
@ -232,8 +236,9 @@ void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_ge
} }
} }
void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
GestureType& type, f32 time_difference) { GestureProperties& last_gesture_props, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry(); const auto& last_entry = GetLastGestureEntry();
next_state.delta = gesture.mid_point - last_entry.pos; next_state.delta = gesture.mid_point - last_entry.pos;
@ -258,8 +263,9 @@ void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last
} }
} }
void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
GestureType& type, f32 time_difference) { GestureProperties& last_gesture_props, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry(); const auto& last_entry = GetLastGestureEntry();
next_state.vel_x = next_state.vel_x =
static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
@ -281,8 +287,8 @@ void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_ge
force_update = true; force_update = true;
} }
void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
GestureType& type) { GestureProperties& last_gesture_props, GestureType& type) {
const auto& last_entry = GetLastGestureEntry(); const auto& last_entry = GetLastGestureEntry();
type = GestureType::Swipe; type = GestureType::Swipe;
@ -305,11 +311,11 @@ void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_
next_state.direction = GestureDirection::Up; next_state.direction = GestureDirection::Up;
} }
const Gesture::GestureState& Gesture::GetLastGestureEntry() const { const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
return shared_memory->gesture_lifo.ReadCurrentEntry().state; return shared_memory->gesture_lifo.ReadCurrentEntry().state;
} }
Gesture::GestureProperties Gesture::GetGestureProperties() { Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
GestureProperties gesture; GestureProperties gesture;
std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),

View File

@ -12,10 +12,10 @@
#include "core/hle/service/hid/ring_lifo.h" #include "core/hle/service/hid/ring_lifo.h"
namespace Service::HID { namespace Service::HID {
class Gesture final : public ControllerBase { class Controller_Gesture final : public ControllerBase {
public: public:
explicit Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Gesture() override; ~Controller_Gesture() override;
// Called when the controller is initialized // Called when the controller is initialized
void OnInit() override; void OnInit() override;

View File

@ -12,7 +12,7 @@
namespace Service::HID { namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} { : ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size, static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
"KeyboardSharedMemory is bigger than the shared memory"); "KeyboardSharedMemory is bigger than the shared memory");
@ -21,13 +21,13 @@ Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
emulated_devices = hid_core.GetEmulatedDevices(); emulated_devices = hid_core.GetEmulatedDevices();
} }
Keyboard::~Keyboard() = default; Controller_Keyboard::~Controller_Keyboard() = default;
void Keyboard::OnInit() {} void Controller_Keyboard::OnInit() {}
void Keyboard::OnRelease() {} void Controller_Keyboard::OnRelease() {}
void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) { if (!IsControllerActivated()) {
shared_memory->keyboard_lifo.buffer_count = 0; shared_memory->keyboard_lifo.buffer_count = 0;
shared_memory->keyboard_lifo.buffer_tail = 0; shared_memory->keyboard_lifo.buffer_tail = 0;

View File

@ -14,10 +14,10 @@ struct KeyboardKey;
} // namespace Core::HID } // namespace Core::HID
namespace Service::HID { namespace Service::HID {
class Keyboard final : public ControllerBase { class Controller_Keyboard final : public ControllerBase {
public: public:
explicit Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Keyboard() override; ~Controller_Keyboard() override;
// Called when the controller is initialized // Called when the controller is initialized
void OnInit() override; void OnInit() override;

View File

@ -12,7 +12,8 @@
namespace Service::HID { namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
Mouse::Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size, static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size,
"MouseSharedMemory is bigger than the shared memory"); "MouseSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at( shared_memory = std::construct_at(
@ -20,12 +21,12 @@ Mouse::Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : Controller
emulated_devices = hid_core.GetEmulatedDevices(); emulated_devices = hid_core.GetEmulatedDevices();
} }
Mouse::~Mouse() = default; Controller_Mouse::~Controller_Mouse() = default;
void Mouse::OnInit() {} void Controller_Mouse::OnInit() {}
void Mouse::OnRelease() {} void Controller_Mouse::OnRelease() {}
void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) { if (!IsControllerActivated()) {
shared_memory->mouse_lifo.buffer_count = 0; shared_memory->mouse_lifo.buffer_count = 0;
shared_memory->mouse_lifo.buffer_tail = 0; shared_memory->mouse_lifo.buffer_tail = 0;

View File

@ -14,10 +14,10 @@ struct AnalogStickState;
} // namespace Core::HID } // namespace Core::HID
namespace Service::HID { namespace Service::HID {
class Mouse final : public ControllerBase { class Controller_Mouse final : public ControllerBase {
public: public:
explicit Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Mouse() override; ~Controller_Mouse() override;
// Called when the controller is initialized // Called when the controller is initialized
void OnInit() override; void OnInit() override;

View File

@ -18,7 +18,6 @@
#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid_util.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
namespace Service::HID { namespace Service::HID {
@ -30,8 +29,60 @@ constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
Core::HID::NpadIdType::Handheld, Core::HID::NpadIdType::Handheld,
}; };
NPad::NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
KernelHelpers::ServiceContext& service_context_) switch (npad_id) {
case Core::HID::NpadIdType::Player1:
case Core::HID::NpadIdType::Player2:
case Core::HID::NpadIdType::Player3:
case Core::HID::NpadIdType::Player4:
case Core::HID::NpadIdType::Player5:
case Core::HID::NpadIdType::Player6:
case Core::HID::NpadIdType::Player7:
case Core::HID::NpadIdType::Player8:
case Core::HID::NpadIdType::Other:
case Core::HID::NpadIdType::Handheld:
return true;
default:
LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id);
return false;
}
}
Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
if (!npad_type) {
return VibrationInvalidStyleIndex;
}
if (!npad_id) {
return VibrationInvalidNpadId;
}
if (!device_index) {
return VibrationDeviceIndexOutOfRange;
}
return ResultSuccess;
}
Result Controller_NPad::VerifyValidSixAxisSensorHandle(
const Core::HID::SixAxisSensorHandle& device_handle) {
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
if (!npad_id) {
return InvalidNpadId;
}
if (!device_index) {
return NpadDeviceIndexOutOfRange;
}
return ResultSuccess;
}
Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_)
: ControllerBase{hid_core_}, service_context{service_context_} { : ControllerBase{hid_core_}, service_context{service_context_} {
static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size); static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size);
for (std::size_t i = 0; i < controller_data.size(); ++i) { for (std::size_t i = 0; i < controller_data.size(); ++i) {
@ -52,7 +103,7 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
} }
} }
NPad::~NPad() { Controller_NPad::~Controller_NPad() {
for (std::size_t i = 0; i < controller_data.size(); ++i) { for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i]; auto& controller = controller_data[i];
controller.device->DeleteCallback(controller.callback_key); controller.device->DeleteCallback(controller.callback_key);
@ -60,7 +111,8 @@ NPad::~NPad() {
OnRelease(); OnRelease();
} }
void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) { void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
std::size_t controller_idx) {
if (type == Core::HID::ControllerTriggerType::All) { if (type == Core::HID::ControllerTriggerType::All) {
ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
@ -98,7 +150,7 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c
} }
} }
void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
auto& controller = GetControllerFromNpadIdType(npad_id); auto& controller = GetControllerFromNpadIdType(npad_id);
if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) { if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
return; return;
@ -298,7 +350,7 @@ void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
hid_core.SetLastActiveController(npad_id); hid_core.SetLastActiveController(npad_id);
} }
void NPad::OnInit() { void Controller_NPad::OnInit() {
if (!IsControllerActivated()) { if (!IsControllerActivated()) {
return; return;
} }
@ -332,7 +384,7 @@ void NPad::OnInit() {
} }
} }
void NPad::WriteEmptyEntry(NpadInternalState* npad) { void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) {
NPadGenericState dummy_pad_state{}; NPadGenericState dummy_pad_state{};
NpadGcTriggerState dummy_gc_state{}; NpadGcTriggerState dummy_gc_state{};
dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
@ -353,7 +405,7 @@ void NPad::WriteEmptyEntry(NpadInternalState* npad) {
npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
} }
void NPad::OnRelease() { void Controller_NPad::OnRelease() {
is_controller_initialized = false; is_controller_initialized = false;
for (std::size_t i = 0; i < controller_data.size(); ++i) { for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i]; auto& controller = controller_data[i];
@ -364,7 +416,7 @@ void NPad::OnRelease() {
} }
} }
void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};
auto& controller = GetControllerFromNpadIdType(npad_id); auto& controller = GetControllerFromNpadIdType(npad_id);
const auto controller_type = controller.device->GetNpadStyleIndex(); const auto controller_type = controller.device->GetNpadStyleIndex();
@ -433,7 +485,7 @@ void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
} }
} }
void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) { if (!IsControllerActivated()) {
return; return;
} }
@ -563,7 +615,134 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
} }
} }
void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i];
const auto& controller_type = controller.device->GetNpadStyleIndex();
if (controller_type == Core::HID::NpadStyleIndex::None ||
!controller.device->IsConnected()) {
continue;
}
auto* npad = controller.shared_memory;
const auto& motion_state = controller.device->GetMotions();
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
// Clear previous state
sixaxis_fullkey_state = {};
sixaxis_handheld_state = {};
sixaxis_dual_left_state = {};
sixaxis_dual_right_state = {};
sixaxis_left_lifo_state = {};
sixaxis_right_lifo_state = {};
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
controller.sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_state.size(); ++e) {
controller.sixaxis_at_rest =
controller.sixaxis_at_rest && motion_state[e].is_at_rest;
}
}
const auto set_motion_state = [&](SixAxisSensorState& state,
const Core::HID::ControllerMotion& hid_state) {
using namespace std::literals::chrono_literals;
static constexpr SixAxisSensorState default_motion_state = {
.delta_time = std::chrono::nanoseconds(5ms).count(),
.accel = {0, 0, -1.0f},
.orientation =
{
Common::Vec3f{1.0f, 0, 0},
Common::Vec3f{0, 1.0f, 0},
Common::Vec3f{0, 0, 1.0f},
},
.attribute = {1},
};
if (!controller.sixaxis_sensor_enabled) {
state = default_motion_state;
return;
}
if (!Settings::values.motion_enabled.GetValue()) {
state = default_motion_state;
return;
}
state.attribute.is_connected.Assign(1);
state.delta_time = std::chrono::nanoseconds(5ms).count();
state.accel = hid_state.accel;
state.gyro = hid_state.gyro;
state.rotation = hid_state.rotation;
state.orientation = hid_state.orientation;
};
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
set_motion_state(sixaxis_handheld_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
set_motion_state(sixaxis_dual_left_state, motion_state[0]);
set_motion_state(sixaxis_dual_right_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::Pokeball:
using namespace std::literals::chrono_literals;
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
break;
default:
break;
}
sixaxis_fullkey_state.sampling_number =
npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
// This buffer only is updated on handheld on HW
npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
}
npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
}
}
void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
hid_core.SetSupportedStyleTag(style_set); hid_core.SetSupportedStyleTag(style_set);
if (is_controller_initialized) { if (is_controller_initialized) {
@ -574,14 +753,14 @@ void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
is_controller_initialized = true; is_controller_initialized = true;
} }
Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const { Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
if (!is_controller_initialized) { if (!is_controller_initialized) {
return {Core::HID::NpadStyleSet::None}; return {Core::HID::NpadStyleSet::None};
} }
return hid_core.GetSupportedStyleTag(); return hid_core.GetSupportedStyleTag();
} }
Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
constexpr std::size_t max_number_npad_ids = 0xa; constexpr std::size_t max_number_npad_ids = 0xa;
const auto length = data.size(); const auto length = data.size();
ASSERT(length > 0 && (length % sizeof(u32)) == 0); ASSERT(length > 0 && (length % sizeof(u32)) == 0);
@ -597,17 +776,17 @@ Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
return ResultSuccess; return ResultSuccess;
} }
void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); const auto copy_amount = supported_npad_id_types.size() * sizeof(u32);
ASSERT(max_length <= copy_amount); ASSERT(max_length <= copy_amount);
std::memcpy(data, supported_npad_id_types.data(), copy_amount); std::memcpy(data, supported_npad_id_types.data(), copy_amount);
} }
std::size_t NPad::GetSupportedNpadIdTypesSize() const { std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
return supported_npad_id_types.size(); return supported_npad_id_types.size();
} }
void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
if (joy_hold_type != NpadJoyHoldType::Horizontal && if (joy_hold_type != NpadJoyHoldType::Horizontal &&
joy_hold_type != NpadJoyHoldType::Vertical) { joy_hold_type != NpadJoyHoldType::Vertical) {
LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}", LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
@ -617,11 +796,11 @@ void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
hold_type = joy_hold_type; hold_type = joy_hold_type;
} }
NPad::NpadJoyHoldType NPad::GetHoldType() const { Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const {
return hold_type; return hold_type;
} }
void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) {
ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); ASSERT_MSG(false, "Activation mode should be always None, Single or Dual");
return; return;
@ -630,20 +809,21 @@ void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_m
handheld_activation_mode = activation_mode; handheld_activation_mode = activation_mode;
} }
NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const { Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const {
return handheld_activation_mode; return handheld_activation_mode;
} }
void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { void Controller_NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) {
communication_mode = communication_mode_; communication_mode = communication_mode_;
} }
NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const { Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode() const {
return communication_mode; return communication_mode;
} }
bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) { NpadJoyDeviceType npad_device_type,
NpadJoyAssignmentMode assignment_mode) {
if (!IsNpadIdValid(npad_id)) { if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return false; return false;
@ -712,8 +892,9 @@ bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType
return true; return true;
} }
bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
const Core::HID::VibrationValue& vibration_value) { std::size_t device_index,
const Core::HID::VibrationValue& vibration_value) {
auto& controller = GetControllerFromNpadIdType(npad_id); auto& controller = GetControllerFromNpadIdType(npad_id);
if (!controller.device->IsConnected()) { if (!controller.device->IsConnected()) {
return false; return false;
@ -757,9 +938,10 @@ bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t d
return controller.device->SetVibration(device_index, vibration); return controller.device->SetVibration(device_index, vibration);
} }
void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle, void Controller_NPad::VibrateController(
const Core::HID::VibrationValue& vibration_value) { const Core::HID::VibrationDeviceHandle& vibration_device_handle,
if (IsVibrationHandleValid(vibration_device_handle).IsError()) { const Core::HID::VibrationValue& vibration_value) {
if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return; return;
} }
@ -803,7 +985,7 @@ void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_d
} }
} }
void NPad::VibrateControllers( void Controller_NPad::VibrateControllers(
std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
std::span<const Core::HID::VibrationValue> vibration_values) { std::span<const Core::HID::VibrationValue> vibration_values) {
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
@ -820,9 +1002,9 @@ void NPad::VibrateControllers(
} }
} }
Core::HID::VibrationValue NPad::GetLastVibration( Core::HID::VibrationValue Controller_NPad::GetLastVibration(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
if (IsVibrationHandleValid(vibration_device_handle).IsError()) { if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return {}; return {};
} }
@ -831,9 +1013,9 @@ Core::HID::VibrationValue NPad::GetLastVibration(
return controller.vibration[device_index].latest_vibration_value; return controller.vibration[device_index].latest_vibration_value;
} }
void NPad::InitializeVibrationDevice( void Controller_NPad::InitializeVibrationDevice(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) { const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
if (IsVibrationHandleValid(vibration_device_handle).IsError()) { if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return; return;
} }
@ -842,8 +1024,8 @@ void NPad::InitializeVibrationDevice(
InitializeVibrationDeviceAtIndex(npad_index, device_index); InitializeVibrationDeviceAtIndex(npad_index, device_index);
} }
void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
std::size_t device_index) { std::size_t device_index) {
auto& controller = GetControllerFromNpadIdType(npad_id); auto& controller = GetControllerFromNpadIdType(npad_id);
if (!Settings::values.vibration_enabled.GetValue()) { if (!Settings::values.vibration_enabled.GetValue()) {
controller.vibration[device_index].device_mounted = false; controller.vibration[device_index].device_mounted = false;
@ -854,13 +1036,13 @@ void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
controller.device->IsVibrationEnabled(device_index); controller.device->IsVibrationEnabled(device_index);
} }
void NPad::SetPermitVibrationSession(bool permit_vibration_session) { void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
permit_vibration_session_enabled = permit_vibration_session; permit_vibration_session_enabled = permit_vibration_session;
} }
bool NPad::IsVibrationDeviceMounted( bool Controller_NPad::IsVibrationDeviceMounted(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
if (IsVibrationHandleValid(vibration_device_handle).IsError()) { if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return false; return false;
} }
@ -869,7 +1051,7 @@ bool NPad::IsVibrationDeviceMounted(
return controller.vibration[device_index].device_mounted; return controller.vibration[device_index].device_mounted;
} }
Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) { if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
// Fallback to player 1 // Fallback to player 1
@ -881,17 +1063,18 @@ Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad
return controller.styleset_changed_event->GetReadableEvent(); return controller.styleset_changed_event->GetReadableEvent();
} }
void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
const auto& controller = GetControllerFromNpadIdType(npad_id); const auto& controller = GetControllerFromNpadIdType(npad_id);
controller.styleset_changed_event->Signal(); controller.styleset_changed_event->Signal();
} }
void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) { void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller,
Core::HID::NpadIdType npad_id) {
UpdateControllerAt(controller, npad_id, true); UpdateControllerAt(controller, npad_id, true);
} }
void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id, void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
bool connected) { Core::HID::NpadIdType npad_id, bool connected) {
auto& controller = GetControllerFromNpadIdType(npad_id); auto& controller = GetControllerFromNpadIdType(npad_id);
if (!connected) { if (!connected) {
DisconnectNpad(npad_id); DisconnectNpad(npad_id);
@ -902,7 +1085,7 @@ void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdT
InitNewlyAddedController(npad_id); InitNewlyAddedController(npad_id);
} }
Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) { if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return InvalidNpadId; return InvalidNpadId;
@ -951,9 +1134,54 @@ Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
return ResultSuccess; return ResultSuccess;
} }
Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor( Result Controller_NPad::SetGyroscopeZeroDriftMode(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::GyroscopeZeroDriftMode drift_mode) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
auto& controller = GetControllerFromHandle(sixaxis_handle);
sixaxis.gyroscope_zero_drift_mode = drift_mode;
controller.device->SetGyroscopeZeroDriftMode(drift_mode);
return ResultSuccess;
}
Result Controller_NPad::GetGyroscopeZeroDriftMode(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
drift_mode = sixaxis.gyroscope_zero_drift_mode;
return ResultSuccess;
}
Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_at_rest) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& controller = GetControllerFromHandle(sixaxis_handle);
is_at_rest = controller.sixaxis_at_rest;
return ResultSuccess;
}
Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) { if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid; return is_valid;
@ -964,9 +1192,65 @@ Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
return ResultSuccess; return ResultSuccess;
} }
Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
sixaxis.unaltered_passtrough = is_enabled;
return ResultSuccess;
}
Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
is_enabled = sixaxis.unaltered_passtrough;
return ResultSuccess;
}
Result Controller_NPad::LoadSixAxisSensorCalibrationParameter(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
// TODO: Request this data to the controller. On error return 0xd8ca
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
calibration = sixaxis.calibration;
return ResultSuccess;
}
Result Controller_NPad::GetSixAxisSensorIcInformation(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorIcInformation& ic_information) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
// TODO: Request this data to the controller. On error return 0xd8ca
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
ic_information = sixaxis.ic_information;
return ResultSuccess;
}
Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) { const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) { if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid; return is_valid;
@ -978,32 +1262,83 @@ Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
return ResultSuccess; return ResultSuccess;
} }
NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) { Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo; bool sixaxis_status) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& controller = GetControllerFromHandle(sixaxis_handle);
controller.sixaxis_sensor_enabled = sixaxis_status;
return ResultSuccess;
} }
NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) { Result Controller_NPad::IsSixAxisSensorFusionEnabled(
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo; const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
is_fusion_enabled = sixaxis.is_fusion_enabled;
return ResultSuccess;
}
Result Controller_NPad::SetSixAxisFusionEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
sixaxis.is_fusion_enabled = is_fusion_enabled;
return ResultSuccess;
} }
NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) { Result Controller_NPad::SetSixAxisFusionParameters(
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo; const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto param1 = sixaxis_fusion_parameters.parameter1;
if (param1 < 0.0f || param1 > 1.0f) {
return InvalidSixAxisFusionRange;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
sixaxis.fusion = sixaxis_fusion_parameters;
return ResultSuccess;
} }
NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) { Result Controller_NPad::GetSixAxisFusionParameters(
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo; const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters& parameters) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
parameters = sixaxis.fusion;
return ResultSuccess;
} }
NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) { Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo; Core::HID::NpadIdType npad_id_2) {
}
NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
}
Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2); npad_id_2);
@ -1065,17 +1400,18 @@ Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
return ResultSuccess; return ResultSuccess;
} }
void NPad::StartLRAssignmentMode() { void Controller_NPad::StartLRAssignmentMode() {
// Nothing internally is used for lr assignment mode. Since we have the ability to set the // Nothing internally is used for lr assignment mode. Since we have the ability to set the
// controller types from boot, it doesn't really matter about showing a selection screen // controller types from boot, it doesn't really matter about showing a selection screen
is_in_lr_assignment_mode = true; is_in_lr_assignment_mode = true;
} }
void NPad::StopLRAssignmentMode() { void Controller_NPad::StopLRAssignmentMode() {
is_in_lr_assignment_mode = false; is_in_lr_assignment_mode = false;
} }
Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) { Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2); npad_id_2);
@ -1106,7 +1442,8 @@ Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::Npad
return ResultSuccess; return ResultSuccess;
} }
Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const { Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
Core::HID::LedPattern& pattern) const {
if (!IsNpadIdValid(npad_id)) { if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return InvalidNpadId; return InvalidNpadId;
@ -1116,8 +1453,8 @@ Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern&
return ResultSuccess; return ResultSuccess;
} }
Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
bool& is_valid) const { bool& is_valid) const {
if (!IsNpadIdValid(npad_id)) { if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return InvalidNpadId; return InvalidNpadId;
@ -1127,8 +1464,8 @@ Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType
return ResultSuccess; return ResultSuccess;
} }
Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
Core::HID::NpadIdType npad_id) { bool is_protection_enabled, Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) { if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return InvalidNpadId; return InvalidNpadId;
@ -1138,11 +1475,11 @@ Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_en
return ResultSuccess; return ResultSuccess;
} }
void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
analog_stick_use_center_clamp = use_center_clamp; analog_stick_use_center_clamp = use_center_clamp;
} }
void NPad::ClearAllConnectedControllers() { void Controller_NPad::ClearAllConnectedControllers() {
for (auto& controller : controller_data) { for (auto& controller : controller_data) {
if (controller.device->IsConnected() && if (controller.device->IsConnected() &&
controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
@ -1152,13 +1489,13 @@ void NPad::ClearAllConnectedControllers() {
} }
} }
void NPad::DisconnectAllConnectedControllers() { void Controller_NPad::DisconnectAllConnectedControllers() {
for (auto& controller : controller_data) { for (auto& controller : controller_data) {
controller.device->Disconnect(); controller.device->Disconnect();
} }
} }
void NPad::ConnectAllDisconnectedControllers() { void Controller_NPad::ConnectAllDisconnectedControllers() {
for (auto& controller : controller_data) { for (auto& controller : controller_data) {
if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
!controller.device->IsConnected()) { !controller.device->IsConnected()) {
@ -1167,18 +1504,18 @@ void NPad::ConnectAllDisconnectedControllers() {
} }
} }
void NPad::ClearAllControllers() { void Controller_NPad::ClearAllControllers() {
for (auto& controller : controller_data) { for (auto& controller : controller_data) {
controller.device->Disconnect(); controller.device->Disconnect();
controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
} }
} }
Core::HID::NpadButton NPad::GetAndResetPressState() { Core::HID::NpadButton Controller_NPad::GetAndResetPressState() {
return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
} }
void NPad::ApplyNpadSystemCommonPolicy() { void Controller_NPad::ApplyNpadSystemCommonPolicy() {
Core::HID::NpadStyleTag styletag{}; Core::HID::NpadStyleTag styletag{};
styletag.fullkey.Assign(1); styletag.fullkey.Assign(1);
styletag.handheld.Assign(1); styletag.handheld.Assign(1);
@ -1203,7 +1540,7 @@ void NPad::ApplyNpadSystemCommonPolicy() {
supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
} }
bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
if (controller == Core::HID::NpadStyleIndex::Handheld) { if (controller == Core::HID::NpadStyleIndex::Handheld) {
const bool support_handheld = const bool support_handheld =
std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
@ -1254,50 +1591,51 @@ bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
return false; return false;
} }
NPad::NpadControllerData& NPad::GetControllerFromHandle( Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
const Core::HID::VibrationDeviceHandle& device_handle) {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
return GetControllerFromNpadIdType(npad_id);
}
const NPad::NpadControllerData& NPad::GetControllerFromHandle(
const Core::HID::VibrationDeviceHandle& device_handle) const {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
return GetControllerFromNpadIdType(npad_id);
}
NPad::NpadControllerData& NPad::GetControllerFromHandle(
const Core::HID::SixAxisSensorHandle& device_handle) { const Core::HID::SixAxisSensorHandle& device_handle) {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
return GetControllerFromNpadIdType(npad_id); return GetControllerFromNpadIdType(npad_id);
} }
const NPad::NpadControllerData& NPad::GetControllerFromHandle( const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
const Core::HID::SixAxisSensorHandle& device_handle) const { const Core::HID::SixAxisSensorHandle& device_handle) const {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
return GetControllerFromNpadIdType(npad_id); return GetControllerFromNpadIdType(npad_id);
} }
NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) { Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
const Core::HID::VibrationDeviceHandle& device_handle) {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
return GetControllerFromNpadIdType(npad_id);
}
const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
const Core::HID::VibrationDeviceHandle& device_handle) const {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
return GetControllerFromNpadIdType(npad_id);
}
Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) { if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
npad_id = Core::HID::NpadIdType::Player1; npad_id = Core::HID::NpadIdType::Player1;
} }
const auto npad_index = NpadIdTypeToIndex(npad_id); const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
return controller_data[npad_index]; return controller_data[npad_index];
} }
const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType( const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
Core::HID::NpadIdType npad_id) const { Core::HID::NpadIdType npad_id) const {
if (!IsNpadIdValid(npad_id)) { if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
npad_id = Core::HID::NpadIdType::Player1; npad_id = Core::HID::NpadIdType::Player1;
} }
const auto npad_index = NpadIdTypeToIndex(npad_id); const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
return controller_data[npad_index]; return controller_data[npad_index];
} }
Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) { const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
auto& controller = GetControllerFromHandle(sixaxis_handle); auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) { switch (sixaxis_handle.npad_type) {
@ -1320,7 +1658,7 @@ Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
} }
} }
const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
const auto& controller = GetControllerFromHandle(sixaxis_handle); const auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) { switch (sixaxis_handle.npad_type) {
@ -1343,13 +1681,65 @@ const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
} }
} }
NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) { Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory; const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.sixaxis_fullkey;
case Core::HID::NpadStyleIndex::Handheld:
return controller.sixaxis_handheld;
case Core::HID::NpadStyleIndex::JoyconDual:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
return controller.sixaxis_dual_left;
}
return controller.sixaxis_dual_right;
case Core::HID::NpadStyleIndex::JoyconLeft:
return controller.sixaxis_left;
case Core::HID::NpadStyleIndex::JoyconRight:
return controller.sixaxis_right;
default:
return controller.sixaxis_unknown;
}
}
return { const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
const auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.sixaxis_fullkey;
case Core::HID::NpadStyleIndex::Handheld:
return controller.sixaxis_handheld;
case Core::HID::NpadStyleIndex::JoyconDual:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
return controller.sixaxis_dual_left;
}
return controller.sixaxis_dual_right;
case Core::HID::NpadStyleIndex::JoyconLeft:
return controller.sixaxis_left;
case Core::HID::NpadStyleIndex::JoyconRight:
return controller.sixaxis_right;
default:
return controller.sixaxis_unknown;
}
}
Controller_NPad::AppletDetailedUiType Controller_NPad::GetAppletDetailedUiType(
Core::HID::NpadIdType npad_id) {
auto controller = GetControllerFromNpadIdType(npad_id);
auto shared_memory = controller.shared_memory;
Service::HID::Controller_NPad::AppletFooterUiType applet_footer_type =
shared_memory->applet_footer_type;
Controller_NPad::AppletDetailedUiType detailed_ui_type{
.ui_variant = 0, .ui_variant = 0,
.footer = shared_memory->applet_footer_type, .footer = applet_footer_type,
}; };
return detailed_ui_type;
} }
} // namespace Service::HID } // namespace Service::HID

View File

@ -10,6 +10,7 @@
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h" #include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/controllers/controller_base.h"
@ -33,11 +34,11 @@ union Result;
namespace Service::HID { namespace Service::HID {
class NPad final : public ControllerBase { class Controller_NPad final : public ControllerBase {
public: public:
explicit NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_); KernelHelpers::ServiceContext& service_context_);
~NPad() override; ~Controller_NPad() override;
// Called when the controller is initialized // Called when the controller is initialized
void OnInit() override; void OnInit() override;
@ -48,6 +49,9 @@ public:
// When the controller is requesting an update for the shared memory // When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
// When the controller is requesting a motion update for the shared memory
void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override;
// This is nn::hid::NpadJoyHoldType // This is nn::hid::NpadJoyHoldType
enum class NpadJoyHoldType : u64 { enum class NpadJoyHoldType : u64 {
Vertical = 0, Vertical = 0,
@ -129,8 +133,6 @@ public:
Revision3 = 3, Revision3 = 3,
}; };
using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>;
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
Core::HID::NpadStyleTag GetSupportedStyleSet() const; Core::HID::NpadStyleTag GetSupportedStyleSet() const;
@ -183,18 +185,37 @@ public:
Result DisconnectNpad(Core::HID::NpadIdType npad_id); Result DisconnectNpad(Core::HID::NpadIdType npad_id);
Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::GyroscopeZeroDriftMode drift_mode);
Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_at_rest) const;
Result IsFirmwareUpdateAvailableForSixAxisSensor( Result IsFirmwareUpdateAvailableForSixAxisSensor(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
Result EnableSixAxisSensorUnalteredPassthrough(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
Result IsSixAxisSensorUnalteredPassthroughEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
Result LoadSixAxisSensorCalibrationParameter(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
Result GetSixAxisSensorIcInformation(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorIcInformation& ic_information) const;
Result ResetIsSixAxisSensorDeviceNewlyAssigned( Result ResetIsSixAxisSensorDeviceNewlyAssigned(
const Core::HID::SixAxisSensorHandle& sixaxis_handle); const Core::HID::SixAxisSensorHandle& sixaxis_handle);
Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id); bool sixaxis_status);
SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id); Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id); bool& is_fusion_enabled) const;
SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id); Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id); bool is_fusion_enabled);
SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id); Result SetSixAxisFusionParameters(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters& parameters) const;
Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
bool& is_enabled) const; bool& is_enabled) const;
@ -218,6 +239,10 @@ public:
void ApplyNpadSystemCommonPolicy(); void ApplyNpadSystemCommonPolicy();
static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
static Result VerifyValidSixAxisSensorHandle(
const Core::HID::SixAxisSensorHandle& device_handle);
AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
private: private:
@ -277,6 +302,29 @@ private:
}; };
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
// This is nn::hid::SixAxisSensorAttribute
struct SixAxisSensorAttribute {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_interpolated;
};
};
static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
// This is nn::hid::SixAxisSensorState
struct SixAxisSensorState {
s64 delta_time{};
s64 sampling_number{};
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
SixAxisSensorAttribute attribute{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
// This is nn::hid::server::NpadGcTriggerState // This is nn::hid::server::NpadGcTriggerState
struct NpadGcTriggerState { struct NpadGcTriggerState {
s64 sampling_number{}; s64 sampling_number{};
@ -396,12 +444,12 @@ private:
Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
DeviceType device_type{}; DeviceType device_type{};
INSERT_PADDING_BYTES(0x4); // Reserved INSERT_PADDING_BYTES(0x4); // Reserved
NPadSystemProperties system_properties{}; NPadSystemProperties system_properties{};
@ -435,6 +483,16 @@ private:
std::chrono::steady_clock::time_point last_vibration_timepoint{}; std::chrono::steady_clock::time_point last_vibration_timepoint{};
}; };
struct SixaxisParameters {
bool is_fusion_enabled{true};
bool unaltered_passtrough{false};
Core::HID::SixAxisSensorFusionParameters fusion{};
Core::HID::SixAxisSensorCalibrationParameter calibration{};
Core::HID::SixAxisSensorIcInformation ic_information{};
Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
Core::HID::GyroscopeZeroDriftMode::Standard};
};
struct NpadControllerData { struct NpadControllerData {
Kernel::KEvent* styleset_changed_event{}; Kernel::KEvent* styleset_changed_event{};
NpadInternalState* shared_memory = nullptr; NpadInternalState* shared_memory = nullptr;
@ -448,10 +506,27 @@ private:
bool is_dual_left_connected{true}; bool is_dual_left_connected{true};
bool is_dual_right_connected{true}; bool is_dual_right_connected{true};
// Motion parameters
bool sixaxis_at_rest{true};
bool sixaxis_sensor_enabled{true};
SixaxisParameters sixaxis_fullkey{};
SixaxisParameters sixaxis_handheld{};
SixaxisParameters sixaxis_dual_left{};
SixaxisParameters sixaxis_dual_right{};
SixaxisParameters sixaxis_left{};
SixaxisParameters sixaxis_right{};
SixaxisParameters sixaxis_unknown{};
// Current pad state // Current pad state
NPadGenericState npad_pad_state{}; NPadGenericState npad_pad_state{};
NPadGenericState npad_libnx_state{}; NPadGenericState npad_libnx_state{};
NpadGcTriggerState npad_trigger_state{}; NpadGcTriggerState npad_trigger_state{};
SixAxisSensorState sixaxis_fullkey_state{};
SixAxisSensorState sixaxis_handheld_state{};
SixAxisSensorState sixaxis_dual_left_state{};
SixAxisSensorState sixaxis_dual_right_state{};
SixAxisSensorState sixaxis_left_lifo_state{};
SixAxisSensorState sixaxis_right_lifo_state{};
int callback_key{}; int callback_key{};
}; };
@ -461,14 +536,14 @@ private:
void RequestPadStateUpdate(Core::HID::NpadIdType npad_id); void RequestPadStateUpdate(Core::HID::NpadIdType npad_id);
void WriteEmptyEntry(NpadInternalState* npad); void WriteEmptyEntry(NpadInternalState* npad);
NpadControllerData& GetControllerFromHandle(
const Core::HID::VibrationDeviceHandle& device_handle);
const NpadControllerData& GetControllerFromHandle(
const Core::HID::VibrationDeviceHandle& device_handle) const;
NpadControllerData& GetControllerFromHandle( NpadControllerData& GetControllerFromHandle(
const Core::HID::SixAxisSensorHandle& device_handle); const Core::HID::SixAxisSensorHandle& device_handle);
const NpadControllerData& GetControllerFromHandle( const NpadControllerData& GetControllerFromHandle(
const Core::HID::SixAxisSensorHandle& device_handle) const; const Core::HID::SixAxisSensorHandle& device_handle) const;
NpadControllerData& GetControllerFromHandle(
const Core::HID::VibrationDeviceHandle& device_handle);
const NpadControllerData& GetControllerFromHandle(
const Core::HID::VibrationDeviceHandle& device_handle) const;
NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
@ -476,6 +551,9 @@ private:
const Core::HID::SixAxisSensorHandle& device_handle); const Core::HID::SixAxisSensorHandle& device_handle);
const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
const Core::HID::SixAxisSensorHandle& device_handle) const; const Core::HID::SixAxisSensorHandle& device_handle) const;
SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
const SixaxisParameters& GetSixaxisState(
const Core::HID::SixAxisSensorHandle& device_handle) const;
std::atomic<u64> press_state{}; std::atomic<u64> press_state{};

View File

@ -12,35 +12,35 @@
namespace Service::HID { namespace Service::HID {
Palma::Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_) KernelHelpers::ServiceContext& service_context_)
: ControllerBase{hid_core_}, service_context{service_context_} { : ControllerBase{hid_core_}, service_context{service_context_} {
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
} }
Palma::~Palma() { Controller_Palma::~Controller_Palma() {
service_context.CloseEvent(operation_complete_event); service_context.CloseEvent(operation_complete_event);
}; };
void Palma::OnInit() {} void Controller_Palma::OnInit() {}
void Palma::OnRelease() {} void Controller_Palma::OnRelease() {}
void Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) { if (!IsControllerActivated()) {
return; return;
} }
} }
Result Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
PalmaConnectionHandle& handle) { PalmaConnectionHandle& handle) {
active_handle.npad_id = npad_id; active_handle.npad_id = npad_id;
handle = active_handle; handle = active_handle;
return ResultSuccess; return ResultSuccess;
} }
Result Palma::InitializePalma(const PalmaConnectionHandle& handle) { Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -48,7 +48,7 @@ Result Palma::InitializePalma(const PalmaConnectionHandle& handle) {
return ResultSuccess; return ResultSuccess;
} }
Kernel::KReadableEvent& Palma::AcquirePalmaOperationCompleteEvent( Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent(
const PalmaConnectionHandle& handle) const { const PalmaConnectionHandle& handle) const {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id);
@ -56,9 +56,9 @@ Kernel::KReadableEvent& Palma::AcquirePalmaOperationCompleteEvent(
return operation_complete_event->GetReadableEvent(); return operation_complete_event->GetReadableEvent();
} }
Result Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
PalmaOperationType& operation_type, PalmaOperationType& operation_type,
PalmaOperationData& data) const { PalmaOperationData& data) const {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -67,7 +67,8 @@ Result Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
return ResultSuccess; return ResultSuccess;
} }
Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity) { Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
u64 palma_activity) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -78,7 +79,8 @@ Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_a
return ResultSuccess; return ResultSuccess;
} }
Result Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_) { Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle,
PalmaFrModeType fr_mode_) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -86,7 +88,7 @@ Result Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrMod
return ResultSuccess; return ResultSuccess;
} }
Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -97,25 +99,25 @@ Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
return ResultSuccess; return ResultSuccess;
} }
Result Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
return ResultSuccess; return ResultSuccess;
} }
Result Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
return ResultSuccess; return ResultSuccess;
} }
void Palma::ReadPalmaApplicationSection() {} void Controller_Palma::ReadPalmaApplicationSection() {}
void Palma::WritePalmaApplicationSection() {} void Controller_Palma::WritePalmaApplicationSection() {}
Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -126,7 +128,7 @@ Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
return ResultSuccess; return ResultSuccess;
} }
Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -137,9 +139,10 @@ Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
return ResultSuccess; return ResultSuccess;
} }
void Palma::WritePalmaActivityEntry() {} void Controller_Palma::WritePalmaActivityEntry() {}
Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown) { Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle,
u64 unknown) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -150,8 +153,8 @@ Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle,
return ResultSuccess; return ResultSuccess;
} }
Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
Common::ProcessAddress t_mem, u64 size) { Common::ProcessAddress t_mem, u64 size) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -162,8 +165,8 @@ Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWave
return ResultSuccess; return ResultSuccess;
} }
Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
s32 database_id_version_) { s32 database_id_version_) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -175,7 +178,8 @@ Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle&
return ResultSuccess; return ResultSuccess;
} }
Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle) { Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -187,26 +191,26 @@ Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle&
return ResultSuccess; return ResultSuccess;
} }
void Palma::SuspendPalmaFeature() {} void Controller_Palma::SuspendPalmaFeature() {}
Result Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
return operation.result; return operation.result;
} }
void Palma::ReadPalmaPlayLog() {} void Controller_Palma::ReadPalmaPlayLog() {}
void Palma::ResetPalmaPlayLog() {} void Controller_Palma::ResetPalmaPlayLog() {}
void Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
// If true controllers are able to be paired // If true controllers are able to be paired
is_connectable = is_all_connectable; is_connectable = is_all_connectable;
} }
void Palma::SetIsPalmaPairedConnectable() {} void Controller_Palma::SetIsPalmaPairedConnectable() {}
Result Palma::PairPalma(const PalmaConnectionHandle& handle) { Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) { if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle; return InvalidPalmaHandle;
} }
@ -214,14 +218,14 @@ Result Palma::PairPalma(const PalmaConnectionHandle& handle) {
return ResultSuccess; return ResultSuccess;
} }
void Palma::SetPalmaBoostMode(bool boost_mode) {} void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {}
void Palma::CancelWritePalmaWaveEntry() {} void Controller_Palma::CancelWritePalmaWaveEntry() {}
void Palma::EnablePalmaBoostMode() {} void Controller_Palma::EnablePalmaBoostMode() {}
void Palma::GetPalmaBluetoothAddress() {} void Controller_Palma::GetPalmaBluetoothAddress() {}
void Palma::SetDisallowedPalmaConnection() {} void Controller_Palma::SetDisallowedPalmaConnection() {}
} // namespace Service::HID } // namespace Service::HID

View File

@ -23,7 +23,7 @@ class EmulatedController;
} // namespace Core::HID } // namespace Core::HID
namespace Service::HID { namespace Service::HID {
class Palma final : public ControllerBase { class Controller_Palma final : public ControllerBase {
public: public:
using PalmaOperationData = std::array<u8, 0x140>; using PalmaOperationData = std::array<u8, 0x140>;
@ -97,9 +97,9 @@ public:
static_assert(sizeof(PalmaConnectionHandle) == 0x8, static_assert(sizeof(PalmaConnectionHandle) == 0x8,
"PalmaConnectionHandle has incorrect size."); "PalmaConnectionHandle has incorrect size.");
explicit Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_); KernelHelpers::ServiceContext& service_context_);
~Palma() override; ~Controller_Palma() override;
// Called when the controller is initialized // Called when the controller is initialized
void OnInit() override; void OnInit() override;

View File

@ -1,413 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/controllers/six_axis.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid_util.h"
namespace Service::HID {
SixAxis::SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_)
: ControllerBase{hid_core_}, npad{npad_} {
for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i];
controller.device = hid_core.GetEmulatedControllerByIndex(i);
}
}
SixAxis::~SixAxis() = default;
void SixAxis::OnInit() {}
void SixAxis::OnRelease() {}
void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i];
const auto npad_id = IndexToNpadIdType(i);
const auto& controller_type = controller.device->GetNpadStyleIndex();
if (controller_type == Core::HID::NpadStyleIndex::None ||
!controller.device->IsConnected()) {
continue;
}
const auto& motion_state = controller.device->GetMotions();
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
auto& sixaxis_fullkey_lifo = npad->GetSixAxisFullkeyLifo(npad_id);
auto& sixaxis_handheld_lifo = npad->GetSixAxisHandheldLifo(npad_id);
auto& sixaxis_dual_left_lifo = npad->GetSixAxisDualLeftLifo(npad_id);
auto& sixaxis_dual_right_lifo = npad->GetSixAxisDualRightLifo(npad_id);
auto& sixaxis_left_lifo = npad->GetSixAxisLeftLifo(npad_id);
auto& sixaxis_right_lifo = npad->GetSixAxisRightLifo(npad_id);
// Clear previous state
sixaxis_fullkey_state = {};
sixaxis_handheld_state = {};
sixaxis_dual_left_state = {};
sixaxis_dual_right_state = {};
sixaxis_left_lifo_state = {};
sixaxis_right_lifo_state = {};
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
controller.sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_state.size(); ++e) {
controller.sixaxis_at_rest =
controller.sixaxis_at_rest && motion_state[e].is_at_rest;
}
}
const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state,
const Core::HID::ControllerMotion& hid_state) {
using namespace std::literals::chrono_literals;
static constexpr Core::HID::SixAxisSensorState default_motion_state = {
.delta_time = std::chrono::nanoseconds(5ms).count(),
.accel = {0, 0, -1.0f},
.orientation =
{
Common::Vec3f{1.0f, 0, 0},
Common::Vec3f{0, 1.0f, 0},
Common::Vec3f{0, 0, 1.0f},
},
.attribute = {1},
};
if (!controller.sixaxis_sensor_enabled) {
state = default_motion_state;
return;
}
if (!Settings::values.motion_enabled.GetValue()) {
state = default_motion_state;
return;
}
state.attribute.is_connected.Assign(1);
state.delta_time = std::chrono::nanoseconds(5ms).count();
state.accel = hid_state.accel;
state.gyro = hid_state.gyro;
state.rotation = hid_state.rotation;
state.orientation = hid_state.orientation;
};
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
set_motion_state(sixaxis_handheld_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
set_motion_state(sixaxis_dual_left_state, motion_state[0]);
set_motion_state(sixaxis_dual_right_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::Pokeball:
using namespace std::literals::chrono_literals;
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
break;
default:
break;
}
sixaxis_fullkey_state.sampling_number =
sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
// This buffer only is updated on handheld on HW
sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
}
sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
}
}
Result SixAxis::SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::GyroscopeZeroDriftMode drift_mode) {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
auto& controller = GetControllerFromHandle(sixaxis_handle);
sixaxis.gyroscope_zero_drift_mode = drift_mode;
controller.device->SetGyroscopeZeroDriftMode(drift_mode);
return ResultSuccess;
}
Result SixAxis::GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
drift_mode = sixaxis.gyroscope_zero_drift_mode;
return ResultSuccess;
}
Result SixAxis::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_at_rest) const {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& controller = GetControllerFromHandle(sixaxis_handle);
is_at_rest = controller.sixaxis_at_rest;
return ResultSuccess;
}
Result SixAxis::LoadSixAxisSensorCalibrationParameter(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
// TODO: Request this data to the controller. On error return 0xd8ca
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
calibration = sixaxis.calibration;
return ResultSuccess;
}
Result SixAxis::GetSixAxisSensorIcInformation(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorIcInformation& ic_information) const {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
// TODO: Request this data to the controller. On error return 0xd8ca
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
ic_information = sixaxis.ic_information;
return ResultSuccess;
}
Result SixAxis::EnableSixAxisSensorUnalteredPassthrough(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
sixaxis.unaltered_passtrough = is_enabled;
return ResultSuccess;
}
Result SixAxis::IsSixAxisSensorUnalteredPassthroughEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
is_enabled = sixaxis.unaltered_passtrough;
return ResultSuccess;
}
Result SixAxis::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool sixaxis_status) {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& controller = GetControllerFromHandle(sixaxis_handle);
controller.sixaxis_sensor_enabled = sixaxis_status;
return ResultSuccess;
}
Result SixAxis::IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_fusion_enabled) const {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
is_fusion_enabled = sixaxis.is_fusion_enabled;
return ResultSuccess;
}
Result SixAxis::SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool is_fusion_enabled) {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
sixaxis.is_fusion_enabled = is_fusion_enabled;
return ResultSuccess;
}
Result SixAxis::SetSixAxisFusionParameters(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto param1 = sixaxis_fusion_parameters.parameter1;
if (param1 < 0.0f || param1 > 1.0f) {
return InvalidSixAxisFusionRange;
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
sixaxis.fusion = sixaxis_fusion_parameters;
return ResultSuccess;
}
Result SixAxis::GetSixAxisFusionParameters(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters& parameters) const {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
parameters = sixaxis.fusion;
return ResultSuccess;
}
SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.sixaxis_fullkey;
case Core::HID::NpadStyleIndex::Handheld:
return controller.sixaxis_handheld;
case Core::HID::NpadStyleIndex::JoyconDual:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
return controller.sixaxis_dual_left;
}
return controller.sixaxis_dual_right;
case Core::HID::NpadStyleIndex::JoyconLeft:
return controller.sixaxis_left;
case Core::HID::NpadStyleIndex::JoyconRight:
return controller.sixaxis_right;
default:
return controller.sixaxis_unknown;
}
}
const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
const auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.sixaxis_fullkey;
case Core::HID::NpadStyleIndex::Handheld:
return controller.sixaxis_handheld;
case Core::HID::NpadStyleIndex::JoyconDual:
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
return controller.sixaxis_dual_left;
}
return controller.sixaxis_dual_right;
case Core::HID::NpadStyleIndex::JoyconLeft:
return controller.sixaxis_left;
case Core::HID::NpadStyleIndex::JoyconRight:
return controller.sixaxis_right;
default:
return controller.sixaxis_unknown;
}
}
SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
const Core::HID::SixAxisSensorHandle& device_handle) {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
return GetControllerFromNpadIdType(npad_id);
}
const SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
const Core::HID::SixAxisSensorHandle& device_handle) const {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
return GetControllerFromNpadIdType(npad_id);
}
SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
npad_id = Core::HID::NpadIdType::Player1;
}
const auto npad_index = NpadIdTypeToIndex(npad_id);
return controller_data[npad_index];
}
const SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(
Core::HID::NpadIdType npad_id) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
npad_id = Core::HID::NpadIdType::Player1;
}
const auto npad_index = NpadIdTypeToIndex(npad_id);
return controller_data[npad_index];
}
} // namespace Service::HID

View File

@ -1,111 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::HID {
class NPad;
class SixAxis final : public ControllerBase {
public:
explicit SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_);
~SixAxis() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::GyroscopeZeroDriftMode drift_mode);
Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_at_rest) const;
Result EnableSixAxisSensorUnalteredPassthrough(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
Result IsSixAxisSensorUnalteredPassthroughEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
Result LoadSixAxisSensorCalibrationParameter(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
Result GetSixAxisSensorIcInformation(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorIcInformation& ic_information) const;
Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool sixaxis_status);
Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_fusion_enabled) const;
Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool is_fusion_enabled);
Result SetSixAxisFusionParameters(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters& parameters) const;
private:
static constexpr std::size_t NPAD_COUNT = 10;
struct SixaxisParameters {
bool is_fusion_enabled{true};
bool unaltered_passtrough{false};
Core::HID::SixAxisSensorFusionParameters fusion{};
Core::HID::SixAxisSensorCalibrationParameter calibration{};
Core::HID::SixAxisSensorIcInformation ic_information{};
Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
Core::HID::GyroscopeZeroDriftMode::Standard};
};
struct NpadControllerData {
Core::HID::EmulatedController* device = nullptr;
// Motion parameters
bool sixaxis_at_rest{true};
bool sixaxis_sensor_enabled{true};
SixaxisParameters sixaxis_fullkey{};
SixaxisParameters sixaxis_handheld{};
SixaxisParameters sixaxis_dual_left{};
SixaxisParameters sixaxis_dual_right{};
SixaxisParameters sixaxis_left{};
SixaxisParameters sixaxis_right{};
SixaxisParameters sixaxis_unknown{};
// Current pad state
Core::HID::SixAxisSensorState sixaxis_fullkey_state{};
Core::HID::SixAxisSensorState sixaxis_handheld_state{};
Core::HID::SixAxisSensorState sixaxis_dual_left_state{};
Core::HID::SixAxisSensorState sixaxis_dual_right_state{};
Core::HID::SixAxisSensorState sixaxis_left_lifo_state{};
Core::HID::SixAxisSensorState sixaxis_right_lifo_state{};
int callback_key{};
};
SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
const SixaxisParameters& GetSixaxisState(
const Core::HID::SixAxisSensorHandle& device_handle) const;
NpadControllerData& GetControllerFromHandle(
const Core::HID::SixAxisSensorHandle& device_handle);
const NpadControllerData& GetControllerFromHandle(
const Core::HID::SixAxisSensorHandle& device_handle) const;
NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
std::shared_ptr<NPad> npad;
std::array<NpadControllerData, NPAD_COUNT> controller_data{};
};
} // namespace Service::HID

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