332 lines
7.4 KiB
C++
332 lines
7.4 KiB
C++
#include "fbxdom.h"
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
|
|
#define FBXDOM_NATIVE_WRITE 1
|
|
#else
|
|
#define FBXDOM_NATIVE_WRITE 0
|
|
#endif
|
|
|
|
namespace fbxdom {
|
|
|
|
struct binary_reader
|
|
{
|
|
const char *ptr;
|
|
const char *start;
|
|
const char *end;
|
|
|
|
binary_reader(const void *data, size_t size)
|
|
: ptr((const char*)data), start((const char*)data)
|
|
, end((const char*)data + size) { }
|
|
|
|
size_t offset() const { return ptr - start; }
|
|
|
|
void skip(size_t size) {
|
|
assert((size_t)(end - ptr) >= size);
|
|
ptr += size;
|
|
}
|
|
|
|
template <typename T>
|
|
T peek() {
|
|
assert((size_t)(end - ptr) >= sizeof(T));
|
|
uint64_t value = 0;
|
|
for (size_t i = 0; i < sizeof(T); i++) {
|
|
value |= (uint64_t)(uint8_t)ptr[i] << i * 8;
|
|
}
|
|
T result;
|
|
memcpy(&result, &value, sizeof(T));
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T read() {
|
|
T result = peek<T>();
|
|
ptr += sizeof(T);
|
|
return result;
|
|
}
|
|
|
|
std::vector<char> read_vector(size_t size)
|
|
{
|
|
std::vector<char> result;
|
|
assert((size_t)(end - ptr) >= size);
|
|
result.insert(result.begin(), ptr, ptr + size);
|
|
ptr += size;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
struct binary_writer
|
|
{
|
|
char *ptr;
|
|
char *start;
|
|
char *end;
|
|
|
|
binary_writer(const void *data, size_t size)
|
|
: ptr((char*)data), start((char*)data)
|
|
, end((char*)data + size) { }
|
|
|
|
|
|
size_t offset() { return ptr - start; }
|
|
|
|
void skip(size_t size)
|
|
{
|
|
assert((size_t)(end - ptr) >= size);
|
|
for (size_t i = 0; i < size; i++) {
|
|
*ptr++ = 0;
|
|
}
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
void write(U value, size_t offset) {
|
|
#if FBXDOM_NATIVE_WRITE
|
|
*(T*)(start + offset) = (T)value;
|
|
#else
|
|
T tvalue = (T)value;
|
|
uint64_t uint = 0;
|
|
memcpy(&uint, &tvalue, sizeof(T));
|
|
for (size_t i = 0; i < sizeof(T); i++) {
|
|
start[offset + i] = (uint >> (i*8)) & 0xff;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
void write(U value) {
|
|
#if FBXDOM_NATIVE_WRITE
|
|
*(T*)ptr = (T)value;
|
|
ptr += sizeof(T);
|
|
#else
|
|
T tvalue = (T)value;
|
|
uint64_t uint = 0;
|
|
memcpy(&uint, &tvalue, sizeof(T));
|
|
for (size_t i = 0; i < sizeof(T); i++) {
|
|
ptr[i] = (uint >> (i*8)) & 0xff;
|
|
}
|
|
ptr += sizeof(T);
|
|
#endif
|
|
}
|
|
|
|
void write(const char *data, size_t size)
|
|
{
|
|
memcpy(ptr, data, size);
|
|
ptr += size;
|
|
}
|
|
|
|
void write(const std::vector<char> &data)
|
|
{
|
|
write(data.data(), data.size());
|
|
}
|
|
};
|
|
|
|
struct binary_parser
|
|
{
|
|
binary_reader reader;
|
|
uint32_t version = 0;
|
|
|
|
binary_parser(const void *data, size_t size)
|
|
: reader(data, size) { }
|
|
|
|
void parse_array(value &v, size_t elem_size)
|
|
{
|
|
v.data_array.length = reader.read<uint32_t>();
|
|
v.data_array.encoding = reader.read<uint32_t>();
|
|
v.data_array.compressed_length = reader.read<uint32_t>();
|
|
v.data = reader.read_vector(v.data_array.compressed_length);
|
|
}
|
|
|
|
value parse_value()
|
|
{
|
|
value v;
|
|
|
|
char type = reader.read<char>();
|
|
v.type = type;
|
|
switch (type) {
|
|
case 'B': case 'C': v.data_float = (double)(v.data_int = reader.read<int8_t>()); break;
|
|
case 'Y': v.data_float = (double)(v.data_int = reader.read<int16_t>()); break;
|
|
case 'I': v.data_float = (double)(v.data_int = reader.read<int32_t>()); break;
|
|
case 'L': v.data_float = (double)(v.data_int = reader.read<int64_t>()); break;
|
|
case 'F': v.data_int = (int64_t)(v.data_float = reader.read<float>()); break;
|
|
case 'D': v.data_int = (int64_t)(v.data_float = reader.read<double>()); break;
|
|
case 'b': case 'c': parse_array(v, 1); break;
|
|
case 'i': case 'f': parse_array(v, 4); break;
|
|
case 'l': case 'd': parse_array(v, 8); break;
|
|
case 'S': case 'R': v.data = reader.read_vector(reader.read<uint32_t>()); break;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
node_ptr parse_node()
|
|
{
|
|
size_t num_properties;
|
|
size_t end_offset;
|
|
|
|
if (version >= 7500) {
|
|
end_offset = (size_t)reader.read<uint64_t>();
|
|
num_properties = (size_t)reader.read<uint64_t>();
|
|
reader.skip(8);
|
|
} else {
|
|
end_offset = (size_t)reader.read<uint32_t>();
|
|
num_properties = (size_t)reader.read<uint32_t>();
|
|
reader.skip(4);
|
|
}
|
|
|
|
size_t name_len = reader.read<uint8_t>();
|
|
if (end_offset == 0 && name_len == 0) return { };
|
|
|
|
node_ptr n = std::make_shared<node>();
|
|
|
|
n->name = reader.read_vector(name_len);
|
|
n->values.reserve(num_properties);
|
|
for (size_t i = 0; i < num_properties; i++) {
|
|
n->values.push_back(parse_value());
|
|
}
|
|
|
|
while (reader.offset() < end_offset || end_offset == 0) {
|
|
node_ptr child = parse_node();
|
|
if (!child) break;
|
|
n->children.push_back(std::move(child));
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
node_ptr parse_root()
|
|
{
|
|
std::vector<char> magic = reader.read_vector(21);
|
|
if (memcmp(magic.data(), "Kaydara FBX Binary \x00", 21) != 0) return { };
|
|
reader.skip(2);
|
|
version = reader.read<uint32_t>();
|
|
|
|
node_ptr root = std::make_shared<node>();
|
|
|
|
{
|
|
value v;
|
|
v.data_int = version;
|
|
root->values.push_back(v);
|
|
}
|
|
|
|
for (;;) {
|
|
node_ptr child = parse_node();
|
|
if (!child) break;
|
|
root->children.push_back(std::move(child));
|
|
}
|
|
|
|
return root;
|
|
}
|
|
};
|
|
|
|
struct binary_dumper
|
|
{
|
|
binary_writer writer;
|
|
uint32_t version = 0;
|
|
|
|
binary_dumper(void *data, size_t size)
|
|
: writer(data, size) { }
|
|
|
|
void dump_array(const value &v)
|
|
{
|
|
writer.write<uint32_t>(v.data_array.length);
|
|
writer.write<uint32_t>(v.data_array.encoding);
|
|
writer.write<uint32_t>(v.data_array.compressed_length);
|
|
writer.write(v.data);
|
|
}
|
|
|
|
void dump_value(const value &v)
|
|
{
|
|
writer.write<char>(v.type);
|
|
switch (v.type) {
|
|
case 'B': case 'C': writer.write<int8_t>(v.data_int); break;
|
|
case 'Y': writer.write<int16_t>(v.data_int); break;
|
|
case 'I': writer.write<int32_t>(v.data_int); break;
|
|
case 'L': writer.write<int64_t>(v.data_int); break;
|
|
case 'F': writer.write<float>(v.data_float); break;
|
|
case 'D': writer.write<double>(v.data_float); break;
|
|
case 'b': case 'c': dump_array(v); break;
|
|
case 'i': case 'f': dump_array(v); break;
|
|
case 'l': case 'd': dump_array(v); break;
|
|
case 'S': case 'R':
|
|
writer.write<uint32_t>(v.data.size());
|
|
writer.write(v.data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void dump_node(node_ptr node)
|
|
{
|
|
size_t node_start = writer.offset();
|
|
if (version >= 7500) {
|
|
writer.skip(8);
|
|
writer.write<uint64_t>(node->values.size());
|
|
writer.skip(8);
|
|
} else {
|
|
writer.skip(4);
|
|
writer.write<uint32_t>(node->values.size());
|
|
writer.skip(4);
|
|
}
|
|
|
|
writer.write<uint8_t>(node->name.size());
|
|
writer.write(node->name);
|
|
|
|
size_t value_start = writer.offset();
|
|
for (const value &v : node->values) {
|
|
dump_value(v);
|
|
}
|
|
size_t value_end = writer.offset();
|
|
|
|
if (!node->children.empty()) {
|
|
for (const node_ptr child : node->children) {
|
|
dump_node(child);
|
|
}
|
|
writer.skip(version >= 7500 ? 25 : 13);
|
|
}
|
|
|
|
size_t node_end = writer.offset();
|
|
|
|
if (version >= 7500) {
|
|
writer.write<uint64_t>(node_end, node_start + 0);
|
|
writer.write<uint64_t>(value_end - value_start, node_start + 16);
|
|
} else {
|
|
writer.write<uint32_t>(node_end, node_start + 0);
|
|
writer.write<uint32_t>(value_end - value_start, node_start + 8);
|
|
}
|
|
}
|
|
|
|
void dump_root(node_ptr root)
|
|
{
|
|
if (root->values.size() > 0) {
|
|
version = (uint32_t)root->values[0].data_int;
|
|
} else {
|
|
version = 7500;
|
|
}
|
|
|
|
writer.write("Kaydara FBX Binary \x00\x1a\x00", 23);
|
|
writer.write<uint32_t>(version);
|
|
|
|
for (const node_ptr child : root->children) {
|
|
dump_node(child);
|
|
}
|
|
writer.skip(version >= 7500 ? 25 : 13);
|
|
}
|
|
};
|
|
|
|
node_ptr node::copy() const
|
|
{
|
|
return std::make_shared<node>(*this);
|
|
}
|
|
|
|
node_ptr parse(const void *data, size_t size)
|
|
{
|
|
return binary_parser(data, size).parse_root();
|
|
}
|
|
|
|
size_t dump(void *dst, size_t size, node_ptr root)
|
|
{
|
|
binary_dumper d { dst, size };
|
|
d.dump_root(root);
|
|
return d.writer.offset();
|
|
}
|
|
|
|
}
|