citra-emu
/
citra-canary
Archived
1
0
Fork 0

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
*.pyc
# Flatpak generated files
.flatpak-builder/
repo/

View File

@ -61,6 +61,18 @@ matrix:
script: "./.travis/linux-mingw/build.sh"
after_success: "./.travis/linux-mingw/upload.sh"
cache: ccache
- if: repo =~ ^.*\/(citra-canary|citra-nightly)$ AND tag IS present
git:
depth: false
os: linux
env: NAME="flatpak build"
sudo: required
dist: trusty
services: docker
cache: ccache
install: "./.travis/linux-flatpak/deps.sh"
script: "./.travis/linux-flatpak/build.sh"
after_script: "./.travis/linux-flatpak/finish.sh"
deploy:
provider: releases

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

View File

@ -1,137 +1 @@
# Reporting Issues
**The issue tracker is not a support forum.** Unless you can provide precise *technical information* regarding an issue, you *should not post in it*. If you need support, first read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) and then either visit our IRC or [Discord](https://citra-emu.org/discord/) channel, [our forum](https://community.citra-emu.org) or ask in a general emulation forum such as [/r/emulation](https://www.reddit.com/r/emulation/). If you post support questions, generic messages to the developers or vague reports without technical details, they will be closed and locked.
If you believe you have a valid issue report, please post text or a screenshot from the log (the console window that opens alongside Citra) and build version (hex string visible in the titlebar and zip filename), as well as your hardware and software information if applicable.
# Contributing
Citra is a brand new project, so we have a great opportunity to keep things clean and well organized early on. As such, coding style is very important when making commits. We run clang-format on our CI to check the code. Please use it to format your code when contributing. However, it doesn't cover all the rules below. Some of them aren't very strict rules since we want to be flexible and we understand that under certain circumstances some of them can be counterproductive. Just try to follow as many of them as possible.
# Using clang format (version 6.0)
When generating the native build script for your toolset, cmake will try to find the correct version of clang format (or will download it on windows). Before running cmake, please install clang format version 6.0 for your platform as follows:
* Windows: do nothing; cmake will download a pre built binary for MSVC and MINGW. MSVC users can additionally install a clang format Visual Studio extension to add features like format on save.
* OSX: run `brew install clang-format`.
* Linux: use your package manager to get an appropriate binary.
If clang format is found, then cmake will add a custom build target that can be run at any time to run clang format against *all* source files and update the formatting in them. This should be used before making a pull request so that the reviewers can spend more time reviewing the code instead of having to worry about minor style violations. On MSVC, you can run clang format by building the clang-format project in the solution. On OSX, you can either use the Makefile target `make clang-format` or by building the clang-format target in XCode. For Makefile builds, you can use the clang-format target with `make clang-format`
### General Rules
* A lot of code was taken from other projects (e.g. Dolphin, PPSSPP, Gekko, SkyEye). In general, when editing other people's code, follow the style of the module you're in (or better yet, fix the style if it drastically differs from our guide).
* Line width is typically 100 characters. Please do not use 80-characters.
* Don't ever introduce new external dependencies into Core
* Don't use any platform specific code in Core
* Use namespaces often
* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`.
### Naming Rules
* Functions: `PascalCase`
* Variables: `lower_case_underscored`. Prefix with `g_` if global.
* Classes: `PascalCase`
* Files and Directories: `lower_case_underscored`
* Namespaces: `PascalCase`, `_` may also be used for clarity (e.g. `ARM_InitCore`)
### Indentation/Whitespace Style
Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead.
### Comments
* For regular comments, use C++ style (`//`) comments, even for multi-line ones.
* For doc-comments (Doxygen comments), use `/// ` if it's a single line, else use the `/**` `*/` style featured in the example. Start the text on the second line, not the first containing `/**`.
* For items that are both defined and declared in two separate files, put the doc-comment only next to the associated declaration. (In a header file, usually.) Otherwise, put it next to the implementation. Never duplicate doc-comments in both places.
```cpp
// Includes should be sorted lexicographically
// STD includes first
#include <map>
#include <memory>
// then, library includes
#include <nihstro/shared_binary.h>
// finally, citra includes
#include "common/math_util.h"
#include "common/vector_math.h"
// each major module is separated
#include "video_core/pica.h"
#include "video_core/video_core.h"
namespace Example {
// Namespace contents are not indented
// Declare globals at the top
int g_foo{}; // {} can be used to initialize types as 0, false, or nullptr
char* g_some_pointer{}; // Pointer * and reference & stick to the type name, and make sure to initialize as nullptr!
/// A colorful enum.
enum SomeEnum {
ColorRed, ///< The color of fire.
ColorGreen, ///< The color of grass.
ColorBlue, ///< Not actually the color of water.
};
/**
* Very important struct that does a lot of stuff.
* Note that the asterisks are indented by one space to align to the first line.
*/
struct Position {
int x{}, y{}; // Always intitialize member variables!
};
// Use "typename" rather than "class" here
template <typename T>
void FooBar() {
const std::string some_string{ "prefer uniform initialization" };
int some_array[]{
5,
25,
7,
42,
};
if (note == the_space_after_the_if) {
CallAfunction();
} else {
// Use a space after the // when commenting
}
// Place a single space after the for loop semicolons, prefer pre-increment
for (int i{}; i != 25; ++i) {
// This is how we write loops
}
DoStuff(this, function, call, takes, up, multiple,
lines, like, this);
if (this || condition_takes_up_multiple &&
lines && like && this || everything ||
alright || then) {
// Leave a blank space before the if block body if the condition was continued across
// several lines.
}
switch (var) {
// No indentation for case label
case 1: {
int case_var{ var + 3 };
DoSomething(case_var);
break;
}
case 3:
DoSomething(var);
return;
default:
// Yes, even break for the last case
break;
}
std::vector<T> you_can_declare, a_few, variables, like_this;
}
}
```
**The Contributor's Guide has moved to [the wiki](https://github.com/citra-emu/citra/wiki/Contributing).**

File diff suppressed because it is too large Load Diff

1045
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

877
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

1296
dist/languages/id.ts vendored

File diff suppressed because it is too large Load Diff

1123
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

1138
dist/languages/nb.ts vendored

File diff suppressed because it is too large Load Diff

2183
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

1201
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

1033
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 struct
import random
import binascii
import enum
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 device{
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
return target_device_name == info.friendly_name;
return info.friendly_name != nullptr &&
target_device_name == info.friendly_name;
})};
if (device != collection_end) {
output_device = device->devid;

View File

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

View File

@ -14,6 +14,8 @@ add_executable(citra-qt
applets/swkbd.h
bootmanager.cpp
bootmanager.h
compatibility_list.cpp
compatibility_list.h
camera/camera_util.cpp
camera/camera_util.h
camera/still_image_camera.cpp
@ -76,6 +78,8 @@ add_executable(citra-qt
game_list.cpp
game_list.h
game_list_p.h
game_list_worker.cpp
game_list_worker.h
hotkeys.cpp
hotkeys.h
main.cpp
@ -136,15 +140,15 @@ set(UIS
)
file(GLOB COMPAT_LIST
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*)
file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*)
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*)
file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
qt5_wrap_ui(UI_HDRS ${UIS})
if (ENABLE_QT_TRANSLATION)
set(CITRA_QT_LANGUAGES "${CMAKE_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
set(CITRA_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
# Update source TS file if enabled
@ -209,7 +213,7 @@ target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Op
target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
if (CITRA_ENABLE_COMPATIBILITY_REPORTING)
add_definitions(-DCITRA_ENABLE_COMPATIBILITY_REPORTING)
target_compile_definitions(citra-qt PRIVATE -DCITRA_ENABLE_COMPATIBILITY_REPORTING)
endif()
if (USE_DISCORD_PRESENCE)

View File

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

View File

@ -5,6 +5,7 @@
#include <QButtonGroup>
#include <QMessageBox>
#include <QPushButton>
#include <QtConcurrent/qtconcurrentrun.h>
#include "citra_qt/compatdb.h"
#include "common/telemetry.h"
#include "core/core.h"
@ -21,6 +22,8 @@ CompatDB::CompatDB(QWidget* parent)
connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext);
connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext);
connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);
connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,
&CompatDB::OnTestcaseSubmitted);
}
CompatDB::~CompatDB() = default;
@ -46,18 +49,38 @@ void CompatDB::Submit() {
}
break;
case CompatDBPage::Final:
back();
LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility",
compatibility->checkedId());
// older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a
// workaround
button(NextButton)->setEnabled(false);
button(NextButton)->setText(tr("Submitting"));
button(QWizard::CancelButton)->setVisible(false);
testcase_watcher.setFuture(QtConcurrent::run(
[this]() { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); }));
break;
default:
LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
}
}
void CompatDB::OnTestcaseSubmitted() {
if (!testcase_watcher.result()) {
QMessageBox::critical(this, tr("Communication error"),
tr("An error occured while sending the Testcase"));
button(NextButton)->setEnabled(true);
button(NextButton)->setText(tr("Next"));
button(QWizard::CancelButton)->setVisible(true);
} else {
next();
// older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a
// workaround
button(QWizard::CancelButton)->setVisible(false);
}
}
void CompatDB::EnableNext() {
button(NextButton)->setEnabled(true);
}

View File

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

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.
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
FileUtil::CreateFullPath(qt_config_loc);
qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
qt_config =
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
Reload();
}
Config::~Config() {
Save();
}
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_1, Qt::Key_2, Qt::Key_B,
@ -561,9 +566,3 @@ void Config::Reload() {
void Config::Save() {
SaveValues();
}
Config::~Config() {
Save();
delete qt_config;
}

View File

@ -5,6 +5,7 @@
#pragma once
#include <array>
#include <memory>
#include <string>
#include <QVariant>
#include "core/settings.h"
@ -12,16 +13,6 @@
class QSettings;
class Config {
QSettings* qt_config;
std::string qt_config_loc;
void ReadValues();
void SaveValues();
QVariant ReadSetting(const QString& name);
QVariant ReadSetting(const QString& name, const QVariant& default_value);
void WriteSetting(const QString& name, const QVariant& value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
public:
Config();
~Config();
@ -31,4 +22,15 @@ public:
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
private:
void ReadValues();
void SaveValues();
QVariant ReadSetting(const QString& name);
QVariant ReadSetting(const QString& name, const QVariant& default_value);
void WriteSetting(const QString& name, const QVariant& value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
};

View File

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QMessageBox>
#include "citra_qt/configuration/configure_general.h"
#include "citra_qt/ui_settings.h"
#include "core/core.h"
@ -15,6 +16,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
this->setConfiguration();
ui->updateBox->setVisible(UISettings::values.updater_found);
connect(ui->button_reset_defaults, &QPushButton::clicked, this,
&ConfigureGeneral::ResetDefaults);
}
ConfigureGeneral::~ConfigureGeneral() = default;
@ -33,6 +36,19 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
ui->hotkeysDialog->Populate(registry);
}
void ConfigureGeneral::ResetDefaults() {
QMessageBox::StandardButton answer = QMessageBox::question(
this, tr("Citra"),
tr("Are you sure you want to <b>reset your settings</b> and close Citra?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (answer == QMessageBox::No)
return;
FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini");
std::exit(0);
}
void ConfigureGeneral::applyConfiguration() {
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,23 +30,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
switch (role) {
case Qt::DisplayRole: {
if (index.column() == 0) {
static const std::map<Pica::DebugContext::Event, QString> map = {
{Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded")},
{Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed")},
{Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")},
{Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")},
{Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation")},
{Pica::DebugContext::Event::IncomingDisplayTransfer,
tr("Incoming display transfer")},
{Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed")},
{Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped")},
};
DEBUG_ASSERT(map.size() ==
static_cast<std::size_t>(Pica::DebugContext::Event::NumEvents));
return (map.find(event) != map.end()) ? map.at(event) : QString();
return DebugContextEventToString(event);
}
break;
}
@ -128,6 +113,30 @@ void BreakPointModel::OnResumed() {
active_breakpoint = context->active_breakpoint;
}
QString BreakPointModel::DebugContextEventToString(Pica::DebugContext::Event event) {
switch (event) {
case Pica::DebugContext::Event::PicaCommandLoaded:
return tr("Pica command loaded");
case Pica::DebugContext::Event::PicaCommandProcessed:
return tr("Pica command processed");
case Pica::DebugContext::Event::IncomingPrimitiveBatch:
return tr("Incoming primitive batch");
case Pica::DebugContext::Event::FinishedPrimitiveBatch:
return tr("Finished primitive batch");
case Pica::DebugContext::Event::VertexShaderInvocation:
return tr("Vertex shader invocation");
case Pica::DebugContext::Event::IncomingDisplayTransfer:
return tr("Incoming display transfer");
case Pica::DebugContext::Event::GSPCommandProcessed:
return tr("GSP command processed");
case Pica::DebugContext::Event::BufferSwapped:
return tr("Buffers swapped");
case Pica::DebugContext::Event::NumEvents:
break;
}
return tr("Unknown debug context event");
}
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
: QDockWidget(tr("Pica Breakpoints"), parent), Pica::DebugContext::BreakPointObserver(

View File

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

View File

@ -51,7 +51,7 @@ std::size_t WaitTreeItem::Row() const {
}
std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() {
const auto& threads = Kernel::GetThreadList();
const auto& threads = Core::System::GetInstance().Kernel().GetThreadManager().GetThreadList();
std::vector<std::unique_ptr<WaitTreeThread>> item_list;
item_list.reserve(threads.size());
for (std::size_t i = 0; i < threads.size(); ++i) {

View File

@ -21,8 +21,10 @@
#include <QToolButton>
#include <QTreeView>
#include <fmt/format.h>
#include "citra_qt/compatibility_list.h"
#include "citra_qt/game_list.h"
#include "citra_qt/game_list_p.h"
#include "citra_qt/game_list_worker.h"
#include "citra_qt/main.h"
#include "citra_qt/ui_settings.h"
#include "common/common_paths.h"
@ -30,7 +32,6 @@
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/archive_source_sd_savedata.h"
#include "core/hle/service/fs/archive.h"
#include "core/loader/loader.h"
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {}
@ -288,11 +289,11 @@ GameList::GameList(GMainWindow* parent) : QWidget{parent} {
tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
item_model->insertColumns(0, COLUMN_COUNT);
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name");
item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility");
item_model->setHeaderData(COLUMN_REGION, Qt::Horizontal, "Region");
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
item_model->setHeaderData(COLUMN_REGION, Qt::Horizontal, tr("Region"));
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
item_model->setSortRole(GameListItemPath::TitleRole);
connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::onUpdateThemedIcons);
@ -648,11 +649,6 @@ void GameList::LoadInterfaceLayout() {
const QStringList GameList::supported_file_extensions = {"3ds", "3dsx", "elf", "axf",
"cci", "cxi", "app"};
static bool HasSupportedFileExtension(const std::string& file_name) {
QFileInfo file = QFileInfo(QString::fromStdString(file_name));
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
}
void GameList::RefreshGameDirectory() {
if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) {
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
@ -678,123 +674,6 @@ QString GameList::FindGameByProgramID(QStandardItem* current_item, u64 program_i
return "";
}
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion,
GameListDir* parent_dir) {
const auto callback = [this, recursion, parent_dir](u64* num_entries_out,
const std::string& directory,
const std::string& virtual_name) -> bool {
std::string physical_name = directory + DIR_SEP + virtual_name;
if (stop_processing)
return false; // Breaks the callback loop.
bool is_dir = FileUtil::IsDirectory(physical_name);
if (!is_dir && HasSupportedFileExtension(physical_name)) {
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
if (!loader)
return true;
u64 program_id = 0;
loader->ReadProgramId(program_id);
u64 extdata_id = 0;
loader->ReadExtdataId(extdata_id);
std::vector<u8> smdh = [program_id, &loader]() -> std::vector<u8> {
std::vector<u8> original_smdh;
loader->ReadIcon(original_smdh);
if (program_id < 0x0004000000000000 || program_id > 0x00040000FFFFFFFF)
return original_smdh;
std::string update_path = Service::AM::GetTitleContentPath(
Service::FS::MediaType::SDMC, program_id + 0x0000000E00000000);
if (!FileUtil::Exists(update_path))
return original_smdh;
std::unique_ptr<Loader::AppLoader> update_loader = Loader::GetLoader(update_path);
if (!update_loader)
return original_smdh;
std::vector<u8> update_smdh;
update_loader->ReadIcon(update_smdh);
return update_smdh;
}();
if (!Loader::IsValidSMDH(smdh) && UISettings::values.game_list_hide_no_icon) {
// Skip this invalid entry
return true;
}
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
// The game list uses this as compatibility number for untested games
QString compatibility("99");
if (it != compatibility_list.end())
compatibility = it->second.first;
emit EntryReady(
{
new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id,
extdata_id),
new GameListItemCompat(compatibility),
new GameListItemRegion(smdh),
new GameListItem(
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
new GameListItemSize(FileUtil::GetSize(physical_name)),
},
parent_dir);
} else if (is_dir && recursion > 0) {
watch_list.append(QString::fromStdString(physical_name));
AddFstEntriesToGameList(physical_name, recursion - 1, parent_dir);
}
return true;
};
FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
}
void GameListWorker::run() {
stop_processing = false;
for (UISettings::GameDir& game_dir : game_dirs) {
if (game_dir.path == "INSTALLED") {
QString path =
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)) +
"Nintendo "
"3DS/00000000000000000000000000000000/"
"00000000000000000000000000000000/title/00040000";
watch_list.append(path);
GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::InstalledDir);
emit DirEntryReady({game_list_dir});
AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir);
} else if (game_dir.path == "SYSTEM") {
QString path =
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)) +
"00000000000000000000000000000000/title/00040010";
watch_list.append(path);
GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir);
emit DirEntryReady({game_list_dir});
AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir);
} else {
watch_list.append(game_dir.path);
GameListDir* game_list_dir = new GameListDir(game_dir);
emit DirEntryReady({game_list_dir});
AddFstEntriesToGameList(game_dir.path.toStdString(), game_dir.deep_scan ? 256 : 0,
game_list_dir);
}
};
emit Finished(watch_list);
}
void GameListWorker::Cancel() {
this->disconnect();
stop_processing = true;
}
GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} {
this->main_window = parent;

View File

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

View File

@ -4,7 +4,6 @@
#pragma once
#include <atomic>
#include <map>
#include <unordered_map>
#include <utility>
@ -59,17 +58,6 @@ static QPixmap GetDefaultIcon(bool large) {
return icon;
}
static auto FindMatchingCompatibilityEntry(
const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list,
u64 program_id) {
return std::find_if(
compatibility_list.begin(), compatibility_list.end(),
[program_id](const std::pair<std::string, std::pair<QString, QString>>& element) {
std::string pid = fmt::format("{:016X}", program_id);
return element.first == pid;
});
}
/**
* Gets the short game title from SMDH data.
* @param smdh SMDH data
@ -216,7 +204,7 @@ class GameListItemCompat : public GameListItem {
public:
static const int CompatNumberRole = SortRole;
GameListItemCompat() = default;
explicit GameListItemCompat(const QString& compatiblity) {
explicit GameListItemCompat(const QString& compatibility) {
setData(type(), TypeRole);
struct CompatStatus {
@ -235,13 +223,13 @@ public:
{"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}};
// clang-format on
auto iterator = status_data.find(compatiblity);
auto iterator = status_data.find(compatibility);
if (iterator == status_data.end()) {
LOG_WARNING(Frontend, "Invalid compatibility number {}", compatiblity.toStdString());
LOG_WARNING(Frontend, "Invalid compatibility number {}", compatibility.toStdString());
return;
}
CompatStatus status = iterator->second;
setData(compatiblity, CompatNumberRole);
const CompatStatus& status = iterator->second;
setData(compatibility, CompatNumberRole);
setText(QObject::tr(status.text));
setToolTip(QObject::tr(status.tooltip));
setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole);
@ -373,51 +361,6 @@ public:
}
};
/**
* Asynchronous worker object for populating the game list.
* Communicates with other threads through Qt's signal/slot system.
*/
class GameListWorker : public QObject, public QRunnable {
Q_OBJECT
public:
explicit GameListWorker(
QList<UISettings::GameDir>& game_dirs,
const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list)
: game_dirs(game_dirs), compatibility_list(compatibility_list) {}
public slots:
/// Starts the processing of directory tree information.
void run() override;
/// Tells the worker that it should no longer continue processing. Thread-safe.
void Cancel();
signals:
/**
* The `EntryReady` signal is emitted once an entry has been prepared and is ready
* to be added to the game list.
* @param entry_items a list with `QStandardItem`s that make up the columns of the new
* entry.
*/
void DirEntryReady(GameListDir* entry_items);
void EntryReady(QList<QStandardItem*> entry_items, GameListDir* parent_dir);
/**
* After the worker has traversed the game directory looking for entries, this signal is
* emitted with a list of folders that should be watched for changes as well.
*/
void Finished(QStringList watch_list);
private:
QStringList watch_list;
const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list;
QList<UISettings::GameDir>& game_dirs;
std::atomic_bool stop_processing;
void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion,
GameListDir* parent_dir);
};
class GameList;
class QHBoxLayout;
class QTreeView;

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/still_image_camera.h"
#include "citra_qt/compatdb.h"
#include "citra_qt/compatibility_list.h"
#include "citra_qt/configuration/config.h"
#include "citra_qt/configuration/configure_dialog.h"
#include "citra_qt/debugger/console.h"
@ -44,6 +45,7 @@
#include "citra_qt/util/clickable_label.h"
#include "common/common_paths.h"
#include "common/detached_tasks.h"
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
@ -57,6 +59,7 @@
#include "core/frontend/applets/default_applets.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/loader/loader.h"
#include "core/movie.h"
#include "core/settings.h"
@ -341,8 +344,9 @@ void GMainWindow::InitializeHotkeys() {
hotkey_registry.RegisterHotkey("Main Window", "Start Emulation");
hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
hotkey_registry.RegisterHotkey("Main Window", "Restart", QKeySequence(Qt::Key_F5));
hotkey_registry.RegisterHotkey("Main Window", "Swap Screens", QKeySequence(tr("F9")));
hotkey_registry.RegisterHotkey("Main Window", "Toggle Screen Layout", QKeySequence(tr("F10")));
hotkey_registry.RegisterHotkey("Main Window", "Swap Screens", QKeySequence(Qt::Key_F9));
hotkey_registry.RegisterHotkey("Main Window", "Toggle Screen Layout",
QKeySequence(Qt::Key_F10));
hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
Qt::ApplicationShortcut);
@ -356,6 +360,11 @@ void GMainWindow::InitializeHotkeys() {
Qt::ApplicationShortcut);
hotkey_registry.RegisterHotkey("Main Window", "Advance Frame", QKeySequence(Qt::Key_Backslash),
Qt::ApplicationShortcut);
hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
Qt::ApplicationShortcut);
hotkey_registry.RegisterHotkey("Main Window", "Remove Amiibo", QKeySequence(Qt::Key_F3),
Qt::ApplicationShortcut);
hotkey_registry.LoadHotkeys();
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
@ -417,6 +426,18 @@ void GMainWindow::InitializeHotkeys() {
&QShortcut::activated, ui.action_Enable_Frame_Advancing, &QAction::trigger);
connect(hotkey_registry.GetHotkey("Main Window", "Advance Frame", this), &QShortcut::activated,
ui.action_Advance_Frame, &QAction::trigger);
connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated,
this, [&] {
if (ui.action_Load_Amiibo->isEnabled()) {
OnLoadAmiibo();
}
});
connect(hotkey_registry.GetHotkey("Main Window", "Remove Amiibo", this), &QShortcut::activated,
this, [&] {
if (ui.action_Remove_Amiibo->isEnabled()) {
OnRemoveAmiibo();
}
});
}
void GMainWindow::ShowUpdaterWidgets() {
@ -495,6 +516,8 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile);
connect(ui.action_Install_CIA, &QAction::triggered, this, &GMainWindow::OnMenuInstallCIA);
connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
connect(ui.action_Remove_Amiibo, &QAction::triggered, this, &GMainWindow::OnRemoveAmiibo);
// Emulation
connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
@ -667,12 +690,12 @@ bool GMainWindow::LoadROM(const QString& filename) {
render_window->InitRenderTarget();
render_window->MakeCurrent();
const char* below_gl33_title = "OpenGL 3.3 Unsupported";
const char* below_gl33_message = "Your GPU may not support OpenGL 3.3, or you do not "
"have the latest graphics driver.";
const QString below_gl33_title = tr("OpenGL 3.3 Unsupported");
const QString below_gl33_message = tr("Your GPU may not support OpenGL 3.3, or you do not "
"have the latest graphics driver.");
if (!gladLoadGL()) {
QMessageBox::critical(this, tr(below_gl33_title), tr(below_gl33_message));
QMessageBox::critical(this, below_gl33_title, below_gl33_message);
return false;
}
@ -743,7 +766,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
break;
case Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL33:
QMessageBox::critical(this, tr(below_gl33_title), tr(below_gl33_message));
QMessageBox::critical(this, below_gl33_title, below_gl33_message);
break;
default:
@ -847,6 +870,8 @@ void GMainWindow::ShutdownGame() {
ui.action_Pause->setEnabled(false);
ui.action_Stop->setEnabled(false);
ui.action_Restart->setEnabled(false);
ui.action_Load_Amiibo->setEnabled(false);
ui.action_Remove_Amiibo->setEnabled(false);
ui.action_Report_Compatibility->setEnabled(false);
ui.action_Enable_Frame_Advancing->setEnabled(false);
ui.action_Enable_Frame_Advancing->setChecked(false);
@ -960,14 +985,11 @@ void GMainWindow::OnGameListOpenFolder(u64 data_id, GameListOpenTarget target) {
QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
}
void GMainWindow::OnGameListNavigateToGamedbEntry(
u64 program_id,
std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) {
void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
const CompatibilityList& compatibility_list) {
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
QString directory;
if (it != compatibility_list.end())
directory = it->second.second;
@ -1137,6 +1159,7 @@ void GMainWindow::OnStartGame() {
ui.action_Pause->setEnabled(true);
ui.action_Stop->setEnabled(true);
ui.action_Restart->setEnabled(true);
ui.action_Load_Amiibo->setEnabled(true);
ui.action_Report_Compatibility->setEnabled(true);
ui.action_Enable_Frame_Advancing->setEnabled(true);
@ -1291,6 +1314,57 @@ void GMainWindow::OnConfigure() {
}
}
void GMainWindow::OnLoadAmiibo() {
const QString extensions{"*.bin"};
const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter);
if (filename.isEmpty()) {
return;
}
Core::System& system{Core::System::GetInstance()};
Service::SM::ServiceManager& sm = system.ServiceManager();
auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u");
if (nfc == nullptr) {
return;
}
QFile nfc_file{filename};
if (!nfc_file.open(QIODevice::ReadOnly)) {
QMessageBox::warning(this, tr("Error opening Amiibo data file"),
tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename));
return;
}
Service::NFC::AmiiboData amiibo_data{};
const u64 read_size =
nfc_file.read(reinterpret_cast<char*>(&amiibo_data), sizeof(Service::NFC::AmiiboData));
if (read_size != sizeof(Service::NFC::AmiiboData)) {
QMessageBox::warning(this, tr("Error reading Amiibo data file"),
tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but "
"was only able to read %2 bytes.")
.arg(sizeof(Service::NFC::AmiiboData))
.arg(read_size));
return;
}
nfc->LoadAmiibo(amiibo_data);
ui.action_Remove_Amiibo->setEnabled(true);
}
void GMainWindow::OnRemoveAmiibo() {
Core::System& system{Core::System::GetInstance()};
Service::SM::ServiceManager& sm = system.ServiceManager();
auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u");
if (nfc == nullptr) {
return;
}
nfc->RemoveAmiibo();
ui.action_Remove_Amiibo->setEnabled(false);
}
void GMainWindow::OnToggleFilterBar() {
game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked());
if (ui.action_Show_Filter_Bar->isChecked()) {

View File

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

View File

@ -57,11 +57,20 @@
<string>Recent Files</string>
</property>
</widget>
<widget class="QMenu" name="menu_Amiibo">
<property name="title">
<string>Amiibo</string>
</property>
<addaction name="action_Load_Amiibo"/>
<addaction name="action_Remove_Amiibo"/>
</widget>
<addaction name="action_Load_File"/>
<addaction name="action_Install_CIA"/>
<addaction name="separator"/>
<addaction name="menu_recent_files"/>
<addaction name="separator"/>
<addaction name="menu_Amiibo"/>
<addaction name="separator"/>
<addaction name="action_Exit"/>
</widget>
<widget class="QMenu" name="menu_Emulation">
@ -73,6 +82,8 @@
<addaction name="action_Stop"/>
<addaction name="action_Restart"/>
<addaction name="separator"/>
<addaction name="action_Report_Compatibility"/>
<addaction name="separator"/>
<addaction name="action_Configure"/>
</widget>
<widget class="QMenu" name="menu_View">
@ -139,8 +150,6 @@
<addaction name="action_Check_For_Updates"/>
<addaction name="action_Open_Maintenance_Tool"/>
<addaction name="separator"/>
<addaction name="action_Report_Compatibility"/>
<addaction name="separator"/>
<addaction name="action_FAQ"/>
<addaction name="action_About"/>
</widget>
@ -415,6 +424,22 @@
<string>Restart</string>
</property>
</action>
<action name="action_Load_Amiibo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Load...</string>
</property>
</action>
<action name="action_Remove_Amiibo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Remove</string>
</property>
</action>
</widget>
<resources/>
<connections/>

View File

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

View File

@ -9,23 +9,7 @@
#include <string>
#include <vector>
#include "common/common_types.h"
namespace Common {
struct WebResult {
enum class Code : u32 {
Success,
InvalidURL,
CredentialsMissing,
LibError,
HttpError,
WrongContent,
NoWebservice,
};
Code result_code;
std::string result_string;
std::string returned_data;
};
} // namespace Common
#include "common/web_result.h"
namespace AnnounceMultiplayerRoom {

View File

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

View File

@ -14,7 +14,6 @@
#ifdef _WIN32
#include <windows.h>
// windows.h needs to be included before other windows headers
#include <commdlg.h> // for GetSaveFileName
#include <direct.h> // getcwd
#include <io.h>
#include <shellapi.h>
@ -22,13 +21,17 @@
#include <tchar.h>
#include "common/string_util.h"
// 64 bit offsets for windows
#ifdef _MSC_VER
// 64 bit offsets for MSVC
#define fseeko _fseeki64
#define ftello _ftelli64
#define atoll _atoi64
#define fileno _fileno
#endif
// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64
#define stat _stat64
#define fstat _fstat64
#define fileno _fileno
#else
#ifdef __APPLE__
#include <sys/param.h>

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
virtual void Complete() = 0;
virtual bool SubmitTestcase() = 0;
};
/**
@ -178,6 +179,9 @@ struct NullVisitor : public VisitorInterface {
void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
void Complete() override {}
bool SubmitTestcase() override {
return false;
}
};
} // namespace Telemetry

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@ -2,75 +2,25 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "core/core_timing.h"
#include <algorithm>
#include <cinttypes>
#include <mutex>
#include <string>
#include <tuple>
#include <unordered_map>
#include <vector>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/thread.h"
#include "common/threadsafe_queue.h"
#include "core/core_timing.h"
namespace CoreTiming {
static s64 global_timer;
static s64 slice_length;
static s64 downcount;
struct EventType {
TimedCallback callback;
const std::string* name;
};
struct Event {
s64 time;
u64 fifo_order;
u64 userdata;
const EventType* type;
};
namespace Core {
// Sort by time, unless the times are the same, in which case sort by the order added to the queue
static bool operator>(const Event& left, const Event& right) {
return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order);
bool Timing::Event::operator>(const Event& right) const {
return std::tie(time, fifo_order) > std::tie(right.time, right.fifo_order);
}
static bool operator<(const Event& left, const Event& right) {
return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order);
bool Timing::Event::operator<(const Event& right) const {
return std::tie(time, fifo_order) < std::tie(right.time, right.fifo_order);
}
// unordered_map stores each element separately as a linked list node so pointers to elements
// remain stable regardless of rehashes/resizing.
static std::unordered_map<std::string, EventType> event_types;
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated
// by the standard adaptor class.
static std::vector<Event> event_queue;
static u64 event_fifo_id;
// the queue for storing the events from other threads threadsafe until they will be added
// to the event_queue by the emu thread
static Common::MPSCQueue<Event, false> ts_queue;
static constexpr int MAX_SLICE_LENGTH = 20000;
static s64 idled_cycles;
// Are we in a function that has been called from Advance()
// If events are sheduled from a function that gets called from Advance(),
// don't change slice_length and downcount.
static bool is_global_timer_sane;
static EventType* ev_lost = nullptr;
static void EmptyTimedCallback(u64 userdata, s64 cyclesLate) {}
EventType* RegisterEvent(const std::string& name, TimedCallback callback) {
TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback callback) {
// check for existing type with same name.
// we want event type names to remain unique so that we can use them for serialization.
ASSERT_MSG(event_types.find(name) == event_types.end(),
@ -78,42 +28,17 @@ EventType* RegisterEvent(const std::string& name, TimedCallback callback) {
"during Init to avoid breaking save states.",
name);
auto info = event_types.emplace(name, EventType{callback, nullptr});
EventType* event_type = &info.first->second;
auto info = event_types.emplace(name, TimingEventType{callback, nullptr});
TimingEventType* event_type = &info.first->second;
event_type->name = &info.first->first;
return event_type;
}
void UnregisterAllEvents() {
ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending");
event_types.clear();
}
void Init() {
downcount = MAX_SLICE_LENGTH;
slice_length = MAX_SLICE_LENGTH;
global_timer = 0;
idled_cycles = 0;
// The time between CoreTiming being intialized and the first call to Advance() is considered
// the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before
// executing the first cycle of each slice to prepare the slice length and downcount for
// that slice.
is_global_timer_sane = true;
event_fifo_id = 0;
ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback);
}
void Shutdown() {
Timing::~Timing() {
MoveEvents();
ClearPendingEvents();
UnregisterAllEvents();
}
// This should only be called from the CPU thread. If you are calling
// it from any other thread, you are doing something evil
u64 GetTicks() {
u64 Timing::GetTicks() const {
u64 ticks = static_cast<u64>(global_timer);
if (!is_global_timer_sane) {
ticks += slice_length - downcount;
@ -121,19 +46,16 @@ u64 GetTicks() {
return ticks;
}
void AddTicks(u64 ticks) {
void Timing::AddTicks(u64 ticks) {
downcount -= ticks;
}
u64 GetIdleTicks() {
u64 Timing::GetIdleTicks() const {
return static_cast<u64>(idled_cycles);
}
void ClearPendingEvents() {
event_queue.clear();
}
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type,
u64 userdata) {
ASSERT(event_type != nullptr);
s64 timeout = GetTicks() + cycles_into_future;
@ -145,11 +67,12 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
void Timing::ScheduleEventThreadsafe(s64 cycles_into_future, const TimingEventType* event_type,
u64 userdata) {
ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
}
void UnscheduleEvent(const EventType* event_type, u64 userdata) {
void Timing::UnscheduleEvent(const TimingEventType* event_type, u64 userdata) {
auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type == event_type && e.userdata == userdata;
});
@ -161,7 +84,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
}
}
void RemoveEvent(const EventType* event_type) {
void Timing::RemoveEvent(const TimingEventType* event_type) {
auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
[&](const Event& e) { return e.type == event_type; });
@ -172,12 +95,12 @@ void RemoveEvent(const EventType* event_type) {
}
}
void RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
void Timing::RemoveNormalAndThreadsafeEvent(const TimingEventType* event_type) {
MoveEvents();
RemoveEvent(event_type);
}
void ForceExceptionCheck(s64 cycles) {
void Timing::ForceExceptionCheck(s64 cycles) {
cycles = std::max<s64>(0, cycles);
if (downcount > cycles) {
slice_length -= downcount - cycles;
@ -185,7 +108,7 @@ void ForceExceptionCheck(s64 cycles) {
}
}
void MoveEvents() {
void Timing::MoveEvents() {
for (Event ev; ts_queue.Pop(ev);) {
ev.fifo_order = event_fifo_id++;
event_queue.emplace_back(std::move(ev));
@ -193,7 +116,7 @@ void MoveEvents() {
}
}
void Advance() {
void Timing::Advance() {
MoveEvents();
s64 cycles_executed = slice_length - downcount;
@ -220,17 +143,17 @@ void Advance() {
downcount = slice_length;
}
void Idle() {
void Timing::Idle() {
idled_cycles += downcount;
downcount = 0;
}
std::chrono::microseconds GetGlobalTimeUs() {
std::chrono::microseconds Timing::GetGlobalTimeUs() const {
return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE_ARM11};
}
s64 GetDowncount() {
s64 Timing::GetDowncount() const {
return downcount;
}
} // namespace CoreTiming
} // namespace Core

View File

@ -21,8 +21,11 @@
#include <functional>
#include <limits>
#include <string>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/threadsafe_queue.h"
// The timing we get from the assembly is 268,111,855.956 Hz
// It is possible that this number isn't just an integer because the compiler could have
@ -120,39 +123,38 @@ inline u64 cyclesToMs(s64 cycles) {
return cycles * 1000 / BASE_CLOCK_RATE_ARM11;
}
namespace CoreTiming {
struct EventType;
namespace Core {
using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
/**
* CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
* required to end slice -1 and start slice 0 before the first cycle of code is executed.
*/
void Init();
void Shutdown();
struct TimingEventType {
TimedCallback callback;
const std::string* name;
};
class Timing {
public:
~Timing();
/**
* This should only be called from the emu thread, if you are calling it any other thread, you are
* doing something evil
* This should only be called from the emu thread, if you are calling it any other thread, you
* are doing something evil
*/
u64 GetTicks();
u64 GetIdleTicks();
u64 GetTicks() const;
u64 GetIdleTicks() const;
void AddTicks(u64 ticks);
/**
* Returns the event_type identifier. if name is not unique, it will assert.
*/
EventType* RegisterEvent(const std::string& name, TimedCallback callback);
void UnregisterAllEvents();
TimingEventType* RegisterEvent(const std::string& name, TimedCallback callback);
/**
* After the first Advance, the slice lengths and the downcount will be reduced whenever an event
* is scheduled earlier than the current values.
* Scheduling from a callback will not update the downcount until the Advance() completes.
* After the first Advance, the slice lengths and the downcount will be reduced whenever an
* event is scheduled earlier than the current values. Scheduling from a callback will not
* update the downcount until the Advance() completes.
*/
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
void ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, u64 userdata = 0);
/**
* This is to be called when outside of hle threads, such as the graphics thread, wants to
@ -160,13 +162,14 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user
* Not that this doesn't change slice_length and thus events scheduled by this might be called
* with a delay of up to MAX_SLICE_LENGTH
*/
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata);
void ScheduleEventThreadsafe(s64 cycles_into_future, const TimingEventType* event_type,
u64 userdata);
void UnscheduleEvent(const EventType* event_type, u64 userdata);
void UnscheduleEvent(const TimingEventType* event_type, u64 userdata);
/// We only permit one event of each type in the queue at a time.
void RemoveEvent(const EventType* event_type);
void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
void RemoveEvent(const TimingEventType* event_type);
void RemoveNormalAndThreadsafeEvent(const TimingEventType* event_type);
/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
* the previous timing slice and begins the next one, you must Advance from the previous
@ -180,13 +183,52 @@ void MoveEvents();
/// Pretend that the main CPU has executed enough cycles to reach the next event.
void Idle();
/// Clear all pending events. This should ONLY be done on exit.
void ClearPendingEvents();
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.
#include <utility>
#include "core/core.h"
#include "core/file_sys/archive_savedata.h"
#include "core/hle/kernel/process.h"
@ -16,16 +17,19 @@ ArchiveFactory_SaveData::ArchiveFactory_SaveData(
: sd_savedata_source(std::move(sd_savedata)) {}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) {
return sd_savedata_source->Open(Kernel::g_current_process->codeset->program_id);
return sd_savedata_source->Open(
Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id);
}
ResultCode ArchiveFactory_SaveData::Format(const Path& path,
const FileSys::ArchiveFormatInfo& format_info) {
return sd_savedata_source->Format(Kernel::g_current_process->codeset->program_id, format_info);
return sd_savedata_source->Format(
Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id, format_info);
}
ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const {
return sd_savedata_source->GetFormatInfo(Kernel::g_current_process->codeset->program_id);
return sd_savedata_source->GetFormatInfo(
Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id);
}
} // namespace FileSys

View File

@ -7,6 +7,7 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/archive_selfncch.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/ivfc_archive.h"
@ -279,7 +280,7 @@ void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) {
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) {
auto archive = std::make_unique<SelfNCCHArchive>(
ncch_data[Kernel::g_current_process->codeset->program_id]);
ncch_data[Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id]);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}

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
#include <cstddef>
#include "common/common_types.h"
namespace FileSys {
class DelayGenerator {
public:
virtual ~DelayGenerator();
virtual u64 GetReadDelayNs(std::size_t length) = 0;
// TODO (B3N30): Add getter for all other file/directory io operations
@ -15,15 +19,7 @@ public:
class DefaultDelayGenerator : public DelayGenerator {
public:
u64 GetReadDelayNs(std::size_t length) override {
// This is the delay measured for a romfs read.
// For now we will take that as a default
static constexpr u64 slope(94);
static constexpr u64 offset(582778);
static constexpr u64 minimum(663124);
u64 IPCDelayNanoseconds = std::max<u64>(static_cast<u64>(length) * slope + offset, minimum);
return IPCDelayNanoseconds;
}
u64 GetReadDelayNs(std::size_t length) override;
};
} // namespace FileSys

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.size(), seed.data(), seed.size());
CryptoPP::SHA256 sha;
sha.CalculateDigest(key_y_secondary.data(), input.data(), input.size());
std::array<u8, CryptoPP::SHA256::DIGESTSIZE> hash;
sha.CalculateDigest(hash.data(), input.data(), input.size());
std::memcpy(key_y_secondary.data(), hash.data(), key_y_secondary.size());
}
}

View File

@ -34,7 +34,7 @@ bool SeedDB::Load() {
LOG_ERROR(Service_FS, "Failed to read seed database count fully");
return false;
}
if (!file.Seek(file.Tell() + SEEDDB_PADDING_BYTES, SEEK_SET)) {
if (!file.Seek(SEEDDB_PADDING_BYTES, SEEK_CUR)) {
LOG_ERROR(Service_FS, "Failed to skip seed database padding");
return false;
}

View File

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

View File

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

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/applets/erreula.h"
#include "core/hle/service/apt/apt.h"
@ -27,11 +28,9 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param
// TODO: allocated memory never released
using Kernel::MemoryPermission;
// Allocate a heap block of the required size for this applet.
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
// Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
"ErrEula Memory");
// Send the response message with the newly created SharedMemory

View File

@ -7,6 +7,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/applets/mii_selector.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
@ -34,11 +35,9 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
using Kernel::MemoryPermission;
// Allocate a heap block of the required size for this applet.
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
// Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
"MiiSelector Memory");
// Send the response message with the newly created SharedMemory

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/applets/mint.h"
#include "core/hle/service/apt/apt.h"
@ -27,11 +28,9 @@ ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& paramete
// TODO: allocated memory never released
using Kernel::MemoryPermission;
// Allocate a heap block of the required size for this applet.
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
// Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
"Mint Memory");
// Send the response message with the newly created SharedMemory

View File

@ -8,6 +8,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/applets/swkbd.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
@ -38,11 +39,9 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
using Kernel::MemoryPermission;
// Allocate a heap block of the required size for this applet.
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
// Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
"SoftwareKeyboard Memory");
// Send the response message with the newly created SharedMemory

View File

@ -9,27 +9,6 @@
#include "core/hle/kernel/thread.h"
#include "core/memory.h"
namespace Kernel {
/// Offset into command buffer of header
static const int kCommandHeaderOffset = 0x80;
/**
* Returns a pointer to the command buffer in the current thread's TLS
* TODO(Subv): This is not entirely correct, the command buffer should be copied from
* the thread's TLS to an intermediate buffer in kernel memory, and then copied again to
* the service handler process' memory.
* @param offset Optional offset into command buffer
* @param offset Optional offset into command buffer (in bytes)
* @return Pointer to command buffer
*/
inline u32* GetCommandBuffer(const int offset = 0) {
return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset +
offset);
}
} // namespace Kernel
namespace IPC {
/// Size of the command buffer area, in 32-bit words.

View File

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

View File

@ -31,14 +31,6 @@ enum class ArbitrationType : u32 {
class AddressArbiter final : public Object {
public:
/**
* Creates an address arbiter.
*
* @param name Optional name used for debugging.
* @returns The created AddressArbiter.
*/
static SharedPtr<AddressArbiter> Create(std::string name = "Unknown");
std::string GetTypeName() const override {
return "Arbiter";
}
@ -57,7 +49,7 @@ public:
s32 value, u64 nanoseconds);
private:
AddressArbiter();
explicit AddressArbiter(KernelSystem& kernel);
~AddressArbiter() override;
/// Puts the thread to wait on the specified arbitration address under this address arbiter.
@ -72,6 +64,8 @@ private:
/// Threads waiting for the address arbiter to be signaled.
std::vector<SharedPtr<Thread>> waiting_threads;
friend class KernelSystem;
};
} // namespace Kernel

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -67,6 +67,10 @@ constexpr ResultCode ERR_MISALIGNED_SIZE(ErrorDescription::MisalignedSize, Error
constexpr ResultCode ERR_OUT_OF_MEMORY(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
ErrorSummary::OutOfResource,
ErrorLevel::Permanent); // 0xD86007F3
/// Returned when out of heap or linear heap memory when allocating
constexpr ResultCode ERR_OUT_OF_HEAP_MEMORY(ErrorDescription::OutOfMemory, ErrorModule::OS,
ErrorSummary::OutOfResource,
ErrorLevel::Status); // 0xC860180A
constexpr ResultCode ERR_NOT_IMPLEMENTED(ErrorDescription::NotImplemented, ErrorModule::OS,
ErrorSummary::InvalidArgument,
ErrorLevel::Usage); // 0xE0E01BF4

View File

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

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