Compare commits
175 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c68aa65226 | ||
|
|
a921d22545 | ||
|
|
ee07041b3a | ||
|
|
9c977d2215 | ||
|
|
f2c7b5dcd6 | ||
|
|
d37da52cb3 | ||
|
|
969326bd58 | ||
|
|
224071a652 | ||
|
|
2dad1204e8 | ||
|
|
249341d08f | ||
|
|
261a4f0311 | ||
|
|
ca4bf671ce | ||
|
|
28e90fa0e0 | ||
|
|
0a93b45b6a | ||
|
|
403dfd68fc | ||
|
|
c519354506 | ||
|
|
0b668d5ff3 | ||
|
|
bc286c169f | ||
|
|
670a2c1f80 | ||
|
|
88ffa422d4 | ||
|
|
0471976b48 | ||
|
|
c1ad973881 | ||
|
|
305a05f820 | ||
|
|
d64303d185 | ||
|
|
b8b9f41b6b | ||
|
|
dfcde52f39 | ||
|
|
4f0818144e | ||
|
|
20c2928c2b | ||
|
|
f380496728 | ||
|
|
2e80e7480d | ||
|
|
8eb97706b8 | ||
|
|
aaf671a309 | ||
|
|
be53097577 | ||
|
|
0a003efde4 | ||
|
|
2e8620c877 | ||
|
|
6b0bc48a42 | ||
|
|
a5b65df9cf | ||
|
|
57626fda7b | ||
|
|
6313d54cef | ||
|
|
7e6a73963e | ||
|
|
2156cb3cbe | ||
|
|
ef41983c84 | ||
|
|
8069fbd37f | ||
|
|
ec3bef7b4c | ||
|
|
0e1510ac29 | ||
|
|
b8c43b6080 | ||
|
|
3a67876252 | ||
|
|
6828c25498 | ||
|
|
5191c20b71 | ||
|
|
a91bb7080d | ||
|
|
69cd213fac | ||
|
|
05c33d89a1 | ||
|
|
96ef22d3d0 | ||
|
|
e8c52d4c89 | ||
|
|
37e1ed3744 | ||
|
|
75e12a33ae | ||
|
|
e58f7ff843 | ||
|
|
6ef027b958 | ||
|
|
59ea37daa7 | ||
|
|
0bfe974281 | ||
|
|
88b18b9ba4 | ||
|
|
b125137493 | ||
|
|
f765a6b902 | ||
|
|
5cb6eceecf | ||
|
|
b46a5c42ff | ||
|
|
ff5024ee2a | ||
|
|
5c908c0373 | ||
|
|
c333bfc193 | ||
|
|
e831b80d69 | ||
|
|
434f352eb3 | ||
|
|
5b1b758326 | ||
|
|
dfc3eed0cb | ||
|
|
06d0b96ca9 | ||
|
|
4283019aa0 | ||
|
|
efe6b473c5 | ||
|
|
557c466994 | ||
|
|
25ba4d1b68 | ||
|
|
668458525e | ||
|
|
2f4f4f147f | ||
|
|
94cf327e77 | ||
|
|
2b6128fe0b | ||
|
|
dad2ae1ee0 | ||
|
|
656e97df16 | ||
|
|
52a2e42cb9 | ||
|
|
4b471f0554 | ||
|
|
aaa8fdea52 | ||
|
|
2de2ec25d6 | ||
|
|
3f82dad1e4 | ||
|
|
3bf488ce52 | ||
|
|
b36dee364e | ||
|
|
ddec200290 | ||
|
|
cf917a5e93 | ||
|
|
9ceceb212f | ||
|
|
9f48454ea9 | ||
|
|
7353cfc781 | ||
|
|
4afb05d0cc | ||
|
|
cc2526dd51 | ||
|
|
096b04f1a4 | ||
|
|
756e1e6f9b | ||
|
|
6e90f0bf6a | ||
|
|
599c011f40 | ||
|
|
5a9c00ea04 | ||
|
|
76197a4be9 | ||
|
|
cc9d7bbf01 | ||
|
|
d224eb7c39 | ||
|
|
fd9da4232b | ||
|
|
507e6ae100 | ||
|
|
6a2f415298 | ||
|
|
448264e719 | ||
|
|
c0d44d3b2a | ||
|
|
f156a45c01 | ||
|
|
6a5d032809 | ||
|
|
4941e3d412 | ||
|
|
7bf422d58c | ||
|
|
9fde7d739a | ||
|
|
3c498189b6 | ||
|
|
7f0d0a93f7 | ||
|
|
57982df105 | ||
|
|
8c6338b6f9 | ||
|
|
c120ed7d18 | ||
|
|
0f834e2284 | ||
|
|
aaf8d9ac2f | ||
|
|
e542356d0c | ||
|
|
b7fb9f2071 | ||
|
|
934a2b9604 | ||
|
|
2bc296801a | ||
|
|
80cfd88e4e | ||
|
|
d378d98e26 | ||
|
|
766c1a2d50 | ||
|
|
5c323d96e0 | ||
|
|
4e3bc37791 | ||
|
|
a7d6efc520 | ||
|
|
cd1a96f389 | ||
|
|
62b0b83fd8 | ||
|
|
825e8cb925 | ||
|
|
1fbb7aff1e | ||
|
|
4fa3511a63 | ||
|
|
7139f05fc5 | ||
|
|
e530f82dd3 | ||
|
|
1abfd4166e | ||
|
|
b9829a05be | ||
|
|
7ed8565978 | ||
|
|
6576bc8927 | ||
|
|
2d57cbaec1 | ||
|
|
3242e7c164 | ||
|
|
c707240685 | ||
|
|
573a66c23d | ||
|
|
97c6f984dc | ||
|
|
07595987ac | ||
|
|
b09c4f45c7 | ||
|
|
0735a0c8a1 | ||
|
|
bc6d91a103 | ||
|
|
c392650e21 | ||
|
|
8f73f41824 | ||
|
|
c4397ec77e | ||
|
|
438d9aa407 | ||
|
|
3e81c09094 | ||
|
|
d3f64785d1 | ||
|
|
300ab211e8 | ||
|
|
fa8017295b | ||
|
|
e40b0cf437 | ||
|
|
7e49881b7f | ||
|
|
0f5c4615ae | ||
|
|
890e543304 | ||
|
|
20c976ff2a | ||
|
|
45bc449ff9 | ||
|
|
c8f6754417 | ||
|
|
0db0e4c8f3 | ||
|
|
91cfe70301 | ||
|
|
e4422b09b6 | ||
|
|
5927cf0e17 | ||
|
|
9e88f03e75 | ||
|
|
da2f00ab7d | ||
|
|
e3321c2e00 | ||
|
|
2cc962e171 |
@@ -4,8 +4,10 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
|
||||
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(PLATFORMS ${DLL_DEST}platforms/)
|
||||
set(STYLES ${DLL_DEST}styles/)
|
||||
set(IMAGEFORMATS ${DLL_DEST}imageformats/)
|
||||
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
|
||||
icudt*.dll
|
||||
icuin*.dll
|
||||
@@ -17,4 +19,5 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
)
|
||||
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>.*)
|
||||
endfunction(copy_yuzu_Qt5_deps)
|
||||
|
||||
@@ -117,6 +117,7 @@ after_build:
|
||||
mkdir $RELEASE_DIST
|
||||
mkdir $RELEASE_DIST/platforms
|
||||
mkdir $RELEASE_DIST/styles
|
||||
mkdir $RELEASE_DIST/imageformats
|
||||
|
||||
# copy the compiled binaries and other release files to the release folder
|
||||
Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
@@ -140,6 +141,9 @@ after_build:
|
||||
# copy the qt windows vista style dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles"
|
||||
|
||||
# copy the qt jpeg imageformat dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
|
||||
|
||||
7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
|
||||
7z a $MINGW_SEVENZIP $RELEASE_DIST
|
||||
}
|
||||
|
||||
6
externals/CMakeLists.txt
vendored
6
externals/CMakeLists.txt
vendored
@@ -32,11 +32,11 @@ add_subdirectory(inih)
|
||||
|
||||
# lz4
|
||||
set(LZ4_BUNDLED_MODE ON)
|
||||
add_subdirectory(lz4/contrib/cmake_unofficial)
|
||||
add_subdirectory(lz4/contrib/cmake_unofficial EXCLUDE_FROM_ALL)
|
||||
target_include_directories(lz4_static INTERFACE ./lz4/lib)
|
||||
|
||||
# mbedtls
|
||||
add_subdirectory(mbedtls)
|
||||
add_subdirectory(mbedtls EXCLUDE_FROM_ALL)
|
||||
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
|
||||
# MicroProfile
|
||||
@@ -62,5 +62,5 @@ target_include_directories(opus INTERFACE ./opus/include)
|
||||
# Cubeb
|
||||
if(ENABLE_CUBEB)
|
||||
set(BUILD_TESTS OFF CACHE BOOL "")
|
||||
add_subdirectory(cubeb)
|
||||
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
2
externals/catch
vendored
2
externals/catch
vendored
Submodule externals/catch updated: d2a130f243...15cf3caace
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 4f96c63025...0118ee04f9
23
externals/glad/include/glad/glad.h
vendored
23
externals/glad/include/glad/glad.h
vendored
File diff suppressed because one or more lines are too long
6385
externals/glad/src/glad.c
vendored
6385
externals/glad/src/glad.c
vendored
File diff suppressed because one or more lines are too long
2
externals/mbedtls
vendored
2
externals/mbedtls
vendored
Submodule externals/mbedtls updated: 06442b840e...d409b75a4c
@@ -4,8 +4,6 @@ add_library(audio_core STATIC
|
||||
audio_renderer.cpp
|
||||
audio_renderer.h
|
||||
buffer.h
|
||||
cubeb_sink.cpp
|
||||
cubeb_sink.h
|
||||
codec.cpp
|
||||
codec.h
|
||||
null_sink.h
|
||||
@@ -15,6 +13,8 @@ add_library(audio_core STATIC
|
||||
sink_details.cpp
|
||||
sink_details.h
|
||||
sink_stream.h
|
||||
|
||||
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
|
||||
)
|
||||
|
||||
create_target_directory_groups(audio_core)
|
||||
|
||||
@@ -9,13 +9,13 @@ namespace Common {
|
||||
|
||||
template <typename T>
|
||||
constexpr T AlignUp(T value, size_t size) {
|
||||
static_assert(std::is_unsigned<T>::value, "T must be an unsigned value.");
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value + (size - value % size) % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T AlignDown(T value, size_t size) {
|
||||
static_assert(std::is_unsigned<T>::value, "T must be an unsigned value.");
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ static inline int LeastSignificantSetBit(u64 val) {
|
||||
|
||||
template <typename IntTy>
|
||||
class BitSet {
|
||||
static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
|
||||
static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types");
|
||||
|
||||
public:
|
||||
// A reference to a particular bit, returned from operator[].
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "common/vector_math.h"
|
||||
@@ -55,7 +57,7 @@ constexpr u8 Convert8To6(u8 value) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
|
||||
inline Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
|
||||
return {bytes[3], bytes[2], bytes[1], bytes[0]};
|
||||
}
|
||||
|
||||
@@ -64,7 +66,7 @@ inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
|
||||
inline Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
|
||||
return {bytes[2], bytes[1], bytes[0], 255};
|
||||
}
|
||||
|
||||
@@ -73,7 +75,7 @@ inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
|
||||
inline Math::Vec4<u8> DecodeRG8(const u8* bytes) {
|
||||
return {bytes[1], bytes[0], 0, 255};
|
||||
}
|
||||
|
||||
@@ -82,8 +84,9 @@ inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
|
||||
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
|
||||
inline Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
|
||||
u16_le pixel;
|
||||
std::memcpy(&pixel, bytes, sizeof(pixel));
|
||||
return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
|
||||
Convert5To8(pixel & 0x1F), 255};
|
||||
}
|
||||
@@ -93,8 +96,9 @@ inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
|
||||
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
|
||||
inline Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
|
||||
u16_le pixel;
|
||||
std::memcpy(&pixel, bytes, sizeof(pixel));
|
||||
return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
|
||||
Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
|
||||
}
|
||||
@@ -104,8 +108,9 @@ inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
|
||||
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
|
||||
inline Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
|
||||
u16_le pixel;
|
||||
std::memcpy(&pixel, bytes, sizeof(pixel));
|
||||
return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
|
||||
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
|
||||
}
|
||||
@@ -116,7 +121,9 @@ inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
|
||||
* @return Depth value as an u32
|
||||
*/
|
||||
inline u32 DecodeD16(const u8* bytes) {
|
||||
return *reinterpret_cast<const u16_le*>(bytes);
|
||||
u16_le data;
|
||||
std::memcpy(&data, bytes, sizeof(data));
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +140,7 @@ inline u32 DecodeD24(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source values
|
||||
* @return Resulting values stored as a Math::Vec2
|
||||
*/
|
||||
inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
|
||||
inline Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
|
||||
return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
|
||||
}
|
||||
|
||||
@@ -175,8 +182,10 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
*reinterpret_cast<u16_le*>(bytes) =
|
||||
const u16_le data =
|
||||
(Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
|
||||
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,9 +194,10 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
*reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
|
||||
(Convert8To5(color.g()) << 6) |
|
||||
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
|
||||
const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) |
|
||||
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
|
||||
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,9 +206,10 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
*reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) |
|
||||
(Convert8To4(color.g()) << 8) |
|
||||
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
|
||||
const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) |
|
||||
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
|
||||
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,7 +218,8 @@ inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
* @param bytes Pointer where to store the encoded value
|
||||
*/
|
||||
inline void EncodeD16(u32 value, u8* bytes) {
|
||||
*reinterpret_cast<u16_le*>(bytes) = value & 0xFFFF;
|
||||
const u16_le data = static_cast<u16>(value);
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -884,11 +884,21 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string SanitizePath(std::string_view path_) {
|
||||
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
|
||||
std::string path(path_);
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
|
||||
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
|
||||
|
||||
if (directory_separator == DirectorySeparator::PlatformDefault) {
|
||||
#ifdef _WIN32
|
||||
type1 = '/';
|
||||
type2 = '\\';
|
||||
#endif
|
||||
}
|
||||
|
||||
std::replace(path.begin(), path.end(), type1, type2);
|
||||
path.erase(std::unique(path.begin(), path.end(),
|
||||
[](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
|
||||
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
||||
path.end());
|
||||
return std::string(RemoveTrailingSlash(path));
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
@@ -181,8 +182,12 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la
|
||||
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
|
||||
}
|
||||
|
||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
|
||||
std::string SanitizePath(std::string_view path);
|
||||
enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault };
|
||||
|
||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
|
||||
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
|
||||
std::string SanitizePath(std::string_view path,
|
||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||
|
||||
// simple wrapper for cstdlib file functions to
|
||||
// hopefully will make error checking easier
|
||||
@@ -207,39 +212,42 @@ public:
|
||||
|
||||
template <typename T>
|
||||
size_t ReadArray(T* data, size_t length) const {
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Given array does not consist of trivially copyable objects");
|
||||
|
||||
if (!IsOpen())
|
||||
return -1;
|
||||
if (!IsOpen()) {
|
||||
return std::numeric_limits<size_t>::max();
|
||||
}
|
||||
|
||||
return std::fread(data, sizeof(T), length, m_file);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t WriteArray(const T* data, size_t length) {
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Given array does not consist of trivially copyable objects");
|
||||
if (!IsOpen())
|
||||
return -1;
|
||||
if (!IsOpen()) {
|
||||
return std::numeric_limits<size_t>::max();
|
||||
}
|
||||
|
||||
return std::fwrite(data, sizeof(T), length, m_file);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t ReadBytes(T* data, size_t length) const {
|
||||
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
||||
return ReadArray(reinterpret_cast<char*>(data), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t WriteBytes(const T* data, size_t length) {
|
||||
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
||||
return WriteArray(reinterpret_cast<const char*>(data), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t WriteObject(const T& object) {
|
||||
static_assert(!std::is_pointer<T>::value, "Given object is a pointer");
|
||||
static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
|
||||
return WriteArray(&object, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ static inline u64 ComputeHash64(const void* data, size_t len) {
|
||||
*/
|
||||
template <typename T>
|
||||
static inline u64 ComputeStructHash64(const T& data) {
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Type passed to ComputeStructHash64 must be trivially copyable");
|
||||
return ComputeHash64(&data, sizeof(data));
|
||||
}
|
||||
@@ -38,7 +38,7 @@ template <typename T>
|
||||
struct HashableStruct {
|
||||
// In addition to being trivially copyable, T must also have a trivial default constructor,
|
||||
// because any member initialization would be overridden by memset
|
||||
static_assert(std::is_trivial<T>(), "Type passed to HashableStruct must be trivial");
|
||||
static_assert(std::is_trivial_v<T>, "Type passed to HashableStruct must be trivial");
|
||||
/*
|
||||
* We use a union because "implicitly-defined copy/move constructor for a union X copies the
|
||||
* object representation of X." and "implicitly-defined copy assignment operator for a union X
|
||||
|
||||
@@ -171,15 +171,21 @@ void FileBackend::Write(const Entry& entry) {
|
||||
SUB(Service, ARP) \
|
||||
SUB(Service, BCAT) \
|
||||
SUB(Service, BPC) \
|
||||
SUB(Service, BTDRV) \
|
||||
SUB(Service, BTM) \
|
||||
SUB(Service, Capture) \
|
||||
SUB(Service, ERPT) \
|
||||
SUB(Service, ETicket) \
|
||||
SUB(Service, EUPLD) \
|
||||
SUB(Service, Fatal) \
|
||||
SUB(Service, FGM) \
|
||||
SUB(Service, Friend) \
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, GRC) \
|
||||
SUB(Service, HID) \
|
||||
SUB(Service, LBL) \
|
||||
SUB(Service, LDN) \
|
||||
SUB(Service, LDR) \
|
||||
SUB(Service, LM) \
|
||||
SUB(Service, Migration) \
|
||||
SUB(Service, Mii) \
|
||||
@@ -188,11 +194,13 @@ void FileBackend::Write(const Entry& entry) {
|
||||
SUB(Service, NFC) \
|
||||
SUB(Service, NFP) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NIM) \
|
||||
SUB(Service, NS) \
|
||||
SUB(Service, NVDRV) \
|
||||
SUB(Service, PCIE) \
|
||||
SUB(Service, PCTL) \
|
||||
SUB(Service, PCV) \
|
||||
SUB(Service, PM) \
|
||||
SUB(Service, PREPO) \
|
||||
SUB(Service, PSC) \
|
||||
SUB(Service, SET) \
|
||||
@@ -200,6 +208,7 @@ void FileBackend::Write(const Entry& entry) {
|
||||
SUB(Service, SPL) \
|
||||
SUB(Service, SSL) \
|
||||
SUB(Service, Time) \
|
||||
SUB(Service, USB) \
|
||||
SUB(Service, VI) \
|
||||
SUB(Service, WLAN) \
|
||||
CLS(HW) \
|
||||
|
||||
@@ -58,15 +58,21 @@ enum class Class : ClassType {
|
||||
Service_Audio, ///< The Audio (Audio control) service
|
||||
Service_BCAT, ///< The BCAT service
|
||||
Service_BPC, ///< The BPC service
|
||||
Service_BTDRV, ///< The Bluetooth driver service
|
||||
Service_BTM, ///< The BTM service
|
||||
Service_Capture, ///< The capture service
|
||||
Service_ERPT, ///< The error reporting service
|
||||
Service_ETicket, ///< The ETicket service
|
||||
Service_EUPLD, ///< The error upload service
|
||||
Service_Fatal, ///< The Fatal service
|
||||
Service_FGM, ///< The FGM service
|
||||
Service_Friend, ///< The friend service
|
||||
Service_FS, ///< The FS (Filesystem) service
|
||||
Service_GRC, ///< The game recording service
|
||||
Service_HID, ///< The HID (Human interface device) service
|
||||
Service_LBL, ///< The LBL (LCD backlight) service
|
||||
Service_LDN, ///< The LDN (Local domain network) service
|
||||
Service_LDR, ///< The loader service
|
||||
Service_LM, ///< The LM (Logger) service
|
||||
Service_Migration, ///< The migration service
|
||||
Service_Mii, ///< The Mii service
|
||||
@@ -75,11 +81,13 @@ enum class Class : ClassType {
|
||||
Service_NFC, ///< The NFC (Near-field communication) service
|
||||
Service_NFP, ///< The NFP service
|
||||
Service_NIFM, ///< The NIFM (Network interface) service
|
||||
Service_NIM, ///< The NIM service
|
||||
Service_NS, ///< The NS services
|
||||
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
||||
Service_PCIE, ///< The PCIe service
|
||||
Service_PCTL, ///< The PCTL (Parental control) service
|
||||
Service_PCV, ///< The PCV service
|
||||
Service_PM, ///< The PM service
|
||||
Service_PREPO, ///< The PREPO (Play report) service
|
||||
Service_PSC, ///< The PSC service
|
||||
Service_SET, ///< The SET (Settings) service
|
||||
@@ -87,6 +95,7 @@ enum class Class : ClassType {
|
||||
Service_SPL, ///< The SPL service
|
||||
Service_SSL, ///< The SSL service
|
||||
Service_Time, ///< The time service
|
||||
Service_USB, ///< The USB (Universal Serial Bus) service
|
||||
Service_VI, ///< The VI (Video interface) service
|
||||
Service_WLAN, ///< The WLAN (Wireless local area network) service
|
||||
HW, ///< Low-level hardware emulation
|
||||
|
||||
@@ -42,140 +42,136 @@ class Vec3;
|
||||
template <typename T>
|
||||
class Vec4;
|
||||
|
||||
template <typename T>
|
||||
static inline Vec2<T> MakeVec(const T& x, const T& y);
|
||||
template <typename T>
|
||||
static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z);
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w);
|
||||
|
||||
template <typename T>
|
||||
class Vec2 {
|
||||
public:
|
||||
T x{};
|
||||
T y{};
|
||||
|
||||
Vec2() = default;
|
||||
Vec2(const T& _x, const T& _y) : x(_x), y(_y) {}
|
||||
constexpr Vec2() = default;
|
||||
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
|
||||
|
||||
template <typename T2>
|
||||
Vec2<T2> Cast() const {
|
||||
return Vec2<T2>((T2)x, (T2)y);
|
||||
constexpr Vec2<T2> Cast() const {
|
||||
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
|
||||
}
|
||||
|
||||
static Vec2 AssignToAll(const T& f) {
|
||||
return Vec2<T>(f, f);
|
||||
static constexpr Vec2 AssignToAll(const T& f) {
|
||||
return Vec2{f, f};
|
||||
}
|
||||
|
||||
Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
|
||||
return MakeVec(x + other.x, y + other.y);
|
||||
constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
|
||||
return {x + other.x, y + other.y};
|
||||
}
|
||||
void operator+=(const Vec2& other) {
|
||||
constexpr Vec2& operator+=(const Vec2& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
|
||||
return MakeVec(x - other.x, y - other.y);
|
||||
constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
|
||||
return {x - other.x, y - other.y};
|
||||
}
|
||||
void operator-=(const Vec2& other) {
|
||||
constexpr Vec2& operator-=(const Vec2& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
Vec2<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
|
||||
return MakeVec(-x, -y);
|
||||
constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y};
|
||||
}
|
||||
Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
|
||||
return MakeVec(x * other.x, y * other.y);
|
||||
}
|
||||
template <typename V>
|
||||
Vec2<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return MakeVec(x * f, y * f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
}
|
||||
template <typename V>
|
||||
Vec2<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return MakeVec(x / f, y / f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
|
||||
return {x * other.x, y * other.y};
|
||||
}
|
||||
|
||||
T Length2() const {
|
||||
template <typename V>
|
||||
constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return {x * f, y * f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec2& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return {x / f, y / f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec2& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr T Length2() const {
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
// Only implemented for T=float
|
||||
float Length() const;
|
||||
void SetLength(const float l);
|
||||
Vec2 WithLength(const float l) const;
|
||||
float Distance2To(Vec2& other);
|
||||
Vec2 Normalized() const;
|
||||
float Normalize(); // returns the previous length, which is often useful
|
||||
|
||||
T& operator[](int i) // allow vector[1] = 3 (vector.y=3)
|
||||
{
|
||||
constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
T operator[](const int i) const {
|
||||
constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
void SetZero() {
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
// Common aliases: UV (texel coordinates), ST (texture coordinates)
|
||||
T& u() {
|
||||
constexpr T& u() {
|
||||
return x;
|
||||
}
|
||||
T& v() {
|
||||
constexpr T& v() {
|
||||
return y;
|
||||
}
|
||||
T& s() {
|
||||
constexpr T& s() {
|
||||
return x;
|
||||
}
|
||||
T& t() {
|
||||
constexpr T& t() {
|
||||
return y;
|
||||
}
|
||||
|
||||
const T& u() const {
|
||||
constexpr const T& u() const {
|
||||
return x;
|
||||
}
|
||||
const T& v() const {
|
||||
constexpr const T& v() const {
|
||||
return y;
|
||||
}
|
||||
const T& s() const {
|
||||
constexpr const T& s() const {
|
||||
return x;
|
||||
}
|
||||
const T& t() const {
|
||||
constexpr const T& t() const {
|
||||
return y;
|
||||
}
|
||||
|
||||
// swizzlers - create a subvector of specific components
|
||||
const Vec2 yx() const {
|
||||
constexpr Vec2 yx() const {
|
||||
return Vec2(y, x);
|
||||
}
|
||||
const Vec2 vu() const {
|
||||
constexpr Vec2 vu() const {
|
||||
return Vec2(y, x);
|
||||
}
|
||||
const Vec2 ts() const {
|
||||
constexpr Vec2 ts() const {
|
||||
return Vec2(y, x);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
|
||||
constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
|
||||
return Vec2<T>(f * vec.x, f * vec.y);
|
||||
}
|
||||
|
||||
typedef Vec2<float> Vec2f;
|
||||
using Vec2f = Vec2<float>;
|
||||
|
||||
template <>
|
||||
inline float Vec2<float>::Length() const {
|
||||
@@ -196,147 +192,151 @@ public:
|
||||
T y{};
|
||||
T z{};
|
||||
|
||||
Vec3() = default;
|
||||
Vec3(const T& _x, const T& _y, const T& _z) : x(_x), y(_y), z(_z) {}
|
||||
constexpr Vec3() = default;
|
||||
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
|
||||
|
||||
template <typename T2>
|
||||
Vec3<T2> Cast() const {
|
||||
return MakeVec<T2>((T2)x, (T2)y, (T2)z);
|
||||
constexpr Vec3<T2> Cast() const {
|
||||
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
|
||||
}
|
||||
|
||||
// Only implemented for T=int and T=float
|
||||
static Vec3 FromRGB(unsigned int rgb);
|
||||
unsigned int ToRGB() const; // alpha bits set to zero
|
||||
|
||||
static Vec3 AssignToAll(const T& f) {
|
||||
return MakeVec(f, f, f);
|
||||
static constexpr Vec3 AssignToAll(const T& f) {
|
||||
return Vec3(f, f, f);
|
||||
}
|
||||
|
||||
Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
|
||||
return MakeVec(x + other.x, y + other.y, z + other.z);
|
||||
constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
|
||||
return {x + other.x, y + other.y, z + other.z};
|
||||
}
|
||||
void operator+=(const Vec3& other) {
|
||||
|
||||
constexpr Vec3& operator+=(const Vec3& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
return *this;
|
||||
}
|
||||
Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
|
||||
return MakeVec(x - other.x, y - other.y, z - other.z);
|
||||
|
||||
constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
|
||||
return {x - other.x, y - other.y, z - other.z};
|
||||
}
|
||||
void operator-=(const Vec3& other) {
|
||||
|
||||
constexpr Vec3& operator-=(const Vec3& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
Vec3<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
|
||||
return MakeVec(-x, -y, -z);
|
||||
}
|
||||
Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
|
||||
return MakeVec(x * other.x, y * other.y, z * other.z);
|
||||
}
|
||||
template <typename V>
|
||||
Vec3<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return MakeVec(x * f, y * f, z * f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
}
|
||||
template <typename V>
|
||||
Vec3<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return MakeVec(x / f, y / f, z / f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y, -z};
|
||||
}
|
||||
|
||||
T Length2() const {
|
||||
constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
|
||||
return {x * other.x, y * other.y, z * other.z};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return {x * f, y * f, z * f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec3& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
template <typename V>
|
||||
constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return {x / f, y / f, z / f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec3& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr T Length2() const {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
// Only implemented for T=float
|
||||
float Length() const;
|
||||
void SetLength(const float l);
|
||||
Vec3 WithLength(const float l) const;
|
||||
float Distance2To(Vec3& other);
|
||||
Vec3 Normalized() const;
|
||||
float Normalize(); // returns the previous length, which is often useful
|
||||
|
||||
T& operator[](int i) // allow vector[2] = 3 (vector.z=3)
|
||||
{
|
||||
return *((&x) + i);
|
||||
}
|
||||
T operator[](const int i) const {
|
||||
constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
void SetZero() {
|
||||
constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
|
||||
T& u() {
|
||||
constexpr T& u() {
|
||||
return x;
|
||||
}
|
||||
T& v() {
|
||||
constexpr T& v() {
|
||||
return y;
|
||||
}
|
||||
T& w() {
|
||||
constexpr T& w() {
|
||||
return z;
|
||||
}
|
||||
|
||||
T& r() {
|
||||
constexpr T& r() {
|
||||
return x;
|
||||
}
|
||||
T& g() {
|
||||
constexpr T& g() {
|
||||
return y;
|
||||
}
|
||||
T& b() {
|
||||
constexpr T& b() {
|
||||
return z;
|
||||
}
|
||||
|
||||
T& s() {
|
||||
constexpr T& s() {
|
||||
return x;
|
||||
}
|
||||
T& t() {
|
||||
constexpr T& t() {
|
||||
return y;
|
||||
}
|
||||
T& q() {
|
||||
constexpr T& q() {
|
||||
return z;
|
||||
}
|
||||
|
||||
const T& u() const {
|
||||
constexpr const T& u() const {
|
||||
return x;
|
||||
}
|
||||
const T& v() const {
|
||||
constexpr const T& v() const {
|
||||
return y;
|
||||
}
|
||||
const T& w() const {
|
||||
constexpr const T& w() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
const T& r() const {
|
||||
constexpr const T& r() const {
|
||||
return x;
|
||||
}
|
||||
const T& g() const {
|
||||
constexpr const T& g() const {
|
||||
return y;
|
||||
}
|
||||
const T& b() const {
|
||||
constexpr const T& b() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
const T& s() const {
|
||||
constexpr const T& s() const {
|
||||
return x;
|
||||
}
|
||||
const T& t() const {
|
||||
constexpr const T& t() const {
|
||||
return y;
|
||||
}
|
||||
const T& q() const {
|
||||
constexpr const T& q() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ public:
|
||||
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
|
||||
// component names (x<->r) and permutations (xy<->yx)
|
||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||
const Vec2<T> name() const { \
|
||||
constexpr Vec2<T> name() const { \
|
||||
return Vec2<T>(a, b); \
|
||||
}
|
||||
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
|
||||
@@ -366,7 +366,7 @@ public:
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
|
||||
constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
|
||||
return Vec3<T>(f * vec.x, f * vec.y, f * vec.z);
|
||||
}
|
||||
|
||||
@@ -387,7 +387,7 @@ inline float Vec3<float>::Normalize() {
|
||||
return length;
|
||||
}
|
||||
|
||||
typedef Vec3<float> Vec3f;
|
||||
using Vec3f = Vec3<float>;
|
||||
|
||||
template <typename T>
|
||||
class Vec4 {
|
||||
@@ -397,86 +397,88 @@ public:
|
||||
T z{};
|
||||
T w{};
|
||||
|
||||
Vec4() = default;
|
||||
Vec4(const T& _x, const T& _y, const T& _z, const T& _w) : x(_x), y(_y), z(_z), w(_w) {}
|
||||
constexpr Vec4() = default;
|
||||
constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_)
|
||||
: x(x_), y(y_), z(z_), w(w_) {}
|
||||
|
||||
template <typename T2>
|
||||
Vec4<T2> Cast() const {
|
||||
return Vec4<T2>((T2)x, (T2)y, (T2)z, (T2)w);
|
||||
constexpr Vec4<T2> Cast() const {
|
||||
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
|
||||
static_cast<T2>(w));
|
||||
}
|
||||
|
||||
// Only implemented for T=int and T=float
|
||||
static Vec4 FromRGBA(unsigned int rgba);
|
||||
unsigned int ToRGBA() const;
|
||||
|
||||
static Vec4 AssignToAll(const T& f) {
|
||||
return Vec4<T>(f, f, f, f);
|
||||
static constexpr Vec4 AssignToAll(const T& f) {
|
||||
return Vec4(f, f, f, f);
|
||||
}
|
||||
|
||||
Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
|
||||
return MakeVec(x + other.x, y + other.y, z + other.z, w + other.w);
|
||||
constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
|
||||
return {x + other.x, y + other.y, z + other.z, w + other.w};
|
||||
}
|
||||
void operator+=(const Vec4& other) {
|
||||
|
||||
constexpr Vec4& operator+=(const Vec4& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
w += other.w;
|
||||
return *this;
|
||||
}
|
||||
Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
|
||||
return MakeVec(x - other.x, y - other.y, z - other.z, w - other.w);
|
||||
|
||||
constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
|
||||
return {x - other.x, y - other.y, z - other.z, w - other.w};
|
||||
}
|
||||
void operator-=(const Vec4& other) {
|
||||
|
||||
constexpr Vec4& operator-=(const Vec4& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
w -= other.w;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
Vec4<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
|
||||
return MakeVec(-x, -y, -z, -w);
|
||||
}
|
||||
Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
|
||||
return MakeVec(x * other.x, y * other.y, z * other.z, w * other.w);
|
||||
}
|
||||
template <typename V>
|
||||
Vec4<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return MakeVec(x * f, y * f, z * f, w * f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
}
|
||||
template <typename V>
|
||||
Vec4<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return MakeVec(x / f, y / f, z / f, w / f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y, -z, -w};
|
||||
}
|
||||
|
||||
T Length2() const {
|
||||
constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
|
||||
return {x * other.x, y * other.y, z * other.z, w * other.w};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return {x * f, y * f, z * f, w * f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return {x / f, y / f, z / f, w / f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr T Length2() const {
|
||||
return x * x + y * y + z * z + w * w;
|
||||
}
|
||||
|
||||
// Only implemented for T=float
|
||||
float Length() const;
|
||||
void SetLength(const float l);
|
||||
Vec4 WithLength(const float l) const;
|
||||
float Distance2To(Vec4& other);
|
||||
Vec4 Normalized() const;
|
||||
float Normalize(); // returns the previous length, which is often useful
|
||||
|
||||
T& operator[](int i) // allow vector[2] = 3 (vector.z=3)
|
||||
{
|
||||
return *((&x) + i);
|
||||
}
|
||||
T operator[](const int i) const {
|
||||
constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
void SetZero() {
|
||||
constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
@@ -484,29 +486,29 @@ public:
|
||||
}
|
||||
|
||||
// Common alias: RGBA (colors)
|
||||
T& r() {
|
||||
constexpr T& r() {
|
||||
return x;
|
||||
}
|
||||
T& g() {
|
||||
constexpr T& g() {
|
||||
return y;
|
||||
}
|
||||
T& b() {
|
||||
constexpr T& b() {
|
||||
return z;
|
||||
}
|
||||
T& a() {
|
||||
constexpr T& a() {
|
||||
return w;
|
||||
}
|
||||
|
||||
const T& r() const {
|
||||
constexpr const T& r() const {
|
||||
return x;
|
||||
}
|
||||
const T& g() const {
|
||||
constexpr const T& g() const {
|
||||
return y;
|
||||
}
|
||||
const T& b() const {
|
||||
constexpr const T& b() const {
|
||||
return z;
|
||||
}
|
||||
const T& a() const {
|
||||
constexpr const T& a() const {
|
||||
return w;
|
||||
}
|
||||
|
||||
@@ -518,7 +520,7 @@ public:
|
||||
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
|
||||
// permutations (xy<->yx)
|
||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||
const Vec2<T> name() const { \
|
||||
constexpr Vec2<T> name() const { \
|
||||
return Vec2<T>(a, b); \
|
||||
}
|
||||
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
|
||||
@@ -545,7 +547,7 @@ public:
|
||||
#undef _DEFINE_SWIZZLER2
|
||||
|
||||
#define _DEFINE_SWIZZLER3(a, b, c, name) \
|
||||
const Vec3<T> name() const { \
|
||||
constexpr Vec3<T> name() const { \
|
||||
return Vec3<T>(a, b, c); \
|
||||
}
|
||||
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
|
||||
@@ -579,51 +581,51 @@ public:
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
|
||||
return MakeVec(f * vec.x, f * vec.y, f * vec.z, f * vec.w);
|
||||
constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
|
||||
return {f * vec.x, f * vec.y, f * vec.z, f * vec.w};
|
||||
}
|
||||
|
||||
typedef Vec4<float> Vec4f;
|
||||
using Vec4f = Vec4<float>;
|
||||
|
||||
template <typename T>
|
||||
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
|
||||
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
|
||||
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
|
||||
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) {
|
||||
return MakeVec(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
|
||||
constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) {
|
||||
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
|
||||
}
|
||||
|
||||
// linear interpolation via float: 0.0=begin, 1.0=end
|
||||
template <typename X>
|
||||
static inline decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
|
||||
const float t) {
|
||||
constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
|
||||
const float t) {
|
||||
return begin * (1.f - t) + end * t;
|
||||
}
|
||||
|
||||
// linear interpolation via int: 0=begin, base=end
|
||||
template <typename X, int base>
|
||||
static inline decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end,
|
||||
const int t) {
|
||||
constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end,
|
||||
const int t) {
|
||||
return (begin * (base - t) + end * t) / base;
|
||||
}
|
||||
|
||||
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
|
||||
// interpolation.
|
||||
template <typename X>
|
||||
inline auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s,
|
||||
const float t) {
|
||||
constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s,
|
||||
const float t) {
|
||||
auto y0 = Lerp(x00, x01, s);
|
||||
auto y1 = Lerp(x10, x11, s);
|
||||
return Lerp(y0, y1, t);
|
||||
@@ -631,42 +633,42 @@ inline auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x1
|
||||
|
||||
// Utility vector factories
|
||||
template <typename T>
|
||||
static inline Vec2<T> MakeVec(const T& x, const T& y) {
|
||||
constexpr Vec2<T> MakeVec(const T& x, const T& y) {
|
||||
return Vec2<T>{x, y};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
|
||||
constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
|
||||
return Vec3<T>{x, y, z};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
|
||||
constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
|
||||
return MakeVec(x, y, zw[0], zw[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
|
||||
constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
|
||||
return MakeVec(xy[0], xy[1], z);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
|
||||
constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
|
||||
return MakeVec(x, yz[0], yz[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
|
||||
constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
|
||||
return Vec4<T>{x, y, z, w};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
|
||||
constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
|
||||
return MakeVec(xy[0], xy[1], z, w);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
|
||||
constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
|
||||
return MakeVec(x, yz[0], yz[1], w);
|
||||
}
|
||||
|
||||
@@ -674,17 +676,17 @@ static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
|
||||
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
|
||||
// out soon enough due to misuse of the returned structure.
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
|
||||
constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
|
||||
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
|
||||
constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
|
||||
return MakeVec(xyz[0], xyz[1], xyz[2], w);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
|
||||
constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
|
||||
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
|
||||
|
||||
template <typename T>
|
||||
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
|
||||
static_assert(std::is_pointer<T>(), "Argument must be a (function) pointer.");
|
||||
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
||||
size_t addr = reinterpret_cast<size_t>(f);
|
||||
if (IsWithin2G(code, addr)) {
|
||||
code.call(f);
|
||||
|
||||
@@ -104,8 +104,6 @@ add_library(core STATIC
|
||||
hle/lock.cpp
|
||||
hle/lock.h
|
||||
hle/result.h
|
||||
hle/romfs.cpp
|
||||
hle/romfs.h
|
||||
hle/service/acc/acc.cpp
|
||||
hle/service/acc/acc.h
|
||||
hle/service/acc/acc_aa.cpp
|
||||
@@ -315,6 +313,8 @@ add_library(core STATIC
|
||||
hle/service/time/interface.h
|
||||
hle/service/time/time.cpp
|
||||
hle/service/time/time.h
|
||||
hle/service/usb/usb.cpp
|
||||
hle/service/usb/usb.h
|
||||
hle/service/vi/vi.cpp
|
||||
hle/service/vi/vi.h
|
||||
hle/service/vi/vi_m.cpp
|
||||
|
||||
@@ -88,8 +88,8 @@ System::ResultStatus System::SingleStep() {
|
||||
return RunLoop(false);
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) {
|
||||
app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath));
|
||||
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
|
||||
app_loader = Loader::GetLoader(virtual_filesystem->OpenFile(filepath, FileSys::Mode::Read));
|
||||
|
||||
if (!app_loader) {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
@@ -102,18 +102,8 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
|
||||
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
|
||||
static_cast<int>(system_mode.second));
|
||||
|
||||
switch (system_mode.second) {
|
||||
case Loader::ResultStatus::ErrorMissingKeys:
|
||||
return ResultStatus::ErrorLoader_ErrorMissingKeys;
|
||||
case Loader::ResultStatus::ErrorDecrypting:
|
||||
return ResultStatus::ErrorLoader_ErrorDecrypting;
|
||||
case Loader::ResultStatus::ErrorInvalidFormat:
|
||||
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
|
||||
case Loader::ResultStatus::ErrorUnsupportedArch:
|
||||
return ResultStatus::ErrorUnsupportedArch;
|
||||
default:
|
||||
if (system_mode.second != Loader::ResultStatus::Success)
|
||||
return ResultStatus::ErrorSystemMode;
|
||||
}
|
||||
}
|
||||
|
||||
ResultStatus init_result{Init(emu_window)};
|
||||
@@ -129,17 +119,9 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
|
||||
System::Shutdown();
|
||||
|
||||
switch (load_result) {
|
||||
case Loader::ResultStatus::ErrorMissingKeys:
|
||||
return ResultStatus::ErrorLoader_ErrorMissingKeys;
|
||||
case Loader::ResultStatus::ErrorDecrypting:
|
||||
return ResultStatus::ErrorLoader_ErrorDecrypting;
|
||||
case Loader::ResultStatus::ErrorInvalidFormat:
|
||||
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
|
||||
case Loader::ResultStatus::ErrorUnsupportedArch:
|
||||
return ResultStatus::ErrorUnsupportedArch;
|
||||
default:
|
||||
return ResultStatus::ErrorLoader;
|
||||
if (load_result != Loader::ResultStatus::Success) {
|
||||
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
|
||||
static_cast<u32>(load_result));
|
||||
}
|
||||
}
|
||||
status = ResultStatus::Success;
|
||||
@@ -169,11 +151,15 @@ Cpu& System::CpuCore(size_t core_index) {
|
||||
return *cpu_cores[core_index];
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(EmuWindow& emu_window) {
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
|
||||
CoreTiming::Init();
|
||||
|
||||
// Create a default fs if one doesn't already exist.
|
||||
if (virtual_filesystem == nullptr)
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
|
||||
current_process = Kernel::Process::Create("main");
|
||||
|
||||
cpu_barrier = std::make_shared<CpuBarrier>();
|
||||
@@ -186,7 +172,7 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
||||
|
||||
Kernel::Init();
|
||||
Service::Init(service_manager);
|
||||
Service::Init(service_manager, virtual_filesystem);
|
||||
GDBStub::Init();
|
||||
|
||||
renderer = VideoCore::CreateRenderer(emu_window);
|
||||
|
||||
@@ -17,12 +17,17 @@
|
||||
#include "core/memory.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "file_sys/vfs_real.h"
|
||||
#include "hle/service/filesystem/filesystem.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
class EmuWindow;
|
||||
class ARM_Interface;
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
@@ -47,21 +52,15 @@ public:
|
||||
|
||||
/// Enumeration representing the return values of the System Initialize and Load process.
|
||||
enum class ResultStatus : u32 {
|
||||
Success, ///< Succeeded
|
||||
ErrorNotInitialized, ///< Error trying to use core prior to initialization
|
||||
ErrorGetLoader, ///< Error finding the correct application loader
|
||||
ErrorSystemMode, ///< Error determining the system mode
|
||||
ErrorLoader, ///< Error loading the specified application
|
||||
ErrorLoader_ErrorMissingKeys, ///< Error because the key/keys needed to run could not be
|
||||
///< found.
|
||||
ErrorLoader_ErrorDecrypting, ///< Error loading the specified application due to encryption
|
||||
ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
|
||||
/// invalid format
|
||||
ErrorSystemFiles, ///< Error in finding system files
|
||||
ErrorSharedFont, ///< Error in finding shared font
|
||||
ErrorVideoCore, ///< Error in the video core
|
||||
ErrorUnsupportedArch, ///< Unsupported Architecture (32-Bit ROMs)
|
||||
ErrorUnknown ///< Any other error
|
||||
Success, ///< Succeeded
|
||||
ErrorNotInitialized, ///< Error trying to use core prior to initialization
|
||||
ErrorGetLoader, ///< Error finding the correct application loader
|
||||
ErrorSystemMode, ///< Error determining the system mode
|
||||
ErrorSystemFiles, ///< Error in finding system files
|
||||
ErrorSharedFont, ///< Error in finding shared font
|
||||
ErrorVideoCore, ///< Error in the video core
|
||||
ErrorUnknown, ///< Any other error
|
||||
ErrorLoader, ///< The base for loader errors (too many to repeat)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -103,7 +102,7 @@ public:
|
||||
* @param filepath String path to the executable application to load on the host file system.
|
||||
* @returns ResultStatus code, indicating if the operation succeeded.
|
||||
*/
|
||||
ResultStatus Load(EmuWindow& emu_window, const std::string& filepath);
|
||||
ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath);
|
||||
|
||||
/**
|
||||
* Indicates if the emulated system is powered on (all subsystems initialized and able to run an
|
||||
@@ -211,6 +210,14 @@ public:
|
||||
return debug_context;
|
||||
}
|
||||
|
||||
void SetFilesystem(FileSys::VirtualFilesystem vfs) {
|
||||
virtual_filesystem = std::move(vfs);
|
||||
}
|
||||
|
||||
FileSys::VirtualFilesystem GetFilesystem() const {
|
||||
return virtual_filesystem;
|
||||
}
|
||||
|
||||
private:
|
||||
System();
|
||||
|
||||
@@ -223,8 +230,10 @@ private:
|
||||
* input.
|
||||
* @return ResultStatus code, indicating if the operation succeeded.
|
||||
*/
|
||||
ResultStatus Init(EmuWindow& emu_window);
|
||||
ResultStatus Init(Frontend::EmuWindow& emu_window);
|
||||
|
||||
/// RealVfsFilesystem instance
|
||||
FileSys::VirtualFilesystem virtual_filesystem;
|
||||
/// AppLoader used to load the current executing application
|
||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
|
||||
@@ -5,20 +5,23 @@
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <core/loader/loader.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"};
|
||||
|
||||
XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
|
||||
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
|
||||
status = Loader::ResultStatus::ErrorInvalidFormat;
|
||||
status = Loader::ResultStatus::ErrorBadXCIHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) {
|
||||
status = Loader::ResultStatus::ErrorInvalidFormat;
|
||||
status = Loader::ResultStatus::ErrorBadXCIHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -30,9 +33,6 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure",
|
||||
"logo"};
|
||||
|
||||
for (XCIPartition partition :
|
||||
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
|
||||
auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]);
|
||||
@@ -129,15 +129,21 @@ bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
|
||||
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
if (partitions[static_cast<size_t>(part)] == nullptr) {
|
||||
return Loader::ResultStatus::ErrorInvalidFormat;
|
||||
return Loader::ResultStatus::ErrorXCIMissingPartition;
|
||||
}
|
||||
|
||||
for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) {
|
||||
if (file->GetExtension() != "nca")
|
||||
continue;
|
||||
auto nca = std::make_shared<NCA>(file);
|
||||
if (nca->GetStatus() == Loader::ResultStatus::Success)
|
||||
if (nca->GetStatus() == Loader::ResultStatus::Success) {
|
||||
ncas.push_back(std::move(nca));
|
||||
} else {
|
||||
const u16 error_id = static_cast<u16>(nca->GetStatus());
|
||||
LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})",
|
||||
partition_names[static_cast<size_t>(part)], nca->GetName(), error_id,
|
||||
Loader::GetMessageForResultStatus(nca->GetStatus()));
|
||||
}
|
||||
}
|
||||
|
||||
return Loader::ResultStatus::Success;
|
||||
|
||||
@@ -76,12 +76,17 @@ bool IsValidNCA(const NCAHeader& header) {
|
||||
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
|
||||
}
|
||||
|
||||
boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
|
||||
u8 NCA::GetCryptoRevision() const {
|
||||
u8 master_key_id = header.crypto_type;
|
||||
if (header.crypto_type_2 > master_key_id)
|
||||
master_key_id = header.crypto_type_2;
|
||||
if (master_key_id > 0)
|
||||
--master_key_id;
|
||||
return master_key_id;
|
||||
}
|
||||
|
||||
boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
|
||||
const auto master_key_id = GetCryptoRevision();
|
||||
|
||||
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
|
||||
return boost::none;
|
||||
@@ -108,44 +113,105 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
|
||||
return out;
|
||||
}
|
||||
|
||||
VirtualFile NCA::Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const {
|
||||
boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
|
||||
const auto master_key_id = GetCryptoRevision();
|
||||
|
||||
u128 rights_id{};
|
||||
memcpy(rights_id.data(), header.rights_id.data(), 16);
|
||||
if (rights_id == u128{}) {
|
||||
status = Loader::ResultStatus::ErrorInvalidRightsID;
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
|
||||
if (titlekey == Core::Crypto::Key128{}) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekek;
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
|
||||
keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB);
|
||||
cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt);
|
||||
|
||||
return titlekey;
|
||||
}
|
||||
|
||||
VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) {
|
||||
if (!encrypted)
|
||||
return in;
|
||||
|
||||
switch (header.raw.header.crypto_type) {
|
||||
switch (s_header.raw.header.crypto_type) {
|
||||
case NCASectionCryptoType::NONE:
|
||||
LOG_DEBUG(Crypto, "called with mode=NONE");
|
||||
return in;
|
||||
case NCASectionCryptoType::CTR:
|
||||
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
|
||||
{
|
||||
const auto key = GetKeyAreaKey(NCASectionCryptoType::CTR);
|
||||
if (key == boost::none)
|
||||
return nullptr;
|
||||
boost::optional<Core::Crypto::Key128> key = boost::none;
|
||||
if (has_rights_id) {
|
||||
status = Loader::ResultStatus::Success;
|
||||
key = GetTitlekey();
|
||||
if (key == boost::none) {
|
||||
if (status == Loader::ResultStatus::Success)
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
key = GetKeyAreaKey(NCASectionCryptoType::CTR);
|
||||
if (key == boost::none) {
|
||||
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(
|
||||
std::move(in), key.value(), starting_offset);
|
||||
std::vector<u8> iv(16);
|
||||
for (u8 i = 0; i < 8; ++i)
|
||||
iv[i] = header.raw.section_ctr[0x8 - i - 1];
|
||||
iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
|
||||
out->SetIV(iv);
|
||||
return std::static_pointer_cast<VfsFile>(out);
|
||||
}
|
||||
case NCASectionCryptoType::XTS:
|
||||
// TODO(DarkLordZach): Implement XTSEncryptionLayer and title key encryption.
|
||||
// TODO(DarkLordZach): Implement XTSEncryptionLayer.
|
||||
default:
|
||||
LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
|
||||
static_cast<u8>(header.raw.header.crypto_type));
|
||||
static_cast<u8>(s_header.raw.header.crypto_type));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
|
||||
if (sizeof(NCAHeader) != file->ReadObject(&header))
|
||||
status = Loader::ResultStatus::Success;
|
||||
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sizeof(NCAHeader) != file->ReadObject(&header)) {
|
||||
LOG_ERROR(Loader, "File reader errored out during header read.");
|
||||
status = Loader::ResultStatus::ErrorBadNCAHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
encrypted = false;
|
||||
|
||||
if (!IsValidNCA(header)) {
|
||||
if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
|
||||
status = Loader::ResultStatus::ErrorNCA2;
|
||||
return;
|
||||
}
|
||||
if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
|
||||
status = Loader::ResultStatus::ErrorNCA0;
|
||||
return;
|
||||
}
|
||||
|
||||
NCAHeader dec_header{};
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
|
||||
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
|
||||
@@ -155,14 +221,26 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
|
||||
header = dec_header;
|
||||
encrypted = true;
|
||||
} else {
|
||||
if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
|
||||
status = Loader::ResultStatus::ErrorNCA2;
|
||||
return;
|
||||
}
|
||||
if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
|
||||
status = Loader::ResultStatus::ErrorNCA0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
|
||||
status = Loader::ResultStatus::ErrorMissingKeys;
|
||||
status = Loader::ResultStatus::ErrorMissingHeaderKey;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorDecrypting;
|
||||
status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
|
||||
[](char c) { return c == '\0'; }) != header.rights_id.end();
|
||||
|
||||
const std::ptrdiff_t number_sections =
|
||||
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
|
||||
[](NCASectionTableEntry entry) { return entry.media_offset > 0; });
|
||||
@@ -195,7 +273,12 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
|
||||
files.push_back(std::move(dec));
|
||||
romfs = files.back();
|
||||
} else {
|
||||
status = Loader::ResultStatus::ErrorMissingKeys;
|
||||
if (status != Loader::ResultStatus::Success)
|
||||
return;
|
||||
if (has_rights_id)
|
||||
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
|
||||
return;
|
||||
}
|
||||
} else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
|
||||
@@ -215,7 +298,12 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
|
||||
exefs = dirs.back();
|
||||
}
|
||||
} else {
|
||||
status = Loader::ResultStatus::ErrorMissingKeys;
|
||||
if (status != Loader::ResultStatus::Success)
|
||||
return;
|
||||
if (has_rights_id)
|
||||
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "control_metadata.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
@@ -95,8 +96,10 @@ protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
u8 GetCryptoRevision() const;
|
||||
boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
|
||||
VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const;
|
||||
boost::optional<Core::Crypto::Key128> GetTitlekey();
|
||||
VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset);
|
||||
|
||||
std::vector<VirtualDir> dirs;
|
||||
std::vector<VirtualFile> files;
|
||||
@@ -106,6 +109,7 @@ private:
|
||||
VirtualFile file;
|
||||
|
||||
NCAHeader header{};
|
||||
bool has_rights_id{};
|
||||
|
||||
Loader::ResultStatus status{};
|
||||
|
||||
|
||||
@@ -62,6 +62,13 @@ enum class Language : u8 {
|
||||
Chinese = 14,
|
||||
};
|
||||
|
||||
static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
|
||||
"AmericanEnglish", "BritishEnglish", "Japanese",
|
||||
"French", "German", "LatinAmericanSpanish",
|
||||
"Spanish", "Italian", "Dutch",
|
||||
"CanadianFrench", "Portugese", "Russian",
|
||||
"Korean", "Taiwanese", "Chinese"};
|
||||
|
||||
// A class representing the format used by NX metadata files, typically named Control.nacp.
|
||||
// These store application name, dev name, title id, and other miscellaneous data.
|
||||
class NACP {
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <string_view>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -21,9 +22,14 @@ enum EntryType : u8 {
|
||||
|
||||
// Structure of a directory entry, from
|
||||
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
|
||||
const size_t FILENAME_LENGTH = 0x300;
|
||||
struct Entry {
|
||||
char filename[FILENAME_LENGTH];
|
||||
Entry(std::string_view view, EntryType entry_type, u64 entry_size)
|
||||
: type{entry_type}, file_size{entry_size} {
|
||||
const size_t copy_size = view.copy(filename, std::size(filename) - 1);
|
||||
filename[copy_size] = '\0';
|
||||
}
|
||||
|
||||
char filename[0x300];
|
||||
INSERT_PADDING_BYTES(4);
|
||||
EntryType type;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
|
||||
@@ -24,19 +24,19 @@ bool PartitionFilesystem::Header::HasValidMagicValue() const {
|
||||
PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
|
||||
// At least be as large as the header
|
||||
if (file->GetSize() < sizeof(Header)) {
|
||||
status = Loader::ResultStatus::Error;
|
||||
status = Loader::ResultStatus::ErrorBadPFSHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
// For cartridges, HFSs can get very large, so we need to calculate the size up to
|
||||
// the actual content itself instead of just blindly reading in the entire file.
|
||||
if (sizeof(Header) != file->ReadObject(&pfs_header)) {
|
||||
status = Loader::ResultStatus::Error;
|
||||
status = Loader::ResultStatus::ErrorBadPFSHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pfs_header.HasValidMagicValue()) {
|
||||
status = Loader::ResultStatus::ErrorInvalidFormat;
|
||||
status = Loader::ResultStatus::ErrorBadPFSHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
|
||||
const size_t total_size = file_data.size();
|
||||
|
||||
if (total_size != metadata_size) {
|
||||
status = Loader::ResultStatus::Error;
|
||||
status = Loader::ResultStatus::ErrorIncorrectPFSFileSize;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,26 +12,26 @@ namespace FileSys {
|
||||
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
size_t total_size = static_cast<size_t>(file->GetSize());
|
||||
if (total_size < sizeof(Header))
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadNPDMHeader;
|
||||
|
||||
// TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable.
|
||||
std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header));
|
||||
if (sizeof(Header) != npdm_header_data.size())
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadNPDMHeader;
|
||||
std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header));
|
||||
|
||||
std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset);
|
||||
if (sizeof(AcidHeader) != acid_header_data.size())
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadACIDHeader;
|
||||
std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader));
|
||||
|
||||
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset))
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadACIHeader;
|
||||
|
||||
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset))
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadFileAccessControl;
|
||||
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -4,12 +4,160 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {}
|
||||
|
||||
VfsFilesystem::~VfsFilesystem() = default;
|
||||
|
||||
std::string VfsFilesystem::GetName() const {
|
||||
return root->GetName();
|
||||
}
|
||||
|
||||
bool VfsFilesystem::IsReadable() const {
|
||||
return root->IsReadable();
|
||||
}
|
||||
|
||||
bool VfsFilesystem::IsWritable() const {
|
||||
return root->IsWritable();
|
||||
}
|
||||
|
||||
VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
|
||||
const auto path = FileUtil::SanitizePath(path_);
|
||||
if (root->GetFileRelative(path) != nullptr)
|
||||
return VfsEntryType::File;
|
||||
if (root->GetDirectoryRelative(path) != nullptr)
|
||||
return VfsEntryType::Directory;
|
||||
|
||||
return VfsEntryType::None;
|
||||
}
|
||||
|
||||
VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
|
||||
const auto path = FileUtil::SanitizePath(path_);
|
||||
return root->GetFileRelative(path);
|
||||
}
|
||||
|
||||
VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
|
||||
const auto path = FileUtil::SanitizePath(path_);
|
||||
return root->CreateFileRelative(path);
|
||||
}
|
||||
|
||||
VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
|
||||
const auto old_path = FileUtil::SanitizePath(old_path_);
|
||||
const auto new_path = FileUtil::SanitizePath(new_path_);
|
||||
|
||||
// VfsDirectory impls are only required to implement copy across the current directory.
|
||||
if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) {
|
||||
if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path)))
|
||||
return nullptr;
|
||||
return OpenFile(new_path, Mode::ReadWrite);
|
||||
}
|
||||
|
||||
// Do it using RawCopy. Non-default impls are encouraged to optimize this.
|
||||
const auto old_file = OpenFile(old_path, Mode::Read);
|
||||
if (old_file == nullptr)
|
||||
return nullptr;
|
||||
auto new_file = OpenFile(new_path, Mode::Read);
|
||||
if (new_file != nullptr)
|
||||
return nullptr;
|
||||
new_file = CreateFile(new_path, Mode::Write);
|
||||
if (new_file == nullptr)
|
||||
return nullptr;
|
||||
if (!VfsRawCopy(old_file, new_file))
|
||||
return nullptr;
|
||||
return new_file;
|
||||
}
|
||||
|
||||
VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
|
||||
const auto old_path = FileUtil::SanitizePath(old_path_);
|
||||
const auto new_path = FileUtil::SanitizePath(new_path_);
|
||||
|
||||
// Again, non-default impls are highly encouraged to provide a more optimized version of this.
|
||||
auto out = CopyFile(old_path_, new_path_);
|
||||
if (out == nullptr)
|
||||
return nullptr;
|
||||
if (DeleteFile(old_path))
|
||||
return out;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool VfsFilesystem::DeleteFile(std::string_view path_) {
|
||||
const auto path = FileUtil::SanitizePath(path_);
|
||||
auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
|
||||
if (parent == nullptr)
|
||||
return false;
|
||||
return parent->DeleteFile(FileUtil::GetFilename(path));
|
||||
}
|
||||
|
||||
VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
|
||||
const auto path = FileUtil::SanitizePath(path_);
|
||||
return root->GetDirectoryRelative(path);
|
||||
}
|
||||
|
||||
VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
|
||||
const auto path = FileUtil::SanitizePath(path_);
|
||||
return root->CreateDirectoryRelative(path);
|
||||
}
|
||||
|
||||
VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
|
||||
const auto old_path = FileUtil::SanitizePath(old_path_);
|
||||
const auto new_path = FileUtil::SanitizePath(new_path_);
|
||||
|
||||
// Non-default impls are highly encouraged to provide a more optimized version of this.
|
||||
auto old_dir = OpenDirectory(old_path, Mode::Read);
|
||||
if (old_dir == nullptr)
|
||||
return nullptr;
|
||||
auto new_dir = OpenDirectory(new_path, Mode::Read);
|
||||
if (new_dir != nullptr)
|
||||
return nullptr;
|
||||
new_dir = CreateDirectory(new_path, Mode::Write);
|
||||
if (new_dir == nullptr)
|
||||
return nullptr;
|
||||
|
||||
for (const auto& file : old_dir->GetFiles()) {
|
||||
const auto x =
|
||||
CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName());
|
||||
if (x == nullptr)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const auto& dir : old_dir->GetSubdirectories()) {
|
||||
const auto x =
|
||||
CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName());
|
||||
if (x == nullptr)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new_dir;
|
||||
}
|
||||
|
||||
VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) {
|
||||
const auto old_path = FileUtil::SanitizePath(old_path_);
|
||||
const auto new_path = FileUtil::SanitizePath(new_path_);
|
||||
|
||||
// Non-default impls are highly encouraged to provide a more optimized version of this.
|
||||
auto out = CopyDirectory(old_path_, new_path_);
|
||||
if (out == nullptr)
|
||||
return nullptr;
|
||||
if (DeleteDirectory(old_path))
|
||||
return out;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
|
||||
const auto path = FileUtil::SanitizePath(path_);
|
||||
auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
|
||||
if (parent == nullptr)
|
||||
return false;
|
||||
return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path));
|
||||
}
|
||||
|
||||
VfsFile::~VfsFile() = default;
|
||||
|
||||
std::string VfsFile::GetExtension() const {
|
||||
|
||||
@@ -11,14 +11,74 @@
|
||||
#include <vector>
|
||||
#include "boost/optional.hpp"
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
struct VfsFilesystem;
|
||||
struct VfsFile;
|
||||
struct VfsDirectory;
|
||||
|
||||
// Convenience typedefs to use VfsDirectory and VfsFile
|
||||
using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>;
|
||||
using VirtualFile = std::shared_ptr<FileSys::VfsFile>;
|
||||
// Convenience typedefs to use Vfs* interfaces
|
||||
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
|
||||
using VirtualDir = std::shared_ptr<VfsDirectory>;
|
||||
using VirtualFile = std::shared_ptr<VfsFile>;
|
||||
|
||||
// An enumeration representing what can be at the end of a path in a VfsFilesystem
|
||||
enum class VfsEntryType {
|
||||
None,
|
||||
File,
|
||||
Directory,
|
||||
};
|
||||
|
||||
// A class representing an abstract filesystem. A default implementation given the root VirtualDir
|
||||
// is provided for convenience, but if the Vfs implementation has any additional state or
|
||||
// functionality, they will need to override.
|
||||
struct VfsFilesystem : NonCopyable {
|
||||
VfsFilesystem(VirtualDir root);
|
||||
virtual ~VfsFilesystem();
|
||||
|
||||
// Gets the friendly name for the filesystem.
|
||||
virtual std::string GetName() const;
|
||||
|
||||
// Return whether or not the user has read permissions on this filesystem.
|
||||
virtual bool IsReadable() const;
|
||||
// Return whether or not the user has write permission on this filesystem.
|
||||
virtual bool IsWritable() const;
|
||||
|
||||
// Determine if the entry at path is non-existant, a file, or a directory.
|
||||
virtual VfsEntryType GetEntryType(std::string_view path) const;
|
||||
|
||||
// Opens the file with path relative to root. If it doesn't exist, returns nullptr.
|
||||
virtual VirtualFile OpenFile(std::string_view path, Mode perms);
|
||||
// Creates a new, empty file at path
|
||||
virtual VirtualFile CreateFile(std::string_view path, Mode perms);
|
||||
// Copies the file from old_path to new_path, returning the new file on success and nullptr on
|
||||
// failure.
|
||||
virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path);
|
||||
// Moves the file from old_path to new_path, returning the moved file on success and nullptr on
|
||||
// failure.
|
||||
virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path);
|
||||
// Deletes the file with path relative to root, returing true on success.
|
||||
virtual bool DeleteFile(std::string_view path);
|
||||
|
||||
// Opens the directory with path relative to root. If it doesn't exist, returns nullptr.
|
||||
virtual VirtualDir OpenDirectory(std::string_view path, Mode perms);
|
||||
// Creates a new, empty directory at path
|
||||
virtual VirtualDir CreateDirectory(std::string_view path, Mode perms);
|
||||
// Copies the directory from old_path to new_path, returning the new directory on success and
|
||||
// nullptr on failure.
|
||||
virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path);
|
||||
// Moves the directory from old_path to new_path, returning the moved directory on success and
|
||||
// nullptr on failure.
|
||||
virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path);
|
||||
// Deletes the directory with path relative to root, returing true on success.
|
||||
virtual bool DeleteDirectory(std::string_view path);
|
||||
|
||||
protected:
|
||||
// Root directory in default implementation.
|
||||
VirtualDir root;
|
||||
};
|
||||
|
||||
// A class representing a file in an abstract filesystem.
|
||||
struct VfsFile : NonCopyable {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
@@ -29,6 +29,8 @@ static std::string ModeFlagsToString(Mode mode) {
|
||||
mode_str = "a";
|
||||
else if (mode & Mode::Write)
|
||||
mode_str = "w";
|
||||
else
|
||||
UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode));
|
||||
}
|
||||
|
||||
mode_str += "b";
|
||||
@@ -36,8 +38,174 @@ static std::string ModeFlagsToString(Mode mode) {
|
||||
return mode_str;
|
||||
}
|
||||
|
||||
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
|
||||
: backing(path_, ModeFlagsToString(perms_).c_str()), path(path_),
|
||||
RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
|
||||
|
||||
std::string RealVfsFilesystem::GetName() const {
|
||||
return "Real";
|
||||
}
|
||||
|
||||
bool RealVfsFilesystem::IsReadable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RealVfsFilesystem::IsWritable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
|
||||
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
if (!FileUtil::Exists(path))
|
||||
return VfsEntryType::None;
|
||||
if (FileUtil::IsDirectory(path))
|
||||
return VfsEntryType::Directory;
|
||||
|
||||
return VfsEntryType::File;
|
||||
}
|
||||
|
||||
VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
|
||||
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
if (cache.find(path) != cache.end()) {
|
||||
auto weak = cache[path];
|
||||
if (!weak.expired()) {
|
||||
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
|
||||
}
|
||||
}
|
||||
|
||||
if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0)
|
||||
FileUtil::CreateEmptyFile(path);
|
||||
|
||||
auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str());
|
||||
cache[path] = backing;
|
||||
|
||||
// Cannot use make_shared as RealVfsFile constructor is private
|
||||
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
|
||||
}
|
||||
|
||||
VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
|
||||
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
if (!FileUtil::Exists(path) && !FileUtil::CreateEmptyFile(path))
|
||||
return nullptr;
|
||||
return OpenFile(path, perms);
|
||||
}
|
||||
|
||||
VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
|
||||
const auto old_path =
|
||||
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
const auto new_path =
|
||||
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
|
||||
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
|
||||
FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path))
|
||||
return nullptr;
|
||||
return OpenFile(new_path, Mode::ReadWrite);
|
||||
}
|
||||
|
||||
VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
|
||||
const auto old_path =
|
||||
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
const auto new_path =
|
||||
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
|
||||
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
|
||||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
|
||||
return nullptr;
|
||||
|
||||
if (cache.find(old_path) != cache.end()) {
|
||||
auto cached = cache[old_path];
|
||||
if (!cached.expired()) {
|
||||
auto file = cached.lock();
|
||||
file->Open(new_path, "r+b");
|
||||
cache.erase(old_path);
|
||||
cache[new_path] = file;
|
||||
}
|
||||
}
|
||||
return OpenFile(new_path, Mode::ReadWrite);
|
||||
}
|
||||
|
||||
bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
|
||||
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
if (cache.find(path) != cache.end()) {
|
||||
if (!cache[path].expired())
|
||||
cache[path].lock()->Close();
|
||||
cache.erase(path);
|
||||
}
|
||||
return FileUtil::Delete(path);
|
||||
}
|
||||
|
||||
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
|
||||
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
// Cannot use make_shared as RealVfsDirectory constructor is private
|
||||
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
||||
}
|
||||
|
||||
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
|
||||
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
if (!FileUtil::Exists(path) && !FileUtil::CreateDir(path))
|
||||
return nullptr;
|
||||
// Cannot use make_shared as RealVfsDirectory constructor is private
|
||||
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
||||
}
|
||||
|
||||
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
|
||||
std::string_view new_path_) {
|
||||
const auto old_path =
|
||||
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
const auto new_path =
|
||||
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
|
||||
!FileUtil::IsDirectory(old_path))
|
||||
return nullptr;
|
||||
FileUtil::CopyDir(old_path, new_path);
|
||||
return OpenDirectory(new_path, Mode::ReadWrite);
|
||||
}
|
||||
|
||||
VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
|
||||
std::string_view new_path_) {
|
||||
const auto old_path =
|
||||
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
const auto new_path =
|
||||
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
|
||||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
|
||||
return nullptr;
|
||||
|
||||
for (auto& kv : cache) {
|
||||
// Path in cache starts with old_path
|
||||
if (kv.first.rfind(old_path, 0) == 0) {
|
||||
const auto file_old_path =
|
||||
FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
const auto file_new_path =
|
||||
FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
|
||||
FileUtil::DirectorySeparator::PlatformDefault);
|
||||
auto cached = cache[file_old_path];
|
||||
if (!cached.expired()) {
|
||||
auto file = cached.lock();
|
||||
file->Open(file_new_path, "r+b");
|
||||
cache.erase(file_old_path);
|
||||
cache[file_new_path] = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OpenDirectory(new_path, Mode::ReadWrite);
|
||||
}
|
||||
|
||||
bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
|
||||
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
for (auto& kv : cache) {
|
||||
// Path in cache starts with old_path
|
||||
if (kv.first.rfind(path, 0) == 0) {
|
||||
if (!cache[kv.first].expired())
|
||||
cache[kv.first].lock()->Close();
|
||||
cache.erase(kv.first);
|
||||
}
|
||||
}
|
||||
return FileUtil::DeleteDirRecursively(path);
|
||||
}
|
||||
|
||||
RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_,
|
||||
const std::string& path_, Mode perms_)
|
||||
: base(base_), backing(std::move(backing_)), path(path_),
|
||||
parent_path(FileUtil::GetParentPath(path_)),
|
||||
path_components(FileUtil::SplitPathComponents(path_)),
|
||||
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
|
||||
@@ -48,15 +216,15 @@ std::string RealVfsFile::GetName() const {
|
||||
}
|
||||
|
||||
size_t RealVfsFile::GetSize() const {
|
||||
return backing.GetSize();
|
||||
return backing->GetSize();
|
||||
}
|
||||
|
||||
bool RealVfsFile::Resize(size_t new_size) {
|
||||
return backing.Resize(new_size);
|
||||
return backing->Resize(new_size);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
|
||||
return std::make_shared<RealVfsDirectory>(parent_path, perms);
|
||||
return base.OpenDirectory(parent_path, perms);
|
||||
}
|
||||
|
||||
bool RealVfsFile::IsWritable() const {
|
||||
@@ -68,62 +236,118 @@ bool RealVfsFile::IsReadable() const {
|
||||
}
|
||||
|
||||
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
|
||||
if (!backing.Seek(offset, SEEK_SET))
|
||||
if (!backing->Seek(offset, SEEK_SET))
|
||||
return 0;
|
||||
return backing.ReadBytes(data, length);
|
||||
return backing->ReadBytes(data, length);
|
||||
}
|
||||
|
||||
size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
|
||||
if (!backing.Seek(offset, SEEK_SET))
|
||||
if (!backing->Seek(offset, SEEK_SET))
|
||||
return 0;
|
||||
return backing.WriteBytes(data, length);
|
||||
return backing->WriteBytes(data, length);
|
||||
}
|
||||
|
||||
bool RealVfsFile::Rename(std::string_view name) {
|
||||
std::string name_str(name.begin(), name.end());
|
||||
const auto out = FileUtil::Rename(GetName(), name_str);
|
||||
return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr;
|
||||
}
|
||||
|
||||
path = (parent_path + DIR_SEP).append(name);
|
||||
path_components = parent_components;
|
||||
path_components.push_back(std::move(name_str));
|
||||
backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str());
|
||||
bool RealVfsFile::Close() {
|
||||
return backing->Close();
|
||||
}
|
||||
|
||||
// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if
|
||||
// constexpr' because there is a compile error in the branch not used.
|
||||
|
||||
template <>
|
||||
std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
|
||||
if (perms == Mode::Append)
|
||||
return {};
|
||||
|
||||
std::vector<VirtualFile> out;
|
||||
FileUtil::ForeachDirectoryEntry(
|
||||
nullptr, path,
|
||||
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
|
||||
const std::string full_path = directory + DIR_SEP + filename;
|
||||
if (!FileUtil::IsDirectory(full_path))
|
||||
out.emplace_back(base.OpenFile(full_path, perms));
|
||||
return true;
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
bool RealVfsFile::Close() {
|
||||
return backing.Close();
|
||||
template <>
|
||||
std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
|
||||
if (perms == Mode::Append)
|
||||
return {};
|
||||
|
||||
std::vector<VirtualDir> out;
|
||||
FileUtil::ForeachDirectoryEntry(
|
||||
nullptr, path,
|
||||
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
|
||||
const std::string full_path = directory + DIR_SEP + filename;
|
||||
if (FileUtil::IsDirectory(full_path))
|
||||
out.emplace_back(base.OpenDirectory(full_path, perms));
|
||||
return true;
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
|
||||
: path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)),
|
||||
RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
|
||||
: base(base_), path(FileUtil::RemoveTrailingSlash(path_)),
|
||||
parent_path(FileUtil::GetParentPath(path)),
|
||||
path_components(FileUtil::SplitPathComponents(path)),
|
||||
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
|
||||
perms(perms_) {
|
||||
if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
|
||||
FileUtil::CreateDir(path);
|
||||
}
|
||||
|
||||
if (perms == Mode::Append)
|
||||
return;
|
||||
std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
|
||||
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
|
||||
if (!FileUtil::Exists(full_path))
|
||||
return nullptr;
|
||||
return base.OpenFile(full_path, perms);
|
||||
}
|
||||
|
||||
FileUtil::ForeachDirectoryEntry(
|
||||
nullptr, path,
|
||||
[this](u64* entries_out, const std::string& directory, const std::string& filename) {
|
||||
std::string full_path = directory + DIR_SEP + filename;
|
||||
if (FileUtil::IsDirectory(full_path))
|
||||
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms));
|
||||
else
|
||||
files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms));
|
||||
return true;
|
||||
});
|
||||
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const {
|
||||
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
|
||||
if (!FileUtil::Exists(full_path))
|
||||
return nullptr;
|
||||
return base.OpenDirectory(full_path, perms);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> RealVfsDirectory::GetFile(std::string_view name) const {
|
||||
return GetFileRelative(name);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view name) const {
|
||||
return GetDirectoryRelative(name);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) {
|
||||
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
|
||||
return base.CreateFile(full_path, perms);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
|
||||
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
|
||||
auto parent = std::string(FileUtil::GetParentPath(full_path));
|
||||
return base.CreateDirectory(full_path, perms);
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
|
||||
auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name));
|
||||
return base.DeleteDirectory(full_path);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
|
||||
return files;
|
||||
return IterateEntries<RealVfsFile, VfsFile>();
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
|
||||
return subdirectories;
|
||||
return IterateEntries<RealVfsDirectory, VfsDirectory>();
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::IsWritable() const {
|
||||
@@ -142,57 +366,32 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
|
||||
if (path_components.size() <= 1)
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<RealVfsDirectory>(parent_path, perms);
|
||||
return base.OpenDirectory(parent_path, perms);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) {
|
||||
const std::string subdir_path = (path + DIR_SEP).append(name);
|
||||
|
||||
if (!FileUtil::CreateDir(subdir_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms));
|
||||
return subdirectories.back();
|
||||
return base.CreateDirectory(subdir_path, perms);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) {
|
||||
const std::string file_path = (path + DIR_SEP).append(name);
|
||||
|
||||
if (!FileUtil::CreateEmptyFile(file_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms));
|
||||
return files.back();
|
||||
return base.CreateFile(file_path, perms);
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
|
||||
const std::string subdir_path = (path + DIR_SEP).append(name);
|
||||
|
||||
return FileUtil::DeleteDirRecursively(subdir_path);
|
||||
return base.DeleteDirectory(subdir_path);
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::DeleteFile(std::string_view name) {
|
||||
const auto file = GetFile(name);
|
||||
|
||||
if (file == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
files.erase(std::find(files.begin(), files.end(), file));
|
||||
|
||||
auto real_file = std::static_pointer_cast<RealVfsFile>(file);
|
||||
real_file->Close();
|
||||
|
||||
const std::string file_path = (path + DIR_SEP).append(name);
|
||||
return FileUtil::Delete(file_path);
|
||||
return base.DeleteFile(file_path);
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::Rename(std::string_view name) {
|
||||
const std::string new_name = (parent_path + DIR_SEP).append(name);
|
||||
|
||||
return FileUtil::Rename(path, new_name);
|
||||
return base.MoveFile(path, new_name) != nullptr;
|
||||
}
|
||||
|
||||
std::string RealVfsDirectory::GetFullPath() const {
|
||||
@@ -202,16 +401,6 @@ std::string RealVfsDirectory::GetFullPath() const {
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
const auto iter = std::find(files.begin(), files.end(), file);
|
||||
if (iter == files.end())
|
||||
return false;
|
||||
|
||||
const std::ptrdiff_t offset = std::distance(files.begin(), iter);
|
||||
files[offset] = files.back();
|
||||
files.pop_back();
|
||||
|
||||
subdirectories.emplace_back(std::move(dir));
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -6,18 +6,45 @@
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class RealVfsFilesystem : public VfsFilesystem {
|
||||
public:
|
||||
RealVfsFilesystem();
|
||||
|
||||
std::string GetName() const override;
|
||||
bool IsReadable() const override;
|
||||
bool IsWritable() const override;
|
||||
VfsEntryType GetEntryType(std::string_view path) const override;
|
||||
VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override;
|
||||
VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override;
|
||||
VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override;
|
||||
VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override;
|
||||
bool DeleteFile(std::string_view path) override;
|
||||
VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override;
|
||||
VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override;
|
||||
VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override;
|
||||
VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override;
|
||||
bool DeleteDirectory(std::string_view path) override;
|
||||
|
||||
private:
|
||||
boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache;
|
||||
};
|
||||
|
||||
// An implmentation of VfsFile that represents a file on the user's computer.
|
||||
struct RealVfsFile : public VfsFile {
|
||||
friend struct RealVfsDirectory;
|
||||
class RealVfsFile : public VfsFile {
|
||||
friend class RealVfsDirectory;
|
||||
friend class RealVfsFilesystem;
|
||||
|
||||
RealVfsFile(const std::string& name, Mode perms = Mode::Read);
|
||||
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
|
||||
const std::string& path, Mode perms = Mode::Read);
|
||||
|
||||
public:
|
||||
std::string GetName() const override;
|
||||
size_t GetSize() const override;
|
||||
bool Resize(size_t new_size) override;
|
||||
@@ -31,7 +58,8 @@ struct RealVfsFile : public VfsFile {
|
||||
private:
|
||||
bool Close();
|
||||
|
||||
FileUtil::IOFile backing;
|
||||
RealVfsFilesystem& base;
|
||||
std::shared_ptr<FileUtil::IOFile> backing;
|
||||
std::string path;
|
||||
std::string parent_path;
|
||||
std::vector<std::string> path_components;
|
||||
@@ -40,9 +68,19 @@ private:
|
||||
};
|
||||
|
||||
// An implementation of VfsDirectory that represents a directory on the user's computer.
|
||||
struct RealVfsDirectory : public VfsDirectory {
|
||||
RealVfsDirectory(const std::string& path, Mode perms = Mode::Read);
|
||||
class RealVfsDirectory : public VfsDirectory {
|
||||
friend class RealVfsFilesystem;
|
||||
|
||||
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
|
||||
|
||||
public:
|
||||
std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
|
||||
std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
|
||||
std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
|
||||
std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override;
|
||||
std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override;
|
||||
std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override;
|
||||
bool DeleteSubdirectoryRecursive(std::string_view name) override;
|
||||
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
|
||||
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
|
||||
bool IsWritable() const override;
|
||||
@@ -60,13 +98,15 @@ protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
template <typename T, typename R>
|
||||
std::vector<std::shared_ptr<R>> IterateEntries() const;
|
||||
|
||||
RealVfsFilesystem& base;
|
||||
std::string path;
|
||||
std::string parent_path;
|
||||
std::vector<std::string> path_components;
|
||||
std::vector<std::string> parent_components;
|
||||
Mode perms;
|
||||
std::vector<std::shared_ptr<VfsFile>> files;
|
||||
std::vector<std::shared_ptr<VfsDirectory>> subdirectories;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
|
||||
public std::enable_shared_from_this<TouchState> {
|
||||
public:
|
||||
@@ -108,3 +110,5 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
|
||||
NotifyFramebufferLayoutChanged(Layout::DefaultFrameLayout(width, height));
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
/**
|
||||
* Abstraction class used to provide an interface between emulation code and the frontend
|
||||
* (e.g. SDL, QGLWidget, GLFW, etc...).
|
||||
@@ -166,3 +168,5 @@ private:
|
||||
*/
|
||||
std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ClientPort::ClientPort() {}
|
||||
ClientPort::~ClientPort() {}
|
||||
ClientPort::ClientPort() = default;
|
||||
ClientPort::~ClientPort() = default;
|
||||
|
||||
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
|
||||
// Note: Threads do not wait for the server endpoint to call
|
||||
@@ -40,4 +40,12 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
|
||||
return MakeResult(std::get<SharedPtr<ClientSession>>(sessions));
|
||||
}
|
||||
|
||||
void ClientPort::ConnectionClosed() {
|
||||
if (active_sessions == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
--active_sessions;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -37,14 +37,20 @@ public:
|
||||
*/
|
||||
ResultVal<SharedPtr<ClientSession>> Connect();
|
||||
|
||||
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
|
||||
u32 max_sessions; ///< Maximum number of simultaneous sessions the port can have
|
||||
u32 active_sessions; ///< Number of currently open sessions to this port
|
||||
std::string name; ///< Name of client port (optional)
|
||||
/**
|
||||
* Signifies that a previously active connection has been closed,
|
||||
* decreasing the total number of active connections to this port.
|
||||
*/
|
||||
void ConnectionClosed();
|
||||
|
||||
private:
|
||||
ClientPort();
|
||||
~ClientPort() override;
|
||||
|
||||
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
|
||||
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have
|
||||
u32 active_sessions = 0; ///< Number of currently open sessions to this port
|
||||
std::string name; ///< Name of client port (optional)
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -201,7 +201,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
|
||||
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
|
||||
HandleTable& src_table);
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
|
||||
ResultCode WriteToOutgoingCommandBuffer(const Thread& thread);
|
||||
|
||||
u32_le GetCommand() const {
|
||||
return command;
|
||||
|
||||
@@ -27,7 +27,7 @@ ServerSession::~ServerSession() {
|
||||
|
||||
// Decrease the port's connection count.
|
||||
if (parent->port)
|
||||
parent->port->active_sessions--;
|
||||
parent->port->ConnectionClosed();
|
||||
|
||||
// TODO(Subv): Wake up all the ClientSession's waiting threads and set
|
||||
// the SendSyncRequest result to 0xC920181A.
|
||||
@@ -71,6 +71,14 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
|
||||
const u32 object_id{context.GetDomainMessageHeader()->object_id};
|
||||
switch (domain_message_header->command) {
|
||||
case IPC::DomainMessageHeader::CommandType::SendMessage:
|
||||
if (object_id > domain_request_handlers.size()) {
|
||||
LOG_CRITICAL(IPC,
|
||||
"object_id {} is too big! This probably means a recent service call "
|
||||
"to {} needed to return a new interface!",
|
||||
object_id, name);
|
||||
UNREACHABLE();
|
||||
return RESULT_SUCCESS; // Ignore error if asserts are off
|
||||
}
|
||||
return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
|
||||
|
||||
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/romfs.h"
|
||||
|
||||
namespace RomFS {
|
||||
|
||||
struct Header {
|
||||
u32_le header_length;
|
||||
u32_le dir_hash_table_offset;
|
||||
u32_le dir_hash_table_length;
|
||||
u32_le dir_table_offset;
|
||||
u32_le dir_table_length;
|
||||
u32_le file_hash_table_offset;
|
||||
u32_le file_hash_table_length;
|
||||
u32_le file_table_offset;
|
||||
u32_le file_table_length;
|
||||
u32_le data_offset;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Header) == 0x28, "Header has incorrect size");
|
||||
|
||||
struct DirectoryMetadata {
|
||||
u32_le parent_dir_offset;
|
||||
u32_le next_dir_offset;
|
||||
u32_le first_child_dir_offset;
|
||||
u32_le first_file_offset;
|
||||
u32_le same_hash_next_dir_offset;
|
||||
u32_le name_length; // in bytes
|
||||
// followed by directory name
|
||||
};
|
||||
|
||||
static_assert(sizeof(DirectoryMetadata) == 0x18, "DirectoryMetadata has incorrect size");
|
||||
|
||||
struct FileMetadata {
|
||||
u32_le parent_dir_offset;
|
||||
u32_le next_file_offset;
|
||||
u64_le data_offset;
|
||||
u64_le data_length;
|
||||
u32_le same_hash_next_file_offset;
|
||||
u32_le name_length; // in bytes
|
||||
// followed by file name
|
||||
};
|
||||
|
||||
static_assert(sizeof(FileMetadata) == 0x20, "FileMetadata has incorrect size");
|
||||
|
||||
static bool MatchName(const u8* buffer, u32 name_length, const std::u16string& name) {
|
||||
std::vector<char16_t> name_buffer(name_length / sizeof(char16_t));
|
||||
std::memcpy(name_buffer.data(), buffer, name_length);
|
||||
return name == std::u16string(name_buffer.begin(), name_buffer.end());
|
||||
}
|
||||
|
||||
const u8* GetFilePointer(const u8* romfs, const std::vector<std::u16string>& path) {
|
||||
constexpr u32 INVALID_FIELD = 0xFFFFFFFF;
|
||||
|
||||
// Split path into directory names and file name
|
||||
std::vector<std::u16string> dir_names = path;
|
||||
dir_names.pop_back();
|
||||
const std::u16string& file_name = path.back();
|
||||
|
||||
Header header;
|
||||
std::memcpy(&header, romfs, sizeof(header));
|
||||
|
||||
// Find directories of each level
|
||||
DirectoryMetadata dir;
|
||||
const u8* current_dir = romfs + header.dir_table_offset;
|
||||
std::memcpy(&dir, current_dir, sizeof(dir));
|
||||
for (const std::u16string& dir_name : dir_names) {
|
||||
u32 child_dir_offset;
|
||||
child_dir_offset = dir.first_child_dir_offset;
|
||||
while (true) {
|
||||
if (child_dir_offset == INVALID_FIELD) {
|
||||
return nullptr;
|
||||
}
|
||||
const u8* current_child_dir = romfs + header.dir_table_offset + child_dir_offset;
|
||||
std::memcpy(&dir, current_child_dir, sizeof(dir));
|
||||
if (MatchName(current_child_dir + sizeof(dir), dir.name_length, dir_name)) {
|
||||
current_dir = current_child_dir;
|
||||
break;
|
||||
}
|
||||
child_dir_offset = dir.next_dir_offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the file
|
||||
FileMetadata file;
|
||||
u32 file_offset = dir.first_file_offset;
|
||||
while (file_offset != INVALID_FIELD) {
|
||||
const u8* current_file = romfs + header.file_table_offset + file_offset;
|
||||
std::memcpy(&file, current_file, sizeof(file));
|
||||
if (MatchName(current_file + sizeof(file), file.name_length, file_name)) {
|
||||
return romfs + header.data_offset + file.data_offset;
|
||||
}
|
||||
file_offset = file.next_file_offset;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace RomFS
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace RomFS {
|
||||
|
||||
/**
|
||||
* Gets the pointer to a file in a RomFS image.
|
||||
* @param romfs The pointer to the RomFS image
|
||||
* @param path A vector containing the directory names and file name of the path to the file
|
||||
* @return the pointer to the file
|
||||
* @todo reimplement this with a full RomFS manager
|
||||
*/
|
||||
const u8* GetFilePointer(const u8* romfs, const std::vector<std::u16string>& path);
|
||||
|
||||
} // namespace RomFS
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
{0, &IProfile::Get, "Get"},
|
||||
{1, &IProfile::GetBase, "GetBase"},
|
||||
{10, nullptr, "GetImageSize"},
|
||||
{11, nullptr, "LoadImage"},
|
||||
{11, &IProfile::LoadImage, "LoadImage"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
@@ -83,6 +83,27 @@ private:
|
||||
rb.PushRaw(profile_base);
|
||||
}
|
||||
|
||||
void LoadImage(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
// smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
|
||||
// TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
|
||||
const u32 jpeg_size = 107;
|
||||
static const std::array<u8, jpeg_size> jpeg{
|
||||
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
|
||||
0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
|
||||
0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
|
||||
0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
|
||||
0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
|
||||
0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||
};
|
||||
ctx.WriteBuffer(jpeg.data(), jpeg_size);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(jpeg_size);
|
||||
}
|
||||
|
||||
u128 user_id; ///< The user id this profile refers to.
|
||||
};
|
||||
|
||||
@@ -119,6 +140,13 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(1);
|
||||
}
|
||||
|
||||
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
public:
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void GetUserCount(Kernel::HLERequestContext& ctx);
|
||||
void GetUserExistence(Kernel::HLERequestContext& ctx);
|
||||
void ListAllUsers(Kernel::HLERequestContext& ctx);
|
||||
void ListOpenUsers(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Service::Account {
|
||||
|
||||
ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:su") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetUserCount"},
|
||||
{0, &ACC_SU::GetUserCount, "GetUserCount"},
|
||||
{1, &ACC_SU::GetUserExistence, "GetUserExistence"},
|
||||
{2, &ACC_SU::ListAllUsers, "ListAllUsers"},
|
||||
{3, &ACC_SU::ListOpenUsers, "ListOpenUsers"},
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Service::Account {
|
||||
|
||||
ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u0") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetUserCount"},
|
||||
{0, &ACC_U0::GetUserCount, "GetUserCount"},
|
||||
{1, &ACC_U0::GetUserExistence, "GetUserExistence"},
|
||||
{2, &ACC_U0::ListAllUsers, "ListAllUsers"},
|
||||
{3, &ACC_U0::ListOpenUsers, "ListOpenUsers"},
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Service::Account {
|
||||
|
||||
ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u1") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetUserCount"},
|
||||
{0, &ACC_U1::GetUserCount, "GetUserCount"},
|
||||
{1, &ACC_U1::GetUserExistence, "GetUserExistence"},
|
||||
{2, &ACC_U1::ListAllUsers, "ListAllUsers"},
|
||||
{3, &ACC_U1::ListOpenUsers, "ListOpenUsers"},
|
||||
|
||||
@@ -136,7 +136,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
{16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"},
|
||||
{17, nullptr, "SetControllerFirmwareUpdateSection"},
|
||||
{18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
|
||||
{19, nullptr, "SetScreenShotImageOrientation"},
|
||||
{19, &ISelfController::SetScreenShotImageOrientation, "SetScreenShotImageOrientation"},
|
||||
{20, nullptr, "SetDesirableKeyboardLayout"},
|
||||
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
|
||||
{41, nullptr, "IsSystemBufferSharingEnabled"},
|
||||
@@ -254,6 +254,13 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext&
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just create the layer
|
||||
// in the Default display.
|
||||
|
||||
@@ -83,6 +83,7 @@ private:
|
||||
void LockExit(Kernel::HLERequestContext& ctx);
|
||||
void UnlockExit(Kernel::HLERequestContext& ctx);
|
||||
void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx);
|
||||
void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx);
|
||||
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
|
||||
void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
|
||||
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -59,7 +59,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
|
||||
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (path == "/" || path == "\\") {
|
||||
if (path.empty()) {
|
||||
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -281,15 +281,15 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
|
||||
return sdmc_factory->Open();
|
||||
}
|
||||
|
||||
void RegisterFileSystems() {
|
||||
void RegisterFileSystems(const FileSys::VirtualFilesystem& vfs) {
|
||||
romfs_factory = nullptr;
|
||||
save_data_factory = nullptr;
|
||||
sdmc_factory = nullptr;
|
||||
|
||||
auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite);
|
||||
auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite);
|
||||
auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
|
||||
FileSys::Mode::ReadWrite);
|
||||
auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
|
||||
FileSys::Mode::ReadWrite);
|
||||
|
||||
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
|
||||
save_data_factory = std::move(savedata);
|
||||
@@ -298,8 +298,8 @@ void RegisterFileSystems() {
|
||||
sdmc_factory = std::move(sdcard);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
RegisterFileSystems();
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) {
|
||||
RegisterFileSystems(vfs);
|
||||
std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
|
||||
std::make_shared<FSP_PR>()->InstallAsService(service_manager);
|
||||
std::make_shared<FSP_SRV>()->InstallAsService(service_manager);
|
||||
|
||||
@@ -36,7 +36,7 @@ ResultVal<FileSys::VirtualDir> OpenSDMC();
|
||||
// ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();
|
||||
|
||||
/// Registers all Filesystem services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs);
|
||||
|
||||
// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
|
||||
// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
|
||||
|
||||
@@ -193,13 +193,10 @@ private:
|
||||
template <typename T>
|
||||
static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data,
|
||||
FileSys::EntryType type) {
|
||||
entries.reserve(entries.size() + new_data.size());
|
||||
|
||||
for (const auto& new_entry : new_data) {
|
||||
FileSys::Entry entry;
|
||||
entry.filename[0] = '\0';
|
||||
std::strncat(entry.filename, new_entry->GetName().c_str(), FileSys::FILENAME_LENGTH - 1);
|
||||
entry.type = type;
|
||||
entry.file_size = new_entry->GetSize();
|
||||
entries.emplace_back(std::move(entry));
|
||||
entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,103 @@
|
||||
|
||||
namespace Service::Friend {
|
||||
|
||||
class IFriendService final : public ServiceFramework<IFriendService> {
|
||||
public:
|
||||
IFriendService() : ServiceFramework("IFriendService") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetCompletionEvent"},
|
||||
{1, nullptr, "Cancel"},
|
||||
{10100, nullptr, "GetFriendListIds"},
|
||||
{10101, nullptr, "GetFriendList"},
|
||||
{10102, nullptr, "UpdateFriendInfo"},
|
||||
{10110, nullptr, "GetFriendProfileImage"},
|
||||
{10200, nullptr, "SendFriendRequestForApplication"},
|
||||
{10211, nullptr, "AddFacedFriendRequestForApplication"},
|
||||
{10400, nullptr, "GetBlockedUserListIds"},
|
||||
{10500, nullptr, "GetProfileList"},
|
||||
{10600, nullptr, "DeclareOpenOnlinePlaySession"},
|
||||
{10601, &IFriendService::DeclareCloseOnlinePlaySession,
|
||||
"DeclareCloseOnlinePlaySession"},
|
||||
{10610, nullptr, "UpdateUserPresence"},
|
||||
{10700, nullptr, "GetPlayHistoryRegistrationKey"},
|
||||
{10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"},
|
||||
{10702, nullptr, "AddPlayHistory"},
|
||||
{11000, nullptr, "GetProfileImageUrl"},
|
||||
{20100, nullptr, "GetFriendCount"},
|
||||
{20101, nullptr, "GetNewlyFriendCount"},
|
||||
{20102, nullptr, "GetFriendDetailedInfo"},
|
||||
{20103, nullptr, "SyncFriendList"},
|
||||
{20104, nullptr, "RequestSyncFriendList"},
|
||||
{20110, nullptr, "LoadFriendSetting"},
|
||||
{20200, nullptr, "GetReceivedFriendRequestCount"},
|
||||
{20201, nullptr, "GetFriendRequestList"},
|
||||
{20300, nullptr, "GetFriendCandidateList"},
|
||||
{20301, nullptr, "GetNintendoNetworkIdInfo"},
|
||||
{20302, nullptr, "GetSnsAccountLinkage"},
|
||||
{20303, nullptr, "GetSnsAccountProfile"},
|
||||
{20304, nullptr, "GetSnsAccountFriendList"},
|
||||
{20400, nullptr, "GetBlockedUserList"},
|
||||
{20401, nullptr, "SyncBlockedUserList"},
|
||||
{20500, nullptr, "GetProfileExtraList"},
|
||||
{20501, nullptr, "GetRelationship"},
|
||||
{20600, nullptr, "GetUserPresenceView"},
|
||||
{20700, nullptr, "GetPlayHistoryList"},
|
||||
{20701, nullptr, "GetPlayHistoryStatistics"},
|
||||
{20800, nullptr, "LoadUserSetting"},
|
||||
{20801, nullptr, "SyncUserSetting"},
|
||||
{20900, nullptr, "RequestListSummaryOverlayNotification"},
|
||||
{21000, nullptr, "GetExternalApplicationCatalog"},
|
||||
{30100, nullptr, "DropFriendNewlyFlags"},
|
||||
{30101, nullptr, "DeleteFriend"},
|
||||
{30110, nullptr, "DropFriendNewlyFlag"},
|
||||
{30120, nullptr, "ChangeFriendFavoriteFlag"},
|
||||
{30121, nullptr, "ChangeFriendOnlineNotificationFlag"},
|
||||
{30200, nullptr, "SendFriendRequest"},
|
||||
{30201, nullptr, "SendFriendRequestWithApplicationInfo"},
|
||||
{30202, nullptr, "CancelFriendRequest"},
|
||||
{30203, nullptr, "AcceptFriendRequest"},
|
||||
{30204, nullptr, "RejectFriendRequest"},
|
||||
{30205, nullptr, "ReadFriendRequest"},
|
||||
{30210, nullptr, "GetFacedFriendRequestRegistrationKey"},
|
||||
{30211, nullptr, "AddFacedFriendRequest"},
|
||||
{30212, nullptr, "CancelFacedFriendRequest"},
|
||||
{30213, nullptr, "GetFacedFriendRequestProfileImage"},
|
||||
{30214, nullptr, "GetFacedFriendRequestProfileImageFromPath"},
|
||||
{30215, nullptr, "SendFriendRequestWithExternalApplicationCatalogId"},
|
||||
{30216, nullptr, "ResendFacedFriendRequest"},
|
||||
{30217, nullptr, "SendFriendRequestWithNintendoNetworkIdInfo"},
|
||||
{30300, nullptr, "GetSnsAccountLinkPageUrl"},
|
||||
{30301, nullptr, "UnlinkSnsAccount"},
|
||||
{30400, nullptr, "BlockUser"},
|
||||
{30401, nullptr, "BlockUserWithApplicationInfo"},
|
||||
{30402, nullptr, "UnblockUser"},
|
||||
{30500, nullptr, "GetProfileExtraFromFriendCode"},
|
||||
{30700, nullptr, "DeletePlayHistory"},
|
||||
{30810, nullptr, "ChangePresencePermission"},
|
||||
{30811, nullptr, "ChangeFriendRequestReception"},
|
||||
{30812, nullptr, "ChangePlayLogPermission"},
|
||||
{30820, nullptr, "IssueFriendCode"},
|
||||
{30830, nullptr, "ClearPlayLog"},
|
||||
{49900, nullptr, "DeleteNetworkServiceAccountCache"},
|
||||
};
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void DeclareCloseOnlinePlaySession(Kernel::HLERequestContext& ctx) {
|
||||
// Stub used by Splatoon 2
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
};
|
||||
|
||||
void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called");
|
||||
rb.PushIpcInterface<IFriendService>();
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
|
||||
@@ -337,6 +337,7 @@ public:
|
||||
"AcquireNpadStyleSetUpdateEventHandle"},
|
||||
{107, nullptr, "DisconnectNpad"},
|
||||
{108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
|
||||
{109, nullptr, "ActivateNpadWithRevision"},
|
||||
{120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
|
||||
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
|
||||
{122, &Hid::SetNpadJoyAssignmentModeSingleByDefault,
|
||||
@@ -456,7 +457,7 @@ private:
|
||||
}
|
||||
|
||||
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
// TODO (Hexagon12): Properly implement reading gyroscope values from controllers.
|
||||
rb.Push(true);
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
@@ -145,7 +147,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
|
||||
}
|
||||
params.fence_out.id = 0;
|
||||
params.fence_out.value = 0;
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -16,19 +16,18 @@
|
||||
#include "core/hle/service/nvdrv/interface.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvdrv/nvmemp.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
std::weak_ptr<Module> nvdrv;
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger) {
|
||||
auto module_ = std::make_shared<Module>();
|
||||
std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager);
|
||||
std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager);
|
||||
std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager);
|
||||
std::make_shared<NVDRV>(module_, "nvdrv:t")->InstallAsService(service_manager);
|
||||
std::make_shared<NVMEMP>()->InstallAsService(service_manager);
|
||||
nvdrv = module_;
|
||||
nvflinger.SetNVDrvInstance(module_);
|
||||
}
|
||||
|
||||
Module::Module() {
|
||||
@@ -54,7 +53,7 @@ u32 Module::Open(const std::string& device_name) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
u32 Module::Ioctl(u32 fd, u32_le command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
auto itr = open_files.find(fd);
|
||||
ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
namespace Devices {
|
||||
@@ -56,8 +60,6 @@ private:
|
||||
};
|
||||
|
||||
/// Registers all NVDRV services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
extern std::weak_ptr<Module> nvdrv;
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger);
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvdrv/nvmemp.h"
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
@@ -16,7 +16,7 @@ BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
|
||||
Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
|
||||
}
|
||||
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
|
||||
Buffer buffer{};
|
||||
buffer.slot = slot;
|
||||
buffer.igbp_buffer = igbp_buffer;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
@@ -72,7 +73,7 @@ public:
|
||||
MathUtil::Rectangle<int> crop_rect;
|
||||
};
|
||||
|
||||
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
|
||||
void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
|
||||
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
|
||||
const IGBPBuffer& RequestBuffer(u32 slot) const;
|
||||
void QueueBuffer(u32 slot, BufferTransformFlags transform,
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
@@ -31,7 +34,7 @@ NVFlinger::NVFlinger() {
|
||||
|
||||
// Schedule the screen composition events
|
||||
composition_event =
|
||||
CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) {
|
||||
CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
|
||||
Compose();
|
||||
CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
|
||||
});
|
||||
@@ -43,7 +46,11 @@ NVFlinger::~NVFlinger() {
|
||||
CoreTiming::UnscheduleEvent(composition_event, 0);
|
||||
}
|
||||
|
||||
u64 NVFlinger::OpenDisplay(const std::string& name) {
|
||||
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
|
||||
nvdrv = std::move(instance);
|
||||
}
|
||||
|
||||
u64 NVFlinger::OpenDisplay(std::string_view name) {
|
||||
LOG_WARNING(Service, "Opening display {}", name);
|
||||
|
||||
// TODO(Subv): Currently we only support the Default display.
|
||||
@@ -138,9 +145,6 @@ void NVFlinger::Compose() {
|
||||
auto& igbp_buffer = buffer->igbp_buffer;
|
||||
|
||||
// Now send the buffer to the GPU for drawing.
|
||||
auto nvdrv = Nvidia::nvdrv.lock();
|
||||
ASSERT(nvdrv);
|
||||
|
||||
// TODO(Subv): Support more than just disp0. The display device selection is probably based
|
||||
// on which display we're drawing (Default, Internal, External, etc)
|
||||
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
|
||||
|
||||
@@ -5,13 +5,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <boost/optional.hpp>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia {
|
||||
class Module;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
class BufferQueue;
|
||||
@@ -40,8 +48,11 @@ public:
|
||||
NVFlinger();
|
||||
~NVFlinger();
|
||||
|
||||
/// Sets the NVDrv module instance to use to send buffers to the GPU.
|
||||
void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
|
||||
|
||||
/// Opens the specified display and returns the id.
|
||||
u64 OpenDisplay(const std::string& name);
|
||||
u64 OpenDisplay(std::string_view name);
|
||||
|
||||
/// Creates a layer on the specified display and returns the layer id.
|
||||
u64 CreateLayer(u64 display_id);
|
||||
@@ -66,6 +77,8 @@ private:
|
||||
/// Returns the layer identified by the specified id in the desired display.
|
||||
Layer& GetLayer(u64 display_id, u64 layer_id);
|
||||
|
||||
std::shared_ptr<Nvidia::Module> nvdrv;
|
||||
|
||||
std::vector<Display> displays;
|
||||
std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "core/hle/service/spl/module.h"
|
||||
#include "core/hle/service/ssl/ssl.h"
|
||||
#include "core/hle/service/time/time.h"
|
||||
#include "core/hle/service/usb/usb.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/wlan/wlan.h"
|
||||
|
||||
@@ -197,7 +198,7 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
|
||||
}
|
||||
|
||||
/// Initialize ServiceManager
|
||||
void Init(std::shared_ptr<SM::ServiceManager>& sm) {
|
||||
void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) {
|
||||
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
|
||||
// here and pass it into the respective InstallInterfaces functions.
|
||||
auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
|
||||
@@ -220,7 +221,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
|
||||
EUPLD::InstallInterfaces(*sm);
|
||||
Fatal::InstallInterfaces(*sm);
|
||||
FGM::InstallInterfaces(*sm);
|
||||
FileSystem::InstallInterfaces(*sm);
|
||||
FileSystem::InstallInterfaces(*sm, rfs);
|
||||
Friend::InstallInterfaces(*sm);
|
||||
GRC::InstallInterfaces(*sm);
|
||||
HID::InstallInterfaces(*sm);
|
||||
@@ -237,7 +238,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
|
||||
NIFM::InstallInterfaces(*sm);
|
||||
NIM::InstallInterfaces(*sm);
|
||||
NS::InstallInterfaces(*sm);
|
||||
Nvidia::InstallInterfaces(*sm);
|
||||
Nvidia::InstallInterfaces(*sm, *nv_flinger);
|
||||
PCIe::InstallInterfaces(*sm);
|
||||
PCTL::InstallInterfaces(*sm);
|
||||
PCV::InstallInterfaces(*sm);
|
||||
@@ -249,6 +250,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
|
||||
SPL::InstallInterfaces(*sm);
|
||||
SSL::InstallInterfaces(*sm);
|
||||
Time::InstallInterfaces(*sm);
|
||||
USB::InstallInterfaces(*sm);
|
||||
VI::InstallInterfaces(*sm, nv_flinger);
|
||||
WLAN::InstallInterfaces(*sm);
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@ class ServerSession;
|
||||
class HLERequestContext;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace FileSys {
|
||||
struct VfsFilesystem;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
|
||||
namespace SM {
|
||||
@@ -177,7 +181,8 @@ private:
|
||||
};
|
||||
|
||||
/// Initialize ServiceManager
|
||||
void Init(std::shared_ptr<SM::ServiceManager>& sm);
|
||||
void Init(std::shared_ptr<SM::ServiceManager>& sm,
|
||||
const std::shared_ptr<FileSys::VfsFilesystem>& vfs);
|
||||
|
||||
/// Shutdown ServiceManager
|
||||
void Shutdown();
|
||||
|
||||
@@ -80,8 +80,8 @@ public:
|
||||
{5, nullptr, "GetTimeZoneRuleVersion"},
|
||||
{100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
|
||||
{101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
|
||||
{200, nullptr, "ToPosixTime"},
|
||||
{201, nullptr, "ToPosixTimeWithMyRule"},
|
||||
{201, nullptr, "ToPosixTime"},
|
||||
{202, nullptr, "ToPosixTimeWithMyRule"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
238
src/core/hle/service/usb/usb.cpp
Normal file
238
src/core/hle/service/usb/usb.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/usb/usb.h"
|
||||
|
||||
namespace Service::USB {
|
||||
|
||||
class IDsInterface final : public ServiceFramework<IDsInterface> {
|
||||
public:
|
||||
explicit IDsInterface() : ServiceFramework{"IDsInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetDsEndpoint"},
|
||||
{1, nullptr, "GetSetupEvent"},
|
||||
{2, nullptr, "Unknown"},
|
||||
{3, nullptr, "EnableInterface"},
|
||||
{4, nullptr, "DisableInterface"},
|
||||
{5, nullptr, "CtrlInPostBufferAsync"},
|
||||
{6, nullptr, "CtrlOutPostBufferAsync"},
|
||||
{7, nullptr, "GetCtrlInCompletionEvent"},
|
||||
{8, nullptr, "GetCtrlInReportData"},
|
||||
{9, nullptr, "GetCtrlOutCompletionEvent"},
|
||||
{10, nullptr, "GetCtrlOutReportData"},
|
||||
{11, nullptr, "StallCtrl"},
|
||||
{12, nullptr, "AppendConfigurationData"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class USB_DS final : public ServiceFramework<USB_DS> {
|
||||
public:
|
||||
explicit USB_DS() : ServiceFramework{"usb:ds"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "BindDevice"},
|
||||
{1, nullptr, "BindClientProcess"},
|
||||
{2, nullptr, "GetDsInterface"},
|
||||
{3, nullptr, "GetStateChangeEvent"},
|
||||
{4, nullptr, "GetState"},
|
||||
{5, nullptr, "ClearDeviceData"},
|
||||
{6, nullptr, "AddUsbStringDescriptor"},
|
||||
{7, nullptr, "DeleteUsbStringDescriptor"},
|
||||
{8, nullptr, "SetUsbDeviceDescriptor"},
|
||||
{9, nullptr, "SetBinaryObjectStore"},
|
||||
{10, nullptr, "Enable"},
|
||||
{11, nullptr, "Disable"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class IClientEpSession final : public ServiceFramework<IClientEpSession> {
|
||||
public:
|
||||
explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "PostBufferAsync"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class IClientIfSession final : public ServiceFramework<IClientIfSession> {
|
||||
public:
|
||||
explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "CtrlXferAsync"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "GetCtrlXferReport"},
|
||||
{8, nullptr, "Unknown7"},
|
||||
{9, nullptr, "GetClientEpSession"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class USB_HS final : public ServiceFramework<USB_HS> {
|
||||
public:
|
||||
explicit USB_HS() : ServiceFramework{"usb:hs"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "BindClientProcess"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "GetInterfaceStateChangeEvent"},
|
||||
{7, nullptr, "GetClientIfSession"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class IPdSession final : public ServiceFramework<IPdSession> {
|
||||
public:
|
||||
explicit IPdSession() : ServiceFramework{"IPdSession"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "BindNoticeEvent"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "GetStatus"},
|
||||
{3, nullptr, "GetNotice"},
|
||||
{4, nullptr, "Unknown2"},
|
||||
{5, nullptr, "Unknown3"},
|
||||
{6, nullptr, "ReplyPowerRequest"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class USB_PD final : public ServiceFramework<USB_PD> {
|
||||
public:
|
||||
explicit USB_PD() : ServiceFramework{"usb:pd"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &USB_PD::GetPdSession, "GetPdSession"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetPdSession(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IPdSession>();
|
||||
|
||||
LOG_DEBUG(Service_USB, "called");
|
||||
}
|
||||
};
|
||||
|
||||
class IPdCradleSession final : public ServiceFramework<IPdCradleSession> {
|
||||
public:
|
||||
explicit IPdCradleSession() : ServiceFramework{"IPdCradleSession"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "VdmUserWrite"},
|
||||
{1, nullptr, "VdmUserRead"},
|
||||
{2, nullptr, "Vdm20Init"},
|
||||
{3, nullptr, "GetFwType"},
|
||||
{4, nullptr, "GetFwRevision"},
|
||||
{5, nullptr, "GetManufacturerId"},
|
||||
{6, nullptr, "GetDeviceId"},
|
||||
{7, nullptr, "Unknown1"},
|
||||
{8, nullptr, "Unknown2"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class USB_PD_C final : public ServiceFramework<USB_PD_C> {
|
||||
public:
|
||||
explicit USB_PD_C() : ServiceFramework{"usb:pd:c"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &USB_PD_C::GetPdCradleSession, "GetPdCradleSession"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetPdCradleSession(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IPdCradleSession>();
|
||||
|
||||
LOG_DEBUG(Service_USB, "called");
|
||||
}
|
||||
};
|
||||
|
||||
class USB_PM final : public ServiceFramework<USB_PM> {
|
||||
public:
|
||||
explicit USB_PM() : ServiceFramework{"usb:pm"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<USB_DS>()->InstallAsService(sm);
|
||||
std::make_shared<USB_HS>()->InstallAsService(sm);
|
||||
std::make_shared<USB_PD>()->InstallAsService(sm);
|
||||
std::make_shared<USB_PD_C>()->InstallAsService(sm);
|
||||
std::make_shared<USB_PM>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::USB
|
||||
15
src/core/hle/service/usb/usb.h
Normal file
15
src/core/hle/service/usb/usb.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::USB {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm);
|
||||
|
||||
} // namespace Service::USB
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
@@ -17,8 +18,50 @@
|
||||
|
||||
namespace Loader {
|
||||
|
||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file)
|
||||
: AppLoader(std::move(file)) {}
|
||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_)
|
||||
: AppLoader(std::move(file_)) {
|
||||
const auto dir = file->GetContainingDirectory();
|
||||
|
||||
// Icon
|
||||
FileSys::VirtualFile icon_file = nullptr;
|
||||
for (const auto& language : FileSys::LANGUAGE_NAMES) {
|
||||
icon_file = dir->GetFile("icon_" + std::string(language) + ".dat");
|
||||
if (icon_file != nullptr) {
|
||||
icon_data = icon_file->ReadAllBytes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (icon_data.empty()) {
|
||||
// Any png, jpeg, or bmp file
|
||||
const auto& files = dir->GetFiles();
|
||||
const auto icon_iter =
|
||||
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
|
||||
return file->GetExtension() == "png" || file->GetExtension() == "jpg" ||
|
||||
file->GetExtension() == "bmp" || file->GetExtension() == "jpeg";
|
||||
});
|
||||
if (icon_iter != files.end())
|
||||
icon_data = (*icon_iter)->ReadAllBytes();
|
||||
}
|
||||
|
||||
// Metadata
|
||||
FileSys::VirtualFile nacp_file = dir->GetFile("control.nacp");
|
||||
if (nacp_file == nullptr) {
|
||||
const auto& files = dir->GetFiles();
|
||||
const auto nacp_iter =
|
||||
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
|
||||
return file->GetExtension() == "nacp";
|
||||
});
|
||||
if (nacp_iter != files.end())
|
||||
nacp_file = *nacp_iter;
|
||||
}
|
||||
|
||||
if (nacp_file != nullptr) {
|
||||
FileSys::NACP nacp(nacp_file);
|
||||
title_id = nacp.GetTitleId();
|
||||
name = nacp.GetApplicationName();
|
||||
}
|
||||
}
|
||||
|
||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
|
||||
FileSys::VirtualDir directory)
|
||||
@@ -40,13 +83,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
|
||||
|
||||
if (dir == nullptr) {
|
||||
if (file == nullptr)
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
const FileSys::VirtualDir dir = file->GetContainingDirectory();
|
||||
return ResultStatus::ErrorNullFile;
|
||||
dir = file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
const FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
|
||||
if (npdm == nullptr)
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorMissingNPDM;
|
||||
|
||||
ResultStatus result = metadata.Load(npdm);
|
||||
if (result != ResultStatus::Success) {
|
||||
@@ -56,7 +99,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
|
||||
|
||||
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
|
||||
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
|
||||
return ResultStatus::ErrorUnsupportedArch;
|
||||
return ResultStatus::Error32BitISA;
|
||||
}
|
||||
|
||||
// Load NSO modules
|
||||
@@ -100,9 +143,30 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
if (romfs == nullptr)
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
dir = romfs;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_data.empty())
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
buffer = icon_data;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
|
||||
if (name.empty())
|
||||
return ResultStatus::ErrorNoControl;
|
||||
out_program_id = title_id;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) {
|
||||
if (name.empty())
|
||||
return ResultStatus::ErrorNoControl;
|
||||
title = name;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
@@ -39,11 +39,18 @@ public:
|
||||
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
|
||||
private:
|
||||
FileSys::ProgramMetadata metadata;
|
||||
FileSys::VirtualFile romfs;
|
||||
FileSys::VirtualDir dir;
|
||||
|
||||
std::vector<u8> icon_data;
|
||||
std::string name;
|
||||
u64 title_id{};
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
@@ -390,7 +390,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
|
||||
std::vector<u8> buffer = file->ReadAllBytes();
|
||||
if (buffer.size() != file->GetSize())
|
||||
return ResultStatus::Error;
|
||||
return ResultStatus::ErrorIncorrectELFFileSize;
|
||||
|
||||
ElfReader elf_reader(&buffer[0]);
|
||||
SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
|
||||
|
||||
@@ -43,10 +43,6 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
|
||||
return FileType::Unknown;
|
||||
}
|
||||
|
||||
FileType IdentifyFile(const std::string& file_name) {
|
||||
return IdentifyFile(std::make_shared<FileSys::RealVfsFile>(file_name));
|
||||
}
|
||||
|
||||
FileType GuessFromFilename(const std::string& name) {
|
||||
if (name == "main")
|
||||
return FileType::DeconstructedRomDirectory;
|
||||
@@ -68,7 +64,7 @@ FileType GuessFromFilename(const std::string& name) {
|
||||
return FileType::Unknown;
|
||||
}
|
||||
|
||||
const char* GetFileTypeString(FileType type) {
|
||||
std::string GetFileTypeString(FileType type) {
|
||||
switch (type) {
|
||||
case FileType::ELF:
|
||||
return "ELF";
|
||||
@@ -90,6 +86,55 @@ const char* GetFileTypeString(FileType type) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
constexpr std::array<const char*, 36> RESULT_MESSAGES{
|
||||
"The operation completed successfully.",
|
||||
"The loader requested to load is already loaded.",
|
||||
"The operation is not implemented.",
|
||||
"The loader is not initialized properly.",
|
||||
"The NPDM file has a bad header.",
|
||||
"The NPDM has a bad ACID header.",
|
||||
"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 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.",
|
||||
"The general keyfile could not be found.",
|
||||
"The NCA Header key could not be found.",
|
||||
"The NCA Header key is incorrect or the header is invalid.",
|
||||
"Support for NCA2-type NCAs is not implemented.",
|
||||
"Support for NCA0-type NCAs is not implemented.",
|
||||
"The titlekey for this Rights ID could not be found.",
|
||||
"The titlekek for this crypto revision could not be found.",
|
||||
"The Rights ID in the header is invalid.",
|
||||
"The key area key for this application type and crypto revision could not be found.",
|
||||
"The key area key is incorrect or the section header is invalid.",
|
||||
"The titlekey and/or titlekek is incorrect or the section header is invalid.",
|
||||
"The XCI file is missing a Program-type NCA.",
|
||||
"The NCA file is not an application.",
|
||||
"The ExeFS partition could not be found.",
|
||||
"The XCI file has a bad header.",
|
||||
"The XCI file is missing a partition.",
|
||||
"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.",
|
||||
"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.",
|
||||
"There is no icon available.",
|
||||
"There is no control data available.",
|
||||
};
|
||||
|
||||
std::string GetMessageForResultStatus(ResultStatus status) {
|
||||
return GetMessageForResultStatus(static_cast<size_t>(status));
|
||||
}
|
||||
|
||||
std::string GetMessageForResultStatus(u16 status) {
|
||||
if (status >= 36)
|
||||
return "";
|
||||
return RESULT_MESSAGES[status];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a loader for a file with a specific type
|
||||
* @param file The file to load
|
||||
|
||||
@@ -42,14 +42,6 @@ enum class FileType {
|
||||
*/
|
||||
FileType IdentifyFile(FileSys::VirtualFile file);
|
||||
|
||||
/**
|
||||
* Identifies the type of a bootable file based on the magic value in its header.
|
||||
* @param file_name path to file
|
||||
* @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine
|
||||
* a filetype, and will never return FileType::Error.
|
||||
*/
|
||||
FileType IdentifyFile(const std::string& file_name);
|
||||
|
||||
/**
|
||||
* Guess the type of a bootable file from its name
|
||||
* @param name String name of bootable file
|
||||
@@ -61,23 +53,51 @@ FileType GuessFromFilename(const std::string& name);
|
||||
/**
|
||||
* Convert a FileType into a string which can be displayed to the user.
|
||||
*/
|
||||
const char* GetFileTypeString(FileType type);
|
||||
std::string GetFileTypeString(FileType type);
|
||||
|
||||
/// Return type for functions in Loader namespace
|
||||
enum class ResultStatus {
|
||||
Success,
|
||||
Error,
|
||||
ErrorInvalidFormat,
|
||||
ErrorNotImplemented,
|
||||
ErrorNotLoaded,
|
||||
ErrorNotUsed,
|
||||
ErrorAlreadyLoaded,
|
||||
ErrorMemoryAllocationFailed,
|
||||
ErrorMissingKeys,
|
||||
ErrorDecrypting,
|
||||
ErrorUnsupportedArch,
|
||||
ErrorNotImplemented,
|
||||
ErrorNotInitialized,
|
||||
ErrorBadNPDMHeader,
|
||||
ErrorBadACIDHeader,
|
||||
ErrorBadACIHeader,
|
||||
ErrorBadFileAccessControl,
|
||||
ErrorBadFileAccessHeader,
|
||||
ErrorBadPFSHeader,
|
||||
ErrorIncorrectPFSFileSize,
|
||||
ErrorBadNCAHeader,
|
||||
ErrorMissingProductionKeyFile,
|
||||
ErrorMissingHeaderKey,
|
||||
ErrorIncorrectHeaderKey,
|
||||
ErrorNCA2,
|
||||
ErrorNCA0,
|
||||
ErrorMissingTitlekey,
|
||||
ErrorMissingTitlekek,
|
||||
ErrorInvalidRightsID,
|
||||
ErrorMissingKeyAreaKey,
|
||||
ErrorIncorrectKeyAreaKey,
|
||||
ErrorIncorrectTitlekeyOrTitlekek,
|
||||
ErrorXCIMissingProgramNCA,
|
||||
ErrorNCANotProgram,
|
||||
ErrorNoExeFS,
|
||||
ErrorBadXCIHeader,
|
||||
ErrorXCIMissingPartition,
|
||||
ErrorNullFile,
|
||||
ErrorMissingNPDM,
|
||||
Error32BitISA,
|
||||
ErrorNoRomFS,
|
||||
ErrorIncorrectELFFileSize,
|
||||
ErrorLoadingNRO,
|
||||
ErrorNoIcon,
|
||||
ErrorNoControl,
|
||||
};
|
||||
|
||||
std::string GetMessageForResultStatus(ResultStatus status);
|
||||
std::string GetMessageForResultStatus(u16 status);
|
||||
|
||||
/// Interface for loading an application
|
||||
class AppLoader : NonCopyable {
|
||||
public:
|
||||
|
||||
@@ -46,12 +46,12 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
}
|
||||
|
||||
if (nca->GetType() != FileSys::NCAContentType::Program)
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorNCANotProgram;
|
||||
|
||||
const auto exefs = nca->GetExeFS();
|
||||
|
||||
if (exefs == nullptr)
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorNoExeFS;
|
||||
|
||||
directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs);
|
||||
|
||||
@@ -69,16 +69,16 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
if (nca == nullptr)
|
||||
return ResultStatus::ErrorNotLoaded;
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
if (nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0)
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
dir = nca->GetRomFS();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
|
||||
if (nca == nullptr)
|
||||
return ResultStatus::ErrorNotLoaded;
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
out_program_id = nca->GetTitleId();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ public:
|
||||
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
|
||||
~AppLoader_NCA();
|
||||
@@ -41,6 +40,7 @@ public:
|
||||
private:
|
||||
FileSys::ProgramMetadata metadata;
|
||||
|
||||
FileSys::NCAHeader header;
|
||||
std::unique_ptr<FileSys::NCA> nca;
|
||||
std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
|
||||
};
|
||||
|
||||
@@ -182,7 +182,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR};
|
||||
|
||||
if (!LoadNro(file, base_addr)) {
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorLoadingNRO;
|
||||
}
|
||||
|
||||
process->svc_access_mask.set();
|
||||
@@ -197,7 +197,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_data.empty()) {
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
}
|
||||
|
||||
buffer = icon_data;
|
||||
@@ -206,7 +206,7 @@ ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) {
|
||||
if (nacp == nullptr) {
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
out_program_id = nacp->GetTitleId();
|
||||
@@ -215,7 +215,7 @@ ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) {
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
if (romfs == nullptr) {
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
}
|
||||
|
||||
dir = romfs;
|
||||
@@ -224,7 +224,7 @@ ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadTitle(std::string& title) {
|
||||
if (nacp == nullptr) {
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
title = nacp->GetApplicationName();
|
||||
|
||||
@@ -26,7 +26,25 @@ namespace Loader {
|
||||
AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
|
||||
: AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
|
||||
nca_loader(std::make_unique<AppLoader_NCA>(
|
||||
xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {}
|
||||
xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {
|
||||
if (xci->GetStatus() != ResultStatus::Success)
|
||||
return;
|
||||
const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
|
||||
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
|
||||
return;
|
||||
const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS());
|
||||
if (romfs == nullptr)
|
||||
return;
|
||||
for (const auto& language : FileSys::LANGUAGE_NAMES) {
|
||||
icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat");
|
||||
if (icon_file != nullptr)
|
||||
break;
|
||||
}
|
||||
const auto nacp_raw = romfs->GetFile("control.nacp");
|
||||
if (nacp_raw == nullptr)
|
||||
return;
|
||||
nacp_file = std::make_shared<FileSys::NACP>(nacp_raw);
|
||||
}
|
||||
|
||||
AppLoader_XCI::~AppLoader_XCI() = default;
|
||||
|
||||
@@ -48,10 +66,13 @@ ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
return ResultStatus::ErrorAlreadyLoaded;
|
||||
}
|
||||
|
||||
if (xci->GetStatus() != ResultStatus::Success)
|
||||
return xci->GetStatus();
|
||||
|
||||
if (xci->GetNCAFileByType(FileSys::NCAContentType::Program) == nullptr) {
|
||||
if (!Core::Crypto::KeyManager::KeyFileExists(false))
|
||||
return ResultStatus::ErrorMissingKeys;
|
||||
return ResultStatus::ErrorDecrypting;
|
||||
return ResultStatus::ErrorMissingProductionKeyFile;
|
||||
return ResultStatus::ErrorXCIMissingProgramNCA;
|
||||
}
|
||||
|
||||
auto result = nca_loader->Load(process);
|
||||
@@ -71,4 +92,17 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
|
||||
return nca_loader->ReadProgramId(out_program_id);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_file == nullptr)
|
||||
return ResultStatus::ErrorNoControl;
|
||||
buffer = icon_file->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
|
||||
if (nacp_file == nullptr)
|
||||
return ResultStatus::ErrorNoControl;
|
||||
title = nacp_file->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
} // namespace Loader
|
||||
|
||||
@@ -33,12 +33,17 @@ public:
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
|
||||
private:
|
||||
FileSys::ProgramMetadata metadata;
|
||||
|
||||
std::unique_ptr<FileSys::XCI> xci;
|
||||
std::unique_ptr<AppLoader_NCA> nca_loader;
|
||||
|
||||
FileSys::VirtualFile icon_file;
|
||||
std::shared_ptr<FileSys::NACP> nacp_file;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
@@ -7,22 +7,18 @@
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
Values values = {};
|
||||
|
||||
void Apply() {
|
||||
|
||||
GDBStub::SetServerPort(values.gdbstub_port);
|
||||
GDBStub::ToggleServer(values.use_gdbstub);
|
||||
|
||||
VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit;
|
||||
|
||||
auto& system_instance = Core::System::GetInstance();
|
||||
if (system_instance.IsPoweredOn()) {
|
||||
system_instance.Renderer().UpdateCurrentFramebufferLayout();
|
||||
system_instance.Renderer().RefreshBaseSettings();
|
||||
}
|
||||
|
||||
Service::HID::ReloadInputDevices();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <catch.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <math.h>
|
||||
#include "common/param_package.h"
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <catch.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <catch.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <glad/glad.h>
|
||||
|
||||
// This is not an actual test, but a work-around for issue #2183.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
// Catch provides the main function since we've given it the
|
||||
// CATCH_CONFIG_MAIN preprocessor directive.
|
||||
|
||||
@@ -29,10 +29,10 @@ enum class BufferMethods {
|
||||
};
|
||||
|
||||
void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) {
|
||||
LOG_WARNING(HW_GPU,
|
||||
"Processing method {:08X} on subchannel {} value "
|
||||
"{:08X} remaining params {}",
|
||||
method, subchannel, value, remaining_params);
|
||||
LOG_TRACE(HW_GPU,
|
||||
"Processing method {:08X} on subchannel {} value "
|
||||
"{:08X} remaining params {}",
|
||||
method, subchannel, value, remaining_params);
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::BindObject)) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
|
||||
@@ -30,8 +30,7 @@ union CommandHeader {
|
||||
|
||||
BitField<29, 3, SubmissionMode> mode;
|
||||
};
|
||||
static_assert(std::is_standard_layout<CommandHeader>::value == true,
|
||||
"CommandHeader does not use standard layout");
|
||||
static_assert(std::is_standard_layout_v<CommandHeader>, "CommandHeader is not standard layout");
|
||||
static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -23,12 +23,17 @@ Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager&
|
||||
: memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
|
||||
auto macro_code = uploaded_macros.find(method);
|
||||
// The requested macro must have been uploaded already.
|
||||
ASSERT_MSG(macro_code != uploaded_macros.end(), "Macro %08X was not uploaded", method);
|
||||
|
||||
// Reset the current macro and execute it.
|
||||
// Reset the current macro.
|
||||
executing_macro = 0;
|
||||
|
||||
// The requested macro must have been uploaded already.
|
||||
auto macro_code = uploaded_macros.find(method);
|
||||
if (macro_code == uploaded_macros.end()) {
|
||||
LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method);
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute the current macro.
|
||||
macro_interpreter.Execute(macro_code->second, std::move(parameters));
|
||||
}
|
||||
|
||||
@@ -238,6 +243,8 @@ void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
|
||||
|
||||
auto& buffer = shader.const_buffers[bind_data.index];
|
||||
|
||||
ASSERT(bind_data.index < Regs::MaxConstBuffers);
|
||||
|
||||
buffer.enabled = bind_data.valid.Value() != 0;
|
||||
buffer.index = bind_data.index;
|
||||
buffer.address = regs.const_buffer.BufferAddress();
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
static constexpr size_t MaxShaderProgram = 6;
|
||||
static constexpr size_t MaxShaderStage = 5;
|
||||
// Maximum number of const buffers per shader stage.
|
||||
static constexpr size_t MaxConstBuffers = 16;
|
||||
static constexpr size_t MaxConstBuffers = 18;
|
||||
|
||||
enum class QueryMode : u32 {
|
||||
Write = 0,
|
||||
@@ -93,6 +93,7 @@ public:
|
||||
|
||||
struct VertexAttribute {
|
||||
enum class Size : u32 {
|
||||
Invalid = 0x0,
|
||||
Size_32_32_32_32 = 0x01,
|
||||
Size_32_32_32 = 0x02,
|
||||
Size_16_16_16_16 = 0x03,
|
||||
@@ -257,6 +258,10 @@ public:
|
||||
bool IsNormalized() const {
|
||||
return (type == Type::SignedNorm) || (type == Type::UnsignedNorm);
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
return size != Size::Invalid;
|
||||
}
|
||||
};
|
||||
|
||||
enum class PrimitiveTopology : u32 {
|
||||
@@ -352,6 +357,27 @@ public:
|
||||
OneMinusConstantColor = 0x62,
|
||||
ConstantAlpha = 0x63,
|
||||
OneMinusConstantAlpha = 0x64,
|
||||
|
||||
// These values are used by Nouveau and some games.
|
||||
ZeroGL = 0x4000,
|
||||
OneGL = 0x4001,
|
||||
SourceColorGL = 0x4300,
|
||||
OneMinusSourceColorGL = 0x4301,
|
||||
SourceAlphaGL = 0x4302,
|
||||
OneMinusSourceAlphaGL = 0x4303,
|
||||
DestAlphaGL = 0x4304,
|
||||
OneMinusDestAlphaGL = 0x4305,
|
||||
DestColorGL = 0x4306,
|
||||
OneMinusDestColorGL = 0x4307,
|
||||
SourceAlphaSaturateGL = 0x4308,
|
||||
ConstantColorGL = 0xc001,
|
||||
OneMinusConstantColorGL = 0xc002,
|
||||
ConstantAlphaGL = 0xc003,
|
||||
OneMinusConstantAlphaGL = 0xc004,
|
||||
Source1ColorGL = 0xc900,
|
||||
OneMinusSource1ColorGL = 0xc901,
|
||||
Source1AlphaGL = 0xc902,
|
||||
OneMinusSource1AlphaGL = 0xc903,
|
||||
};
|
||||
|
||||
u32 separate_alpha;
|
||||
|
||||
@@ -74,6 +74,7 @@ union Attribute {
|
||||
enum class Index : u64 {
|
||||
Position = 7,
|
||||
Attribute_0 = 8,
|
||||
Attribute_31 = 39,
|
||||
// This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex
|
||||
// shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
|
||||
// shader.
|
||||
@@ -254,20 +255,15 @@ union Instruction {
|
||||
BitField<56, 1, u64> invert_b;
|
||||
} lop32i;
|
||||
|
||||
float GetImm20_19() const {
|
||||
float result{};
|
||||
u32 GetImm20_19() const {
|
||||
u32 imm{static_cast<u32>(imm20_19)};
|
||||
imm <<= 12;
|
||||
imm |= negate_imm ? 0x80000000 : 0;
|
||||
std::memcpy(&result, &imm, sizeof(imm));
|
||||
return result;
|
||||
return imm;
|
||||
}
|
||||
|
||||
float GetImm20_32() const {
|
||||
float result{};
|
||||
s32 imm{static_cast<s32>(imm20_32)};
|
||||
std::memcpy(&result, &imm, sizeof(imm));
|
||||
return result;
|
||||
u32 GetImm20_32() const {
|
||||
return static_cast<u32>(imm20_32);
|
||||
}
|
||||
|
||||
s32 GetSignedImm20_20() const {
|
||||
@@ -480,8 +476,7 @@ union Instruction {
|
||||
u64 value;
|
||||
};
|
||||
static_assert(sizeof(Instruction) == 0x8, "Incorrect structure size");
|
||||
static_assert(std::is_standard_layout<Instruction>::value,
|
||||
"Structure does not have standard layout");
|
||||
static_assert(std::is_standard_layout_v<Instruction>, "Instruction is not standard layout");
|
||||
|
||||
class OpCode {
|
||||
public:
|
||||
@@ -601,6 +596,13 @@ public:
|
||||
Unknown,
|
||||
};
|
||||
|
||||
/// Returns whether an opcode has an execution predicate field or not (ie, whether it can be
|
||||
/// conditionally executed).
|
||||
static bool IsPredicatedInstruction(Id opcode) {
|
||||
// TODO(Subv): Add the rest of unpredicated instructions.
|
||||
return opcode != Id::SSY;
|
||||
}
|
||||
|
||||
class Matcher {
|
||||
public:
|
||||
Matcher(const char* const name, u16 mask, u16 expected, OpCode::Id id, OpCode::Type type)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
@@ -11,6 +12,15 @@
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
return 4;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
|
||||
memory_manager = std::make_unique<MemoryManager>();
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
|
||||
@@ -34,18 +44,54 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
|
||||
|
||||
switch (format) {
|
||||
case RenderTargetFormat::RGBA32_FLOAT:
|
||||
case RenderTargetFormat::RGBA32_UINT:
|
||||
return 16;
|
||||
case RenderTargetFormat::RGBA16_FLOAT:
|
||||
case RenderTargetFormat::RG32_FLOAT:
|
||||
return 8;
|
||||
case RenderTargetFormat::RGBA8_UNORM:
|
||||
case RenderTargetFormat::RGBA8_SNORM:
|
||||
case RenderTargetFormat::RGBA8_SRGB:
|
||||
case RenderTargetFormat::RGB10_A2_UNORM:
|
||||
case RenderTargetFormat::BGRA8_UNORM:
|
||||
case RenderTargetFormat::RG16_UNORM:
|
||||
case RenderTargetFormat::RG16_SNORM:
|
||||
case RenderTargetFormat::RG16_UINT:
|
||||
case RenderTargetFormat::RG16_SINT:
|
||||
case RenderTargetFormat::RG16_FLOAT:
|
||||
case RenderTargetFormat::R32_FLOAT:
|
||||
case RenderTargetFormat::R11G11B10_FLOAT:
|
||||
return 4;
|
||||
case RenderTargetFormat::R16_UNORM:
|
||||
case RenderTargetFormat::R16_SNORM:
|
||||
case RenderTargetFormat::R16_UINT:
|
||||
case RenderTargetFormat::R16_SINT:
|
||||
case RenderTargetFormat::R16_FLOAT:
|
||||
case RenderTargetFormat::RG8_SNORM:
|
||||
return 2;
|
||||
case RenderTargetFormat::R8_UNORM:
|
||||
case RenderTargetFormat::R8_UINT:
|
||||
return 1;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
|
||||
}
|
||||
}
|
||||
|
||||
u32 DepthFormatBytesPerPixel(DepthFormat format) {
|
||||
switch (format) {
|
||||
case DepthFormat::Z32_S8_X24_FLOAT:
|
||||
return 8;
|
||||
case DepthFormat::Z32_FLOAT:
|
||||
case DepthFormat::S8_Z24_UNORM:
|
||||
case DepthFormat::Z24_X8_UNORM:
|
||||
case DepthFormat::Z24_S8_UNORM:
|
||||
case DepthFormat::Z24_C8_UNORM:
|
||||
return 4;
|
||||
case DepthFormat::Z16_UNORM:
|
||||
return 2;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast<u32>(format));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
@@ -27,6 +26,7 @@ enum class RenderTargetFormat : u32 {
|
||||
RGB10_A2_UNORM = 0xD1,
|
||||
RGBA8_UNORM = 0xD5,
|
||||
RGBA8_SRGB = 0xD6,
|
||||
RGBA8_SNORM = 0xD7,
|
||||
RG16_UNORM = 0xDA,
|
||||
RG16_SNORM = 0xDB,
|
||||
RG16_SINT = 0xDC,
|
||||
@@ -34,8 +34,15 @@ enum class RenderTargetFormat : u32 {
|
||||
RG16_FLOAT = 0xDE,
|
||||
R11G11B10_FLOAT = 0xE0,
|
||||
R32_FLOAT = 0xE5,
|
||||
B5G6R5_UNORM = 0xE8,
|
||||
RG8_SNORM = 0xEB,
|
||||
R16_UNORM = 0xEE,
|
||||
R16_SNORM = 0xEF,
|
||||
R16_SINT = 0xF0,
|
||||
R16_UINT = 0xF1,
|
||||
R16_FLOAT = 0xF2,
|
||||
R8_UNORM = 0xF3,
|
||||
R8_UINT = 0xF6,
|
||||
};
|
||||
|
||||
enum class DepthFormat : u32 {
|
||||
@@ -51,6 +58,9 @@ enum class DepthFormat : u32 {
|
||||
/// Returns the number of bytes per pixel of each rendertarget format.
|
||||
u32 RenderTargetBytesPerPixel(RenderTargetFormat format);
|
||||
|
||||
/// Returns the number of bytes per pixel of each depth format.
|
||||
u32 DepthFormatBytesPerPixel(DepthFormat format);
|
||||
|
||||
class DebugContext;
|
||||
|
||||
/**
|
||||
@@ -64,14 +74,7 @@ struct FramebufferConfig {
|
||||
/**
|
||||
* Returns the number of bytes per pixel.
|
||||
*/
|
||||
static u32 BytesPerPixel(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
return 4;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
static u32 BytesPerPixel(PixelFormat format);
|
||||
|
||||
VAddr address;
|
||||
u32 offset;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user