Browse Source
Merge pull request #12707 from FearlessTobi/fs-housekeeping
Merge pull request #12707 from FearlessTobi/fs-housekeeping
fs: Various cleanups & add path class for later usence_cpp
committed by
GitHub
141 changed files with 3350 additions and 974 deletions
-
12src/android/app/src/main/jni/game_metadata.cpp
-
15src/android/app/src/main/jni/native.cpp
-
18src/common/overflow.h
-
161src/core/CMakeLists.txt
-
12src/core/core.cpp
-
2src/core/core.h
-
2src/core/crypto/aes_util.h
-
2src/core/crypto/encryption_layer.h
-
6src/core/crypto/partition_data_manager.cpp
-
2src/core/crypto/partition_data_manager.h
-
5src/core/file_sys/bis_factory.cpp
-
2src/core/file_sys/bis_factory.h
-
4src/core/file_sys/card_image.cpp
-
2src/core/file_sys/card_image.h
-
2src/core/file_sys/content_archive.cpp
-
2src/core/file_sys/content_archive.h
-
2src/core/file_sys/control_metadata.cpp
-
2src/core/file_sys/control_metadata.h
-
39src/core/file_sys/directory.h
-
26src/core/file_sys/errors.h
-
33src/core/file_sys/fs_directory.h
-
65src/core/file_sys/fs_file.h
-
39src/core/file_sys/fs_filesystem.h
-
40src/core/file_sys/fs_memory_management.h
-
22src/core/file_sys/fs_operate_range.h
-
566src/core/file_sys/fs_path.h
-
1239src/core/file_sys/fs_path_utility.h
-
226src/core/file_sys/fs_string_util.h
-
4src/core/file_sys/fsmitm_romfsbuild.cpp
-
2src/core/file_sys/fsmitm_romfsbuild.h
-
2src/core/file_sys/fssystem/fs_i_storage.h
-
2src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
-
2src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h
-
2src/core/file_sys/fssystem/fssystem_bucket_tree.h
-
2src/core/file_sys/fssystem/fssystem_compressed_storage.h
-
2src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp
-
2src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h
-
2src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h
-
4src/core/file_sys/fssystem/fssystem_indirect_storage.h
-
2src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h
-
4src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp
-
2src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h
-
2src/core/file_sys/fssystem/fssystem_nca_reader.cpp
-
2src/core/file_sys/ips_layer.cpp
-
2src/core/file_sys/ips_layer.h
-
2src/core/file_sys/kernel_executable.cpp
-
2src/core/file_sys/kernel_executable.h
-
23src/core/file_sys/mode.h
-
2src/core/file_sys/nca_metadata.cpp
-
2src/core/file_sys/nca_metadata.h
-
2src/core/file_sys/partition_filesystem.cpp
-
2src/core/file_sys/partition_filesystem.h
-
6src/core/file_sys/patch_manager.cpp
-
2src/core/file_sys/patch_manager.h
-
2src/core/file_sys/program_metadata.cpp
-
2src/core/file_sys/program_metadata.h
-
2src/core/file_sys/registered_cache.cpp
-
2src/core/file_sys/registered_cache.h
-
10src/core/file_sys/romfs.cpp
-
2src/core/file_sys/romfs.h
-
2src/core/file_sys/romfs_factory.h
-
2src/core/file_sys/savedata_factory.cpp
-
2src/core/file_sys/savedata_factory.h
-
2src/core/file_sys/sdmc_factory.cpp
-
2src/core/file_sys/sdmc_factory.h
-
2src/core/file_sys/submission_package.h
-
2src/core/file_sys/system_archive/mii_model.cpp
-
2src/core/file_sys/system_archive/mii_model.h
-
2src/core/file_sys/system_archive/ng_word.cpp
-
2src/core/file_sys/system_archive/ng_word.h
-
2src/core/file_sys/system_archive/shared_font.cpp
-
2src/core/file_sys/system_archive/shared_font.h
-
2src/core/file_sys/system_archive/system_archive.h
-
2src/core/file_sys/system_archive/system_version.cpp
-
2src/core/file_sys/system_archive/system_version.h
-
2src/core/file_sys/system_archive/time_zone_binary.cpp
-
2src/core/file_sys/system_archive/time_zone_binary.h
-
29src/core/file_sys/vfs/vfs.cpp
-
13src/core/file_sys/vfs/vfs.h
-
4src/core/file_sys/vfs/vfs_cached.cpp
-
2src/core/file_sys/vfs/vfs_cached.h
-
4src/core/file_sys/vfs/vfs_concat.cpp
-
2src/core/file_sys/vfs/vfs_concat.h
-
2src/core/file_sys/vfs/vfs_layered.cpp
-
2src/core/file_sys/vfs/vfs_layered.h
-
2src/core/file_sys/vfs/vfs_offset.cpp
-
2src/core/file_sys/vfs/vfs_offset.h
-
55src/core/file_sys/vfs/vfs_real.cpp
-
27src/core/file_sys/vfs/vfs_real.h
-
2src/core/file_sys/vfs/vfs_static.h
-
0src/core/file_sys/vfs/vfs_types.h
-
2src/core/file_sys/vfs/vfs_vector.cpp
-
2src/core/file_sys/vfs/vfs_vector.h
-
2src/core/file_sys/xts_archive.cpp
-
2src/core/file_sys/xts_archive.h
-
6src/core/frontend/applets/error.cpp
-
38src/core/hle/result.h
-
4src/core/hle/service/am/applets/applet_error.cpp
-
8src/core/hle/service/am/applets/applet_web_browser.cpp
-
2src/core/hle/service/am/applets/applet_web_browser.h
@ -1,39 +0,0 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <cstddef> |
|||
#include "common/common_funcs.h" |
|||
#include "common/common_types.h" |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////////////////// |
|||
// FileSys namespace |
|||
|
|||
namespace FileSys { |
|||
|
|||
enum class EntryType : u8 { |
|||
Directory = 0, |
|||
File = 1, |
|||
}; |
|||
|
|||
// Structure of a directory entry, from |
|||
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry |
|||
struct Entry { |
|||
Entry(std::string_view view, EntryType entry_type, u64 entry_size) |
|||
: type{entry_type}, file_size{entry_size} { |
|||
const std::size_t copy_size = view.copy(filename, std::size(filename) - 1); |
|||
filename[copy_size] = '\0'; |
|||
} |
|||
|
|||
char filename[0x301]; |
|||
INSERT_PADDING_BYTES(3); |
|||
EntryType type; |
|||
INSERT_PADDING_BYTES(3); |
|||
u64 file_size; |
|||
}; |
|||
static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x310 bytes long!"); |
|||
static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry."); |
|||
static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry."); |
|||
|
|||
} // namespace FileSys |
|||
@ -0,0 +1,33 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
namespace FileSys { |
|||
|
|||
constexpr inline size_t EntryNameLengthMax = 0x300; |
|||
|
|||
struct DirectoryEntry { |
|||
DirectoryEntry(std::string_view view, s8 entry_type, u64 entry_size) |
|||
: type{entry_type}, file_size{static_cast<s64>(entry_size)} { |
|||
const std::size_t copy_size = view.copy(name, std::size(name) - 1); |
|||
name[copy_size] = '\0'; |
|||
} |
|||
|
|||
char name[EntryNameLengthMax + 1]; |
|||
INSERT_PADDING_BYTES(3); |
|||
s8 type; |
|||
INSERT_PADDING_BYTES(3); |
|||
s64 file_size; |
|||
}; |
|||
|
|||
static_assert(sizeof(DirectoryEntry) == 0x310, |
|||
"Directory Entry struct isn't exactly 0x310 bytes long!"); |
|||
static_assert(offsetof(DirectoryEntry, type) == 0x304, "Wrong offset for type in Entry."); |
|||
static_assert(offsetof(DirectoryEntry, file_size) == 0x308, "Wrong offset for file_size in Entry."); |
|||
|
|||
struct DirectoryHandle { |
|||
void* handle; |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
@ -0,0 +1,65 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_types.h" |
|||
|
|||
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 |
|||
@ -0,0 +1,39 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_funcs.h" |
|||
#include "common/common_types.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
enum class OpenMode : u32 { |
|||
Read = (1 << 0), |
|||
Write = (1 << 1), |
|||
AllowAppend = (1 << 2), |
|||
|
|||
ReadWrite = (Read | Write), |
|||
All = (ReadWrite | AllowAppend), |
|||
}; |
|||
DECLARE_ENUM_FLAG_OPERATORS(OpenMode) |
|||
|
|||
enum class OpenDirectoryMode : u64 { |
|||
Directory = (1 << 0), |
|||
File = (1 << 1), |
|||
|
|||
All = (Directory | File), |
|||
}; |
|||
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode) |
|||
|
|||
enum class DirectoryEntryType : u8 { |
|||
Directory = 0, |
|||
File = 1, |
|||
}; |
|||
|
|||
enum class CreateOption : u8 { |
|||
None = (0 << 0), |
|||
BigFile = (1 << 0), |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
@ -0,0 +1,40 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <mutex> |
|||
#include "common/alignment.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
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) { |
|||
return AllocateUnsafe(size); |
|||
} |
|||
|
|||
void Deallocate(void* ptr, size_t size) { |
|||
// If the pointer is non-null, deallocate it |
|||
if (ptr != nullptr) { |
|||
DeallocateUnsafe(ptr, size); |
|||
} |
|||
} |
|||
|
|||
} // namespace FileSys |
|||
@ -0,0 +1,22 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_types.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
enum class OperationId : s64 { |
|||
FillZero = 0, |
|||
DestroySignature = 1, |
|||
Invalidate = 2, |
|||
QueryRange = 3, |
|||
QueryUnpreparedRange = 4, |
|||
QueryLazyLoadCompletionRate = 5, |
|||
SetLazyLoadPriority = 6, |
|||
|
|||
ReadLazyLoadFileForciblyForDebug = 10001, |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
@ -0,0 +1,566 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2024 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() = default; |
|||
|
|||
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 have 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 |
|||
1239
src/core/file_sys/fs_path_utility.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,226 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2024 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); |
|||
} |
|||
|
|||
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,23 +0,0 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_funcs.h" |
|||
#include "common/common_types.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
enum class Mode : u32 { |
|||
Read = 1 << 0, |
|||
Write = 1 << 1, |
|||
ReadWrite = Read | Write, |
|||
Append = 1 << 2, |
|||
ReadAppend = Read | Append, |
|||
WriteAppend = Write | Append, |
|||
All = ReadWrite | Append, |
|||
}; |
|||
|
|||
DECLARE_ENUM_FLAG_OPERATORS(Mode) |
|||
|
|||
} // namespace FileSys |
|||
@ -1,8 +1,8 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include "core/file_sys/vfs_cached.h"
|
|||
#include "core/file_sys/vfs_types.h"
|
|||
#include "core/file_sys/vfs/vfs_cached.h"
|
|||
#include "core/file_sys/vfs/vfs_types.h"
|
|||
|
|||
namespace FileSys { |
|||
|
|||
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue