fs: Add path class
This commit is contained in:
		| @@ -19,4 +19,21 @@ inline T WrappingAdd(T lhs, T rhs) { | ||||
|     return BitCast<T>(lhs_u + rhs_u); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
|     requires(std::is_integral_v<T> && std::is_signed_v<T>) | ||||
| inline bool CanAddWithoutOverflow(T lhs, T rhs) { | ||||
| #ifdef _MSC_VER | ||||
|     if (lhs >= 0 && rhs >= 0) { | ||||
|         return WrappingAdd(lhs, rhs) >= std::max(lhs, rhs); | ||||
|     } else if (lhs < 0 && rhs < 0) { | ||||
|         return WrappingAdd(lhs, rhs) <= std::min(lhs, rhs); | ||||
|     } else { | ||||
|         return true; | ||||
|     } | ||||
| #else | ||||
|     T res; | ||||
|     return !__builtin_add_overflow(lhs, rhs, &res); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| } // namespace Common | ||||
|   | ||||
| @@ -58,7 +58,7 @@ add_library(core STATIC | ||||
|     file_sys/fs_operate_range.h | ||||
|     file_sys/fs_path.h | ||||
|     file_sys/fs_path_utility.h | ||||
|     file_sys/fs_util_character_encoding.h | ||||
|     file_sys/fs_string_util.h | ||||
|     file_sys/fsmitm_romfsbuild.cpp | ||||
|     file_sys/fsmitm_romfsbuild.h | ||||
|     file_sys/fssystem/fs_i_storage.h | ||||
| @@ -104,25 +104,10 @@ add_library(core STATIC | ||||
|     file_sys/fssystem/fssystem_switch_storage.h | ||||
|     file_sys/fssystem/fssystem_utility.cpp | ||||
|     file_sys/fssystem/fssystem_utility.h | ||||
|     file_sys/fssystem/fs_types.h | ||||
|     file_sys/bis_factory.cpp | ||||
|     file_sys/bis_factory.h | ||||
|     file_sys/card_image.cpp | ||||
|     file_sys/card_image.h | ||||
|     file_sys/common_funcs.h | ||||
|     file_sys/content_archive.cpp | ||||
|     file_sys/content_archive.h | ||||
|     file_sys/control_metadata.cpp | ||||
|     file_sys/control_metadata.h | ||||
|     file_sys/directory.h | ||||
|     file_sys/errors.h | ||||
|     file_sys/fsmitm_romfsbuild.cpp | ||||
|     file_sys/fsmitm_romfsbuild.h | ||||
|     file_sys/ips_layer.cpp | ||||
|     file_sys/ips_layer.h | ||||
|     file_sys/kernel_executable.cpp | ||||
|     file_sys/kernel_executable.h | ||||
|     file_sys/mode.h | ||||
|     file_sys/nca_metadata.cpp | ||||
|     file_sys/nca_metadata.h | ||||
|     file_sys/partition_filesystem.cpp | ||||
| @@ -215,7 +200,6 @@ add_library(core STATIC | ||||
|     hle/kernel/board/nintendo/nx/secure_monitor.h | ||||
|     hle/kernel/code_set.cpp | ||||
|     hle/kernel/code_set.h | ||||
|     hle/kernel/svc_results.h | ||||
|     hle/kernel/global_scheduler_context.cpp | ||||
|     hle/kernel/global_scheduler_context.h | ||||
|     hle/kernel/init/init_slab_setup.cpp | ||||
| @@ -225,11 +209,11 @@ add_library(core STATIC | ||||
|     hle/kernel/k_address_arbiter.h | ||||
|     hle/kernel/k_address_space_info.cpp | ||||
|     hle/kernel/k_address_space_info.h | ||||
|     hle/kernel/k_affinity_mask.h | ||||
|     hle/kernel/k_auto_object.cpp | ||||
|     hle/kernel/k_auto_object.h | ||||
|     hle/kernel/k_auto_object_container.cpp | ||||
|     hle/kernel/k_auto_object_container.h | ||||
|     hle/kernel/k_affinity_mask.h | ||||
|     hle/kernel/k_capabilities.cpp | ||||
|     hle/kernel/k_capabilities.h | ||||
|     hle/kernel/k_class_token.cpp | ||||
| @@ -253,9 +237,9 @@ add_library(core STATIC | ||||
|     hle/kernel/k_event_info.h | ||||
|     hle/kernel/k_handle_table.cpp | ||||
|     hle/kernel/k_handle_table.h | ||||
|     hle/kernel/k_hardware_timer_base.h | ||||
|     hle/kernel/k_hardware_timer.cpp | ||||
|     hle/kernel/k_hardware_timer.h | ||||
|     hle/kernel/k_hardware_timer_base.h | ||||
|     hle/kernel/k_interrupt_manager.cpp | ||||
|     hle/kernel/k_interrupt_manager.h | ||||
|     hle/kernel/k_light_client_session.cpp | ||||
| @@ -282,10 +266,10 @@ add_library(core STATIC | ||||
|     hle/kernel/k_page_bitmap.h | ||||
|     hle/kernel/k_page_buffer.cpp | ||||
|     hle/kernel/k_page_buffer.h | ||||
|     hle/kernel/k_page_heap.cpp | ||||
|     hle/kernel/k_page_heap.h | ||||
|     hle/kernel/k_page_group.cpp | ||||
|     hle/kernel/k_page_group.h | ||||
|     hle/kernel/k_page_heap.cpp | ||||
|     hle/kernel/k_page_heap.h | ||||
|     hle/kernel/k_page_table.h | ||||
|     hle/kernel/k_page_table_base.cpp | ||||
|     hle/kernel/k_page_table_base.h | ||||
| @@ -350,8 +334,6 @@ add_library(core STATIC | ||||
|     hle/kernel/slab_helpers.h | ||||
|     hle/kernel/svc.cpp | ||||
|     hle/kernel/svc.h | ||||
|     hle/kernel/svc_common.h | ||||
|     hle/kernel/svc_types.h | ||||
|     hle/kernel/svc/svc_activity.cpp | ||||
|     hle/kernel/svc/svc_address_arbiter.cpp | ||||
|     hle/kernel/svc/svc_address_translation.cpp | ||||
| @@ -389,6 +371,9 @@ add_library(core STATIC | ||||
|     hle/kernel/svc/svc_thread_profiler.cpp | ||||
|     hle/kernel/svc/svc_tick.cpp | ||||
|     hle/kernel/svc/svc_transfer_memory.cpp | ||||
|     hle/kernel/svc_common.h | ||||
|     hle/kernel/svc_results.h | ||||
|     hle/kernel/svc_types.h | ||||
|     hle/result.h | ||||
|     hle/service/acc/acc.cpp | ||||
|     hle/service/acc/acc.h | ||||
| @@ -519,17 +504,17 @@ add_library(core STATIC | ||||
|     hle/service/filesystem/fsp/fs_i_filesystem.h | ||||
|     hle/service/filesystem/fsp/fs_i_storage.cpp | ||||
|     hle/service/filesystem/fsp/fs_i_storage.h | ||||
|     hle/service/filesystem/fsp/fsp_ldr.cpp | ||||
|     hle/service/filesystem/fsp/fsp_ldr.h | ||||
|     hle/service/filesystem/fsp/fsp_pr.cpp | ||||
|     hle/service/filesystem/fsp/fsp_pr.h | ||||
|     hle/service/filesystem/fsp/fsp_srv.cpp | ||||
|     hle/service/filesystem/fsp/fsp_srv.h | ||||
|     hle/service/filesystem/fsp_ldr.cpp | ||||
|     hle/service/filesystem/fsp_ldr.h | ||||
|     hle/service/filesystem/fsp_pr.cpp | ||||
|     hle/service/filesystem/fsp_pr.h | ||||
|     hle/service/filesystem/fsp/fsp_util.h | ||||
|     hle/service/filesystem/romfs_controller.cpp | ||||
|     hle/service/filesystem/romfs_controller.h | ||||
|     hle/service/filesystem/save_data_controller.cpp | ||||
|     hle/service/filesystem/save_data_controller.h | ||||
|     hle/service/filesystem/fsp_util.h | ||||
|     hle/service/fgm/fgm.cpp | ||||
|     hle/service/fgm/fgm.h | ||||
|     hle/service/friend/friend.cpp | ||||
|   | ||||
| @@ -73,10 +73,21 @@ constexpr Result ResultUnexpectedInCompressedStorageA{ErrorModule::FS, 5324}; | ||||
| constexpr Result ResultUnexpectedInCompressedStorageB{ErrorModule::FS, 5325}; | ||||
| constexpr Result ResultUnexpectedInCompressedStorageC{ErrorModule::FS, 5326}; | ||||
| constexpr Result ResultUnexpectedInCompressedStorageD{ErrorModule::FS, 5327}; | ||||
| constexpr Result ResultUnexpectedInPathA{ErrorModule::FS, 5328}; | ||||
| constexpr Result ResultInvalidArgument{ErrorModule::FS, 6001}; | ||||
| constexpr Result ResultInvalidPath{ErrorModule::FS, 6002}; | ||||
| constexpr Result ResultTooLongPath{ErrorModule::FS, 6003}; | ||||
| constexpr Result ResultInvalidCharacter{ErrorModule::FS, 6004}; | ||||
| constexpr Result ResultInvalidPathFormat{ErrorModule::FS, 6005}; | ||||
| constexpr Result ResultDirectoryUnobtainable{ErrorModule::FS, 6006}; | ||||
| constexpr Result ResultNotNormalized{ErrorModule::FS, 6007}; | ||||
| constexpr Result ResultInvalidOffset{ErrorModule::FS, 6061}; | ||||
| constexpr Result ResultInvalidSize{ErrorModule::FS, 6062}; | ||||
| constexpr Result ResultNullptrArgument{ErrorModule::FS, 6063}; | ||||
| constexpr Result ResultInvalidOpenMode{ErrorModule::FS, 6072}; | ||||
| constexpr Result ResultFileExtensionWithoutOpenModeAllowAppend{ErrorModule::FS, 6201}; | ||||
| constexpr Result ResultReadNotPermitted{ErrorModule::FS, 6202}; | ||||
| constexpr Result ResultWriteNotPermitted{ErrorModule::FS, 6203}; | ||||
| constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325}; | ||||
| constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387}; | ||||
| constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388}; | ||||
|   | ||||
							
								
								
									
										63
									
								
								src/core/file_sys/fs_file.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/core/file_sys/fs_file.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| namespace FileSys { | ||||
|  | ||||
| struct ReadOption { | ||||
|     u32 _value; | ||||
|  | ||||
|     static const ReadOption None; | ||||
| }; | ||||
|  | ||||
| enum ReadOptionFlag : u32 { | ||||
|     ReadOptionFlag_None = (0 << 0), | ||||
| }; | ||||
|  | ||||
| inline constexpr const ReadOption ReadOption::None = {ReadOptionFlag_None}; | ||||
|  | ||||
| inline constexpr bool operator==(const ReadOption& lhs, const ReadOption& rhs) { | ||||
|     return lhs._value == rhs._value; | ||||
| } | ||||
|  | ||||
| inline constexpr bool operator!=(const ReadOption& lhs, const ReadOption& rhs) { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| static_assert(sizeof(ReadOption) == sizeof(u32)); | ||||
|  | ||||
| enum WriteOptionFlag : u32 { | ||||
|     WriteOptionFlag_None = (0 << 0), | ||||
|     WriteOptionFlag_Flush = (1 << 0), | ||||
| }; | ||||
|  | ||||
| struct WriteOption { | ||||
|     u32 _value; | ||||
|  | ||||
|     constexpr inline bool HasFlushFlag() const { | ||||
|         return _value & WriteOptionFlag_Flush; | ||||
|     } | ||||
|  | ||||
|     static const WriteOption None; | ||||
|     static const WriteOption Flush; | ||||
| }; | ||||
|  | ||||
| inline constexpr const WriteOption WriteOption::None = {WriteOptionFlag_None}; | ||||
| inline constexpr const WriteOption WriteOption::Flush = {WriteOptionFlag_Flush}; | ||||
|  | ||||
| inline constexpr bool operator==(const WriteOption& lhs, const WriteOption& rhs) { | ||||
|     return lhs._value == rhs._value; | ||||
| } | ||||
|  | ||||
| inline constexpr bool operator!=(const WriteOption& lhs, const WriteOption& rhs) { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| static_assert(sizeof(WriteOption) == sizeof(u32)); | ||||
|  | ||||
| struct FileHandle { | ||||
|     void* handle; | ||||
| }; | ||||
|  | ||||
| } // namespace FileSys | ||||
							
								
								
									
										48
									
								
								src/core/file_sys/fs_memory_management.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/core/file_sys/fs_memory_management.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <mutex> | ||||
| #include "common/alignment.h" | ||||
|  | ||||
| namespace FileSys { | ||||
|  | ||||
| std::mutex g_mutex; | ||||
|  | ||||
| constexpr size_t RequiredAlignment = alignof(u64); | ||||
|  | ||||
| void* AllocateUnsafe(size_t size) { | ||||
|     /* Allocate. */ | ||||
|     void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment}); | ||||
|  | ||||
|     /* Check alignment. */ | ||||
|     ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(ptr), RequiredAlignment)); | ||||
|  | ||||
|     /* Return allocated pointer. */ | ||||
|     return ptr; | ||||
| } | ||||
|  | ||||
| void DeallocateUnsafe(void* ptr, size_t size) { | ||||
|     /* Deallocate the pointer. */ | ||||
|     ::operator delete(ptr, std::align_val_t{RequiredAlignment}); | ||||
| } | ||||
|  | ||||
| void* Allocate(size_t size) { | ||||
|     /* Lock the allocator. */ | ||||
|     std::scoped_lock lk(g_mutex); | ||||
|  | ||||
|     return AllocateUnsafe(size); | ||||
| } | ||||
|  | ||||
| void Deallocate(void* ptr, size_t size) { | ||||
|     /* If the pointer is non-null, deallocate it. */ | ||||
|     if (ptr != nullptr) { | ||||
|         /* Lock the allocator. */ | ||||
|         std::scoped_lock lk(g_mutex); | ||||
|  | ||||
|         DeallocateUnsafe(ptr, size); | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // namespace FileSys | ||||
							
								
								
									
										20
									
								
								src/core/file_sys/fs_operate_range.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/core/file_sys/fs_operate_range.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| namespace FileSys { | ||||
|  | ||||
| enum class OperationId : s64 { | ||||
|     FillZero = 0, | ||||
|     DestroySignature = 1, | ||||
|     Invalidate = 2, | ||||
|     QueryRange = 3, | ||||
|     QueryUnpreparedRange = 4, | ||||
|     QueryLazyLoadCompletionRate = 5, | ||||
|     SetLazyLoadPriority = 6, | ||||
|  | ||||
|     ReadLazyLoadFileForciblyForDebug = 10001, | ||||
| }; | ||||
|  | ||||
| } // namespace FileSys | ||||
							
								
								
									
										570
									
								
								src/core/file_sys/fs_path.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										570
									
								
								src/core/file_sys/fs_path.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,570 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/alignment.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "core/file_sys/errors.h" | ||||
| #include "core/file_sys/fs_memory_management.h" | ||||
| #include "core/file_sys/fs_path_utility.h" | ||||
| #include "core/file_sys/fs_string_util.h" | ||||
| #include "core/hle/result.h" | ||||
|  | ||||
| namespace FileSys { | ||||
| class DirectoryPathParser; | ||||
|  | ||||
| class Path { | ||||
|     YUZU_NON_COPYABLE(Path); | ||||
|     YUZU_NON_MOVEABLE(Path); | ||||
|  | ||||
| private: | ||||
|     static constexpr const char* EmptyPath = ""; | ||||
|     static constexpr size_t WriteBufferAlignmentLength = 8; | ||||
|  | ||||
| private: | ||||
|     friend class DirectoryPathParser; | ||||
|  | ||||
| public: | ||||
|     class WriteBuffer { | ||||
|         YUZU_NON_COPYABLE(WriteBuffer); | ||||
|  | ||||
|     private: | ||||
|         char* m_buffer; | ||||
|         size_t m_length_and_is_normalized; | ||||
|  | ||||
|     public: | ||||
|         constexpr WriteBuffer() : m_buffer(nullptr), m_length_and_is_normalized(0) { /* ... */ | ||||
|         } | ||||
|  | ||||
|         constexpr ~WriteBuffer() { | ||||
|             if (m_buffer != nullptr) { | ||||
|                 Deallocate(m_buffer, this->GetLength()); | ||||
|                 this->ResetBuffer(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         constexpr WriteBuffer(WriteBuffer&& rhs) | ||||
|             : m_buffer(rhs.m_buffer), m_length_and_is_normalized(rhs.m_length_and_is_normalized) { | ||||
|             rhs.ResetBuffer(); | ||||
|         } | ||||
|  | ||||
|         constexpr WriteBuffer& operator=(WriteBuffer&& rhs) { | ||||
|             if (m_buffer != nullptr) { | ||||
|                 Deallocate(m_buffer, this->GetLength()); | ||||
|             } | ||||
|  | ||||
|             m_buffer = rhs.m_buffer; | ||||
|             m_length_and_is_normalized = rhs.m_length_and_is_normalized; | ||||
|  | ||||
|             rhs.ResetBuffer(); | ||||
|  | ||||
|             return *this; | ||||
|         } | ||||
|  | ||||
|         constexpr void ResetBuffer() { | ||||
|             m_buffer = nullptr; | ||||
|             this->SetLength(0); | ||||
|         } | ||||
|  | ||||
|         constexpr char* Get() const { | ||||
|             return m_buffer; | ||||
|         } | ||||
|  | ||||
|         constexpr size_t GetLength() const { | ||||
|             return m_length_and_is_normalized >> 1; | ||||
|         } | ||||
|  | ||||
|         constexpr bool IsNormalized() const { | ||||
|             return static_cast<bool>(m_length_and_is_normalized & 1); | ||||
|         } | ||||
|  | ||||
|         constexpr void SetNormalized() { | ||||
|             m_length_and_is_normalized |= static_cast<size_t>(1); | ||||
|         } | ||||
|  | ||||
|         constexpr void SetNotNormalized() { | ||||
|             m_length_and_is_normalized &= ~static_cast<size_t>(1); | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         constexpr WriteBuffer(char* buffer, size_t length) | ||||
|             : m_buffer(buffer), m_length_and_is_normalized(0) { | ||||
|             this->SetLength(length); | ||||
|         } | ||||
|  | ||||
|     public: | ||||
|         static WriteBuffer Make(size_t length) { | ||||
|             if (void* alloc = Allocate(length); alloc != nullptr) { | ||||
|                 return WriteBuffer(static_cast<char*>(alloc), length); | ||||
|             } else { | ||||
|                 return WriteBuffer(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         constexpr void SetLength(size_t size) { | ||||
|             m_length_and_is_normalized = (m_length_and_is_normalized & 1) | (size << 1); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| private: | ||||
|     const char* m_str; | ||||
|     WriteBuffer m_write_buffer; | ||||
|  | ||||
| public: | ||||
|     constexpr Path() : m_str(EmptyPath), m_write_buffer() { | ||||
|         /* ... */ | ||||
|     } | ||||
|  | ||||
|     constexpr Path(const char* s) : m_str(s), m_write_buffer() { | ||||
|         m_write_buffer.SetNormalized(); | ||||
|     } | ||||
|  | ||||
|     constexpr ~Path() { /* ... */ | ||||
|     } | ||||
|  | ||||
|     constexpr Result SetShallowBuffer(const char* buffer) { | ||||
|         /* Check pre-conditions. */ | ||||
|         ASSERT(m_write_buffer.GetLength() == 0); | ||||
|  | ||||
|         /* Check the buffer is valid. */ | ||||
|         R_UNLESS(buffer != nullptr, ResultNullptrArgument); | ||||
|  | ||||
|         /* Set buffer. */ | ||||
|         this->SetReadOnlyBuffer(buffer); | ||||
|  | ||||
|         /* Note that we're normalized. */ | ||||
|         this->SetNormalized(); | ||||
|  | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     constexpr const char* GetString() const { | ||||
|         /* Check pre-conditions. */ | ||||
|         ASSERT(this->IsNormalized()); | ||||
|  | ||||
|         return m_str; | ||||
|     } | ||||
|  | ||||
|     constexpr size_t GetLength() const { | ||||
|         if (std::is_constant_evaluated()) { | ||||
|             return Strlen(this->GetString()); | ||||
|         } else { | ||||
|             return std::strlen(this->GetString()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     constexpr bool IsEmpty() const { | ||||
|         return *m_str == '\x00'; | ||||
|     } | ||||
|  | ||||
|     constexpr bool IsMatchHead(const char* p, size_t len) const { | ||||
|         return Strncmp(this->GetString(), p, len) == 0; | ||||
|     } | ||||
|  | ||||
|     Result Initialize(const Path& rhs) { | ||||
|         /* Check the other path is normalized. */ | ||||
|         const bool normalized = rhs.IsNormalized(); | ||||
|         R_UNLESS(normalized, ResultNotNormalized); | ||||
|  | ||||
|         /* Allocate buffer for our path. */ | ||||
|         const auto len = rhs.GetLength(); | ||||
|         R_TRY(this->Preallocate(len + 1)); | ||||
|  | ||||
|         /* Copy the path. */ | ||||
|         const size_t copied = Strlcpy<char>(m_write_buffer.Get(), rhs.GetString(), len + 1); | ||||
|         R_UNLESS(copied == len, ResultUnexpectedInPathA); | ||||
|  | ||||
|         /* Set normalized. */ | ||||
|         this->SetNormalized(); | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     Result Initialize(const char* path, size_t len) { | ||||
|         /* Check the path is valid. */ | ||||
|         R_UNLESS(path != nullptr, ResultNullptrArgument); | ||||
|  | ||||
|         /* Initialize. */ | ||||
|         R_TRY(this->InitializeImpl(path, len)); | ||||
|  | ||||
|         /* Set not normalized. */ | ||||
|         this->SetNotNormalized(); | ||||
|  | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     Result Initialize(const char* path) { | ||||
|         /* Check the path is valid. */ | ||||
|         R_UNLESS(path != nullptr, ResultNullptrArgument); | ||||
|  | ||||
|         R_RETURN(this->Initialize(path, std::strlen(path))); | ||||
|     } | ||||
|  | ||||
|     Result InitializeWithReplaceBackslash(const char* path) { | ||||
|         /* Check the path is valid. */ | ||||
|         R_UNLESS(path != nullptr, ResultNullptrArgument); | ||||
|  | ||||
|         /* Initialize. */ | ||||
|         R_TRY(this->InitializeImpl(path, std::strlen(path))); | ||||
|  | ||||
|         /* Replace slashes as desired. */ | ||||
|         if (const auto write_buffer_length = m_write_buffer.GetLength(); write_buffer_length > 1) { | ||||
|             Replace(m_write_buffer.Get(), write_buffer_length - 1, '\\', '/'); | ||||
|         } | ||||
|  | ||||
|         /* Set not normalized. */ | ||||
|         this->SetNotNormalized(); | ||||
|  | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     Result InitializeWithReplaceForwardSlashes(const char* path) { | ||||
|         /* Check the path is valid. */ | ||||
|         R_UNLESS(path != nullptr, ResultNullptrArgument); | ||||
|  | ||||
|         /* Initialize. */ | ||||
|         R_TRY(this->InitializeImpl(path, std::strlen(path))); | ||||
|  | ||||
|         /* Replace slashes as desired. */ | ||||
|         if (m_write_buffer.GetLength() > 1) { | ||||
|             if (auto* p = m_write_buffer.Get(); p[0] == '/' && p[1] == '/') { | ||||
|                 p[0] = '\\'; | ||||
|                 p[1] = '\\'; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* Set not normalized. */ | ||||
|         this->SetNotNormalized(); | ||||
|  | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     Result InitializeWithNormalization(const char* path, size_t size) { | ||||
|         /* Check the path is valid. */ | ||||
|         R_UNLESS(path != nullptr, ResultNullptrArgument); | ||||
|  | ||||
|         /* Initialize. */ | ||||
|         R_TRY(this->InitializeImpl(path, size)); | ||||
|  | ||||
|         /* Set not normalized. */ | ||||
|         this->SetNotNormalized(); | ||||
|  | ||||
|         /* Perform normalization. */ | ||||
|         PathFlags path_flags; | ||||
|         if (IsPathRelative(m_str)) { | ||||
|             path_flags.AllowRelativePath(); | ||||
|         } else if (IsWindowsPath(m_str, true)) { | ||||
|             path_flags.AllowWindowsPath(); | ||||
|         } else { | ||||
|             /* NOTE: In this case, Nintendo checks is normalized, then sets is normalized, then | ||||
|              * returns success. */ | ||||
|             /* This seems like a bug. */ | ||||
|             size_t dummy; | ||||
|             bool normalized; | ||||
|             R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy), | ||||
|                                               m_str)); | ||||
|  | ||||
|             this->SetNormalized(); | ||||
|             R_SUCCEED(); | ||||
|         } | ||||
|  | ||||
|         /* Normalize. */ | ||||
|         R_TRY(this->Normalize(path_flags)); | ||||
|  | ||||
|         this->SetNormalized(); | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     Result InitializeWithNormalization(const char* path) { | ||||
|         /* Check the path is valid. */ | ||||
|         R_UNLESS(path != nullptr, ResultNullptrArgument); | ||||
|  | ||||
|         R_RETURN(this->InitializeWithNormalization(path, std::strlen(path))); | ||||
|     } | ||||
|  | ||||
|     Result InitializeAsEmpty() { | ||||
|         /* Clear our buffer. */ | ||||
|         this->ClearBuffer(); | ||||
|  | ||||
|         /* Set normalized. */ | ||||
|         this->SetNormalized(); | ||||
|  | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     Result AppendChild(const char* child) { | ||||
|         /* Check the path is valid. */ | ||||
|         R_UNLESS(child != nullptr, ResultNullptrArgument); | ||||
|  | ||||
|         /* Basic checks. If we hvea a path and the child is empty, we have nothing to do. */ | ||||
|         const char* c = child; | ||||
|         if (m_str[0]) { | ||||
|             /* Skip an early separator. */ | ||||
|             if (*c == '/') { | ||||
|                 ++c; | ||||
|             } | ||||
|  | ||||
|             R_SUCCEED_IF(*c == '\x00'); | ||||
|         } | ||||
|  | ||||
|         /* If we don't have a string, we can just initialize. */ | ||||
|         auto cur_len = std::strlen(m_str); | ||||
|         if (cur_len == 0) { | ||||
|             R_RETURN(this->Initialize(child)); | ||||
|         } | ||||
|  | ||||
|         /* Remove a trailing separator. */ | ||||
|         if (m_str[cur_len - 1] == '/' || m_str[cur_len - 1] == '\\') { | ||||
|             --cur_len; | ||||
|         } | ||||
|  | ||||
|         /* Get the child path's length. */ | ||||
|         auto child_len = std::strlen(c); | ||||
|  | ||||
|         /* Reset our write buffer. */ | ||||
|         WriteBuffer old_write_buffer; | ||||
|         if (m_write_buffer.Get() != nullptr) { | ||||
|             old_write_buffer = std::move(m_write_buffer); | ||||
|             this->ClearBuffer(); | ||||
|         } | ||||
|  | ||||
|         /* Pre-allocate the new buffer. */ | ||||
|         R_TRY(this->Preallocate(cur_len + 1 + child_len + 1)); | ||||
|  | ||||
|         /* Get our write buffer. */ | ||||
|         auto* dst = m_write_buffer.Get(); | ||||
|         if (old_write_buffer.Get() != nullptr && cur_len > 0) { | ||||
|             Strlcpy<char>(dst, old_write_buffer.Get(), cur_len + 1); | ||||
|         } | ||||
|  | ||||
|         /* Add separator. */ | ||||
|         dst[cur_len] = '/'; | ||||
|  | ||||
|         /* Copy the child path. */ | ||||
|         const size_t copied = Strlcpy<char>(dst + cur_len + 1, c, child_len + 1); | ||||
|         R_UNLESS(copied == child_len, ResultUnexpectedInPathA); | ||||
|  | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     Result AppendChild(const Path& rhs) { | ||||
|         R_RETURN(this->AppendChild(rhs.GetString())); | ||||
|     } | ||||
|  | ||||
|     Result Combine(const Path& parent, const Path& child) { | ||||
|         /* Get the lengths. */ | ||||
|         const auto p_len = parent.GetLength(); | ||||
|         const auto c_len = child.GetLength(); | ||||
|  | ||||
|         /* Allocate our buffer. */ | ||||
|         R_TRY(this->Preallocate(p_len + c_len + 1)); | ||||
|  | ||||
|         /* Initialize as parent. */ | ||||
|         R_TRY(this->Initialize(parent)); | ||||
|  | ||||
|         /* If we're empty, we can just initialize as child. */ | ||||
|         if (this->IsEmpty()) { | ||||
|             R_TRY(this->Initialize(child)); | ||||
|         } else { | ||||
|             /* Otherwise, we should append the child. */ | ||||
|             R_TRY(this->AppendChild(child)); | ||||
|         } | ||||
|  | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     Result RemoveChild() { | ||||
|         /* If we don't have a write-buffer, ensure that we have one. */ | ||||
|         if (m_write_buffer.Get() == nullptr) { | ||||
|             if (const auto len = std::strlen(m_str); len > 0) { | ||||
|                 R_TRY(this->Preallocate(len)); | ||||
|                 Strlcpy<char>(m_write_buffer.Get(), m_str, len + 1); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* Check that it's possible for us to remove a child. */ | ||||
|         auto* p = m_write_buffer.Get(); | ||||
|         s32 len = std::strlen(p); | ||||
|         R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented); | ||||
|  | ||||
|         /* Handle a trailing separator. */ | ||||
|         if (len > 0 && (p[len - 1] == '\\' || p[len - 1] == '/')) { | ||||
|             --len; | ||||
|         } | ||||
|  | ||||
|         /* Remove the child path segment. */ | ||||
|         while ((--len) >= 0 && p[len]) { | ||||
|             if (p[len] == '/' || p[len] == '\\') { | ||||
|                 if (len > 0) { | ||||
|                     p[len] = 0; | ||||
|                 } else { | ||||
|                     p[1] = 0; | ||||
|                     len = 1; | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* Check that length remains > 0. */ | ||||
|         R_UNLESS(len > 0, ResultNotImplemented); | ||||
|  | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     Result Normalize(const PathFlags& flags) { | ||||
|         /* If we're already normalized, nothing to do. */ | ||||
|         R_SUCCEED_IF(this->IsNormalized()); | ||||
|  | ||||
|         /* Check if we're normalized. */ | ||||
|         bool normalized; | ||||
|         size_t dummy; | ||||
|         R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy), m_str, | ||||
|                                           flags)); | ||||
|  | ||||
|         /* If we're not normalized, normalize. */ | ||||
|         if (!normalized) { | ||||
|             /* Determine necessary buffer length. */ | ||||
|             auto len = m_write_buffer.GetLength(); | ||||
|             if (flags.IsRelativePathAllowed() && IsPathRelative(m_str)) { | ||||
|                 len += 2; | ||||
|             } | ||||
|             if (flags.IsWindowsPathAllowed() && IsWindowsPath(m_str, true)) { | ||||
|                 len += 1; | ||||
|             } | ||||
|  | ||||
|             /* Allocate a new buffer. */ | ||||
|             const size_t size = Common::AlignUp(len, WriteBufferAlignmentLength); | ||||
|             auto buf = WriteBuffer::Make(size); | ||||
|             R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique); | ||||
|  | ||||
|             /* Normalize into it. */ | ||||
|             R_TRY(PathFormatter::Normalize(buf.Get(), size, m_write_buffer.Get(), | ||||
|                                            m_write_buffer.GetLength(), flags)); | ||||
|  | ||||
|             /* Set the normalized buffer as our buffer. */ | ||||
|             this->SetModifiableBuffer(std::move(buf)); | ||||
|         } | ||||
|  | ||||
|         /* Set normalized. */ | ||||
|         this->SetNormalized(); | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     void ClearBuffer() { | ||||
|         m_write_buffer.ResetBuffer(); | ||||
|         m_str = EmptyPath; | ||||
|     } | ||||
|  | ||||
|     void SetModifiableBuffer(WriteBuffer&& buffer) { | ||||
|         /* Check pre-conditions. */ | ||||
|         ASSERT(buffer.Get() != nullptr); | ||||
|         ASSERT(buffer.GetLength() > 0); | ||||
|         ASSERT(Common::IsAligned(buffer.GetLength(), WriteBufferAlignmentLength)); | ||||
|  | ||||
|         /* Get whether we're normalized. */ | ||||
|         if (m_write_buffer.IsNormalized()) { | ||||
|             buffer.SetNormalized(); | ||||
|         } else { | ||||
|             buffer.SetNotNormalized(); | ||||
|         } | ||||
|  | ||||
|         /* Set write buffer. */ | ||||
|         m_write_buffer = std::move(buffer); | ||||
|         m_str = m_write_buffer.Get(); | ||||
|     } | ||||
|  | ||||
|     constexpr void SetReadOnlyBuffer(const char* buffer) { | ||||
|         m_str = buffer; | ||||
|         m_write_buffer.ResetBuffer(); | ||||
|     } | ||||
|  | ||||
|     Result Preallocate(size_t length) { | ||||
|         /* Allocate additional space, if needed. */ | ||||
|         if (length > m_write_buffer.GetLength()) { | ||||
|             /* Allocate buffer. */ | ||||
|             const size_t size = Common::AlignUp(length, WriteBufferAlignmentLength); | ||||
|             auto buf = WriteBuffer::Make(size); | ||||
|             R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique); | ||||
|  | ||||
|             /* Set write buffer. */ | ||||
|             this->SetModifiableBuffer(std::move(buf)); | ||||
|         } | ||||
|  | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     Result InitializeImpl(const char* path, size_t size) { | ||||
|         if (size > 0 && path[0]) { | ||||
|             /* Pre allocate a buffer for the path. */ | ||||
|             R_TRY(this->Preallocate(size + 1)); | ||||
|  | ||||
|             /* Copy the path. */ | ||||
|             const size_t copied = Strlcpy<char>(m_write_buffer.Get(), path, size + 1); | ||||
|             R_UNLESS(copied >= size, ResultUnexpectedInPathA); | ||||
|         } else { | ||||
|             /* We can just clear the buffer. */ | ||||
|             this->ClearBuffer(); | ||||
|         } | ||||
|  | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|  | ||||
|     constexpr char* GetWriteBuffer() { | ||||
|         ASSERT(m_write_buffer.Get() != nullptr); | ||||
|         return m_write_buffer.Get(); | ||||
|     } | ||||
|  | ||||
|     constexpr size_t GetWriteBufferLength() const { | ||||
|         return m_write_buffer.GetLength(); | ||||
|     } | ||||
|  | ||||
|     constexpr bool IsNormalized() const { | ||||
|         return m_write_buffer.IsNormalized(); | ||||
|     } | ||||
|  | ||||
|     constexpr void SetNormalized() { | ||||
|         m_write_buffer.SetNormalized(); | ||||
|     } | ||||
|  | ||||
|     constexpr void SetNotNormalized() { | ||||
|         m_write_buffer.SetNotNormalized(); | ||||
|     } | ||||
|  | ||||
| public: | ||||
|     bool operator==(const FileSys::Path& rhs) const { | ||||
|         return std::strcmp(this->GetString(), rhs.GetString()) == 0; | ||||
|     } | ||||
|     bool operator!=(const FileSys::Path& rhs) const { | ||||
|         return !(*this == rhs); | ||||
|     } | ||||
|     bool operator==(const char* p) const { | ||||
|         return std::strcmp(this->GetString(), p) == 0; | ||||
|     } | ||||
|     bool operator!=(const char* p) const { | ||||
|         return !(*this == p); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| inline Result SetUpFixedPath(FileSys::Path* out, const char* s) { | ||||
|     /* Verify the path is normalized. */ | ||||
|     bool normalized; | ||||
|     size_t dummy; | ||||
|     R_TRY(PathNormalizer::IsNormalized(std::addressof(normalized), std::addressof(dummy), s)); | ||||
|  | ||||
|     R_UNLESS(normalized, ResultInvalidPathFormat); | ||||
|  | ||||
|     /* Set the fixed path. */ | ||||
|     R_RETURN(out->SetShallowBuffer(s)); | ||||
| } | ||||
|  | ||||
| constexpr inline bool IsWindowsDriveRootPath(const FileSys::Path& path) { | ||||
|     const char* const str = path.GetString(); | ||||
|     return IsWindowsDrive(str) && | ||||
|            (str[2] == StringTraits::DirectorySeparator || | ||||
|             str[2] == StringTraits::AlternateDirectorySeparator) && | ||||
|            str[3] == StringTraits::NullTerminator; | ||||
| } | ||||
|  | ||||
| } // namespace FileSys | ||||
							
								
								
									
										1240
									
								
								src/core/file_sys/fs_path_utility.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1240
									
								
								src/core/file_sys/fs_path_utility.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										241
									
								
								src/core/file_sys/fs_string_util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								src/core/file_sys/fs_string_util.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/assert.h" | ||||
|  | ||||
| namespace FileSys { | ||||
|  | ||||
| template <typename T> | ||||
| constexpr int Strlen(const T* str) { | ||||
|     ASSERT(str != nullptr); | ||||
|  | ||||
|     int length = 0; | ||||
|     while (*str++) { | ||||
|         ++length; | ||||
|     } | ||||
|  | ||||
|     return length; | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| constexpr int Strnlen(const T* str, int count) { | ||||
|     ASSERT(str != nullptr); | ||||
|     ASSERT(count >= 0); | ||||
|  | ||||
|     int length = 0; | ||||
|     while (count-- && *str++) { | ||||
|         ++length; | ||||
|     } | ||||
|  | ||||
|     return length; | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| constexpr int Strncmp(const T* lhs, const T* rhs, int count) { | ||||
|     ASSERT(lhs != nullptr); | ||||
|     ASSERT(rhs != nullptr); | ||||
|     ASSERT(count >= 0); | ||||
|  | ||||
|     if (count == 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     T l, r; | ||||
|     do { | ||||
|         l = *(lhs++); | ||||
|         r = *(rhs++); | ||||
|     } while (l && (l == r) && (--count)); | ||||
|  | ||||
|     return l - r; | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| static constexpr int Strlcpy(T* dst, const T* src, int count) { | ||||
|     ASSERT(dst != nullptr); | ||||
|     ASSERT(src != nullptr); | ||||
|  | ||||
|     const T* cur = src; | ||||
|     if (count > 0) { | ||||
|         while ((--count) && *cur) { | ||||
|             *(dst++) = *(cur++); | ||||
|         } | ||||
|         *dst = 0; | ||||
|     } | ||||
|  | ||||
|     while (*cur) { | ||||
|         cur++; | ||||
|     } | ||||
|  | ||||
|     return static_cast<int>(cur - src); | ||||
| } | ||||
|  | ||||
| /* std::size() does not support zero-size C arrays. We're fixing that. */ | ||||
| template <class C> | ||||
| constexpr auto size(const C& c) -> decltype(c.size()) { | ||||
|     return std::size(c); | ||||
| } | ||||
|  | ||||
| template <class C> | ||||
| constexpr std::size_t size(const C& c) { | ||||
|     if constexpr (sizeof(C) == 0) { | ||||
|         return 0; | ||||
|     } else { | ||||
|         return std::size(c); | ||||
|     } | ||||
| } | ||||
|  | ||||
| enum CharacterEncodingResult { | ||||
|     CharacterEncodingResult_Success = 0, | ||||
|     CharacterEncodingResult_InsufficientLength = 1, | ||||
|     CharacterEncodingResult_InvalidFormat = 2, | ||||
| }; | ||||
|  | ||||
| namespace impl { | ||||
|  | ||||
| class CharacterEncodingHelper { | ||||
| public: | ||||
|     static constexpr int8_t Utf8NBytesInnerTable[0x100 + 1] = { | ||||
|         -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
|         1,  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
|         1,  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
|         1,  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||
|         1,  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
|         0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||
|         0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | ||||
|         2,  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, | ||||
|         3,  3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, | ||||
|     }; | ||||
|  | ||||
|     static constexpr char GetUtf8NBytes(size_t i) { | ||||
|         return static_cast<char>(Utf8NBytesInnerTable[1 + i]); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| } // namespace impl | ||||
|  | ||||
| constexpr inline CharacterEncodingResult ConvertCharacterUtf8ToUtf32(u32* dst, const char* src) { | ||||
|     /* Check pre-conditions. */ | ||||
|     ASSERT(dst != nullptr); | ||||
|     ASSERT(src != nullptr); | ||||
|  | ||||
|     /* Perform the conversion. */ | ||||
|     const auto* p = src; | ||||
|     switch (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[0]))) { | ||||
|     case 1: | ||||
|         *dst = static_cast<u32>(p[0]); | ||||
|         return CharacterEncodingResult_Success; | ||||
|     case 2: | ||||
|         if ((static_cast<u32>(p[0]) & 0x1E) != 0) { | ||||
|             if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == | ||||
|                 0) { | ||||
|                 *dst = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0); | ||||
|                 return CharacterEncodingResult_Success; | ||||
|             } | ||||
|         } | ||||
|         break; | ||||
|     case 3: | ||||
|         if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && | ||||
|             impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) { | ||||
|             const u32 c = (static_cast<u32>(p[0] & 0xF) << 12) | | ||||
|                           (static_cast<u32>(p[1] & 0x3F) << 6) | | ||||
|                           (static_cast<u32>(p[2] & 0x3F) << 0); | ||||
|             if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) { | ||||
|                 *dst = c; | ||||
|                 return CharacterEncodingResult_Success; | ||||
|             } | ||||
|         } | ||||
|         return CharacterEncodingResult_InvalidFormat; | ||||
|     case 4: | ||||
|         if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && | ||||
|             impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 && | ||||
|             impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) { | ||||
|             const u32 c = | ||||
|                 (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) | | ||||
|                 (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0); | ||||
|             if (c >= 0x10000 && c < 0x110000) { | ||||
|                 *dst = c; | ||||
|                 return CharacterEncodingResult_Success; | ||||
|             } | ||||
|         } | ||||
|         return CharacterEncodingResult_InvalidFormat; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     /* We failed to convert. */ | ||||
|     return CharacterEncodingResult_InvalidFormat; | ||||
| } | ||||
|  | ||||
| constexpr inline CharacterEncodingResult PickOutCharacterFromUtf8String(char* dst, | ||||
|                                                                         const char** str) { | ||||
|     /* Check pre-conditions. */ | ||||
|     ASSERT(dst != nullptr); | ||||
|     ASSERT(str != nullptr); | ||||
|     ASSERT(*str != nullptr); | ||||
|  | ||||
|     /* Clear the output. */ | ||||
|     dst[0] = 0; | ||||
|     dst[1] = 0; | ||||
|     dst[2] = 0; | ||||
|     dst[3] = 0; | ||||
|  | ||||
|     /* Perform the conversion. */ | ||||
|     const auto* p = *str; | ||||
|     u32 c = static_cast<u32>(*p); | ||||
|     switch (impl::CharacterEncodingHelper::GetUtf8NBytes(c)) { | ||||
|     case 1: | ||||
|         dst[0] = (*str)[0]; | ||||
|         ++(*str); | ||||
|         break; | ||||
|     case 2: | ||||
|         if ((p[0] & 0x1E) != 0) { | ||||
|             if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == | ||||
|                 0) { | ||||
|                 c = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0); | ||||
|                 dst[0] = (*str)[0]; | ||||
|                 dst[1] = (*str)[1]; | ||||
|                 (*str) += 2; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         return CharacterEncodingResult_InvalidFormat; | ||||
|     case 3: | ||||
|         if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && | ||||
|             impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) { | ||||
|             c = (static_cast<u32>(p[0] & 0xF) << 12) | (static_cast<u32>(p[1] & 0x3F) << 6) | | ||||
|                 (static_cast<u32>(p[2] & 0x3F) << 0); | ||||
|             if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) { | ||||
|                 dst[0] = (*str)[0]; | ||||
|                 dst[1] = (*str)[1]; | ||||
|                 dst[2] = (*str)[2]; | ||||
|                 (*str) += 3; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         return CharacterEncodingResult_InvalidFormat; | ||||
|     case 4: | ||||
|         if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && | ||||
|             impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 && | ||||
|             impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) { | ||||
|             c = (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) | | ||||
|                 (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0); | ||||
|             if (c >= 0x10000 && c < 0x110000) { | ||||
|                 dst[0] = (*str)[0]; | ||||
|                 dst[1] = (*str)[1]; | ||||
|                 dst[2] = (*str)[2]; | ||||
|                 dst[3] = (*str)[3]; | ||||
|                 (*str) += 4; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         return CharacterEncodingResult_InvalidFormat; | ||||
|     default: | ||||
|         return CharacterEncodingResult_InvalidFormat; | ||||
|     } | ||||
|  | ||||
|     return CharacterEncodingResult_Success; | ||||
| } | ||||
|  | ||||
| } // namespace FileSys | ||||
| @@ -1,6 +1,7 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "core/file_sys/fs_filesystem.h" | ||||
| #include "core/file_sys/savedata_factory.h" | ||||
| #include "core/hle/service/filesystem/fsp/fs_i_directory.h" | ||||
| #include "core/hle/service/ipc_helpers.h" | ||||
|   | ||||
| @@ -3,12 +3,14 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "core/file_sys/fs_filesystem.h" | ||||
| #include "core/file_sys/vfs/vfs.h" | ||||
| #include "core/hle/service/filesystem/filesystem.h" | ||||
| #include "core/hle/service/filesystem/fsp/fsp_util.h" | ||||
| #include "core/hle/service/service.h" | ||||
|  | ||||
| namespace FileSys { | ||||
| struct DirectoryEntry; | ||||
| } | ||||
|  | ||||
| namespace Service::FileSystem { | ||||
|  | ||||
| class IDirectory final : public ServiceFramework<IDirectory> { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user