mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-19 03:33:56 +00:00
Implement root fs handling
This commit is contained in:
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@@ -224,8 +224,13 @@ jobs:
|
||||
name: Temp ${{ matrix.filesystem }} Root FS
|
||||
path: build/${{matrix.preset}}/artifacts/root
|
||||
|
||||
- name: Copy Test Sample
|
||||
run: cp build/${{matrix.preset}}/artifacts/test-sample.exe build/${{matrix.preset}}/artifacts/root/filesys/c/
|
||||
|
||||
- name: CMake Test
|
||||
run: cd build/${{matrix.preset}} && ctest --verbose
|
||||
env:
|
||||
EMULATOR_ROOT: ${{github.workspace}}/build/${{matrix.preset}}/artifacts/root
|
||||
|
||||
summary:
|
||||
name: Pipeline Summary
|
||||
|
||||
@@ -91,6 +91,8 @@ if(MSVC)
|
||||
momo_add_release_link_options(
|
||||
#/LTCG
|
||||
)
|
||||
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
##########################################
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace
|
||||
bool use_gdb{false};
|
||||
bool concise_logging{false};
|
||||
bool verbose_logging{false};
|
||||
std::string registry_path{"./registry"};
|
||||
std::string root_filesystem{"./root"};
|
||||
};
|
||||
|
||||
void watch_system_objects(windows_emulator& win_emu, const bool cache_logging)
|
||||
@@ -107,7 +107,7 @@ namespace
|
||||
|
||||
emulator_settings settings{
|
||||
.application = args[0],
|
||||
.registry_directory = options.registry_path,
|
||||
.root_filesystem = options.root_filesystem,
|
||||
.arguments = parse_arguments(args),
|
||||
.silent_until_main = options.concise_logging,
|
||||
};
|
||||
@@ -213,10 +213,10 @@ namespace
|
||||
{
|
||||
if (args.size() < 2)
|
||||
{
|
||||
throw std::runtime_error("No registry path provided after -r");
|
||||
throw std::runtime_error("No root path provided after -r");
|
||||
}
|
||||
arg_it = args.erase(arg_it);
|
||||
options.registry_path = args[0];
|
||||
options.root_filesystem = args[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#if _WIN32
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#define _CRT_NO_POSIX_ERROR_CODES
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -205,6 +206,26 @@ namespace utils
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_list(std::list<T>& result)
|
||||
{
|
||||
const auto size = this->read<uint64_t>();
|
||||
result.clear();
|
||||
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
result.emplace_back(this->read<T>());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::list<T> read_list()
|
||||
{
|
||||
std::list<T> result{};
|
||||
this->read_list(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
void read_map(Map& map)
|
||||
{
|
||||
@@ -397,6 +418,17 @@ namespace utils
|
||||
this->write_span(std::span(vec));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_list(const std::list<T> vec)
|
||||
{
|
||||
this->write(static_cast<uint64_t>(vec.size()));
|
||||
|
||||
for (const auto& v : vec)
|
||||
{
|
||||
this->write(v);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_string(const std::basic_string_view<T> str)
|
||||
{
|
||||
|
||||
@@ -42,12 +42,12 @@ namespace
|
||||
|
||||
struct fuzzer_executer : fuzzer::executer
|
||||
{
|
||||
windows_emulator emu{};
|
||||
windows_emulator emu{"./"}; // TODO: Fix root directory
|
||||
std::span<const std::byte> emulator_data{};
|
||||
std::unordered_set<uint64_t> visited_blocks{};
|
||||
const std::function<fuzzer::coverage_functor>* handler{nullptr};
|
||||
|
||||
fuzzer_executer(std::span<const std::byte> data)
|
||||
fuzzer_executer(const std::span<const std::byte> data)
|
||||
: emulator_data(data)
|
||||
{
|
||||
emu.fuzzing = true;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <gtest/gtest.h>
|
||||
#include <windows_emulator.hpp>
|
||||
|
||||
@@ -20,9 +21,21 @@
|
||||
|
||||
namespace test
|
||||
{
|
||||
inline std::filesystem::path get_emulator_root()
|
||||
{
|
||||
auto* env = getenv("EMULATOR_ROOT");
|
||||
if (!env)
|
||||
{
|
||||
throw std::runtime_error("No EMULATOR_ROOT set!");
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
inline windows_emulator create_sample_emulator(emulator_settings settings, emulator_callbacks callbacks = {})
|
||||
{
|
||||
settings.application = "./test-sample.exe";
|
||||
settings.application = "c:/test-sample.exe";
|
||||
settings.root_filesystem = get_emulator_root();
|
||||
return windows_emulator{std::move(settings), std::move(callbacks)};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace test
|
||||
|
||||
utils::buffer_deserializer deserializer{serializer1.get_buffer()};
|
||||
|
||||
windows_emulator new_emu{};
|
||||
windows_emulator new_emu{get_emulator_root()};
|
||||
new_emu.deserialize(deserializer);
|
||||
|
||||
utils::buffer_serializer serializer2{};
|
||||
@@ -57,7 +57,7 @@ namespace test
|
||||
|
||||
utils::buffer_deserializer deserializer{serializer.get_buffer()};
|
||||
|
||||
windows_emulator new_emu{};
|
||||
windows_emulator new_emu{get_emulator_root()};
|
||||
new_emu.log.disable_output(true);
|
||||
new_emu.deserialize(deserializer);
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
#include "std_include.hpp"
|
||||
@@ -1,85 +1,67 @@
|
||||
#pragma once
|
||||
#include "std_include.hpp"
|
||||
#include <utils/path_key.hpp>
|
||||
#include "windows_path.hpp"
|
||||
|
||||
class file_system
|
||||
{
|
||||
public:
|
||||
static constexpr std::u16string_view nt_prefix = u"\\??\\";
|
||||
|
||||
file_system(std::filesystem::path root, std::u16string working_dir)
|
||||
file_system(std::filesystem::path root, windows_path working_dir)
|
||||
: root_(std::move(root)),
|
||||
working_dir_(std::move(working_dir))
|
||||
{
|
||||
}
|
||||
|
||||
static std::filesystem::path canonicalize_windows_path(const std::filesystem::path& file)
|
||||
std::filesystem::path translate(const windows_path& win_path) const
|
||||
{
|
||||
const auto wide_file = file.u16string();
|
||||
|
||||
if (!wide_file.starts_with(nt_prefix))
|
||||
if (win_path.is_absolute())
|
||||
{
|
||||
return file;
|
||||
return this->root_ / win_path.to_portable_path();
|
||||
}
|
||||
|
||||
return canonicalize_windows_path(wide_file.substr(nt_prefix.size()));
|
||||
return this->root_ / (this->working_dir_ / win_path).to_portable_path();
|
||||
}
|
||||
|
||||
static std::filesystem::path canonicalize(const std::filesystem::path& path)
|
||||
{
|
||||
return utils::path_key::canonicalize_path(canonicalize_windows_path(path));
|
||||
}
|
||||
|
||||
static std::filesystem::path canonicalize_drive_letter(const std::filesystem::path& path)
|
||||
{
|
||||
auto canonical_path = canonicalize(path);
|
||||
if (canonical_path.empty() || !path.has_root_path())
|
||||
{
|
||||
return canonical_path;
|
||||
}
|
||||
|
||||
return adapt_drive_component(canonical_path);
|
||||
}
|
||||
|
||||
std::filesystem::path translate(const std::filesystem::path& win_path) const
|
||||
{
|
||||
const auto absolute_win_dir = make_absolute_windows_path(win_path.u16string());
|
||||
return this->root_ / canonicalize_drive_letter(absolute_win_dir);
|
||||
}
|
||||
|
||||
void set_working_directory(std::u16string working_dir)
|
||||
void set_working_directory(windows_path working_dir)
|
||||
{
|
||||
this->working_dir_ = std::move(working_dir);
|
||||
}
|
||||
|
||||
const std::u16string& get_working_directory() const
|
||||
const windows_path& get_working_directory() const
|
||||
{
|
||||
return this->working_dir_;
|
||||
}
|
||||
|
||||
windows_path local_to_windows_path(const std::filesystem::path& local_path) const
|
||||
{
|
||||
const auto absolute_local_path = absolute(local_path);
|
||||
const auto relative_path = relative(absolute_local_path, this->root_);
|
||||
|
||||
if (relative_path.empty() || *relative_path.begin() == "..")
|
||||
{
|
||||
throw std::runtime_error("Path '" + local_path.string() + "' is not within the root filesystem!");
|
||||
}
|
||||
|
||||
char drive{};
|
||||
std::list<std::u16string> folders{};
|
||||
|
||||
for (auto i = relative_path.begin(); i != relative_path.end(); ++i)
|
||||
{
|
||||
if (i == relative_path.begin())
|
||||
{
|
||||
const auto str = i->string();
|
||||
assert(str.size() == 1);
|
||||
drive = str[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
folders.push_back(i->u16string());
|
||||
}
|
||||
}
|
||||
|
||||
return windows_path{drive, std::move(folders)};
|
||||
}
|
||||
|
||||
private:
|
||||
std::filesystem::path root_;
|
||||
std::u16string working_dir_;
|
||||
|
||||
std::u16string make_absolute_windows_path(const std::u16string& path) const
|
||||
{
|
||||
if (!path.starts_with(nt_prefix) && (path.size() < 2 || path[1] != ':'))
|
||||
{
|
||||
return this->working_dir_ + u'/' + path;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static std::filesystem::path adapt_drive_component(const std::filesystem::path& original_path)
|
||||
{
|
||||
auto root_name = original_path.root_name().u16string();
|
||||
|
||||
if (!root_name.empty() && root_name.back() == u':')
|
||||
{
|
||||
root_name.pop_back();
|
||||
}
|
||||
|
||||
return root_name + original_path.root_directory().u16string() + original_path.relative_path().u16string();
|
||||
}
|
||||
std::filesystem::path root_{};
|
||||
windows_path working_dir_{};
|
||||
};
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace
|
||||
kusd.QpcFrequency = std::chrono::steady_clock::period::den;
|
||||
}
|
||||
|
||||
constexpr std::wstring_view root_dir{L"C:\\WINDOWS"};
|
||||
constexpr std::u16string_view root_dir{u"C:\\WINDOWS"};
|
||||
memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2);
|
||||
|
||||
kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386;
|
||||
|
||||
@@ -28,8 +28,8 @@ namespace utils
|
||||
|
||||
static void serialize(buffer_serializer& buffer, const mapped_module& mod)
|
||||
{
|
||||
buffer.write_string(mod.name);
|
||||
buffer.write(mod.path.u16string());
|
||||
buffer.write(mod.name);
|
||||
buffer.write(mod.path);
|
||||
|
||||
buffer.write(mod.image_base);
|
||||
buffer.write(mod.size_of_image);
|
||||
@@ -41,8 +41,8 @@ namespace utils
|
||||
|
||||
static void deserialize(buffer_deserializer& buffer, mapped_module& mod)
|
||||
{
|
||||
mod.name = buffer.read_string();
|
||||
mod.path = buffer.read_string<std::u16string::value_type>();
|
||||
buffer.read(mod.name);
|
||||
buffer.read(mod.path);
|
||||
|
||||
buffer.read(mod.image_base);
|
||||
buffer.read(mod.size_of_image);
|
||||
@@ -59,7 +59,7 @@ module_manager::module_manager(emulator& emu, file_system& file_sys)
|
||||
{
|
||||
}
|
||||
|
||||
mapped_module* module_manager::map_module(const std::filesystem::path& file, const logger& logger)
|
||||
mapped_module* module_manager::map_module(const windows_path& file, const logger& logger)
|
||||
{
|
||||
return this->map_local_module(this->file_sys_->translate(file), logger);
|
||||
}
|
||||
@@ -70,7 +70,7 @@ mapped_module* module_manager::map_local_module(const std::filesystem::path& fil
|
||||
|
||||
for (auto& mod : this->modules_ | std::views::values)
|
||||
{
|
||||
if (mod.path == file)
|
||||
if (mod.path == local_file)
|
||||
{
|
||||
return &mod;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class module_manager
|
||||
using module_map = std::map<uint64_t, mapped_module>;
|
||||
module_manager(emulator& emu, file_system& file_sys);
|
||||
|
||||
mapped_module* map_module(const std::filesystem::path& file, const logger& logger);
|
||||
mapped_module* map_module(const windows_path& file, const logger& logger);
|
||||
mapped_module* map_local_module(const std::filesystem::path& file, const logger& logger);
|
||||
|
||||
mapped_module* find_by_address(const uint64_t address)
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
const std::wstring original_name(value->name.begin(), value->name.end());
|
||||
const std::u16string original_name(value->name.begin(), value->name.end());
|
||||
|
||||
if (key_value_information_class == KeyValueBasicInformation)
|
||||
{
|
||||
@@ -2897,10 +2897,10 @@ namespace
|
||||
|
||||
const auto filename = read_unicode_string(
|
||||
c.emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{c.emu, attributes.ObjectName});
|
||||
const auto u8_filename = u16_to_u8(filename);
|
||||
const auto local_filename = c.win_emu.file_sys.translate(filename).string();
|
||||
|
||||
struct _stat64 file_stat{};
|
||||
if (_stat64(u8_filename.c_str(), &file_stat) != 0)
|
||||
if (_stat64(local_filename.c_str(), &file_stat) != 0)
|
||||
{
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
@@ -208,11 +208,6 @@ namespace
|
||||
emu.reg<uint16_t>(x64_register::ss, 0x2B);
|
||||
}
|
||||
|
||||
std::filesystem::path canonicalize_path(const std::filesystem::path& path)
|
||||
{
|
||||
return canonical(absolute(path)).make_preferred();
|
||||
}
|
||||
|
||||
void setup_context(windows_emulator& win_emu, const emulator_settings& settings)
|
||||
{
|
||||
auto& emu = win_emu.emu();
|
||||
@@ -220,7 +215,7 @@ namespace
|
||||
|
||||
setup_gdt(emu);
|
||||
|
||||
context.registry = registry_manager(settings.registry_directory);
|
||||
context.registry = registry_manager(win_emu.root_directory / "registry");
|
||||
|
||||
context.kusd.setup(settings.use_relative_time);
|
||||
|
||||
@@ -260,7 +255,9 @@ namespace
|
||||
allocator.copy_string(u"SystemRoot=C:\\WINDOWS");
|
||||
allocator.copy_string(u"");
|
||||
|
||||
std::u16string command_line = u"\"" + settings.application.u16string() + u"\"";
|
||||
const auto application_str = settings.application.u16string();
|
||||
|
||||
std::u16string command_line = u"\"" + application_str + u"\"";
|
||||
|
||||
for (const auto& arg : settings.arguments)
|
||||
{
|
||||
@@ -270,9 +267,8 @@ namespace
|
||||
|
||||
allocator.make_unicode_string(proc_params.CommandLine, command_line);
|
||||
allocator.make_unicode_string(proc_params.CurrentDirectory.DosPath,
|
||||
win_emu.file_sys.get_working_directory());
|
||||
allocator.make_unicode_string(proc_params.ImagePathName,
|
||||
canonicalize_path(settings.application).u16string());
|
||||
win_emu.file_sys.get_working_directory().u16string());
|
||||
allocator.make_unicode_string(proc_params.ImagePathName, application_str);
|
||||
|
||||
const auto total_length = allocator.get_next_address() - context.process_params.value();
|
||||
|
||||
@@ -824,11 +820,15 @@ std::unique_ptr<x64_emulator> create_default_x64_emulator()
|
||||
|
||||
windows_emulator::windows_emulator(const emulator_settings& settings, emulator_callbacks callbacks,
|
||||
std::unique_ptr<x64_emulator> emu)
|
||||
: windows_emulator(std::move(emu))
|
||||
: windows_emulator(settings.root_filesystem, std::move(emu))
|
||||
{
|
||||
if (!settings.working_directory.empty())
|
||||
{
|
||||
this->file_sys.set_working_directory(settings.working_directory.u16string());
|
||||
this->file_sys.set_working_directory(settings.working_directory);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->file_sys.set_working_directory(settings.application.parent());
|
||||
}
|
||||
|
||||
this->silent_until_main_ = settings.silent_until_main && !settings.disable_logging;
|
||||
@@ -838,8 +838,9 @@ windows_emulator::windows_emulator(const emulator_settings& settings, emulator_c
|
||||
this->setup_process(settings);
|
||||
}
|
||||
|
||||
windows_emulator::windows_emulator(std::unique_ptr<x64_emulator> emu)
|
||||
: file_sys(R"(C:\Users\mahe.WIBU\Desktop\emulator\src\tools\root)", u"C:\\"),
|
||||
windows_emulator::windows_emulator(const std::filesystem::path& root_path, std::unique_ptr<x64_emulator> emu)
|
||||
: root_directory{absolute(root_path)},
|
||||
file_sys(root_directory / "filesys", u"C:\\"),
|
||||
emu_(std::move(emu)),
|
||||
process_(*emu_, file_sys)
|
||||
{
|
||||
@@ -855,7 +856,7 @@ void windows_emulator::setup_process(const emulator_settings& settings)
|
||||
|
||||
setup_context(*this, settings);
|
||||
|
||||
context.executable = context.mod_manager.map_local_module(settings.application, this->log);
|
||||
context.executable = context.mod_manager.map_module(settings.application, this->log);
|
||||
|
||||
context.peb.access([&](PEB64& peb) {
|
||||
peb.ImageBaseAddress = reinterpret_cast<std::uint64_t*>(context.executable->image_base); //
|
||||
|
||||
@@ -27,9 +27,9 @@ struct emulator_callbacks
|
||||
// TODO: Split up into application and emulator settings
|
||||
struct emulator_settings
|
||||
{
|
||||
std::filesystem::path application{};
|
||||
std::filesystem::path working_directory{};
|
||||
std::filesystem::path registry_directory{"./registry"};
|
||||
windows_path application{};
|
||||
windows_path working_directory{};
|
||||
std::filesystem::path root_filesystem{};
|
||||
std::vector<std::u16string> arguments{};
|
||||
bool disable_logging{false};
|
||||
bool silent_until_main{false};
|
||||
@@ -47,7 +47,8 @@ enum class apiset_location
|
||||
class windows_emulator
|
||||
{
|
||||
public:
|
||||
windows_emulator(std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||
windows_emulator(const std::filesystem::path& root_path,
|
||||
std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||
windows_emulator(const emulator_settings& settings, emulator_callbacks callbacks = {},
|
||||
std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||
|
||||
@@ -132,6 +133,7 @@ class windows_emulator
|
||||
return this->callbacks_;
|
||||
}
|
||||
|
||||
std::filesystem::path root_directory{};
|
||||
file_system file_sys;
|
||||
|
||||
private:
|
||||
|
||||
236
src/windows-emulator/windows_path.hpp
Normal file
236
src/windows-emulator/windows_path.hpp
Normal file
@@ -0,0 +1,236 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include <serialization.hpp>
|
||||
|
||||
namespace windows_path_detail
|
||||
{
|
||||
constexpr std::u16string_view unc_prefix = u"\\??\\";
|
||||
|
||||
inline std::u16string_view strip_unc_prefix(const std::u16string_view path)
|
||||
{
|
||||
if (!path.starts_with(unc_prefix))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return path.substr(unc_prefix.size());
|
||||
}
|
||||
|
||||
inline bool is_slash(const char16_t chr)
|
||||
{
|
||||
return chr == u'\\' || chr == u'/';
|
||||
}
|
||||
}
|
||||
|
||||
class windows_path
|
||||
{
|
||||
public:
|
||||
windows_path() = default;
|
||||
|
||||
windows_path(const std::filesystem::path& path)
|
||||
{
|
||||
const auto full_path = path.u16string();
|
||||
const auto canonical_path = windows_path_detail::strip_unc_prefix(full_path);
|
||||
|
||||
std::u16string folder{};
|
||||
|
||||
for (const auto chr : canonical_path)
|
||||
{
|
||||
if (chr == u':' && this->folders_.empty() && !this->drive_.has_value() && folder.size() == 1)
|
||||
{
|
||||
this->drive_ = static_cast<char>(folder[0]);
|
||||
folder.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (windows_path_detail::is_slash(chr))
|
||||
{
|
||||
if (folder.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
this->folders_.push_back(std::move(folder));
|
||||
folder = {};
|
||||
continue;
|
||||
}
|
||||
|
||||
folder.push_back(chr);
|
||||
}
|
||||
|
||||
if (!folder.empty())
|
||||
{
|
||||
this->folders_.push_back(folder);
|
||||
}
|
||||
|
||||
this->canonicalize();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(!std::is_same_v<T, windows_path> && !std::is_same_v<T, std::filesystem::path>)
|
||||
windows_path(T&& path_like)
|
||||
: windows_path(std::filesystem::path(std::forward<T>(path_like)))
|
||||
{
|
||||
}
|
||||
|
||||
windows_path(const std::optional<char> drive, std::list<std::u16string> folders)
|
||||
: drive_(drive),
|
||||
folders_(std::move(folders))
|
||||
{
|
||||
this->canonicalize();
|
||||
}
|
||||
|
||||
bool is_absolute() const
|
||||
{
|
||||
return this->drive_.has_value();
|
||||
}
|
||||
|
||||
bool is_relative() const
|
||||
{
|
||||
return !this->is_absolute();
|
||||
}
|
||||
|
||||
std::u16string u16string() const
|
||||
{
|
||||
std::u16string path{};
|
||||
if (this->drive_)
|
||||
{
|
||||
path.push_back(static_cast<char16_t>(*this->drive_));
|
||||
path.push_back(u':');
|
||||
}
|
||||
|
||||
for (const auto& folder : this->folders_)
|
||||
{
|
||||
if (!path.empty())
|
||||
{
|
||||
path.push_back(u'\\');
|
||||
}
|
||||
|
||||
path.append(folder);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string string() const
|
||||
{
|
||||
return u16_to_u8(this->u16string());
|
||||
}
|
||||
|
||||
std::u16string to_unc_path() const
|
||||
{
|
||||
if (this->is_relative())
|
||||
{
|
||||
return this->u16string();
|
||||
}
|
||||
|
||||
return std::u16string(windows_path_detail::unc_prefix) + this->u16string();
|
||||
}
|
||||
|
||||
std::filesystem::path to_portable_path() const
|
||||
{
|
||||
std::u16string path{};
|
||||
if (this->drive_)
|
||||
{
|
||||
path.push_back(static_cast<char16_t>(*this->drive_));
|
||||
}
|
||||
|
||||
for (const auto& folder : this->folders_)
|
||||
{
|
||||
if (!path.empty())
|
||||
{
|
||||
path.push_back(u'/');
|
||||
}
|
||||
|
||||
path.append(folder);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
windows_path operator/(const windows_path& path) const
|
||||
{
|
||||
if (path.is_absolute())
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
auto folders = this->folders_;
|
||||
|
||||
for (const auto& folder : path.folders_)
|
||||
{
|
||||
folders.push_back(folder);
|
||||
}
|
||||
|
||||
return {this->drive_, std::move(folders)};
|
||||
}
|
||||
|
||||
windows_path& operator/=(const windows_path& path)
|
||||
{
|
||||
*this = *this / path;
|
||||
return *this;
|
||||
}
|
||||
|
||||
windows_path parent() const
|
||||
{
|
||||
auto folders = this->folders_;
|
||||
if (!folders.empty())
|
||||
{
|
||||
folders.pop_back();
|
||||
}
|
||||
|
||||
return {this->drive_, std::move(folders)};
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write_optional(this->drive_);
|
||||
buffer.write_list(this->folders_);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read_optional(this->drive_);
|
||||
buffer.read_list(this->folders_);
|
||||
}
|
||||
|
||||
bool operator==(const windows_path& other) const
|
||||
{
|
||||
return this->drive_ == other.drive_ && this->folders_ == other.folders_;
|
||||
}
|
||||
|
||||
bool operator!=(const windows_path& other) const
|
||||
{
|
||||
return !this->operator==(other);
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return !this->is_relative() && this->folders_.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<char> drive_{};
|
||||
std::list<std::u16string> folders_{};
|
||||
|
||||
void canonicalize()
|
||||
{
|
||||
if (this->drive_.has_value())
|
||||
{
|
||||
this->drive_ = utils::string::char_to_lower(*this->drive_);
|
||||
}
|
||||
|
||||
for (auto& folder : this->folders_)
|
||||
{
|
||||
for (auto& chr : folder)
|
||||
{
|
||||
chr = utils::string::char_to_lower(chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user