#include "MetaCoreFoundation/MetaCorePackage.h" #include namespace MetaCore { namespace { [[nodiscard]] std::optional> 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(input.tellg()); input.seekg(0, std::ios::beg); std::vector bytes(size); if (size > 0 && !input.read(reinterpret_cast(bytes.data()), static_cast(size))) { return std::nullopt; } return bytes; } template [[nodiscard]] std::optional> MetaCoreSerializeSection( const T& value, const MetaCoreTypeRegistry& registry ) { return MetaCoreSerializeToBytes(value, registry); } template [[nodiscard]] bool MetaCoreDeserializeSection( const MetaCorePackageSection& section, std::span buffer, T& value, const MetaCoreTypeRegistry& registry ) { if (section.Offset + section.Size > buffer.size()) { return false; } return MetaCoreDeserializeFromBytes( buffer.subspan(static_cast(section.Offset), static_cast(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(provisionalHeaderBytes->size()); document.Header.NameTable = MetaCorePackageSection{cursor, static_cast(serializedNames->size())}; cursor += document.Header.NameTable.Size; document.Header.ImportTable = MetaCorePackageSection{cursor, static_cast(serializedImports->size())}; cursor += document.Header.ImportTable.Size; document.Header.ExportTable = MetaCorePackageSection{cursor, static_cast(serializedExports->size())}; cursor += document.Header.ExportTable.Size; document.Header.DependencyTable = MetaCorePackageSection{cursor, static_cast(serializedDependencies->size())}; cursor += document.Header.DependencyTable.Size; document.Header.CustomVersionTable = MetaCorePackageSection{cursor, static_cast(serializedCustomVersions->size())}; cursor += document.Header.CustomVersionTable.Size; document.Header.PayloadSections = MetaCorePackageSection{cursor, static_cast(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(finalHeaderBytes->data()), static_cast(finalHeaderBytes->size())); output.write(reinterpret_cast(serializedNames->data()), static_cast(serializedNames->size())); output.write(reinterpret_cast(serializedImports->data()), static_cast(serializedImports->size())); output.write(reinterpret_cast(serializedExports->data()), static_cast(serializedExports->size())); output.write(reinterpret_cast(serializedDependencies->data()), static_cast(serializedDependencies->size())); output.write(reinterpret_cast(serializedCustomVersions->data()), static_cast(serializedCustomVersions->size())); output.write(reinterpret_cast(serializedPayloads->data()), static_cast(serializedPayloads->size())); return output.good(); } std::optional 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(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 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 #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::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(pData); mappedPackage.MappedSize_ = static_cast(fileSize.QuadPart); const std::span 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(count); } std::span 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(size)}; } ptr += size; } return {}; } } // namespace MetaCore