Merge branch 'master' of github.com:citra-emu/citra into ips-patches
commit
1ded48f5a3
@ -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
|
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh -ex
|
||||||
|
|
||||||
|
# Download the docker image that contains flatpak build dependencies
|
||||||
|
docker pull citraemu/build-environments:linux-flatpak
|
@ -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"
|
@ -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/*"
|
@ -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
|
@ -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
|
@ -1,137 +1 @@
|
|||||||
# Reporting Issues
|
**The Contributor's Guide has moved to [the wiki](https://github.com/citra-emu/citra/wiki/Contributing).**
|
||||||
|
|
||||||
**The issue tracker is not a support forum.** Unless you can provide precise *technical information* regarding an issue, you *should not post in it*. If you need support, first read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) and then either visit our IRC or [Discord](https://citra-emu.org/discord/) channel, [our forum](https://community.citra-emu.org) or ask in a general emulation forum such as [/r/emulation](https://www.reddit.com/r/emulation/). If you post support questions, generic messages to the developers or vague reports without technical details, they will be closed and locked.
|
|
||||||
|
|
||||||
If you believe you have a valid issue report, please post text or a screenshot from the log (the console window that opens alongside Citra) and build version (hex string visible in the titlebar and zip filename), as well as your hardware and software information if applicable.
|
|
||||||
|
|
||||||
# Contributing
|
|
||||||
|
|
||||||
Citra is a brand new project, so we have a great opportunity to keep things clean and well organized early on. As such, coding style is very important when making commits. We run clang-format on our CI to check the code. Please use it to format your code when contributing. However, it doesn't cover all the rules below. Some of them aren't very strict rules since we want to be flexible and we understand that under certain circumstances some of them can be counterproductive. Just try to follow as many of them as possible.
|
|
||||||
|
|
||||||
# Using clang format (version 6.0)
|
|
||||||
When generating the native build script for your toolset, cmake will try to find the correct version of clang format (or will download it on windows). Before running cmake, please install clang format version 6.0 for your platform as follows:
|
|
||||||
|
|
||||||
* Windows: do nothing; cmake will download a pre built binary for MSVC and MINGW. MSVC users can additionally install a clang format Visual Studio extension to add features like format on save.
|
|
||||||
* OSX: run `brew install clang-format`.
|
|
||||||
* Linux: use your package manager to get an appropriate binary.
|
|
||||||
|
|
||||||
If clang format is found, then cmake will add a custom build target that can be run at any time to run clang format against *all* source files and update the formatting in them. This should be used before making a pull request so that the reviewers can spend more time reviewing the code instead of having to worry about minor style violations. On MSVC, you can run clang format by building the clang-format project in the solution. On OSX, you can either use the Makefile target `make clang-format` or by building the clang-format target in XCode. For Makefile builds, you can use the clang-format target with `make clang-format`
|
|
||||||
|
|
||||||
### General Rules
|
|
||||||
* A lot of code was taken from other projects (e.g. Dolphin, PPSSPP, Gekko, SkyEye). In general, when editing other people's code, follow the style of the module you're in (or better yet, fix the style if it drastically differs from our guide).
|
|
||||||
* Line width is typically 100 characters. Please do not use 80-characters.
|
|
||||||
* Don't ever introduce new external dependencies into Core
|
|
||||||
* Don't use any platform specific code in Core
|
|
||||||
* Use namespaces often
|
|
||||||
* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`.
|
|
||||||
|
|
||||||
### Naming Rules
|
|
||||||
* Functions: `PascalCase`
|
|
||||||
* Variables: `lower_case_underscored`. Prefix with `g_` if global.
|
|
||||||
* Classes: `PascalCase`
|
|
||||||
* Files and Directories: `lower_case_underscored`
|
|
||||||
* Namespaces: `PascalCase`, `_` may also be used for clarity (e.g. `ARM_InitCore`)
|
|
||||||
|
|
||||||
### Indentation/Whitespace Style
|
|
||||||
Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead.
|
|
||||||
|
|
||||||
### Comments
|
|
||||||
* For regular comments, use C++ style (`//`) comments, even for multi-line ones.
|
|
||||||
* For doc-comments (Doxygen comments), use `/// ` if it's a single line, else use the `/**` `*/` style featured in the example. Start the text on the second line, not the first containing `/**`.
|
|
||||||
* For items that are both defined and declared in two separate files, put the doc-comment only next to the associated declaration. (In a header file, usually.) Otherwise, put it next to the implementation. Never duplicate doc-comments in both places.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// Includes should be sorted lexicographically
|
|
||||||
// STD includes first
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// then, library includes
|
|
||||||
#include <nihstro/shared_binary.h>
|
|
||||||
|
|
||||||
// finally, citra includes
|
|
||||||
#include "common/math_util.h"
|
|
||||||
#include "common/vector_math.h"
|
|
||||||
|
|
||||||
// each major module is separated
|
|
||||||
#include "video_core/pica.h"
|
|
||||||
#include "video_core/video_core.h"
|
|
||||||
|
|
||||||
namespace Example {
|
|
||||||
|
|
||||||
// Namespace contents are not indented
|
|
||||||
|
|
||||||
// Declare globals at the top
|
|
||||||
int g_foo{}; // {} can be used to initialize types as 0, false, or nullptr
|
|
||||||
char* g_some_pointer{}; // Pointer * and reference & stick to the type name, and make sure to initialize as nullptr!
|
|
||||||
|
|
||||||
/// A colorful enum.
|
|
||||||
enum SomeEnum {
|
|
||||||
ColorRed, ///< The color of fire.
|
|
||||||
ColorGreen, ///< The color of grass.
|
|
||||||
ColorBlue, ///< Not actually the color of water.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Very important struct that does a lot of stuff.
|
|
||||||
* Note that the asterisks are indented by one space to align to the first line.
|
|
||||||
*/
|
|
||||||
struct Position {
|
|
||||||
int x{}, y{}; // Always intitialize member variables!
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use "typename" rather than "class" here
|
|
||||||
template <typename T>
|
|
||||||
void FooBar() {
|
|
||||||
const std::string some_string{ "prefer uniform initialization" };
|
|
||||||
|
|
||||||
int some_array[]{
|
|
||||||
5,
|
|
||||||
25,
|
|
||||||
7,
|
|
||||||
42,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (note == the_space_after_the_if) {
|
|
||||||
CallAfunction();
|
|
||||||
} else {
|
|
||||||
// Use a space after the // when commenting
|
|
||||||
}
|
|
||||||
|
|
||||||
// Place a single space after the for loop semicolons, prefer pre-increment
|
|
||||||
for (int i{}; i != 25; ++i) {
|
|
||||||
// This is how we write loops
|
|
||||||
}
|
|
||||||
|
|
||||||
DoStuff(this, function, call, takes, up, multiple,
|
|
||||||
lines, like, this);
|
|
||||||
|
|
||||||
if (this || condition_takes_up_multiple &&
|
|
||||||
lines && like && this || everything ||
|
|
||||||
alright || then) {
|
|
||||||
|
|
||||||
// Leave a blank space before the if block body if the condition was continued across
|
|
||||||
// several lines.
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (var) {
|
|
||||||
// No indentation for case label
|
|
||||||
case 1: {
|
|
||||||
int case_var{ var + 3 };
|
|
||||||
DoSomething(case_var);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
DoSomething(var);
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Yes, even break for the last case
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<T> you_can_declare, a_few, variables, like_this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
File diff suppressed because it is too large
Load Diff
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
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
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
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
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
Binary file not shown.
@ -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;
|
||||||
|
});
|
||||||
|
}
|
@ -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);
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
};
|
@ -1,178 +0,0 @@
|
|||||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include "common/logging/log.h"
|
|
||||||
#include "common/memory_util.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
// Windows.h needs to be included before psapi.h
|
|
||||||
#include <psapi.h>
|
|
||||||
#include "common/common_funcs.h"
|
|
||||||
#include "common/string_util.h"
|
|
||||||
#else
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
|
||||||
#include <unistd.h>
|
|
||||||
#define PAGE_MASK (getpagesize() - 1)
|
|
||||||
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// This is purposely not a full wrapper for virtualalloc/mmap, but it
|
|
||||||
// provides exactly the primitive operations that Dolphin needs.
|
|
||||||
|
|
||||||
void* AllocateExecutableMemory(std::size_t size, bool low) {
|
|
||||||
#if defined(_WIN32)
|
|
||||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
|
||||||
#else
|
|
||||||
static char* map_hint = nullptr;
|
|
||||||
#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
|
||||||
// This OS has no flag to enforce allocation below the 4 GB boundary,
|
|
||||||
// but if we hint that we want a low address it is very likely we will
|
|
||||||
// get one.
|
|
||||||
// An older version of this code used MAP_FIXED, but that has the side
|
|
||||||
// effect of discarding already mapped pages that happen to be in the
|
|
||||||
// requested virtual memory range (such as the emulated RAM, sometimes).
|
|
||||||
if (low && (!map_hint))
|
|
||||||
map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */
|
|
||||||
#endif
|
|
||||||
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
|
||||||
MAP_ANON | MAP_PRIVATE
|
|
||||||
#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
|
|
||||||
| (low ? MAP_32BIT : 0)
|
|
||||||
#endif
|
|
||||||
,
|
|
||||||
-1, 0);
|
|
||||||
#endif /* defined(_WIN32) */
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (ptr == nullptr) {
|
|
||||||
#else
|
|
||||||
if (ptr == MAP_FAILED) {
|
|
||||||
ptr = nullptr;
|
|
||||||
#endif
|
|
||||||
LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
|
|
||||||
}
|
|
||||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
|
||||||
else {
|
|
||||||
if (low) {
|
|
||||||
map_hint += size;
|
|
||||||
map_hint = (char*)round_page(map_hint); /* round up to the next page */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if EMU_ARCH_BITS == 64
|
|
||||||
if ((u64)ptr >= 0x80000000 && low == true)
|
|
||||||
LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
|
||||||
#else
|
|
||||||
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
|
||||||
|
|
||||||
if (ptr == MAP_FAILED)
|
|
||||||
ptr = nullptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ptr == nullptr)
|
|
||||||
LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
|
|
||||||
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
void* ptr = _aligned_malloc(size, alignment);
|
|
||||||
#else
|
|
||||||
void* ptr = nullptr;
|
|
||||||
#ifdef ANDROID
|
|
||||||
ptr = memalign(alignment, size);
|
|
||||||
#else
|
|
||||||
if (posix_memalign(&ptr, alignment, size) != 0)
|
|
||||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ptr == nullptr)
|
|
||||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
|
||||||
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeMemoryPages(void* ptr, std::size_t size) {
|
|
||||||
if (ptr) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (!VirtualFree(ptr, 0, MEM_RELEASE))
|
|
||||||
LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
|
|
||||||
#else
|
|
||||||
munmap(ptr, size);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeAlignedMemory(void* ptr) {
|
|
||||||
if (ptr) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
_aligned_free(ptr);
|
|
||||||
#else
|
|
||||||
free(ptr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
DWORD oldValue;
|
|
||||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
|
|
||||||
LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
|
|
||||||
#else
|
|
||||||
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
DWORD oldValue;
|
|
||||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
|
|
||||||
&oldValue))
|
|
||||||
LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
|
|
||||||
#else
|
|
||||||
mprotect(ptr, size,
|
|
||||||
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string MemUsage() {
|
|
||||||
#ifdef _WIN32
|
|
||||||
#pragma comment(lib, "psapi")
|
|
||||||
DWORD processID = GetCurrentProcessId();
|
|
||||||
HANDLE hProcess;
|
|
||||||
PROCESS_MEMORY_COUNTERS pmc;
|
|
||||||
std::string Ret;
|
|
||||||
|
|
||||||
// Print information about the memory usage of the process.
|
|
||||||
|
|
||||||
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
|
|
||||||
if (nullptr == hProcess)
|
|
||||||
return "MemUsage Error";
|
|
||||||
|
|
||||||
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
|
|
||||||
Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7));
|
|
||||||
|
|
||||||
CloseHandle(hProcess);
|
|
||||||
return Ret;
|
|
||||||
#else
|
|
||||||
return "";
|
|
||||||
#endif
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
void* AllocateExecutableMemory(std::size_t size, bool low = true);
|
|
||||||
void* AllocateMemoryPages(std::size_t size);
|
|
||||||
void FreeMemoryPages(void* ptr, std::size_t size);
|
|
||||||
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment);
|
|
||||||
void FreeAlignedMemory(void* ptr);
|
|
||||||
void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
|
|
||||||
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
|
|
||||||
std::string MemUsage();
|
|
||||||
|
|
||||||
inline int GetPageSize() {
|
|
||||||
return 4096;
|
|
||||||
}
|
|
@ -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
|
@ -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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue