mirror of https://github.com/AxioDL/metaforce.git
Add direct SDNA parser for blend files
This commit is contained in:
parent
a230eeb3b1
commit
344148e3b3
|
@ -1 +1 @@
|
||||||
Subproject commit 7bda89073daeec487f9c82f220f199a687a1826b
|
Subproject commit 3b4d7abae6dc0d11cefa6b3aaf545bff90963ce4
|
|
@ -0,0 +1,72 @@
|
||||||
|
#pragma once
|
||||||
|
#include "hecl/hecl.hpp"
|
||||||
|
#include "hecl/FourCC.hpp"
|
||||||
|
#include "athena/DNA.hpp"
|
||||||
|
#include "athena/MemoryReader.hpp"
|
||||||
|
|
||||||
|
namespace hecl::blender
|
||||||
|
{
|
||||||
|
|
||||||
|
struct SDNABlock : public athena::io::DNA<athena::Little>
|
||||||
|
{
|
||||||
|
AT_DECL_DNA
|
||||||
|
DNAFourCC magic;
|
||||||
|
DNAFourCC nameMagic;
|
||||||
|
Value<atUint32> numNames;
|
||||||
|
Vector<String<-1>, AT_DNA_COUNT(numNames)> names;
|
||||||
|
Align<4> align1;
|
||||||
|
DNAFourCC typeMagic;
|
||||||
|
Value<atUint32> numTypes;
|
||||||
|
Vector<String<-1>, AT_DNA_COUNT(numTypes)> types;
|
||||||
|
Align<4> align2;
|
||||||
|
DNAFourCC tlenMagic;
|
||||||
|
Vector<Value<atUint16>, AT_DNA_COUNT(numTypes)> tlens;
|
||||||
|
Align<4> align3;
|
||||||
|
DNAFourCC strcMagic;
|
||||||
|
Value<atUint32> numStrcs;
|
||||||
|
struct SDNAStruct : public athena::io::DNA<athena::Little>
|
||||||
|
{
|
||||||
|
AT_DECL_DNA
|
||||||
|
Value<atUint16> type;
|
||||||
|
Value<atUint16> numFields;
|
||||||
|
struct SDNAField : public athena::io::DNA<athena::Little>
|
||||||
|
{
|
||||||
|
AT_DECL_DNA
|
||||||
|
Value<atUint16> type;
|
||||||
|
Value<atUint16> name;
|
||||||
|
atUint32 offset;
|
||||||
|
};
|
||||||
|
Vector<SDNAField, AT_DNA_COUNT(numFields)> fields;
|
||||||
|
|
||||||
|
void computeOffsets(const SDNABlock& block);
|
||||||
|
const SDNAField* lookupField(const SDNABlock& block, const char* n) const;
|
||||||
|
};
|
||||||
|
Vector<SDNAStruct, AT_DNA_COUNT(numStrcs)> strcs;
|
||||||
|
|
||||||
|
const SDNAStruct* lookupStruct(const char* n, int& idx) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FileBlock : public athena::io::DNA<athena::Little>
|
||||||
|
{
|
||||||
|
AT_DECL_DNA
|
||||||
|
DNAFourCC type;
|
||||||
|
Value<atUint32> size;
|
||||||
|
Value<atUint64> ptr;
|
||||||
|
Value<atUint32> sdnaIdx;
|
||||||
|
Value<atUint32> count;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SDNARead
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> m_data;
|
||||||
|
SDNABlock m_sdnaBlock;
|
||||||
|
public:
|
||||||
|
explicit SDNARead(SystemStringView path);
|
||||||
|
operator bool() const { return !m_data.empty(); }
|
||||||
|
const SDNABlock& sdnaBlock() const { return m_sdnaBlock; }
|
||||||
|
void enumerate(const std::function<bool(const FileBlock& block, athena::io::MemoryReader& r)>& func) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
BlendType GetBlendType(SystemStringView path);
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "athena/DNA.hpp"
|
||||||
|
|
||||||
namespace hecl
|
namespace hecl
|
||||||
{
|
{
|
||||||
|
@ -47,6 +48,32 @@ public:
|
||||||
};
|
};
|
||||||
#define FOURCC(chars) FourCC(SBIG(chars))
|
#define FOURCC(chars) FourCC(SBIG(chars))
|
||||||
|
|
||||||
|
using BigDNA = athena::io::DNA<athena::Big>;
|
||||||
|
|
||||||
|
/** FourCC with DNA read/write */
|
||||||
|
class DNAFourCC final : public BigDNA, public FourCC
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DNAFourCC() : FourCC() {}
|
||||||
|
DNAFourCC(const FourCC& other)
|
||||||
|
: FourCC() {num = other.toUint32();}
|
||||||
|
DNAFourCC(const char* name)
|
||||||
|
: FourCC(name) {}
|
||||||
|
DNAFourCC(uint32_t n)
|
||||||
|
: FourCC(n) {}
|
||||||
|
AT_DECL_EXPLICIT_DNA_YAML
|
||||||
|
};
|
||||||
|
template <> inline void DNAFourCC::Enumerate<BigDNA::Read>(typename Read::StreamT& r)
|
||||||
|
{ r.readUBytesToBuf(fcc, 4); }
|
||||||
|
template <> inline void DNAFourCC::Enumerate<BigDNA::Write>(typename Write::StreamT& w)
|
||||||
|
{ w.writeUBytes((atUint8*)fcc, 4); }
|
||||||
|
template <> inline void DNAFourCC::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& r)
|
||||||
|
{ std::string rs = r.readString(nullptr); strncpy(fcc, rs.c_str(), 4); }
|
||||||
|
template <> inline void DNAFourCC::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& w)
|
||||||
|
{ w.writeString(nullptr, std::string(fcc, 4)); }
|
||||||
|
template <> inline void DNAFourCC::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s)
|
||||||
|
{ s += 4; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace std
|
namespace std
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
set(BLENDER_SOURCES
|
set(BLENDER_SOURCES
|
||||||
Connection.cpp
|
Connection.cpp
|
||||||
|
SDNARead.cpp
|
||||||
HMDL.cpp)
|
HMDL.cpp)
|
||||||
|
|
||||||
hecl_add_list(Blender BLENDER_SOURCES)
|
hecl_add_list(Blender BLENDER_SOURCES)
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
#include "hecl/Blender/SDNARead.hpp"
|
||||||
|
#include "athena/FileReader.hpp"
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
namespace hecl::blender
|
||||||
|
{
|
||||||
|
|
||||||
|
void SDNABlock::SDNAStruct::computeOffsets(const SDNABlock& block)
|
||||||
|
{
|
||||||
|
atUint32 offset = 0;
|
||||||
|
for (SDNAField& f : fields)
|
||||||
|
{
|
||||||
|
const auto& name = block.names[f.name];
|
||||||
|
f.offset = offset;
|
||||||
|
if (name.front() == '*')
|
||||||
|
{
|
||||||
|
offset += 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
atUint32 length = block.tlens[f.type];
|
||||||
|
auto bracket = name.find('[');
|
||||||
|
if (bracket != std::string::npos)
|
||||||
|
length *= strtoul(name.data() + bracket + 1, nullptr, 10);
|
||||||
|
offset += length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SDNABlock::SDNAStruct::SDNAField* SDNABlock::SDNAStruct::lookupField(const SDNABlock& block, const char* n) const
|
||||||
|
{
|
||||||
|
for (const SDNAField& field : fields)
|
||||||
|
{
|
||||||
|
const auto& name = block.names[field.name];
|
||||||
|
auto bracket = name.find('[');
|
||||||
|
if (bracket != std::string::npos)
|
||||||
|
{
|
||||||
|
if (!name.compare(0, bracket, n))
|
||||||
|
return &field;
|
||||||
|
}
|
||||||
|
else if (!name.compare(n))
|
||||||
|
return &field;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SDNABlock::SDNAStruct* SDNABlock::lookupStruct(const char* n, int& idx) const
|
||||||
|
{
|
||||||
|
idx = 0;
|
||||||
|
for (const SDNAStruct& strc : strcs)
|
||||||
|
{
|
||||||
|
const auto& name = types[strc.type];
|
||||||
|
if (!name.compare(n))
|
||||||
|
return &strc;
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDNARead::enumerate(const std::function<bool(const FileBlock& block, athena::io::MemoryReader& r)>& func) const
|
||||||
|
{
|
||||||
|
athena::io::MemoryReader r(m_data.data(), m_data.size());
|
||||||
|
r.seek(12);
|
||||||
|
while (r.position() < r.length())
|
||||||
|
{
|
||||||
|
FileBlock block;
|
||||||
|
block.read(r);
|
||||||
|
if (block.type == FOURCC('ENDB'))
|
||||||
|
break;
|
||||||
|
athena::io::MemoryReader r2(m_data.data() + r.position(), block.size);
|
||||||
|
if (!func(block, r2))
|
||||||
|
break;
|
||||||
|
r.seek(block.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDNARead::SDNARead(SystemStringView path)
|
||||||
|
{
|
||||||
|
athena::io::FileReader r(path);
|
||||||
|
if (r.hasError())
|
||||||
|
return;
|
||||||
|
|
||||||
|
atUint64 length = r.length();
|
||||||
|
char magicBuf[7];
|
||||||
|
r.readUBytesToBuf(magicBuf, 7);
|
||||||
|
r.seek(0, athena::Begin);
|
||||||
|
if (strncmp(magicBuf, "BLENDER", 7))
|
||||||
|
{
|
||||||
|
/* Try gzip decompression */
|
||||||
|
std::unique_ptr<uint8_t[]> compBuf(new uint8_t[4096]);
|
||||||
|
m_data.resize((length * 2 + 4095) & ~4095);
|
||||||
|
z_stream zstrm = {};
|
||||||
|
inflateInit2(&zstrm, 16 + MAX_WBITS);
|
||||||
|
zstrm.next_out = (Bytef*)m_data.data();
|
||||||
|
zstrm.avail_out = m_data.size();
|
||||||
|
zstrm.total_out = 0;
|
||||||
|
|
||||||
|
atUint64 rs;
|
||||||
|
while ((rs = r.readUBytesToBuf(compBuf.get(), 4096)))
|
||||||
|
{
|
||||||
|
int inflateRet;
|
||||||
|
zstrm.next_in = compBuf.get();
|
||||||
|
zstrm.avail_in = rs;
|
||||||
|
while (zstrm.avail_in)
|
||||||
|
{
|
||||||
|
if (!zstrm.avail_out)
|
||||||
|
{
|
||||||
|
zstrm.avail_out = m_data.size();
|
||||||
|
m_data.resize(zstrm.avail_out * 2);
|
||||||
|
zstrm.next_out = (Bytef*)m_data.data() + zstrm.avail_out;
|
||||||
|
}
|
||||||
|
inflateRet = inflate(&zstrm, Z_NO_FLUSH);
|
||||||
|
if (inflateRet == Z_STREAM_END)
|
||||||
|
break;
|
||||||
|
if (inflateRet != Z_OK)
|
||||||
|
{
|
||||||
|
inflateEnd(&zstrm);
|
||||||
|
m_data = std::vector<uint8_t>();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inflateRet == Z_STREAM_END)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
inflateEnd(&zstrm);
|
||||||
|
|
||||||
|
if (strncmp((char*)m_data.data(), "BLENDER", 7))
|
||||||
|
{
|
||||||
|
m_data = std::vector<uint8_t>();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_data.resize(length);
|
||||||
|
r.readUBytesToBuf(m_data.data(), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
enumerate([this](const FileBlock& block, athena::io::MemoryReader& r)
|
||||||
|
{
|
||||||
|
if (block.type == FOURCC('DNA1'))
|
||||||
|
{
|
||||||
|
m_sdnaBlock.read(r);
|
||||||
|
for (SDNABlock::SDNAStruct& s : m_sdnaBlock.strcs)
|
||||||
|
s.computeOffsets(m_sdnaBlock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
BlendType GetBlendType(SystemStringView path)
|
||||||
|
{
|
||||||
|
SDNARead r(path);
|
||||||
|
if (!r)
|
||||||
|
return BlendType::None;
|
||||||
|
|
||||||
|
int idPropIdx;
|
||||||
|
const auto* idPropStruct = r.sdnaBlock().lookupStruct("IDProperty", idPropIdx);
|
||||||
|
if (!idPropStruct)
|
||||||
|
return BlendType::None;
|
||||||
|
const auto* typeField = idPropStruct->lookupField(r.sdnaBlock(), "type");
|
||||||
|
if (!typeField)
|
||||||
|
return BlendType::None;
|
||||||
|
atUint32 typeOffset = typeField->offset;
|
||||||
|
const auto* nameField = idPropStruct->lookupField(r.sdnaBlock(), "name");
|
||||||
|
if (!nameField)
|
||||||
|
return BlendType::None;
|
||||||
|
atUint32 nameOffset = nameField->offset;
|
||||||
|
const auto* dataField = idPropStruct->lookupField(r.sdnaBlock(), "data");
|
||||||
|
if (!dataField)
|
||||||
|
return BlendType::None;
|
||||||
|
atUint32 dataOffset = dataField->offset;
|
||||||
|
|
||||||
|
int idPropDataIdx;
|
||||||
|
const auto* idPropDataStruct = r.sdnaBlock().lookupStruct("IDPropertyData", idPropDataIdx);
|
||||||
|
if (!idPropDataStruct)
|
||||||
|
return BlendType::None;
|
||||||
|
const auto* valField = idPropDataStruct->lookupField(r.sdnaBlock(), "val");
|
||||||
|
if (!valField)
|
||||||
|
return BlendType::None;
|
||||||
|
atUint32 valOffset = dataOffset + valField->offset;
|
||||||
|
|
||||||
|
BlendType ret = BlendType::None;
|
||||||
|
r.enumerate([idPropIdx, typeOffset, nameOffset, valOffset, &ret](const FileBlock& block, athena::io::MemoryReader& r)
|
||||||
|
{
|
||||||
|
if (block.type == FOURCC('DATA') && block.sdnaIdx == idPropIdx)
|
||||||
|
{
|
||||||
|
r.seek(typeOffset, athena::Begin);
|
||||||
|
if (r.readUByte() != 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
r.seek(nameOffset, athena::Begin);
|
||||||
|
if (r.readString() != "hecl_type")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
r.seek(valOffset, athena::Begin);
|
||||||
|
ret = BlendType(r.readUint32Little());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ endif()
|
||||||
atdna(atdna_HMDLMeta.cpp ../include/hecl/HMDLMeta.hpp)
|
atdna(atdna_HMDLMeta.cpp ../include/hecl/HMDLMeta.hpp)
|
||||||
atdna(atdna_Frontend.cpp ../include/hecl/Frontend.hpp)
|
atdna(atdna_Frontend.cpp ../include/hecl/Frontend.hpp)
|
||||||
atdna(atdna_CVar.cpp ../include/hecl/CVar.hpp)
|
atdna(atdna_CVar.cpp ../include/hecl/CVar.hpp)
|
||||||
|
atdna(atdna_SDNARead.cpp ../include/hecl/Blender/SDNARead.hpp)
|
||||||
|
|
||||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
|
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
|
||||||
add_definitions(-DHECL_MULTIPROCESSOR)
|
add_definitions(-DHECL_MULTIPROCESSOR)
|
||||||
|
@ -41,6 +42,7 @@ set(HECL_HEADERS
|
||||||
../include/hecl/Backend/HLSL.hpp
|
../include/hecl/Backend/HLSL.hpp
|
||||||
../include/hecl/Backend/Metal.hpp
|
../include/hecl/Backend/Metal.hpp
|
||||||
../include/hecl/Blender/Connection.hpp
|
../include/hecl/Blender/Connection.hpp
|
||||||
|
../include/hecl/Blender/SDNARead.hpp
|
||||||
../include/hecl/Blender/Token.hpp
|
../include/hecl/Blender/Token.hpp
|
||||||
../include/hecl/SteamFinder.hpp
|
../include/hecl/SteamFinder.hpp
|
||||||
../include/hecl/Frontend.hpp
|
../include/hecl/Frontend.hpp
|
||||||
|
@ -78,6 +80,7 @@ add_library(hecl-full
|
||||||
${FRONTEND_SOURCES}
|
${FRONTEND_SOURCES}
|
||||||
${RUNTIME_SOURCES}
|
${RUNTIME_SOURCES}
|
||||||
${BLENDER_SOURCES}
|
${BLENDER_SOURCES}
|
||||||
|
atdna_SDNARead.cpp
|
||||||
${COMMON_SOURCES}
|
${COMMON_SOURCES}
|
||||||
${HECL_HEADERS}
|
${HECL_HEADERS}
|
||||||
${PLAT_SRCS})
|
${PLAT_SRCS})
|
||||||
|
|
Loading…
Reference in New Issue