mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu.git
				synced 2025-05-12 00:45:25 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into better-account
This commit is contained in:
		
						commit
						b76ddb7647
					
				| @ -4,8 +4,10 @@ function(copy_yuzu_Qt5_deps target_dir) | |||||||
|     set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") |     set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") | ||||||
|     set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") |     set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") | ||||||
|     set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/") |     set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/") | ||||||
|  |     set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/") | ||||||
|     set(PLATFORMS ${DLL_DEST}platforms/) |     set(PLATFORMS ${DLL_DEST}platforms/) | ||||||
|     set(STYLES ${DLL_DEST}styles/) |     set(STYLES ${DLL_DEST}styles/) | ||||||
|  |     set(IMAGEFORMATS ${DLL_DEST}imageformats/) | ||||||
|     windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} |     windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} | ||||||
|         icudt*.dll |         icudt*.dll | ||||||
|         icuin*.dll |         icuin*.dll | ||||||
| @ -17,4 +19,5 @@ function(copy_yuzu_Qt5_deps target_dir) | |||||||
|     ) |     ) | ||||||
|     windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*) |     windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*) | ||||||
|     windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*) |     windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*) | ||||||
|  |     windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*) | ||||||
| endfunction(copy_yuzu_Qt5_deps) | endfunction(copy_yuzu_Qt5_deps) | ||||||
|  | |||||||
| @ -117,6 +117,7 @@ after_build: | |||||||
|           mkdir $RELEASE_DIST |           mkdir $RELEASE_DIST | ||||||
|           mkdir $RELEASE_DIST/platforms |           mkdir $RELEASE_DIST/platforms | ||||||
|           mkdir $RELEASE_DIST/styles |           mkdir $RELEASE_DIST/styles | ||||||
|  |           mkdir $RELEASE_DIST/imageformats | ||||||
| 
 | 
 | ||||||
|           # copy the compiled binaries and other release files to the release folder |           # copy the compiled binaries and other release files to the release folder | ||||||
|           Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST |           Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST | ||||||
| @ -140,6 +141,9 @@ after_build: | |||||||
|           # copy the qt windows vista style dll to platforms |           # copy the qt windows vista style dll to platforms | ||||||
|           Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles" |           Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles" | ||||||
| 
 | 
 | ||||||
|  |           # copy the qt jpeg imageformat dll to platforms | ||||||
|  |           Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats" | ||||||
|  | 
 | ||||||
|           7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\* |           7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\* | ||||||
|           7z a $MINGW_SEVENZIP $RELEASE_DIST |           7z a $MINGW_SEVENZIP $RELEASE_DIST | ||||||
|         } |         } | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								externals/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								externals/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							| @ -32,7 +32,7 @@ add_subdirectory(inih) | |||||||
| 
 | 
 | ||||||
| # lz4 | # lz4 | ||||||
| set(LZ4_BUNDLED_MODE ON) | set(LZ4_BUNDLED_MODE ON) | ||||||
| add_subdirectory(lz4/contrib/cmake_unofficial) | add_subdirectory(lz4/contrib/cmake_unofficial EXCLUDE_FROM_ALL) | ||||||
| target_include_directories(lz4_static INTERFACE ./lz4/lib) | target_include_directories(lz4_static INTERFACE ./lz4/lib) | ||||||
| 
 | 
 | ||||||
| # mbedtls | # mbedtls | ||||||
|  | |||||||
| @ -9,13 +9,13 @@ namespace Common { | |||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| constexpr T AlignUp(T value, size_t size) { | constexpr T AlignUp(T value, size_t size) { | ||||||
|     static_assert(std::is_unsigned<T>::value, "T must be an unsigned value."); |     static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | ||||||
|     return static_cast<T>(value + (size - value % size) % size); |     return static_cast<T>(value + (size - value % size) % size); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| constexpr T AlignDown(T value, size_t size) { | constexpr T AlignDown(T value, size_t size) { | ||||||
|     static_assert(std::is_unsigned<T>::value, "T must be an unsigned value."); |     static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | ||||||
|     return static_cast<T>(value - value % size); |     return static_cast<T>(value - value % size); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -96,7 +96,7 @@ static inline int LeastSignificantSetBit(u64 val) { | |||||||
| 
 | 
 | ||||||
| template <typename IntTy> | template <typename IntTy> | ||||||
| class BitSet { | class BitSet { | ||||||
|     static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types"); |     static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types"); | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     // A reference to a particular bit, returned from operator[].
 |     // A reference to a particular bit, returned from operator[].
 | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <cstring> | ||||||
|  | 
 | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/swap.h" | #include "common/swap.h" | ||||||
| #include "common/vector_math.h" | #include "common/vector_math.h" | ||||||
| @ -55,7 +57,7 @@ constexpr u8 Convert8To6(u8 value) { | |||||||
|  * @param bytes Pointer to encoded source color |  * @param bytes Pointer to encoded source color | ||||||
|  * @return Result color decoded as Math::Vec4<u8> |  * @return Result color decoded as Math::Vec4<u8> | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) { | inline Math::Vec4<u8> DecodeRGBA8(const u8* bytes) { | ||||||
|     return {bytes[3], bytes[2], bytes[1], bytes[0]}; |     return {bytes[3], bytes[2], bytes[1], bytes[0]}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -64,7 +66,7 @@ inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) { | |||||||
|  * @param bytes Pointer to encoded source color |  * @param bytes Pointer to encoded source color | ||||||
|  * @return Result color decoded as Math::Vec4<u8> |  * @return Result color decoded as Math::Vec4<u8> | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) { | inline Math::Vec4<u8> DecodeRGB8(const u8* bytes) { | ||||||
|     return {bytes[2], bytes[1], bytes[0], 255}; |     return {bytes[2], bytes[1], bytes[0], 255}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -73,7 +75,7 @@ inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) { | |||||||
|  * @param bytes Pointer to encoded source color |  * @param bytes Pointer to encoded source color | ||||||
|  * @return Result color decoded as Math::Vec4<u8> |  * @return Result color decoded as Math::Vec4<u8> | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) { | inline Math::Vec4<u8> DecodeRG8(const u8* bytes) { | ||||||
|     return {bytes[1], bytes[0], 0, 255}; |     return {bytes[1], bytes[0], 0, 255}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -82,8 +84,9 @@ inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) { | |||||||
|  * @param bytes Pointer to encoded source color |  * @param bytes Pointer to encoded source color | ||||||
|  * @return Result color decoded as Math::Vec4<u8> |  * @return Result color decoded as Math::Vec4<u8> | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) { | inline Math::Vec4<u8> DecodeRGB565(const u8* bytes) { | ||||||
|     const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); |     u16_le pixel; | ||||||
|  |     std::memcpy(&pixel, bytes, sizeof(pixel)); | ||||||
|     return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), |     return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), | ||||||
|             Convert5To8(pixel & 0x1F), 255}; |             Convert5To8(pixel & 0x1F), 255}; | ||||||
| } | } | ||||||
| @ -93,8 +96,9 @@ inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) { | |||||||
|  * @param bytes Pointer to encoded source color |  * @param bytes Pointer to encoded source color | ||||||
|  * @return Result color decoded as Math::Vec4<u8> |  * @return Result color decoded as Math::Vec4<u8> | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) { | inline Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) { | ||||||
|     const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); |     u16_le pixel; | ||||||
|  |     std::memcpy(&pixel, bytes, sizeof(pixel)); | ||||||
|     return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), |     return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), | ||||||
|             Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)}; |             Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)}; | ||||||
| } | } | ||||||
| @ -104,8 +108,9 @@ inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) { | |||||||
|  * @param bytes Pointer to encoded source color |  * @param bytes Pointer to encoded source color | ||||||
|  * @return Result color decoded as Math::Vec4<u8> |  * @return Result color decoded as Math::Vec4<u8> | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) { | inline Math::Vec4<u8> DecodeRGBA4(const u8* bytes) { | ||||||
|     const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes); |     u16_le pixel; | ||||||
|  |     std::memcpy(&pixel, bytes, sizeof(pixel)); | ||||||
|     return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), |     return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), | ||||||
|             Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)}; |             Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)}; | ||||||
| } | } | ||||||
| @ -116,7 +121,9 @@ inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) { | |||||||
|  * @return Depth value as an u32 |  * @return Depth value as an u32 | ||||||
|  */ |  */ | ||||||
| inline u32 DecodeD16(const u8* bytes) { | inline u32 DecodeD16(const u8* bytes) { | ||||||
|     return *reinterpret_cast<const u16_le*>(bytes); |     u16_le data; | ||||||
|  |     std::memcpy(&data, bytes, sizeof(data)); | ||||||
|  |     return data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -133,7 +140,7 @@ inline u32 DecodeD24(const u8* bytes) { | |||||||
|  * @param bytes Pointer to encoded source values |  * @param bytes Pointer to encoded source values | ||||||
|  * @return Resulting values stored as a Math::Vec2 |  * @return Resulting values stored as a Math::Vec2 | ||||||
|  */ |  */ | ||||||
| inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) { | inline Math::Vec2<u32> DecodeD24S8(const u8* bytes) { | ||||||
|     return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]}; |     return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -175,8 +182,10 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) { | |||||||
|  * @param bytes Destination pointer to store encoded color |  * @param bytes Destination pointer to store encoded color | ||||||
|  */ |  */ | ||||||
| inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) { | inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) { | ||||||
|     *reinterpret_cast<u16_le*>(bytes) = |     const u16_le data = | ||||||
|         (Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b()); |         (Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b()); | ||||||
|  | 
 | ||||||
|  |     std::memcpy(bytes, &data, sizeof(data)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -185,9 +194,10 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) { | |||||||
|  * @param bytes Destination pointer to store encoded color |  * @param bytes Destination pointer to store encoded color | ||||||
|  */ |  */ | ||||||
| inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) { | inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) { | ||||||
|     *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) | |     const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) | | ||||||
|                                         (Convert8To5(color.g()) << 6) | |                         (Convert8To5(color.b()) << 1) | Convert8To1(color.a()); | ||||||
|                                         (Convert8To5(color.b()) << 1) | Convert8To1(color.a()); | 
 | ||||||
|  |     std::memcpy(bytes, &data, sizeof(data)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -196,9 +206,10 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) { | |||||||
|  * @param bytes Destination pointer to store encoded color |  * @param bytes Destination pointer to store encoded color | ||||||
|  */ |  */ | ||||||
| inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) { | inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) { | ||||||
|     *reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) | |     const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) | | ||||||
|                                         (Convert8To4(color.g()) << 8) | |                      (Convert8To4(color.b()) << 4) | Convert8To4(color.a()); | ||||||
|                                         (Convert8To4(color.b()) << 4) | Convert8To4(color.a()); | 
 | ||||||
|  |     std::memcpy(bytes, &data, sizeof(data)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -207,7 +218,8 @@ inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) { | |||||||
|  * @param bytes Pointer where to store the encoded value |  * @param bytes Pointer where to store the encoded value | ||||||
|  */ |  */ | ||||||
| inline void EncodeD16(u32 value, u8* bytes) { | inline void EncodeD16(u32 value, u8* bytes) { | ||||||
|     *reinterpret_cast<u16_le*>(bytes) = value & 0xFFFF; |     const u16_le data = static_cast<u16>(value); | ||||||
|  |     std::memcpy(bytes, &data, sizeof(data)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | |||||||
| @ -884,11 +884,21 @@ std::string_view RemoveTrailingSlash(std::string_view path) { | |||||||
|     return path; |     return path; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string SanitizePath(std::string_view path_) { | std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { | ||||||
|     std::string path(path_); |     std::string path(path_); | ||||||
|     std::replace(path.begin(), path.end(), '\\', '/'); |     char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\'; | ||||||
|  |     char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/'; | ||||||
|  | 
 | ||||||
|  |     if (directory_separator == DirectorySeparator::PlatformDefault) { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |         type1 = '/'; | ||||||
|  |         type2 = '\\'; | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::replace(path.begin(), path.end(), type1, type2); | ||||||
|     path.erase(std::unique(path.begin(), path.end(), |     path.erase(std::unique(path.begin(), path.end(), | ||||||
|                            [](char c1, char c2) { return c1 == '/' && c2 == '/'; }), |                            [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }), | ||||||
|                path.end()); |                path.end()); | ||||||
|     return std::string(RemoveTrailingSlash(path)); |     return std::string(RemoveTrailingSlash(path)); | ||||||
| } | } | ||||||
|  | |||||||
| @ -182,8 +182,12 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la | |||||||
|     return std::vector<T>(vector.begin() + first, vector.begin() + first + last); |     return std::vector<T>(vector.begin() + first, vector.begin() + first + last); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
 | enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault }; | ||||||
| std::string SanitizePath(std::string_view path); | 
 | ||||||
|  | // Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
 | ||||||
|  | // depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
 | ||||||
|  | std::string SanitizePath(std::string_view path, | ||||||
|  |                          DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash); | ||||||
| 
 | 
 | ||||||
| // simple wrapper for cstdlib file functions to
 | // simple wrapper for cstdlib file functions to
 | ||||||
| // hopefully will make error checking easier
 | // hopefully will make error checking easier
 | ||||||
| @ -208,7 +212,7 @@ public: | |||||||
| 
 | 
 | ||||||
|     template <typename T> |     template <typename T> | ||||||
|     size_t ReadArray(T* data, size_t length) const { |     size_t ReadArray(T* data, size_t length) const { | ||||||
|         static_assert(std::is_trivially_copyable<T>(), |         static_assert(std::is_trivially_copyable_v<T>, | ||||||
|                       "Given array does not consist of trivially copyable objects"); |                       "Given array does not consist of trivially copyable objects"); | ||||||
| 
 | 
 | ||||||
|         if (!IsOpen()) { |         if (!IsOpen()) { | ||||||
| @ -220,7 +224,7 @@ public: | |||||||
| 
 | 
 | ||||||
|     template <typename T> |     template <typename T> | ||||||
|     size_t WriteArray(const T* data, size_t length) { |     size_t WriteArray(const T* data, size_t length) { | ||||||
|         static_assert(std::is_trivially_copyable<T>(), |         static_assert(std::is_trivially_copyable_v<T>, | ||||||
|                       "Given array does not consist of trivially copyable objects"); |                       "Given array does not consist of trivially copyable objects"); | ||||||
|         if (!IsOpen()) { |         if (!IsOpen()) { | ||||||
|             return std::numeric_limits<size_t>::max(); |             return std::numeric_limits<size_t>::max(); | ||||||
| @ -231,19 +235,19 @@ public: | |||||||
| 
 | 
 | ||||||
|     template <typename T> |     template <typename T> | ||||||
|     size_t ReadBytes(T* data, size_t length) const { |     size_t ReadBytes(T* data, size_t length) const { | ||||||
|         static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable"); |         static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | ||||||
|         return ReadArray(reinterpret_cast<char*>(data), length); |         return ReadArray(reinterpret_cast<char*>(data), length); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template <typename T> |     template <typename T> | ||||||
|     size_t WriteBytes(const T* data, size_t length) { |     size_t WriteBytes(const T* data, size_t length) { | ||||||
|         static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable"); |         static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | ||||||
|         return WriteArray(reinterpret_cast<const char*>(data), length); |         return WriteArray(reinterpret_cast<const char*>(data), length); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template <typename T> |     template <typename T> | ||||||
|     size_t WriteObject(const T& object) { |     size_t WriteObject(const T& object) { | ||||||
|         static_assert(!std::is_pointer<T>::value, "Given object is a pointer"); |         static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer"); | ||||||
|         return WriteArray(&object, 1); |         return WriteArray(&object, 1); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -28,7 +28,7 @@ static inline u64 ComputeHash64(const void* data, size_t len) { | |||||||
|  */ |  */ | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline u64 ComputeStructHash64(const T& data) { | static inline u64 ComputeStructHash64(const T& data) { | ||||||
|     static_assert(std::is_trivially_copyable<T>(), |     static_assert(std::is_trivially_copyable_v<T>, | ||||||
|                   "Type passed to ComputeStructHash64 must be trivially copyable"); |                   "Type passed to ComputeStructHash64 must be trivially copyable"); | ||||||
|     return ComputeHash64(&data, sizeof(data)); |     return ComputeHash64(&data, sizeof(data)); | ||||||
| } | } | ||||||
| @ -38,7 +38,7 @@ template <typename T> | |||||||
| struct HashableStruct { | struct HashableStruct { | ||||||
|     // In addition to being trivially copyable, T must also have a trivial default constructor,
 |     // In addition to being trivially copyable, T must also have a trivial default constructor,
 | ||||||
|     // because any member initialization would be overridden by memset
 |     // because any member initialization would be overridden by memset
 | ||||||
|     static_assert(std::is_trivial<T>(), "Type passed to HashableStruct must be trivial"); |     static_assert(std::is_trivial_v<T>, "Type passed to HashableStruct must be trivial"); | ||||||
|     /*
 |     /*
 | ||||||
|      * We use a union because "implicitly-defined copy/move constructor for a union X copies the |      * We use a union because "implicitly-defined copy/move constructor for a union X copies the | ||||||
|      * object representation of X." and "implicitly-defined copy assignment operator for a union X |      * object representation of X." and "implicitly-defined copy assignment operator for a union X | ||||||
|  | |||||||
| @ -171,15 +171,21 @@ void FileBackend::Write(const Entry& entry) { | |||||||
|     SUB(Service, ARP)                                                                              \ |     SUB(Service, ARP)                                                                              \ | ||||||
|     SUB(Service, BCAT)                                                                             \ |     SUB(Service, BCAT)                                                                             \ | ||||||
|     SUB(Service, BPC)                                                                              \ |     SUB(Service, BPC)                                                                              \ | ||||||
|  |     SUB(Service, BTDRV)                                                                            \ | ||||||
|     SUB(Service, BTM)                                                                              \ |     SUB(Service, BTM)                                                                              \ | ||||||
|     SUB(Service, Capture)                                                                          \ |     SUB(Service, Capture)                                                                          \ | ||||||
|  |     SUB(Service, ERPT)                                                                             \ | ||||||
|  |     SUB(Service, ETicket)                                                                          \ | ||||||
|  |     SUB(Service, EUPLD)                                                                            \ | ||||||
|     SUB(Service, Fatal)                                                                            \ |     SUB(Service, Fatal)                                                                            \ | ||||||
|     SUB(Service, FGM)                                                                              \ |     SUB(Service, FGM)                                                                              \ | ||||||
|     SUB(Service, Friend)                                                                           \ |     SUB(Service, Friend)                                                                           \ | ||||||
|     SUB(Service, FS)                                                                               \ |     SUB(Service, FS)                                                                               \ | ||||||
|  |     SUB(Service, GRC)                                                                              \ | ||||||
|     SUB(Service, HID)                                                                              \ |     SUB(Service, HID)                                                                              \ | ||||||
|     SUB(Service, LBL)                                                                              \ |     SUB(Service, LBL)                                                                              \ | ||||||
|     SUB(Service, LDN)                                                                              \ |     SUB(Service, LDN)                                                                              \ | ||||||
|  |     SUB(Service, LDR)                                                                              \ | ||||||
|     SUB(Service, LM)                                                                               \ |     SUB(Service, LM)                                                                               \ | ||||||
|     SUB(Service, Migration)                                                                        \ |     SUB(Service, Migration)                                                                        \ | ||||||
|     SUB(Service, Mii)                                                                              \ |     SUB(Service, Mii)                                                                              \ | ||||||
| @ -188,11 +194,13 @@ void FileBackend::Write(const Entry& entry) { | |||||||
|     SUB(Service, NFC)                                                                              \ |     SUB(Service, NFC)                                                                              \ | ||||||
|     SUB(Service, NFP)                                                                              \ |     SUB(Service, NFP)                                                                              \ | ||||||
|     SUB(Service, NIFM)                                                                             \ |     SUB(Service, NIFM)                                                                             \ | ||||||
|  |     SUB(Service, NIM)                                                                              \ | ||||||
|     SUB(Service, NS)                                                                               \ |     SUB(Service, NS)                                                                               \ | ||||||
|     SUB(Service, NVDRV)                                                                            \ |     SUB(Service, NVDRV)                                                                            \ | ||||||
|     SUB(Service, PCIE)                                                                             \ |     SUB(Service, PCIE)                                                                             \ | ||||||
|     SUB(Service, PCTL)                                                                             \ |     SUB(Service, PCTL)                                                                             \ | ||||||
|     SUB(Service, PCV)                                                                              \ |     SUB(Service, PCV)                                                                              \ | ||||||
|  |     SUB(Service, PM)                                                                               \ | ||||||
|     SUB(Service, PREPO)                                                                            \ |     SUB(Service, PREPO)                                                                            \ | ||||||
|     SUB(Service, PSC)                                                                              \ |     SUB(Service, PSC)                                                                              \ | ||||||
|     SUB(Service, SET)                                                                              \ |     SUB(Service, SET)                                                                              \ | ||||||
|  | |||||||
| @ -58,15 +58,21 @@ enum class Class : ClassType { | |||||||
|     Service_Audio,     ///< The Audio (Audio control) service
 |     Service_Audio,     ///< The Audio (Audio control) service
 | ||||||
|     Service_BCAT,      ///< The BCAT service
 |     Service_BCAT,      ///< The BCAT service
 | ||||||
|     Service_BPC,       ///< The BPC service
 |     Service_BPC,       ///< The BPC service
 | ||||||
|  |     Service_BTDRV,     ///< The Bluetooth driver service
 | ||||||
|     Service_BTM,       ///< The BTM service
 |     Service_BTM,       ///< The BTM service
 | ||||||
|     Service_Capture,   ///< The capture service
 |     Service_Capture,   ///< The capture service
 | ||||||
|  |     Service_ERPT,      ///< The error reporting service
 | ||||||
|  |     Service_ETicket,   ///< The ETicket service
 | ||||||
|  |     Service_EUPLD,     ///< The error upload service
 | ||||||
|     Service_Fatal,     ///< The Fatal service
 |     Service_Fatal,     ///< The Fatal service
 | ||||||
|     Service_FGM,       ///< The FGM service
 |     Service_FGM,       ///< The FGM service
 | ||||||
|     Service_Friend,    ///< The friend service
 |     Service_Friend,    ///< The friend service
 | ||||||
|     Service_FS,        ///< The FS (Filesystem) service
 |     Service_FS,        ///< The FS (Filesystem) service
 | ||||||
|  |     Service_GRC,       ///< The game recording service
 | ||||||
|     Service_HID,       ///< The HID (Human interface device) service
 |     Service_HID,       ///< The HID (Human interface device) service
 | ||||||
|     Service_LBL,       ///< The LBL (LCD backlight) service
 |     Service_LBL,       ///< The LBL (LCD backlight) service
 | ||||||
|     Service_LDN,       ///< The LDN (Local domain network) service
 |     Service_LDN,       ///< The LDN (Local domain network) service
 | ||||||
|  |     Service_LDR,       ///< The loader service
 | ||||||
|     Service_LM,        ///< The LM (Logger) service
 |     Service_LM,        ///< The LM (Logger) service
 | ||||||
|     Service_Migration, ///< The migration service
 |     Service_Migration, ///< The migration service
 | ||||||
|     Service_Mii,       ///< The Mii service
 |     Service_Mii,       ///< The Mii service
 | ||||||
| @ -75,11 +81,13 @@ enum class Class : ClassType { | |||||||
|     Service_NFC,       ///< The NFC (Near-field communication) service
 |     Service_NFC,       ///< The NFC (Near-field communication) service
 | ||||||
|     Service_NFP,       ///< The NFP service
 |     Service_NFP,       ///< The NFP service
 | ||||||
|     Service_NIFM,      ///< The NIFM (Network interface) service
 |     Service_NIFM,      ///< The NIFM (Network interface) service
 | ||||||
|  |     Service_NIM,       ///< The NIM service
 | ||||||
|     Service_NS,        ///< The NS services
 |     Service_NS,        ///< The NS services
 | ||||||
|     Service_NVDRV,     ///< The NVDRV (Nvidia driver) service
 |     Service_NVDRV,     ///< The NVDRV (Nvidia driver) service
 | ||||||
|     Service_PCIE,      ///< The PCIe service
 |     Service_PCIE,      ///< The PCIe service
 | ||||||
|     Service_PCTL,      ///< The PCTL (Parental control) service
 |     Service_PCTL,      ///< The PCTL (Parental control) service
 | ||||||
|     Service_PCV,       ///< The PCV service
 |     Service_PCV,       ///< The PCV service
 | ||||||
|  |     Service_PM,        ///< The PM service
 | ||||||
|     Service_PREPO,     ///< The PREPO (Play report) service
 |     Service_PREPO,     ///< The PREPO (Play report) service
 | ||||||
|     Service_PSC,       ///< The PSC service
 |     Service_PSC,       ///< The PSC service
 | ||||||
|     Service_SET,       ///< The SET (Settings) service
 |     Service_SET,       ///< The SET (Settings) service
 | ||||||
|  | |||||||
| @ -42,140 +42,136 @@ class Vec3; | |||||||
| template <typename T> | template <typename T> | ||||||
| class Vec4; | class Vec4; | ||||||
| 
 | 
 | ||||||
| template <typename T> |  | ||||||
| static inline Vec2<T> MakeVec(const T& x, const T& y); |  | ||||||
| template <typename T> |  | ||||||
| static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z); |  | ||||||
| template <typename T> |  | ||||||
| static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w); |  | ||||||
| 
 |  | ||||||
| template <typename T> | template <typename T> | ||||||
| class Vec2 { | class Vec2 { | ||||||
| public: | public: | ||||||
|     T x{}; |     T x{}; | ||||||
|     T y{}; |     T y{}; | ||||||
| 
 | 
 | ||||||
|     Vec2() = default; |     constexpr Vec2() = default; | ||||||
|     Vec2(const T& _x, const T& _y) : x(_x), y(_y) {} |     constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {} | ||||||
| 
 | 
 | ||||||
|     template <typename T2> |     template <typename T2> | ||||||
|     Vec2<T2> Cast() const { |     constexpr Vec2<T2> Cast() const { | ||||||
|         return Vec2<T2>((T2)x, (T2)y); |         return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static Vec2 AssignToAll(const T& f) { |     static constexpr Vec2 AssignToAll(const T& f) { | ||||||
|         return Vec2<T>(f, f); |         return Vec2{f, f}; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const { |     constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const { | ||||||
|         return MakeVec(x + other.x, y + other.y); |         return {x + other.x, y + other.y}; | ||||||
|     } |     } | ||||||
|     void operator+=(const Vec2& other) { |     constexpr Vec2& operator+=(const Vec2& other) { | ||||||
|         x += other.x; |         x += other.x; | ||||||
|         y += other.y; |         y += other.y; | ||||||
|  |         return *this; | ||||||
|     } |     } | ||||||
|     Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const { |     constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const { | ||||||
|         return MakeVec(x - other.x, y - other.y); |         return {x - other.x, y - other.y}; | ||||||
|     } |     } | ||||||
|     void operator-=(const Vec2& other) { |     constexpr Vec2& operator-=(const Vec2& other) { | ||||||
|         x -= other.x; |         x -= other.x; | ||||||
|         y -= other.y; |         y -= other.y; | ||||||
|  |         return *this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template <typename U = T> |     template <typename U = T> | ||||||
|     Vec2<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const { |     constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { | ||||||
|         return MakeVec(-x, -y); |         return {-x, -y}; | ||||||
|     } |     } | ||||||
|     Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const { |     constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const { | ||||||
|         return MakeVec(x * other.x, y * other.y); |         return {x * other.x, y * other.y}; | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     Vec2<decltype(T{} * V{})> operator*(const V& f) const { |  | ||||||
|         return MakeVec(x * f, y * f); |  | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     void operator*=(const V& f) { |  | ||||||
|         *this = *this * f; |  | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     Vec2<decltype(T{} / V{})> operator/(const V& f) const { |  | ||||||
|         return MakeVec(x / f, y / f); |  | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     void operator/=(const V& f) { |  | ||||||
|         *this = *this / f; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     T Length2() const { |     template <typename V> | ||||||
|  |     constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const { | ||||||
|  |         return {x * f, y * f}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename V> | ||||||
|  |     constexpr Vec2& operator*=(const V& f) { | ||||||
|  |         *this = *this * f; | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename V> | ||||||
|  |     constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const { | ||||||
|  |         return {x / f, y / f}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename V> | ||||||
|  |     constexpr Vec2& operator/=(const V& f) { | ||||||
|  |         *this = *this / f; | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     constexpr T Length2() const { | ||||||
|         return x * x + y * y; |         return x * x + y * y; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Only implemented for T=float
 |     // Only implemented for T=float
 | ||||||
|     float Length() const; |     float Length() const; | ||||||
|     void SetLength(const float l); |  | ||||||
|     Vec2 WithLength(const float l) const; |  | ||||||
|     float Distance2To(Vec2& other); |  | ||||||
|     Vec2 Normalized() const; |  | ||||||
|     float Normalize(); // returns the previous length, which is often useful
 |     float Normalize(); // returns the previous length, which is often useful
 | ||||||
| 
 | 
 | ||||||
|     T& operator[](int i) // allow vector[1] = 3   (vector.y=3)
 |     constexpr T& operator[](std::size_t i) { | ||||||
|     { |  | ||||||
|         return *((&x) + i); |         return *((&x) + i); | ||||||
|     } |     } | ||||||
|     T operator[](const int i) const { |     constexpr const T& operator[](std::size_t i) const { | ||||||
|         return *((&x) + i); |         return *((&x) + i); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void SetZero() { |     constexpr void SetZero() { | ||||||
|         x = 0; |         x = 0; | ||||||
|         y = 0; |         y = 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Common aliases: UV (texel coordinates), ST (texture coordinates)
 |     // Common aliases: UV (texel coordinates), ST (texture coordinates)
 | ||||||
|     T& u() { |     constexpr T& u() { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     T& v() { |     constexpr T& v() { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
|     T& s() { |     constexpr T& s() { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     T& t() { |     constexpr T& t() { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const T& u() const { |     constexpr const T& u() const { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     const T& v() const { |     constexpr const T& v() const { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
|     const T& s() const { |     constexpr const T& s() const { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     const T& t() const { |     constexpr const T& t() const { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // swizzlers - create a subvector of specific components
 |     // swizzlers - create a subvector of specific components
 | ||||||
|     const Vec2 yx() const { |     constexpr Vec2 yx() const { | ||||||
|         return Vec2(y, x); |         return Vec2(y, x); | ||||||
|     } |     } | ||||||
|     const Vec2 vu() const { |     constexpr Vec2 vu() const { | ||||||
|         return Vec2(y, x); |         return Vec2(y, x); | ||||||
|     } |     } | ||||||
|     const Vec2 ts() const { |     constexpr Vec2 ts() const { | ||||||
|         return Vec2(y, x); |         return Vec2(y, x); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <typename T, typename V> | template <typename T, typename V> | ||||||
| Vec2<T> operator*(const V& f, const Vec2<T>& vec) { | constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) { | ||||||
|     return Vec2<T>(f * vec.x, f * vec.y); |     return Vec2<T>(f * vec.x, f * vec.y); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| typedef Vec2<float> Vec2f; | using Vec2f = Vec2<float>; | ||||||
| 
 | 
 | ||||||
| template <> | template <> | ||||||
| inline float Vec2<float>::Length() const { | inline float Vec2<float>::Length() const { | ||||||
| @ -196,147 +192,151 @@ public: | |||||||
|     T y{}; |     T y{}; | ||||||
|     T z{}; |     T z{}; | ||||||
| 
 | 
 | ||||||
|     Vec3() = default; |     constexpr Vec3() = default; | ||||||
|     Vec3(const T& _x, const T& _y, const T& _z) : x(_x), y(_y), z(_z) {} |     constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {} | ||||||
| 
 | 
 | ||||||
|     template <typename T2> |     template <typename T2> | ||||||
|     Vec3<T2> Cast() const { |     constexpr Vec3<T2> Cast() const { | ||||||
|         return MakeVec<T2>((T2)x, (T2)y, (T2)z); |         return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Only implemented for T=int and T=float
 |     static constexpr Vec3 AssignToAll(const T& f) { | ||||||
|     static Vec3 FromRGB(unsigned int rgb); |         return Vec3(f, f, f); | ||||||
|     unsigned int ToRGB() const; // alpha bits set to zero
 |  | ||||||
| 
 |  | ||||||
|     static Vec3 AssignToAll(const T& f) { |  | ||||||
|         return MakeVec(f, f, f); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const { |     constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const { | ||||||
|         return MakeVec(x + other.x, y + other.y, z + other.z); |         return {x + other.x, y + other.y, z + other.z}; | ||||||
|     } |     } | ||||||
|     void operator+=(const Vec3& other) { | 
 | ||||||
|  |     constexpr Vec3& operator+=(const Vec3& other) { | ||||||
|         x += other.x; |         x += other.x; | ||||||
|         y += other.y; |         y += other.y; | ||||||
|         z += other.z; |         z += other.z; | ||||||
|  |         return *this; | ||||||
|     } |     } | ||||||
|     Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const { | 
 | ||||||
|         return MakeVec(x - other.x, y - other.y, z - other.z); |     constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const { | ||||||
|  |         return {x - other.x, y - other.y, z - other.z}; | ||||||
|     } |     } | ||||||
|     void operator-=(const Vec3& other) { | 
 | ||||||
|  |     constexpr Vec3& operator-=(const Vec3& other) { | ||||||
|         x -= other.x; |         x -= other.x; | ||||||
|         y -= other.y; |         y -= other.y; | ||||||
|         z -= other.z; |         z -= other.z; | ||||||
|  |         return *this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template <typename U = T> |     template <typename U = T> | ||||||
|     Vec3<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const { |     constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { | ||||||
|         return MakeVec(-x, -y, -z); |         return {-x, -y, -z}; | ||||||
|     } |  | ||||||
|     Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const { |  | ||||||
|         return MakeVec(x * other.x, y * other.y, z * other.z); |  | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     Vec3<decltype(T{} * V{})> operator*(const V& f) const { |  | ||||||
|         return MakeVec(x * f, y * f, z * f); |  | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     void operator*=(const V& f) { |  | ||||||
|         *this = *this * f; |  | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     Vec3<decltype(T{} / V{})> operator/(const V& f) const { |  | ||||||
|         return MakeVec(x / f, y / f, z / f); |  | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     void operator/=(const V& f) { |  | ||||||
|         *this = *this / f; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     T Length2() const { |     constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const { | ||||||
|  |         return {x * other.x, y * other.y, z * other.z}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename V> | ||||||
|  |     constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const { | ||||||
|  |         return {x * f, y * f, z * f}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename V> | ||||||
|  |     constexpr Vec3& operator*=(const V& f) { | ||||||
|  |         *this = *this * f; | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
|  |     template <typename V> | ||||||
|  |     constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const { | ||||||
|  |         return {x / f, y / f, z / f}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename V> | ||||||
|  |     constexpr Vec3& operator/=(const V& f) { | ||||||
|  |         *this = *this / f; | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     constexpr T Length2() const { | ||||||
|         return x * x + y * y + z * z; |         return x * x + y * y + z * z; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Only implemented for T=float
 |     // Only implemented for T=float
 | ||||||
|     float Length() const; |     float Length() const; | ||||||
|     void SetLength(const float l); |  | ||||||
|     Vec3 WithLength(const float l) const; |  | ||||||
|     float Distance2To(Vec3& other); |  | ||||||
|     Vec3 Normalized() const; |     Vec3 Normalized() const; | ||||||
|     float Normalize(); // returns the previous length, which is often useful
 |     float Normalize(); // returns the previous length, which is often useful
 | ||||||
| 
 | 
 | ||||||
|     T& operator[](int i) // allow vector[2] = 3   (vector.z=3)
 |     constexpr T& operator[](std::size_t i) { | ||||||
|     { |  | ||||||
|         return *((&x) + i); |  | ||||||
|     } |  | ||||||
|     T operator[](const int i) const { |  | ||||||
|         return *((&x) + i); |         return *((&x) + i); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void SetZero() { |     constexpr const T& operator[](std::size_t i) const { | ||||||
|  |         return *((&x) + i); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     constexpr void SetZero() { | ||||||
|         x = 0; |         x = 0; | ||||||
|         y = 0; |         y = 0; | ||||||
|         z = 0; |         z = 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
 |     // Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
 | ||||||
|     T& u() { |     constexpr T& u() { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     T& v() { |     constexpr T& v() { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
|     T& w() { |     constexpr T& w() { | ||||||
|         return z; |         return z; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     T& r() { |     constexpr T& r() { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     T& g() { |     constexpr T& g() { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
|     T& b() { |     constexpr T& b() { | ||||||
|         return z; |         return z; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     T& s() { |     constexpr T& s() { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     T& t() { |     constexpr T& t() { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
|     T& q() { |     constexpr T& q() { | ||||||
|         return z; |         return z; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const T& u() const { |     constexpr const T& u() const { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     const T& v() const { |     constexpr const T& v() const { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
|     const T& w() const { |     constexpr const T& w() const { | ||||||
|         return z; |         return z; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const T& r() const { |     constexpr const T& r() const { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     const T& g() const { |     constexpr const T& g() const { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
|     const T& b() const { |     constexpr const T& b() const { | ||||||
|         return z; |         return z; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const T& s() const { |     constexpr const T& s() const { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     const T& t() const { |     constexpr const T& t() const { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
|     const T& q() const { |     constexpr const T& q() const { | ||||||
|         return z; |         return z; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -345,7 +345,7 @@ public: | |||||||
| // _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
 | // _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
 | ||||||
| // component names (x<->r) and permutations (xy<->yx)
 | // component names (x<->r) and permutations (xy<->yx)
 | ||||||
| #define _DEFINE_SWIZZLER2(a, b, name)                                                              \ | #define _DEFINE_SWIZZLER2(a, b, name)                                                              \ | ||||||
|     const Vec2<T> name() const {                                                                   \ |     constexpr Vec2<T> name() const {                                                               \ | ||||||
|         return Vec2<T>(a, b);                                                                      \ |         return Vec2<T>(a, b);                                                                      \ | ||||||
|     } |     } | ||||||
| #define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4)                                             \ | #define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4)                                             \ | ||||||
| @ -366,7 +366,7 @@ public: | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <typename T, typename V> | template <typename T, typename V> | ||||||
| Vec3<T> operator*(const V& f, const Vec3<T>& vec) { | constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) { | ||||||
|     return Vec3<T>(f * vec.x, f * vec.y, f * vec.z); |     return Vec3<T>(f * vec.x, f * vec.y, f * vec.z); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -387,7 +387,7 @@ inline float Vec3<float>::Normalize() { | |||||||
|     return length; |     return length; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| typedef Vec3<float> Vec3f; | using Vec3f = Vec3<float>; | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| class Vec4 { | class Vec4 { | ||||||
| @ -397,86 +397,88 @@ public: | |||||||
|     T z{}; |     T z{}; | ||||||
|     T w{}; |     T w{}; | ||||||
| 
 | 
 | ||||||
|     Vec4() = default; |     constexpr Vec4() = default; | ||||||
|     Vec4(const T& _x, const T& _y, const T& _z, const T& _w) : x(_x), y(_y), z(_z), w(_w) {} |     constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_) | ||||||
|  |         : x(x_), y(y_), z(z_), w(w_) {} | ||||||
| 
 | 
 | ||||||
|     template <typename T2> |     template <typename T2> | ||||||
|     Vec4<T2> Cast() const { |     constexpr Vec4<T2> Cast() const { | ||||||
|         return Vec4<T2>((T2)x, (T2)y, (T2)z, (T2)w); |         return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z), | ||||||
|  |                         static_cast<T2>(w)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Only implemented for T=int and T=float
 |     static constexpr Vec4 AssignToAll(const T& f) { | ||||||
|     static Vec4 FromRGBA(unsigned int rgba); |         return Vec4(f, f, f, f); | ||||||
|     unsigned int ToRGBA() const; |  | ||||||
| 
 |  | ||||||
|     static Vec4 AssignToAll(const T& f) { |  | ||||||
|         return Vec4<T>(f, f, f, f); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const { |     constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const { | ||||||
|         return MakeVec(x + other.x, y + other.y, z + other.z, w + other.w); |         return {x + other.x, y + other.y, z + other.z, w + other.w}; | ||||||
|     } |     } | ||||||
|     void operator+=(const Vec4& other) { | 
 | ||||||
|  |     constexpr Vec4& operator+=(const Vec4& other) { | ||||||
|         x += other.x; |         x += other.x; | ||||||
|         y += other.y; |         y += other.y; | ||||||
|         z += other.z; |         z += other.z; | ||||||
|         w += other.w; |         w += other.w; | ||||||
|  |         return *this; | ||||||
|     } |     } | ||||||
|     Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const { | 
 | ||||||
|         return MakeVec(x - other.x, y - other.y, z - other.z, w - other.w); |     constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const { | ||||||
|  |         return {x - other.x, y - other.y, z - other.z, w - other.w}; | ||||||
|     } |     } | ||||||
|     void operator-=(const Vec4& other) { | 
 | ||||||
|  |     constexpr Vec4& operator-=(const Vec4& other) { | ||||||
|         x -= other.x; |         x -= other.x; | ||||||
|         y -= other.y; |         y -= other.y; | ||||||
|         z -= other.z; |         z -= other.z; | ||||||
|         w -= other.w; |         w -= other.w; | ||||||
|  |         return *this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template <typename U = T> |     template <typename U = T> | ||||||
|     Vec4<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const { |     constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { | ||||||
|         return MakeVec(-x, -y, -z, -w); |         return {-x, -y, -z, -w}; | ||||||
|     } |  | ||||||
|     Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const { |  | ||||||
|         return MakeVec(x * other.x, y * other.y, z * other.z, w * other.w); |  | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     Vec4<decltype(T{} * V{})> operator*(const V& f) const { |  | ||||||
|         return MakeVec(x * f, y * f, z * f, w * f); |  | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     void operator*=(const V& f) { |  | ||||||
|         *this = *this * f; |  | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     Vec4<decltype(T{} / V{})> operator/(const V& f) const { |  | ||||||
|         return MakeVec(x / f, y / f, z / f, w / f); |  | ||||||
|     } |  | ||||||
|     template <typename V> |  | ||||||
|     void operator/=(const V& f) { |  | ||||||
|         *this = *this / f; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     T Length2() const { |     constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const { | ||||||
|  |         return {x * other.x, y * other.y, z * other.z, w * other.w}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename V> | ||||||
|  |     constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const { | ||||||
|  |         return {x * f, y * f, z * f, w * f}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename V> | ||||||
|  |     constexpr Vec4& operator*=(const V& f) { | ||||||
|  |         *this = *this * f; | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename V> | ||||||
|  |     constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const { | ||||||
|  |         return {x / f, y / f, z / f, w / f}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename V> | ||||||
|  |     constexpr Vec4& operator/=(const V& f) { | ||||||
|  |         *this = *this / f; | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     constexpr T Length2() const { | ||||||
|         return x * x + y * y + z * z + w * w; |         return x * x + y * y + z * z + w * w; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Only implemented for T=float
 |     constexpr T& operator[](std::size_t i) { | ||||||
|     float Length() const; |  | ||||||
|     void SetLength(const float l); |  | ||||||
|     Vec4 WithLength(const float l) const; |  | ||||||
|     float Distance2To(Vec4& other); |  | ||||||
|     Vec4 Normalized() const; |  | ||||||
|     float Normalize(); // returns the previous length, which is often useful
 |  | ||||||
| 
 |  | ||||||
|     T& operator[](int i) // allow vector[2] = 3   (vector.z=3)
 |  | ||||||
|     { |  | ||||||
|         return *((&x) + i); |  | ||||||
|     } |  | ||||||
|     T operator[](const int i) const { |  | ||||||
|         return *((&x) + i); |         return *((&x) + i); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void SetZero() { |     constexpr const T& operator[](std::size_t i) const { | ||||||
|  |         return *((&x) + i); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     constexpr void SetZero() { | ||||||
|         x = 0; |         x = 0; | ||||||
|         y = 0; |         y = 0; | ||||||
|         z = 0; |         z = 0; | ||||||
| @ -484,29 +486,29 @@ public: | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Common alias: RGBA (colors)
 |     // Common alias: RGBA (colors)
 | ||||||
|     T& r() { |     constexpr T& r() { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     T& g() { |     constexpr T& g() { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
|     T& b() { |     constexpr T& b() { | ||||||
|         return z; |         return z; | ||||||
|     } |     } | ||||||
|     T& a() { |     constexpr T& a() { | ||||||
|         return w; |         return w; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const T& r() const { |     constexpr const T& r() const { | ||||||
|         return x; |         return x; | ||||||
|     } |     } | ||||||
|     const T& g() const { |     constexpr const T& g() const { | ||||||
|         return y; |         return y; | ||||||
|     } |     } | ||||||
|     const T& b() const { |     constexpr const T& b() const { | ||||||
|         return z; |         return z; | ||||||
|     } |     } | ||||||
|     const T& a() const { |     constexpr const T& a() const { | ||||||
|         return w; |         return w; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -518,7 +520,7 @@ public: | |||||||
| // DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
 | // DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
 | ||||||
| // permutations (xy<->yx)
 | // permutations (xy<->yx)
 | ||||||
| #define _DEFINE_SWIZZLER2(a, b, name)                                                              \ | #define _DEFINE_SWIZZLER2(a, b, name)                                                              \ | ||||||
|     const Vec2<T> name() const {                                                                   \ |     constexpr Vec2<T> name() const {                                                               \ | ||||||
|         return Vec2<T>(a, b);                                                                      \ |         return Vec2<T>(a, b);                                                                      \ | ||||||
|     } |     } | ||||||
| #define DEFINE_SWIZZLER2_COMP1(a, a2)                                                              \ | #define DEFINE_SWIZZLER2_COMP1(a, a2)                                                              \ | ||||||
| @ -545,7 +547,7 @@ public: | |||||||
| #undef _DEFINE_SWIZZLER2 | #undef _DEFINE_SWIZZLER2 | ||||||
| 
 | 
 | ||||||
| #define _DEFINE_SWIZZLER3(a, b, c, name)                                                           \ | #define _DEFINE_SWIZZLER3(a, b, c, name)                                                           \ | ||||||
|     const Vec3<T> name() const {                                                                   \ |     constexpr Vec3<T> name() const {                                                               \ | ||||||
|         return Vec3<T>(a, b, c);                                                                   \ |         return Vec3<T>(a, b, c);                                                                   \ | ||||||
|     } |     } | ||||||
| #define DEFINE_SWIZZLER3_COMP1(a, a2)                                                              \ | #define DEFINE_SWIZZLER3_COMP1(a, a2)                                                              \ | ||||||
| @ -579,51 +581,51 @@ public: | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <typename T, typename V> | template <typename T, typename V> | ||||||
| Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) { | constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) { | ||||||
|     return MakeVec(f * vec.x, f * vec.y, f * vec.z, f * vec.w); |     return {f * vec.x, f * vec.y, f * vec.z, f * vec.w}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| typedef Vec4<float> Vec4f; | using Vec4f = Vec4<float>; | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) { | constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) { | ||||||
|     return a.x * b.x + a.y * b.y; |     return a.x * b.x + a.y * b.y; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) { | constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) { | ||||||
|     return a.x * b.x + a.y * b.y + a.z * b.z; |     return a.x * b.x + a.y * b.y + a.z * b.z; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) { | constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) { | ||||||
|     return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; |     return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) { | constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) { | ||||||
|     return MakeVec(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); |     return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // linear interpolation via float: 0.0=begin, 1.0=end
 | // linear interpolation via float: 0.0=begin, 1.0=end
 | ||||||
| template <typename X> | template <typename X> | ||||||
| static inline decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end, | constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end, | ||||||
|                                                            const float t) { |                                                        const float t) { | ||||||
|     return begin * (1.f - t) + end * t; |     return begin * (1.f - t) + end * t; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // linear interpolation via int: 0=begin, base=end
 | // linear interpolation via int: 0=begin, base=end
 | ||||||
| template <typename X, int base> | template <typename X, int base> | ||||||
| static inline decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end, | constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end, | ||||||
|                                                                    const int t) { |                                                                const int t) { | ||||||
|     return (begin * (base - t) + end * t) / base; |     return (begin * (base - t) + end * t) / base; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
 | // bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
 | ||||||
| // interpolation.
 | // interpolation.
 | ||||||
| template <typename X> | template <typename X> | ||||||
| inline auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s, | constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s, | ||||||
|                            const float t) { |                               const float t) { | ||||||
|     auto y0 = Lerp(x00, x01, s); |     auto y0 = Lerp(x00, x01, s); | ||||||
|     auto y1 = Lerp(x10, x11, s); |     auto y1 = Lerp(x10, x11, s); | ||||||
|     return Lerp(y0, y1, t); |     return Lerp(y0, y1, t); | ||||||
| @ -631,42 +633,42 @@ inline auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x1 | |||||||
| 
 | 
 | ||||||
| // Utility vector factories
 | // Utility vector factories
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec2<T> MakeVec(const T& x, const T& y) { | constexpr Vec2<T> MakeVec(const T& x, const T& y) { | ||||||
|     return Vec2<T>{x, y}; |     return Vec2<T>{x, y}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z) { | constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) { | ||||||
|     return Vec3<T>{x, y, z}; |     return Vec3<T>{x, y, z}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) { | constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) { | ||||||
|     return MakeVec(x, y, zw[0], zw[1]); |     return MakeVec(x, y, zw[0], zw[1]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) { | constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) { | ||||||
|     return MakeVec(xy[0], xy[1], z); |     return MakeVec(xy[0], xy[1], z); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) { | constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) { | ||||||
|     return MakeVec(x, yz[0], yz[1]); |     return MakeVec(x, yz[0], yz[1]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) { | constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) { | ||||||
|     return Vec4<T>{x, y, z, w}; |     return Vec4<T>{x, y, z, w}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) { | constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) { | ||||||
|     return MakeVec(xy[0], xy[1], z, w); |     return MakeVec(xy[0], xy[1], z, w); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) { | constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) { | ||||||
|     return MakeVec(x, yz[0], yz[1], w); |     return MakeVec(x, yz[0], yz[1], w); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -674,17 +676,17 @@ static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) { | |||||||
| //       Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
 | //       Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
 | ||||||
| //       out soon enough due to misuse of the returned structure.
 | //       out soon enough due to misuse of the returned structure.
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) { | constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) { | ||||||
|     return MakeVec(xy[0], xy[1], zw[0], zw[1]); |     return MakeVec(xy[0], xy[1], zw[0], zw[1]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) { | constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) { | ||||||
|     return MakeVec(xyz[0], xyz[1], xyz[2], w); |     return MakeVec(xyz[0], xyz[1], xyz[2], w); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| static inline Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) { | constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) { | ||||||
|     return MakeVec(x, yzw[0], yzw[1], yzw[2]); |     return MakeVec(x, yzw[0], yzw[1], yzw[2]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) { | |||||||
| 
 | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { | inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { | ||||||
|     static_assert(std::is_pointer<T>(), "Argument must be a (function) pointer."); |     static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer."); | ||||||
|     size_t addr = reinterpret_cast<size_t>(f); |     size_t addr = reinterpret_cast<size_t>(f); | ||||||
|     if (IsWithin2G(code, addr)) { |     if (IsWithin2G(code, addr)) { | ||||||
|         code.call(f); |         code.call(f); | ||||||
|  | |||||||
| @ -89,7 +89,7 @@ System::ResultStatus System::SingleStep() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) { | System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) { | ||||||
|     app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath)); |     app_loader = Loader::GetLoader(virtual_filesystem->OpenFile(filepath, FileSys::Mode::Read)); | ||||||
| 
 | 
 | ||||||
|     if (!app_loader) { |     if (!app_loader) { | ||||||
|         LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); |         LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); | ||||||
| @ -174,6 +174,10 @@ System::ResultStatus System::Init(EmuWindow& emu_window) { | |||||||
| 
 | 
 | ||||||
|     CoreTiming::Init(); |     CoreTiming::Init(); | ||||||
| 
 | 
 | ||||||
|  |     // Create a default fs if one doesn't already exist.
 | ||||||
|  |     if (virtual_filesystem == nullptr) | ||||||
|  |         virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); | ||||||
|  | 
 | ||||||
|     current_process = Kernel::Process::Create("main"); |     current_process = Kernel::Process::Create("main"); | ||||||
| 
 | 
 | ||||||
|     cpu_barrier = std::make_shared<CpuBarrier>(); |     cpu_barrier = std::make_shared<CpuBarrier>(); | ||||||
| @ -186,7 +190,7 @@ System::ResultStatus System::Init(EmuWindow& emu_window) { | |||||||
|     service_manager = std::make_shared<Service::SM::ServiceManager>(); |     service_manager = std::make_shared<Service::SM::ServiceManager>(); | ||||||
| 
 | 
 | ||||||
|     Kernel::Init(); |     Kernel::Init(); | ||||||
|     Service::Init(service_manager); |     Service::Init(service_manager, virtual_filesystem); | ||||||
|     GDBStub::Init(); |     GDBStub::Init(); | ||||||
| 
 | 
 | ||||||
|     renderer = VideoCore::CreateRenderer(emu_window); |     renderer = VideoCore::CreateRenderer(emu_window); | ||||||
|  | |||||||
| @ -17,6 +17,8 @@ | |||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| #include "core/perf_stats.h" | #include "core/perf_stats.h" | ||||||
| #include "core/telemetry_session.h" | #include "core/telemetry_session.h" | ||||||
|  | #include "file_sys/vfs_real.h" | ||||||
|  | #include "hle/service/filesystem/filesystem.h" | ||||||
| #include "video_core/debug_utils/debug_utils.h" | #include "video_core/debug_utils/debug_utils.h" | ||||||
| #include "video_core/gpu.h" | #include "video_core/gpu.h" | ||||||
| 
 | 
 | ||||||
| @ -211,6 +213,14 @@ public: | |||||||
|         return debug_context; |         return debug_context; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void SetFilesystem(FileSys::VirtualFilesystem vfs) { | ||||||
|  |         virtual_filesystem = std::move(vfs); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     FileSys::VirtualFilesystem GetFilesystem() const { | ||||||
|  |         return virtual_filesystem; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     System(); |     System(); | ||||||
| 
 | 
 | ||||||
| @ -225,6 +235,8 @@ private: | |||||||
|      */ |      */ | ||||||
|     ResultStatus Init(EmuWindow& emu_window); |     ResultStatus Init(EmuWindow& emu_window); | ||||||
| 
 | 
 | ||||||
|  |     /// RealVfsFilesystem instance
 | ||||||
|  |     FileSys::VirtualFilesystem virtual_filesystem; | ||||||
|     /// AppLoader used to load the current executing application
 |     /// AppLoader used to load the current executing application
 | ||||||
|     std::unique_ptr<Loader::AppLoader> app_loader; |     std::unique_ptr<Loader::AppLoader> app_loader; | ||||||
|     std::unique_ptr<VideoCore::RendererBase> renderer; |     std::unique_ptr<VideoCore::RendererBase> renderer; | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| #include <array> | #include <array> | ||||||
| #include <string> | #include <string> | ||||||
| #include <core/loader/loader.h> | #include <core/loader/loader.h> | ||||||
|  | #include "common/logging/log.h" | ||||||
| #include "core/file_sys/card_image.h" | #include "core/file_sys/card_image.h" | ||||||
| #include "core/file_sys/partition_filesystem.h" | #include "core/file_sys/partition_filesystem.h" | ||||||
| #include "core/file_sys/vfs_offset.h" | #include "core/file_sys/vfs_offset.h" | ||||||
|  | |||||||
| @ -170,6 +170,10 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | ||||||
|  |     if (file == nullptr) { | ||||||
|  |         status = Loader::ResultStatus::ErrorInvalidFormat; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|     if (sizeof(NCAHeader) != file->ReadObject(&header)) |     if (sizeof(NCAHeader) != file->ReadObject(&header)) | ||||||
|         LOG_ERROR(Loader, "File reader errored out during header read."); |         LOG_ERROR(Loader, "File reader errored out during header read."); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ | |||||||
| #include "common/common_funcs.h" | #include "common/common_funcs.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/swap.h" | #include "common/swap.h" | ||||||
|  | #include "control_metadata.h" | ||||||
| #include "core/crypto/key_manager.h" | #include "core/crypto/key_manager.h" | ||||||
| #include "core/file_sys/partition_filesystem.h" | #include "core/file_sys/partition_filesystem.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
|  | |||||||
| @ -62,6 +62,13 @@ enum class Language : u8 { | |||||||
|     Chinese = 14, |     Chinese = 14, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static constexpr std::array<const char*, 15> LANGUAGE_NAMES = { | ||||||
|  |     "AmericanEnglish", "BritishEnglish", "Japanese", | ||||||
|  |     "French",          "German",         "LatinAmericanSpanish", | ||||||
|  |     "Spanish",         "Italian",        "Dutch", | ||||||
|  |     "CanadianFrench",  "Portugese",      "Russian", | ||||||
|  |     "Korean",          "Taiwanese",      "Chinese"}; | ||||||
|  | 
 | ||||||
| // A class representing the format used by NX metadata files, typically named Control.nacp.
 | // A class representing the format used by NX metadata files, typically named Control.nacp.
 | ||||||
| // These store application name, dev name, title id, and other miscellaneous data.
 | // These store application name, dev name, title id, and other miscellaneous data.
 | ||||||
| class NACP { | class NACP { | ||||||
|  | |||||||
| @ -4,8 +4,9 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <array> |  | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
|  | #include <iterator> | ||||||
|  | #include <string_view> | ||||||
| #include "common/common_funcs.h" | #include "common/common_funcs.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| @ -21,9 +22,14 @@ enum EntryType : u8 { | |||||||
| 
 | 
 | ||||||
| // Structure of a directory entry, from
 | // Structure of a directory entry, from
 | ||||||
| // http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
 | // http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
 | ||||||
| const size_t FILENAME_LENGTH = 0x300; |  | ||||||
| struct Entry { | struct Entry { | ||||||
|     char filename[FILENAME_LENGTH]; |     Entry(std::string_view view, EntryType entry_type, u64 entry_size) | ||||||
|  |         : type{entry_type}, file_size{entry_size} { | ||||||
|  |         const size_t copy_size = view.copy(filename, std::size(filename) - 1); | ||||||
|  |         filename[copy_size] = '\0'; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     char filename[0x300]; | ||||||
|     INSERT_PADDING_BYTES(4); |     INSERT_PADDING_BYTES(4); | ||||||
|     EntryType type; |     EntryType type; | ||||||
|     INSERT_PADDING_BYTES(3); |     INSERT_PADDING_BYTES(3); | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "common/swap.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
|  | |||||||
| @ -4,12 +4,160 @@ | |||||||
| 
 | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <numeric> | #include <numeric> | ||||||
|  | #include <string> | ||||||
|  | #include "common/common_paths.h" | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| #include "common/logging/backend.h" | #include "common/logging/backend.h" | ||||||
| #include "core/file_sys/vfs.h" | #include "core/file_sys/vfs.h" | ||||||
| 
 | 
 | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
| 
 | 
 | ||||||
|  | VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {} | ||||||
|  | 
 | ||||||
|  | VfsFilesystem::~VfsFilesystem() = default; | ||||||
|  | 
 | ||||||
|  | std::string VfsFilesystem::GetName() const { | ||||||
|  |     return root->GetName(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool VfsFilesystem::IsReadable() const { | ||||||
|  |     return root->IsReadable(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool VfsFilesystem::IsWritable() const { | ||||||
|  |     return root->IsWritable(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_); | ||||||
|  |     if (root->GetFileRelative(path) != nullptr) | ||||||
|  |         return VfsEntryType::File; | ||||||
|  |     if (root->GetDirectoryRelative(path) != nullptr) | ||||||
|  |         return VfsEntryType::Directory; | ||||||
|  | 
 | ||||||
|  |     return VfsEntryType::None; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_); | ||||||
|  |     return root->GetFileRelative(path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_); | ||||||
|  |     return root->CreateFileRelative(path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { | ||||||
|  |     const auto old_path = FileUtil::SanitizePath(old_path_); | ||||||
|  |     const auto new_path = FileUtil::SanitizePath(new_path_); | ||||||
|  | 
 | ||||||
|  |     // VfsDirectory impls are only required to implement copy across the current directory.
 | ||||||
|  |     if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) { | ||||||
|  |         if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path))) | ||||||
|  |             return nullptr; | ||||||
|  |         return OpenFile(new_path, Mode::ReadWrite); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Do it using RawCopy. Non-default impls are encouraged to optimize this.
 | ||||||
|  |     const auto old_file = OpenFile(old_path, Mode::Read); | ||||||
|  |     if (old_file == nullptr) | ||||||
|  |         return nullptr; | ||||||
|  |     auto new_file = OpenFile(new_path, Mode::Read); | ||||||
|  |     if (new_file != nullptr) | ||||||
|  |         return nullptr; | ||||||
|  |     new_file = CreateFile(new_path, Mode::Write); | ||||||
|  |     if (new_file == nullptr) | ||||||
|  |         return nullptr; | ||||||
|  |     if (!VfsRawCopy(old_file, new_file)) | ||||||
|  |         return nullptr; | ||||||
|  |     return new_file; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { | ||||||
|  |     const auto old_path = FileUtil::SanitizePath(old_path_); | ||||||
|  |     const auto new_path = FileUtil::SanitizePath(new_path_); | ||||||
|  | 
 | ||||||
|  |     // Again, non-default impls are highly encouraged to provide a more optimized version of this.
 | ||||||
|  |     auto out = CopyFile(old_path_, new_path_); | ||||||
|  |     if (out == nullptr) | ||||||
|  |         return nullptr; | ||||||
|  |     if (DeleteFile(old_path)) | ||||||
|  |         return out; | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool VfsFilesystem::DeleteFile(std::string_view path_) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_); | ||||||
|  |     auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); | ||||||
|  |     if (parent == nullptr) | ||||||
|  |         return false; | ||||||
|  |     return parent->DeleteFile(FileUtil::GetFilename(path)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_); | ||||||
|  |     return root->GetDirectoryRelative(path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_); | ||||||
|  |     return root->CreateDirectoryRelative(path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { | ||||||
|  |     const auto old_path = FileUtil::SanitizePath(old_path_); | ||||||
|  |     const auto new_path = FileUtil::SanitizePath(new_path_); | ||||||
|  | 
 | ||||||
|  |     // Non-default impls are highly encouraged to provide a more optimized version of this.
 | ||||||
|  |     auto old_dir = OpenDirectory(old_path, Mode::Read); | ||||||
|  |     if (old_dir == nullptr) | ||||||
|  |         return nullptr; | ||||||
|  |     auto new_dir = OpenDirectory(new_path, Mode::Read); | ||||||
|  |     if (new_dir != nullptr) | ||||||
|  |         return nullptr; | ||||||
|  |     new_dir = CreateDirectory(new_path, Mode::Write); | ||||||
|  |     if (new_dir == nullptr) | ||||||
|  |         return nullptr; | ||||||
|  | 
 | ||||||
|  |     for (const auto& file : old_dir->GetFiles()) { | ||||||
|  |         const auto x = | ||||||
|  |             CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName()); | ||||||
|  |         if (x == nullptr) | ||||||
|  |             return nullptr; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (const auto& dir : old_dir->GetSubdirectories()) { | ||||||
|  |         const auto x = | ||||||
|  |             CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName()); | ||||||
|  |         if (x == nullptr) | ||||||
|  |             return nullptr; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return new_dir; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) { | ||||||
|  |     const auto old_path = FileUtil::SanitizePath(old_path_); | ||||||
|  |     const auto new_path = FileUtil::SanitizePath(new_path_); | ||||||
|  | 
 | ||||||
|  |     // Non-default impls are highly encouraged to provide a more optimized version of this.
 | ||||||
|  |     auto out = CopyDirectory(old_path_, new_path_); | ||||||
|  |     if (out == nullptr) | ||||||
|  |         return nullptr; | ||||||
|  |     if (DeleteDirectory(old_path)) | ||||||
|  |         return out; | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool VfsFilesystem::DeleteDirectory(std::string_view path_) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_); | ||||||
|  |     auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); | ||||||
|  |     if (parent == nullptr) | ||||||
|  |         return false; | ||||||
|  |     return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| VfsFile::~VfsFile() = default; | VfsFile::~VfsFile() = default; | ||||||
| 
 | 
 | ||||||
| std::string VfsFile::GetExtension() const { | std::string VfsFile::GetExtension() const { | ||||||
|  | |||||||
| @ -11,14 +11,74 @@ | |||||||
| #include <vector> | #include <vector> | ||||||
| #include "boost/optional.hpp" | #include "boost/optional.hpp" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "core/file_sys/mode.h" | ||||||
| 
 | 
 | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
|  | 
 | ||||||
|  | struct VfsFilesystem; | ||||||
| struct VfsFile; | struct VfsFile; | ||||||
| struct VfsDirectory; | struct VfsDirectory; | ||||||
| 
 | 
 | ||||||
| // Convenience typedefs to use VfsDirectory and VfsFile
 | // Convenience typedefs to use Vfs* interfaces
 | ||||||
| using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>; | using VirtualFilesystem = std::shared_ptr<VfsFilesystem>; | ||||||
| using VirtualFile = std::shared_ptr<FileSys::VfsFile>; | using VirtualDir = std::shared_ptr<VfsDirectory>; | ||||||
|  | using VirtualFile = std::shared_ptr<VfsFile>; | ||||||
|  | 
 | ||||||
|  | // An enumeration representing what can be at the end of a path in a VfsFilesystem
 | ||||||
|  | enum class VfsEntryType { | ||||||
|  |     None, | ||||||
|  |     File, | ||||||
|  |     Directory, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // A class representing an abstract filesystem. A default implementation given the root VirtualDir
 | ||||||
|  | // is provided for convenience, but if the Vfs implementation has any additional state or
 | ||||||
|  | // functionality, they will need to override.
 | ||||||
|  | struct VfsFilesystem : NonCopyable { | ||||||
|  |     VfsFilesystem(VirtualDir root); | ||||||
|  |     virtual ~VfsFilesystem(); | ||||||
|  | 
 | ||||||
|  |     // Gets the friendly name for the filesystem.
 | ||||||
|  |     virtual std::string GetName() const; | ||||||
|  | 
 | ||||||
|  |     // Return whether or not the user has read permissions on this filesystem.
 | ||||||
|  |     virtual bool IsReadable() const; | ||||||
|  |     // Return whether or not the user has write permission on this filesystem.
 | ||||||
|  |     virtual bool IsWritable() const; | ||||||
|  | 
 | ||||||
|  |     // Determine if the entry at path is non-existant, a file, or a directory.
 | ||||||
|  |     virtual VfsEntryType GetEntryType(std::string_view path) const; | ||||||
|  | 
 | ||||||
|  |     // Opens the file with path relative to root. If it doesn't exist, returns nullptr.
 | ||||||
|  |     virtual VirtualFile OpenFile(std::string_view path, Mode perms); | ||||||
|  |     // Creates a new, empty file at path
 | ||||||
|  |     virtual VirtualFile CreateFile(std::string_view path, Mode perms); | ||||||
|  |     // Copies the file from old_path to new_path, returning the new file on success and nullptr on
 | ||||||
|  |     // failure.
 | ||||||
|  |     virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path); | ||||||
|  |     // Moves the file from old_path to new_path, returning the moved file on success and nullptr on
 | ||||||
|  |     // failure.
 | ||||||
|  |     virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path); | ||||||
|  |     // Deletes the file with path relative to root, returing true on success.
 | ||||||
|  |     virtual bool DeleteFile(std::string_view path); | ||||||
|  | 
 | ||||||
|  |     // Opens the directory with path relative to root. If it doesn't exist, returns nullptr.
 | ||||||
|  |     virtual VirtualDir OpenDirectory(std::string_view path, Mode perms); | ||||||
|  |     // Creates a new, empty directory at path
 | ||||||
|  |     virtual VirtualDir CreateDirectory(std::string_view path, Mode perms); | ||||||
|  |     // Copies the directory from old_path to new_path, returning the new directory on success and
 | ||||||
|  |     // nullptr on failure.
 | ||||||
|  |     virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path); | ||||||
|  |     // Moves the directory from old_path to new_path, returning the moved directory on success and
 | ||||||
|  |     // nullptr on failure.
 | ||||||
|  |     virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path); | ||||||
|  |     // Deletes the directory with path relative to root, returing true on success.
 | ||||||
|  |     virtual bool DeleteDirectory(std::string_view path); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     // Root directory in default implementation.
 | ||||||
|  |     VirtualDir root; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| // A class representing a file in an abstract filesystem.
 | // A class representing a file in an abstract filesystem.
 | ||||||
| struct VfsFile : NonCopyable { | struct VfsFile : NonCopyable { | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ | |||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <iterator> | #include <iterator> | ||||||
| #include <utility> | #include <utility> | ||||||
| 
 | #include "common/assert.h" | ||||||
| #include "common/common_paths.h" | #include "common/common_paths.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/file_sys/vfs_real.h" | #include "core/file_sys/vfs_real.h" | ||||||
| @ -29,6 +29,8 @@ static std::string ModeFlagsToString(Mode mode) { | |||||||
|             mode_str = "a"; |             mode_str = "a"; | ||||||
|         else if (mode & Mode::Write) |         else if (mode & Mode::Write) | ||||||
|             mode_str = "w"; |             mode_str = "w"; | ||||||
|  |         else | ||||||
|  |             UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     mode_str += "b"; |     mode_str += "b"; | ||||||
| @ -36,8 +38,174 @@ static std::string ModeFlagsToString(Mode mode) { | |||||||
|     return mode_str; |     return mode_str; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_) | RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} | ||||||
|     : backing(path_, ModeFlagsToString(perms_).c_str()), path(path_), | 
 | ||||||
|  | std::string RealVfsFilesystem::GetName() const { | ||||||
|  |     return "Real"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool RealVfsFilesystem::IsReadable() const { | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool RealVfsFilesystem::IsWritable() const { | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     if (!FileUtil::Exists(path)) | ||||||
|  |         return VfsEntryType::None; | ||||||
|  |     if (FileUtil::IsDirectory(path)) | ||||||
|  |         return VfsEntryType::Directory; | ||||||
|  | 
 | ||||||
|  |     return VfsEntryType::File; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     if (cache.find(path) != cache.end()) { | ||||||
|  |         auto weak = cache[path]; | ||||||
|  |         if (!weak.expired()) { | ||||||
|  |             return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0) | ||||||
|  |         FileUtil::CreateEmptyFile(path); | ||||||
|  | 
 | ||||||
|  |     auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str()); | ||||||
|  |     cache[path] = backing; | ||||||
|  | 
 | ||||||
|  |     // Cannot use make_shared as RealVfsFile constructor is private
 | ||||||
|  |     return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     if (!FileUtil::Exists(path) && !FileUtil::CreateEmptyFile(path)) | ||||||
|  |         return nullptr; | ||||||
|  |     return OpenFile(path, perms); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { | ||||||
|  |     const auto old_path = | ||||||
|  |         FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     const auto new_path = | ||||||
|  |         FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  | 
 | ||||||
|  |     if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || | ||||||
|  |         FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path)) | ||||||
|  |         return nullptr; | ||||||
|  |     return OpenFile(new_path, Mode::ReadWrite); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { | ||||||
|  |     const auto old_path = | ||||||
|  |         FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     const auto new_path = | ||||||
|  |         FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  | 
 | ||||||
|  |     if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || | ||||||
|  |         FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) | ||||||
|  |         return nullptr; | ||||||
|  | 
 | ||||||
|  |     if (cache.find(old_path) != cache.end()) { | ||||||
|  |         auto cached = cache[old_path]; | ||||||
|  |         if (!cached.expired()) { | ||||||
|  |             auto file = cached.lock(); | ||||||
|  |             file->Open(new_path, "r+b"); | ||||||
|  |             cache.erase(old_path); | ||||||
|  |             cache[new_path] = file; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return OpenFile(new_path, Mode::ReadWrite); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool RealVfsFilesystem::DeleteFile(std::string_view path_) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     if (cache.find(path) != cache.end()) { | ||||||
|  |         if (!cache[path].expired()) | ||||||
|  |             cache[path].lock()->Close(); | ||||||
|  |         cache.erase(path); | ||||||
|  |     } | ||||||
|  |     return FileUtil::Delete(path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     // Cannot use make_shared as RealVfsDirectory constructor is private
 | ||||||
|  |     return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     if (!FileUtil::Exists(path) && !FileUtil::CreateDir(path)) | ||||||
|  |         return nullptr; | ||||||
|  |     // Cannot use make_shared as RealVfsDirectory constructor is private
 | ||||||
|  |     return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, | ||||||
|  |                                             std::string_view new_path_) { | ||||||
|  |     const auto old_path = | ||||||
|  |         FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     const auto new_path = | ||||||
|  |         FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || | ||||||
|  |         !FileUtil::IsDirectory(old_path)) | ||||||
|  |         return nullptr; | ||||||
|  |     FileUtil::CopyDir(old_path, new_path); | ||||||
|  |     return OpenDirectory(new_path, Mode::ReadWrite); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, | ||||||
|  |                                             std::string_view new_path_) { | ||||||
|  |     const auto old_path = | ||||||
|  |         FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     const auto new_path = | ||||||
|  |         FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || | ||||||
|  |         FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) | ||||||
|  |         return nullptr; | ||||||
|  | 
 | ||||||
|  |     for (auto& kv : cache) { | ||||||
|  |         // Path in cache starts with old_path
 | ||||||
|  |         if (kv.first.rfind(old_path, 0) == 0) { | ||||||
|  |             const auto file_old_path = | ||||||
|  |                 FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |             const auto file_new_path = | ||||||
|  |                 FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), | ||||||
|  |                                        FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |             auto cached = cache[file_old_path]; | ||||||
|  |             if (!cached.expired()) { | ||||||
|  |                 auto file = cached.lock(); | ||||||
|  |                 file->Open(file_new_path, "r+b"); | ||||||
|  |                 cache.erase(file_old_path); | ||||||
|  |                 cache[file_new_path] = file; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return OpenDirectory(new_path, Mode::ReadWrite); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { | ||||||
|  |     const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | ||||||
|  |     for (auto& kv : cache) { | ||||||
|  |         // Path in cache starts with old_path
 | ||||||
|  |         if (kv.first.rfind(path, 0) == 0) { | ||||||
|  |             if (!cache[kv.first].expired()) | ||||||
|  |                 cache[kv.first].lock()->Close(); | ||||||
|  |             cache.erase(kv.first); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return FileUtil::DeleteDirRecursively(path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_, | ||||||
|  |                          const std::string& path_, Mode perms_) | ||||||
|  |     : base(base_), backing(std::move(backing_)), path(path_), | ||||||
|       parent_path(FileUtil::GetParentPath(path_)), |       parent_path(FileUtil::GetParentPath(path_)), | ||||||
|       path_components(FileUtil::SplitPathComponents(path_)), |       path_components(FileUtil::SplitPathComponents(path_)), | ||||||
|       parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), |       parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | ||||||
| @ -48,15 +216,15 @@ std::string RealVfsFile::GetName() const { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t RealVfsFile::GetSize() const { | size_t RealVfsFile::GetSize() const { | ||||||
|     return backing.GetSize(); |     return backing->GetSize(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RealVfsFile::Resize(size_t new_size) { | bool RealVfsFile::Resize(size_t new_size) { | ||||||
|     return backing.Resize(new_size); |     return backing->Resize(new_size); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { | std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { | ||||||
|     return std::make_shared<RealVfsDirectory>(parent_path, perms); |     return base.OpenDirectory(parent_path, perms); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RealVfsFile::IsWritable() const { | bool RealVfsFile::IsWritable() const { | ||||||
| @ -68,62 +236,118 @@ bool RealVfsFile::IsReadable() const { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { | size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { | ||||||
|     if (!backing.Seek(offset, SEEK_SET)) |     if (!backing->Seek(offset, SEEK_SET)) | ||||||
|         return 0; |         return 0; | ||||||
|     return backing.ReadBytes(data, length); |     return backing->ReadBytes(data, length); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { | size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { | ||||||
|     if (!backing.Seek(offset, SEEK_SET)) |     if (!backing->Seek(offset, SEEK_SET)) | ||||||
|         return 0; |         return 0; | ||||||
|     return backing.WriteBytes(data, length); |     return backing->WriteBytes(data, length); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RealVfsFile::Rename(std::string_view name) { | bool RealVfsFile::Rename(std::string_view name) { | ||||||
|     std::string name_str(name.begin(), name.end()); |     return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr; | ||||||
|     const auto out = FileUtil::Rename(GetName(), name_str); | } | ||||||
| 
 | 
 | ||||||
|     path = (parent_path + DIR_SEP).append(name); | bool RealVfsFile::Close() { | ||||||
|     path_components = parent_components; |     return backing->Close(); | ||||||
|     path_components.push_back(std::move(name_str)); | } | ||||||
|     backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str()); | 
 | ||||||
|  | // TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if
 | ||||||
|  | // constexpr' because there is a compile error in the branch not used.
 | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { | ||||||
|  |     if (perms == Mode::Append) | ||||||
|  |         return {}; | ||||||
|  | 
 | ||||||
|  |     std::vector<VirtualFile> out; | ||||||
|  |     FileUtil::ForeachDirectoryEntry( | ||||||
|  |         nullptr, path, | ||||||
|  |         [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { | ||||||
|  |             const std::string full_path = directory + DIR_SEP + filename; | ||||||
|  |             if (!FileUtil::IsDirectory(full_path)) | ||||||
|  |                 out.emplace_back(base.OpenFile(full_path, perms)); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|     return out; |     return out; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RealVfsFile::Close() { | template <> | ||||||
|     return backing.Close(); | std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { | ||||||
|  |     if (perms == Mode::Append) | ||||||
|  |         return {}; | ||||||
|  | 
 | ||||||
|  |     std::vector<VirtualDir> out; | ||||||
|  |     FileUtil::ForeachDirectoryEntry( | ||||||
|  |         nullptr, path, | ||||||
|  |         [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { | ||||||
|  |             const std::string full_path = directory + DIR_SEP + filename; | ||||||
|  |             if (FileUtil::IsDirectory(full_path)) | ||||||
|  |                 out.emplace_back(base.OpenDirectory(full_path, perms)); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     return out; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_) | RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) | ||||||
|     : path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)), |     : base(base_), path(FileUtil::RemoveTrailingSlash(path_)), | ||||||
|  |       parent_path(FileUtil::GetParentPath(path)), | ||||||
|       path_components(FileUtil::SplitPathComponents(path)), |       path_components(FileUtil::SplitPathComponents(path)), | ||||||
|       parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), |       parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | ||||||
|       perms(perms_) { |       perms(perms_) { | ||||||
|     if (!FileUtil::Exists(path) && perms & Mode::WriteAppend) |     if (!FileUtil::Exists(path) && perms & Mode::WriteAppend) | ||||||
|         FileUtil::CreateDir(path); |         FileUtil::CreateDir(path); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     if (perms == Mode::Append) | std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { | ||||||
|         return; |     const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); | ||||||
|  |     if (!FileUtil::Exists(full_path)) | ||||||
|  |         return nullptr; | ||||||
|  |     return base.OpenFile(full_path, perms); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     FileUtil::ForeachDirectoryEntry( | std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const { | ||||||
|         nullptr, path, |     const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); | ||||||
|         [this](u64* entries_out, const std::string& directory, const std::string& filename) { |     if (!FileUtil::Exists(full_path)) | ||||||
|             std::string full_path = directory + DIR_SEP + filename; |         return nullptr; | ||||||
|             if (FileUtil::IsDirectory(full_path)) |     return base.OpenDirectory(full_path, perms); | ||||||
|                 subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms)); | } | ||||||
|             else | 
 | ||||||
|                 files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms)); | std::shared_ptr<VfsFile> RealVfsDirectory::GetFile(std::string_view name) const { | ||||||
|             return true; |     return GetFileRelative(name); | ||||||
|         }); | } | ||||||
|  | 
 | ||||||
|  | std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view name) const { | ||||||
|  |     return GetDirectoryRelative(name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) { | ||||||
|  |     const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); | ||||||
|  |     return base.CreateFile(full_path, perms); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) { | ||||||
|  |     const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); | ||||||
|  |     auto parent = std::string(FileUtil::GetParentPath(full_path)); | ||||||
|  |     return base.CreateDirectory(full_path, perms); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { | ||||||
|  |     auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name)); | ||||||
|  |     return base.DeleteDirectory(full_path); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const { | std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const { | ||||||
|     return files; |     return IterateEntries<RealVfsFile, VfsFile>(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const { | std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const { | ||||||
|     return subdirectories; |     return IterateEntries<RealVfsDirectory, VfsDirectory>(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RealVfsDirectory::IsWritable() const { | bool RealVfsDirectory::IsWritable() const { | ||||||
| @ -142,57 +366,32 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { | |||||||
|     if (path_components.size() <= 1) |     if (path_components.size() <= 1) | ||||||
|         return nullptr; |         return nullptr; | ||||||
| 
 | 
 | ||||||
|     return std::make_shared<RealVfsDirectory>(parent_path, perms); |     return base.OpenDirectory(parent_path, perms); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) { | std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) { | ||||||
|     const std::string subdir_path = (path + DIR_SEP).append(name); |     const std::string subdir_path = (path + DIR_SEP).append(name); | ||||||
| 
 |     return base.CreateDirectory(subdir_path, perms); | ||||||
|     if (!FileUtil::CreateDir(subdir_path)) { |  | ||||||
|         return nullptr; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms)); |  | ||||||
|     return subdirectories.back(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) { | std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) { | ||||||
|     const std::string file_path = (path + DIR_SEP).append(name); |     const std::string file_path = (path + DIR_SEP).append(name); | ||||||
| 
 |     return base.CreateFile(file_path, perms); | ||||||
|     if (!FileUtil::CreateEmptyFile(file_path)) { |  | ||||||
|         return nullptr; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms)); |  | ||||||
|     return files.back(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) { | bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) { | ||||||
|     const std::string subdir_path = (path + DIR_SEP).append(name); |     const std::string subdir_path = (path + DIR_SEP).append(name); | ||||||
| 
 |     return base.DeleteDirectory(subdir_path); | ||||||
|     return FileUtil::DeleteDirRecursively(subdir_path); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RealVfsDirectory::DeleteFile(std::string_view name) { | bool RealVfsDirectory::DeleteFile(std::string_view name) { | ||||||
|     const auto file = GetFile(name); |  | ||||||
| 
 |  | ||||||
|     if (file == nullptr) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     files.erase(std::find(files.begin(), files.end(), file)); |  | ||||||
| 
 |  | ||||||
|     auto real_file = std::static_pointer_cast<RealVfsFile>(file); |  | ||||||
|     real_file->Close(); |  | ||||||
| 
 |  | ||||||
|     const std::string file_path = (path + DIR_SEP).append(name); |     const std::string file_path = (path + DIR_SEP).append(name); | ||||||
|     return FileUtil::Delete(file_path); |     return base.DeleteFile(file_path); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RealVfsDirectory::Rename(std::string_view name) { | bool RealVfsDirectory::Rename(std::string_view name) { | ||||||
|     const std::string new_name = (parent_path + DIR_SEP).append(name); |     const std::string new_name = (parent_path + DIR_SEP).append(name); | ||||||
| 
 |     return base.MoveFile(path, new_name) != nullptr; | ||||||
|     return FileUtil::Rename(path, new_name); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string RealVfsDirectory::GetFullPath() const { | std::string RealVfsDirectory::GetFullPath() const { | ||||||
| @ -202,16 +401,6 @@ std::string RealVfsDirectory::GetFullPath() const { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||||||
|     const auto iter = std::find(files.begin(), files.end(), file); |     return false; | ||||||
|     if (iter == files.end()) |  | ||||||
|         return false; |  | ||||||
| 
 |  | ||||||
|     const std::ptrdiff_t offset = std::distance(files.begin(), iter); |  | ||||||
|     files[offset] = files.back(); |  | ||||||
|     files.pop_back(); |  | ||||||
| 
 |  | ||||||
|     subdirectories.emplace_back(std::move(dir)); |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } | } | ||||||
| } // namespace FileSys
 | } // namespace FileSys
 | ||||||
|  | |||||||
| @ -6,18 +6,45 @@ | |||||||
| 
 | 
 | ||||||
| #include <string_view> | #include <string_view> | ||||||
| 
 | 
 | ||||||
|  | #include <boost/container/flat_map.hpp> | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| #include "core/file_sys/mode.h" | #include "core/file_sys/mode.h" | ||||||
| #include "core/file_sys/vfs.h" | #include "core/file_sys/vfs.h" | ||||||
| 
 | 
 | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
| 
 | 
 | ||||||
|  | class RealVfsFilesystem : public VfsFilesystem { | ||||||
|  | public: | ||||||
|  |     RealVfsFilesystem(); | ||||||
|  | 
 | ||||||
|  |     std::string GetName() const override; | ||||||
|  |     bool IsReadable() const override; | ||||||
|  |     bool IsWritable() const override; | ||||||
|  |     VfsEntryType GetEntryType(std::string_view path) const override; | ||||||
|  |     VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override; | ||||||
|  |     VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override; | ||||||
|  |     VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override; | ||||||
|  |     VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override; | ||||||
|  |     bool DeleteFile(std::string_view path) override; | ||||||
|  |     VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override; | ||||||
|  |     VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override; | ||||||
|  |     VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override; | ||||||
|  |     VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override; | ||||||
|  |     bool DeleteDirectory(std::string_view path) override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| // An implmentation of VfsFile that represents a file on the user's computer.
 | // An implmentation of VfsFile that represents a file on the user's computer.
 | ||||||
| struct RealVfsFile : public VfsFile { | class RealVfsFile : public VfsFile { | ||||||
|     friend struct RealVfsDirectory; |     friend class RealVfsDirectory; | ||||||
|  |     friend class RealVfsFilesystem; | ||||||
| 
 | 
 | ||||||
|     RealVfsFile(const std::string& name, Mode perms = Mode::Read); |     RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing, | ||||||
|  |                 const std::string& path, Mode perms = Mode::Read); | ||||||
| 
 | 
 | ||||||
|  | public: | ||||||
|     std::string GetName() const override; |     std::string GetName() const override; | ||||||
|     size_t GetSize() const override; |     size_t GetSize() const override; | ||||||
|     bool Resize(size_t new_size) override; |     bool Resize(size_t new_size) override; | ||||||
| @ -31,7 +58,8 @@ struct RealVfsFile : public VfsFile { | |||||||
| private: | private: | ||||||
|     bool Close(); |     bool Close(); | ||||||
| 
 | 
 | ||||||
|     FileUtil::IOFile backing; |     RealVfsFilesystem& base; | ||||||
|  |     std::shared_ptr<FileUtil::IOFile> backing; | ||||||
|     std::string path; |     std::string path; | ||||||
|     std::string parent_path; |     std::string parent_path; | ||||||
|     std::vector<std::string> path_components; |     std::vector<std::string> path_components; | ||||||
| @ -40,9 +68,19 @@ private: | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // An implementation of VfsDirectory that represents a directory on the user's computer.
 | // An implementation of VfsDirectory that represents a directory on the user's computer.
 | ||||||
| struct RealVfsDirectory : public VfsDirectory { | class RealVfsDirectory : public VfsDirectory { | ||||||
|     RealVfsDirectory(const std::string& path, Mode perms = Mode::Read); |     friend class RealVfsFilesystem; | ||||||
| 
 | 
 | ||||||
|  |     RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override; | ||||||
|  |     std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override; | ||||||
|  |     std::shared_ptr<VfsFile> GetFile(std::string_view name) const override; | ||||||
|  |     std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override; | ||||||
|  |     std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override; | ||||||
|  |     std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override; | ||||||
|  |     bool DeleteSubdirectoryRecursive(std::string_view name) override; | ||||||
|     std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; |     std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | ||||||
|     std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; |     std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | ||||||
|     bool IsWritable() const override; |     bool IsWritable() const override; | ||||||
| @ -60,13 +98,15 @@ protected: | |||||||
|     bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; |     bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |     template <typename T, typename R> | ||||||
|  |     std::vector<std::shared_ptr<R>> IterateEntries() const; | ||||||
|  | 
 | ||||||
|  |     RealVfsFilesystem& base; | ||||||
|     std::string path; |     std::string path; | ||||||
|     std::string parent_path; |     std::string parent_path; | ||||||
|     std::vector<std::string> path_components; |     std::vector<std::string> path_components; | ||||||
|     std::vector<std::string> parent_components; |     std::vector<std::string> parent_components; | ||||||
|     Mode perms; |     Mode perms; | ||||||
|     std::vector<std::shared_ptr<VfsFile>> files; |  | ||||||
|     std::vector<std::shared_ptr<VfsDirectory>> subdirectories; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace FileSys
 | } // namespace FileSys
 | ||||||
|  | |||||||
| @ -201,7 +201,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb | |||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { | ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) { | ||||||
|     std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; |     std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; | ||||||
|     Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), |     Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), | ||||||
|                       dst_cmdbuf.size() * sizeof(u32)); |                       dst_cmdbuf.size() * sizeof(u32)); | ||||||
|  | |||||||
| @ -132,7 +132,7 @@ public: | |||||||
|     ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, |     ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, | ||||||
|                                                  HandleTable& src_table); |                                                  HandleTable& src_table); | ||||||
|     /// Writes data from this context back to the requesting process/thread.
 |     /// Writes data from this context back to the requesting process/thread.
 | ||||||
|     ResultCode WriteToOutgoingCommandBuffer(Thread& thread); |     ResultCode WriteToOutgoingCommandBuffer(const Thread& thread); | ||||||
| 
 | 
 | ||||||
|     u32_le GetCommand() const { |     u32_le GetCommand() const { | ||||||
|         return command; |         return command; | ||||||
|  | |||||||
| @ -38,7 +38,7 @@ public: | |||||||
|             {0, &IProfile::Get, "Get"}, |             {0, &IProfile::Get, "Get"}, | ||||||
|             {1, &IProfile::GetBase, "GetBase"}, |             {1, &IProfile::GetBase, "GetBase"}, | ||||||
|             {10, nullptr, "GetImageSize"}, |             {10, nullptr, "GetImageSize"}, | ||||||
|             {11, nullptr, "LoadImage"}, |             {11, &IProfile::LoadImage, "LoadImage"}, | ||||||
|         }; |         }; | ||||||
|         RegisterHandlers(functions); |         RegisterHandlers(functions); | ||||||
|     } |     } | ||||||
| @ -72,6 +72,27 @@ private: | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void LoadImage(Kernel::HLERequestContext& ctx) { | ||||||
|  |         LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||||||
|  |         // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
 | ||||||
|  |         // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
 | ||||||
|  |         const u32 jpeg_size = 107; | ||||||
|  |         static const std::array<u8, jpeg_size> jpeg{ | ||||||
|  |             0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, | ||||||
|  |             0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, | ||||||
|  |             0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, | ||||||
|  |             0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, | ||||||
|  |             0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, | ||||||
|  |             0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00, | ||||||
|  |             0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, | ||||||
|  |             0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||||||
|  |         }; | ||||||
|  |         ctx.WriteBuffer(jpeg.data(), jpeg_size); | ||||||
|  |         IPC::ResponseBuilder rb{ctx, 3}; | ||||||
|  |         rb.Push(RESULT_SUCCESS); | ||||||
|  |         rb.Push<u32>(jpeg_size); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     ProfileManager& profile_manager; |     ProfileManager& profile_manager; | ||||||
|     UUID user_id; ///< The user id this profile refers to.
 |     UUID user_id; ///< The user id this profile refers to.
 | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -136,7 +136,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger | |||||||
|         {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, |         {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, | ||||||
|         {17, nullptr, "SetControllerFirmwareUpdateSection"}, |         {17, nullptr, "SetControllerFirmwareUpdateSection"}, | ||||||
|         {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, |         {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, | ||||||
|         {19, nullptr, "SetScreenShotImageOrientation"}, |         {19, &ISelfController::SetScreenShotImageOrientation, "SetScreenShotImageOrientation"}, | ||||||
|         {20, nullptr, "SetDesirableKeyboardLayout"}, |         {20, nullptr, "SetDesirableKeyboardLayout"}, | ||||||
|         {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, |         {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, | ||||||
|         {41, nullptr, "IsSystemBufferSharingEnabled"}, |         {41, nullptr, "IsSystemBufferSharingEnabled"}, | ||||||
| @ -254,6 +254,13 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& | |||||||
|     LOG_WARNING(Service_AM, "(STUBBED) called"); |     LOG_WARNING(Service_AM, "(STUBBED) called"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::ResponseBuilder rb{ctx, 2}; | ||||||
|  |     rb.Push(RESULT_SUCCESS); | ||||||
|  | 
 | ||||||
|  |     LOG_WARNING(Service_AM, "(STUBBED) called"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { | void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { | ||||||
|     // TODO(Subv): Find out how AM determines the display to use, for now just create the layer
 |     // TODO(Subv): Find out how AM determines the display to use, for now just create the layer
 | ||||||
|     // in the Default display.
 |     // in the Default display.
 | ||||||
|  | |||||||
| @ -83,6 +83,7 @@ private: | |||||||
|     void LockExit(Kernel::HLERequestContext& ctx); |     void LockExit(Kernel::HLERequestContext& ctx); | ||||||
|     void UnlockExit(Kernel::HLERequestContext& ctx); |     void UnlockExit(Kernel::HLERequestContext& ctx); | ||||||
|     void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx); |     void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx); | ||||||
|  |     void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx); | ||||||
|     void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); |     void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); | ||||||
|     void SetScreenShotPermission(Kernel::HLERequestContext& ctx); |     void SetScreenShotPermission(Kernel::HLERequestContext& ctx); | ||||||
|     void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); |     void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); | ||||||
|  | |||||||
| @ -59,7 +59,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 | |||||||
| ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { | ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { | ||||||
|     std::string path(FileUtil::SanitizePath(path_)); |     std::string path(FileUtil::SanitizePath(path_)); | ||||||
|     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); |     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||||||
|     if (path == "/" || path == "\\") { |     if (path.empty()) { | ||||||
|         // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
 |         // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
 | ||||||
|         return RESULT_SUCCESS; |         return RESULT_SUCCESS; | ||||||
|     } |     } | ||||||
| @ -281,15 +281,15 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() { | |||||||
|     return sdmc_factory->Open(); |     return sdmc_factory->Open(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RegisterFileSystems() { | void RegisterFileSystems(const FileSys::VirtualFilesystem& vfs) { | ||||||
|     romfs_factory = nullptr; |     romfs_factory = nullptr; | ||||||
|     save_data_factory = nullptr; |     save_data_factory = nullptr; | ||||||
|     sdmc_factory = nullptr; |     sdmc_factory = nullptr; | ||||||
| 
 | 
 | ||||||
|     auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>( |     auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), | ||||||
|         FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite); |                                              FileSys::Mode::ReadWrite); | ||||||
|     auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>( |     auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), | ||||||
|         FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite); |                                            FileSys::Mode::ReadWrite); | ||||||
| 
 | 
 | ||||||
|     auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); |     auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); | ||||||
|     save_data_factory = std::move(savedata); |     save_data_factory = std::move(savedata); | ||||||
| @ -298,8 +298,8 @@ void RegisterFileSystems() { | |||||||
|     sdmc_factory = std::move(sdcard); |     sdmc_factory = std::move(sdcard); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void InstallInterfaces(SM::ServiceManager& service_manager) { | void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) { | ||||||
|     RegisterFileSystems(); |     RegisterFileSystems(vfs); | ||||||
|     std::make_shared<FSP_LDR>()->InstallAsService(service_manager); |     std::make_shared<FSP_LDR>()->InstallAsService(service_manager); | ||||||
|     std::make_shared<FSP_PR>()->InstallAsService(service_manager); |     std::make_shared<FSP_PR>()->InstallAsService(service_manager); | ||||||
|     std::make_shared<FSP_SRV>()->InstallAsService(service_manager); |     std::make_shared<FSP_SRV>()->InstallAsService(service_manager); | ||||||
|  | |||||||
| @ -36,7 +36,7 @@ ResultVal<FileSys::VirtualDir> OpenSDMC(); | |||||||
| // ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();
 | // ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();
 | ||||||
| 
 | 
 | ||||||
| /// Registers all Filesystem services with the specified service manager.
 | /// Registers all Filesystem services with the specified service manager.
 | ||||||
| void InstallInterfaces(SM::ServiceManager& service_manager); | void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs); | ||||||
| 
 | 
 | ||||||
| // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
 | // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
 | ||||||
| // pointers and booleans. This makes using a VfsDirectory with switch services much easier and
 | // pointers and booleans. This makes using a VfsDirectory with switch services much easier and
 | ||||||
|  | |||||||
| @ -193,13 +193,10 @@ private: | |||||||
| template <typename T> | template <typename T> | ||||||
| static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data, | static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data, | ||||||
|                             FileSys::EntryType type) { |                             FileSys::EntryType type) { | ||||||
|  |     entries.reserve(entries.size() + new_data.size()); | ||||||
|  | 
 | ||||||
|     for (const auto& new_entry : new_data) { |     for (const auto& new_entry : new_data) { | ||||||
|         FileSys::Entry entry; |         entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize()); | ||||||
|         entry.filename[0] = '\0'; |  | ||||||
|         std::strncat(entry.filename, new_entry->GetName().c_str(), FileSys::FILENAME_LENGTH - 1); |  | ||||||
|         entry.type = type; |  | ||||||
|         entry.file_size = new_entry->GetSize(); |  | ||||||
|         entries.emplace_back(std::move(entry)); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -147,7 +147,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp | |||||||
|     } |     } | ||||||
|     params.fence_out.id = 0; |     params.fence_out.id = 0; | ||||||
|     params.fence_out.value = 0; |     params.fence_out.value = 0; | ||||||
|     std::memcpy(output.data(), ¶ms, output.size()); |     std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -16,19 +16,18 @@ | |||||||
| #include "core/hle/service/nvdrv/interface.h" | #include "core/hle/service/nvdrv/interface.h" | ||||||
| #include "core/hle/service/nvdrv/nvdrv.h" | #include "core/hle/service/nvdrv/nvdrv.h" | ||||||
| #include "core/hle/service/nvdrv/nvmemp.h" | #include "core/hle/service/nvdrv/nvmemp.h" | ||||||
|  | #include "core/hle/service/nvflinger/nvflinger.h" | ||||||
| 
 | 
 | ||||||
| namespace Service::Nvidia { | namespace Service::Nvidia { | ||||||
| 
 | 
 | ||||||
| std::weak_ptr<Module> nvdrv; | void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger) { | ||||||
| 
 |  | ||||||
| void InstallInterfaces(SM::ServiceManager& service_manager) { |  | ||||||
|     auto module_ = std::make_shared<Module>(); |     auto module_ = std::make_shared<Module>(); | ||||||
|     std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager); |     std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager); | ||||||
|     std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager); |     std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager); | ||||||
|     std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager); |     std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager); | ||||||
|     std::make_shared<NVDRV>(module_, "nvdrv:t")->InstallAsService(service_manager); |     std::make_shared<NVDRV>(module_, "nvdrv:t")->InstallAsService(service_manager); | ||||||
|     std::make_shared<NVMEMP>()->InstallAsService(service_manager); |     std::make_shared<NVMEMP>()->InstallAsService(service_manager); | ||||||
|     nvdrv = module_; |     nvflinger.SetNVDrvInstance(module_); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Module::Module() { | Module::Module() { | ||||||
|  | |||||||
| @ -10,6 +10,10 @@ | |||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
| 
 | 
 | ||||||
|  | namespace Service::NVFlinger { | ||||||
|  | class NVFlinger; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| namespace Service::Nvidia { | namespace Service::Nvidia { | ||||||
| 
 | 
 | ||||||
| namespace Devices { | namespace Devices { | ||||||
| @ -56,8 +60,6 @@ private: | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Registers all NVDRV services with the specified service manager.
 | /// Registers all NVDRV services with the specified service manager.
 | ||||||
| void InstallInterfaces(SM::ServiceManager& service_manager); | void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger); | ||||||
| 
 |  | ||||||
| extern std::weak_ptr<Module> nvdrv; |  | ||||||
| 
 | 
 | ||||||
| } // namespace Service::Nvidia
 | } // namespace Service::Nvidia
 | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { | |||||||
|         Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle"); |         Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) { | void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { | ||||||
|     Buffer buffer{}; |     Buffer buffer{}; | ||||||
|     buffer.slot = slot; |     buffer.slot = slot; | ||||||
|     buffer.igbp_buffer = igbp_buffer; |     buffer.igbp_buffer = igbp_buffer; | ||||||
|  | |||||||
| @ -72,7 +72,7 @@ public: | |||||||
|         MathUtil::Rectangle<int> crop_rect; |         MathUtil::Rectangle<int> crop_rect; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer); |     void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); | ||||||
|     boost::optional<u32> DequeueBuffer(u32 width, u32 height); |     boost::optional<u32> DequeueBuffer(u32 width, u32 height); | ||||||
|     const IGBPBuffer& RequestBuffer(u32 slot) const; |     const IGBPBuffer& RequestBuffer(u32 slot) const; | ||||||
|     void QueueBuffer(u32 slot, BufferTransformFlags transform, |     void QueueBuffer(u32 slot, BufferTransformFlags transform, | ||||||
|  | |||||||
| @ -46,6 +46,10 @@ NVFlinger::~NVFlinger() { | |||||||
|     CoreTiming::UnscheduleEvent(composition_event, 0); |     CoreTiming::UnscheduleEvent(composition_event, 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { | ||||||
|  |     nvdrv = std::move(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| u64 NVFlinger::OpenDisplay(std::string_view name) { | u64 NVFlinger::OpenDisplay(std::string_view name) { | ||||||
|     LOG_WARNING(Service, "Opening display {}", name); |     LOG_WARNING(Service, "Opening display {}", name); | ||||||
| 
 | 
 | ||||||
| @ -141,9 +145,6 @@ void NVFlinger::Compose() { | |||||||
|         auto& igbp_buffer = buffer->igbp_buffer; |         auto& igbp_buffer = buffer->igbp_buffer; | ||||||
| 
 | 
 | ||||||
|         // Now send the buffer to the GPU for drawing.
 |         // Now send the buffer to the GPU for drawing.
 | ||||||
|         auto nvdrv = Nvidia::nvdrv.lock(); |  | ||||||
|         ASSERT(nvdrv); |  | ||||||
| 
 |  | ||||||
|         // TODO(Subv): Support more than just disp0. The display device selection is probably based
 |         // TODO(Subv): Support more than just disp0. The display device selection is probably based
 | ||||||
|         // on which display we're drawing (Default, Internal, External, etc)
 |         // on which display we're drawing (Default, Internal, External, etc)
 | ||||||
|         auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); |         auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); | ||||||
|  | |||||||
| @ -16,6 +16,10 @@ namespace CoreTiming { | |||||||
| struct EventType; | struct EventType; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | namespace Service::Nvidia { | ||||||
|  | class Module; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| namespace Service::NVFlinger { | namespace Service::NVFlinger { | ||||||
| 
 | 
 | ||||||
| class BufferQueue; | class BufferQueue; | ||||||
| @ -44,6 +48,9 @@ public: | |||||||
|     NVFlinger(); |     NVFlinger(); | ||||||
|     ~NVFlinger(); |     ~NVFlinger(); | ||||||
| 
 | 
 | ||||||
|  |     /// Sets the NVDrv module instance to use to send buffers to the GPU.
 | ||||||
|  |     void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance); | ||||||
|  | 
 | ||||||
|     /// Opens the specified display and returns the id.
 |     /// Opens the specified display and returns the id.
 | ||||||
|     u64 OpenDisplay(std::string_view name); |     u64 OpenDisplay(std::string_view name); | ||||||
| 
 | 
 | ||||||
| @ -70,6 +77,8 @@ private: | |||||||
|     /// Returns the layer identified by the specified id in the desired display.
 |     /// Returns the layer identified by the specified id in the desired display.
 | ||||||
|     Layer& GetLayer(u64 display_id, u64 layer_id); |     Layer& GetLayer(u64 display_id, u64 layer_id); | ||||||
| 
 | 
 | ||||||
|  |     std::shared_ptr<Nvidia::Module> nvdrv; | ||||||
|  | 
 | ||||||
|     std::vector<Display> displays; |     std::vector<Display> displays; | ||||||
|     std::vector<std::shared_ptr<BufferQueue>> buffer_queues; |     std::vector<std::shared_ptr<BufferQueue>> buffer_queues; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -198,7 +198,7 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Initialize ServiceManager
 | /// Initialize ServiceManager
 | ||||||
| void Init(std::shared_ptr<SM::ServiceManager>& sm) { | void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) { | ||||||
|     // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
 |     // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
 | ||||||
|     // here and pass it into the respective InstallInterfaces functions.
 |     // here and pass it into the respective InstallInterfaces functions.
 | ||||||
|     auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); |     auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); | ||||||
| @ -221,7 +221,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) { | |||||||
|     EUPLD::InstallInterfaces(*sm); |     EUPLD::InstallInterfaces(*sm); | ||||||
|     Fatal::InstallInterfaces(*sm); |     Fatal::InstallInterfaces(*sm); | ||||||
|     FGM::InstallInterfaces(*sm); |     FGM::InstallInterfaces(*sm); | ||||||
|     FileSystem::InstallInterfaces(*sm); |     FileSystem::InstallInterfaces(*sm, rfs); | ||||||
|     Friend::InstallInterfaces(*sm); |     Friend::InstallInterfaces(*sm); | ||||||
|     GRC::InstallInterfaces(*sm); |     GRC::InstallInterfaces(*sm); | ||||||
|     HID::InstallInterfaces(*sm); |     HID::InstallInterfaces(*sm); | ||||||
| @ -238,7 +238,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) { | |||||||
|     NIFM::InstallInterfaces(*sm); |     NIFM::InstallInterfaces(*sm); | ||||||
|     NIM::InstallInterfaces(*sm); |     NIM::InstallInterfaces(*sm); | ||||||
|     NS::InstallInterfaces(*sm); |     NS::InstallInterfaces(*sm); | ||||||
|     Nvidia::InstallInterfaces(*sm); |     Nvidia::InstallInterfaces(*sm, *nv_flinger); | ||||||
|     PCIe::InstallInterfaces(*sm); |     PCIe::InstallInterfaces(*sm); | ||||||
|     PCTL::InstallInterfaces(*sm); |     PCTL::InstallInterfaces(*sm); | ||||||
|     PCV::InstallInterfaces(*sm); |     PCV::InstallInterfaces(*sm); | ||||||
|  | |||||||
| @ -22,6 +22,10 @@ class ServerSession; | |||||||
| class HLERequestContext; | class HLERequestContext; | ||||||
| } // namespace Kernel
 | } // namespace Kernel
 | ||||||
| 
 | 
 | ||||||
|  | namespace FileSys { | ||||||
|  | struct VfsFilesystem; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| namespace Service { | namespace Service { | ||||||
| 
 | 
 | ||||||
| namespace SM { | namespace SM { | ||||||
| @ -177,7 +181,8 @@ private: | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Initialize ServiceManager
 | /// Initialize ServiceManager
 | ||||||
| void Init(std::shared_ptr<SM::ServiceManager>& sm); | void Init(std::shared_ptr<SM::ServiceManager>& sm, | ||||||
|  |           const std::shared_ptr<FileSys::VfsFilesystem>& vfs); | ||||||
| 
 | 
 | ||||||
| /// Shutdown ServiceManager
 | /// Shutdown ServiceManager
 | ||||||
| void Shutdown(); | void Shutdown(); | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/file_sys/content_archive.h" | #include "core/file_sys/content_archive.h" | ||||||
|  | #include "core/file_sys/control_metadata.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/kernel/resource_limit.h" | #include "core/hle/kernel/resource_limit.h" | ||||||
| @ -17,8 +18,50 @@ | |||||||
| 
 | 
 | ||||||
| namespace Loader { | namespace Loader { | ||||||
| 
 | 
 | ||||||
| AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file) | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_) | ||||||
|     : AppLoader(std::move(file)) {} |     : AppLoader(std::move(file_)) { | ||||||
|  |     const auto dir = file->GetContainingDirectory(); | ||||||
|  | 
 | ||||||
|  |     // Icon
 | ||||||
|  |     FileSys::VirtualFile icon_file = nullptr; | ||||||
|  |     for (const auto& language : FileSys::LANGUAGE_NAMES) { | ||||||
|  |         icon_file = dir->GetFile("icon_" + std::string(language) + ".dat"); | ||||||
|  |         if (icon_file != nullptr) { | ||||||
|  |             icon_data = icon_file->ReadAllBytes(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (icon_data.empty()) { | ||||||
|  |         // Any png, jpeg, or bmp file
 | ||||||
|  |         const auto& files = dir->GetFiles(); | ||||||
|  |         const auto icon_iter = | ||||||
|  |             std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { | ||||||
|  |                 return file->GetExtension() == "png" || file->GetExtension() == "jpg" || | ||||||
|  |                        file->GetExtension() == "bmp" || file->GetExtension() == "jpeg"; | ||||||
|  |             }); | ||||||
|  |         if (icon_iter != files.end()) | ||||||
|  |             icon_data = (*icon_iter)->ReadAllBytes(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Metadata
 | ||||||
|  |     FileSys::VirtualFile nacp_file = dir->GetFile("control.nacp"); | ||||||
|  |     if (nacp_file == nullptr) { | ||||||
|  |         const auto& files = dir->GetFiles(); | ||||||
|  |         const auto nacp_iter = | ||||||
|  |             std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { | ||||||
|  |                 return file->GetExtension() == "nacp"; | ||||||
|  |             }); | ||||||
|  |         if (nacp_iter != files.end()) | ||||||
|  |             nacp_file = *nacp_iter; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (nacp_file != nullptr) { | ||||||
|  |         FileSys::NACP nacp(nacp_file); | ||||||
|  |         title_id = nacp.GetTitleId(); | ||||||
|  |         name = nacp.GetApplicationName(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( | ||||||
|     FileSys::VirtualDir directory) |     FileSys::VirtualDir directory) | ||||||
| @ -105,4 +148,25 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile | |||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) { | ||||||
|  |     if (icon_data.empty()) | ||||||
|  |         return ResultStatus::ErrorNotUsed; | ||||||
|  |     buffer = icon_data; | ||||||
|  |     return ResultStatus::Success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) { | ||||||
|  |     if (name.empty()) | ||||||
|  |         return ResultStatus::ErrorNotUsed; | ||||||
|  |     out_program_id = title_id; | ||||||
|  |     return ResultStatus::Success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) { | ||||||
|  |     if (name.empty()) | ||||||
|  |         return ResultStatus::ErrorNotUsed; | ||||||
|  |     title = name; | ||||||
|  |     return ResultStatus::Success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace Loader
 | } // namespace Loader
 | ||||||
|  | |||||||
| @ -39,11 +39,18 @@ public: | |||||||
|     ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |     ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | ||||||
| 
 | 
 | ||||||
|     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | ||||||
|  |     ResultStatus ReadIcon(std::vector<u8>& buffer) override; | ||||||
|  |     ResultStatus ReadProgramId(u64& out_program_id) override; | ||||||
|  |     ResultStatus ReadTitle(std::string& title) override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     FileSys::ProgramMetadata metadata; |     FileSys::ProgramMetadata metadata; | ||||||
|     FileSys::VirtualFile romfs; |     FileSys::VirtualFile romfs; | ||||||
|     FileSys::VirtualDir dir; |     FileSys::VirtualDir dir; | ||||||
|  | 
 | ||||||
|  |     std::vector<u8> icon_data; | ||||||
|  |     std::string name; | ||||||
|  |     u64 title_id{}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace Loader
 | } // namespace Loader
 | ||||||
|  | |||||||
| @ -43,10 +43,6 @@ FileType IdentifyFile(FileSys::VirtualFile file) { | |||||||
|     return FileType::Unknown; |     return FileType::Unknown; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FileType IdentifyFile(const std::string& file_name) { |  | ||||||
|     return IdentifyFile(std::make_shared<FileSys::RealVfsFile>(file_name)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| FileType GuessFromFilename(const std::string& name) { | FileType GuessFromFilename(const std::string& name) { | ||||||
|     if (name == "main") |     if (name == "main") | ||||||
|         return FileType::DeconstructedRomDirectory; |         return FileType::DeconstructedRomDirectory; | ||||||
| @ -68,7 +64,7 @@ FileType GuessFromFilename(const std::string& name) { | |||||||
|     return FileType::Unknown; |     return FileType::Unknown; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const char* GetFileTypeString(FileType type) { | std::string GetFileTypeString(FileType type) { | ||||||
|     switch (type) { |     switch (type) { | ||||||
|     case FileType::ELF: |     case FileType::ELF: | ||||||
|         return "ELF"; |         return "ELF"; | ||||||
|  | |||||||
| @ -42,14 +42,6 @@ enum class FileType { | |||||||
|  */ |  */ | ||||||
| FileType IdentifyFile(FileSys::VirtualFile file); | FileType IdentifyFile(FileSys::VirtualFile file); | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * Identifies the type of a bootable file based on the magic value in its header. |  | ||||||
|  * @param file_name path to file |  | ||||||
|  * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine |  | ||||||
|  * a filetype, and will never return FileType::Error. |  | ||||||
|  */ |  | ||||||
| FileType IdentifyFile(const std::string& file_name); |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * Guess the type of a bootable file from its name |  * Guess the type of a bootable file from its name | ||||||
|  * @param name String name of bootable file |  * @param name String name of bootable file | ||||||
| @ -61,7 +53,7 @@ FileType GuessFromFilename(const std::string& name); | |||||||
| /**
 | /**
 | ||||||
|  * Convert a FileType into a string which can be displayed to the user. |  * Convert a FileType into a string which can be displayed to the user. | ||||||
|  */ |  */ | ||||||
| const char* GetFileTypeString(FileType type); | std::string GetFileTypeString(FileType type); | ||||||
| 
 | 
 | ||||||
| /// Return type for functions in Loader namespace
 | /// Return type for functions in Loader namespace
 | ||||||
| enum class ResultStatus { | enum class ResultStatus { | ||||||
|  | |||||||
| @ -77,8 +77,8 @@ ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { | ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { | ||||||
|     if (nca == nullptr) |     if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) | ||||||
|         return ResultStatus::ErrorNotLoaded; |         return ResultStatus::ErrorInvalidFormat; | ||||||
|     out_program_id = nca->GetTitleId(); |     out_program_id = nca->GetTitleId(); | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
|  | |||||||
| @ -33,7 +33,6 @@ public: | |||||||
|     ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |     ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | ||||||
| 
 | 
 | ||||||
|     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | ||||||
| 
 |  | ||||||
|     ResultStatus ReadProgramId(u64& out_program_id) override; |     ResultStatus ReadProgramId(u64& out_program_id) override; | ||||||
| 
 | 
 | ||||||
|     ~AppLoader_NCA(); |     ~AppLoader_NCA(); | ||||||
| @ -41,6 +40,7 @@ public: | |||||||
| private: | private: | ||||||
|     FileSys::ProgramMetadata metadata; |     FileSys::ProgramMetadata metadata; | ||||||
| 
 | 
 | ||||||
|  |     FileSys::NCAHeader header; | ||||||
|     std::unique_ptr<FileSys::NCA> nca; |     std::unique_ptr<FileSys::NCA> nca; | ||||||
|     std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader; |     std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -26,7 +26,25 @@ namespace Loader { | |||||||
| AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) | AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) | ||||||
|     : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), |     : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), | ||||||
|       nca_loader(std::make_unique<AppLoader_NCA>( |       nca_loader(std::make_unique<AppLoader_NCA>( | ||||||
|           xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {} |           xci->GetNCAFileByType(FileSys::NCAContentType::Program))) { | ||||||
|  |     if (xci->GetStatus() != ResultStatus::Success) | ||||||
|  |         return; | ||||||
|  |     const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control); | ||||||
|  |     if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) | ||||||
|  |         return; | ||||||
|  |     const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS()); | ||||||
|  |     if (romfs == nullptr) | ||||||
|  |         return; | ||||||
|  |     for (const auto& language : FileSys::LANGUAGE_NAMES) { | ||||||
|  |         icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat"); | ||||||
|  |         if (icon_file != nullptr) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |     const auto nacp_raw = romfs->GetFile("control.nacp"); | ||||||
|  |     if (nacp_raw == nullptr) | ||||||
|  |         return; | ||||||
|  |     nacp_file = std::make_shared<FileSys::NACP>(nacp_raw); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| AppLoader_XCI::~AppLoader_XCI() = default; | AppLoader_XCI::~AppLoader_XCI() = default; | ||||||
| 
 | 
 | ||||||
| @ -71,4 +89,17 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) { | |||||||
|     return nca_loader->ReadProgramId(out_program_id); |     return nca_loader->ReadProgramId(out_program_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) { | ||||||
|  |     if (icon_file == nullptr) | ||||||
|  |         return ResultStatus::ErrorInvalidFormat; | ||||||
|  |     buffer = icon_file->ReadAllBytes(); | ||||||
|  |     return ResultStatus::Success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ResultStatus AppLoader_XCI::ReadTitle(std::string& title) { | ||||||
|  |     if (nacp_file == nullptr) | ||||||
|  |         return ResultStatus::ErrorInvalidFormat; | ||||||
|  |     title = nacp_file->GetApplicationName(); | ||||||
|  |     return ResultStatus::Success; | ||||||
|  | } | ||||||
| } // namespace Loader
 | } // namespace Loader
 | ||||||
|  | |||||||
| @ -33,12 +33,17 @@ public: | |||||||
| 
 | 
 | ||||||
|     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | ||||||
|     ResultStatus ReadProgramId(u64& out_program_id) override; |     ResultStatus ReadProgramId(u64& out_program_id) override; | ||||||
|  |     ResultStatus ReadIcon(std::vector<u8>& buffer) override; | ||||||
|  |     ResultStatus ReadTitle(std::string& title) override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     FileSys::ProgramMetadata metadata; |     FileSys::ProgramMetadata metadata; | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<FileSys::XCI> xci; |     std::unique_ptr<FileSys::XCI> xci; | ||||||
|     std::unique_ptr<AppLoader_NCA> nca_loader; |     std::unique_ptr<AppLoader_NCA> nca_loader; | ||||||
|  | 
 | ||||||
|  |     FileSys::VirtualFile icon_file; | ||||||
|  |     std::shared_ptr<FileSys::NACP> nacp_file; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace Loader
 | } // namespace Loader
 | ||||||
|  | |||||||
| @ -23,12 +23,17 @@ Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& | |||||||
|     : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {} |     : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {} | ||||||
| 
 | 
 | ||||||
| void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { | void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { | ||||||
|     auto macro_code = uploaded_macros.find(method); |     // Reset the current macro.
 | ||||||
|     // The requested macro must have been uploaded already.
 |  | ||||||
|     ASSERT_MSG(macro_code != uploaded_macros.end(), "Macro %08X was not uploaded", method); |  | ||||||
| 
 |  | ||||||
|     // Reset the current macro and execute it.
 |  | ||||||
|     executing_macro = 0; |     executing_macro = 0; | ||||||
|  | 
 | ||||||
|  |     // The requested macro must have been uploaded already.
 | ||||||
|  |     auto macro_code = uploaded_macros.find(method); | ||||||
|  |     if (macro_code == uploaded_macros.end()) { | ||||||
|  |         LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Execute the current macro.
 | ||||||
|     macro_interpreter.Execute(macro_code->second, std::move(parameters)); |     macro_interpreter.Execute(macro_code->second, std::move(parameters)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -238,6 +243,8 @@ void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) { | |||||||
| 
 | 
 | ||||||
|     auto& buffer = shader.const_buffers[bind_data.index]; |     auto& buffer = shader.const_buffers[bind_data.index]; | ||||||
| 
 | 
 | ||||||
|  |     ASSERT(bind_data.index < Regs::MaxConstBuffers); | ||||||
|  | 
 | ||||||
|     buffer.enabled = bind_data.valid.Value() != 0; |     buffer.enabled = bind_data.valid.Value() != 0; | ||||||
|     buffer.index = bind_data.index; |     buffer.index = bind_data.index; | ||||||
|     buffer.address = regs.const_buffer.BufferAddress(); |     buffer.address = regs.const_buffer.BufferAddress(); | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ public: | |||||||
|         static constexpr size_t MaxShaderProgram = 6; |         static constexpr size_t MaxShaderProgram = 6; | ||||||
|         static constexpr size_t MaxShaderStage = 5; |         static constexpr size_t MaxShaderStage = 5; | ||||||
|         // Maximum number of const buffers per shader stage.
 |         // Maximum number of const buffers per shader stage.
 | ||||||
|         static constexpr size_t MaxConstBuffers = 16; |         static constexpr size_t MaxConstBuffers = 18; | ||||||
| 
 | 
 | ||||||
|         enum class QueryMode : u32 { |         enum class QueryMode : u32 { | ||||||
|             Write = 0, |             Write = 0, | ||||||
|  | |||||||
| @ -78,6 +78,8 @@ union Attribute { | |||||||
|         // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
 |         // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
 | ||||||
|         // shader.
 |         // shader.
 | ||||||
|         TessCoordInstanceIDVertexID = 47, |         TessCoordInstanceIDVertexID = 47, | ||||||
|  |         // TODO(bunnei): Figure out what this is used for. Super Mario Odyssey uses this.
 | ||||||
|  |         Unknown_63 = 63, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     union { |     union { | ||||||
| @ -254,20 +256,15 @@ union Instruction { | |||||||
|             BitField<56, 1, u64> invert_b; |             BitField<56, 1, u64> invert_b; | ||||||
|         } lop32i; |         } lop32i; | ||||||
| 
 | 
 | ||||||
|         float GetImm20_19() const { |         u32 GetImm20_19() const { | ||||||
|             float result{}; |  | ||||||
|             u32 imm{static_cast<u32>(imm20_19)}; |             u32 imm{static_cast<u32>(imm20_19)}; | ||||||
|             imm <<= 12; |             imm <<= 12; | ||||||
|             imm |= negate_imm ? 0x80000000 : 0; |             imm |= negate_imm ? 0x80000000 : 0; | ||||||
|             std::memcpy(&result, &imm, sizeof(imm)); |             return imm; | ||||||
|             return result; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         float GetImm20_32() const { |         u32 GetImm20_32() const { | ||||||
|             float result{}; |             return static_cast<u32>(imm20_32); | ||||||
|             s32 imm{static_cast<s32>(imm20_32)}; |  | ||||||
|             std::memcpy(&result, &imm, sizeof(imm)); |  | ||||||
|             return result; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         s32 GetSignedImm20_20() const { |         s32 GetSignedImm20_20() const { | ||||||
|  | |||||||
| @ -42,6 +42,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { | |||||||
|     case RenderTargetFormat::RGB10_A2_UNORM: |     case RenderTargetFormat::RGB10_A2_UNORM: | ||||||
|     case RenderTargetFormat::BGRA8_UNORM: |     case RenderTargetFormat::BGRA8_UNORM: | ||||||
|     case RenderTargetFormat::R32_FLOAT: |     case RenderTargetFormat::R32_FLOAT: | ||||||
|  |     case RenderTargetFormat::R11G11B10_FLOAT: | ||||||
|         return 4; |         return 4; | ||||||
|     default: |     default: | ||||||
|         UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format)); |         UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format)); | ||||||
|  | |||||||
| @ -34,6 +34,7 @@ enum class RenderTargetFormat : u32 { | |||||||
|     RG16_FLOAT = 0xDE, |     RG16_FLOAT = 0xDE, | ||||||
|     R11G11B10_FLOAT = 0xE0, |     R11G11B10_FLOAT = 0xE0, | ||||||
|     R32_FLOAT = 0xE5, |     R32_FLOAT = 0xE5, | ||||||
|  |     B5G6R5_UNORM = 0xE8, | ||||||
|     R16_FLOAT = 0xF2, |     R16_FLOAT = 0xF2, | ||||||
|     R8_UNORM = 0xF3, |     R8_UNORM = 0xF3, | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -161,7 +161,7 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, | |||||||
|     // assume every shader uses them all.
 |     // assume every shader uses them all.
 | ||||||
|     for (unsigned index = 0; index < 16; ++index) { |     for (unsigned index = 0; index < 16; ++index) { | ||||||
|         auto& attrib = regs.vertex_attrib_format[index]; |         auto& attrib = regs.vertex_attrib_format[index]; | ||||||
|         LOG_DEBUG(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}", |         LOG_TRACE(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}", | ||||||
|                   index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(), |                   index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(), | ||||||
|                   attrib.offset.Value(), attrib.IsNormalized()); |                   attrib.offset.Value(), attrib.IsNormalized()); | ||||||
| 
 | 
 | ||||||
| @ -324,11 +324,14 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c | |||||||
|                                                                     bool using_depth_fb) { |                                                                     bool using_depth_fb) { | ||||||
|     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||||||
| 
 | 
 | ||||||
|  |     if (regs.rt[0].format == Tegra::RenderTargetFormat::NONE) { | ||||||
|  |         LOG_ERROR(HW_GPU, "RenderTargetFormat is not configured"); | ||||||
|  |         using_color_fb = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // TODO(bunnei): Implement this
 |     // TODO(bunnei): Implement this
 | ||||||
|     const bool has_stencil = false; |     const bool has_stencil = false; | ||||||
| 
 | 
 | ||||||
|     const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; |  | ||||||
| 
 |  | ||||||
|     const bool write_color_fb = |     const bool write_color_fb = | ||||||
|         state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE || |         state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE || | ||||||
|         state.color_mask.blue_enabled == GL_TRUE || state.color_mask.alpha_enabled == GL_TRUE; |         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 | |||||||
|     Surface depth_surface; |     Surface depth_surface; | ||||||
|     MathUtil::Rectangle<u32> surfaces_rect; |     MathUtil::Rectangle<u32> surfaces_rect; | ||||||
|     std::tie(color_surface, depth_surface, surfaces_rect) = |     std::tie(color_surface, depth_surface, surfaces_rect) = | ||||||
|         res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect); |         res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb); | ||||||
| 
 | 
 | ||||||
|     MathUtil::Rectangle<u32> draw_rect{ |     const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; | ||||||
|  |     const MathUtil::Rectangle<u32> draw_rect{ | ||||||
|         static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left, |         static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left, | ||||||
|                                          surfaces_rect.left, surfaces_rect.right)), // Left
 |                                          surfaces_rect.left, surfaces_rect.right)), // Left
 | ||||||
|         static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top, |         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 | |||||||
|         auto& buffer_draw_state = |         auto& buffer_draw_state = | ||||||
|             state.draw.const_buffers[static_cast<size_t>(stage)][used_buffer.GetIndex()]; |             state.draw.const_buffers[static_cast<size_t>(stage)][used_buffer.GetIndex()]; | ||||||
| 
 | 
 | ||||||
|         ASSERT_MSG(buffer.enabled, "Attempted to upload disabled constbuffer"); |         if (!buffer.enabled) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         buffer_draw_state.enabled = true; |         buffer_draw_state.enabled = true; | ||||||
|         buffer_draw_state.bindpoint = current_bindpoint + bindpoint; |         buffer_draw_state.bindpoint = current_bindpoint + bindpoint; | ||||||
| 
 | 
 | ||||||
| @ -804,9 +811,7 @@ void RasterizerOpenGL::SyncClipCoef() { | |||||||
| void RasterizerOpenGL::SyncCullMode() { | void RasterizerOpenGL::SyncCullMode() { | ||||||
|     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||||||
| 
 | 
 | ||||||
|     // TODO(bunnei): Enable the below once more things work - until then, this may hide regressions
 |     state.cull.enabled = regs.cull.enabled != 0; | ||||||
|     // state.cull.enabled = regs.cull.enabled != 0;
 |  | ||||||
|     state.cull.enabled = false; |  | ||||||
| 
 | 
 | ||||||
|     if (state.cull.enabled) { |     if (state.cull.enabled) { | ||||||
|         state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face); |         state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face); | ||||||
|  | |||||||
| @ -109,6 +109,9 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form | |||||||
|     {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, |     {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, | ||||||
|      true},                                                                                 // DXT45
 |      true},                                                                                 // DXT45
 | ||||||
|     {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1
 |     {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1
 | ||||||
|  |     {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, | ||||||
|  |      true},                                                                     // DXN2UNORM
 | ||||||
|  |     {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM
 | ||||||
|     {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, |     {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, | ||||||
|      true},                                                                    // BC7U
 |      true},                                                                    // BC7U
 | ||||||
|     {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false},        // ASTC_2D_4X4
 |     {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false},        // ASTC_2D_4X4
 | ||||||
| @ -180,36 +183,49 @@ MathUtil::Rectangle<u32> SurfaceParams::GetRect() const { | |||||||
|     return {0, actual_height, width, 0}; |     return {0, actual_height, width, 0}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN
 | ||||||
|  | static bool IsFormatBCn(PixelFormat format) { | ||||||
|  |     switch (format) { | ||||||
|  |     case PixelFormat::DXT1: | ||||||
|  |     case PixelFormat::DXT23: | ||||||
|  |     case PixelFormat::DXT45: | ||||||
|  |     case PixelFormat::DXN1: | ||||||
|  |     case PixelFormat::DXN2SNORM: | ||||||
|  |     case PixelFormat::DXN2UNORM: | ||||||
|  |     case PixelFormat::BC7U: | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| template <bool morton_to_gl, PixelFormat format> | template <bool morton_to_gl, PixelFormat format> | ||||||
| void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::GPUVAddr addr) { | void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer, | ||||||
|  |                 Tegra::GPUVAddr addr) { | ||||||
|     constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; |     constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; | ||||||
|     constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); |     constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); | ||||||
|     const auto& gpu = Core::System::GetInstance().GPU(); |     const auto& gpu = Core::System::GetInstance().GPU(); | ||||||
| 
 | 
 | ||||||
|     if (morton_to_gl) { |     if (morton_to_gl) { | ||||||
|         if (SurfaceParams::GetFormatType(format) == SurfaceType::ColorTexture) { |         // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
 | ||||||
|             auto data = Tegra::Texture::UnswizzleTexture( |         // pixel values.
 | ||||||
|                 *gpu.memory_manager->GpuToCpuAddress(addr), |         const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; | ||||||
|                 SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height); |         const std::vector<u8> data = | ||||||
|             std::memcpy(gl_buffer, data.data(), data.size()); |             Tegra::Texture::UnswizzleTexture(*gpu.memory_manager->GpuToCpuAddress(addr), tile_size, | ||||||
|         } else { |                                              bytes_per_pixel, stride, height, block_height); | ||||||
|             auto data = Tegra::Texture::UnswizzleDepthTexture( |         const size_t size_to_copy{std::min(gl_buffer.size(), data.size())}; | ||||||
|                 *gpu.memory_manager->GpuToCpuAddress(addr), |         gl_buffer.assign(data.begin(), data.begin() + size_to_copy); | ||||||
|                 SurfaceParams::DepthFormatFromPixelFormat(format), stride, height, block_height); |  | ||||||
|             std::memcpy(gl_buffer, data.data(), data.size()); |  | ||||||
|         } |  | ||||||
|     } else { |     } else { | ||||||
|         // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
 |         // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
 | ||||||
|         // check the configuration for this and perform more generic un/swizzle
 |         // check the configuration for this and perform more generic un/swizzle
 | ||||||
|         LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); |         LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); | ||||||
|         VideoCore::MortonCopyPixels128( |         VideoCore::MortonCopyPixels128( | ||||||
|             stride, height, bytes_per_pixel, gl_bytes_per_pixel, |             stride, height, bytes_per_pixel, gl_bytes_per_pixel, | ||||||
|             Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(addr)), gl_buffer, |             Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(addr)), gl_buffer.data(), | ||||||
|             morton_to_gl); |             morton_to_gl); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), | static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr), | ||||||
|                             SurfaceParams::MaxPixelFormat> |                             SurfaceParams::MaxPixelFormat> | ||||||
|     morton_to_gl_fns = { |     morton_to_gl_fns = { | ||||||
|         MortonCopy<true, PixelFormat::ABGR8>,        MortonCopy<true, PixelFormat::B5G6R5>, |         MortonCopy<true, PixelFormat::ABGR8>,        MortonCopy<true, PixelFormat::B5G6R5>, | ||||||
| @ -218,6 +234,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), | |||||||
|         MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>, |         MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>, | ||||||
|         MortonCopy<true, PixelFormat::DXT1>,         MortonCopy<true, PixelFormat::DXT23>, |         MortonCopy<true, PixelFormat::DXT1>,         MortonCopy<true, PixelFormat::DXT23>, | ||||||
|         MortonCopy<true, PixelFormat::DXT45>,        MortonCopy<true, PixelFormat::DXN1>, |         MortonCopy<true, PixelFormat::DXT45>,        MortonCopy<true, PixelFormat::DXN1>, | ||||||
|  |         MortonCopy<true, PixelFormat::DXN2UNORM>,    MortonCopy<true, PixelFormat::DXN2SNORM>, | ||||||
|         MortonCopy<true, PixelFormat::BC7U>,         MortonCopy<true, PixelFormat::ASTC_2D_4X4>, |         MortonCopy<true, PixelFormat::BC7U>,         MortonCopy<true, PixelFormat::ASTC_2D_4X4>, | ||||||
|         MortonCopy<true, PixelFormat::G8R8>,         MortonCopy<true, PixelFormat::BGRA8>, |         MortonCopy<true, PixelFormat::G8R8>,         MortonCopy<true, PixelFormat::BGRA8>, | ||||||
|         MortonCopy<true, PixelFormat::RGBA32F>,      MortonCopy<true, PixelFormat::RG32F>, |         MortonCopy<true, PixelFormat::RGBA32F>,      MortonCopy<true, PixelFormat::RG32F>, | ||||||
| @ -231,7 +248,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), | |||||||
|         MortonCopy<true, PixelFormat::Z32FS8>, |         MortonCopy<true, PixelFormat::Z32FS8>, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), | static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr), | ||||||
|                             SurfaceParams::MaxPixelFormat> |                             SurfaceParams::MaxPixelFormat> | ||||||
|     gl_to_morton_fns = { |     gl_to_morton_fns = { | ||||||
|         MortonCopy<false, PixelFormat::ABGR8>, |         MortonCopy<false, PixelFormat::ABGR8>, | ||||||
| @ -242,7 +259,10 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), | |||||||
|         MortonCopy<false, PixelFormat::RGBA16F>, |         MortonCopy<false, PixelFormat::RGBA16F>, | ||||||
|         MortonCopy<false, PixelFormat::R11FG11FB10F>, |         MortonCopy<false, PixelFormat::R11FG11FB10F>, | ||||||
|         MortonCopy<false, PixelFormat::RGBA32UI>, |         MortonCopy<false, PixelFormat::RGBA32UI>, | ||||||
|         // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/BC7U/ASTC_2D_4X4 formats is not supported
 |         // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/ASTC_2D_4X4 formats is not
 | ||||||
|  |         // supported
 | ||||||
|  |         nullptr, | ||||||
|  |         nullptr, | ||||||
|         nullptr, |         nullptr, | ||||||
|         nullptr, |         nullptr, | ||||||
|         nullptr, |         nullptr, | ||||||
| @ -447,22 +467,24 @@ MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64 | |||||||
| void CachedSurface::LoadGLBuffer() { | void CachedSurface::LoadGLBuffer() { | ||||||
|     ASSERT(params.type != SurfaceType::Fill); |     ASSERT(params.type != SurfaceType::Fill); | ||||||
| 
 | 
 | ||||||
|     u8* const texture_src_data = Memory::GetPointer(params.GetCpuAddr()); |     const u8* const texture_src_data = Memory::GetPointer(params.GetCpuAddr()); | ||||||
| 
 | 
 | ||||||
|     ASSERT(texture_src_data); |     ASSERT(texture_src_data); | ||||||
| 
 | 
 | ||||||
|     gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); |     const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format); | ||||||
|  |     const u32 copy_size = params.width * params.height * bytes_per_pixel; | ||||||
| 
 | 
 | ||||||
|     MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); |     MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); | ||||||
| 
 | 
 | ||||||
|     if (!params.is_tiled) { |     if (params.is_tiled) { | ||||||
|         const u32 bytes_per_pixel{params.GetFormatBpp() >> 3}; |         gl_buffer.resize(copy_size); | ||||||
| 
 | 
 | ||||||
|         std::memcpy(gl_buffer.data(), texture_src_data, |  | ||||||
|                     bytes_per_pixel * params.width * params.height); |  | ||||||
|     } else { |  | ||||||
|         morton_to_gl_fns[static_cast<size_t>(params.pixel_format)]( |         morton_to_gl_fns[static_cast<size_t>(params.pixel_format)]( | ||||||
|             params.width, params.block_height, params.height, gl_buffer.data(), params.addr); |             params.width, params.block_height, params.height, gl_buffer, params.addr); | ||||||
|  |     } else { | ||||||
|  |         const u8* const texture_src_data_end = texture_src_data + copy_size; | ||||||
|  | 
 | ||||||
|  |         gl_buffer.assign(texture_src_data, texture_src_data_end); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height); |     ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height); | ||||||
| @ -485,7 +507,7 @@ void CachedSurface::FlushGLBuffer() { | |||||||
|         std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes); |         std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes); | ||||||
|     } else { |     } else { | ||||||
|         gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( |         gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( | ||||||
|             params.width, params.block_height, params.height, gl_buffer.data(), params.addr); |             params.width, params.block_height, params.height, gl_buffer, params.addr); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -600,8 +622,8 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu | |||||||
|     return GetSurface(SurfaceParams::CreateForTexture(config)); |     return GetSurface(SurfaceParams::CreateForTexture(config)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( | SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool using_color_fb, | ||||||
|     bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport) { |                                                                        bool using_depth_fb) { | ||||||
|     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||||||
| 
 | 
 | ||||||
|     // TODO(bunnei): This is hard corded to use just the first render buffer
 |     // 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* | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, size_t size) { | void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, size_t size) { | ||||||
|     for (const auto& pair : surface_cache) { |     for (auto iter = surface_cache.cbegin(); iter != surface_cache.cend();) { | ||||||
|         const auto& surface{pair.second}; |         const auto& surface{iter->second}; | ||||||
|         const auto& params{surface->GetSurfaceParams()}; |         const auto& params{surface->GetSurfaceParams()}; | ||||||
| 
 | 
 | ||||||
|  |         ++iter; | ||||||
|  | 
 | ||||||
|         if (params.IsOverlappingRegion(addr, size)) { |         if (params.IsOverlappingRegion(addr, size)) { | ||||||
|             UnregisterSurface(surface); |             UnregisterSurface(surface); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -35,31 +35,33 @@ struct SurfaceParams { | |||||||
|         DXT23 = 9, |         DXT23 = 9, | ||||||
|         DXT45 = 10, |         DXT45 = 10, | ||||||
|         DXN1 = 11, // This is also known as BC4
 |         DXN1 = 11, // This is also known as BC4
 | ||||||
|         BC7U = 12, |         DXN2UNORM = 12, | ||||||
|         ASTC_2D_4X4 = 13, |         DXN2SNORM = 13, | ||||||
|         G8R8 = 14, |         BC7U = 14, | ||||||
|         BGRA8 = 15, |         ASTC_2D_4X4 = 15, | ||||||
|         RGBA32F = 16, |         G8R8 = 16, | ||||||
|         RG32F = 17, |         BGRA8 = 17, | ||||||
|         R32F = 18, |         RGBA32F = 18, | ||||||
|         R16F = 19, |         RG32F = 19, | ||||||
|         R16UNORM = 20, |         R32F = 20, | ||||||
|         RG16 = 21, |         R16F = 21, | ||||||
|         RG16F = 22, |         R16UNORM = 22, | ||||||
|         RG16UI = 23, |         RG16 = 23, | ||||||
|         RG16I = 24, |         RG16F = 24, | ||||||
|         RG16S = 25, |         RG16UI = 25, | ||||||
|         RGB32F = 26, |         RG16I = 26, | ||||||
|         SRGBA8 = 27, |         RG16S = 27, | ||||||
|  |         RGB32F = 28, | ||||||
|  |         SRGBA8 = 29, | ||||||
| 
 | 
 | ||||||
|         MaxColorFormat, |         MaxColorFormat, | ||||||
| 
 | 
 | ||||||
|         // DepthStencil formats
 |         // DepthStencil formats
 | ||||||
|         Z24S8 = 28, |         Z24S8 = 30, | ||||||
|         S8Z24 = 29, |         S8Z24 = 31, | ||||||
|         Z32F = 30, |         Z32F = 32, | ||||||
|         Z16 = 31, |         Z16 = 33, | ||||||
|         Z32FS8 = 32, |         Z32FS8 = 34, | ||||||
| 
 | 
 | ||||||
|         MaxDepthStencilFormat, |         MaxDepthStencilFormat, | ||||||
| 
 | 
 | ||||||
| @ -109,6 +111,8 @@ struct SurfaceParams { | |||||||
|             4, // DXT23
 |             4, // DXT23
 | ||||||
|             4, // DXT45
 |             4, // DXT45
 | ||||||
|             4, // DXN1
 |             4, // DXN1
 | ||||||
|  |             4, // DXN2UNORM
 | ||||||
|  |             4, // DXN2SNORM
 | ||||||
|             4, // BC7U
 |             4, // BC7U
 | ||||||
|             4, // ASTC_2D_4X4
 |             4, // ASTC_2D_4X4
 | ||||||
|             1, // G8R8
 |             1, // G8R8
 | ||||||
| @ -153,6 +157,8 @@ struct SurfaceParams { | |||||||
|             128, // DXT23
 |             128, // DXT23
 | ||||||
|             128, // DXT45
 |             128, // DXT45
 | ||||||
|             64,  // DXN1
 |             64,  // DXN1
 | ||||||
|  |             128, // DXN2UNORM
 | ||||||
|  |             128, // DXN2SNORM
 | ||||||
|             128, // BC7U
 |             128, // BC7U
 | ||||||
|             32,  // ASTC_2D_4X4
 |             32,  // ASTC_2D_4X4
 | ||||||
|             16,  // G8R8
 |             16,  // G8R8
 | ||||||
| @ -221,6 +227,8 @@ struct SurfaceParams { | |||||||
|             return PixelFormat::RG32F; |             return PixelFormat::RG32F; | ||||||
|         case Tegra::RenderTargetFormat::R11G11B10_FLOAT: |         case Tegra::RenderTargetFormat::R11G11B10_FLOAT: | ||||||
|             return PixelFormat::R11FG11FB10F; |             return PixelFormat::R11FG11FB10F; | ||||||
|  |         case Tegra::RenderTargetFormat::B5G6R5_UNORM: | ||||||
|  |             return PixelFormat::B5G6R5; | ||||||
|         case Tegra::RenderTargetFormat::RGBA32_UINT: |         case Tegra::RenderTargetFormat::RGBA32_UINT: | ||||||
|             return PixelFormat::RGBA32UI; |             return PixelFormat::RGBA32UI; | ||||||
|         case Tegra::RenderTargetFormat::R8_UNORM: |         case Tegra::RenderTargetFormat::R8_UNORM: | ||||||
| @ -303,6 +311,16 @@ struct SurfaceParams { | |||||||
|             return PixelFormat::DXT45; |             return PixelFormat::DXT45; | ||||||
|         case Tegra::Texture::TextureFormat::DXN1: |         case Tegra::Texture::TextureFormat::DXN1: | ||||||
|             return PixelFormat::DXN1; |             return PixelFormat::DXN1; | ||||||
|  |         case Tegra::Texture::TextureFormat::DXN2: | ||||||
|  |             switch (component_type) { | ||||||
|  |             case Tegra::Texture::ComponentType::UNORM: | ||||||
|  |                 return PixelFormat::DXN2UNORM; | ||||||
|  |             case Tegra::Texture::ComponentType::SNORM: | ||||||
|  |                 return PixelFormat::DXN2SNORM; | ||||||
|  |             } | ||||||
|  |             LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", | ||||||
|  |                          static_cast<u32>(component_type)); | ||||||
|  |             UNREACHABLE(); | ||||||
|         case Tegra::Texture::TextureFormat::BC7U: |         case Tegra::Texture::TextureFormat::BC7U: | ||||||
|             return PixelFormat::BC7U; |             return PixelFormat::BC7U; | ||||||
|         case Tegra::Texture::TextureFormat::ASTC_2D_4X4: |         case Tegra::Texture::TextureFormat::ASTC_2D_4X4: | ||||||
| @ -330,89 +348,6 @@ struct SurfaceParams { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static Tegra::Texture::TextureFormat TextureFormatFromPixelFormat(PixelFormat format) { |  | ||||||
|         // TODO(Subv): Properly implement this
 |  | ||||||
|         switch (format) { |  | ||||||
|         case PixelFormat::ABGR8: |  | ||||||
|         case PixelFormat::SRGBA8: |  | ||||||
|             return Tegra::Texture::TextureFormat::A8R8G8B8; |  | ||||||
|         case PixelFormat::B5G6R5: |  | ||||||
|             return Tegra::Texture::TextureFormat::B5G6R5; |  | ||||||
|         case PixelFormat::A2B10G10R10: |  | ||||||
|             return Tegra::Texture::TextureFormat::A2B10G10R10; |  | ||||||
|         case PixelFormat::A1B5G5R5: |  | ||||||
|             return Tegra::Texture::TextureFormat::A1B5G5R5; |  | ||||||
|         case PixelFormat::R8: |  | ||||||
|             return Tegra::Texture::TextureFormat::R8; |  | ||||||
|         case PixelFormat::G8R8: |  | ||||||
|             return Tegra::Texture::TextureFormat::G8R8; |  | ||||||
|         case PixelFormat::RGBA16F: |  | ||||||
|             return Tegra::Texture::TextureFormat::R16_G16_B16_A16; |  | ||||||
|         case PixelFormat::R11FG11FB10F: |  | ||||||
|             return Tegra::Texture::TextureFormat::BF10GF11RF11; |  | ||||||
|         case PixelFormat::RGBA32UI: |  | ||||||
|             return Tegra::Texture::TextureFormat::R32_G32_B32_A32; |  | ||||||
|         case PixelFormat::DXT1: |  | ||||||
|             return Tegra::Texture::TextureFormat::DXT1; |  | ||||||
|         case PixelFormat::DXT23: |  | ||||||
|             return Tegra::Texture::TextureFormat::DXT23; |  | ||||||
|         case PixelFormat::DXT45: |  | ||||||
|             return Tegra::Texture::TextureFormat::DXT45; |  | ||||||
|         case PixelFormat::DXN1: |  | ||||||
|             return Tegra::Texture::TextureFormat::DXN1; |  | ||||||
|         case PixelFormat::BC7U: |  | ||||||
|             return Tegra::Texture::TextureFormat::BC7U; |  | ||||||
|         case PixelFormat::ASTC_2D_4X4: |  | ||||||
|             return Tegra::Texture::TextureFormat::ASTC_2D_4X4; |  | ||||||
|         case PixelFormat::BGRA8: |  | ||||||
|             // TODO(bunnei): This is fine for unswizzling (since we just need the right component
 |  | ||||||
|             // sizes), but could be a bug if we used this function in different ways.
 |  | ||||||
|             return Tegra::Texture::TextureFormat::A8R8G8B8; |  | ||||||
|         case PixelFormat::RGBA32F: |  | ||||||
|             return Tegra::Texture::TextureFormat::R32_G32_B32_A32; |  | ||||||
|         case PixelFormat::RGB32F: |  | ||||||
|             return Tegra::Texture::TextureFormat::R32_G32_B32; |  | ||||||
|         case PixelFormat::RG32F: |  | ||||||
|             return Tegra::Texture::TextureFormat::R32_G32; |  | ||||||
|         case PixelFormat::R32F: |  | ||||||
|             return Tegra::Texture::TextureFormat::R32; |  | ||||||
|         case PixelFormat::R16F: |  | ||||||
|         case PixelFormat::R16UNORM: |  | ||||||
|             return Tegra::Texture::TextureFormat::R16; |  | ||||||
|         case PixelFormat::Z32F: |  | ||||||
|             return Tegra::Texture::TextureFormat::ZF32; |  | ||||||
|         case PixelFormat::Z24S8: |  | ||||||
|             return Tegra::Texture::TextureFormat::Z24S8; |  | ||||||
|         case PixelFormat::RG16F: |  | ||||||
|         case PixelFormat::RG16: |  | ||||||
|         case PixelFormat::RG16UI: |  | ||||||
|         case PixelFormat::RG16I: |  | ||||||
|         case PixelFormat::RG16S: |  | ||||||
|             return Tegra::Texture::TextureFormat::R16_G16; |  | ||||||
|         default: |  | ||||||
|             LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); |  | ||||||
|             UNREACHABLE(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     static Tegra::DepthFormat DepthFormatFromPixelFormat(PixelFormat format) { |  | ||||||
|         switch (format) { |  | ||||||
|         case PixelFormat::S8Z24: |  | ||||||
|             return Tegra::DepthFormat::S8_Z24_UNORM; |  | ||||||
|         case PixelFormat::Z24S8: |  | ||||||
|             return Tegra::DepthFormat::Z24_S8_UNORM; |  | ||||||
|         case PixelFormat::Z32F: |  | ||||||
|             return Tegra::DepthFormat::Z32_FLOAT; |  | ||||||
|         case PixelFormat::Z16: |  | ||||||
|             return Tegra::DepthFormat::Z16_UNORM; |  | ||||||
|         case PixelFormat::Z32FS8: |  | ||||||
|             return Tegra::DepthFormat::Z32_S8_X24_FLOAT; |  | ||||||
|         default: |  | ||||||
|             LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); |  | ||||||
|             UNREACHABLE(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { |     static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { | ||||||
|         // TODO(Subv): Implement more component types
 |         // TODO(Subv): Implement more component types
 | ||||||
|         switch (type) { |         switch (type) { | ||||||
| @ -441,6 +376,7 @@ struct SurfaceParams { | |||||||
|         case Tegra::RenderTargetFormat::RGB10_A2_UNORM: |         case Tegra::RenderTargetFormat::RGB10_A2_UNORM: | ||||||
|         case Tegra::RenderTargetFormat::R8_UNORM: |         case Tegra::RenderTargetFormat::R8_UNORM: | ||||||
|         case Tegra::RenderTargetFormat::RG16_UNORM: |         case Tegra::RenderTargetFormat::RG16_UNORM: | ||||||
|  |         case Tegra::RenderTargetFormat::B5G6R5_UNORM: | ||||||
|             return ComponentType::UNorm; |             return ComponentType::UNorm; | ||||||
|         case Tegra::RenderTargetFormat::RG16_SNORM: |         case Tegra::RenderTargetFormat::RG16_SNORM: | ||||||
|             return ComponentType::SNorm; |             return ComponentType::SNorm; | ||||||
| @ -612,8 +548,7 @@ public: | |||||||
|     Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config); |     Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config); | ||||||
| 
 | 
 | ||||||
|     /// Get the color and depth surfaces based on the framebuffer configuration
 |     /// Get the color and depth surfaces based on the framebuffer configuration
 | ||||||
|     SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, |     SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb); | ||||||
|                                                     const MathUtil::Rectangle<s32>& viewport); |  | ||||||
| 
 | 
 | ||||||
|     /// Flushes the surface to Switch memory
 |     /// Flushes the surface to Switch memory
 | ||||||
|     void FlushSurface(const Surface& surface); |     void FlushSurface(const Surface& surface); | ||||||
|  | |||||||
| @ -507,6 +507,8 @@ private: | |||||||
| 
 | 
 | ||||||
|     /// Build the GLSL register list.
 |     /// Build the GLSL register list.
 | ||||||
|     void BuildRegisterList() { |     void BuildRegisterList() { | ||||||
|  |         regs.reserve(Register::NumRegisters); | ||||||
|  | 
 | ||||||
|         for (size_t index = 0; index < Register::NumRegisters; ++index) { |         for (size_t index = 0; index < Register::NumRegisters; ++index) { | ||||||
|             regs.emplace_back(index, suffix); |             regs.emplace_back(index, suffix); | ||||||
|         } |         } | ||||||
| @ -523,6 +525,11 @@ private: | |||||||
|             // shader.
 |             // shader.
 | ||||||
|             ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex); |             ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex); | ||||||
|             return "vec4(0, 0, uintBitsToFloat(gl_InstanceID), uintBitsToFloat(gl_VertexID))"; |             return "vec4(0, 0, uintBitsToFloat(gl_InstanceID), uintBitsToFloat(gl_VertexID))"; | ||||||
|  |         case Attribute::Index::Unknown_63: | ||||||
|  |             // TODO(bunnei): Figure out what this is used for. Super Mario Odyssey uses this.
 | ||||||
|  |             LOG_CRITICAL(HW_GPU, "Unhandled input attribute Unknown_63"); | ||||||
|  |             UNREACHABLE(); | ||||||
|  |             break; | ||||||
|         default: |         default: | ||||||
|             const u32 index{static_cast<u32>(attribute) - |             const u32 index{static_cast<u32>(attribute) - | ||||||
|                             static_cast<u32>(Attribute::Index::Attribute_0)}; |                             static_cast<u32>(Attribute::Index::Attribute_0)}; | ||||||
| @ -534,6 +541,8 @@ private: | |||||||
|             LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index); |             LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index); | ||||||
|             UNREACHABLE(); |             UNREACHABLE(); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         return "vec4(0, 0, 0, 0)"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Generates code representing an output attribute register.
 |     /// Generates code representing an output attribute register.
 | ||||||
| @ -602,12 +611,12 @@ private: | |||||||
| 
 | 
 | ||||||
|     /// Generates code representing a 19-bit immediate value
 |     /// Generates code representing a 19-bit immediate value
 | ||||||
|     static std::string GetImmediate19(const Instruction& instr) { |     static std::string GetImmediate19(const Instruction& instr) { | ||||||
|         return std::to_string(instr.alu.GetImm20_19()); |         return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_19()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Generates code representing a 32-bit immediate value
 |     /// Generates code representing a 32-bit immediate value
 | ||||||
|     static std::string GetImmediate32(const Instruction& instr) { |     static std::string GetImmediate32(const Instruction& instr) { | ||||||
|         return std::to_string(instr.alu.GetImm20_32()); |         return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Generates code representing a texture sampler.
 |     /// Generates code representing a texture sampler.
 | ||||||
| @ -650,16 +659,17 @@ private: | |||||||
|      * @param instr Instruction to generate the if condition for. |      * @param instr Instruction to generate the if condition for. | ||||||
|      * @returns string containing the predicate condition. |      * @returns string containing the predicate condition. | ||||||
|      */ |      */ | ||||||
|     std::string GetPredicateCondition(u64 index, bool negate) const { |     std::string GetPredicateCondition(u64 index, bool negate) { | ||||||
|         using Tegra::Shader::Pred; |         using Tegra::Shader::Pred; | ||||||
|         std::string variable; |         std::string variable; | ||||||
| 
 | 
 | ||||||
|         // Index 7 is used as an 'Always True' condition.
 |         // Index 7 is used as an 'Always True' condition.
 | ||||||
|         if (index == static_cast<u64>(Pred::UnusedIndex)) |         if (index == static_cast<u64>(Pred::UnusedIndex)) { | ||||||
|             variable = "true"; |             variable = "true"; | ||||||
|         else |         } else { | ||||||
|             variable = 'p' + std::to_string(index) + '_' + suffix; |             variable = 'p' + std::to_string(index) + '_' + suffix; | ||||||
| 
 |             declr_predicates.insert(variable); | ||||||
|  |         } | ||||||
|         if (negate) { |         if (negate) { | ||||||
|             return "!(" + variable + ')'; |             return "!(" + variable + ')'; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -7,6 +7,10 @@ | |||||||
| #include <array> | #include <array> | ||||||
| #include <glad/glad.h> | #include <glad/glad.h> | ||||||
| 
 | 
 | ||||||
|  | #include "video_core/engines/maxwell_3d.h" | ||||||
|  | 
 | ||||||
|  | using Regs = Tegra::Engines::Maxwell3D::Regs; | ||||||
|  | 
 | ||||||
| namespace TextureUnits { | namespace TextureUnits { | ||||||
| 
 | 
 | ||||||
| struct TextureUnit { | struct TextureUnit { | ||||||
| @ -120,7 +124,7 @@ public: | |||||||
|             GLuint bindpoint; |             GLuint bindpoint; | ||||||
|             GLuint ssbo; |             GLuint ssbo; | ||||||
|         }; |         }; | ||||||
|         std::array<std::array<ConstBufferConfig, 16>, 5> const_buffers{}; |         std::array<std::array<ConstBufferConfig, Regs::MaxConstBuffers>, 5> const_buffers; | ||||||
|     } draw; |     } draw; | ||||||
| 
 | 
 | ||||||
|     struct { |     struct { | ||||||
|  | |||||||
| @ -27,9 +27,11 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||||||
|     case Maxwell::VertexAttribute::Type::UnsignedNorm: { |     case Maxwell::VertexAttribute::Type::UnsignedNorm: { | ||||||
| 
 | 
 | ||||||
|         switch (attrib.size) { |         switch (attrib.size) { | ||||||
|  |         case Maxwell::VertexAttribute::Size::Size_8_8: | ||||||
|         case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |         case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | ||||||
|             return GL_UNSIGNED_BYTE; |             return GL_UNSIGNED_BYTE; | ||||||
|         case Maxwell::VertexAttribute::Size::Size_16_16: |         case Maxwell::VertexAttribute::Size::Size_16_16: | ||||||
|  |         case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | ||||||
|             return GL_UNSIGNED_SHORT; |             return GL_UNSIGNED_SHORT; | ||||||
|         case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |         case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | ||||||
|             return GL_UNSIGNED_INT_2_10_10_10_REV; |             return GL_UNSIGNED_INT_2_10_10_10_REV; | ||||||
| @ -43,6 +45,9 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||||||
|     case Maxwell::VertexAttribute::Type::SignedNorm: { |     case Maxwell::VertexAttribute::Type::SignedNorm: { | ||||||
| 
 | 
 | ||||||
|         switch (attrib.size) { |         switch (attrib.size) { | ||||||
|  |         case Maxwell::VertexAttribute::Size::Size_32_32_32: | ||||||
|  |             return GL_INT; | ||||||
|  |         case Maxwell::VertexAttribute::Size::Size_8_8: | ||||||
|         case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |         case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | ||||||
|             return GL_BYTE; |             return GL_BYTE; | ||||||
|         case Maxwell::VertexAttribute::Size::Size_16_16: |         case Maxwell::VertexAttribute::Size::Size_16_16: | ||||||
| @ -84,6 +89,8 @@ inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { | |||||||
| 
 | 
 | ||||||
| inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { | inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { | ||||||
|     switch (topology) { |     switch (topology) { | ||||||
|  |     case Maxwell::PrimitiveTopology::Points: | ||||||
|  |         return GL_POINTS; | ||||||
|     case Maxwell::PrimitiveTopology::Triangles: |     case Maxwell::PrimitiveTopology::Triangles: | ||||||
|         return GL_TRIANGLES; |         return GL_TRIANGLES; | ||||||
|     case Maxwell::PrimitiveTopology::TriangleStrip: |     case Maxwell::PrimitiveTopology::TriangleStrip: | ||||||
|  | |||||||
| @ -430,7 +430,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum | |||||||
|         break; |         break; | ||||||
|     case GL_DEBUG_SEVERITY_NOTIFICATION: |     case GL_DEBUG_SEVERITY_NOTIFICATION: | ||||||
|     case GL_DEBUG_SEVERITY_LOW: |     case GL_DEBUG_SEVERITY_LOW: | ||||||
|         LOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message); |         LOG_TRACE(Render_OpenGL, format, str_source, str_type, id, message); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -54,6 +54,7 @@ u32 BytesPerPixel(TextureFormat format) { | |||||||
|         return 8; |         return 8; | ||||||
|     case TextureFormat::DXT23: |     case TextureFormat::DXT23: | ||||||
|     case TextureFormat::DXT45: |     case TextureFormat::DXT45: | ||||||
|  |     case TextureFormat::DXN2: | ||||||
|     case TextureFormat::BC7U: |     case TextureFormat::BC7U: | ||||||
|         // In this case a 'pixel' actually refers to a 4x4 tile.
 |         // In this case a 'pixel' actually refers to a 4x4 tile.
 | ||||||
|         return 16; |         return 16; | ||||||
| @ -85,87 +86,11 @@ u32 BytesPerPixel(TextureFormat format) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static u32 DepthBytesPerPixel(DepthFormat format) { | std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, | ||||||
|     switch (format) { |                                  u32 height, u32 block_height) { | ||||||
|     case DepthFormat::Z16_UNORM: |  | ||||||
|         return 2; |  | ||||||
|     case DepthFormat::S8_Z24_UNORM: |  | ||||||
|     case DepthFormat::Z24_S8_UNORM: |  | ||||||
|     case DepthFormat::Z32_FLOAT: |  | ||||||
|         return 4; |  | ||||||
|     case DepthFormat::Z32_S8_X24_FLOAT: |  | ||||||
|         return 8; |  | ||||||
|     default: |  | ||||||
|         UNIMPLEMENTED_MSG("Format not implemented"); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height, |  | ||||||
|                                  u32 block_height) { |  | ||||||
|     u8* data = Memory::GetPointer(address); |  | ||||||
|     u32 bytes_per_pixel = BytesPerPixel(format); |  | ||||||
| 
 |  | ||||||
|     std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); |     std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); | ||||||
| 
 |     CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, | ||||||
|     switch (format) { |                      Memory::GetPointer(address), unswizzled_data.data(), true, block_height); | ||||||
|     case TextureFormat::DXT1: |  | ||||||
|     case TextureFormat::DXT23: |  | ||||||
|     case TextureFormat::DXT45: |  | ||||||
|     case TextureFormat::DXN1: |  | ||||||
|     case TextureFormat::BC7U: |  | ||||||
|         // In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel
 |  | ||||||
|         // values.
 |  | ||||||
|         CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data, |  | ||||||
|                          unswizzled_data.data(), true, block_height); |  | ||||||
|         break; |  | ||||||
|     case TextureFormat::A8R8G8B8: |  | ||||||
|     case TextureFormat::A2B10G10R10: |  | ||||||
|     case TextureFormat::A1B5G5R5: |  | ||||||
|     case TextureFormat::B5G6R5: |  | ||||||
|     case TextureFormat::R8: |  | ||||||
|     case TextureFormat::G8R8: |  | ||||||
|     case TextureFormat::R16_G16_B16_A16: |  | ||||||
|     case TextureFormat::R32_G32_B32_A32: |  | ||||||
|     case TextureFormat::R32_G32: |  | ||||||
|     case TextureFormat::R32: |  | ||||||
|     case TextureFormat::R16: |  | ||||||
|     case TextureFormat::R16_G16: |  | ||||||
|     case TextureFormat::BF10GF11RF11: |  | ||||||
|     case TextureFormat::ASTC_2D_4X4: |  | ||||||
|     case TextureFormat::R32_G32_B32: |  | ||||||
|         CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, |  | ||||||
|                          unswizzled_data.data(), true, block_height); |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         UNIMPLEMENTED_MSG("Format not implemented"); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return unswizzled_data; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height, |  | ||||||
|                                       u32 block_height) { |  | ||||||
|     u8* data = Memory::GetPointer(address); |  | ||||||
|     u32 bytes_per_pixel = DepthBytesPerPixel(format); |  | ||||||
| 
 |  | ||||||
|     std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); |  | ||||||
| 
 |  | ||||||
|     switch (format) { |  | ||||||
|     case DepthFormat::Z16_UNORM: |  | ||||||
|     case DepthFormat::S8_Z24_UNORM: |  | ||||||
|     case DepthFormat::Z24_S8_UNORM: |  | ||||||
|     case DepthFormat::Z32_FLOAT: |  | ||||||
|     case DepthFormat::Z32_S8_X24_FLOAT: |  | ||||||
|         CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, |  | ||||||
|                          unswizzled_data.data(), true, block_height); |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         UNIMPLEMENTED_MSG("Format not implemented"); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return unswizzled_data; |     return unswizzled_data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -179,6 +104,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat | |||||||
|     case TextureFormat::DXT23: |     case TextureFormat::DXT23: | ||||||
|     case TextureFormat::DXT45: |     case TextureFormat::DXT45: | ||||||
|     case TextureFormat::DXN1: |     case TextureFormat::DXN1: | ||||||
|  |     case TextureFormat::DXN2: | ||||||
|     case TextureFormat::BC7U: |     case TextureFormat::BC7U: | ||||||
|     case TextureFormat::ASTC_2D_4X4: |     case TextureFormat::ASTC_2D_4X4: | ||||||
|     case TextureFormat::A8R8G8B8: |     case TextureFormat::A8R8G8B8: | ||||||
|  | |||||||
| @ -13,8 +13,8 @@ namespace Tegra::Texture { | |||||||
| /**
 | /**
 | ||||||
|  * Unswizzles a swizzled texture without changing its format. |  * Unswizzles a swizzled texture without changing its format. | ||||||
|  */ |  */ | ||||||
| std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height, | std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, | ||||||
|                                  u32 block_height = TICEntry::DefaultBlockHeight); |                                  u32 height, u32 block_height = TICEntry::DefaultBlockHeight); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Unswizzles a swizzled depth texture without changing its format. |  * Unswizzles a swizzled depth texture without changing its format. | ||||||
|  | |||||||
| @ -17,6 +17,8 @@ add_executable(yuzu | |||||||
|     configuration/configure_debug.h |     configuration/configure_debug.h | ||||||
|     configuration/configure_dialog.cpp |     configuration/configure_dialog.cpp | ||||||
|     configuration/configure_dialog.h |     configuration/configure_dialog.h | ||||||
|  |     configuration/configure_gamelist.cpp | ||||||
|  |     configuration/configure_gamelist.h | ||||||
|     configuration/configure_general.cpp |     configuration/configure_general.cpp | ||||||
|     configuration/configure_general.h |     configuration/configure_general.h | ||||||
|     configuration/configure_graphics.cpp |     configuration/configure_graphics.cpp | ||||||
| @ -59,6 +61,7 @@ set(UIS | |||||||
|     configuration/configure.ui |     configuration/configure.ui | ||||||
|     configuration/configure_audio.ui |     configuration/configure_audio.ui | ||||||
|     configuration/configure_debug.ui |     configuration/configure_debug.ui | ||||||
|  |     configuration/configure_gamelist.ui | ||||||
|     configuration/configure_general.ui |     configuration/configure_general.ui | ||||||
|     configuration/configure_graphics.ui |     configuration/configure_graphics.ui | ||||||
|     configuration/configure_input.ui |     configuration/configure_input.ui | ||||||
|  | |||||||
| @ -122,6 +122,13 @@ void Config::ReadValues() { | |||||||
|     qt_config->beginGroup("UI"); |     qt_config->beginGroup("UI"); | ||||||
|     UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); |     UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); | ||||||
| 
 | 
 | ||||||
|  |     qt_config->beginGroup("UIGameList"); | ||||||
|  |     UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool(); | ||||||
|  |     UISettings::values.icon_size = qt_config->value("icon_size", 48).toUInt(); | ||||||
|  |     UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 0).toUInt(); | ||||||
|  |     UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 3).toUInt(); | ||||||
|  |     qt_config->endGroup(); | ||||||
|  | 
 | ||||||
|     qt_config->beginGroup("UILayout"); |     qt_config->beginGroup("UILayout"); | ||||||
|     UISettings::values.geometry = qt_config->value("geometry").toByteArray(); |     UISettings::values.geometry = qt_config->value("geometry").toByteArray(); | ||||||
|     UISettings::values.state = qt_config->value("state").toByteArray(); |     UISettings::values.state = qt_config->value("state").toByteArray(); | ||||||
| @ -234,6 +241,13 @@ void Config::SaveValues() { | |||||||
|     qt_config->beginGroup("UI"); |     qt_config->beginGroup("UI"); | ||||||
|     qt_config->setValue("theme", UISettings::values.theme); |     qt_config->setValue("theme", UISettings::values.theme); | ||||||
| 
 | 
 | ||||||
|  |     qt_config->beginGroup("UIGameList"); | ||||||
|  |     qt_config->setValue("show_unknown", UISettings::values.show_unknown); | ||||||
|  |     qt_config->setValue("icon_size", UISettings::values.icon_size); | ||||||
|  |     qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id); | ||||||
|  |     qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id); | ||||||
|  |     qt_config->endGroup(); | ||||||
|  | 
 | ||||||
|     qt_config->beginGroup("UILayout"); |     qt_config->beginGroup("UILayout"); | ||||||
|     qt_config->setValue("geometry", UISettings::values.geometry); |     qt_config->setValue("geometry", UISettings::values.geometry); | ||||||
|     qt_config->setValue("state", UISettings::values.state); |     qt_config->setValue("state", UISettings::values.state); | ||||||
|  | |||||||
| @ -24,6 +24,11 @@ | |||||||
|        <string>General</string> |        <string>General</string> | ||||||
|       </attribute> |       </attribute> | ||||||
|      </widget> |      </widget> | ||||||
|  |       <widget class="ConfigureGameList" name="gameListTab"> | ||||||
|  |         <attribute name="title"> | ||||||
|  |           <string>Game List</string> | ||||||
|  |         </attribute> | ||||||
|  |       </widget> | ||||||
|      <widget class="ConfigureSystem" name="systemTab"> |      <widget class="ConfigureSystem" name="systemTab"> | ||||||
|       <attribute name="title"> |       <attribute name="title"> | ||||||
|        <string>System</string> |        <string>System</string> | ||||||
| @ -67,6 +72,12 @@ | |||||||
|    <header>configuration/configure_general.h</header> |    <header>configuration/configure_general.h</header> | ||||||
|    <container>1</container> |    <container>1</container> | ||||||
|   </customwidget> |   </customwidget> | ||||||
|  |    <customwidget> | ||||||
|  |      <class>ConfigureGameList</class> | ||||||
|  |      <extends>QWidget</extends> | ||||||
|  |      <header>configuration/configure_gamelist.h</header> | ||||||
|  |      <container>1</container> | ||||||
|  |    </customwidget> | ||||||
|   <customwidget> |   <customwidget> | ||||||
|    <class>ConfigureSystem</class> |    <class>ConfigureSystem</class> | ||||||
|    <extends>QWidget</extends> |    <extends>QWidget</extends> | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ void ConfigureDialog::setConfiguration() {} | |||||||
| 
 | 
 | ||||||
| void ConfigureDialog::applyConfiguration() { | void ConfigureDialog::applyConfiguration() { | ||||||
|     ui->generalTab->applyConfiguration(); |     ui->generalTab->applyConfiguration(); | ||||||
|  |     ui->gameListTab->applyConfiguration(); | ||||||
|     ui->systemTab->applyConfiguration(); |     ui->systemTab->applyConfiguration(); | ||||||
|     ui->inputTab->applyConfiguration(); |     ui->inputTab->applyConfiguration(); | ||||||
|     ui->graphicsTab->applyConfiguration(); |     ui->graphicsTab->applyConfiguration(); | ||||||
|  | |||||||
							
								
								
									
										63
									
								
								src/yuzu/configuration/configure_gamelist.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/yuzu/configuration/configure_gamelist.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | |||||||
|  | // Copyright 2016 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "core/core.h" | ||||||
|  | #include "core/settings.h" | ||||||
|  | #include "ui_configure_gamelist.h" | ||||||
|  | #include "ui_settings.h" | ||||||
|  | #include "yuzu/configuration/configure_gamelist.h" | ||||||
|  | 
 | ||||||
|  | ConfigureGameList::ConfigureGameList(QWidget* parent) | ||||||
|  |     : QWidget(parent), ui(new Ui::ConfigureGameList) { | ||||||
|  |     ui->setupUi(this); | ||||||
|  | 
 | ||||||
|  |     static const std::vector<std::pair<u32, std::string>> default_icon_sizes{ | ||||||
|  |         std::make_pair(0, "None"),        std::make_pair(32, "Small"), | ||||||
|  |         std::make_pair(64, "Standard"),   std::make_pair(128, "Large"), | ||||||
|  |         std::make_pair(256, "Full Size"), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     for (const auto& size : default_icon_sizes) { | ||||||
|  |         ui->icon_size_combobox->addItem(QString::fromStdString(size.second + " (" + | ||||||
|  |                                                                std::to_string(size.first) + "x" + | ||||||
|  |                                                                std::to_string(size.first) + ")"), | ||||||
|  |                                         size.first); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static const std::vector<std::string> row_text_names{ | ||||||
|  |         "Filename", | ||||||
|  |         "Filetype", | ||||||
|  |         "Title ID", | ||||||
|  |         "Title Name", | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     for (size_t i = 0; i < row_text_names.size(); ++i) { | ||||||
|  |         ui->row_1_text_combobox->addItem(QString::fromStdString(row_text_names[i]), | ||||||
|  |                                          QVariant::fromValue(i)); | ||||||
|  |         ui->row_2_text_combobox->addItem(QString::fromStdString(row_text_names[i]), | ||||||
|  |                                          QVariant::fromValue(i)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     this->setConfiguration(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ConfigureGameList::~ConfigureGameList() {} | ||||||
|  | 
 | ||||||
|  | void ConfigureGameList::setConfiguration() { | ||||||
|  |     ui->show_unknown->setChecked(UISettings::values.show_unknown); | ||||||
|  |     ui->icon_size_combobox->setCurrentIndex( | ||||||
|  |         ui->icon_size_combobox->findData(UISettings::values.icon_size)); | ||||||
|  |     ui->row_1_text_combobox->setCurrentIndex( | ||||||
|  |         ui->row_1_text_combobox->findData(UISettings::values.row_1_text_id)); | ||||||
|  |     ui->row_2_text_combobox->setCurrentIndex( | ||||||
|  |         ui->row_2_text_combobox->findData(UISettings::values.row_2_text_id)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureGameList::applyConfiguration() { | ||||||
|  |     UISettings::values.show_unknown = ui->show_unknown->isChecked(); | ||||||
|  |     UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); | ||||||
|  |     UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); | ||||||
|  |     UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); | ||||||
|  |     Settings::Apply(); | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								src/yuzu/configuration/configure_gamelist.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/yuzu/configuration/configure_gamelist.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | // Copyright 2016 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include <QWidget> | ||||||
|  | 
 | ||||||
|  | namespace Ui { | ||||||
|  | class ConfigureGameList; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class ConfigureGameList : public QWidget { | ||||||
|  |     Q_OBJECT | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     explicit ConfigureGameList(QWidget* parent = nullptr); | ||||||
|  |     ~ConfigureGameList(); | ||||||
|  | 
 | ||||||
|  |     void applyConfiguration(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void setConfiguration(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::unique_ptr<Ui::ConfigureGameList> ui; | ||||||
|  | }; | ||||||
							
								
								
									
										126
									
								
								src/yuzu/configuration/configure_gamelist.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/yuzu/configuration/configure_gamelist.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,126 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>ConfigureGameList</class> | ||||||
|  |   <widget class="QWidget" name="ConfigureGeneral"> | ||||||
|  |     <property name="geometry"> | ||||||
|  |       <rect> | ||||||
|  |         <x>0</x> | ||||||
|  |         <y>0</y> | ||||||
|  |         <width>300</width> | ||||||
|  |         <height>377</height> | ||||||
|  |       </rect> | ||||||
|  |     </property> | ||||||
|  |     <property name="windowTitle"> | ||||||
|  |       <string>Form</string> | ||||||
|  |     </property> | ||||||
|  |     <layout class="QHBoxLayout" name="HorizontalLayout"> | ||||||
|  |       <item> | ||||||
|  |         <layout class="QVBoxLayout" name="VerticalLayout"> | ||||||
|  |           <item> | ||||||
|  |             <widget class="QGroupBox" name="GeneralGroupBox"> | ||||||
|  |               <property name="title"> | ||||||
|  |                 <string>General</string> | ||||||
|  |               </property> | ||||||
|  |               <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> | ||||||
|  |                 <item> | ||||||
|  |                   <layout class="QVBoxLayout" name="GeneralVerticalLayout"> | ||||||
|  |                     <item> | ||||||
|  |                       <widget class="QCheckBox" name="show_unknown"> | ||||||
|  |                         <property name="text"> | ||||||
|  |                           <string>Show files with type 'Unknown'</string> | ||||||
|  |                         </property> | ||||||
|  |                       </widget> | ||||||
|  |                     </item> | ||||||
|  |                   </layout> | ||||||
|  |                 </item> | ||||||
|  |               </layout> | ||||||
|  |             </widget> | ||||||
|  |           </item> | ||||||
|  |           <item> | ||||||
|  |             <widget class="QGroupBox" name="IconSizeGroupBox"> | ||||||
|  |               <property name="title"> | ||||||
|  |                 <string>Icon Size</string> | ||||||
|  |               </property> | ||||||
|  |               <layout class="QHBoxLayout" name="icon_size_qhbox_layout"> | ||||||
|  |                 <item> | ||||||
|  |                   <layout class="QVBoxLayout" name="icon_size_qvbox_layout"> | ||||||
|  |                     <item> | ||||||
|  |                       <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2"> | ||||||
|  |                         <item> | ||||||
|  |                           <widget class="QLabel" name="icon_size_label"> | ||||||
|  |                             <property name="text"> | ||||||
|  |                               <string>Icon Size:</string> | ||||||
|  |                             </property> | ||||||
|  |                           </widget> | ||||||
|  |                         </item> | ||||||
|  |                         <item> | ||||||
|  |                           <widget class="QComboBox" name="icon_size_combobox"/> | ||||||
|  |                         </item> | ||||||
|  |                       </layout> | ||||||
|  |                     </item> | ||||||
|  |                   </layout> | ||||||
|  |                 </item> | ||||||
|  |               </layout> | ||||||
|  |             </widget> | ||||||
|  |           </item> | ||||||
|  |           <item> | ||||||
|  |             <widget class="QGroupBox" name="RowGroupBox"> | ||||||
|  |               <property name="title"> | ||||||
|  |                 <string>Row Text</string> | ||||||
|  |               </property> | ||||||
|  |               <layout class="QHBoxLayout" name="RowHorizontalLayout"> | ||||||
|  |                 <item> | ||||||
|  |                   <layout class="QVBoxLayout" name="RowVerticalLayout"> | ||||||
|  |                     <item> | ||||||
|  |                       <layout class="QHBoxLayout" name="row_1_qhbox_layout"> | ||||||
|  |                         <item> | ||||||
|  |                           <widget class="QLabel" name="row_1_label"> | ||||||
|  |                             <property name="text"> | ||||||
|  |                               <string>Row 1 Text:</string> | ||||||
|  |                             </property> | ||||||
|  |                           </widget> | ||||||
|  |                         </item> | ||||||
|  |                         <item> | ||||||
|  |                           <widget class="QComboBox" name="row_1_text_combobox"/> | ||||||
|  |                         </item> | ||||||
|  |                       </layout> | ||||||
|  |                     </item> | ||||||
|  |                     <item> | ||||||
|  |                       <layout class="QHBoxLayout" name="row_2_qhbox_layout"> | ||||||
|  |                         <item> | ||||||
|  |                           <widget class="QLabel" name="row_2_label"> | ||||||
|  |                             <property name="text"> | ||||||
|  |                               <string>Row 2 Text:</string> | ||||||
|  |                             </property> | ||||||
|  |                           </widget> | ||||||
|  |                         </item> | ||||||
|  |                         <item> | ||||||
|  |                           <widget class="QComboBox" name="row_2_text_combobox"/> | ||||||
|  |                         </item> | ||||||
|  |                       </layout> | ||||||
|  |                     </item> | ||||||
|  |                   </layout> | ||||||
|  |                 </item> | ||||||
|  |               </layout> | ||||||
|  |             </widget> | ||||||
|  |           </item> | ||||||
|  |           <item> | ||||||
|  |             <spacer name="verticalSpacer"> | ||||||
|  |               <property name="orientation"> | ||||||
|  |                 <enum>Qt::Vertical</enum> | ||||||
|  |               </property> | ||||||
|  |               <property name="sizeHint" stdset="0"> | ||||||
|  |                 <size> | ||||||
|  |                   <width>20</width> | ||||||
|  |                   <height>40</height> | ||||||
|  |                 </size> | ||||||
|  |               </property> | ||||||
|  |             </spacer> | ||||||
|  |           </item> | ||||||
|  |         </layout> | ||||||
|  |       </item> | ||||||
|  |     </layout> | ||||||
|  |   </widget> | ||||||
|  |  <resources/> | ||||||
|  |  <connections/> | ||||||
|  | </ui> | ||||||
| @ -383,8 +383,10 @@ void GraphicsSurfaceWidget::OnUpdate() { | |||||||
|     QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); |     QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); | ||||||
|     boost::optional<VAddr> address = gpu.memory_manager->GpuToCpuAddress(surface_address); |     boost::optional<VAddr> address = gpu.memory_manager->GpuToCpuAddress(surface_address); | ||||||
| 
 | 
 | ||||||
|     auto unswizzled_data = |     // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
 | ||||||
|         Tegra::Texture::UnswizzleTexture(*address, surface_format, surface_width, surface_height); |     // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
 | ||||||
|  |     auto unswizzled_data = Tegra::Texture::UnswizzleTexture( | ||||||
|  |         *address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height); | ||||||
| 
 | 
 | ||||||
|     auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, |     auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, | ||||||
|                                                       surface_width, surface_height); |                                                       surface_width, surface_height); | ||||||
|  | |||||||
| @ -9,9 +9,13 @@ | |||||||
| #include <QKeyEvent> | #include <QKeyEvent> | ||||||
| #include <QMenu> | #include <QMenu> | ||||||
| #include <QThreadPool> | #include <QThreadPool> | ||||||
|  | #include <boost/container/flat_map.hpp> | ||||||
| #include "common/common_paths.h" | #include "common/common_paths.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
|  | #include "core/file_sys/content_archive.h" | ||||||
|  | #include "core/file_sys/control_metadata.h" | ||||||
|  | #include "core/file_sys/romfs.h" | ||||||
| #include "core/file_sys/vfs_real.h" | #include "core/file_sys/vfs_real.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| #include "game_list.h" | #include "game_list.h" | ||||||
| @ -194,7 +198,8 @@ void GameList::onFilterCloseClicked() { | |||||||
|     main_window->filterBarSetChecked(false); |     main_window->filterBarSetChecked(false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GameList::GameList(GMainWindow* parent) : QWidget{parent} { | GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) | ||||||
|  |     : QWidget{parent}, vfs(std::move(vfs)) { | ||||||
|     watcher = new QFileSystemWatcher(this); |     watcher = new QFileSystemWatcher(this); | ||||||
|     connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); |     connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); | ||||||
| 
 | 
 | ||||||
| @ -338,7 +343,7 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { | |||||||
| 
 | 
 | ||||||
|     emit ShouldCancelWorker(); |     emit ShouldCancelWorker(); | ||||||
| 
 | 
 | ||||||
|     GameListWorker* worker = new GameListWorker(dir_path, deep_scan); |     GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan); | ||||||
| 
 | 
 | ||||||
|     connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); |     connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); | ||||||
|     connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, |     connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, | ||||||
| @ -398,8 +403,32 @@ void GameList::RefreshGameDirectory() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { | void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { | ||||||
|     const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory, |     boost::container::flat_map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map; | ||||||
|                                             const std::string& virtual_name) -> bool { | 
 | ||||||
|  |     const auto nca_control_callback = | ||||||
|  |         [this, &nca_control_map](u64* num_entries_out, const std::string& directory, | ||||||
|  |                                  const std::string& virtual_name) -> bool { | ||||||
|  |         std::string physical_name = directory + DIR_SEP + virtual_name; | ||||||
|  | 
 | ||||||
|  |         if (stop_processing) | ||||||
|  |             return false; // Breaks the callback loop.
 | ||||||
|  | 
 | ||||||
|  |         bool is_dir = FileUtil::IsDirectory(physical_name); | ||||||
|  |         QFileInfo file_info(physical_name.c_str()); | ||||||
|  |         if (!is_dir && file_info.suffix().toStdString() == "nca") { | ||||||
|  |             auto nca = | ||||||
|  |                 std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); | ||||||
|  |             if (nca->GetType() == FileSys::NCAContentType::Control) | ||||||
|  |                 nca_control_map.insert_or_assign(nca->GetTitleId(), nca); | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback); | ||||||
|  | 
 | ||||||
|  |     const auto callback = [this, recursion, | ||||||
|  |                            &nca_control_map](u64* num_entries_out, const std::string& directory, | ||||||
|  |                                              const std::string& virtual_name) -> bool { | ||||||
|         std::string physical_name = directory + DIR_SEP + virtual_name; |         std::string physical_name = directory + DIR_SEP + virtual_name; | ||||||
| 
 | 
 | ||||||
|         if (stop_processing) |         if (stop_processing) | ||||||
| @ -409,18 +438,51 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||||||
|         if (!is_dir && |         if (!is_dir && | ||||||
|             (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { |             (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { | ||||||
|             std::unique_ptr<Loader::AppLoader> loader = |             std::unique_ptr<Loader::AppLoader> loader = | ||||||
|                 Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name)); |                 Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); | ||||||
|             if (!loader) |             if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown || | ||||||
|  |                              loader->GetFileType() == Loader::FileType::Error) && | ||||||
|  |                             !UISettings::values.show_unknown)) | ||||||
|                 return true; |                 return true; | ||||||
| 
 | 
 | ||||||
|             std::vector<u8> smdh; |             std::vector<u8> icon; | ||||||
|             loader->ReadIcon(smdh); |             const auto res1 = loader->ReadIcon(icon); | ||||||
| 
 | 
 | ||||||
|             u64 program_id = 0; |             u64 program_id; | ||||||
|             loader->ReadProgramId(program_id); |             const auto res2 = loader->ReadProgramId(program_id); | ||||||
|  | 
 | ||||||
|  |             std::string name = " "; | ||||||
|  |             const auto res3 = loader->ReadTitle(name); | ||||||
|  | 
 | ||||||
|  |             if ((res1 == Loader::ResultStatus::ErrorNotUsed || | ||||||
|  |                  res1 == Loader::ResultStatus::ErrorNotImplemented) && | ||||||
|  |                 (res3 == Loader::ResultStatus::ErrorNotUsed || | ||||||
|  |                  res3 == Loader::ResultStatus::ErrorNotImplemented) && | ||||||
|  |                 res2 == Loader::ResultStatus::Success) { | ||||||
|  |                 // Use from metadata pool.
 | ||||||
|  |                 if (nca_control_map.find(program_id) != nca_control_map.end()) { | ||||||
|  |                     const auto nca = nca_control_map[program_id]; | ||||||
|  |                     const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS()); | ||||||
|  | 
 | ||||||
|  |                     const auto nacp_file = control_dir->GetFile("control.nacp"); | ||||||
|  |                     FileSys::NACP nacp(nacp_file); | ||||||
|  |                     name = nacp.GetApplicationName(); | ||||||
|  | 
 | ||||||
|  |                     FileSys::VirtualFile icon_file = nullptr; | ||||||
|  |                     for (const auto& language : FileSys::LANGUAGE_NAMES) { | ||||||
|  |                         icon_file = control_dir->GetFile("icon_" + std::string(language) + ".dat"); | ||||||
|  |                         if (icon_file != nullptr) { | ||||||
|  |                             icon = icon_file->ReadAllBytes(); | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             emit EntryReady({ |             emit EntryReady({ | ||||||
|                 new GameListItemPath(FormatGameName(physical_name), smdh, program_id), |                 new GameListItemPath( | ||||||
|  |                     FormatGameName(physical_name), icon, QString::fromStdString(name), | ||||||
|  |                     QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), | ||||||
|  |                     program_id), | ||||||
|                 new GameListItem( |                 new GameListItem( | ||||||
|                     QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), |                     QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), | ||||||
|                 new GameListItemSize(FileUtil::GetSize(physical_name)), |                 new GameListItemSize(FileUtil::GetSize(physical_name)), | ||||||
|  | |||||||
| @ -59,7 +59,7 @@ public: | |||||||
|         QToolButton* button_filter_close = nullptr; |         QToolButton* button_filter_close = nullptr; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     explicit GameList(GMainWindow* parent = nullptr); |     explicit GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent = nullptr); | ||||||
|     ~GameList() override; |     ~GameList() override; | ||||||
| 
 | 
 | ||||||
|     void clearFilter(); |     void clearFilter(); | ||||||
| @ -90,6 +90,7 @@ private: | |||||||
|     void PopupContextMenu(const QPoint& menu_location); |     void PopupContextMenu(const QPoint& menu_location); | ||||||
|     void RefreshGameDirectory(); |     void RefreshGameDirectory(); | ||||||
| 
 | 
 | ||||||
|  |     FileSys::VirtualFilesystem vfs; | ||||||
|     SearchField* search_field; |     SearchField* search_field; | ||||||
|     GMainWindow* main_window = nullptr; |     GMainWindow* main_window = nullptr; | ||||||
|     QVBoxLayout* layout = nullptr; |     QVBoxLayout* layout = nullptr; | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ | |||||||
| #include <QStandardItem> | #include <QStandardItem> | ||||||
| #include <QString> | #include <QString> | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
|  | #include "ui_settings.h" | ||||||
| #include "yuzu/util/util.h" | #include "yuzu/util/util.h" | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -18,8 +19,7 @@ | |||||||
|  * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) |  * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) | ||||||
|  * @return QPixmap default icon |  * @return QPixmap default icon | ||||||
|  */ |  */ | ||||||
| static QPixmap GetDefaultIcon(bool large) { | static QPixmap GetDefaultIcon(u32 size) { | ||||||
|     int size = large ? 48 : 24; |  | ||||||
|     QPixmap icon(size, size); |     QPixmap icon(size, size); | ||||||
|     icon.fill(Qt::transparent); |     icon.fill(Qt::transparent); | ||||||
|     return icon; |     return icon; | ||||||
| @ -44,11 +44,25 @@ public: | |||||||
|     static const int FullPathRole = Qt::UserRole + 1; |     static const int FullPathRole = Qt::UserRole + 1; | ||||||
|     static const int TitleRole = Qt::UserRole + 2; |     static const int TitleRole = Qt::UserRole + 2; | ||||||
|     static const int ProgramIdRole = Qt::UserRole + 3; |     static const int ProgramIdRole = Qt::UserRole + 3; | ||||||
|  |     static const int FileTypeRole = Qt::UserRole + 4; | ||||||
| 
 | 
 | ||||||
|     GameListItemPath() = default; |     GameListItemPath() = default; | ||||||
|     GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) { |     GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data, | ||||||
|  |                      const QString& game_name, const QString& game_type, u64 program_id) | ||||||
|  |         : GameListItem() { | ||||||
|         setData(game_path, FullPathRole); |         setData(game_path, FullPathRole); | ||||||
|  |         setData(game_name, TitleRole); | ||||||
|         setData(qulonglong(program_id), ProgramIdRole); |         setData(qulonglong(program_id), ProgramIdRole); | ||||||
|  |         setData(game_type, FileTypeRole); | ||||||
|  | 
 | ||||||
|  |         QPixmap picture; | ||||||
|  |         u32 size = UISettings::values.icon_size; | ||||||
|  |         if (!picture.loadFromData(picture_data.data(), picture_data.size())) | ||||||
|  |             picture = GetDefaultIcon(size); | ||||||
|  | 
 | ||||||
|  |         picture = picture.scaled(size, size); | ||||||
|  | 
 | ||||||
|  |         setData(picture, Qt::DecorationRole); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     QVariant data(int role) const override { |     QVariant data(int role) const override { | ||||||
| @ -57,7 +71,23 @@ public: | |||||||
|             Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, |             Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, | ||||||
|                               nullptr); |                               nullptr); | ||||||
|             QString title = data(TitleRole).toString(); |             QString title = data(TitleRole).toString(); | ||||||
|             return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n    " + title); | 
 | ||||||
|  |             std::vector<QString> row_data{ | ||||||
|  |                 QString::fromStdString(filename), | ||||||
|  |                 data(FileTypeRole).toString(), | ||||||
|  |                 QString::fromStdString(fmt::format("0x{:016X}", data(ProgramIdRole).toULongLong())), | ||||||
|  |                 data(TitleRole).toString(), | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             auto row1 = row_data.at(UISettings::values.row_1_text_id); | ||||||
|  |             auto row2 = row_data.at(UISettings::values.row_2_text_id); | ||||||
|  | 
 | ||||||
|  |             if (row1.isEmpty() || row1 == row2) | ||||||
|  |                 return row2; | ||||||
|  |             if (row2.isEmpty()) | ||||||
|  |                 return row1; | ||||||
|  | 
 | ||||||
|  |             return row1 + "\n    " + row2; | ||||||
|         } else { |         } else { | ||||||
|             return GameListItem::data(role); |             return GameListItem::data(role); | ||||||
|         } |         } | ||||||
| @ -109,8 +139,8 @@ class GameListWorker : public QObject, public QRunnable { | |||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     GameListWorker(QString dir_path, bool deep_scan) |     GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan) | ||||||
|         : dir_path(std::move(dir_path)), deep_scan(deep_scan) {} |         : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan) {} | ||||||
| 
 | 
 | ||||||
| public slots: | public slots: | ||||||
|     /// Starts the processing of directory tree information.
 |     /// Starts the processing of directory tree information.
 | ||||||
| @ -133,6 +163,7 @@ signals: | |||||||
|     void Finished(QStringList watch_list); |     void Finished(QStringList watch_list); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |     FileSys::VirtualFilesystem vfs; | ||||||
|     QStringList watch_list; |     QStringList watch_list; | ||||||
|     QString dir_path; |     QString dir_path; | ||||||
|     bool deep_scan; |     bool deep_scan; | ||||||
|  | |||||||
| @ -24,6 +24,7 @@ | |||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/crypto/key_manager.h" | #include "core/crypto/key_manager.h" | ||||||
|  | #include "core/file_sys/vfs_real.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| #include "core/settings.h" | #include "core/settings.h" | ||||||
| @ -83,7 +84,9 @@ void GMainWindow::ShowCallouts() {} | |||||||
| 
 | 
 | ||||||
| const int GMainWindow::max_recent_files_item; | const int GMainWindow::max_recent_files_item; | ||||||
| 
 | 
 | ||||||
| GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | GMainWindow::GMainWindow() | ||||||
|  |     : config(new Config()), emu_thread(nullptr), | ||||||
|  |       vfs(std::make_shared<FileSys::RealVfsFilesystem>()) { | ||||||
| 
 | 
 | ||||||
|     debug_context = Tegra::DebugContext::Construct(); |     debug_context = Tegra::DebugContext::Construct(); | ||||||
| 
 | 
 | ||||||
| @ -132,7 +135,7 @@ void GMainWindow::InitializeWidgets() { | |||||||
|     render_window = new GRenderWindow(this, emu_thread.get()); |     render_window = new GRenderWindow(this, emu_thread.get()); | ||||||
|     render_window->hide(); |     render_window->hide(); | ||||||
| 
 | 
 | ||||||
|     game_list = new GameList(this); |     game_list = new GameList(vfs, this); | ||||||
|     ui.horizontalLayout->addWidget(game_list); |     ui.horizontalLayout->addWidget(game_list); | ||||||
| 
 | 
 | ||||||
|     // Create status bar
 |     // Create status bar
 | ||||||
| @ -406,6 +409,7 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Core::System& system{Core::System::GetInstance()}; |     Core::System& system{Core::System::GetInstance()}; | ||||||
|  |     system.SetFilesystem(vfs); | ||||||
| 
 | 
 | ||||||
|     system.SetGPUDebugContext(debug_context); |     system.SetGPUDebugContext(debug_context); | ||||||
| 
 | 
 | ||||||
| @ -768,6 +772,7 @@ void GMainWindow::OnConfigure() { | |||||||
|         configureDialog.applyConfiguration(); |         configureDialog.applyConfiguration(); | ||||||
|         if (UISettings::values.theme != old_theme) |         if (UISettings::values.theme != old_theme) | ||||||
|             UpdateUITheme(); |             UpdateUITheme(); | ||||||
|  |         game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | ||||||
|         config->Save(); |         config->Save(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -161,6 +161,9 @@ private: | |||||||
|     bool emulation_running = false; |     bool emulation_running = false; | ||||||
|     std::unique_ptr<EmuThread> emu_thread; |     std::unique_ptr<EmuThread> emu_thread; | ||||||
| 
 | 
 | ||||||
|  |     // FS
 | ||||||
|  |     FileSys::VirtualFilesystem vfs; | ||||||
|  | 
 | ||||||
|     // Debugger panes
 |     // Debugger panes
 | ||||||
|     ProfilerWidget* profilerWidget; |     ProfilerWidget* profilerWidget; | ||||||
|     MicroProfileDialog* microProfileDialog; |     MicroProfileDialog* microProfileDialog; | ||||||
|  | |||||||
| @ -54,6 +54,12 @@ struct Values { | |||||||
| 
 | 
 | ||||||
|     // logging
 |     // logging
 | ||||||
|     bool show_console; |     bool show_console; | ||||||
|  | 
 | ||||||
|  |     // Game List
 | ||||||
|  |     bool show_unknown; | ||||||
|  |     uint32_t icon_size; | ||||||
|  |     uint8_t row_1_text_id; | ||||||
|  |     uint8_t row_2_text_id; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| extern Values values; | extern Values values; | ||||||
|  | |||||||
| @ -161,6 +161,7 @@ int main(int argc, char** argv) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Core::System& system{Core::System::GetInstance()}; |     Core::System& system{Core::System::GetInstance()}; | ||||||
|  |     system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | ||||||
| 
 | 
 | ||||||
|     SCOPE_EXIT({ system.Shutdown(); }); |     SCOPE_EXIT({ system.Shutdown(); }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 David Marcec
						David Marcec