whiteoutlib 0.1.0

Parsers and writers for Blizzard model and texture formats (MDX, M2, M3, BLP, DDS, PNG).
Documentation
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Fernando Sahmkow

#include "whiteout/utils/os_file_system.h"

#include "../common/unicode_path.h"

#include <filesystem>
#include <fstream>

namespace fs = std::filesystem;

namespace whiteout::utils {

struct OsFileSystem::Impl {
    fs::path root;

    explicit Impl(std::string rootPath) : root(common::utf8_to_path(rootPath)) {}

    fs::path resolve(const std::string& path) const {
        return root / common::utf8_to_path(path);
    }
};

OsFileSystem::OsFileSystem(std::string rootPath)
    : m_impl(std::make_unique<Impl>(std::move(rootPath))) {}

OsFileSystem::~OsFileSystem() = default;

OsFileSystem::OsFileSystem(OsFileSystem&&) noexcept = default;
OsFileSystem& OsFileSystem::operator=(OsFileSystem&&) noexcept = default;

std::vector<u8> OsFileSystem::readFile(const std::string& path) const {
    const fs::path fullPath = m_impl->resolve(path);

    std::ifstream file(fullPath, std::ios::binary | std::ios::ate);
    if (!file.is_open())
        return {};

    const auto size = file.tellg();
    if (size <= 0)
        return {};

    file.seekg(0);
    std::vector<u8> buffer(static_cast<size_t>(size));
    file.read(reinterpret_cast<char*>(buffer.data()), size);
    if (!file)
        return {};

    return buffer;
}

bool OsFileSystem::fileExists(const std::string& path) const {
    return fs::is_regular_file(m_impl->resolve(path));
}

bool OsFileSystem::writeFile(const std::string& path, const std::vector<u8>& data) {
    const fs::path fullPath = m_impl->resolve(path);

    // Ensure parent directories exist
    std::error_code ec;
    fs::create_directories(fullPath.parent_path(), ec);

    std::ofstream file(fullPath, std::ios::binary);
    if (!file.is_open())
        return false;

    file.write(reinterpret_cast<const char*>(data.data()),
               static_cast<std::streamsize>(data.size()));
    return file.good();
}

std::vector<interfaces::DirectoryEntry> OsFileSystem::listDirectory(const std::string& path) const {
    std::vector<interfaces::DirectoryEntry> entries;
    const fs::path fullPath = m_impl->resolve(path);

    std::error_code ec;
    for (const auto& entry : fs::directory_iterator(fullPath, ec)) {
        if (ec)
            break;
        entries.push_back({entry.path().filename().string(), entry.is_directory()});
    }
    return entries;
}

} // namespace whiteout::utils