mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
cppkore uses string/wstring as StringBase while we use std::string/std::wstring as string/wstring. Changed all types in cppkore to String/WString instead.
539 lines
14 KiB
C++
539 lines
14 KiB
C++
#include "stdafx.h"
|
|
#include "KaydaraFBXContainer.h"
|
|
|
|
namespace Assets::Exporters
|
|
{
|
|
constexpr std::tuple<const char*, int> HeaderExtensionProperties[] =
|
|
{
|
|
{"FBXHeaderVersion", 1003},
|
|
{"FBXVersion", 7400},
|
|
{"EncryptionType", 0}
|
|
};
|
|
|
|
constexpr std::tuple<const char*, int> HeaderExtensionTimeProperties[] =
|
|
{
|
|
{"Version", 1000},
|
|
{"Year", 2019},
|
|
{"Month", 4},
|
|
{"Day", 26},
|
|
{"Hour", 10},
|
|
{"Minute", 39},
|
|
{"Second", 36},
|
|
{"Millisecond", 240}
|
|
};
|
|
|
|
constexpr std::pair<const char*, const char*> HeaderExtensionCreatorProperty =
|
|
{
|
|
"Creator", "KoreLib by DTZxPorter"
|
|
};
|
|
|
|
constexpr std::pair<const char*, const char*> HeaderFileIdNode =
|
|
{
|
|
"FileId", "\x28\xb3\x2a\xeb\xb6\x24\xcc\xc2\xbf\xc8\xb0\x2a\xa9\x2b\xfc\xf1"
|
|
};
|
|
|
|
constexpr std::pair<const char*, const char*> HeaderCreationTimeNode =
|
|
{
|
|
"CreationTime", "1970-01-01 10:00:00:000"
|
|
};
|
|
|
|
constexpr std::tuple<const char*, const char*, const char*> HeaderDefinitionsNode[] =
|
|
{
|
|
{"ObjectType", "GlobalSettings", ""},
|
|
{"ObjectType", "NodeAttribute", ""},
|
|
{"ObjectType", "Geometry", "FbxMesh"},
|
|
{"ObjectType", "Model", "FbxNode"},
|
|
{"ObjectType", "Pose", ""},
|
|
{"ObjectType", "Deformer", ""},
|
|
{"ObjectType", "Material", "FbxSurfacePhong"},
|
|
{"ObjectType", "Texture", "FbxFileTexture"}
|
|
};
|
|
|
|
constexpr uint8_t FooterData[] =
|
|
{
|
|
0xFA, 0xBC, 0xAB, 0x09, 0xD0, 0xC8, 0xD4, 0x66, 0xB1, 0x76, 0xFB, 0x83,
|
|
0x1C, 0xF7, 0x26, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0xE8, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x5A, 0x8C, 0x6A, 0xDE, 0xF5,
|
|
0xD9, 0x7E, 0xEC, 0xE9, 0x0C, 0xE3, 0x75, 0x8F, 0x29, 0x0B
|
|
};
|
|
|
|
#pragma pack(push, 1)
|
|
struct KaydaraFBXHeader
|
|
{
|
|
char Magic[0x15];
|
|
|
|
uint16_t VersionMinor;
|
|
uint32_t VersionMajor;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
KaydaraFBXDocument::KaydaraFBXDocument()
|
|
{
|
|
this->InitializeFBXHeaderExtension();
|
|
this->InitializeGenerics();
|
|
this->InitializeGlobalSettings();
|
|
this->InitializeDocuments();
|
|
this->InitializeReferences();
|
|
this->InitializeDefinitions();
|
|
this->InitializeDynamics();
|
|
}
|
|
|
|
void KaydaraFBXDocument::Serialize(IO::BinaryWriter& Writer)
|
|
{
|
|
Writer.Write<KaydaraFBXHeader>({ "Kaydara FBX Binary ", 26, 7400 });
|
|
|
|
for (auto& Child : this->Nodes)
|
|
Child.Prepare();
|
|
for (auto& Child : this->Nodes)
|
|
Child.Serialize(Writer);
|
|
|
|
uint8_t EmptyNode[13]{};
|
|
Writer.Write(EmptyNode, 0, sizeof(EmptyNode));
|
|
|
|
Writer.Write((uint8_t*)&FooterData[0], 0, sizeof(FooterData));
|
|
}
|
|
|
|
KaydaraFBXNode& KaydaraFBXDocument::GetRootNode(const char* Name)
|
|
{
|
|
for (auto& Node : this->Nodes)
|
|
if (Node.Name == Name)
|
|
return Node;
|
|
|
|
throw std::exception("No FBX node was found");
|
|
}
|
|
|
|
void KaydaraFBXDocument::InitializeFBXHeaderExtension()
|
|
{
|
|
auto& Node = this->Nodes.Emplace("FBXHeaderExtension");
|
|
|
|
for (auto& Property : HeaderExtensionProperties)
|
|
Node.Children.Emplace(std::get<0>(Property)).Properties.EmplaceBack('I', &std::get<1>(Property));
|
|
|
|
auto& CreationTime = Node.Children.Emplace("CreationTimeStamp");
|
|
|
|
for (auto& Property : HeaderExtensionTimeProperties)
|
|
CreationTime.Children.Emplace(std::get<0>(Property)).Properties.EmplaceBack('I', &std::get<1>(Property));
|
|
}
|
|
|
|
void KaydaraFBXDocument::InitializeGenerics()
|
|
{
|
|
String s1(HeaderFileIdNode.second);
|
|
String s2(HeaderCreationTimeNode.second);
|
|
String s3(HeaderExtensionCreatorProperty.second);
|
|
this->Nodes.Emplace(HeaderFileIdNode.first).Properties.EmplaceBack('R', &s1);
|
|
this->Nodes.Emplace(HeaderCreationTimeNode.first).Properties.EmplaceBack('S', &s2);
|
|
this->Nodes.Emplace(HeaderExtensionCreatorProperty.first).Properties.EmplaceBack('S', &s3);
|
|
}
|
|
|
|
void KaydaraFBXDocument::InitializeGlobalSettings()
|
|
{
|
|
auto& Node = this->Nodes.Emplace("GlobalSettings");
|
|
|
|
Node.Children.Emplace("Version").AddPropertyInteger32(1000);
|
|
|
|
auto& Properties = Node.Children.Emplace("Properties70");
|
|
|
|
{
|
|
auto& Prop = Properties.Children.Emplace("P");
|
|
|
|
Prop.AddPropertyString("UpAxis");
|
|
Prop.AddPropertyString("int");
|
|
Prop.AddPropertyString("Integer");
|
|
Prop.AddPropertyString("");
|
|
Prop.AddPropertyInteger32(1);
|
|
}
|
|
|
|
{
|
|
auto& Prop = Properties.Children.Emplace("P");
|
|
|
|
Prop.AddPropertyString("FrontAxis");
|
|
Prop.AddPropertyString("int");
|
|
Prop.AddPropertyString("Integer");
|
|
Prop.AddPropertyString("");
|
|
Prop.AddPropertyInteger32(2);
|
|
}
|
|
}
|
|
|
|
void KaydaraFBXDocument::InitializeDocuments()
|
|
{
|
|
auto& Node = this->Nodes.Emplace("Documents");
|
|
|
|
Node.Children.Emplace("Count").AddPropertyInteger32(1);
|
|
|
|
auto& Document = Node.Children.Emplace("Document");
|
|
|
|
Document.AddPropertyInteger64(318825901);
|
|
Document.AddPropertyString("Scene");
|
|
Document.AddPropertyString("Scene");
|
|
|
|
Document.Children.Emplace("RootNode").AddPropertyInteger64(0);
|
|
}
|
|
|
|
void KaydaraFBXDocument::InitializeReferences()
|
|
{
|
|
this->Nodes.EmplaceBack("References");
|
|
}
|
|
|
|
void KaydaraFBXDocument::InitializeDefinitions()
|
|
{
|
|
auto& Node = this->Nodes.Emplace("Definitions");
|
|
|
|
Node.Children.Emplace("Version").AddPropertyInteger32(100);
|
|
Node.Children.Emplace("Count").AddPropertyInteger32(ARRAYSIZE(HeaderDefinitionsNode));
|
|
|
|
for (auto& Type : HeaderDefinitionsNode)
|
|
{
|
|
auto& Definition = Node.Children.Emplace(std::get<0>(Type));
|
|
|
|
Definition.AddPropertyString(std::get<1>(Type));
|
|
Definition.Children.Emplace("Count").AddPropertyInteger32(1);
|
|
|
|
if (strlen(std::get<2>(Type)))
|
|
Definition.Children.Emplace("PropertyTemplate").AddPropertyString(std::get<2>(Type));
|
|
}
|
|
}
|
|
|
|
void KaydaraFBXDocument::InitializeDynamics()
|
|
{
|
|
// Create each dynamic node
|
|
auto& NodeObjects = this->Nodes.Emplace("Objects");
|
|
auto& NodeConnections = this->Nodes.Emplace("Connections");
|
|
auto& NodeTakes = this->Nodes.Emplace("Takes");
|
|
|
|
NodeTakes.Children.Emplace("Current").AddPropertyString("");
|
|
}
|
|
|
|
KaydaraFBXProperty::KaydaraFBXProperty()
|
|
: _Type(0), _IntegralValue{}
|
|
{
|
|
}
|
|
|
|
KaydaraFBXProperty::KaydaraFBXProperty(const char Type, const void* Data)
|
|
: _Type(Type), _IntegralValue{}
|
|
{
|
|
switch (Type)
|
|
{
|
|
case 'Y': _IntegralValue.Integer16 = *(uint16_t*)Data; break;
|
|
case 'C': _IntegralValue.Boolean = *(bool*)Data; break;
|
|
case 'I': _IntegralValue.Integer32 = *(uint32_t*)Data; break;
|
|
case 'F': _IntegralValue.Float32 = *(float*)Data; break;
|
|
case 'D': _IntegralValue.Float64 = *(double*)Data; break;
|
|
case 'L': _IntegralValue.Integer64 = *(uint64_t*)Data; break;
|
|
|
|
case 'R':
|
|
case 'S':
|
|
_StringValue = *(String*)Data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
KaydaraFBXProperty::KaydaraFBXProperty(const char Type, const void* Data, const uint32_t Count)
|
|
: _Type(Type), _IntegralValue{}
|
|
{
|
|
if (Data == nullptr || Count == 0)
|
|
return;
|
|
|
|
for (uint32_t i = 0; i < Count; i++)
|
|
{
|
|
KaydaraFBXIntegralProperty Property;
|
|
|
|
switch (Type)
|
|
{
|
|
case 'b': Property.Boolean = ((bool*)Data)[i]; break;
|
|
case 'i': Property.Integer32 = ((uint32_t*)Data)[i]; break;
|
|
case 'f': Property.Float32 = ((float*)Data)[i]; break;
|
|
case 'd': Property.Float64 = ((double*)Data)[i]; break;
|
|
case 'l': Property.Integer64 = ((uint64_t*)Data)[i]; break;
|
|
}
|
|
|
|
this->_IntegralArrayValues.EmplaceBack(std::move(Property));
|
|
}
|
|
}
|
|
|
|
void KaydaraFBXProperty::AddValueBoolean(bool Value)
|
|
{
|
|
KaydaraFBXIntegralProperty Property;
|
|
Property.Boolean = Value;
|
|
|
|
this->_IntegralArrayValues.EmplaceBack(std::move(Property));
|
|
}
|
|
|
|
void KaydaraFBXProperty::AddValueInteger32(uint32_t Value)
|
|
{
|
|
KaydaraFBXIntegralProperty Property;
|
|
Property.Integer32 = Value;
|
|
|
|
this->_IntegralArrayValues.EmplaceBack(std::move(Property));
|
|
}
|
|
|
|
void KaydaraFBXProperty::AddValueInteger64(uint64_t Value)
|
|
{
|
|
KaydaraFBXIntegralProperty Property;
|
|
Property.Integer64 = Value;
|
|
|
|
this->_IntegralArrayValues.EmplaceBack(std::move(Property));
|
|
}
|
|
|
|
void KaydaraFBXProperty::AddValueFloat32(float Value)
|
|
{
|
|
KaydaraFBXIntegralProperty Property;
|
|
Property.Float32 = Value;
|
|
|
|
this->_IntegralArrayValues.EmplaceBack(std::move(Property));
|
|
}
|
|
|
|
void KaydaraFBXProperty::AddValueFloat64(double Value)
|
|
{
|
|
KaydaraFBXIntegralProperty Property;
|
|
Property.Float64 = Value;
|
|
|
|
this->_IntegralArrayValues.EmplaceBack(std::move(Property));
|
|
}
|
|
|
|
void KaydaraFBXProperty::Serialize(IO::BinaryWriter& Writer)
|
|
{
|
|
Writer.Write<char>(this->_Type);
|
|
|
|
switch (this->_Type)
|
|
{
|
|
case 'Y':
|
|
Writer.Write<uint16_t>(this->_IntegralValue.Integer16);
|
|
break;
|
|
case 'C':
|
|
Writer.Write<bool>(this->_IntegralValue.Boolean);
|
|
break;
|
|
case 'I':
|
|
Writer.Write<uint32_t>(this->_IntegralValue.Integer32);
|
|
break;
|
|
case 'F':
|
|
Writer.Write<float>(this->_IntegralValue.Float32);
|
|
break;
|
|
case 'D':
|
|
Writer.Write<double>(this->_IntegralValue.Float64);
|
|
break;
|
|
case 'L':
|
|
Writer.Write<uint64_t>(this->_IntegralValue.Integer64);
|
|
break;
|
|
case 'R':
|
|
case 'S':
|
|
Writer.Write<uint32_t>(this->_StringValue.Length());
|
|
Writer.Write((uint8_t*)&this->_StringValue[0], 0, this->_StringValue.Length());
|
|
break;
|
|
|
|
default:
|
|
Writer.Write<uint32_t>(this->_IntegralArrayValues.Count());
|
|
Writer.Write<uint32_t>(0);
|
|
|
|
{
|
|
uint32_t CompressedLength = this->_IntegralArrayValues.Count();
|
|
|
|
switch (this->_Type)
|
|
{
|
|
case 'f': CompressedLength *= sizeof(float); break;
|
|
case 'd': CompressedLength *= sizeof(double); break;
|
|
case 'l': CompressedLength *= sizeof(uint64_t); break;
|
|
case 'i': CompressedLength *= sizeof(uint32_t); break;
|
|
case 'b': CompressedLength *= sizeof(bool); break;
|
|
}
|
|
|
|
Writer.Write<uint32_t>(CompressedLength);
|
|
|
|
for (auto& Value : this->_IntegralArrayValues)
|
|
{
|
|
switch (this->_Type)
|
|
{
|
|
case 'f':
|
|
Writer.Write<float>(Value.Float32);
|
|
break;
|
|
case 'd':
|
|
Writer.Write<double>(Value.Float64);
|
|
break;
|
|
case 'l':
|
|
Writer.Write<uint64_t>(Value.Integer64);
|
|
break;
|
|
case 'i':
|
|
Writer.Write<uint32_t>(Value.Integer32);
|
|
break;
|
|
case 'b':
|
|
Writer.Write<bool>(Value.Boolean);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
const uint32_t KaydaraFBXProperty::GetLength() const
|
|
{
|
|
switch (this->_Type)
|
|
{
|
|
case 'Y': return sizeof(uint16_t) + sizeof(char);
|
|
case 'C': return sizeof(bool) + sizeof(char);
|
|
case 'I': return sizeof(uint32_t) + sizeof(char);
|
|
case 'F': return sizeof(float) + sizeof(char);
|
|
case 'D': return sizeof(double) + sizeof(char);
|
|
case 'L': return sizeof(uint64_t) + sizeof(char);
|
|
case 'S':
|
|
case 'R':
|
|
return this->_StringValue.Length() + sizeof(uint32_t) + sizeof(char);
|
|
case 'f': return this->_IntegralArrayValues.Count() * sizeof(float) + 13;
|
|
case 'd': return this->_IntegralArrayValues.Count() * sizeof(double) + 13;
|
|
case 'l': return this->_IntegralArrayValues.Count() * sizeof(uint64_t) + 13;
|
|
case 'i': return this->_IntegralArrayValues.Count() * sizeof(uint32_t) + 13;
|
|
case 'b': return this->_IntegralArrayValues.Count() * sizeof(bool) + 13;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
KaydaraFBXIntegralProperty KaydaraFBXProperty::GetIntegralValue() const
|
|
{
|
|
return this->_IntegralValue;
|
|
}
|
|
|
|
KaydaraFBXNode::KaydaraFBXNode(const String& Name)
|
|
: Name(Name)
|
|
{
|
|
}
|
|
|
|
KaydaraFBXNode::KaydaraFBXNode(const char* Name)
|
|
: Name(Name)
|
|
{
|
|
}
|
|
|
|
void KaydaraFBXNode::Prepare()
|
|
{
|
|
for (auto& Child : this->Children)
|
|
Child.Prepare();
|
|
|
|
if (this->Children.Count() > 0 || (this->Properties.Count() == 0 && this->Name.Length() > 0))
|
|
this->Children.EmplaceBack();
|
|
}
|
|
|
|
void KaydaraFBXNode::Serialize(IO::BinaryWriter& Writer)
|
|
{
|
|
if (this->Name.Length() == 0 && this->Children.Count() == 0 && this->Properties.Count() == 0)
|
|
{
|
|
uint8_t EmptyNode[13]{};
|
|
Writer.Write(EmptyNode, 0, sizeof(EmptyNode));
|
|
|
|
return;
|
|
}
|
|
|
|
uint32_t PropertyListLength = 0;
|
|
uint32_t NodeLength = 13 + Name.Length();
|
|
|
|
for (auto& Property : this->Properties)
|
|
PropertyListLength += Property.GetLength();
|
|
|
|
NodeLength += PropertyListLength;
|
|
|
|
for (auto& Child : this->Children)
|
|
NodeLength += Child.GetLength();
|
|
|
|
Writer.Write<uint32_t>((uint32_t)Writer.GetBaseStream()->GetPosition() + NodeLength);
|
|
Writer.Write<uint32_t>(this->Properties.Count());
|
|
Writer.Write<uint32_t>(PropertyListLength);
|
|
Writer.Write<uint8_t>((uint8_t)Name.Length());
|
|
Writer.Write((uint8_t*)&Name[0], 0, Name.Length());
|
|
|
|
for (auto& Property : this->Properties)
|
|
Property.Serialize(Writer);
|
|
for (auto& Child : this->Children)
|
|
Child.Serialize(Writer);
|
|
}
|
|
|
|
void KaydaraFBXNode::AddPropertyBoolean(bool Value)
|
|
{
|
|
this->Properties.EmplaceBack('C', &Value);
|
|
}
|
|
|
|
void KaydaraFBXNode::AddPropertyInteger16(uint16_t Value)
|
|
{
|
|
this->Properties.EmplaceBack('Y', &Value);
|
|
}
|
|
|
|
void KaydaraFBXNode::AddPropertyInteger32(uint32_t Value)
|
|
{
|
|
this->Properties.EmplaceBack('I', &Value);
|
|
}
|
|
|
|
void KaydaraFBXNode::AddPropertyInteger64(uint64_t Value)
|
|
{
|
|
this->Properties.EmplaceBack('L', &Value);
|
|
}
|
|
|
|
void KaydaraFBXNode::AddPropertyFloat32(float Value)
|
|
{
|
|
this->Properties.EmplaceBack('F', &Value);
|
|
}
|
|
|
|
void KaydaraFBXNode::AddPropertyFloat64(double Value)
|
|
{
|
|
this->Properties.EmplaceBack('D', &Value);
|
|
}
|
|
|
|
void KaydaraFBXNode::AddPropertyString(const String& Value)
|
|
{
|
|
this->Properties.EmplaceBack('S', &Value);
|
|
}
|
|
|
|
void KaydaraFBXNode::AddPropertyString(const char* Value)
|
|
{
|
|
String s(Value);
|
|
this->Properties.EmplaceBack('S', &s);
|
|
}
|
|
|
|
void KaydaraFBXNode::AddPropertyString(const char* Value, const uint32_t Length)
|
|
{
|
|
String s(Value, Length);
|
|
this->Properties.EmplaceBack('S', &s);
|
|
}
|
|
|
|
void KaydaraFBXNode::AddPropertyRaw(const char* Value)
|
|
{
|
|
String s(Value);
|
|
this->Properties.EmplaceBack('R', &s);
|
|
}
|
|
|
|
void KaydaraFBXNode::AddPropertyRaw(const char* Value, const uint32_t Length)
|
|
{
|
|
String s(Value, Length);
|
|
this->Properties.EmplaceBack('R', &s);
|
|
}
|
|
|
|
KaydaraFBXNode* KaydaraFBXNode::FindByUID(const uint64_t UID)
|
|
{
|
|
for (auto& Child : this->Children)
|
|
{
|
|
if (Child.Properties.Count() > 0 && Child.Properties[0].GetIntegralValue().Integer64 == UID)
|
|
return &Child;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const uint32_t KaydaraFBXNode::GetLength() const
|
|
{
|
|
uint32_t Result = 13 + Name.Length();
|
|
|
|
for (auto& Child : this->Children)
|
|
Result += Child.GetLength();
|
|
for (auto& Property : this->Properties)
|
|
Result += Property.GetLength();
|
|
|
|
return Result;
|
|
}
|
|
}
|