321 lines
12 KiB
C++
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
|