chore: checkpoint current workspace changes
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include <XCEngine/Resources/Model/ModelArtifactIO.h>
|
||||
|
||||
#include <XCEngine/Core/Asset/ArtifactContainer.h>
|
||||
#include <XCEngine/Core/Asset/ArtifactFormats.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Resources/Model/Model.h>
|
||||
@@ -7,6 +8,7 @@
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -35,7 +37,46 @@ std::filesystem::path ResolveArtifactPath(const Containers::String& path) {
|
||||
return resolvedPath.lexically_normal();
|
||||
}
|
||||
|
||||
void WriteString(std::ofstream& stream, const Containers::String& value) {
|
||||
bool ReadArtifactFileData(const Containers::String& path,
|
||||
ResourceType resourceType,
|
||||
Containers::Array<Core::uint8>& outData) {
|
||||
outData.Clear();
|
||||
|
||||
const std::filesystem::path resolvedPath = ResolveArtifactPath(path);
|
||||
Containers::String containerError;
|
||||
if (ReadArtifactContainerPayloadByPath(
|
||||
Containers::String(resolvedPath.generic_string().c_str()),
|
||||
resourceType,
|
||||
outData,
|
||||
&containerError)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ifstream input(resolvedPath, std::ios::binary | std::ios::ate);
|
||||
if (!input.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::streamsize size = input.tellg();
|
||||
if (size < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
input.seekg(0, std::ios::beg);
|
||||
outData.Resize(static_cast<size_t>(size));
|
||||
if (size == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!input.read(reinterpret_cast<char*>(outData.Data()), size)) {
|
||||
outData.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteString(std::ostream& stream, const Containers::String& value) {
|
||||
const Core::uint32 length = static_cast<Core::uint32>(value.Length());
|
||||
stream.write(reinterpret_cast<const char*>(&length), sizeof(length));
|
||||
if (length > 0) {
|
||||
@@ -43,20 +84,65 @@ void WriteString(std::ofstream& stream, const Containers::String& value) {
|
||||
}
|
||||
}
|
||||
|
||||
Containers::String ReadString(std::ifstream& stream) {
|
||||
bool ReadString(const Containers::Array<Core::uint8>& data,
|
||||
size_t& offset,
|
||||
Containers::String& outValue) {
|
||||
Core::uint32 length = 0;
|
||||
stream.read(reinterpret_cast<char*>(&length), sizeof(length));
|
||||
if (!stream || length == 0) {
|
||||
return Containers::String();
|
||||
if (offset + sizeof(length) > data.Size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string buffer(length, '\0');
|
||||
stream.read(buffer.data(), length);
|
||||
if (!stream) {
|
||||
return Containers::String();
|
||||
std::memcpy(&length, data.Data() + offset, sizeof(length));
|
||||
offset += sizeof(length);
|
||||
if (length == 0) {
|
||||
outValue.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
return Containers::String(buffer.c_str());
|
||||
if (offset + length > data.Size()) {
|
||||
outValue.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
outValue = Containers::String(reinterpret_cast<const char*>(data.Data() + offset), length);
|
||||
offset += length;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadValue(const Containers::Array<Core::uint8>& data, size_t& offset, T& outValue) {
|
||||
if (offset + sizeof(T) > data.Size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memcpy(&outValue, data.Data() + offset, sizeof(T));
|
||||
offset += sizeof(T);
|
||||
return true;
|
||||
}
|
||||
|
||||
Containers::Array<Core::uint8> ToByteArray(const std::string& text) {
|
||||
Containers::Array<Core::uint8> bytes;
|
||||
if (text.empty()) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
bytes.ResizeUninitialized(text.size());
|
||||
std::memcpy(bytes.Data(), text.data(), text.size());
|
||||
return bytes;
|
||||
}
|
||||
|
||||
ModelArtifactHeader BuildModelArtifactHeader(const Model& model) {
|
||||
ModelArtifactHeader header;
|
||||
header.nodeCount = static_cast<Core::uint32>(model.GetNodes().Size());
|
||||
header.meshBindingCount = static_cast<Core::uint32>(model.GetMeshBindings().Size());
|
||||
header.materialBindingCount = static_cast<Core::uint32>(model.GetMaterialBindings().Size());
|
||||
header.rootNodeIndex = model.GetRootNodeIndex();
|
||||
return header;
|
||||
}
|
||||
|
||||
bool ValidateModelArtifactHeader(const ModelArtifactHeader& header) {
|
||||
return header.rootNodeIndex == kInvalidModelNodeIndex ||
|
||||
header.rootNodeIndex < header.nodeCount;
|
||||
}
|
||||
|
||||
LoadResult CreateOwnedModelResource(const Containers::String& path,
|
||||
@@ -94,40 +180,17 @@ LoadResult CreateOwnedModelResource(const Containers::String& path,
|
||||
|
||||
} // namespace
|
||||
|
||||
bool WriteModelArtifactFile(const Containers::String& artifactPath,
|
||||
const Model& model,
|
||||
Containers::String* outErrorMessage) {
|
||||
const std::filesystem::path resolvedPath = ResolveArtifactPath(artifactPath);
|
||||
std::error_code ec;
|
||||
const std::filesystem::path parentPath = resolvedPath.parent_path();
|
||||
if (!parentPath.empty()) {
|
||||
std::filesystem::create_directories(parentPath, ec);
|
||||
if (ec) {
|
||||
if (outErrorMessage != nullptr) {
|
||||
*outErrorMessage =
|
||||
Containers::String("Failed to create model artifact directory: ") +
|
||||
Containers::String(parentPath.generic_string().c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool SerializeModelArtifactPayload(const Model& model,
|
||||
Containers::Array<Core::uint8>& outPayload,
|
||||
Containers::String* outErrorMessage) {
|
||||
(void)outErrorMessage;
|
||||
|
||||
std::ofstream output(resolvedPath, std::ios::binary | std::ios::trunc);
|
||||
if (!output.is_open()) {
|
||||
if (outErrorMessage != nullptr) {
|
||||
*outErrorMessage = Containers::String("Failed to open model artifact for write: ") + artifactPath;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::ostringstream output(std::ios::binary | std::ios::out);
|
||||
|
||||
ModelArtifactFileHeader fileHeader;
|
||||
output.write(reinterpret_cast<const char*>(&fileHeader), sizeof(fileHeader));
|
||||
|
||||
ModelArtifactHeader header;
|
||||
header.nodeCount = static_cast<Core::uint32>(model.GetNodes().Size());
|
||||
header.meshBindingCount = static_cast<Core::uint32>(model.GetMeshBindings().Size());
|
||||
header.materialBindingCount = static_cast<Core::uint32>(model.GetMaterialBindings().Size());
|
||||
header.rootNodeIndex = model.GetRootNodeIndex();
|
||||
const ModelArtifactHeader header = BuildModelArtifactHeader(model);
|
||||
output.write(reinterpret_cast<const char*>(&header), sizeof(header));
|
||||
|
||||
for (const ModelNode& node : model.GetNodes()) {
|
||||
@@ -158,27 +221,25 @@ bool WriteModelArtifactFile(const Containers::String& artifactPath,
|
||||
output.write(reinterpret_cast<const char*>(&bindingArtifact), sizeof(bindingArtifact));
|
||||
}
|
||||
|
||||
if (!output && outErrorMessage != nullptr) {
|
||||
*outErrorMessage = Containers::String("Failed to write model artifact: ") + artifactPath;
|
||||
if (!output) {
|
||||
outPayload.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return static_cast<bool>(output);
|
||||
outPayload = ToByteArray(output.str());
|
||||
return true;
|
||||
}
|
||||
|
||||
LoadResult LoadModelArtifact(const Containers::String& path) {
|
||||
const std::filesystem::path resolvedPath = ResolveArtifactPath(path);
|
||||
namespace {
|
||||
|
||||
std::ifstream input(resolvedPath, std::ios::binary);
|
||||
if (!input.is_open()) {
|
||||
return LoadResult(Containers::String("Failed to read model artifact: ") + path);
|
||||
}
|
||||
LoadResult ParseModelArtifactPayload(const Containers::String& path,
|
||||
const Containers::Array<Core::uint8>& data) {
|
||||
size_t offset = 0;
|
||||
|
||||
ModelArtifactFileHeader fileHeader;
|
||||
input.read(reinterpret_cast<char*>(&fileHeader), sizeof(fileHeader));
|
||||
if (!input) {
|
||||
if (!ReadValue(data, offset, fileHeader)) {
|
||||
return LoadResult(Containers::String("Failed to parse model artifact file header: ") + path);
|
||||
}
|
||||
|
||||
const bool validFileHeader =
|
||||
std::memcmp(fileHeader.magic, "XCMOD01", 7) == 0 &&
|
||||
fileHeader.schemaVersion == kModelArtifactSchemaVersion;
|
||||
@@ -187,13 +248,11 @@ LoadResult LoadModelArtifact(const Containers::String& path) {
|
||||
}
|
||||
|
||||
ModelArtifactHeader header;
|
||||
input.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (!input) {
|
||||
if (!ReadValue(data, offset, header)) {
|
||||
return LoadResult(Containers::String("Failed to parse model artifact header: ") + path);
|
||||
}
|
||||
|
||||
if (header.rootNodeIndex != kInvalidModelNodeIndex &&
|
||||
header.rootNodeIndex >= header.nodeCount) {
|
||||
if (!ValidateModelArtifactHeader(header)) {
|
||||
return LoadResult(Containers::String("Invalid model artifact root node index: ") + path);
|
||||
}
|
||||
|
||||
@@ -201,11 +260,12 @@ LoadResult LoadModelArtifact(const Containers::String& path) {
|
||||
nodes.Reserve(header.nodeCount);
|
||||
for (Core::uint32 index = 0; index < header.nodeCount; ++index) {
|
||||
ModelNode node;
|
||||
node.name = ReadString(input);
|
||||
if (!ReadString(data, offset, node.name)) {
|
||||
return LoadResult(Containers::String("Failed to read model node artifact name: ") + path);
|
||||
}
|
||||
|
||||
ModelNodeArtifactHeader nodeHeader;
|
||||
input.read(reinterpret_cast<char*>(&nodeHeader), sizeof(nodeHeader));
|
||||
if (!input) {
|
||||
if (!ReadValue(data, offset, nodeHeader)) {
|
||||
return LoadResult(Containers::String("Failed to read model node artifact: ") + path);
|
||||
}
|
||||
|
||||
@@ -222,8 +282,7 @@ LoadResult LoadModelArtifact(const Containers::String& path) {
|
||||
meshBindings.Reserve(header.meshBindingCount);
|
||||
for (Core::uint32 index = 0; index < header.meshBindingCount; ++index) {
|
||||
ModelMeshBindingArtifact bindingArtifact;
|
||||
input.read(reinterpret_cast<char*>(&bindingArtifact), sizeof(bindingArtifact));
|
||||
if (!input) {
|
||||
if (!ReadValue(data, offset, bindingArtifact)) {
|
||||
return LoadResult(Containers::String("Failed to read model mesh binding artifact: ") + path);
|
||||
}
|
||||
|
||||
@@ -238,8 +297,7 @@ LoadResult LoadModelArtifact(const Containers::String& path) {
|
||||
materialBindings.Reserve(header.materialBindingCount);
|
||||
for (Core::uint32 index = 0; index < header.materialBindingCount; ++index) {
|
||||
ModelMaterialBindingArtifact bindingArtifact;
|
||||
input.read(reinterpret_cast<char*>(&bindingArtifact), sizeof(bindingArtifact));
|
||||
if (!input) {
|
||||
if (!ReadValue(data, offset, bindingArtifact)) {
|
||||
return LoadResult(Containers::String("Failed to read model material binding artifact: ") + path);
|
||||
}
|
||||
|
||||
@@ -257,5 +315,57 @@ LoadResult LoadModelArtifact(const Containers::String& path) {
|
||||
std::move(materialBindings));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool WriteModelArtifactFile(const Containers::String& artifactPath,
|
||||
const Model& model,
|
||||
Containers::String* outErrorMessage) {
|
||||
const std::filesystem::path resolvedPath = ResolveArtifactPath(artifactPath);
|
||||
std::error_code ec;
|
||||
const std::filesystem::path parentPath = resolvedPath.parent_path();
|
||||
if (!parentPath.empty()) {
|
||||
std::filesystem::create_directories(parentPath, ec);
|
||||
if (ec) {
|
||||
if (outErrorMessage != nullptr) {
|
||||
*outErrorMessage =
|
||||
Containers::String("Failed to create model artifact directory: ") +
|
||||
Containers::String(parentPath.generic_string().c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Containers::Array<Core::uint8> payload;
|
||||
if (!SerializeModelArtifactPayload(model, payload, outErrorMessage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ofstream output(resolvedPath, std::ios::binary | std::ios::trunc);
|
||||
if (!output.is_open()) {
|
||||
if (outErrorMessage != nullptr) {
|
||||
*outErrorMessage = Containers::String("Failed to open model artifact for write: ") + artifactPath;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!payload.Empty()) {
|
||||
output.write(reinterpret_cast<const char*>(payload.Data()), static_cast<std::streamsize>(payload.Size()));
|
||||
}
|
||||
|
||||
if (!output && outErrorMessage != nullptr) {
|
||||
*outErrorMessage = Containers::String("Failed to write model artifact: ") + artifactPath;
|
||||
}
|
||||
return static_cast<bool>(output);
|
||||
}
|
||||
|
||||
LoadResult LoadModelArtifact(const Containers::String& path) {
|
||||
Containers::Array<Core::uint8> data;
|
||||
if (!ReadArtifactFileData(path, ResourceType::Model, data)) {
|
||||
return LoadResult(Containers::String("Failed to read model artifact: ") + path);
|
||||
}
|
||||
|
||||
return ParseModelArtifactPayload(path, data);
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
|
||||
Reference in New Issue
Block a user