MetaCore/Source/MetaCoreFoundation/Private/MetaCorePackage.cpp

321 lines
12 KiB
C++

#include "MetaCoreFoundation/MetaCorePackage.h"
#include <fstream>
namespace MetaCore {
namespace {
[[nodiscard]] std::optional<std::vector<std::byte>> MetaCoreReadAllBytes(const std::filesystem::path& path) {
std::ifstream input(path, std::ios::binary);
if (!input.is_open()) {
return std::nullopt;
}
input.seekg(0, std::ios::end);
const auto size = static_cast<std::size_t>(input.tellg());
input.seekg(0, std::ios::beg);
std::vector<std::byte> bytes(size);
if (size > 0 && !input.read(reinterpret_cast<char*>(bytes.data()), static_cast<std::streamsize>(size))) {
return std::nullopt;
}
return bytes;
}
template <typename T>
[[nodiscard]] std::optional<std::vector<std::byte>> MetaCoreSerializeSection(
const T& value,
const MetaCoreTypeRegistry& registry
) {
return MetaCoreSerializeToBytes(value, registry);
}
template <typename T>
[[nodiscard]] bool MetaCoreDeserializeSection(
const MetaCorePackageSection& section,
std::span<const std::byte> buffer,
T& value,
const MetaCoreTypeRegistry& registry
) {
if (section.Offset + section.Size > buffer.size()) {
return false;
}
return MetaCoreDeserializeFromBytes(
buffer.subspan(static_cast<std::size_t>(section.Offset), static_cast<std::size_t>(section.Size)),
value,
registry
);
}
} // namespace
bool MetaCoreWritePackageFile(
const std::filesystem::path& path,
MetaCorePackageDocument document,
const MetaCoreTypeRegistry& registry
) {
const auto serializedNames = MetaCoreSerializeSection(document.NameTable, registry);
const auto serializedImports = MetaCoreSerializeSection(document.Imports, registry);
const auto serializedExports = MetaCoreSerializeSection(document.Exports, registry);
const auto serializedDependencies = MetaCoreSerializeSection(document.Dependencies, registry);
const auto serializedCustomVersions = MetaCoreSerializeSection(document.CustomVersions, registry);
const auto serializedPayloads = MetaCoreSerializeSection(document.PayloadSections, registry);
if (!serializedNames.has_value() ||
!serializedImports.has_value() ||
!serializedExports.has_value() ||
!serializedDependencies.has_value() ||
!serializedCustomVersions.has_value() ||
!serializedPayloads.has_value()) {
return false;
}
const auto provisionalHeaderBytes = MetaCoreSerializeToBytes(document.Header, registry);
if (!provisionalHeaderBytes.has_value()) {
return false;
}
std::uint64_t cursor = static_cast<std::uint64_t>(provisionalHeaderBytes->size());
document.Header.NameTable = MetaCorePackageSection{cursor, static_cast<std::uint64_t>(serializedNames->size())};
cursor += document.Header.NameTable.Size;
document.Header.ImportTable = MetaCorePackageSection{cursor, static_cast<std::uint64_t>(serializedImports->size())};
cursor += document.Header.ImportTable.Size;
document.Header.ExportTable = MetaCorePackageSection{cursor, static_cast<std::uint64_t>(serializedExports->size())};
cursor += document.Header.ExportTable.Size;
document.Header.DependencyTable = MetaCorePackageSection{cursor, static_cast<std::uint64_t>(serializedDependencies->size())};
cursor += document.Header.DependencyTable.Size;
document.Header.CustomVersionTable = MetaCorePackageSection{cursor, static_cast<std::uint64_t>(serializedCustomVersions->size())};
cursor += document.Header.CustomVersionTable.Size;
document.Header.PayloadSections = MetaCorePackageSection{cursor, static_cast<std::uint64_t>(serializedPayloads->size())};
cursor += document.Header.PayloadSections.Size;
document.Header.FileSize = cursor;
const auto finalHeaderBytes = MetaCoreSerializeToBytes(document.Header, registry);
if (!finalHeaderBytes.has_value()) {
return false;
}
std::filesystem::create_directories(path.parent_path());
std::ofstream output(path, std::ios::binary | std::ios::trunc);
if (!output.is_open()) {
return false;
}
output.write(reinterpret_cast<const char*>(finalHeaderBytes->data()), static_cast<std::streamsize>(finalHeaderBytes->size()));
output.write(reinterpret_cast<const char*>(serializedNames->data()), static_cast<std::streamsize>(serializedNames->size()));
output.write(reinterpret_cast<const char*>(serializedImports->data()), static_cast<std::streamsize>(serializedImports->size()));
output.write(reinterpret_cast<const char*>(serializedExports->data()), static_cast<std::streamsize>(serializedExports->size()));
output.write(reinterpret_cast<const char*>(serializedDependencies->data()), static_cast<std::streamsize>(serializedDependencies->size()));
output.write(reinterpret_cast<const char*>(serializedCustomVersions->data()), static_cast<std::streamsize>(serializedCustomVersions->size()));
output.write(reinterpret_cast<const char*>(serializedPayloads->data()), static_cast<std::streamsize>(serializedPayloads->size()));
return output.good();
}
std::optional<MetaCorePackageDocument> MetaCoreReadPackageFile(
const std::filesystem::path& path,
const MetaCoreTypeRegistry& registry
) {
const auto bytes = MetaCoreReadAllBytes(path);
if (!bytes.has_value()) {
return std::nullopt;
}
MetaCorePackageDocument document;
MetaCoreArchiveReader headerReader(std::span<const std::byte>(bytes->data(), bytes->size()));
if (!MetaCoreDeserializeValue(headerReader, document.Header, registry)) {
return std::nullopt;
}
if (document.Header.Magic != GMetaCorePackageMagic ||
document.Header.FormatVersion != GMetaCorePackageFormatVersion ||
document.Header.Endianness != MetaCorePackageEndianness::Little ||
document.Header.FileSize != bytes->size()) {
return std::nullopt;
}
const std::span<const std::byte> byteSpan(bytes->data(), bytes->size());
if (!MetaCoreDeserializeSection(document.Header.NameTable, byteSpan, document.NameTable, registry) ||
!MetaCoreDeserializeSection(document.Header.ImportTable, byteSpan, document.Imports, registry) ||
!MetaCoreDeserializeSection(document.Header.ExportTable, byteSpan, document.Exports, registry) ||
!MetaCoreDeserializeSection(document.Header.DependencyTable, byteSpan, document.Dependencies, registry) ||
!MetaCoreDeserializeSection(document.Header.CustomVersionTable, byteSpan, document.CustomVersions, registry) ||
!MetaCoreDeserializeSection(document.Header.PayloadSections, byteSpan, document.PayloadSections, registry)) {
return std::nullopt;
}
return document;
}
} // namespace MetaCore
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
namespace MetaCore {
MetaCoreMappedPackage::~MetaCoreMappedPackage() {
#ifdef _WIN32
if (MappedData_ != nullptr) {
UnmapViewOfFile(MappedData_);
MappedData_ = nullptr;
}
if (MapHandle_ != nullptr) {
CloseHandle(MapHandle_);
MapHandle_ = nullptr;
}
if (FileHandle_ != INVALID_HANDLE_VALUE && FileHandle_ != nullptr) {
CloseHandle(FileHandle_);
FileHandle_ = nullptr;
}
#endif
}
MetaCoreMappedPackage::MetaCoreMappedPackage(MetaCoreMappedPackage&& other) noexcept
: FileHandle_(other.FileHandle_),
MapHandle_(other.MapHandle_),
MappedData_(other.MappedData_),
MappedSize_(other.MappedSize_),
Document_(std::move(other.Document_)) {
other.FileHandle_ = nullptr;
other.MapHandle_ = nullptr;
other.MappedData_ = nullptr;
other.MappedSize_ = 0;
}
MetaCoreMappedPackage& MetaCoreMappedPackage::operator=(MetaCoreMappedPackage&& other) noexcept {
if (this != &other) {
this->~MetaCoreMappedPackage();
FileHandle_ = other.FileHandle_;
MapHandle_ = other.MapHandle_;
MappedData_ = other.MappedData_;
MappedSize_ = other.MappedSize_;
Document_ = std::move(other.Document_);
other.FileHandle_ = nullptr;
other.MapHandle_ = nullptr;
other.MappedData_ = nullptr;
other.MappedSize_ = 0;
}
return *this;
}
std::optional<MetaCoreMappedPackage> MetaCoreMappedPackage::Open(
const std::filesystem::path& path,
const MetaCoreTypeRegistry& registry
) {
#ifdef _WIN32
HANDLE hFile = CreateFileW(
path.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile == INVALID_HANDLE_VALUE) return std::nullopt;
LARGE_INTEGER fileSize;
if (!GetFileSizeEx(hFile, &fileSize) || fileSize.QuadPart == 0) {
CloseHandle(hFile);
return std::nullopt;
}
HANDLE hMap = CreateFileMappingW(
hFile,
NULL,
PAGE_READONLY,
0, 0,
NULL
);
if (hMap == NULL) {
CloseHandle(hFile);
return std::nullopt;
}
const void* pData = MapViewOfFile(
hMap,
FILE_MAP_READ,
0, 0, 0
);
if (pData == nullptr) {
CloseHandle(hMap);
CloseHandle(hFile);
return std::nullopt;
}
MetaCoreMappedPackage mappedPackage;
mappedPackage.FileHandle_ = hFile;
mappedPackage.MapHandle_ = hMap;
mappedPackage.MappedData_ = static_cast<const std::byte*>(pData);
mappedPackage.MappedSize_ = static_cast<std::size_t>(fileSize.QuadPart);
const std::span<const std::byte> byteSpan(mappedPackage.MappedData_, mappedPackage.MappedSize_);
MetaCoreArchiveReader headerReader(byteSpan);
if (!MetaCoreDeserializeValue(headerReader, mappedPackage.Document_.Header, registry)) {
return std::nullopt; // destructor handles cleanup
}
auto& header = mappedPackage.Document_.Header;
if (header.Magic != GMetaCorePackageMagic ||
header.FormatVersion != GMetaCorePackageFormatVersion ||
header.Endianness != MetaCorePackageEndianness::Little ||
header.FileSize != mappedPackage.MappedSize_) {
return std::nullopt;
}
if (!MetaCoreDeserializeSection(header.NameTable, byteSpan, mappedPackage.Document_.NameTable, registry) ||
!MetaCoreDeserializeSection(header.ImportTable, byteSpan, mappedPackage.Document_.Imports, registry) ||
!MetaCoreDeserializeSection(header.ExportTable, byteSpan, mappedPackage.Document_.Exports, registry) ||
!MetaCoreDeserializeSection(header.DependencyTable, byteSpan, mappedPackage.Document_.Dependencies, registry) ||
!MetaCoreDeserializeSection(header.CustomVersionTable, byteSpan, mappedPackage.Document_.CustomVersions, registry)) {
// Deliberately skipping PayloadSections to avoid memory copying of large binary data
return std::nullopt;
}
return mappedPackage;
#else
// Non-Windows fallback not implemented
return std::nullopt;
#endif
}
std::size_t MetaCoreMappedPackage::GetPayloadSectionCount() const {
if (Document_.Header.PayloadSections.Size < sizeof(std::uint64_t)) {
return 0;
}
const std::byte* ptr = MappedData_ + Document_.Header.PayloadSections.Offset;
std::uint64_t count = 0;
std::memcpy(&count, ptr, sizeof(count));
return static_cast<std::size_t>(count);
}
std::span<const std::byte> MetaCoreMappedPackage::GetPayloadSection(std::size_t index) const {
const std::size_t count = GetPayloadSectionCount();
if (index >= count) {
return {};
}
const std::byte* ptr = MappedData_ + Document_.Header.PayloadSections.Offset;
ptr += sizeof(std::uint64_t); // skip count
for (std::size_t i = 0; i < count; ++i) {
std::uint64_t size = 0;
std::memcpy(&size, ptr, sizeof(size));
ptr += sizeof(size);
if (i == index) {
return {ptr, static_cast<std::size_t>(size)};
}
ptr += size;
}
return {};
}
} // namespace MetaCore