summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar David Marcec2018-08-11 10:35:47 +1000
committerGravatar David Marcec2018-08-11 10:35:47 +1000
commitb76ddb7647cbb390cce4143d91a1db171b0fa503 (patch)
treea6e2e334e82b035923c41458150604dd5fb31d65 /src
parentAdded IsUserRegistrationRequestPermitted (diff)
parentMerge pull request #1007 from MerryMage/dynarmic (diff)
downloadyuzu-b76ddb7647cbb390cce4143d91a1db171b0fa503.tar.gz
yuzu-b76ddb7647cbb390cce4143d91a1db171b0fa503.tar.xz
yuzu-b76ddb7647cbb390cce4143d91a1db171b0fa503.zip
Merge remote-tracking branch 'origin/master' into better-account
Diffstat (limited to 'src')
-rw-r--r--src/common/alignment.h4
-rw-r--r--src/common/bit_set.h2
-rw-r--r--src/common/color.h50
-rw-r--r--src/common/file_util.cpp16
-rw-r--r--src/common/file_util.h18
-rw-r--r--src/common/hash.h4
-rw-r--r--src/common/logging/backend.cpp8
-rw-r--r--src/common/logging/log.h8
-rw-r--r--src/common/vector_math.h362
-rw-r--r--src/common/x64/xbyak_util.h2
-rw-r--r--src/core/core.cpp8
-rw-r--r--src/core/core.h12
-rw-r--r--src/core/file_sys/card_image.cpp1
-rw-r--r--src/core/file_sys/content_archive.cpp4
-rw-r--r--src/core/file_sys/content_archive.h1
-rw-r--r--src/core/file_sys/control_metadata.h7
-rw-r--r--src/core/file_sys/directory.h12
-rw-r--r--src/core/file_sys/savedata_factory.h1
-rw-r--r--src/core/file_sys/vfs.cpp148
-rw-r--r--src/core/file_sys/vfs.h66
-rw-r--r--src/core/file_sys/vfs_real.cpp341
-rw-r--r--src/core/file_sys/vfs_real.h56
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp2
-rw-r--r--src/core/hle/kernel/hle_ipc.h2
-rw-r--r--src/core/hle/service/acc/acc.cpp23
-rw-r--r--src/core/hle/service/am/am.cpp9
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp16
-rw-r--r--src/core/hle/service/filesystem/filesystem.h2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp9
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp7
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h8
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp7
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h9
-rw-r--r--src/core/hle/service/service.cpp6
-rw-r--r--src/core/hle/service/service.h7
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp68
-rw-r--r--src/core/loader/deconstructed_rom_directory.h7
-rw-r--r--src/core/loader/loader.cpp6
-rw-r--r--src/core/loader/loader.h10
-rw-r--r--src/core/loader/nca.cpp4
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/loader/xci.cpp33
-rw-r--r--src/core/loader/xci.h5
-rw-r--r--src/video_core/engines/maxwell_3d.cpp15
-rw-r--r--src/video_core/engines/maxwell_3d.h2
-rw-r--r--src/video_core/engines/shader_bytecode.h15
-rw-r--r--src/video_core/gpu.cpp1
-rw-r--r--src/video_core/gpu.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp82
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h147
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp22
-rw-r--r--src/video_core/renderer_opengl/gl_state.h6
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h7
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp2
-rw-r--r--src/video_core/textures/decoders.cpp86
-rw-r--r--src/video_core/textures/decoders.h4
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/configuration/config.cpp14
-rw-r--r--src/yuzu/configuration/configure.ui11
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp1
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp63
-rw-r--r--src/yuzu/configuration/configure_gamelist.h28
-rw-r--r--src/yuzu/configuration/configure_gamelist.ui126
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp6
-rw-r--r--src/yuzu/game_list.cpp86
-rw-r--r--src/yuzu/game_list.h3
-rw-r--r--src/yuzu/game_list_p.h43
-rw-r--r--src/yuzu/main.cpp9
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu/ui_settings.h6
-rw-r--r--src/yuzu_cmd/yuzu.cpp1
76 files changed, 1562 insertions, 634 deletions
diff --git a/src/common/alignment.h b/src/common/alignment.h
index b77da4a92..b9dd38746 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -9,13 +9,13 @@ namespace Common {
9 9
10template <typename T> 10template <typename T>
11constexpr T AlignUp(T value, size_t size) { 11constexpr T AlignUp(T value, size_t size) {
12 static_assert(std::is_unsigned<T>::value, "T must be an unsigned value."); 12 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
13 return static_cast<T>(value + (size - value % size) % size); 13 return static_cast<T>(value + (size - value % size) % size);
14} 14}
15 15
16template <typename T> 16template <typename T>
17constexpr T AlignDown(T value, size_t size) { 17constexpr T AlignDown(T value, size_t size) {
18 static_assert(std::is_unsigned<T>::value, "T must be an unsigned value."); 18 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
19 return static_cast<T>(value - value % size); 19 return static_cast<T>(value - value % size);
20} 20}
21 21
diff --git a/src/common/bit_set.h b/src/common/bit_set.h
index 84e3cbe58..5a197d8c1 100644
--- a/src/common/bit_set.h
+++ b/src/common/bit_set.h
@@ -96,7 +96,7 @@ static inline int LeastSignificantSetBit(u64 val) {
96 96
97template <typename IntTy> 97template <typename IntTy>
98class BitSet { 98class BitSet {
99 static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types"); 99 static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types");
100 100
101public: 101public:
102 // A reference to a particular bit, returned from operator[]. 102 // A reference to a particular bit, returned from operator[].
diff --git a/src/common/color.h b/src/common/color.h
index 24a445dac..0379040be 100644
--- a/src/common/color.h
+++ b/src/common/color.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstring>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "common/swap.h" 10#include "common/swap.h"
9#include "common/vector_math.h" 11#include "common/vector_math.h"
@@ -55,7 +57,7 @@ constexpr u8 Convert8To6(u8 value) {
55 * @param bytes Pointer to encoded source color 57 * @param bytes Pointer to encoded source color
56 * @return Result color decoded as Math::Vec4<u8> 58 * @return Result color decoded as Math::Vec4<u8>
57 */ 59 */
58inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) { 60inline Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
59 return {bytes[3], bytes[2], bytes[1], bytes[0]}; 61 return {bytes[3], bytes[2], bytes[1], bytes[0]};
60} 62}
61 63
@@ -64,7 +66,7 @@ inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
64 * @param bytes Pointer to encoded source color 66 * @param bytes Pointer to encoded source color
65 * @return Result color decoded as Math::Vec4<u8> 67 * @return Result color decoded as Math::Vec4<u8>
66 */ 68 */
67inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) { 69inline Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
68 return {bytes[2], bytes[1], bytes[0], 255}; 70 return {bytes[2], bytes[1], bytes[0], 255};
69} 71}
70 72
@@ -73,7 +75,7 @@ inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
73 * @param bytes Pointer to encoded source color 75 * @param bytes Pointer to encoded source color
74 * @return Result color decoded as Math::Vec4<u8> 76 * @return Result color decoded as Math::Vec4<u8>
75 */ 77 */
76inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) { 78inline Math::Vec4<u8> DecodeRG8(const u8* bytes) {
77 return {bytes[1], bytes[0], 0, 255}; 79 return {bytes[1], bytes[0], 0, 255};
78} 80}
79 81
@@ -82,8 +84,9 @@ inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
82 * @param bytes Pointer to encoded source color 84 * @param bytes Pointer to encoded source color
83 * @return Result color decoded as Math::Vec4<u8> 85 * @return Result color decoded as Math::Vec4<u8>
84 */ 86 */
85inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) { 87inline Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
86 const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); 88 u16_le pixel;
89 std::memcpy(&pixel, bytes, sizeof(pixel));
87 return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), 90 return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
88 Convert5To8(pixel & 0x1F), 255}; 91 Convert5To8(pixel & 0x1F), 255};
89} 92}
@@ -93,8 +96,9 @@ inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
93 * @param bytes Pointer to encoded source color 96 * @param bytes Pointer to encoded source color
94 * @return Result color decoded as Math::Vec4<u8> 97 * @return Result color decoded as Math::Vec4<u8>
95 */ 98 */
96inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) { 99inline Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
97 const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); 100 u16_le pixel;
101 std::memcpy(&pixel, bytes, sizeof(pixel));
98 return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), 102 return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
99 Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)}; 103 Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
100} 104}
@@ -104,8 +108,9 @@ inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
104 * @param bytes Pointer to encoded source color 108 * @param bytes Pointer to encoded source color
105 * @return Result color decoded as Math::Vec4<u8> 109 * @return Result color decoded as Math::Vec4<u8>
106 */ 110 */
107inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) { 111inline Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
108 const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); 112 u16_le pixel;
113 std::memcpy(&pixel, bytes, sizeof(pixel));
109 return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), 114 return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
110 Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)}; 115 Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
111} 116}
@@ -116,7 +121,9 @@ inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
116 * @return Depth value as an u32 121 * @return Depth value as an u32
117 */ 122 */
118inline u32 DecodeD16(const u8* bytes) { 123inline u32 DecodeD16(const u8* bytes) {
119 return *reinterpret_cast<const u16_le*>(bytes); 124 u16_le data;
125 std::memcpy(&data, bytes, sizeof(data));
126 return data;
120} 127}
121 128
122/** 129/**
@@ -133,7 +140,7 @@ inline u32 DecodeD24(const u8* bytes) {
133 * @param bytes Pointer to encoded source values 140 * @param bytes Pointer to encoded source values
134 * @return Resulting values stored as a Math::Vec2 141 * @return Resulting values stored as a Math::Vec2
135 */ 142 */
136inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) { 143inline Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
137 return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]}; 144 return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
138} 145}
139 146
@@ -175,8 +182,10 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) {
175 * @param bytes Destination pointer to store encoded color 182 * @param bytes Destination pointer to store encoded color
176 */ 183 */
177inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) { 184inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
178 *reinterpret_cast<u16_le*>(bytes) = 185 const u16_le data =
179 (Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b()); 186 (Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
187
188 std::memcpy(bytes, &data, sizeof(data));
180} 189}
181 190
182/** 191/**
@@ -185,9 +194,10 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
185 * @param bytes Destination pointer to store encoded color 194 * @param bytes Destination pointer to store encoded color
186 */ 195 */
187inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) { 196inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
188 *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) | 197 const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) |
189 (Convert8To5(color.g()) << 6) | 198 (Convert8To5(color.b()) << 1) | Convert8To1(color.a());
190 (Convert8To5(color.b()) << 1) | Convert8To1(color.a()); 199
200 std::memcpy(bytes, &data, sizeof(data));
191} 201}
192 202
193/** 203/**
@@ -196,9 +206,10 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
196 * @param bytes Destination pointer to store encoded color 206 * @param bytes Destination pointer to store encoded color
197 */ 207 */
198inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) { 208inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
199 *reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) | 209 const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) |
200 (Convert8To4(color.g()) << 8) | 210 (Convert8To4(color.b()) << 4) | Convert8To4(color.a());
201 (Convert8To4(color.b()) << 4) | Convert8To4(color.a()); 211
212 std::memcpy(bytes, &data, sizeof(data));
202} 213}
203 214
204/** 215/**
@@ -207,7 +218,8 @@ inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
207 * @param bytes Pointer where to store the encoded value 218 * @param bytes Pointer where to store the encoded value
208 */ 219 */
209inline void EncodeD16(u32 value, u8* bytes) { 220inline void EncodeD16(u32 value, u8* bytes) {
210 *reinterpret_cast<u16_le*>(bytes) = value & 0xFFFF; 221 const u16_le data = static_cast<u16>(value);
222 std::memcpy(bytes, &data, sizeof(data));
211} 223}
212 224
213/** 225/**
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 7aeda737f..3ce590062 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -884,11 +884,21 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
884 return path; 884 return path;
885} 885}
886 886
887std::string SanitizePath(std::string_view path_) { 887std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
888 std::string path(path_); 888 std::string path(path_);
889 std::replace(path.begin(), path.end(), '\\', '/'); 889 char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
890 char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
891
892 if (directory_separator == DirectorySeparator::PlatformDefault) {
893#ifdef _WIN32
894 type1 = '/';
895 type2 = '\\';
896#endif
897 }
898
899 std::replace(path.begin(), path.end(), type1, type2);
890 path.erase(std::unique(path.begin(), path.end(), 900 path.erase(std::unique(path.begin(), path.end(),
891 [](char c1, char c2) { return c1 == '/' && c2 == '/'; }), 901 [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
892 path.end()); 902 path.end());
893 return std::string(RemoveTrailingSlash(path)); 903 return std::string(RemoveTrailingSlash(path));
894} 904}
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 430dac41c..2711872ae 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -182,8 +182,12 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la
182 return std::vector<T>(vector.begin() + first, vector.begin() + first + last); 182 return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
183} 183}
184 184
185// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. 185enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault };
186std::string SanitizePath(std::string_view path); 186
187// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
188// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
189std::string SanitizePath(std::string_view path,
190 DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
187 191
188// simple wrapper for cstdlib file functions to 192// simple wrapper for cstdlib file functions to
189// hopefully will make error checking easier 193// hopefully will make error checking easier
@@ -208,7 +212,7 @@ public:
208 212
209 template <typename T> 213 template <typename T>
210 size_t ReadArray(T* data, size_t length) const { 214 size_t ReadArray(T* data, size_t length) const {
211 static_assert(std::is_trivially_copyable<T>(), 215 static_assert(std::is_trivially_copyable_v<T>,
212 "Given array does not consist of trivially copyable objects"); 216 "Given array does not consist of trivially copyable objects");
213 217
214 if (!IsOpen()) { 218 if (!IsOpen()) {
@@ -220,7 +224,7 @@ public:
220 224
221 template <typename T> 225 template <typename T>
222 size_t WriteArray(const T* data, size_t length) { 226 size_t WriteArray(const T* data, size_t length) {
223 static_assert(std::is_trivially_copyable<T>(), 227 static_assert(std::is_trivially_copyable_v<T>,
224 "Given array does not consist of trivially copyable objects"); 228 "Given array does not consist of trivially copyable objects");
225 if (!IsOpen()) { 229 if (!IsOpen()) {
226 return std::numeric_limits<size_t>::max(); 230 return std::numeric_limits<size_t>::max();
@@ -231,19 +235,19 @@ public:
231 235
232 template <typename T> 236 template <typename T>
233 size_t ReadBytes(T* data, size_t length) const { 237 size_t ReadBytes(T* data, size_t length) const {
234 static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable"); 238 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
235 return ReadArray(reinterpret_cast<char*>(data), length); 239 return ReadArray(reinterpret_cast<char*>(data), length);
236 } 240 }
237 241
238 template <typename T> 242 template <typename T>
239 size_t WriteBytes(const T* data, size_t length) { 243 size_t WriteBytes(const T* data, size_t length) {
240 static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable"); 244 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
241 return WriteArray(reinterpret_cast<const char*>(data), length); 245 return WriteArray(reinterpret_cast<const char*>(data), length);
242 } 246 }
243 247
244 template <typename T> 248 template <typename T>
245 size_t WriteObject(const T& object) { 249 size_t WriteObject(const T& object) {
246 static_assert(!std::is_pointer<T>::value, "Given object is a pointer"); 250 static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
247 return WriteArray(&object, 1); 251 return WriteArray(&object, 1);
248 } 252 }
249 253
diff --git a/src/common/hash.h b/src/common/hash.h
index 73c326980..2c761e545 100644
--- a/src/common/hash.h
+++ b/src/common/hash.h
@@ -28,7 +28,7 @@ static inline u64 ComputeHash64(const void* data, size_t len) {
28 */ 28 */
29template <typename T> 29template <typename T>
30static inline u64 ComputeStructHash64(const T& data) { 30static inline u64 ComputeStructHash64(const T& data) {
31 static_assert(std::is_trivially_copyable<T>(), 31 static_assert(std::is_trivially_copyable_v<T>,
32 "Type passed to ComputeStructHash64 must be trivially copyable"); 32 "Type passed to ComputeStructHash64 must be trivially copyable");
33 return ComputeHash64(&data, sizeof(data)); 33 return ComputeHash64(&data, sizeof(data));
34} 34}
@@ -38,7 +38,7 @@ template <typename T>
38struct HashableStruct { 38struct HashableStruct {
39 // In addition to being trivially copyable, T must also have a trivial default constructor, 39 // In addition to being trivially copyable, T must also have a trivial default constructor,
40 // because any member initialization would be overridden by memset 40 // because any member initialization would be overridden by memset
41 static_assert(std::is_trivial<T>(), "Type passed to HashableStruct must be trivial"); 41 static_assert(std::is_trivial_v<T>, "Type passed to HashableStruct must be trivial");
42 /* 42 /*
43 * We use a union because "implicitly-defined copy/move constructor for a union X copies the 43 * We use a union because "implicitly-defined copy/move constructor for a union X copies the
44 * object representation of X." and "implicitly-defined copy assignment operator for a union X 44 * object representation of X." and "implicitly-defined copy assignment operator for a union X
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 355abd682..e80784c3c 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -171,15 +171,21 @@ void FileBackend::Write(const Entry& entry) {
171 SUB(Service, ARP) \ 171 SUB(Service, ARP) \
172 SUB(Service, BCAT) \ 172 SUB(Service, BCAT) \
173 SUB(Service, BPC) \ 173 SUB(Service, BPC) \
174 SUB(Service, BTDRV) \
174 SUB(Service, BTM) \ 175 SUB(Service, BTM) \
175 SUB(Service, Capture) \ 176 SUB(Service, Capture) \
177 SUB(Service, ERPT) \
178 SUB(Service, ETicket) \
179 SUB(Service, EUPLD) \
176 SUB(Service, Fatal) \ 180 SUB(Service, Fatal) \
177 SUB(Service, FGM) \ 181 SUB(Service, FGM) \
178 SUB(Service, Friend) \ 182 SUB(Service, Friend) \
179 SUB(Service, FS) \ 183 SUB(Service, FS) \
184 SUB(Service, GRC) \
180 SUB(Service, HID) \ 185 SUB(Service, HID) \
181 SUB(Service, LBL) \ 186 SUB(Service, LBL) \
182 SUB(Service, LDN) \ 187 SUB(Service, LDN) \
188 SUB(Service, LDR) \
183 SUB(Service, LM) \ 189 SUB(Service, LM) \
184 SUB(Service, Migration) \ 190 SUB(Service, Migration) \
185 SUB(Service, Mii) \ 191 SUB(Service, Mii) \
@@ -188,11 +194,13 @@ void FileBackend::Write(const Entry& entry) {
188 SUB(Service, NFC) \ 194 SUB(Service, NFC) \
189 SUB(Service, NFP) \ 195 SUB(Service, NFP) \
190 SUB(Service, NIFM) \ 196 SUB(Service, NIFM) \
197 SUB(Service, NIM) \
191 SUB(Service, NS) \ 198 SUB(Service, NS) \
192 SUB(Service, NVDRV) \ 199 SUB(Service, NVDRV) \
193 SUB(Service, PCIE) \ 200 SUB(Service, PCIE) \
194 SUB(Service, PCTL) \ 201 SUB(Service, PCTL) \
195 SUB(Service, PCV) \ 202 SUB(Service, PCV) \
203 SUB(Service, PM) \
196 SUB(Service, PREPO) \ 204 SUB(Service, PREPO) \
197 SUB(Service, PSC) \ 205 SUB(Service, PSC) \
198 SUB(Service, SET) \ 206 SUB(Service, SET) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index a889ebefa..e12f47f8f 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -58,15 +58,21 @@ enum class Class : ClassType {
58 Service_Audio, ///< The Audio (Audio control) service 58 Service_Audio, ///< The Audio (Audio control) service
59 Service_BCAT, ///< The BCAT service 59 Service_BCAT, ///< The BCAT service
60 Service_BPC, ///< The BPC service 60 Service_BPC, ///< The BPC service
61 Service_BTDRV, ///< The Bluetooth driver service
61 Service_BTM, ///< The BTM service 62 Service_BTM, ///< The BTM service
62 Service_Capture, ///< The capture service 63 Service_Capture, ///< The capture service
64 Service_ERPT, ///< The error reporting service
65 Service_ETicket, ///< The ETicket service
66 Service_EUPLD, ///< The error upload service
63 Service_Fatal, ///< The Fatal service 67 Service_Fatal, ///< The Fatal service
64 Service_FGM, ///< The FGM service 68 Service_FGM, ///< The FGM service
65 Service_Friend, ///< The friend service 69 Service_Friend, ///< The friend service
66 Service_FS, ///< The FS (Filesystem) service 70 Service_FS, ///< The FS (Filesystem) service
71 Service_GRC, ///< The game recording service
67 Service_HID, ///< The HID (Human interface device) service 72 Service_HID, ///< The HID (Human interface device) service
68 Service_LBL, ///< The LBL (LCD backlight) service 73 Service_LBL, ///< The LBL (LCD backlight) service
69 Service_LDN, ///< The LDN (Local domain network) service 74 Service_LDN, ///< The LDN (Local domain network) service
75 Service_LDR, ///< The loader service
70 Service_LM, ///< The LM (Logger) service 76 Service_LM, ///< The LM (Logger) service
71 Service_Migration, ///< The migration service 77 Service_Migration, ///< The migration service
72 Service_Mii, ///< The Mii service 78 Service_Mii, ///< The Mii service
@@ -75,11 +81,13 @@ enum class Class : ClassType {
75 Service_NFC, ///< The NFC (Near-field communication) service 81 Service_NFC, ///< The NFC (Near-field communication) service
76 Service_NFP, ///< The NFP service 82 Service_NFP, ///< The NFP service
77 Service_NIFM, ///< The NIFM (Network interface) service 83 Service_NIFM, ///< The NIFM (Network interface) service
84 Service_NIM, ///< The NIM service
78 Service_NS, ///< The NS services 85 Service_NS, ///< The NS services
79 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 86 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
80 Service_PCIE, ///< The PCIe service 87 Service_PCIE, ///< The PCIe service
81 Service_PCTL, ///< The PCTL (Parental control) service 88 Service_PCTL, ///< The PCTL (Parental control) service
82 Service_PCV, ///< The PCV service 89 Service_PCV, ///< The PCV service
90 Service_PM, ///< The PM service
83 Service_PREPO, ///< The PREPO (Play report) service 91 Service_PREPO, ///< The PREPO (Play report) service
84 Service_PSC, ///< The PSC service 92 Service_PSC, ///< The PSC service
85 Service_SET, ///< The SET (Settings) service 93 Service_SET, ///< The SET (Settings) service
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index cca43bd4c..8feb49941 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -43,139 +43,135 @@ template <typename T>
43class Vec4; 43class Vec4;
44 44
45template <typename T> 45template <typename T>
46static inline Vec2<T> MakeVec(const T& x, const T& y);
47template <typename T>
48static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z);
49template <typename T>
50static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w);
51
52template <typename T>
53class Vec2 { 46class Vec2 {
54public: 47public:
55 T x{}; 48 T x{};
56 T y{}; 49 T y{};
57 50
58 Vec2() = default; 51 constexpr Vec2() = default;
59 Vec2(const T& _x, const T& _y) : x(_x), y(_y) {} 52 constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
60 53
61 template <typename T2> 54 template <typename T2>
62 Vec2<T2> Cast() const { 55 constexpr Vec2<T2> Cast() const {
63 return Vec2<T2>((T2)x, (T2)y); 56 return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
64 } 57 }
65 58
66 static Vec2 AssignToAll(const T& f) { 59 static constexpr Vec2 AssignToAll(const T& f) {
67 return Vec2<T>(f, f); 60 return Vec2{f, f};
68 } 61 }
69 62
70 Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const { 63 constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
71 return MakeVec(x + other.x, y + other.y); 64 return {x + other.x, y + other.y};
72 } 65 }
73 void operator+=(const Vec2& other) { 66 constexpr Vec2& operator+=(const Vec2& other) {
74 x += other.x; 67 x += other.x;
75 y += other.y; 68 y += other.y;
69 return *this;
76 } 70 }
77 Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const { 71 constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
78 return MakeVec(x - other.x, y - other.y); 72 return {x - other.x, y - other.y};
79 } 73 }
80 void operator-=(const Vec2& other) { 74 constexpr Vec2& operator-=(const Vec2& other) {
81 x -= other.x; 75 x -= other.x;
82 y -= other.y; 76 y -= other.y;
77 return *this;
83 } 78 }
84 79
85 template <typename U = T> 80 template <typename U = T>
86 Vec2<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const { 81 constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
87 return MakeVec(-x, -y); 82 return {-x, -y};
88 } 83 }
89 Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const { 84 constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
90 return MakeVec(x * other.x, y * other.y); 85 return {x * other.x, y * other.y};
91 } 86 }
87
92 template <typename V> 88 template <typename V>
93 Vec2<decltype(T{} * V{})> operator*(const V& f) const { 89 constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
94 return MakeVec(x * f, y * f); 90 return {x * f, y * f};
95 } 91 }
92
96 template <typename V> 93 template <typename V>
97 void operator*=(const V& f) { 94 constexpr Vec2& operator*=(const V& f) {
98 *this = *this * f; 95 *this = *this * f;
96 return *this;
99 } 97 }
98
100 template <typename V> 99 template <typename V>
101 Vec2<decltype(T{} / V{})> operator/(const V& f) const { 100 constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
102 return MakeVec(x / f, y / f); 101 return {x / f, y / f};
103 } 102 }
103
104 template <typename V> 104 template <typename V>
105 void operator/=(const V& f) { 105 constexpr Vec2& operator/=(const V& f) {
106 *this = *this / f; 106 *this = *this / f;
107 return *this;
107 } 108 }
108 109
109 T Length2() const { 110 constexpr T Length2() const {
110 return x * x + y * y; 111 return x * x + y * y;
111 } 112 }
112 113
113 // Only implemented for T=float 114 // Only implemented for T=float
114 float Length() const; 115 float Length() const;
115 void SetLength(const float l);
116 Vec2 WithLength(const float l) const;
117 float Distance2To(Vec2& other);
118 Vec2 Normalized() const;
119 float Normalize(); // returns the previous length, which is often useful 116 float Normalize(); // returns the previous length, which is often useful
120 117
121 T& operator[](int i) // allow vector[1] = 3 (vector.y=3) 118 constexpr T& operator[](std::size_t i) {
122 {
123 return *((&x) + i); 119 return *((&x) + i);
124 } 120 }
125 T operator[](const int i) const { 121 constexpr const T& operator[](std::size_t i) const {
126 return *((&x) + i); 122 return *((&x) + i);
127 } 123 }
128 124
129 void SetZero() { 125 constexpr void SetZero() {
130 x = 0; 126 x = 0;
131 y = 0; 127 y = 0;
132 } 128 }
133 129
134 // Common aliases: UV (texel coordinates), ST (texture coordinates) 130 // Common aliases: UV (texel coordinates), ST (texture coordinates)
135 T& u() { 131 constexpr T& u() {
136 return x; 132 return x;
137 } 133 }
138 T& v() { 134 constexpr T& v() {
139 return y; 135 return y;
140 } 136 }
141 T& s() { 137 constexpr T& s() {
142 return x; 138 return x;
143 } 139 }
144 T& t() { 140 constexpr T& t() {
145 return y; 141 return y;
146 } 142 }
147 143
148 const T& u() const { 144 constexpr const T& u() const {
149 return x; 145 return x;
150 } 146 }
151 const T& v() const { 147 constexpr const T& v() const {
152 return y; 148 return y;
153 } 149 }
154 const T& s() const { 150 constexpr const T& s() const {
155 return x; 151 return x;
156 } 152 }
157 const T& t() const { 153 constexpr const T& t() const {
158 return y; 154 return y;
159 } 155 }
160 156
161 // swizzlers - create a subvector of specific components 157 // swizzlers - create a subvector of specific components
162 const Vec2 yx() const { 158 constexpr Vec2 yx() const {
163 return Vec2(y, x); 159 return Vec2(y, x);
164 } 160 }
165 const Vec2 vu() const { 161 constexpr Vec2 vu() const {
166 return Vec2(y, x); 162 return Vec2(y, x);
167 } 163 }
168 const Vec2 ts() const { 164 constexpr Vec2 ts() const {
169 return Vec2(y, x); 165 return Vec2(y, x);
170 } 166 }
171}; 167};
172 168
173template <typename T, typename V> 169template <typename T, typename V>
174Vec2<T> operator*(const V& f, const Vec2<T>& vec) { 170constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
175 return Vec2<T>(f * vec.x, f * vec.y); 171 return Vec2<T>(f * vec.x, f * vec.y);
176} 172}
177 173
178typedef Vec2<float> Vec2f; 174using Vec2f = Vec2<float>;
179 175
180template <> 176template <>
181inline float Vec2<float>::Length() const { 177inline float Vec2<float>::Length() const {
@@ -196,147 +192,151 @@ public:
196 T y{}; 192 T y{};
197 T z{}; 193 T z{};
198 194
199 Vec3() = default; 195 constexpr Vec3() = default;
200 Vec3(const T& _x, const T& _y, const T& _z) : x(_x), y(_y), z(_z) {} 196 constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
201 197
202 template <typename T2> 198 template <typename T2>
203 Vec3<T2> Cast() const { 199 constexpr Vec3<T2> Cast() const {
204 return MakeVec<T2>((T2)x, (T2)y, (T2)z); 200 return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
205 } 201 }
206 202
207 // Only implemented for T=int and T=float 203 static constexpr Vec3 AssignToAll(const T& f) {
208 static Vec3 FromRGB(unsigned int rgb); 204 return Vec3(f, f, f);
209 unsigned int ToRGB() const; // alpha bits set to zero
210
211 static Vec3 AssignToAll(const T& f) {
212 return MakeVec(f, f, f);
213 } 205 }
214 206
215 Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const { 207 constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
216 return MakeVec(x + other.x, y + other.y, z + other.z); 208 return {x + other.x, y + other.y, z + other.z};
217 } 209 }
218 void operator+=(const Vec3& other) { 210
211 constexpr Vec3& operator+=(const Vec3& other) {
219 x += other.x; 212 x += other.x;
220 y += other.y; 213 y += other.y;
221 z += other.z; 214 z += other.z;
215 return *this;
222 } 216 }
223 Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const { 217
224 return MakeVec(x - other.x, y - other.y, z - other.z); 218 constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
219 return {x - other.x, y - other.y, z - other.z};
225 } 220 }
226 void operator-=(const Vec3& other) { 221
222 constexpr Vec3& operator-=(const Vec3& other) {
227 x -= other.x; 223 x -= other.x;
228 y -= other.y; 224 y -= other.y;
229 z -= other.z; 225 z -= other.z;
226 return *this;
230 } 227 }
231 228
232 template <typename U = T> 229 template <typename U = T>
233 Vec3<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const { 230 constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
234 return MakeVec(-x, -y, -z); 231 return {-x, -y, -z};
235 } 232 }
236 Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const { 233
237 return MakeVec(x * other.x, y * other.y, z * other.z); 234 constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
235 return {x * other.x, y * other.y, z * other.z};
238 } 236 }
237
239 template <typename V> 238 template <typename V>
240 Vec3<decltype(T{} * V{})> operator*(const V& f) const { 239 constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
241 return MakeVec(x * f, y * f, z * f); 240 return {x * f, y * f, z * f};
242 } 241 }
242
243 template <typename V> 243 template <typename V>
244 void operator*=(const V& f) { 244 constexpr Vec3& operator*=(const V& f) {
245 *this = *this * f; 245 *this = *this * f;
246 return *this;
246 } 247 }
247 template <typename V> 248 template <typename V>
248 Vec3<decltype(T{} / V{})> operator/(const V& f) const { 249 constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
249 return MakeVec(x / f, y / f, z / f); 250 return {x / f, y / f, z / f};
250 } 251 }
252
251 template <typename V> 253 template <typename V>
252 void operator/=(const V& f) { 254 constexpr Vec3& operator/=(const V& f) {
253 *this = *this / f; 255 *this = *this / f;
256 return *this;
254 } 257 }
255 258
256 T Length2() const { 259 constexpr T Length2() const {
257 return x * x + y * y + z * z; 260 return x * x + y * y + z * z;
258 } 261 }
259 262
260 // Only implemented for T=float 263 // Only implemented for T=float
261 float Length() const; 264 float Length() const;
262 void SetLength(const float l);
263 Vec3 WithLength(const float l) const;
264 float Distance2To(Vec3& other);
265 Vec3 Normalized() const; 265 Vec3 Normalized() const;
266 float Normalize(); // returns the previous length, which is often useful 266 float Normalize(); // returns the previous length, which is often useful
267 267
268 T& operator[](int i) // allow vector[2] = 3 (vector.z=3) 268 constexpr T& operator[](std::size_t i) {
269 {
270 return *((&x) + i); 269 return *((&x) + i);
271 } 270 }
272 T operator[](const int i) const { 271
272 constexpr const T& operator[](std::size_t i) const {
273 return *((&x) + i); 273 return *((&x) + i);
274 } 274 }
275 275
276 void SetZero() { 276 constexpr void SetZero() {
277 x = 0; 277 x = 0;
278 y = 0; 278 y = 0;
279 z = 0; 279 z = 0;
280 } 280 }
281 281
282 // Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates) 282 // Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
283 T& u() { 283 constexpr T& u() {
284 return x; 284 return x;
285 } 285 }
286 T& v() { 286 constexpr T& v() {
287 return y; 287 return y;
288 } 288 }
289 T& w() { 289 constexpr T& w() {
290 return z; 290 return z;
291 } 291 }
292 292
293 T& r() { 293 constexpr T& r() {
294 return x; 294 return x;
295 } 295 }
296 T& g() { 296 constexpr T& g() {
297 return y; 297 return y;
298 } 298 }
299 T& b() { 299 constexpr T& b() {
300 return z; 300 return z;
301 } 301 }
302 302
303 T& s() { 303 constexpr T& s() {
304 return x; 304 return x;
305 } 305 }
306 T& t() { 306 constexpr T& t() {
307 return y; 307 return y;
308 } 308 }
309 T& q() { 309 constexpr T& q() {
310 return z; 310 return z;
311 } 311 }
312 312
313 const T& u() const { 313 constexpr const T& u() const {
314 return x; 314 return x;
315 } 315 }
316 const T& v() const { 316 constexpr const T& v() const {
317 return y; 317 return y;
318 } 318 }
319 const T& w() const { 319 constexpr const T& w() const {
320 return z; 320 return z;
321 } 321 }
322 322
323 const T& r() const { 323 constexpr const T& r() const {
324 return x; 324 return x;
325 } 325 }
326 const T& g() const { 326 constexpr const T& g() const {
327 return y; 327 return y;
328 } 328 }
329 const T& b() const { 329 constexpr const T& b() const {
330 return z; 330 return z;
331 } 331 }
332 332
333 const T& s() const { 333 constexpr const T& s() const {
334 return x; 334 return x;
335 } 335 }
336 const T& t() const { 336 constexpr const T& t() const {
337 return y; 337 return y;
338 } 338 }
339 const T& q() const { 339 constexpr const T& q() const {
340 return z; 340 return z;
341 } 341 }
342 342
@@ -345,7 +345,7 @@ public:
345// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all 345// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
346// component names (x<->r) and permutations (xy<->yx) 346// component names (x<->r) and permutations (xy<->yx)
347#define _DEFINE_SWIZZLER2(a, b, name) \ 347#define _DEFINE_SWIZZLER2(a, b, name) \
348 const Vec2<T> name() const { \ 348 constexpr Vec2<T> name() const { \
349 return Vec2<T>(a, b); \ 349 return Vec2<T>(a, b); \
350 } 350 }
351#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \ 351#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
@@ -366,7 +366,7 @@ public:
366}; 366};
367 367
368template <typename T, typename V> 368template <typename T, typename V>
369Vec3<T> operator*(const V& f, const Vec3<T>& vec) { 369constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
370 return Vec3<T>(f * vec.x, f * vec.y, f * vec.z); 370 return Vec3<T>(f * vec.x, f * vec.y, f * vec.z);
371} 371}
372 372
@@ -387,7 +387,7 @@ inline float Vec3<float>::Normalize() {
387 return length; 387 return length;
388} 388}
389 389
390typedef Vec3<float> Vec3f; 390using Vec3f = Vec3<float>;
391 391
392template <typename T> 392template <typename T>
393class Vec4 { 393class Vec4 {
@@ -397,86 +397,88 @@ public:
397 T z{}; 397 T z{};
398 T w{}; 398 T w{};
399 399
400 Vec4() = default; 400 constexpr Vec4() = default;
401 Vec4(const T& _x, const T& _y, const T& _z, const T& _w) : x(_x), y(_y), z(_z), w(_w) {} 401 constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_)
402 : x(x_), y(y_), z(z_), w(w_) {}
402 403
403 template <typename T2> 404 template <typename T2>
404 Vec4<T2> Cast() const { 405 constexpr Vec4<T2> Cast() const {
405 return Vec4<T2>((T2)x, (T2)y, (T2)z, (T2)w); 406 return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
407 static_cast<T2>(w));
406 } 408 }
407 409
408 // Only implemented for T=int and T=float 410 static constexpr Vec4 AssignToAll(const T& f) {
409 static Vec4 FromRGBA(unsigned int rgba); 411 return Vec4(f, f, f, f);
410 unsigned int ToRGBA() const;
411
412 static Vec4 AssignToAll(const T& f) {
413 return Vec4<T>(f, f, f, f);
414 } 412 }
415 413
416 Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const { 414 constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
417 return MakeVec(x + other.x, y + other.y, z + other.z, w + other.w); 415 return {x + other.x, y + other.y, z + other.z, w + other.w};
418 } 416 }
419 void operator+=(const Vec4& other) { 417
418 constexpr Vec4& operator+=(const Vec4& other) {
420 x += other.x; 419 x += other.x;
421 y += other.y; 420 y += other.y;
422 z += other.z; 421 z += other.z;
423 w += other.w; 422 w += other.w;
423 return *this;
424 } 424 }
425 Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const { 425
426 return MakeVec(x - other.x, y - other.y, z - other.z, w - other.w); 426 constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
427 return {x - other.x, y - other.y, z - other.z, w - other.w};
427 } 428 }
428 void operator-=(const Vec4& other) { 429
430 constexpr Vec4& operator-=(const Vec4& other) {
429 x -= other.x; 431 x -= other.x;
430 y -= other.y; 432 y -= other.y;
431 z -= other.z; 433 z -= other.z;
432 w -= other.w; 434 w -= other.w;
435 return *this;
433 } 436 }
434 437
435 template <typename U = T> 438 template <typename U = T>
436 Vec4<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const { 439 constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
437 return MakeVec(-x, -y, -z, -w); 440 return {-x, -y, -z, -w};
438 } 441 }
439 Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const { 442
440 return MakeVec(x * other.x, y * other.y, z * other.z, w * other.w); 443 constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
444 return {x * other.x, y * other.y, z * other.z, w * other.w};
441 } 445 }
446
442 template <typename V> 447 template <typename V>
443 Vec4<decltype(T{} * V{})> operator*(const V& f) const { 448 constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
444 return MakeVec(x * f, y * f, z * f, w * f); 449 return {x * f, y * f, z * f, w * f};
445 } 450 }
451
446 template <typename V> 452 template <typename V>
447 void operator*=(const V& f) { 453 constexpr Vec4& operator*=(const V& f) {
448 *this = *this * f; 454 *this = *this * f;
455 return *this;
449 } 456 }
457
450 template <typename V> 458 template <typename V>
451 Vec4<decltype(T{} / V{})> operator/(const V& f) const { 459 constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
452 return MakeVec(x / f, y / f, z / f, w / f); 460 return {x / f, y / f, z / f, w / f};
453 } 461 }
462
454 template <typename V> 463 template <typename V>
455 void operator/=(const V& f) { 464 constexpr Vec4& operator/=(const V& f) {
456 *this = *this / f; 465 *this = *this / f;
466 return *this;
457 } 467 }
458 468
459 T Length2() const { 469 constexpr T Length2() const {
460 return x * x + y * y + z * z + w * w; 470 return x * x + y * y + z * z + w * w;
461 } 471 }
462 472
463 // Only implemented for T=float 473 constexpr T& operator[](std::size_t i) {
464 float Length() const;
465 void SetLength(const float l);
466 Vec4 WithLength(const float l) const;
467 float Distance2To(Vec4& other);
468 Vec4 Normalized() const;
469 float Normalize(); // returns the previous length, which is often useful
470
471 T& operator[](int i) // allow vector[2] = 3 (vector.z=3)
472 {
473 return *((&x) + i); 474 return *((&x) + i);
474 } 475 }
475 T operator[](const int i) const { 476
477 constexpr const T& operator[](std::size_t i) const {
476 return *((&x) + i); 478 return *((&x) + i);
477 } 479 }
478 480
479 void SetZero() { 481 constexpr void SetZero() {
480 x = 0; 482 x = 0;
481 y = 0; 483 y = 0;
482 z = 0; 484 z = 0;
@@ -484,29 +486,29 @@ public:
484 } 486 }
485 487
486 // Common alias: RGBA (colors) 488 // Common alias: RGBA (colors)
487 T& r() { 489 constexpr T& r() {
488 return x; 490 return x;
489 } 491 }
490 T& g() { 492 constexpr T& g() {
491 return y; 493 return y;
492 } 494 }
493 T& b() { 495 constexpr T& b() {
494 return z; 496 return z;
495 } 497 }
496 T& a() { 498 constexpr T& a() {
497 return w; 499 return w;
498 } 500 }
499 501
500 const T& r() const { 502 constexpr const T& r() const {
501 return x; 503 return x;
502 } 504 }
503 const T& g() const { 505 constexpr const T& g() const {
504 return y; 506 return y;
505 } 507 }
506 const T& b() const { 508 constexpr const T& b() const {
507 return z; 509 return z;
508 } 510 }
509 const T& a() const { 511 constexpr const T& a() const {
510 return w; 512 return w;
511 } 513 }
512 514
@@ -518,7 +520,7 @@ public:
518// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and 520// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
519// permutations (xy<->yx) 521// permutations (xy<->yx)
520#define _DEFINE_SWIZZLER2(a, b, name) \ 522#define _DEFINE_SWIZZLER2(a, b, name) \
521 const Vec2<T> name() const { \ 523 constexpr Vec2<T> name() const { \
522 return Vec2<T>(a, b); \ 524 return Vec2<T>(a, b); \
523 } 525 }
524#define DEFINE_SWIZZLER2_COMP1(a, a2) \ 526#define DEFINE_SWIZZLER2_COMP1(a, a2) \
@@ -545,7 +547,7 @@ public:
545#undef _DEFINE_SWIZZLER2 547#undef _DEFINE_SWIZZLER2
546 548
547#define _DEFINE_SWIZZLER3(a, b, c, name) \ 549#define _DEFINE_SWIZZLER3(a, b, c, name) \
548 const Vec3<T> name() const { \ 550 constexpr Vec3<T> name() const { \
549 return Vec3<T>(a, b, c); \ 551 return Vec3<T>(a, b, c); \
550 } 552 }
551#define DEFINE_SWIZZLER3_COMP1(a, a2) \ 553#define DEFINE_SWIZZLER3_COMP1(a, a2) \
@@ -579,51 +581,51 @@ public:
579}; 581};
580 582
581template <typename T, typename V> 583template <typename T, typename V>
582Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) { 584constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
583 return MakeVec(f * vec.x, f * vec.y, f * vec.z, f * vec.w); 585 return {f * vec.x, f * vec.y, f * vec.z, f * vec.w};
584} 586}
585 587
586typedef Vec4<float> Vec4f; 588using Vec4f = Vec4<float>;
587 589
588template <typename T> 590template <typename T>
589static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) { 591constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
590 return a.x * b.x + a.y * b.y; 592 return a.x * b.x + a.y * b.y;
591} 593}
592 594
593template <typename T> 595template <typename T>
594static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) { 596constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
595 return a.x * b.x + a.y * b.y + a.z * b.z; 597 return a.x * b.x + a.y * b.y + a.z * b.z;
596} 598}
597 599
598template <typename T> 600template <typename T>
599static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) { 601constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
600 return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; 602 return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
601} 603}
602 604
603template <typename T> 605template <typename T>
604static inline Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) { 606constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) {
605 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); 607 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};
606} 608}
607 609
608// linear interpolation via float: 0.0=begin, 1.0=end 610// linear interpolation via float: 0.0=begin, 1.0=end
609template <typename X> 611template <typename X>
610static inline decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end, 612constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
611 const float t) { 613 const float t) {
612 return begin * (1.f - t) + end * t; 614 return begin * (1.f - t) + end * t;
613} 615}
614 616
615// linear interpolation via int: 0=begin, base=end 617// linear interpolation via int: 0=begin, base=end
616template <typename X, int base> 618template <typename X, int base>
617static inline decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end, 619constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end,
618 const int t) { 620 const int t) {
619 return (begin * (base - t) + end * t) / base; 621 return (begin * (base - t) + end * t) / base;
620} 622}
621 623
622// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second 624// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
623// interpolation. 625// interpolation.
624template <typename X> 626template <typename X>
625inline auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s, 627constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s,
626 const float t) { 628 const float t) {
627 auto y0 = Lerp(x00, x01, s); 629 auto y0 = Lerp(x00, x01, s);
628 auto y1 = Lerp(x10, x11, s); 630 auto y1 = Lerp(x10, x11, s);
629 return Lerp(y0, y1, t); 631 return Lerp(y0, y1, t);
@@ -631,42 +633,42 @@ inline auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x1
631 633
632// Utility vector factories 634// Utility vector factories
633template <typename T> 635template <typename T>
634static inline Vec2<T> MakeVec(const T& x, const T& y) { 636constexpr Vec2<T> MakeVec(const T& x, const T& y) {
635 return Vec2<T>{x, y}; 637 return Vec2<T>{x, y};
636} 638}
637 639
638template <typename T> 640template <typename T>
639static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z) { 641constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
640 return Vec3<T>{x, y, z}; 642 return Vec3<T>{x, y, z};
641} 643}
642 644
643template <typename T> 645template <typename T>
644static inline Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) { 646constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
645 return MakeVec(x, y, zw[0], zw[1]); 647 return MakeVec(x, y, zw[0], zw[1]);
646} 648}
647 649
648template <typename T> 650template <typename T>
649static inline Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) { 651constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
650 return MakeVec(xy[0], xy[1], z); 652 return MakeVec(xy[0], xy[1], z);
651} 653}
652 654
653template <typename T> 655template <typename T>
654static inline Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) { 656constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
655 return MakeVec(x, yz[0], yz[1]); 657 return MakeVec(x, yz[0], yz[1]);
656} 658}
657 659
658template <typename T> 660template <typename T>
659static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) { 661constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
660 return Vec4<T>{x, y, z, w}; 662 return Vec4<T>{x, y, z, w};
661} 663}
662 664
663template <typename T> 665template <typename T>
664static inline Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) { 666constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
665 return MakeVec(xy[0], xy[1], z, w); 667 return MakeVec(xy[0], xy[1], z, w);
666} 668}
667 669
668template <typename T> 670template <typename T>
669static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) { 671constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
670 return MakeVec(x, yz[0], yz[1], w); 672 return MakeVec(x, yz[0], yz[1], w);
671} 673}
672 674
@@ -674,17 +676,17 @@ static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
674// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error 676// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
675// out soon enough due to misuse of the returned structure. 677// out soon enough due to misuse of the returned structure.
676template <typename T> 678template <typename T>
677static inline Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) { 679constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
678 return MakeVec(xy[0], xy[1], zw[0], zw[1]); 680 return MakeVec(xy[0], xy[1], zw[0], zw[1]);
679} 681}
680 682
681template <typename T> 683template <typename T>
682static inline Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) { 684constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
683 return MakeVec(xyz[0], xyz[1], xyz[2], w); 685 return MakeVec(xyz[0], xyz[1], xyz[2], w);
684} 686}
685 687
686template <typename T> 688template <typename T>
687static inline Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) { 689constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
688 return MakeVec(x, yzw[0], yzw[1], yzw[2]); 690 return MakeVec(x, yzw[0], yzw[1], yzw[2]);
689} 691}
690 692
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
index 0f52f704b..ec76e0a47 100644
--- a/src/common/x64/xbyak_util.h
+++ b/src/common/x64/xbyak_util.h
@@ -34,7 +34,7 @@ inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
34 34
35template <typename T> 35template <typename T>
36inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { 36inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
37 static_assert(std::is_pointer<T>(), "Argument must be a (function) pointer."); 37 static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
38 size_t addr = reinterpret_cast<size_t>(f); 38 size_t addr = reinterpret_cast<size_t>(f);
39 if (IsWithin2G(code, addr)) { 39 if (IsWithin2G(code, addr)) {
40 code.call(f); 40 code.call(f);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 085ba68d0..69c45c026 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -89,7 +89,7 @@ System::ResultStatus System::SingleStep() {
89} 89}
90 90
91System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) { 91System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) {
92 app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath)); 92 app_loader = Loader::GetLoader(virtual_filesystem->OpenFile(filepath, FileSys::Mode::Read));
93 93
94 if (!app_loader) { 94 if (!app_loader) {
95 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 95 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -174,6 +174,10 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
174 174
175 CoreTiming::Init(); 175 CoreTiming::Init();
176 176
177 // Create a default fs if one doesn't already exist.
178 if (virtual_filesystem == nullptr)
179 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
180
177 current_process = Kernel::Process::Create("main"); 181 current_process = Kernel::Process::Create("main");
178 182
179 cpu_barrier = std::make_shared<CpuBarrier>(); 183 cpu_barrier = std::make_shared<CpuBarrier>();
@@ -186,7 +190,7 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
186 service_manager = std::make_shared<Service::SM::ServiceManager>(); 190 service_manager = std::make_shared<Service::SM::ServiceManager>();
187 191
188 Kernel::Init(); 192 Kernel::Init();
189 Service::Init(service_manager); 193 Service::Init(service_manager, virtual_filesystem);
190 GDBStub::Init(); 194 GDBStub::Init();
191 195
192 renderer = VideoCore::CreateRenderer(emu_window); 196 renderer = VideoCore::CreateRenderer(emu_window);
diff --git a/src/core/core.h b/src/core/core.h
index c8ca4b247..7cf7ea4e1 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -17,6 +17,8 @@
17#include "core/memory.h" 17#include "core/memory.h"
18#include "core/perf_stats.h" 18#include "core/perf_stats.h"
19#include "core/telemetry_session.h" 19#include "core/telemetry_session.h"
20#include "file_sys/vfs_real.h"
21#include "hle/service/filesystem/filesystem.h"
20#include "video_core/debug_utils/debug_utils.h" 22#include "video_core/debug_utils/debug_utils.h"
21#include "video_core/gpu.h" 23#include "video_core/gpu.h"
22 24
@@ -211,6 +213,14 @@ public:
211 return debug_context; 213 return debug_context;
212 } 214 }
213 215
216 void SetFilesystem(FileSys::VirtualFilesystem vfs) {
217 virtual_filesystem = std::move(vfs);
218 }
219
220 FileSys::VirtualFilesystem GetFilesystem() const {
221 return virtual_filesystem;
222 }
223
214private: 224private:
215 System(); 225 System();
216 226
@@ -225,6 +235,8 @@ private:
225 */ 235 */
226 ResultStatus Init(EmuWindow& emu_window); 236 ResultStatus Init(EmuWindow& emu_window);
227 237
238 /// RealVfsFilesystem instance
239 FileSys::VirtualFilesystem virtual_filesystem;
228 /// AppLoader used to load the current executing application 240 /// AppLoader used to load the current executing application
229 std::unique_ptr<Loader::AppLoader> app_loader; 241 std::unique_ptr<Loader::AppLoader> app_loader;
230 std::unique_ptr<VideoCore::RendererBase> renderer; 242 std::unique_ptr<VideoCore::RendererBase> renderer;
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 395eea8ae..e897d9913 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -5,6 +5,7 @@
5#include <array> 5#include <array>
6#include <string> 6#include <string>
7#include <core/loader/loader.h> 7#include <core/loader/loader.h>
8#include "common/logging/log.h"
8#include "core/file_sys/card_image.h" 9#include "core/file_sys/card_image.h"
9#include "core/file_sys/partition_filesystem.h" 10#include "core/file_sys/partition_filesystem.h"
10#include "core/file_sys/vfs_offset.h" 11#include "core/file_sys/vfs_offset.h"
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 3529166ac..d3007d981 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -170,6 +170,10 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
170} 170}
171 171
172NCA::NCA(VirtualFile file_) : file(std::move(file_)) { 172NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
173 if (file == nullptr) {
174 status = Loader::ResultStatus::ErrorInvalidFormat;
175 return;
176 }
173 if (sizeof(NCAHeader) != file->ReadObject(&header)) 177 if (sizeof(NCAHeader) != file->ReadObject(&header))
174 LOG_ERROR(Loader, "File reader errored out during header read."); 178 LOG_ERROR(Loader, "File reader errored out during header read.");
175 179
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index a8879d9a8..5cfd5031a 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -12,6 +12,7 @@
12#include "common/common_funcs.h" 12#include "common/common_funcs.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/swap.h" 14#include "common/swap.h"
15#include "control_metadata.h"
15#include "core/crypto/key_manager.h" 16#include "core/crypto/key_manager.h"
16#include "core/file_sys/partition_filesystem.h" 17#include "core/file_sys/partition_filesystem.h"
17#include "core/loader/loader.h" 18#include "core/loader/loader.h"
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index cc3b745f7..6582cc240 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -62,6 +62,13 @@ enum class Language : u8 {
62 Chinese = 14, 62 Chinese = 14,
63}; 63};
64 64
65static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
66 "AmericanEnglish", "BritishEnglish", "Japanese",
67 "French", "German", "LatinAmericanSpanish",
68 "Spanish", "Italian", "Dutch",
69 "CanadianFrench", "Portugese", "Russian",
70 "Korean", "Taiwanese", "Chinese"};
71
65// A class representing the format used by NX metadata files, typically named Control.nacp. 72// A class representing the format used by NX metadata files, typically named Control.nacp.
66// These store application name, dev name, title id, and other miscellaneous data. 73// These store application name, dev name, title id, and other miscellaneous data.
67class NACP { 74class NACP {
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h
index 213ce1826..3759e743a 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory.h
@@ -4,8 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <cstddef> 7#include <cstddef>
8#include <iterator>
9#include <string_view>
9#include "common/common_funcs.h" 10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11 12
@@ -21,9 +22,14 @@ enum EntryType : u8 {
21 22
22// Structure of a directory entry, from 23// Structure of a directory entry, from
23// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry 24// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
24const size_t FILENAME_LENGTH = 0x300;
25struct Entry { 25struct Entry {
26 char filename[FILENAME_LENGTH]; 26 Entry(std::string_view view, EntryType entry_type, u64 entry_size)
27 : type{entry_type}, file_size{entry_size} {
28 const size_t copy_size = view.copy(filename, std::size(filename) - 1);
29 filename[copy_size] = '\0';
30 }
31
32 char filename[0x300];
27 INSERT_PADDING_BYTES(4); 33 INSERT_PADDING_BYTES(4);
28 EntryType type; 34 EntryType type;
29 INSERT_PADDING_BYTES(3); 35 INSERT_PADDING_BYTES(3);
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index e3a578c0f..f3cf50d5a 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h"
10#include "core/hle/result.h" 11#include "core/hle/result.h"
11 12
12namespace FileSys { 13namespace FileSys {
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index dae1c16ef..24e158962 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -4,12 +4,160 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <numeric> 6#include <numeric>
7#include <string>
8#include "common/common_paths.h"
7#include "common/file_util.h" 9#include "common/file_util.h"
8#include "common/logging/backend.h" 10#include "common/logging/backend.h"
9#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs.h"
10 12
11namespace FileSys { 13namespace FileSys {
12 14
15VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {}
16
17VfsFilesystem::~VfsFilesystem() = default;
18
19std::string VfsFilesystem::GetName() const {
20 return root->GetName();
21}
22
23bool VfsFilesystem::IsReadable() const {
24 return root->IsReadable();
25}
26
27bool VfsFilesystem::IsWritable() const {
28 return root->IsWritable();
29}
30
31VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
32 const auto path = FileUtil::SanitizePath(path_);
33 if (root->GetFileRelative(path) != nullptr)
34 return VfsEntryType::File;
35 if (root->GetDirectoryRelative(path) != nullptr)
36 return VfsEntryType::Directory;
37
38 return VfsEntryType::None;
39}
40
41VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
42 const auto path = FileUtil::SanitizePath(path_);
43 return root->GetFileRelative(path);
44}
45
46VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
47 const auto path = FileUtil::SanitizePath(path_);
48 return root->CreateFileRelative(path);
49}
50
51VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
52 const auto old_path = FileUtil::SanitizePath(old_path_);
53 const auto new_path = FileUtil::SanitizePath(new_path_);
54
55 // VfsDirectory impls are only required to implement copy across the current directory.
56 if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) {
57 if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path)))
58 return nullptr;
59 return OpenFile(new_path, Mode::ReadWrite);
60 }
61
62 // Do it using RawCopy. Non-default impls are encouraged to optimize this.
63 const auto old_file = OpenFile(old_path, Mode::Read);
64 if (old_file == nullptr)
65 return nullptr;
66 auto new_file = OpenFile(new_path, Mode::Read);
67 if (new_file != nullptr)
68 return nullptr;
69 new_file = CreateFile(new_path, Mode::Write);
70 if (new_file == nullptr)
71 return nullptr;
72 if (!VfsRawCopy(old_file, new_file))
73 return nullptr;
74 return new_file;
75}
76
77VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
78 const auto old_path = FileUtil::SanitizePath(old_path_);
79 const auto new_path = FileUtil::SanitizePath(new_path_);
80
81 // Again, non-default impls are highly encouraged to provide a more optimized version of this.
82 auto out = CopyFile(old_path_, new_path_);
83 if (out == nullptr)
84 return nullptr;
85 if (DeleteFile(old_path))
86 return out;
87 return nullptr;
88}
89
90bool VfsFilesystem::DeleteFile(std::string_view path_) {
91 const auto path = FileUtil::SanitizePath(path_);
92 auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
93 if (parent == nullptr)
94 return false;
95 return parent->DeleteFile(FileUtil::GetFilename(path));
96}
97
98VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
99 const auto path = FileUtil::SanitizePath(path_);
100 return root->GetDirectoryRelative(path);
101}
102
103VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
104 const auto path = FileUtil::SanitizePath(path_);
105 return root->CreateDirectoryRelative(path);
106}
107
108VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
109 const auto old_path = FileUtil::SanitizePath(old_path_);
110 const auto new_path = FileUtil::SanitizePath(new_path_);
111
112 // Non-default impls are highly encouraged to provide a more optimized version of this.
113 auto old_dir = OpenDirectory(old_path, Mode::Read);
114 if (old_dir == nullptr)
115 return nullptr;
116 auto new_dir = OpenDirectory(new_path, Mode::Read);
117 if (new_dir != nullptr)
118 return nullptr;
119 new_dir = CreateDirectory(new_path, Mode::Write);
120 if (new_dir == nullptr)
121 return nullptr;
122
123 for (const auto& file : old_dir->GetFiles()) {
124 const auto x =
125 CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName());
126 if (x == nullptr)
127 return nullptr;
128 }
129
130 for (const auto& dir : old_dir->GetSubdirectories()) {
131 const auto x =
132 CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName());
133 if (x == nullptr)
134 return nullptr;
135 }
136
137 return new_dir;
138}
139
140VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) {
141 const auto old_path = FileUtil::SanitizePath(old_path_);
142 const auto new_path = FileUtil::SanitizePath(new_path_);
143
144 // Non-default impls are highly encouraged to provide a more optimized version of this.
145 auto out = CopyDirectory(old_path_, new_path_);
146 if (out == nullptr)
147 return nullptr;
148 if (DeleteDirectory(old_path))
149 return out;
150 return nullptr;
151}
152
153bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
154 const auto path = FileUtil::SanitizePath(path_);
155 auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
156 if (parent == nullptr)
157 return false;
158 return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path));
159}
160
13VfsFile::~VfsFile() = default; 161VfsFile::~VfsFile() = default;
14 162
15std::string VfsFile::GetExtension() const { 163std::string VfsFile::GetExtension() const {
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index fab9e2b45..141a053ce 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -11,14 +11,74 @@
11#include <vector> 11#include <vector>
12#include "boost/optional.hpp" 12#include "boost/optional.hpp"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "core/file_sys/mode.h"
14 15
15namespace FileSys { 16namespace FileSys {
17
18struct VfsFilesystem;
16struct VfsFile; 19struct VfsFile;
17struct VfsDirectory; 20struct VfsDirectory;
18 21
19// Convenience typedefs to use VfsDirectory and VfsFile 22// Convenience typedefs to use Vfs* interfaces
20using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>; 23using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
21using VirtualFile = std::shared_ptr<FileSys::VfsFile>; 24using VirtualDir = std::shared_ptr<VfsDirectory>;
25using VirtualFile = std::shared_ptr<VfsFile>;
26
27// An enumeration representing what can be at the end of a path in a VfsFilesystem
28enum class VfsEntryType {
29 None,
30 File,
31 Directory,
32};
33
34// A class representing an abstract filesystem. A default implementation given the root VirtualDir
35// is provided for convenience, but if the Vfs implementation has any additional state or
36// functionality, they will need to override.
37struct VfsFilesystem : NonCopyable {
38 VfsFilesystem(VirtualDir root);
39 virtual ~VfsFilesystem();
40
41 // Gets the friendly name for the filesystem.
42 virtual std::string GetName() const;
43
44 // Return whether or not the user has read permissions on this filesystem.
45 virtual bool IsReadable() const;
46 // Return whether or not the user has write permission on this filesystem.
47 virtual bool IsWritable() const;
48
49 // Determine if the entry at path is non-existant, a file, or a directory.
50 virtual VfsEntryType GetEntryType(std::string_view path) const;
51
52 // Opens the file with path relative to root. If it doesn't exist, returns nullptr.
53 virtual VirtualFile OpenFile(std::string_view path, Mode perms);
54 // Creates a new, empty file at path
55 virtual VirtualFile CreateFile(std::string_view path, Mode perms);
56 // Copies the file from old_path to new_path, returning the new file on success and nullptr on
57 // failure.
58 virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path);
59 // Moves the file from old_path to new_path, returning the moved file on success and nullptr on
60 // failure.
61 virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path);
62 // Deletes the file with path relative to root, returing true on success.
63 virtual bool DeleteFile(std::string_view path);
64
65 // Opens the directory with path relative to root. If it doesn't exist, returns nullptr.
66 virtual VirtualDir OpenDirectory(std::string_view path, Mode perms);
67 // Creates a new, empty directory at path
68 virtual VirtualDir CreateDirectory(std::string_view path, Mode perms);
69 // Copies the directory from old_path to new_path, returning the new directory on success and
70 // nullptr on failure.
71 virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path);
72 // Moves the directory from old_path to new_path, returning the moved directory on success and
73 // nullptr on failure.
74 virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path);
75 // Deletes the directory with path relative to root, returing true on success.
76 virtual bool DeleteDirectory(std::string_view path);
77
78protected:
79 // Root directory in default implementation.
80 VirtualDir root;
81};
22 82
23// A class representing a file in an abstract filesystem. 83// A class representing a file in an abstract filesystem.
24struct VfsFile : NonCopyable { 84struct VfsFile : NonCopyable {
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 82d54da4a..1b5919737 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -6,7 +6,7 @@
6#include <cstddef> 6#include <cstddef>
7#include <iterator> 7#include <iterator>
8#include <utility> 8#include <utility>
9 9#include "common/assert.h"
10#include "common/common_paths.h" 10#include "common/common_paths.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/file_sys/vfs_real.h" 12#include "core/file_sys/vfs_real.h"
@@ -29,6 +29,8 @@ static std::string ModeFlagsToString(Mode mode) {
29 mode_str = "a"; 29 mode_str = "a";
30 else if (mode & Mode::Write) 30 else if (mode & Mode::Write)
31 mode_str = "w"; 31 mode_str = "w";
32 else
33 UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode));
32 } 34 }
33 35
34 mode_str += "b"; 36 mode_str += "b";
@@ -36,8 +38,174 @@ static std::string ModeFlagsToString(Mode mode) {
36 return mode_str; 38 return mode_str;
37} 39}
38 40
39RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_) 41RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
40 : backing(path_, ModeFlagsToString(perms_).c_str()), path(path_), 42
43std::string RealVfsFilesystem::GetName() const {
44 return "Real";
45}
46
47bool RealVfsFilesystem::IsReadable() const {
48 return true;
49}
50
51bool RealVfsFilesystem::IsWritable() const {
52 return true;
53}
54
55VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
56 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
57 if (!FileUtil::Exists(path))
58 return VfsEntryType::None;
59 if (FileUtil::IsDirectory(path))
60 return VfsEntryType::Directory;
61
62 return VfsEntryType::File;
63}
64
65VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
66 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
67 if (cache.find(path) != cache.end()) {
68 auto weak = cache[path];
69 if (!weak.expired()) {
70 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
71 }
72 }
73
74 if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0)
75 FileUtil::CreateEmptyFile(path);
76
77 auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str());
78 cache[path] = backing;
79
80 // Cannot use make_shared as RealVfsFile constructor is private
81 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
82}
83
84VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
85 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
86 if (!FileUtil::Exists(path) && !FileUtil::CreateEmptyFile(path))
87 return nullptr;
88 return OpenFile(path, perms);
89}
90
91VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
92 const auto old_path =
93 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
94 const auto new_path =
95 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
96
97 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
98 FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path))
99 return nullptr;
100 return OpenFile(new_path, Mode::ReadWrite);
101}
102
103VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
104 const auto old_path =
105 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
106 const auto new_path =
107 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
108
109 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
110 FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
111 return nullptr;
112
113 if (cache.find(old_path) != cache.end()) {
114 auto cached = cache[old_path];
115 if (!cached.expired()) {
116 auto file = cached.lock();
117 file->Open(new_path, "r+b");
118 cache.erase(old_path);
119 cache[new_path] = file;
120 }
121 }
122 return OpenFile(new_path, Mode::ReadWrite);
123}
124
125bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
126 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
127 if (cache.find(path) != cache.end()) {
128 if (!cache[path].expired())
129 cache[path].lock()->Close();
130 cache.erase(path);
131 }
132 return FileUtil::Delete(path);
133}
134
135VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
136 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
137 // Cannot use make_shared as RealVfsDirectory constructor is private
138 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
139}
140
141VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
142 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
143 if (!FileUtil::Exists(path) && !FileUtil::CreateDir(path))
144 return nullptr;
145 // Cannot use make_shared as RealVfsDirectory constructor is private
146 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
147}
148
149VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
150 std::string_view new_path_) {
151 const auto old_path =
152 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
153 const auto new_path =
154 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
155 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
156 !FileUtil::IsDirectory(old_path))
157 return nullptr;
158 FileUtil::CopyDir(old_path, new_path);
159 return OpenDirectory(new_path, Mode::ReadWrite);
160}
161
162VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
163 std::string_view new_path_) {
164 const auto old_path =
165 FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
166 const auto new_path =
167 FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
168 if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
169 FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
170 return nullptr;
171
172 for (auto& kv : cache) {
173 // Path in cache starts with old_path
174 if (kv.first.rfind(old_path, 0) == 0) {
175 const auto file_old_path =
176 FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault);
177 const auto file_new_path =
178 FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
179 FileUtil::DirectorySeparator::PlatformDefault);
180 auto cached = cache[file_old_path];
181 if (!cached.expired()) {
182 auto file = cached.lock();
183 file->Open(file_new_path, "r+b");
184 cache.erase(file_old_path);
185 cache[file_new_path] = file;
186 }
187 }
188 }
189
190 return OpenDirectory(new_path, Mode::ReadWrite);
191}
192
193bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
194 const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
195 for (auto& kv : cache) {
196 // Path in cache starts with old_path
197 if (kv.first.rfind(path, 0) == 0) {
198 if (!cache[kv.first].expired())
199 cache[kv.first].lock()->Close();
200 cache.erase(kv.first);
201 }
202 }
203 return FileUtil::DeleteDirRecursively(path);
204}
205
206RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_,
207 const std::string& path_, Mode perms_)
208 : base(base_), backing(std::move(backing_)), path(path_),
41 parent_path(FileUtil::GetParentPath(path_)), 209 parent_path(FileUtil::GetParentPath(path_)),
42 path_components(FileUtil::SplitPathComponents(path_)), 210 path_components(FileUtil::SplitPathComponents(path_)),
43 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), 211 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
@@ -48,15 +216,15 @@ std::string RealVfsFile::GetName() const {
48} 216}
49 217
50size_t RealVfsFile::GetSize() const { 218size_t RealVfsFile::GetSize() const {
51 return backing.GetSize(); 219 return backing->GetSize();
52} 220}
53 221
54bool RealVfsFile::Resize(size_t new_size) { 222bool RealVfsFile::Resize(size_t new_size) {
55 return backing.Resize(new_size); 223 return backing->Resize(new_size);
56} 224}
57 225
58std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { 226std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
59 return std::make_shared<RealVfsDirectory>(parent_path, perms); 227 return base.OpenDirectory(parent_path, perms);
60} 228}
61 229
62bool RealVfsFile::IsWritable() const { 230bool RealVfsFile::IsWritable() const {
@@ -68,62 +236,118 @@ bool RealVfsFile::IsReadable() const {
68} 236}
69 237
70size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { 238size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
71 if (!backing.Seek(offset, SEEK_SET)) 239 if (!backing->Seek(offset, SEEK_SET))
72 return 0; 240 return 0;
73 return backing.ReadBytes(data, length); 241 return backing->ReadBytes(data, length);
74} 242}
75 243
76size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { 244size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
77 if (!backing.Seek(offset, SEEK_SET)) 245 if (!backing->Seek(offset, SEEK_SET))
78 return 0; 246 return 0;
79 return backing.WriteBytes(data, length); 247 return backing->WriteBytes(data, length);
80} 248}
81 249
82bool RealVfsFile::Rename(std::string_view name) { 250bool RealVfsFile::Rename(std::string_view name) {
83 std::string name_str(name.begin(), name.end()); 251 return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr;
84 const auto out = FileUtil::Rename(GetName(), name_str); 252}
253
254bool RealVfsFile::Close() {
255 return backing->Close();
256}
85 257
86 path = (parent_path + DIR_SEP).append(name); 258// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if
87 path_components = parent_components; 259// constexpr' because there is a compile error in the branch not used.
88 path_components.push_back(std::move(name_str)); 260
89 backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str()); 261template <>
262std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
263 if (perms == Mode::Append)
264 return {};
265
266 std::vector<VirtualFile> out;
267 FileUtil::ForeachDirectoryEntry(
268 nullptr, path,
269 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
270 const std::string full_path = directory + DIR_SEP + filename;
271 if (!FileUtil::IsDirectory(full_path))
272 out.emplace_back(base.OpenFile(full_path, perms));
273 return true;
274 });
90 275
91 return out; 276 return out;
92} 277}
93 278
94bool RealVfsFile::Close() { 279template <>
95 return backing.Close(); 280std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
281 if (perms == Mode::Append)
282 return {};
283
284 std::vector<VirtualDir> out;
285 FileUtil::ForeachDirectoryEntry(
286 nullptr, path,
287 [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
288 const std::string full_path = directory + DIR_SEP + filename;
289 if (FileUtil::IsDirectory(full_path))
290 out.emplace_back(base.OpenDirectory(full_path, perms));
291 return true;
292 });
293
294 return out;
96} 295}
97 296
98RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_) 297RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
99 : path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)), 298 : base(base_), path(FileUtil::RemoveTrailingSlash(path_)),
299 parent_path(FileUtil::GetParentPath(path)),
100 path_components(FileUtil::SplitPathComponents(path)), 300 path_components(FileUtil::SplitPathComponents(path)),
101 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), 301 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
102 perms(perms_) { 302 perms(perms_) {
103 if (!FileUtil::Exists(path) && perms & Mode::WriteAppend) 303 if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
104 FileUtil::CreateDir(path); 304 FileUtil::CreateDir(path);
305}
105 306
106 if (perms == Mode::Append) 307std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
107 return; 308 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
309 if (!FileUtil::Exists(full_path))
310 return nullptr;
311 return base.OpenFile(full_path, perms);
312}
108 313
109 FileUtil::ForeachDirectoryEntry( 314std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const {
110 nullptr, path, 315 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
111 [this](u64* entries_out, const std::string& directory, const std::string& filename) { 316 if (!FileUtil::Exists(full_path))
112 std::string full_path = directory + DIR_SEP + filename; 317 return nullptr;
113 if (FileUtil::IsDirectory(full_path)) 318 return base.OpenDirectory(full_path, perms);
114 subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms)); 319}
115 else 320
116 files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms)); 321std::shared_ptr<VfsFile> RealVfsDirectory::GetFile(std::string_view name) const {
117 return true; 322 return GetFileRelative(name);
118 }); 323}
324
325std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view name) const {
326 return GetDirectoryRelative(name);
327}
328
329std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) {
330 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
331 return base.CreateFile(full_path, perms);
332}
333
334std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
335 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
336 auto parent = std::string(FileUtil::GetParentPath(full_path));
337 return base.CreateDirectory(full_path, perms);
338}
339
340bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
341 auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name));
342 return base.DeleteDirectory(full_path);
119} 343}
120 344
121std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const { 345std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
122 return files; 346 return IterateEntries<RealVfsFile, VfsFile>();
123} 347}
124 348
125std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const { 349std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
126 return subdirectories; 350 return IterateEntries<RealVfsDirectory, VfsDirectory>();
127} 351}
128 352
129bool RealVfsDirectory::IsWritable() const { 353bool RealVfsDirectory::IsWritable() const {
@@ -142,57 +366,32 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
142 if (path_components.size() <= 1) 366 if (path_components.size() <= 1)
143 return nullptr; 367 return nullptr;
144 368
145 return std::make_shared<RealVfsDirectory>(parent_path, perms); 369 return base.OpenDirectory(parent_path, perms);
146} 370}
147 371
148std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) { 372std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) {
149 const std::string subdir_path = (path + DIR_SEP).append(name); 373 const std::string subdir_path = (path + DIR_SEP).append(name);
150 374 return base.CreateDirectory(subdir_path, perms);
151 if (!FileUtil::CreateDir(subdir_path)) {
152 return nullptr;
153 }
154
155 subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms));
156 return subdirectories.back();
157} 375}
158 376
159std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) { 377std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) {
160 const std::string file_path = (path + DIR_SEP).append(name); 378 const std::string file_path = (path + DIR_SEP).append(name);
161 379 return base.CreateFile(file_path, perms);
162 if (!FileUtil::CreateEmptyFile(file_path)) {
163 return nullptr;
164 }
165
166 files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms));
167 return files.back();
168} 380}
169 381
170bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) { 382bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
171 const std::string subdir_path = (path + DIR_SEP).append(name); 383 const std::string subdir_path = (path + DIR_SEP).append(name);
172 384 return base.DeleteDirectory(subdir_path);
173 return FileUtil::DeleteDirRecursively(subdir_path);
174} 385}
175 386
176bool RealVfsDirectory::DeleteFile(std::string_view name) { 387bool RealVfsDirectory::DeleteFile(std::string_view name) {
177 const auto file = GetFile(name);
178
179 if (file == nullptr) {
180 return false;
181 }
182
183 files.erase(std::find(files.begin(), files.end(), file));
184
185 auto real_file = std::static_pointer_cast<RealVfsFile>(file);
186 real_file->Close();
187
188 const std::string file_path = (path + DIR_SEP).append(name); 388 const std::string file_path = (path + DIR_SEP).append(name);
189 return FileUtil::Delete(file_path); 389 return base.DeleteFile(file_path);
190} 390}
191 391
192bool RealVfsDirectory::Rename(std::string_view name) { 392bool RealVfsDirectory::Rename(std::string_view name) {
193 const std::string new_name = (parent_path + DIR_SEP).append(name); 393 const std::string new_name = (parent_path + DIR_SEP).append(name);
194 394 return base.MoveFile(path, new_name) != nullptr;
195 return FileUtil::Rename(path, new_name);
196} 395}
197 396
198std::string RealVfsDirectory::GetFullPath() const { 397std::string RealVfsDirectory::GetFullPath() const {
@@ -202,16 +401,6 @@ std::string RealVfsDirectory::GetFullPath() const {
202} 401}
203 402
204bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 403bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
205 const auto iter = std::find(files.begin(), files.end(), file); 404 return false;
206 if (iter == files.end())
207 return false;
208
209 const std::ptrdiff_t offset = std::distance(files.begin(), iter);
210 files[offset] = files.back();
211 files.pop_back();
212
213 subdirectories.emplace_back(std::move(dir));
214
215 return true;
216} 405}
217} // namespace FileSys 406} // namespace FileSys
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 243d58576..8a1e79ef6 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -6,18 +6,45 @@
6 6
7#include <string_view> 7#include <string_view>
8 8
9#include <boost/container/flat_map.hpp>
9#include "common/file_util.h" 10#include "common/file_util.h"
10#include "core/file_sys/mode.h" 11#include "core/file_sys/mode.h"
11#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
12 13
13namespace FileSys { 14namespace FileSys {
14 15
16class RealVfsFilesystem : public VfsFilesystem {
17public:
18 RealVfsFilesystem();
19
20 std::string GetName() const override;
21 bool IsReadable() const override;
22 bool IsWritable() const override;
23 VfsEntryType GetEntryType(std::string_view path) const override;
24 VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override;
25 VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override;
26 VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override;
27 VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override;
28 bool DeleteFile(std::string_view path) override;
29 VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override;
30 VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override;
31 VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override;
32 VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override;
33 bool DeleteDirectory(std::string_view path) override;
34
35private:
36 boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache;
37};
38
15// An implmentation of VfsFile that represents a file on the user's computer. 39// An implmentation of VfsFile that represents a file on the user's computer.
16struct RealVfsFile : public VfsFile { 40class RealVfsFile : public VfsFile {
17 friend struct RealVfsDirectory; 41 friend class RealVfsDirectory;
42 friend class RealVfsFilesystem;
18 43
19 RealVfsFile(const std::string& name, Mode perms = Mode::Read); 44 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
45 const std::string& path, Mode perms = Mode::Read);
20 46
47public:
21 std::string GetName() const override; 48 std::string GetName() const override;
22 size_t GetSize() const override; 49 size_t GetSize() const override;
23 bool Resize(size_t new_size) override; 50 bool Resize(size_t new_size) override;
@@ -31,7 +58,8 @@ struct RealVfsFile : public VfsFile {
31private: 58private:
32 bool Close(); 59 bool Close();
33 60
34 FileUtil::IOFile backing; 61 RealVfsFilesystem& base;
62 std::shared_ptr<FileUtil::IOFile> backing;
35 std::string path; 63 std::string path;
36 std::string parent_path; 64 std::string parent_path;
37 std::vector<std::string> path_components; 65 std::vector<std::string> path_components;
@@ -40,9 +68,19 @@ private:
40}; 68};
41 69
42// An implementation of VfsDirectory that represents a directory on the user's computer. 70// An implementation of VfsDirectory that represents a directory on the user's computer.
43struct RealVfsDirectory : public VfsDirectory { 71class RealVfsDirectory : public VfsDirectory {
44 RealVfsDirectory(const std::string& path, Mode perms = Mode::Read); 72 friend class RealVfsFilesystem;
45 73
74 RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
75
76public:
77 std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
78 std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
79 std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
80 std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override;
81 std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override;
82 std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override;
83 bool DeleteSubdirectoryRecursive(std::string_view name) override;
46 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; 84 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
47 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; 85 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
48 bool IsWritable() const override; 86 bool IsWritable() const override;
@@ -60,13 +98,15 @@ protected:
60 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 98 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
61 99
62private: 100private:
101 template <typename T, typename R>
102 std::vector<std::shared_ptr<R>> IterateEntries() const;
103
104 RealVfsFilesystem& base;
63 std::string path; 105 std::string path;
64 std::string parent_path; 106 std::string parent_path;
65 std::vector<std::string> path_components; 107 std::vector<std::string> path_components;
66 std::vector<std::string> parent_components; 108 std::vector<std::string> parent_components;
67 Mode perms; 109 Mode perms;
68 std::vector<std::shared_ptr<VfsFile>> files;
69 std::vector<std::shared_ptr<VfsDirectory>> subdirectories;
70}; 110};
71 111
72} // namespace FileSys 112} // namespace FileSys
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 5dd1b68d7..82a3fb5a8 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -201,7 +201,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
201 return RESULT_SUCCESS; 201 return RESULT_SUCCESS;
202} 202}
203 203
204ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { 204ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
205 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; 205 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
206 Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), 206 Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
207 dst_cmdbuf.size() * sizeof(u32)); 207 dst_cmdbuf.size() * sizeof(u32));
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 9ce52db24..f0d07f1b6 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -132,7 +132,7 @@ public:
132 ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, 132 ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
133 HandleTable& src_table); 133 HandleTable& src_table);
134 /// Writes data from this context back to the requesting process/thread. 134 /// Writes data from this context back to the requesting process/thread.
135 ResultCode WriteToOutgoingCommandBuffer(Thread& thread); 135 ResultCode WriteToOutgoingCommandBuffer(const Thread& thread);
136 136
137 u32_le GetCommand() const { 137 u32_le GetCommand() const {
138 return command; 138 return command;
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 0a6cac5b7..e74379a24 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -38,7 +38,7 @@ public:
38 {0, &IProfile::Get, "Get"}, 38 {0, &IProfile::Get, "Get"},
39 {1, &IProfile::GetBase, "GetBase"}, 39 {1, &IProfile::GetBase, "GetBase"},
40 {10, nullptr, "GetImageSize"}, 40 {10, nullptr, "GetImageSize"},
41 {11, nullptr, "LoadImage"}, 41 {11, &IProfile::LoadImage, "LoadImage"},
42 }; 42 };
43 RegisterHandlers(functions); 43 RegisterHandlers(functions);
44 } 44 }
@@ -72,6 +72,27 @@ private:
72 } 72 }
73 } 73 }
74 74
75 void LoadImage(Kernel::HLERequestContext& ctx) {
76 LOG_WARNING(Service_ACC, "(STUBBED) called");
77 // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
78 // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
79 const u32 jpeg_size = 107;
80 static const std::array<u8, jpeg_size> jpeg{
81 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
82 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
83 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
84 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
85 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
86 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
87 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
88 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
89 };
90 ctx.WriteBuffer(jpeg.data(), jpeg_size);
91 IPC::ResponseBuilder rb{ctx, 3};
92 rb.Push(RESULT_SUCCESS);
93 rb.Push<u32>(jpeg_size);
94 }
95
75 ProfileManager& profile_manager; 96 ProfileManager& profile_manager;
76 UUID user_id; ///< The user id this profile refers to. 97 UUID user_id; ///< The user id this profile refers to.
77}; 98};
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 9404d6b8c..762763463 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -136,7 +136,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
136 {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, 136 {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"},
137 {17, nullptr, "SetControllerFirmwareUpdateSection"}, 137 {17, nullptr, "SetControllerFirmwareUpdateSection"},
138 {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, 138 {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
139 {19, nullptr, "SetScreenShotImageOrientation"}, 139 {19, &ISelfController::SetScreenShotImageOrientation, "SetScreenShotImageOrientation"},
140 {20, nullptr, "SetDesirableKeyboardLayout"}, 140 {20, nullptr, "SetDesirableKeyboardLayout"},
141 {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, 141 {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
142 {41, nullptr, "IsSystemBufferSharingEnabled"}, 142 {41, nullptr, "IsSystemBufferSharingEnabled"},
@@ -254,6 +254,13 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext&
254 LOG_WARNING(Service_AM, "(STUBBED) called"); 254 LOG_WARNING(Service_AM, "(STUBBED) called");
255} 255}
256 256
257void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) {
258 IPC::ResponseBuilder rb{ctx, 2};
259 rb.Push(RESULT_SUCCESS);
260
261 LOG_WARNING(Service_AM, "(STUBBED) called");
262}
263
257void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { 264void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
258 // TODO(Subv): Find out how AM determines the display to use, for now just create the layer 265 // TODO(Subv): Find out how AM determines the display to use, for now just create the layer
259 // in the Default display. 266 // in the Default display.
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 8f4f98346..862f338ac 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -83,6 +83,7 @@ private:
83 void LockExit(Kernel::HLERequestContext& ctx); 83 void LockExit(Kernel::HLERequestContext& ctx);
84 void UnlockExit(Kernel::HLERequestContext& ctx); 84 void UnlockExit(Kernel::HLERequestContext& ctx);
85 void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx); 85 void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx);
86 void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx);
86 void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); 87 void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
87 void SetScreenShotPermission(Kernel::HLERequestContext& ctx); 88 void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
88 void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); 89 void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index e17d637e4..5e416cde2 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -59,7 +59,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
59ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { 59ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
60 std::string path(FileUtil::SanitizePath(path_)); 60 std::string path(FileUtil::SanitizePath(path_));
61 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); 61 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
62 if (path == "/" || path == "\\") { 62 if (path.empty()) {
63 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... 63 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
64 return RESULT_SUCCESS; 64 return RESULT_SUCCESS;
65 } 65 }
@@ -281,15 +281,15 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
281 return sdmc_factory->Open(); 281 return sdmc_factory->Open();
282} 282}
283 283
284void RegisterFileSystems() { 284void RegisterFileSystems(const FileSys::VirtualFilesystem& vfs) {
285 romfs_factory = nullptr; 285 romfs_factory = nullptr;
286 save_data_factory = nullptr; 286 save_data_factory = nullptr;
287 sdmc_factory = nullptr; 287 sdmc_factory = nullptr;
288 288
289 auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>( 289 auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
290 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite); 290 FileSys::Mode::ReadWrite);
291 auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>( 291 auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
292 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite); 292 FileSys::Mode::ReadWrite);
293 293
294 auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); 294 auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
295 save_data_factory = std::move(savedata); 295 save_data_factory = std::move(savedata);
@@ -298,8 +298,8 @@ void RegisterFileSystems() {
298 sdmc_factory = std::move(sdcard); 298 sdmc_factory = std::move(sdcard);
299} 299}
300 300
301void InstallInterfaces(SM::ServiceManager& service_manager) { 301void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) {
302 RegisterFileSystems(); 302 RegisterFileSystems(vfs);
303 std::make_shared<FSP_LDR>()->InstallAsService(service_manager); 303 std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
304 std::make_shared<FSP_PR>()->InstallAsService(service_manager); 304 std::make_shared<FSP_PR>()->InstallAsService(service_manager);
305 std::make_shared<FSP_SRV>()->InstallAsService(service_manager); 305 std::make_shared<FSP_SRV>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index d4483daa5..462c13f20 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -36,7 +36,7 @@ ResultVal<FileSys::VirtualDir> OpenSDMC();
36// ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS(); 36// ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();
37 37
38/// Registers all Filesystem services with the specified service manager. 38/// Registers all Filesystem services with the specified service manager.
39void InstallInterfaces(SM::ServiceManager& service_manager); 39void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs);
40 40
41// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of 41// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
42// pointers and booleans. This makes using a VfsDirectory with switch services much easier and 42// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index e7ffb6bd1..1470f9017 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -193,13 +193,10 @@ private:
193template <typename T> 193template <typename T>
194static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data, 194static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data,
195 FileSys::EntryType type) { 195 FileSys::EntryType type) {
196 entries.reserve(entries.size() + new_data.size());
197
196 for (const auto& new_entry : new_data) { 198 for (const auto& new_entry : new_data) {
197 FileSys::Entry entry; 199 entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize());
198 entry.filename[0] = '\0';
199 std::strncat(entry.filename, new_entry->GetName().c_str(), FileSys::FILENAME_LENGTH - 1);
200 entry.type = type;
201 entry.file_size = new_entry->GetSize();
202 entries.emplace_back(std::move(entry));
203 } 200 }
204} 201}
205 202
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 116dabedb..4cdf7f613 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -147,7 +147,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
147 } 147 }
148 params.fence_out.id = 0; 148 params.fence_out.id = 0;
149 params.fence_out.value = 0; 149 params.fence_out.value = 0;
150 std::memcpy(output.data(), &params, output.size()); 150 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
151 return 0; 151 return 0;
152} 152}
153 153
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index e8b30921a..427f4b574 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -16,19 +16,18 @@
16#include "core/hle/service/nvdrv/interface.h" 16#include "core/hle/service/nvdrv/interface.h"
17#include "core/hle/service/nvdrv/nvdrv.h" 17#include "core/hle/service/nvdrv/nvdrv.h"
18#include "core/hle/service/nvdrv/nvmemp.h" 18#include "core/hle/service/nvdrv/nvmemp.h"
19#include "core/hle/service/nvflinger/nvflinger.h"
19 20
20namespace Service::Nvidia { 21namespace Service::Nvidia {
21 22
22std::weak_ptr<Module> nvdrv; 23void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger) {
23
24void InstallInterfaces(SM::ServiceManager& service_manager) {
25 auto module_ = std::make_shared<Module>(); 24 auto module_ = std::make_shared<Module>();
26 std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager); 25 std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager);
27 std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager); 26 std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager);
28 std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager); 27 std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager);
29 std::make_shared<NVDRV>(module_, "nvdrv:t")->InstallAsService(service_manager); 28 std::make_shared<NVDRV>(module_, "nvdrv:t")->InstallAsService(service_manager);
30 std::make_shared<NVMEMP>()->InstallAsService(service_manager); 29 std::make_shared<NVMEMP>()->InstallAsService(service_manager);
31 nvdrv = module_; 30 nvflinger.SetNVDrvInstance(module_);
32} 31}
33 32
34Module::Module() { 33Module::Module() {
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 184f3c9fc..99eb1128a 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -10,6 +10,10 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
12 12
13namespace Service::NVFlinger {
14class NVFlinger;
15}
16
13namespace Service::Nvidia { 17namespace Service::Nvidia {
14 18
15namespace Devices { 19namespace Devices {
@@ -56,8 +60,6 @@ private:
56}; 60};
57 61
58/// Registers all NVDRV services with the specified service manager. 62/// Registers all NVDRV services with the specified service manager.
59void InstallInterfaces(SM::ServiceManager& service_manager); 63void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger);
60
61extern std::weak_ptr<Module> nvdrv;
62 64
63} // namespace Service::Nvidia 65} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index adf180509..ef5713a71 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -16,7 +16,7 @@ BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
16 Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle"); 16 Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
17} 17}
18 18
19void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) { 19void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
20 Buffer buffer{}; 20 Buffer buffer{};
21 buffer.slot = slot; 21 buffer.slot = slot;
22 buffer.igbp_buffer = igbp_buffer; 22 buffer.igbp_buffer = igbp_buffer;
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 004170538..f86e1056c 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -72,7 +72,7 @@ public:
72 MathUtil::Rectangle<int> crop_rect; 72 MathUtil::Rectangle<int> crop_rect;
73 }; 73 };
74 74
75 void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer); 75 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
76 boost::optional<u32> DequeueBuffer(u32 width, u32 height); 76 boost::optional<u32> DequeueBuffer(u32 width, u32 height);
77 const IGBPBuffer& RequestBuffer(u32 slot) const; 77 const IGBPBuffer& RequestBuffer(u32 slot) const;
78 void QueueBuffer(u32 slot, BufferTransformFlags transform, 78 void QueueBuffer(u32 slot, BufferTransformFlags transform,
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 570aa8493..a26a5f812 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -46,6 +46,10 @@ NVFlinger::~NVFlinger() {
46 CoreTiming::UnscheduleEvent(composition_event, 0); 46 CoreTiming::UnscheduleEvent(composition_event, 0);
47} 47}
48 48
49void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
50 nvdrv = std::move(instance);
51}
52
49u64 NVFlinger::OpenDisplay(std::string_view name) { 53u64 NVFlinger::OpenDisplay(std::string_view name) {
50 LOG_WARNING(Service, "Opening display {}", name); 54 LOG_WARNING(Service, "Opening display {}", name);
51 55
@@ -141,9 +145,6 @@ void NVFlinger::Compose() {
141 auto& igbp_buffer = buffer->igbp_buffer; 145 auto& igbp_buffer = buffer->igbp_buffer;
142 146
143 // Now send the buffer to the GPU for drawing. 147 // Now send the buffer to the GPU for drawing.
144 auto nvdrv = Nvidia::nvdrv.lock();
145 ASSERT(nvdrv);
146
147 // TODO(Subv): Support more than just disp0. The display device selection is probably based 148 // TODO(Subv): Support more than just disp0. The display device selection is probably based
148 // on which display we're drawing (Default, Internal, External, etc) 149 // on which display we're drawing (Default, Internal, External, etc)
149 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); 150 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 5374df175..f7112949f 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -16,6 +16,10 @@ namespace CoreTiming {
16struct EventType; 16struct EventType;
17} 17}
18 18
19namespace Service::Nvidia {
20class Module;
21}
22
19namespace Service::NVFlinger { 23namespace Service::NVFlinger {
20 24
21class BufferQueue; 25class BufferQueue;
@@ -44,6 +48,9 @@ public:
44 NVFlinger(); 48 NVFlinger();
45 ~NVFlinger(); 49 ~NVFlinger();
46 50
51 /// Sets the NVDrv module instance to use to send buffers to the GPU.
52 void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
53
47 /// Opens the specified display and returns the id. 54 /// Opens the specified display and returns the id.
48 u64 OpenDisplay(std::string_view name); 55 u64 OpenDisplay(std::string_view name);
49 56
@@ -70,6 +77,8 @@ private:
70 /// Returns the layer identified by the specified id in the desired display. 77 /// Returns the layer identified by the specified id in the desired display.
71 Layer& GetLayer(u64 display_id, u64 layer_id); 78 Layer& GetLayer(u64 display_id, u64 layer_id);
72 79
80 std::shared_ptr<Nvidia::Module> nvdrv;
81
73 std::vector<Display> displays; 82 std::vector<Display> displays;
74 std::vector<std::shared_ptr<BufferQueue>> buffer_queues; 83 std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
75 84
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 889cdd41a..11951adaf 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -198,7 +198,7 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
198} 198}
199 199
200/// Initialize ServiceManager 200/// Initialize ServiceManager
201void Init(std::shared_ptr<SM::ServiceManager>& sm) { 201void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) {
202 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 202 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
203 // here and pass it into the respective InstallInterfaces functions. 203 // here and pass it into the respective InstallInterfaces functions.
204 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); 204 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
@@ -221,7 +221,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
221 EUPLD::InstallInterfaces(*sm); 221 EUPLD::InstallInterfaces(*sm);
222 Fatal::InstallInterfaces(*sm); 222 Fatal::InstallInterfaces(*sm);
223 FGM::InstallInterfaces(*sm); 223 FGM::InstallInterfaces(*sm);
224 FileSystem::InstallInterfaces(*sm); 224 FileSystem::InstallInterfaces(*sm, rfs);
225 Friend::InstallInterfaces(*sm); 225 Friend::InstallInterfaces(*sm);
226 GRC::InstallInterfaces(*sm); 226 GRC::InstallInterfaces(*sm);
227 HID::InstallInterfaces(*sm); 227 HID::InstallInterfaces(*sm);
@@ -238,7 +238,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
238 NIFM::InstallInterfaces(*sm); 238 NIFM::InstallInterfaces(*sm);
239 NIM::InstallInterfaces(*sm); 239 NIM::InstallInterfaces(*sm);
240 NS::InstallInterfaces(*sm); 240 NS::InstallInterfaces(*sm);
241 Nvidia::InstallInterfaces(*sm); 241 Nvidia::InstallInterfaces(*sm, *nv_flinger);
242 PCIe::InstallInterfaces(*sm); 242 PCIe::InstallInterfaces(*sm);
243 PCTL::InstallInterfaces(*sm); 243 PCTL::InstallInterfaces(*sm);
244 PCV::InstallInterfaces(*sm); 244 PCV::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 046c5e18d..8a294c0f2 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -22,6 +22,10 @@ class ServerSession;
22class HLERequestContext; 22class HLERequestContext;
23} // namespace Kernel 23} // namespace Kernel
24 24
25namespace FileSys {
26struct VfsFilesystem;
27}
28
25namespace Service { 29namespace Service {
26 30
27namespace SM { 31namespace SM {
@@ -177,7 +181,8 @@ private:
177}; 181};
178 182
179/// Initialize ServiceManager 183/// Initialize ServiceManager
180void Init(std::shared_ptr<SM::ServiceManager>& sm); 184void Init(std::shared_ptr<SM::ServiceManager>& sm,
185 const std::shared_ptr<FileSys::VfsFilesystem>& vfs);
181 186
182/// Shutdown ServiceManager 187/// Shutdown ServiceManager
183void Shutdown(); 188void Shutdown();
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 9a8cdd0ff..915d525b0 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -7,6 +7,7 @@
7#include "common/file_util.h" 7#include "common/file_util.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h"
10#include "core/gdbstub/gdbstub.h" 11#include "core/gdbstub/gdbstub.h"
11#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
12#include "core/hle/kernel/resource_limit.h" 13#include "core/hle/kernel/resource_limit.h"
@@ -17,8 +18,50 @@
17 18
18namespace Loader { 19namespace Loader {
19 20
20AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file) 21AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_)
21 : AppLoader(std::move(file)) {} 22 : AppLoader(std::move(file_)) {
23 const auto dir = file->GetContainingDirectory();
24
25 // Icon
26 FileSys::VirtualFile icon_file = nullptr;
27 for (const auto& language : FileSys::LANGUAGE_NAMES) {
28 icon_file = dir->GetFile("icon_" + std::string(language) + ".dat");
29 if (icon_file != nullptr) {
30 icon_data = icon_file->ReadAllBytes();
31 break;
32 }
33 }
34
35 if (icon_data.empty()) {
36 // Any png, jpeg, or bmp file
37 const auto& files = dir->GetFiles();
38 const auto icon_iter =
39 std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
40 return file->GetExtension() == "png" || file->GetExtension() == "jpg" ||
41 file->GetExtension() == "bmp" || file->GetExtension() == "jpeg";
42 });
43 if (icon_iter != files.end())
44 icon_data = (*icon_iter)->ReadAllBytes();
45 }
46
47 // Metadata
48 FileSys::VirtualFile nacp_file = dir->GetFile("control.nacp");
49 if (nacp_file == nullptr) {
50 const auto& files = dir->GetFiles();
51 const auto nacp_iter =
52 std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
53 return file->GetExtension() == "nacp";
54 });
55 if (nacp_iter != files.end())
56 nacp_file = *nacp_iter;
57 }
58
59 if (nacp_file != nullptr) {
60 FileSys::NACP nacp(nacp_file);
61 title_id = nacp.GetTitleId();
62 name = nacp.GetApplicationName();
63 }
64}
22 65
23AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( 66AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
24 FileSys::VirtualDir directory) 67 FileSys::VirtualDir directory)
@@ -105,4 +148,25 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile
105 return ResultStatus::Success; 148 return ResultStatus::Success;
106} 149}
107 150
151ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) {
152 if (icon_data.empty())
153 return ResultStatus::ErrorNotUsed;
154 buffer = icon_data;
155 return ResultStatus::Success;
156}
157
158ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
159 if (name.empty())
160 return ResultStatus::ErrorNotUsed;
161 out_program_id = title_id;
162 return ResultStatus::Success;
163}
164
165ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) {
166 if (name.empty())
167 return ResultStatus::ErrorNotUsed;
168 title = name;
169 return ResultStatus::Success;
170}
171
108} // namespace Loader 172} // namespace Loader
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 7d5433563..b20804f75 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -39,11 +39,18 @@ public:
39 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 39 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
40 40
41 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 41 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
42 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
43 ResultStatus ReadProgramId(u64& out_program_id) override;
44 ResultStatus ReadTitle(std::string& title) override;
42 45
43private: 46private:
44 FileSys::ProgramMetadata metadata; 47 FileSys::ProgramMetadata metadata;
45 FileSys::VirtualFile romfs; 48 FileSys::VirtualFile romfs;
46 FileSys::VirtualDir dir; 49 FileSys::VirtualDir dir;
50
51 std::vector<u8> icon_data;
52 std::string name;
53 u64 title_id{};
47}; 54};
48 55
49} // namespace Loader 56} // namespace Loader
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 57e6c0365..a288654df 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -43,10 +43,6 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
43 return FileType::Unknown; 43 return FileType::Unknown;
44} 44}
45 45
46FileType IdentifyFile(const std::string& file_name) {
47 return IdentifyFile(std::make_shared<FileSys::RealVfsFile>(file_name));
48}
49
50FileType GuessFromFilename(const std::string& name) { 46FileType GuessFromFilename(const std::string& name) {
51 if (name == "main") 47 if (name == "main")
52 return FileType::DeconstructedRomDirectory; 48 return FileType::DeconstructedRomDirectory;
@@ -68,7 +64,7 @@ FileType GuessFromFilename(const std::string& name) {
68 return FileType::Unknown; 64 return FileType::Unknown;
69} 65}
70 66
71const char* GetFileTypeString(FileType type) { 67std::string GetFileTypeString(FileType type) {
72 switch (type) { 68 switch (type) {
73 case FileType::ELF: 69 case FileType::ELF:
74 return "ELF"; 70 return "ELF";
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index e69ab85ef..6a9e5a68b 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -43,14 +43,6 @@ enum class FileType {
43FileType IdentifyFile(FileSys::VirtualFile file); 43FileType IdentifyFile(FileSys::VirtualFile file);
44 44
45/** 45/**
46 * Identifies the type of a bootable file based on the magic value in its header.
47 * @param file_name path to file
48 * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine
49 * a filetype, and will never return FileType::Error.
50 */
51FileType IdentifyFile(const std::string& file_name);
52
53/**
54 * Guess the type of a bootable file from its name 46 * Guess the type of a bootable file from its name
55 * @param name String name of bootable file 47 * @param name String name of bootable file
56 * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine 48 * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine
@@ -61,7 +53,7 @@ FileType GuessFromFilename(const std::string& name);
61/** 53/**
62 * Convert a FileType into a string which can be displayed to the user. 54 * Convert a FileType into a string which can be displayed to the user.
63 */ 55 */
64const char* GetFileTypeString(FileType type); 56std::string GetFileTypeString(FileType type);
65 57
66/// Return type for functions in Loader namespace 58/// Return type for functions in Loader namespace
67enum class ResultStatus { 59enum class ResultStatus {
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index dbc67c0b5..46f5cd393 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -77,8 +77,8 @@ ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
77} 77}
78 78
79ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { 79ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
80 if (nca == nullptr) 80 if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
81 return ResultStatus::ErrorNotLoaded; 81 return ResultStatus::ErrorInvalidFormat;
82 out_program_id = nca->GetTitleId(); 82 out_program_id = nca->GetTitleId();
83 return ResultStatus::Success; 83 return ResultStatus::Success;
84} 84}
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index 0fd2d0417..7f7d8ea0b 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -33,7 +33,6 @@ public:
33 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 33 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
34 34
35 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 35 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
36
37 ResultStatus ReadProgramId(u64& out_program_id) override; 36 ResultStatus ReadProgramId(u64& out_program_id) override;
38 37
39 ~AppLoader_NCA(); 38 ~AppLoader_NCA();
@@ -41,6 +40,7 @@ public:
41private: 40private:
42 FileSys::ProgramMetadata metadata; 41 FileSys::ProgramMetadata metadata;
43 42
43 FileSys::NCAHeader header;
44 std::unique_ptr<FileSys::NCA> nca; 44 std::unique_ptr<FileSys::NCA> nca;
45 std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader; 45 std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
46}; 46};
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index eb4dee2c2..d3fe24419 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -26,7 +26,25 @@ namespace Loader {
26AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) 26AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
27 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), 27 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
28 nca_loader(std::make_unique<AppLoader_NCA>( 28 nca_loader(std::make_unique<AppLoader_NCA>(
29 xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {} 29 xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {
30 if (xci->GetStatus() != ResultStatus::Success)
31 return;
32 const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
33 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
34 return;
35 const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS());
36 if (romfs == nullptr)
37 return;
38 for (const auto& language : FileSys::LANGUAGE_NAMES) {
39 icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat");
40 if (icon_file != nullptr)
41 break;
42 }
43 const auto nacp_raw = romfs->GetFile("control.nacp");
44 if (nacp_raw == nullptr)
45 return;
46 nacp_file = std::make_shared<FileSys::NACP>(nacp_raw);
47}
30 48
31AppLoader_XCI::~AppLoader_XCI() = default; 49AppLoader_XCI::~AppLoader_XCI() = default;
32 50
@@ -71,4 +89,17 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
71 return nca_loader->ReadProgramId(out_program_id); 89 return nca_loader->ReadProgramId(out_program_id);
72} 90}
73 91
92ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) {
93 if (icon_file == nullptr)
94 return ResultStatus::ErrorInvalidFormat;
95 buffer = icon_file->ReadAllBytes();
96 return ResultStatus::Success;
97}
98
99ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
100 if (nacp_file == nullptr)
101 return ResultStatus::ErrorInvalidFormat;
102 title = nacp_file->GetApplicationName();
103 return ResultStatus::Success;
104}
74} // namespace Loader 105} // namespace Loader
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 0dbcfbdf8..973833050 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -33,12 +33,17 @@ public:
33 33
34 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 34 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
35 ResultStatus ReadProgramId(u64& out_program_id) override; 35 ResultStatus ReadProgramId(u64& out_program_id) override;
36 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
37 ResultStatus ReadTitle(std::string& title) override;
36 38
37private: 39private:
38 FileSys::ProgramMetadata metadata; 40 FileSys::ProgramMetadata metadata;
39 41
40 std::unique_ptr<FileSys::XCI> xci; 42 std::unique_ptr<FileSys::XCI> xci;
41 std::unique_ptr<AppLoader_NCA> nca_loader; 43 std::unique_ptr<AppLoader_NCA> nca_loader;
44
45 FileSys::VirtualFile icon_file;
46 std::shared_ptr<FileSys::NACP> nacp_file;
42}; 47};
43 48
44} // namespace Loader 49} // namespace Loader
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 5c0ae8009..a46ed4bd7 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -23,12 +23,17 @@ Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager&
23 : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {} 23 : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {}
24 24
25void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 25void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
26 auto macro_code = uploaded_macros.find(method); 26 // Reset the current macro.
27 executing_macro = 0;
28
27 // The requested macro must have been uploaded already. 29 // The requested macro must have been uploaded already.
28 ASSERT_MSG(macro_code != uploaded_macros.end(), "Macro %08X was not uploaded", method); 30 auto macro_code = uploaded_macros.find(method);
31 if (macro_code == uploaded_macros.end()) {
32 LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method);
33 return;
34 }
29 35
30 // Reset the current macro and execute it. 36 // Execute the current macro.
31 executing_macro = 0;
32 macro_interpreter.Execute(macro_code->second, std::move(parameters)); 37 macro_interpreter.Execute(macro_code->second, std::move(parameters));
33} 38}
34 39
@@ -238,6 +243,8 @@ void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
238 243
239 auto& buffer = shader.const_buffers[bind_data.index]; 244 auto& buffer = shader.const_buffers[bind_data.index];
240 245
246 ASSERT(bind_data.index < Regs::MaxConstBuffers);
247
241 buffer.enabled = bind_data.valid.Value() != 0; 248 buffer.enabled = bind_data.valid.Value() != 0;
242 buffer.index = bind_data.index; 249 buffer.index = bind_data.index;
243 buffer.address = regs.const_buffer.BufferAddress(); 250 buffer.address = regs.const_buffer.BufferAddress();
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 4d0ff96a5..0506ac8fe 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -44,7 +44,7 @@ public:
44 static constexpr size_t MaxShaderProgram = 6; 44 static constexpr size_t MaxShaderProgram = 6;
45 static constexpr size_t MaxShaderStage = 5; 45 static constexpr size_t MaxShaderStage = 5;
46 // Maximum number of const buffers per shader stage. 46 // Maximum number of const buffers per shader stage.
47 static constexpr size_t MaxConstBuffers = 16; 47 static constexpr size_t MaxConstBuffers = 18;
48 48
49 enum class QueryMode : u32 { 49 enum class QueryMode : u32 {
50 Write = 0, 50 Write = 0,
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index c7e3fb4b1..3d4557b7e 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -78,6 +78,8 @@ union Attribute {
78 // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval 78 // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
79 // shader. 79 // shader.
80 TessCoordInstanceIDVertexID = 47, 80 TessCoordInstanceIDVertexID = 47,
81 // TODO(bunnei): Figure out what this is used for. Super Mario Odyssey uses this.
82 Unknown_63 = 63,
81 }; 83 };
82 84
83 union { 85 union {
@@ -254,20 +256,15 @@ union Instruction {
254 BitField<56, 1, u64> invert_b; 256 BitField<56, 1, u64> invert_b;
255 } lop32i; 257 } lop32i;
256 258
257 float GetImm20_19() const { 259 u32 GetImm20_19() const {
258 float result{};
259 u32 imm{static_cast<u32>(imm20_19)}; 260 u32 imm{static_cast<u32>(imm20_19)};
260 imm <<= 12; 261 imm <<= 12;
261 imm |= negate_imm ? 0x80000000 : 0; 262 imm |= negate_imm ? 0x80000000 : 0;
262 std::memcpy(&result, &imm, sizeof(imm)); 263 return imm;
263 return result;
264 } 264 }
265 265
266 float GetImm20_32() const { 266 u32 GetImm20_32() const {
267 float result{}; 267 return static_cast<u32>(imm20_32);
268 s32 imm{static_cast<s32>(imm20_32)};
269 std::memcpy(&result, &imm, sizeof(imm));
270 return result;
271 } 268 }
272 269
273 s32 GetSignedImm20_20() const { 270 s32 GetSignedImm20_20() const {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index b2a83ce0b..4ff4d71c5 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -42,6 +42,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
42 case RenderTargetFormat::RGB10_A2_UNORM: 42 case RenderTargetFormat::RGB10_A2_UNORM:
43 case RenderTargetFormat::BGRA8_UNORM: 43 case RenderTargetFormat::BGRA8_UNORM:
44 case RenderTargetFormat::R32_FLOAT: 44 case RenderTargetFormat::R32_FLOAT:
45 case RenderTargetFormat::R11G11B10_FLOAT:
45 return 4; 46 return 4;
46 default: 47 default:
47 UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format)); 48 UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 440505c9d..874eddd78 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -34,6 +34,7 @@ enum class RenderTargetFormat : u32 {
34 RG16_FLOAT = 0xDE, 34 RG16_FLOAT = 0xDE,
35 R11G11B10_FLOAT = 0xE0, 35 R11G11B10_FLOAT = 0xE0,
36 R32_FLOAT = 0xE5, 36 R32_FLOAT = 0xE5,
37 B5G6R5_UNORM = 0xE8,
37 R16_FLOAT = 0xF2, 38 R16_FLOAT = 0xF2,
38 R8_UNORM = 0xF3, 39 R8_UNORM = 0xF3,
39}; 40};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index c2a931469..8360feb5d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -161,7 +161,7 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
161 // assume every shader uses them all. 161 // assume every shader uses them all.
162 for (unsigned index = 0; index < 16; ++index) { 162 for (unsigned index = 0; index < 16; ++index) {
163 auto& attrib = regs.vertex_attrib_format[index]; 163 auto& attrib = regs.vertex_attrib_format[index];
164 LOG_DEBUG(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}", 164 LOG_TRACE(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}",
165 index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(), 165 index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(),
166 attrib.offset.Value(), attrib.IsNormalized()); 166 attrib.offset.Value(), attrib.IsNormalized());
167 167
@@ -324,11 +324,14 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c
324 bool using_depth_fb) { 324 bool using_depth_fb) {
325 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 325 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
326 326
327 if (regs.rt[0].format == Tegra::RenderTargetFormat::NONE) {
328 LOG_ERROR(HW_GPU, "RenderTargetFormat is not configured");
329 using_color_fb = false;
330 }
331
327 // TODO(bunnei): Implement this 332 // TODO(bunnei): Implement this
328 const bool has_stencil = false; 333 const bool has_stencil = false;
329 334
330 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
331
332 const bool write_color_fb = 335 const bool write_color_fb =
333 state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE || 336 state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE ||
334 state.color_mask.blue_enabled == GL_TRUE || state.color_mask.alpha_enabled == GL_TRUE; 337 state.color_mask.blue_enabled == GL_TRUE || state.color_mask.alpha_enabled == GL_TRUE;
@@ -341,9 +344,10 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c
341 Surface depth_surface; 344 Surface depth_surface;
342 MathUtil::Rectangle<u32> surfaces_rect; 345 MathUtil::Rectangle<u32> surfaces_rect;
343 std::tie(color_surface, depth_surface, surfaces_rect) = 346 std::tie(color_surface, depth_surface, surfaces_rect) =
344 res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect); 347 res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb);
345 348
346 MathUtil::Rectangle<u32> draw_rect{ 349 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
350 const MathUtil::Rectangle<u32> draw_rect{
347 static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left, 351 static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left,
348 surfaces_rect.left, surfaces_rect.right)), // Left 352 surfaces_rect.left, surfaces_rect.right)), // Left
349 static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top, 353 static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top,
@@ -659,7 +663,10 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
659 auto& buffer_draw_state = 663 auto& buffer_draw_state =
660 state.draw.const_buffers[static_cast<size_t>(stage)][used_buffer.GetIndex()]; 664 state.draw.const_buffers[static_cast<size_t>(stage)][used_buffer.GetIndex()];
661 665
662 ASSERT_MSG(buffer.enabled, "Attempted to upload disabled constbuffer"); 666 if (!buffer.enabled) {
667 continue;
668 }
669
663 buffer_draw_state.enabled = true; 670 buffer_draw_state.enabled = true;
664 buffer_draw_state.bindpoint = current_bindpoint + bindpoint; 671 buffer_draw_state.bindpoint = current_bindpoint + bindpoint;
665 672
@@ -804,9 +811,7 @@ void RasterizerOpenGL::SyncClipCoef() {
804void RasterizerOpenGL::SyncCullMode() { 811void RasterizerOpenGL::SyncCullMode() {
805 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 812 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
806 813
807 // TODO(bunnei): Enable the below once more things work - until then, this may hide regressions 814 state.cull.enabled = regs.cull.enabled != 0;
808 // state.cull.enabled = regs.cull.enabled != 0;
809 state.cull.enabled = false;
810 815
811 if (state.cull.enabled) { 816 if (state.cull.enabled) {
812 state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face); 817 state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 257aa9571..9fb734b77 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -109,6 +109,9 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
109 {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 109 {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
110 true}, // DXT45 110 true}, // DXT45
111 {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1 111 {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1
112 {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
113 true}, // DXN2UNORM
114 {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM
112 {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 115 {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
113 true}, // BC7U 116 true}, // BC7U
114 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 117 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
@@ -180,36 +183,49 @@ MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {
180 return {0, actual_height, width, 0}; 183 return {0, actual_height, width, 0};
181} 184}
182 185
186/// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN
187static bool IsFormatBCn(PixelFormat format) {
188 switch (format) {
189 case PixelFormat::DXT1:
190 case PixelFormat::DXT23:
191 case PixelFormat::DXT45:
192 case PixelFormat::DXN1:
193 case PixelFormat::DXN2SNORM:
194 case PixelFormat::DXN2UNORM:
195 case PixelFormat::BC7U:
196 return true;
197 }
198 return false;
199}
200
183template <bool morton_to_gl, PixelFormat format> 201template <bool morton_to_gl, PixelFormat format>
184void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::GPUVAddr addr) { 202void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer,
203 Tegra::GPUVAddr addr) {
185 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 204 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
186 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); 205 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
187 const auto& gpu = Core::System::GetInstance().GPU(); 206 const auto& gpu = Core::System::GetInstance().GPU();
188 207
189 if (morton_to_gl) { 208 if (morton_to_gl) {
190 if (SurfaceParams::GetFormatType(format) == SurfaceType::ColorTexture) { 209 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
191 auto data = Tegra::Texture::UnswizzleTexture( 210 // pixel values.
192 *gpu.memory_manager->GpuToCpuAddress(addr), 211 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
193 SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height); 212 const std::vector<u8> data =
194 std::memcpy(gl_buffer, data.data(), data.size()); 213 Tegra::Texture::UnswizzleTexture(*gpu.memory_manager->GpuToCpuAddress(addr), tile_size,
195 } else { 214 bytes_per_pixel, stride, height, block_height);
196 auto data = Tegra::Texture::UnswizzleDepthTexture( 215 const size_t size_to_copy{std::min(gl_buffer.size(), data.size())};
197 *gpu.memory_manager->GpuToCpuAddress(addr), 216 gl_buffer.assign(data.begin(), data.begin() + size_to_copy);
198 SurfaceParams::DepthFormatFromPixelFormat(format), stride, height, block_height);
199 std::memcpy(gl_buffer, data.data(), data.size());
200 }
201 } else { 217 } else {
202 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should 218 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
203 // check the configuration for this and perform more generic un/swizzle 219 // check the configuration for this and perform more generic un/swizzle
204 LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); 220 LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
205 VideoCore::MortonCopyPixels128( 221 VideoCore::MortonCopyPixels128(
206 stride, height, bytes_per_pixel, gl_bytes_per_pixel, 222 stride, height, bytes_per_pixel, gl_bytes_per_pixel,
207 Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(addr)), gl_buffer, 223 Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(addr)), gl_buffer.data(),
208 morton_to_gl); 224 morton_to_gl);
209 } 225 }
210} 226}
211 227
212static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), 228static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr),
213 SurfaceParams::MaxPixelFormat> 229 SurfaceParams::MaxPixelFormat>
214 morton_to_gl_fns = { 230 morton_to_gl_fns = {
215 MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>, 231 MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>,
@@ -218,6 +234,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
218 MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>, 234 MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>,
219 MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>, 235 MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
220 MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>, 236 MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>,
237 MortonCopy<true, PixelFormat::DXN2UNORM>, MortonCopy<true, PixelFormat::DXN2SNORM>,
221 MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>, 238 MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
222 MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::BGRA8>, 239 MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::BGRA8>,
223 MortonCopy<true, PixelFormat::RGBA32F>, MortonCopy<true, PixelFormat::RG32F>, 240 MortonCopy<true, PixelFormat::RGBA32F>, MortonCopy<true, PixelFormat::RG32F>,
@@ -231,7 +248,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
231 MortonCopy<true, PixelFormat::Z32FS8>, 248 MortonCopy<true, PixelFormat::Z32FS8>,
232}; 249};
233 250
234static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), 251static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr),
235 SurfaceParams::MaxPixelFormat> 252 SurfaceParams::MaxPixelFormat>
236 gl_to_morton_fns = { 253 gl_to_morton_fns = {
237 MortonCopy<false, PixelFormat::ABGR8>, 254 MortonCopy<false, PixelFormat::ABGR8>,
@@ -242,7 +259,10 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
242 MortonCopy<false, PixelFormat::RGBA16F>, 259 MortonCopy<false, PixelFormat::RGBA16F>,
243 MortonCopy<false, PixelFormat::R11FG11FB10F>, 260 MortonCopy<false, PixelFormat::R11FG11FB10F>,
244 MortonCopy<false, PixelFormat::RGBA32UI>, 261 MortonCopy<false, PixelFormat::RGBA32UI>,
245 // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/BC7U/ASTC_2D_4X4 formats is not supported 262 // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/ASTC_2D_4X4 formats is not
263 // supported
264 nullptr,
265 nullptr,
246 nullptr, 266 nullptr,
247 nullptr, 267 nullptr,
248 nullptr, 268 nullptr,
@@ -447,22 +467,24 @@ MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64
447void CachedSurface::LoadGLBuffer() { 467void CachedSurface::LoadGLBuffer() {
448 ASSERT(params.type != SurfaceType::Fill); 468 ASSERT(params.type != SurfaceType::Fill);
449 469
450 u8* const texture_src_data = Memory::GetPointer(params.GetCpuAddr()); 470 const u8* const texture_src_data = Memory::GetPointer(params.GetCpuAddr());
451 471
452 ASSERT(texture_src_data); 472 ASSERT(texture_src_data);
453 473
454 gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); 474 const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format);
475 const u32 copy_size = params.width * params.height * bytes_per_pixel;
455 476
456 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 477 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
457 478
458 if (!params.is_tiled) { 479 if (params.is_tiled) {
459 const u32 bytes_per_pixel{params.GetFormatBpp() >> 3}; 480 gl_buffer.resize(copy_size);
460 481
461 std::memcpy(gl_buffer.data(), texture_src_data,
462 bytes_per_pixel * params.width * params.height);
463 } else {
464 morton_to_gl_fns[static_cast<size_t>(params.pixel_format)]( 482 morton_to_gl_fns[static_cast<size_t>(params.pixel_format)](
465 params.width, params.block_height, params.height, gl_buffer.data(), params.addr); 483 params.width, params.block_height, params.height, gl_buffer, params.addr);
484 } else {
485 const u8* const texture_src_data_end = texture_src_data + copy_size;
486
487 gl_buffer.assign(texture_src_data, texture_src_data_end);
466 } 488 }
467 489
468 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height); 490 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height);
@@ -485,7 +507,7 @@ void CachedSurface::FlushGLBuffer() {
485 std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes); 507 std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes);
486 } else { 508 } else {
487 gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( 509 gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
488 params.width, params.block_height, params.height, gl_buffer.data(), params.addr); 510 params.width, params.block_height, params.height, gl_buffer, params.addr);
489 } 511 }
490} 512}
491 513
@@ -600,8 +622,8 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
600 return GetSurface(SurfaceParams::CreateForTexture(config)); 622 return GetSurface(SurfaceParams::CreateForTexture(config));
601} 623}
602 624
603SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( 625SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool using_color_fb,
604 bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport) { 626 bool using_depth_fb) {
605 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 627 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
606 628
607 // TODO(bunnei): This is hard corded to use just the first render buffer 629 // TODO(bunnei): This is hard corded to use just the first render buffer
@@ -757,10 +779,12 @@ void RasterizerCacheOpenGL::FlushRegion(Tegra::GPUVAddr /*addr*/, size_t /*size*
757} 779}
758 780
759void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, size_t size) { 781void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, size_t size) {
760 for (const auto& pair : surface_cache) { 782 for (auto iter = surface_cache.cbegin(); iter != surface_cache.cend();) {
761 const auto& surface{pair.second}; 783 const auto& surface{iter->second};
762 const auto& params{surface->GetSurfaceParams()}; 784 const auto& params{surface->GetSurfaceParams()};
763 785
786 ++iter;
787
764 if (params.IsOverlappingRegion(addr, size)) { 788 if (params.IsOverlappingRegion(addr, size)) {
765 UnregisterSurface(surface); 789 UnregisterSurface(surface);
766 } 790 }
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 0c6652c7a..829a76dfe 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -35,31 +35,33 @@ struct SurfaceParams {
35 DXT23 = 9, 35 DXT23 = 9,
36 DXT45 = 10, 36 DXT45 = 10,
37 DXN1 = 11, // This is also known as BC4 37 DXN1 = 11, // This is also known as BC4
38 BC7U = 12, 38 DXN2UNORM = 12,
39 ASTC_2D_4X4 = 13, 39 DXN2SNORM = 13,
40 G8R8 = 14, 40 BC7U = 14,
41 BGRA8 = 15, 41 ASTC_2D_4X4 = 15,
42 RGBA32F = 16, 42 G8R8 = 16,
43 RG32F = 17, 43 BGRA8 = 17,
44 R32F = 18, 44 RGBA32F = 18,
45 R16F = 19, 45 RG32F = 19,
46 R16UNORM = 20, 46 R32F = 20,
47 RG16 = 21, 47 R16F = 21,
48 RG16F = 22, 48 R16UNORM = 22,
49 RG16UI = 23, 49 RG16 = 23,
50 RG16I = 24, 50 RG16F = 24,
51 RG16S = 25, 51 RG16UI = 25,
52 RGB32F = 26, 52 RG16I = 26,
53 SRGBA8 = 27, 53 RG16S = 27,
54 RGB32F = 28,
55 SRGBA8 = 29,
54 56
55 MaxColorFormat, 57 MaxColorFormat,
56 58
57 // DepthStencil formats 59 // DepthStencil formats
58 Z24S8 = 28, 60 Z24S8 = 30,
59 S8Z24 = 29, 61 S8Z24 = 31,
60 Z32F = 30, 62 Z32F = 32,
61 Z16 = 31, 63 Z16 = 33,
62 Z32FS8 = 32, 64 Z32FS8 = 34,
63 65
64 MaxDepthStencilFormat, 66 MaxDepthStencilFormat,
65 67
@@ -109,6 +111,8 @@ struct SurfaceParams {
109 4, // DXT23 111 4, // DXT23
110 4, // DXT45 112 4, // DXT45
111 4, // DXN1 113 4, // DXN1
114 4, // DXN2UNORM
115 4, // DXN2SNORM
112 4, // BC7U 116 4, // BC7U
113 4, // ASTC_2D_4X4 117 4, // ASTC_2D_4X4
114 1, // G8R8 118 1, // G8R8
@@ -153,6 +157,8 @@ struct SurfaceParams {
153 128, // DXT23 157 128, // DXT23
154 128, // DXT45 158 128, // DXT45
155 64, // DXN1 159 64, // DXN1
160 128, // DXN2UNORM
161 128, // DXN2SNORM
156 128, // BC7U 162 128, // BC7U
157 32, // ASTC_2D_4X4 163 32, // ASTC_2D_4X4
158 16, // G8R8 164 16, // G8R8
@@ -221,6 +227,8 @@ struct SurfaceParams {
221 return PixelFormat::RG32F; 227 return PixelFormat::RG32F;
222 case Tegra::RenderTargetFormat::R11G11B10_FLOAT: 228 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
223 return PixelFormat::R11FG11FB10F; 229 return PixelFormat::R11FG11FB10F;
230 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
231 return PixelFormat::B5G6R5;
224 case Tegra::RenderTargetFormat::RGBA32_UINT: 232 case Tegra::RenderTargetFormat::RGBA32_UINT:
225 return PixelFormat::RGBA32UI; 233 return PixelFormat::RGBA32UI;
226 case Tegra::RenderTargetFormat::R8_UNORM: 234 case Tegra::RenderTargetFormat::R8_UNORM:
@@ -303,6 +311,16 @@ struct SurfaceParams {
303 return PixelFormat::DXT45; 311 return PixelFormat::DXT45;
304 case Tegra::Texture::TextureFormat::DXN1: 312 case Tegra::Texture::TextureFormat::DXN1:
305 return PixelFormat::DXN1; 313 return PixelFormat::DXN1;
314 case Tegra::Texture::TextureFormat::DXN2:
315 switch (component_type) {
316 case Tegra::Texture::ComponentType::UNORM:
317 return PixelFormat::DXN2UNORM;
318 case Tegra::Texture::ComponentType::SNORM:
319 return PixelFormat::DXN2SNORM;
320 }
321 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
322 static_cast<u32>(component_type));
323 UNREACHABLE();
306 case Tegra::Texture::TextureFormat::BC7U: 324 case Tegra::Texture::TextureFormat::BC7U:
307 return PixelFormat::BC7U; 325 return PixelFormat::BC7U;
308 case Tegra::Texture::TextureFormat::ASTC_2D_4X4: 326 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
@@ -330,89 +348,6 @@ struct SurfaceParams {
330 } 348 }
331 } 349 }
332 350
333 static Tegra::Texture::TextureFormat TextureFormatFromPixelFormat(PixelFormat format) {
334 // TODO(Subv): Properly implement this
335 switch (format) {
336 case PixelFormat::ABGR8:
337 case PixelFormat::SRGBA8:
338 return Tegra::Texture::TextureFormat::A8R8G8B8;
339 case PixelFormat::B5G6R5:
340 return Tegra::Texture::TextureFormat::B5G6R5;
341 case PixelFormat::A2B10G10R10:
342 return Tegra::Texture::TextureFormat::A2B10G10R10;
343 case PixelFormat::A1B5G5R5:
344 return Tegra::Texture::TextureFormat::A1B5G5R5;
345 case PixelFormat::R8:
346 return Tegra::Texture::TextureFormat::R8;
347 case PixelFormat::G8R8:
348 return Tegra::Texture::TextureFormat::G8R8;
349 case PixelFormat::RGBA16F:
350 return Tegra::Texture::TextureFormat::R16_G16_B16_A16;
351 case PixelFormat::R11FG11FB10F:
352 return Tegra::Texture::TextureFormat::BF10GF11RF11;
353 case PixelFormat::RGBA32UI:
354 return Tegra::Texture::TextureFormat::R32_G32_B32_A32;
355 case PixelFormat::DXT1:
356 return Tegra::Texture::TextureFormat::DXT1;
357 case PixelFormat::DXT23:
358 return Tegra::Texture::TextureFormat::DXT23;
359 case PixelFormat::DXT45:
360 return Tegra::Texture::TextureFormat::DXT45;
361 case PixelFormat::DXN1:
362 return Tegra::Texture::TextureFormat::DXN1;
363 case PixelFormat::BC7U:
364 return Tegra::Texture::TextureFormat::BC7U;
365 case PixelFormat::ASTC_2D_4X4:
366 return Tegra::Texture::TextureFormat::ASTC_2D_4X4;
367 case PixelFormat::BGRA8:
368 // TODO(bunnei): This is fine for unswizzling (since we just need the right component
369 // sizes), but could be a bug if we used this function in different ways.
370 return Tegra::Texture::TextureFormat::A8R8G8B8;
371 case PixelFormat::RGBA32F:
372 return Tegra::Texture::TextureFormat::R32_G32_B32_A32;
373 case PixelFormat::RGB32F:
374 return Tegra::Texture::TextureFormat::R32_G32_B32;
375 case PixelFormat::RG32F:
376 return Tegra::Texture::TextureFormat::R32_G32;
377 case PixelFormat::R32F:
378 return Tegra::Texture::TextureFormat::R32;
379 case PixelFormat::R16F:
380 case PixelFormat::R16UNORM:
381 return Tegra::Texture::TextureFormat::R16;
382 case PixelFormat::Z32F:
383 return Tegra::Texture::TextureFormat::ZF32;
384 case PixelFormat::Z24S8:
385 return Tegra::Texture::TextureFormat::Z24S8;
386 case PixelFormat::RG16F:
387 case PixelFormat::RG16:
388 case PixelFormat::RG16UI:
389 case PixelFormat::RG16I:
390 case PixelFormat::RG16S:
391 return Tegra::Texture::TextureFormat::R16_G16;
392 default:
393 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
394 UNREACHABLE();
395 }
396 }
397
398 static Tegra::DepthFormat DepthFormatFromPixelFormat(PixelFormat format) {
399 switch (format) {
400 case PixelFormat::S8Z24:
401 return Tegra::DepthFormat::S8_Z24_UNORM;
402 case PixelFormat::Z24S8:
403 return Tegra::DepthFormat::Z24_S8_UNORM;
404 case PixelFormat::Z32F:
405 return Tegra::DepthFormat::Z32_FLOAT;
406 case PixelFormat::Z16:
407 return Tegra::DepthFormat::Z16_UNORM;
408 case PixelFormat::Z32FS8:
409 return Tegra::DepthFormat::Z32_S8_X24_FLOAT;
410 default:
411 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
412 UNREACHABLE();
413 }
414 }
415
416 static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { 351 static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
417 // TODO(Subv): Implement more component types 352 // TODO(Subv): Implement more component types
418 switch (type) { 353 switch (type) {
@@ -441,6 +376,7 @@ struct SurfaceParams {
441 case Tegra::RenderTargetFormat::RGB10_A2_UNORM: 376 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
442 case Tegra::RenderTargetFormat::R8_UNORM: 377 case Tegra::RenderTargetFormat::R8_UNORM:
443 case Tegra::RenderTargetFormat::RG16_UNORM: 378 case Tegra::RenderTargetFormat::RG16_UNORM:
379 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
444 return ComponentType::UNorm; 380 return ComponentType::UNorm;
445 case Tegra::RenderTargetFormat::RG16_SNORM: 381 case Tegra::RenderTargetFormat::RG16_SNORM:
446 return ComponentType::SNorm; 382 return ComponentType::SNorm;
@@ -612,8 +548,7 @@ public:
612 Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config); 548 Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config);
613 549
614 /// Get the color and depth surfaces based on the framebuffer configuration 550 /// Get the color and depth surfaces based on the framebuffer configuration
615 SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, 551 SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb);
616 const MathUtil::Rectangle<s32>& viewport);
617 552
618 /// Flushes the surface to Switch memory 553 /// Flushes the surface to Switch memory
619 void FlushSurface(const Surface& surface); 554 void FlushSurface(const Surface& surface);
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index e3217db81..32f06f409 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -507,6 +507,8 @@ private:
507 507
508 /// Build the GLSL register list. 508 /// Build the GLSL register list.
509 void BuildRegisterList() { 509 void BuildRegisterList() {
510 regs.reserve(Register::NumRegisters);
511
510 for (size_t index = 0; index < Register::NumRegisters; ++index) { 512 for (size_t index = 0; index < Register::NumRegisters; ++index) {
511 regs.emplace_back(index, suffix); 513 regs.emplace_back(index, suffix);
512 } 514 }
@@ -523,6 +525,11 @@ private:
523 // shader. 525 // shader.
524 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex); 526 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex);
525 return "vec4(0, 0, uintBitsToFloat(gl_InstanceID), uintBitsToFloat(gl_VertexID))"; 527 return "vec4(0, 0, uintBitsToFloat(gl_InstanceID), uintBitsToFloat(gl_VertexID))";
528 case Attribute::Index::Unknown_63:
529 // TODO(bunnei): Figure out what this is used for. Super Mario Odyssey uses this.
530 LOG_CRITICAL(HW_GPU, "Unhandled input attribute Unknown_63");
531 UNREACHABLE();
532 break;
526 default: 533 default:
527 const u32 index{static_cast<u32>(attribute) - 534 const u32 index{static_cast<u32>(attribute) -
528 static_cast<u32>(Attribute::Index::Attribute_0)}; 535 static_cast<u32>(Attribute::Index::Attribute_0)};
@@ -534,6 +541,8 @@ private:
534 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index); 541 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index);
535 UNREACHABLE(); 542 UNREACHABLE();
536 } 543 }
544
545 return "vec4(0, 0, 0, 0)";
537 } 546 }
538 547
539 /// Generates code representing an output attribute register. 548 /// Generates code representing an output attribute register.
@@ -602,12 +611,12 @@ private:
602 611
603 /// Generates code representing a 19-bit immediate value 612 /// Generates code representing a 19-bit immediate value
604 static std::string GetImmediate19(const Instruction& instr) { 613 static std::string GetImmediate19(const Instruction& instr) {
605 return std::to_string(instr.alu.GetImm20_19()); 614 return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_19());
606 } 615 }
607 616
608 /// Generates code representing a 32-bit immediate value 617 /// Generates code representing a 32-bit immediate value
609 static std::string GetImmediate32(const Instruction& instr) { 618 static std::string GetImmediate32(const Instruction& instr) {
610 return std::to_string(instr.alu.GetImm20_32()); 619 return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32());
611 } 620 }
612 621
613 /// Generates code representing a texture sampler. 622 /// Generates code representing a texture sampler.
@@ -650,16 +659,17 @@ private:
650 * @param instr Instruction to generate the if condition for. 659 * @param instr Instruction to generate the if condition for.
651 * @returns string containing the predicate condition. 660 * @returns string containing the predicate condition.
652 */ 661 */
653 std::string GetPredicateCondition(u64 index, bool negate) const { 662 std::string GetPredicateCondition(u64 index, bool negate) {
654 using Tegra::Shader::Pred; 663 using Tegra::Shader::Pred;
655 std::string variable; 664 std::string variable;
656 665
657 // Index 7 is used as an 'Always True' condition. 666 // Index 7 is used as an 'Always True' condition.
658 if (index == static_cast<u64>(Pred::UnusedIndex)) 667 if (index == static_cast<u64>(Pred::UnusedIndex)) {
659 variable = "true"; 668 variable = "true";
660 else 669 } else {
661 variable = 'p' + std::to_string(index) + '_' + suffix; 670 variable = 'p' + std::to_string(index) + '_' + suffix;
662 671 declr_predicates.insert(variable);
672 }
663 if (negate) { 673 if (negate) {
664 return "!(" + variable + ')'; 674 return "!(" + variable + ')';
665 } 675 }
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 24b1d956b..5c7b636e4 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -7,6 +7,10 @@
7#include <array> 7#include <array>
8#include <glad/glad.h> 8#include <glad/glad.h>
9 9
10#include "video_core/engines/maxwell_3d.h"
11
12using Regs = Tegra::Engines::Maxwell3D::Regs;
13
10namespace TextureUnits { 14namespace TextureUnits {
11 15
12struct TextureUnit { 16struct TextureUnit {
@@ -120,7 +124,7 @@ public:
120 GLuint bindpoint; 124 GLuint bindpoint;
121 GLuint ssbo; 125 GLuint ssbo;
122 }; 126 };
123 std::array<std::array<ConstBufferConfig, 16>, 5> const_buffers{}; 127 std::array<std::array<ConstBufferConfig, Regs::MaxConstBuffers>, 5> const_buffers;
124 } draw; 128 } draw;
125 129
126 struct { 130 struct {
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 16b1bd606..c439446b1 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -27,9 +27,11 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
27 case Maxwell::VertexAttribute::Type::UnsignedNorm: { 27 case Maxwell::VertexAttribute::Type::UnsignedNorm: {
28 28
29 switch (attrib.size) { 29 switch (attrib.size) {
30 case Maxwell::VertexAttribute::Size::Size_8_8:
30 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 31 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
31 return GL_UNSIGNED_BYTE; 32 return GL_UNSIGNED_BYTE;
32 case Maxwell::VertexAttribute::Size::Size_16_16: 33 case Maxwell::VertexAttribute::Size::Size_16_16:
34 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
33 return GL_UNSIGNED_SHORT; 35 return GL_UNSIGNED_SHORT;
34 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 36 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
35 return GL_UNSIGNED_INT_2_10_10_10_REV; 37 return GL_UNSIGNED_INT_2_10_10_10_REV;
@@ -43,6 +45,9 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
43 case Maxwell::VertexAttribute::Type::SignedNorm: { 45 case Maxwell::VertexAttribute::Type::SignedNorm: {
44 46
45 switch (attrib.size) { 47 switch (attrib.size) {
48 case Maxwell::VertexAttribute::Size::Size_32_32_32:
49 return GL_INT;
50 case Maxwell::VertexAttribute::Size::Size_8_8:
46 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 51 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
47 return GL_BYTE; 52 return GL_BYTE;
48 case Maxwell::VertexAttribute::Size::Size_16_16: 53 case Maxwell::VertexAttribute::Size::Size_16_16:
@@ -84,6 +89,8 @@ inline GLenum IndexFormat(Maxwell::IndexFormat index_format) {
84 89
85inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { 90inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
86 switch (topology) { 91 switch (topology) {
92 case Maxwell::PrimitiveTopology::Points:
93 return GL_POINTS;
87 case Maxwell::PrimitiveTopology::Triangles: 94 case Maxwell::PrimitiveTopology::Triangles:
88 return GL_TRIANGLES; 95 return GL_TRIANGLES;
89 case Maxwell::PrimitiveTopology::TriangleStrip: 96 case Maxwell::PrimitiveTopology::TriangleStrip:
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index bf9131193..899865e3b 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -430,7 +430,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
430 break; 430 break;
431 case GL_DEBUG_SEVERITY_NOTIFICATION: 431 case GL_DEBUG_SEVERITY_NOTIFICATION:
432 case GL_DEBUG_SEVERITY_LOW: 432 case GL_DEBUG_SEVERITY_LOW:
433 LOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message); 433 LOG_TRACE(Render_OpenGL, format, str_source, str_type, id, message);
434 break; 434 break;
435 } 435 }
436} 436}
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 65db84ad3..70746a34e 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -54,6 +54,7 @@ u32 BytesPerPixel(TextureFormat format) {
54 return 8; 54 return 8;
55 case TextureFormat::DXT23: 55 case TextureFormat::DXT23:
56 case TextureFormat::DXT45: 56 case TextureFormat::DXT45:
57 case TextureFormat::DXN2:
57 case TextureFormat::BC7U: 58 case TextureFormat::BC7U:
58 // In this case a 'pixel' actually refers to a 4x4 tile. 59 // In this case a 'pixel' actually refers to a 4x4 tile.
59 return 16; 60 return 16;
@@ -85,87 +86,11 @@ u32 BytesPerPixel(TextureFormat format) {
85 } 86 }
86} 87}
87 88
88static u32 DepthBytesPerPixel(DepthFormat format) { 89std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
89 switch (format) { 90 u32 height, u32 block_height) {
90 case DepthFormat::Z16_UNORM:
91 return 2;
92 case DepthFormat::S8_Z24_UNORM:
93 case DepthFormat::Z24_S8_UNORM:
94 case DepthFormat::Z32_FLOAT:
95 return 4;
96 case DepthFormat::Z32_S8_X24_FLOAT:
97 return 8;
98 default:
99 UNIMPLEMENTED_MSG("Format not implemented");
100 break;
101 }
102}
103
104std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height,
105 u32 block_height) {
106 u8* data = Memory::GetPointer(address);
107 u32 bytes_per_pixel = BytesPerPixel(format);
108
109 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); 91 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
110 92 CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel,
111 switch (format) { 93 Memory::GetPointer(address), unswizzled_data.data(), true, block_height);
112 case TextureFormat::DXT1:
113 case TextureFormat::DXT23:
114 case TextureFormat::DXT45:
115 case TextureFormat::DXN1:
116 case TextureFormat::BC7U:
117 // In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel
118 // values.
119 CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data,
120 unswizzled_data.data(), true, block_height);
121 break;
122 case TextureFormat::A8R8G8B8:
123 case TextureFormat::A2B10G10R10:
124 case TextureFormat::A1B5G5R5:
125 case TextureFormat::B5G6R5:
126 case TextureFormat::R8:
127 case TextureFormat::G8R8:
128 case TextureFormat::R16_G16_B16_A16:
129 case TextureFormat::R32_G32_B32_A32:
130 case TextureFormat::R32_G32:
131 case TextureFormat::R32:
132 case TextureFormat::R16:
133 case TextureFormat::R16_G16:
134 case TextureFormat::BF10GF11RF11:
135 case TextureFormat::ASTC_2D_4X4:
136 case TextureFormat::R32_G32_B32:
137 CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
138 unswizzled_data.data(), true, block_height);
139 break;
140 default:
141 UNIMPLEMENTED_MSG("Format not implemented");
142 break;
143 }
144
145 return unswizzled_data;
146}
147
148std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height,
149 u32 block_height) {
150 u8* data = Memory::GetPointer(address);
151 u32 bytes_per_pixel = DepthBytesPerPixel(format);
152
153 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
154
155 switch (format) {
156 case DepthFormat::Z16_UNORM:
157 case DepthFormat::S8_Z24_UNORM:
158 case DepthFormat::Z24_S8_UNORM:
159 case DepthFormat::Z32_FLOAT:
160 case DepthFormat::Z32_S8_X24_FLOAT:
161 CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
162 unswizzled_data.data(), true, block_height);
163 break;
164 default:
165 UNIMPLEMENTED_MSG("Format not implemented");
166 break;
167 }
168
169 return unswizzled_data; 94 return unswizzled_data;
170} 95}
171 96
@@ -179,6 +104,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
179 case TextureFormat::DXT23: 104 case TextureFormat::DXT23:
180 case TextureFormat::DXT45: 105 case TextureFormat::DXT45:
181 case TextureFormat::DXN1: 106 case TextureFormat::DXN1:
107 case TextureFormat::DXN2:
182 case TextureFormat::BC7U: 108 case TextureFormat::BC7U:
183 case TextureFormat::ASTC_2D_4X4: 109 case TextureFormat::ASTC_2D_4X4:
184 case TextureFormat::A8R8G8B8: 110 case TextureFormat::A8R8G8B8:
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 73a4924d1..1f7b731be 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -13,8 +13,8 @@ namespace Tegra::Texture {
13/** 13/**
14 * Unswizzles a swizzled texture without changing its format. 14 * Unswizzles a swizzled texture without changing its format.
15 */ 15 */
16std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height, 16std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
17 u32 block_height = TICEntry::DefaultBlockHeight); 17 u32 height, u32 block_height = TICEntry::DefaultBlockHeight);
18 18
19/** 19/**
20 * Unswizzles a swizzled depth texture without changing its format. 20 * Unswizzles a swizzled depth texture without changing its format.
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 475556806..46ed232d8 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -17,6 +17,8 @@ add_executable(yuzu
17 configuration/configure_debug.h 17 configuration/configure_debug.h
18 configuration/configure_dialog.cpp 18 configuration/configure_dialog.cpp
19 configuration/configure_dialog.h 19 configuration/configure_dialog.h
20 configuration/configure_gamelist.cpp
21 configuration/configure_gamelist.h
20 configuration/configure_general.cpp 22 configuration/configure_general.cpp
21 configuration/configure_general.h 23 configuration/configure_general.h
22 configuration/configure_graphics.cpp 24 configuration/configure_graphics.cpp
@@ -59,6 +61,7 @@ set(UIS
59 configuration/configure.ui 61 configuration/configure.ui
60 configuration/configure_audio.ui 62 configuration/configure_audio.ui
61 configuration/configure_debug.ui 63 configuration/configure_debug.ui
64 configuration/configure_gamelist.ui
62 configuration/configure_general.ui 65 configuration/configure_general.ui
63 configuration/configure_graphics.ui 66 configuration/configure_graphics.ui
64 configuration/configure_input.ui 67 configuration/configure_input.ui
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index bf469ee73..0bd46dbac 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -122,6 +122,13 @@ void Config::ReadValues() {
122 qt_config->beginGroup("UI"); 122 qt_config->beginGroup("UI");
123 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); 123 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
124 124
125 qt_config->beginGroup("UIGameList");
126 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
127 UISettings::values.icon_size = qt_config->value("icon_size", 48).toUInt();
128 UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 0).toUInt();
129 UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 3).toUInt();
130 qt_config->endGroup();
131
125 qt_config->beginGroup("UILayout"); 132 qt_config->beginGroup("UILayout");
126 UISettings::values.geometry = qt_config->value("geometry").toByteArray(); 133 UISettings::values.geometry = qt_config->value("geometry").toByteArray();
127 UISettings::values.state = qt_config->value("state").toByteArray(); 134 UISettings::values.state = qt_config->value("state").toByteArray();
@@ -234,6 +241,13 @@ void Config::SaveValues() {
234 qt_config->beginGroup("UI"); 241 qt_config->beginGroup("UI");
235 qt_config->setValue("theme", UISettings::values.theme); 242 qt_config->setValue("theme", UISettings::values.theme);
236 243
244 qt_config->beginGroup("UIGameList");
245 qt_config->setValue("show_unknown", UISettings::values.show_unknown);
246 qt_config->setValue("icon_size", UISettings::values.icon_size);
247 qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id);
248 qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id);
249 qt_config->endGroup();
250
237 qt_config->beginGroup("UILayout"); 251 qt_config->beginGroup("UILayout");
238 qt_config->setValue("geometry", UISettings::values.geometry); 252 qt_config->setValue("geometry", UISettings::values.geometry);
239 qt_config->setValue("state", UISettings::values.state); 253 qt_config->setValue("state", UISettings::values.state);
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index c8e0b88af..20f120134 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -24,6 +24,11 @@
24 <string>General</string> 24 <string>General</string>
25 </attribute> 25 </attribute>
26 </widget> 26 </widget>
27 <widget class="ConfigureGameList" name="gameListTab">
28 <attribute name="title">
29 <string>Game List</string>
30 </attribute>
31 </widget>
27 <widget class="ConfigureSystem" name="systemTab"> 32 <widget class="ConfigureSystem" name="systemTab">
28 <attribute name="title"> 33 <attribute name="title">
29 <string>System</string> 34 <string>System</string>
@@ -67,6 +72,12 @@
67 <header>configuration/configure_general.h</header> 72 <header>configuration/configure_general.h</header>
68 <container>1</container> 73 <container>1</container>
69 </customwidget> 74 </customwidget>
75 <customwidget>
76 <class>ConfigureGameList</class>
77 <extends>QWidget</extends>
78 <header>configuration/configure_gamelist.h</header>
79 <container>1</container>
80 </customwidget>
70 <customwidget> 81 <customwidget>
71 <class>ConfigureSystem</class> 82 <class>ConfigureSystem</class>
72 <extends>QWidget</extends> 83 <extends>QWidget</extends>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index cc4b326ae..daa4cc0d9 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -21,6 +21,7 @@ void ConfigureDialog::setConfiguration() {}
21 21
22void ConfigureDialog::applyConfiguration() { 22void ConfigureDialog::applyConfiguration() {
23 ui->generalTab->applyConfiguration(); 23 ui->generalTab->applyConfiguration();
24 ui->gameListTab->applyConfiguration();
24 ui->systemTab->applyConfiguration(); 25 ui->systemTab->applyConfiguration();
25 ui->inputTab->applyConfiguration(); 26 ui->inputTab->applyConfiguration();
26 ui->graphicsTab->applyConfiguration(); 27 ui->graphicsTab->applyConfiguration();
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp
new file mode 100644
index 000000000..1ae3423cf
--- /dev/null
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -0,0 +1,63 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core.h"
6#include "core/settings.h"
7#include "ui_configure_gamelist.h"
8#include "ui_settings.h"
9#include "yuzu/configuration/configure_gamelist.h"
10
11ConfigureGameList::ConfigureGameList(QWidget* parent)
12 : QWidget(parent), ui(new Ui::ConfigureGameList) {
13 ui->setupUi(this);
14
15 static const std::vector<std::pair<u32, std::string>> default_icon_sizes{
16 std::make_pair(0, "None"), std::make_pair(32, "Small"),
17 std::make_pair(64, "Standard"), std::make_pair(128, "Large"),
18 std::make_pair(256, "Full Size"),
19 };
20
21 for (const auto& size : default_icon_sizes) {
22 ui->icon_size_combobox->addItem(QString::fromStdString(size.second + " (" +
23 std::to_string(size.first) + "x" +
24 std::to_string(size.first) + ")"),
25 size.first);
26 }
27
28 static const std::vector<std::string> row_text_names{
29 "Filename",
30 "Filetype",
31 "Title ID",
32 "Title Name",
33 };
34
35 for (size_t i = 0; i < row_text_names.size(); ++i) {
36 ui->row_1_text_combobox->addItem(QString::fromStdString(row_text_names[i]),
37 QVariant::fromValue(i));
38 ui->row_2_text_combobox->addItem(QString::fromStdString(row_text_names[i]),
39 QVariant::fromValue(i));
40 }
41
42 this->setConfiguration();
43}
44
45ConfigureGameList::~ConfigureGameList() {}
46
47void ConfigureGameList::setConfiguration() {
48 ui->show_unknown->setChecked(UISettings::values.show_unknown);
49 ui->icon_size_combobox->setCurrentIndex(
50 ui->icon_size_combobox->findData(UISettings::values.icon_size));
51 ui->row_1_text_combobox->setCurrentIndex(
52 ui->row_1_text_combobox->findData(UISettings::values.row_1_text_id));
53 ui->row_2_text_combobox->setCurrentIndex(
54 ui->row_2_text_combobox->findData(UISettings::values.row_2_text_id));
55}
56
57void ConfigureGameList::applyConfiguration() {
58 UISettings::values.show_unknown = ui->show_unknown->isChecked();
59 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
60 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
61 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
62 Settings::Apply();
63}
diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_gamelist.h
new file mode 100644
index 000000000..94fba6373
--- /dev/null
+++ b/src/yuzu/configuration/configure_gamelist.h
@@ -0,0 +1,28 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QWidget>
9
10namespace Ui {
11class ConfigureGameList;
12}
13
14class ConfigureGameList : public QWidget {
15 Q_OBJECT
16
17public:
18 explicit ConfigureGameList(QWidget* parent = nullptr);
19 ~ConfigureGameList();
20
21 void applyConfiguration();
22
23private:
24 void setConfiguration();
25
26private:
27 std::unique_ptr<Ui::ConfigureGameList> ui;
28};
diff --git a/src/yuzu/configuration/configure_gamelist.ui b/src/yuzu/configuration/configure_gamelist.ui
new file mode 100644
index 000000000..7471fdb60
--- /dev/null
+++ b/src/yuzu/configuration/configure_gamelist.ui
@@ -0,0 +1,126 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureGameList</class>
4 <widget class="QWidget" name="ConfigureGeneral">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>300</width>
10 <height>377</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QHBoxLayout" name="HorizontalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="VerticalLayout">
19 <item>
20 <widget class="QGroupBox" name="GeneralGroupBox">
21 <property name="title">
22 <string>General</string>
23 </property>
24 <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
25 <item>
26 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
27 <item>
28 <widget class="QCheckBox" name="show_unknown">
29 <property name="text">
30 <string>Show files with type 'Unknown'</string>
31 </property>
32 </widget>
33 </item>
34 </layout>
35 </item>
36 </layout>
37 </widget>
38 </item>
39 <item>
40 <widget class="QGroupBox" name="IconSizeGroupBox">
41 <property name="title">
42 <string>Icon Size</string>
43 </property>
44 <layout class="QHBoxLayout" name="icon_size_qhbox_layout">
45 <item>
46 <layout class="QVBoxLayout" name="icon_size_qvbox_layout">
47 <item>
48 <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
49 <item>
50 <widget class="QLabel" name="icon_size_label">
51 <property name="text">
52 <string>Icon Size:</string>
53 </property>
54 </widget>
55 </item>
56 <item>
57 <widget class="QComboBox" name="icon_size_combobox"/>
58 </item>
59 </layout>
60 </item>
61 </layout>
62 </item>
63 </layout>
64 </widget>
65 </item>
66 <item>
67 <widget class="QGroupBox" name="RowGroupBox">
68 <property name="title">
69 <string>Row Text</string>
70 </property>
71 <layout class="QHBoxLayout" name="RowHorizontalLayout">
72 <item>
73 <layout class="QVBoxLayout" name="RowVerticalLayout">
74 <item>
75 <layout class="QHBoxLayout" name="row_1_qhbox_layout">
76 <item>
77 <widget class="QLabel" name="row_1_label">
78 <property name="text">
79 <string>Row 1 Text:</string>
80 </property>
81 </widget>
82 </item>
83 <item>
84 <widget class="QComboBox" name="row_1_text_combobox"/>
85 </item>
86 </layout>
87 </item>
88 <item>
89 <layout class="QHBoxLayout" name="row_2_qhbox_layout">
90 <item>
91 <widget class="QLabel" name="row_2_label">
92 <property name="text">
93 <string>Row 2 Text:</string>
94 </property>
95 </widget>
96 </item>
97 <item>
98 <widget class="QComboBox" name="row_2_text_combobox"/>
99 </item>
100 </layout>
101 </item>
102 </layout>
103 </item>
104 </layout>
105 </widget>
106 </item>
107 <item>
108 <spacer name="verticalSpacer">
109 <property name="orientation">
110 <enum>Qt::Vertical</enum>
111 </property>
112 <property name="sizeHint" stdset="0">
113 <size>
114 <width>20</width>
115 <height>40</height>
116 </size>
117 </property>
118 </spacer>
119 </item>
120 </layout>
121 </item>
122 </layout>
123 </widget>
124 <resources/>
125 <connections/>
126</ui>
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 3f7103ab9..e037223c2 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -383,8 +383,10 @@ void GraphicsSurfaceWidget::OnUpdate() {
383 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); 383 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
384 boost::optional<VAddr> address = gpu.memory_manager->GpuToCpuAddress(surface_address); 384 boost::optional<VAddr> address = gpu.memory_manager->GpuToCpuAddress(surface_address);
385 385
386 auto unswizzled_data = 386 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
387 Tegra::Texture::UnswizzleTexture(*address, surface_format, surface_width, surface_height); 387 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
388 auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
389 *address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height);
388 390
389 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 391 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
390 surface_width, surface_height); 392 surface_width, surface_height);
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 24f38a3c7..1c738d2a4 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -9,9 +9,13 @@
9#include <QKeyEvent> 9#include <QKeyEvent>
10#include <QMenu> 10#include <QMenu>
11#include <QThreadPool> 11#include <QThreadPool>
12#include <boost/container/flat_map.hpp>
12#include "common/common_paths.h" 13#include "common/common_paths.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "common/string_util.h" 15#include "common/string_util.h"
16#include "core/file_sys/content_archive.h"
17#include "core/file_sys/control_metadata.h"
18#include "core/file_sys/romfs.h"
15#include "core/file_sys/vfs_real.h" 19#include "core/file_sys/vfs_real.h"
16#include "core/loader/loader.h" 20#include "core/loader/loader.h"
17#include "game_list.h" 21#include "game_list.h"
@@ -194,7 +198,8 @@ void GameList::onFilterCloseClicked() {
194 main_window->filterBarSetChecked(false); 198 main_window->filterBarSetChecked(false);
195} 199}
196 200
197GameList::GameList(GMainWindow* parent) : QWidget{parent} { 201GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
202 : QWidget{parent}, vfs(std::move(vfs)) {
198 watcher = new QFileSystemWatcher(this); 203 watcher = new QFileSystemWatcher(this);
199 connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); 204 connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
200 205
@@ -338,7 +343,7 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
338 343
339 emit ShouldCancelWorker(); 344 emit ShouldCancelWorker();
340 345
341 GameListWorker* worker = new GameListWorker(dir_path, deep_scan); 346 GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan);
342 347
343 connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); 348 connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
344 connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, 349 connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating,
@@ -398,8 +403,32 @@ void GameList::RefreshGameDirectory() {
398} 403}
399 404
400void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { 405void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
401 const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory, 406 boost::container::flat_map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map;
402 const std::string& virtual_name) -> bool { 407
408 const auto nca_control_callback =
409 [this, &nca_control_map](u64* num_entries_out, const std::string& directory,
410 const std::string& virtual_name) -> bool {
411 std::string physical_name = directory + DIR_SEP + virtual_name;
412
413 if (stop_processing)
414 return false; // Breaks the callback loop.
415
416 bool is_dir = FileUtil::IsDirectory(physical_name);
417 QFileInfo file_info(physical_name.c_str());
418 if (!is_dir && file_info.suffix().toStdString() == "nca") {
419 auto nca =
420 std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
421 if (nca->GetType() == FileSys::NCAContentType::Control)
422 nca_control_map.insert_or_assign(nca->GetTitleId(), nca);
423 }
424 return true;
425 };
426
427 FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback);
428
429 const auto callback = [this, recursion,
430 &nca_control_map](u64* num_entries_out, const std::string& directory,
431 const std::string& virtual_name) -> bool {
403 std::string physical_name = directory + DIR_SEP + virtual_name; 432 std::string physical_name = directory + DIR_SEP + virtual_name;
404 433
405 if (stop_processing) 434 if (stop_processing)
@@ -409,18 +438,51 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
409 if (!is_dir && 438 if (!is_dir &&
410 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { 439 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
411 std::unique_ptr<Loader::AppLoader> loader = 440 std::unique_ptr<Loader::AppLoader> loader =
412 Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name)); 441 Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));
413 if (!loader) 442 if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||
443 loader->GetFileType() == Loader::FileType::Error) &&
444 !UISettings::values.show_unknown))
414 return true; 445 return true;
415 446
416 std::vector<u8> smdh; 447 std::vector<u8> icon;
417 loader->ReadIcon(smdh); 448 const auto res1 = loader->ReadIcon(icon);
418 449
419 u64 program_id = 0; 450 u64 program_id;
420 loader->ReadProgramId(program_id); 451 const auto res2 = loader->ReadProgramId(program_id);
452
453 std::string name = " ";
454 const auto res3 = loader->ReadTitle(name);
455
456 if ((res1 == Loader::ResultStatus::ErrorNotUsed ||
457 res1 == Loader::ResultStatus::ErrorNotImplemented) &&
458 (res3 == Loader::ResultStatus::ErrorNotUsed ||
459 res3 == Loader::ResultStatus::ErrorNotImplemented) &&
460 res2 == Loader::ResultStatus::Success) {
461 // Use from metadata pool.
462 if (nca_control_map.find(program_id) != nca_control_map.end()) {
463 const auto nca = nca_control_map[program_id];
464 const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS());
465
466 const auto nacp_file = control_dir->GetFile("control.nacp");
467 FileSys::NACP nacp(nacp_file);
468 name = nacp.GetApplicationName();
469
470 FileSys::VirtualFile icon_file = nullptr;
471 for (const auto& language : FileSys::LANGUAGE_NAMES) {
472 icon_file = control_dir->GetFile("icon_" + std::string(language) + ".dat");
473 if (icon_file != nullptr) {
474 icon = icon_file->ReadAllBytes();
475 break;
476 }
477 }
478 }
479 }
421 480
422 emit EntryReady({ 481 emit EntryReady({
423 new GameListItemPath(FormatGameName(physical_name), smdh, program_id), 482 new GameListItemPath(
483 FormatGameName(physical_name), icon, QString::fromStdString(name),
484 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
485 program_id),
424 new GameListItem( 486 new GameListItem(
425 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 487 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
426 new GameListItemSize(FileUtil::GetSize(physical_name)), 488 new GameListItemSize(FileUtil::GetSize(physical_name)),
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 3bc14f07f..afe624b32 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -59,7 +59,7 @@ public:
59 QToolButton* button_filter_close = nullptr; 59 QToolButton* button_filter_close = nullptr;
60 }; 60 };
61 61
62 explicit GameList(GMainWindow* parent = nullptr); 62 explicit GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent = nullptr);
63 ~GameList() override; 63 ~GameList() override;
64 64
65 void clearFilter(); 65 void clearFilter();
@@ -90,6 +90,7 @@ private:
90 void PopupContextMenu(const QPoint& menu_location); 90 void PopupContextMenu(const QPoint& menu_location);
91 void RefreshGameDirectory(); 91 void RefreshGameDirectory();
92 92
93 FileSys::VirtualFilesystem vfs;
93 SearchField* search_field; 94 SearchField* search_field;
94 GMainWindow* main_window = nullptr; 95 GMainWindow* main_window = nullptr;
95 QVBoxLayout* layout = nullptr; 96 QVBoxLayout* layout = nullptr;
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index aa69a098f..114a0fc7f 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -11,6 +11,7 @@
11#include <QStandardItem> 11#include <QStandardItem>
12#include <QString> 12#include <QString>
13#include "common/string_util.h" 13#include "common/string_util.h"
14#include "ui_settings.h"
14#include "yuzu/util/util.h" 15#include "yuzu/util/util.h"
15 16
16/** 17/**
@@ -18,8 +19,7 @@
18 * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) 19 * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
19 * @return QPixmap default icon 20 * @return QPixmap default icon
20 */ 21 */
21static QPixmap GetDefaultIcon(bool large) { 22static QPixmap GetDefaultIcon(u32 size) {
22 int size = large ? 48 : 24;
23 QPixmap icon(size, size); 23 QPixmap icon(size, size);
24 icon.fill(Qt::transparent); 24 icon.fill(Qt::transparent);
25 return icon; 25 return icon;
@@ -44,11 +44,25 @@ public:
44 static const int FullPathRole = Qt::UserRole + 1; 44 static const int FullPathRole = Qt::UserRole + 1;
45 static const int TitleRole = Qt::UserRole + 2; 45 static const int TitleRole = Qt::UserRole + 2;
46 static const int ProgramIdRole = Qt::UserRole + 3; 46 static const int ProgramIdRole = Qt::UserRole + 3;
47 static const int FileTypeRole = Qt::UserRole + 4;
47 48
48 GameListItemPath() = default; 49 GameListItemPath() = default;
49 GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) { 50 GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data,
51 const QString& game_name, const QString& game_type, u64 program_id)
52 : GameListItem() {
50 setData(game_path, FullPathRole); 53 setData(game_path, FullPathRole);
54 setData(game_name, TitleRole);
51 setData(qulonglong(program_id), ProgramIdRole); 55 setData(qulonglong(program_id), ProgramIdRole);
56 setData(game_type, FileTypeRole);
57
58 QPixmap picture;
59 u32 size = UISettings::values.icon_size;
60 if (!picture.loadFromData(picture_data.data(), picture_data.size()))
61 picture = GetDefaultIcon(size);
62
63 picture = picture.scaled(size, size);
64
65 setData(picture, Qt::DecorationRole);
52 } 66 }
53 67
54 QVariant data(int role) const override { 68 QVariant data(int role) const override {
@@ -57,7 +71,23 @@ public:
57 Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, 71 Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename,
58 nullptr); 72 nullptr);
59 QString title = data(TitleRole).toString(); 73 QString title = data(TitleRole).toString();
60 return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title); 74
75 std::vector<QString> row_data{
76 QString::fromStdString(filename),
77 data(FileTypeRole).toString(),
78 QString::fromStdString(fmt::format("0x{:016X}", data(ProgramIdRole).toULongLong())),
79 data(TitleRole).toString(),
80 };
81
82 auto row1 = row_data.at(UISettings::values.row_1_text_id);
83 auto row2 = row_data.at(UISettings::values.row_2_text_id);
84
85 if (row1.isEmpty() || row1 == row2)
86 return row2;
87 if (row2.isEmpty())
88 return row1;
89
90 return row1 + "\n " + row2;
61 } else { 91 } else {
62 return GameListItem::data(role); 92 return GameListItem::data(role);
63 } 93 }
@@ -109,8 +139,8 @@ class GameListWorker : public QObject, public QRunnable {
109 Q_OBJECT 139 Q_OBJECT
110 140
111public: 141public:
112 GameListWorker(QString dir_path, bool deep_scan) 142 GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan)
113 : dir_path(std::move(dir_path)), deep_scan(deep_scan) {} 143 : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
114 144
115public slots: 145public slots:
116 /// Starts the processing of directory tree information. 146 /// Starts the processing of directory tree information.
@@ -133,6 +163,7 @@ signals:
133 void Finished(QStringList watch_list); 163 void Finished(QStringList watch_list);
134 164
135private: 165private:
166 FileSys::VirtualFilesystem vfs;
136 QStringList watch_list; 167 QStringList watch_list;
137 QString dir_path; 168 QString dir_path;
138 bool deep_scan; 169 bool deep_scan;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 17ed62c72..67e3c6549 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -24,6 +24,7 @@
24#include "common/string_util.h" 24#include "common/string_util.h"
25#include "core/core.h" 25#include "core/core.h"
26#include "core/crypto/key_manager.h" 26#include "core/crypto/key_manager.h"
27#include "core/file_sys/vfs_real.h"
27#include "core/gdbstub/gdbstub.h" 28#include "core/gdbstub/gdbstub.h"
28#include "core/loader/loader.h" 29#include "core/loader/loader.h"
29#include "core/settings.h" 30#include "core/settings.h"
@@ -83,7 +84,9 @@ void GMainWindow::ShowCallouts() {}
83 84
84const int GMainWindow::max_recent_files_item; 85const int GMainWindow::max_recent_files_item;
85 86
86GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { 87GMainWindow::GMainWindow()
88 : config(new Config()), emu_thread(nullptr),
89 vfs(std::make_shared<FileSys::RealVfsFilesystem>()) {
87 90
88 debug_context = Tegra::DebugContext::Construct(); 91 debug_context = Tegra::DebugContext::Construct();
89 92
@@ -132,7 +135,7 @@ void GMainWindow::InitializeWidgets() {
132 render_window = new GRenderWindow(this, emu_thread.get()); 135 render_window = new GRenderWindow(this, emu_thread.get());
133 render_window->hide(); 136 render_window->hide();
134 137
135 game_list = new GameList(this); 138 game_list = new GameList(vfs, this);
136 ui.horizontalLayout->addWidget(game_list); 139 ui.horizontalLayout->addWidget(game_list);
137 140
138 // Create status bar 141 // Create status bar
@@ -406,6 +409,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
406 } 409 }
407 410
408 Core::System& system{Core::System::GetInstance()}; 411 Core::System& system{Core::System::GetInstance()};
412 system.SetFilesystem(vfs);
409 413
410 system.SetGPUDebugContext(debug_context); 414 system.SetGPUDebugContext(debug_context);
411 415
@@ -768,6 +772,7 @@ void GMainWindow::OnConfigure() {
768 configureDialog.applyConfiguration(); 772 configureDialog.applyConfiguration();
769 if (UISettings::values.theme != old_theme) 773 if (UISettings::values.theme != old_theme)
770 UpdateUITheme(); 774 UpdateUITheme();
775 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
771 config->Save(); 776 config->Save();
772 } 777 }
773} 778}
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 6e335b8f8..74487c58c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -161,6 +161,9 @@ private:
161 bool emulation_running = false; 161 bool emulation_running = false;
162 std::unique_ptr<EmuThread> emu_thread; 162 std::unique_ptr<EmuThread> emu_thread;
163 163
164 // FS
165 FileSys::VirtualFilesystem vfs;
166
164 // Debugger panes 167 // Debugger panes
165 ProfilerWidget* profilerWidget; 168 ProfilerWidget* profilerWidget;
166 MicroProfileDialog* microProfileDialog; 169 MicroProfileDialog* microProfileDialog;
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 2286c2559..051494bc5 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -54,6 +54,12 @@ struct Values {
54 54
55 // logging 55 // logging
56 bool show_console; 56 bool show_console;
57
58 // Game List
59 bool show_unknown;
60 uint32_t icon_size;
61 uint8_t row_1_text_id;
62 uint8_t row_2_text_id;
57}; 63};
58 64
59extern Values values; 65extern Values values;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index d637dbd0c..0605c92e3 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -161,6 +161,7 @@ int main(int argc, char** argv) {
161 } 161 }
162 162
163 Core::System& system{Core::System::GetInstance()}; 163 Core::System& system{Core::System::GetInstance()};
164 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
164 165
165 SCOPE_EXIT({ system.Shutdown(); }); 166 SCOPE_EXIT({ system.Shutdown(); });
166 167