Compare commits
193 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ebc27234d | ||
|
|
bbb396d7f1 | ||
|
|
9ff72ca9f2 | ||
|
|
5f2d9f282a | ||
|
|
f2a2f818b6 | ||
|
|
c6a0ab9792 | ||
|
|
125599c2d5 | ||
|
|
3049ea45d3 | ||
|
|
372245e0b5 | ||
|
|
3ca0af8bb3 | ||
|
|
3740adb6f5 | ||
|
|
aa427bb2a7 | ||
|
|
636cc2a496 | ||
|
|
ea73ffe202 | ||
|
|
56541b1ae5 | ||
|
|
4bce57b149 | ||
|
|
63783db1b3 | ||
|
|
e8bd6b1fcc | ||
|
|
1c733bf175 | ||
|
|
197d0d9d24 | ||
|
|
cbf8bea9d5 | ||
|
|
eff61c5c42 | ||
|
|
f9e69faf4a | ||
|
|
69da267540 | ||
|
|
08fcf41b0a | ||
|
|
83f8d1aa2e | ||
|
|
966405d64b | ||
|
|
d0e4e43e3c | ||
|
|
96644385ca | ||
|
|
a1b1ea47ed | ||
|
|
549164d425 | ||
|
|
b8b87ec01f | ||
|
|
5961928543 | ||
|
|
a661025637 | ||
|
|
66978a772d | ||
|
|
e4fa77ef6a | ||
|
|
b273b19576 | ||
|
|
318bf7c8e3 | ||
|
|
7c6bb8c17f | ||
|
|
215ca770df | ||
|
|
bc879ae880 | ||
|
|
c9ef8b0af1 | ||
|
|
83e8ad2331 | ||
|
|
877a978a22 | ||
|
|
912f2a520a | ||
|
|
3121408a90 | ||
|
|
ac7d8983eb | ||
|
|
05dbb47af5 | ||
|
|
dbb1eb9c29 | ||
|
|
21f1b2889d | ||
|
|
26c9f12271 | ||
|
|
c6016856d8 | ||
|
|
19cf995225 | ||
|
|
23ebd4920e | ||
|
|
b683e41fca | ||
|
|
179ee963db | ||
|
|
0ab17ab406 | ||
|
|
17a68e5ebe | ||
|
|
e04d75f44c | ||
|
|
37ac1bb576 | ||
|
|
5933b3ea96 | ||
|
|
35c095898b | ||
|
|
ea4928393f | ||
|
|
2378ecd0e8 | ||
|
|
fc8a8789da | ||
|
|
59c665b28e | ||
|
|
9e8737b535 | ||
|
|
56e51da1d9 | ||
|
|
40aa1ea9f9 | ||
|
|
a01832b2e0 | ||
|
|
1c7696e0d9 | ||
|
|
bf036b46fc | ||
|
|
54d7b664da | ||
|
|
0f887daa72 | ||
|
|
c5262b1890 | ||
|
|
4f41fd84ff | ||
|
|
de4f931d13 | ||
|
|
548fe0fc62 | ||
|
|
48a526dba1 | ||
|
|
f2536cafe5 | ||
|
|
9ec9709f68 | ||
|
|
5af0340066 | ||
|
|
2c0f831468 | ||
|
|
56a97647a3 | ||
|
|
a6aa710b84 | ||
|
|
11f990975c | ||
|
|
ceeff8ebfd | ||
|
|
7538fa4a67 | ||
|
|
5bfd810ab5 | ||
|
|
dea1db0414 | ||
|
|
c91d2bac45 | ||
|
|
351f010cfc | ||
|
|
fe3bf4f075 | ||
|
|
6a0e258dde | ||
|
|
dd0b17a5f0 | ||
|
|
c8c8412901 | ||
|
|
cd8924520c | ||
|
|
f5f35caf20 | ||
|
|
1f0c8bfeda | ||
|
|
15501477e7 | ||
|
|
f96bb2520b | ||
|
|
09ffa0cb21 | ||
|
|
244b46af5a | ||
|
|
b4242633ad | ||
|
|
3a8d38be7e | ||
|
|
8769604144 | ||
|
|
205e6d3b97 | ||
|
|
039e58a984 | ||
|
|
a17dd30057 | ||
|
|
776ce5d74c | ||
|
|
387bffda5e | ||
|
|
e5dfbe22ee | ||
|
|
331c252509 | ||
|
|
f80229b676 | ||
|
|
97fb6179b9 | ||
|
|
fa97f50bff | ||
|
|
614de1dba7 | ||
|
|
af51f123fa | ||
|
|
22d4e10664 | ||
|
|
8495f1df2a | ||
|
|
0c5ede492f | ||
|
|
cb930c4b5a | ||
|
|
2020ba06e1 | ||
|
|
af9336df57 | ||
|
|
ef4c4e239d | ||
|
|
45da3be40e | ||
|
|
e00e1fc755 | ||
|
|
32bfa92c71 | ||
|
|
85a3368e6d | ||
|
|
69f622be36 | ||
|
|
4c20a39828 | ||
|
|
621b25b6be | ||
|
|
abbcc8e61e | ||
|
|
94db6e5f3f | ||
|
|
a73c7c73eb | ||
|
|
a81ff6f54c | ||
|
|
f80bc712ea | ||
|
|
771431f625 | ||
|
|
19bfbb28ce | ||
|
|
357bc956ae | ||
|
|
9aa68212d9 | ||
|
|
c6da772b24 | ||
|
|
fbeaa330a3 | ||
|
|
e84e4fd3f8 | ||
|
|
fbe900ba6d | ||
|
|
795335af0f | ||
|
|
fa9acc26d9 | ||
|
|
8e23e40a72 | ||
|
|
2991c747c9 | ||
|
|
7477c12fe2 | ||
|
|
c4515d305b | ||
|
|
c643f364b4 | ||
|
|
2e6b67a079 | ||
|
|
4082c4eda6 | ||
|
|
0756f29a2c | ||
|
|
417e1ef09c | ||
|
|
5c4259ec1a | ||
|
|
4a6ba58073 | ||
|
|
73c9ffc422 | ||
|
|
17fa0ffff9 | ||
|
|
1392597ede | ||
|
|
c9269a4a4b | ||
|
|
0c18d47348 | ||
|
|
faa9110541 | ||
|
|
67fa21e143 | ||
|
|
33056dd833 | ||
|
|
8047873a66 | ||
|
|
ae582b6669 | ||
|
|
46b8b03015 | ||
|
|
7e622c5575 | ||
|
|
9c2d83cb9c | ||
|
|
4d61ac08aa | ||
|
|
bbf8d6bf01 | ||
|
|
946777601b | ||
|
|
7e72b5e453 | ||
|
|
1120e0b4d2 | ||
|
|
002ae08bbd | ||
|
|
d09fb82113 | ||
|
|
10824c5d63 | ||
|
|
e0e84aede0 | ||
|
|
010bc677f3 | ||
|
|
0f216d20e3 | ||
|
|
3dc59b74ec | ||
|
|
27caf71204 | ||
|
|
6ff5135521 | ||
|
|
fc8da2d5e3 | ||
|
|
08d5663cb8 | ||
|
|
a3d78b77f8 | ||
|
|
3e75175d02 | ||
|
|
6d7514ccec | ||
|
|
5102c91256 | ||
|
|
7149332712 | ||
|
|
4a32d92e6e |
@@ -1,12 +1,12 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev wget cmake ninja-build ccache
|
||||
apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev qtwebengine5-dev wget cmake ninja-build ccache
|
||||
|
||||
cd /yuzu
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -G Ninja
|
||||
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -G Ninja
|
||||
ninja
|
||||
|
||||
ccache -s
|
||||
|
||||
@@ -9,7 +9,7 @@ export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
mkdir build && cd build
|
||||
cmake --version
|
||||
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
|
||||
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
|
||||
make -j4
|
||||
|
||||
ccache -s
|
||||
|
||||
@@ -19,6 +19,8 @@ option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||
|
||||
option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
|
||||
|
||||
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
|
||||
|
||||
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
@@ -302,7 +304,7 @@ endif()
|
||||
if (ENABLE_QT)
|
||||
if (YUZU_USE_BUNDLED_QT)
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
|
||||
set(QT_VER qt-5.10.0-msvc2015_64)
|
||||
set(QT_VER qt-5.12.0-msvc2017_64)
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
|
||||
endif()
|
||||
@@ -319,6 +321,10 @@ if (ENABLE_QT)
|
||||
endif()
|
||||
|
||||
find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
|
||||
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets ${QT_PREFIX_HINT})
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
# Platform-specific library requirements
|
||||
|
||||
@@ -5,6 +5,7 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
|
||||
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
|
||||
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
|
||||
set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
|
||||
set(PLATFORMS ${DLL_DEST}platforms/)
|
||||
set(STYLES ${DLL_DEST}styles/)
|
||||
set(IMAGEFORMATS ${DLL_DEST}imageformats/)
|
||||
@@ -17,7 +18,35 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
Qt5OpenGL$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
||||
)
|
||||
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
|
||||
Qt5Network$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Positioning$<$<CONFIG:Debug>:d>.*
|
||||
Qt5PrintSupport$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Qml$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Quick$<$<CONFIG:Debug>:d>.*
|
||||
Qt5QuickWidgets$<$<CONFIG:Debug>:d>.*
|
||||
Qt5WebChannel$<$<CONFIG:Debug>:d>.*
|
||||
Qt5WebEngine$<$<CONFIG:Debug>:d>.*
|
||||
Qt5WebEngineCore$<$<CONFIG:Debug>:d>.*
|
||||
Qt5WebEngineWidgets$<$<CONFIG:Debug>:d>.*
|
||||
QtWebEngineProcess$<$<CONFIG:Debug>:d>.*
|
||||
)
|
||||
|
||||
windows_copy_files(${target_dir} ${Qt5_RESOURCES_DIR} ${DLL_DEST}
|
||||
qtwebengine_resources.pak
|
||||
qtwebengine_devtools_resources.pak
|
||||
qtwebengine_resources_100p.pak
|
||||
qtwebengine_resources_200p.pak
|
||||
icudtl.dat
|
||||
)
|
||||
endif ()
|
||||
|
||||
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
|
||||
qjpeg$<$<CONFIG:Debug>:d>.*
|
||||
qgif$<$<CONFIG:Debug>:d>.*
|
||||
)
|
||||
endfunction(copy_yuzu_Qt5_deps)
|
||||
|
||||
137
CONTRIBUTING.md
137
CONTRIBUTING.md
@@ -1,136 +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/yuzu-emu/yuzu/wiki/FAQ) and then either visit our [Discord server](https://discordapp.com/invite/u77vRWY), [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 yuzu) and build version (hex string visible in the titlebar and zip filename), as well as your hardware and software information if applicable.
|
||||
|
||||
# Contributing
|
||||
yuzu 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. Citra, Dolphin, PPSSPP, Gekko). 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, yuzu 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 Citra wiki](https://github.com/citra-emu/citra/wiki/Contributing).**
|
||||
|
||||
@@ -42,7 +42,7 @@ before_build:
|
||||
$COMPAT = if ($env:ENABLE_COMPATIBILITY_REPORTING -eq $null) {0} else {$env:ENABLE_COMPATIBILITY_REPORTING}
|
||||
if ($env:BUILD_TYPE -eq 'msvc') {
|
||||
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
|
||||
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1 && exit 0'
|
||||
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1 && exit 0'
|
||||
} else {
|
||||
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1"
|
||||
}
|
||||
@@ -94,6 +94,7 @@ after_build:
|
||||
Copy-Item "$BUILD_DIR\*" -Destination $RELEASE_DIST -Recurse
|
||||
rm "$RELEASE_DIST\*.exe"
|
||||
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
Copy-Item .\license.txt -Destination $RELEASE_DIST
|
||||
Copy-Item .\README.md -Destination $RELEASE_DIST
|
||||
7z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\*
|
||||
|
||||
2
externals/fmt
vendored
2
externals/fmt
vendored
Submodule externals/fmt updated: 3e75ad9822...9e554999ce
@@ -22,8 +22,7 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
|
||||
return Stream::Format::Multi51Channel16;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented num_channels={}", num_channels);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -260,8 +260,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented sample_format={}", info.sample_format);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -280,8 +279,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented channel_count={}", info.channel_count);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,7 @@ u32 Stream::GetNumChannels() const {
|
||||
case Format::Multi51Channel16:
|
||||
return 6;
|
||||
}
|
||||
LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented format={}", static_cast<u32>(format));
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -49,7 +48,7 @@ void Stream::Play() {
|
||||
|
||||
void Stream::Stop() {
|
||||
state = State::Stopped;
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
Stream::State Stream::GetState() const {
|
||||
@@ -120,7 +119,7 @@ bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
||||
}
|
||||
|
||||
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -53,8 +53,8 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
|
||||
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
|
||||
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
|
||||
|
||||
// Place a lower limit of 5% speed. When a game boots up, there will be
|
||||
// many silence samples. These do not need to be timestretched.
|
||||
// Place a lower limit of 5% speed. When a game boots up, there will be
|
||||
// many silence samples. These do not need to be timestretched.
|
||||
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
|
||||
m_sound_touch.setTempo(m_stretch_ratio);
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ add_library(common STATIC
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
bit_field.h
|
||||
bit_util.h
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
color.h
|
||||
|
||||
61
src/common/bit_util.h
Normal file
61
src/common/bit_util.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// Gets the size of a specified type T in bits.
|
||||
template <typename T>
|
||||
constexpr std::size_t BitSize() {
|
||||
return sizeof(T) * CHAR_BIT;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
inline u32 CountLeadingZeroes32(u32 value) {
|
||||
unsigned long leading_zero = 0;
|
||||
|
||||
if (_BitScanReverse(&leading_zero, value) != 0) {
|
||||
return 31 - leading_zero;
|
||||
}
|
||||
|
||||
return 32;
|
||||
}
|
||||
|
||||
inline u64 CountLeadingZeroes64(u64 value) {
|
||||
unsigned long leading_zero = 0;
|
||||
|
||||
if (_BitScanReverse64(&leading_zero, value) != 0) {
|
||||
return 63 - leading_zero;
|
||||
}
|
||||
|
||||
return 64;
|
||||
}
|
||||
#else
|
||||
inline u32 CountLeadingZeroes32(u32 value) {
|
||||
if (value == 0) {
|
||||
return 32;
|
||||
}
|
||||
|
||||
return __builtin_clz(value);
|
||||
}
|
||||
|
||||
inline u64 CountLeadingZeroes64(u64 value) {
|
||||
if (value == 0) {
|
||||
return 64;
|
||||
}
|
||||
|
||||
return __builtin_clzll(value);
|
||||
}
|
||||
#endif
|
||||
} // namespace Common
|
||||
@@ -1,5 +1,6 @@
|
||||
add_library(core STATIC
|
||||
arm/arm_interface.h
|
||||
arm/arm_interface.cpp
|
||||
arm/exclusive_monitor.cpp
|
||||
arm/exclusive_monitor.h
|
||||
arm/unicorn/arm_unicorn.cpp
|
||||
@@ -87,6 +88,8 @@ add_library(core STATIC
|
||||
frontend/applets/profile_select.h
|
||||
frontend/applets/software_keyboard.cpp
|
||||
frontend/applets/software_keyboard.h
|
||||
frontend/applets/web_browser.cpp
|
||||
frontend/applets/web_browser.h
|
||||
frontend/emu_window.cpp
|
||||
frontend/emu_window.h
|
||||
frontend/framebuffer_layout.cpp
|
||||
@@ -115,6 +118,8 @@ add_library(core STATIC
|
||||
hle/kernel/object.h
|
||||
hle/kernel/process.cpp
|
||||
hle/kernel/process.h
|
||||
hle/kernel/process_capability.cpp
|
||||
hle/kernel/process_capability.h
|
||||
hle/kernel/readable_event.cpp
|
||||
hle/kernel/readable_event.h
|
||||
hle/kernel/resource_limit.cpp
|
||||
@@ -170,6 +175,8 @@ add_library(core STATIC
|
||||
hle/service/am/applets/software_keyboard.h
|
||||
hle/service/am/applets/stub_applet.cpp
|
||||
hle/service/am/applets/stub_applet.h
|
||||
hle/service/am/applets/web_browser.cpp
|
||||
hle/service/am/applets/web_browser.h
|
||||
hle/service/am/idle.cpp
|
||||
hle/service/am/idle.h
|
||||
hle/service/am/omm.cpp
|
||||
|
||||
27
src/core/arm/arm_interface.cpp
Normal file
27
src/core/arm/arm_interface.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
void ARM_Interface::LogBacktrace() const {
|
||||
VAddr fp = GetReg(29);
|
||||
VAddr lr = GetReg(30);
|
||||
const VAddr sp = GetReg(13);
|
||||
const VAddr pc = GetPC();
|
||||
|
||||
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
|
||||
while (true) {
|
||||
LOG_ERROR(Core_ARM, "{:016X}", lr);
|
||||
if (!fp) {
|
||||
break;
|
||||
}
|
||||
lr = Memory::Read64(fp + 8) - 4;
|
||||
fp = Memory::Read64(fp);
|
||||
}
|
||||
}
|
||||
} // namespace Core
|
||||
@@ -141,6 +141,14 @@ public:
|
||||
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
|
||||
/// fp (= r29) points to the last frame record.
|
||||
/// Note that this is the frame record for the *previous* frame, not the current one.
|
||||
/// Note we need to subtract 4 from our last read to get the proper address
|
||||
/// Frame records are two words long:
|
||||
/// fp+0 : pointer to previous frame record
|
||||
/// fp+8 : value of lr for frame
|
||||
void LogBacktrace() const;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
||||
@@ -30,8 +30,11 @@
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "frontend/applets/profile_select.h"
|
||||
#include "frontend/applets/software_keyboard.h"
|
||||
#include "frontend/applets/web_browser.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@@ -94,6 +97,11 @@ struct System::Impl {
|
||||
CoreTiming::Init();
|
||||
kernel.Initialize();
|
||||
|
||||
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
Settings::values.custom_rtc_differential =
|
||||
Settings::values.custom_rtc.value_or(current_time) - current_time;
|
||||
|
||||
// Create a default fs if one doesn't already exist.
|
||||
if (virtual_filesystem == nullptr)
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
@@ -103,6 +111,8 @@ struct System::Impl {
|
||||
profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
|
||||
if (software_keyboard == nullptr)
|
||||
software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
|
||||
if (web_browser == nullptr)
|
||||
web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
|
||||
|
||||
auto main_process = Kernel::Process::Create(kernel, "main");
|
||||
kernel.MakeCurrentProcess(main_process.get());
|
||||
@@ -199,6 +209,11 @@ struct System::Impl {
|
||||
// Close app loader
|
||||
app_loader.reset();
|
||||
|
||||
// Clear all applets
|
||||
profile_selector.reset();
|
||||
software_keyboard.reset();
|
||||
web_browser.reset();
|
||||
|
||||
LOG_DEBUG(Core, "Shutdown OK");
|
||||
}
|
||||
|
||||
@@ -233,6 +248,7 @@ struct System::Impl {
|
||||
/// Frontend applets
|
||||
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
|
||||
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
|
||||
std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
@@ -427,22 +443,34 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
|
||||
return impl->virtual_filesystem;
|
||||
}
|
||||
|
||||
void System::SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet) {
|
||||
void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) {
|
||||
impl->profile_selector = std::move(applet);
|
||||
}
|
||||
|
||||
const Core::Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
|
||||
const Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
|
||||
return *impl->profile_selector;
|
||||
}
|
||||
|
||||
void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
|
||||
void System::SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet) {
|
||||
impl->software_keyboard = std::move(applet);
|
||||
}
|
||||
|
||||
const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
|
||||
const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
|
||||
return *impl->software_keyboard;
|
||||
}
|
||||
|
||||
void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) {
|
||||
impl->web_browser = std::move(applet);
|
||||
}
|
||||
|
||||
Frontend::WebBrowserApplet& System::GetWebBrowser() {
|
||||
return *impl->web_browser;
|
||||
}
|
||||
|
||||
const Frontend::WebBrowserApplet& System::GetWebBrowser() const {
|
||||
return *impl->web_browser;
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
|
||||
return impl->Init(*this, emu_window);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,12 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "frontend/applets/profile_select.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
class ProfileSelectApplet;
|
||||
class SoftwareKeyboardApplet;
|
||||
class WebBrowserApplet;
|
||||
} // namespace Core::Frontend
|
||||
|
||||
namespace FileSys {
|
||||
@@ -242,13 +243,18 @@ public:
|
||||
|
||||
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
|
||||
|
||||
void SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet);
|
||||
void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
|
||||
|
||||
const Core::Frontend::ProfileSelectApplet& GetProfileSelector() const;
|
||||
const Frontend::ProfileSelectApplet& GetProfileSelector() const;
|
||||
|
||||
void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
|
||||
void SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet);
|
||||
|
||||
const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
|
||||
const Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
|
||||
|
||||
void SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet);
|
||||
|
||||
Frontend::WebBrowserApplet& GetWebBrowser();
|
||||
const Frontend::WebBrowserApplet& GetWebBrowser() const;
|
||||
|
||||
private:
|
||||
System();
|
||||
|
||||
@@ -359,6 +359,8 @@ bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTable
|
||||
dirs.push_back(std::move(npfs));
|
||||
if (IsDirectoryExeFS(dirs.back()))
|
||||
exefs = dirs.back();
|
||||
else if (IsDirectoryLogoPartition(dirs.back()))
|
||||
logo = dirs.back();
|
||||
} else {
|
||||
if (has_rights_id)
|
||||
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
|
||||
@@ -546,4 +548,8 @@ u64 NCA::GetBaseIVFCOffset() const {
|
||||
return ivfc_offset;
|
||||
}
|
||||
|
||||
VirtualDir NCA::GetLogoPartition() const {
|
||||
return logo;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -74,6 +74,13 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
|
||||
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
|
||||
}
|
||||
|
||||
inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) {
|
||||
// NintendoLogo is the static image in the top left corner while StartupMovie is the animation
|
||||
// in the bottom right corner.
|
||||
return pfs->GetFile("NintendoLogo.png") != nullptr &&
|
||||
pfs->GetFile("StartupMovie.gif") != nullptr;
|
||||
}
|
||||
|
||||
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
|
||||
// After construction, use GetStatus to determine if the file is valid and ready to be used.
|
||||
class NCA : public ReadOnlyVfsDirectory {
|
||||
@@ -102,6 +109,8 @@ public:
|
||||
// Returns the base ivfc offset used in BKTR patching.
|
||||
u64 GetBaseIVFCOffset() const;
|
||||
|
||||
VirtualDir GetLogoPartition() const;
|
||||
|
||||
private:
|
||||
bool CheckSupportedNCA(const NCAHeader& header);
|
||||
bool HandlePotentialHeaderDecryption();
|
||||
@@ -122,6 +131,7 @@ private:
|
||||
|
||||
VirtualFile romfs = nullptr;
|
||||
VirtualDir exefs = nullptr;
|
||||
VirtualDir logo = nullptr;
|
||||
VirtualFile file;
|
||||
VirtualFile bktr_base_romfs;
|
||||
u64 ivfc_offset = 0;
|
||||
|
||||
@@ -36,18 +36,20 @@ std::string LanguageEntry::GetDeveloperName() const {
|
||||
developer_name.size());
|
||||
}
|
||||
|
||||
NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
|
||||
file->ReadObject(raw.get());
|
||||
NACP::NACP() = default;
|
||||
|
||||
NACP::NACP(VirtualFile file) {
|
||||
file->ReadObject(&raw);
|
||||
}
|
||||
|
||||
NACP::~NACP() = default;
|
||||
|
||||
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
|
||||
if (language != Language::Default) {
|
||||
return raw->language_entries.at(static_cast<u8>(language));
|
||||
return raw.language_entries.at(static_cast<u8>(language));
|
||||
}
|
||||
|
||||
for (const auto& language_entry : raw->language_entries) {
|
||||
for (const auto& language_entry : raw.language_entries) {
|
||||
if (!language_entry.GetApplicationName().empty())
|
||||
return language_entry;
|
||||
}
|
||||
@@ -65,21 +67,29 @@ std::string NACP::GetDeveloperName(Language language) const {
|
||||
}
|
||||
|
||||
u64 NACP::GetTitleId() const {
|
||||
return raw->title_id;
|
||||
return raw.title_id;
|
||||
}
|
||||
|
||||
u64 NACP::GetDLCBaseTitleId() const {
|
||||
return raw->dlc_base_title_id;
|
||||
return raw.dlc_base_title_id;
|
||||
}
|
||||
|
||||
std::string NACP::GetVersionString() const {
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
|
||||
raw->version_string.size());
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(raw.version_string.data(),
|
||||
raw.version_string.size());
|
||||
}
|
||||
|
||||
u64 NACP::GetDefaultNormalSaveSize() const {
|
||||
return raw.normal_save_data_size;
|
||||
}
|
||||
|
||||
u64 NACP::GetDefaultJournalSaveSize() const {
|
||||
return raw.journal_sava_data_size;
|
||||
}
|
||||
|
||||
std::vector<u8> NACP::GetRawBytes() const {
|
||||
std::vector<u8> out(sizeof(RawNACP));
|
||||
std::memcpy(out.data(), raw.get(), sizeof(RawNACP));
|
||||
std::memcpy(out.data(), &raw, sizeof(RawNACP));
|
||||
return out;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -28,17 +28,30 @@ static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size.
|
||||
// The raw file format of a NACP file.
|
||||
struct RawNACP {
|
||||
std::array<LanguageEntry, 16> language_entries;
|
||||
INSERT_PADDING_BYTES(0x38);
|
||||
std::array<u8, 0x25> isbn;
|
||||
u8 startup_user_account;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
u32_le application_attribute;
|
||||
u32_le supported_languages;
|
||||
u32_le parental_control;
|
||||
bool screenshot_enabled;
|
||||
u8 video_capture_mode;
|
||||
bool data_loss_confirmation;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u64_le title_id;
|
||||
INSERT_PADDING_BYTES(0x20);
|
||||
std::array<u8, 0x20> rating_age;
|
||||
std::array<char, 0x10> version_string;
|
||||
u64_le dlc_base_title_id;
|
||||
u64_le title_id_2;
|
||||
INSERT_PADDING_BYTES(0x28);
|
||||
u64_le normal_save_data_size;
|
||||
u64_le journal_sava_data_size;
|
||||
INSERT_PADDING_BYTES(0x18);
|
||||
u64_le product_code;
|
||||
u64_le title_id_3;
|
||||
std::array<u64_le, 0x7> title_id_array;
|
||||
INSERT_PADDING_BYTES(0x8);
|
||||
std::array<u64_le, 0x8> local_communication;
|
||||
u8 logo_type;
|
||||
u8 logo_handling;
|
||||
bool runtime_add_on_content_install;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
u64_le title_id_update;
|
||||
std::array<u8, 0x40> bcat_passphrase;
|
||||
INSERT_PADDING_BYTES(0xEC0);
|
||||
@@ -72,6 +85,7 @@ extern const std::array<const char*, 15> LANGUAGE_NAMES;
|
||||
// These store application name, dev name, title id, and other miscellaneous data.
|
||||
class NACP {
|
||||
public:
|
||||
explicit NACP();
|
||||
explicit NACP(VirtualFile file);
|
||||
~NACP();
|
||||
|
||||
@@ -81,10 +95,12 @@ public:
|
||||
u64 GetTitleId() const;
|
||||
u64 GetDLCBaseTitleId() const;
|
||||
std::string GetVersionString() const;
|
||||
u64 GetDefaultNormalSaveSize() const;
|
||||
u64 GetDefaultJournalSaveSize() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<RawNACP> raw;
|
||||
RawNACP raw{};
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -39,27 +39,4 @@ static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x31
|
||||
static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry.");
|
||||
static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
|
||||
|
||||
class DirectoryBackend : NonCopyable {
|
||||
public:
|
||||
DirectoryBackend() {}
|
||||
virtual ~DirectoryBackend() {}
|
||||
|
||||
/**
|
||||
* List files contained in the directory
|
||||
* @param count Number of entries to return at once in entries
|
||||
* @param entries Buffer to read data into
|
||||
* @return Number of entries listed
|
||||
*/
|
||||
virtual u64 Read(const u64 count, Entry* entries) = 0;
|
||||
|
||||
/// Returns the number of entries still left to read.
|
||||
virtual u64 GetEntryCount() const = 0;
|
||||
|
||||
/**
|
||||
* Close the directory
|
||||
* @return true if the directory closed correctly
|
||||
*/
|
||||
virtual bool Close() const = 0;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -40,6 +40,13 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
|
||||
aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
|
||||
const u64 read_size = aci_header.kac_size;
|
||||
const u64 read_offset = npdm_header.aci_offset + aci_header.kac_offset;
|
||||
if (file->ReadBytes(aci_kernel_capabilities.data(), read_size, read_offset) != read_size) {
|
||||
return Loader::ResultStatus::ErrorBadKernelCapabilityDescriptors;
|
||||
}
|
||||
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
@@ -71,6 +78,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const {
|
||||
return aci_file_access.permissions;
|
||||
}
|
||||
|
||||
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
|
||||
return aci_kernel_capabilities;
|
||||
}
|
||||
|
||||
void ProgramMetadata::Print() const {
|
||||
LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data());
|
||||
LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority);
|
||||
@@ -81,16 +92,20 @@ void ProgramMetadata::Print() const {
|
||||
LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
|
||||
npdm_header.has_64_bit_instructions ? "YES" : "NO");
|
||||
|
||||
auto address_space = "Unknown";
|
||||
const char* address_space = "Unknown";
|
||||
switch (npdm_header.address_space_type) {
|
||||
case ProgramAddressSpaceType::Is36Bit:
|
||||
address_space = "64-bit (36-bit address space)";
|
||||
break;
|
||||
case ProgramAddressSpaceType::Is39Bit:
|
||||
address_space = "64-bit";
|
||||
address_space = "64-bit (39-bit address space)";
|
||||
break;
|
||||
case ProgramAddressSpaceType::Is32Bit:
|
||||
case ProgramAddressSpaceType::Is32BitNoMap:
|
||||
address_space = "32-bit";
|
||||
break;
|
||||
case ProgramAddressSpaceType::Is32BitNoMap:
|
||||
address_space = "32-bit (no map region)";
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_FS, " > Address space: {}\n", address_space);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
@@ -38,6 +39,8 @@ enum class ProgramFilePermission : u64 {
|
||||
*/
|
||||
class ProgramMetadata {
|
||||
public:
|
||||
using KernelCapabilityDescriptors = std::vector<u32>;
|
||||
|
||||
ProgramMetadata();
|
||||
~ProgramMetadata();
|
||||
|
||||
@@ -50,6 +53,7 @@ public:
|
||||
u32 GetMainThreadStackSize() const;
|
||||
u64 GetTitleID() const;
|
||||
u64 GetFilesystemPermissions() const;
|
||||
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
|
||||
|
||||
void Print() const;
|
||||
|
||||
@@ -154,6 +158,8 @@ private:
|
||||
|
||||
FileAccessControl acid_file_access;
|
||||
FileAccessHeader aci_file_access;
|
||||
|
||||
KernelCapabilityDescriptors aci_kernel_capabilities;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -119,6 +119,9 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
|
||||
|
||||
VirtualDir out = std::move(root);
|
||||
|
||||
if (type == RomFSExtractionType::SingleDiscard)
|
||||
return out->GetSubdirectories().front();
|
||||
|
||||
while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
|
||||
if (out->GetSubdirectories().front()->GetName() == "data" &&
|
||||
type == RomFSExtractionType::Truncated)
|
||||
|
||||
@@ -33,8 +33,9 @@ struct IVFCHeader {
|
||||
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
|
||||
|
||||
enum class RomFSExtractionType {
|
||||
Full, // Includes data directory
|
||||
Truncated, // Traverses into data directory
|
||||
Full, // Includes data directory
|
||||
Truncated, // Traverses into data directory
|
||||
SingleDiscard, // Traverses into the first subdirectory of root
|
||||
};
|
||||
|
||||
// Converts a RomFS binary blob to VFS Filesystem
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
|
||||
|
||||
std::string SaveDataDescriptor::DebugInfo() const {
|
||||
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
|
||||
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
|
||||
@@ -132,4 +134,32 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
|
||||
}
|
||||
}
|
||||
|
||||
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
||||
u128 user_id) const {
|
||||
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
|
||||
|
||||
const auto size_file = dir->GetFile(SAVE_DATA_SIZE_FILENAME);
|
||||
if (size_file == nullptr || size_file->GetSize() < sizeof(SaveDataSize))
|
||||
return {0, 0};
|
||||
|
||||
SaveDataSize out;
|
||||
if (size_file->ReadObject(&out) != sizeof(SaveDataSize))
|
||||
return {0, 0};
|
||||
return out;
|
||||
}
|
||||
|
||||
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
||||
SaveDataSize new_value) {
|
||||
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
|
||||
|
||||
const auto size_file = dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
|
||||
if (size_file == nullptr)
|
||||
return;
|
||||
|
||||
size_file->Resize(sizeof(SaveDataSize));
|
||||
size_file->WriteObject(new_value);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -46,6 +46,11 @@ struct SaveDataDescriptor {
|
||||
};
|
||||
static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
|
||||
|
||||
struct SaveDataSize {
|
||||
u64 normal;
|
||||
u64 journal;
|
||||
};
|
||||
|
||||
/// File system interface to the SaveData archive
|
||||
class SaveDataFactory {
|
||||
public:
|
||||
@@ -60,6 +65,9 @@ public:
|
||||
static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
|
||||
u128 user_id, u64 save_id);
|
||||
|
||||
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
|
||||
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value);
|
||||
|
||||
private:
|
||||
VirtualDir dir;
|
||||
};
|
||||
|
||||
@@ -150,7 +150,7 @@ public:
|
||||
template <typename T>
|
||||
std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Write(data, number_elements * sizeof(T), offset);
|
||||
return Write(reinterpret_cast<const u8*>(data), number_elements * sizeof(T), offset);
|
||||
}
|
||||
|
||||
// Writes size bytes starting at memory location data to offset in file.
|
||||
@@ -166,7 +166,7 @@ public:
|
||||
template <typename T>
|
||||
std::size_t WriteObject(const T& data, std::size_t offset = 0) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Write(&data, sizeof(T), offset);
|
||||
return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset);
|
||||
}
|
||||
|
||||
// Renames the file to name. Returns whether or not the operation was successsful.
|
||||
|
||||
24
src/core/frontend/applets/web_browser.cpp
Normal file
24
src/core/frontend/applets/web_browser.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/frontend/applets/web_browser.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
WebBrowserApplet::~WebBrowserApplet() = default;
|
||||
|
||||
DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
|
||||
|
||||
void DefaultWebBrowserApplet::OpenPage(std::string_view filename,
|
||||
std::function<void()> unpack_romfs_callback,
|
||||
std::function<void()> finished_callback) {
|
||||
LOG_INFO(Service_AM,
|
||||
"(STUBBED) called - No suitable web browser implementation found to open website page "
|
||||
"at '{}'!",
|
||||
filename);
|
||||
finished_callback();
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
28
src/core/frontend/applets/web_browser.h
Normal file
28
src/core/frontend/applets/web_browser.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class WebBrowserApplet {
|
||||
public:
|
||||
virtual ~WebBrowserApplet();
|
||||
|
||||
virtual void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
|
||||
std::function<void()> finished_callback) = 0;
|
||||
};
|
||||
|
||||
class DefaultWebBrowserApplet final : public WebBrowserApplet {
|
||||
public:
|
||||
~DefaultWebBrowserApplet() override;
|
||||
|
||||
void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
|
||||
std::function<void()> finished_callback) override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -12,6 +12,23 @@
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
/**
|
||||
* Represents a graphics context that can be used for background computation or drawing. If the
|
||||
* graphics backend doesn't require the context, then the implementation of these methods can be
|
||||
* stubs
|
||||
*/
|
||||
class GraphicsContext {
|
||||
public:
|
||||
/// Makes the graphics context current for the caller thread
|
||||
virtual void MakeCurrent() = 0;
|
||||
|
||||
/// Releases (dunno if this is the "right" word) the context from the caller thread
|
||||
virtual void DoneCurrent() = 0;
|
||||
|
||||
/// Swap buffers to display the next frame
|
||||
virtual void SwapBuffers() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstraction class used to provide an interface between emulation code and the frontend
|
||||
* (e.g. SDL, QGLWidget, GLFW, etc...).
|
||||
@@ -30,7 +47,7 @@ namespace Core::Frontend {
|
||||
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
|
||||
* re-read the upper points again and think about it if you don't see this.
|
||||
*/
|
||||
class EmuWindow {
|
||||
class EmuWindow : public GraphicsContext {
|
||||
public:
|
||||
/// Data structure to store emuwindow configuration
|
||||
struct WindowConfig {
|
||||
@@ -40,17 +57,21 @@ public:
|
||||
std::pair<unsigned, unsigned> min_client_area_size;
|
||||
};
|
||||
|
||||
/// Swap buffers to display the next frame
|
||||
virtual void SwapBuffers() = 0;
|
||||
|
||||
/// Polls window events
|
||||
virtual void PollEvents() = 0;
|
||||
|
||||
/// Makes the graphics context current for the caller thread
|
||||
virtual void MakeCurrent() = 0;
|
||||
|
||||
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
|
||||
virtual void DoneCurrent() = 0;
|
||||
/**
|
||||
* Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
|
||||
* context can be used from other threads for background graphics computation. If the frontend
|
||||
* is using a graphics backend that doesn't need anything specific to run on a different thread,
|
||||
* then it can use a stubbed implemenation for GraphicsContext.
|
||||
*
|
||||
* If the return value is null, then the core should assume that the frontend cannot provide a
|
||||
* Shared Context
|
||||
*/
|
||||
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Kernel {
|
||||
// Confirmed Switch kernel error codes
|
||||
|
||||
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
|
||||
constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
|
||||
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
|
||||
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
|
||||
@@ -27,9 +28,10 @@ constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
|
||||
constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
|
||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
|
||||
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
|
||||
constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
|
||||
constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122};
|
||||
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
|
||||
constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
|
||||
constexpr ResultCode ERR_RESERVED_VALUE{ErrorModule::Kernel, 126};
|
||||
constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -43,6 +43,9 @@ enum KernelHandle : Handle {
|
||||
*/
|
||||
class HandleTable final : NonCopyable {
|
||||
public:
|
||||
/// This is the maximum limit of handles allowed per process in Horizon
|
||||
static constexpr std::size_t MAX_COUNT = 1024;
|
||||
|
||||
HandleTable();
|
||||
~HandleTable();
|
||||
|
||||
@@ -91,9 +94,6 @@ public:
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
/// This is the maximum limit of handles allowed per process in Horizon
|
||||
static constexpr std::size_t MAX_COUNT = 1024;
|
||||
|
||||
/// Stores the Object referenced by the handle or null if the slot is empty.
|
||||
std::array<SharedPtr<Object>, MAX_COUNT> objects;
|
||||
|
||||
|
||||
@@ -22,11 +22,16 @@
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
SessionRequestHandler::SessionRequestHandler() = default;
|
||||
|
||||
SessionRequestHandler::~SessionRequestHandler() = default;
|
||||
|
||||
void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) {
|
||||
server_session->SetHleHandler(shared_from_this());
|
||||
connected_sessions.push_back(std::move(server_session));
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Service {
|
||||
class ServiceFrameworkBase;
|
||||
@@ -27,9 +25,13 @@ class Domain;
|
||||
class HandleTable;
|
||||
class HLERequestContext;
|
||||
class Process;
|
||||
class ServerSession;
|
||||
class Thread;
|
||||
class ReadableEvent;
|
||||
class WritableEvent;
|
||||
|
||||
enum class ThreadWakeupReason;
|
||||
|
||||
/**
|
||||
* Interface implemented by HLE Session handlers.
|
||||
* This can be provided to a ServerSession in order to hook into several relevant events
|
||||
@@ -37,7 +39,8 @@ class WritableEvent;
|
||||
*/
|
||||
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
|
||||
public:
|
||||
virtual ~SessionRequestHandler() = default;
|
||||
SessionRequestHandler();
|
||||
virtual ~SessionRequestHandler();
|
||||
|
||||
/**
|
||||
* Handles a sync request from the emulated application.
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
@@ -36,7 +36,6 @@ enum class HandleType : u32 {
|
||||
enum class ResetType {
|
||||
OneShot, ///< Reset automatically on object acquisition
|
||||
Sticky, ///< Never reset automatically
|
||||
Pulse, ///< Reset automatically on wakeup
|
||||
};
|
||||
|
||||
class Object : NonCopyable {
|
||||
|
||||
@@ -20,6 +20,35 @@
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
/**
|
||||
* Sets up the primary application thread
|
||||
*
|
||||
* @param owner_process The parent process for the main thread
|
||||
* @param kernel The kernel instance to create the main thread under.
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The priority to give the main thread
|
||||
*/
|
||||
void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
|
||||
// Setup page table so we can write to memory
|
||||
SetCurrentPageTable(&owner_process.VMManager().page_table);
|
||||
|
||||
// Initialize new "main" thread
|
||||
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
|
||||
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
|
||||
owner_process.GetIdealCore(), stack_top, owner_process);
|
||||
|
||||
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
// Register 1 must be a handle to the main thread
|
||||
const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
|
||||
thread->SetGuestHandle(guest_handle);
|
||||
thread->GetContext().cpu_registers[1] = guest_handle;
|
||||
|
||||
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
|
||||
thread->ResumeFromWait();
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
CodeSet::CodeSet() = default;
|
||||
CodeSet::~CodeSet() = default;
|
||||
@@ -28,13 +57,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
|
||||
SharedPtr<Process> process(new Process(kernel));
|
||||
|
||||
process->name = std::move(name);
|
||||
process->flags.raw = 0;
|
||||
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
|
||||
process->resource_limit = kernel.GetSystemResourceLimit();
|
||||
process->status = ProcessStatus::Created;
|
||||
process->program_id = 0;
|
||||
process->process_id = kernel.CreateNewProcessID();
|
||||
process->svc_access_mask.set();
|
||||
process->capabilities.InitializeForMetadatalessProcess();
|
||||
|
||||
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
|
||||
std::uniform_int_distribution<u64> distribution;
|
||||
@@ -64,83 +91,15 @@ ResultCode Process::ClearSignalState() {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
||||
ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
||||
program_id = metadata.GetTitleID();
|
||||
ideal_processor = metadata.GetMainThreadCore();
|
||||
ideal_core = metadata.GetMainThreadCore();
|
||||
is_64bit_process = metadata.Is64BitProgram();
|
||||
|
||||
vm_manager.Reset(metadata.GetAddressSpaceType());
|
||||
}
|
||||
|
||||
void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
u32 descriptor = kernel_caps[i];
|
||||
u32 type = descriptor >> 20;
|
||||
|
||||
if (descriptor == 0xFFFFFFFF) {
|
||||
// Unused descriptor entry
|
||||
continue;
|
||||
} else if ((type & 0xF00) == 0xE00) { // 0x0FFF
|
||||
// Allowed interrupts list
|
||||
LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored");
|
||||
} else if ((type & 0xF80) == 0xF00) { // 0x07FF
|
||||
// Allowed syscalls mask
|
||||
unsigned int index = ((descriptor >> 24) & 7) * 24;
|
||||
u32 bits = descriptor & 0xFFFFFF;
|
||||
|
||||
while (bits && index < svc_access_mask.size()) {
|
||||
svc_access_mask.set(index, bits & 1);
|
||||
++index;
|
||||
bits >>= 1;
|
||||
}
|
||||
} else if ((type & 0xFF0) == 0xFE0) { // 0x00FF
|
||||
// Handle table size
|
||||
handle_table_size = descriptor & 0x3FF;
|
||||
} else if ((type & 0xFF8) == 0xFF0) { // 0x007F
|
||||
// Misc. flags
|
||||
flags.raw = descriptor & 0xFFFF;
|
||||
} else if ((type & 0xFFE) == 0xFF8) { // 0x001F
|
||||
// Mapped memory range
|
||||
if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) {
|
||||
LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored.");
|
||||
continue;
|
||||
}
|
||||
u32 end_desc = kernel_caps[i + 1];
|
||||
++i; // Skip over the second descriptor on the next iteration
|
||||
|
||||
AddressMapping mapping;
|
||||
mapping.address = descriptor << 12;
|
||||
VAddr end_address = end_desc << 12;
|
||||
|
||||
if (mapping.address < end_address) {
|
||||
mapping.size = end_address - mapping.address;
|
||||
} else {
|
||||
mapping.size = 0;
|
||||
}
|
||||
|
||||
mapping.read_only = (descriptor & (1 << 20)) != 0;
|
||||
mapping.unk_flag = (end_desc & (1 << 20)) != 0;
|
||||
|
||||
address_mappings.push_back(mapping);
|
||||
} else if ((type & 0xFFF) == 0xFFE) { // 0x000F
|
||||
// Mapped memory page
|
||||
AddressMapping mapping;
|
||||
mapping.address = descriptor << 12;
|
||||
mapping.size = Memory::PAGE_SIZE;
|
||||
mapping.read_only = false;
|
||||
mapping.unk_flag = false;
|
||||
|
||||
address_mappings.push_back(mapping);
|
||||
} else if ((type & 0xFE0) == 0xFC0) { // 0x01FF
|
||||
// Kernel version
|
||||
kernel_version = descriptor & 0xFFFF;
|
||||
|
||||
int minor = kernel_version & 0xFF;
|
||||
int major = (kernel_version >> 8) & 0xFF;
|
||||
LOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor);
|
||||
} else {
|
||||
LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor);
|
||||
}
|
||||
}
|
||||
const auto& caps = metadata.GetKernelCapabilities();
|
||||
return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager);
|
||||
}
|
||||
|
||||
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
|
||||
@@ -156,7 +115,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
|
||||
vm_manager.LogLayout();
|
||||
ChangeStatus(ProcessStatus::Running);
|
||||
|
||||
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
|
||||
SetupMainThread(*this, kernel, entry_point, main_thread_priority);
|
||||
}
|
||||
|
||||
void Process::PrepareForTermination() {
|
||||
@@ -268,22 +227,6 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
||||
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
|
||||
}
|
||||
|
||||
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
|
||||
return vm_manager.HeapAllocate(target, size, perms);
|
||||
}
|
||||
|
||||
ResultCode Process::HeapFree(VAddr target, u32 size) {
|
||||
return vm_manager.HeapFree(target, size);
|
||||
}
|
||||
|
||||
ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
|
||||
return vm_manager.MirrorMemory(dst_addr, src_addr, size, state);
|
||||
}
|
||||
|
||||
ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
|
||||
return vm_manager.UnmapRange(dst_addr, size);
|
||||
}
|
||||
|
||||
Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
Kernel::Process::~Process() {}
|
||||
|
||||
|
||||
@@ -11,10 +11,9 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/process_capability.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -27,6 +26,7 @@ namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class ResourceLimit;
|
||||
class Thread;
|
||||
|
||||
struct AddressMapping {
|
||||
// Address and size must be page-aligned
|
||||
@@ -42,24 +42,6 @@ enum class MemoryRegion : u16 {
|
||||
BASE = 3,
|
||||
};
|
||||
|
||||
union ProcessFlags {
|
||||
u16 raw;
|
||||
|
||||
BitField<0, 1, u16>
|
||||
allow_debug; ///< Allows other processes to attach to and debug this process.
|
||||
BitField<1, 1, u16> force_debug; ///< Allows this process to attach to processes even if they
|
||||
/// don't have allow_debug set.
|
||||
BitField<2, 1, u16> allow_nonalphanum;
|
||||
BitField<3, 1, u16> shared_page_writable; ///< Shared page is mapped with write permissions.
|
||||
BitField<4, 1, u16> privileged_priority; ///< Can use priority levels higher than 24.
|
||||
BitField<5, 1, u16> allow_main_args;
|
||||
BitField<6, 1, u16> shared_device_mem;
|
||||
BitField<7, 1, u16> runnable_on_sleep;
|
||||
BitField<8, 4, MemoryRegion>
|
||||
memory_region; ///< Default region for memory allocations for this process
|
||||
BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates the status of a Process instance.
|
||||
*
|
||||
@@ -186,19 +168,19 @@ public:
|
||||
/// Gets the resource limit descriptor for this process
|
||||
SharedPtr<ResourceLimit> GetResourceLimit() const;
|
||||
|
||||
/// Gets the default CPU ID for this process
|
||||
u8 GetDefaultProcessorID() const {
|
||||
return ideal_processor;
|
||||
/// Gets the ideal CPU core ID for this process
|
||||
u8 GetIdealCore() const {
|
||||
return ideal_core;
|
||||
}
|
||||
|
||||
/// Gets the bitmask of allowed CPUs that this process' threads can run on.
|
||||
u32 GetAllowedProcessorMask() const {
|
||||
return allowed_processor_mask;
|
||||
/// Gets the bitmask of allowed cores that this process' threads can run on.
|
||||
u64 GetCoreMask() const {
|
||||
return capabilities.GetCoreMask();
|
||||
}
|
||||
|
||||
/// Gets the bitmask of allowed thread priorities.
|
||||
u32 GetAllowedThreadPriorityMask() const {
|
||||
return allowed_thread_priority_mask;
|
||||
u64 GetPriorityMask() const {
|
||||
return capabilities.GetPriorityMask();
|
||||
}
|
||||
|
||||
u32 IsVirtualMemoryEnabled() const {
|
||||
@@ -239,15 +221,12 @@ public:
|
||||
* Loads process-specifics configuration info with metadata provided
|
||||
* by an executable.
|
||||
*
|
||||
* @param metadata The provided metadata to load process specific info.
|
||||
* @param metadata The provided metadata to load process specific info from.
|
||||
*
|
||||
* @returns RESULT_SUCCESS if all relevant metadata was able to be
|
||||
* loaded and parsed. Otherwise, an error code is returned.
|
||||
*/
|
||||
void LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
|
||||
|
||||
/**
|
||||
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
|
||||
* to this process.
|
||||
*/
|
||||
void ParseKernelCaps(const u32* kernel_caps, std::size_t len);
|
||||
ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
|
||||
|
||||
/**
|
||||
* Applies address space changes and launches the process main thread.
|
||||
@@ -263,7 +242,7 @@ public:
|
||||
void LoadModule(CodeSet module_, VAddr base_addr);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Memory Management
|
||||
// Thread-local storage management
|
||||
|
||||
// Marks the next available region as used and returns the address of the slot.
|
||||
VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
|
||||
@@ -271,13 +250,6 @@ public:
|
||||
// Frees a used TLS slot identified by the given address
|
||||
void FreeTLSSlot(VAddr tls_address);
|
||||
|
||||
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
|
||||
ResultCode HeapFree(VAddr target, u32 size);
|
||||
|
||||
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state);
|
||||
|
||||
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
|
||||
|
||||
private:
|
||||
explicit Process(KernelCore& kernel);
|
||||
~Process() override;
|
||||
@@ -308,22 +280,8 @@ private:
|
||||
/// Resource limit descriptor for this process
|
||||
SharedPtr<ResourceLimit> resource_limit;
|
||||
|
||||
/// The process may only call SVCs which have the corresponding bit set.
|
||||
std::bitset<0x80> svc_access_mask;
|
||||
/// Maximum size of the handle table for the process.
|
||||
u32 handle_table_size = 0x200;
|
||||
/// Special memory ranges mapped into this processes address space. This is used to give
|
||||
/// processes access to specific I/O regions and device memory.
|
||||
boost::container::static_vector<AddressMapping, 8> address_mappings;
|
||||
ProcessFlags flags;
|
||||
/// Kernel compatibility version for this process
|
||||
u16 kernel_version = 0;
|
||||
/// The default CPU for this process, threads are scheduled on this cpu by default.
|
||||
u8 ideal_processor = 0;
|
||||
/// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse
|
||||
/// this value from the process header.
|
||||
u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
|
||||
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
|
||||
/// The ideal CPU core for this process, threads are scheduled on this core by default.
|
||||
u8 ideal_core = 0;
|
||||
u32 is_virtual_address_memory_enabled = 0;
|
||||
|
||||
/// The Thread Local Storage area is allocated as processes create threads,
|
||||
@@ -333,6 +291,9 @@ private:
|
||||
/// This vector will grow as more pages are allocated for new threads.
|
||||
std::vector<std::bitset<8>> tls_slots;
|
||||
|
||||
/// Contains the parsed process capability descriptors.
|
||||
ProcessCapabilities capabilities;
|
||||
|
||||
/// Whether or not this process is AArch64, or AArch32.
|
||||
/// By default, we currently assume this is true, unless otherwise
|
||||
/// specified by metadata provided to the process during loading.
|
||||
|
||||
355
src/core/hle/kernel/process_capability.cpp
Normal file
355
src/core/hle/kernel/process_capability.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/bit_util.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/process_capability.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
|
||||
// clang-format off
|
||||
|
||||
// Shift offsets for kernel capability types.
|
||||
enum : u32 {
|
||||
CapabilityOffset_PriorityAndCoreNum = 3,
|
||||
CapabilityOffset_Syscall = 4,
|
||||
CapabilityOffset_MapPhysical = 6,
|
||||
CapabilityOffset_MapIO = 7,
|
||||
CapabilityOffset_Interrupt = 11,
|
||||
CapabilityOffset_ProgramType = 13,
|
||||
CapabilityOffset_KernelVersion = 14,
|
||||
CapabilityOffset_HandleTableSize = 15,
|
||||
CapabilityOffset_Debug = 16,
|
||||
};
|
||||
|
||||
// Combined mask of all parameters that may be initialized only once.
|
||||
constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) |
|
||||
(1U << CapabilityOffset_ProgramType) |
|
||||
(1U << CapabilityOffset_KernelVersion) |
|
||||
(1U << CapabilityOffset_HandleTableSize) |
|
||||
(1U << CapabilityOffset_Debug);
|
||||
|
||||
// Packed kernel version indicating 10.4.0
|
||||
constexpr u32 PackedKernelVersion = 0x520000;
|
||||
|
||||
// Indicates possible types of capabilities that can be specified.
|
||||
enum class CapabilityType : u32 {
|
||||
Unset = 0U,
|
||||
PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1,
|
||||
Syscall = (1U << CapabilityOffset_Syscall) - 1,
|
||||
MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1,
|
||||
MapIO = (1U << CapabilityOffset_MapIO) - 1,
|
||||
Interrupt = (1U << CapabilityOffset_Interrupt) - 1,
|
||||
ProgramType = (1U << CapabilityOffset_ProgramType) - 1,
|
||||
KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1,
|
||||
HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1,
|
||||
Debug = (1U << CapabilityOffset_Debug) - 1,
|
||||
Ignorable = 0xFFFFFFFFU,
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
|
||||
constexpr CapabilityType GetCapabilityType(u32 value) {
|
||||
return static_cast<CapabilityType>((~value & (value + 1)) - 1);
|
||||
}
|
||||
|
||||
u32 GetFlagBitOffset(CapabilityType type) {
|
||||
const auto value = static_cast<u32>(type);
|
||||
return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value));
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
|
||||
std::size_t num_capabilities,
|
||||
VMManager& vm_manager) {
|
||||
Clear();
|
||||
|
||||
// Allow all cores and priorities.
|
||||
core_mask = 0xF;
|
||||
priority_mask = 0xFFFFFFFFFFFFFFFF;
|
||||
kernel_version = PackedKernelVersion;
|
||||
|
||||
return ParseCapabilities(capabilities, num_capabilities, vm_manager);
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
|
||||
std::size_t num_capabilities,
|
||||
VMManager& vm_manager) {
|
||||
Clear();
|
||||
|
||||
return ParseCapabilities(capabilities, num_capabilities, vm_manager);
|
||||
}
|
||||
|
||||
void ProcessCapabilities::InitializeForMetadatalessProcess() {
|
||||
// Allow all cores and priorities
|
||||
core_mask = 0xF;
|
||||
priority_mask = 0xFFFFFFFFFFFFFFFF;
|
||||
kernel_version = PackedKernelVersion;
|
||||
|
||||
// Allow all system calls and interrupts.
|
||||
svc_capabilities.set();
|
||||
interrupt_capabilities.set();
|
||||
|
||||
// Allow using the maximum possible amount of handles
|
||||
handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT);
|
||||
|
||||
// Allow all debugging capabilities.
|
||||
is_debuggable = true;
|
||||
can_force_debug = true;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
|
||||
std::size_t num_capabilities,
|
||||
VMManager& vm_manager) {
|
||||
u32 set_flags = 0;
|
||||
u32 set_svc_bits = 0;
|
||||
|
||||
for (std::size_t i = 0; i < num_capabilities; ++i) {
|
||||
const u32 descriptor = capabilities[i];
|
||||
const auto type = GetCapabilityType(descriptor);
|
||||
|
||||
if (type == CapabilityType::MapPhysical) {
|
||||
i++;
|
||||
|
||||
// The MapPhysical type uses two descriptor flags for its parameters.
|
||||
// If there's only one, then there's a problem.
|
||||
if (i >= num_capabilities) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
const auto size_flags = capabilities[i];
|
||||
if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager);
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
const auto result =
|
||||
ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager);
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits,
|
||||
u32 flag, VMManager& vm_manager) {
|
||||
const auto type = GetCapabilityType(flag);
|
||||
|
||||
if (type == CapabilityType::Unset) {
|
||||
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||
}
|
||||
|
||||
// Bail early on ignorable entries, as one would expect,
|
||||
// ignorable descriptors can be ignored.
|
||||
if (type == CapabilityType::Ignorable) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// Ensure that the give flag hasn't already been initialized before.
|
||||
// If it has been, then bail.
|
||||
const u32 flag_length = GetFlagBitOffset(type);
|
||||
const u32 set_flag = 1U << flag_length;
|
||||
if ((set_flag & set_flags & InitializeOnceMask) != 0) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
set_flags |= set_flag;
|
||||
|
||||
switch (type) {
|
||||
case CapabilityType::PriorityAndCoreNum:
|
||||
return HandlePriorityCoreNumFlags(flag);
|
||||
case CapabilityType::Syscall:
|
||||
return HandleSyscallFlags(set_svc_bits, flag);
|
||||
case CapabilityType::MapIO:
|
||||
return HandleMapIOFlags(flag, vm_manager);
|
||||
case CapabilityType::Interrupt:
|
||||
return HandleInterruptFlags(flag);
|
||||
case CapabilityType::ProgramType:
|
||||
return HandleProgramTypeFlags(flag);
|
||||
case CapabilityType::KernelVersion:
|
||||
return HandleKernelVersionFlags(flag);
|
||||
case CapabilityType::HandleTableSize:
|
||||
return HandleHandleTableFlags(flag);
|
||||
case CapabilityType::Debug:
|
||||
return HandleDebugFlags(flag);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||
}
|
||||
|
||||
void ProcessCapabilities::Clear() {
|
||||
svc_capabilities.reset();
|
||||
interrupt_capabilities.reset();
|
||||
|
||||
core_mask = 0;
|
||||
priority_mask = 0;
|
||||
|
||||
handle_table_size = 0;
|
||||
kernel_version = 0;
|
||||
|
||||
program_type = ProgramType::SysModule;
|
||||
|
||||
is_debuggable = false;
|
||||
can_force_debug = false;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
|
||||
if (priority_mask != 0 || core_mask != 0) {
|
||||
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||
}
|
||||
|
||||
const u32 core_num_min = (flags >> 16) & 0xFF;
|
||||
const u32 core_num_max = (flags >> 24) & 0xFF;
|
||||
if (core_num_min > core_num_max) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
const u32 priority_min = (flags >> 10) & 0x3F;
|
||||
const u32 priority_max = (flags >> 4) & 0x3F;
|
||||
if (priority_min > priority_max) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
// The switch only has 4 usable cores.
|
||||
if (core_num_max >= 4) {
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
const auto make_mask = [](u64 min, u64 max) {
|
||||
const u64 range = max - min + 1;
|
||||
const u64 mask = (1ULL << range) - 1;
|
||||
|
||||
return mask << min;
|
||||
};
|
||||
|
||||
core_mask = make_mask(core_num_min, core_num_max);
|
||||
priority_mask = make_mask(priority_min, priority_max);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
|
||||
const u32 index = flags >> 29;
|
||||
const u32 svc_bit = 1U << index;
|
||||
|
||||
// If we've already set this svc before, bail.
|
||||
if ((set_svc_bits & svc_bit) != 0) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
set_svc_bits |= svc_bit;
|
||||
|
||||
const u32 svc_mask = (flags >> 5) & 0xFFFFFF;
|
||||
for (u32 i = 0; i < 24; ++i) {
|
||||
const u32 svc_number = index * 24 + i;
|
||||
|
||||
if ((svc_mask & (1U << i)) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (svc_number >= svc_capabilities.size()) {
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
svc_capabilities[svc_number] = true;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
|
||||
VMManager& vm_manager) {
|
||||
// TODO(Lioncache): Implement once the memory manager can handle this.
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) {
|
||||
// TODO(Lioncache): Implement once the memory manager can handle this.
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
|
||||
constexpr u32 interrupt_ignore_value = 0x3FF;
|
||||
const u32 interrupt0 = (flags >> 12) & 0x3FF;
|
||||
const u32 interrupt1 = (flags >> 22) & 0x3FF;
|
||||
|
||||
for (u32 interrupt : {interrupt0, interrupt1}) {
|
||||
if (interrupt == interrupt_ignore_value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
// This should be checking a generic interrupt controller value
|
||||
// as part of the calculation, however, given we don't currently
|
||||
// emulate that, it's sufficient to mark every interrupt as defined.
|
||||
|
||||
if (interrupt >= interrupt_capabilities.size()) {
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
interrupt_capabilities[interrupt] = true;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
|
||||
const u32 reserved = flags >> 17;
|
||||
if (reserved != 0) {
|
||||
return ERR_RESERVED_VALUE;
|
||||
}
|
||||
|
||||
program_type = static_cast<ProgramType>((flags >> 14) & 0b111);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
|
||||
// Yes, the internal member variable is checked in the actual kernel here.
|
||||
// This might look odd for options that are only allowed to be initialized
|
||||
// just once, however the kernel has a separate initialization function for
|
||||
// kernel processes and userland processes. The kernel variant sets this
|
||||
// member variable ahead of time.
|
||||
|
||||
const u32 major_version = kernel_version >> 19;
|
||||
|
||||
if (major_version != 0 || flags < 0x80000) {
|
||||
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
|
||||
}
|
||||
|
||||
kernel_version = flags;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
|
||||
const u32 reserved = flags >> 26;
|
||||
if (reserved != 0) {
|
||||
return ERR_RESERVED_VALUE;
|
||||
}
|
||||
|
||||
handle_table_size = (flags >> 16) & 0x3FF;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
|
||||
const u32 reserved = flags >> 19;
|
||||
if (reserved != 0) {
|
||||
return ERR_RESERVED_VALUE;
|
||||
}
|
||||
|
||||
is_debuggable = (flags & 0x20000) != 0;
|
||||
can_force_debug = (flags & 0x40000) != 0;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
264
src/core/hle/kernel/process_capability.h
Normal file
264
src/core/hle/kernel/process_capability.h
Normal file
@@ -0,0 +1,264 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class VMManager;
|
||||
|
||||
/// The possible types of programs that may be indicated
|
||||
/// by the program type capability descriptor.
|
||||
enum class ProgramType {
|
||||
SysModule,
|
||||
Application,
|
||||
Applet,
|
||||
};
|
||||
|
||||
/// Handles kernel capability descriptors that are provided by
|
||||
/// application metadata. These descriptors provide information
|
||||
/// that alters certain parameters for kernel process instance
|
||||
/// that will run said application (or applet).
|
||||
///
|
||||
/// Capabilities are a sequence of flag descriptors, that indicate various
|
||||
/// configurations and constraints for a particular process.
|
||||
///
|
||||
/// Flag types are indicated by a sequence of set low bits. E.g. the
|
||||
/// types are indicated with the low bits as follows (where x indicates "don't care"):
|
||||
///
|
||||
/// - Priority and core mask : 0bxxxxxxxxxxxx0111
|
||||
/// - Allowed service call mask: 0bxxxxxxxxxxx01111
|
||||
/// - Map physical memory : 0bxxxxxxxxx0111111
|
||||
/// - Map IO memory : 0bxxxxxxxx01111111
|
||||
/// - Interrupts : 0bxxxx011111111111
|
||||
/// - Application type : 0bxx01111111111111
|
||||
/// - Kernel version : 0bx011111111111111
|
||||
/// - Handle table size : 0b0111111111111111
|
||||
/// - Debugger flags : 0b1111111111111111
|
||||
///
|
||||
/// These are essentially a bit offset subtracted by 1 to create a mask.
|
||||
/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000)
|
||||
/// subtracted by one (7 -> 0b0111)
|
||||
///
|
||||
/// An example of a bit layout (using the map physical layout):
|
||||
/// <example>
|
||||
/// The MapPhysical type indicates a sequence entry pair of:
|
||||
///
|
||||
/// [initial, memory_flags], where:
|
||||
///
|
||||
/// initial:
|
||||
/// bits:
|
||||
/// 7-24: Starting page to map memory at.
|
||||
/// 25 : Indicates if the memory should be mapped as read only.
|
||||
///
|
||||
/// memory_flags:
|
||||
/// bits:
|
||||
/// 7-20 : Number of pages to map
|
||||
/// 21-25: Seems to be reserved (still checked against though)
|
||||
/// 26 : Whether or not the memory being mapped is IO memory, or physical memory
|
||||
/// </example>
|
||||
///
|
||||
class ProcessCapabilities {
|
||||
public:
|
||||
using InterruptCapabilities = std::bitset<1024>;
|
||||
using SyscallCapabilities = std::bitset<128>;
|
||||
|
||||
ProcessCapabilities() = default;
|
||||
ProcessCapabilities(const ProcessCapabilities&) = delete;
|
||||
ProcessCapabilities(ProcessCapabilities&&) = default;
|
||||
|
||||
ProcessCapabilities& operator=(const ProcessCapabilities&) = delete;
|
||||
ProcessCapabilities& operator=(ProcessCapabilities&&) = default;
|
||||
|
||||
/// Initializes this process capabilities instance for a kernel process.
|
||||
///
|
||||
/// @param capabilities The capabilities to parse
|
||||
/// @param num_capabilities The number of capabilities to parse.
|
||||
/// @param vm_manager The memory manager to use for handling any mapping-related
|
||||
/// operations (such as mapping IO memory, etc).
|
||||
///
|
||||
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
|
||||
/// otherwise, an error code upon failure.
|
||||
///
|
||||
ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
|
||||
VMManager& vm_manager);
|
||||
|
||||
/// Initializes this process capabilities instance for a userland process.
|
||||
///
|
||||
/// @param capabilities The capabilities to parse.
|
||||
/// @param num_capabilities The total number of capabilities to parse.
|
||||
/// @param vm_manager The memory manager to use for handling any mapping-related
|
||||
/// operations (such as mapping IO memory, etc).
|
||||
///
|
||||
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
|
||||
/// otherwise, an error code upon failure.
|
||||
///
|
||||
ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
|
||||
VMManager& vm_manager);
|
||||
|
||||
/// Initializes this process capabilities instance for a process that does not
|
||||
/// have any metadata to parse.
|
||||
///
|
||||
/// This is necessary, as we allow running raw executables, and the internal
|
||||
/// kernel process capabilities also determine what CPU cores the process is
|
||||
/// allowed to run on, and what priorities are allowed for threads. It also
|
||||
/// determines the max handle table size, what the program type is, whether or
|
||||
/// not the process can be debugged, or whether it's possible for a process to
|
||||
/// forcibly debug another process.
|
||||
///
|
||||
/// Given the above, this essentially enables all capabilities across the board
|
||||
/// for the process. It allows the process to:
|
||||
///
|
||||
/// - Run on any core
|
||||
/// - Use any thread priority
|
||||
/// - Use the maximum amount of handles a process is allowed to.
|
||||
/// - Be debuggable
|
||||
/// - Forcibly debug other processes.
|
||||
///
|
||||
/// Note that this is not a behavior that the kernel allows a process to do via
|
||||
/// a single function like this. This is yuzu-specific behavior to handle
|
||||
/// executables with no capability descriptors whatsoever to derive behavior from.
|
||||
/// It being yuzu-specific is why this is also not the default behavior and not
|
||||
/// done by default in the constructor.
|
||||
///
|
||||
void InitializeForMetadatalessProcess();
|
||||
|
||||
/// Gets the allowable core mask
|
||||
u64 GetCoreMask() const {
|
||||
return core_mask;
|
||||
}
|
||||
|
||||
/// Gets the allowable priority mask
|
||||
u64 GetPriorityMask() const {
|
||||
return priority_mask;
|
||||
}
|
||||
|
||||
/// Gets the SVC access permission bits
|
||||
const SyscallCapabilities& GetServiceCapabilities() const {
|
||||
return svc_capabilities;
|
||||
}
|
||||
|
||||
/// Gets the valid interrupt bits.
|
||||
const InterruptCapabilities& GetInterruptCapabilities() const {
|
||||
return interrupt_capabilities;
|
||||
}
|
||||
|
||||
/// Gets the program type for this process.
|
||||
ProgramType GetProgramType() const {
|
||||
return program_type;
|
||||
}
|
||||
|
||||
/// Gets the number of total allowable handles for the process' handle table.
|
||||
u32 GetHandleTableSize() const {
|
||||
return handle_table_size;
|
||||
}
|
||||
|
||||
/// Gets the kernel version value.
|
||||
u32 GetKernelVersion() const {
|
||||
return kernel_version;
|
||||
}
|
||||
|
||||
/// Whether or not this process can be debugged.
|
||||
bool IsDebuggable() const {
|
||||
return is_debuggable;
|
||||
}
|
||||
|
||||
/// Whether or not this process can forcibly debug another
|
||||
/// process, even if that process is not considered debuggable.
|
||||
bool CanForceDebug() const {
|
||||
return can_force_debug;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Attempts to parse a given sequence of capability descriptors.
|
||||
///
|
||||
/// @param capabilities The sequence of capability descriptors to parse.
|
||||
/// @param num_capabilities The number of descriptors within the given sequence.
|
||||
/// @param vm_manager The memory manager that will perform any memory
|
||||
/// mapping if necessary.
|
||||
///
|
||||
/// @return RESULT_SUCCESS if no errors occur, otherwise an error code.
|
||||
///
|
||||
ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
|
||||
VMManager& vm_manager);
|
||||
|
||||
/// Attempts to parse a capability descriptor that is only represented by a
|
||||
/// single flag set.
|
||||
///
|
||||
/// @param set_flags Running set of flags that are used to catch
|
||||
/// flags being initialized more than once when they shouldn't be.
|
||||
/// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.
|
||||
/// @param flag The flag to attempt to parse.
|
||||
/// @param vm_manager The memory manager that will perform any memory
|
||||
/// mapping if necessary.
|
||||
///
|
||||
/// @return RESULT_SUCCESS if no errors occurred, otherwise an error code.
|
||||
///
|
||||
ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
|
||||
VMManager& vm_manager);
|
||||
|
||||
/// Clears the internal state of this process capability instance. Necessary,
|
||||
/// to have a sane starting point due to us allowing running executables without
|
||||
/// configuration metadata. We assume a process is not going to have metadata,
|
||||
/// and if it turns out that the process does, in fact, have metadata, then
|
||||
/// we attempt to parse it. Thus, we need this to reset data members back to
|
||||
/// a good state.
|
||||
///
|
||||
/// DO NOT ever make this a public member function. This isn't an invariant
|
||||
/// anything external should depend upon (and if anything comes to rely on it,
|
||||
/// you should immediately be questioning the design of that thing, not this
|
||||
/// class. If the kernel itself can run without depending on behavior like that,
|
||||
/// then so can yuzu).
|
||||
///
|
||||
void Clear();
|
||||
|
||||
/// Handles flags related to the priority and core number capability flags.
|
||||
ResultCode HandlePriorityCoreNumFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to determining the allowable SVC mask.
|
||||
ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);
|
||||
|
||||
/// Handles flags related to mapping physical memory pages.
|
||||
ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager);
|
||||
|
||||
/// Handles flags related to mapping IO pages.
|
||||
ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager);
|
||||
|
||||
/// Handles flags related to the interrupt capability flags.
|
||||
ResultCode HandleInterruptFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to the program type.
|
||||
ResultCode HandleProgramTypeFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to the handle table size.
|
||||
ResultCode HandleHandleTableFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to the kernel version capability flags.
|
||||
ResultCode HandleKernelVersionFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to debug-specific capabilities.
|
||||
ResultCode HandleDebugFlags(u32 flags);
|
||||
|
||||
SyscallCapabilities svc_capabilities;
|
||||
InterruptCapabilities interrupt_capabilities;
|
||||
|
||||
u64 core_mask = 0;
|
||||
u64 priority_mask = 0;
|
||||
|
||||
u32 handle_table_size = 0;
|
||||
u32 kernel_version = 0;
|
||||
|
||||
ProgramType program_type = ProgramType::SysModule;
|
||||
|
||||
bool is_debuggable = false;
|
||||
bool can_force_debug = false;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -46,9 +46,6 @@ ResultCode ReadableEvent::Reset() {
|
||||
|
||||
void ReadableEvent::WakeupAllWaitingThreads() {
|
||||
WaitObject::WakeupAllWaitingThreads();
|
||||
|
||||
if (reset_type == ResetType::Pulse)
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@@ -190,10 +190,16 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
auto& process = *Core::CurrentProcess();
|
||||
const VAddr heap_base = process.VMManager().GetHeapRegionBaseAddress();
|
||||
CASCADE_RESULT(*heap_addr,
|
||||
process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite));
|
||||
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress();
|
||||
const auto alloc_result =
|
||||
vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite);
|
||||
|
||||
if (alloc_result.Failed()) {
|
||||
return alloc_result.Code();
|
||||
}
|
||||
|
||||
*heap_addr = *alloc_result;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -307,15 +313,14 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
||||
src_addr, size);
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
|
||||
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
|
||||
if (result != RESULT_SUCCESS) {
|
||||
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return current_process->MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack);
|
||||
return vm_manager.MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack);
|
||||
}
|
||||
|
||||
/// Unmaps a region that was previously mapped with svcMapMemory
|
||||
@@ -323,15 +328,14 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
||||
src_addr, size);
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
|
||||
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
|
||||
if (result != RESULT_SUCCESS) {
|
||||
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return current_process->UnmapMemory(dst_addr, src_addr, size);
|
||||
return vm_manager.UnmapRange(dst_addr, size);
|
||||
}
|
||||
|
||||
/// Connect to an OS service given the port name, returns the handle to the port to out
|
||||
@@ -680,6 +684,9 @@ static void Break(u32 reason, u64 info1, u64 info2) {
|
||||
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
reason, info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(static_cast<std::size_t>(GetCurrentThread()->GetProcessorID()))
|
||||
.LogBacktrace();
|
||||
ASSERT(false);
|
||||
|
||||
Core::CurrentProcess()->PrepareForTermination();
|
||||
@@ -708,8 +715,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
|
||||
enum class GetInfoType : u64 {
|
||||
// 1.0.0+
|
||||
AllowedCpuIdBitmask = 0,
|
||||
AllowedThreadPrioBitmask = 1,
|
||||
AllowedCPUCoreMask = 0,
|
||||
AllowedThreadPriorityMask = 1,
|
||||
MapRegionBaseAddr = 2,
|
||||
MapRegionSize = 3,
|
||||
HeapRegionBaseAddr = 4,
|
||||
@@ -740,8 +747,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
const auto info_id_type = static_cast<GetInfoType>(info_id);
|
||||
|
||||
switch (info_id_type) {
|
||||
case GetInfoType::AllowedCpuIdBitmask:
|
||||
case GetInfoType::AllowedThreadPrioBitmask:
|
||||
case GetInfoType::AllowedCPUCoreMask:
|
||||
case GetInfoType::AllowedThreadPriorityMask:
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
case GetInfoType::MapRegionSize:
|
||||
case GetInfoType::HeapRegionBaseAddr:
|
||||
@@ -767,12 +774,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
}
|
||||
|
||||
switch (info_id_type) {
|
||||
case GetInfoType::AllowedCpuIdBitmask:
|
||||
*result = process->GetAllowedProcessorMask();
|
||||
case GetInfoType::AllowedCPUCoreMask:
|
||||
*result = process->GetCoreMask();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::AllowedThreadPrioBitmask:
|
||||
*result = process->GetAllowedThreadPriorityMask();
|
||||
case GetInfoType::AllowedThreadPriorityMask:
|
||||
*result = process->GetPriorityMask();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
@@ -932,8 +939,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
}
|
||||
|
||||
/// Sets the thread activity
|
||||
static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, unknown);
|
||||
static ResultCode SetThreadActivity(Handle handle, u32 activity) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
|
||||
if (activity > static_cast<u32>(ThreadActivity::Paused)) {
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
|
||||
const auto* current_process = Core::CurrentProcess();
|
||||
const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (thread->GetOwnerProcess() != current_process) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"The current process does not own the current thread, thread_handle={:08X} "
|
||||
"thread_pid={}, "
|
||||
"current_process_pid={}",
|
||||
handle, thread->GetOwnerProcess()->GetProcessID(),
|
||||
current_process->GetProcessID());
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (thread == GetCurrentThread()) {
|
||||
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
|
||||
return ERR_BUSY;
|
||||
}
|
||||
|
||||
thread->SetActivity(static_cast<ThreadActivity>(activity));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -960,7 +994,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
|
||||
|
||||
if (thread == GetCurrentThread()) {
|
||||
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
|
||||
return ERR_ALREADY_REGISTERED;
|
||||
return ERR_BUSY;
|
||||
}
|
||||
|
||||
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
|
||||
@@ -1185,29 +1219,35 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
|
||||
entry_point, arg, stack_top, priority, processor_id, *out_handle);
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
|
||||
if (processor_id == THREADPROCESSORID_IDEAL) {
|
||||
// Set the target CPU to the one specified by the process.
|
||||
processor_id = current_process->GetIdealCore();
|
||||
ASSERT(processor_id != THREADPROCESSORID_IDEAL);
|
||||
}
|
||||
|
||||
if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
const u64 core_mask = current_process->GetCoreMask();
|
||||
if ((core_mask | (1ULL << processor_id)) != core_mask) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread core specified ({})", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
if (priority > THREADPRIO_LOWEST) {
|
||||
LOG_ERROR(Kernel_SVC, "An invalid priority was specified, expected {} but got {}",
|
||||
THREADPRIO_LOWEST, priority);
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Invalid thread priority specified ({}). Must be within the range 0-64",
|
||||
priority);
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
|
||||
if (processor_id == THREADPROCESSORID_DEFAULT) {
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
processor_id = current_process->GetDefaultProcessorID();
|
||||
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
|
||||
}
|
||||
|
||||
switch (processor_id) {
|
||||
case THREADPROCESSORID_0:
|
||||
case THREADPROCESSORID_1:
|
||||
case THREADPROCESSORID_2:
|
||||
case THREADPROCESSORID_3:
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority);
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
const std::string name = fmt::format("thread-{:X}", entry_point);
|
||||
@@ -1245,7 +1285,10 @@ static ResultCode StartThread(Handle thread_handle) {
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
|
||||
|
||||
thread->ResumeFromWait();
|
||||
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
|
||||
if (thread->GetStatus() == ThreadStatus::Ready) {
|
||||
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -1602,13 +1645,13 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
|
||||
const u8 default_processor_id = thread->GetOwnerProcess()->GetDefaultProcessorID();
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
|
||||
const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore();
|
||||
|
||||
ASSERT(default_processor_id != static_cast<u8>(THREADPROCESSORID_DEFAULT));
|
||||
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
|
||||
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
core = default_processor_id;
|
||||
// Set the target CPU to the ideal core specified by the process.
|
||||
core = ideal_cpu_core;
|
||||
mask = 1ULL << core;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
@@ -50,7 +49,7 @@ void Thread::Stop() {
|
||||
|
||||
// Clean up thread from ready queue
|
||||
// This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
|
||||
if (status == ThreadStatus::Ready) {
|
||||
if (status == ThreadStatus::Ready || status == ThreadStatus::Paused) {
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
}
|
||||
|
||||
@@ -140,6 +139,11 @@ void Thread::ResumeFromWait() {
|
||||
|
||||
wakeup_callback = nullptr;
|
||||
|
||||
if (activity == ThreadActivity::Paused) {
|
||||
status = ThreadStatus::Paused;
|
||||
return;
|
||||
}
|
||||
|
||||
status = ThreadStatus::Ready;
|
||||
|
||||
ChangeScheduler();
|
||||
@@ -227,29 +231,6 @@ void Thread::BoostPriority(u32 priority) {
|
||||
current_priority = priority;
|
||||
}
|
||||
|
||||
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
|
||||
Process& owner_process) {
|
||||
// Setup page table so we can write to memory
|
||||
SetCurrentPageTable(&owner_process.VMManager().page_table);
|
||||
|
||||
// Initialize new "main" thread
|
||||
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
|
||||
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
|
||||
stack_top, owner_process);
|
||||
|
||||
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
// Register 1 must be a handle to the main thread
|
||||
const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
|
||||
thread->SetGuestHandle(guest_handle);
|
||||
thread->GetContext().cpu_registers[1] = guest_handle;
|
||||
|
||||
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
|
||||
thread->ResumeFromWait();
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
void Thread::SetWaitSynchronizationResult(ResultCode result) {
|
||||
context.cpu_registers[0] = result.raw;
|
||||
}
|
||||
@@ -391,6 +372,23 @@ bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> t
|
||||
return wakeup_callback(reason, std::move(thread), std::move(object), index);
|
||||
}
|
||||
|
||||
void Thread::SetActivity(ThreadActivity value) {
|
||||
activity = value;
|
||||
|
||||
if (value == ThreadActivity::Paused) {
|
||||
// Set status if not waiting
|
||||
if (status == ThreadStatus::Ready) {
|
||||
status = ThreadStatus::Paused;
|
||||
} else if (status == ThreadStatus::Running) {
|
||||
status = ThreadStatus::Paused;
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
}
|
||||
} else if (status == ThreadStatus::Paused) {
|
||||
// Ready to reschedule
|
||||
ResumeFromWait();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,12 +30,12 @@ enum ThreadPriority : u32 {
|
||||
};
|
||||
|
||||
enum ThreadProcessorId : s32 {
|
||||
THREADPROCESSORID_DEFAULT = -2, ///< Run thread on default core specified by exheader
|
||||
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
|
||||
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
|
||||
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
|
||||
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
|
||||
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
|
||||
THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process.
|
||||
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
|
||||
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
|
||||
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
|
||||
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
|
||||
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
|
||||
|
||||
/// Allowed CPU mask
|
||||
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
|
||||
@@ -45,6 +45,7 @@ enum ThreadProcessorId : s32 {
|
||||
enum class ThreadStatus {
|
||||
Running, ///< Currently running
|
||||
Ready, ///< Ready to run
|
||||
Paused, ///< Paused by SetThreadActivity or debug
|
||||
WaitHLEEvent, ///< Waiting for hle event to finish
|
||||
WaitSleep, ///< Waiting due to a SleepThread SVC
|
||||
WaitIPC, ///< Waiting for the reply from an IPC request
|
||||
@@ -61,6 +62,11 @@ enum class ThreadWakeupReason {
|
||||
Timeout // The thread was woken up due to a wait timeout.
|
||||
};
|
||||
|
||||
enum class ThreadActivity : u32 {
|
||||
Normal = 0,
|
||||
Paused = 1,
|
||||
};
|
||||
|
||||
class Thread final : public WaitObject {
|
||||
public:
|
||||
using TLSMemory = std::vector<u8>;
|
||||
@@ -371,6 +377,12 @@ public:
|
||||
return affinity_mask;
|
||||
}
|
||||
|
||||
ThreadActivity GetActivity() const {
|
||||
return activity;
|
||||
}
|
||||
|
||||
void SetActivity(ThreadActivity value);
|
||||
|
||||
private:
|
||||
explicit Thread(KernelCore& kernel);
|
||||
~Thread() override;
|
||||
@@ -439,18 +451,9 @@ private:
|
||||
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets up the primary application thread
|
||||
* @param kernel The kernel instance to create the main thread under.
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The priority to give the main thread
|
||||
* @param owner_process The parent process for the main thread
|
||||
* @return A shared pointer to the main thread
|
||||
*/
|
||||
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
|
||||
Process& owner_process);
|
||||
ThreadActivity activity = ThreadActivity::Normal;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current thread
|
||||
|
||||
@@ -68,9 +68,6 @@ void Timer::Clear() {
|
||||
|
||||
void Timer::WakeupAllWaitingThreads() {
|
||||
WaitObject::WakeupAllWaitingThreads();
|
||||
|
||||
if (reset_type == ResetType::Pulse)
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
void Timer::Signal(int cycles_late) {
|
||||
|
||||
@@ -190,6 +190,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
|
||||
vma.type = VMAType::Free;
|
||||
vma.permissions = VMAPermission::None;
|
||||
vma.state = MemoryState::Unmapped;
|
||||
vma.attribute = MemoryAttribute::None;
|
||||
|
||||
vma.backing_block = nullptr;
|
||||
vma.offset = 0;
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/timer.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <vector>
|
||||
#include <boost/smart_ptr/intrusive_ptr.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <stack>
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@@ -22,6 +23,7 @@
|
||||
#include "core/hle/service/am/applets/profile_select.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||
#include "core/hle/service/am/applets/stub_applet.h"
|
||||
#include "core/hle/service/am/applets/web_browser.h"
|
||||
#include "core/hle/service/am/idle.h"
|
||||
#include "core/hle/service/am/omm.h"
|
||||
#include "core/hle/service/am/spsm.h"
|
||||
@@ -37,11 +39,13 @@
|
||||
namespace Service::AM {
|
||||
|
||||
constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
|
||||
constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
|
||||
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
|
||||
|
||||
enum class AppletId : u32 {
|
||||
ProfileSelect = 0x10,
|
||||
SoftwareKeyboard = 0x11,
|
||||
LibAppletOff = 0x17,
|
||||
};
|
||||
|
||||
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
|
||||
@@ -459,9 +463,17 @@ void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
|
||||
void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
const auto message = msg_queue->PopMessage();
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
if (message == AppletMessageQueue::AppletMessage::NoMessage) {
|
||||
LOG_ERROR(Service_AM, "Message queue is empty");
|
||||
rb.Push(ERR_NO_MESSAGES);
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
|
||||
return;
|
||||
}
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
|
||||
@@ -720,10 +732,10 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
|
||||
const std::vector<u8> data{ctx.ReadBuffer()};
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
|
||||
|
||||
if (data.size() > backing.buffer.size() - offset) {
|
||||
LOG_ERROR(Service_AM,
|
||||
"offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}",
|
||||
@@ -743,10 +755,10 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
|
||||
const std::size_t size{ctx.GetWriteBufferSize()};
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
|
||||
|
||||
if (size > backing.buffer.size() - offset) {
|
||||
LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}",
|
||||
backing.buffer.size(), size, offset);
|
||||
@@ -781,6 +793,8 @@ static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
|
||||
return std::make_shared<Applets::ProfileSelect>();
|
||||
case AppletId::SoftwareKeyboard:
|
||||
return std::make_shared<Applets::SoftwareKeyboard>();
|
||||
case AppletId::LibAppletOff:
|
||||
return std::make_shared<Applets::WebBrowser>();
|
||||
default:
|
||||
LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
|
||||
static_cast<u32>(id));
|
||||
@@ -865,8 +879,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
|
||||
{23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
|
||||
{24, nullptr, "GetLaunchStorageInfoForDebug"},
|
||||
{25, nullptr, "ExtendSaveData"},
|
||||
{26, nullptr, "GetSaveDataSize"},
|
||||
{25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
|
||||
{26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
|
||||
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
|
||||
{31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
|
||||
{32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
|
||||
@@ -1043,6 +1057,48 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u64>(0);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto type{rp.PopRaw<FileSys::SaveDataType>()};
|
||||
rp.Skip(1, false);
|
||||
const auto user_id{rp.PopRaw<u128>()};
|
||||
const auto new_normal_size{rp.PopRaw<u64>()};
|
||||
const auto new_journal_size{rp.PopRaw<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_AM,
|
||||
"called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, "
|
||||
"new_journal={:016X}",
|
||||
static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
|
||||
|
||||
FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id,
|
||||
{new_normal_size, new_journal_size});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
// The following value is used upon failure to help the system recover.
|
||||
// Since we always succeed, this should be 0.
|
||||
rb.Push<u64>(0);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto type{rp.PopRaw<FileSys::SaveDataType>()};
|
||||
rp.Skip(1, false);
|
||||
const auto user_id{rp.PopRaw<u128>()};
|
||||
|
||||
LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
|
||||
user_id[1], user_id[0]);
|
||||
|
||||
const auto size =
|
||||
FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(size.normal);
|
||||
rb.Push(size.journal);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>();
|
||||
|
||||
@@ -206,6 +206,8 @@ private:
|
||||
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
|
||||
void NotifyRunning(Kernel::HLERequestContext& ctx);
|
||||
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
|
||||
void ExtendSaveData(Kernel::HLERequestContext& ctx);
|
||||
void GetSaveDataSize(Kernel::HLERequestContext& ctx);
|
||||
void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/frontend/applets/profile_select.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/profile_select.h"
|
||||
|
||||
|
||||
190
src/core/hle/service/am/applets/web_browser.cpp
Normal file
190
src/core/hle/service/am/applets/web_browser.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/frontend/applets/web_browser.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/am/applets/web_browser.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
// TODO(DarkLordZach): There are other arguments in the WebBuffer structure that are currently not
|
||||
// parsed, for example footer mode and left stick mode. Some of these are not particularly relevant,
|
||||
// but some may be worth an implementation.
|
||||
constexpr u16 WEB_ARGUMENT_URL_TYPE = 0x6;
|
||||
|
||||
struct WebBufferHeader {
|
||||
u16 count;
|
||||
INSERT_PADDING_BYTES(6);
|
||||
};
|
||||
static_assert(sizeof(WebBufferHeader) == 0x8, "WebBufferHeader has incorrect size.");
|
||||
|
||||
struct WebArgumentHeader {
|
||||
u16 type;
|
||||
u16 size;
|
||||
u32 offset;
|
||||
};
|
||||
static_assert(sizeof(WebArgumentHeader) == 0x8, "WebArgumentHeader has incorrect size.");
|
||||
|
||||
struct WebArgumentResult {
|
||||
u32 result_code;
|
||||
std::array<char, 0x1000> last_url;
|
||||
u64 last_url_size;
|
||||
};
|
||||
static_assert(sizeof(WebArgumentResult) == 0x1010, "WebArgumentResult has incorrect size.");
|
||||
|
||||
static std::vector<u8> GetArgumentDataForTagType(const std::vector<u8>& data, u16 type) {
|
||||
WebBufferHeader header;
|
||||
ASSERT(sizeof(WebBufferHeader) <= data.size());
|
||||
std::memcpy(&header, data.data(), sizeof(WebBufferHeader));
|
||||
|
||||
u64 offset = sizeof(WebBufferHeader);
|
||||
for (u16 i = 0; i < header.count; ++i) {
|
||||
WebArgumentHeader arg;
|
||||
ASSERT(offset + sizeof(WebArgumentHeader) <= data.size());
|
||||
std::memcpy(&arg, data.data() + offset, sizeof(WebArgumentHeader));
|
||||
offset += sizeof(WebArgumentHeader);
|
||||
|
||||
if (arg.type == type) {
|
||||
std::vector<u8> out(arg.size);
|
||||
offset += arg.offset;
|
||||
ASSERT(offset + arg.size <= data.size());
|
||||
std::memcpy(out.data(), data.data() + offset, out.size());
|
||||
return out;
|
||||
}
|
||||
|
||||
offset += arg.offset + arg.size;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static FileSys::VirtualFile GetManualRomFS() {
|
||||
auto& loader{Core::System::GetInstance().GetAppLoader()};
|
||||
|
||||
FileSys::VirtualFile out;
|
||||
if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success)
|
||||
return out;
|
||||
|
||||
const auto& installed{FileSystem::GetUnionContents()};
|
||||
const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(),
|
||||
FileSys::ContentRecordType::Manual);
|
||||
|
||||
if (res != nullptr)
|
||||
return res->GetRomFS();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WebBrowser::WebBrowser() = default;
|
||||
|
||||
WebBrowser::~WebBrowser() = default;
|
||||
|
||||
void WebBrowser::Initialize() {
|
||||
Applet::Initialize();
|
||||
|
||||
complete = false;
|
||||
temporary_dir.clear();
|
||||
filename.clear();
|
||||
status = RESULT_SUCCESS;
|
||||
|
||||
const auto web_arg_storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(web_arg_storage != nullptr);
|
||||
const auto& web_arg = web_arg_storage->GetData();
|
||||
|
||||
const auto url_data = GetArgumentDataForTagType(web_arg, WEB_ARGUMENT_URL_TYPE);
|
||||
filename = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(url_data.data()), url_data.size());
|
||||
|
||||
temporary_dir = FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
|
||||
"web_applet_manual",
|
||||
FileUtil::DirectorySeparator::PlatformDefault);
|
||||
FileUtil::DeleteDirRecursively(temporary_dir);
|
||||
|
||||
manual_romfs = GetManualRomFS();
|
||||
if (manual_romfs == nullptr) {
|
||||
status = ResultCode(-1);
|
||||
LOG_ERROR(Service_AM, "Failed to find manual for current process!");
|
||||
}
|
||||
|
||||
filename =
|
||||
FileUtil::SanitizePath(temporary_dir + DIR_SEP + "html-document" + DIR_SEP + filename,
|
||||
FileUtil::DirectorySeparator::PlatformDefault);
|
||||
}
|
||||
|
||||
bool WebBrowser::TransactionComplete() const {
|
||||
return complete;
|
||||
}
|
||||
|
||||
ResultCode WebBrowser::GetStatus() const {
|
||||
return status;
|
||||
}
|
||||
|
||||
void WebBrowser::ExecuteInteractive() {
|
||||
UNIMPLEMENTED_MSG("Unexpected interactive data recieved!");
|
||||
}
|
||||
|
||||
void WebBrowser::Execute() {
|
||||
if (complete)
|
||||
return;
|
||||
|
||||
if (status != RESULT_SUCCESS) {
|
||||
complete = true;
|
||||
return;
|
||||
}
|
||||
|
||||
auto& frontend{Core::System::GetInstance().GetWebBrowser()};
|
||||
|
||||
frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
|
||||
}
|
||||
|
||||
void WebBrowser::UnpackRomFS() {
|
||||
if (unpacked)
|
||||
return;
|
||||
|
||||
ASSERT(manual_romfs != nullptr);
|
||||
const auto dir =
|
||||
FileSys::ExtractRomFS(manual_romfs, FileSys::RomFSExtractionType::SingleDiscard);
|
||||
const auto& vfs{Core::System::GetInstance().GetFilesystem()};
|
||||
const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite);
|
||||
FileSys::VfsRawCopyD(dir, temp_dir);
|
||||
|
||||
unpacked = true;
|
||||
}
|
||||
|
||||
void WebBrowser::Finalize() {
|
||||
complete = true;
|
||||
|
||||
WebArgumentResult out{};
|
||||
out.result_code = 0;
|
||||
out.last_url_size = 0;
|
||||
|
||||
std::vector<u8> data(sizeof(WebArgumentResult));
|
||||
std::memcpy(data.data(), &out, sizeof(WebArgumentResult));
|
||||
|
||||
broker.PushNormalDataFromApplet(IStorage{data});
|
||||
broker.SignalStateChanged();
|
||||
|
||||
FileUtil::DeleteDirRecursively(temporary_dir);
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
44
src/core/hle/service/am/applets/web_browser.h
Normal file
44
src/core/hle/service/am/applets/web_browser.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
class WebBrowser final : public Applet {
|
||||
public:
|
||||
WebBrowser();
|
||||
~WebBrowser() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
||||
bool TransactionComplete() const override;
|
||||
ResultCode GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
|
||||
// Callback to be fired when the frontend needs the manual RomFS unpacked to temporary
|
||||
// directory. This is a blocking call and may take a while as some manuals can be up to 100MB in
|
||||
// size. Attempting to access files at filename before invocation is likely to not work.
|
||||
void UnpackRomFS();
|
||||
|
||||
// Callback to be fired when the frontend is finished browsing. This will delete the temporary
|
||||
// manual RomFS extracted files, so ensure this is only called at actual finalization.
|
||||
void Finalize();
|
||||
|
||||
private:
|
||||
bool complete = false;
|
||||
bool unpacked = false;
|
||||
ResultCode status = RESULT_SUCCESS;
|
||||
|
||||
FileSys::VirtualFile manual_romfs;
|
||||
std::string temporary_dir;
|
||||
std::string filename;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
@@ -111,7 +111,8 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
|
||||
}
|
||||
|
||||
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
|
||||
LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type));
|
||||
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}",
|
||||
static_cast<u32>(fatal_type), error_code.raw);
|
||||
switch (fatal_type) {
|
||||
case FatalType::ErrorReportAndScreen:
|
||||
GenerateErrorReport(error_code, info);
|
||||
|
||||
@@ -8,18 +8,23 @@
|
||||
#include "common/file_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/sdmc_factory.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/filesystem/fsp_ldr.h"
|
||||
#include "core/hle/service/filesystem/fsp_pr.h"
|
||||
#include "core/hle/service/filesystem/fsp_srv.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
@@ -28,6 +33,10 @@ namespace Service::FileSystem {
|
||||
// TODO(DarkLordZach): Eventually make this configurable in settings.
|
||||
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
|
||||
|
||||
// A default size for normal/journal save data size if application control metadata cannot be found.
|
||||
// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
|
||||
constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
|
||||
|
||||
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
||||
std::string_view dir_name_) {
|
||||
std::string dir_name(FileUtil::SanitizePath(dir_name_));
|
||||
@@ -341,6 +350,44 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
|
||||
return sdmc_factory->Open();
|
||||
}
|
||||
|
||||
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) {
|
||||
if (save_data_factory == nullptr) {
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id);
|
||||
|
||||
if (value.normal == 0 && value.journal == 0) {
|
||||
FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
|
||||
|
||||
FileSys::NACP nacp;
|
||||
const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp);
|
||||
|
||||
if (res != Loader::ResultStatus::Success) {
|
||||
FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()};
|
||||
auto [nacp_unique, discard] = pm.GetControlMetadata();
|
||||
|
||||
if (nacp_unique != nullptr) {
|
||||
new_size = {nacp_unique->GetDefaultNormalSaveSize(),
|
||||
nacp_unique->GetDefaultJournalSaveSize()};
|
||||
}
|
||||
} else {
|
||||
new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()};
|
||||
}
|
||||
|
||||
WriteSaveDataSize(type, title_id, user_id, new_size);
|
||||
return new_size;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
|
||||
FileSys::SaveDataSize new_value) {
|
||||
if (save_data_factory != nullptr)
|
||||
save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
|
||||
}
|
||||
|
||||
FileSys::RegisteredCacheUnion GetUnionContents() {
|
||||
return FileSys::RegisteredCacheUnion{
|
||||
{GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};
|
||||
|
||||
@@ -21,9 +21,11 @@ class SDMCFactory;
|
||||
enum class ContentRecordType : u8;
|
||||
enum class Mode : u32;
|
||||
enum class SaveDataSpaceId : u8;
|
||||
enum class SaveDataType : u8;
|
||||
enum class StorageId : u8;
|
||||
|
||||
struct SaveDataDescriptor;
|
||||
struct SaveDataSize;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Service {
|
||||
@@ -48,6 +50,10 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC();
|
||||
|
||||
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
|
||||
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
|
||||
FileSys::SaveDataSize new_value);
|
||||
|
||||
FileSys::RegisteredCacheUnion GetUnionContents();
|
||||
|
||||
FileSys::RegisteredCache* GetSystemNANDContents();
|
||||
|
||||
@@ -339,52 +339,6 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
|
||||
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
|
||||
|
||||
if (hold_type == NpadHoldType::Horizontal) {
|
||||
ControllerPadState state{};
|
||||
AnalogPosition temp_lstick_entry{};
|
||||
AnalogPosition temp_rstick_entry{};
|
||||
if (controller_type == NPadControllerType::JoyLeft) {
|
||||
state.d_down.Assign(pad_state.pad_states.d_left.Value());
|
||||
state.d_left.Assign(pad_state.pad_states.d_up.Value());
|
||||
state.d_right.Assign(pad_state.pad_states.d_down.Value());
|
||||
state.d_up.Assign(pad_state.pad_states.d_right.Value());
|
||||
state.l.Assign(pad_state.pad_states.l.Value() |
|
||||
pad_state.pad_states.left_sl.Value());
|
||||
state.r.Assign(pad_state.pad_states.r.Value() |
|
||||
pad_state.pad_states.left_sr.Value());
|
||||
|
||||
state.zl.Assign(pad_state.pad_states.zl.Value());
|
||||
state.plus.Assign(pad_state.pad_states.minus.Value());
|
||||
|
||||
temp_lstick_entry = pad_state.l_stick;
|
||||
temp_rstick_entry = pad_state.r_stick;
|
||||
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
|
||||
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
|
||||
temp_lstick_entry.y *= -1;
|
||||
} else if (controller_type == NPadControllerType::JoyRight) {
|
||||
state.x.Assign(pad_state.pad_states.a.Value());
|
||||
state.a.Assign(pad_state.pad_states.b.Value());
|
||||
state.b.Assign(pad_state.pad_states.y.Value());
|
||||
state.y.Assign(pad_state.pad_states.b.Value());
|
||||
|
||||
state.l.Assign(pad_state.pad_states.l.Value() |
|
||||
pad_state.pad_states.right_sl.Value());
|
||||
state.r.Assign(pad_state.pad_states.r.Value() |
|
||||
pad_state.pad_states.right_sr.Value());
|
||||
state.zr.Assign(pad_state.pad_states.zr.Value());
|
||||
state.plus.Assign(pad_state.pad_states.plus.Value());
|
||||
|
||||
temp_lstick_entry = pad_state.l_stick;
|
||||
temp_rstick_entry = pad_state.r_stick;
|
||||
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
|
||||
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
|
||||
temp_rstick_entry.x *= -1;
|
||||
}
|
||||
pad_state.pad_states.raw = state.raw;
|
||||
pad_state.l_stick = temp_lstick_entry;
|
||||
pad_state.r_stick = temp_rstick_entry;
|
||||
}
|
||||
|
||||
libnx_entry.connection_status.raw = 0;
|
||||
|
||||
switch (controller_type) {
|
||||
@@ -456,6 +410,8 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw;
|
||||
libnx_entry.pad.l_stick = pad_state.l_stick;
|
||||
libnx_entry.pad.r_stick = pad_state.r_stick;
|
||||
|
||||
press_state |= static_cast<u32>(pad_state.pad_states.raw);
|
||||
}
|
||||
std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
|
||||
shared_memory_entries.size() * sizeof(NPadEntry));
|
||||
@@ -682,6 +638,10 @@ void Controller_NPad::ClearAllControllers() {
|
||||
});
|
||||
}
|
||||
|
||||
u32 Controller_NPad::GetAndResetPressState() {
|
||||
return std::exchange(press_state, 0);
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
|
||||
const bool support_handheld =
|
||||
std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
|
||||
|
||||
@@ -124,6 +124,10 @@ public:
|
||||
void ConnectAllDisconnectedControllers();
|
||||
void ClearAllControllers();
|
||||
|
||||
// Logical OR for all buttons presses on all controllers
|
||||
// Specifically for cheat engine and other features.
|
||||
u32 GetAndResetPressState();
|
||||
|
||||
static std::size_t NPadIdToIndex(u32 npad_id);
|
||||
static u32 IndexToNPad(std::size_t index);
|
||||
|
||||
@@ -292,6 +296,8 @@ private:
|
||||
bool is_connected;
|
||||
};
|
||||
|
||||
u32 press_state{};
|
||||
|
||||
NPadType style{};
|
||||
std::array<NPadEntry, 10> shared_memory_entries{};
|
||||
std::array<
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,12 +4,122 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "controllers/controller_base.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class SharedMemory;
|
||||
}
|
||||
|
||||
namespace SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
enum class HidController : std::size_t {
|
||||
DebugPad,
|
||||
Touchscreen,
|
||||
Mouse,
|
||||
Keyboard,
|
||||
XPad,
|
||||
Unknown1,
|
||||
Unknown2,
|
||||
Unknown3,
|
||||
SixAxisSensor,
|
||||
NPad,
|
||||
Gesture,
|
||||
|
||||
MaxControllers,
|
||||
};
|
||||
|
||||
class IAppletResource final : public ServiceFramework<IAppletResource> {
|
||||
public:
|
||||
IAppletResource();
|
||||
~IAppletResource() override;
|
||||
|
||||
void ActivateController(HidController controller);
|
||||
void DeactivateController(HidController controller);
|
||||
|
||||
template <typename T>
|
||||
T& GetController(HidController controller) {
|
||||
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& GetController(HidController controller) const {
|
||||
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void MakeController(HidController controller) {
|
||||
controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
|
||||
}
|
||||
|
||||
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
|
||||
void UpdateControllers(u64 userdata, int cycles_late);
|
||||
|
||||
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
|
||||
|
||||
CoreTiming::EventType* pad_update_event;
|
||||
|
||||
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
|
||||
controllers{};
|
||||
};
|
||||
|
||||
class Hid final : public ServiceFramework<Hid> {
|
||||
public:
|
||||
Hid();
|
||||
~Hid() override;
|
||||
|
||||
std::shared_ptr<IAppletResource> GetAppletResource();
|
||||
|
||||
private:
|
||||
void CreateAppletResource(Kernel::HLERequestContext& ctx);
|
||||
void ActivateXpad(Kernel::HLERequestContext& ctx);
|
||||
void ActivateDebugPad(Kernel::HLERequestContext& ctx);
|
||||
void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
|
||||
void ActivateMouse(Kernel::HLERequestContext& ctx);
|
||||
void ActivateKeyboard(Kernel::HLERequestContext& ctx);
|
||||
void ActivateGesture(Kernel::HLERequestContext& ctx);
|
||||
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
|
||||
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
|
||||
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
|
||||
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
|
||||
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
|
||||
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
|
||||
void ActivateNpad(Kernel::HLERequestContext& ctx);
|
||||
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
|
||||
void DisconnectNpad(Kernel::HLERequestContext& ctx);
|
||||
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
|
||||
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationValue(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationValues(Kernel::HLERequestContext& ctx);
|
||||
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx);
|
||||
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
|
||||
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
|
||||
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
|
||||
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
|
||||
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<IAppletResource> applet_resource;
|
||||
};
|
||||
|
||||
/// Reload input devices. Used when input configuration changed
|
||||
void ReloadInputDevices();
|
||||
|
||||
|
||||
@@ -318,14 +318,18 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
|
||||
ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS);
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(*map_address, nro_addr, nro_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic)
|
||||
.IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
|
||||
|
||||
if (bss_size > 0) {
|
||||
ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
|
||||
ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS);
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic)
|
||||
.IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
|
||||
}
|
||||
|
||||
vm_manager.ReprotectRange(*map_address, header.text_size,
|
||||
@@ -380,13 +384,14 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto* process = Core::CurrentProcess();
|
||||
auto& vm_manager = process->VMManager();
|
||||
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
const auto& nro_size = iter->second.size;
|
||||
|
||||
ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
|
||||
ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS);
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(heap_addr, mapped_addr, nro_size,
|
||||
Kernel::MemoryState::ModuleCodeStatic)
|
||||
.IsSuccess());
|
||||
ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());
|
||||
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/nfp/nfp_user.h"
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace Service::NFP {
|
||||
namespace ErrCodes {
|
||||
constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
|
||||
-1); // TODO(ogniK): Find the actual error code
|
||||
}
|
||||
constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
|
||||
} // namespace ErrCodes
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {
|
||||
@@ -292,10 +293,9 @@ private:
|
||||
}
|
||||
|
||||
void OpenApplicationArea(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFP, "called");
|
||||
// We don't need to worry about this since we can just open the file
|
||||
LOG_WARNING(Service_NFP, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA);
|
||||
}
|
||||
|
||||
void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -166,7 +166,7 @@ Layer::~Layer() = default;
|
||||
|
||||
Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Pulse,
|
||||
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
fmt::format("Display VSync Event {}", id));
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,16 @@
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/service/time/interface.h"
|
||||
#include "core/hle/service/time/time.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
static std::chrono::seconds GetSecondsSinceEpoch() {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()) +
|
||||
Settings::values.custom_rtc_differential;
|
||||
}
|
||||
|
||||
static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
|
||||
CalendarAdditionalInfo& additional_info,
|
||||
[[maybe_unused]] const TimeZoneRule& /*rule*/) {
|
||||
@@ -68,9 +75,7 @@ public:
|
||||
|
||||
private:
|
||||
void GetCurrentTime(Kernel::HLERequestContext& ctx) {
|
||||
const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()};
|
||||
const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
@@ -264,14 +269,9 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto unknown_u8 = rp.PopRaw<u8>();
|
||||
const auto initial_type = rp.PopRaw<u8>();
|
||||
|
||||
ClockSnapshot clock_snapshot{};
|
||||
|
||||
const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()};
|
||||
CalendarTime calendar_time{};
|
||||
const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
|
||||
const std::time_t time(time_since_epoch);
|
||||
const std::tm* tm = std::localtime(&time);
|
||||
if (tm == nullptr) {
|
||||
@@ -280,16 +280,19 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code
|
||||
return;
|
||||
}
|
||||
SteadyClockTimePoint steady_clock_time_point{CoreTiming::cyclesToMs(CoreTiming::GetTicks()) /
|
||||
1000};
|
||||
|
||||
LocationName location_name{"UTC"};
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000, {}};
|
||||
|
||||
CalendarTime calendar_time{};
|
||||
calendar_time.year = tm->tm_year + 1900;
|
||||
calendar_time.month = tm->tm_mon + 1;
|
||||
calendar_time.day = tm->tm_mday;
|
||||
calendar_time.hour = tm->tm_hour;
|
||||
calendar_time.minute = tm->tm_min;
|
||||
calendar_time.second = tm->tm_sec;
|
||||
|
||||
ClockSnapshot clock_snapshot{};
|
||||
clock_snapshot.system_posix_time = time_since_epoch;
|
||||
clock_snapshot.network_posix_time = time_since_epoch;
|
||||
clock_snapshot.system_calendar_time = calendar_time;
|
||||
@@ -302,9 +305,10 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
clock_snapshot.network_calendar_info = additional_info;
|
||||
|
||||
clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
|
||||
clock_snapshot.location_name = location_name;
|
||||
clock_snapshot.location_name = LocationName{"UTC"};
|
||||
clock_snapshot.clock_auto_adjustment_enabled = 1;
|
||||
clock_snapshot.ipc_u8 = unknown_u8;
|
||||
clock_snapshot.type = initial_type;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
|
||||
|
||||
@@ -22,7 +22,6 @@ struct CalendarTime {
|
||||
u8 hour;
|
||||
u8 minute;
|
||||
u8 second;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size");
|
||||
|
||||
@@ -30,7 +29,7 @@ struct CalendarAdditionalInfo {
|
||||
u32_le day_of_week;
|
||||
u32_le day_of_year;
|
||||
std::array<u8, 8> name;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 is_dst;
|
||||
s32_le utc_offset;
|
||||
};
|
||||
static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
|
||||
@@ -42,8 +41,10 @@ struct TimeZoneRule {
|
||||
};
|
||||
|
||||
struct SteadyClockTimePoint {
|
||||
using SourceID = std::array<u8, 16>;
|
||||
|
||||
u64_le value;
|
||||
INSERT_PADDING_WORDS(4);
|
||||
SourceID source_id;
|
||||
};
|
||||
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
|
||||
|
||||
@@ -66,8 +67,9 @@ struct ClockSnapshot {
|
||||
SteadyClockTimePoint steady_clock_timepoint;
|
||||
LocationName location_name;
|
||||
u8 clock_auto_adjustment_enabled;
|
||||
u8 ipc_u8;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
u8 type;
|
||||
u8 version;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
@@ -31,12 +32,26 @@
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
|
||||
constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
|
||||
|
||||
struct DisplayInfo {
|
||||
/// The name of this particular display.
|
||||
char display_name[0x40]{"Default"};
|
||||
u64 unknown_1{1};
|
||||
u64 unknown_2{1};
|
||||
u64 width{1280};
|
||||
u64 height{720};
|
||||
|
||||
/// Whether or not the display has a limited number of layers.
|
||||
u8 has_limited_layers{1};
|
||||
INSERT_PADDING_BYTES(7){};
|
||||
|
||||
/// Indicates the total amount of layers supported by the display.
|
||||
/// @note This is only valid if has_limited_layers is set.
|
||||
u64 max_layers{1};
|
||||
|
||||
/// Maximum width in pixels.
|
||||
u64 width{1920};
|
||||
|
||||
/// Maximum height in pixels.
|
||||
u64 height{1080};
|
||||
};
|
||||
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
|
||||
|
||||
@@ -502,10 +517,12 @@ private:
|
||||
|
||||
void TransactParcel(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 id = rp.Pop<u32>();
|
||||
auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
|
||||
u32 flags = rp.Pop<u32>();
|
||||
LOG_DEBUG(Service_VI, "called, transaction={:X}", static_cast<u32>(transaction));
|
||||
const u32 id = rp.Pop<u32>();
|
||||
const auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
|
||||
const u32 flags = rp.Pop<u32>();
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
|
||||
static_cast<u32>(transaction), flags);
|
||||
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
|
||||
@@ -593,9 +610,10 @@ private:
|
||||
|
||||
void AdjustRefcount(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 id = rp.Pop<u32>();
|
||||
s32 addval = rp.PopRaw<s32>();
|
||||
u32 type = rp.Pop<u32>();
|
||||
const u32 id = rp.Pop<u32>();
|
||||
const s32 addval = rp.PopRaw<s32>();
|
||||
const u32 type = rp.Pop<u32>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={:08X}, type={:08X}", id, addval,
|
||||
type);
|
||||
|
||||
@@ -605,11 +623,12 @@ private:
|
||||
|
||||
void GetNativeHandle(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 id = rp.Pop<u32>();
|
||||
u32 unknown = rp.Pop<u32>();
|
||||
const u32 id = rp.Pop<u32>();
|
||||
const u32 unknown = rp.Pop<u32>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
|
||||
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
const auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
|
||||
// TODO(Subv): Find out what this actually is.
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -674,11 +693,12 @@ public:
|
||||
|
||||
private:
|
||||
void SetLayerZ(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
u64 z_value = rp.Pop<u64>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
const u64 z_value = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}, z_value=0x{:016X}", layer_id,
|
||||
z_value);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -686,8 +706,9 @@ private:
|
||||
|
||||
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
bool visibility = rp.Pop<bool>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
const bool visibility = rp.Pop<bool>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
|
||||
visibility);
|
||||
|
||||
@@ -796,25 +817,27 @@ public:
|
||||
|
||||
private:
|
||||
void CloseDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display = rp.Pop<u64>();
|
||||
const u64 display = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. display=0x{:016X}", display);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void CreateManagedLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 unknown = rp.Pop<u32>();
|
||||
const u32 unknown = rp.Pop<u32>();
|
||||
rp.Skip(1, false);
|
||||
u64 display = rp.Pop<u64>();
|
||||
u64 aruid = rp.Pop<u64>();
|
||||
const u64 display = rp.Pop<u64>();
|
||||
const u64 aruid = rp.Pop<u64>();
|
||||
|
||||
u64 layer_id = nv_flinger->CreateLayer(display);
|
||||
LOG_WARNING(Service_VI,
|
||||
"(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
|
||||
unknown, display, aruid);
|
||||
|
||||
const u64 layer_id = nv_flinger->CreateLayer(display);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -822,11 +845,12 @@ private:
|
||||
}
|
||||
|
||||
void AddToLayerStack(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 stack = rp.Pop<u32>();
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
const u32 stack = rp.Pop<u32>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. stack=0x{:08X}, layer_id=0x{:016X}", stack,
|
||||
layer_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -834,8 +858,9 @@ private:
|
||||
|
||||
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
bool visibility = rp.Pop<bool>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
const bool visibility = rp.Pop<bool>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id,
|
||||
visibility);
|
||||
|
||||
@@ -852,6 +877,22 @@ public:
|
||||
~IApplicationDisplayService() = default;
|
||||
|
||||
private:
|
||||
enum class ConvertedScaleMode : u64 {
|
||||
Freeze = 0,
|
||||
ScaleToWindow = 1,
|
||||
ScaleAndCrop = 2,
|
||||
None = 3,
|
||||
PreserveAspectRatio = 4,
|
||||
};
|
||||
|
||||
enum class NintendoScaleMode : u32 {
|
||||
None = 0,
|
||||
Freeze = 1,
|
||||
ScaleToWindow = 2,
|
||||
ScaleAndCrop = 3,
|
||||
PreserveAspectRatio = 4,
|
||||
};
|
||||
|
||||
void GetRelayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
@@ -888,10 +929,23 @@ private:
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
const auto name_buf = rp.PopRaw<std::array<char, 0x40>>();
|
||||
|
||||
std::string name(name_buf.begin(), end);
|
||||
OpenDisplayImpl(ctx, std::string_view{name_buf.data(), name_buf.size()});
|
||||
}
|
||||
|
||||
void OpenDefaultDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
|
||||
OpenDisplayImpl(ctx, "Default");
|
||||
}
|
||||
|
||||
void OpenDisplayImpl(Kernel::HLERequestContext& ctx, std::string_view name) {
|
||||
const auto trim_pos = name.find('\0');
|
||||
|
||||
if (trim_pos != std::string_view::npos) {
|
||||
name.remove_suffix(name.size() - trim_pos);
|
||||
}
|
||||
|
||||
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
|
||||
|
||||
@@ -901,45 +955,65 @@ private:
|
||||
}
|
||||
|
||||
void CloseDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
const u64 display_id = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
// This literally does nothing internally in the actual service itself,
|
||||
// and just returns a successful result code regardless of the input.
|
||||
void SetDisplayEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called.");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetDisplayResolution(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
const u64 display_id = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. display_id=0x{:016X}", display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
rb.Push(static_cast<u64>(DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::DockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
} else {
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
}
|
||||
// This only returns the fixed values of 1280x720 and makes no distinguishing
|
||||
// between docked and undocked dimensions. We take the liberty of applying
|
||||
// the resolution scaling factor here.
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
}
|
||||
|
||||
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 scaling_mode = rp.Pop<u32>();
|
||||
u64 unknown = rp.Pop<u64>();
|
||||
const auto scaling_mode = rp.PopEnum<NintendoScaleMode>();
|
||||
const u64 unknown = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. scaling_mode=0x{:08X}, unknown=0x{:016X}",
|
||||
static_cast<u32>(scaling_mode), unknown);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) {
|
||||
LOG_ERROR(Service_VI, "Invalid scaling mode provided.");
|
||||
rb.Push(ERR_OPERATION_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (scaling_mode != NintendoScaleMode::ScaleToWindow &&
|
||||
scaling_mode != NintendoScaleMode::PreserveAspectRatio) {
|
||||
LOG_ERROR(Service_VI, "Unsupported scaling mode supplied.");
|
||||
rb.Push(ERR_UNSUPPORTED);
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -957,19 +1031,19 @@ private:
|
||||
}
|
||||
|
||||
void OpenLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
std::string display_name(name_buf.begin(), end);
|
||||
const std::string display_name(name_buf.begin(), end);
|
||||
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
u64 aruid = rp.Pop<u64>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
const u64 aruid = rp.Pop<u64>();
|
||||
|
||||
u64 display_id = nv_flinger->OpenDisplay(display_name);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
|
||||
|
||||
const u64 display_id = nv_flinger->OpenDisplay(display_name);
|
||||
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
@@ -978,17 +1052,17 @@ private:
|
||||
}
|
||||
|
||||
void CreateStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 flags = rp.Pop<u32>();
|
||||
const u32 flags = rp.Pop<u32>();
|
||||
rp.Pop<u32>(); // padding
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
const u64 display_id = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. flags=0x{:08X}, display_id=0x{:016X}", flags, display_id);
|
||||
|
||||
// TODO(Subv): What's the difference between a Stray and a Managed layer?
|
||||
|
||||
u64 layer_id = nv_flinger->CreateLayer(display_id);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
const u64 layer_id = nv_flinger->CreateLayer(display_id);
|
||||
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
@@ -998,73 +1072,59 @@ private:
|
||||
}
|
||||
|
||||
void DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
const u64 display_id = rp.Pop<u64>();
|
||||
|
||||
auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
|
||||
|
||||
const auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(vsync_event);
|
||||
}
|
||||
|
||||
enum class ConvertedScaleMode : u64 {
|
||||
None = 0, // VI seems to name this as "Unknown" but lots of games pass it, assume it's no
|
||||
// scaling/default
|
||||
Freeze = 1,
|
||||
ScaleToWindow = 2,
|
||||
Crop = 3,
|
||||
NoCrop = 4,
|
||||
};
|
||||
|
||||
// This struct is different, currently it's 1:1 but this might change in the future.
|
||||
enum class NintendoScaleMode : u32 {
|
||||
None = 0,
|
||||
Freeze = 1,
|
||||
ScaleToWindow = 2,
|
||||
Crop = 3,
|
||||
NoCrop = 4,
|
||||
};
|
||||
|
||||
void ConvertScalingMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto mode = rp.PopEnum<NintendoScaleMode>();
|
||||
const auto mode = rp.PopEnum<NintendoScaleMode>();
|
||||
LOG_DEBUG(Service_VI, "called mode={}", static_cast<u32>(mode));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
const auto converted_mode = ConvertScalingModeImpl(mode);
|
||||
|
||||
if (converted_mode.Succeeded()) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum(*converted_mode);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(converted_mode.Code());
|
||||
}
|
||||
}
|
||||
|
||||
static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) {
|
||||
switch (mode) {
|
||||
case NintendoScaleMode::None:
|
||||
rb.PushEnum(ConvertedScaleMode::None);
|
||||
break;
|
||||
return MakeResult(ConvertedScaleMode::None);
|
||||
case NintendoScaleMode::Freeze:
|
||||
rb.PushEnum(ConvertedScaleMode::Freeze);
|
||||
break;
|
||||
return MakeResult(ConvertedScaleMode::Freeze);
|
||||
case NintendoScaleMode::ScaleToWindow:
|
||||
rb.PushEnum(ConvertedScaleMode::ScaleToWindow);
|
||||
break;
|
||||
case NintendoScaleMode::Crop:
|
||||
rb.PushEnum(ConvertedScaleMode::Crop);
|
||||
break;
|
||||
case NintendoScaleMode::NoCrop:
|
||||
rb.PushEnum(ConvertedScaleMode::NoCrop);
|
||||
break;
|
||||
return MakeResult(ConvertedScaleMode::ScaleToWindow);
|
||||
case NintendoScaleMode::ScaleAndCrop:
|
||||
return MakeResult(ConvertedScaleMode::ScaleAndCrop);
|
||||
case NintendoScaleMode::PreserveAspectRatio:
|
||||
return MakeResult(ConvertedScaleMode::PreserveAspectRatio);
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown scaling mode {}", static_cast<u32>(mode));
|
||||
rb.PushEnum(ConvertedScaleMode::None);
|
||||
break;
|
||||
return ERR_OPERATION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1082,9 +1142,9 @@ IApplicationDisplayService::IApplicationDisplayService(
|
||||
"GetIndirectDisplayTransactionService"},
|
||||
{1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
|
||||
{1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
|
||||
{1011, nullptr, "OpenDefaultDisplay"},
|
||||
{1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
|
||||
{1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
|
||||
{1101, nullptr, "SetDisplayEnabled"},
|
||||
{1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
|
||||
{1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
|
||||
{2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
|
||||
{2021, nullptr, "CloseLayer"},
|
||||
|
||||
@@ -129,7 +129,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
|
||||
return ResultStatus::Error32BitISA;
|
||||
}
|
||||
|
||||
process.LoadFromMetadata(metadata);
|
||||
if (process.LoadFromMetadata(metadata).IsError()) {
|
||||
return ResultStatus::ErrorUnableToParseKernelMetadata;
|
||||
}
|
||||
|
||||
const FileSys::PatchManager pm(metadata.GetTitleID());
|
||||
|
||||
// Load NSO modules
|
||||
|
||||
@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
constexpr std::array<const char*, 60> RESULT_MESSAGES{
|
||||
constexpr std::array<const char*, 62> RESULT_MESSAGES{
|
||||
"The operation completed successfully.",
|
||||
"The loader requested to load is already loaded.",
|
||||
"The operation is not implemented.",
|
||||
@@ -103,6 +103,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{
|
||||
"The NPDM has a bad ACI header,",
|
||||
"The NPDM file has a bad file access control.",
|
||||
"The NPDM has a bad file access header.",
|
||||
"The NPDM has bad kernel capability descriptors.",
|
||||
"The PFS/HFS partition has a bad header.",
|
||||
"The PFS/HFS partition has incorrect size as determined by the header.",
|
||||
"The NCA file has a bad header.",
|
||||
@@ -125,6 +126,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{
|
||||
"The file could not be found or does not exist.",
|
||||
"The game is missing a program metadata file (main.npdm).",
|
||||
"The game uses the currently-unimplemented 32-bit architecture.",
|
||||
"Unable to completely parse the kernel metadata when loading the emulated process",
|
||||
"The RomFS could not be found.",
|
||||
"The ELF file has incorrect size as determined by the header.",
|
||||
"There was a general error loading the NRO into emulated memory.",
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
class NACP;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Kernel {
|
||||
struct AddressMapping;
|
||||
class Process;
|
||||
@@ -67,6 +71,7 @@ enum class ResultStatus : u16 {
|
||||
ErrorBadACIHeader,
|
||||
ErrorBadFileAccessControl,
|
||||
ErrorBadFileAccessHeader,
|
||||
ErrorBadKernelCapabilityDescriptors,
|
||||
ErrorBadPFSHeader,
|
||||
ErrorIncorrectPFSFileSize,
|
||||
ErrorBadNCAHeader,
|
||||
@@ -89,6 +94,7 @@ enum class ResultStatus : u16 {
|
||||
ErrorNullFile,
|
||||
ErrorMissingNPDM,
|
||||
Error32BitISA,
|
||||
ErrorUnableToParseKernelMetadata,
|
||||
ErrorNoRomFS,
|
||||
ErrorIncorrectELFFileSize,
|
||||
ErrorLoadingNRO,
|
||||
@@ -172,6 +178,8 @@ public:
|
||||
|
||||
/**
|
||||
* Get the banner (typically banner section) of the application
|
||||
* In the context of NX, this is the animation that displays in the bottom right of the screen
|
||||
* when a game boots. Stored in GIF format.
|
||||
* @param buffer Reference to buffer to store data
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
@@ -181,6 +189,8 @@ public:
|
||||
|
||||
/**
|
||||
* Get the logo (typically logo section) of the application
|
||||
* In the context of NX, this is the static image that displays in the top left of the screen
|
||||
* when a game boots. Stored in JPEG format.
|
||||
* @param buffer Reference to buffer to store data
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
@@ -245,11 +255,20 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the developer of the application
|
||||
* @param developer Reference to store the application developer into
|
||||
* Get the control data (CNMT) of the application
|
||||
* @param control Reference to store the application control data into
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadDeveloper(std::string& developer) {
|
||||
virtual ResultStatus ReadControlData(FileSys::NACP& control) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the RomFS of the manual of the application
|
||||
* @param file The raw manual RomFS of the game
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,4 +79,13 @@ u64 AppLoader_NAX::ReadRomFSIVFCOffset() const {
|
||||
ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) {
|
||||
return nca_loader->ReadProgramId(out_program_id);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadBanner(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadBanner(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NAX::ReadLogo(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadLogo(buffer);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
@@ -39,6 +39,9 @@ public:
|
||||
u64 ReadRomFSIVFCOffset() const override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NAX> nax;
|
||||
std::unique_ptr<AppLoader_NCA> nca_loader;
|
||||
|
||||
@@ -84,4 +84,23 @@ ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadBanner(std::vector<u8>& buffer) {
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
const auto logo = nca->GetLogoPartition();
|
||||
if (logo == nullptr)
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
buffer = logo->GetFile("StartupMovie.gif")->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadLogo(std::vector<u8>& buffer) {
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
const auto logo = nca->GetLogoPartition();
|
||||
if (logo == nullptr)
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
} // namespace Loader
|
||||
|
||||
@@ -39,6 +39,9 @@ public:
|
||||
u64 ReadRomFSIVFCOffset() const override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NCA> nca;
|
||||
std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
|
||||
|
||||
@@ -152,10 +152,27 @@ ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) {
|
||||
ResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) {
|
||||
if (nacp_file == nullptr)
|
||||
return ResultStatus::ErrorNoControl;
|
||||
developer = nacp_file->GetDeveloperName();
|
||||
nacp = *nacp_file;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& file) {
|
||||
const auto nca = nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Manual);
|
||||
if (nsp->GetStatus() != ResultStatus::Success || nca == nullptr)
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
file = nca->GetRomFS();
|
||||
return file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadBanner(std::vector<u8>& buffer) {
|
||||
return secondary_loader->ReadBanner(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadLogo(std::vector<u8>& buffer) {
|
||||
return secondary_loader->ReadLogo(buffer);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
@@ -43,7 +43,11 @@ public:
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadDeveloper(std::string& developer) override;
|
||||
ResultStatus ReadControlData(FileSys::NACP& nacp) override;
|
||||
ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NSP> nsp;
|
||||
|
||||
@@ -121,10 +121,28 @@ ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) {
|
||||
ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
|
||||
if (nacp_file == nullptr)
|
||||
return ResultStatus::ErrorNoControl;
|
||||
developer = nacp_file->GetDeveloperName();
|
||||
control = *nacp_file;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& file) {
|
||||
const auto nca = xci->GetSecurePartitionNSP()->GetNCA(xci->GetProgramTitleID(),
|
||||
FileSys::ContentRecordType::Manual);
|
||||
if (xci->GetStatus() != ResultStatus::Success || nca == nullptr)
|
||||
return ResultStatus::ErrorXCIMissingPartition;
|
||||
file = nca->GetRomFS();
|
||||
return file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadBanner(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadBanner(buffer);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadLogo(std::vector<u8>& buffer) {
|
||||
return nca_loader->ReadLogo(buffer);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
@@ -43,7 +43,11 @@ public:
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadDeveloper(std::string& developer) override;
|
||||
ResultStatus ReadControlData(FileSys::NACP& control) override;
|
||||
ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) override;
|
||||
|
||||
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::XCI> xci;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -350,6 +351,11 @@ struct Values {
|
||||
bool use_docked_mode;
|
||||
bool enable_nfc;
|
||||
std::optional<u32> rng_seed;
|
||||
// Measured in seconds since epoch
|
||||
std::optional<std::chrono::seconds> custom_rtc;
|
||||
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
|
||||
std::chrono::seconds custom_rtc_differential;
|
||||
|
||||
s32 current_user;
|
||||
s32 language_index;
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ add_library(video_core STATIC
|
||||
renderer_base.h
|
||||
renderer_opengl/gl_buffer_cache.cpp
|
||||
renderer_opengl/gl_buffer_cache.h
|
||||
renderer_opengl/gl_global_cache.cpp
|
||||
renderer_opengl/gl_global_cache.h
|
||||
renderer_opengl/gl_primitive_assembler.cpp
|
||||
renderer_opengl/gl_primitive_assembler.h
|
||||
renderer_opengl/gl_rasterizer.cpp
|
||||
|
||||
@@ -135,6 +135,33 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
|
||||
if (regs.reg_array[method_call.method] != method_call.argument) {
|
||||
regs.reg_array[method_call.method] = method_call.argument;
|
||||
// Color buffers
|
||||
constexpr u32 first_rt_reg = MAXWELL3D_REG_INDEX(rt);
|
||||
constexpr u32 registers_per_rt = sizeof(regs.rt[0]) / sizeof(u32);
|
||||
if (method_call.method >= first_rt_reg &&
|
||||
method_call.method < first_rt_reg + registers_per_rt * Regs::NumRenderTargets) {
|
||||
const std::size_t rt_index = (method_call.method - first_rt_reg) / registers_per_rt;
|
||||
dirty_flags.color_buffer |= 1u << static_cast<u32>(rt_index);
|
||||
}
|
||||
|
||||
// Zeta buffer
|
||||
constexpr u32 registers_in_zeta = sizeof(regs.zeta) / sizeof(u32);
|
||||
if (method_call.method == MAXWELL3D_REG_INDEX(zeta_enable) ||
|
||||
method_call.method == MAXWELL3D_REG_INDEX(zeta_width) ||
|
||||
method_call.method == MAXWELL3D_REG_INDEX(zeta_height) ||
|
||||
(method_call.method >= MAXWELL3D_REG_INDEX(zeta) &&
|
||||
method_call.method < MAXWELL3D_REG_INDEX(zeta) + registers_in_zeta)) {
|
||||
dirty_flags.zeta_buffer = true;
|
||||
}
|
||||
|
||||
// Shader
|
||||
constexpr u32 shader_registers_count =
|
||||
sizeof(regs.shader_config[0]) * Regs::MaxShaderProgram / sizeof(u32);
|
||||
if (method_call.method >= MAXWELL3D_REG_INDEX(shader_config[0]) &&
|
||||
method_call.method < MAXWELL3D_REG_INDEX(shader_config[0]) + shader_registers_count) {
|
||||
dirty_flags.shaders = true;
|
||||
}
|
||||
|
||||
// Vertex format
|
||||
if (method_call.method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) &&
|
||||
method_call.method <
|
||||
|
||||
@@ -1089,10 +1089,18 @@ public:
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
struct DirtyFlags {
|
||||
u8 color_buffer = 0xFF;
|
||||
bool zeta_buffer = true;
|
||||
|
||||
bool shaders = true;
|
||||
|
||||
bool vertex_attrib_format = true;
|
||||
u32 vertex_array = 0xFFFFFFFF;
|
||||
|
||||
void OnMemoryWrite() {
|
||||
color_buffer = 0xFF;
|
||||
zeta_buffer = true;
|
||||
shaders = true;
|
||||
vertex_array = 0xFFFFFFFF;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -66,8 +66,6 @@ static constexpr ConversionArray morton_to_linear_fns = {
|
||||
MortonCopy<true, PixelFormat::BC6H_UF16>,
|
||||
MortonCopy<true, PixelFormat::BC6H_SF16>,
|
||||
MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
|
||||
MortonCopy<true, PixelFormat::G8R8U>,
|
||||
MortonCopy<true, PixelFormat::G8R8S>,
|
||||
MortonCopy<true, PixelFormat::BGRA8>,
|
||||
MortonCopy<true, PixelFormat::RGBA32F>,
|
||||
MortonCopy<true, PixelFormat::RG32F>,
|
||||
@@ -138,8 +136,6 @@ static constexpr ConversionArray linear_to_morton_fns = {
|
||||
MortonCopy<false, PixelFormat::BC6H_SF16>,
|
||||
// TODO(Subv): Swizzling ASTC formats are not supported
|
||||
nullptr,
|
||||
MortonCopy<false, PixelFormat::G8R8U>,
|
||||
MortonCopy<false, PixelFormat::G8R8S>,
|
||||
MortonCopy<false, PixelFormat::BGRA8>,
|
||||
MortonCopy<false, PixelFormat::RGBA32F>,
|
||||
MortonCopy<false, PixelFormat::RG32F>,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/gpu.h"
|
||||
@@ -11,6 +12,14 @@
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
enum class LoadCallbackStage {
|
||||
Prepare,
|
||||
Decompile,
|
||||
Build,
|
||||
Complete,
|
||||
};
|
||||
using DiskResourceLoadCallback = std::function<void(LoadCallbackStage, std::size_t, std::size_t)>;
|
||||
|
||||
class RasterizerInterface {
|
||||
public:
|
||||
virtual ~RasterizerInterface() {}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace OpenGL {
|
||||
|
||||
OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
|
||||
: RasterizerCache{rasterizer}, stream_buffer(GL_ARRAY_BUFFER, size) {}
|
||||
: RasterizerCache{rasterizer}, stream_buffer(size, true) {}
|
||||
|
||||
GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
|
||||
std::size_t alignment, bool cache) {
|
||||
|
||||
24
src/video_core/renderer_opengl/gl_global_cache.cpp
Normal file
24
src/video_core/renderer_opengl/gl_global_cache.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "video_core/renderer_opengl/gl_global_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
CachedGlobalRegion::CachedGlobalRegion(VAddr addr, u32 size) : addr{addr}, size{size} {
|
||||
buffer.Create();
|
||||
// Bind and unbind the buffer so it gets allocated by the driver
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
LabelGLObject(GL_BUFFER, buffer.handle, addr, "GlobalMemory");
|
||||
}
|
||||
|
||||
GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
|
||||
: RasterizerCache{rasterizer} {}
|
||||
|
||||
} // namespace OpenGL
|
||||
60
src/video_core/renderer_opengl/gl_global_cache.h
Normal file
60
src/video_core/renderer_opengl/gl_global_cache.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
namespace GLShader {
|
||||
class GlobalMemoryEntry;
|
||||
} // namespace GLShader
|
||||
|
||||
class RasterizerOpenGL;
|
||||
class CachedGlobalRegion;
|
||||
using GlobalRegion = std::shared_ptr<CachedGlobalRegion>;
|
||||
|
||||
class CachedGlobalRegion final : public RasterizerCacheObject {
|
||||
public:
|
||||
explicit CachedGlobalRegion(VAddr addr, u32 size);
|
||||
|
||||
/// Gets the address of the shader in guest memory, required for cache management
|
||||
VAddr GetAddr() const {
|
||||
return addr;
|
||||
}
|
||||
|
||||
/// Gets the size of the shader in guest memory, required for cache management
|
||||
std::size_t GetSizeInBytes() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
/// Gets the GL program handle for the buffer
|
||||
GLuint GetBufferHandle() const {
|
||||
return buffer.handle;
|
||||
}
|
||||
|
||||
// TODO(Rodrigo): When global memory is written (STG), implement flushing
|
||||
void Flush() override {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
private:
|
||||
VAddr addr{};
|
||||
u32 size{};
|
||||
|
||||
OGLBuffer buffer;
|
||||
};
|
||||
|
||||
class GlobalRegionCacheOpenGL final : public RasterizerCache<GlobalRegion> {
|
||||
public:
|
||||
explicit GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer);
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -101,7 +101,7 @@ struct FramebufferCacheKey {
|
||||
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info)
|
||||
: res_cache{*this}, shader_cache{*this}, emu_window{window}, screen_info{info},
|
||||
buffer_cache(*this, STREAM_BUFFER_SIZE) {
|
||||
buffer_cache(*this, STREAM_BUFFER_SIZE), global_cache{*this} {
|
||||
// Create sampler objects
|
||||
for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
|
||||
texture_samplers[i].Create();
|
||||
@@ -135,27 +135,31 @@ void RasterizerOpenGL::CheckExtensions() {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupVertexFormat() {
|
||||
GLuint RasterizerOpenGL::SetupVertexFormat() {
|
||||
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
if (!gpu.dirty_flags.vertex_attrib_format)
|
||||
return;
|
||||
if (!gpu.dirty_flags.vertex_attrib_format) {
|
||||
return state.draw.vertex_array;
|
||||
}
|
||||
gpu.dirty_flags.vertex_attrib_format = false;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_VAO);
|
||||
|
||||
auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format);
|
||||
auto& VAO = iter->second;
|
||||
auto& vao_entry = iter->second;
|
||||
|
||||
if (is_cache_miss) {
|
||||
VAO.Create();
|
||||
state.draw.vertex_array = VAO.handle;
|
||||
state.ApplyVertexBufferState();
|
||||
vao_entry.Create();
|
||||
const GLuint vao = vao_entry.handle;
|
||||
|
||||
// The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work
|
||||
// around.
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_cache.GetHandle());
|
||||
// Eventhough we are using DSA to create this vertex array, there is a bug on Intel's blob
|
||||
// that fails to properly create the vertex array if it's not bound even after creating it
|
||||
// with glCreateVertexArrays
|
||||
state.draw.vertex_array = vao;
|
||||
state.ApplyVertexArrayState();
|
||||
|
||||
glVertexArrayElementBuffer(vao, buffer_cache.GetHandle());
|
||||
|
||||
// Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL.
|
||||
// Enables the first 16 vertex attributes always, as we don't know which ones are actually
|
||||
@@ -163,7 +167,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
|
||||
// for now to avoid OpenGL errors.
|
||||
// TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
|
||||
// assume every shader uses them all.
|
||||
for (unsigned index = 0; index < 16; ++index) {
|
||||
for (u32 index = 0; index < 16; ++index) {
|
||||
const auto& attrib = regs.vertex_attrib_format[index];
|
||||
|
||||
// Ignore invalid attributes.
|
||||
@@ -178,28 +182,29 @@ void RasterizerOpenGL::SetupVertexFormat() {
|
||||
|
||||
ASSERT(buffer.IsEnabled());
|
||||
|
||||
glEnableVertexAttribArray(index);
|
||||
glEnableVertexArrayAttrib(vao, index);
|
||||
if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
|
||||
attrib.type ==
|
||||
Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
|
||||
glVertexAttribIFormat(index, attrib.ComponentCount(),
|
||||
MaxwellToGL::VertexType(attrib), attrib.offset);
|
||||
glVertexArrayAttribIFormat(vao, index, attrib.ComponentCount(),
|
||||
MaxwellToGL::VertexType(attrib), attrib.offset);
|
||||
} else {
|
||||
glVertexAttribFormat(index, attrib.ComponentCount(),
|
||||
MaxwellToGL::VertexType(attrib),
|
||||
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
|
||||
glVertexArrayAttribFormat(
|
||||
vao, index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
|
||||
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
|
||||
}
|
||||
glVertexAttribBinding(index, attrib.buffer);
|
||||
glVertexArrayAttribBinding(vao, index, attrib.buffer);
|
||||
}
|
||||
}
|
||||
state.draw.vertex_array = VAO.handle;
|
||||
state.ApplyVertexBufferState();
|
||||
|
||||
// Rebinding the VAO invalidates the vertex buffer bindings.
|
||||
gpu.dirty_flags.vertex_array = 0xFFFFFFFF;
|
||||
|
||||
state.draw.vertex_array = vao_entry.handle;
|
||||
return vao_entry.handle;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupVertexBuffer() {
|
||||
void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
|
||||
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
@@ -217,7 +222,7 @@ void RasterizerOpenGL::SetupVertexBuffer() {
|
||||
if (!vertex_array.IsEnabled())
|
||||
continue;
|
||||
|
||||
Tegra::GPUVAddr start = vertex_array.StartAddress();
|
||||
const Tegra::GPUVAddr start = vertex_array.StartAddress();
|
||||
const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
|
||||
|
||||
ASSERT(end > start);
|
||||
@@ -225,21 +230,18 @@ void RasterizerOpenGL::SetupVertexBuffer() {
|
||||
const GLintptr vertex_buffer_offset = buffer_cache.UploadMemory(start, size);
|
||||
|
||||
// Bind the vertex array to the buffer at the current offset.
|
||||
glBindVertexBuffer(index, buffer_cache.GetHandle(), vertex_buffer_offset,
|
||||
vertex_array.stride);
|
||||
glVertexArrayVertexBuffer(vao, index, buffer_cache.GetHandle(), vertex_buffer_offset,
|
||||
vertex_array.stride);
|
||||
|
||||
if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
|
||||
// Enable vertex buffer instancing with the specified divisor.
|
||||
glVertexBindingDivisor(index, vertex_array.divisor);
|
||||
glVertexArrayBindingDivisor(vao, index, vertex_array.divisor);
|
||||
} else {
|
||||
// Disable the vertex buffer instancing.
|
||||
glVertexBindingDivisor(index, 0);
|
||||
glVertexArrayBindingDivisor(vao, index, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Implicit set by glBindVertexBuffer. Stupid glstate handling...
|
||||
state.draw.vertex_buffer = buffer_cache.GetHandle();
|
||||
|
||||
gpu.dirty_flags.vertex_array = 0;
|
||||
}
|
||||
|
||||
@@ -293,7 +295,7 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
|
||||
|
||||
void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Shader);
|
||||
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
|
||||
// Next available bindpoints to use when uploading the const buffers and textures to the GLSL
|
||||
// shaders. The constbuffer bindpoint starts after the shader stage configuration bind points.
|
||||
@@ -365,7 +367,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
// (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the
|
||||
// clip distances only when it's written by a shader stage.
|
||||
for (std::size_t i = 0; i < Maxwell::NumClipDistances; ++i) {
|
||||
clip_distances[i] |= shader->GetShaderEntries().clip_distances[i];
|
||||
clip_distances[i] = clip_distances[i] || shader->GetShaderEntries().clip_distances[i];
|
||||
}
|
||||
|
||||
// When VertexA is enabled, we have dual vertex shaders
|
||||
@@ -376,6 +378,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
}
|
||||
|
||||
SyncClipEnabled(clip_distances);
|
||||
|
||||
gpu.dirty_flags.shaders = false;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupCachedFramebuffer(const FramebufferCacheKey& fbkey,
|
||||
@@ -486,7 +490,19 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us
|
||||
bool using_depth_fb, bool preserve_contents,
|
||||
std::optional<std::size_t> single_color_target) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents,
|
||||
single_color_target};
|
||||
if (fb_config_state == current_framebuffer_config_state && gpu.dirty_flags.color_buffer == 0 &&
|
||||
!gpu.dirty_flags.zeta_buffer) {
|
||||
// Only skip if the previous ConfigureFramebuffers call was from the same kind (multiple or
|
||||
// single color targets). This is done because the guest registers may not change but the
|
||||
// host framebuffer may contain different attachments
|
||||
return;
|
||||
}
|
||||
current_framebuffer_config_state = fb_config_state;
|
||||
|
||||
Surface depth_surface;
|
||||
if (using_depth_fb) {
|
||||
@@ -689,9 +705,6 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
// Draw the vertex batch
|
||||
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
|
||||
|
||||
state.draw.vertex_buffer = buffer_cache.GetHandle();
|
||||
state.ApplyVertexBufferState();
|
||||
|
||||
std::size_t buffer_size = CalculateVertexArraysSize();
|
||||
|
||||
// Add space for index buffer (keeping in mind non-core primitives)
|
||||
@@ -721,8 +734,9 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
gpu.dirty_flags.vertex_array = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
SetupVertexFormat();
|
||||
SetupVertexBuffer();
|
||||
const GLuint vao = SetupVertexFormat();
|
||||
SetupVertexBuffer(vao);
|
||||
|
||||
DrawParameters params = SetupDraw();
|
||||
SetupShaders(params.primitive_mode);
|
||||
|
||||
@@ -761,6 +775,7 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.InvalidateRegion(addr, size);
|
||||
shader_cache.InvalidateRegion(addr, size);
|
||||
global_cache.InvalidateRegion(addr, size);
|
||||
buffer_cache.InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
@@ -1014,8 +1029,11 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
||||
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
|
||||
Surface surface = res_cache.GetTextureSurface(texture, entry);
|
||||
if (surface != nullptr) {
|
||||
state.texture_units[current_bindpoint].texture = surface->Texture().handle;
|
||||
state.texture_units[current_bindpoint].target = surface->Target();
|
||||
const GLuint handle =
|
||||
entry.IsArray() ? surface->TextureLayer().handle : surface->Texture().handle;
|
||||
const GLenum target = entry.IsArray() ? surface->TargetLayer() : surface->Target();
|
||||
state.texture_units[current_bindpoint].texture = handle;
|
||||
state.texture_units[current_bindpoint].target = target;
|
||||
state.texture_units[current_bindpoint].swizzle.r =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.x_source);
|
||||
state.texture_units[current_bindpoint].swizzle.g =
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_global_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_primitive_assembler.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
@@ -66,6 +67,10 @@ public:
|
||||
static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0,
|
||||
"The maximum size of a constbuffer must be a multiple of the size of GLvec4");
|
||||
|
||||
static constexpr std::size_t MaxGlobalMemorySize = 0x10000;
|
||||
static_assert(MaxGlobalMemorySize % sizeof(float) == 0,
|
||||
"The maximum size of a global memory must be a multiple of the size of float");
|
||||
|
||||
private:
|
||||
class SamplerInfo {
|
||||
public:
|
||||
@@ -94,6 +99,23 @@ private:
|
||||
float max_anisotropic = 1.0f;
|
||||
};
|
||||
|
||||
struct FramebufferConfigState {
|
||||
bool using_color_fb{};
|
||||
bool using_depth_fb{};
|
||||
bool preserve_contents{};
|
||||
std::optional<std::size_t> single_color_target;
|
||||
|
||||
bool operator==(const FramebufferConfigState& rhs) const {
|
||||
return std::tie(using_color_fb, using_depth_fb, preserve_contents,
|
||||
single_color_target) == std::tie(rhs.using_color_fb, rhs.using_depth_fb,
|
||||
rhs.preserve_contents,
|
||||
rhs.single_color_target);
|
||||
}
|
||||
bool operator!=(const FramebufferConfigState& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Configures the color and depth framebuffer states.
|
||||
* @param use_color_fb If true, configure color framebuffers.
|
||||
@@ -105,7 +127,7 @@ private:
|
||||
bool using_depth_fb = true, bool preserve_contents = true,
|
||||
std::optional<std::size_t> single_color_target = {});
|
||||
|
||||
/*
|
||||
/**
|
||||
* Configures the current constbuffers to use for the draw command.
|
||||
* @param stage The shader stage to configure buffers for.
|
||||
* @param shader The shader object that contains the specified stage.
|
||||
@@ -115,7 +137,7 @@ private:
|
||||
u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
|
||||
GLenum primitive_mode, u32 current_bindpoint);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Configures the current textures to use for the draw command.
|
||||
* @param stage The shader stage to configure textures for.
|
||||
* @param shader The shader object that contains the specified stage.
|
||||
@@ -185,6 +207,7 @@ private:
|
||||
|
||||
RasterizerCacheOpenGL res_cache;
|
||||
ShaderCacheOpenGL shader_cache;
|
||||
GlobalRegionCacheOpenGL global_cache;
|
||||
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
|
||||
@@ -197,6 +220,7 @@ private:
|
||||
vertex_array_cache;
|
||||
|
||||
std::map<FramebufferCacheKey, OGLFramebuffer> framebuffer_cache;
|
||||
FramebufferConfigState current_framebuffer_config_state;
|
||||
|
||||
std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers;
|
||||
|
||||
@@ -209,8 +233,10 @@ private:
|
||||
|
||||
std::size_t CalculateIndexBufferSize() const;
|
||||
|
||||
void SetupVertexFormat();
|
||||
void SetupVertexBuffer();
|
||||
/// Updates and returns a vertex array object representing current vertex format
|
||||
GLuint SetupVertexFormat();
|
||||
|
||||
void SetupVertexBuffer(GLuint vao);
|
||||
|
||||
DrawParameters SetupDraw();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user