Merge branch 'master' of github.com:citra-emu/citra into ips-patches
This commit is contained in:
commit
1ded48f5a3
4
.gitignore
vendored
4
.gitignore
vendored
@ -33,3 +33,7 @@ Thumbs.db
|
||||
|
||||
# Python files
|
||||
*.pyc
|
||||
|
||||
# Flatpak generated files
|
||||
.flatpak-builder/
|
||||
repo/
|
||||
|
12
.travis.yml
12
.travis.yml
@ -61,6 +61,18 @@ matrix:
|
||||
script: "./.travis/linux-mingw/build.sh"
|
||||
after_success: "./.travis/linux-mingw/upload.sh"
|
||||
cache: ccache
|
||||
- if: repo =~ ^.*\/(citra-canary|citra-nightly)$ AND tag IS present
|
||||
git:
|
||||
depth: false
|
||||
os: linux
|
||||
env: NAME="flatpak build"
|
||||
sudo: required
|
||||
dist: trusty
|
||||
services: docker
|
||||
cache: ccache
|
||||
install: "./.travis/linux-flatpak/deps.sh"
|
||||
script: "./.travis/linux-flatpak/build.sh"
|
||||
after_script: "./.travis/linux-flatpak/finish.sh"
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
|
4
.travis/linux-flatpak/build.sh
Executable file
4
.travis/linux-flatpak/build.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash -ex
|
||||
mkdir -p "$HOME/.ccache"
|
||||
# Configure docker and call the script that generates application data and build scripts
|
||||
docker run --env-file .travis/common/travis-ci.env --env-file .travis/linux-flatpak/travis-ci-flatpak.env -v $(pwd):/citra -v "$HOME/.ccache":/root/.ccache -v "$HOME/.ssh":/root/.ssh --privileged citraemu/build-environments:linux-flatpak /bin/bash -ex /citra/.travis/linux-flatpak/generate-data.sh
|
4
.travis/linux-flatpak/deps.sh
Executable file
4
.travis/linux-flatpak/deps.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
# Download the docker image that contains flatpak build dependencies
|
||||
docker pull citraemu/build-environments:linux-flatpak
|
35
.travis/linux-flatpak/docker.sh
Executable file
35
.travis/linux-flatpak/docker.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Converts "citra-emu/citra-nightly" to "citra-nightly"
|
||||
REPO_NAME=$(echo $TRAVIS_REPO_SLUG | cut -d'/' -f 2)
|
||||
CITRA_SRC_DIR="/citra"
|
||||
BUILD_DIR="$CITRA_SRC_DIR/build"
|
||||
REPO_DIR="$CITRA_SRC_DIR/repo"
|
||||
STATE_DIR="$CITRA_SRC_DIR/.flatpak-builder"
|
||||
KEYS_ARCHIVE="/tmp/keys.tar"
|
||||
SSH_DIR="/upload"
|
||||
SSH_KEY="/tmp/ssh.key"
|
||||
GPG_KEY="/tmp/gpg.key"
|
||||
|
||||
# Extract keys
|
||||
openssl aes-256-cbc -K $FLATPAK_ENC_K -iv $FLATPAK_ENC_IV -in "$CITRA_SRC_DIR/keys.tar.enc" -out "$KEYS_ARCHIVE" -d
|
||||
tar -C /tmp -xvf $KEYS_ARCHIVE
|
||||
|
||||
# Configure SSH keys
|
||||
eval "$(ssh-agent -s)"
|
||||
chmod -R 600 "$HOME/.ssh"
|
||||
chown -R root "$HOME/.ssh"
|
||||
chmod 600 "$SSH_KEY"
|
||||
ssh-add "$SSH_KEY"
|
||||
echo "[$FLATPAK_SSH_HOSTNAME]:$FLATPAK_SSH_PORT,[$(dig +short $FLATPAK_SSH_HOSTNAME)]:$FLATPAK_SSH_PORT $FLATPAK_SSH_PUBLIC_KEY" > ~/.ssh/known_hosts
|
||||
|
||||
# Configure GPG keys
|
||||
gpg2 --import "$GPG_KEY"
|
||||
|
||||
# Mount our flatpak repository
|
||||
mkdir -p "$REPO_DIR"
|
||||
sshfs "$FLATPAK_SSH_USER@$FLATPAK_SSH_HOSTNAME:$SSH_DIR" "$REPO_DIR" -C -p "$FLATPAK_SSH_PORT" -o IdentityFile="$SSH_KEY"
|
||||
|
||||
# Build the citra flatpak
|
||||
flatpak-builder -v --jobs=4 --ccache --force-clean --state-dir="$STATE_DIR" --gpg-sign="$FLATPAK_GPG_PUBLIC_KEY" --repo="$REPO_DIR" "$BUILD_DIR" "/tmp/org.citra.$REPO_NAME.json"
|
||||
flatpak build-update-repo "$REPO_DIR" -v --generate-static-deltas --gpg-sign="$FLATPAK_GPG_PUBLIC_KEY"
|
9
.travis/linux-flatpak/finish.sh
Executable file
9
.travis/linux-flatpak/finish.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
CITRA_SRC_DIR="/citra"
|
||||
REPO_DIR="$CITRA_SRC_DIR/repo"
|
||||
|
||||
# When the script finishes, unmount the repository and delete sensitive files,
|
||||
# regardless of whether the build passes or fails
|
||||
umount "$REPO_DIR"
|
||||
rm -rf "$REPO_DIR" "/tmp/*"
|
142
.travis/linux-flatpak/generate-data.sh
Normal file
142
.travis/linux-flatpak/generate-data.sh
Normal file
@ -0,0 +1,142 @@
|
||||
#!/bin/bash -ex
|
||||
# This script generates the appdata.xml and org.citra.$REPO_NAME.json files
|
||||
# needed to define application metadata and build citra depending on what version
|
||||
# of citra we're building (nightly or canary)
|
||||
|
||||
# Converts "citra-emu/citra-nightly" to "citra-nightly"
|
||||
REPO_NAME=$(echo $TRAVIS_REPO_SLUG | cut -d'/' -f 2)
|
||||
# Converts "citra-nightly" to "Citra Nightly"
|
||||
REPO_NAME_FRIENDLY=$(echo $REPO_NAME | sed -e 's/-/ /g' -e 's/\b\(.\)/\u\1/g')
|
||||
|
||||
# Generate the correct appdata.xml for the version of Citra we're building
|
||||
cat > /tmp/appdata.xml <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<application>
|
||||
<id type="desktop">org.citra.$REPO_NAME.desktop</id>
|
||||
<name>$REPO_NAME_FRIENDLY</name>
|
||||
<summary>Nintendo 3DS emulator</summary>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-2.0</project_license>
|
||||
<description>
|
||||
<p>Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS.</p>
|
||||
<p>Citra emulates a subset of 3DS hardware and therefore is useful for running/debugging homebrew applications, and it is also able to run many commercial games! Some of these do not run at a playable state, but we are working every day to advance the project forward. (Playable here means compatibility of at least "Okay" on our game compatibility list.)</p>
|
||||
</description>
|
||||
<url type="homepage">https://citra-emu.org/</url>
|
||||
<url type="donation">https://citra-emu.org/donate/</url>
|
||||
<url type="bugtracker">https://github.com/citra-emu/citra/issues</url>
|
||||
<url type="faq">https://citra-emu.org/wiki/faq/</url>
|
||||
<url type="help">https://citra-emu.org/wiki/home/</url>
|
||||
<screenshot>https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/01-Super%20Mario%203D%20Land.jpg</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/02-Mario%20Kart%207.jpg</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/28-The%20Legend%20of%20Zelda%20Ocarina%20of%20Time%203D.jpg</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/35-Pok%C3%A9mon%20ORAS.png</screenshot>
|
||||
<categories>
|
||||
<category>Games</category>
|
||||
<category>Emulator</category>
|
||||
</categories>
|
||||
</application>
|
||||
EOF
|
||||
|
||||
# Generate the citra flatpak manifest, appending certain variables depending on
|
||||
# whether we're building nightly or canary.
|
||||
cat > /tmp/org.citra.$REPO_NAME.json <<EOF
|
||||
{
|
||||
"app-id": "org.citra.$REPO_NAME",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "5.11",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"command": "citra-qt",
|
||||
"rename-desktop-file": "citra.desktop",
|
||||
"rename-icon": "citra",
|
||||
"rename-appdata-file": "org.citra.$REPO_NAME.appdata.xml",
|
||||
"sdk-extensions": [
|
||||
"org.freedesktop.Sdk.Extension.gcc7"
|
||||
],
|
||||
"build-options": {
|
||||
"build-args": [
|
||||
"--share=network"
|
||||
],
|
||||
"env": {
|
||||
"CC": "/usr/lib/sdk/gcc7/bin/gcc",
|
||||
"CXX": "/usr/lib/sdk/gcc7/bin/g++",
|
||||
"CI": "$CI",
|
||||
"TRAVIS": "$TRAVIS",
|
||||
"CONTINUOUS_INTEGRATION": "$CONTINUOUS_INTEGRATION",
|
||||
"TRAVIS_BRANCH": "$TRAVIS_BRANCH",
|
||||
"TRAVIS_BUILD_ID": "$TRAVIS_BUILD_ID",
|
||||
"TRAVIS_BUILD_NUMBER": "$TRAVIS_BUILD_NUMBER",
|
||||
"TRAVIS_COMMIT": "$TRAVIS_COMMIT",
|
||||
"TRAVIS_JOB_ID": "$TRAVIS_JOB_ID",
|
||||
"TRAVIS_JOB_NUMBER": "$TRAVIS_JOB_NUMBER",
|
||||
"TRAVIS_REPO_SLUG": "$TRAVIS_REPO_SLUG",
|
||||
"TRAVIS_TAG": "$TRAVIS_TAG"
|
||||
}
|
||||
},
|
||||
"finish-args": [
|
||||
"--device=all",
|
||||
"--socket=x11",
|
||||
"--socket=pulseaudio",
|
||||
"--share=network",
|
||||
"--share=ipc",
|
||||
"--filesystem=xdg-config/citra-emu:create",
|
||||
"--filesystem=xdg-data/citra-emu:create",
|
||||
"--filesystem=host:ro"
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"name": "cmake",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"cleanup": ["*"],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://cmake.org/files/v3.12/cmake-3.12.3.tar.gz",
|
||||
"sha256": "acbf13af31a741794106b76e5d22448b004a66485fc99f6d7df4d22e99da164a"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "citra",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"builddir": true,
|
||||
"config-opts": [
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DENABLE_QT_TRANSLATION=ON",
|
||||
"-DCITRA_ENABLE_COMPATIBILITY_REPORTING=ON",
|
||||
"-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON"
|
||||
],
|
||||
"cleanup": [
|
||||
"/bin/citra",
|
||||
"/share/man",
|
||||
"/share/pixmaps"
|
||||
],
|
||||
"post-install": [
|
||||
"install -Dm644 ../appdata.xml /app/share/appdata/org.citra.$REPO_NAME.appdata.xml",
|
||||
"desktop-file-install --dir=/app/share/applications ../dist/citra.desktop",
|
||||
"sed -i 's/Name=Citra/Name=$REPO_NAME_FRIENDLY/g' /app/share/applications/citra.desktop",
|
||||
"echo 'StartupWMClass=citra-qt' >> /app/share/applications/citra.desktop",
|
||||
"install -Dm644 ../dist/citra.svg /app/share/icons/hicolor/scalable/apps/citra.svg",
|
||||
"install -Dm644 ../dist/icon.png /app/share/icons/hicolor/512x512/apps/citra.png",
|
||||
"mv /app/share/mime/packages/citra.xml /app/share/mime/packages/org.citra.$REPO_NAME.xml",
|
||||
"sed 's/citra/org.citra.citra-nightly/g' -i /app/share/mime/packages/org.citra.$REPO_NAME.xml",
|
||||
"install -m644 --target-directory=/app/lib /usr/lib/sdk/gcc7/lib/libstdc++.so*"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/citra-emu/$REPO_NAME.git",
|
||||
"branch": "$TRAVIS_BRANCH",
|
||||
"disable-shallow-clone": true
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"path": "/tmp/appdata.xml"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
# Call the script to build citra
|
||||
/bin/bash -ex /citra/.travis/linux-flatpak/docker.sh
|
9
.travis/linux-flatpak/travis-ci-flatpak.env
Normal file
9
.travis/linux-flatpak/travis-ci-flatpak.env
Normal file
@ -0,0 +1,9 @@
|
||||
# Flatpak specific environment variables
|
||||
FLATPAK_ENC_IV
|
||||
FLATPAK_ENC_K
|
||||
FLATPAK_GPG_PUBLIC_KEY
|
||||
FLATPAK_SSH_HOSTNAME
|
||||
FLATPAK_SSH_LOCATION
|
||||
FLATPAK_SSH_PORT
|
||||
FLATPAK_SSH_PUBLIC_KEY
|
||||
FLATPAK_SSH_USER
|
@ -24,21 +24,21 @@ option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
option(ENABLE_SCRIPTING "Enables scripting support" OFF)
|
||||
|
||||
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
message(STATUS "Copying pre-commit hook")
|
||||
file(COPY hooks/pre-commit
|
||||
DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks)
|
||||
DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
|
||||
endif()
|
||||
|
||||
# Sanity check : Check that all submodules are present
|
||||
# =======================================================================
|
||||
|
||||
function(check_submodules_present)
|
||||
file(READ "${CMAKE_SOURCE_DIR}/.gitmodules" gitmodules)
|
||||
file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules)
|
||||
string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
|
||||
foreach(module ${gitmodules})
|
||||
string(REGEX REPLACE "path *= *" "" module ${module})
|
||||
if (NOT EXISTS "${CMAKE_SOURCE_DIR}/${module}/.git")
|
||||
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
|
||||
message(SEND_ERROR "Git submodule ${module} not found."
|
||||
"Please run: git submodule update --init --recursive")
|
||||
endif()
|
||||
@ -46,17 +46,17 @@ function(check_submodules_present)
|
||||
endfunction()
|
||||
check_submodules_present()
|
||||
|
||||
configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
COPYONLY)
|
||||
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
message(STATUS "Downloading compatibility list for citra...")
|
||||
file(DOWNLOAD
|
||||
https://api.citra-emu.org/gamedb/
|
||||
"${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
|
||||
"${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
|
||||
endif()
|
||||
if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
|
||||
endif()
|
||||
|
||||
# Detect current compilation architecture and create standard definitions
|
||||
@ -107,7 +107,7 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# set up output paths for executable binaries
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
|
||||
if (NOT MSVC)
|
||||
@ -170,7 +170,7 @@ endif()
|
||||
# On modern Unixes, this is typically already the case. The lone exception is
|
||||
# glibc, which may default to 32 bits. glibc allows this to be configured
|
||||
# by setting _FILE_OFFSET_BITS.
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
|
||||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||
endif()
|
||||
|
||||
@ -179,9 +179,6 @@ add_definitions(-DSINGLETHREADED)
|
||||
set_property(DIRECTORY APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
|
||||
|
||||
math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
|
||||
add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
|
||||
|
||||
# System imported libraries
|
||||
# ======================
|
||||
|
||||
@ -189,7 +186,7 @@ find_package(Boost 1.66.0 QUIET)
|
||||
if (NOT Boost_FOUND)
|
||||
message(STATUS "Boost 1.66.0 or newer not found, falling back to externals")
|
||||
|
||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost")
|
||||
set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost")
|
||||
set(Boost_NO_SYSTEM_PATHS OFF)
|
||||
find_package(Boost QUIET REQUIRED)
|
||||
endif()
|
||||
@ -290,12 +287,12 @@ set(CLANG_FORMAT_POSTFIX "-6.0")
|
||||
find_program(CLANG_FORMAT
|
||||
NAMES clang-format${CLANG_FORMAT_POSTFIX}
|
||||
clang-format
|
||||
PATHS ${CMAKE_BINARY_DIR}/externals)
|
||||
PATHS ${PROJECT_BINARY_DIR}/externals)
|
||||
# if find_program doesn't find it, try to download from externals
|
||||
if (NOT CLANG_FORMAT)
|
||||
if (WIN32)
|
||||
message(STATUS "Clang format not found! Downloading...")
|
||||
set(CLANG_FORMAT "${CMAKE_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
|
||||
set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
|
||||
file(DOWNLOAD
|
||||
https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
|
||||
"${CLANG_FORMAT}" SHOW_PROGRESS
|
||||
@ -311,7 +308,7 @@ if (NOT CLANG_FORMAT)
|
||||
endif()
|
||||
|
||||
if (CLANG_FORMAT)
|
||||
set(SRCS ${CMAKE_SOURCE_DIR}/src)
|
||||
set(SRCS ${PROJECT_SOURCE_DIR}/src)
|
||||
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
|
||||
if (WIN32)
|
||||
add_custom_target(clang-format
|
||||
@ -392,22 +389,22 @@ endif()
|
||||
# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
||||
# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
|
||||
if(ENABLE_QT AND UNIX AND NOT APPLE)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.desktop"
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/citra.desktop"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.svg"
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/citra.svg"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.xml"
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/citra.xml"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
if(ENABLE_SDL2)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/citra.6"
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/citra.6"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man6")
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/citra-qt.6"
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/citra-qt.6"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man6")
|
||||
endif()
|
||||
endif()
|
||||
|
138
CONTRIBUTING.md
138
CONTRIBUTING.md
@ -1,137 +1 @@
|
||||
# Reporting Issues
|
||||
|
||||
**The issue tracker is not a support forum.** Unless you can provide precise *technical information* regarding an issue, you *should not post in it*. If you need support, first read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) and then either visit our IRC or [Discord](https://citra-emu.org/discord/) channel, [our forum](https://community.citra-emu.org) or ask in a general emulation forum such as [/r/emulation](https://www.reddit.com/r/emulation/). If you post support questions, generic messages to the developers or vague reports without technical details, they will be closed and locked.
|
||||
|
||||
If you believe you have a valid issue report, please post text or a screenshot from the log (the console window that opens alongside Citra) and build version (hex string visible in the titlebar and zip filename), as well as your hardware and software information if applicable.
|
||||
|
||||
# Contributing
|
||||
|
||||
Citra is a brand new project, so we have a great opportunity to keep things clean and well organized early on. As such, coding style is very important when making commits. We run clang-format on our CI to check the code. Please use it to format your code when contributing. However, it doesn't cover all the rules below. Some of them aren't very strict rules since we want to be flexible and we understand that under certain circumstances some of them can be counterproductive. Just try to follow as many of them as possible.
|
||||
|
||||
# Using clang format (version 6.0)
|
||||
When generating the native build script for your toolset, cmake will try to find the correct version of clang format (or will download it on windows). Before running cmake, please install clang format version 6.0 for your platform as follows:
|
||||
|
||||
* Windows: do nothing; cmake will download a pre built binary for MSVC and MINGW. MSVC users can additionally install a clang format Visual Studio extension to add features like format on save.
|
||||
* OSX: run `brew install clang-format`.
|
||||
* Linux: use your package manager to get an appropriate binary.
|
||||
|
||||
If clang format is found, then cmake will add a custom build target that can be run at any time to run clang format against *all* source files and update the formatting in them. This should be used before making a pull request so that the reviewers can spend more time reviewing the code instead of having to worry about minor style violations. On MSVC, you can run clang format by building the clang-format project in the solution. On OSX, you can either use the Makefile target `make clang-format` or by building the clang-format target in XCode. For Makefile builds, you can use the clang-format target with `make clang-format`
|
||||
|
||||
### General Rules
|
||||
* A lot of code was taken from other projects (e.g. Dolphin, PPSSPP, Gekko, SkyEye). In general, when editing other people's code, follow the style of the module you're in (or better yet, fix the style if it drastically differs from our guide).
|
||||
* Line width is typically 100 characters. Please do not use 80-characters.
|
||||
* Don't ever introduce new external dependencies into Core
|
||||
* Don't use any platform specific code in Core
|
||||
* Use namespaces often
|
||||
* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`.
|
||||
|
||||
### Naming Rules
|
||||
* Functions: `PascalCase`
|
||||
* Variables: `lower_case_underscored`. Prefix with `g_` if global.
|
||||
* Classes: `PascalCase`
|
||||
* Files and Directories: `lower_case_underscored`
|
||||
* Namespaces: `PascalCase`, `_` may also be used for clarity (e.g. `ARM_InitCore`)
|
||||
|
||||
### Indentation/Whitespace Style
|
||||
Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead.
|
||||
|
||||
### Comments
|
||||
* For regular comments, use C++ style (`//`) comments, even for multi-line ones.
|
||||
* For doc-comments (Doxygen comments), use `/// ` if it's a single line, else use the `/**` `*/` style featured in the example. Start the text on the second line, not the first containing `/**`.
|
||||
* For items that are both defined and declared in two separate files, put the doc-comment only next to the associated declaration. (In a header file, usually.) Otherwise, put it next to the implementation. Never duplicate doc-comments in both places.
|
||||
|
||||
```cpp
|
||||
// Includes should be sorted lexicographically
|
||||
// STD includes first
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
// then, library includes
|
||||
#include <nihstro/shared_binary.h>
|
||||
|
||||
// finally, citra includes
|
||||
#include "common/math_util.h"
|
||||
#include "common/vector_math.h"
|
||||
|
||||
// each major module is separated
|
||||
#include "video_core/pica.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Example {
|
||||
|
||||
// Namespace contents are not indented
|
||||
|
||||
// Declare globals at the top
|
||||
int g_foo{}; // {} can be used to initialize types as 0, false, or nullptr
|
||||
char* g_some_pointer{}; // Pointer * and reference & stick to the type name, and make sure to initialize as nullptr!
|
||||
|
||||
/// A colorful enum.
|
||||
enum SomeEnum {
|
||||
ColorRed, ///< The color of fire.
|
||||
ColorGreen, ///< The color of grass.
|
||||
ColorBlue, ///< Not actually the color of water.
|
||||
};
|
||||
|
||||
/**
|
||||
* Very important struct that does a lot of stuff.
|
||||
* Note that the asterisks are indented by one space to align to the first line.
|
||||
*/
|
||||
struct Position {
|
||||
int x{}, y{}; // Always intitialize member variables!
|
||||
};
|
||||
|
||||
// Use "typename" rather than "class" here
|
||||
template <typename T>
|
||||
void FooBar() {
|
||||
const std::string some_string{ "prefer uniform initialization" };
|
||||
|
||||
int some_array[]{
|
||||
5,
|
||||
25,
|
||||
7,
|
||||
42,
|
||||
};
|
||||
|
||||
if (note == the_space_after_the_if) {
|
||||
CallAfunction();
|
||||
} else {
|
||||
// Use a space after the // when commenting
|
||||
}
|
||||
|
||||
// Place a single space after the for loop semicolons, prefer pre-increment
|
||||
for (int i{}; i != 25; ++i) {
|
||||
// This is how we write loops
|
||||
}
|
||||
|
||||
DoStuff(this, function, call, takes, up, multiple,
|
||||
lines, like, this);
|
||||
|
||||
if (this || condition_takes_up_multiple &&
|
||||
lines && like && this || everything ||
|
||||
alright || then) {
|
||||
|
||||
// Leave a blank space before the if block body if the condition was continued across
|
||||
// several lines.
|
||||
}
|
||||
|
||||
switch (var) {
|
||||
// No indentation for case label
|
||||
case 1: {
|
||||
int case_var{ var + 3 };
|
||||
DoSomething(case_var);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
DoSomething(var);
|
||||
return;
|
||||
|
||||
default:
|
||||
// Yes, even break for the last case
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<T> you_can_declare, a_few, variables, like_this;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
**The Contributor's Guide has moved to [the wiki](https://github.com/citra-emu/citra/wiki/Contributing).**
|
851
dist/languages/da_DK.ts
vendored
851
dist/languages/da_DK.ts
vendored
File diff suppressed because it is too large
Load Diff
1049
dist/languages/de.ts
vendored
1049
dist/languages/de.ts
vendored
File diff suppressed because it is too large
Load Diff
855
dist/languages/es_ES.ts
vendored
855
dist/languages/es_ES.ts
vendored
File diff suppressed because it is too large
Load Diff
919
dist/languages/fr.ts
vendored
919
dist/languages/fr.ts
vendored
File diff suppressed because it is too large
Load Diff
851
dist/languages/hu_HU.ts
vendored
851
dist/languages/hu_HU.ts
vendored
File diff suppressed because it is too large
Load Diff
1306
dist/languages/id.ts
vendored
1306
dist/languages/id.ts
vendored
File diff suppressed because it is too large
Load Diff
1127
dist/languages/it.ts
vendored
1127
dist/languages/it.ts
vendored
File diff suppressed because it is too large
Load Diff
851
dist/languages/ja_JP.ts
vendored
851
dist/languages/ja_JP.ts
vendored
File diff suppressed because it is too large
Load Diff
875
dist/languages/ko_KR.ts
vendored
875
dist/languages/ko_KR.ts
vendored
File diff suppressed because it is too large
Load Diff
921
dist/languages/lt_LT.ts
vendored
921
dist/languages/lt_LT.ts
vendored
File diff suppressed because it is too large
Load Diff
1140
dist/languages/nb.ts
vendored
1140
dist/languages/nb.ts
vendored
File diff suppressed because it is too large
Load Diff
2193
dist/languages/nl.ts
vendored
2193
dist/languages/nl.ts
vendored
File diff suppressed because it is too large
Load Diff
853
dist/languages/pl_PL.ts
vendored
853
dist/languages/pl_PL.ts
vendored
File diff suppressed because it is too large
Load Diff
1205
dist/languages/pt_BR.ts
vendored
1205
dist/languages/pt_BR.ts
vendored
File diff suppressed because it is too large
Load Diff
851
dist/languages/pt_PT.ts
vendored
851
dist/languages/pt_PT.ts
vendored
File diff suppressed because it is too large
Load Diff
853
dist/languages/ru_RU.ts
vendored
853
dist/languages/ru_RU.ts
vendored
File diff suppressed because it is too large
Load Diff
861
dist/languages/tr_TR.ts
vendored
861
dist/languages/tr_TR.ts
vendored
File diff suppressed because it is too large
Load Diff
1035
dist/languages/vi_VN.ts
vendored
1035
dist/languages/vi_VN.ts
vendored
File diff suppressed because it is too large
Load Diff
857
dist/languages/zh_CN.ts
vendored
857
dist/languages/zh_CN.ts
vendored
File diff suppressed because it is too large
Load Diff
853
dist/languages/zh_TW.ts
vendored
853
dist/languages/zh_TW.ts
vendored
File diff suppressed because it is too large
Load Diff
1
dist/scripting/citra.py
vendored
1
dist/scripting/citra.py
vendored
@ -1,7 +1,6 @@
|
||||
import zmq
|
||||
import struct
|
||||
import random
|
||||
import binascii
|
||||
import enum
|
||||
|
||||
CURRENT_REQUEST_VERSION = 1
|
||||
|
BIN
keys.tar.enc
Normal file
BIN
keys.tar.enc
Normal file
Binary file not shown.
@ -56,7 +56,8 @@ CubebSink::CubebSink(std::string target_device_name) : impl(std::make_unique<Imp
|
||||
const auto collection_end{collection.device + collection.count};
|
||||
const auto device{
|
||||
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
|
||||
return target_device_name == info.friendly_name;
|
||||
return info.friendly_name != nullptr &&
|
||||
target_device_name == info.friendly_name;
|
||||
})};
|
||||
if (device != collection_end) {
|
||||
output_device = device->devid;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
|
||||
using InterruptType = Service::DSP::DSP_DSP::InterruptType;
|
||||
@ -63,7 +64,7 @@ private:
|
||||
HLE::Mixers mixers;
|
||||
|
||||
DspHle& parent;
|
||||
CoreTiming::EventType* tick_event;
|
||||
Core::TimingEventType* tick_event;
|
||||
|
||||
std::weak_ptr<DSP_DSP> dsp_dsp;
|
||||
};
|
||||
@ -71,15 +72,17 @@ private:
|
||||
DspHle::Impl::Impl(DspHle& parent_) : parent(parent_) {
|
||||
dsp_memory.raw_memory.fill(0);
|
||||
|
||||
Core::Timing& timing = Core::System::GetInstance().CoreTiming();
|
||||
tick_event =
|
||||
CoreTiming::RegisterEvent("AudioCore::DspHle::tick_event", [this](u64, s64 cycles_late) {
|
||||
timing.RegisterEvent("AudioCore::DspHle::tick_event", [this](u64, s64 cycles_late) {
|
||||
this->AudioTickCallback(cycles_late);
|
||||
});
|
||||
CoreTiming::ScheduleEvent(audio_frame_ticks, tick_event);
|
||||
timing.ScheduleEvent(audio_frame_ticks, tick_event);
|
||||
}
|
||||
|
||||
DspHle::Impl::~Impl() {
|
||||
CoreTiming::UnscheduleEvent(tick_event, 0);
|
||||
Core::Timing& timing = Core::System::GetInstance().CoreTiming();
|
||||
timing.UnscheduleEvent(tick_event, 0);
|
||||
}
|
||||
|
||||
DspState DspHle::Impl::GetDspState() const {
|
||||
@ -328,7 +331,8 @@ void DspHle::Impl::AudioTickCallback(s64 cycles_late) {
|
||||
}
|
||||
|
||||
// Reschedule recurrent event
|
||||
CoreTiming::ScheduleEvent(audio_frame_ticks - cycles_late, tick_event);
|
||||
Core::Timing& timing = Core::System::GetInstance().CoreTiming();
|
||||
timing.ScheduleEvent(audio_frame_ticks - cycles_late, tick_event);
|
||||
}
|
||||
|
||||
DspHle::DspHle() : impl(std::make_unique<Impl>(*this)) {}
|
||||
|
@ -14,6 +14,8 @@ add_executable(citra-qt
|
||||
applets/swkbd.h
|
||||
bootmanager.cpp
|
||||
bootmanager.h
|
||||
compatibility_list.cpp
|
||||
compatibility_list.h
|
||||
camera/camera_util.cpp
|
||||
camera/camera_util.h
|
||||
camera/still_image_camera.cpp
|
||||
@ -76,6 +78,8 @@ add_executable(citra-qt
|
||||
game_list.cpp
|
||||
game_list.h
|
||||
game_list_p.h
|
||||
game_list_worker.cpp
|
||||
game_list_worker.h
|
||||
hotkeys.cpp
|
||||
hotkeys.h
|
||||
main.cpp
|
||||
@ -136,15 +140,15 @@ set(UIS
|
||||
)
|
||||
|
||||
file(GLOB COMPAT_LIST
|
||||
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*)
|
||||
file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*)
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*)
|
||||
file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
|
||||
|
||||
qt5_wrap_ui(UI_HDRS ${UIS})
|
||||
|
||||
if (ENABLE_QT_TRANSLATION)
|
||||
set(CITRA_QT_LANGUAGES "${CMAKE_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
|
||||
set(CITRA_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
|
||||
option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
|
||||
|
||||
# Update source TS file if enabled
|
||||
@ -209,7 +213,7 @@ target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Op
|
||||
target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||
|
||||
if (CITRA_ENABLE_COMPATIBILITY_REPORTING)
|
||||
add_definitions(-DCITRA_ENABLE_COMPATIBILITY_REPORTING)
|
||||
target_compile_definitions(citra-qt PRIVATE -DCITRA_ENABLE_COMPATIBILITY_REPORTING)
|
||||
endif()
|
||||
|
||||
if (USE_DISCORD_PRESENCE)
|
||||
|
@ -109,9 +109,8 @@ private:
|
||||
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
||||
: QWidget(parent), child(nullptr), emu_thread(emu_thread) {
|
||||
|
||||
std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_name,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
setWindowTitle(QString::fromStdString(window_title));
|
||||
setWindowTitle(QStringLiteral("Citra %1 | %2-%3")
|
||||
.arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
|
||||
InputCommon::Init();
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QButtonGroup>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QtConcurrent/qtconcurrentrun.h>
|
||||
#include "citra_qt/compatdb.h"
|
||||
#include "common/telemetry.h"
|
||||
#include "core/core.h"
|
||||
@ -21,6 +22,8 @@ CompatDB::CompatDB(QWidget* parent)
|
||||
connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext);
|
||||
connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext);
|
||||
connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);
|
||||
connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,
|
||||
&CompatDB::OnTestcaseSubmitted);
|
||||
}
|
||||
|
||||
CompatDB::~CompatDB() = default;
|
||||
@ -46,18 +49,38 @@ void CompatDB::Submit() {
|
||||
}
|
||||
break;
|
||||
case CompatDBPage::Final:
|
||||
back();
|
||||
LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
|
||||
Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility",
|
||||
compatibility->checkedId());
|
||||
// older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a
|
||||
// workaround
|
||||
|
||||
button(NextButton)->setEnabled(false);
|
||||
button(NextButton)->setText(tr("Submitting"));
|
||||
button(QWizard::CancelButton)->setVisible(false);
|
||||
|
||||
testcase_watcher.setFuture(QtConcurrent::run(
|
||||
[this]() { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); }));
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
|
||||
}
|
||||
}
|
||||
|
||||
void CompatDB::OnTestcaseSubmitted() {
|
||||
if (!testcase_watcher.result()) {
|
||||
QMessageBox::critical(this, tr("Communication error"),
|
||||
tr("An error occured while sending the Testcase"));
|
||||
button(NextButton)->setEnabled(true);
|
||||
button(NextButton)->setText(tr("Next"));
|
||||
button(QWizard::CancelButton)->setVisible(true);
|
||||
} else {
|
||||
next();
|
||||
// older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a
|
||||
// workaround
|
||||
button(QWizard::CancelButton)->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void CompatDB::EnableNext() {
|
||||
button(NextButton)->setEnabled(true);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QFutureWatcher>
|
||||
#include <QWizard>
|
||||
|
||||
namespace Ui {
|
||||
@ -19,8 +20,11 @@ public:
|
||||
~CompatDB();
|
||||
|
||||
private:
|
||||
QFutureWatcher<bool> testcase_watcher;
|
||||
|
||||
std::unique_ptr<Ui::CompatDB> ui;
|
||||
|
||||
void Submit();
|
||||
void OnTestcaseSubmitted();
|
||||
void EnableNext();
|
||||
};
|
||||
|
16
src/citra_qt/compatibility_list.cpp
Normal file
16
src/citra_qt/compatibility_list.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <fmt/format.h>
|
||||
#include "citra_qt/compatibility_list.h"
|
||||
|
||||
CompatibilityList::const_iterator FindMatchingCompatibilityEntry(
|
||||
const CompatibilityList& compatibility_list, u64 program_id) {
|
||||
return std::find_if(compatibility_list.begin(), compatibility_list.end(),
|
||||
[program_id](const auto& element) {
|
||||
std::string pid = fmt::format("{:016X}", program_id);
|
||||
return element.first == pid;
|
||||
});
|
||||
}
|
15
src/citra_qt/compatibility_list.h
Normal file
15
src/citra_qt/compatibility_list.h
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <QString>
|
||||
#include "common/common_types.h"
|
||||
|
||||
using CompatibilityList = std::unordered_map<std::string, std::pair<QString, QString>>;
|
||||
|
||||
CompatibilityList::const_iterator FindMatchingCompatibilityEntry(
|
||||
const CompatibilityList& compatibility_list, u64 program_id);
|
@ -16,11 +16,16 @@ Config::Config() {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
|
||||
FileUtil::CreateFullPath(qt_config_loc);
|
||||
qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
|
||||
qt_config =
|
||||
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
|
||||
|
||||
Reload();
|
||||
}
|
||||
|
||||
Config::~Config() {
|
||||
Save();
|
||||
}
|
||||
|
||||
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
|
||||
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
|
||||
Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_1, Qt::Key_2, Qt::Key_B,
|
||||
@ -561,9 +566,3 @@ void Config::Reload() {
|
||||
void Config::Save() {
|
||||
SaveValues();
|
||||
}
|
||||
|
||||
Config::~Config() {
|
||||
Save();
|
||||
|
||||
delete qt_config;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <QVariant>
|
||||
#include "core/settings.h"
|
||||
@ -12,16 +13,6 @@
|
||||
class QSettings;
|
||||
|
||||
class Config {
|
||||
QSettings* qt_config;
|
||||
std::string qt_config_loc;
|
||||
|
||||
void ReadValues();
|
||||
void SaveValues();
|
||||
QVariant ReadSetting(const QString& name);
|
||||
QVariant ReadSetting(const QString& name, const QVariant& default_value);
|
||||
void WriteSetting(const QString& name, const QVariant& value);
|
||||
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
|
||||
|
||||
public:
|
||||
Config();
|
||||
~Config();
|
||||
@ -31,4 +22,15 @@ public:
|
||||
|
||||
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
|
||||
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
||||
|
||||
private:
|
||||
void ReadValues();
|
||||
void SaveValues();
|
||||
QVariant ReadSetting(const QString& name);
|
||||
QVariant ReadSetting(const QString& name, const QVariant& default_value);
|
||||
void WriteSetting(const QString& name, const QVariant& value);
|
||||
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
|
||||
|
||||
std::unique_ptr<QSettings> qt_config;
|
||||
std::string qt_config_loc;
|
||||
};
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QMessageBox>
|
||||
#include "citra_qt/configuration/configure_general.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
#include "core/core.h"
|
||||
@ -15,6 +16,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
this->setConfiguration();
|
||||
|
||||
ui->updateBox->setVisible(UISettings::values.updater_found);
|
||||
connect(ui->button_reset_defaults, &QPushButton::clicked, this,
|
||||
&ConfigureGeneral::ResetDefaults);
|
||||
}
|
||||
|
||||
ConfigureGeneral::~ConfigureGeneral() = default;
|
||||
@ -33,6 +36,19 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
|
||||
ui->hotkeysDialog->Populate(registry);
|
||||
}
|
||||
|
||||
void ConfigureGeneral::ResetDefaults() {
|
||||
QMessageBox::StandardButton answer = QMessageBox::question(
|
||||
this, tr("Citra"),
|
||||
tr("Are you sure you want to <b>reset your settings</b> and close Citra?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
|
||||
if (answer == QMessageBox::No)
|
||||
return;
|
||||
|
||||
FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini");
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
void ConfigureGeneral::applyConfiguration() {
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
|
||||
|
@ -21,6 +21,7 @@ public:
|
||||
~ConfigureGeneral();
|
||||
|
||||
void PopulateHotkeyList(const HotkeyRegistry& registry);
|
||||
void ResetDefaults();
|
||||
void applyConfiguration();
|
||||
void retranslateUi();
|
||||
|
||||
|
@ -147,6 +147,13 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignRight">
|
||||
<widget class="QPushButton" name="button_reset_defaults">
|
||||
<property name="text">
|
||||
<string>Reset All Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -220,12 +220,12 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
|
||||
ui->setupUi(this);
|
||||
connect(ui->combo_birthmonth,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureSystem::updateBirthdayComboBox);
|
||||
&ConfigureSystem::UpdateBirthdayComboBox);
|
||||
connect(ui->combo_init_clock,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureSystem::updateInitTime);
|
||||
&ConfigureSystem::UpdateInitTime);
|
||||
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
|
||||
&ConfigureSystem::refreshConsoleID);
|
||||
&ConfigureSystem::RefreshConsoleID);
|
||||
for (u8 i = 0; i < country_names.size(); i++) {
|
||||
if (country_names.at(i) != "") {
|
||||
ui->combo_country->addItem(tr(country_names.at(i)), i);
|
||||
@ -270,7 +270,7 @@ void ConfigureSystem::ReadSystemSettings() {
|
||||
// set birthday
|
||||
std::tie(birthmonth, birthday) = cfg->GetBirthday();
|
||||
ui->combo_birthmonth->setCurrentIndex(birthmonth - 1);
|
||||
updateBirthdayComboBox(
|
||||
UpdateBirthdayComboBox(
|
||||
birthmonth -
|
||||
1); // explicitly update it because the signal from setCurrentIndex is not reliable
|
||||
ui->combo_birthday->setCurrentIndex(birthday - 1);
|
||||
@ -358,7 +358,7 @@ void ConfigureSystem::applyConfiguration() {
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) {
|
||||
void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
|
||||
if (birthmonth_index < 0 || birthmonth_index >= 12)
|
||||
return;
|
||||
|
||||
@ -391,17 +391,17 @@ void ConfigureSystem::ConfigureTime() {
|
||||
|
||||
this->setConfiguration();
|
||||
|
||||
updateInitTime(ui->combo_init_clock->currentIndex());
|
||||
UpdateInitTime(ui->combo_init_clock->currentIndex());
|
||||
}
|
||||
|
||||
void ConfigureSystem::updateInitTime(int init_clock) {
|
||||
void ConfigureSystem::UpdateInitTime(int init_clock) {
|
||||
const bool is_fixed_time =
|
||||
static_cast<Settings::InitClock>(init_clock) == Settings::InitClock::FixedTime;
|
||||
ui->label_init_time->setVisible(is_fixed_time);
|
||||
ui->edit_init_time->setVisible(is_fixed_time);
|
||||
}
|
||||
|
||||
void ConfigureSystem::refreshConsoleID() {
|
||||
void ConfigureSystem::RefreshConsoleID() {
|
||||
QMessageBox::StandardButton reply;
|
||||
QString warning_text = tr("This will replace your current virtual 3DS with a new one. "
|
||||
"Your current virtual 3DS will not be recoverable. "
|
||||
|
@ -23,29 +23,29 @@ class ConfigureSystem : public QWidget {
|
||||
|
||||
public:
|
||||
explicit ConfigureSystem(QWidget* parent = nullptr);
|
||||
~ConfigureSystem();
|
||||
~ConfigureSystem() override;
|
||||
|
||||
void applyConfiguration();
|
||||
void setConfiguration();
|
||||
void retranslateUi();
|
||||
|
||||
public slots:
|
||||
void updateBirthdayComboBox(int birthmonth_index);
|
||||
void updateInitTime(int init_clock);
|
||||
void refreshConsoleID();
|
||||
|
||||
private:
|
||||
void ReadSystemSettings();
|
||||
void ConfigureTime();
|
||||
|
||||
void UpdateBirthdayComboBox(int birthmonth_index);
|
||||
void UpdateInitTime(int init_clock);
|
||||
void RefreshConsoleID();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureSystem> ui;
|
||||
bool enabled;
|
||||
bool enabled = false;
|
||||
|
||||
std::shared_ptr<Service::CFG::Module> cfg;
|
||||
std::u16string username;
|
||||
int birthmonth, birthday;
|
||||
int language_index;
|
||||
int sound_index;
|
||||
int birthmonth = 0;
|
||||
int birthday = 0;
|
||||
int language_index = 0;
|
||||
int sound_index = 0;
|
||||
u8 country_code;
|
||||
u16 play_coin;
|
||||
};
|
||||
|
@ -261,6 +261,9 @@
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QDateTimeEdit" name="edit_init_time">
|
||||
<property name="displayFormat">
|
||||
<string>yyyy-MM-ddTHH:mm:ss</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
|
@ -30,23 +30,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole: {
|
||||
if (index.column() == 0) {
|
||||
static const std::map<Pica::DebugContext::Event, QString> map = {
|
||||
{Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded")},
|
||||
{Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed")},
|
||||
{Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")},
|
||||
{Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")},
|
||||
{Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation")},
|
||||
{Pica::DebugContext::Event::IncomingDisplayTransfer,
|
||||
tr("Incoming display transfer")},
|
||||
{Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed")},
|
||||
{Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped")},
|
||||
};
|
||||
|
||||
DEBUG_ASSERT(map.size() ==
|
||||
static_cast<std::size_t>(Pica::DebugContext::Event::NumEvents));
|
||||
return (map.find(event) != map.end()) ? map.at(event) : QString();
|
||||
return DebugContextEventToString(event);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -128,6 +113,30 @@ void BreakPointModel::OnResumed() {
|
||||
active_breakpoint = context->active_breakpoint;
|
||||
}
|
||||
|
||||
QString BreakPointModel::DebugContextEventToString(Pica::DebugContext::Event event) {
|
||||
switch (event) {
|
||||
case Pica::DebugContext::Event::PicaCommandLoaded:
|
||||
return tr("Pica command loaded");
|
||||
case Pica::DebugContext::Event::PicaCommandProcessed:
|
||||
return tr("Pica command processed");
|
||||
case Pica::DebugContext::Event::IncomingPrimitiveBatch:
|
||||
return tr("Incoming primitive batch");
|
||||
case Pica::DebugContext::Event::FinishedPrimitiveBatch:
|
||||
return tr("Finished primitive batch");
|
||||
case Pica::DebugContext::Event::VertexShaderInvocation:
|
||||
return tr("Vertex shader invocation");
|
||||
case Pica::DebugContext::Event::IncomingDisplayTransfer:
|
||||
return tr("Incoming display transfer");
|
||||
case Pica::DebugContext::Event::GSPCommandProcessed:
|
||||
return tr("GSP command processed");
|
||||
case Pica::DebugContext::Event::BufferSwapped:
|
||||
return tr("Buffers swapped");
|
||||
case Pica::DebugContext::Event::NumEvents:
|
||||
break;
|
||||
}
|
||||
return tr("Unknown debug context event");
|
||||
}
|
||||
|
||||
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
|
||||
std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
|
||||
: QDockWidget(tr("Pica Breakpoints"), parent), Pica::DebugContext::BreakPointObserver(
|
||||
|
@ -29,6 +29,8 @@ public:
|
||||
void OnResumed();
|
||||
|
||||
private:
|
||||
static QString DebugContextEventToString(Pica::DebugContext::Event event);
|
||||
|
||||
std::weak_ptr<Pica::DebugContext> context_weak;
|
||||
bool at_breakpoint;
|
||||
Pica::DebugContext::Event active_breakpoint;
|
||||
|
@ -51,7 +51,7 @@ std::size_t WaitTreeItem::Row() const {
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() {
|
||||
const auto& threads = Kernel::GetThreadList();
|
||||
const auto& threads = Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList();
|
||||
std::vector<std::unique_ptr<WaitTreeThread>> item_list;
|
||||
item_list.reserve(threads.size());
|
||||
for (std::size_t i = 0; i < threads.size(); ++i) {
|
||||
|
@ -21,8 +21,10 @@
|
||||
#include <QToolButton>
|
||||
#include <QTreeView>
|
||||
#include <fmt/format.h>
|
||||
#include "citra_qt/compatibility_list.h"
|
||||
#include "citra_qt/game_list.h"
|
||||
#include "citra_qt/game_list_p.h"
|
||||
#include "citra_qt/game_list_worker.h"
|
||||
#include "citra_qt/main.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
#include "common/common_paths.h"
|
||||
@ -30,7 +32,6 @@
|
||||
#include "core/file_sys/archive_extsavedata.h"
|
||||
#include "core/file_sys/archive_source_sd_savedata.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {}
|
||||
|
||||
@ -288,11 +289,11 @@ GameList::GameList(GMainWindow* parent) : QWidget{parent} {
|
||||
tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
item_model->insertColumns(0, COLUMN_COUNT);
|
||||
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name");
|
||||
item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility");
|
||||
item_model->setHeaderData(COLUMN_REGION, Qt::Horizontal, "Region");
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
|
||||
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
|
||||
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
|
||||
item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
|
||||
item_model->setHeaderData(COLUMN_REGION, Qt::Horizontal, tr("Region"));
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
|
||||
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
|
||||
item_model->setSortRole(GameListItemPath::TitleRole);
|
||||
|
||||
connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::onUpdateThemedIcons);
|
||||
@ -648,11 +649,6 @@ void GameList::LoadInterfaceLayout() {
|
||||
const QStringList GameList::supported_file_extensions = {"3ds", "3dsx", "elf", "axf",
|
||||
"cci", "cxi", "app"};
|
||||
|
||||
static bool HasSupportedFileExtension(const std::string& file_name) {
|
||||
QFileInfo file = QFileInfo(QString::fromStdString(file_name));
|
||||
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
void GameList::RefreshGameDirectory() {
|
||||
if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) {
|
||||
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
|
||||
@ -678,123 +674,6 @@ QString GameList::FindGameByProgramID(QStandardItem* current_item, u64 program_i
|
||||
return "";
|
||||
}
|
||||
|
||||
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion,
|
||||
GameListDir* parent_dir) {
|
||||
const auto callback = [this, recursion, parent_dir](u64* num_entries_out,
|
||||
const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
std::string physical_name = directory + DIR_SEP + virtual_name;
|
||||
|
||||
if (stop_processing)
|
||||
return false; // Breaks the callback loop.
|
||||
|
||||
bool is_dir = FileUtil::IsDirectory(physical_name);
|
||||
if (!is_dir && HasSupportedFileExtension(physical_name)) {
|
||||
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
|
||||
if (!loader)
|
||||
return true;
|
||||
|
||||
u64 program_id = 0;
|
||||
loader->ReadProgramId(program_id);
|
||||
|
||||
u64 extdata_id = 0;
|
||||
loader->ReadExtdataId(extdata_id);
|
||||
|
||||
std::vector<u8> smdh = [program_id, &loader]() -> std::vector<u8> {
|
||||
std::vector<u8> original_smdh;
|
||||
loader->ReadIcon(original_smdh);
|
||||
|
||||
if (program_id < 0x0004000000000000 || program_id > 0x00040000FFFFFFFF)
|
||||
return original_smdh;
|
||||
|
||||
std::string update_path = Service::AM::GetTitleContentPath(
|
||||
Service::FS::MediaType::SDMC, program_id + 0x0000000E00000000);
|
||||
|
||||
if (!FileUtil::Exists(update_path))
|
||||
return original_smdh;
|
||||
|
||||
std::unique_ptr<Loader::AppLoader> update_loader = Loader::GetLoader(update_path);
|
||||
|
||||
if (!update_loader)
|
||||
return original_smdh;
|
||||
|
||||
std::vector<u8> update_smdh;
|
||||
update_loader->ReadIcon(update_smdh);
|
||||
return update_smdh;
|
||||
}();
|
||||
|
||||
if (!Loader::IsValidSMDH(smdh) && UISettings::values.game_list_hide_no_icon) {
|
||||
// Skip this invalid entry
|
||||
return true;
|
||||
}
|
||||
|
||||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
|
||||
// The game list uses this as compatibility number for untested games
|
||||
QString compatibility("99");
|
||||
if (it != compatibility_list.end())
|
||||
compatibility = it->second.first;
|
||||
|
||||
emit EntryReady(
|
||||
{
|
||||
new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id,
|
||||
extdata_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItemRegion(smdh),
|
||||
new GameListItem(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||
new GameListItemSize(FileUtil::GetSize(physical_name)),
|
||||
},
|
||||
parent_dir);
|
||||
|
||||
} else if (is_dir && recursion > 0) {
|
||||
watch_list.append(QString::fromStdString(physical_name));
|
||||
AddFstEntriesToGameList(physical_name, recursion - 1, parent_dir);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
|
||||
}
|
||||
|
||||
void GameListWorker::run() {
|
||||
stop_processing = false;
|
||||
for (UISettings::GameDir& game_dir : game_dirs) {
|
||||
if (game_dir.path == "INSTALLED") {
|
||||
QString path =
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)) +
|
||||
"Nintendo "
|
||||
"3DS/00000000000000000000000000000000/"
|
||||
"00000000000000000000000000000000/title/00040000";
|
||||
watch_list.append(path);
|
||||
GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::InstalledDir);
|
||||
emit DirEntryReady({game_list_dir});
|
||||
AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir);
|
||||
} else if (game_dir.path == "SYSTEM") {
|
||||
QString path =
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)) +
|
||||
"00000000000000000000000000000000/title/00040010";
|
||||
watch_list.append(path);
|
||||
GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir);
|
||||
emit DirEntryReady({game_list_dir});
|
||||
AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir);
|
||||
} else {
|
||||
watch_list.append(game_dir.path);
|
||||
GameListDir* game_list_dir = new GameListDir(game_dir);
|
||||
emit DirEntryReady({game_list_dir});
|
||||
AddFstEntriesToGameList(game_dir.path.toStdString(), game_dir.deep_scan ? 256 : 0,
|
||||
game_list_dir);
|
||||
}
|
||||
};
|
||||
emit Finished(watch_list);
|
||||
}
|
||||
|
||||
void GameListWorker::Cancel() {
|
||||
this->disconnect();
|
||||
stop_processing = true;
|
||||
}
|
||||
|
||||
GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} {
|
||||
this->main_window = parent;
|
||||
|
||||
|
@ -4,9 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <QMenu>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
#include "citra_qt/compatibility_list.h"
|
||||
#include "common/common_types.h"
|
||||
#include "ui_settings.h"
|
||||
|
||||
@ -70,9 +71,8 @@ signals:
|
||||
void GameChosen(QString game_path);
|
||||
void ShouldCancelWorker();
|
||||
void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
|
||||
void NavigateToGamedbEntryRequested(
|
||||
u64 program_id,
|
||||
std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
|
||||
void NavigateToGamedbEntryRequested(u64 program_id,
|
||||
const CompatibilityList& compatibility_list);
|
||||
void OpenDirectory(QString directory);
|
||||
void AddDirectory();
|
||||
void ShowList(bool show);
|
||||
@ -103,7 +103,7 @@ private:
|
||||
QStandardItemModel* item_model = nullptr;
|
||||
GameListWorker* current_worker = nullptr;
|
||||
QFileSystemWatcher* watcher = nullptr;
|
||||
std::unordered_map<std::string, std::pair<QString, QString>> compatibility_list;
|
||||
CompatibilityList compatibility_list;
|
||||
|
||||
friend class GameListSearchField;
|
||||
};
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
@ -59,17 +58,6 @@ static QPixmap GetDefaultIcon(bool large) {
|
||||
return icon;
|
||||
}
|
||||
|
||||
static auto FindMatchingCompatibilityEntry(
|
||||
const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list,
|
||||
u64 program_id) {
|
||||
return std::find_if(
|
||||
compatibility_list.begin(), compatibility_list.end(),
|
||||
[program_id](const std::pair<std::string, std::pair<QString, QString>>& element) {
|
||||
std::string pid = fmt::format("{:016X}", program_id);
|
||||
return element.first == pid;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the short game title from SMDH data.
|
||||
* @param smdh SMDH data
|
||||
@ -216,7 +204,7 @@ class GameListItemCompat : public GameListItem {
|
||||
public:
|
||||
static const int CompatNumberRole = SortRole;
|
||||
GameListItemCompat() = default;
|
||||
explicit GameListItemCompat(const QString& compatiblity) {
|
||||
explicit GameListItemCompat(const QString& compatibility) {
|
||||
setData(type(), TypeRole);
|
||||
|
||||
struct CompatStatus {
|
||||
@ -235,13 +223,13 @@ public:
|
||||
{"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}};
|
||||
// clang-format on
|
||||
|
||||
auto iterator = status_data.find(compatiblity);
|
||||
auto iterator = status_data.find(compatibility);
|
||||
if (iterator == status_data.end()) {
|
||||
LOG_WARNING(Frontend, "Invalid compatibility number {}", compatiblity.toStdString());
|
||||
LOG_WARNING(Frontend, "Invalid compatibility number {}", compatibility.toStdString());
|
||||
return;
|
||||
}
|
||||
CompatStatus status = iterator->second;
|
||||
setData(compatiblity, CompatNumberRole);
|
||||
const CompatStatus& status = iterator->second;
|
||||
setData(compatibility, CompatNumberRole);
|
||||
setText(QObject::tr(status.text));
|
||||
setToolTip(QObject::tr(status.tooltip));
|
||||
setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole);
|
||||
@ -373,51 +361,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronous worker object for populating the game list.
|
||||
* Communicates with other threads through Qt's signal/slot system.
|
||||
*/
|
||||
class GameListWorker : public QObject, public QRunnable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GameListWorker(
|
||||
QList<UISettings::GameDir>& game_dirs,
|
||||
const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list)
|
||||
: game_dirs(game_dirs), compatibility_list(compatibility_list) {}
|
||||
|
||||
public slots:
|
||||
/// Starts the processing of directory tree information.
|
||||
void run() override;
|
||||
/// Tells the worker that it should no longer continue processing. Thread-safe.
|
||||
void Cancel();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* The `EntryReady` signal is emitted once an entry has been prepared and is ready
|
||||
* to be added to the game list.
|
||||
* @param entry_items a list with `QStandardItem`s that make up the columns of the new
|
||||
* entry.
|
||||
*/
|
||||
void DirEntryReady(GameListDir* entry_items);
|
||||
void EntryReady(QList<QStandardItem*> entry_items, GameListDir* parent_dir);
|
||||
|
||||
/**
|
||||
* After the worker has traversed the game directory looking for entries, this signal is
|
||||
* emitted with a list of folders that should be watched for changes as well.
|
||||
*/
|
||||
void Finished(QStringList watch_list);
|
||||
|
||||
private:
|
||||
QStringList watch_list;
|
||||
const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list;
|
||||
QList<UISettings::GameDir>& game_dirs;
|
||||
std::atomic_bool stop_processing;
|
||||
|
||||
void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion,
|
||||
GameListDir* parent_dir);
|
||||
};
|
||||
|
||||
class GameList;
|
||||
class QHBoxLayout;
|
||||
class QTreeView;
|
||||
|
150
src/citra_qt/game_list_worker.cpp
Normal file
150
src/citra_qt/game_list_worker.cpp
Normal file
@ -0,0 +1,150 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include "citra_qt/compatibility_list.h"
|
||||
#include "citra_qt/game_list.h"
|
||||
#include "citra_qt/game_list_p.h"
|
||||
#include "citra_qt/game_list_worker.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace {
|
||||
bool HasSupportedFileExtension(const std::string& file_name) {
|
||||
const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
|
||||
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
GameListWorker::GameListWorker(QList<UISettings::GameDir>& game_dirs,
|
||||
const CompatibilityList& compatibility_list)
|
||||
: game_dirs(game_dirs), compatibility_list(compatibility_list) {}
|
||||
|
||||
GameListWorker::~GameListWorker() = default;
|
||||
|
||||
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion,
|
||||
GameListDir* parent_dir) {
|
||||
const auto callback = [this, recursion, parent_dir](u64* num_entries_out,
|
||||
const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
std::string physical_name = directory + DIR_SEP + virtual_name;
|
||||
|
||||
if (stop_processing)
|
||||
return false; // Breaks the callback loop.
|
||||
|
||||
bool is_dir = FileUtil::IsDirectory(physical_name);
|
||||
if (!is_dir && HasSupportedFileExtension(physical_name)) {
|
||||
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
|
||||
if (!loader)
|
||||
return true;
|
||||
|
||||
u64 program_id = 0;
|
||||
loader->ReadProgramId(program_id);
|
||||
|
||||
u64 extdata_id = 0;
|
||||
loader->ReadExtdataId(extdata_id);
|
||||
|
||||
std::vector<u8> smdh = [program_id, &loader]() -> std::vector<u8> {
|
||||
std::vector<u8> original_smdh;
|
||||
loader->ReadIcon(original_smdh);
|
||||
|
||||
if (program_id < 0x0004000000000000 || program_id > 0x00040000FFFFFFFF)
|
||||
return original_smdh;
|
||||
|
||||
std::string update_path = Service::AM::GetTitleContentPath(
|
||||
Service::FS::MediaType::SDMC, program_id + 0x0000000E00000000);
|
||||
|
||||
if (!FileUtil::Exists(update_path))
|
||||
return original_smdh;
|
||||
|
||||
std::unique_ptr<Loader::AppLoader> update_loader = Loader::GetLoader(update_path);
|
||||
|
||||
if (!update_loader)
|
||||
return original_smdh;
|
||||
|
||||
std::vector<u8> update_smdh;
|
||||
update_loader->ReadIcon(update_smdh);
|
||||
return update_smdh;
|
||||
}();
|
||||
|
||||
if (!Loader::IsValidSMDH(smdh) && UISettings::values.game_list_hide_no_icon) {
|
||||
// Skip this invalid entry
|
||||
return true;
|
||||
}
|
||||
|
||||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
|
||||
// The game list uses this as compatibility number for untested games
|
||||
QString compatibility("99");
|
||||
if (it != compatibility_list.end())
|
||||
compatibility = it->second.first;
|
||||
|
||||
emit EntryReady(
|
||||
{
|
||||
new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id,
|
||||
extdata_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItemRegion(smdh),
|
||||
new GameListItem(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||
new GameListItemSize(FileUtil::GetSize(physical_name)),
|
||||
},
|
||||
parent_dir);
|
||||
|
||||
} else if (is_dir && recursion > 0) {
|
||||
watch_list.append(QString::fromStdString(physical_name));
|
||||
AddFstEntriesToGameList(physical_name, recursion - 1, parent_dir);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
|
||||
}
|
||||
|
||||
void GameListWorker::run() {
|
||||
stop_processing = false;
|
||||
for (UISettings::GameDir& game_dir : game_dirs) {
|
||||
if (game_dir.path == "INSTALLED") {
|
||||
QString path =
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)) +
|
||||
"Nintendo "
|
||||
"3DS/00000000000000000000000000000000/"
|
||||
"00000000000000000000000000000000/title/00040000";
|
||||
watch_list.append(path);
|
||||
GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::InstalledDir);
|
||||
emit DirEntryReady({game_list_dir});
|
||||
AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir);
|
||||
} else if (game_dir.path == "SYSTEM") {
|
||||
QString path =
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)) +
|
||||
"00000000000000000000000000000000/title/00040010";
|
||||
watch_list.append(path);
|
||||
GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir);
|
||||
emit DirEntryReady({game_list_dir});
|
||||
AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir);
|
||||
} else {
|
||||
watch_list.append(game_dir.path);
|
||||
GameListDir* game_list_dir = new GameListDir(game_dir);
|
||||
emit DirEntryReady({game_list_dir});
|
||||
AddFstEntriesToGameList(game_dir.path.toStdString(), game_dir.deep_scan ? 256 : 0,
|
||||
game_list_dir);
|
||||
}
|
||||
};
|
||||
emit Finished(watch_list);
|
||||
}
|
||||
|
||||
void GameListWorker::Cancel() {
|
||||
this->disconnect();
|
||||
stop_processing = true;
|
||||
}
|
62
src/citra_qt/game_list_worker.h
Normal file
62
src/citra_qt/game_list_worker.h
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QRunnable>
|
||||
#include <QString>
|
||||
#include "citra_qt/compatibility_list.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
class QStandardItem;
|
||||
|
||||
/**
|
||||
* Asynchronous worker object for populating the game list.
|
||||
* Communicates with other threads through Qt's signal/slot system.
|
||||
*/
|
||||
class GameListWorker : public QObject, public QRunnable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GameListWorker(QList<UISettings::GameDir>& game_dirs,
|
||||
const CompatibilityList& compatibility_list);
|
||||
~GameListWorker() override;
|
||||
|
||||
/// Starts the processing of directory tree information.
|
||||
void run() override;
|
||||
|
||||
/// Tells the worker that it should no longer continue processing. Thread-safe.
|
||||
void Cancel();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* The `EntryReady` signal is emitted once an entry has been prepared and is ready
|
||||
* to be added to the game list.
|
||||
* @param entry_items a list with `QStandardItem`s that make up the columns of the new entry.
|
||||
*/
|
||||
void DirEntryReady(GameListDir* entry_items);
|
||||
void EntryReady(QList<QStandardItem*> entry_items, GameListDir* parent_dir);
|
||||
|
||||
/**
|
||||
* After the worker has traversed the game directory looking for entries, this signal is emitted
|
||||
* with a list of folders that should be watched for changes as well.
|
||||
*/
|
||||
void Finished(QStringList watch_list);
|
||||
|
||||
private:
|
||||
void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion,
|
||||
GameListDir* parent_dir);
|
||||
|
||||
QStringList watch_list;
|
||||
const CompatibilityList& compatibility_list;
|
||||
QList<UISettings::GameDir>& game_dirs;
|
||||
std::atomic_bool stop_processing;
|
||||
};
|
@ -21,6 +21,7 @@
|
||||
#include "citra_qt/camera/qt_multimedia_camera.h"
|
||||
#include "citra_qt/camera/still_image_camera.h"
|
||||
#include "citra_qt/compatdb.h"
|
||||
#include "citra_qt/compatibility_list.h"
|
||||
#include "citra_qt/configuration/config.h"
|
||||
#include "citra_qt/configuration/configure_dialog.h"
|
||||
#include "citra_qt/debugger/console.h"
|
||||
@ -44,6 +45,7 @@
|
||||
#include "citra_qt/util/clickable_label.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/detached_tasks.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
@ -57,6 +59,7 @@
|
||||
#include "core/frontend/applets/default_applets.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/movie.h"
|
||||
#include "core/settings.h"
|
||||
@ -341,8 +344,9 @@ void GMainWindow::InitializeHotkeys() {
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Start Emulation");
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Restart", QKeySequence(Qt::Key_F5));
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Swap Screens", QKeySequence(tr("F9")));
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Toggle Screen Layout", QKeySequence(tr("F10")));
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Swap Screens", QKeySequence(Qt::Key_F9));
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Toggle Screen Layout",
|
||||
QKeySequence(Qt::Key_F10));
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
|
||||
Qt::ApplicationShortcut);
|
||||
@ -356,6 +360,11 @@ void GMainWindow::InitializeHotkeys() {
|
||||
Qt::ApplicationShortcut);
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Advance Frame", QKeySequence(Qt::Key_Backslash),
|
||||
Qt::ApplicationShortcut);
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
|
||||
Qt::ApplicationShortcut);
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Remove Amiibo", QKeySequence(Qt::Key_F3),
|
||||
Qt::ApplicationShortcut);
|
||||
|
||||
hotkey_registry.LoadHotkeys();
|
||||
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
|
||||
@ -417,6 +426,18 @@ void GMainWindow::InitializeHotkeys() {
|
||||
&QShortcut::activated, ui.action_Enable_Frame_Advancing, &QAction::trigger);
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Advance Frame", this), &QShortcut::activated,
|
||||
ui.action_Advance_Frame, &QAction::trigger);
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated,
|
||||
this, [&] {
|
||||
if (ui.action_Load_Amiibo->isEnabled()) {
|
||||
OnLoadAmiibo();
|
||||
}
|
||||
});
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Remove Amiibo", this), &QShortcut::activated,
|
||||
this, [&] {
|
||||
if (ui.action_Remove_Amiibo->isEnabled()) {
|
||||
OnRemoveAmiibo();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void GMainWindow::ShowUpdaterWidgets() {
|
||||
@ -495,6 +516,8 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile);
|
||||
connect(ui.action_Install_CIA, &QAction::triggered, this, &GMainWindow::OnMenuInstallCIA);
|
||||
connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
|
||||
connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
|
||||
connect(ui.action_Remove_Amiibo, &QAction::triggered, this, &GMainWindow::OnRemoveAmiibo);
|
||||
|
||||
// Emulation
|
||||
connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
|
||||
@ -667,12 +690,12 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
render_window->InitRenderTarget();
|
||||
render_window->MakeCurrent();
|
||||
|
||||
const char* below_gl33_title = "OpenGL 3.3 Unsupported";
|
||||
const char* below_gl33_message = "Your GPU may not support OpenGL 3.3, or you do not "
|
||||
"have the latest graphics driver.";
|
||||
const QString below_gl33_title = tr("OpenGL 3.3 Unsupported");
|
||||
const QString below_gl33_message = tr("Your GPU may not support OpenGL 3.3, or you do not "
|
||||
"have the latest graphics driver.");
|
||||
|
||||
if (!gladLoadGL()) {
|
||||
QMessageBox::critical(this, tr(below_gl33_title), tr(below_gl33_message));
|
||||
QMessageBox::critical(this, below_gl33_title, below_gl33_message);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -743,7 +766,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
break;
|
||||
|
||||
case Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL33:
|
||||
QMessageBox::critical(this, tr(below_gl33_title), tr(below_gl33_message));
|
||||
QMessageBox::critical(this, below_gl33_title, below_gl33_message);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -847,6 +870,8 @@ void GMainWindow::ShutdownGame() {
|
||||
ui.action_Pause->setEnabled(false);
|
||||
ui.action_Stop->setEnabled(false);
|
||||
ui.action_Restart->setEnabled(false);
|
||||
ui.action_Load_Amiibo->setEnabled(false);
|
||||
ui.action_Remove_Amiibo->setEnabled(false);
|
||||
ui.action_Report_Compatibility->setEnabled(false);
|
||||
ui.action_Enable_Frame_Advancing->setEnabled(false);
|
||||
ui.action_Enable_Frame_Advancing->setChecked(false);
|
||||
@ -960,14 +985,11 @@ void GMainWindow::OnGameListOpenFolder(u64 data_id, GameListOpenTarget target) {
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
|
||||
}
|
||||
|
||||
void GMainWindow::OnGameListNavigateToGamedbEntry(
|
||||
u64 program_id,
|
||||
std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) {
|
||||
|
||||
void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
|
||||
const CompatibilityList& compatibility_list) {
|
||||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
|
||||
QString directory;
|
||||
|
||||
if (it != compatibility_list.end())
|
||||
directory = it->second.second;
|
||||
|
||||
@ -1137,6 +1159,7 @@ void GMainWindow::OnStartGame() {
|
||||
ui.action_Pause->setEnabled(true);
|
||||
ui.action_Stop->setEnabled(true);
|
||||
ui.action_Restart->setEnabled(true);
|
||||
ui.action_Load_Amiibo->setEnabled(true);
|
||||
ui.action_Report_Compatibility->setEnabled(true);
|
||||
ui.action_Enable_Frame_Advancing->setEnabled(true);
|
||||
|
||||
@ -1291,6 +1314,57 @@ void GMainWindow::OnConfigure() {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnLoadAmiibo() {
|
||||
const QString extensions{"*.bin"};
|
||||
const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
|
||||
const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter);
|
||||
|
||||
if (filename.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u");
|
||||
if (nfc == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile nfc_file{filename};
|
||||
if (!nfc_file.open(QIODevice::ReadOnly)) {
|
||||
QMessageBox::warning(this, tr("Error opening Amiibo data file"),
|
||||
tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename));
|
||||
return;
|
||||
}
|
||||
|
||||
Service::NFC::AmiiboData amiibo_data{};
|
||||
const u64 read_size =
|
||||
nfc_file.read(reinterpret_cast<char*>(&amiibo_data), sizeof(Service::NFC::AmiiboData));
|
||||
if (read_size != sizeof(Service::NFC::AmiiboData)) {
|
||||
QMessageBox::warning(this, tr("Error reading Amiibo data file"),
|
||||
tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but "
|
||||
"was only able to read %2 bytes.")
|
||||
.arg(sizeof(Service::NFC::AmiiboData))
|
||||
.arg(read_size));
|
||||
return;
|
||||
}
|
||||
|
||||
nfc->LoadAmiibo(amiibo_data);
|
||||
ui.action_Remove_Amiibo->setEnabled(true);
|
||||
}
|
||||
|
||||
void GMainWindow::OnRemoveAmiibo() {
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u");
|
||||
if (nfc == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
nfc->RemoveAmiibo();
|
||||
ui.action_Remove_Amiibo->setEnabled(false);
|
||||
}
|
||||
|
||||
void GMainWindow::OnToggleFilterBar() {
|
||||
game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked());
|
||||
if (ui.action_Show_Filter_Bar->isChecked()) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <QMainWindow>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
#include "citra_qt/compatibility_list.h"
|
||||
#include "citra_qt/hotkeys.h"
|
||||
#include "common/announce_multiplayer_room.h"
|
||||
#include "core/core.h"
|
||||
@ -153,9 +154,8 @@ private slots:
|
||||
/// Called whenever a user selects a game in the game list widget.
|
||||
void OnGameListLoadFile(QString game_path);
|
||||
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
|
||||
void OnGameListNavigateToGamedbEntry(
|
||||
u64 program_id,
|
||||
std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
|
||||
void OnGameListNavigateToGamedbEntry(u64 program_id,
|
||||
const CompatibilityList& compatibility_list);
|
||||
void OnGameListOpenDirectory(QString path);
|
||||
void OnGameListAddDirectory();
|
||||
void OnGameListShowList(bool show);
|
||||
@ -166,6 +166,8 @@ private slots:
|
||||
void OnCIAInstallFinished();
|
||||
void OnMenuRecentFile();
|
||||
void OnConfigure();
|
||||
void OnLoadAmiibo();
|
||||
void OnRemoveAmiibo();
|
||||
void OnToggleFilterBar();
|
||||
void OnDisplayTitleBars(bool);
|
||||
void ToggleFullscreen();
|
||||
|
@ -57,11 +57,20 @@
|
||||
<string>Recent Files</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Amiibo">
|
||||
<property name="title">
|
||||
<string>Amiibo</string>
|
||||
</property>
|
||||
<addaction name="action_Load_Amiibo"/>
|
||||
<addaction name="action_Remove_Amiibo"/>
|
||||
</widget>
|
||||
<addaction name="action_Load_File"/>
|
||||
<addaction name="action_Install_CIA"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menu_recent_files"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menu_Amiibo"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Exit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Emulation">
|
||||
@ -73,6 +82,8 @@
|
||||
<addaction name="action_Stop"/>
|
||||
<addaction name="action_Restart"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Report_Compatibility"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Configure"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_View">
|
||||
@ -139,8 +150,6 @@
|
||||
<addaction name="action_Check_For_Updates"/>
|
||||
<addaction name="action_Open_Maintenance_Tool"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Report_Compatibility"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_FAQ"/>
|
||||
<addaction name="action_About"/>
|
||||
</widget>
|
||||
@ -415,6 +424,22 @@
|
||||
<string>Restart</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_Amiibo">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Remove_Amiibo">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
@ -65,8 +65,6 @@ add_library(common STATIC
|
||||
logging/text_formatter.cpp
|
||||
logging/text_formatter.h
|
||||
math_util.h
|
||||
memory_util.cpp
|
||||
memory_util.h
|
||||
microprofile.cpp
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
@ -90,6 +88,7 @@ add_library(common STATIC
|
||||
timer.cpp
|
||||
timer.h
|
||||
vector_math.h
|
||||
web_result.h
|
||||
)
|
||||
|
||||
if(ARCHITECTURE_x86_64)
|
||||
|
@ -9,23 +9,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
struct WebResult {
|
||||
enum class Code : u32 {
|
||||
Success,
|
||||
InvalidURL,
|
||||
CredentialsMissing,
|
||||
LibError,
|
||||
HttpError,
|
||||
WrongContent,
|
||||
NoWebservice,
|
||||
};
|
||||
Code result_code;
|
||||
std::string result_string;
|
||||
std::string returned_data;
|
||||
};
|
||||
} // namespace Common
|
||||
#include "common/web_result.h"
|
||||
|
||||
namespace AnnounceMultiplayerRoom {
|
||||
|
||||
|
@ -51,3 +51,4 @@
|
||||
#define SHARED_FONT "shared_font.bin"
|
||||
#define AES_KEYS "aes_keys.txt"
|
||||
#define BOOTROM9 "boot9.bin"
|
||||
#define SECRET_SECTOR "sector0x96.bin"
|
||||
|
@ -14,21 +14,24 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
// windows.h needs to be included before other windows headers
|
||||
#include <commdlg.h> // for GetSaveFileName
|
||||
#include <direct.h> // getcwd
|
||||
#include <direct.h> // getcwd
|
||||
#include <io.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h> // for SHGetFolderPath
|
||||
#include <tchar.h>
|
||||
#include "common/string_util.h"
|
||||
|
||||
// 64 bit offsets for windows
|
||||
#ifdef _MSC_VER
|
||||
// 64 bit offsets for MSVC
|
||||
#define fseeko _fseeki64
|
||||
#define ftello _ftelli64
|
||||
#define atoll _atoi64
|
||||
#define fileno _fileno
|
||||
#endif
|
||||
|
||||
// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64
|
||||
#define stat _stat64
|
||||
#define fstat _fstat64
|
||||
#define fileno _fileno
|
||||
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
#include <sys/param.h>
|
||||
|
@ -1,178 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/memory_util.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
// Windows.h needs to be included before psapi.h
|
||||
#include <psapi.h>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/string_util.h"
|
||||
#else
|
||||
#include <cstdlib>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
#include <unistd.h>
|
||||
#define PAGE_MASK (getpagesize() - 1)
|
||||
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
|
||||
#endif
|
||||
|
||||
// This is purposely not a full wrapper for virtualalloc/mmap, but it
|
||||
// provides exactly the primitive operations that Dolphin needs.
|
||||
|
||||
void* AllocateExecutableMemory(std::size_t size, bool low) {
|
||||
#if defined(_WIN32)
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
#else
|
||||
static char* map_hint = nullptr;
|
||||
#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
// This OS has no flag to enforce allocation below the 4 GB boundary,
|
||||
// but if we hint that we want a low address it is very likely we will
|
||||
// get one.
|
||||
// An older version of this code used MAP_FIXED, but that has the side
|
||||
// effect of discarding already mapped pages that happen to be in the
|
||||
// requested virtual memory range (such as the emulated RAM, sometimes).
|
||||
if (low && (!map_hint))
|
||||
map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */
|
||||
#endif
|
||||
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANON | MAP_PRIVATE
|
||||
#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
|
||||
| (low ? MAP_32BIT : 0)
|
||||
#endif
|
||||
,
|
||||
-1, 0);
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
#ifdef _WIN32
|
||||
if (ptr == nullptr) {
|
||||
#else
|
||||
if (ptr == MAP_FAILED) {
|
||||
ptr = nullptr;
|
||||
#endif
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
|
||||
}
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
else {
|
||||
if (low) {
|
||||
map_hint += size;
|
||||
map_hint = (char*)round_page(map_hint); /* round up to the next page */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if EMU_ARCH_BITS == 64
|
||||
if ((u64)ptr >= 0x80000000 && low == true)
|
||||
LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
|
||||
#endif
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) {
|
||||
#ifdef _WIN32
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
#else
|
||||
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (ptr == MAP_FAILED)
|
||||
ptr = nullptr;
|
||||
#endif
|
||||
|
||||
if (ptr == nullptr)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
|
||||
#ifdef _WIN32
|
||||
void* ptr = _aligned_malloc(size, alignment);
|
||||
#else
|
||||
void* ptr = nullptr;
|
||||
#ifdef ANDROID
|
||||
ptr = memalign(alignment, size);
|
||||
#else
|
||||
if (posix_memalign(&ptr, alignment, size) != 0)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (ptr == nullptr)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* ptr, std::size_t size) {
|
||||
if (ptr) {
|
||||
#ifdef _WIN32
|
||||
if (!VirtualFree(ptr, 0, MEM_RELEASE))
|
||||
LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
munmap(ptr, size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void FreeAlignedMemory(void* ptr) {
|
||||
if (ptr) {
|
||||
#ifdef _WIN32
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
|
||||
LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
|
||||
&oldValue))
|
||||
LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
mprotect(ptr, size,
|
||||
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string MemUsage() {
|
||||
#ifdef _WIN32
|
||||
#pragma comment(lib, "psapi")
|
||||
DWORD processID = GetCurrentProcessId();
|
||||
HANDLE hProcess;
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
std::string Ret;
|
||||
|
||||
// Print information about the memory usage of the process.
|
||||
|
||||
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
|
||||
if (nullptr == hProcess)
|
||||
return "MemUsage Error";
|
||||
|
||||
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
|
||||
Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7));
|
||||
|
||||
CloseHandle(hProcess);
|
||||
return Ret;
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
void* AllocateExecutableMemory(std::size_t size, bool low = true);
|
||||
void* AllocateMemoryPages(std::size_t size);
|
||||
void FreeMemoryPages(void* ptr, std::size_t size);
|
||||
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment);
|
||||
void FreeAlignedMemory(void* ptr);
|
||||
void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
|
||||
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
|
||||
std::string MemUsage();
|
||||
|
||||
inline int GetPageSize() {
|
||||
return 4096;
|
||||
}
|
@ -153,6 +153,7 @@ struct VisitorInterface : NonCopyable {
|
||||
|
||||
/// Completion method, called once all fields have been visited
|
||||
virtual void Complete() = 0;
|
||||
virtual bool SubmitTestcase() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -178,6 +179,9 @@ struct NullVisitor : public VisitorInterface {
|
||||
void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
|
||||
|
||||
void Complete() override {}
|
||||
bool SubmitTestcase() override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Telemetry
|
||||
|
25
src/common/web_result.h
Normal file
25
src/common/web_result.h
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
struct WebResult {
|
||||
enum class Code : u32 {
|
||||
Success,
|
||||
InvalidURL,
|
||||
CredentialsMissing,
|
||||
LibError,
|
||||
HttpError,
|
||||
WrongContent,
|
||||
NoWebservice,
|
||||
};
|
||||
Code result_code;
|
||||
std::string result_string;
|
||||
std::string returned_data;
|
||||
};
|
||||
} // namespace Common
|
@ -58,6 +58,7 @@ add_library(core STATIC
|
||||
file_sys/disk_archive.h
|
||||
file_sys/errors.h
|
||||
file_sys/file_backend.h
|
||||
file_sys/delay_generator.cpp
|
||||
file_sys/delay_generator.h
|
||||
file_sys/ivfc_archive.cpp
|
||||
file_sys/ivfc_archive.h
|
||||
@ -102,8 +103,6 @@ add_library(core STATIC
|
||||
hle/applets/mint.h
|
||||
hle/applets/swkbd.cpp
|
||||
hle/applets/swkbd.h
|
||||
hle/config_mem.cpp
|
||||
hle/config_mem.h
|
||||
hle/function_wrappers.h
|
||||
hle/ipc.h
|
||||
hle/ipc_helpers.h
|
||||
@ -113,6 +112,8 @@ add_library(core STATIC
|
||||
hle/kernel/client_port.h
|
||||
hle/kernel/client_session.cpp
|
||||
hle/kernel/client_session.h
|
||||
hle/kernel/config_mem.cpp
|
||||
hle/kernel/config_mem.h
|
||||
hle/kernel/errors.h
|
||||
hle/kernel/event.cpp
|
||||
hle/kernel/event.h
|
||||
@ -143,6 +144,8 @@ add_library(core STATIC
|
||||
hle/kernel/session.h
|
||||
hle/kernel/shared_memory.cpp
|
||||
hle/kernel/shared_memory.h
|
||||
hle/kernel/shared_page.cpp
|
||||
hle/kernel/shared_page.h
|
||||
hle/kernel/svc.cpp
|
||||
hle/kernel/svc.h
|
||||
hle/kernel/thread.cpp
|
||||
@ -385,8 +388,6 @@ add_library(core STATIC
|
||||
hle/service/ssl_c.h
|
||||
hle/service/y2r_u.cpp
|
||||
hle/service/y2r_u.h
|
||||
hle/shared_page.cpp
|
||||
hle/shared_page.h
|
||||
hw/aes/arithmetic128.cpp
|
||||
hw/aes/arithmetic128.h
|
||||
hw/aes/ccm.cpp
|
||||
@ -446,7 +447,7 @@ target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt open_source_archives)
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
|
||||
target_link_libraries(core PRIVATE json-headers web_service)
|
||||
target_link_libraries(core PRIVATE web_service)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
|
@ -71,7 +71,8 @@ private:
|
||||
|
||||
class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicUserCallbacks(ARM_Dynarmic& parent) : parent(parent) {}
|
||||
explicit DynarmicUserCallbacks(ARM_Dynarmic& parent)
|
||||
: parent(parent), timing(parent.system.CoreTiming()) {}
|
||||
~DynarmicUserCallbacks() = default;
|
||||
|
||||
std::uint8_t MemoryRead8(VAddr vaddr) override {
|
||||
@ -134,7 +135,8 @@ public:
|
||||
if (GDBStub::IsConnected()) {
|
||||
parent.jit->HaltExecution();
|
||||
parent.SetPC(pc);
|
||||
Kernel::Thread* thread = Kernel::GetCurrentThread();
|
||||
Kernel::Thread* thread =
|
||||
Core::System::GetInstance().Kernel().GetThreadManager().GetCurrentThread();
|
||||
parent.SaveContext(thread->context);
|
||||
GDBStub::Break();
|
||||
GDBStub::SendTrap(thread, 5);
|
||||
@ -147,18 +149,19 @@ public:
|
||||
}
|
||||
|
||||
void AddTicks(std::uint64_t ticks) override {
|
||||
CoreTiming::AddTicks(ticks);
|
||||
timing.AddTicks(ticks);
|
||||
}
|
||||
std::uint64_t GetTicksRemaining() override {
|
||||
s64 ticks = CoreTiming::GetDowncount();
|
||||
s64 ticks = timing.GetDowncount();
|
||||
return static_cast<u64>(ticks <= 0 ? 0 : ticks);
|
||||
}
|
||||
|
||||
ARM_Dynarmic& parent;
|
||||
Core::Timing& timing;
|
||||
};
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode)
|
||||
: cb(std::make_unique<DynarmicUserCallbacks>(*this)) {
|
||||
ARM_Dynarmic::ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode)
|
||||
: system(system), cb(std::make_unique<DynarmicUserCallbacks>(*this)) {
|
||||
interpreter_state = std::make_shared<ARMul_State>(initial_mode);
|
||||
PageTableChanged();
|
||||
}
|
||||
|
@ -15,11 +15,15 @@ namespace Memory {
|
||||
struct PageTable;
|
||||
} // namespace Memory
|
||||
|
||||
namespace Core {
|
||||
struct System;
|
||||
}
|
||||
|
||||
class DynarmicUserCallbacks;
|
||||
|
||||
class ARM_Dynarmic final : public ARM_Interface {
|
||||
public:
|
||||
explicit ARM_Dynarmic(PrivilegeMode initial_mode);
|
||||
ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode);
|
||||
~ARM_Dynarmic();
|
||||
|
||||
void Run() override;
|
||||
@ -50,6 +54,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class DynarmicUserCallbacks;
|
||||
Core::System& system;
|
||||
std::unique_ptr<DynarmicUserCallbacks> cb;
|
||||
std::unique_ptr<Dynarmic::A32::Jit> MakeJit();
|
||||
|
||||
|
@ -75,7 +75,7 @@ ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) {
|
||||
ARM_DynCom::~ARM_DynCom() {}
|
||||
|
||||
void ARM_DynCom::Run() {
|
||||
ExecuteInstructions(std::max<s64>(CoreTiming::GetDowncount(), 0));
|
||||
ExecuteInstructions(std::max<s64>(Core::System::GetInstance().CoreTiming().GetDowncount(), 0));
|
||||
}
|
||||
|
||||
void ARM_DynCom::Step() {
|
||||
@ -146,7 +146,7 @@ void ARM_DynCom::SetCP15Register(CP15Register reg, u32 value) {
|
||||
void ARM_DynCom::ExecuteInstructions(u64 num_instructions) {
|
||||
state->NumInstrsToExecute = num_instructions;
|
||||
unsigned ticks_executed = InterpreterMainLoop(state.get());
|
||||
CoreTiming::AddTicks(ticks_executed);
|
||||
Core::System::GetInstance().CoreTiming().AddTicks(ticks_executed);
|
||||
state->ServeBreak();
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "core/arm/skyeye_common/armstate.h"
|
||||
#include "core/arm/skyeye_common/armsupp.h"
|
||||
#include "core/arm/skyeye_common/vfp/vfp.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
@ -3859,7 +3860,7 @@ SUB_INST : {
|
||||
SWI_INST : {
|
||||
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
|
||||
swi_inst* const inst_cream = (swi_inst*)inst_base->component;
|
||||
CoreTiming::AddTicks(num_instrs);
|
||||
Core::System::GetInstance().CoreTiming().AddTicks(num_instrs);
|
||||
cpu->NumInstrsToExecute =
|
||||
num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs;
|
||||
num_instrs = 0;
|
||||
|
@ -604,7 +604,8 @@ void ARMul_State::ServeBreak() {
|
||||
if (last_bkpt_hit) {
|
||||
Reg[15] = last_bkpt.address;
|
||||
}
|
||||
Kernel::Thread* thread = Kernel::GetCurrentThread();
|
||||
Kernel::Thread* thread =
|
||||
Core::System::GetInstance().Kernel().GetThreadManager().GetCurrentThread();
|
||||
Core::CPU().SaveContext(thread->context);
|
||||
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
|
||||
last_bkpt_hit = false;
|
||||
|
@ -59,13 +59,13 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
|
||||
// If we don't have a currently active thread then don't execute instructions,
|
||||
// instead advance to the next event and try to yield to the next thread
|
||||
if (Kernel::GetCurrentThread() == nullptr) {
|
||||
if (kernel->GetThreadManager().GetCurrentThread() == nullptr) {
|
||||
LOG_TRACE(Core_ARM11, "Idling");
|
||||
CoreTiming::Idle();
|
||||
CoreTiming::Advance();
|
||||
timing->Idle();
|
||||
timing->Advance();
|
||||
PrepareReschedule();
|
||||
} else {
|
||||
CoreTiming::Advance();
|
||||
timing->Advance();
|
||||
if (tight_loop) {
|
||||
cpu_core->Run();
|
||||
} else {
|
||||
@ -126,7 +126,9 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
|
||||
return init_result;
|
||||
}
|
||||
|
||||
const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)};
|
||||
Kernel::SharedPtr<Kernel::Process> process;
|
||||
const Loader::ResultStatus load_result{app_loader->Load(process)};
|
||||
kernel->SetCurrentProcess(process);
|
||||
if (Loader::ResultStatus::Success != load_result) {
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<u32>(load_result));
|
||||
System::Shutdown();
|
||||
@ -140,7 +142,7 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
|
||||
return ResultStatus::ErrorLoader;
|
||||
}
|
||||
}
|
||||
Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
|
||||
Memory::SetCurrentPageTable(&kernel->GetCurrentProcess()->vm_manager.page_table);
|
||||
status = ResultStatus::Success;
|
||||
m_emu_window = &emu_window;
|
||||
m_filepath = filepath;
|
||||
@ -153,7 +155,7 @@ void System::PrepareReschedule() {
|
||||
}
|
||||
|
||||
PerfStats::Results System::GetAndResetPerfStats() {
|
||||
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
|
||||
return perf_stats.GetAndResetStats(timing->GetGlobalTimeUs());
|
||||
}
|
||||
|
||||
void System::Reschedule() {
|
||||
@ -162,17 +164,17 @@ void System::Reschedule() {
|
||||
}
|
||||
|
||||
reschedule_pending = false;
|
||||
Kernel::Reschedule();
|
||||
kernel->GetThreadManager().Reschedule();
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) {
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
|
||||
CoreTiming::Init();
|
||||
timing = std::make_unique<Timing>();
|
||||
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
cpu_core = std::make_unique<ARM_Dynarmic>(USER32MODE);
|
||||
cpu_core = std::make_unique<ARM_Dynarmic>(*this, USER32MODE);
|
||||
#else
|
||||
cpu_core = std::make_unique<ARM_DynCom>(USER32MODE);
|
||||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
@ -191,13 +193,12 @@ System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) {
|
||||
rpc_server = std::make_unique<RPC::RPCServer>();
|
||||
#endif
|
||||
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
||||
shared_page_handler = std::make_shared<SharedPage::Handler>();
|
||||
archive_manager = std::make_unique<Service::FS::ArchiveManager>();
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>(*this);
|
||||
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
|
||||
|
||||
HW::Init();
|
||||
Kernel::Init(system_mode);
|
||||
Service::Init(*this, service_manager);
|
||||
kernel = std::make_unique<Kernel::KernelSystem>(system_mode);
|
||||
Service::Init(*this);
|
||||
GDBStub::Init();
|
||||
|
||||
ResultStatus result = VideoCore::Init(emu_window);
|
||||
@ -230,6 +231,22 @@ const Service::FS::ArchiveManager& System::ArchiveManager() const {
|
||||
return *archive_manager;
|
||||
}
|
||||
|
||||
Kernel::KernelSystem& System::Kernel() {
|
||||
return *kernel;
|
||||
}
|
||||
|
||||
const Kernel::KernelSystem& System::Kernel() const {
|
||||
return *kernel;
|
||||
}
|
||||
|
||||
Timing& System::CoreTiming() {
|
||||
return *timing;
|
||||
}
|
||||
|
||||
const Timing& System::CoreTiming() const {
|
||||
return *timing;
|
||||
}
|
||||
|
||||
void System::RegisterSoftwareKeyboard(std::shared_ptr<Frontend::SoftwareKeyboard> swkbd) {
|
||||
registered_swkbd = std::move(swkbd);
|
||||
}
|
||||
@ -247,8 +264,7 @@ void System::Shutdown() {
|
||||
// Shutdown emulation session
|
||||
GDBStub::Shutdown();
|
||||
VideoCore::Shutdown();
|
||||
Service::Shutdown();
|
||||
Kernel::Shutdown();
|
||||
kernel.reset();
|
||||
HW::Shutdown();
|
||||
telemetry_session.reset();
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
@ -257,7 +273,7 @@ void System::Shutdown() {
|
||||
service_manager.reset();
|
||||
dsp_core.reset();
|
||||
cpu_core.reset();
|
||||
CoreTiming::Shutdown();
|
||||
timing.reset();
|
||||
app_loader.reset();
|
||||
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/applets/swkbd.h"
|
||||
#include "core/hle/shared_page.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/perf_stats.h"
|
||||
@ -36,8 +35,14 @@ class ArchiveManager;
|
||||
}
|
||||
} // namespace Service
|
||||
|
||||
namespace Kernel {
|
||||
class KernelSystem;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Timing;
|
||||
|
||||
class System {
|
||||
public:
|
||||
/**
|
||||
@ -167,6 +172,18 @@ public:
|
||||
/// Gets a const reference to the archive manager
|
||||
const Service::FS::ArchiveManager& ArchiveManager() const;
|
||||
|
||||
/// Gets a reference to the kernel
|
||||
Kernel::KernelSystem& Kernel();
|
||||
|
||||
/// Gets a const reference to the kernel
|
||||
const Kernel::KernelSystem& Kernel() const;
|
||||
|
||||
/// Gets a reference to the timing system
|
||||
Timing& CoreTiming();
|
||||
|
||||
/// Gets a const reference to the timing system
|
||||
const Timing& CoreTiming() const;
|
||||
|
||||
PerfStats perf_stats;
|
||||
FrameLimiter frame_limiter;
|
||||
|
||||
@ -193,10 +210,6 @@ public:
|
||||
return registered_swkbd;
|
||||
}
|
||||
|
||||
std::shared_ptr<SharedPage::Handler> GetSharedPageHandler() const {
|
||||
return shared_page_handler;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Initialize the emulated system.
|
||||
@ -236,11 +249,14 @@ private:
|
||||
std::unique_ptr<RPC::RPCServer> rpc_server;
|
||||
#endif
|
||||
|
||||
/// Shared Page
|
||||
std::shared_ptr<SharedPage::Handler> shared_page_handler;
|
||||
|
||||
std::unique_ptr<Service::FS::ArchiveManager> archive_manager;
|
||||
|
||||
public: // HACK: this is temporary exposed for tests,
|
||||
// due to WIP kernel refactor causing desync state in memory
|
||||
std::unique_ptr<Kernel::KernelSystem> kernel;
|
||||
std::unique_ptr<Timing> timing;
|
||||
|
||||
private:
|
||||
static System s_instance;
|
||||
|
||||
ResultStatus status = ResultStatus::Success;
|
||||
|
@ -2,75 +2,25 @@
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core_timing.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "core/core_timing.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
|
||||
static s64 global_timer;
|
||||
static s64 slice_length;
|
||||
static s64 downcount;
|
||||
|
||||
struct EventType {
|
||||
TimedCallback callback;
|
||||
const std::string* name;
|
||||
};
|
||||
|
||||
struct Event {
|
||||
s64 time;
|
||||
u64 fifo_order;
|
||||
u64 userdata;
|
||||
const EventType* type;
|
||||
};
|
||||
namespace Core {
|
||||
|
||||
// Sort by time, unless the times are the same, in which case sort by the order added to the queue
|
||||
static bool operator>(const Event& left, const Event& right) {
|
||||
return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order);
|
||||
bool Timing::Event::operator>(const Event& right) const {
|
||||
return std::tie(time, fifo_order) > std::tie(right.time, right.fifo_order);
|
||||
}
|
||||
|
||||
static bool operator<(const Event& left, const Event& right) {
|
||||
return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order);
|
||||
bool Timing::Event::operator<(const Event& right) const {
|
||||
return std::tie(time, fifo_order) < std::tie(right.time, right.fifo_order);
|
||||
}
|
||||
|
||||
// unordered_map stores each element separately as a linked list node so pointers to elements
|
||||
// remain stable regardless of rehashes/resizing.
|
||||
static std::unordered_map<std::string, EventType> event_types;
|
||||
|
||||
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
|
||||
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
|
||||
// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated
|
||||
// by the standard adaptor class.
|
||||
static std::vector<Event> event_queue;
|
||||
static u64 event_fifo_id;
|
||||
// the queue for storing the events from other threads threadsafe until they will be added
|
||||
// to the event_queue by the emu thread
|
||||
static Common::MPSCQueue<Event, false> ts_queue;
|
||||
|
||||
static constexpr int MAX_SLICE_LENGTH = 20000;
|
||||
|
||||
static s64 idled_cycles;
|
||||
|
||||
// Are we in a function that has been called from Advance()
|
||||
// If events are sheduled from a function that gets called from Advance(),
|
||||
// don't change slice_length and downcount.
|
||||
static bool is_global_timer_sane;
|
||||
|
||||
static EventType* ev_lost = nullptr;
|
||||
|
||||
static void EmptyTimedCallback(u64 userdata, s64 cyclesLate) {}
|
||||
|
||||
EventType* RegisterEvent(const std::string& name, TimedCallback callback) {
|
||||
TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback callback) {
|
||||
// check for existing type with same name.
|
||||
// we want event type names to remain unique so that we can use them for serialization.
|
||||
ASSERT_MSG(event_types.find(name) == event_types.end(),
|
||||
@ -78,42 +28,17 @@ EventType* RegisterEvent(const std::string& name, TimedCallback callback) {
|
||||
"during Init to avoid breaking save states.",
|
||||
name);
|
||||
|
||||
auto info = event_types.emplace(name, EventType{callback, nullptr});
|
||||
EventType* event_type = &info.first->second;
|
||||
auto info = event_types.emplace(name, TimingEventType{callback, nullptr});
|
||||
TimingEventType* event_type = &info.first->second;
|
||||
event_type->name = &info.first->first;
|
||||
return event_type;
|
||||
}
|
||||
|
||||
void UnregisterAllEvents() {
|
||||
ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending");
|
||||
event_types.clear();
|
||||
}
|
||||
|
||||
void Init() {
|
||||
downcount = MAX_SLICE_LENGTH;
|
||||
slice_length = MAX_SLICE_LENGTH;
|
||||
global_timer = 0;
|
||||
idled_cycles = 0;
|
||||
|
||||
// The time between CoreTiming being intialized and the first call to Advance() is considered
|
||||
// the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before
|
||||
// executing the first cycle of each slice to prepare the slice length and downcount for
|
||||
// that slice.
|
||||
is_global_timer_sane = true;
|
||||
|
||||
event_fifo_id = 0;
|
||||
ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
Timing::~Timing() {
|
||||
MoveEvents();
|
||||
ClearPendingEvents();
|
||||
UnregisterAllEvents();
|
||||
}
|
||||
|
||||
// This should only be called from the CPU thread. If you are calling
|
||||
// it from any other thread, you are doing something evil
|
||||
u64 GetTicks() {
|
||||
u64 Timing::GetTicks() const {
|
||||
u64 ticks = static_cast<u64>(global_timer);
|
||||
if (!is_global_timer_sane) {
|
||||
ticks += slice_length - downcount;
|
||||
@ -121,19 +46,16 @@ u64 GetTicks() {
|
||||
return ticks;
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) {
|
||||
void Timing::AddTicks(u64 ticks) {
|
||||
downcount -= ticks;
|
||||
}
|
||||
|
||||
u64 GetIdleTicks() {
|
||||
u64 Timing::GetIdleTicks() const {
|
||||
return static_cast<u64>(idled_cycles);
|
||||
}
|
||||
|
||||
void ClearPendingEvents() {
|
||||
event_queue.clear();
|
||||
}
|
||||
|
||||
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
|
||||
void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type,
|
||||
u64 userdata) {
|
||||
ASSERT(event_type != nullptr);
|
||||
s64 timeout = GetTicks() + cycles_into_future;
|
||||
|
||||
@ -145,11 +67,12 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
}
|
||||
|
||||
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
|
||||
void Timing::ScheduleEventThreadsafe(s64 cycles_into_future, const TimingEventType* event_type,
|
||||
u64 userdata) {
|
||||
ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
|
||||
}
|
||||
|
||||
void UnscheduleEvent(const EventType* event_type, u64 userdata) {
|
||||
void Timing::UnscheduleEvent(const TimingEventType* event_type, u64 userdata) {
|
||||
auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
|
||||
return e.type == event_type && e.userdata == userdata;
|
||||
});
|
||||
@ -161,7 +84,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveEvent(const EventType* event_type) {
|
||||
void Timing::RemoveEvent(const TimingEventType* event_type) {
|
||||
auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
|
||||
[&](const Event& e) { return e.type == event_type; });
|
||||
|
||||
@ -172,12 +95,12 @@ void RemoveEvent(const EventType* event_type) {
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
|
||||
void Timing::RemoveNormalAndThreadsafeEvent(const TimingEventType* event_type) {
|
||||
MoveEvents();
|
||||
RemoveEvent(event_type);
|
||||
}
|
||||
|
||||
void ForceExceptionCheck(s64 cycles) {
|
||||
void Timing::ForceExceptionCheck(s64 cycles) {
|
||||
cycles = std::max<s64>(0, cycles);
|
||||
if (downcount > cycles) {
|
||||
slice_length -= downcount - cycles;
|
||||
@ -185,7 +108,7 @@ void ForceExceptionCheck(s64 cycles) {
|
||||
}
|
||||
}
|
||||
|
||||
void MoveEvents() {
|
||||
void Timing::MoveEvents() {
|
||||
for (Event ev; ts_queue.Pop(ev);) {
|
||||
ev.fifo_order = event_fifo_id++;
|
||||
event_queue.emplace_back(std::move(ev));
|
||||
@ -193,7 +116,7 @@ void MoveEvents() {
|
||||
}
|
||||
}
|
||||
|
||||
void Advance() {
|
||||
void Timing::Advance() {
|
||||
MoveEvents();
|
||||
|
||||
s64 cycles_executed = slice_length - downcount;
|
||||
@ -220,17 +143,17 @@ void Advance() {
|
||||
downcount = slice_length;
|
||||
}
|
||||
|
||||
void Idle() {
|
||||
void Timing::Idle() {
|
||||
idled_cycles += downcount;
|
||||
downcount = 0;
|
||||
}
|
||||
|
||||
std::chrono::microseconds GetGlobalTimeUs() {
|
||||
std::chrono::microseconds Timing::GetGlobalTimeUs() const {
|
||||
return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE_ARM11};
|
||||
}
|
||||
|
||||
s64 GetDowncount() {
|
||||
s64 Timing::GetDowncount() const {
|
||||
return downcount;
|
||||
}
|
||||
|
||||
} // namespace CoreTiming
|
||||
} // namespace Core
|
||||
|
@ -21,8 +21,11 @@
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
|
||||
// The timing we get from the assembly is 268,111,855.956 Hz
|
||||
// It is possible that this number isn't just an integer because the compiler could have
|
||||
@ -120,73 +123,112 @@ inline u64 cyclesToMs(s64 cycles) {
|
||||
return cycles * 1000 / BASE_CLOCK_RATE_ARM11;
|
||||
}
|
||||
|
||||
namespace CoreTiming {
|
||||
|
||||
struct EventType;
|
||||
namespace Core {
|
||||
|
||||
using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
|
||||
|
||||
/**
|
||||
* CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
|
||||
* required to end slice -1 and start slice 0 before the first cycle of code is executed.
|
||||
*/
|
||||
void Init();
|
||||
void Shutdown();
|
||||
struct TimingEventType {
|
||||
TimedCallback callback;
|
||||
const std::string* name;
|
||||
};
|
||||
|
||||
/**
|
||||
* This should only be called from the emu thread, if you are calling it any other thread, you are
|
||||
* doing something evil
|
||||
*/
|
||||
u64 GetTicks();
|
||||
u64 GetIdleTicks();
|
||||
void AddTicks(u64 ticks);
|
||||
class Timing {
|
||||
public:
|
||||
~Timing();
|
||||
|
||||
/**
|
||||
* Returns the event_type identifier. if name is not unique, it will assert.
|
||||
*/
|
||||
EventType* RegisterEvent(const std::string& name, TimedCallback callback);
|
||||
void UnregisterAllEvents();
|
||||
/**
|
||||
* This should only be called from the emu thread, if you are calling it any other thread, you
|
||||
* are doing something evil
|
||||
*/
|
||||
u64 GetTicks() const;
|
||||
u64 GetIdleTicks() const;
|
||||
void AddTicks(u64 ticks);
|
||||
|
||||
/**
|
||||
* After the first Advance, the slice lengths and the downcount will be reduced whenever an event
|
||||
* is scheduled earlier than the current values.
|
||||
* Scheduling from a callback will not update the downcount until the Advance() completes.
|
||||
*/
|
||||
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
|
||||
/**
|
||||
* Returns the event_type identifier. if name is not unique, it will assert.
|
||||
*/
|
||||
TimingEventType* RegisterEvent(const std::string& name, TimedCallback callback);
|
||||
|
||||
/**
|
||||
* This is to be called when outside of hle threads, such as the graphics thread, wants to
|
||||
* schedule things to be executed on the main thread.
|
||||
* Not that this doesn't change slice_length and thus events scheduled by this might be called
|
||||
* with a delay of up to MAX_SLICE_LENGTH
|
||||
*/
|
||||
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata);
|
||||
/**
|
||||
* After the first Advance, the slice lengths and the downcount will be reduced whenever an
|
||||
* event is scheduled earlier than the current values. Scheduling from a callback will not
|
||||
* update the downcount until the Advance() completes.
|
||||
*/
|
||||
void ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, u64 userdata = 0);
|
||||
|
||||
void UnscheduleEvent(const EventType* event_type, u64 userdata);
|
||||
/**
|
||||
* This is to be called when outside of hle threads, such as the graphics thread, wants to
|
||||
* schedule things to be executed on the main thread.
|
||||
* Not that this doesn't change slice_length and thus events scheduled by this might be called
|
||||
* with a delay of up to MAX_SLICE_LENGTH
|
||||
*/
|
||||
void ScheduleEventThreadsafe(s64 cycles_into_future, const TimingEventType* event_type,
|
||||
u64 userdata);
|
||||
|
||||
/// We only permit one event of each type in the queue at a time.
|
||||
void RemoveEvent(const EventType* event_type);
|
||||
void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
|
||||
void UnscheduleEvent(const TimingEventType* event_type, u64 userdata);
|
||||
|
||||
/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
|
||||
* the previous timing slice and begins the next one, you must Advance from the previous
|
||||
* slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
|
||||
* Advance() is required to initialize the slice length before the first cycle of emulated
|
||||
* instructions is executed.
|
||||
*/
|
||||
void Advance();
|
||||
void MoveEvents();
|
||||
/// We only permit one event of each type in the queue at a time.
|
||||
void RemoveEvent(const TimingEventType* event_type);
|
||||
void RemoveNormalAndThreadsafeEvent(const TimingEventType* event_type);
|
||||
|
||||
/// Pretend that the main CPU has executed enough cycles to reach the next event.
|
||||
void Idle();
|
||||
/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
|
||||
* the previous timing slice and begins the next one, you must Advance from the previous
|
||||
* slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
|
||||
* Advance() is required to initialize the slice length before the first cycle of emulated
|
||||
* instructions is executed.
|
||||
*/
|
||||
void Advance();
|
||||
void MoveEvents();
|
||||
|
||||
/// Clear all pending events. This should ONLY be done on exit.
|
||||
void ClearPendingEvents();
|
||||
/// Pretend that the main CPU has executed enough cycles to reach the next event.
|
||||
void Idle();
|
||||
|
||||
void ForceExceptionCheck(s64 cycles);
|
||||
void ForceExceptionCheck(s64 cycles);
|
||||
|
||||
std::chrono::microseconds GetGlobalTimeUs();
|
||||
std::chrono::microseconds GetGlobalTimeUs() const;
|
||||
|
||||
s64 GetDowncount();
|
||||
s64 GetDowncount() const;
|
||||
|
||||
} // namespace CoreTiming
|
||||
private:
|
||||
struct Event {
|
||||
s64 time;
|
||||
u64 fifo_order;
|
||||
u64 userdata;
|
||||
const TimingEventType* type;
|
||||
|
||||
bool operator>(const Event& right) const;
|
||||
bool operator<(const Event& right) const;
|
||||
};
|
||||
|
||||
static constexpr int MAX_SLICE_LENGTH = 20000;
|
||||
|
||||
s64 global_timer = 0;
|
||||
s64 slice_length = MAX_SLICE_LENGTH;
|
||||
s64 downcount = MAX_SLICE_LENGTH;
|
||||
|
||||
// unordered_map stores each element separately as a linked list node so pointers to
|
||||
// elements remain stable regardless of rehashes/resizing.
|
||||
std::unordered_map<std::string, TimingEventType> event_types;
|
||||
|
||||
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
|
||||
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
|
||||
// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't
|
||||
// accomodated by the standard adaptor class.
|
||||
std::vector<Event> event_queue;
|
||||
u64 event_fifo_id = 0;
|
||||
// the queue for storing the events from other threads threadsafe until they will be added
|
||||
// to the event_queue by the emu thread
|
||||
Common::MPSCQueue<Event, false> ts_queue;
|
||||
s64 idled_cycles = 0;
|
||||
|
||||
// Are we in a function that has been called from Advance()
|
||||
// If events are sheduled from a function that gets called from Advance(),
|
||||
// don't change slice_length and downcount.
|
||||
// The time between CoreTiming being intialized and the first call to Advance() is considered
|
||||
// the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before
|
||||
// executing the first cycle of each slice to prepare the slice length and downcount for
|
||||
// that slice.
|
||||
bool is_global_timer_sane = true;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <utility>
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/archive_savedata.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
|
||||
@ -16,16 +17,19 @@ ArchiveFactory_SaveData::ArchiveFactory_SaveData(
|
||||
: sd_savedata_source(std::move(sd_savedata)) {}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) {
|
||||
return sd_savedata_source->Open(Kernel::g_current_process->codeset->program_id);
|
||||
return sd_savedata_source->Open(
|
||||
Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id);
|
||||
}
|
||||
|
||||
ResultCode ArchiveFactory_SaveData::Format(const Path& path,
|
||||
const FileSys::ArchiveFormatInfo& format_info) {
|
||||
return sd_savedata_source->Format(Kernel::g_current_process->codeset->program_id, format_info);
|
||||
return sd_savedata_source->Format(
|
||||
Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id, format_info);
|
||||
}
|
||||
|
||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const {
|
||||
return sd_savedata_source->GetFormatInfo(Kernel::g_current_process->codeset->program_id);
|
||||
return sd_savedata_source->GetFormatInfo(
|
||||
Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/archive_selfncch.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/ivfc_archive.h"
|
||||
@ -279,7 +280,7 @@ void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) {
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) {
|
||||
auto archive = std::make_unique<SelfNCCHArchive>(
|
||||
ncch_data[Kernel::g_current_process->codeset->program_id]);
|
||||
ncch_data[Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id]);
|
||||
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
|
||||
}
|
||||
|
||||
|
22
src/core/file_sys/delay_generator.cpp
Normal file
22
src/core/file_sys/delay_generator.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include "core/file_sys/delay_generator.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
DelayGenerator::~DelayGenerator() = default;
|
||||
|
||||
u64 DefaultDelayGenerator::GetReadDelayNs(std::size_t length) {
|
||||
// This is the delay measured for a romfs read.
|
||||
// For now we will take that as a default
|
||||
static constexpr u64 slope(94);
|
||||
static constexpr u64 offset(582778);
|
||||
static constexpr u64 minimum(663124);
|
||||
u64 IPCDelayNanoseconds = std::max<u64>(static_cast<u64>(length) * slope + offset, minimum);
|
||||
return IPCDelayNanoseconds;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
@ -4,10 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class DelayGenerator {
|
||||
public:
|
||||
virtual ~DelayGenerator();
|
||||
virtual u64 GetReadDelayNs(std::size_t length) = 0;
|
||||
|
||||
// TODO (B3N30): Add getter for all other file/directory io operations
|
||||
@ -15,15 +19,7 @@ public:
|
||||
|
||||
class DefaultDelayGenerator : public DelayGenerator {
|
||||
public:
|
||||
u64 GetReadDelayNs(std::size_t length) override {
|
||||
// This is the delay measured for a romfs read.
|
||||
// For now we will take that as a default
|
||||
static constexpr u64 slope(94);
|
||||
static constexpr u64 offset(582778);
|
||||
static constexpr u64 minimum(663124);
|
||||
u64 IPCDelayNanoseconds = std::max<u64>(static_cast<u64>(length) * slope + offset, minimum);
|
||||
return IPCDelayNanoseconds;
|
||||
}
|
||||
u64 GetReadDelayNs(std::size_t length) override;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
@ -226,7 +226,9 @@ Loader::ResultStatus NCCHContainer::Load() {
|
||||
std::memcpy(input.data(), key_y_primary.data(), key_y_primary.size());
|
||||
std::memcpy(input.data() + key_y_primary.size(), seed.data(), seed.size());
|
||||
CryptoPP::SHA256 sha;
|
||||
sha.CalculateDigest(key_y_secondary.data(), input.data(), input.size());
|
||||
std::array<u8, CryptoPP::SHA256::DIGESTSIZE> hash;
|
||||
sha.CalculateDigest(hash.data(), input.data(), input.size());
|
||||
std::memcpy(key_y_secondary.data(), hash.data(), key_y_secondary.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ bool SeedDB::Load() {
|
||||
LOG_ERROR(Service_FS, "Failed to read seed database count fully");
|
||||
return false;
|
||||
}
|
||||
if (!file.Seek(file.Tell() + SEEDDB_PADDING_BYTES, SEEK_SET)) {
|
||||
if (!file.Seek(SEEDDB_PADDING_BYTES, SEEK_CUR)) {
|
||||
LOG_ERROR(Service_FS, "Failed to skip seed database padding");
|
||||
return false;
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ BreakpointMap breakpoints_write;
|
||||
} // Anonymous namespace
|
||||
|
||||
static Kernel::Thread* FindThreadById(int id) {
|
||||
const auto& threads = Kernel::GetThreadList();
|
||||
const auto& threads = Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList();
|
||||
for (auto& thread : threads) {
|
||||
if (thread->GetThreadId() == static_cast<u32>(id)) {
|
||||
return thread.get();
|
||||
@ -535,7 +535,8 @@ static void HandleQuery() {
|
||||
SendReply(target_xml);
|
||||
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
|
||||
std::string val = "m";
|
||||
const auto& threads = Kernel::GetThreadList();
|
||||
const auto& threads =
|
||||
Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
val += fmt::format("{:x},", thread->GetThreadId());
|
||||
}
|
||||
@ -547,7 +548,8 @@ static void HandleQuery() {
|
||||
std::string buffer;
|
||||
buffer += "l<?xml version=\"1.0\"?>";
|
||||
buffer += "<threads>";
|
||||
const auto& threads = Kernel::GetThreadList();
|
||||
const auto& threads =
|
||||
Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
buffer += fmt::format(R"*(<thread id="{:x}" name="Thread {:x}"></thread>)*",
|
||||
thread->GetThreadId(), thread->GetThreadId());
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <unordered_map>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/applets/applet.h"
|
||||
#include "core/hle/applets/erreula.h"
|
||||
@ -38,7 +39,7 @@ namespace Applets {
|
||||
|
||||
static std::unordered_map<Service::APT::AppletId, std::shared_ptr<Applet>> applets;
|
||||
/// The CoreTiming event identifier for the Applet update callback.
|
||||
static CoreTiming::EventType* applet_update_event = nullptr;
|
||||
static Core::TimingEventType* applet_update_event = nullptr;
|
||||
/// The interval at which the Applet update callback will be called, 16.6ms
|
||||
static const u64 applet_update_interval_us = 16666;
|
||||
|
||||
@ -88,8 +89,8 @@ static void AppletUpdateEvent(u64 applet_id, s64 cycles_late) {
|
||||
|
||||
// If the applet is still running after the last update, reschedule the event
|
||||
if (applet->IsRunning()) {
|
||||
CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us) - cycles_late,
|
||||
applet_update_event, applet_id);
|
||||
Core::System::GetInstance().CoreTiming().ScheduleEvent(
|
||||
usToCycles(applet_update_interval_us) - cycles_late, applet_update_event, applet_id);
|
||||
} else {
|
||||
// Otherwise the applet has terminated, in which case we should clean it up
|
||||
applets[id] = nullptr;
|
||||
@ -101,8 +102,8 @@ ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter)
|
||||
if (result.IsError())
|
||||
return result;
|
||||
// Schedule the update event
|
||||
CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us), applet_update_event,
|
||||
static_cast<u64>(id));
|
||||
Core::System::GetInstance().CoreTiming().ScheduleEvent(
|
||||
usToCycles(applet_update_interval_us), applet_update_event, static_cast<u64>(id));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -128,11 +129,12 @@ bool IsLibraryAppletRunning() {
|
||||
|
||||
void Init() {
|
||||
// Register the applet update callback
|
||||
applet_update_event = CoreTiming::RegisterEvent("HLE Applet Update Event", AppletUpdateEvent);
|
||||
applet_update_event = Core::System::GetInstance().CoreTiming().RegisterEvent(
|
||||
"HLE Applet Update Event", AppletUpdateEvent);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
CoreTiming::RemoveEvent(applet_update_event);
|
||||
Core::System::GetInstance().CoreTiming().RemoveEvent(applet_update_event);
|
||||
}
|
||||
} // namespace Applets
|
||||
} // namespace HLE
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/applets/erreula.h"
|
||||
#include "core/hle/service/apt/apt.h"
|
||||
|
||||
@ -27,11 +28,9 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param
|
||||
|
||||
// TODO: allocated memory never released
|
||||
using Kernel::MemoryPermission;
|
||||
// Allocate a heap block of the required size for this applet.
|
||||
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
|
||||
// Create a SharedMemory that directly points to this heap block.
|
||||
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
|
||||
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
|
||||
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
"ErrEula Memory");
|
||||
|
||||
// Send the response message with the newly created SharedMemory
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/applets/mii_selector.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
@ -34,11 +35,9 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
|
||||
memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
|
||||
|
||||
using Kernel::MemoryPermission;
|
||||
// Allocate a heap block of the required size for this applet.
|
||||
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
|
||||
// Create a SharedMemory that directly points to this heap block.
|
||||
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
|
||||
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
|
||||
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
"MiiSelector Memory");
|
||||
|
||||
// Send the response message with the newly created SharedMemory
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/applets/mint.h"
|
||||
#include "core/hle/service/apt/apt.h"
|
||||
|
||||
@ -27,11 +28,9 @@ ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& paramete
|
||||
|
||||
// TODO: allocated memory never released
|
||||
using Kernel::MemoryPermission;
|
||||
// Allocate a heap block of the required size for this applet.
|
||||
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
|
||||
// Create a SharedMemory that directly points to this heap block.
|
||||
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
|
||||
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
|
||||
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
"Mint Memory");
|
||||
|
||||
// Send the response message with the newly created SharedMemory
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/applets/swkbd.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
@ -38,11 +39,9 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
|
||||
memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
|
||||
|
||||
using Kernel::MemoryPermission;
|
||||
// Allocate a heap block of the required size for this applet.
|
||||
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
|
||||
// Create a SharedMemory that directly points to this heap block.
|
||||
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
|
||||
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
|
||||
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
"SoftwareKeyboard Memory");
|
||||
|
||||
// Send the response message with the newly created SharedMemory
|
||||
|
@ -9,27 +9,6 @@
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
/// Offset into command buffer of header
|
||||
static const int kCommandHeaderOffset = 0x80;
|
||||
|
||||
/**
|
||||
* Returns a pointer to the command buffer in the current thread's TLS
|
||||
* TODO(Subv): This is not entirely correct, the command buffer should be copied from
|
||||
* the thread's TLS to an intermediate buffer in kernel memory, and then copied again to
|
||||
* the service handler process' memory.
|
||||
* @param offset Optional offset into command buffer
|
||||
* @param offset Optional offset into command buffer (in bytes)
|
||||
* @return Pointer to command buffer
|
||||
*/
|
||||
inline u32* GetCommandBuffer(const int offset = 0) {
|
||||
return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset +
|
||||
offset);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
namespace IPC {
|
||||
|
||||
/// Size of the command buffer area, in 32-bit words.
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@ -64,11 +65,11 @@ SharedPtr<Thread> AddressArbiter::ResumeHighestPriorityThread(VAddr address) {
|
||||
return thread;
|
||||
}
|
||||
|
||||
AddressArbiter::AddressArbiter() {}
|
||||
AddressArbiter::AddressArbiter(KernelSystem& kernel) : Object(kernel) {}
|
||||
AddressArbiter::~AddressArbiter() {}
|
||||
|
||||
SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) {
|
||||
SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter);
|
||||
SharedPtr<AddressArbiter> KernelSystem::CreateAddressArbiter(std::string name) {
|
||||
SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter(*this));
|
||||
|
||||
address_arbiter->name = std::move(name);
|
||||
|
||||
|
@ -31,14 +31,6 @@ enum class ArbitrationType : u32 {
|
||||
|
||||
class AddressArbiter final : public Object {
|
||||
public:
|
||||
/**
|
||||
* Creates an address arbiter.
|
||||
*
|
||||
* @param name Optional name used for debugging.
|
||||
* @returns The created AddressArbiter.
|
||||
*/
|
||||
static SharedPtr<AddressArbiter> Create(std::string name = "Unknown");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "Arbiter";
|
||||
}
|
||||
@ -57,7 +49,7 @@ public:
|
||||
s32 value, u64 nanoseconds);
|
||||
|
||||
private:
|
||||
AddressArbiter();
|
||||
explicit AddressArbiter(KernelSystem& kernel);
|
||||
~AddressArbiter() override;
|
||||
|
||||
/// Puts the thread to wait on the specified arbitration address under this address arbiter.
|
||||
@ -72,6 +64,8 @@ private:
|
||||
|
||||
/// Threads waiting for the address arbiter to be signaled.
|
||||
std::vector<SharedPtr<Thread>> waiting_threads;
|
||||
|
||||
friend class KernelSystem;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ClientPort::ClientPort() = default;
|
||||
ClientPort::ClientPort(KernelSystem& kernel) : kernel(kernel), Object(kernel) {}
|
||||
ClientPort::~ClientPort() = default;
|
||||
|
||||
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
|
||||
@ -26,7 +26,7 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
|
||||
active_sessions++;
|
||||
|
||||
// Create a new session pair, let the created sessions inherit the parent port's HLE handler.
|
||||
auto sessions = ServerSession::CreateSessionPair(server_port->GetName(), this);
|
||||
auto sessions = kernel.CreateSessionPair(server_port->GetName(), this);
|
||||
|
||||
if (server_port->hle_handler)
|
||||
server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
|
||||
|
@ -48,13 +48,16 @@ public:
|
||||
void ConnectionClosed();
|
||||
|
||||
private:
|
||||
ClientPort();
|
||||
explicit ClientPort(KernelSystem& kernel);
|
||||
~ClientPort() override;
|
||||
|
||||
KernelSystem& kernel;
|
||||
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
|
||||
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have
|
||||
u32 active_sessions = 0; ///< Number of currently open sessions to this port
|
||||
std::string name; ///< Name of client port (optional)
|
||||
|
||||
friend class KernelSystem;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ClientSession::ClientSession() = default;
|
||||
ClientSession::ClientSession(KernelSystem& kernel) : Object(kernel) {}
|
||||
ClientSession::~ClientSession() {
|
||||
// This destructor will be called automatically when the last ClientSession handle is closed by
|
||||
// the emulated application.
|
||||
|
@ -12,13 +12,12 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ServerSession;
|
||||
class Session;
|
||||
class Thread;
|
||||
|
||||
class ClientSession final : public Object {
|
||||
public:
|
||||
friend class ServerSession;
|
||||
friend class KernelSystem;
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "ClientSession";
|
||||
@ -46,7 +45,7 @@ public:
|
||||
std::shared_ptr<Session> parent;
|
||||
|
||||
private:
|
||||
ClientSession();
|
||||
explicit ClientSession(KernelSystem& kernel);
|
||||
~ClientSession() override;
|
||||
};
|
||||
|
||||
|
@ -3,15 +3,13 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "core/hle/config_mem.h"
|
||||
#include "core/hle/kernel/config_mem.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace ConfigMem {
|
||||
|
||||
ConfigMemDef config_mem;
|
||||
|
||||
void Init() {
|
||||
Handler::Handler() {
|
||||
std::memset(&config_mem, 0, sizeof(config_mem));
|
||||
|
||||
// Values extracted from firmware 11.2.0-35E
|
||||
@ -28,4 +26,8 @@ void Init() {
|
||||
config_mem.firm_ctr_sdk_ver = 0x0000F297;
|
||||
}
|
||||
|
||||
ConfigMemDef& Handler::GetConfigMem() {
|
||||
return config_mem;
|
||||
}
|
||||
|
||||
} // namespace ConfigMem
|
@ -49,8 +49,13 @@ struct ConfigMemDef {
|
||||
static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE,
|
||||
"Config Memory structure size is wrong");
|
||||
|
||||
extern ConfigMemDef config_mem;
|
||||
class Handler {
|
||||
public:
|
||||
Handler();
|
||||
ConfigMemDef& GetConfigMem();
|
||||
|
||||
void Init();
|
||||
private:
|
||||
ConfigMemDef config_mem;
|
||||
};
|
||||
|
||||
} // namespace ConfigMem
|
@ -67,6 +67,10 @@ constexpr ResultCode ERR_MISALIGNED_SIZE(ErrorDescription::MisalignedSize, Error
|
||||
constexpr ResultCode ERR_OUT_OF_MEMORY(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
|
||||
ErrorSummary::OutOfResource,
|
||||
ErrorLevel::Permanent); // 0xD86007F3
|
||||
/// Returned when out of heap or linear heap memory when allocating
|
||||
constexpr ResultCode ERR_OUT_OF_HEAP_MEMORY(ErrorDescription::OutOfMemory, ErrorModule::OS,
|
||||
ErrorSummary::OutOfResource,
|
||||
ErrorLevel::Status); // 0xC860180A
|
||||
constexpr ResultCode ERR_NOT_IMPLEMENTED(ErrorDescription::NotImplemented, ErrorModule::OS,
|
||||
ErrorSummary::InvalidArgument,
|
||||
ErrorLevel::Usage); // 0xE0E01BF4
|
||||
|
@ -7,16 +7,16 @@
|
||||
#include <vector>
|
||||
#include "common/assert.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
Event::Event() {}
|
||||
Event::Event(KernelSystem& kernel) : WaitObject(kernel) {}
|
||||
Event::~Event() {}
|
||||
|
||||
SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) {
|
||||
SharedPtr<Event> evt(new Event);
|
||||
SharedPtr<Event> KernelSystem::CreateEvent(ResetType reset_type, std::string name) {
|
||||
SharedPtr<Event> evt(new Event(*this));
|
||||
|
||||
evt->signaled = false;
|
||||
evt->reset_type = reset_type;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user