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 { return file->GetContainingDirectory(); } -bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} - Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { if (partitions[static_cast<std::size_t>(part)] == nullptr) { 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: VirtualDir GetParentDirectory() const override; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: Loader::ResultStatus AddNCAFromPartition(XCIPartition part); 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 { return ivfc_offset; } -bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} } // 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: // Returns the base ivfc offset used in BKTR patching. u64 GetBaseIVFCOffset() const; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: bool CheckSupportedNCA(const NCAHeader& header); 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) { u16 rle_size{}; if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) return nullptr; - rle_size = Common::swap16(data_size); + rle_size = Common::swap16(rle_size); offset += sizeof(u16); 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 { } std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { - return pfs_dirs; + return {}; } std::string PartitionFilesystem::GetName() const { @@ -103,18 +103,4 @@ void PartitionFilesystem::PrintDebugInfo() const { pfs_files[i]->GetName(), pfs_files[i]->GetSize()); } } - -bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file); - if (iter == pfs_files.end()) - return false; - - const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter); - pfs_files[offset] = std::move(pfs_files.back()); - pfs_files.pop_back(); - - pfs_dirs.emplace_back(std::move(dir)); - - return true; -} } // 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: std::shared_ptr<VfsDirectory> GetParentDirectory() const override; void PrintDebugInfo() const; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: struct Header { u32_le magic; @@ -84,7 +81,6 @@ private: std::size_t content_offset = 0; std::vector<VirtualFile> pfs_files; - std::vector<VirtualDir> pfs_dirs; }; } // 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 { return file->GetContainingDirectory(); } -bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} - void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { exefs = pfs; 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: VirtualDir GetParentDirectory() const override; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files); 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: // item name -> type. virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const; - // Interprets the file with name file instead as a directory of type directory. - // The directory must have a constructor that takes a single argument of type - // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a - // subdirectory in one call. - template <typename Directory> - bool InterpretAsDirectory(std::string_view file) { - auto file_p = GetFile(file); - - if (file_p == nullptr) { - return false; - } - - return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p)); - } - - bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function, - const std::string& file) { - auto file_p = GetFile(file); - if (file_p == nullptr) - return false; - return ReplaceFileWithSubdirectory(file_p, function(file_p)); - } - // Returns the full path of this directory as a string, recursively virtual std::string GetFullPath() const; - -protected: - // Backend for InterpretAsDirectory. - // Removes all references to file and adds a reference to dir in the directory's implementation. - virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0; }; // 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_) { return true; } -bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} } // 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: bool DeleteFile(std::string_view name) override; bool Rename(std::string_view name) override; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: std::vector<VirtualDir> dirs; 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() return out; } -bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} } // 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: std::string GetFullPath() const override; std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); 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) { void VectorVfsDirectory::AddDirectory(VirtualDir dir) { dirs.push_back(std::move(dir)); } - -bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - if (!DeleteFile(file->GetName())) - return false; - dirs.emplace_back(std::move(dir)); - return true; -} } // 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: virtual void AddFile(VirtualFile file); virtual void AddDirectory(VirtualDir dir); -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: std::vector<VirtualFile> files; 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 { return file->GetContainingDirectory(); } -bool NAX::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} } // 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: std::shared_ptr<VfsDirectory> GetParentDirectory() const override; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: Loader::ResultStatus Parse(std::string_view path); 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 { /// Size of the command buffer area, in 32-bit words. constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); -// These errors are commonly returned by invalid IPC translations, so alias them here for -// convenience. -// TODO(yuriks): These will probably go away once translation is implemented inside the kernel. -constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS; - enum class ControlCommand : u32 { ConvertSessionToDomain = 0, 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 { namespace ErrCodes { enum { - // TODO(Subv): Remove these 3DS OS error codes. - SessionClosedByRemote = 26, - NoPendingSessions = 35, - InvalidBufferDescriptor = 48, - // Confirmed Switch OS error codes MaxConnectionsReached = 7, InvalidSize = 101, @@ -26,6 +21,7 @@ enum { InvalidThreadPriority = 112, InvalidProcessorId = 113, InvalidHandle = 114, + InvalidPointer = 115, InvalidCombination = 116, Timeout = 117, SynchronizationCanceled = 118, @@ -33,6 +29,7 @@ enum { InvalidEnumValue = 120, NoSuchEntry = 121, AlreadyRegistered = 122, + SessionClosed = 123, InvalidState = 125, ResourceLimitExceeded = 132, }; @@ -41,18 +38,14 @@ enum { // WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always // double check that the code matches before re-using the constant. -// TODO(bunnei): Replace -1 with correct errors for Switch OS constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); -constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); +constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed); constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, ErrCodes::MaxConnectionsReached); constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); -constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1); -constexpr ResultCode ERR_INVALID_COMBINATION(-1); constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, ErrCodes::InvalidCombination); -constexpr ResultCode ERR_OUT_OF_MEMORY(-1); constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, @@ -65,14 +58,8 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::Alrea constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, ErrCodes::InvalidThreadPriority); -constexpr ResultCode ERR_INVALID_POINTER(-1); -constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); -constexpr ResultCode ERR_NOT_AUTHORIZED(-1); -/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. -constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); +constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer); constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); -/// Returned when Accept() is called on a port with no sessions to be accepted. -constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); } // 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) { MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); + + // Clear instruction cache in CPU JIT + Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); } 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; ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { if (pending_sessions.empty()) { - return ERR_NO_PENDING_SESSIONS; + return ERR_NOT_FOUND; } auto session = std::move(pending_sessions.back()); @@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { bool ServerPort::ShouldWait(Thread* thread) const { // If there are no pending sessions, we wait until a new one is added. - return pending_sessions.size() == 0; + return pending_sessions.empty(); } 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( ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, MemoryPermission other_permissions) { - - MemoryPermission own_other_permissions = + const MemoryPermission own_other_permissions = target_process == owner_process ? this->permissions : this->other_permissions; // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { - return ERR_INVALID_COMBINATION; + return ERR_INVALID_MEMORY_PERMISSIONS; } // Error out if the requested permissions don't match what the creator process allows. if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", GetObjectId(), address, name); - return ERR_INVALID_COMBINATION; + return ERR_INVALID_MEMORY_PERMISSIONS; } // 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) { } const auto* const current_process = Core::CurrentProcess(); - SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); - if (!thread) { - return ERR_INVALID_HANDLE; - } // Note: The kernel uses the current process's resource limit instead of // the one from the thread owner's resource limit. const ResourceLimit& resource_limit = current_process->GetResourceLimit(); if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { - return ERR_NOT_AUTHORIZED; + return ERR_INVALID_THREAD_PRIORITY; + } + + SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); + if (!thread) { + return ERR_INVALID_HANDLE; } thread->SetPriority(priority); @@ -745,7 +746,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V auto* const current_process = Core::CurrentProcess(); const ResourceLimit& resource_limit = current_process->GetResourceLimit(); if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { - return ERR_NOT_AUTHORIZED; + return ERR_INVALID_THREAD_PRIORITY; } 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 return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); } +ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const { + // Find the first Free VMA. + const VAddr base = GetASLRRegionBaseAddress(); + const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { + if (vma.second.type != VMAType::Free) + return false; + + const VAddr vma_end = vma.second.base + vma.second.size; + return vma_end > base && vma_end >= base + size; + }); + + if (vma_handle == vma_map.end()) { + // TODO(Subv): Find the correct error code here. + return ResultCode(-1); + } + + const VAddr target = std::max(base, vma_handle->second.base); + return MakeResult<VAddr>(target); +} + ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, 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: ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); /** + * Finds the first free address that can hold a region of the desired size. + * + * @param size Size of the desired region. + * @return The found free address. + */ + ResultVal<VAddr> FindFreeRegion(u64 size) const; + + /** * Maps a memory-mapped IO region at a given address. * * @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 @@ namespace Service::Account { -constexpr u32 MAX_JPEG_IMAGE_SIZE = 0x20000; - // TODO: RE this structure struct UserData { INSERT_PADDING_WORDS(1); @@ -34,11 +32,29 @@ struct UserData { }; static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); +// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg +// used as a backup should the one on disk not exist +constexpr u32 backup_jpeg_size = 107; +constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{ + 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, + 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, + 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, + 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, + 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, + 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, +}}; + static std::string GetImagePath(UUID uuid) { return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; } +static constexpr u32 SanitizeJPEGSize(std::size_t size) { + constexpr std::size_t max_jpeg_image_size = 0x20000; + return static_cast<u32>(std::min(size, max_jpeg_image_size)); +} + class IProfile final : public ServiceFramework<IProfile> { public: explicit IProfile(UUID user_id, ProfileManager& profile_manager) @@ -86,43 +102,29 @@ private: void LoadImage(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); - // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg - // used as a backup should the one on disk not exist - constexpr u32 backup_jpeg_size = 107; - static constexpr std::array<u8, backup_jpeg_size> backup_jpeg{ - 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, - 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, - 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, - 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, - 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00, - 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, - 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, - }; IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); const FileUtil::IOFile image(GetImagePath(user_id), "rb"); - if (!image.IsOpen()) { LOG_WARNING(Service_ACC, "Failed to load user provided image! Falling back to built-in backup..."); ctx.WriteBuffer(backup_jpeg); rb.Push<u32>(backup_jpeg_size); - } else { - const auto size = std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE); - std::vector<u8> buffer(size); - image.ReadBytes(buffer.data(), buffer.size()); - - ctx.WriteBuffer(buffer.data(), buffer.size()); - rb.Push<u32>(buffer.size()); + return; } + + const u32 size = SanitizeJPEGSize(image.GetSize()); + std::vector<u8> buffer(size); + image.ReadBytes(buffer.data(), buffer.size()); + + ctx.WriteBuffer(buffer.data(), buffer.size()); + rb.Push<u32>(size); } void GetImageSize(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); - constexpr u32 backup_jpeg_size = 107; IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -133,7 +135,7 @@ private: "Failed to load user provided image! Falling back to built-in backup..."); rb.Push<u32>(backup_jpeg_size); } else { - rb.Push<u32>(std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE)); + rb.Push<u32>(SanitizeJPEGSize(image.GetSize())); } } 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 @@ // Refer to the license.txt file included. #include <random> -#include <boost/optional.hpp> + #include "common/file_util.h" #include "core/hle/service/acc/profile_manager.h" #include "core/settings.h" @@ -58,11 +58,11 @@ ProfileManager::~ProfileManager() { /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the /// internal management of the users profiles -boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { +std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { if (user_count >= MAX_USERS) { - return boost::none; + return {}; } - profiles[user_count] = user; + profiles[user_count] = profile; return user_count++; } @@ -81,7 +81,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { /// Helper function to register a user to the system ResultCode ProfileManager::AddUser(const ProfileInfo& user) { - if (AddToProfiles(user) == boost::none) { + if (!AddToProfiles(user)) { return ERROR_TOO_MANY_USERS; } return RESULT_SUCCESS; @@ -126,37 +126,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) return CreateNewUser(uuid, username_output); } -boost::optional<UUID> ProfileManager::GetUser(std::size_t index) const { - if (index >= MAX_USERS) - return boost::none; +std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { + if (index >= MAX_USERS) { + return {}; + } + return profiles[index].user_uuid; } /// Returns a users profile index based on their user id. -boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { +std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { if (!uuid) { - return boost::none; + return {}; } - auto iter = std::find_if(profiles.begin(), profiles.end(), - [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); + + const auto iter = std::find_if(profiles.begin(), profiles.end(), + [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); if (iter == profiles.end()) { - return boost::none; + return {}; } + return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); } /// Returns a users profile index based on their profile -boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { +std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { return GetUserIndex(user.user_uuid); } /// Returns the data structure used by the switch when GetProfileBase is called on acc:* -bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, - ProfileBase& profile) const { - if (index == boost::none || index >= MAX_USERS) { +bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const { + if (!index || index >= MAX_USERS) { return false; } - const auto& prof_info = profiles[index.get()]; + const auto& prof_info = profiles[*index]; profile.user_uuid = prof_info.user_uuid; profile.username = prof_info.username; profile.timestamp = prof_info.creation_time; @@ -165,7 +168,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, /// Returns the data structure used by the switch when GetProfileBase is called on acc:* bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { - auto idx = GetUserIndex(uuid); + const auto idx = GetUserIndex(uuid); return GetProfileBase(idx, profile); } @@ -192,7 +195,7 @@ std::size_t ProfileManager::GetOpenUserCount() const { /// Checks if a user id exists in our profile manager bool ProfileManager::UserExists(UUID uuid) const { - return (GetUserIndex(uuid) != boost::none); + return GetUserIndex(uuid) != std::nullopt; } bool ProfileManager::UserExistsIndex(std::size_t index) const { @@ -203,21 +206,23 @@ bool ProfileManager::UserExistsIndex(std::size_t index) const { /// Opens a specific user void ProfileManager::OpenUser(UUID uuid) { - auto idx = GetUserIndex(uuid); - if (idx == boost::none) { + const auto idx = GetUserIndex(uuid); + if (!idx) { return; } - profiles[idx.get()].is_open = true; + + profiles[*idx].is_open = true; last_opened_user = uuid; } /// Closes a specific user void ProfileManager::CloseUser(UUID uuid) { - auto idx = GetUserIndex(uuid); - if (idx == boost::none) { + const auto idx = GetUserIndex(uuid); + if (!idx) { return; } - profiles[idx.get()].is_open = false; + + profiles[*idx].is_open = false; } /// Gets all valid user ids on the system @@ -247,10 +252,10 @@ UUID ProfileManager::GetLastOpenedUser() const { } /// Return the users profile base and the unknown arbitary data. -bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, +bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, ProfileData& data) const { if (GetProfileBase(index, profile)) { - data = profiles[index.get()].data; + data = profiles[*index].data; return true; } return false; @@ -259,7 +264,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const { - auto idx = GetUserIndex(uuid); + const auto idx = GetUserIndex(uuid); return GetProfileBaseAndData(idx, profile, data); } @@ -277,8 +282,8 @@ bool ProfileManager::CanSystemRegisterUser() const { } bool ProfileManager::RemoveUser(UUID uuid) { - auto index = GetUserIndex(uuid); - if (index == boost::none) { + const auto index = GetUserIndex(uuid); + if (!index) { return false; } @@ -289,8 +294,8 @@ bool ProfileManager::RemoveUser(UUID uuid) { } bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { - auto index = GetUserIndex(uuid); - if (profile_new.user_uuid == UUID(INVALID_UUID) || index == boost::none) { + const auto index = GetUserIndex(uuid); + if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) { return false; } 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 @@ #pragma once #include <array> +#include <optional> -#include "boost/optional.hpp" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/result.h" @@ -96,13 +96,13 @@ public: ResultCode AddUser(const ProfileInfo& user); ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); ResultCode CreateNewUser(UUID uuid, const std::string& username); - boost::optional<UUID> GetUser(std::size_t index) const; - boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; - boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; - bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; + std::optional<UUID> GetUser(std::size_t index) const; + std::optional<std::size_t> GetUserIndex(const UUID& uuid) const; + std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; + bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const; bool GetProfileBase(UUID uuid, ProfileBase& profile) const; bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; - bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, + bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, ProfileData& data) const; bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, @@ -120,16 +120,16 @@ public: bool CanSystemRegisterUser() const; bool RemoveUser(UUID uuid); - bool SetProfileBase(UUID uuid, const ProfileBase& profile); + bool SetProfileBase(UUID uuid, const ProfileBase& profile_new); private: void ParseUserSaveFile(); void WriteUserSaveFile(); + std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); + bool RemoveProfileAtIndex(std::size_t index); std::array<ProfileInfo, MAX_USERS> profiles{}; std::size_t user_count = 0; - boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); - bool RemoveProfileAtIndex(std::size_t index); UUID last_opened_user{INVALID_UUID}; }; 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) { Account::ProfileManager profile_manager{}; const auto uuid = profile_manager.GetUser(Settings::values.current_user); - ASSERT(uuid != boost::none); + ASSERT(uuid != std::nullopt); params.current_user = uuid->uuid; 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 @@ // Refer to the license.txt file included. #include <memory> +#include <fmt/format.h> +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/ldr/ldr.h" #include "core/hle/service/service.h" +#include "core/loader/nro.h" namespace Service::LDR { @@ -59,16 +63,58 @@ public: explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "LoadNro"}, + {0, &RelocatableObject::LoadNro, "LoadNro"}, {1, nullptr, "UnloadNro"}, - {2, nullptr, "LoadNrr"}, + {2, &RelocatableObject::LoadNrr, "LoadNrr"}, {3, nullptr, "UnloadNrr"}, - {4, nullptr, "Initialize"}, + {4, &RelocatableObject::Initialize, "Initialize"}, }; // clang-format on RegisterHandlers(functions); } + + void LoadNrr(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_LDR, "(STUBBED) called"); + } + + void LoadNro(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + rp.Skip(2, false); + const VAddr nro_addr{rp.Pop<VAddr>()}; + const u64 nro_size{rp.Pop<u64>()}; + const VAddr bss_addr{rp.Pop<VAddr>()}; + const u64 bss_size{rp.Pop<u64>()}; + + // Read NRO data from memory + std::vector<u8> nro_data(nro_size); + Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); + + // Load NRO as new executable module + const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; + Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); + + // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. + // It is currently missing: + // - Signature checks with LoadNRR + // - Checking if a module has already been loaded + // - Using/validating BSS, etc. params (these are used from NRO header instead) + // - Error checking + // - ...Probably other things + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(addr); + LOG_WARNING(Service_LDR, "(STUBBED) called"); + } + + void Initialize(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_LDR, "(STUBBED) called"); + } }; 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) { rb.PushIpcInterface<IUser>(*this); } -void Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { +bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); if (buffer.size() < sizeof(AmiiboFile)) { - return; // Failed to load file + return false; } + std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); nfc_tag_load->Signal(); + return true; } const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const { 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: static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); void CreateUserInterface(Kernel::HLERequestContext& ctx); - void LoadAmiibo(const std::vector<u8>& buffer); + bool LoadAmiibo(const std::vector<u8>& buffer); const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const; const AmiiboFile& GetAmiiboBuffer() const; 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: explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Unknown1"}, - {1, nullptr, "Unknown2"}, - {2, nullptr, "Unknown3"}, - {3, nullptr, "Unknown4"}, + {0, nullptr, "Open"}, + {1, nullptr, "Close"}, + {2, nullptr, "Unknown1"}, + {3, nullptr, "Populate"}, {4, nullptr, "PostBufferAsync"}, - {5, nullptr, "Unknown5"}, - {6, nullptr, "Unknown6"}, - {7, nullptr, "Unknown7"}, - {8, nullptr, "Unknown8"}, + {5, nullptr, "GetXferReport"}, + {6, nullptr, "Unknown2"}, + {7, nullptr, "Unknown3"}, + {8, nullptr, "Unknown4"}, }; // clang-format on @@ -89,15 +89,15 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "Unknown1"}, - {1, nullptr, "Unknown2"}, - {2, nullptr, "Unknown3"}, - {3, nullptr, "Unknown4"}, - {4, nullptr, "Unknown5"}, + {1, nullptr, "SetInterface"}, + {2, nullptr, "GetInterface"}, + {3, nullptr, "GetAlternateInterface"}, + {4, nullptr, "GetCurrentFrame"}, {5, nullptr, "CtrlXferAsync"}, - {6, nullptr, "Unknown6"}, + {6, nullptr, "Unknown2"}, {7, nullptr, "GetCtrlXferReport"}, - {8, nullptr, "Unknown7"}, - {9, nullptr, "GetClientEpSession"}, + {8, nullptr, "ResetDevice"}, + {9, nullptr, "OpenUsbEp"}, }; // clang-format on @@ -111,13 +111,14 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "BindClientProcess"}, - {1, nullptr, "Unknown1"}, - {2, nullptr, "Unknown2"}, - {3, nullptr, "Unknown3"}, - {4, nullptr, "Unknown4"}, - {5, nullptr, "Unknown5"}, + {1, nullptr, "QueryAllInterfaces"}, + {2, nullptr, "QueryAvailableInterfaces"}, + {3, nullptr, "QueryAcquiredInterfaces"}, + {4, nullptr, "CreateInterfaceAvailableEvent"}, + {5, nullptr, "DestroyInterfaceAvailableEvent"}, {6, nullptr, "GetInterfaceStateChangeEvent"}, - {7, nullptr, "GetClientIfSession"}, + {7, nullptr, "AcquireUsbIf"}, + {8, nullptr, "Unknown1"}, }; // clang-format on 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) { return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; } -bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { - // Read NSO header - NroHeader nro_header{}; - if (sizeof(NroHeader) != file.ReadObject(&nro_header)) { +/*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name, + VAddr load_base) { + + if (data.size() < sizeof(NroHeader)) { return {}; } + + // Read NSO header + NroHeader nro_header{}; + std::memcpy(&nro_header, data.data(), sizeof(NroHeader)); if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { return {}; } // Build program image - std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size)); + std::vector<u8> program_image(PageAlignSize(nro_header.file_size)); + std::memcpy(program_image.data(), data.data(), program_image.size()); if (program_image.size() != PageAlignSize(nro_header.file_size)) { return {}; } @@ -182,11 +187,15 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); // Register module with GDBStub - GDBStub::RegisterModule(file.GetName(), load_base, load_base); + GDBStub::RegisterModule(name, load_base, load_base); return true; } +bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { + return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base); +} + ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { if (is_loaded) { 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 @@ #pragma once #include <string> +#include <vector> #include "common/common_types.h" #include "core/loader/linker.h" #include "core/loader/loader.h" @@ -40,6 +41,8 @@ public: ResultStatus ReadTitle(std::string& title) override; bool IsRomFSUpdatable() const override; + static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base); + private: bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); 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 @@ // Refer to the license.txt file included. #include <cinttypes> +#include <cstring> #include "common/assert.h" #include "core/core.h" #include "core/core_timing.h" @@ -19,7 +20,24 @@ namespace Tegra::Engines { constexpr u32 MacroRegistersStart = 0xE00; Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) - : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {} + : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) { + InitializeRegisterDefaults(); +} + +void Maxwell3D::InitializeRegisterDefaults() { + // Initializes registers to their default values - what games expect them to be at boot. This is + // for certain registers that may not be explicitly set by games. + + // Reset all registers to zero + std::memset(®s, 0, sizeof(regs)); + + // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is + // needed for ARMS. + for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { + regs.viewport[viewport].depth_range_near = 0.0f; + regs.viewport[viewport].depth_range_far = 1.0f; + } +} void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { // 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: Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; private: + void InitializeRegisterDefaults(); + VideoCore::RasterizerInterface& rasterizer; 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() { SyncBlendState(); SyncLogicOpState(); SyncCullMode(); + SyncDepthRange(); SyncScissorTest(); // Alpha Testing is synced on shaders. SyncTransformFeedback(); @@ -923,12 +924,11 @@ void RasterizerOpenGL::SyncCullMode() { } } -void RasterizerOpenGL::SyncDepthScale() { - UNREACHABLE(); -} +void RasterizerOpenGL::SyncDepthRange() { + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; -void RasterizerOpenGL::SyncDepthOffset() { - UNREACHABLE(); + state.depth.depth_range_near = regs.viewport->depth_range_near; + state.depth.depth_range_far = regs.viewport->depth_range_far; } 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: /// Syncs the cull mode to match the guest state void SyncCullMode(); - /// Syncs the depth scale to match the guest state - void SyncDepthScale(); - - /// Syncs the depth offset to match the guest state - void SyncDepthOffset(); + /// Syncs the depth range to match the guest state + void SyncDepthRange(); /// Syncs the depth test state to match the guest state 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_) { } } +std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { + const u32 compression_factor{GetCompressionFactor(pixel_format)}; + const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; + u32 m_depth = (layer_only ? 1U : depth); + u32 m_width = std::max(1U, width / compression_factor); + u32 m_height = std::max(1U, height / compression_factor); + std::size_t size = Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, + m_depth, block_height, block_depth); + u32 m_block_height = block_height; + u32 m_block_depth = block_depth; + std::size_t block_size_bytes = 512 * block_height * block_depth; // 512 is GOB size + for (u32 i = 1; i < max_mip_level; i++) { + m_width = std::max(1U, m_width / 2); + m_height = std::max(1U, m_height / 2); + m_depth = std::max(1U, m_depth / 2); + m_block_height = std::max(1U, m_block_height / 2); + m_block_depth = std::max(1U, m_block_depth / 2); + size += Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, m_depth, + m_block_height, m_block_depth); + } + return is_tiled ? Common::AlignUp(size, block_size_bytes) : size; +} + /*static*/ SurfaceParams SurfaceParams::CreateForTexture( const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { SurfaceParams params{}; @@ -124,6 +147,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { break; } + params.is_layered = SurfaceTargetIsLayered(params.target); params.max_mip_level = config.tic.max_mip_level + 1; params.rt = {}; @@ -150,6 +174,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { params.target = SurfaceTarget::Texture2D; params.depth = 1; params.max_mip_level = 0; + params.is_layered = false; // Render target specific parameters, not used for caching params.rt.index = static_cast<u32>(index); @@ -182,6 +207,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { params.target = SurfaceTarget::Texture2D; params.depth = 1; params.max_mip_level = 0; + params.is_layered = false; params.rt = {}; params.InitCacheParameters(zeta_address); @@ -361,10 +387,11 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d } } -static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), - SurfaceParams::MaxPixelFormat> - morton_to_gl_fns = { - // clang-format off +using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), + SurfaceParams::MaxPixelFormat>; + +static constexpr GLConversionArray morton_to_gl_fns = { + // clang-format off MortonCopy<true, PixelFormat::ABGR8U>, MortonCopy<true, PixelFormat::ABGR8S>, MortonCopy<true, PixelFormat::ABGR8UI>, @@ -418,13 +445,11 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32FS8>, - // clang-format on + // clang-format on }; -static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), - SurfaceParams::MaxPixelFormat> - gl_to_morton_fns = { - // clang-format off +static constexpr GLConversionArray gl_to_morton_fns = { + // clang-format off MortonCopy<false, PixelFormat::ABGR8U>, MortonCopy<false, PixelFormat::ABGR8S>, MortonCopy<false, PixelFormat::ABGR8UI>, @@ -479,9 +504,35 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, MortonCopy<false, PixelFormat::Z24S8>, MortonCopy<false, PixelFormat::S8Z24>, MortonCopy<false, PixelFormat::Z32FS8>, - // clang-format on + // clang-format on }; +void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params, + std::vector<u8>& gl_buffer) { + u32 depth = params.depth; + if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { + // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. + depth = 1U; + } + if (params.is_layered) { + u64 offset = 0; + u64 offset_gl = 0; + u64 layer_size = params.LayerMemorySize(); + u64 gl_size = params.LayerSizeGL(); + for (u32 i = 0; i < depth; i++) { + functions[static_cast<std::size_t>(params.pixel_format)]( + params.width, params.block_height, params.height, params.block_depth, 1, + gl_buffer.data() + offset_gl, gl_size, params.addr + offset); + offset += layer_size; + offset_gl += gl_size; + } + } else { + functions[static_cast<std::size_t>(params.pixel_format)]( + params.width, params.block_height, params.height, params.block_depth, depth, + gl_buffer.data(), gl_buffer.size(), params.addr); + } +} + static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { @@ -881,21 +932,10 @@ void CachedSurface::LoadGLBuffer() { gl_buffer.resize(params.size_in_bytes_gl); if (params.is_tiled) { - u32 depth = params.depth; - u32 block_depth = params.block_depth; - ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", params.block_width, static_cast<u32>(params.target)); - if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { - // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. - depth = 1U; - block_depth = 1U; - } - - morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( - params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), - gl_buffer.size(), params.addr); + SwizzleFunc(morton_to_gl_fns, params, gl_buffer); } else { const auto texture_src_data{Memory::GetPointer(params.addr)}; const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; @@ -929,19 +969,10 @@ void CachedSurface::FlushGLBuffer() { const u8* const texture_src_data = Memory::GetPointer(params.addr); ASSERT(texture_src_data); if (params.is_tiled) { - u32 depth = params.depth; - u32 block_depth = params.block_depth; - ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", params.block_width, static_cast<u32>(params.target)); - if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { - // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. - depth = 1U; - } - gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( - params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), - gl_buffer.size(), GetAddr()); + SwizzleFunc(gl_to_morton_fns, params, gl_buffer); } else { std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); } @@ -1179,7 +1210,7 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface) { const auto& src_params{src_surface->GetSurfaceParams()}; const auto& dst_params{dst_surface->GetSurfaceParams()}; - FlushRegion(src_params.addr, dst_params.size_in_bytes); + FlushRegion(src_params.addr, dst_params.MemorySize()); LoadSurface(dst_surface); } @@ -1221,44 +1252,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, CopySurface(old_surface, new_surface, copy_pbo.handle); } break; + case SurfaceParams::SurfaceTarget::TextureCubemap: case SurfaceParams::SurfaceTarget::Texture3D: AccurateCopySurface(old_surface, new_surface); break; - case SurfaceParams::SurfaceTarget::TextureCubemap: { - if (old_params.rt.array_mode != 1) { - // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this - // yet (array rendering used as a cubemap texture). - LOG_CRITICAL(HW_GPU, "Unhandled rendertarget array_mode {}", old_params.rt.array_mode); - UNREACHABLE(); - return new_surface; - } - - // This seems to be used for render-to-cubemap texture - ASSERT_MSG(old_params.target == SurfaceParams::SurfaceTarget::Texture2D, "Unexpected"); - ASSERT_MSG(old_params.pixel_format == new_params.pixel_format, "Unexpected"); - ASSERT_MSG(old_params.rt.base_layer == 0, "Unimplemented"); - - // TODO(bunnei): Verify the below - this stride seems to be in 32-bit words, not pixels. - // Tested with Splatoon 2, Super Mario Odyssey, and Breath of the Wild. - const std::size_t byte_stride{old_params.rt.layer_stride * sizeof(u32)}; - - for (std::size_t index = 0; index < new_params.depth; ++index) { - Surface face_surface{TryGetReservedSurface(old_params)}; - ASSERT_MSG(face_surface, "Unexpected"); - - if (is_blit) { - BlitSurface(face_surface, new_surface, read_framebuffer.handle, - draw_framebuffer.handle, face_surface->GetSurfaceParams().rt.index, - new_params.rt.index, index); - } else { - CopySurface(face_surface, new_surface, copy_pbo.handle, - face_surface->GetSurfaceParams().rt.index, new_params.rt.index, index); - } - - old_params.addr += byte_stride; - } - break; - } default: LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", static_cast<u32>(new_params.target)); @@ -1266,7 +1263,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, } return new_surface; -} +} // namespace OpenGL Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { 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 { } } + static bool SurfaceTargetIsLayered(SurfaceTarget target) { + switch (target) { + case SurfaceTarget::Texture1D: + case SurfaceTarget::Texture2D: + case SurfaceTarget::Texture3D: + return false; + case SurfaceTarget::Texture1DArray: + case SurfaceTarget::Texture2DArray: + case SurfaceTarget::TextureCubemap: + return true; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); + UNREACHABLE(); + return false; + } + } + /** * Gets the compression factor for the specified PixelFormat. This applies to just the * "compressed width" and "compressed height", not the overall compression factor of a @@ -742,6 +759,25 @@ struct SurfaceParams { return size_in_bytes_gl / 6; } + /// Returns the exact size of memory occupied by the texture in VRAM, including mipmaps. + std::size_t MemorySize() const { + std::size_t size = InnerMemorySize(is_layered); + if (is_layered) + return size * depth; + return size; + } + + /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including + /// mipmaps. + std::size_t LayerMemorySize() const { + return InnerMemorySize(true); + } + + /// Returns the size of a layer of this surface in OpenGL. + std::size_t LayerSizeGL() const { + return SizeInBytesRaw(true) / depth; + } + /// Creates SurfaceParams from a texture configuration static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry); @@ -782,6 +818,7 @@ struct SurfaceParams { u32 unaligned_height; SurfaceTarget target; u32 max_mip_level; + bool is_layered; // Parameters used for caching VAddr addr; @@ -797,6 +834,9 @@ struct SurfaceParams { u32 layer_stride; u32 base_layer; } rt; + +private: + std::size_t InnerMemorySize(bool layer_only = false) const; }; }; // 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() { depth.test_enabled = false; depth.test_func = GL_LESS; depth.write_mask = GL_TRUE; + depth.depth_range_near = 0.0f; + depth.depth_range_far = 1.0f; color_mask.red_enabled = GL_TRUE; color_mask.green_enabled = GL_TRUE; @@ -119,6 +121,12 @@ void OpenGLState::Apply() const { glDepthMask(depth.write_mask); } + // Depth range + if (depth.depth_range_near != cur_state.depth.depth_range_near || + depth.depth_range_far != cur_state.depth.depth_range_far) { + glDepthRange(depth.depth_range_near, depth.depth_range_far); + } + // Color mask if (color_mask.red_enabled != cur_state.color_mask.red_enabled || 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: } cull; struct { - bool test_enabled; // GL_DEPTH_TEST - GLenum test_func; // GL_DEPTH_FUNC - GLboolean write_mask; // GL_DEPTH_WRITEMASK + bool test_enabled; // GL_DEPTH_TEST + GLenum test_func; // GL_DEPTH_FUNC + GLboolean write_mask; // GL_DEPTH_WRITEMASK + GLfloat depth_range_near; // GL_DEPTH_RANGE + GLfloat depth_range_far; // GL_DEPTH_RANGE } depth; 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 std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth) { if (tiled) { - const u32 gobs_in_x = 64 / bytes_per_pixel; + const u32 gobs_in_x = 64; const u32 gobs_in_y = 8; const u32 gobs_in_z = 1; - const u32 aligned_width = Common::AlignUp(width, gobs_in_x); + const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x); const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); - return aligned_width * aligned_height * aligned_depth * bytes_per_pixel; + return aligned_width * aligned_height * aligned_depth; } else { return width * height * depth * bytes_per_pixel; } 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() { // TODO: Don't hardcode the path; let the frontend decide where to put the config files. qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; FileUtil::CreateFullPath(qt_config_loc); - qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); + qt_config = + std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); Reload(); } +Config::~Config() { + Save(); +} + const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, 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() { void Config::Save() { SaveValues(); } - -Config::~Config() { - Save(); - - delete qt_config; -} 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 @@ #pragma once #include <array> +#include <memory> #include <string> #include <QVariant> #include "core/settings.h" @@ -12,12 +13,6 @@ class QSettings; class Config { - QSettings* qt_config; - std::string qt_config_loc; - - void ReadValues(); - void SaveValues(); - public: Config(); ~Config(); @@ -27,4 +22,11 @@ public: static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; + +private: + void ReadValues(); + void SaveValues(); + + std::unique_ptr<QSettings> qt_config; + std::string qt_config_loc; }; 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 @@ #include "yuzu/configuration/configure_system.h" #include "yuzu/main.h" -static std::string GetImagePath(Service::Account::UUID uuid) { - return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + - "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; -} - -static const std::array<int, 12> days_in_month = {{ +namespace { +constexpr std::array<int, 12> days_in_month = {{ 31, 29, 31, @@ -42,7 +38,7 @@ static const std::array<int, 12> days_in_month = {{ }}; // Same backup JPEG used by acc IProfile::GetImage if no jpeg found -static constexpr std::array<u8, 107> backup_jpeg{ +constexpr std::array<u8, 107> backup_jpeg{ 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, @@ -52,15 +48,32 @@ static constexpr std::array<u8, 107> backup_jpeg{ 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, }; +std::string GetImagePath(Service::Account::UUID uuid) { + return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; +} + +std::string GetAccountUsername(const Service::Account::ProfileManager& manager, + Service::Account::UUID uuid) { + Service::Account::ProfileBase profile; + if (!manager.GetProfileBase(uuid, profile)) { + return ""; + } + + return Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); +} +} // Anonymous namespace + ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { ui->setupUi(this); connect(ui->combo_birthmonth, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, - &ConfigureSystem::updateBirthdayComboBox); + &ConfigureSystem::UpdateBirthdayComboBox); connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, - &ConfigureSystem::refreshConsoleID); + &ConfigureSystem::RefreshConsoleID); layout = new QVBoxLayout; tree_view = new QTreeView; @@ -153,8 +166,8 @@ void ConfigureSystem::UpdateCurrentUser() { ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); const auto& current_user = profile_manager->GetUser(Settings::values.current_user); - ASSERT(current_user != boost::none); - const auto username = GetAccountUsername(*current_user); + ASSERT(current_user != std::nullopt); + const auto username = GetAccountUsername(*profile_manager, *current_user); scene->clear(); scene->addPixmap( @@ -164,14 +177,6 @@ void ConfigureSystem::UpdateCurrentUser() { void ConfigureSystem::ReadSystemSettings() {} -std::string ConfigureSystem::GetAccountUsername(Service::Account::UUID uuid) const { - Service::Account::ProfileBase profile; - if (!profile_manager->GetProfileBase(uuid, profile)) - return ""; - return Common::StringFromFixedZeroTerminatedBuffer( - reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); -} - void ConfigureSystem::applyConfiguration() { if (!enabled) return; @@ -180,7 +185,7 @@ void ConfigureSystem::applyConfiguration() { Settings::Apply(); } -void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { +void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { if (birthmonth_index < 0 || birthmonth_index >= 12) return; @@ -205,7 +210,7 @@ void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { ui->combo_birthday->setCurrentIndex(birthday_index); } -void ConfigureSystem::refreshConsoleID() { +void ConfigureSystem::RefreshConsoleID() { QMessageBox::StandardButton reply; QString warning_text = tr("This will replace your current virtual Switch with a new one. " "Your current virtual Switch will not be recoverable. " @@ -232,8 +237,7 @@ void ConfigureSystem::SelectUser(const QModelIndex& index) { } void ConfigureSystem::AddUser() { - Service::Account::UUID uuid; - uuid.Generate(); + const auto uuid = Service::Account::UUID::Generate(); bool ok = false; const auto username = @@ -252,8 +256,8 @@ void ConfigureSystem::AddUser() { void ConfigureSystem::RenameUser() { const auto user = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(user); - ASSERT(uuid != boost::none); - const auto username = GetAccountUsername(*uuid); + ASSERT(uuid != std::nullopt); + const auto username = GetAccountUsername(*profile_manager, *uuid); Service::Account::ProfileBase profile; if (!profile_manager->GetProfileBase(*uuid, profile)) @@ -292,8 +296,8 @@ void ConfigureSystem::RenameUser() { void ConfigureSystem::DeleteUser() { const auto index = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(index); - ASSERT(uuid != boost::none); - const auto username = GetAccountUsername(*uuid); + ASSERT(uuid != std::nullopt); + const auto username = GetAccountUsername(*profile_manager, *uuid); const auto confirm = QMessageBox::question(this, tr("Confirm Delete"), @@ -320,11 +324,11 @@ void ConfigureSystem::DeleteUser() { void ConfigureSystem::SetUserImage() { const auto index = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(index); - ASSERT(uuid != boost::none); - const auto username = GetAccountUsername(*uuid); + ASSERT(uuid != std::nullopt); + const auto username = GetAccountUsername(*profile_manager, *uuid); const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), - "JPEG Images (*.jpg *.jpeg)"); + tr("JPEG Images (*.jpg *.jpeg)")); if (file.isEmpty()) 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 @@ #include <QList> #include <QWidget> -namespace Service::Account { -class ProfileManager; -struct UUID; -} // namespace Service::Account - class QGraphicsScene; class QStandardItem; class QStandardItemModel; class QTreeView; class QVBoxLayout; +namespace Service::Account { +class ProfileManager; +} + namespace Ui { class ConfigureSystem; } @@ -29,28 +28,25 @@ class ConfigureSystem : public QWidget { public: explicit ConfigureSystem(QWidget* parent = nullptr); - ~ConfigureSystem(); + ~ConfigureSystem() override; void applyConfiguration(); void setConfiguration(); - void PopulateUserList(); - void UpdateCurrentUser(); +private: + void ReadSystemSettings(); -public slots: - void updateBirthdayComboBox(int birthmonth_index); - void refreshConsoleID(); + void UpdateBirthdayComboBox(int birthmonth_index); + void RefreshConsoleID(); + void PopulateUserList(); + void UpdateCurrentUser(); void SelectUser(const QModelIndex& index); void AddUser(); void RenameUser(); void DeleteUser(); void SetUserImage(); -private: - void ReadSystemSettings(); - std::string GetAccountUsername(Service::Account::UUID uuid) const; - QVBoxLayout* layout; QTreeView* tree_view; QStandardItemModel* item_model; @@ -59,11 +55,12 @@ private: std::vector<QList<QStandardItem*>> list_items; std::unique_ptr<Ui::ConfigureSystem> ui; - bool enabled; + bool enabled = false; - int birthmonth, birthday; - int language_index; - int sound_index; + int birthmonth = 0; + int birthday = 0; + int language_index = 0; + int sound_index = 0; std::unique_ptr<Service::Account::ProfileManager> profile_manager; }; 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, FileSys::VirtualFile update_raw; loader.ReadUpdateRaw(update_raw); for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { - if (!updatable && kv.first == "Update") + const bool is_update = kv.first == "Update"; + if (!updatable && is_update) { continue; + } + + const QString type = QString::fromStdString(kv.first); if (kv.second.empty()) { - out.append(fmt::format("{}\n", kv.first).c_str()); + out.append(QStringLiteral("%1\n").arg(type)); } else { auto ver = kv.second; // Display container name for packed updates - if (ver == "PACKED" && kv.first == "Update") + if (is_update && ver == "PACKED") { ver = Loader::GetFileTypeString(loader.GetFileType()); + } - out.append(fmt::format("{} ({})\n", kv.first, ver).c_str()); + out.append(QStringLiteral("%1 (%2)\n").arg(type, QString::fromStdString(ver))); } } 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 #define QT_NO_OPENGL #include <QDesktopWidget> #include <QDialogButtonBox> +#include <QFile> #include <QFileDialog> #include <QMessageBox> #include <QtConcurrent/QtConcurrent> @@ -785,7 +786,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target ASSERT(index != -1 && index < 8); const auto user_id = manager.GetUser(index); - ASSERT(user_id != boost::none); + ASSERT(user_id != std::nullopt); path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, program_id, user_id->uuid, 0); @@ -1336,20 +1337,40 @@ void GMainWindow::OnLoadAmiibo() { const QString extensions{"*.bin"}; const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter); - if (!filename.isEmpty()) { - Core::System& system{Core::System::GetInstance()}; - Service::SM::ServiceManager& sm = system.ServiceManager(); - auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); - if (nfc != nullptr) { - auto nfc_file = FileUtil::IOFile(filename.toStdString(), "rb"); - if (!nfc_file.IsOpen()) { - return; - } - std::vector<u8> amiibo_buffer(nfc_file.GetSize()); - nfc_file.ReadBytes(amiibo_buffer.data(), amiibo_buffer.size()); - nfc_file.Close(); - nfc->LoadAmiibo(amiibo_buffer); - } + + if (filename.isEmpty()) { + return; + } + + Core::System& system{Core::System::GetInstance()}; + Service::SM::ServiceManager& sm = system.ServiceManager(); + auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); + if (nfc == nullptr) { + return; + } + + QFile nfc_file{filename}; + if (!nfc_file.open(QIODevice::ReadOnly)) { + QMessageBox::warning(this, tr("Error opening Amiibo data file"), + tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename)); + return; + } + + const u64 nfc_file_size = nfc_file.size(); + std::vector<u8> buffer(nfc_file_size); + const u64 read_size = nfc_file.read(reinterpret_cast<char*>(buffer.data()), nfc_file_size); + if (nfc_file_size != read_size) { + QMessageBox::warning(this, tr("Error reading Amiibo data file"), + tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but " + "was only able to read %2 bytes.") + .arg(nfc_file_size) + .arg(read_size)); + return; + } + + if (!nfc->LoadAmiibo(buffer)) { + QMessageBox::warning(this, tr("Error loading Amiibo data"), + tr("Unable to load Amiibo data.")); } } |