Merge branch 'master' of github.com:citra-emu/citra into ips-patches

This commit is contained in:
Zak Kurka 2018-11-15 07:11:18 -06:00
commit 1ded48f5a3
209 changed files with 17211 additions and 9498 deletions

4
.gitignore vendored
View File

@ -33,3 +33,7 @@ Thumbs.db
# Python files # Python files
*.pyc *.pyc
# Flatpak generated files
.flatpak-builder/
repo/

View File

@ -61,6 +61,18 @@ matrix:
script: "./.travis/linux-mingw/build.sh" script: "./.travis/linux-mingw/build.sh"
after_success: "./.travis/linux-mingw/upload.sh" after_success: "./.travis/linux-mingw/upload.sh"
cache: ccache 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: deploy:
provider: releases provider: releases

4
.travis/linux-flatpak/build.sh Executable file
View 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
View 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
View 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"

View 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/*"

View 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

View 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

View File

@ -24,21 +24,21 @@ option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
option(ENABLE_SCRIPTING "Enables scripting support" 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") message(STATUS "Copying pre-commit hook")
file(COPY hooks/pre-commit file(COPY hooks/pre-commit
DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks) DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
endif() endif()
# Sanity check : Check that all submodules are present # Sanity check : Check that all submodules are present
# ======================================================================= # =======================================================================
function(check_submodules_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}) string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
foreach(module ${gitmodules}) foreach(module ${gitmodules})
string(REGEX REPLACE "path *= *" "" module ${module}) 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." message(SEND_ERROR "Git submodule ${module} not found."
"Please run: git submodule update --init --recursive") "Please run: git submodule update --init --recursive")
endif() endif()
@ -46,17 +46,17 @@ function(check_submodules_present)
endfunction() endfunction()
check_submodules_present() check_submodules_present()
configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
COPYONLY) 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...") message(STATUS "Downloading compatibility list for citra...")
file(DOWNLOAD file(DOWNLOAD
https://api.citra-emu.org/gamedb/ 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() endif()
if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "") file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
endif() endif()
# Detect current compilation architecture and create standard definitions # Detect current compilation architecture and create standard definitions
@ -107,7 +107,7 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
# set up output paths for executable binaries # 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) if (NOT MSVC)
@ -170,7 +170,7 @@ endif()
# On modern Unixes, this is typically already the case. The lone exception is # 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 # glibc, which may default to 32 bits. glibc allows this to be configured
# by setting _FILE_OFFSET_BITS. # 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) add_definitions(-D_FILE_OFFSET_BITS=64)
endif() endif()
@ -179,9 +179,6 @@ add_definitions(-DSINGLETHREADED)
set_property(DIRECTORY APPEND PROPERTY set_property(DIRECTORY APPEND PROPERTY
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>) 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 # System imported libraries
# ====================== # ======================
@ -189,7 +186,7 @@ find_package(Boost 1.66.0 QUIET)
if (NOT Boost_FOUND) if (NOT Boost_FOUND)
message(STATUS "Boost 1.66.0 or newer not found, falling back to externals") 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) set(Boost_NO_SYSTEM_PATHS OFF)
find_package(Boost QUIET REQUIRED) find_package(Boost QUIET REQUIRED)
endif() endif()
@ -290,12 +287,12 @@ set(CLANG_FORMAT_POSTFIX "-6.0")
find_program(CLANG_FORMAT find_program(CLANG_FORMAT
NAMES clang-format${CLANG_FORMAT_POSTFIX} NAMES clang-format${CLANG_FORMAT_POSTFIX}
clang-format 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 find_program doesn't find it, try to download from externals
if (NOT CLANG_FORMAT) if (NOT CLANG_FORMAT)
if (WIN32) if (WIN32)
message(STATUS "Clang format not found! Downloading...") 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 file(DOWNLOAD
https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
"${CLANG_FORMAT}" SHOW_PROGRESS "${CLANG_FORMAT}" SHOW_PROGRESS
@ -311,7 +308,7 @@ if (NOT CLANG_FORMAT)
endif() endif()
if (CLANG_FORMAT) 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/") set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
if (WIN32) if (WIN32)
add_custom_target(clang-format 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/icon-theme-spec/icon-theme-spec-latest.html
# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-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) 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") 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") 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") DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
endif() endif()
if(UNIX) if(UNIX)
if(ENABLE_SDL2) 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") DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man6")
endif() endif()
if (ENABLE_QT) 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") DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man6")
endif() endif()
endif() endif()

View File

@ -1,137 +1 @@
# Reporting Issues **The Contributor's Guide has moved to [the wiki](https://github.com/citra-emu/citra/wiki/Contributing).**
**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;
}
}
```

File diff suppressed because it is too large Load Diff

1049
dist/languages/de.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

919
dist/languages/fr.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1306
dist/languages/id.ts vendored

File diff suppressed because it is too large Load Diff

1127
dist/languages/it.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1140
dist/languages/nb.ts vendored

File diff suppressed because it is too large Load Diff

2193
dist/languages/nl.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1205
dist/languages/pt_BR.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1035
dist/languages/vi_VN.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,6 @@
import zmq import zmq
import struct import struct
import random import random
import binascii
import enum import enum
CURRENT_REQUEST_VERSION = 1 CURRENT_REQUEST_VERSION = 1

BIN
keys.tar.enc Normal file

Binary file not shown.

View File

@ -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 collection_end{collection.device + collection.count};
const auto device{ const auto device{
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) { 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) { if (device != collection_end) {
output_device = device->devid; output_device = device->devid;

View File

@ -12,6 +12,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
using InterruptType = Service::DSP::DSP_DSP::InterruptType; using InterruptType = Service::DSP::DSP_DSP::InterruptType;
@ -63,7 +64,7 @@ private:
HLE::Mixers mixers; HLE::Mixers mixers;
DspHle& parent; DspHle& parent;
CoreTiming::EventType* tick_event; Core::TimingEventType* tick_event;
std::weak_ptr<DSP_DSP> dsp_dsp; std::weak_ptr<DSP_DSP> dsp_dsp;
}; };
@ -71,15 +72,17 @@ private:
DspHle::Impl::Impl(DspHle& parent_) : parent(parent_) { DspHle::Impl::Impl(DspHle& parent_) : parent(parent_) {
dsp_memory.raw_memory.fill(0); dsp_memory.raw_memory.fill(0);
Core::Timing& timing = Core::System::GetInstance().CoreTiming();
tick_event = 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); this->AudioTickCallback(cycles_late);
}); });
CoreTiming::ScheduleEvent(audio_frame_ticks, tick_event); timing.ScheduleEvent(audio_frame_ticks, tick_event);
} }
DspHle::Impl::~Impl() { 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 { DspState DspHle::Impl::GetDspState() const {
@ -328,7 +331,8 @@ void DspHle::Impl::AudioTickCallback(s64 cycles_late) {
} }
// Reschedule recurrent event // 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)) {} DspHle::DspHle() : impl(std::make_unique<Impl>(*this)) {}

View File

@ -14,6 +14,8 @@ add_executable(citra-qt
applets/swkbd.h applets/swkbd.h
bootmanager.cpp bootmanager.cpp
bootmanager.h bootmanager.h
compatibility_list.cpp
compatibility_list.h
camera/camera_util.cpp camera/camera_util.cpp
camera/camera_util.h camera/camera_util.h
camera/still_image_camera.cpp camera/still_image_camera.cpp
@ -76,6 +78,8 @@ add_executable(citra-qt
game_list.cpp game_list.cpp
game_list.h game_list.h
game_list_p.h game_list_p.h
game_list_worker.cpp
game_list_worker.h
hotkeys.cpp hotkeys.cpp
hotkeys.h hotkeys.h
main.cpp main.cpp
@ -136,15 +140,15 @@ set(UIS
) )
file(GLOB COMPAT_LIST file(GLOB COMPAT_LIST
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*) file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*)
file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
qt5_wrap_ui(UI_HDRS ${UIS}) qt5_wrap_ui(UI_HDRS ${UIS})
if (ENABLE_QT_TRANSLATION) 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) option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
# Update source TS file if enabled # 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) target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
if (CITRA_ENABLE_COMPATIBILITY_REPORTING) if (CITRA_ENABLE_COMPATIBILITY_REPORTING)
add_definitions(-DCITRA_ENABLE_COMPATIBILITY_REPORTING) target_compile_definitions(citra-qt PRIVATE -DCITRA_ENABLE_COMPATIBILITY_REPORTING)
endif() endif()
if (USE_DISCORD_PRESENCE) if (USE_DISCORD_PRESENCE)

View File

@ -109,9 +109,8 @@ private:
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
: QWidget(parent), child(nullptr), emu_thread(emu_thread) { : QWidget(parent), child(nullptr), emu_thread(emu_thread) {
std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_name, setWindowTitle(QStringLiteral("Citra %1 | %2-%3")
Common::g_scm_branch, Common::g_scm_desc); .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
setWindowTitle(QString::fromStdString(window_title));
setAttribute(Qt::WA_AcceptTouchEvents); setAttribute(Qt::WA_AcceptTouchEvents);
InputCommon::Init(); InputCommon::Init();

View File

@ -5,6 +5,7 @@
#include <QButtonGroup> #include <QButtonGroup>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <QtConcurrent/qtconcurrentrun.h>
#include "citra_qt/compatdb.h" #include "citra_qt/compatdb.h"
#include "common/telemetry.h" #include "common/telemetry.h"
#include "core/core.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_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext);
connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext);
connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit); connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);
connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,
&CompatDB::OnTestcaseSubmitted);
} }
CompatDB::~CompatDB() = default; CompatDB::~CompatDB() = default;
@ -46,18 +49,38 @@ void CompatDB::Submit() {
} }
break; break;
case CompatDBPage::Final: case CompatDBPage::Final:
back();
LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility", Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility",
compatibility->checkedId()); 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); button(QWizard::CancelButton)->setVisible(false);
testcase_watcher.setFuture(QtConcurrent::run(
[this]() { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); }));
break; break;
default: default:
LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); 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() { void CompatDB::EnableNext() {
button(NextButton)->setEnabled(true); button(NextButton)->setEnabled(true);
} }

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <QFutureWatcher>
#include <QWizard> #include <QWizard>
namespace Ui { namespace Ui {
@ -19,8 +20,11 @@ public:
~CompatDB(); ~CompatDB();
private: private:
QFutureWatcher<bool> testcase_watcher;
std::unique_ptr<Ui::CompatDB> ui; std::unique_ptr<Ui::CompatDB> ui;
void Submit(); void Submit();
void OnTestcaseSubmitted();
void EnableNext(); void EnableNext();
}; };

View 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;
});
}

View 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);

View File

@ -16,11 +16,16 @@ Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files. // 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"; qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
FileUtil::CreateFullPath(qt_config_loc); 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(); Reload();
} }
Config::~Config() {
Save();
}
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { 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_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, 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() { void Config::Save() {
SaveValues(); SaveValues();
} }
Config::~Config() {
Save();
delete qt_config;
}

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <memory>
#include <string> #include <string>
#include <QVariant> #include <QVariant>
#include "core/settings.h" #include "core/settings.h"
@ -12,16 +13,6 @@
class QSettings; class QSettings;
class Config { 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: public:
Config(); Config();
~Config(); ~Config();
@ -31,4 +22,15 @@ public:
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; 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;
}; };

View File

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QMessageBox>
#include "citra_qt/configuration/configure_general.h" #include "citra_qt/configuration/configure_general.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
#include "core/core.h" #include "core/core.h"
@ -15,6 +16,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
this->setConfiguration(); this->setConfiguration();
ui->updateBox->setVisible(UISettings::values.updater_found); ui->updateBox->setVisible(UISettings::values.updater_found);
connect(ui->button_reset_defaults, &QPushButton::clicked, this,
&ConfigureGeneral::ResetDefaults);
} }
ConfigureGeneral::~ConfigureGeneral() = default; ConfigureGeneral::~ConfigureGeneral() = default;
@ -33,6 +36,19 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
ui->hotkeysDialog->Populate(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() { void ConfigureGeneral::applyConfiguration() {
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();

View File

@ -21,6 +21,7 @@ public:
~ConfigureGeneral(); ~ConfigureGeneral();
void PopulateHotkeyList(const HotkeyRegistry& registry); void PopulateHotkeyList(const HotkeyRegistry& registry);
void ResetDefaults();
void applyConfiguration(); void applyConfiguration();
void retranslateUi(); void retranslateUi();

View File

@ -147,6 +147,13 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item alignment="Qt::AlignRight">
<widget class="QPushButton" name="button_reset_defaults">
<property name="text">
<string>Reset All Settings</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
</layout> </layout>

View File

@ -220,12 +220,12 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
ui->setupUi(this); ui->setupUi(this);
connect(ui->combo_birthmonth, connect(ui->combo_birthmonth,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ConfigureSystem::updateBirthdayComboBox); &ConfigureSystem::UpdateBirthdayComboBox);
connect(ui->combo_init_clock, connect(ui->combo_init_clock,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ConfigureSystem::updateInitTime); &ConfigureSystem::UpdateInitTime);
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
&ConfigureSystem::refreshConsoleID); &ConfigureSystem::RefreshConsoleID);
for (u8 i = 0; i < country_names.size(); i++) { for (u8 i = 0; i < country_names.size(); i++) {
if (country_names.at(i) != "") { if (country_names.at(i) != "") {
ui->combo_country->addItem(tr(country_names.at(i)), i); ui->combo_country->addItem(tr(country_names.at(i)), i);
@ -270,7 +270,7 @@ void ConfigureSystem::ReadSystemSettings() {
// set birthday // set birthday
std::tie(birthmonth, birthday) = cfg->GetBirthday(); std::tie(birthmonth, birthday) = cfg->GetBirthday();
ui->combo_birthmonth->setCurrentIndex(birthmonth - 1); ui->combo_birthmonth->setCurrentIndex(birthmonth - 1);
updateBirthdayComboBox( UpdateBirthdayComboBox(
birthmonth - birthmonth -
1); // explicitly update it because the signal from setCurrentIndex is not reliable 1); // explicitly update it because the signal from setCurrentIndex is not reliable
ui->combo_birthday->setCurrentIndex(birthday - 1); ui->combo_birthday->setCurrentIndex(birthday - 1);
@ -358,7 +358,7 @@ void ConfigureSystem::applyConfiguration() {
Settings::Apply(); Settings::Apply();
} }
void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
if (birthmonth_index < 0 || birthmonth_index >= 12) if (birthmonth_index < 0 || birthmonth_index >= 12)
return; return;
@ -391,17 +391,17 @@ void ConfigureSystem::ConfigureTime() {
this->setConfiguration(); 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 = const bool is_fixed_time =
static_cast<Settings::InitClock>(init_clock) == Settings::InitClock::FixedTime; static_cast<Settings::InitClock>(init_clock) == Settings::InitClock::FixedTime;
ui->label_init_time->setVisible(is_fixed_time); ui->label_init_time->setVisible(is_fixed_time);
ui->edit_init_time->setVisible(is_fixed_time); ui->edit_init_time->setVisible(is_fixed_time);
} }
void ConfigureSystem::refreshConsoleID() { void ConfigureSystem::RefreshConsoleID() {
QMessageBox::StandardButton reply; QMessageBox::StandardButton reply;
QString warning_text = tr("This will replace your current virtual 3DS with a new one. " QString warning_text = tr("This will replace your current virtual 3DS with a new one. "
"Your current virtual 3DS will not be recoverable. " "Your current virtual 3DS will not be recoverable. "

View File

@ -23,29 +23,29 @@ class ConfigureSystem : public QWidget {
public: public:
explicit ConfigureSystem(QWidget* parent = nullptr); explicit ConfigureSystem(QWidget* parent = nullptr);
~ConfigureSystem(); ~ConfigureSystem() override;
void applyConfiguration(); void applyConfiguration();
void setConfiguration(); void setConfiguration();
void retranslateUi(); void retranslateUi();
public slots:
void updateBirthdayComboBox(int birthmonth_index);
void updateInitTime(int init_clock);
void refreshConsoleID();
private: private:
void ReadSystemSettings(); void ReadSystemSettings();
void ConfigureTime(); void ConfigureTime();
void UpdateBirthdayComboBox(int birthmonth_index);
void UpdateInitTime(int init_clock);
void RefreshConsoleID();
std::unique_ptr<Ui::ConfigureSystem> ui; std::unique_ptr<Ui::ConfigureSystem> ui;
bool enabled; bool enabled = false;
std::shared_ptr<Service::CFG::Module> cfg; std::shared_ptr<Service::CFG::Module> cfg;
std::u16string username; std::u16string username;
int birthmonth, birthday; int birthmonth = 0;
int language_index; int birthday = 0;
int sound_index; int language_index = 0;
int sound_index = 0;
u8 country_code; u8 country_code;
u16 play_coin; u16 play_coin;
}; };

View File

@ -261,6 +261,9 @@
</item> </item>
<item row="6" column="1"> <item row="6" column="1">
<widget class="QDateTimeEdit" name="edit_init_time"> <widget class="QDateTimeEdit" name="edit_init_time">
<property name="displayFormat">
<string>yyyy-MM-ddTHH:mm:ss</string>
</property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="7" column="0">

View File

@ -30,23 +30,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
switch (role) { switch (role) {
case Qt::DisplayRole: { case Qt::DisplayRole: {
if (index.column() == 0) { if (index.column() == 0) {
static const std::map<Pica::DebugContext::Event, QString> map = { return DebugContextEventToString(event);
{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();
} }
break; break;
} }
@ -128,6 +113,30 @@ void BreakPointModel::OnResumed() {
active_breakpoint = context->active_breakpoint; 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( GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent) std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
: QDockWidget(tr("Pica Breakpoints"), parent), Pica::DebugContext::BreakPointObserver( : QDockWidget(tr("Pica Breakpoints"), parent), Pica::DebugContext::BreakPointObserver(

View File

@ -29,6 +29,8 @@ public:
void OnResumed(); void OnResumed();
private: private:
static QString DebugContextEventToString(Pica::DebugContext::Event event);
std::weak_ptr<Pica::DebugContext> context_weak; std::weak_ptr<Pica::DebugContext> context_weak;
bool at_breakpoint; bool at_breakpoint;
Pica::DebugContext::Event active_breakpoint; Pica::DebugContext::Event active_breakpoint;

View File

@ -51,7 +51,7 @@ std::size_t WaitTreeItem::Row() const {
} }
std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() { 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; std::vector<std::unique_ptr<WaitTreeThread>> item_list;
item_list.reserve(threads.size()); item_list.reserve(threads.size());
for (std::size_t i = 0; i < threads.size(); ++i) { for (std::size_t i = 0; i < threads.size(); ++i) {

View File

@ -21,8 +21,10 @@
#include <QToolButton> #include <QToolButton>
#include <QTreeView> #include <QTreeView>
#include <fmt/format.h> #include <fmt/format.h>
#include "citra_qt/compatibility_list.h"
#include "citra_qt/game_list.h" #include "citra_qt/game_list.h"
#include "citra_qt/game_list_p.h" #include "citra_qt/game_list_p.h"
#include "citra_qt/game_list_worker.h"
#include "citra_qt/main.h" #include "citra_qt/main.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
#include "common/common_paths.h" #include "common/common_paths.h"
@ -30,7 +32,6 @@
#include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/archive_source_sd_savedata.h" #include "core/file_sys/archive_source_sd_savedata.h"
#include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/archive.h"
#include "core/loader/loader.h"
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {} GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {}
@ -288,11 +289,11 @@ GameList::GameList(GMainWindow* parent) : QWidget{parent} {
tree_view->setContextMenuPolicy(Qt::CustomContextMenu); tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
item_model->insertColumns(0, COLUMN_COUNT); item_model->insertColumns(0, COLUMN_COUNT);
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
item_model->setHeaderData(COLUMN_REGION, Qt::Horizontal, "Region"); item_model->setHeaderData(COLUMN_REGION, Qt::Horizontal, tr("Region"));
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
item_model->setSortRole(GameListItemPath::TitleRole); item_model->setSortRole(GameListItemPath::TitleRole);
connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::onUpdateThemedIcons); 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", const QStringList GameList::supported_file_extensions = {"3ds", "3dsx", "elf", "axf",
"cci", "cxi", "app"}; "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() { void GameList::RefreshGameDirectory() {
if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) {
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); 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 ""; 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} { GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} {
this->main_window = parent; this->main_window = parent;

View File

@ -4,9 +4,10 @@
#pragma once #pragma once
#include <unordered_map> #include <QMenu>
#include <QString> #include <QString>
#include <QWidget> #include <QWidget>
#include "citra_qt/compatibility_list.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "ui_settings.h" #include "ui_settings.h"
@ -70,9 +71,8 @@ signals:
void GameChosen(QString game_path); void GameChosen(QString game_path);
void ShouldCancelWorker(); void ShouldCancelWorker();
void OpenFolderRequested(u64 program_id, GameListOpenTarget target); void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
void NavigateToGamedbEntryRequested( void NavigateToGamedbEntryRequested(u64 program_id,
u64 program_id, const CompatibilityList& compatibility_list);
std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
void OpenDirectory(QString directory); void OpenDirectory(QString directory);
void AddDirectory(); void AddDirectory();
void ShowList(bool show); void ShowList(bool show);
@ -103,7 +103,7 @@ private:
QStandardItemModel* item_model = nullptr; QStandardItemModel* item_model = nullptr;
GameListWorker* current_worker = nullptr; GameListWorker* current_worker = nullptr;
QFileSystemWatcher* watcher = nullptr; QFileSystemWatcher* watcher = nullptr;
std::unordered_map<std::string, std::pair<QString, QString>> compatibility_list; CompatibilityList compatibility_list;
friend class GameListSearchField; friend class GameListSearchField;
}; };

View File

@ -4,7 +4,6 @@
#pragma once #pragma once
#include <atomic>
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
@ -59,17 +58,6 @@ static QPixmap GetDefaultIcon(bool large) {
return icon; 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. * Gets the short game title from SMDH data.
* @param smdh SMDH data * @param smdh SMDH data
@ -216,7 +204,7 @@ class GameListItemCompat : public GameListItem {
public: public:
static const int CompatNumberRole = SortRole; static const int CompatNumberRole = SortRole;
GameListItemCompat() = default; GameListItemCompat() = default;
explicit GameListItemCompat(const QString& compatiblity) { explicit GameListItemCompat(const QString& compatibility) {
setData(type(), TypeRole); setData(type(), TypeRole);
struct CompatStatus { struct CompatStatus {
@ -235,13 +223,13 @@ public:
{"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}};
// clang-format on // clang-format on
auto iterator = status_data.find(compatiblity); auto iterator = status_data.find(compatibility);
if (iterator == status_data.end()) { if (iterator == status_data.end()) {
LOG_WARNING(Frontend, "Invalid compatibility number {}", compatiblity.toStdString()); LOG_WARNING(Frontend, "Invalid compatibility number {}", compatibility.toStdString());
return; return;
} }
CompatStatus status = iterator->second; const CompatStatus& status = iterator->second;
setData(compatiblity, CompatNumberRole); setData(compatibility, CompatNumberRole);
setText(QObject::tr(status.text)); setText(QObject::tr(status.text));
setToolTip(QObject::tr(status.tooltip)); setToolTip(QObject::tr(status.tooltip));
setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); 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 GameList;
class QHBoxLayout; class QHBoxLayout;
class QTreeView; class QTreeView;

View 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;
}

View 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;
};

View File

@ -21,6 +21,7 @@
#include "citra_qt/camera/qt_multimedia_camera.h" #include "citra_qt/camera/qt_multimedia_camera.h"
#include "citra_qt/camera/still_image_camera.h" #include "citra_qt/camera/still_image_camera.h"
#include "citra_qt/compatdb.h" #include "citra_qt/compatdb.h"
#include "citra_qt/compatibility_list.h"
#include "citra_qt/configuration/config.h" #include "citra_qt/configuration/config.h"
#include "citra_qt/configuration/configure_dialog.h" #include "citra_qt/configuration/configure_dialog.h"
#include "citra_qt/debugger/console.h" #include "citra_qt/debugger/console.h"
@ -44,6 +45,7 @@
#include "citra_qt/util/clickable_label.h" #include "citra_qt/util/clickable_label.h"
#include "common/common_paths.h" #include "common/common_paths.h"
#include "common/detached_tasks.h" #include "common/detached_tasks.h"
#include "common/file_util.h"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/logging/filter.h" #include "common/logging/filter.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -57,6 +59,7 @@
#include "core/frontend/applets/default_applets.h" #include "core/frontend/applets/default_applets.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/archive.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/movie.h" #include "core/movie.h"
#include "core/settings.h" #include "core/settings.h"
@ -341,8 +344,9 @@ void GMainWindow::InitializeHotkeys() {
hotkey_registry.RegisterHotkey("Main Window", "Start Emulation"); hotkey_registry.RegisterHotkey("Main Window", "Start Emulation");
hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4)); 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", "Restart", QKeySequence(Qt::Key_F5));
hotkey_registry.RegisterHotkey("Main Window", "Swap Screens", QKeySequence(tr("F9"))); hotkey_registry.RegisterHotkey("Main Window", "Swap Screens", QKeySequence(Qt::Key_F9));
hotkey_registry.RegisterHotkey("Main Window", "Toggle Screen Layout", QKeySequence(tr("F10"))); 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", "Fullscreen", QKeySequence::FullScreen);
hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape), hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
Qt::ApplicationShortcut); Qt::ApplicationShortcut);
@ -356,6 +360,11 @@ void GMainWindow::InitializeHotkeys() {
Qt::ApplicationShortcut); Qt::ApplicationShortcut);
hotkey_registry.RegisterHotkey("Main Window", "Advance Frame", QKeySequence(Qt::Key_Backslash), hotkey_registry.RegisterHotkey("Main Window", "Advance Frame", QKeySequence(Qt::Key_Backslash),
Qt::ApplicationShortcut); 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(); hotkey_registry.LoadHotkeys();
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, 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); &QShortcut::activated, ui.action_Enable_Frame_Advancing, &QAction::trigger);
connect(hotkey_registry.GetHotkey("Main Window", "Advance Frame", this), &QShortcut::activated, connect(hotkey_registry.GetHotkey("Main Window", "Advance Frame", this), &QShortcut::activated,
ui.action_Advance_Frame, &QAction::trigger); 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() { void GMainWindow::ShowUpdaterWidgets() {
@ -495,6 +516,8 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile); connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile);
connect(ui.action_Install_CIA, &QAction::triggered, this, &GMainWindow::OnMenuInstallCIA); connect(ui.action_Install_CIA, &QAction::triggered, this, &GMainWindow::OnMenuInstallCIA);
connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); 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 // Emulation
connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
@ -667,12 +690,12 @@ bool GMainWindow::LoadROM(const QString& filename) {
render_window->InitRenderTarget(); render_window->InitRenderTarget();
render_window->MakeCurrent(); render_window->MakeCurrent();
const char* below_gl33_title = "OpenGL 3.3 Unsupported"; const QString below_gl33_title = tr("OpenGL 3.3 Unsupported");
const char* below_gl33_message = "Your GPU may not support OpenGL 3.3, or you do not " const QString below_gl33_message = tr("Your GPU may not support OpenGL 3.3, or you do not "
"have the latest graphics driver."; "have the latest graphics driver.");
if (!gladLoadGL()) { if (!gladLoadGL()) {
QMessageBox::critical(this, tr(below_gl33_title), tr(below_gl33_message)); QMessageBox::critical(this, below_gl33_title, below_gl33_message);
return false; return false;
} }
@ -743,7 +766,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
break; break;
case Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL33: 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; break;
default: default:
@ -847,6 +870,8 @@ void GMainWindow::ShutdownGame() {
ui.action_Pause->setEnabled(false); ui.action_Pause->setEnabled(false);
ui.action_Stop->setEnabled(false); ui.action_Stop->setEnabled(false);
ui.action_Restart->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_Report_Compatibility->setEnabled(false);
ui.action_Enable_Frame_Advancing->setEnabled(false); ui.action_Enable_Frame_Advancing->setEnabled(false);
ui.action_Enable_Frame_Advancing->setChecked(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)); QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
} }
void GMainWindow::OnGameListNavigateToGamedbEntry( void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
u64 program_id, const CompatibilityList& compatibility_list) {
std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) {
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
QString directory; QString directory;
if (it != compatibility_list.end()) if (it != compatibility_list.end())
directory = it->second.second; directory = it->second.second;
@ -1137,6 +1159,7 @@ void GMainWindow::OnStartGame() {
ui.action_Pause->setEnabled(true); ui.action_Pause->setEnabled(true);
ui.action_Stop->setEnabled(true); ui.action_Stop->setEnabled(true);
ui.action_Restart->setEnabled(true); ui.action_Restart->setEnabled(true);
ui.action_Load_Amiibo->setEnabled(true);
ui.action_Report_Compatibility->setEnabled(true); ui.action_Report_Compatibility->setEnabled(true);
ui.action_Enable_Frame_Advancing->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() { void GMainWindow::OnToggleFilterBar() {
game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked()); game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked());
if (ui.action_Show_Filter_Bar->isChecked()) { if (ui.action_Show_Filter_Bar->isChecked()) {

View File

@ -9,6 +9,7 @@
#include <QMainWindow> #include <QMainWindow>
#include <QTimer> #include <QTimer>
#include <QTranslator> #include <QTranslator>
#include "citra_qt/compatibility_list.h"
#include "citra_qt/hotkeys.h" #include "citra_qt/hotkeys.h"
#include "common/announce_multiplayer_room.h" #include "common/announce_multiplayer_room.h"
#include "core/core.h" #include "core/core.h"
@ -153,9 +154,8 @@ private slots:
/// Called whenever a user selects a game in the game list widget. /// Called whenever a user selects a game in the game list widget.
void OnGameListLoadFile(QString game_path); void OnGameListLoadFile(QString game_path);
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
void OnGameListNavigateToGamedbEntry( void OnGameListNavigateToGamedbEntry(u64 program_id,
u64 program_id, const CompatibilityList& compatibility_list);
std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
void OnGameListOpenDirectory(QString path); void OnGameListOpenDirectory(QString path);
void OnGameListAddDirectory(); void OnGameListAddDirectory();
void OnGameListShowList(bool show); void OnGameListShowList(bool show);
@ -166,6 +166,8 @@ private slots:
void OnCIAInstallFinished(); void OnCIAInstallFinished();
void OnMenuRecentFile(); void OnMenuRecentFile();
void OnConfigure(); void OnConfigure();
void OnLoadAmiibo();
void OnRemoveAmiibo();
void OnToggleFilterBar(); void OnToggleFilterBar();
void OnDisplayTitleBars(bool); void OnDisplayTitleBars(bool);
void ToggleFullscreen(); void ToggleFullscreen();

View File

@ -57,11 +57,20 @@
<string>Recent Files</string> <string>Recent Files</string>
</property> </property>
</widget> </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_Load_File"/>
<addaction name="action_Install_CIA"/> <addaction name="action_Install_CIA"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="menu_recent_files"/> <addaction name="menu_recent_files"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="menu_Amiibo"/>
<addaction name="separator"/>
<addaction name="action_Exit"/> <addaction name="action_Exit"/>
</widget> </widget>
<widget class="QMenu" name="menu_Emulation"> <widget class="QMenu" name="menu_Emulation">
@ -73,6 +82,8 @@
<addaction name="action_Stop"/> <addaction name="action_Stop"/>
<addaction name="action_Restart"/> <addaction name="action_Restart"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_Report_Compatibility"/>
<addaction name="separator"/>
<addaction name="action_Configure"/> <addaction name="action_Configure"/>
</widget> </widget>
<widget class="QMenu" name="menu_View"> <widget class="QMenu" name="menu_View">
@ -139,8 +150,6 @@
<addaction name="action_Check_For_Updates"/> <addaction name="action_Check_For_Updates"/>
<addaction name="action_Open_Maintenance_Tool"/> <addaction name="action_Open_Maintenance_Tool"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_Report_Compatibility"/>
<addaction name="separator"/>
<addaction name="action_FAQ"/> <addaction name="action_FAQ"/>
<addaction name="action_About"/> <addaction name="action_About"/>
</widget> </widget>
@ -415,6 +424,22 @@
<string>Restart</string> <string>Restart</string>
</property> </property>
</action> </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> </widget>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -65,8 +65,6 @@ add_library(common STATIC
logging/text_formatter.cpp logging/text_formatter.cpp
logging/text_formatter.h logging/text_formatter.h
math_util.h math_util.h
memory_util.cpp
memory_util.h
microprofile.cpp microprofile.cpp
microprofile.h microprofile.h
microprofileui.h microprofileui.h
@ -90,6 +88,7 @@ add_library(common STATIC
timer.cpp timer.cpp
timer.h timer.h
vector_math.h vector_math.h
web_result.h
) )
if(ARCHITECTURE_x86_64) if(ARCHITECTURE_x86_64)

View File

@ -9,23 +9,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/web_result.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
namespace AnnounceMultiplayerRoom { namespace AnnounceMultiplayerRoom {

View File

@ -51,3 +51,4 @@
#define SHARED_FONT "shared_font.bin" #define SHARED_FONT "shared_font.bin"
#define AES_KEYS "aes_keys.txt" #define AES_KEYS "aes_keys.txt"
#define BOOTROM9 "boot9.bin" #define BOOTROM9 "boot9.bin"
#define SECRET_SECTOR "sector0x96.bin"

View File

@ -14,21 +14,24 @@
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
// windows.h needs to be included before other windows headers // 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 <io.h>
#include <shellapi.h> #include <shellapi.h>
#include <shlobj.h> // for SHGetFolderPath #include <shlobj.h> // for SHGetFolderPath
#include <tchar.h> #include <tchar.h>
#include "common/string_util.h" #include "common/string_util.h"
// 64 bit offsets for windows #ifdef _MSC_VER
// 64 bit offsets for MSVC
#define fseeko _fseeki64 #define fseeko _fseeki64
#define ftello _ftelli64 #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 stat _stat64
#define fstat _fstat64 #define fstat _fstat64
#define fileno _fileno
#else #else
#ifdef __APPLE__ #ifdef __APPLE__
#include <sys/param.h> #include <sys/param.h>

View File

@ -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
}

View File

@ -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;
}

View File

@ -153,6 +153,7 @@ struct VisitorInterface : NonCopyable {
/// Completion method, called once all fields have been visited /// Completion method, called once all fields have been visited
virtual void Complete() = 0; 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 Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
void Complete() override {} void Complete() override {}
bool SubmitTestcase() override {
return false;
}
}; };
} // namespace Telemetry } // namespace Telemetry

25
src/common/web_result.h Normal file
View 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

View File

@ -58,6 +58,7 @@ add_library(core STATIC
file_sys/disk_archive.h file_sys/disk_archive.h
file_sys/errors.h file_sys/errors.h
file_sys/file_backend.h file_sys/file_backend.h
file_sys/delay_generator.cpp
file_sys/delay_generator.h file_sys/delay_generator.h
file_sys/ivfc_archive.cpp file_sys/ivfc_archive.cpp
file_sys/ivfc_archive.h file_sys/ivfc_archive.h
@ -102,8 +103,6 @@ add_library(core STATIC
hle/applets/mint.h hle/applets/mint.h
hle/applets/swkbd.cpp hle/applets/swkbd.cpp
hle/applets/swkbd.h hle/applets/swkbd.h
hle/config_mem.cpp
hle/config_mem.h
hle/function_wrappers.h hle/function_wrappers.h
hle/ipc.h hle/ipc.h
hle/ipc_helpers.h hle/ipc_helpers.h
@ -113,6 +112,8 @@ add_library(core STATIC
hle/kernel/client_port.h hle/kernel/client_port.h
hle/kernel/client_session.cpp hle/kernel/client_session.cpp
hle/kernel/client_session.h hle/kernel/client_session.h
hle/kernel/config_mem.cpp
hle/kernel/config_mem.h
hle/kernel/errors.h hle/kernel/errors.h
hle/kernel/event.cpp hle/kernel/event.cpp
hle/kernel/event.h hle/kernel/event.h
@ -143,6 +144,8 @@ add_library(core STATIC
hle/kernel/session.h hle/kernel/session.h
hle/kernel/shared_memory.cpp hle/kernel/shared_memory.cpp
hle/kernel/shared_memory.h hle/kernel/shared_memory.h
hle/kernel/shared_page.cpp
hle/kernel/shared_page.h
hle/kernel/svc.cpp hle/kernel/svc.cpp
hle/kernel/svc.h hle/kernel/svc.h
hle/kernel/thread.cpp hle/kernel/thread.cpp
@ -385,8 +388,6 @@ add_library(core STATIC
hle/service/ssl_c.h hle/service/ssl_c.h
hle/service/y2r_u.cpp hle/service/y2r_u.cpp
hle/service/y2r_u.h hle/service/y2r_u.h
hle/shared_page.cpp
hle/shared_page.h
hw/aes/arithmetic128.cpp hw/aes/arithmetic128.cpp
hw/aes/arithmetic128.h hw/aes/arithmetic128.h
hw/aes/ccm.cpp 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) target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt open_source_archives)
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PRIVATE -DENABLE_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() endif()
if (ARCHITECTURE_x86_64) if (ARCHITECTURE_x86_64)

View File

@ -71,7 +71,8 @@ private:
class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks { class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks {
public: public:
explicit DynarmicUserCallbacks(ARM_Dynarmic& parent) : parent(parent) {} explicit DynarmicUserCallbacks(ARM_Dynarmic& parent)
: parent(parent), timing(parent.system.CoreTiming()) {}
~DynarmicUserCallbacks() = default; ~DynarmicUserCallbacks() = default;
std::uint8_t MemoryRead8(VAddr vaddr) override { std::uint8_t MemoryRead8(VAddr vaddr) override {
@ -134,7 +135,8 @@ public:
if (GDBStub::IsConnected()) { if (GDBStub::IsConnected()) {
parent.jit->HaltExecution(); parent.jit->HaltExecution();
parent.SetPC(pc); parent.SetPC(pc);
Kernel::Thread* thread = Kernel::GetCurrentThread(); Kernel::Thread* thread =
Core::System::GetInstance().Kernel().GetThreadManager().GetCurrentThread();
parent.SaveContext(thread->context); parent.SaveContext(thread->context);
GDBStub::Break(); GDBStub::Break();
GDBStub::SendTrap(thread, 5); GDBStub::SendTrap(thread, 5);
@ -147,18 +149,19 @@ public:
} }
void AddTicks(std::uint64_t ticks) override { void AddTicks(std::uint64_t ticks) override {
CoreTiming::AddTicks(ticks); timing.AddTicks(ticks);
} }
std::uint64_t GetTicksRemaining() override { std::uint64_t GetTicksRemaining() override {
s64 ticks = CoreTiming::GetDowncount(); s64 ticks = timing.GetDowncount();
return static_cast<u64>(ticks <= 0 ? 0 : ticks); return static_cast<u64>(ticks <= 0 ? 0 : ticks);
} }
ARM_Dynarmic& parent; ARM_Dynarmic& parent;
Core::Timing& timing;
}; };
ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) ARM_Dynarmic::ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode)
: cb(std::make_unique<DynarmicUserCallbacks>(*this)) { : system(system), cb(std::make_unique<DynarmicUserCallbacks>(*this)) {
interpreter_state = std::make_shared<ARMul_State>(initial_mode); interpreter_state = std::make_shared<ARMul_State>(initial_mode);
PageTableChanged(); PageTableChanged();
} }

View File

@ -15,11 +15,15 @@ namespace Memory {
struct PageTable; struct PageTable;
} // namespace Memory } // namespace Memory
namespace Core {
struct System;
}
class DynarmicUserCallbacks; class DynarmicUserCallbacks;
class ARM_Dynarmic final : public ARM_Interface { class ARM_Dynarmic final : public ARM_Interface {
public: public:
explicit ARM_Dynarmic(PrivilegeMode initial_mode); ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode);
~ARM_Dynarmic(); ~ARM_Dynarmic();
void Run() override; void Run() override;
@ -50,6 +54,7 @@ public:
private: private:
friend class DynarmicUserCallbacks; friend class DynarmicUserCallbacks;
Core::System& system;
std::unique_ptr<DynarmicUserCallbacks> cb; std::unique_ptr<DynarmicUserCallbacks> cb;
std::unique_ptr<Dynarmic::A32::Jit> MakeJit(); std::unique_ptr<Dynarmic::A32::Jit> MakeJit();

View File

@ -75,7 +75,7 @@ ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) {
ARM_DynCom::~ARM_DynCom() {} ARM_DynCom::~ARM_DynCom() {}
void ARM_DynCom::Run() { 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() { void ARM_DynCom::Step() {
@ -146,7 +146,7 @@ void ARM_DynCom::SetCP15Register(CP15Register reg, u32 value) {
void ARM_DynCom::ExecuteInstructions(u64 num_instructions) { void ARM_DynCom::ExecuteInstructions(u64 num_instructions) {
state->NumInstrsToExecute = num_instructions; state->NumInstrsToExecute = num_instructions;
unsigned ticks_executed = InterpreterMainLoop(state.get()); unsigned ticks_executed = InterpreterMainLoop(state.get());
CoreTiming::AddTicks(ticks_executed); Core::System::GetInstance().CoreTiming().AddTicks(ticks_executed);
state->ServeBreak(); state->ServeBreak();
} }

View File

@ -18,6 +18,7 @@
#include "core/arm/skyeye_common/armstate.h" #include "core/arm/skyeye_common/armstate.h"
#include "core/arm/skyeye_common/armsupp.h" #include "core/arm/skyeye_common/armsupp.h"
#include "core/arm/skyeye_common/vfp/vfp.h" #include "core/arm/skyeye_common/vfp/vfp.h"
#include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc.h"
@ -3859,7 +3860,7 @@ SUB_INST : {
SWI_INST : { SWI_INST : {
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
swi_inst* const inst_cream = (swi_inst*)inst_base->component; swi_inst* const inst_cream = (swi_inst*)inst_base->component;
CoreTiming::AddTicks(num_instrs); Core::System::GetInstance().CoreTiming().AddTicks(num_instrs);
cpu->NumInstrsToExecute = cpu->NumInstrsToExecute =
num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs; num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs;
num_instrs = 0; num_instrs = 0;

View File

@ -604,7 +604,8 @@ void ARMul_State::ServeBreak() {
if (last_bkpt_hit) { if (last_bkpt_hit) {
Reg[15] = last_bkpt.address; Reg[15] = last_bkpt.address;
} }
Kernel::Thread* thread = Kernel::GetCurrentThread(); Kernel::Thread* thread =
Core::System::GetInstance().Kernel().GetThreadManager().GetCurrentThread();
Core::CPU().SaveContext(thread->context); Core::CPU().SaveContext(thread->context);
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) { if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false; last_bkpt_hit = false;

View File

@ -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, // 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 // 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"); LOG_TRACE(Core_ARM11, "Idling");
CoreTiming::Idle(); timing->Idle();
CoreTiming::Advance(); timing->Advance();
PrepareReschedule(); PrepareReschedule();
} else { } else {
CoreTiming::Advance(); timing->Advance();
if (tight_loop) { if (tight_loop) {
cpu_core->Run(); cpu_core->Run();
} else { } else {
@ -126,7 +126,9 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
return init_result; 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) { if (Loader::ResultStatus::Success != load_result) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<u32>(load_result)); LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<u32>(load_result));
System::Shutdown(); System::Shutdown();
@ -140,7 +142,7 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
return ResultStatus::ErrorLoader; return ResultStatus::ErrorLoader;
} }
} }
Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); Memory::SetCurrentPageTable(&kernel->GetCurrentProcess()->vm_manager.page_table);
status = ResultStatus::Success; status = ResultStatus::Success;
m_emu_window = &emu_window; m_emu_window = &emu_window;
m_filepath = filepath; m_filepath = filepath;
@ -153,7 +155,7 @@ void System::PrepareReschedule() {
} }
PerfStats::Results System::GetAndResetPerfStats() { PerfStats::Results System::GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs()); return perf_stats.GetAndResetStats(timing->GetGlobalTimeUs());
} }
void System::Reschedule() { void System::Reschedule() {
@ -162,17 +164,17 @@ void System::Reschedule() {
} }
reschedule_pending = false; reschedule_pending = false;
Kernel::Reschedule(); kernel->GetThreadManager().Reschedule();
} }
System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) { System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) {
LOG_DEBUG(HW_Memory, "initialized OK"); LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init(); timing = std::make_unique<Timing>();
if (Settings::values.use_cpu_jit) { if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64 #ifdef ARCHITECTURE_x86_64
cpu_core = std::make_unique<ARM_Dynarmic>(USER32MODE); cpu_core = std::make_unique<ARM_Dynarmic>(*this, USER32MODE);
#else #else
cpu_core = std::make_unique<ARM_DynCom>(USER32MODE); cpu_core = std::make_unique<ARM_DynCom>(USER32MODE);
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); 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>(); rpc_server = std::make_unique<RPC::RPCServer>();
#endif #endif
service_manager = std::make_shared<Service::SM::ServiceManager>(); service_manager = std::make_shared<Service::SM::ServiceManager>(*this);
shared_page_handler = std::make_shared<SharedPage::Handler>(); archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
archive_manager = std::make_unique<Service::FS::ArchiveManager>();
HW::Init(); HW::Init();
Kernel::Init(system_mode); kernel = std::make_unique<Kernel::KernelSystem>(system_mode);
Service::Init(*this, service_manager); Service::Init(*this);
GDBStub::Init(); GDBStub::Init();
ResultStatus result = VideoCore::Init(emu_window); ResultStatus result = VideoCore::Init(emu_window);
@ -230,6 +231,22 @@ const Service::FS::ArchiveManager& System::ArchiveManager() const {
return *archive_manager; 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) { void System::RegisterSoftwareKeyboard(std::shared_ptr<Frontend::SoftwareKeyboard> swkbd) {
registered_swkbd = std::move(swkbd); registered_swkbd = std::move(swkbd);
} }
@ -247,8 +264,7 @@ void System::Shutdown() {
// Shutdown emulation session // Shutdown emulation session
GDBStub::Shutdown(); GDBStub::Shutdown();
VideoCore::Shutdown(); VideoCore::Shutdown();
Service::Shutdown(); kernel.reset();
Kernel::Shutdown();
HW::Shutdown(); HW::Shutdown();
telemetry_session.reset(); telemetry_session.reset();
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
@ -257,7 +273,7 @@ void System::Shutdown() {
service_manager.reset(); service_manager.reset();
dsp_core.reset(); dsp_core.reset();
cpu_core.reset(); cpu_core.reset();
CoreTiming::Shutdown(); timing.reset();
app_loader.reset(); app_loader.reset();
if (auto room_member = Network::GetRoomMember().lock()) { if (auto room_member = Network::GetRoomMember().lock()) {

View File

@ -8,7 +8,6 @@
#include <string> #include <string>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/frontend/applets/swkbd.h" #include "core/frontend/applets/swkbd.h"
#include "core/hle/shared_page.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/perf_stats.h" #include "core/perf_stats.h"
@ -36,8 +35,14 @@ class ArchiveManager;
} }
} // namespace Service } // namespace Service
namespace Kernel {
class KernelSystem;
}
namespace Core { namespace Core {
class Timing;
class System { class System {
public: public:
/** /**
@ -167,6 +172,18 @@ public:
/// Gets a const reference to the archive manager /// Gets a const reference to the archive manager
const Service::FS::ArchiveManager& ArchiveManager() const; 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; PerfStats perf_stats;
FrameLimiter frame_limiter; FrameLimiter frame_limiter;
@ -193,10 +210,6 @@ public:
return registered_swkbd; return registered_swkbd;
} }
std::shared_ptr<SharedPage::Handler> GetSharedPageHandler() const {
return shared_page_handler;
}
private: private:
/** /**
* Initialize the emulated system. * Initialize the emulated system.
@ -236,11 +249,14 @@ private:
std::unique_ptr<RPC::RPCServer> rpc_server; std::unique_ptr<RPC::RPCServer> rpc_server;
#endif #endif
/// Shared Page
std::shared_ptr<SharedPage::Handler> shared_page_handler;
std::unique_ptr<Service::FS::ArchiveManager> archive_manager; 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; static System s_instance;
ResultStatus status = ResultStatus::Success; ResultStatus status = ResultStatus::Success;

View File

@ -2,75 +2,25 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "core/core_timing.h"
#include <algorithm> #include <algorithm>
#include <cinttypes> #include <cinttypes>
#include <mutex>
#include <string>
#include <tuple> #include <tuple>
#include <unordered_map>
#include <vector>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/thread.h" #include "core/core_timing.h"
#include "common/threadsafe_queue.h"
namespace CoreTiming { namespace Core {
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;
};
// Sort by time, unless the times are the same, in which case sort by the order added to the queue // 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) { bool Timing::Event::operator>(const Event& right) const {
return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order); return std::tie(time, fifo_order) > std::tie(right.time, right.fifo_order);
} }
static bool operator<(const Event& left, const Event& right) { bool Timing::Event::operator<(const Event& right) const {
return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order); 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 TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback callback) {
// 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) {
// check for existing type with same name. // check for existing type with same name.
// we want event type names to remain unique so that we can use them for serialization. // 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(), 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.", "during Init to avoid breaking save states.",
name); name);
auto info = event_types.emplace(name, EventType{callback, nullptr}); auto info = event_types.emplace(name, TimingEventType{callback, nullptr});
EventType* event_type = &info.first->second; TimingEventType* event_type = &info.first->second;
event_type->name = &info.first->first; event_type->name = &info.first->first;
return event_type; return event_type;
} }
void UnregisterAllEvents() { Timing::~Timing() {
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() {
MoveEvents(); MoveEvents();
ClearPendingEvents();
UnregisterAllEvents();
} }
// This should only be called from the CPU thread. If you are calling u64 Timing::GetTicks() const {
// it from any other thread, you are doing something evil
u64 GetTicks() {
u64 ticks = static_cast<u64>(global_timer); u64 ticks = static_cast<u64>(global_timer);
if (!is_global_timer_sane) { if (!is_global_timer_sane) {
ticks += slice_length - downcount; ticks += slice_length - downcount;
@ -121,19 +46,16 @@ u64 GetTicks() {
return ticks; return ticks;
} }
void AddTicks(u64 ticks) { void Timing::AddTicks(u64 ticks) {
downcount -= ticks; downcount -= ticks;
} }
u64 GetIdleTicks() { u64 Timing::GetIdleTicks() const {
return static_cast<u64>(idled_cycles); return static_cast<u64>(idled_cycles);
} }
void ClearPendingEvents() { void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type,
event_queue.clear(); u64 userdata) {
}
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
ASSERT(event_type != nullptr); ASSERT(event_type != nullptr);
s64 timeout = GetTicks() + cycles_into_future; 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<>()); 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}); 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) { auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type == event_type && e.userdata == userdata; 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(), auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
[&](const Event& e) { return e.type == event_type; }); [&](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(); MoveEvents();
RemoveEvent(event_type); RemoveEvent(event_type);
} }
void ForceExceptionCheck(s64 cycles) { void Timing::ForceExceptionCheck(s64 cycles) {
cycles = std::max<s64>(0, cycles); cycles = std::max<s64>(0, cycles);
if (downcount > cycles) { if (downcount > cycles) {
slice_length -= 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);) { for (Event ev; ts_queue.Pop(ev);) {
ev.fifo_order = event_fifo_id++; ev.fifo_order = event_fifo_id++;
event_queue.emplace_back(std::move(ev)); event_queue.emplace_back(std::move(ev));
@ -193,7 +116,7 @@ void MoveEvents() {
} }
} }
void Advance() { void Timing::Advance() {
MoveEvents(); MoveEvents();
s64 cycles_executed = slice_length - downcount; s64 cycles_executed = slice_length - downcount;
@ -220,17 +143,17 @@ void Advance() {
downcount = slice_length; downcount = slice_length;
} }
void Idle() { void Timing::Idle() {
idled_cycles += downcount; idled_cycles += downcount;
downcount = 0; downcount = 0;
} }
std::chrono::microseconds GetGlobalTimeUs() { std::chrono::microseconds Timing::GetGlobalTimeUs() const {
return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE_ARM11}; return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE_ARM11};
} }
s64 GetDowncount() { s64 Timing::GetDowncount() const {
return downcount; return downcount;
} }
} // namespace CoreTiming } // namespace Core

View File

@ -21,8 +21,11 @@
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <string> #include <string>
#include <unordered_map>
#include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/threadsafe_queue.h"
// The timing we get from the assembly is 268,111,855.956 Hz // 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 // 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; return cycles * 1000 / BASE_CLOCK_RATE_ARM11;
} }
namespace CoreTiming { namespace Core {
struct EventType;
using TimedCallback = std::function<void(u64 userdata, int cycles_late)>; using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
/** struct TimingEventType {
* CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is TimedCallback callback;
* required to end slice -1 and start slice 0 before the first cycle of code is executed. const std::string* name;
*/ };
void Init();
void Shutdown();
/** class Timing {
* This should only be called from the emu thread, if you are calling it any other thread, you are public:
* doing something evil ~Timing();
*/
u64 GetTicks();
u64 GetIdleTicks();
void AddTicks(u64 ticks);
/** /**
* Returns the event_type identifier. if name is not unique, it will assert. * This should only be called from the emu thread, if you are calling it any other thread, you
*/ * are doing something evil
EventType* RegisterEvent(const std::string& name, TimedCallback callback); */
void UnregisterAllEvents(); 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 * Returns the event_type identifier. if name is not unique, it will assert.
* is scheduled earlier than the current values. */
* Scheduling from a callback will not update the downcount until the Advance() completes. TimingEventType* RegisterEvent(const std::string& name, TimedCallback callback);
*/
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
/** /**
* This is to be called when outside of hle threads, such as the graphics thread, wants to * After the first Advance, the slice lengths and the downcount will be reduced whenever an
* schedule things to be executed on the main thread. * event is scheduled earlier than the current values. Scheduling from a callback will not
* Not that this doesn't change slice_length and thus events scheduled by this might be called * update the downcount until the Advance() completes.
* with a delay of up to MAX_SLICE_LENGTH */
*/ void ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, u64 userdata = 0);
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata);
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 UnscheduleEvent(const TimingEventType* event_type, u64 userdata);
void RemoveEvent(const EventType* event_type);
void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends /// We only permit one event of each type in the queue at a time.
* the previous timing slice and begins the next one, you must Advance from the previous void RemoveEvent(const TimingEventType* event_type);
* slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an void RemoveNormalAndThreadsafeEvent(const TimingEventType* event_type);
* Advance() is required to initialize the slice length before the first cycle of emulated
* instructions is executed.
*/
void Advance();
void MoveEvents();
/// Pretend that the main CPU has executed enough cycles to reach the next event. /** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
void Idle(); * 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. /// Pretend that the main CPU has executed enough cycles to reach the next event.
void ClearPendingEvents(); 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

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <utility> #include <utility>
#include "core/core.h"
#include "core/file_sys/archive_savedata.h" #include "core/file_sys/archive_savedata.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
@ -16,16 +17,19 @@ ArchiveFactory_SaveData::ArchiveFactory_SaveData(
: sd_savedata_source(std::move(sd_savedata)) {} : sd_savedata_source(std::move(sd_savedata)) {}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) { 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, ResultCode ArchiveFactory_SaveData::Format(const Path& path,
const FileSys::ArchiveFormatInfo& format_info) { 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 { 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 } // namespace FileSys

View File

@ -7,6 +7,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/archive_selfncch.h" #include "core/file_sys/archive_selfncch.h"
#include "core/file_sys/errors.h" #include "core/file_sys/errors.h"
#include "core/file_sys/ivfc_archive.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) { ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) {
auto archive = std::make_unique<SelfNCCHArchive>( 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)); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
} }

View 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

View File

@ -4,10 +4,14 @@
#pragma once #pragma once
#include <cstddef>
#include "common/common_types.h"
namespace FileSys { namespace FileSys {
class DelayGenerator { class DelayGenerator {
public: public:
virtual ~DelayGenerator();
virtual u64 GetReadDelayNs(std::size_t length) = 0; virtual u64 GetReadDelayNs(std::size_t length) = 0;
// TODO (B3N30): Add getter for all other file/directory io operations // TODO (B3N30): Add getter for all other file/directory io operations
@ -15,15 +19,7 @@ public:
class DefaultDelayGenerator : public DelayGenerator { class DefaultDelayGenerator : public DelayGenerator {
public: public:
u64 GetReadDelayNs(std::size_t length) override { 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;
}
}; };
} // namespace FileSys } // namespace FileSys

View File

@ -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.data(), key_y_primary.size());
std::memcpy(input.data() + key_y_primary.size(), seed.data(), seed.size()); std::memcpy(input.data() + key_y_primary.size(), seed.data(), seed.size());
CryptoPP::SHA256 sha; 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());
} }
} }

View File

@ -34,7 +34,7 @@ bool SeedDB::Load() {
LOG_ERROR(Service_FS, "Failed to read seed database count fully"); LOG_ERROR(Service_FS, "Failed to read seed database count fully");
return false; 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"); LOG_ERROR(Service_FS, "Failed to skip seed database padding");
return false; return false;
} }

View File

@ -160,7 +160,7 @@ BreakpointMap breakpoints_write;
} // Anonymous namespace } // Anonymous namespace
static Kernel::Thread* FindThreadById(int id) { static Kernel::Thread* FindThreadById(int id) {
const auto& threads = Kernel::GetThreadList(); const auto& threads = Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList();
for (auto& thread : threads) { for (auto& thread : threads) {
if (thread->GetThreadId() == static_cast<u32>(id)) { if (thread->GetThreadId() == static_cast<u32>(id)) {
return thread.get(); return thread.get();
@ -535,7 +535,8 @@ static void HandleQuery() {
SendReply(target_xml); SendReply(target_xml);
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
std::string val = "m"; std::string val = "m";
const auto& threads = Kernel::GetThreadList(); const auto& threads =
Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList();
for (const auto& thread : threads) { for (const auto& thread : threads) {
val += fmt::format("{:x},", thread->GetThreadId()); val += fmt::format("{:x},", thread->GetThreadId());
} }
@ -547,7 +548,8 @@ static void HandleQuery() {
std::string buffer; std::string buffer;
buffer += "l<?xml version=\"1.0\"?>"; buffer += "l<?xml version=\"1.0\"?>";
buffer += "<threads>"; buffer += "<threads>";
const auto& threads = Kernel::GetThreadList(); const auto& threads =
Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList();
for (const auto& thread : threads) { for (const auto& thread : threads) {
buffer += fmt::format(R"*(<thread id="{:x}" name="Thread {:x}"></thread>)*", buffer += fmt::format(R"*(<thread id="{:x}" name="Thread {:x}"></thread>)*",
thread->GetThreadId(), thread->GetThreadId()); thread->GetThreadId(), thread->GetThreadId());

View File

@ -8,6 +8,7 @@
#include <unordered_map> #include <unordered_map>
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/applets/applet.h" #include "core/hle/applets/applet.h"
#include "core/hle/applets/erreula.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; static std::unordered_map<Service::APT::AppletId, std::shared_ptr<Applet>> applets;
/// The CoreTiming event identifier for the Applet update callback. /// 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 /// The interval at which the Applet update callback will be called, 16.6ms
static const u64 applet_update_interval_us = 16666; 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 the applet is still running after the last update, reschedule the event
if (applet->IsRunning()) { if (applet->IsRunning()) {
CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us) - cycles_late, Core::System::GetInstance().CoreTiming().ScheduleEvent(
applet_update_event, applet_id); usToCycles(applet_update_interval_us) - cycles_late, applet_update_event, applet_id);
} else { } else {
// Otherwise the applet has terminated, in which case we should clean it up // Otherwise the applet has terminated, in which case we should clean it up
applets[id] = nullptr; applets[id] = nullptr;
@ -101,8 +102,8 @@ ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter)
if (result.IsError()) if (result.IsError())
return result; return result;
// Schedule the update event // Schedule the update event
CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us), applet_update_event, Core::System::GetInstance().CoreTiming().ScheduleEvent(
static_cast<u64>(id)); usToCycles(applet_update_interval_us), applet_update_event, static_cast<u64>(id));
return result; return result;
} }
@ -128,11 +129,12 @@ bool IsLibraryAppletRunning() {
void Init() { void Init() {
// Register the applet update callback // 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() { void Shutdown() {
CoreTiming::RemoveEvent(applet_update_event); Core::System::GetInstance().CoreTiming().RemoveEvent(applet_update_event);
} }
} // namespace Applets } // namespace Applets
} // namespace HLE } // namespace HLE

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h"
#include "core/hle/applets/erreula.h" #include "core/hle/applets/erreula.h"
#include "core/hle/service/apt/apt.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 // TODO: allocated memory never released
using Kernel::MemoryPermission; 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. // Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet( framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
"ErrEula Memory"); "ErrEula Memory");
// Send the response message with the newly created SharedMemory // Send the response message with the newly created SharedMemory

View File

@ -7,6 +7,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h"
#include "core/hle/applets/mii_selector.h" #include "core/hle/applets/mii_selector.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.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)); memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
using Kernel::MemoryPermission; 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. // Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet( framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
"MiiSelector Memory"); "MiiSelector Memory");
// Send the response message with the newly created SharedMemory // Send the response message with the newly created SharedMemory

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h"
#include "core/hle/applets/mint.h" #include "core/hle/applets/mint.h"
#include "core/hle/service/apt/apt.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 // TODO: allocated memory never released
using Kernel::MemoryPermission; 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. // Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet( framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
"Mint Memory"); "Mint Memory");
// Send the response message with the newly created SharedMemory // Send the response message with the newly created SharedMemory

View File

@ -8,6 +8,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h"
#include "core/hle/applets/swkbd.h" #include "core/hle/applets/swkbd.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.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)); memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
using Kernel::MemoryPermission; 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. // Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet( framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
"SoftwareKeyboard Memory"); "SoftwareKeyboard Memory");
// Send the response message with the newly created SharedMemory // Send the response message with the newly created SharedMemory

View File

@ -9,27 +9,6 @@
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/memory.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 { namespace IPC {
/// Size of the command buffer area, in 32-bit words. /// Size of the command buffer area, in 32-bit words.

View File

@ -7,6 +7,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/memory.h" #include "core/memory.h"
@ -64,11 +65,11 @@ SharedPtr<Thread> AddressArbiter::ResumeHighestPriorityThread(VAddr address) {
return thread; return thread;
} }
AddressArbiter::AddressArbiter() {} AddressArbiter::AddressArbiter(KernelSystem& kernel) : Object(kernel) {}
AddressArbiter::~AddressArbiter() {} AddressArbiter::~AddressArbiter() {}
SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) { SharedPtr<AddressArbiter> KernelSystem::CreateAddressArbiter(std::string name) {
SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter); SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter(*this));
address_arbiter->name = std::move(name); address_arbiter->name = std::move(name);

View File

@ -31,14 +31,6 @@ enum class ArbitrationType : u32 {
class AddressArbiter final : public Object { class AddressArbiter final : public Object {
public: 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 { std::string GetTypeName() const override {
return "Arbiter"; return "Arbiter";
} }
@ -57,7 +49,7 @@ public:
s32 value, u64 nanoseconds); s32 value, u64 nanoseconds);
private: private:
AddressArbiter(); explicit AddressArbiter(KernelSystem& kernel);
~AddressArbiter() override; ~AddressArbiter() override;
/// Puts the thread to wait on the specified arbitration address under this address arbiter. /// 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. /// Threads waiting for the address arbiter to be signaled.
std::vector<SharedPtr<Thread>> waiting_threads; std::vector<SharedPtr<Thread>> waiting_threads;
friend class KernelSystem;
}; };
} // namespace Kernel } // namespace Kernel

View File

@ -13,7 +13,7 @@
namespace Kernel { namespace Kernel {
ClientPort::ClientPort() = default; ClientPort::ClientPort(KernelSystem& kernel) : kernel(kernel), Object(kernel) {}
ClientPort::~ClientPort() = default; ClientPort::~ClientPort() = default;
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
@ -26,7 +26,7 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
active_sessions++; active_sessions++;
// Create a new session pair, let the created sessions inherit the parent port's HLE handler. // 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) if (server_port->hle_handler)
server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));

View File

@ -48,13 +48,16 @@ public:
void ConnectionClosed(); void ConnectionClosed();
private: private:
ClientPort(); explicit ClientPort(KernelSystem& kernel);
~ClientPort() override; ~ClientPort() override;
KernelSystem& kernel;
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port. SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have 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 u32 active_sessions = 0; ///< Number of currently open sessions to this port
std::string name; ///< Name of client port (optional) std::string name; ///< Name of client port (optional)
friend class KernelSystem;
}; };
} // namespace Kernel } // namespace Kernel

View File

@ -13,7 +13,7 @@
namespace Kernel { namespace Kernel {
ClientSession::ClientSession() = default; ClientSession::ClientSession(KernelSystem& kernel) : Object(kernel) {}
ClientSession::~ClientSession() { ClientSession::~ClientSession() {
// This destructor will be called automatically when the last ClientSession handle is closed by // This destructor will be called automatically when the last ClientSession handle is closed by
// the emulated application. // the emulated application.

View File

@ -12,13 +12,12 @@
namespace Kernel { namespace Kernel {
class ServerSession;
class Session; class Session;
class Thread; class Thread;
class ClientSession final : public Object { class ClientSession final : public Object {
public: public:
friend class ServerSession; friend class KernelSystem;
std::string GetTypeName() const override { std::string GetTypeName() const override {
return "ClientSession"; return "ClientSession";
@ -46,7 +45,7 @@ public:
std::shared_ptr<Session> parent; std::shared_ptr<Session> parent;
private: private:
ClientSession(); explicit ClientSession(KernelSystem& kernel);
~ClientSession() override; ~ClientSession() override;
}; };

View File

@ -3,15 +3,13 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <cstring> #include <cstring>
#include "core/hle/config_mem.h" #include "core/hle/kernel/config_mem.h"
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
namespace ConfigMem { namespace ConfigMem {
ConfigMemDef config_mem; Handler::Handler() {
void Init() {
std::memset(&config_mem, 0, sizeof(config_mem)); std::memset(&config_mem, 0, sizeof(config_mem));
// Values extracted from firmware 11.2.0-35E // Values extracted from firmware 11.2.0-35E
@ -28,4 +26,8 @@ void Init() {
config_mem.firm_ctr_sdk_ver = 0x0000F297; config_mem.firm_ctr_sdk_ver = 0x0000F297;
} }
ConfigMemDef& Handler::GetConfigMem() {
return config_mem;
}
} // namespace ConfigMem } // namespace ConfigMem

View File

@ -49,8 +49,13 @@ struct ConfigMemDef {
static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE, static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE,
"Config Memory structure size is wrong"); "Config Memory structure size is wrong");
extern ConfigMemDef config_mem; class Handler {
public:
Handler();
ConfigMemDef& GetConfigMem();
void Init(); private:
ConfigMemDef config_mem;
};
} // namespace ConfigMem } // namespace ConfigMem

View File

@ -67,6 +67,10 @@ constexpr ResultCode ERR_MISALIGNED_SIZE(ErrorDescription::MisalignedSize, Error
constexpr ResultCode ERR_OUT_OF_MEMORY(ErrorDescription::OutOfMemory, ErrorModule::Kernel, constexpr ResultCode ERR_OUT_OF_MEMORY(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
ErrorSummary::OutOfResource, ErrorSummary::OutOfResource,
ErrorLevel::Permanent); // 0xD86007F3 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, constexpr ResultCode ERR_NOT_IMPLEMENTED(ErrorDescription::NotImplemented, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorSummary::InvalidArgument,
ErrorLevel::Usage); // 0xE0E01BF4 ErrorLevel::Usage); // 0xE0E01BF4

View File

@ -7,16 +7,16 @@
#include <vector> #include <vector>
#include "common/assert.h" #include "common/assert.h"
#include "core/hle/kernel/event.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" #include "core/hle/kernel/thread.h"
namespace Kernel { namespace Kernel {
Event::Event() {} Event::Event(KernelSystem& kernel) : WaitObject(kernel) {}
Event::~Event() {} Event::~Event() {}
SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) { SharedPtr<Event> KernelSystem::CreateEvent(ResetType reset_type, std::string name) {
SharedPtr<Event> evt(new Event); SharedPtr<Event> evt(new Event(*this));
evt->signaled = false; evt->signaled = false;
evt->reset_type = reset_type; evt->reset_type = reset_type;

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