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);
 | 
					    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
 | 
					} // namespace Common
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,7 +58,7 @@ add_library(core STATIC
 | 
				
			|||||||
    file_sys/fs_operate_range.h
 | 
					    file_sys/fs_operate_range.h
 | 
				
			||||||
    file_sys/fs_path.h
 | 
					    file_sys/fs_path.h
 | 
				
			||||||
    file_sys/fs_path_utility.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.cpp
 | 
				
			||||||
    file_sys/fsmitm_romfsbuild.h
 | 
					    file_sys/fsmitm_romfsbuild.h
 | 
				
			||||||
    file_sys/fssystem/fs_i_storage.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_switch_storage.h
 | 
				
			||||||
    file_sys/fssystem/fssystem_utility.cpp
 | 
					    file_sys/fssystem/fssystem_utility.cpp
 | 
				
			||||||
    file_sys/fssystem/fssystem_utility.h
 | 
					    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.cpp
 | 
				
			||||||
    file_sys/ips_layer.h
 | 
					    file_sys/ips_layer.h
 | 
				
			||||||
    file_sys/kernel_executable.cpp
 | 
					    file_sys/kernel_executable.cpp
 | 
				
			||||||
    file_sys/kernel_executable.h
 | 
					    file_sys/kernel_executable.h
 | 
				
			||||||
    file_sys/mode.h
 | 
					 | 
				
			||||||
    file_sys/nca_metadata.cpp
 | 
					    file_sys/nca_metadata.cpp
 | 
				
			||||||
    file_sys/nca_metadata.h
 | 
					    file_sys/nca_metadata.h
 | 
				
			||||||
    file_sys/partition_filesystem.cpp
 | 
					    file_sys/partition_filesystem.cpp
 | 
				
			||||||
@@ -215,7 +200,6 @@ add_library(core STATIC
 | 
				
			|||||||
    hle/kernel/board/nintendo/nx/secure_monitor.h
 | 
					    hle/kernel/board/nintendo/nx/secure_monitor.h
 | 
				
			||||||
    hle/kernel/code_set.cpp
 | 
					    hle/kernel/code_set.cpp
 | 
				
			||||||
    hle/kernel/code_set.h
 | 
					    hle/kernel/code_set.h
 | 
				
			||||||
    hle/kernel/svc_results.h
 | 
					 | 
				
			||||||
    hle/kernel/global_scheduler_context.cpp
 | 
					    hle/kernel/global_scheduler_context.cpp
 | 
				
			||||||
    hle/kernel/global_scheduler_context.h
 | 
					    hle/kernel/global_scheduler_context.h
 | 
				
			||||||
    hle/kernel/init/init_slab_setup.cpp
 | 
					    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_arbiter.h
 | 
				
			||||||
    hle/kernel/k_address_space_info.cpp
 | 
					    hle/kernel/k_address_space_info.cpp
 | 
				
			||||||
    hle/kernel/k_address_space_info.h
 | 
					    hle/kernel/k_address_space_info.h
 | 
				
			||||||
 | 
					    hle/kernel/k_affinity_mask.h
 | 
				
			||||||
    hle/kernel/k_auto_object.cpp
 | 
					    hle/kernel/k_auto_object.cpp
 | 
				
			||||||
    hle/kernel/k_auto_object.h
 | 
					    hle/kernel/k_auto_object.h
 | 
				
			||||||
    hle/kernel/k_auto_object_container.cpp
 | 
					    hle/kernel/k_auto_object_container.cpp
 | 
				
			||||||
    hle/kernel/k_auto_object_container.h
 | 
					    hle/kernel/k_auto_object_container.h
 | 
				
			||||||
    hle/kernel/k_affinity_mask.h
 | 
					 | 
				
			||||||
    hle/kernel/k_capabilities.cpp
 | 
					    hle/kernel/k_capabilities.cpp
 | 
				
			||||||
    hle/kernel/k_capabilities.h
 | 
					    hle/kernel/k_capabilities.h
 | 
				
			||||||
    hle/kernel/k_class_token.cpp
 | 
					    hle/kernel/k_class_token.cpp
 | 
				
			||||||
@@ -253,9 +237,9 @@ add_library(core STATIC
 | 
				
			|||||||
    hle/kernel/k_event_info.h
 | 
					    hle/kernel/k_event_info.h
 | 
				
			||||||
    hle/kernel/k_handle_table.cpp
 | 
					    hle/kernel/k_handle_table.cpp
 | 
				
			||||||
    hle/kernel/k_handle_table.h
 | 
					    hle/kernel/k_handle_table.h
 | 
				
			||||||
    hle/kernel/k_hardware_timer_base.h
 | 
					 | 
				
			||||||
    hle/kernel/k_hardware_timer.cpp
 | 
					    hle/kernel/k_hardware_timer.cpp
 | 
				
			||||||
    hle/kernel/k_hardware_timer.h
 | 
					    hle/kernel/k_hardware_timer.h
 | 
				
			||||||
 | 
					    hle/kernel/k_hardware_timer_base.h
 | 
				
			||||||
    hle/kernel/k_interrupt_manager.cpp
 | 
					    hle/kernel/k_interrupt_manager.cpp
 | 
				
			||||||
    hle/kernel/k_interrupt_manager.h
 | 
					    hle/kernel/k_interrupt_manager.h
 | 
				
			||||||
    hle/kernel/k_light_client_session.cpp
 | 
					    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_bitmap.h
 | 
				
			||||||
    hle/kernel/k_page_buffer.cpp
 | 
					    hle/kernel/k_page_buffer.cpp
 | 
				
			||||||
    hle/kernel/k_page_buffer.h
 | 
					    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.cpp
 | 
				
			||||||
    hle/kernel/k_page_group.h
 | 
					    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.h
 | 
				
			||||||
    hle/kernel/k_page_table_base.cpp
 | 
					    hle/kernel/k_page_table_base.cpp
 | 
				
			||||||
    hle/kernel/k_page_table_base.h
 | 
					    hle/kernel/k_page_table_base.h
 | 
				
			||||||
@@ -350,8 +334,6 @@ add_library(core STATIC
 | 
				
			|||||||
    hle/kernel/slab_helpers.h
 | 
					    hle/kernel/slab_helpers.h
 | 
				
			||||||
    hle/kernel/svc.cpp
 | 
					    hle/kernel/svc.cpp
 | 
				
			||||||
    hle/kernel/svc.h
 | 
					    hle/kernel/svc.h
 | 
				
			||||||
    hle/kernel/svc_common.h
 | 
					 | 
				
			||||||
    hle/kernel/svc_types.h
 | 
					 | 
				
			||||||
    hle/kernel/svc/svc_activity.cpp
 | 
					    hle/kernel/svc/svc_activity.cpp
 | 
				
			||||||
    hle/kernel/svc/svc_address_arbiter.cpp
 | 
					    hle/kernel/svc/svc_address_arbiter.cpp
 | 
				
			||||||
    hle/kernel/svc/svc_address_translation.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_thread_profiler.cpp
 | 
				
			||||||
    hle/kernel/svc/svc_tick.cpp
 | 
					    hle/kernel/svc/svc_tick.cpp
 | 
				
			||||||
    hle/kernel/svc/svc_transfer_memory.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/result.h
 | 
				
			||||||
    hle/service/acc/acc.cpp
 | 
					    hle/service/acc/acc.cpp
 | 
				
			||||||
    hle/service/acc/acc.h
 | 
					    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_filesystem.h
 | 
				
			||||||
    hle/service/filesystem/fsp/fs_i_storage.cpp
 | 
					    hle/service/filesystem/fsp/fs_i_storage.cpp
 | 
				
			||||||
    hle/service/filesystem/fsp/fs_i_storage.h
 | 
					    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.cpp
 | 
				
			||||||
    hle/service/filesystem/fsp/fsp_srv.h
 | 
					    hle/service/filesystem/fsp/fsp_srv.h
 | 
				
			||||||
    hle/service/filesystem/fsp_ldr.cpp
 | 
					    hle/service/filesystem/fsp/fsp_util.h
 | 
				
			||||||
    hle/service/filesystem/fsp_ldr.h
 | 
					 | 
				
			||||||
    hle/service/filesystem/fsp_pr.cpp
 | 
					 | 
				
			||||||
    hle/service/filesystem/fsp_pr.h
 | 
					 | 
				
			||||||
    hle/service/filesystem/romfs_controller.cpp
 | 
					    hle/service/filesystem/romfs_controller.cpp
 | 
				
			||||||
    hle/service/filesystem/romfs_controller.h
 | 
					    hle/service/filesystem/romfs_controller.h
 | 
				
			||||||
    hle/service/filesystem/save_data_controller.cpp
 | 
					    hle/service/filesystem/save_data_controller.cpp
 | 
				
			||||||
    hle/service/filesystem/save_data_controller.h
 | 
					    hle/service/filesystem/save_data_controller.h
 | 
				
			||||||
    hle/service/filesystem/fsp_util.h
 | 
					 | 
				
			||||||
    hle/service/fgm/fgm.cpp
 | 
					    hle/service/fgm/fgm.cpp
 | 
				
			||||||
    hle/service/fgm/fgm.h
 | 
					    hle/service/fgm/fgm.h
 | 
				
			||||||
    hle/service/friend/friend.cpp
 | 
					    hle/service/friend/friend.cpp
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,10 +73,21 @@ constexpr Result ResultUnexpectedInCompressedStorageA{ErrorModule::FS, 5324};
 | 
				
			|||||||
constexpr Result ResultUnexpectedInCompressedStorageB{ErrorModule::FS, 5325};
 | 
					constexpr Result ResultUnexpectedInCompressedStorageB{ErrorModule::FS, 5325};
 | 
				
			||||||
constexpr Result ResultUnexpectedInCompressedStorageC{ErrorModule::FS, 5326};
 | 
					constexpr Result ResultUnexpectedInCompressedStorageC{ErrorModule::FS, 5326};
 | 
				
			||||||
constexpr Result ResultUnexpectedInCompressedStorageD{ErrorModule::FS, 5327};
 | 
					constexpr Result ResultUnexpectedInCompressedStorageD{ErrorModule::FS, 5327};
 | 
				
			||||||
 | 
					constexpr Result ResultUnexpectedInPathA{ErrorModule::FS, 5328};
 | 
				
			||||||
constexpr Result ResultInvalidArgument{ErrorModule::FS, 6001};
 | 
					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 ResultInvalidOffset{ErrorModule::FS, 6061};
 | 
				
			||||||
constexpr Result ResultInvalidSize{ErrorModule::FS, 6062};
 | 
					constexpr Result ResultInvalidSize{ErrorModule::FS, 6062};
 | 
				
			||||||
constexpr Result ResultNullptrArgument{ErrorModule::FS, 6063};
 | 
					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 ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325};
 | 
				
			||||||
constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387};
 | 
					constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387};
 | 
				
			||||||
constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388};
 | 
					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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
 | 
				
			||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "core/file_sys/fs_filesystem.h"
 | 
				
			||||||
#include "core/file_sys/savedata_factory.h"
 | 
					#include "core/file_sys/savedata_factory.h"
 | 
				
			||||||
#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
 | 
					#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
 | 
				
			||||||
#include "core/hle/service/ipc_helpers.h"
 | 
					#include "core/hle/service/ipc_helpers.h"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,12 +3,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "core/file_sys/fs_filesystem.h"
 | 
					 | 
				
			||||||
#include "core/file_sys/vfs/vfs.h"
 | 
					#include "core/file_sys/vfs/vfs.h"
 | 
				
			||||||
#include "core/hle/service/filesystem/filesystem.h"
 | 
					#include "core/hle/service/filesystem/filesystem.h"
 | 
				
			||||||
#include "core/hle/service/filesystem/fsp/fsp_util.h"
 | 
					 | 
				
			||||||
#include "core/hle/service/service.h"
 | 
					#include "core/hle/service/service.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace FileSys {
 | 
				
			||||||
 | 
					struct DirectoryEntry;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Service::FileSystem {
 | 
					namespace Service::FileSystem {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class IDirectory final : public ServiceFramework<IDirectory> {
 | 
					class IDirectory final : public ServiceFramework<IDirectory> {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user