diff options
Diffstat (limited to 'src')
51 files changed, 480 insertions, 398 deletions
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index ecdd7505b..1ece55731 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp | |||
| @@ -168,10 +168,6 @@ VirtualDir XCI::GetParentDirectory() const { | |||
| 168 | return file->GetContainingDirectory(); | 168 | return file->GetContainingDirectory(); |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 172 | return false; | ||
| 173 | } | ||
| 174 | |||
| 175 | Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | 171 | Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { |
| 176 | if (partitions[static_cast<std::size_t>(part)] == nullptr) { | 172 | if (partitions[static_cast<std::size_t>(part)] == nullptr) { |
| 177 | return Loader::ResultStatus::ErrorXCIMissingPartition; | 173 | return Loader::ResultStatus::ErrorXCIMissingPartition; |
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 48cbef666..8f62571cf 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h | |||
| @@ -94,9 +94,6 @@ public: | |||
| 94 | 94 | ||
| 95 | VirtualDir GetParentDirectory() const override; | 95 | VirtualDir GetParentDirectory() const override; |
| 96 | 96 | ||
| 97 | protected: | ||
| 98 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 99 | |||
| 100 | private: | 97 | private: |
| 101 | Loader::ResultStatus AddNCAFromPartition(XCIPartition part); | 98 | Loader::ResultStatus AddNCAFromPartition(XCIPartition part); |
| 102 | 99 | ||
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 6c356d85d..77e04704e 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -546,7 +546,4 @@ u64 NCA::GetBaseIVFCOffset() const { | |||
| 546 | return ivfc_offset; | 546 | return ivfc_offset; |
| 547 | } | 547 | } |
| 548 | 548 | ||
| 549 | bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 550 | return false; | ||
| 551 | } | ||
| 552 | } // namespace FileSys | 549 | } // namespace FileSys |
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 1c903cd3f..211946686 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h | |||
| @@ -100,9 +100,6 @@ public: | |||
| 100 | // Returns the base ivfc offset used in BKTR patching. | 100 | // Returns the base ivfc offset used in BKTR patching. |
| 101 | u64 GetBaseIVFCOffset() const; | 101 | u64 GetBaseIVFCOffset() const; |
| 102 | 102 | ||
| 103 | protected: | ||
| 104 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 105 | |||
| 106 | private: | 103 | private: |
| 107 | bool CheckSupportedNCA(const NCAHeader& header); | 104 | bool CheckSupportedNCA(const NCAHeader& header); |
| 108 | bool HandlePotentialHeaderDecryption(); | 105 | bool HandlePotentialHeaderDecryption(); |
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 554eae9bc..999939d5a 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp | |||
| @@ -99,7 +99,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { | |||
| 99 | u16 rle_size{}; | 99 | u16 rle_size{}; |
| 100 | if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) | 100 | if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) |
| 101 | return nullptr; | 101 | return nullptr; |
| 102 | rle_size = Common::swap16(data_size); | 102 | rle_size = Common::swap16(rle_size); |
| 103 | offset += sizeof(u16); | 103 | offset += sizeof(u16); |
| 104 | 104 | ||
| 105 | const auto data = ips->ReadByte(offset++); | 105 | const auto data = ips->ReadByte(offset++); |
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 5791c76ff..a5259a593 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp | |||
| @@ -83,7 +83,7 @@ std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const { | |||
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { | 85 | std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { |
| 86 | return pfs_dirs; | 86 | return {}; |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | std::string PartitionFilesystem::GetName() const { | 89 | std::string PartitionFilesystem::GetName() const { |
| @@ -103,18 +103,4 @@ void PartitionFilesystem::PrintDebugInfo() const { | |||
| 103 | pfs_files[i]->GetName(), pfs_files[i]->GetSize()); | 103 | pfs_files[i]->GetName(), pfs_files[i]->GetSize()); |
| 104 | } | 104 | } |
| 105 | } | 105 | } |
| 106 | |||
| 107 | bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 108 | const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file); | ||
| 109 | if (iter == pfs_files.end()) | ||
| 110 | return false; | ||
| 111 | |||
| 112 | const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter); | ||
| 113 | pfs_files[offset] = std::move(pfs_files.back()); | ||
| 114 | pfs_files.pop_back(); | ||
| 115 | |||
| 116 | pfs_dirs.emplace_back(std::move(dir)); | ||
| 117 | |||
| 118 | return true; | ||
| 119 | } | ||
| 120 | } // namespace FileSys | 106 | } // namespace FileSys |
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index 739c63a7f..248fdfdeb 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h | |||
| @@ -35,9 +35,6 @@ public: | |||
| 35 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | 35 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; |
| 36 | void PrintDebugInfo() const; | 36 | void PrintDebugInfo() const; |
| 37 | 37 | ||
| 38 | protected: | ||
| 39 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 40 | |||
| 41 | private: | 38 | private: |
| 42 | struct Header { | 39 | struct Header { |
| 43 | u32_le magic; | 40 | u32_le magic; |
| @@ -84,7 +81,6 @@ private: | |||
| 84 | std::size_t content_offset = 0; | 81 | std::size_t content_offset = 0; |
| 85 | 82 | ||
| 86 | std::vector<VirtualFile> pfs_files; | 83 | std::vector<VirtualFile> pfs_files; |
| 87 | std::vector<VirtualDir> pfs_dirs; | ||
| 88 | }; | 84 | }; |
| 89 | 85 | ||
| 90 | } // namespace FileSys | 86 | } // namespace FileSys |
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index ab5dc900c..2aaba4179 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp | |||
| @@ -205,10 +205,6 @@ VirtualDir NSP::GetParentDirectory() const { | |||
| 205 | return file->GetContainingDirectory(); | 205 | return file->GetContainingDirectory(); |
| 206 | } | 206 | } |
| 207 | 207 | ||
| 208 | bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 209 | return false; | ||
| 210 | } | ||
| 211 | |||
| 212 | void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { | 208 | void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { |
| 213 | exefs = pfs; | 209 | exefs = pfs; |
| 214 | 210 | ||
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index da3dc5e9f..338080b7e 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h | |||
| @@ -55,9 +55,6 @@ public: | |||
| 55 | 55 | ||
| 56 | VirtualDir GetParentDirectory() const override; | 56 | VirtualDir GetParentDirectory() const override; |
| 57 | 57 | ||
| 58 | protected: | ||
| 59 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 60 | |||
| 61 | private: | 58 | private: |
| 62 | void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files); | 59 | void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files); |
| 63 | void ReadNCAs(const std::vector<VirtualFile>& files); | 60 | void ReadNCAs(const std::vector<VirtualFile>& files); |
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index 7f0d520ca..09dc9f288 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h | |||
| @@ -262,36 +262,8 @@ public: | |||
| 262 | // item name -> type. | 262 | // item name -> type. |
| 263 | virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const; | 263 | virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const; |
| 264 | 264 | ||
| 265 | // Interprets the file with name file instead as a directory of type directory. | ||
| 266 | // The directory must have a constructor that takes a single argument of type | ||
| 267 | // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a | ||
| 268 | // subdirectory in one call. | ||
| 269 | template <typename Directory> | ||
| 270 | bool InterpretAsDirectory(std::string_view file) { | ||
| 271 | auto file_p = GetFile(file); | ||
| 272 | |||
| 273 | if (file_p == nullptr) { | ||
| 274 | return false; | ||
| 275 | } | ||
| 276 | |||
| 277 | return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p)); | ||
| 278 | } | ||
| 279 | |||
| 280 | bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function, | ||
| 281 | const std::string& file) { | ||
| 282 | auto file_p = GetFile(file); | ||
| 283 | if (file_p == nullptr) | ||
| 284 | return false; | ||
| 285 | return ReplaceFileWithSubdirectory(file_p, function(file_p)); | ||
| 286 | } | ||
| 287 | |||
| 288 | // Returns the full path of this directory as a string, recursively | 265 | // Returns the full path of this directory as a string, recursively |
| 289 | virtual std::string GetFullPath() const; | 266 | virtual std::string GetFullPath() const; |
| 290 | |||
| 291 | protected: | ||
| 292 | // Backend for InterpretAsDirectory. | ||
| 293 | // Removes all references to file and adds a reference to dir in the directory's implementation. | ||
| 294 | virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0; | ||
| 295 | }; | 267 | }; |
| 296 | 268 | ||
| 297 | // A convenience partial-implementation of VfsDirectory that stubs out methods that should only work | 269 | // A convenience partial-implementation of VfsDirectory that stubs out methods that should only work |
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp index bfee01725..338e398da 100644 --- a/src/core/file_sys/vfs_layered.cpp +++ b/src/core/file_sys/vfs_layered.cpp | |||
| @@ -126,7 +126,4 @@ bool LayeredVfsDirectory::Rename(std::string_view name_) { | |||
| 126 | return true; | 126 | return true; |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 130 | return false; | ||
| 131 | } | ||
| 132 | } // namespace FileSys | 129 | } // namespace FileSys |
diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h index d85310f57..8a25c3428 100644 --- a/src/core/file_sys/vfs_layered.h +++ b/src/core/file_sys/vfs_layered.h | |||
| @@ -39,9 +39,6 @@ public: | |||
| 39 | bool DeleteFile(std::string_view name) override; | 39 | bool DeleteFile(std::string_view name) override; |
| 40 | bool Rename(std::string_view name) override; | 40 | bool Rename(std::string_view name) override; |
| 41 | 41 | ||
| 42 | protected: | ||
| 43 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 44 | |||
| 45 | private: | 42 | private: |
| 46 | std::vector<VirtualDir> dirs; | 43 | std::vector<VirtualDir> dirs; |
| 47 | std::string name; | 44 | std::string name; |
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 9defad04c..e21300a7c 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp | |||
| @@ -430,7 +430,4 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() | |||
| 430 | return out; | 430 | return out; |
| 431 | } | 431 | } |
| 432 | 432 | ||
| 433 | bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 434 | return false; | ||
| 435 | } | ||
| 436 | } // namespace FileSys | 433 | } // namespace FileSys |
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index 5b61db90d..a0a857a31 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h | |||
| @@ -100,9 +100,6 @@ public: | |||
| 100 | std::string GetFullPath() const override; | 100 | std::string GetFullPath() const override; |
| 101 | std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; | 101 | std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; |
| 102 | 102 | ||
| 103 | protected: | ||
| 104 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 105 | |||
| 106 | private: | 103 | private: |
| 107 | RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); | 104 | RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); |
| 108 | 105 | ||
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp index 389c7e003..808f31e81 100644 --- a/src/core/file_sys/vfs_vector.cpp +++ b/src/core/file_sys/vfs_vector.cpp | |||
| @@ -132,11 +132,4 @@ void VectorVfsDirectory::AddFile(VirtualFile file) { | |||
| 132 | void VectorVfsDirectory::AddDirectory(VirtualDir dir) { | 132 | void VectorVfsDirectory::AddDirectory(VirtualDir dir) { |
| 133 | dirs.push_back(std::move(dir)); | 133 | dirs.push_back(std::move(dir)); |
| 134 | } | 134 | } |
| 135 | |||
| 136 | bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 137 | if (!DeleteFile(file->GetName())) | ||
| 138 | return false; | ||
| 139 | dirs.emplace_back(std::move(dir)); | ||
| 140 | return true; | ||
| 141 | } | ||
| 142 | } // namespace FileSys | 135 | } // namespace FileSys |
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h index 48a414c98..3e3f790c3 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs_vector.h | |||
| @@ -57,9 +57,6 @@ public: | |||
| 57 | virtual void AddFile(VirtualFile file); | 57 | virtual void AddFile(VirtualFile file); |
| 58 | virtual void AddDirectory(VirtualDir dir); | 58 | virtual void AddDirectory(VirtualDir dir); |
| 59 | 59 | ||
| 60 | protected: | ||
| 61 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 62 | |||
| 63 | private: | 60 | private: |
| 64 | std::vector<VirtualFile> files; | 61 | std::vector<VirtualFile> files; |
| 65 | std::vector<VirtualDir> dirs; | 62 | std::vector<VirtualDir> dirs; |
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index b2b164368..eec51c64e 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp | |||
| @@ -163,7 +163,4 @@ std::shared_ptr<VfsDirectory> NAX::GetParentDirectory() const { | |||
| 163 | return file->GetContainingDirectory(); | 163 | return file->GetContainingDirectory(); |
| 164 | } | 164 | } |
| 165 | 165 | ||
| 166 | bool NAX::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 167 | return false; | ||
| 168 | } | ||
| 169 | } // namespace FileSys | 166 | } // namespace FileSys |
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 8fedd8585..7704dee90 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h | |||
| @@ -51,9 +51,6 @@ public: | |||
| 51 | 51 | ||
| 52 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | 52 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; |
| 53 | 53 | ||
| 54 | protected: | ||
| 55 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 56 | |||
| 57 | private: | 54 | private: |
| 58 | Loader::ResultStatus Parse(std::string_view path); | 55 | Loader::ResultStatus Parse(std::string_view path); |
| 59 | 56 | ||
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 419f45896..ed84197b3 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h | |||
| @@ -14,11 +14,6 @@ namespace IPC { | |||
| 14 | /// Size of the command buffer area, in 32-bit words. | 14 | /// Size of the command buffer area, in 32-bit words. |
| 15 | constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); | 15 | constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); |
| 16 | 16 | ||
| 17 | // These errors are commonly returned by invalid IPC translations, so alias them here for | ||
| 18 | // convenience. | ||
| 19 | // TODO(yuriks): These will probably go away once translation is implemented inside the kernel. | ||
| 20 | constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS; | ||
| 21 | |||
| 22 | enum class ControlCommand : u32 { | 17 | enum class ControlCommand : u32 { |
| 23 | ConvertSessionToDomain = 0, | 18 | ConvertSessionToDomain = 0, |
| 24 | ConvertDomainToSession = 1, | 19 | ConvertDomainToSession = 1, |
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 885259618..ee698c8a7 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h | |||
| @@ -10,11 +10,6 @@ namespace Kernel { | |||
| 10 | 10 | ||
| 11 | namespace ErrCodes { | 11 | namespace ErrCodes { |
| 12 | enum { | 12 | enum { |
| 13 | // TODO(Subv): Remove these 3DS OS error codes. | ||
| 14 | SessionClosedByRemote = 26, | ||
| 15 | NoPendingSessions = 35, | ||
| 16 | InvalidBufferDescriptor = 48, | ||
| 17 | |||
| 18 | // Confirmed Switch OS error codes | 13 | // Confirmed Switch OS error codes |
| 19 | MaxConnectionsReached = 7, | 14 | MaxConnectionsReached = 7, |
| 20 | InvalidSize = 101, | 15 | InvalidSize = 101, |
| @@ -26,6 +21,7 @@ enum { | |||
| 26 | InvalidThreadPriority = 112, | 21 | InvalidThreadPriority = 112, |
| 27 | InvalidProcessorId = 113, | 22 | InvalidProcessorId = 113, |
| 28 | InvalidHandle = 114, | 23 | InvalidHandle = 114, |
| 24 | InvalidPointer = 115, | ||
| 29 | InvalidCombination = 116, | 25 | InvalidCombination = 116, |
| 30 | Timeout = 117, | 26 | Timeout = 117, |
| 31 | SynchronizationCanceled = 118, | 27 | SynchronizationCanceled = 118, |
| @@ -33,6 +29,7 @@ enum { | |||
| 33 | InvalidEnumValue = 120, | 29 | InvalidEnumValue = 120, |
| 34 | NoSuchEntry = 121, | 30 | NoSuchEntry = 121, |
| 35 | AlreadyRegistered = 122, | 31 | AlreadyRegistered = 122, |
| 32 | SessionClosed = 123, | ||
| 36 | InvalidState = 125, | 33 | InvalidState = 125, |
| 37 | ResourceLimitExceeded = 132, | 34 | ResourceLimitExceeded = 132, |
| 38 | }; | 35 | }; |
| @@ -41,18 +38,14 @@ enum { | |||
| 41 | // WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always | 38 | // WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always |
| 42 | // double check that the code matches before re-using the constant. | 39 | // double check that the code matches before re-using the constant. |
| 43 | 40 | ||
| 44 | // TODO(bunnei): Replace -1 with correct errors for Switch OS | ||
| 45 | constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); | 41 | constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); |
| 46 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); | 42 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed); |
| 47 | constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); | 43 | constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); |
| 48 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, | 44 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, |
| 49 | ErrCodes::MaxConnectionsReached); | 45 | ErrCodes::MaxConnectionsReached); |
| 50 | constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); | 46 | constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); |
| 51 | constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1); | ||
| 52 | constexpr ResultCode ERR_INVALID_COMBINATION(-1); | ||
| 53 | constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, | 47 | constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, |
| 54 | ErrCodes::InvalidCombination); | 48 | ErrCodes::InvalidCombination); |
| 55 | constexpr ResultCode ERR_OUT_OF_MEMORY(-1); | ||
| 56 | constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); | 49 | constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); |
| 57 | constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); | 50 | constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); |
| 58 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, | 51 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, |
| @@ -65,14 +58,8 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::Alrea | |||
| 65 | constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); | 58 | constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); |
| 66 | constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, | 59 | constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, |
| 67 | ErrCodes::InvalidThreadPriority); | 60 | ErrCodes::InvalidThreadPriority); |
| 68 | constexpr ResultCode ERR_INVALID_POINTER(-1); | 61 | constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer); |
| 69 | constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); | ||
| 70 | constexpr ResultCode ERR_NOT_AUTHORIZED(-1); | ||
| 71 | /// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. | ||
| 72 | constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); | ||
| 73 | constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); | 62 | constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); |
| 74 | constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); | 63 | constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); |
| 75 | /// Returned when Accept() is called on a port with no sessions to be accepted. | ||
| 76 | constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); | ||
| 77 | 64 | ||
| 78 | } // namespace Kernel | 65 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 073dd5a7d..420218d59 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -232,6 +232,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { | |||
| 232 | MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); | 232 | MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); |
| 233 | MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); | 233 | MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); |
| 234 | MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); | 234 | MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); |
| 235 | |||
| 236 | // Clear instruction cache in CPU JIT | ||
| 237 | Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); | ||
| 238 | Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); | ||
| 239 | Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); | ||
| 240 | Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); | ||
| 235 | } | 241 | } |
| 236 | 242 | ||
| 237 | ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { | 243 | ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { |
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 3792e3e18..d6ceeb2da 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp | |||
| @@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default; | |||
| 18 | 18 | ||
| 19 | ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { | 19 | ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { |
| 20 | if (pending_sessions.empty()) { | 20 | if (pending_sessions.empty()) { |
| 21 | return ERR_NO_PENDING_SESSIONS; | 21 | return ERR_NOT_FOUND; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | auto session = std::move(pending_sessions.back()); | 24 | auto session = std::move(pending_sessions.back()); |
| @@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { | |||
| 28 | 28 | ||
| 29 | bool ServerPort::ShouldWait(Thread* thread) const { | 29 | bool ServerPort::ShouldWait(Thread* thread) const { |
| 30 | // If there are no pending sessions, we wait until a new one is added. | 30 | // If there are no pending sessions, we wait until a new one is added. |
| 31 | return pending_sessions.size() == 0; | 31 | return pending_sessions.empty(); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | void ServerPort::Acquire(Thread* thread) { | 34 | void ServerPort::Acquire(Thread* thread) { |
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index d061e6155..a016a86b6 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp | |||
| @@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet( | |||
| 80 | 80 | ||
| 81 | ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, | 81 | ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, |
| 82 | MemoryPermission other_permissions) { | 82 | MemoryPermission other_permissions) { |
| 83 | 83 | const MemoryPermission own_other_permissions = | |
| 84 | MemoryPermission own_other_permissions = | ||
| 85 | target_process == owner_process ? this->permissions : this->other_permissions; | 84 | target_process == owner_process ? this->permissions : this->other_permissions; |
| 86 | 85 | ||
| 87 | // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare | 86 | // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare |
| 88 | if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { | 87 | if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { |
| 89 | return ERR_INVALID_COMBINATION; | 88 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 90 | } | 89 | } |
| 91 | 90 | ||
| 92 | // Error out if the requested permissions don't match what the creator process allows. | 91 | // Error out if the requested permissions don't match what the creator process allows. |
| 93 | if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { | 92 | if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { |
| 94 | LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", | 93 | LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", |
| 95 | GetObjectId(), address, name); | 94 | GetObjectId(), address, name); |
| 96 | return ERR_INVALID_COMBINATION; | 95 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | // Error out if the provided permissions are not compatible with what the creator process needs. | 98 | // Error out if the provided permissions are not compatible with what the creator process needs. |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 9a783d524..a5302d924 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -594,16 +594,17 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { | |||
| 594 | } | 594 | } |
| 595 | 595 | ||
| 596 | const auto* const current_process = Core::CurrentProcess(); | 596 | const auto* const current_process = Core::CurrentProcess(); |
| 597 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | ||
| 598 | if (!thread) { | ||
| 599 | return ERR_INVALID_HANDLE; | ||
| 600 | } | ||
| 601 | 597 | ||
| 602 | // Note: The kernel uses the current process's resource limit instead of | 598 | // Note: The kernel uses the current process's resource limit instead of |
| 603 | // the one from the thread owner's resource limit. | 599 | // the one from the thread owner's resource limit. |
| 604 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); | 600 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); |
| 605 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { | 601 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { |
| 606 | return ERR_NOT_AUTHORIZED; | 602 | return ERR_INVALID_THREAD_PRIORITY; |
| 603 | } | ||
| 604 | |||
| 605 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | ||
| 606 | if (!thread) { | ||
| 607 | return ERR_INVALID_HANDLE; | ||
| 607 | } | 608 | } |
| 608 | 609 | ||
| 609 | thread->SetPriority(priority); | 610 | thread->SetPriority(priority); |
| @@ -745,7 +746,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 745 | auto* const current_process = Core::CurrentProcess(); | 746 | auto* const current_process = Core::CurrentProcess(); |
| 746 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); | 747 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); |
| 747 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { | 748 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { |
| 748 | return ERR_NOT_AUTHORIZED; | 749 | return ERR_INVALID_THREAD_PRIORITY; |
| 749 | } | 750 | } |
| 750 | 751 | ||
| 751 | if (processor_id == THREADPROCESSORID_DEFAULT) { | 752 | if (processor_id == THREADPROCESSORID_DEFAULT) { |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index e1a34eef1..1a92c8f70 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me | |||
| 143 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | 143 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const { | ||
| 147 | // Find the first Free VMA. | ||
| 148 | const VAddr base = GetASLRRegionBaseAddress(); | ||
| 149 | const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { | ||
| 150 | if (vma.second.type != VMAType::Free) | ||
| 151 | return false; | ||
| 152 | |||
| 153 | const VAddr vma_end = vma.second.base + vma.second.size; | ||
| 154 | return vma_end > base && vma_end >= base + size; | ||
| 155 | }); | ||
| 156 | |||
| 157 | if (vma_handle == vma_map.end()) { | ||
| 158 | // TODO(Subv): Find the correct error code here. | ||
| 159 | return ResultCode(-1); | ||
| 160 | } | ||
| 161 | |||
| 162 | const VAddr target = std::max(base, vma_handle->second.base); | ||
| 163 | return MakeResult<VAddr>(target); | ||
| 164 | } | ||
| 165 | |||
| 146 | ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, | 166 | ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, |
| 147 | MemoryState state, | 167 | MemoryState state, |
| 148 | Memory::MemoryHookPointer mmio_handler) { | 168 | Memory::MemoryHookPointer mmio_handler) { |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 84c890224..2447cbb8f 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -158,6 +158,14 @@ public: | |||
| 158 | ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); | 158 | ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); |
| 159 | 159 | ||
| 160 | /** | 160 | /** |
| 161 | * Finds the first free address that can hold a region of the desired size. | ||
| 162 | * | ||
| 163 | * @param size Size of the desired region. | ||
| 164 | * @return The found free address. | ||
| 165 | */ | ||
| 166 | ResultVal<VAddr> FindFreeRegion(u64 size) const; | ||
| 167 | |||
| 168 | /** | ||
| 161 | * Maps a memory-mapped IO region at a given address. | 169 | * Maps a memory-mapped IO region at a given address. |
| 162 | * | 170 | * |
| 163 | * @param target The guest address to start the mapping at. | 171 | * @param target The guest address to start the mapping at. |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index cf065c2e0..c6437a671 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -21,8 +21,6 @@ | |||
| 21 | 21 | ||
| 22 | namespace Service::Account { | 22 | namespace Service::Account { |
| 23 | 23 | ||
| 24 | constexpr u32 MAX_JPEG_IMAGE_SIZE = 0x20000; | ||
| 25 | |||
| 26 | // TODO: RE this structure | 24 | // TODO: RE this structure |
| 27 | struct UserData { | 25 | struct UserData { |
| 28 | INSERT_PADDING_WORDS(1); | 26 | INSERT_PADDING_WORDS(1); |
| @@ -34,11 +32,29 @@ struct UserData { | |||
| 34 | }; | 32 | }; |
| 35 | static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); | 33 | static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); |
| 36 | 34 | ||
| 35 | // Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg | ||
| 36 | // used as a backup should the one on disk not exist | ||
| 37 | constexpr u32 backup_jpeg_size = 107; | ||
| 38 | constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{ | ||
| 39 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, | ||
| 40 | 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, | ||
| 41 | 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, | ||
| 42 | 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, | ||
| 43 | 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, | ||
| 44 | 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, | ||
| 45 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||
| 46 | }}; | ||
| 47 | |||
| 37 | static std::string GetImagePath(UUID uuid) { | 48 | static std::string GetImagePath(UUID uuid) { |
| 38 | return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | 49 | return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + |
| 39 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | 50 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; |
| 40 | } | 51 | } |
| 41 | 52 | ||
| 53 | static constexpr u32 SanitizeJPEGSize(std::size_t size) { | ||
| 54 | constexpr std::size_t max_jpeg_image_size = 0x20000; | ||
| 55 | return static_cast<u32>(std::min(size, max_jpeg_image_size)); | ||
| 56 | } | ||
| 57 | |||
| 42 | class IProfile final : public ServiceFramework<IProfile> { | 58 | class IProfile final : public ServiceFramework<IProfile> { |
| 43 | public: | 59 | public: |
| 44 | explicit IProfile(UUID user_id, ProfileManager& profile_manager) | 60 | explicit IProfile(UUID user_id, ProfileManager& profile_manager) |
| @@ -86,43 +102,29 @@ private: | |||
| 86 | 102 | ||
| 87 | void LoadImage(Kernel::HLERequestContext& ctx) { | 103 | void LoadImage(Kernel::HLERequestContext& ctx) { |
| 88 | LOG_DEBUG(Service_ACC, "called"); | 104 | LOG_DEBUG(Service_ACC, "called"); |
| 89 | // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg | ||
| 90 | // used as a backup should the one on disk not exist | ||
| 91 | constexpr u32 backup_jpeg_size = 107; | ||
| 92 | static constexpr std::array<u8, backup_jpeg_size> backup_jpeg{ | ||
| 93 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, | ||
| 94 | 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, | ||
| 95 | 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, | ||
| 96 | 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, | ||
| 97 | 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, | ||
| 98 | 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00, | ||
| 99 | 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, | ||
| 100 | 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||
| 101 | }; | ||
| 102 | 105 | ||
| 103 | IPC::ResponseBuilder rb{ctx, 3}; | 106 | IPC::ResponseBuilder rb{ctx, 3}; |
| 104 | rb.Push(RESULT_SUCCESS); | 107 | rb.Push(RESULT_SUCCESS); |
| 105 | 108 | ||
| 106 | const FileUtil::IOFile image(GetImagePath(user_id), "rb"); | 109 | const FileUtil::IOFile image(GetImagePath(user_id), "rb"); |
| 107 | |||
| 108 | if (!image.IsOpen()) { | 110 | if (!image.IsOpen()) { |
| 109 | LOG_WARNING(Service_ACC, | 111 | LOG_WARNING(Service_ACC, |
| 110 | "Failed to load user provided image! Falling back to built-in backup..."); | 112 | "Failed to load user provided image! Falling back to built-in backup..."); |
| 111 | ctx.WriteBuffer(backup_jpeg); | 113 | ctx.WriteBuffer(backup_jpeg); |
| 112 | rb.Push<u32>(backup_jpeg_size); | 114 | rb.Push<u32>(backup_jpeg_size); |
| 113 | } else { | 115 | return; |
| 114 | const auto size = std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE); | ||
| 115 | std::vector<u8> buffer(size); | ||
| 116 | image.ReadBytes(buffer.data(), buffer.size()); | ||
| 117 | |||
| 118 | ctx.WriteBuffer(buffer.data(), buffer.size()); | ||
| 119 | rb.Push<u32>(buffer.size()); | ||
| 120 | } | 116 | } |
| 117 | |||
| 118 | const u32 size = SanitizeJPEGSize(image.GetSize()); | ||
| 119 | std::vector<u8> buffer(size); | ||
| 120 | image.ReadBytes(buffer.data(), buffer.size()); | ||
| 121 | |||
| 122 | ctx.WriteBuffer(buffer.data(), buffer.size()); | ||
| 123 | rb.Push<u32>(size); | ||
| 121 | } | 124 | } |
| 122 | 125 | ||
| 123 | void GetImageSize(Kernel::HLERequestContext& ctx) { | 126 | void GetImageSize(Kernel::HLERequestContext& ctx) { |
| 124 | LOG_DEBUG(Service_ACC, "called"); | 127 | LOG_DEBUG(Service_ACC, "called"); |
| 125 | constexpr u32 backup_jpeg_size = 107; | ||
| 126 | IPC::ResponseBuilder rb{ctx, 3}; | 128 | IPC::ResponseBuilder rb{ctx, 3}; |
| 127 | rb.Push(RESULT_SUCCESS); | 129 | rb.Push(RESULT_SUCCESS); |
| 128 | 130 | ||
| @@ -133,7 +135,7 @@ private: | |||
| 133 | "Failed to load user provided image! Falling back to built-in backup..."); | 135 | "Failed to load user provided image! Falling back to built-in backup..."); |
| 134 | rb.Push<u32>(backup_jpeg_size); | 136 | rb.Push<u32>(backup_jpeg_size); |
| 135 | } else { | 137 | } else { |
| 136 | rb.Push<u32>(std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE)); | 138 | rb.Push<u32>(SanitizeJPEGSize(image.GetSize())); |
| 137 | } | 139 | } |
| 138 | } | 140 | } |
| 139 | 141 | ||
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 06f7d1b15..3cac1b4ff 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <random> | 5 | #include <random> |
| 6 | #include <boost/optional.hpp> | 6 | |
| 7 | #include "common/file_util.h" | 7 | #include "common/file_util.h" |
| 8 | #include "core/hle/service/acc/profile_manager.h" | 8 | #include "core/hle/service/acc/profile_manager.h" |
| 9 | #include "core/settings.h" | 9 | #include "core/settings.h" |
| @@ -58,11 +58,11 @@ ProfileManager::~ProfileManager() { | |||
| 58 | 58 | ||
| 59 | /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the | 59 | /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the |
| 60 | /// internal management of the users profiles | 60 | /// internal management of the users profiles |
| 61 | boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { | 61 | std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { |
| 62 | if (user_count >= MAX_USERS) { | 62 | if (user_count >= MAX_USERS) { |
| 63 | return boost::none; | 63 | return {}; |
| 64 | } | 64 | } |
| 65 | profiles[user_count] = user; | 65 | profiles[user_count] = profile; |
| 66 | return user_count++; | 66 | return user_count++; |
| 67 | } | 67 | } |
| 68 | 68 | ||
| @@ -81,7 +81,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { | |||
| 81 | 81 | ||
| 82 | /// Helper function to register a user to the system | 82 | /// Helper function to register a user to the system |
| 83 | ResultCode ProfileManager::AddUser(const ProfileInfo& user) { | 83 | ResultCode ProfileManager::AddUser(const ProfileInfo& user) { |
| 84 | if (AddToProfiles(user) == boost::none) { | 84 | if (!AddToProfiles(user)) { |
| 85 | return ERROR_TOO_MANY_USERS; | 85 | return ERROR_TOO_MANY_USERS; |
| 86 | } | 86 | } |
| 87 | return RESULT_SUCCESS; | 87 | return RESULT_SUCCESS; |
| @@ -126,37 +126,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) | |||
| 126 | return CreateNewUser(uuid, username_output); | 126 | return CreateNewUser(uuid, username_output); |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | boost::optional<UUID> ProfileManager::GetUser(std::size_t index) const { | 129 | std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { |
| 130 | if (index >= MAX_USERS) | 130 | if (index >= MAX_USERS) { |
| 131 | return boost::none; | 131 | return {}; |
| 132 | } | ||
| 133 | |||
| 132 | return profiles[index].user_uuid; | 134 | return profiles[index].user_uuid; |
| 133 | } | 135 | } |
| 134 | 136 | ||
| 135 | /// Returns a users profile index based on their user id. | 137 | /// Returns a users profile index based on their user id. |
| 136 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { | 138 | std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { |
| 137 | if (!uuid) { | 139 | if (!uuid) { |
| 138 | return boost::none; | 140 | return {}; |
| 139 | } | 141 | } |
| 140 | auto iter = std::find_if(profiles.begin(), profiles.end(), | 142 | |
| 141 | [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); | 143 | const auto iter = std::find_if(profiles.begin(), profiles.end(), |
| 144 | [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); | ||
| 142 | if (iter == profiles.end()) { | 145 | if (iter == profiles.end()) { |
| 143 | return boost::none; | 146 | return {}; |
| 144 | } | 147 | } |
| 148 | |||
| 145 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); | 149 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); |
| 146 | } | 150 | } |
| 147 | 151 | ||
| 148 | /// Returns a users profile index based on their profile | 152 | /// Returns a users profile index based on their profile |
| 149 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { | 153 | std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { |
| 150 | return GetUserIndex(user.user_uuid); | 154 | return GetUserIndex(user.user_uuid); |
| 151 | } | 155 | } |
| 152 | 156 | ||
| 153 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* | 157 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* |
| 154 | bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, | 158 | bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const { |
| 155 | ProfileBase& profile) const { | 159 | if (!index || index >= MAX_USERS) { |
| 156 | if (index == boost::none || index >= MAX_USERS) { | ||
| 157 | return false; | 160 | return false; |
| 158 | } | 161 | } |
| 159 | const auto& prof_info = profiles[index.get()]; | 162 | const auto& prof_info = profiles[*index]; |
| 160 | profile.user_uuid = prof_info.user_uuid; | 163 | profile.user_uuid = prof_info.user_uuid; |
| 161 | profile.username = prof_info.username; | 164 | profile.username = prof_info.username; |
| 162 | profile.timestamp = prof_info.creation_time; | 165 | profile.timestamp = prof_info.creation_time; |
| @@ -165,7 +168,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, | |||
| 165 | 168 | ||
| 166 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* | 169 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* |
| 167 | bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { | 170 | bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { |
| 168 | auto idx = GetUserIndex(uuid); | 171 | const auto idx = GetUserIndex(uuid); |
| 169 | return GetProfileBase(idx, profile); | 172 | return GetProfileBase(idx, profile); |
| 170 | } | 173 | } |
| 171 | 174 | ||
| @@ -192,7 +195,7 @@ std::size_t ProfileManager::GetOpenUserCount() const { | |||
| 192 | 195 | ||
| 193 | /// Checks if a user id exists in our profile manager | 196 | /// Checks if a user id exists in our profile manager |
| 194 | bool ProfileManager::UserExists(UUID uuid) const { | 197 | bool ProfileManager::UserExists(UUID uuid) const { |
| 195 | return (GetUserIndex(uuid) != boost::none); | 198 | return GetUserIndex(uuid) != std::nullopt; |
| 196 | } | 199 | } |
| 197 | 200 | ||
| 198 | bool ProfileManager::UserExistsIndex(std::size_t index) const { | 201 | bool ProfileManager::UserExistsIndex(std::size_t index) const { |
| @@ -203,21 +206,23 @@ bool ProfileManager::UserExistsIndex(std::size_t index) const { | |||
| 203 | 206 | ||
| 204 | /// Opens a specific user | 207 | /// Opens a specific user |
| 205 | void ProfileManager::OpenUser(UUID uuid) { | 208 | void ProfileManager::OpenUser(UUID uuid) { |
| 206 | auto idx = GetUserIndex(uuid); | 209 | const auto idx = GetUserIndex(uuid); |
| 207 | if (idx == boost::none) { | 210 | if (!idx) { |
| 208 | return; | 211 | return; |
| 209 | } | 212 | } |
| 210 | profiles[idx.get()].is_open = true; | 213 | |
| 214 | profiles[*idx].is_open = true; | ||
| 211 | last_opened_user = uuid; | 215 | last_opened_user = uuid; |
| 212 | } | 216 | } |
| 213 | 217 | ||
| 214 | /// Closes a specific user | 218 | /// Closes a specific user |
| 215 | void ProfileManager::CloseUser(UUID uuid) { | 219 | void ProfileManager::CloseUser(UUID uuid) { |
| 216 | auto idx = GetUserIndex(uuid); | 220 | const auto idx = GetUserIndex(uuid); |
| 217 | if (idx == boost::none) { | 221 | if (!idx) { |
| 218 | return; | 222 | return; |
| 219 | } | 223 | } |
| 220 | profiles[idx.get()].is_open = false; | 224 | |
| 225 | profiles[*idx].is_open = false; | ||
| 221 | } | 226 | } |
| 222 | 227 | ||
| 223 | /// Gets all valid user ids on the system | 228 | /// Gets all valid user ids on the system |
| @@ -247,10 +252,10 @@ UUID ProfileManager::GetLastOpenedUser() const { | |||
| 247 | } | 252 | } |
| 248 | 253 | ||
| 249 | /// Return the users profile base and the unknown arbitary data. | 254 | /// Return the users profile base and the unknown arbitary data. |
| 250 | bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, | 255 | bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, |
| 251 | ProfileData& data) const { | 256 | ProfileData& data) const { |
| 252 | if (GetProfileBase(index, profile)) { | 257 | if (GetProfileBase(index, profile)) { |
| 253 | data = profiles[index.get()].data; | 258 | data = profiles[*index].data; |
| 254 | return true; | 259 | return true; |
| 255 | } | 260 | } |
| 256 | return false; | 261 | return false; |
| @@ -259,7 +264,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P | |||
| 259 | /// Return the users profile base and the unknown arbitary data. | 264 | /// Return the users profile base and the unknown arbitary data. |
| 260 | bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, | 265 | bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, |
| 261 | ProfileData& data) const { | 266 | ProfileData& data) const { |
| 262 | auto idx = GetUserIndex(uuid); | 267 | const auto idx = GetUserIndex(uuid); |
| 263 | return GetProfileBaseAndData(idx, profile, data); | 268 | return GetProfileBaseAndData(idx, profile, data); |
| 264 | } | 269 | } |
| 265 | 270 | ||
| @@ -277,8 +282,8 @@ bool ProfileManager::CanSystemRegisterUser() const { | |||
| 277 | } | 282 | } |
| 278 | 283 | ||
| 279 | bool ProfileManager::RemoveUser(UUID uuid) { | 284 | bool ProfileManager::RemoveUser(UUID uuid) { |
| 280 | auto index = GetUserIndex(uuid); | 285 | const auto index = GetUserIndex(uuid); |
| 281 | if (index == boost::none) { | 286 | if (!index) { |
| 282 | return false; | 287 | return false; |
| 283 | } | 288 | } |
| 284 | 289 | ||
| @@ -289,8 +294,8 @@ bool ProfileManager::RemoveUser(UUID uuid) { | |||
| 289 | } | 294 | } |
| 290 | 295 | ||
| 291 | bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { | 296 | bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { |
| 292 | auto index = GetUserIndex(uuid); | 297 | const auto index = GetUserIndex(uuid); |
| 293 | if (profile_new.user_uuid == UUID(INVALID_UUID) || index == boost::none) { | 298 | if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) { |
| 294 | return false; | 299 | return false; |
| 295 | } | 300 | } |
| 296 | 301 | ||
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 235208d56..1cd2e51b2 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h | |||
| @@ -5,8 +5,8 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <optional> | ||
| 8 | 9 | ||
| 9 | #include "boost/optional.hpp" | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 12 | #include "core/hle/result.h" | 12 | #include "core/hle/result.h" |
| @@ -96,13 +96,13 @@ public: | |||
| 96 | ResultCode AddUser(const ProfileInfo& user); | 96 | ResultCode AddUser(const ProfileInfo& user); |
| 97 | ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); | 97 | ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); |
| 98 | ResultCode CreateNewUser(UUID uuid, const std::string& username); | 98 | ResultCode CreateNewUser(UUID uuid, const std::string& username); |
| 99 | boost::optional<UUID> GetUser(std::size_t index) const; | 99 | std::optional<UUID> GetUser(std::size_t index) const; |
| 100 | boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; | 100 | std::optional<std::size_t> GetUserIndex(const UUID& uuid) const; |
| 101 | boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; | 101 | std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; |
| 102 | bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; | 102 | bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const; |
| 103 | bool GetProfileBase(UUID uuid, ProfileBase& profile) const; | 103 | bool GetProfileBase(UUID uuid, ProfileBase& profile) const; |
| 104 | bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; | 104 | bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; |
| 105 | bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, | 105 | bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, |
| 106 | ProfileData& data) const; | 106 | ProfileData& data) const; |
| 107 | bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; | 107 | bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; |
| 108 | bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, | 108 | bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, |
| @@ -120,16 +120,16 @@ public: | |||
| 120 | bool CanSystemRegisterUser() const; | 120 | bool CanSystemRegisterUser() const; |
| 121 | 121 | ||
| 122 | bool RemoveUser(UUID uuid); | 122 | bool RemoveUser(UUID uuid); |
| 123 | bool SetProfileBase(UUID uuid, const ProfileBase& profile); | 123 | bool SetProfileBase(UUID uuid, const ProfileBase& profile_new); |
| 124 | 124 | ||
| 125 | private: | 125 | private: |
| 126 | void ParseUserSaveFile(); | 126 | void ParseUserSaveFile(); |
| 127 | void WriteUserSaveFile(); | 127 | void WriteUserSaveFile(); |
| 128 | std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); | ||
| 129 | bool RemoveProfileAtIndex(std::size_t index); | ||
| 128 | 130 | ||
| 129 | std::array<ProfileInfo, MAX_USERS> profiles{}; | 131 | std::array<ProfileInfo, MAX_USERS> profiles{}; |
| 130 | std::size_t user_count = 0; | 132 | std::size_t user_count = 0; |
| 131 | boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); | ||
| 132 | bool RemoveProfileAtIndex(std::size_t index); | ||
| 133 | UUID last_opened_user{INVALID_UUID}; | 133 | UUID last_opened_user{INVALID_UUID}; |
| 134 | }; | 134 | }; |
| 135 | 135 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4ed66d817..59aafd616 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -743,7 +743,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { | |||
| 743 | 743 | ||
| 744 | Account::ProfileManager profile_manager{}; | 744 | Account::ProfileManager profile_manager{}; |
| 745 | const auto uuid = profile_manager.GetUser(Settings::values.current_user); | 745 | const auto uuid = profile_manager.GetUser(Settings::values.current_user); |
| 746 | ASSERT(uuid != boost::none); | 746 | ASSERT(uuid != std::nullopt); |
| 747 | params.current_user = uuid->uuid; | 747 | params.current_user = uuid->uuid; |
| 748 | 748 | ||
| 749 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 749 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index ec32faf15..d607d985e 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -3,9 +3,13 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <fmt/format.h> | ||
| 6 | 7 | ||
| 8 | #include "core/hle/ipc_helpers.h" | ||
| 9 | #include "core/hle/kernel/process.h" | ||
| 7 | #include "core/hle/service/ldr/ldr.h" | 10 | #include "core/hle/service/ldr/ldr.h" |
| 8 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 12 | #include "core/loader/nro.h" | ||
| 9 | 13 | ||
| 10 | namespace Service::LDR { | 14 | namespace Service::LDR { |
| 11 | 15 | ||
| @@ -59,16 +63,58 @@ public: | |||
| 59 | explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { | 63 | explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { |
| 60 | // clang-format off | 64 | // clang-format off |
| 61 | static const FunctionInfo functions[] = { | 65 | static const FunctionInfo functions[] = { |
| 62 | {0, nullptr, "LoadNro"}, | 66 | {0, &RelocatableObject::LoadNro, "LoadNro"}, |
| 63 | {1, nullptr, "UnloadNro"}, | 67 | {1, nullptr, "UnloadNro"}, |
| 64 | {2, nullptr, "LoadNrr"}, | 68 | {2, &RelocatableObject::LoadNrr, "LoadNrr"}, |
| 65 | {3, nullptr, "UnloadNrr"}, | 69 | {3, nullptr, "UnloadNrr"}, |
| 66 | {4, nullptr, "Initialize"}, | 70 | {4, &RelocatableObject::Initialize, "Initialize"}, |
| 67 | }; | 71 | }; |
| 68 | // clang-format on | 72 | // clang-format on |
| 69 | 73 | ||
| 70 | RegisterHandlers(functions); | 74 | RegisterHandlers(functions); |
| 71 | } | 75 | } |
| 76 | |||
| 77 | void LoadNrr(Kernel::HLERequestContext& ctx) { | ||
| 78 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 79 | rb.Push(RESULT_SUCCESS); | ||
| 80 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 81 | } | ||
| 82 | |||
| 83 | void LoadNro(Kernel::HLERequestContext& ctx) { | ||
| 84 | IPC::RequestParser rp{ctx}; | ||
| 85 | rp.Skip(2, false); | ||
| 86 | const VAddr nro_addr{rp.Pop<VAddr>()}; | ||
| 87 | const u64 nro_size{rp.Pop<u64>()}; | ||
| 88 | const VAddr bss_addr{rp.Pop<VAddr>()}; | ||
| 89 | const u64 bss_size{rp.Pop<u64>()}; | ||
| 90 | |||
| 91 | // Read NRO data from memory | ||
| 92 | std::vector<u8> nro_data(nro_size); | ||
| 93 | Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); | ||
| 94 | |||
| 95 | // Load NRO as new executable module | ||
| 96 | const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; | ||
| 97 | Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); | ||
| 98 | |||
| 99 | // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. | ||
| 100 | // It is currently missing: | ||
| 101 | // - Signature checks with LoadNRR | ||
| 102 | // - Checking if a module has already been loaded | ||
| 103 | // - Using/validating BSS, etc. params (these are used from NRO header instead) | ||
| 104 | // - Error checking | ||
| 105 | // - ...Probably other things | ||
| 106 | |||
| 107 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 108 | rb.Push(RESULT_SUCCESS); | ||
| 109 | rb.Push(addr); | ||
| 110 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 111 | } | ||
| 112 | |||
| 113 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 114 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 115 | rb.Push(RESULT_SUCCESS); | ||
| 116 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 117 | } | ||
| 72 | }; | 118 | }; |
| 73 | 119 | ||
| 74 | void InstallInterfaces(SM::ServiceManager& sm) { | 120 | void InstallInterfaces(SM::ServiceManager& sm) { |
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 9a4eb9301..c1af878fe 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -328,13 +328,15 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { | |||
| 328 | rb.PushIpcInterface<IUser>(*this); | 328 | rb.PushIpcInterface<IUser>(*this); |
| 329 | } | 329 | } |
| 330 | 330 | ||
| 331 | void Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { | 331 | bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { |
| 332 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | 332 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); |
| 333 | if (buffer.size() < sizeof(AmiiboFile)) { | 333 | if (buffer.size() < sizeof(AmiiboFile)) { |
| 334 | return; // Failed to load file | 334 | return false; |
| 335 | } | 335 | } |
| 336 | |||
| 336 | std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); | 337 | std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); |
| 337 | nfc_tag_load->Signal(); | 338 | nfc_tag_load->Signal(); |
| 339 | return true; | ||
| 338 | } | 340 | } |
| 339 | const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const { | 341 | const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const { |
| 340 | return nfc_tag_load; | 342 | return nfc_tag_load; |
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 46370dedd..5c0ae8a54 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h | |||
| @@ -32,7 +32,7 @@ public: | |||
| 32 | static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); | 32 | static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); |
| 33 | 33 | ||
| 34 | void CreateUserInterface(Kernel::HLERequestContext& ctx); | 34 | void CreateUserInterface(Kernel::HLERequestContext& ctx); |
| 35 | void LoadAmiibo(const std::vector<u8>& buffer); | 35 | bool LoadAmiibo(const std::vector<u8>& buffer); |
| 36 | const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const; | 36 | const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const; |
| 37 | const AmiiboFile& GetAmiiboBuffer() const; | 37 | const AmiiboFile& GetAmiiboBuffer() const; |
| 38 | 38 | ||
diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index e7fb5a419..c489da071 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp | |||
| @@ -67,15 +67,15 @@ public: | |||
| 67 | explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { | 67 | explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { |
| 68 | // clang-format off | 68 | // clang-format off |
| 69 | static const FunctionInfo functions[] = { | 69 | static const FunctionInfo functions[] = { |
| 70 | {0, nullptr, "Unknown1"}, | 70 | {0, nullptr, "Open"}, |
| 71 | {1, nullptr, "Unknown2"}, | 71 | {1, nullptr, "Close"}, |
| 72 | {2, nullptr, "Unknown3"}, | 72 | {2, nullptr, "Unknown1"}, |
| 73 | {3, nullptr, "Unknown4"}, | 73 | {3, nullptr, "Populate"}, |
| 74 | {4, nullptr, "PostBufferAsync"}, | 74 | {4, nullptr, "PostBufferAsync"}, |
| 75 | {5, nullptr, "Unknown5"}, | 75 | {5, nullptr, "GetXferReport"}, |
| 76 | {6, nullptr, "Unknown6"}, | 76 | {6, nullptr, "Unknown2"}, |
| 77 | {7, nullptr, "Unknown7"}, | 77 | {7, nullptr, "Unknown3"}, |
| 78 | {8, nullptr, "Unknown8"}, | 78 | {8, nullptr, "Unknown4"}, |
| 79 | }; | 79 | }; |
| 80 | // clang-format on | 80 | // clang-format on |
| 81 | 81 | ||
| @@ -89,15 +89,15 @@ public: | |||
| 89 | // clang-format off | 89 | // clang-format off |
| 90 | static const FunctionInfo functions[] = { | 90 | static const FunctionInfo functions[] = { |
| 91 | {0, nullptr, "Unknown1"}, | 91 | {0, nullptr, "Unknown1"}, |
| 92 | {1, nullptr, "Unknown2"}, | 92 | {1, nullptr, "SetInterface"}, |
| 93 | {2, nullptr, "Unknown3"}, | 93 | {2, nullptr, "GetInterface"}, |
| 94 | {3, nullptr, "Unknown4"}, | 94 | {3, nullptr, "GetAlternateInterface"}, |
| 95 | {4, nullptr, "Unknown5"}, | 95 | {4, nullptr, "GetCurrentFrame"}, |
| 96 | {5, nullptr, "CtrlXferAsync"}, | 96 | {5, nullptr, "CtrlXferAsync"}, |
| 97 | {6, nullptr, "Unknown6"}, | 97 | {6, nullptr, "Unknown2"}, |
| 98 | {7, nullptr, "GetCtrlXferReport"}, | 98 | {7, nullptr, "GetCtrlXferReport"}, |
| 99 | {8, nullptr, "Unknown7"}, | 99 | {8, nullptr, "ResetDevice"}, |
| 100 | {9, nullptr, "GetClientEpSession"}, | 100 | {9, nullptr, "OpenUsbEp"}, |
| 101 | }; | 101 | }; |
| 102 | // clang-format on | 102 | // clang-format on |
| 103 | 103 | ||
| @@ -111,13 +111,14 @@ public: | |||
| 111 | // clang-format off | 111 | // clang-format off |
| 112 | static const FunctionInfo functions[] = { | 112 | static const FunctionInfo functions[] = { |
| 113 | {0, nullptr, "BindClientProcess"}, | 113 | {0, nullptr, "BindClientProcess"}, |
| 114 | {1, nullptr, "Unknown1"}, | 114 | {1, nullptr, "QueryAllInterfaces"}, |
| 115 | {2, nullptr, "Unknown2"}, | 115 | {2, nullptr, "QueryAvailableInterfaces"}, |
| 116 | {3, nullptr, "Unknown3"}, | 116 | {3, nullptr, "QueryAcquiredInterfaces"}, |
| 117 | {4, nullptr, "Unknown4"}, | 117 | {4, nullptr, "CreateInterfaceAvailableEvent"}, |
| 118 | {5, nullptr, "Unknown5"}, | 118 | {5, nullptr, "DestroyInterfaceAvailableEvent"}, |
| 119 | {6, nullptr, "GetInterfaceStateChangeEvent"}, | 119 | {6, nullptr, "GetInterfaceStateChangeEvent"}, |
| 120 | {7, nullptr, "GetClientIfSession"}, | 120 | {7, nullptr, "AcquireUsbIf"}, |
| 121 | {8, nullptr, "Unknown1"}, | ||
| 121 | }; | 122 | }; |
| 122 | // clang-format on | 123 | // clang-format on |
| 123 | 124 | ||
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 243b499f2..bc8e402a8 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -127,18 +127,23 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 127 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | 127 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { | 130 | /*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name, |
| 131 | // Read NSO header | 131 | VAddr load_base) { |
| 132 | NroHeader nro_header{}; | 132 | |
| 133 | if (sizeof(NroHeader) != file.ReadObject(&nro_header)) { | 133 | if (data.size() < sizeof(NroHeader)) { |
| 134 | return {}; | 134 | return {}; |
| 135 | } | 135 | } |
| 136 | |||
| 137 | // Read NSO header | ||
| 138 | NroHeader nro_header{}; | ||
| 139 | std::memcpy(&nro_header, data.data(), sizeof(NroHeader)); | ||
| 136 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { | 140 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { |
| 137 | return {}; | 141 | return {}; |
| 138 | } | 142 | } |
| 139 | 143 | ||
| 140 | // Build program image | 144 | // Build program image |
| 141 | std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size)); | 145 | std::vector<u8> program_image(PageAlignSize(nro_header.file_size)); |
| 146 | std::memcpy(program_image.data(), data.data(), program_image.size()); | ||
| 142 | if (program_image.size() != PageAlignSize(nro_header.file_size)) { | 147 | if (program_image.size() != PageAlignSize(nro_header.file_size)) { |
| 143 | return {}; | 148 | return {}; |
| 144 | } | 149 | } |
| @@ -182,11 +187,15 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { | |||
| 182 | Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); | 187 | Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); |
| 183 | 188 | ||
| 184 | // Register module with GDBStub | 189 | // Register module with GDBStub |
| 185 | GDBStub::RegisterModule(file.GetName(), load_base, load_base); | 190 | GDBStub::RegisterModule(name, load_base, load_base); |
| 186 | 191 | ||
| 187 | return true; | 192 | return true; |
| 188 | } | 193 | } |
| 189 | 194 | ||
| 195 | bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { | ||
| 196 | return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base); | ||
| 197 | } | ||
| 198 | |||
| 190 | ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { | 199 | ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { |
| 191 | if (is_loaded) { | 200 | if (is_loaded) { |
| 192 | return ResultStatus::ErrorAlreadyLoaded; | 201 | return ResultStatus::ErrorAlreadyLoaded; |
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 50ee5a78a..3e6959302 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include <vector> | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "core/loader/linker.h" | 10 | #include "core/loader/linker.h" |
| 10 | #include "core/loader/loader.h" | 11 | #include "core/loader/loader.h" |
| @@ -40,6 +41,8 @@ public: | |||
| 40 | ResultStatus ReadTitle(std::string& title) override; | 41 | ResultStatus ReadTitle(std::string& title) override; |
| 41 | bool IsRomFSUpdatable() const override; | 42 | bool IsRomFSUpdatable() const override; |
| 42 | 43 | ||
| 44 | static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base); | ||
| 45 | |||
| 43 | private: | 46 | private: |
| 44 | bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); | 47 | bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); |
| 45 | 48 | ||
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 78ba29fc1..27ef865a2 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cinttypes> | 5 | #include <cinttypes> |
| 6 | #include <cstring> | ||
| 6 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 7 | #include "core/core.h" | 8 | #include "core/core.h" |
| 8 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| @@ -19,7 +20,24 @@ namespace Tegra::Engines { | |||
| 19 | constexpr u32 MacroRegistersStart = 0xE00; | 20 | constexpr u32 MacroRegistersStart = 0xE00; |
| 20 | 21 | ||
| 21 | Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) | 22 | Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) |
| 22 | : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {} | 23 | : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) { |
| 24 | InitializeRegisterDefaults(); | ||
| 25 | } | ||
| 26 | |||
| 27 | void Maxwell3D::InitializeRegisterDefaults() { | ||
| 28 | // Initializes registers to their default values - what games expect them to be at boot. This is | ||
| 29 | // for certain registers that may not be explicitly set by games. | ||
| 30 | |||
| 31 | // Reset all registers to zero | ||
| 32 | std::memset(®s, 0, sizeof(regs)); | ||
| 33 | |||
| 34 | // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is | ||
| 35 | // needed for ARMS. | ||
| 36 | for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { | ||
| 37 | regs.viewport[viewport].depth_range_near = 0.0f; | ||
| 38 | regs.viewport[viewport].depth_range_far = 1.0f; | ||
| 39 | } | ||
| 40 | } | ||
| 23 | 41 | ||
| 24 | void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { | 42 | void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { |
| 25 | // Reset the current macro. | 43 | // Reset the current macro. |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 0e09a7ee5..754a149fa 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -984,6 +984,8 @@ public: | |||
| 984 | Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; | 984 | Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; |
| 985 | 985 | ||
| 986 | private: | 986 | private: |
| 987 | void InitializeRegisterDefaults(); | ||
| 988 | |||
| 987 | VideoCore::RasterizerInterface& rasterizer; | 989 | VideoCore::RasterizerInterface& rasterizer; |
| 988 | 990 | ||
| 989 | std::unordered_map<u32, std::vector<u32>> uploaded_macros; | 991 | std::unordered_map<u32, std::vector<u32>> uploaded_macros; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index be51c5215..b472f421f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -570,6 +570,7 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 570 | SyncBlendState(); | 570 | SyncBlendState(); |
| 571 | SyncLogicOpState(); | 571 | SyncLogicOpState(); |
| 572 | SyncCullMode(); | 572 | SyncCullMode(); |
| 573 | SyncDepthRange(); | ||
| 573 | SyncScissorTest(); | 574 | SyncScissorTest(); |
| 574 | // Alpha Testing is synced on shaders. | 575 | // Alpha Testing is synced on shaders. |
| 575 | SyncTransformFeedback(); | 576 | SyncTransformFeedback(); |
| @@ -923,12 +924,11 @@ void RasterizerOpenGL::SyncCullMode() { | |||
| 923 | } | 924 | } |
| 924 | } | 925 | } |
| 925 | 926 | ||
| 926 | void RasterizerOpenGL::SyncDepthScale() { | 927 | void RasterizerOpenGL::SyncDepthRange() { |
| 927 | UNREACHABLE(); | 928 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 928 | } | ||
| 929 | 929 | ||
| 930 | void RasterizerOpenGL::SyncDepthOffset() { | 930 | state.depth.depth_range_near = regs.viewport->depth_range_near; |
| 931 | UNREACHABLE(); | 931 | state.depth.depth_range_far = regs.viewport->depth_range_far; |
| 932 | } | 932 | } |
| 933 | 933 | ||
| 934 | void RasterizerOpenGL::SyncDepthTestState() { | 934 | void RasterizerOpenGL::SyncDepthTestState() { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 0e90a31f5..731a336d5 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -144,11 +144,8 @@ private: | |||
| 144 | /// Syncs the cull mode to match the guest state | 144 | /// Syncs the cull mode to match the guest state |
| 145 | void SyncCullMode(); | 145 | void SyncCullMode(); |
| 146 | 146 | ||
| 147 | /// Syncs the depth scale to match the guest state | 147 | /// Syncs the depth range to match the guest state |
| 148 | void SyncDepthScale(); | 148 | void SyncDepthRange(); |
| 149 | |||
| 150 | /// Syncs the depth offset to match the guest state | ||
| 151 | void SyncDepthOffset(); | ||
| 152 | 149 | ||
| 153 | /// Syncs the depth test state to match the guest state | 150 | /// Syncs the depth test state to match the guest state |
| 154 | void SyncDepthTestState(); | 151 | void SyncDepthTestState(); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 9c8925383..591ec7998 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -78,6 +78,29 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 78 | } | 78 | } |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { | ||
| 82 | const u32 compression_factor{GetCompressionFactor(pixel_format)}; | ||
| 83 | const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; | ||
| 84 | u32 m_depth = (layer_only ? 1U : depth); | ||
| 85 | u32 m_width = std::max(1U, width / compression_factor); | ||
| 86 | u32 m_height = std::max(1U, height / compression_factor); | ||
| 87 | std::size_t size = Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, | ||
| 88 | m_depth, block_height, block_depth); | ||
| 89 | u32 m_block_height = block_height; | ||
| 90 | u32 m_block_depth = block_depth; | ||
| 91 | std::size_t block_size_bytes = 512 * block_height * block_depth; // 512 is GOB size | ||
| 92 | for (u32 i = 1; i < max_mip_level; i++) { | ||
| 93 | m_width = std::max(1U, m_width / 2); | ||
| 94 | m_height = std::max(1U, m_height / 2); | ||
| 95 | m_depth = std::max(1U, m_depth / 2); | ||
| 96 | m_block_height = std::max(1U, m_block_height / 2); | ||
| 97 | m_block_depth = std::max(1U, m_block_depth / 2); | ||
| 98 | size += Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, m_depth, | ||
| 99 | m_block_height, m_block_depth); | ||
| 100 | } | ||
| 101 | return is_tiled ? Common::AlignUp(size, block_size_bytes) : size; | ||
| 102 | } | ||
| 103 | |||
| 81 | /*static*/ SurfaceParams SurfaceParams::CreateForTexture( | 104 | /*static*/ SurfaceParams SurfaceParams::CreateForTexture( |
| 82 | const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { | 105 | const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { |
| 83 | SurfaceParams params{}; | 106 | SurfaceParams params{}; |
| @@ -124,6 +147,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 124 | break; | 147 | break; |
| 125 | } | 148 | } |
| 126 | 149 | ||
| 150 | params.is_layered = SurfaceTargetIsLayered(params.target); | ||
| 127 | params.max_mip_level = config.tic.max_mip_level + 1; | 151 | params.max_mip_level = config.tic.max_mip_level + 1; |
| 128 | params.rt = {}; | 152 | params.rt = {}; |
| 129 | 153 | ||
| @@ -150,6 +174,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 150 | params.target = SurfaceTarget::Texture2D; | 174 | params.target = SurfaceTarget::Texture2D; |
| 151 | params.depth = 1; | 175 | params.depth = 1; |
| 152 | params.max_mip_level = 0; | 176 | params.max_mip_level = 0; |
| 177 | params.is_layered = false; | ||
| 153 | 178 | ||
| 154 | // Render target specific parameters, not used for caching | 179 | // Render target specific parameters, not used for caching |
| 155 | params.rt.index = static_cast<u32>(index); | 180 | params.rt.index = static_cast<u32>(index); |
| @@ -182,6 +207,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 182 | params.target = SurfaceTarget::Texture2D; | 207 | params.target = SurfaceTarget::Texture2D; |
| 183 | params.depth = 1; | 208 | params.depth = 1; |
| 184 | params.max_mip_level = 0; | 209 | params.max_mip_level = 0; |
| 210 | params.is_layered = false; | ||
| 185 | params.rt = {}; | 211 | params.rt = {}; |
| 186 | 212 | ||
| 187 | params.InitCacheParameters(zeta_address); | 213 | params.InitCacheParameters(zeta_address); |
| @@ -361,10 +387,11 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d | |||
| 361 | } | 387 | } |
| 362 | } | 388 | } |
| 363 | 389 | ||
| 364 | static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), | 390 | using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), |
| 365 | SurfaceParams::MaxPixelFormat> | 391 | SurfaceParams::MaxPixelFormat>; |
| 366 | morton_to_gl_fns = { | 392 | |
| 367 | // clang-format off | 393 | static constexpr GLConversionArray morton_to_gl_fns = { |
| 394 | // clang-format off | ||
| 368 | MortonCopy<true, PixelFormat::ABGR8U>, | 395 | MortonCopy<true, PixelFormat::ABGR8U>, |
| 369 | MortonCopy<true, PixelFormat::ABGR8S>, | 396 | MortonCopy<true, PixelFormat::ABGR8S>, |
| 370 | MortonCopy<true, PixelFormat::ABGR8UI>, | 397 | MortonCopy<true, PixelFormat::ABGR8UI>, |
| @@ -418,13 +445,11 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, | |||
| 418 | MortonCopy<true, PixelFormat::Z24S8>, | 445 | MortonCopy<true, PixelFormat::Z24S8>, |
| 419 | MortonCopy<true, PixelFormat::S8Z24>, | 446 | MortonCopy<true, PixelFormat::S8Z24>, |
| 420 | MortonCopy<true, PixelFormat::Z32FS8>, | 447 | MortonCopy<true, PixelFormat::Z32FS8>, |
| 421 | // clang-format on | 448 | // clang-format on |
| 422 | }; | 449 | }; |
| 423 | 450 | ||
| 424 | static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), | 451 | static constexpr GLConversionArray gl_to_morton_fns = { |
| 425 | SurfaceParams::MaxPixelFormat> | 452 | // clang-format off |
| 426 | gl_to_morton_fns = { | ||
| 427 | // clang-format off | ||
| 428 | MortonCopy<false, PixelFormat::ABGR8U>, | 453 | MortonCopy<false, PixelFormat::ABGR8U>, |
| 429 | MortonCopy<false, PixelFormat::ABGR8S>, | 454 | MortonCopy<false, PixelFormat::ABGR8S>, |
| 430 | MortonCopy<false, PixelFormat::ABGR8UI>, | 455 | MortonCopy<false, PixelFormat::ABGR8UI>, |
| @@ -479,9 +504,35 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, | |||
| 479 | MortonCopy<false, PixelFormat::Z24S8>, | 504 | MortonCopy<false, PixelFormat::Z24S8>, |
| 480 | MortonCopy<false, PixelFormat::S8Z24>, | 505 | MortonCopy<false, PixelFormat::S8Z24>, |
| 481 | MortonCopy<false, PixelFormat::Z32FS8>, | 506 | MortonCopy<false, PixelFormat::Z32FS8>, |
| 482 | // clang-format on | 507 | // clang-format on |
| 483 | }; | 508 | }; |
| 484 | 509 | ||
| 510 | void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params, | ||
| 511 | std::vector<u8>& gl_buffer) { | ||
| 512 | u32 depth = params.depth; | ||
| 513 | if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { | ||
| 514 | // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. | ||
| 515 | depth = 1U; | ||
| 516 | } | ||
| 517 | if (params.is_layered) { | ||
| 518 | u64 offset = 0; | ||
| 519 | u64 offset_gl = 0; | ||
| 520 | u64 layer_size = params.LayerMemorySize(); | ||
| 521 | u64 gl_size = params.LayerSizeGL(); | ||
| 522 | for (u32 i = 0; i < depth; i++) { | ||
| 523 | functions[static_cast<std::size_t>(params.pixel_format)]( | ||
| 524 | params.width, params.block_height, params.height, params.block_depth, 1, | ||
| 525 | gl_buffer.data() + offset_gl, gl_size, params.addr + offset); | ||
| 526 | offset += layer_size; | ||
| 527 | offset_gl += gl_size; | ||
| 528 | } | ||
| 529 | } else { | ||
| 530 | functions[static_cast<std::size_t>(params.pixel_format)]( | ||
| 531 | params.width, params.block_height, params.height, params.block_depth, depth, | ||
| 532 | gl_buffer.data(), gl_buffer.size(), params.addr); | ||
| 533 | } | ||
| 534 | } | ||
| 535 | |||
| 485 | static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, | 536 | static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, |
| 486 | GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, | 537 | GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, |
| 487 | GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { | 538 | GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { |
| @@ -881,21 +932,10 @@ void CachedSurface::LoadGLBuffer() { | |||
| 881 | 932 | ||
| 882 | gl_buffer.resize(params.size_in_bytes_gl); | 933 | gl_buffer.resize(params.size_in_bytes_gl); |
| 883 | if (params.is_tiled) { | 934 | if (params.is_tiled) { |
| 884 | u32 depth = params.depth; | ||
| 885 | u32 block_depth = params.block_depth; | ||
| 886 | |||
| 887 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", | 935 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", |
| 888 | params.block_width, static_cast<u32>(params.target)); | 936 | params.block_width, static_cast<u32>(params.target)); |
| 889 | 937 | ||
| 890 | if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { | 938 | SwizzleFunc(morton_to_gl_fns, params, gl_buffer); |
| 891 | // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. | ||
| 892 | depth = 1U; | ||
| 893 | block_depth = 1U; | ||
| 894 | } | ||
| 895 | |||
| 896 | morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( | ||
| 897 | params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), | ||
| 898 | gl_buffer.size(), params.addr); | ||
| 899 | } else { | 939 | } else { |
| 900 | const auto texture_src_data{Memory::GetPointer(params.addr)}; | 940 | const auto texture_src_data{Memory::GetPointer(params.addr)}; |
| 901 | const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; | 941 | const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; |
| @@ -929,19 +969,10 @@ void CachedSurface::FlushGLBuffer() { | |||
| 929 | const u8* const texture_src_data = Memory::GetPointer(params.addr); | 969 | const u8* const texture_src_data = Memory::GetPointer(params.addr); |
| 930 | ASSERT(texture_src_data); | 970 | ASSERT(texture_src_data); |
| 931 | if (params.is_tiled) { | 971 | if (params.is_tiled) { |
| 932 | u32 depth = params.depth; | ||
| 933 | u32 block_depth = params.block_depth; | ||
| 934 | |||
| 935 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", | 972 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", |
| 936 | params.block_width, static_cast<u32>(params.target)); | 973 | params.block_width, static_cast<u32>(params.target)); |
| 937 | 974 | ||
| 938 | if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { | 975 | SwizzleFunc(gl_to_morton_fns, params, gl_buffer); |
| 939 | // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. | ||
| 940 | depth = 1U; | ||
| 941 | } | ||
| 942 | gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( | ||
| 943 | params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), | ||
| 944 | gl_buffer.size(), GetAddr()); | ||
| 945 | } else { | 976 | } else { |
| 946 | std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); | 977 | std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); |
| 947 | } | 978 | } |
| @@ -1179,7 +1210,7 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface, | |||
| 1179 | const Surface& dst_surface) { | 1210 | const Surface& dst_surface) { |
| 1180 | const auto& src_params{src_surface->GetSurfaceParams()}; | 1211 | const auto& src_params{src_surface->GetSurfaceParams()}; |
| 1181 | const auto& dst_params{dst_surface->GetSurfaceParams()}; | 1212 | const auto& dst_params{dst_surface->GetSurfaceParams()}; |
| 1182 | FlushRegion(src_params.addr, dst_params.size_in_bytes); | 1213 | FlushRegion(src_params.addr, dst_params.MemorySize()); |
| 1183 | LoadSurface(dst_surface); | 1214 | LoadSurface(dst_surface); |
| 1184 | } | 1215 | } |
| 1185 | 1216 | ||
| @@ -1221,44 +1252,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, | |||
| 1221 | CopySurface(old_surface, new_surface, copy_pbo.handle); | 1252 | CopySurface(old_surface, new_surface, copy_pbo.handle); |
| 1222 | } | 1253 | } |
| 1223 | break; | 1254 | break; |
| 1255 | case SurfaceParams::SurfaceTarget::TextureCubemap: | ||
| 1224 | case SurfaceParams::SurfaceTarget::Texture3D: | 1256 | case SurfaceParams::SurfaceTarget::Texture3D: |
| 1225 | AccurateCopySurface(old_surface, new_surface); | 1257 | AccurateCopySurface(old_surface, new_surface); |
| 1226 | break; | 1258 | break; |
| 1227 | case SurfaceParams::SurfaceTarget::TextureCubemap: { | ||
| 1228 | if (old_params.rt.array_mode != 1) { | ||
| 1229 | // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this | ||
| 1230 | // yet (array rendering used as a cubemap texture). | ||
| 1231 | LOG_CRITICAL(HW_GPU, "Unhandled rendertarget array_mode {}", old_params.rt.array_mode); | ||
| 1232 | UNREACHABLE(); | ||
| 1233 | return new_surface; | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | // This seems to be used for render-to-cubemap texture | ||
| 1237 | ASSERT_MSG(old_params.target == SurfaceParams::SurfaceTarget::Texture2D, "Unexpected"); | ||
| 1238 | ASSERT_MSG(old_params.pixel_format == new_params.pixel_format, "Unexpected"); | ||
| 1239 | ASSERT_MSG(old_params.rt.base_layer == 0, "Unimplemented"); | ||
| 1240 | |||
| 1241 | // TODO(bunnei): Verify the below - this stride seems to be in 32-bit words, not pixels. | ||
| 1242 | // Tested with Splatoon 2, Super Mario Odyssey, and Breath of the Wild. | ||
| 1243 | const std::size_t byte_stride{old_params.rt.layer_stride * sizeof(u32)}; | ||
| 1244 | |||
| 1245 | for (std::size_t index = 0; index < new_params.depth; ++index) { | ||
| 1246 | Surface face_surface{TryGetReservedSurface(old_params)}; | ||
| 1247 | ASSERT_MSG(face_surface, "Unexpected"); | ||
| 1248 | |||
| 1249 | if (is_blit) { | ||
| 1250 | BlitSurface(face_surface, new_surface, read_framebuffer.handle, | ||
| 1251 | draw_framebuffer.handle, face_surface->GetSurfaceParams().rt.index, | ||
| 1252 | new_params.rt.index, index); | ||
| 1253 | } else { | ||
| 1254 | CopySurface(face_surface, new_surface, copy_pbo.handle, | ||
| 1255 | face_surface->GetSurfaceParams().rt.index, new_params.rt.index, index); | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | old_params.addr += byte_stride; | ||
| 1259 | } | ||
| 1260 | break; | ||
| 1261 | } | ||
| 1262 | default: | 1259 | default: |
| 1263 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | 1260 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", |
| 1264 | static_cast<u32>(new_params.target)); | 1261 | static_cast<u32>(new_params.target)); |
| @@ -1266,7 +1263,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, | |||
| 1266 | } | 1263 | } |
| 1267 | 1264 | ||
| 1268 | return new_surface; | 1265 | return new_surface; |
| 1269 | } | 1266 | } // namespace OpenGL |
| 1270 | 1267 | ||
| 1271 | Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { | 1268 | Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { |
| 1272 | return TryGet(addr); | 1269 | return TryGet(addr); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 0dd0d90a3..50a7ab47d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -168,6 +168,23 @@ struct SurfaceParams { | |||
| 168 | } | 168 | } |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | static bool SurfaceTargetIsLayered(SurfaceTarget target) { | ||
| 172 | switch (target) { | ||
| 173 | case SurfaceTarget::Texture1D: | ||
| 174 | case SurfaceTarget::Texture2D: | ||
| 175 | case SurfaceTarget::Texture3D: | ||
| 176 | return false; | ||
| 177 | case SurfaceTarget::Texture1DArray: | ||
| 178 | case SurfaceTarget::Texture2DArray: | ||
| 179 | case SurfaceTarget::TextureCubemap: | ||
| 180 | return true; | ||
| 181 | default: | ||
| 182 | LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); | ||
| 183 | UNREACHABLE(); | ||
| 184 | return false; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 171 | /** | 188 | /** |
| 172 | * Gets the compression factor for the specified PixelFormat. This applies to just the | 189 | * Gets the compression factor for the specified PixelFormat. This applies to just the |
| 173 | * "compressed width" and "compressed height", not the overall compression factor of a | 190 | * "compressed width" and "compressed height", not the overall compression factor of a |
| @@ -742,6 +759,25 @@ struct SurfaceParams { | |||
| 742 | return size_in_bytes_gl / 6; | 759 | return size_in_bytes_gl / 6; |
| 743 | } | 760 | } |
| 744 | 761 | ||
| 762 | /// Returns the exact size of memory occupied by the texture in VRAM, including mipmaps. | ||
| 763 | std::size_t MemorySize() const { | ||
| 764 | std::size_t size = InnerMemorySize(is_layered); | ||
| 765 | if (is_layered) | ||
| 766 | return size * depth; | ||
| 767 | return size; | ||
| 768 | } | ||
| 769 | |||
| 770 | /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including | ||
| 771 | /// mipmaps. | ||
| 772 | std::size_t LayerMemorySize() const { | ||
| 773 | return InnerMemorySize(true); | ||
| 774 | } | ||
| 775 | |||
| 776 | /// Returns the size of a layer of this surface in OpenGL. | ||
| 777 | std::size_t LayerSizeGL() const { | ||
| 778 | return SizeInBytesRaw(true) / depth; | ||
| 779 | } | ||
| 780 | |||
| 745 | /// Creates SurfaceParams from a texture configuration | 781 | /// Creates SurfaceParams from a texture configuration |
| 746 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, | 782 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, |
| 747 | const GLShader::SamplerEntry& entry); | 783 | const GLShader::SamplerEntry& entry); |
| @@ -782,6 +818,7 @@ struct SurfaceParams { | |||
| 782 | u32 unaligned_height; | 818 | u32 unaligned_height; |
| 783 | SurfaceTarget target; | 819 | SurfaceTarget target; |
| 784 | u32 max_mip_level; | 820 | u32 max_mip_level; |
| 821 | bool is_layered; | ||
| 785 | 822 | ||
| 786 | // Parameters used for caching | 823 | // Parameters used for caching |
| 787 | VAddr addr; | 824 | VAddr addr; |
| @@ -797,6 +834,9 @@ struct SurfaceParams { | |||
| 797 | u32 layer_stride; | 834 | u32 layer_stride; |
| 798 | u32 base_layer; | 835 | u32 base_layer; |
| 799 | } rt; | 836 | } rt; |
| 837 | |||
| 838 | private: | ||
| 839 | std::size_t InnerMemorySize(bool layer_only = false) const; | ||
| 800 | }; | 840 | }; |
| 801 | 841 | ||
| 802 | }; // namespace OpenGL | 842 | }; // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 1fe26a2a9..ba6c6919a 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -21,6 +21,8 @@ OpenGLState::OpenGLState() { | |||
| 21 | depth.test_enabled = false; | 21 | depth.test_enabled = false; |
| 22 | depth.test_func = GL_LESS; | 22 | depth.test_func = GL_LESS; |
| 23 | depth.write_mask = GL_TRUE; | 23 | depth.write_mask = GL_TRUE; |
| 24 | depth.depth_range_near = 0.0f; | ||
| 25 | depth.depth_range_far = 1.0f; | ||
| 24 | 26 | ||
| 25 | color_mask.red_enabled = GL_TRUE; | 27 | color_mask.red_enabled = GL_TRUE; |
| 26 | color_mask.green_enabled = GL_TRUE; | 28 | color_mask.green_enabled = GL_TRUE; |
| @@ -119,6 +121,12 @@ void OpenGLState::Apply() const { | |||
| 119 | glDepthMask(depth.write_mask); | 121 | glDepthMask(depth.write_mask); |
| 120 | } | 122 | } |
| 121 | 123 | ||
| 124 | // Depth range | ||
| 125 | if (depth.depth_range_near != cur_state.depth.depth_range_near || | ||
| 126 | depth.depth_range_far != cur_state.depth.depth_range_far) { | ||
| 127 | glDepthRange(depth.depth_range_near, depth.depth_range_far); | ||
| 128 | } | ||
| 129 | |||
| 122 | // Color mask | 130 | // Color mask |
| 123 | if (color_mask.red_enabled != cur_state.color_mask.red_enabled || | 131 | if (color_mask.red_enabled != cur_state.color_mask.red_enabled || |
| 124 | color_mask.green_enabled != cur_state.color_mask.green_enabled || | 132 | color_mask.green_enabled != cur_state.color_mask.green_enabled || |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index dc21a2ee3..daf7eb533 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -42,9 +42,11 @@ public: | |||
| 42 | } cull; | 42 | } cull; |
| 43 | 43 | ||
| 44 | struct { | 44 | struct { |
| 45 | bool test_enabled; // GL_DEPTH_TEST | 45 | bool test_enabled; // GL_DEPTH_TEST |
| 46 | GLenum test_func; // GL_DEPTH_FUNC | 46 | GLenum test_func; // GL_DEPTH_FUNC |
| 47 | GLboolean write_mask; // GL_DEPTH_WRITEMASK | 47 | GLboolean write_mask; // GL_DEPTH_WRITEMASK |
| 48 | GLfloat depth_range_near; // GL_DEPTH_RANGE | ||
| 49 | GLfloat depth_range_far; // GL_DEPTH_RANGE | ||
| 48 | } depth; | 50 | } depth; |
| 49 | 51 | ||
| 50 | struct { | 52 | struct { |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index da7989db9..550ca856c 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -319,13 +319,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat | |||
| 319 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, | 319 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, |
| 320 | u32 block_height, u32 block_depth) { | 320 | u32 block_height, u32 block_depth) { |
| 321 | if (tiled) { | 321 | if (tiled) { |
| 322 | const u32 gobs_in_x = 64 / bytes_per_pixel; | 322 | const u32 gobs_in_x = 64; |
| 323 | const u32 gobs_in_y = 8; | 323 | const u32 gobs_in_y = 8; |
| 324 | const u32 gobs_in_z = 1; | 324 | const u32 gobs_in_z = 1; |
| 325 | const u32 aligned_width = Common::AlignUp(width, gobs_in_x); | 325 | const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x); |
| 326 | const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); | 326 | const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); |
| 327 | const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); | 327 | const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); |
| 328 | return aligned_width * aligned_height * aligned_depth * bytes_per_pixel; | 328 | return aligned_width * aligned_height * aligned_depth; |
| 329 | } else { | 329 | } else { |
| 330 | return width * height * depth * bytes_per_pixel; | 330 | return width * height * depth * bytes_per_pixel; |
| 331 | } | 331 | } |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 1fe9a7edd..d4fd60a73 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -13,11 +13,16 @@ Config::Config() { | |||
| 13 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | 13 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. |
| 14 | qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; | 14 | qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; |
| 15 | FileUtil::CreateFullPath(qt_config_loc); | 15 | FileUtil::CreateFullPath(qt_config_loc); |
| 16 | qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); | 16 | qt_config = |
| 17 | std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); | ||
| 17 | 18 | ||
| 18 | Reload(); | 19 | Reload(); |
| 19 | } | 20 | } |
| 20 | 21 | ||
| 22 | Config::~Config() { | ||
| 23 | Save(); | ||
| 24 | } | ||
| 25 | |||
| 21 | const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { | 26 | const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { |
| 22 | Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, | 27 | Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, |
| 23 | Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, | 28 | Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, |
| @@ -342,9 +347,3 @@ void Config::Reload() { | |||
| 342 | void Config::Save() { | 347 | void Config::Save() { |
| 343 | SaveValues(); | 348 | SaveValues(); |
| 344 | } | 349 | } |
| 345 | |||
| 346 | Config::~Config() { | ||
| 347 | Save(); | ||
| 348 | |||
| 349 | delete qt_config; | ||
| 350 | } | ||
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index cbf745ea2..9c99c1b75 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <memory> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <QVariant> | 10 | #include <QVariant> |
| 10 | #include "core/settings.h" | 11 | #include "core/settings.h" |
| @@ -12,12 +13,6 @@ | |||
| 12 | class QSettings; | 13 | class QSettings; |
| 13 | 14 | ||
| 14 | class Config { | 15 | class Config { |
| 15 | QSettings* qt_config; | ||
| 16 | std::string qt_config_loc; | ||
| 17 | |||
| 18 | void ReadValues(); | ||
| 19 | void SaveValues(); | ||
| 20 | |||
| 21 | public: | 16 | public: |
| 22 | Config(); | 17 | Config(); |
| 23 | ~Config(); | 18 | ~Config(); |
| @@ -27,4 +22,11 @@ public: | |||
| 27 | 22 | ||
| 28 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; | 23 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; |
| 29 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; | 24 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; |
| 25 | |||
| 26 | private: | ||
| 27 | void ReadValues(); | ||
| 28 | void SaveValues(); | ||
| 29 | |||
| 30 | std::unique_ptr<QSettings> qt_config; | ||
| 31 | std::string qt_config_loc; | ||
| 30 | }; | 32 | }; |
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 83cc49dfc..20ffb0a9a 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp | |||
| @@ -21,12 +21,8 @@ | |||
| 21 | #include "yuzu/configuration/configure_system.h" | 21 | #include "yuzu/configuration/configure_system.h" |
| 22 | #include "yuzu/main.h" | 22 | #include "yuzu/main.h" |
| 23 | 23 | ||
| 24 | static std::string GetImagePath(Service::Account::UUID uuid) { | 24 | namespace { |
| 25 | return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | 25 | constexpr std::array<int, 12> days_in_month = {{ |
| 26 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | ||
| 27 | } | ||
| 28 | |||
| 29 | static const std::array<int, 12> days_in_month = {{ | ||
| 30 | 31, | 26 | 31, |
| 31 | 29, | 27 | 29, |
| 32 | 31, | 28 | 31, |
| @@ -42,7 +38,7 @@ static const std::array<int, 12> days_in_month = {{ | |||
| 42 | }}; | 38 | }}; |
| 43 | 39 | ||
| 44 | // Same backup JPEG used by acc IProfile::GetImage if no jpeg found | 40 | // Same backup JPEG used by acc IProfile::GetImage if no jpeg found |
| 45 | static constexpr std::array<u8, 107> backup_jpeg{ | 41 | constexpr std::array<u8, 107> backup_jpeg{ |
| 46 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, | 42 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, |
| 47 | 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, | 43 | 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, |
| 48 | 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, | 44 | 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, |
| @@ -52,15 +48,32 @@ static constexpr std::array<u8, 107> backup_jpeg{ | |||
| 52 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | 48 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, |
| 53 | }; | 49 | }; |
| 54 | 50 | ||
| 51 | std::string GetImagePath(Service::Account::UUID uuid) { | ||
| 52 | return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 53 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | ||
| 54 | } | ||
| 55 | |||
| 56 | std::string GetAccountUsername(const Service::Account::ProfileManager& manager, | ||
| 57 | Service::Account::UUID uuid) { | ||
| 58 | Service::Account::ProfileBase profile; | ||
| 59 | if (!manager.GetProfileBase(uuid, profile)) { | ||
| 60 | return ""; | ||
| 61 | } | ||
| 62 | |||
| 63 | return Common::StringFromFixedZeroTerminatedBuffer( | ||
| 64 | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); | ||
| 65 | } | ||
| 66 | } // Anonymous namespace | ||
| 67 | |||
| 55 | ConfigureSystem::ConfigureSystem(QWidget* parent) | 68 | ConfigureSystem::ConfigureSystem(QWidget* parent) |
| 56 | : QWidget(parent), ui(new Ui::ConfigureSystem), | 69 | : QWidget(parent), ui(new Ui::ConfigureSystem), |
| 57 | profile_manager(std::make_unique<Service::Account::ProfileManager>()) { | 70 | profile_manager(std::make_unique<Service::Account::ProfileManager>()) { |
| 58 | ui->setupUi(this); | 71 | ui->setupUi(this); |
| 59 | connect(ui->combo_birthmonth, | 72 | connect(ui->combo_birthmonth, |
| 60 | static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | 73 | static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, |
| 61 | &ConfigureSystem::updateBirthdayComboBox); | 74 | &ConfigureSystem::UpdateBirthdayComboBox); |
| 62 | connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, | 75 | connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, |
| 63 | &ConfigureSystem::refreshConsoleID); | 76 | &ConfigureSystem::RefreshConsoleID); |
| 64 | 77 | ||
| 65 | layout = new QVBoxLayout; | 78 | layout = new QVBoxLayout; |
| 66 | tree_view = new QTreeView; | 79 | tree_view = new QTreeView; |
| @@ -153,8 +166,8 @@ void ConfigureSystem::UpdateCurrentUser() { | |||
| 153 | ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); | 166 | ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); |
| 154 | 167 | ||
| 155 | const auto& current_user = profile_manager->GetUser(Settings::values.current_user); | 168 | const auto& current_user = profile_manager->GetUser(Settings::values.current_user); |
| 156 | ASSERT(current_user != boost::none); | 169 | ASSERT(current_user != std::nullopt); |
| 157 | const auto username = GetAccountUsername(*current_user); | 170 | const auto username = GetAccountUsername(*profile_manager, *current_user); |
| 158 | 171 | ||
| 159 | scene->clear(); | 172 | scene->clear(); |
| 160 | scene->addPixmap( | 173 | scene->addPixmap( |
| @@ -164,14 +177,6 @@ void ConfigureSystem::UpdateCurrentUser() { | |||
| 164 | 177 | ||
| 165 | void ConfigureSystem::ReadSystemSettings() {} | 178 | void ConfigureSystem::ReadSystemSettings() {} |
| 166 | 179 | ||
| 167 | std::string ConfigureSystem::GetAccountUsername(Service::Account::UUID uuid) const { | ||
| 168 | Service::Account::ProfileBase profile; | ||
| 169 | if (!profile_manager->GetProfileBase(uuid, profile)) | ||
| 170 | return ""; | ||
| 171 | return Common::StringFromFixedZeroTerminatedBuffer( | ||
| 172 | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); | ||
| 173 | } | ||
| 174 | |||
| 175 | void ConfigureSystem::applyConfiguration() { | 180 | void ConfigureSystem::applyConfiguration() { |
| 176 | if (!enabled) | 181 | if (!enabled) |
| 177 | return; | 182 | return; |
| @@ -180,7 +185,7 @@ void ConfigureSystem::applyConfiguration() { | |||
| 180 | Settings::Apply(); | 185 | Settings::Apply(); |
| 181 | } | 186 | } |
| 182 | 187 | ||
| 183 | void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { | 188 | void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { |
| 184 | if (birthmonth_index < 0 || birthmonth_index >= 12) | 189 | if (birthmonth_index < 0 || birthmonth_index >= 12) |
| 185 | return; | 190 | return; |
| 186 | 191 | ||
| @@ -205,7 +210,7 @@ void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { | |||
| 205 | ui->combo_birthday->setCurrentIndex(birthday_index); | 210 | ui->combo_birthday->setCurrentIndex(birthday_index); |
| 206 | } | 211 | } |
| 207 | 212 | ||
| 208 | void ConfigureSystem::refreshConsoleID() { | 213 | void ConfigureSystem::RefreshConsoleID() { |
| 209 | QMessageBox::StandardButton reply; | 214 | QMessageBox::StandardButton reply; |
| 210 | QString warning_text = tr("This will replace your current virtual Switch with a new one. " | 215 | QString warning_text = tr("This will replace your current virtual Switch with a new one. " |
| 211 | "Your current virtual Switch will not be recoverable. " | 216 | "Your current virtual Switch will not be recoverable. " |
| @@ -232,8 +237,7 @@ void ConfigureSystem::SelectUser(const QModelIndex& index) { | |||
| 232 | } | 237 | } |
| 233 | 238 | ||
| 234 | void ConfigureSystem::AddUser() { | 239 | void ConfigureSystem::AddUser() { |
| 235 | Service::Account::UUID uuid; | 240 | const auto uuid = Service::Account::UUID::Generate(); |
| 236 | uuid.Generate(); | ||
| 237 | 241 | ||
| 238 | bool ok = false; | 242 | bool ok = false; |
| 239 | const auto username = | 243 | const auto username = |
| @@ -252,8 +256,8 @@ void ConfigureSystem::AddUser() { | |||
| 252 | void ConfigureSystem::RenameUser() { | 256 | void ConfigureSystem::RenameUser() { |
| 253 | const auto user = tree_view->currentIndex().row(); | 257 | const auto user = tree_view->currentIndex().row(); |
| 254 | const auto uuid = profile_manager->GetUser(user); | 258 | const auto uuid = profile_manager->GetUser(user); |
| 255 | ASSERT(uuid != boost::none); | 259 | ASSERT(uuid != std::nullopt); |
| 256 | const auto username = GetAccountUsername(*uuid); | 260 | const auto username = GetAccountUsername(*profile_manager, *uuid); |
| 257 | 261 | ||
| 258 | Service::Account::ProfileBase profile; | 262 | Service::Account::ProfileBase profile; |
| 259 | if (!profile_manager->GetProfileBase(*uuid, profile)) | 263 | if (!profile_manager->GetProfileBase(*uuid, profile)) |
| @@ -292,8 +296,8 @@ void ConfigureSystem::RenameUser() { | |||
| 292 | void ConfigureSystem::DeleteUser() { | 296 | void ConfigureSystem::DeleteUser() { |
| 293 | const auto index = tree_view->currentIndex().row(); | 297 | const auto index = tree_view->currentIndex().row(); |
| 294 | const auto uuid = profile_manager->GetUser(index); | 298 | const auto uuid = profile_manager->GetUser(index); |
| 295 | ASSERT(uuid != boost::none); | 299 | ASSERT(uuid != std::nullopt); |
| 296 | const auto username = GetAccountUsername(*uuid); | 300 | const auto username = GetAccountUsername(*profile_manager, *uuid); |
| 297 | 301 | ||
| 298 | const auto confirm = | 302 | const auto confirm = |
| 299 | QMessageBox::question(this, tr("Confirm Delete"), | 303 | QMessageBox::question(this, tr("Confirm Delete"), |
| @@ -320,11 +324,11 @@ void ConfigureSystem::DeleteUser() { | |||
| 320 | void ConfigureSystem::SetUserImage() { | 324 | void ConfigureSystem::SetUserImage() { |
| 321 | const auto index = tree_view->currentIndex().row(); | 325 | const auto index = tree_view->currentIndex().row(); |
| 322 | const auto uuid = profile_manager->GetUser(index); | 326 | const auto uuid = profile_manager->GetUser(index); |
| 323 | ASSERT(uuid != boost::none); | 327 | ASSERT(uuid != std::nullopt); |
| 324 | const auto username = GetAccountUsername(*uuid); | 328 | const auto username = GetAccountUsername(*profile_manager, *uuid); |
| 325 | 329 | ||
| 326 | const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), | 330 | const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), |
| 327 | "JPEG Images (*.jpg *.jpeg)"); | 331 | tr("JPEG Images (*.jpg *.jpeg)")); |
| 328 | 332 | ||
| 329 | if (file.isEmpty()) | 333 | if (file.isEmpty()) |
| 330 | return; | 334 | return; |
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index b73e0719c..07764e1f7 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h | |||
| @@ -9,17 +9,16 @@ | |||
| 9 | #include <QList> | 9 | #include <QList> |
| 10 | #include <QWidget> | 10 | #include <QWidget> |
| 11 | 11 | ||
| 12 | namespace Service::Account { | ||
| 13 | class ProfileManager; | ||
| 14 | struct UUID; | ||
| 15 | } // namespace Service::Account | ||
| 16 | |||
| 17 | class QGraphicsScene; | 12 | class QGraphicsScene; |
| 18 | class QStandardItem; | 13 | class QStandardItem; |
| 19 | class QStandardItemModel; | 14 | class QStandardItemModel; |
| 20 | class QTreeView; | 15 | class QTreeView; |
| 21 | class QVBoxLayout; | 16 | class QVBoxLayout; |
| 22 | 17 | ||
| 18 | namespace Service::Account { | ||
| 19 | class ProfileManager; | ||
| 20 | } | ||
| 21 | |||
| 23 | namespace Ui { | 22 | namespace Ui { |
| 24 | class ConfigureSystem; | 23 | class ConfigureSystem; |
| 25 | } | 24 | } |
| @@ -29,28 +28,25 @@ class ConfigureSystem : public QWidget { | |||
| 29 | 28 | ||
| 30 | public: | 29 | public: |
| 31 | explicit ConfigureSystem(QWidget* parent = nullptr); | 30 | explicit ConfigureSystem(QWidget* parent = nullptr); |
| 32 | ~ConfigureSystem(); | 31 | ~ConfigureSystem() override; |
| 33 | 32 | ||
| 34 | void applyConfiguration(); | 33 | void applyConfiguration(); |
| 35 | void setConfiguration(); | 34 | void setConfiguration(); |
| 36 | 35 | ||
| 37 | void PopulateUserList(); | 36 | private: |
| 38 | void UpdateCurrentUser(); | 37 | void ReadSystemSettings(); |
| 39 | 38 | ||
| 40 | public slots: | 39 | void UpdateBirthdayComboBox(int birthmonth_index); |
| 41 | void updateBirthdayComboBox(int birthmonth_index); | 40 | void RefreshConsoleID(); |
| 42 | void refreshConsoleID(); | ||
| 43 | 41 | ||
| 42 | void PopulateUserList(); | ||
| 43 | void UpdateCurrentUser(); | ||
| 44 | void SelectUser(const QModelIndex& index); | 44 | void SelectUser(const QModelIndex& index); |
| 45 | void AddUser(); | 45 | void AddUser(); |
| 46 | void RenameUser(); | 46 | void RenameUser(); |
| 47 | void DeleteUser(); | 47 | void DeleteUser(); |
| 48 | void SetUserImage(); | 48 | void SetUserImage(); |
| 49 | 49 | ||
| 50 | private: | ||
| 51 | void ReadSystemSettings(); | ||
| 52 | std::string GetAccountUsername(Service::Account::UUID uuid) const; | ||
| 53 | |||
| 54 | QVBoxLayout* layout; | 50 | QVBoxLayout* layout; |
| 55 | QTreeView* tree_view; | 51 | QTreeView* tree_view; |
| 56 | QStandardItemModel* item_model; | 52 | QStandardItemModel* item_model; |
| @@ -59,11 +55,12 @@ private: | |||
| 59 | std::vector<QList<QStandardItem*>> list_items; | 55 | std::vector<QList<QStandardItem*>> list_items; |
| 60 | 56 | ||
| 61 | std::unique_ptr<Ui::ConfigureSystem> ui; | 57 | std::unique_ptr<Ui::ConfigureSystem> ui; |
| 62 | bool enabled; | 58 | bool enabled = false; |
| 63 | 59 | ||
| 64 | int birthmonth, birthday; | 60 | int birthmonth = 0; |
| 65 | int language_index; | 61 | int birthday = 0; |
| 66 | int sound_index; | 62 | int language_index = 0; |
| 63 | int sound_index = 0; | ||
| 67 | 64 | ||
| 68 | std::unique_ptr<Service::Account::ProfileManager> profile_manager; | 65 | std::unique_ptr<Service::Account::ProfileManager> profile_manager; |
| 69 | }; | 66 | }; |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 3881aba5f..3d865a12d 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -62,19 +62,24 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, | |||
| 62 | FileSys::VirtualFile update_raw; | 62 | FileSys::VirtualFile update_raw; |
| 63 | loader.ReadUpdateRaw(update_raw); | 63 | loader.ReadUpdateRaw(update_raw); |
| 64 | for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { | 64 | for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { |
| 65 | if (!updatable && kv.first == "Update") | 65 | const bool is_update = kv.first == "Update"; |
| 66 | if (!updatable && is_update) { | ||
| 66 | continue; | 67 | continue; |
| 68 | } | ||
| 69 | |||
| 70 | const QString type = QString::fromStdString(kv.first); | ||
| 67 | 71 | ||
| 68 | if (kv.second.empty()) { | 72 | if (kv.second.empty()) { |
| 69 | out.append(fmt::format("{}\n", kv.first).c_str()); | 73 | out.append(QStringLiteral("%1\n").arg(type)); |
| 70 | } else { | 74 | } else { |
| 71 | auto ver = kv.second; | 75 | auto ver = kv.second; |
| 72 | 76 | ||
| 73 | // Display container name for packed updates | 77 | // Display container name for packed updates |
| 74 | if (ver == "PACKED" && kv.first == "Update") | 78 | if (is_update && ver == "PACKED") { |
| 75 | ver = Loader::GetFileTypeString(loader.GetFileType()); | 79 | ver = Loader::GetFileTypeString(loader.GetFileType()); |
| 80 | } | ||
| 76 | 81 | ||
| 77 | out.append(fmt::format("{} ({})\n", kv.first, ver).c_str()); | 82 | out.append(QStringLiteral("%1 (%2)\n").arg(type, QString::fromStdString(ver))); |
| 78 | } | 83 | } |
| 79 | } | 84 | } |
| 80 | 85 | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 47f494841..b5bfa6741 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -30,6 +30,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 30 | #define QT_NO_OPENGL | 30 | #define QT_NO_OPENGL |
| 31 | #include <QDesktopWidget> | 31 | #include <QDesktopWidget> |
| 32 | #include <QDialogButtonBox> | 32 | #include <QDialogButtonBox> |
| 33 | #include <QFile> | ||
| 33 | #include <QFileDialog> | 34 | #include <QFileDialog> |
| 34 | #include <QMessageBox> | 35 | #include <QMessageBox> |
| 35 | #include <QtConcurrent/QtConcurrent> | 36 | #include <QtConcurrent/QtConcurrent> |
| @@ -785,7 +786,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 785 | ASSERT(index != -1 && index < 8); | 786 | ASSERT(index != -1 && index < 8); |
| 786 | 787 | ||
| 787 | const auto user_id = manager.GetUser(index); | 788 | const auto user_id = manager.GetUser(index); |
| 788 | ASSERT(user_id != boost::none); | 789 | ASSERT(user_id != std::nullopt); |
| 789 | path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, | 790 | path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, |
| 790 | FileSys::SaveDataType::SaveData, | 791 | FileSys::SaveDataType::SaveData, |
| 791 | program_id, user_id->uuid, 0); | 792 | program_id, user_id->uuid, 0); |
| @@ -1336,20 +1337,40 @@ void GMainWindow::OnLoadAmiibo() { | |||
| 1336 | const QString extensions{"*.bin"}; | 1337 | const QString extensions{"*.bin"}; |
| 1337 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); | 1338 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); |
| 1338 | const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter); | 1339 | const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter); |
| 1339 | if (!filename.isEmpty()) { | 1340 | |
| 1340 | Core::System& system{Core::System::GetInstance()}; | 1341 | if (filename.isEmpty()) { |
| 1341 | Service::SM::ServiceManager& sm = system.ServiceManager(); | 1342 | return; |
| 1342 | auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); | 1343 | } |
| 1343 | if (nfc != nullptr) { | 1344 | |
| 1344 | auto nfc_file = FileUtil::IOFile(filename.toStdString(), "rb"); | 1345 | Core::System& system{Core::System::GetInstance()}; |
| 1345 | if (!nfc_file.IsOpen()) { | 1346 | Service::SM::ServiceManager& sm = system.ServiceManager(); |
| 1346 | return; | 1347 | auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); |
| 1347 | } | 1348 | if (nfc == nullptr) { |
| 1348 | std::vector<u8> amiibo_buffer(nfc_file.GetSize()); | 1349 | return; |
| 1349 | nfc_file.ReadBytes(amiibo_buffer.data(), amiibo_buffer.size()); | 1350 | } |
| 1350 | nfc_file.Close(); | 1351 | |
| 1351 | nfc->LoadAmiibo(amiibo_buffer); | 1352 | QFile nfc_file{filename}; |
| 1352 | } | 1353 | if (!nfc_file.open(QIODevice::ReadOnly)) { |
| 1354 | QMessageBox::warning(this, tr("Error opening Amiibo data file"), | ||
| 1355 | tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename)); | ||
| 1356 | return; | ||
| 1357 | } | ||
| 1358 | |||
| 1359 | const u64 nfc_file_size = nfc_file.size(); | ||
| 1360 | std::vector<u8> buffer(nfc_file_size); | ||
| 1361 | const u64 read_size = nfc_file.read(reinterpret_cast<char*>(buffer.data()), nfc_file_size); | ||
| 1362 | if (nfc_file_size != read_size) { | ||
| 1363 | QMessageBox::warning(this, tr("Error reading Amiibo data file"), | ||
| 1364 | tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but " | ||
| 1365 | "was only able to read %2 bytes.") | ||
| 1366 | .arg(nfc_file_size) | ||
| 1367 | .arg(read_size)); | ||
| 1368 | return; | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | if (!nfc->LoadAmiibo(buffer)) { | ||
| 1372 | QMessageBox::warning(this, tr("Error loading Amiibo data"), | ||
| 1373 | tr("Unable to load Amiibo data.")); | ||
| 1353 | } | 1374 | } |
| 1354 | } | 1375 | } |
| 1355 | 1376 | ||