This commit is contained in:
Phillip Stephens 2015-08-11 18:45:55 -07:00
commit cdd7b45394
57 changed files with 5989 additions and 737 deletions

View File

@ -0,0 +1,29 @@
#include <stdint.h>
#include <BlenderConnection.hpp>
#include "BlenderSupport.hpp"
extern "C" uint8_t RETRO_MASTER_SHADER[];
extern "C" size_t RETRO_MASTER_SHADER_SZ;
namespace Retro
{
namespace Blender
{
bool BuildMasterShader(const HECL::ProjectPath& path)
{
if (path.getPathType() == HECL::ProjectPath::PT_FILE)
return true;
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
if (!conn.createBlend(path.getAbsolutePath()))
return false;
{
HECL::BlenderConnection::PyOutStream os = conn.beginPythonOut(true);
os << RETRO_MASTER_SHADER;
os << "make_master_shader_library()\n";
}
return conn.saveBlend();
}
}
}

View File

@ -0,0 +1,16 @@
#ifndef _RETRO_BLENDER_SUPPORT_HPP_
#define _RETRO_BLENDER_SUPPORT_HPP_
#include <HECL/HECL.hpp>
namespace Retro
{
namespace Blender
{
bool BuildMasterShader(const HECL::ProjectPath& path);
}
}
#endif // _RETRO_BLENDER_SUPPORT_HPP_

View File

@ -0,0 +1,419 @@
"Defines a node group for all game shaders to provide individual outputs to"
import bpy
# UV modifier nodes:
# http://www.metroid2002.com/retromodding/wiki/Materials_(Metroid_Prime)#UV_Animations
# 0 - Modelview Inverse (zero translation)
def make_uvm0():
new_grp = bpy.data.node_groups.new('RWKUVMode0Node', 'ShaderNodeTree')
new_grp.inputs.new('NodeSocketVector', 'UV In')
new_grp.outputs.new('NodeSocketVector', 'UV Out')
new_grp.use_fake_user = True
# Group inputs
grp_in = new_grp.nodes.new('NodeGroupInput')
grp_in.location = (-100, 0)
# Group outputs
grp_out = new_grp.nodes.new('NodeGroupOutput')
grp_out.location = (500, 0)
# UV vertical-flip (to match GameCube's UV-coordinate space)
v_flip = new_grp.nodes.new('ShaderNodeMapping')
v_flip.location = (100, 0)
v_flip.vector_type = 'TEXTURE'
v_flip.scale[1] = -1.0
# Links
new_grp.links.new(grp_in.outputs[0], v_flip.inputs[0])
new_grp.links.new(v_flip.outputs[0], grp_out.inputs[0])
# 1 - Modelview Inverse
def make_uvm1():
new_grp = bpy.data.node_groups.new('RWKUVMode1Node', 'ShaderNodeTree')
new_grp.inputs.new('NodeSocketVector', 'UV In')
new_grp.outputs.new('NodeSocketVector', 'UV Out')
new_grp.use_fake_user = True
# Group inputs
grp_in = new_grp.nodes.new('NodeGroupInput')
grp_in.location = (-300, 0)
# Group outputs
grp_out = new_grp.nodes.new('NodeGroupOutput')
grp_out.location = (500, 0)
# Geometry input
geom_in = new_grp.nodes.new('ShaderNodeGeometry')
geom_in.location = (-700, 0)
# View flip
view_flip = new_grp.nodes.new('ShaderNodeMapping')
view_flip.location = (-500, -100)
view_flip.vector_type = 'TEXTURE'
view_flip.scale = (-1.0, -1.0, 1.0)
# Normal/translation add
adder = new_grp.nodes.new('ShaderNodeVectorMath')
adder.location = (-100, 0)
adder.operation = 'ADD'
# UV vertical-flip (to match GameCube's UV-coordinate space)
v_flip = new_grp.nodes.new('ShaderNodeMapping')
v_flip.location = (100, 0)
v_flip.vector_type = 'TEXTURE'
v_flip.scale[1] = -1.0
# Links
new_grp.links.new(grp_in.outputs[0], adder.inputs[0])
new_grp.links.new(geom_in.outputs['View'], view_flip.inputs[0])
new_grp.links.new(view_flip.outputs[0], adder.inputs[1])
new_grp.links.new(adder.outputs[0], v_flip.inputs[0])
new_grp.links.new(v_flip.outputs[0], grp_out.inputs[0])
# 2 - UV Scroll
def make_uvm2():
new_grp = bpy.data.node_groups.new('RWKUVMode2Node', 'ShaderNodeTree')
new_grp.inputs.new('NodeSocketVector', 'UV In')
new_grp.inputs.new('NodeSocketVector', 'Offset')
new_grp.inputs.new('NodeSocketVector', 'Scale')
new_grp.outputs.new('NodeSocketVector', 'UV Out')
new_grp.use_fake_user = True
# Group inputs
grp_in = new_grp.nodes.new('NodeGroupInput')
grp_in.location = (-100, 0)
# Group outputs
grp_out = new_grp.nodes.new('NodeGroupOutput')
grp_out.location = (500, 0)
# Adder1
adder1 = new_grp.nodes.new('ShaderNodeVectorMath')
adder1.operation = 'ADD'
adder1.location = (100, 0)
# Adder2
adder2 = new_grp.nodes.new('ShaderNodeVectorMath')
adder2.operation = 'ADD'
adder2.location = (100, 200)
# Links
new_grp.links.new(grp_in.outputs[0], adder2.inputs[0])
new_grp.links.new(grp_in.outputs[1], adder1.inputs[0])
new_grp.links.new(grp_in.outputs[2], adder1.inputs[1])
new_grp.links.new(adder1.outputs[0], adder2.inputs[1])
new_grp.links.new(adder2.outputs[0], grp_out.inputs[0])
# 3 - Rotation
def make_uvm3():
new_grp = bpy.data.node_groups.new('RWKUVMode3Node', 'ShaderNodeTree')
new_grp.inputs.new('NodeSocketVector', 'UV In')
new_grp.inputs.new('NodeSocketFloat', 'Offset')
new_grp.inputs.new('NodeSocketFloat', 'Scale')
new_grp.outputs.new('NodeSocketVector', 'UV Out')
new_grp.use_fake_user = True
# Group inputs
grp_in = new_grp.nodes.new('NodeGroupInput')
grp_in.location = (-100, 0)
# Group outputs
grp_out = new_grp.nodes.new('NodeGroupOutput')
grp_out.location = (700, 0)
# Adder1
add1 = new_grp.nodes.new('ShaderNodeMath')
add1.operation = 'ADD'
add1.location = (500, 0)
# Links
new_grp.links.new(grp_in.outputs[0], grp_out.inputs[0])
new_grp.links.new(grp_in.outputs[1], add1.inputs[0])
new_grp.links.new(grp_in.outputs[2], add1.inputs[1])
# 4 - Horizontal Filmstrip Animation
def make_uvm4():
new_grp = bpy.data.node_groups.new('RWKUVMode4Node', 'ShaderNodeTree')
new_grp.inputs.new('NodeSocketVector', 'UV In')
new_grp.inputs.new('NodeSocketFloat', 'Scale')
new_grp.inputs.new('NodeSocketFloat', 'NumFrames')
new_grp.inputs.new('NodeSocketFloat', 'Step')
new_grp.inputs.new('NodeSocketFloat', 'Offset')
new_grp.outputs.new('NodeSocketVector', 'UV Out')
new_grp.use_fake_user = True
# Group inputs
grp_in = new_grp.nodes.new('NodeGroupInput')
grp_in.location = (-1000, 0)
# Group outputs
grp_out = new_grp.nodes.new('NodeGroupOutput')
grp_out.location = (800, 0)
# Multiply1
mult1 = new_grp.nodes.new('ShaderNodeMath')
mult1.operation = 'MULTIPLY'
mult1.location = (-800, 0)
# Multiply2
mult2 = new_grp.nodes.new('ShaderNodeMath')
mult2.operation = 'MULTIPLY'
mult2.location = (-600, 0)
# Modulo
mod1 = new_grp.nodes.new('ShaderNodeMath')
mod1.operation = 'MODULO'
mod1.inputs[1].default_value = 1.0
mod1.location = (-400, 0)
# Multiply3
mult3 = new_grp.nodes.new('ShaderNodeMath')
mult3.operation = 'MULTIPLY'
mult3.location = (-200, 0)
# Multiply4
mult4 = new_grp.nodes.new('ShaderNodeMath')
mult4.operation = 'MULTIPLY'
mult4.location = (0, 0)
# Mapping
map1 = new_grp.nodes.new('ShaderNodeMapping')
map1.scale = (1.0, 0.0, 0.0)
map1.location = (200, 0)
# Add
add1 = new_grp.nodes.new('ShaderNodeVectorMath')
add1.operation = 'ADD'
add1.location = (600, 0)
# Links
new_grp.links.new(grp_in.outputs[0], add1.inputs[1])
new_grp.links.new(grp_in.outputs[1], mult1.inputs[1])
new_grp.links.new(grp_in.outputs[2], mult3.inputs[1])
new_grp.links.new(grp_in.outputs[3], mult4.inputs[1])
new_grp.links.new(grp_in.outputs[3], mult1.inputs[0])
new_grp.links.new(grp_in.outputs[4], mult2.inputs[1])
new_grp.links.new(mult1.outputs[0], mult2.inputs[0])
new_grp.links.new(mult2.outputs[0], mod1.inputs[0])
new_grp.links.new(mod1.outputs[0], mult3.inputs[0])
new_grp.links.new(mult3.outputs[0], mult4.inputs[0])
new_grp.links.new(mult4.outputs[0], map1.inputs[0])
new_grp.links.new(map1.outputs[0], add1.inputs[0])
new_grp.links.new(add1.outputs[0], grp_out.inputs[0])
# 5 - Vertical Filmstrip Animation
def make_uvm5():
new_grp = bpy.data.node_groups.new('RWKUVMode5Node', 'ShaderNodeTree')
new_grp.inputs.new('NodeSocketVector', 'UV In')
new_grp.inputs.new('NodeSocketFloat', 'Scale')
new_grp.inputs.new('NodeSocketFloat', 'NumFrames')
new_grp.inputs.new('NodeSocketFloat', 'Step')
new_grp.inputs.new('NodeSocketFloat', 'Offset')
new_grp.outputs.new('NodeSocketVector', 'UV Out')
new_grp.use_fake_user = True
# Group inputs
grp_in = new_grp.nodes.new('NodeGroupInput')
grp_in.location = (-1000, 0)
# Group outputs
grp_out = new_grp.nodes.new('NodeGroupOutput')
grp_out.location = (800, 0)
# Multiply1
mult1 = new_grp.nodes.new('ShaderNodeMath')
mult1.operation = 'MULTIPLY'
mult1.location = (-800, 0)
# Multiply2
mult2 = new_grp.nodes.new('ShaderNodeMath')
mult2.operation = 'MULTIPLY'
mult2.location = (-600, 0)
# Modulo
mod1 = new_grp.nodes.new('ShaderNodeMath')
mod1.operation = 'MODULO'
mod1.inputs[1].default_value = 1.0
mod1.location = (-400, 0)
# Multiply3
mult3 = new_grp.nodes.new('ShaderNodeMath')
mult3.operation = 'MULTIPLY'
mult3.location = (-200, 0)
# Multiply4
mult4 = new_grp.nodes.new('ShaderNodeMath')
mult4.operation = 'MULTIPLY'
mult4.location = (0, 0)
# Mapping
map1 = new_grp.nodes.new('ShaderNodeMapping')
map1.scale = (0.0, 1.0, 0.0)
map1.location = (200, 0)
# Add
add1 = new_grp.nodes.new('ShaderNodeVectorMath')
add1.operation = 'ADD'
add1.location = (600, 0)
# Links
new_grp.links.new(grp_in.outputs[0], add1.inputs[1])
new_grp.links.new(grp_in.outputs[1], mult1.inputs[1])
new_grp.links.new(grp_in.outputs[2], mult3.inputs[1])
new_grp.links.new(grp_in.outputs[3], mult4.inputs[1])
new_grp.links.new(grp_in.outputs[3], mult1.inputs[0])
new_grp.links.new(grp_in.outputs[4], mult2.inputs[1])
new_grp.links.new(mult1.outputs[0], mult2.inputs[0])
new_grp.links.new(mult2.outputs[0], mod1.inputs[0])
new_grp.links.new(mod1.outputs[0], mult3.inputs[0])
new_grp.links.new(mult3.outputs[0], mult4.inputs[0])
new_grp.links.new(mult4.outputs[0], map1.inputs[0])
new_grp.links.new(map1.outputs[0], add1.inputs[0])
new_grp.links.new(add1.outputs[0], grp_out.inputs[0])
# 6 - Model Matrix
def make_uvm6():
new_grp = bpy.data.node_groups.new('RWKUVMode6Node', 'ShaderNodeTree')
new_grp.inputs.new('NodeSocketVector', 'UV In')
new_grp.outputs.new('NodeSocketVector', 'UV Out')
new_grp.use_fake_user = True
# Group inputs
grp_in = new_grp.nodes.new('NodeGroupInput')
grp_in.location = (-100, 0)
# Group outputs
grp_out = new_grp.nodes.new('NodeGroupOutput')
grp_out.location = (300, 0)
# Geometry input
geom_in = new_grp.nodes.new('ShaderNodeGeometry')
geom_in.location = (-300, 0)
# Adder1
adder1 = new_grp.nodes.new('ShaderNodeVectorMath')
adder1.operation = 'ADD'
adder1.location = (100, 0)
# Links
new_grp.links.new(grp_in.outputs[0], adder1.inputs[0])
new_grp.links.new(geom_in.outputs['Global'], adder1.inputs[1])
new_grp.links.new(adder1.outputs[0], grp_out.inputs[0])
# 7 - Mode Who Must Not Be Named
def make_uvm7():
new_grp = bpy.data.node_groups.new('RWKUVMode7Node', 'ShaderNodeTree')
new_grp.inputs.new('NodeSocketVector', 'UV In')
new_grp.inputs.new('NodeSocketFloat', 'ParamA')
new_grp.inputs.new('NodeSocketFloat', 'ParamB')
new_grp.outputs.new('NodeSocketVector', 'UV Out')
new_grp.use_fake_user = True
# Group inputs
grp_in = new_grp.nodes.new('NodeGroupInput')
grp_in.location = (-800, 0)
# Group outputs
grp_out = new_grp.nodes.new('NodeGroupOutput')
grp_out.location = (0, 0)
# Geometry input
geom_in = new_grp.nodes.new('ShaderNodeGeometry')
geom_in.location = (-1000, 0)
# View flip
view_flip = new_grp.nodes.new('ShaderNodeMapping')
view_flip.location = (-800, -150)
view_flip.vector_type = 'TEXTURE'
view_flip.scale = (-1.0, -1.0, 1.0)
# Separate
sep1 = new_grp.nodes.new('ShaderNodeSeparateRGB')
sep1.location = (-400, -200)
# Add1
add1 = new_grp.nodes.new('ShaderNodeMath')
add1.operation = 'ADD'
add1.location = (-200, -200)
# Multiply1
mult1 = new_grp.nodes.new('ShaderNodeMath')
mult1.operation = 'MULTIPLY'
mult1.inputs[1].default_value = 0.025
mult1.location = (0, -200)
# Multiply2
mult2 = new_grp.nodes.new('ShaderNodeMath')
mult2.operation = 'MULTIPLY'
mult2.location = (200, -200)
# Multiply3
mult3 = new_grp.nodes.new('ShaderNodeMath')
mult3.operation = 'MULTIPLY'
mult3.inputs[1].default_value = 0.05
mult3.location = (0, -400)
# Multiply4
mult4 = new_grp.nodes.new('ShaderNodeMath')
mult4.operation = 'MULTIPLY'
mult4.location = (200, -400)
# Combine1
comb1 = new_grp.nodes.new('ShaderNodeCombineRGB')
comb1.location = (400, -300)
# Combine2
comb2 = new_grp.nodes.new('ShaderNodeCombineRGB')
comb2.location = (-600, 0)
# Multiply5
mult5 = new_grp.nodes.new('ShaderNodeMixRGB')
mult5.blend_type = 'MULTIPLY'
mult5.inputs[0].default_value = 1.0
mult5.location = (-400, 0)
# Add2
add2 = new_grp.nodes.new('ShaderNodeVectorMath')
add2.operation = 'ADD'
add2.location = (-200, 0)
# Links
new_grp.links.new(grp_in.outputs[0], add2.inputs[0])
new_grp.links.new(geom_in.outputs['View'], view_flip.inputs[0])
new_grp.links.new(view_flip.outputs[0], sep1.inputs[0])
new_grp.links.new(grp_in.outputs[1], comb2.inputs[0])
new_grp.links.new(grp_in.outputs[1], comb2.inputs[1])
new_grp.links.new(grp_in.outputs[1], comb2.inputs[2])
new_grp.links.new(comb2.outputs[0], mult5.inputs[1])
new_grp.links.new(grp_in.outputs[2], mult2.inputs[1])
new_grp.links.new(grp_in.outputs[2], mult4.inputs[1])
new_grp.links.new(sep1.outputs[0], add1.inputs[0])
new_grp.links.new(sep1.outputs[1], add1.inputs[1])
new_grp.links.new(sep1.outputs[2], mult3.inputs[0])
new_grp.links.new(add1.outputs[0], mult1.inputs[0])
new_grp.links.new(mult1.outputs[0], mult2.inputs[0])
new_grp.links.new(mult2.outputs[0], comb1.inputs[0])
new_grp.links.new(mult3.outputs[0], mult4.inputs[0])
new_grp.links.new(mult4.outputs[0], comb1.inputs[1])
new_grp.links.new(comb1.outputs[0], mult5.inputs[2])
new_grp.links.new(mult5.outputs[0], add2.inputs[1])
new_grp.links.new(add2.outputs[0], grp_out.inputs[0])
UV_MODIFIER_GROUPS = [
make_uvm0,
make_uvm1,
make_uvm2,
make_uvm3,
make_uvm4,
make_uvm5,
make_uvm6,
make_uvm7
]
def make_master_shader_library():
for uvm in UV_MODIFIER_GROUPS:
uvm()

View File

@ -20,9 +20,16 @@ add_subdirectory(DNAMP1)
add_subdirectory(DNAMP2)
add_subdirectory(DNAMP3)
# Embed master shader script
bintoc(RetroMasterShader.c Blender/RetroMasterShader.py RETRO_MASTER_SHADER)
# Each game's DataSpec implementation
add_library(RetroDataSpec
SpecBase.cpp
SpecMP1.cpp
SpecMP2.cpp
SpecMP3.cpp)
SpecMP3.cpp
Blender/BlenderSupport.hpp
Blender/BlenderSupport.cpp
Blender/RetroMasterShader.py
RetroMasterShader.c)

View File

@ -0,0 +1,73 @@
#ifndef _DNACOMMON_ANCS_HPP_
#define _DNACOMMON_ANCS_HPP_
#include "DNACommon.hpp"
#include "BlenderConnection.hpp"
#include "CMDL.hpp"
namespace Retro
{
namespace DNAANCS
{
template <typename IDTYPE>
struct CharacterResInfo
{
std::string name;
IDTYPE cmdl;
IDTYPE cskr;
IDTYPE cinf;
};
template <class PAKRouter, class ANCSDNA, class MaterialSet, atUint32 CMDLVersion>
bool ReadANCSToBlender(HECL::BlenderConnection& conn,
const ANCSDNA& ancs,
const HECL::ProjectPath& outPath,
PAKRouter& pakRouter,
const typename PAKRouter::EntryType& entry,
const HECL::ProjectPath& masterShader,
bool force=false)
{
/* Extract characters first */
std::vector<CharacterResInfo<typename PAKRouter::IDType>> chResInfo;
ancs.getCharacterResInfo(chResInfo);
for (const auto& info : chResInfo)
{
const NOD::DiscBase::IPartition::Node* node;
const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info.cmdl, &node);
HECL::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
if (force || cmdlPath.getPathType() == HECL::ProjectPath::PT_NONE)
{
if (!conn.createBlend(cmdlPath.getAbsolutePath()))
return false;
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, CMDLVersion>
(conn, rs, pakRouter, entry, masterShader);
const typename PAKRouter::EntryType* cskrE = pakRouter.lookupEntry(info.cskr);
const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(info.cinf);
conn.saveBlend();
}
}
/* Establish ANCS blend */
if (!conn.createBlend(outPath.getAbsolutePath() + ".blend"))
return false;
HECL::BlenderConnection::PyOutStream os = conn.beginPythonOut(true);
/* Get animation primitives */
std::unordered_set<typename PAKRouter::IDType> animResInfo;
ancs.getAnimationResInfo(animResInfo);
for (const auto& id : animResInfo)
{
const typename PAKRouter::EntryType* animE = pakRouter.lookupEntry(id);
}
return true;
}
}
}
#endif // _DNACOMMON_ANCS_HPP_

396
DataSpec/DNACommon/ANIM.cpp Normal file
View File

@ -0,0 +1,396 @@
#include "ANIM.hpp"
namespace Retro
{
namespace DNAANIM
{
size_t ComputeBitstreamSize(size_t keyFrameCount, const std::vector<Channel>& channels)
{
size_t bitsPerKeyFrame = 0;
for (const Channel& chan : channels)
{
switch (chan.type)
{
case Channel::ROTATION:
case Channel::TRANSLATION:
{
bitsPerKeyFrame += chan.q[0];
bitsPerKeyFrame += chan.q[1];
bitsPerKeyFrame += chan.q[2];
break;
}
case Channel::SCALE:
{
bitsPerKeyFrame += chan.q[0];
break;
}
default: break;
}
}
return (bitsPerKeyFrame * keyFrameCount + 31) / 32 * 4;
}
static inline QuantizedRot QuantizeRotation(const Value& quat, atUint32 div)
{
float q = M_PI / 2.0 / div;
return
{
{
atInt16(asinf(quat.v4.vec[0]) / q),
atInt16(asinf(quat.v4.vec[1]) / q),
atInt16(asinf(quat.v4.vec[2]) / q),
},
(quat.v4.vec[3] < 0) ? true : false
};
}
static inline Value DequantizeRotation(const QuantizedRot& v, atUint32 div)
{
float q = M_PI / 2.0 / div;
Value retval =
{
sinf(v.v[0] * q),
sinf(v.v[1] * q),
sinf(v.v[2] * q),
};
retval.v4.vec[3] = sqrtf(MAX((1.0 -
(retval.v4.vec[0]*retval.v4.vec[0] +
retval.v4.vec[1]*retval.v4.vec[1] +
retval.v4.vec[2]*retval.v4.vec[2])), 0.0));
retval.v4.vec[3] = v.w ? -retval.v4.vec[3] : retval.v4.vec[3];
return retval;
}
atInt16 BitstreamReader::dequantize(const atUint8* data, atUint8 q)
{
atUint32 byteCur = (m_bitCur / 32) * 4;
atUint32 bitRem = m_bitCur % 32;
/* Fill 32 bit buffer with region containing bits */
/* Make them least significant */
atUint32 tempBuf = HECL::SBig(*(atUint32*)(data + byteCur)) >> bitRem;
/* If this shift underflows the value, buffer the next 32 bits */
/* And tack onto shifted buffer */
if ((bitRem + q) > 32)
{
atUint32 tempBuf2 = HECL::SBig(*(atUint32*)(data + byteCur + 4));
tempBuf |= (tempBuf2 << (32 - bitRem));
}
/* Pick out sign */
atUint32 sign = (tempBuf >> (q - 1)) & 0x1;
if (sign)
tempBuf = ~tempBuf;
/* mask it (excluding sign bit) */
atUint32 mask = (1 << (q - 1)) - 1;
tempBuf &= mask;
/* Return delta value */
m_bitCur += q;
return atInt32(sign ? (tempBuf + 1) * -1 : tempBuf);
}
std::vector<std::vector<Value>>
BitstreamReader::read(const atUint8* data,
size_t keyFrameCount,
const std::vector<Channel>& channels,
atUint32 rotDiv,
float transMult)
{
m_bitCur = 0;
std::vector<std::vector<Value>> chanKeys;
std::vector<QuantizedValue> chanAccum;
chanKeys.reserve(channels.size());
chanAccum.reserve(channels.size());
for (const Channel& chan : channels)
{
chanAccum.push_back({chan.i[0], chan.i[1], chan.i[2]});
QuantizedValue& accum = chanAccum.back();
chanKeys.emplace_back();
std::vector<Value>& keys = chanKeys.back();
keys.reserve(keyFrameCount);
switch (chan.type)
{
case Channel::ROTATION:
{
QuantizedRot qr = {{chan.i[0], chan.i[1], chan.i[2]}, false};
keys.emplace_back(DequantizeRotation(qr, rotDiv));
break;
}
case Channel::TRANSLATION:
{
keys.push_back({chan.i[0] * transMult, chan.i[1] * transMult, chan.i[2] * transMult});
break;
}
case Channel::SCALE:
{
keys.push_back({chan.i[0] * transMult});
break;
}
default: break;
}
}
for (size_t f=0 ; f<keyFrameCount ; ++f)
{
auto kit = chanKeys.begin();
auto ait = chanAccum.begin();
for (const Channel& chan : channels)
{
QuantizedValue& p = *ait;
switch (chan.type)
{
case Channel::ROTATION:
{
bool wBit = dequantize(data, 1);
p[0] += dequantize(data, chan.q[0]);
p[1] += dequantize(data, chan.q[1]);
p[2] += dequantize(data, chan.q[2]);
QuantizedRot qr = {{p[0], p[1], p[2]}, wBit};
kit->emplace_back(DequantizeRotation(qr, rotDiv));
break;
}
case Channel::TRANSLATION:
{
p[0] += dequantize(data, chan.q[0]);
p[1] += dequantize(data, chan.q[1]);
p[2] += dequantize(data, chan.q[2]);
kit->push_back({p[0] * transMult, p[1] * transMult, p[2] * transMult});
break;
}
case Channel::SCALE:
{
p[0] += dequantize(data, chan.q[0]);
kit->push_back({p[0] * transMult});
break;
}
default: break;
}
++kit;
++ait;
}
}
return chanKeys;
}
void BitstreamWriter::quantize(atUint8* data, atUint8 q, atInt16 val)
{
atUint32 byteCur = (m_bitCur / 32) * 4;
atUint32 bitRem = m_bitCur % 32;
atUint32 masked = val & ((1 << q) - 1);
/* Fill 32 bit buffer with region containing bits */
/* Make them least significant */
*(atUint32*)(data + byteCur) =
HECL::SBig(HECL::SBig(*(atUint32*)(data + byteCur)) | (masked << bitRem));
/* If this shift underflows the value, buffer the next 32 bits */
/* And tack onto shifted buffer */
if ((bitRem + q) > 32)
{
*(atUint32*)(data + byteCur + 4) =
HECL::SBig(HECL::SBig(*(atUint32*)(data + byteCur + 4)) | (masked >> (32 - bitRem)));
}
m_bitCur += q;
}
std::unique_ptr<atUint8[]>
BitstreamWriter::write(const std::vector<std::vector<Value>>& chanKeys,
size_t keyFrameCount, std::vector<Channel>& channels,
atUint32& rotDivOut,
float& transMultOut,
size_t& sizeOut)
{
m_bitCur = 0;
rotDivOut = 32767; /* Normalized range of values */
/* Pre-pass to calculate translation multiplier */
float maxTransDiff = 0.0;
auto kit = chanKeys.begin();
for (Channel& chan : channels)
{
switch (chan.type)
{
case Channel::TRANSLATION:
{
const Value* last = &(*kit)[0];
for (auto it=kit->begin() + 1;
it != kit->end();
++it)
{
const Value* current = &*it;
maxTransDiff = MAX(maxTransDiff, current->v3.vec[0] - last->v3.vec[0]);
maxTransDiff = MAX(maxTransDiff, current->v3.vec[1] - last->v3.vec[1]);
maxTransDiff = MAX(maxTransDiff, current->v3.vec[2] - last->v3.vec[2]);
last = current;
}
break;
}
default: break;
}
++kit;
}
transMultOut = maxTransDiff / 32767;
/* Output channel inits */
kit = chanKeys.begin();
for (Channel& chan : channels)
{
chan.q[0] = 1;
chan.q[1] = 1;
chan.q[2] = 1;
switch (chan.type)
{
case Channel::ROTATION:
{
QuantizedRot qr = QuantizeRotation((*kit)[0], rotDivOut);
chan.i = qr.v;
break;
}
case Channel::TRANSLATION:
{
chan.i = {atInt16((*kit)[0].v3.vec[0] / transMultOut),
atInt16((*kit)[0].v3.vec[1] / transMultOut),
atInt16((*kit)[0].v3.vec[2] / transMultOut)};
break;
}
case Channel::SCALE:
{
chan.i = {atInt16((*kit)[0].scale / transMultOut)};
break;
}
default: break;
}
++kit;
}
/* Pre-pass to analyze quantization factors for channels */
kit = chanKeys.begin();
for (Channel& chan : channels)
{
switch (chan.type)
{
case Channel::ROTATION:
{
QuantizedRot qrLast = QuantizeRotation((*kit)[0], rotDivOut);
for (auto it=kit->begin() + 1;
it != kit->end();
++it)
{
QuantizedRot qrCur = QuantizeRotation(*it, rotDivOut);
chan.q[0] = MAX(chan.q[0], ceilf(log2f(qrCur.v[0] - qrLast.v[0])));
chan.q[1] = MAX(chan.q[1], ceilf(log2f(qrCur.v[1] - qrLast.v[1])));
chan.q[2] = MAX(chan.q[2], ceilf(log2f(qrCur.v[2] - qrLast.v[2])));
qrLast = qrCur;
}
break;
}
case Channel::TRANSLATION:
{
QuantizedValue last = {atInt16((*kit)[0].v3.vec[0] / transMultOut),
atInt16((*kit)[0].v3.vec[1] / transMultOut),
atInt16((*kit)[0].v3.vec[2] / transMultOut)};
for (auto it=kit->begin() + 1;
it != kit->end();
++it)
{
QuantizedValue cur = {atInt16(it->v3.vec[0] / transMultOut),
atInt16(it->v3.vec[1] / transMultOut),
atInt16(it->v3.vec[2] / transMultOut)};
chan.q[0] = MAX(chan.q[0], ceilf(log2f(cur[0] - last[0])));
chan.q[1] = MAX(chan.q[1], ceilf(log2f(cur[1] - last[1])));
chan.q[2] = MAX(chan.q[2], ceilf(log2f(cur[2] - last[2])));
last = cur;
}
break;
}
case Channel::SCALE:
{
atUint16 last = (*kit)[0].scale / transMultOut;
for (auto it=kit->begin() + 1;
it != kit->end();
++it)
{
atUint16 cur = it->scale / transMultOut;
chan.q[0] = MAX(chan.q[0], ceilf(log2f(cur - last)));
last = cur;
}
break;
}
default: break;
}
++kit;
}
/* Generate Bitstream */
sizeOut = ComputeBitstreamSize(keyFrameCount, channels);
atUint8* newData = new atUint8[sizeOut];
for (size_t f=0 ; f<keyFrameCount ; ++f)
{
kit = chanKeys.begin();
for (const Channel& chan : channels)
{
switch (chan.type)
{
case Channel::ROTATION:
{
QuantizedRot qrLast = QuantizeRotation((*kit)[0], rotDivOut);
for (auto it=kit->begin() + 1;
it != kit->end();
++it)
{
QuantizedRot qrCur = QuantizeRotation(*it, rotDivOut);
quantize(newData, 1, qrCur.w);
quantize(newData, chan.q[0], qrCur.v[0] - qrLast.v[0]);
quantize(newData, chan.q[1], qrCur.v[1] - qrLast.v[0]);
quantize(newData, chan.q[2], qrCur.v[2] - qrLast.v[0]);
qrLast = qrCur;
}
break;
}
case Channel::TRANSLATION:
{
QuantizedValue last = {atInt16((*kit)[0].v3.vec[0] / transMultOut),
atInt16((*kit)[0].v3.vec[1] / transMultOut),
atInt16((*kit)[0].v3.vec[2] / transMultOut)};
for (auto it=kit->begin() + 1;
it != kit->end();
++it)
{
QuantizedValue cur = {atInt16(it->v3.vec[0] / transMultOut),
atInt16(it->v3.vec[1] / transMultOut),
atInt16(it->v3.vec[2] / transMultOut)};
quantize(newData, chan.q[0], cur[0] - last[0]);
quantize(newData, chan.q[1], cur[1] - last[0]);
quantize(newData, chan.q[2], cur[2] - last[0]);
last = cur;
}
break;
}
case Channel::SCALE:
{
atUint16 last = (*kit)[0].scale / transMultOut;
for (auto it=kit->begin() + 1;
it != kit->end();
++it)
{
atUint16 cur = it->scale / transMultOut;
quantize(newData, chan.q[0], cur - last);
last = cur;
}
break;
}
default: break;
}
++kit;
}
}
return std::unique_ptr<atUint8[]>(newData);
}
}
}

View File

@ -0,0 +1,84 @@
#ifndef _DNACOMMON_ANIMBITSTREAM_HPP_
#define _DNACOMMON_ANIMBITSTREAM_HPP_
#include <math.h>
#include "DNACommon.hpp"
namespace Retro
{
namespace DNAANIM
{
union Value
{
atVec3f v3;
atVec4f v4;
float scale;
Value(atVec3f v) : v3(v) {}
Value(atVec4f v) : v4(v) {}
Value(float v) : scale(v) {}
Value(float x, float y, float z)
{
v3.vec[0] = x;
v3.vec[1] = y;
v3.vec[2] = z;
v4.vec[3] = 0.0;
}
};
struct QuantizedValue
{
atInt16 v[3];
atInt16& operator[] (size_t idx)
{return v[idx];}
const atInt16& operator[] (size_t idx) const
{return v[idx];}
};
struct QuantizedRot
{
QuantizedValue v;
bool w;
};
struct Channel
{
enum Type
{
ROTATION,
TRANSLATION,
SCALE
} type;
QuantizedValue i = {};
atUint8 q[3] = {};
};
size_t ComputeBitstreamSize(size_t keyFrameCount, const std::vector<Channel>& channels);
class BitstreamReader
{
size_t m_bitCur;
atInt16 dequantize(const atUint8* data, atUint8 q);
public:
std::vector<std::vector<Value>>
read(const atUint8* data,
size_t keyFrameCount,
const std::vector<Channel>& channels,
atUint32 rotDiv,
float transMult);
};
class BitstreamWriter
{
size_t m_bitCur;
void quantize(atUint8* data, atUint8 q, atInt16 val);
public:
std::unique_ptr<atUint8[]>
write(const std::vector<std::vector<Value>>& chanKeys,
size_t keyFrameCount, std::vector<Channel>& channels,
atUint32& rotDivOut,
float& transMultOut,
size_t& sizeOut);
};
}
}
#endif // _DNACOMMON_ANIMBITSTREAM_HPP_

826
DataSpec/DNACommon/CMDL.hpp Normal file
View File

@ -0,0 +1,826 @@
#ifndef _DNACOMMON_CMDL_HPP_
#define _DNACOMMON_CMDL_HPP_
#include "DNACommon.hpp"
#include "BlenderConnection.hpp"
#include "GX.hpp"
namespace Retro
{
namespace DNACMDL
{
struct Header : BigDNA
{
DECL_DNA
Value<atUint32> magic;
Value<atUint32> version;
struct Flags : BigDNA
{
DECL_DNA
Value<atUint32> flags;
inline bool shortNormals() const {return (flags & 0x2) != 0;}
inline void setShortNormals(bool val) {flags &= ~0x2; flags |= val << 1;}
inline bool shortUVs() const {return (flags & 0x4) != 0;}
inline void setShortUVs(bool val) {flags &= ~0x4; flags |= val << 2;}
} flags;
Value<atVec3f> aabbMin;
Value<atVec3f> aabbMax;
Value<atUint32> secCount;
Value<atUint32> matSetCount;
Vector<atUint32, DNA_COUNT(secCount)> secSizes;
Align<32> align;
};
struct SurfaceHeader : BigDNA
{
DECL_DNA
Value<atVec3f> centroid;
Value<atUint32> matIdx;
Value<atInt16> qDiv;
Value<atUint16> dlSize;
Seek<8, Athena::Current> seek;
Value<atUint32> aabbSz;
Value<atVec3f> reflectionNormal;
Seek<DNA_COUNT(aabbSz), Athena::Current> seek2;
Align<32> align;
};
struct VertexAttributes
{
GX::AttrType pos = GX::NONE;
GX::AttrType norm = GX::NONE;
GX::AttrType color0 = GX::NONE;
GX::AttrType color1 = GX::NONE;
unsigned uvCount = 0;
GX::AttrType uvs[7] = {GX::NONE};
GX::AttrType pnMtxIdx = GX::NONE;
unsigned texMtxIdxCount = 0;
GX::AttrType texMtxIdx[7] = {GX::NONE};
};
template <class MaterialSet>
void GetVertexAttributes(const MaterialSet& matSet,
std::vector<VertexAttributes>& attributesOut)
{
attributesOut.clear();
attributesOut.reserve(matSet.materials.size());
for (const typename MaterialSet::Material& mat : matSet.materials)
{
const typename MaterialSet::Material::VAFlags& vaFlags = mat.getVAFlags();
attributesOut.emplace_back();
VertexAttributes& va = attributesOut.back();
va.pos = vaFlags.position();
va.norm = vaFlags.normal();
va.color0 = vaFlags.color0();
va.color1 = vaFlags.color1();
if ((va.uvs[0] = vaFlags.tex0()))
++va.uvCount;
if ((va.uvs[1] = vaFlags.tex1()))
++va.uvCount;
if ((va.uvs[2] = vaFlags.tex2()))
++va.uvCount;
if ((va.uvs[3] = vaFlags.tex3()))
++va.uvCount;
if ((va.uvs[4] = vaFlags.tex4()))
++va.uvCount;
if ((va.uvs[5] = vaFlags.tex5()))
++va.uvCount;
if ((va.uvs[6] = vaFlags.tex6()))
++va.uvCount;
va.pnMtxIdx = vaFlags.pnMatIdx();
if ((va.texMtxIdx[0] = vaFlags.tex0MatIdx()))
++va.texMtxIdxCount;
if ((va.texMtxIdx[1] = vaFlags.tex1MatIdx()))
++va.texMtxIdxCount;
if ((va.texMtxIdx[2] = vaFlags.tex2MatIdx()))
++va.texMtxIdxCount;
if ((va.texMtxIdx[3] = vaFlags.tex3MatIdx()))
++va.texMtxIdxCount;
if ((va.texMtxIdx[4] = vaFlags.tex4MatIdx()))
++va.texMtxIdxCount;
if ((va.texMtxIdx[5] = vaFlags.tex5MatIdx()))
++va.texMtxIdxCount;
if ((va.texMtxIdx[6] = vaFlags.tex6MatIdx()))
++va.texMtxIdxCount;
}
}
template <class PAKRouter, class MaterialSet>
void ReadMaterialSetToBlender_1_2(HECL::BlenderConnection::PyOutStream& os,
const MaterialSet& matSet,
const PAKRouter& pakRouter,
const typename PAKRouter::EntryType& entry,
unsigned setIdx)
{
/* Texmaps */
os << "texmap_list = []\n";
for (const UniqueID32& tex : matSet.head.textureIDs)
{
std::string texName = pakRouter.getBestEntryName(tex);
HECL::SystemString resPath = pakRouter.getResourceRelativePath(entry, tex);
HECL::SystemUTF8View resPathView(resPath);
os.format("if '%s' in bpy.data.textures:\n"
" image = bpy.data.images['%s']\n"
" texture = bpy.data.textures[image.name]\n"
"else:\n"
" image = bpy.data.images.load('''//%s''')\n"
" image.name = '%s'\n"
" texture = bpy.data.textures.new(image.name, 'IMAGE')\n"
" texture.image = image\n"
"texmap_list.append(texture)\n"
"\n", texName.c_str(), texName.c_str(),
resPathView.str().c_str(), texName.c_str());
}
unsigned m=0;
for (const typename MaterialSet::Material& mat : matSet.materials)
{
MaterialSet::ConstructMaterial(os, mat, setIdx, m++);
os << "materials.append(new_material)\n";
}
}
template <class PAKRouter, class MaterialSet>
void ReadMaterialSetToBlender_3(HECL::BlenderConnection::PyOutStream& os,
const MaterialSet& matSet,
const PAKRouter& pakRouter,
const typename PAKRouter::EntryType& entry,
unsigned setIdx)
{
unsigned m=0;
for (const typename MaterialSet::Material& mat : matSet.materials)
{
MaterialSet::ConstructMaterial(os, mat, setIdx, m++);
os << "materials.append(new_material)\n";
}
}
class DLReader
{
const VertexAttributes& m_va;
std::unique_ptr<atUint8[]> m_dl;
size_t m_dlSize;
atUint8* m_cur;
atUint16 readVal(GX::AttrType type)
{
atUint16 retval = 0;
switch (type)
{
case GX::DIRECT:
case GX::INDEX8:
retval = *m_cur;
++m_cur;
break;
case GX::INDEX16:
retval = HECL::SBig(*(atUint16*)m_cur);
m_cur += 2;
break;
default: break;
}
return retval;
}
public:
DLReader(const VertexAttributes& va, std::unique_ptr<atUint8[]>&& dl, size_t dlSize)
: m_va(va), m_dl(std::move(dl)), m_dlSize(dlSize)
{
m_cur = m_dl.get();
}
operator bool()
{
return *m_cur && ((m_cur - m_dl.get()) < m_dlSize);
}
GX::Primitive readPrimitive()
{
return GX::Primitive(*m_cur++ & 0xf8);
}
atUint16 readVertCount()
{
return readVal(GX::INDEX16);
}
struct DLPrimVert
{
atUint16 pos = 0;
atUint16 norm = 0;
atUint16 color[2] = {0};
atUint16 uvs[7] = {0};
atUint8 pnMtxIdx = 0;
atUint8 texMtxIdx[7] = {0};
};
DLPrimVert readVert(bool peek=false)
{
atUint8* bakCur = m_cur;
DLPrimVert retval;
retval.pnMtxIdx = readVal(m_va.pnMtxIdx);
retval.texMtxIdx[0] = readVal(m_va.texMtxIdx[0]);
retval.texMtxIdx[1] = readVal(m_va.texMtxIdx[1]);
retval.texMtxIdx[2] = readVal(m_va.texMtxIdx[2]);
retval.texMtxIdx[3] = readVal(m_va.texMtxIdx[3]);
retval.texMtxIdx[4] = readVal(m_va.texMtxIdx[4]);
retval.texMtxIdx[5] = readVal(m_va.texMtxIdx[5]);
retval.texMtxIdx[6] = readVal(m_va.texMtxIdx[6]);
retval.pos = readVal(m_va.pos);
retval.norm = readVal(m_va.norm);
retval.color[0] = readVal(m_va.color0);
retval.color[1] = readVal(m_va.color1);
retval.uvs[0] = readVal(m_va.uvs[0]);
retval.uvs[1] = readVal(m_va.uvs[1]);
retval.uvs[2] = readVal(m_va.uvs[2]);
retval.uvs[3] = readVal(m_va.uvs[3]);
retval.uvs[4] = readVal(m_va.uvs[4]);
retval.uvs[5] = readVal(m_va.uvs[5]);
retval.uvs[6] = readVal(m_va.uvs[6]);
if (peek)
m_cur = bakCur;
return retval;
}
void preReadMaxIdxs(DLPrimVert& out)
{
atUint8* bakCur = m_cur;
while (*this)
{
readPrimitive();
atUint16 vc = readVertCount();
for (atUint16 v=0 ; v<vc ; ++v)
{
atUint16 val;
val = readVal(m_va.pnMtxIdx);
out.pnMtxIdx = MAX(out.pnMtxIdx, val);
val = readVal(m_va.texMtxIdx[0]);
out.texMtxIdx[0] = MAX(out.texMtxIdx[0], val);
val = readVal(m_va.texMtxIdx[1]);
out.texMtxIdx[1] = MAX(out.texMtxIdx[1], val);
val = readVal(m_va.texMtxIdx[2]);
out.texMtxIdx[2] = MAX(out.texMtxIdx[2], val);
val = readVal(m_va.texMtxIdx[3]);
out.texMtxIdx[3] = MAX(out.texMtxIdx[3], val);
val = readVal(m_va.texMtxIdx[4]);
out.texMtxIdx[4] = MAX(out.texMtxIdx[4], val);
val = readVal(m_va.texMtxIdx[5]);
out.texMtxIdx[5] = MAX(out.texMtxIdx[5], val);
val = readVal(m_va.texMtxIdx[6]);
out.texMtxIdx[6] = MAX(out.texMtxIdx[6], val);
val = readVal(m_va.pos);
out.pos = MAX(out.pos, val);
val = readVal(m_va.norm);
out.norm = MAX(out.norm, val);
val = readVal(m_va.color0);
out.color[0] = MAX(out.color[0], val);
val = readVal(m_va.color1);
out.color[1] = MAX(out.color[1], val);
val = readVal(m_va.uvs[0]);
out.uvs[0] = MAX(out.uvs[0], val);
val = readVal(m_va.uvs[1]);
out.uvs[1] = MAX(out.uvs[1], val);
val = readVal(m_va.uvs[2]);
out.uvs[2] = MAX(out.uvs[2], val);
val = readVal(m_va.uvs[3]);
out.uvs[3] = MAX(out.uvs[3], val);
val = readVal(m_va.uvs[4]);
out.uvs[4] = MAX(out.uvs[4], val);
val = readVal(m_va.uvs[5]);
out.uvs[5] = MAX(out.uvs[5], val);
val = readVal(m_va.uvs[6]);
out.uvs[6] = MAX(out.uvs[6], val);
}
}
m_cur = bakCur;
}
};
template <class PAKRouter, class MaterialSet, atUint32 Version>
bool ReadCMDLToBlender(HECL::BlenderConnection& conn,
Athena::io::IStreamReader& reader,
PAKRouter& pakRouter,
const typename PAKRouter::EntryType& entry,
const HECL::ProjectPath& masterShader)
{
reader.setEndian(Athena::BigEndian);
Header head;
head.read(reader);
if (head.magic != 0xDEADBABE)
{
LogDNACommon.report(LogVisor::Error, "invalid CMDL magic");
return false;
}
if (head.version != Version)
{
LogDNACommon.report(LogVisor::Error, "invalid CMDL version");
return false;
}
/* Open Py Stream */
HECL::BlenderConnection::PyOutStream os = conn.beginPythonOut(true);
os.format("import bpy\n"
"import bmesh\n"
"\n"
"bpy.context.scene.name = '%s'\n"
"bpy.context.scene.hecl_type = 'MESH'\n"
"bpy.context.scene.hecl_mesh_obj = bpy.context.scene.name\n"
"\n"
"# Using 'Blender Game'\n"
"bpy.context.scene.render.engine = 'BLENDER_GAME'\n"
"\n"
"# Clear Scene\n"
"for ob in bpy.data.objects:\n"
" if ob.type != 'LAMP':\n"
" bpy.context.scene.objects.unlink(ob)\n"
" bpy.data.objects.remove(ob)\n"
"\n"
"# Property to convey original vert indices in overdraw meshes\n"
"class CMDLOriginalIndex(bpy.types.PropertyGroup):\n"
" index = bpy.props.IntProperty(name='Original Vertex Index')\n"
"bpy.utils.register_class(CMDLOriginalIndex)\n"
"bpy.types.Mesh.cmdl_orig_verts = bpy.props.CollectionProperty(type=CMDLOriginalIndex)\n"
"\n"
"def loop_from_facevert(face, vert_idx):\n"
" for loop in face.loops:\n"
" if loop.vert.index == vert_idx:\n"
" return loop\n"
"\n"
"def add_triangle(bm, vert_seq, vert_indices, norm_seq, norm_indices, mat_nr, od_list):\n"
" if len(set(vert_indices)) != 3:\n"
" return None, None\n"
"\n"
" ret_mesh = bm\n"
" vert_seq.ensure_lookup_table()\n"
" verts = [vert_seq[i] for i in vert_indices]\n"
" norms = [norm_seq[i] for i in norm_indices]\n"
"\n"
" # Make the face\n"
" face = bm.faces.get(verts)\n"
"\n"
" if face is not None and face.material_index != mat_nr: # Same poly, new material\n"
" # Overdraw detected; track copy\n"
" od_entry = None\n"
" for entry in od_list:\n"
" if entry['material'] == mat_nr:\n"
" od_entry = entry\n"
" if od_entry is None:\n"
" bm_cpy = bm.copy()\n"
" od_entry = {'material':mat_nr, 'bm':bm_cpy}\n"
" bmesh.ops.delete(od_entry['bm'], geom=od_entry['bm'].faces, context=3)\n"
" od_list.append(od_entry)\n"
" od_entry['bm'].verts.ensure_lookup_table()\n"
" verts = [od_entry['bm'].verts[i] for i in vert_indices]\n"
" face = od_entry['bm'].faces.get(verts)\n"
" if face is None:\n"
" face = od_entry['bm'].faces.new(verts)\n"
" else: # Probably a double-sided surface\n"
" face = face.copy()\n"
" face.normal_flip()\n"
" ret_mesh = od_entry['bm']\n"
"\n"
" elif face is not None: # Same material, probably double-sided\n"
" face = face.copy()\n"
" face.normal_flip()\n"
"\n"
" else: \n"
" face = bm.faces.new(verts)\n"
"\n"
" # Apply normals\n"
" for i in range(3):\n"
" verts[i].normal = norms[i]\n"
"\n"
" for i in range(3):\n"
" face.verts[i].index = vert_indices[i]\n"
" face.material_index = mat_nr\n"
" face.smooth = True\n"
"\n"
" return face, ret_mesh\n"
"\n"
"# Begin bmesh\n"
"bm = bmesh.new()\n"
"\n", pakRouter.getBestEntryName(entry).c_str());
/* Link master shader library */
os.format("# Master shader library\n"
"with bpy.data.libraries.load('%s', link=True, relative=True) as (data_from, data_to):\n"
" data_to.node_groups = data_from.node_groups\n"
"\n", masterShader.getAbsolutePathUTF8().c_str());
MaterialSet::RegisterMaterialProps(os);
os << "# Materials\n"
"materials = []\n"
"\n"
"# Overdraw-tracking\n"
"od_list = []\n"
"\n";
/* Pre-read pass to determine maximum used vert indices */
bool visitedDLOffsets = false;
std::vector<VertexAttributes> vertAttribs;
atUint64 afterHeaderPos = reader.position();
DLReader::DLPrimVert maxIdxs;
for (size_t s=0 ; s<head.secCount ; ++s)
{
atUint64 secStart = reader.position();
if (s < head.matSetCount)
{
if (!s)
{
MaterialSet matSet;
matSet.read(reader);
GetVertexAttributes(matSet, vertAttribs);
}
}
else
{
switch (s-head.matSetCount)
{
case 0:
{
/* Positions */
break;
}
case 1:
{
/* Normals */
break;
}
case 2:
{
/* Colors */
break;
}
case 3:
{
/* Float UVs */
break;
}
case 4:
{
/* Short UVs */
if (head.flags.shortUVs())
break;
/* DL Offsets (here or next section) */
visitedDLOffsets = true;
break;
}
default:
{
if (!visitedDLOffsets)
{
visitedDLOffsets = true;
break;
}
/* GX Display List (surface) */
SurfaceHeader sHead;
sHead.read(reader);
/* Do max index pre-read */
atUint32 realDlSize = head.secSizes[s] - (reader.position() - secStart);
DLReader dl(vertAttribs[sHead.matIdx], reader.readUBytes(realDlSize), realDlSize);
dl.preReadMaxIdxs(maxIdxs);
}
}
}
if (s < head.secCount - 1)
reader.seek(secStart + head.secSizes[s], Athena::Begin);
}
reader.seek(afterHeaderPos, Athena::Begin);
visitedDLOffsets = false;
unsigned createdUVLayers = 0;
unsigned surfIdx = 0;
for (size_t s=0 ; s<head.secCount ; ++s)
{
atUint64 secStart = reader.position();
if (s < head.matSetCount)
{
MaterialSet matSet;
matSet.read(reader);
matSet.readToBlender(os, pakRouter, entry, s);
if (!s)
GetVertexAttributes(matSet, vertAttribs);
}
else
{
switch (s-head.matSetCount)
{
case 0:
{
/* Positions */
for (size_t i=0 ; i<=maxIdxs.pos ; ++i)
{
atVec3f pos = reader.readVec3f();
os.format("bm.verts.new((%f,%f,%f))\n",
pos.vec[0], pos.vec[1], pos.vec[2]);
}
break;
}
case 1:
{
/* Normals */
os << "norm_list = []\n";
if (head.flags.shortNormals())
{
size_t normCount = head.secSizes[s] / 6;
for (size_t i=0 ; i<normCount ; ++i)
{
os.format("norm_list.append((%f,%f,%f))\n",
reader.readInt16(), reader.readInt16(), reader.readInt16());
}
}
else
{
size_t normCount = head.secSizes[s] / 12;
for (size_t i=0 ; i<normCount ; ++i)
{
atVec3f norm = reader.readVec3f();
os.format("norm_list.append((%f,%f,%f))\n",
norm.vec[0], norm.vec[1], norm.vec[2]);
}
}
break;
}
case 2:
{
/* Colors */
break;
}
case 3:
{
/* Float UVs */
os << "uv_list = []\n";
size_t uvCount = head.secSizes[s] / 8;
for (size_t i=0 ; i<uvCount ; ++i)
{
atVec2f uv = reader.readVec2f();
os.format("uv_list.append((%f,%f))\n",
uv.vec[0], uv.vec[1]);
}
break;
}
case 4:
{
/* Short UVs */
os << "suv_list = []\n";
if (head.flags.shortUVs())
{
size_t uvCount = head.secSizes[s] / 4;
for (size_t i=0 ; i<uvCount ; ++i)
{
os.format("suv_list.append((%f,%f))\n",
reader.readInt16(), reader.readInt16());
}
break;
}
/* DL Offsets (here or next section) */
visitedDLOffsets = true;
break;
}
default:
{
if (!visitedDLOffsets)
{
visitedDLOffsets = true;
break;
}
/* GX Display List (surface) */
SurfaceHeader sHead;
sHead.read(reader);
unsigned matUVCount = vertAttribs[sHead.matIdx].uvCount;
os.format("materials[%u].pass_index = %u\n", sHead.matIdx, surfIdx++);
if (matUVCount > createdUVLayers)
{
for (int l=createdUVLayers ; l<matUVCount ; ++l)
os.format("bm.loops.layers.uv.new('UV_%u')\n", l);
createdUVLayers = matUVCount;
}
atUint32 realDlSize = head.secSizes[s] - (reader.position() - secStart);
DLReader dl(vertAttribs[sHead.matIdx], reader.readUBytes(realDlSize), realDlSize);
while (dl)
{
GX::Primitive ptype = dl.readPrimitive();
atUint16 vertCount = dl.readVertCount();
/* First vert */
DLReader::DLPrimVert firstPrimVert = dl.readVert(true);
/* 3 Prim Verts to start */
int c = 0;
DLReader::DLPrimVert primVerts[3] =
{
dl.readVert(),
dl.readVert(),
dl.readVert()
};
if (ptype == GX::TRIANGLESTRIP)
{
atUint8 flip = 0;
for (int v=0 ; v<vertCount-2 ; ++v)
{
if (flip)
{
os.format("last_face, last_mesh = add_triangle(bm, bm.verts, (%u,%u,%u), norm_list, (%u,%u,%u), %u, od_list)\n",
primVerts[c%3].pos,
primVerts[(c+2)%3].pos,
primVerts[(c+1)%3].pos,
primVerts[c%3].norm,
primVerts[(c+2)%3].norm,
primVerts[(c+1)%3].norm,
sHead.matIdx);
if (matUVCount)
{
os << "if last_face is not None:\n";
for (int j=0 ; j<matUVCount ; ++j)
os.format(" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n"
" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n"
" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n",
primVerts[c%3].pos, j, primVerts[c%3].uvs[j],
primVerts[(c+2)%3].pos, j, primVerts[(c+2)%3].uvs[j],
primVerts[(c+1)%3].pos, j, primVerts[(c+1)%3].uvs[j]);
}
}
else
{
os.format("last_face, last_mesh = add_triangle(bm, bm.verts, (%u,%u,%u), norm_list, (%u,%u,%u), %u, od_list)\n",
primVerts[c%3].pos,
primVerts[(c+1)%3].pos,
primVerts[(c+2)%3].pos,
primVerts[c%3].norm,
primVerts[(c+1)%3].norm,
primVerts[(c+2)%3].norm,
sHead.matIdx);
if (matUVCount)
{
os << "if last_face is not None:\n";
for (int j=0 ; j<matUVCount ; ++j)
os.format(" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n"
" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n"
" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n",
primVerts[c%3].pos, j, primVerts[c%3].uvs[j],
primVerts[(c+1)%3].pos, j, primVerts[(c+1)%3].uvs[j],
primVerts[(c+2)%3].pos, j, primVerts[(c+2)%3].uvs[j]);
}
}
flip ^= 1;
bool peek = (v >= vertCount - 3);
/* Advance one prim vert */
primVerts[c%3] = dl.readVert(peek);
++c;
}
}
else if (ptype == GX::TRIANGLES)
{
for (int v=0 ; v<vertCount ; v+=3)
{
os.format("last_face, last_mesh = add_triangle(bm, bm.verts, (%u,%u,%u), norm_list, (%u,%u,%u), %u, od_list)\n",
primVerts[0].pos,
primVerts[1].pos,
primVerts[2].pos,
primVerts[0].norm,
primVerts[1].norm,
primVerts[2].norm,
sHead.matIdx);
if (matUVCount)
{
os << "if last_face is not None:\n";
for (int j=0 ; j<matUVCount ; ++j)
os.format(" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n"
" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n"
" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n",
primVerts[0].pos, j, primVerts[0].uvs[j],
primVerts[1].pos, j, primVerts[1].uvs[j],
primVerts[2].pos, j, primVerts[2].uvs[j]);
}
/* Break if done */
if (v+3 >= vertCount)
break;
bool peek = (v >= vertCount - 3);
/* Advance 3 Prim Verts */
for (int pv=0 ; pv<3 ; ++pv)
primVerts[pv] = dl.readVert(peek);
}
}
else if (ptype == GX::TRIANGLEFAN)
{
++c;
for (int v=0 ; v<vertCount-2 ; ++v)
{
os.format("last_face, last_mesh = add_triangle(bm, bm.verts, (%u,%u,%u), norm_list, (%u,%u,%u), %u, od_list)\n",
firstPrimVert.pos,
primVerts[c%3].pos,
primVerts[(c+1)%3].pos,
firstPrimVert.norm,
primVerts[c%3].norm,
primVerts[(c+1)%3].norm,
sHead.matIdx);
if (matUVCount)
{
os << "if last_face is not None:\n";
for (int j=0 ; j<matUVCount ; ++j)
os.format(" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n"
" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n"
" loop_from_facevert(last_face, %u)[last_mesh.loops.layers.uv[%u]].uv = uv_list[%u]\n",
firstPrimVert.pos, j, firstPrimVert.uvs[j],
primVerts[c%3].pos, j, primVerts[c%3].uvs[j],
primVerts[(c+1)%3].pos, j, primVerts[(c+1)%3].uvs[j]);
}
bool peek = (v >= vertCount - 3);
/* Advance one prim vert */
primVerts[(c+2)%3] = dl.readVert(peek);
++c;
}
}
os << "\n";
}
}
}
}
if (s < head.secCount - 1)
reader.seek(secStart + head.secSizes[s], Athena::Begin);
}
/* Finish Mesh */
os.format("mesh = bpy.data.meshes.new(bpy.context.scene.name)\n"
"obj = bpy.data.objects.new(mesh.name, mesh)\n"
"obj.show_transparent = True\n"
"bpy.context.scene.objects.link(obj)\n"
"mesh.cmdl_material_count = %u\n"
"for material in materials:\n"
" mesh.materials.append(material)\n"
"\n"
"# Preserve original indices\n"
"for vert in bm.verts:\n"
" ov = mesh.cmdl_orig_verts.add()\n"
" ov.index = vert.index\n"
"\n"
"# Merge OD meshes\n"
"for od_entry in od_list:\n"
" vert_dict = {}\n"
"\n"
" for vert in od_entry['bm'].verts:\n"
" if len(vert.link_faces):\n"
" vert_dict[vert.index] = bm.verts.new(vert.co, vert)\n"
" ov = mesh.cmdl_orig_verts.add()\n"
" ov.index = vert.index\n"
"\n"
" for face in od_entry['bm'].faces:\n"
" merge_verts = [vert_dict[fv.index] for fv in face.verts]\n"
" if bm.faces.get(merge_verts) is not None:\n"
" continue\n"
" merge_face = bm.faces.new(merge_verts)\n"
" for i in range(len(face.loops)):\n"
" old = face.loops[i]\n"
" new = merge_face.loops[i]\n"
" for j in range(len(od_entry['bm'].loops.layers.uv)):\n"
" new[bm.loops.layers.uv[j]] = old[od_entry['bm'].loops.layers.uv[j]]\n"
" merge_face.smooth = True\n"
" merge_face.material_index = face.material_index\n"
"\n"
" od_entry['bm'].free()\n"
"\n"
"# Remove loose vertices\n"
"#to_remove = []\n"
"#for vert in bm.verts:\n"
"# if not len(vert.link_faces):\n"
"# to_remove.append(vert)\n"
"#bmesh.ops.delete(bm, geom=to_remove, context=1)\n"
"\n"
"bm.to_mesh(mesh)\n"
"bm.free()\n"
"\n", head.matSetCount);
return true;
}
}
}
#endif // _DNACOMMON_CMDL_HPP_

View File

@ -1,5 +1,10 @@
make_dnalist(liblist
CMDL)
add_library(DNACommon
DNACommon.hpp DNACommon.cpp
${liblist}
GX.hpp
STRG.hpp STRG.cpp
TXTR.hpp TXTR.cpp)
TXTR.hpp TXTR.cpp
ANCS.hpp
ANIM.hpp ANIM.cpp)

View File

@ -5,47 +5,4 @@ namespace Retro
LogVisor::LogModule LogDNACommon("Retro::DNACommon");
const HECL::FourCC ENGL("ENGL");
const HECL::FourCC FREN("FREN");
const HECL::FourCC GERM("GERM");
const HECL::FourCC SPAN("SPAN");
const HECL::FourCC ITAL("ITAL");
const HECL::FourCC JAPN("JAPN");
const HECL::FourCC AFSM("AFSM");
const HECL::FourCC AGSC("AGSC");
const HECL::FourCC ANCS("ANCS");
const HECL::FourCC ANIM("ANIM");
const HECL::FourCC ATBL("ATBL");
const HECL::FourCC CINF("CINF");
const HECL::FourCC CMDL("CMDL");
const HECL::FourCC CRSC("CRSC");
const HECL::FourCC CSKR("CSKR");
const HECL::FourCC CSMP("CSMP");
const HECL::FourCC CSNG("CSNG");
const HECL::FourCC CTWK("CTWK");
const HECL::FourCC DGRP("DGRP");
const HECL::FourCC DPSC("DPSC");
const HECL::FourCC DUMB("DUMB");
const HECL::FourCC ELSC("ELSC");
const HECL::FourCC EVNT("EVNT");
const HECL::FourCC FONT("FONT");
const HECL::FourCC FRME("FRME");
const HECL::FourCC HINT("HINT");
const HECL::FourCC MAPA("MAPA");
const HECL::FourCC MAPU("MAPU");
const HECL::FourCC MAPW("MAPW");
const HECL::FourCC MLVL("MLVL");
const HECL::FourCC MREA("MREA");
const HECL::FourCC PART("PART");
const HECL::FourCC PATH("PATH");
const HECL::FourCC RFRM("RFRM");
const HECL::FourCC ROOM("ROOM");
const HECL::FourCC SAVW("SAVW");
const HECL::FourCC SCAN("SCAN");
const HECL::FourCC STRG("STRG");
const HECL::FourCC SWHC("SWHC");
const HECL::FourCC TXTR("TXTR");
const HECL::FourCC WPSC("WPSC");
}

View File

@ -2,10 +2,11 @@
#define __DNA_COMMON_HPP__
#include <stdio.h>
#include <Athena/DNA.hpp>
#include <Athena/DNAYaml.hpp>
#include <NOD/DiscBase.hpp>
#include "HECL/HECL.hpp"
#include "HECL/Database.hpp"
#include "../SpecBase.hpp"
namespace Retro
{
@ -14,9 +15,10 @@ extern LogVisor::LogModule LogDNACommon;
/* This comes up a great deal */
typedef Athena::io::DNA<Athena::BigEndian> BigDNA;
typedef Athena::io::DNAYaml<Athena::BigEndian> BigYAML;
/* FourCC with DNA read/write */
class FourCC final : public BigDNA, public HECL::FourCC
class FourCC final : public BigYAML, public HECL::FourCC
{
public:
FourCC() : HECL::FourCC() {}
@ -24,24 +26,35 @@ public:
: HECL::FourCC() {num = other.toUint32();}
FourCC(const char* name)
: HECL::FourCC(name) {}
FourCC(uint32_t n)
: HECL::FourCC(n) {}
Delete expl;
inline void read(Athena::io::IStreamReader& reader)
{reader.readUBytesToBuf(fcc, 4);}
inline void write(Athena::io::IStreamWriter& writer) const
{writer.writeUBytes((atUint8*)fcc, 4);}
inline void fromYAML(Athena::io::YAMLDocReader& reader)
{std::string rs = reader.readString(nullptr); strncpy(fcc, rs.c_str(), 4);}
inline void toYAML(Athena::io::YAMLDocWriter& writer) const
{writer.writeString(nullptr, std::string(fcc, 4));}
};
/* PAK 32-bit Unique ID */
class UniqueID32 : public BigDNA
class UniqueID32 : public BigYAML
{
uint32_t m_id;
uint32_t m_id = 0xffffffff;
public:
Delete expl;
inline operator bool() const {return m_id != 0xffffffff;}
inline void read(Athena::io::IStreamReader& reader)
{m_id = reader.readUint32();}
inline void write(Athena::io::IStreamWriter& writer) const
{writer.writeUint32(m_id);}
inline void fromYAML(Athena::io::YAMLDocReader& reader)
{m_id = reader.readUint32(nullptr);}
inline void toYAML(Athena::io::YAMLDocWriter& writer) const
{writer.writeUint32(nullptr, m_id);}
inline bool operator!=(const UniqueID32& other) const {return m_id != other.m_id;}
inline bool operator==(const UniqueID32& other) const {return m_id == other.m_id;}
@ -57,9 +70,10 @@ public:
/* PAK 64-bit Unique ID */
class UniqueID64 : public BigDNA
{
uint64_t m_id;
uint64_t m_id = 0xffffffffffffffff;
public:
Delete expl;
inline operator bool() const {return m_id != 0xffffffffffffffff;}
inline void read(Athena::io::IStreamReader& reader)
{m_id = reader.readUint64();}
inline void write(Athena::io::IStreamWriter& writer) const
@ -88,6 +102,9 @@ class UniqueID128 : public BigDNA
};
public:
Delete expl;
UniqueID128() {m_id[0]=0xffffffffffffffff; m_id[1]=0xffffffffffffffff;}
inline operator bool() const
{return m_id[0] != 0xffffffffffffffff && m_id[1] != 0xffffffffffffffff;}
inline void read(Athena::io::IStreamReader& reader)
{
m_id[0] = reader.readUint64();
@ -153,6 +170,70 @@ struct CaseInsensitiveCompare
#endif
};
/* Word Bitmap reader/writer */
struct WordBitmap
{
std::vector<atUint32> m_words;
size_t m_bitCount = 0;
void read(Athena::io::IStreamReader& reader, size_t bitCount)
{
m_bitCount = bitCount;
size_t wordCount = (bitCount + 31) / 32;
m_words.clear();
m_words.reserve(wordCount);
for (size_t w=0 ; w<wordCount ; ++w)
m_words.push_back(reader.readUint32());
}
void write(Athena::io::IStreamWriter& writer) const
{
for (atUint32 word : m_words)
writer.writeUint32(word);
}
size_t getBitCount() const {return m_bitCount;}
bool getBit(size_t idx) const
{
size_t wordIdx = idx / 32;
if (wordIdx >= m_words.size())
return false;
size_t wordCur = idx % 32;
return (m_words[wordIdx] >> wordCur) & 0x1;
}
void setBit(size_t idx)
{
size_t wordIdx = idx / 32;
while (wordIdx >= m_words.size())
m_words.push_back(0);
size_t wordCur = idx % 32;
m_words[wordIdx] |= (1 << wordCur);
}
void unsetBit(size_t idx)
{
size_t wordIdx = idx / 32;
while (wordIdx >= m_words.size())
m_words.push_back(0);
size_t wordCur = idx % 32;
m_words[wordIdx] &= ~(1 << wordCur);
}
void clear()
{
m_words.clear();
}
class Iterator : public std::iterator<std::forward_iterator_tag, bool>
{
friend class WordBitmap;
const WordBitmap& m_bmp;
size_t m_idx = 0;
Iterator(const WordBitmap& bmp, size_t idx) : m_bmp(bmp), m_idx(idx) {}
public:
Iterator& operator++() {++m_idx; return *this;}
bool operator*() {return m_bmp.getBit(m_idx);}
bool operator!=(const Iterator& other) const {return m_idx != other.m_idx;}
};
Iterator begin() const {return Iterator(*this, 0);}
Iterator end() const {return Iterator(*this, m_bitCount);}
};
/* PAK entry stream reader */
class PAKEntryReadStream : public Athena::io::IStreamReader
{
@ -197,40 +278,91 @@ public:
}
};
/* Resource extractor type */
typedef struct
struct UniqueResult
{
std::function<bool(PAKEntryReadStream&, const HECL::ProjectPath&)> func;
enum Type
{
UNIQUE_NOTFOUND,
UNIQUE_LEVEL,
UNIQUE_AREA,
UNIQUE_LAYER
} type = UNIQUE_NOTFOUND;
const HECL::SystemString* areaName = nullptr;
const HECL::SystemString* layerName = nullptr;
UniqueResult() = default;
UniqueResult(Type tp) : type(tp) {}
inline HECL::ProjectPath uniquePath(const HECL::ProjectPath& pakPath) const
{
if (type == UNIQUE_AREA)
{
HECL::ProjectPath areaDir(pakPath, *areaName);
areaDir.makeDir();
return areaDir;
}
else if (type == UNIQUE_LAYER)
{
HECL::ProjectPath areaDir(pakPath, *areaName);
areaDir.makeDir();
HECL::ProjectPath layerDir(areaDir, *layerName);
layerDir.makeDir();
return layerDir;
}
return pakPath;
}
};
template <class BRIDGETYPE>
class PAKRouter;
/* Resource extractor type */
template <class PAKBRIDGE>
struct ResExtractor
{
std::function<bool(const SpecBase&, PAKEntryReadStream&, const HECL::ProjectPath&)> func_a;
std::function<bool(const SpecBase&, PAKEntryReadStream&, const HECL::ProjectPath&, PAKRouter<PAKBRIDGE>&,
const typename PAKBRIDGE::PAKType::Entry&, bool)> func_b;
const char* fileExt;
unsigned weight;
} ResExtractor;
};
/* PAKRouter (for detecting shared entry locations) */
template <class BRIDGETYPE>
class PAKRouter
{
public:
using PAKType = typename BRIDGETYPE::PAKType;
using IDType = typename PAKType::IDType;
using EntryType = typename PAKType::Entry;
private:
const SpecBase& m_dataSpec;
const std::vector<BRIDGETYPE>* m_bridges = nullptr;
const HECL::ProjectPath& m_gameWorking;
const HECL::ProjectPath& m_gameCooked;
HECL::ProjectPath m_sharedWorking;
HECL::ProjectPath m_sharedCooked;
const typename BRIDGETYPE::PAKType* m_pak = nullptr;
const PAKType* m_pak = nullptr;
const NOD::DiscBase::IPartition::Node* m_node = nullptr;
HECL::ProjectPath m_pakWorking;
HECL::ProjectPath m_pakCooked;
std::unordered_map<typename BRIDGETYPE::PAKType::IDType, typename BRIDGETYPE::PAKType::Entry*> m_uniqueEntries;
std::unordered_map<typename BRIDGETYPE::PAKType::IDType, typename BRIDGETYPE::PAKType::Entry*> m_sharedEntries;
std::unordered_map<typename PAKType::IDType, typename PAKType::Entry*> m_uniqueEntries;
std::unordered_map<IDType, EntryType*> m_sharedEntries;
public:
PAKRouter(const HECL::ProjectPath& working, const HECL::ProjectPath& cooked)
: m_gameWorking(working), m_gameCooked(cooked),
PAKRouter(const SpecBase& dataSpec, const HECL::ProjectPath& working, const HECL::ProjectPath& cooked)
: m_dataSpec(dataSpec),
m_gameWorking(working), m_gameCooked(cooked),
m_sharedWorking(working, "Shared"), m_sharedCooked(cooked, "Shared") {}
void build(const std::vector<BRIDGETYPE>& bridges, std::function<void(float)> progress)
void build(std::vector<BRIDGETYPE>& bridges, std::function<void(float)> progress)
{
m_bridges = &bridges;
m_uniqueEntries.clear();
m_sharedEntries.clear();
size_t count = 0;
float bridgesSz = bridges.size();
for (const BRIDGETYPE& bridge : bridges)
/* Route entries unique/shared per-pak */
for (BRIDGETYPE& bridge : bridges)
{
bridge.build();
const typename BRIDGETYPE::PAKType& pak = bridge.getPAK();
for (const auto& entry : pak.m_idMap)
{
@ -265,7 +397,7 @@ public:
}
HECL::ProjectPath getWorking(const typename BRIDGETYPE::PAKType::Entry* entry,
const ResExtractor& extractor) const
const ResExtractor<BRIDGETYPE>& extractor) const
{
if (!m_pak)
LogDNACommon.report(LogVisor::FatalError,
@ -273,20 +405,22 @@ public:
auto uniqueSearch = m_uniqueEntries.find(entry->id);
if (uniqueSearch != m_uniqueEntries.end())
{
HECL::ProjectPath uniquePath = entry->unique.uniquePath(m_pakWorking);
HECL::SystemString entName = m_pak->bestEntryName(*entry);
if (extractor.fileExt)
entName += extractor.fileExt;
return HECL::ProjectPath(m_pakWorking, entName);
return HECL::ProjectPath(uniquePath, entName);
}
auto sharedSearch = m_sharedEntries.find(entry->id);
if (sharedSearch != m_sharedEntries.end())
{
HECL::ProjectPath uniquePathPre = entry->unique.uniquePath(m_pakWorking);
HECL::SystemString entName = m_pak->bestEntryName(*entry);
if (extractor.fileExt)
entName += extractor.fileExt;
HECL::ProjectPath sharedPath(m_sharedWorking, entName);
HECL::ProjectPath uniquePath(m_pakWorking, entName);
if (extractor.func)
HECL::ProjectPath uniquePath(uniquePathPre, entName);
if (extractor.func_a || extractor.func_b)
uniquePath.makeLinkTo(sharedPath);
m_sharedWorking.makeDir();
return sharedPath;
@ -295,6 +429,11 @@ public:
return HECL::ProjectPath();
}
HECL::ProjectPath getWorking(const typename BRIDGETYPE::PAKType::Entry* entry) const
{
return getWorking(entry, BRIDGETYPE::LookupExtractor(*entry));
}
HECL::ProjectPath getCooked(const typename BRIDGETYPE::PAKType::Entry* entry) const
{
if (!m_pak)
@ -303,7 +442,8 @@ public:
auto uniqueSearch = m_uniqueEntries.find(entry->id);
if (uniqueSearch != m_uniqueEntries.end())
{
return HECL::ProjectPath(m_pakCooked, m_pak->bestEntryName(*entry));
HECL::ProjectPath uniquePath = entry->unique.uniquePath(m_pakCooked);
return HECL::ProjectPath(uniquePath, m_pak->bestEntryName(*entry));
}
auto sharedSearch = m_sharedEntries.find(entry->id);
if (sharedSearch != m_sharedEntries.end())
@ -315,6 +455,43 @@ public:
return HECL::ProjectPath();
}
HECL::SystemString getResourceRelativePath(const typename BRIDGETYPE::PAKType::Entry& a,
const typename BRIDGETYPE::PAKType::IDType& b) const
{
if (!m_pak)
LogDNACommon.report(LogVisor::FatalError,
"PAKRouter::enterPAKBridge() must be called before PAKRouter::getResourceRelativePath()");
const typename BRIDGETYPE::PAKType::Entry* be = m_pak->lookupEntry(b);
if (!be)
return HECL::SystemString();
HECL::ProjectPath aPath = getWorking(&a, BRIDGETYPE::LookupExtractor(a));
HECL::SystemString ret;
for (int i=0 ; i<aPath.levelCount() ; ++i)
ret += "../";
HECL::ProjectPath bPath = getWorking(be, BRIDGETYPE::LookupExtractor(*be));
ret += bPath.getRelativePath();
return ret;
}
std::string getBestEntryName(const typename BRIDGETYPE::PAKType::Entry& entry) const
{
if (!m_pak)
LogDNACommon.report(LogVisor::FatalError,
"PAKRouter::enterPAKBridge() must be called before PAKRouter::getBestEntryName()");
return m_pak->bestEntryName(entry);
}
std::string getBestEntryName(const typename BRIDGETYPE::PAKType::IDType& entry) const
{
if (!m_pak)
LogDNACommon.report(LogVisor::FatalError,
"PAKRouter::enterPAKBridge() must be called before PAKRouter::getBestEntryName()");
const typename BRIDGETYPE::PAKType::Entry* e = m_pak->lookupEntry(entry);
if (!e)
return entry.toString();
return m_pak->bestEntryName(*e);
}
bool extractResources(const BRIDGETYPE& pakBridge, bool force, std::function<void(float)> progress)
{
enterPAKBridge(pakBridge);
@ -325,7 +502,7 @@ public:
{
for (const auto& item : m_pak->m_idMap)
{
ResExtractor extractor = BRIDGETYPE::LookupExtractor(*item.second);
ResExtractor<BRIDGETYPE> extractor = BRIDGETYPE::LookupExtractor(*item.second);
if (extractor.weight != w)
continue;
@ -339,12 +516,20 @@ public:
}
HECL::ProjectPath working = getWorking(item.second, extractor);
if (extractor.func)
if (extractor.func_a) /* Doesn't need PAKRouter access */
{
if (force || working.getPathType() == HECL::ProjectPath::PT_NONE)
{
PAKEntryReadStream s = item.second->beginReadStream(*m_node);
extractor.func(s, working);
extractor.func_a(m_dataSpec, s, working);
}
}
else if (extractor.func_b) /* Needs PAKRouter access */
{
if (force || working.getPathType() == HECL::ProjectPath::PT_NONE)
{
PAKEntryReadStream s = item.second->beginReadStream(*m_node);
extractor.func_b(m_dataSpec, s, working, *this, *item.second, force);
}
}
@ -354,56 +539,43 @@ public:
return true;
}
const typename BRIDGETYPE::PAKType::Entry* lookupEntry(const typename BRIDGETYPE::PAKType::IDType& entry,
const NOD::DiscBase::IPartition::Node** nodeOut=nullptr)
{
if (!m_bridges)
LogDNACommon.report(LogVisor::FatalError,
"PAKRouter::build() must be called before PAKRouter::lookupEntry()");
if (m_pak)
{
const typename BRIDGETYPE::PAKType::Entry* ent = m_pak->lookupEntry(entry);
if (ent)
{
if (nodeOut)
*nodeOut = m_node;
return ent;
}
}
for (const BRIDGETYPE& bridge : *m_bridges)
{
const typename BRIDGETYPE::PAKType& pak = bridge.getPAK();
const typename BRIDGETYPE::PAKType::Entry* ent = pak.lookupEntry(entry);
if (ent)
{
if (nodeOut)
*nodeOut = &bridge.getNode();
return ent;
}
}
if (nodeOut)
*nodeOut = nullptr;
return nullptr;
}
};
/* Resource cooker function */
typedef std::function<bool(const HECL::ProjectPath&, const HECL::ProjectPath&)> ResCooker;
/* Language-identifiers */
extern const HECL::FourCC ENGL;
extern const HECL::FourCC FREN;
extern const HECL::FourCC GERM;
extern const HECL::FourCC SPAN;
extern const HECL::FourCC ITAL;
extern const HECL::FourCC JAPN;
/* Resource types */
extern const HECL::FourCC AFSM;
extern const HECL::FourCC AGSC;
extern const HECL::FourCC ANCS;
extern const HECL::FourCC ANIM;
extern const HECL::FourCC ATBL;
extern const HECL::FourCC CINF;
extern const HECL::FourCC CMDL;
extern const HECL::FourCC CRSC;
extern const HECL::FourCC CSKR;
extern const HECL::FourCC CSMP;
extern const HECL::FourCC CSNG;
extern const HECL::FourCC CTWK;
extern const HECL::FourCC DGRP;
extern const HECL::FourCC DPSC;
extern const HECL::FourCC DUMB;
extern const HECL::FourCC ELSC;
extern const HECL::FourCC EVNT;
extern const HECL::FourCC FONT;
extern const HECL::FourCC FRME;
extern const HECL::FourCC HINT;
extern const HECL::FourCC MAPA;
extern const HECL::FourCC MAPU;
extern const HECL::FourCC MAPW;
extern const HECL::FourCC MLVL;
extern const HECL::FourCC MREA;
extern const HECL::FourCC PART;
extern const HECL::FourCC PATH;
extern const HECL::FourCC RFRM;
extern const HECL::FourCC ROOM;
extern const HECL::FourCC SAVW;
extern const HECL::FourCC SCAN;
extern const HECL::FourCC STRG;
extern const HECL::FourCC SWHC;
extern const HECL::FourCC TXTR;
extern const HECL::FourCC WPSC;
}
/* Hash template-specializations for UniqueID types */

View File

@ -24,157 +24,241 @@ struct Color : Athena::io::DNA<Athena::BigEndian>
enum AttrType
{
GX_NONE,
GX_DIRECT,
GX_INDEX8,
GX_INDEX16
NONE,
DIRECT,
INDEX8,
INDEX16
};
enum TevColorArg {
GX_CC_CPREV = 0, /*!< Use the color value from previous TEV stage */
GX_CC_APREV = 1, /*!< Use the alpha value from previous TEV stage */
GX_CC_C0 = 2, /*!< Use the color value from the color/output register 0 */
GX_CC_A0 = 3, /*!< Use the alpha value from the color/output register 0 */
GX_CC_C1 = 4, /*!< Use the color value from the color/output register 1 */
GX_CC_A1 = 5, /*!< Use the alpha value from the color/output register 1 */
GX_CC_C2 = 6, /*!< Use the color value from the color/output register 2 */
GX_CC_A2 = 7, /*!< Use the alpha value from the color/output register 2 */
GX_CC_TEXC = 8, /*!< Use the color value from texture */
GX_CC_TEXA = 9, /*!< Use the alpha value from texture */
GX_CC_RASC = 10, /*!< Use the color value from rasterizer */
GX_CC_RASA = 11, /*!< Use the alpha value from rasterizer */
GX_CC_ONE = 12,
GX_CC_HALF = 13,
GX_CC_KONST = 14,
GX_CC_ZERO = 15 /*!< Use to pass zero value */
CC_CPREV = 0, /*!< Use the color value from previous TEV stage */
CC_APREV = 1, /*!< Use the alpha value from previous TEV stage */
CC_C0 = 2, /*!< Use the color value from the color/output register 0 */
CC_A0 = 3, /*!< Use the alpha value from the color/output register 0 */
CC_C1 = 4, /*!< Use the color value from the color/output register 1 */
CC_A1 = 5, /*!< Use the alpha value from the color/output register 1 */
CC_C2 = 6, /*!< Use the color value from the color/output register 2 */
CC_A2 = 7, /*!< Use the alpha value from the color/output register 2 */
CC_TEXC = 8, /*!< Use the color value from texture */
CC_TEXA = 9, /*!< Use the alpha value from texture */
CC_RASC = 10, /*!< Use the color value from rasterizer */
CC_RASA = 11, /*!< Use the alpha value from rasterizer */
CC_ONE = 12,
CC_HALF = 13,
CC_KONST = 14,
CC_ZERO = 15 /*!< Use to pass zero value */
};
enum TevAlphaArg {
GX_CA_APREV = 0, /*!< Use the alpha value from previous TEV stage */
GX_CA_A0 = 1, /*!< Use the alpha value from the color/output register 0 */
GX_CA_A1 = 2, /*!< Use the alpha value from the color/output register 1 */
GX_CA_A2 = 3, /*!< Use the alpha value from the color/output register 2 */
GX_CA_TEXA = 4, /*!< Use the alpha value from texture */
GX_CA_RASA = 5, /*!< Use the alpha value from rasterizer */
GX_CA_KONST = 6,
GX_CA_ZERO = 7 /*!< Use to pass zero value */
CA_APREV = 0, /*!< Use the alpha value from previous TEV stage */
CA_A0 = 1, /*!< Use the alpha value from the color/output register 0 */
CA_A1 = 2, /*!< Use the alpha value from the color/output register 1 */
CA_A2 = 3, /*!< Use the alpha value from the color/output register 2 */
CA_TEXA = 4, /*!< Use the alpha value from texture */
CA_RASA = 5, /*!< Use the alpha value from rasterizer */
CA_KONST = 6,
CA_ZERO = 7 /*!< Use to pass zero value */
};
enum TevKColorSel
{
TEV_KCSEL_8_8 = 0x00,
TEV_KCSEL_7_8 = 0x01,
TEV_KCSEL_6_8 = 0x02,
TEV_KCSEL_5_8 = 0x03,
TEV_KCSEL_4_8 = 0x04,
TEV_KCSEL_3_8 = 0x05,
TEV_KCSEL_2_8 = 0x06,
TEV_KCSEL_1_8 = 0x07,
TEV_KCSEL_1 = TEV_KCSEL_8_8,
TEV_KCSEL_3_4 = TEV_KCSEL_6_8,
TEV_KCSEL_1_2 = TEV_KCSEL_4_8,
TEV_KCSEL_1_4 = TEV_KCSEL_2_8,
TEV_KCSEL_K0 = 0x0C,
TEV_KCSEL_K1 = 0x0D,
TEV_KCSEL_K2 = 0x0E,
TEV_KCSEL_K3 = 0x0F,
TEV_KCSEL_K0_R = 0x10,
TEV_KCSEL_K1_R = 0x11,
TEV_KCSEL_K2_R = 0x12,
TEV_KCSEL_K3_R = 0x13,
TEV_KCSEL_K0_G = 0x14,
TEV_KCSEL_K1_G = 0x15,
TEV_KCSEL_K2_G = 0x16,
TEV_KCSEL_K3_G = 0x17,
TEV_KCSEL_K0_B = 0x18,
TEV_KCSEL_K1_B = 0x19,
TEV_KCSEL_K2_B = 0x1A,
TEV_KCSEL_K3_B = 0x1B,
TEV_KCSEL_K0_A = 0x1C,
TEV_KCSEL_K1_A = 0x1D,
TEV_KCSEL_K2_A = 0x1E,
TEV_KCSEL_K3_A = 0x1F
};
enum TevKAlphaSel
{
TEV_KASEL_8_8 = 0x00,
TEV_KASEL_7_8 = 0x01,
TEV_KASEL_6_8 = 0x02,
TEV_KASEL_5_8 = 0x03,
TEV_KASEL_4_8 = 0x04,
TEV_KASEL_3_8 = 0x05,
TEV_KASEL_2_8 = 0x06,
TEV_KASEL_1_8 = 0x07,
TEV_KASEL_1 = TEV_KASEL_8_8,
TEV_KASEL_3_4 = TEV_KASEL_6_8,
TEV_KASEL_1_2 = TEV_KASEL_4_8,
TEV_KASEL_1_4 = TEV_KASEL_2_8,
TEV_KASEL_K0_R = 0x10,
TEV_KASEL_K1_R = 0x11,
TEV_KASEL_K2_R = 0x12,
TEV_KASEL_K3_R = 0x13,
TEV_KASEL_K0_G = 0x14,
TEV_KASEL_K1_G = 0x15,
TEV_KASEL_K2_G = 0x16,
TEV_KASEL_K3_G = 0x17,
TEV_KASEL_K0_B = 0x18,
TEV_KASEL_K1_B = 0x19,
TEV_KASEL_K2_B = 0x1A,
TEV_KASEL_K3_B = 0x1B,
TEV_KASEL_K0_A = 0x1C,
TEV_KASEL_K1_A = 0x1D,
TEV_KASEL_K2_A = 0x1E,
TEV_KASEL_K3_A = 0x1F
};
enum TevOp {
GX_TEV_ADD = 0,
GX_TEV_SUB = 1,
GX_TEV_COMP_R8_GT = 8,
GX_TEV_COMP_R8_EQ = 9,
GX_TEV_COMP_GR16_GT = 10,
GX_TEV_COMP_GR16_EQ = 11,
GX_TEV_COMP_BGR24_GT = 12,
GX_TEV_COMP_BGR24_EQ = 13,
GX_TEV_COMP_RGB8_GT = 14,
GX_TEV_COMP_RGB8_EQ = 15,
GX_TEV_COMP_A8_GT = GX_TEV_COMP_RGB8_GT, // for alpha channel
GX_TEV_COMP_A8_EQ = GX_TEV_COMP_RGB8_EQ // for alpha channel
TEV_ADD = 0,
TEV_SUB = 1,
TEV_COMP_R8_GT = 8,
TEV_COMP_R8_EQ = 9,
TEV_COMP_GR16_GT = 10,
TEV_COMP_GR16_EQ = 11,
TEV_COMP_BGR24_GT = 12,
TEV_COMP_BGR24_EQ = 13,
TEV_COMP_RGB8_GT = 14,
TEV_COMP_RGB8_EQ = 15,
TEV_COMP_A8_GT = TEV_COMP_RGB8_GT, // for alpha channel
TEV_COMP_A8_EQ = TEV_COMP_RGB8_EQ // for alpha channel
};
enum TevBias {
GX_TB_ZERO = 0,
GX_TB_ADDHALF = 1,
GX_TB_SUBHALF = 2,
TB_ZERO = 0,
TB_ADDHALF = 1,
TB_SUBHALF = 2,
};
enum TevScale {
GX_CS_SCALE_1 = 0,
GX_CS_SCALE_2 = 1,
GX_CS_SCALE_4 = 2,
GX_CS_DIVIDE_2 = 3
CS_SCALE_1 = 0,
CS_SCALE_2 = 1,
CS_SCALE_4 = 2,
CS_DIVIDE_2 = 3
};
enum TevRegID {
GX_TEVPREV = 0,
GX_TEVREG0 = 1,
GX_TEVREG1 = 2,
GX_TEVREG2 = 3
TEVPREV = 0,
TEVREG0 = 1,
TEVREG1 = 2,
TEVREG2 = 3
};
enum TexGenType
{
GX_TG_MTX3x4 = 0,
GX_TG_MTX2x4,
GX_TG_BUMP0,
GX_TG_BUMP1,
GX_TG_BUMP2,
GX_TG_BUMP3,
GX_TG_BUMP4,
GX_TG_BUMP5,
GX_TG_BUMP6,
GX_TG_BUMP7,
GX_TG_SRTG
TG_MTX3x4 = 0,
TG_MTX2x4,
TG_BUMP0,
TG_BUMP1,
TG_BUMP2,
TG_BUMP3,
TG_BUMP4,
TG_BUMP5,
TG_BUMP6,
TG_BUMP7,
TG_SRTG
};
enum TexGenSrc
{
GX_TG_POS = 0,
GX_TG_NRM,
GX_TG_BINRM,
GX_TG_TANGENT,
GX_TG_TEX0,
GX_TG_TEX1,
GX_TG_TEX2,
GX_TG_TEX3,
GX_TG_TEX4,
GX_TG_TEX5,
GX_TG_TEX6,
GX_TG_TEX7,
GX_TG_TEXCOORD0,
GX_TG_TEXCOORD1,
GX_TG_TEXCOORD2,
GX_TG_TEXCOORD3,
GX_TG_TEXCOORD4,
GX_TG_TEXCOORD5,
GX_TG_TEXCOORD6,
GX_TG_COLOR0,
GX_TG_COLOR1
TG_POS = 0,
TG_NRM,
TG_BINRM,
TG_TANGENT,
TG_TEX0,
TG_TEX1,
TG_TEX2,
TG_TEX3,
TG_TEX4,
TG_TEX5,
TG_TEX6,
TG_TEX7,
TG_TEXCOORD0,
TG_TEXCOORD1,
TG_TEXCOORD2,
TG_TEXCOORD3,
TG_TEXCOORD4,
TG_TEXCOORD5,
TG_TEXCOORD6,
TG_COLOR0,
TG_COLOR1
};
enum TexMtx
{
GX_TEXMTX0 = 30,
GX_TEXMTX1 = 33,
GX_TEXMTX2 = 36,
GX_TEXMTX3 = 39,
GX_TEXMTX4 = 42,
GX_TEXMTX5 = 45,
GX_TEXMTX6 = 48,
GX_TEXMTX7 = 51,
GX_TEXMTX8 = 54,
GX_TEXMTX9 = 57,
GX_IDENTITY = 60
TEXMTX0 = 30,
TEXMTX1 = 33,
TEXMTX2 = 36,
TEXMTX3 = 39,
TEXMTX4 = 42,
TEXMTX5 = 45,
TEXMTX6 = 48,
TEXMTX7 = 51,
TEXMTX8 = 54,
TEXMTX9 = 57,
IDENTITY = 60
};
enum PTTexMtx
{
GX_PTTEXMTX0 = 64,
GX_PTTEXMTX1 = 67,
GX_PTTEXMTX2 = 70,
GX_PTTEXMTX3 = 73,
GX_PTTEXMTX4 = 76,
GX_PTTEXMTX5 = 79,
GX_PTTEXMTX6 = 82,
GX_PTTEXMTX7 = 85,
GX_PTTEXMTX8 = 88,
GX_PTTEXMTX9 = 91,
GX_PTTEXMTX10 = 94,
GX_PTTEXMTX11 = 97,
GX_PTTEXMTX12 = 100,
GX_PTTEXMTX13 = 103,
GX_PTTEXMTX14 = 106,
GX_PTTEXMTX15 = 109,
GX_PTTEXMTX16 = 112,
GX_PTTEXMTX17 = 115,
GX_PTTEXMTX18 = 118,
GX_PTTEXMTX19 = 121,
GX_PTIDENTITY = 125
PTTEXMTX0 = 64,
PTTEXMTX1 = 67,
PTTEXMTX2 = 70,
PTTEXMTX3 = 73,
PTTEXMTX4 = 76,
PTTEXMTX5 = 79,
PTTEXMTX6 = 82,
PTTEXMTX7 = 85,
PTTEXMTX8 = 88,
PTTEXMTX9 = 91,
PTTEXMTX10 = 94,
PTTEXMTX11 = 97,
PTTEXMTX12 = 100,
PTTEXMTX13 = 103,
PTTEXMTX14 = 106,
PTTEXMTX15 = 109,
PTTEXMTX16 = 112,
PTTEXMTX17 = 115,
PTTEXMTX18 = 118,
PTTEXMTX19 = 121,
PTIDENTITY = 125
};
enum Primitive
{
POINTS = 0xb8,
LINES = 0xa8,
LINESTRIP = 0xb0,
TRIANGLES = 0x90,
TRIANGLESTRIP = 0x98,
TRIANGLEFAN = 0xa0,
QUADS = 0x80
};
}
#endif
#endif // _DNACOMMON_GX_HPP_

View File

@ -6,8 +6,6 @@
namespace Retro
{
HECL::Database::ASListType<std::string> ASTYPE_STRGLanguage("STRG", "Language", "string");
std::unique_ptr<ISTRG> LoadSTRG(Athena::io::IStreamReader& reader)
{
reader.setEndian(Athena::BigEndian);

View File

@ -3,7 +3,6 @@
#include <string>
#include <fstream>
#include <angelscript.h>
#include <HECL/HECL.hpp>
#include <HECL/Database.hpp>
#include <Athena/FileWriter.hpp>
@ -11,7 +10,7 @@
namespace Retro
{
struct ISTRG
struct ISTRG : BigYAML
{
virtual ~ISTRG() {}
@ -20,14 +19,9 @@ struct ISTRG
virtual std::wstring getUTF16(const FourCC& lang, size_t idx) const=0;
virtual HECL::SystemString getSystemString(const FourCC& lang, size_t idx) const=0;
virtual int32_t lookupIdx(const std::string& name) const=0;
virtual bool readAngelScript(const AngelScript::asIScriptModule& in)=0;
virtual void writeAngelScript(std::ofstream& out) const=0;
};
std::unique_ptr<ISTRG> LoadSTRG(Athena::io::IStreamReader& reader);
extern HECL::Database::ASListType<std::string> ASTYPE_STRGLanguage;
}
#endif // __COMMON_STRG_HPP__

View File

@ -479,7 +479,7 @@ static void PNGWarn(png_structp png, png_const_charp msg)
Log.report(LogVisor::Warning, msg);
}
bool TXTR::Extract(PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
bool TXTR::Extract(const SpecBase& dataspec, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
{
rs.setEndian(Athena::BigEndian);
uint32_t format = rs.readUint32();

View File

@ -8,7 +8,7 @@ namespace Retro
struct TXTR
{
static bool Extract(PAKEntryReadStream& rs, const HECL::ProjectPath& outPath);
static bool Extract(const SpecBase& dataspec, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath);
static bool Cook(const HECL::ProjectPath& inPath, const HECL::ProjectPath& outPath);
};

875
DataSpec/DNAMP1/ANCS.cpp Normal file
View File

@ -0,0 +1,875 @@
#include "ANCS.hpp"
namespace Retro
{
namespace DNAMP1
{
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::ParmInfo::read(Athena::io::IStreamReader& reader)
{
parmType = reader.readUint32();
unk1 = reader.readUint32();
unk2 = reader.readFloat();
switch (DataType(parmType))
{
case DataType::DTInt32:
parmVals[0].int32 = reader.readInt32();
parmVals[1].int32 = reader.readInt32();
break;
case DataType::DTUInt32:
case DataType::DTEnum:
parmVals[0].uint32 = reader.readUint32();
parmVals[1].uint32 = reader.readUint32();
break;
case DataType::DTFloat:
parmVals[0].float32 = reader.readFloat();
parmVals[1].float32 = reader.readFloat();
break;
case DataType::DTBool:
parmVals[0].bool1 = reader.readBool();
parmVals[1].bool1 = reader.readBool();
break;
}
}
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::ParmInfo::write(Athena::io::IStreamWriter& writer) const
{
writer.writeUint32(parmType);
writer.writeUint32(unk1);
writer.writeFloat(unk2);
switch (DataType(parmType))
{
case DataType::DTInt32:
writer.writeInt32(parmVals[0].int32);
writer.writeInt32(parmVals[1].int32);
break;
case DataType::DTUInt32:
case DataType::DTEnum:
writer.writeUint32(parmVals[0].uint32);
writer.writeUint32(parmVals[0].uint32);
break;
case DataType::DTFloat:
writer.writeFloat(parmVals[0].float32);
writer.writeFloat(parmVals[0].float32);
break;
case DataType::DTBool:
writer.writeBool(parmVals[0].bool1);
writer.writeBool(parmVals[0].bool1);
break;
}
}
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::ParmInfo::fromYAML(Athena::io::YAMLDocReader& reader)
{
parmType = reader.readUint32("parmType");
unk1 = reader.readUint32("unk1");
unk2 = reader.readFloat("unk2");
reader.enterSubVector("parmVals");
switch (DataType(parmType))
{
case DataType::DTInt32:
parmVals[0].int32 = reader.readInt32(nullptr);
parmVals[1].int32 = reader.readInt32(nullptr);
break;
case DataType::DTUInt32:
case DataType::DTEnum:
parmVals[0].uint32 = reader.readUint32(nullptr);
parmVals[1].uint32 = reader.readUint32(nullptr);
break;
case DataType::DTFloat:
parmVals[0].float32 = reader.readFloat(nullptr);
parmVals[1].float32 = reader.readFloat(nullptr);
break;
case DataType::DTBool:
parmVals[0].bool1 = reader.readBool(nullptr);
parmVals[1].bool1 = reader.readBool(nullptr);
break;
default: break;
}
reader.leaveSubVector();
}
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::ParmInfo::toYAML(Athena::io::YAMLDocWriter& writer) const
{
writer.writeUint32("parmType", parmType);
writer.writeUint32("unk1", unk1);
writer.writeFloat("unk2", unk2);
writer.enterSubVector("parmVals");
switch (DataType(parmType))
{
case DataType::DTInt32:
writer.writeInt32(nullptr, parmVals[0].int32);
writer.writeInt32(nullptr, parmVals[1].int32);
break;
case DataType::DTUInt32:
case DataType::DTEnum:
writer.writeUint32(nullptr, parmVals[0].uint32);
writer.writeUint32(nullptr, parmVals[0].uint32);
break;
case DataType::DTFloat:
writer.writeFloat(nullptr, parmVals[0].float32);
writer.writeFloat(nullptr, parmVals[0].float32);
break;
case DataType::DTBool:
writer.writeBool(nullptr, parmVals[0].bool1);
writer.writeBool(nullptr, parmVals[0].bool1);
break;
}
writer.leaveSubVector();
}
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::read(Athena::io::IStreamReader& reader)
{
id = reader.readUint32();
atUint32 parmInfoCount = reader.readUint32();
atUint32 animInfoCount = reader.readUint32();
reader.enumerate(parmInfos, parmInfoCount);
animInfos.clear();
animInfos.reserve(animInfoCount);
reader.enumerate<AnimInfo>(animInfos, animInfoCount,
[this, parmInfoCount](Athena::io::IStreamReader& reader, AnimInfo& ai)
{
ai.id = reader.readUint32();
ai.parmVals.reserve(parmInfoCount);
for (const ParmInfo& pi : parmInfos)
{
switch (ParmInfo::DataType(pi.parmType))
{
case ParmInfo::DTInt32:
ai.parmVals.emplace_back(reader.readInt32());
break;
case ParmInfo::DTUInt32:
case ParmInfo::DTEnum:
ai.parmVals.emplace_back(reader.readUint32());
break;
case ParmInfo::DTFloat:
ai.parmVals.emplace_back(reader.readFloat());
break;
case ParmInfo::DTBool:
ai.parmVals.emplace_back(reader.readBool());
break;
default: break;
}
}
});
}
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::write(Athena::io::IStreamWriter& writer) const
{
writer.writeUint32(id);
writer.writeUint32(parmInfos.size());
writer.writeUint32(animInfos.size());
for (const ParmInfo& pi : parmInfos)
pi.write(writer);
for (const AnimInfo& ai : animInfos)
{
writer.writeUint32(ai.id);
auto it = ai.parmVals.begin();
for (const ParmInfo& pi : parmInfos)
{
ParmInfo::Parm pVal;
if (it != ai.parmVals.end())
pVal = *it++;
switch (ParmInfo::DataType(pi.parmType))
{
case ParmInfo::DTInt32:
writer.writeInt32(pVal.int32);
break;
case ParmInfo::DTUInt32:
case ParmInfo::DTEnum:
writer.writeUint32(pVal.uint32);
break;
case ParmInfo::DTFloat:
writer.writeFloat(pVal.float32);
break;
case ParmInfo::DTBool:
writer.writeBool(pVal.bool1);
break;
default: break;
}
}
}
}
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::fromYAML(Athena::io::YAMLDocReader& reader)
{
id = reader.readUint32("id");
atUint32 parmInfoCount = reader.readUint32("parmInfoCount");
atUint32 animInfoCount = reader.readUint32("animInfoCount");
reader.enumerate("parmInfos", parmInfos, parmInfoCount);
reader.enumerate<AnimInfo>("animInfos", animInfos, animInfoCount,
[this, parmInfoCount](Athena::io::YAMLDocReader& reader, AnimInfo& ai)
{
ai.id = reader.readUint32("id");
ai.parmVals.reserve(parmInfoCount);
reader.enterSubVector("parmVals");
for (const ParmInfo& pi : parmInfos)
{
switch (ParmInfo::DataType(pi.parmType))
{
case ParmInfo::DTInt32:
ai.parmVals.emplace_back(reader.readInt32(nullptr));
break;
case ParmInfo::DTUInt32:
case ParmInfo::DTEnum:
ai.parmVals.emplace_back(reader.readUint32(nullptr));
break;
case ParmInfo::DTFloat:
ai.parmVals.emplace_back(reader.readFloat(nullptr));
break;
case ParmInfo::DTBool:
ai.parmVals.emplace_back(reader.readBool(nullptr));
break;
default: break;
}
}
reader.leaveSubVector();
});
}
void ANCS::CharacterSet::CharacterInfo::PASDatabase::AnimState::toYAML(Athena::io::YAMLDocWriter& writer) const
{
writer.writeUint32("id", id);
writer.writeUint32("parmInfoCount", parmInfos.size());
writer.writeUint32("animInfoCount", animInfos.size());
writer.enumerate("parmInfos", parmInfos);
writer.enumerate<AnimInfo>("animInfos", animInfos,
[this](Athena::io::YAMLDocWriter& writer, const AnimInfo& ai)
{
writer.writeUint32("id", ai.id);
auto it = ai.parmVals.begin();
writer.enterSubVector("parms");
for (const ParmInfo& pi : parmInfos)
{
ParmInfo::Parm pVal;
if (it != ai.parmVals.end())
pVal = *it++;
switch (ParmInfo::DataType(pi.parmType))
{
case ParmInfo::DTInt32:
writer.writeInt32(nullptr, pVal.int32);
break;
case ParmInfo::DTUInt32:
case ParmInfo::DTEnum:
writer.writeUint32(nullptr, pVal.uint32);
break;
case ParmInfo::DTFloat:
writer.writeFloat(nullptr, pVal.float32);
break;
case ParmInfo::DTBool:
writer.writeBool(nullptr, pVal.bool1);
break;
default: break;
}
}
writer.leaveSubVector();
});
}
void ANCS::CharacterSet::CharacterInfo::read(Athena::io::IStreamReader& reader)
{
idx = reader.readUint32();
atUint16 sectionCount = reader.readUint16();
name = reader.readString();
cmdl.read(reader);
cskr.read(reader);
cinf.read(reader);
atUint32 animationCount = reader.readUint32();
reader.enumerate(animations, animationCount);
pasDatabase.read(reader);
atUint32 partCount = reader.readUint32();
reader.enumerate(partResData.part, partCount);
atUint32 swhcCount = reader.readUint32();
reader.enumerate(partResData.swhc, swhcCount);
atUint32 unkCount = reader.readUint32();
reader.enumerate(partResData.unk, unkCount);
partResData.elsc.clear();
if (sectionCount > 5)
{
atUint32 elscCount = reader.readUint32();
reader.enumerate(partResData.elsc, elscCount);
}
unk1 = reader.readUint32();
if (sectionCount > 9)
{
unk2 = reader.readUint32();
unk3 = reader.readUint32();
}
animAABBs.clear();
if (sectionCount > 1)
{
atUint32 aabbCount = reader.readUint32();
reader.enumerate(animAABBs, aabbCount);
}
effects.clear();
if (sectionCount > 2)
{
atUint32 effectCount = reader.readUint32();
reader.enumerate(effects, effectCount);
}
if (sectionCount > 3)
{
cmdlOverride.read(reader);
cskrOverride.read(reader);
}
animIdxs.clear();
if (sectionCount > 4)
{
atUint32 aidxCount = reader.readUint32();
reader.enumerate(animIdxs, aidxCount);
}
}
void ANCS::CharacterSet::CharacterInfo::write(Athena::io::IStreamWriter& writer) const
{
writer.writeUint32(idx);
atUint16 sectionCount;
if (unk2 || unk3)
sectionCount = 10;
else if (partResData.elsc.size())
sectionCount = 6;
else if (animIdxs.size())
sectionCount = 5;
else if (cmdlOverride)
sectionCount = 4;
else if (effects.size())
sectionCount = 3;
else if (animAABBs.size())
sectionCount = 2;
else
sectionCount = 1;
writer.writeUint16(sectionCount);
writer.writeString(name);
cmdl.write(writer);
cskr.write(writer);
cinf.write(writer);
writer.writeUint32(animations.size());
writer.enumerate(animations);
pasDatabase.write(writer);
writer.writeUint32(partResData.part.size());
writer.enumerate(partResData.part);
writer.writeUint32(partResData.swhc.size());
writer.enumerate(partResData.swhc);
writer.writeUint32(partResData.unk.size());
writer.enumerate(partResData.unk);
if (sectionCount > 5)
{
writer.writeUint32(partResData.elsc.size());
writer.enumerate(partResData.elsc);
}
writer.writeUint32(unk1);
if (sectionCount > 9)
{
writer.writeUint32(unk2);
writer.writeUint32(unk3);
}
if (sectionCount > 1)
{
writer.writeUint32(animAABBs.size());
writer.enumerate(animAABBs);
}
if (sectionCount > 2)
{
writer.writeUint32(effects.size());
writer.enumerate(effects);
}
if (sectionCount > 3)
{
cmdlOverride.write(writer);
cskrOverride.write(writer);
}
if (sectionCount > 4)
{
writer.writeUint32(animIdxs.size());
for (atUint32 idx : animIdxs)
writer.writeUint32(idx);
}
}
void ANCS::CharacterSet::CharacterInfo::fromYAML(Athena::io::YAMLDocReader& reader)
{
idx = reader.readUint32("idx");
atUint16 sectionCount = reader.readUint16("sectionCount");
name = reader.readString("name");
reader.enumerate("cmdl", cmdl);
reader.enumerate("cskr", cskr);
reader.enumerate("cinf", cinf);
atUint32 animationCount = reader.readUint32("animationCount");
reader.enumerate("animations", animations, animationCount);
reader.enumerate("pasDatabase", pasDatabase);
atUint32 partCount = reader.readUint32("partCount");
reader.enumerate("part", partResData.part, partCount);
atUint32 swhcCount = reader.readUint32("swhcCount");
reader.enumerate("swhc", partResData.swhc, swhcCount);
atUint32 unkCount = reader.readUint32("unkCount");
reader.enumerate("unk", partResData.unk, unkCount);
partResData.elsc.clear();
if (sectionCount > 5)
{
atUint32 elscCount = reader.readUint32("elscCount");
reader.enumerate("elsc", partResData.elsc, elscCount);
}
unk1 = reader.readUint32("unk1");
if (sectionCount > 9)
{
unk2 = reader.readUint32("unk2");
unk3 = reader.readUint32("unk3");
}
animAABBs.clear();
if (sectionCount > 1)
{
atUint32 aabbCount = reader.readUint32("animAABBCount");
reader.enumerate("part", animAABBs, aabbCount);
}
effects.clear();
if (sectionCount > 2)
{
atUint32 effectCount = reader.readUint32("effectCount");
reader.enumerate("effects", effects, effectCount);
}
if (sectionCount > 3)
{
reader.enumerate("cmdlOverride", cmdlOverride);
reader.enumerate("cskrOverride", cskrOverride);
}
animIdxs.clear();
if (sectionCount > 4)
{
atUint32 animIdxCount = reader.readUint32("animIdxCount");
reader.enumerate("animIdxs", animIdxs, animIdxCount);
}
}
void ANCS::CharacterSet::CharacterInfo::toYAML(Athena::io::YAMLDocWriter& writer) const
{
writer.writeUint32("idx", idx);
atUint16 sectionCount;
if (unk2 || unk3)
sectionCount = 10;
else if (partResData.elsc.size())
sectionCount = 6;
else if (animIdxs.size())
sectionCount = 5;
else if (cmdlOverride)
sectionCount = 4;
else if (effects.size())
sectionCount = 3;
else if (animAABBs.size())
sectionCount = 2;
else
sectionCount = 1;
writer.writeUint16("sectionCount", sectionCount);
writer.writeString("name", name);
writer.enumerate("cmdl", cmdl);
writer.enumerate("cskr", cskr);
writer.enumerate("cinf", cinf);
writer.writeUint32("animationCount", animations.size());
writer.enumerate("animations", animations);
writer.enumerate("pasDatabase", pasDatabase);
writer.writeUint32("partCount", partResData.part.size());
writer.enumerate("part", partResData.part);
writer.writeUint32("swhcCount", partResData.swhc.size());
writer.enumerate("swhc", partResData.swhc);
writer.writeUint32("unkCount", partResData.unk.size());
writer.enumerate("unk", partResData.unk);
if (sectionCount > 5)
{
writer.writeUint32("elscCount", partResData.elsc.size());
writer.enumerate("elsc", partResData.elsc);
}
writer.writeUint32("unk1", unk1);
if (sectionCount > 9)
{
writer.writeUint32("unk2", unk2);
writer.writeUint32("unk3", unk3);
}
if (sectionCount > 1)
{
writer.writeUint32("animAABBCount", animAABBs.size());
writer.enumerate("animAABBs", animAABBs);
}
if (sectionCount > 2)
{
writer.writeUint32("effectCount", effects.size());
writer.enumerate("effects", effects);
}
if (sectionCount > 3)
{
writer.enumerate("cmdlOverride", cmdlOverride);
writer.enumerate("cskrOverride", cskrOverride);
}
if (sectionCount > 4)
{
writer.writeUint32("animIdxCount", animIdxs.size());
writer.enumerate("animIdxs", animIdxs);
}
}
void ANCS::AnimationSet::MetaAnimFactory::read(Athena::io::IStreamReader& reader)
{
IMetaAnim::Type type(IMetaAnim::Type(reader.readUint32()));
switch (type)
{
case IMetaAnim::MAPrimitive:
m_anim.reset(new struct MetaAnimPrimitive);
m_anim->read(reader);
break;
case IMetaAnim::MABlend:
m_anim.reset(new struct MetaAnimBlend);
m_anim->read(reader);
break;
case IMetaAnim::MAPhaseBlend:
m_anim.reset(new struct MetaAnimPhaseBlend);
m_anim->read(reader);
break;
case IMetaAnim::MARandom:
m_anim.reset(new struct MetaAnimRandom);
m_anim->read(reader);
break;
case IMetaAnim::MASequence:
m_anim.reset(new struct MetaAnimSequence);
m_anim->read(reader);
break;
default:
m_anim.reset(nullptr);
break;
}
}
void ANCS::AnimationSet::MetaAnimFactory::write(Athena::io::IStreamWriter& writer) const
{
if (!m_anim)
return;
writer.writeInt32(m_anim->m_type);
m_anim->write(writer);
}
void ANCS::AnimationSet::MetaAnimFactory::fromYAML(Athena::io::YAMLDocReader& reader)
{
std::string type = reader.readString("type");
std::transform(type.begin(), type.end(), type.begin(), tolower);
if (!type.compare("primitive"))
{
m_anim.reset(new struct MetaAnimPrimitive);
m_anim->fromYAML(reader);
}
else if (!type.compare("blend"))
{
m_anim.reset(new struct MetaAnimBlend);
m_anim->fromYAML(reader);
}
else if (!type.compare("phaseblend"))
{
m_anim.reset(new struct MetaAnimPhaseBlend);
m_anim->fromYAML(reader);
}
else if (!type.compare("random"))
{
m_anim.reset(new struct MetaAnimRandom);
m_anim->fromYAML(reader);
}
else if (!type.compare("sequence"))
{
m_anim.reset(new struct MetaAnimSequence);
m_anim->fromYAML(reader);
}
else
{
m_anim.reset(nullptr);
}
}
void ANCS::AnimationSet::MetaAnimFactory::toYAML(Athena::io::YAMLDocWriter& writer) const
{
if (!m_anim)
return;
writer.writeString("type", m_anim->m_typeStr);
m_anim->toYAML(writer);
}
void ANCS::AnimationSet::MetaTransFactory::read(Athena::io::IStreamReader& reader)
{
IMetaTrans::Type type(IMetaTrans::Type(reader.readUint32()));
switch (type)
{
case IMetaTrans::MTMetaAnim:
m_trans.reset(new struct MetaTransMetaAnim);
m_trans->read(reader);
break;
case IMetaTrans::MTTrans:
m_trans.reset(new struct MetaTransTrans);
m_trans->read(reader);
break;
case IMetaTrans::MTPhaseTrans:
m_trans.reset(new struct MetaTransPhaseTrans);
m_trans->read(reader);
break;
case IMetaTrans::MTNoTrans:
default:
m_trans.reset(nullptr);
break;
}
}
void ANCS::AnimationSet::MetaTransFactory::write(Athena::io::IStreamWriter& writer) const
{
if (!m_trans)
{
writer.writeInt32(IMetaTrans::MTNoTrans);
return;
}
writer.writeInt32(m_trans->m_type);
m_trans->write(writer);
}
void ANCS::AnimationSet::MetaTransFactory::fromYAML(Athena::io::YAMLDocReader& reader)
{
std::string type = reader.readString("type");
std::transform(type.begin(), type.end(), type.begin(), tolower);
if (!type.compare("metaanim"))
{
m_trans.reset(new struct MetaTransMetaAnim);
m_trans->fromYAML(reader);
}
else if (!type.compare("trans"))
{
m_trans.reset(new struct MetaTransTrans);
m_trans->fromYAML(reader);
}
else if (!type.compare("phasetrans"))
{
m_trans.reset(new struct MetaTransPhaseTrans);
m_trans->fromYAML(reader);
}
else
{
m_trans.reset(nullptr);
}
}
void ANCS::AnimationSet::MetaTransFactory::toYAML(Athena::io::YAMLDocWriter& writer) const
{
if (!m_trans)
{
writer.writeString("type", "NoTrans");
return;
}
writer.writeString("type", m_trans->m_typeStr?m_trans->m_typeStr:"NoTrans");
m_trans->toYAML(writer);
}
void ANCS::AnimationSet::read(Athena::io::IStreamReader& reader)
{
atUint16 sectionCount = reader.readUint16();
atUint32 animationCount = reader.readUint32();
reader.enumerate(animations, animationCount);
atUint32 transitionCount = reader.readUint32();
reader.enumerate(transitions, transitionCount);
defaultTransition.read(reader);
additiveAnims.clear();
if (sectionCount > 1)
{
atUint32 additiveAnimCount = reader.readUint32();
reader.enumerate(additiveAnims, additiveAnimCount);
floatA = reader.readFloat();
floatB = reader.readFloat();
}
halfTransitions.clear();
if (sectionCount > 2)
{
atUint32 halfTransitionCount = reader.readUint32();
reader.enumerate(halfTransitions, halfTransitionCount);
}
animResources.clear();
if (sectionCount > 3)
{
atUint32 animResourcesCount = reader.readUint32();
reader.enumerate(animResources, animResourcesCount);
}
}
void ANCS::AnimationSet::write(Athena::io::IStreamWriter& writer) const
{
atUint16 sectionCount;
if (animResources.size())
sectionCount = 4;
else if (halfTransitions.size())
sectionCount = 3;
else if (additiveAnims.size())
sectionCount = 2;
else
sectionCount = 1;
writer.writeUint16(sectionCount);
writer.writeUint32(animations.size());
writer.enumerate(animations);
writer.writeUint32(transitions.size());
writer.enumerate(transitions);
defaultTransition.write(writer);
if (sectionCount > 1)
{
writer.writeUint32(additiveAnims.size());
writer.enumerate(additiveAnims);
writer.writeFloat(floatA);
writer.writeFloat(floatB);
}
if (sectionCount > 2)
{
writer.writeUint32(halfTransitions.size());
writer.enumerate(halfTransitions);
}
if (sectionCount > 3)
{
writer.writeUint32(animResources.size());
writer.enumerate(animResources);
}
}
void ANCS::AnimationSet::fromYAML(Athena::io::YAMLDocReader& reader)
{
atUint16 sectionCount = reader.readUint16("sectionCount");
atUint32 animationCount = reader.readUint32("animationCount");
reader.enumerate("animations", animations, animationCount);
atUint32 transitionCount = reader.readUint32("transitionCount");
reader.enumerate("transitions", transitions, transitionCount);
reader.enumerate("defaultTransition", defaultTransition);
additiveAnims.clear();
if (sectionCount > 1)
{
atUint32 additiveAnimCount = reader.readUint32("additiveAnimCount");
reader.enumerate("additiveAnims", additiveAnims, additiveAnimCount);
floatA = reader.readFloat("floatA");
floatB = reader.readFloat("floatB");
}
halfTransitions.clear();
if (sectionCount > 2)
{
atUint32 halfTransitionCount = reader.readUint32("halfTransitionCount");
reader.enumerate("halfTransitions", halfTransitions, halfTransitionCount);
}
animResources.clear();
if (sectionCount > 3)
{
atUint32 animResourcesCount = reader.readUint32("animResourcesCount");
reader.enumerate("animResources", animResources, animResourcesCount);
}
}
void ANCS::AnimationSet::toYAML(Athena::io::YAMLDocWriter& writer) const
{
atUint16 sectionCount;
if (animResources.size())
sectionCount = 4;
else if (halfTransitions.size())
sectionCount = 3;
else if (additiveAnims.size())
sectionCount = 2;
else
sectionCount = 1;
writer.writeUint16("sectionCount", sectionCount);
writer.writeUint32("animationCount", animations.size());
writer.enumerate("animations", animations);
writer.writeUint32("transitionCount", transitions.size());
writer.enumerate("transitions", transitions);
writer.enumerate("defaultTransition", defaultTransition);
if (sectionCount > 1)
{
writer.writeUint32("additiveAnimCount", additiveAnims.size());
writer.enumerate("additiveAnims", additiveAnims);
writer.writeFloat("floatA", floatA);
writer.writeFloat("floatB", floatB);
}
if (sectionCount > 2)
{
writer.writeUint32("halfTransitionCount", halfTransitions.size());
writer.enumerate("halfTransitions", halfTransitions);
}
if (sectionCount > 3)
{
writer.writeUint32("animResourcesCount", animResources.size());
writer.enumerate("animResources", animResources);
}
}
}
}

392
DataSpec/DNAMP1/ANCS.hpp Normal file
View File

@ -0,0 +1,392 @@
#ifndef _DNAMP1_ANCS_HPP_
#define _DNAMP1_ANCS_HPP_
#include <unordered_set>
#include "../DNACommon/DNACommon.hpp"
#include "../DNACommon/ANCS.hpp"
#include "CMDLMaterials.hpp"
#include "BlenderConnection.hpp"
namespace Retro
{
namespace DNAMP1
{
struct ANCS : BigYAML
{
DECL_YAML
Value<atUint16> version;
struct CharacterSet : BigYAML
{
DECL_YAML
Value<atUint16> version;
Value<atUint32> characterCount;
struct CharacterInfo : BigYAML
{
DECL_YAML
Delete expl;
atUint32 idx;
std::string name;
UniqueID32 cmdl;
UniqueID32 cskr;
UniqueID32 cinf;
struct Animation : BigYAML
{
DECL_YAML
Value<atUint32> animIdx;
String<-1> strA;
String<-1> strB;
};
std::vector<Animation> animations;
struct PASDatabase : BigYAML
{
DECL_YAML
Value<atUint32> magic;
Value<atUint32> animStateCount;
Value<atUint32> defaultState;
struct AnimState : BigYAML
{
DECL_YAML
Delete expl;
atUint32 id;
struct ParmInfo : BigYAML
{
DECL_YAML
Delete expl;
enum DataType
{
DTInt32 = 0,
DTUInt32 = 1,
DTFloat = 2,
DTBool = 3,
DTEnum = 4
};
union Parm
{
atInt32 int32;
atUint32 uint32;
float float32;
bool bool1;
Parm() : int32(0) {}
Parm(atInt32 val) : int32(val) {}
Parm(atUint32 val) : uint32(val) {}
Parm(float val) : float32(val) {}
Parm(bool val) : bool1(val) {}
};
atUint32 parmType;
atUint32 unk1;
float unk2;
Parm parmVals[2];
};
std::vector<ParmInfo> parmInfos;
struct AnimInfo
{
atUint32 id;
std::vector<ParmInfo::Parm> parmVals;
};
std::vector<AnimInfo> animInfos;
};
Vector<AnimState, DNA_COUNT(animStateCount)> animStates;
} pasDatabase;
struct ParticleResData
{
std::vector<UniqueID32> part;
std::vector<UniqueID32> swhc;
std::vector<UniqueID32> unk;
std::vector<UniqueID32> elsc;
} partResData;
atUint32 unk1 = 0;
atUint32 unk2 = 0;
atUint32 unk3 = 0;
struct ActionAABB : BigYAML
{
DECL_YAML
String<-1> name;
Value<atVec3f> aabb[2];
};
std::vector<ActionAABB> animAABBs;
struct Effect : BigYAML
{
DECL_YAML
String<-1> name;
Value<atUint32> compCount;
struct EffectComponent : BigYAML
{
DECL_YAML
String<-1> name;
FourCC type;
UniqueID32 id;
String<-1> name2;
Value<float> unk1;
Value<atUint32> unk2;
Value<atUint32> unk3;
};
Vector<EffectComponent, DNA_COUNT(compCount)> comps;
};
std::vector<Effect> effects;
UniqueID32 cmdlOverride;
UniqueID32 cskrOverride;
std::vector<atUint32> animIdxs;
};
Vector<CharacterInfo, DNA_COUNT(characterCount)> characters;
} characterSet;
struct AnimationSet : BigYAML
{
DECL_YAML
Delete expl;
struct IMetaAnim : BigYAML
{
Delete expl;
enum Type
{
MAPrimitive = 0,
MABlend = 1,
MAPhaseBlend = 2,
MARandom = 3,
MASequence = 4
} m_type;
const char* m_typeStr;
IMetaAnim(Type type, const char* typeStr)
: m_type(type), m_typeStr(typeStr) {}
virtual void gatherPrimitives(std::unordered_set<UniqueID32>& out)=0;
};
struct MetaAnimFactory : BigYAML
{
DECL_YAML
Delete expl;
std::unique_ptr<IMetaAnim> m_anim;
};
struct MetaAnimPrimitive : IMetaAnim
{
MetaAnimPrimitive() : IMetaAnim(MAPrimitive, "Primitive") {}
DECL_YAML
UniqueID32 animId;
Value<atUint32> animIdx;
String<-1> animName;
Value<float> unk1;
Value<atUint32> unk2;
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
{
out.insert(animId);
}
};
struct MetaAnimBlend : IMetaAnim
{
MetaAnimBlend() : IMetaAnim(MABlend, "Blend") {}
DECL_YAML
MetaAnimFactory animA;
MetaAnimFactory animB;
Value<float> unkFloat;
Value<atUint8> unk;
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
{
animA.m_anim->gatherPrimitives(out);
animB.m_anim->gatherPrimitives(out);
}
};
struct MetaAnimPhaseBlend : IMetaAnim
{
MetaAnimPhaseBlend() : IMetaAnim(MAPhaseBlend, "PhaseBlend") {}
DECL_YAML
MetaAnimFactory animA;
MetaAnimFactory animB;
Value<float> unkFloat;
Value<atUint8> unk;
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
{
animA.m_anim->gatherPrimitives(out);
animB.m_anim->gatherPrimitives(out);
}
};
struct MetaAnimRandom : IMetaAnim
{
MetaAnimRandom() : IMetaAnim(MARandom, "Random") {}
DECL_YAML
Value<atUint32> animCount;
struct Child : BigYAML
{
DECL_YAML
MetaAnimFactory anim;
Value<atUint32> probability;
};
Vector<Child, DNA_COUNT(animCount)> children;
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
{
for (const auto& child : children)
child.anim.m_anim->gatherPrimitives(out);
}
};
struct MetaAnimSequence : IMetaAnim
{
MetaAnimSequence() : IMetaAnim(MASequence, "Sequence") {}
DECL_YAML
Value<atUint32> animCount;
Vector<MetaAnimFactory, DNA_COUNT(animCount)> children;
void gatherPrimitives(std::unordered_set<UniqueID32>& out)
{
for (const auto& child : children)
child.m_anim->gatherPrimitives(out);
}
};
struct Animation : BigYAML
{
DECL_YAML
String<-1> name;
MetaAnimFactory metaAnim;
};
std::vector<Animation> animations;
struct IMetaTrans : BigYAML
{
Delete expl;
enum Type
{
MTMetaAnim = 0,
MTTrans = 1,
MTPhaseTrans = 2,
MTNoTrans = 3,
} m_type;
const char* m_typeStr;
IMetaTrans(Type type, const char* typeStr)
: m_type(type), m_typeStr(typeStr) {}
};
struct MetaTransFactory : BigYAML
{
DECL_YAML
Delete expl;
std::unique_ptr<IMetaTrans> m_trans;
};
struct MetaTransMetaAnim : IMetaTrans
{
MetaTransMetaAnim() : IMetaTrans(MTMetaAnim, "MetaAnim") {}
DECL_YAML
MetaAnimFactory anim;
};
struct MetaTransTrans : IMetaTrans
{
MetaTransTrans() : IMetaTrans(MTTrans, "Trans") {}
DECL_YAML
Value<float> time;
Value<atUint32> unk1;
Value<atUint8> unk2;
Value<atUint8> unk3;
Value<atUint32> unk4;
};
struct MetaTransPhaseTrans : IMetaTrans
{
MetaTransPhaseTrans() : IMetaTrans(MTPhaseTrans, "PhaseTrans") {}
DECL_YAML
Value<float> time;
Value<atUint32> unk1;
Value<atUint8> unk2;
Value<atUint8> unk3;
Value<atUint32> unk4;
};
struct Transition : BigYAML
{
DECL_YAML
Value<atUint32> unk;
Value<atUint32> animIdxA;
Value<atUint32> animIdxB;
MetaTransFactory metaTrans;
};
std::vector<Transition> transitions;
MetaTransFactory defaultTransition;
struct AdditiveAnimationInfo : BigYAML
{
DECL_YAML
Value<atUint32> animIdx;
Value<float> unk1;
Value<float> unk2;
};
std::vector<AdditiveAnimationInfo> additiveAnims;
float floatA = 0.0;
float floatB = 0.0;
struct HalfTransition : BigYAML
{
DECL_YAML
Value<atUint32> animIdx;
MetaTransFactory metaTrans;
};
std::vector<HalfTransition> halfTransitions;
struct AnimationResources : BigYAML
{
DECL_YAML
UniqueID32 animId;
UniqueID32 evntId;
};
std::vector<AnimationResources> animResources;
} animationSet;
void getCharacterResInfo(std::vector<DNAANCS::CharacterResInfo<UniqueID32>>& out) const
{
out.clear();
out.reserve(characterSet.characters.size());
for (const CharacterSet::CharacterInfo& ci : characterSet.characters)
{
out.emplace_back();
DNAANCS::CharacterResInfo<UniqueID32>& chOut = out.back();
chOut.name = ci.name;
chOut.cmdl = ci.cmdl;
chOut.cskr = ci.cskr;
chOut.cinf = ci.cinf;
}
}
void getAnimationResInfo(std::unordered_set<UniqueID32>& out) const
{
out.clear();
for (const AnimationSet::Animation& ai : animationSet.animations)
ai.metaAnim.m_anim->gatherPrimitives(out);
}
static bool Extract(const SpecBase& dataSpec,
PAKEntryReadStream& rs,
const HECL::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry,
bool force)
{
ANCS ancs;
ancs.read(rs);
FILE* fp = HECL::Fopen((outPath.getAbsolutePath() + ".yaml").c_str(), _S("wb"));
ancs.toYAMLFile(fp);
fclose(fp);
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
DNAANCS::ReadANCSToBlender<PAKRouter<PAKBridge>, ANCS, MaterialSet, 2>
(conn, ancs, outPath, pakRouter, entry, dataSpec.getMasterShaderPath(), force);
return conn.saveBlend();
}
};
}
}
#endif // _DNAMP1_ANCS_HPP_

290
DataSpec/DNAMP1/ANIM.cpp Normal file
View File

@ -0,0 +1,290 @@
#include "ANIM.hpp"
namespace Retro
{
namespace DNAMP1
{
void ANIM::ANIM0::read(Athena::io::IStreamReader& reader)
{
Header head;
head.read(reader);
mainInterval = head.interval;
times.clear();
times.reserve(head.keyCount);
float timeAccum = 0.0;
for (size_t k=0 ; k<head.keyCount ; ++k)
{
times.push_back(timeAccum);
timeAccum += head.interval;
}
std::map<atUint8, atUint32> boneMap;
for (size_t b=0 ; b<head.boneSlotCount ; ++b)
{
atUint8 idx = reader.readUByte();
if (idx == 0xff)
continue;
boneMap[idx] = b;
}
atUint32 boneCount = reader.readUint32();
bones.clear();
bones.reserve(boneCount);
channels.clear();
for (size_t b=0 ; b<boneCount ; ++b)
{
bones.emplace_back(boneMap[b], false);
atUint8 idx = reader.readUByte();
channels.emplace_back();
DNAANIM::Channel& chan = channels.back();
chan.type = DNAANIM::Channel::ROTATION;
if (idx != 0xff)
{
bones.back().second = true;
channels.emplace_back();
DNAANIM::Channel& chan = channels.back();
chan.type = DNAANIM::Channel::TRANSLATION;
}
}
reader.readUint32();
chanKeys.clear();
chanKeys.reserve(channels.size());
for (const std::pair<atUint32, bool>& bone : bones)
{
chanKeys.emplace_back();
std::vector<DNAANIM::Value>& keys = chanKeys.back();
for (size_t k=0 ; k<head.keyCount ; ++k)
keys.emplace_back(reader.readVec4f());
if (bone.second)
chanKeys.emplace_back();
}
reader.readUint32();
auto kit = chanKeys.begin();
for (const std::pair<atUint32, bool>& bone : bones)
{
++kit;
if (bone.second)
{
std::vector<DNAANIM::Value>& keys = *kit++;
for (size_t k=0 ; k<head.keyCount ; ++k)
keys.emplace_back(reader.readVec3f());
}
}
evnt.read(reader);
}
void ANIM::ANIM0::write(Athena::io::IStreamWriter& writer) const
{
Header head;
head.unk0 = 0;
head.unk1 = 0;
head.unk2 = 0;
head.keyCount = times.size();
head.duration = head.keyCount * mainInterval;
head.interval = mainInterval;
atUint32 maxId = 0;
for (const std::pair<atUint32, bool>& bone : bones)
maxId = MAX(maxId, bone.first);
head.boneSlotCount = maxId + 1;
head.write(writer);
for (size_t s=0 ; s<head.boneSlotCount ; ++s)
{
size_t boneIdx = 0;
bool found = false;
for (const std::pair<atUint32, bool>& bone : bones)
{
if (s == bone.first)
{
writer.writeUByte(boneIdx);
found = true;
break;
}
++boneIdx;
}
if (!found)
writer.writeUByte(0xff);
}
writer.writeUint32(bones.size());
size_t boneIdx = 0;
for (const std::pair<atUint32, bool>& bone : bones)
{
if (bone.second)
writer.writeUByte(boneIdx);
else
writer.writeUByte(0xff);
++boneIdx;
}
writer.writeUint32(bones.size() * head.keyCount);
auto cit = chanKeys.begin();
atUint32 transKeyCount = 0;
for (const std::pair<atUint32, bool>& bone : bones)
{
const std::vector<DNAANIM::Value>& keys = *cit++;
auto kit = keys.begin();
for (size_t k=0 ; k<head.keyCount ; ++k)
writer.writeVec4f((*kit++).v4);
if (bone.second)
{
transKeyCount += head.keyCount;
++cit;
}
}
writer.writeUint32(transKeyCount);
cit = chanKeys.begin();
for (const std::pair<atUint32, bool>& bone : bones)
{
++cit;
if (bone.second)
{
const std::vector<DNAANIM::Value>& keys = *cit++;
auto kit = keys.begin();
for (size_t k=0 ; k<head.keyCount ; ++k)
writer.writeVec3f((*kit++).v3);
}
}
evnt.write(writer);
}
void ANIM::ANIM2::read(Athena::io::IStreamReader& reader)
{
Header head;
head.read(reader);
evnt = head.evnt;
mainInterval = head.interval;
WordBitmap keyBmp;
keyBmp.read(reader, head.keyBitmapBitCount);
times.clear();
float timeAccum = 0.0;
for (bool bit : keyBmp)
{
if (bit)
times.push_back(timeAccum);
timeAccum += head.interval;
}
bones.clear();
bones.reserve(head.boneChannelCount);
channels.clear();
channels.reserve(head.boneChannelCount);
size_t keyframeCount = 0;
for (size_t b=0 ; b<head.boneChannelCount ; ++b)
{
ChannelDesc desc;
desc.read(reader);
bones.emplace_back(desc.id, desc.keyCount2);
if (desc.keyCount1)
{
channels.emplace_back();
DNAANIM::Channel& chan = channels.back();
chan.type = DNAANIM::Channel::ROTATION;
chan.i[0] = desc.initRX;
chan.q[0] = desc.qRX;
chan.i[1] = desc.initRY;
chan.q[1] = desc.qRY;
chan.i[2] = desc.initRZ;
chan.q[2] = desc.qRZ;
}
keyframeCount = MAX(keyframeCount, desc.keyCount1);
if (desc.keyCount2)
{
channels.emplace_back();
DNAANIM::Channel& chan = channels.back();
chan.type = DNAANIM::Channel::TRANSLATION;
chan.i[0] = desc.initTX;
chan.q[0] = desc.qTX;
chan.i[1] = desc.initTY;
chan.q[1] = desc.qTY;
chan.i[2] = desc.initTZ;
chan.q[2] = desc.qTZ;
}
}
size_t bsSize = DNAANIM::ComputeBitstreamSize(keyframeCount, channels);
std::unique_ptr<atUint8[]> bsData = reader.readUBytes(bsSize);
DNAANIM::BitstreamReader bsReader;
chanKeys = bsReader.read(bsData.get(), keyframeCount, channels, head.rotDiv, head.translationMult);
}
void ANIM::ANIM2::write(Athena::io::IStreamWriter& writer) const
{
Header head;
head.evnt = evnt;
head.unk0 = 1;
head.interval = mainInterval;
head.unk1 = 3;
head.unk2 = 0;
head.unk3 = 1;
WordBitmap keyBmp;
size_t frameCount = 0;
for (float time : times)
{
size_t frameIdx = time / mainInterval;
while (keyBmp.getBit(frameIdx))
++frameIdx;
keyBmp.setBit(frameIdx);
frameCount = frameIdx + 1;
}
head.keyBitmapBitCount = frameCount;
head.duration = frameCount * mainInterval;
head.boneChannelCount = bones.size();
size_t keyframeCount = times.size();
std::vector<DNAANIM::Channel> qChannels = channels;
DNAANIM::BitstreamWriter bsWriter;
size_t bsSize;
std::unique_ptr<atUint8[]> bsData = bsWriter.write(chanKeys, keyframeCount, qChannels,
head.rotDiv, head.translationMult, bsSize);
/* TODO: Figure out proper scratch size computation */
head.scratchSize = keyframeCount * channels.size() * 16;
head.write(writer);
keyBmp.write(writer);
auto cit = qChannels.begin();
for (const std::pair<atUint32, bool>& bone : bones)
{
ChannelDesc desc;
desc.id = bone.first;
DNAANIM::Channel& chan = *cit++;
desc.keyCount1 = keyframeCount;
desc.initRX = chan.i[0];
desc.qRX = chan.q[0];
desc.initRY = chan.i[1];
desc.qRY = chan.q[1];
desc.initRZ = chan.i[2];
desc.qRZ = chan.q[2];
if (bone.second)
{
DNAANIM::Channel& chan = *cit++;
desc.keyCount2 = keyframeCount;
desc.initTX = chan.i[0];
desc.qTX = chan.q[0];
desc.initTY = chan.i[1];
desc.qTY = chan.q[1];
desc.initTZ = chan.i[2];
desc.qTZ = chan.q[2];
}
desc.write(writer);
}
writer.writeUBytes(bsData.get(), bsSize);
}
}
}

164
DataSpec/DNAMP1/ANIM.hpp Normal file
View File

@ -0,0 +1,164 @@
#ifndef _DNAMP1_ANIM_HPP_
#define _DNAMP1_ANIM_HPP_
#include "DNAMP1.hpp"
#include "../DNACommon/ANIM.hpp"
namespace Retro
{
namespace DNAMP1
{
struct ANIM : BigDNA
{
Delete expl;
struct IANIM : BigDNA
{
Delete expl;
atUint32 m_version;
IANIM(atUint32 version) : m_version(version) {}
std::vector<std::pair<atUint32, bool>> bones;
std::vector<float> times;
std::vector<DNAANIM::Channel> channels;
std::vector<std::vector<DNAANIM::Value>> chanKeys;
float mainInterval = 0.0;
UniqueID32 evnt;
};
struct ANIM0 : IANIM
{
DECL_EXPLICIT_DNA
ANIM0() : IANIM(0) {}
struct Header : BigDNA
{
DECL_DNA
Value<float> duration;
Value<atUint32> unk0;
Value<float> interval;
Value<atUint32> unk1;
Value<atUint32> boneSlotCount;
Value<atUint32> unk2;
Value<atUint32> keyCount;
};
};
struct ANIM2 : IANIM
{
DECL_EXPLICIT_DNA
ANIM2() : IANIM(2) {}
struct Header : BigDNA
{
DECL_DNA
Value<atUint32> scratchSize;
UniqueID32 evnt;
Value<atUint32> unk0;
Value<float> duration;
Value<float> interval;
Value<atUint32> unk1;
Value<atUint32> unk2;
Value<atUint32> rotDiv;
Value<float> translationMult;
Value<atUint32> boneChannelCount;
Value<atUint32> unk3;
Value<atUint32> keyBitmapBitCount;
};
struct ChannelDesc : BigDNA
{
Delete expl;
Value<atUint32> id = 0;
Value<atUint16> keyCount1 = 0;
Value<atUint16> initRX = 0;
Value<atUint8> qRX = 0;
Value<atUint16> initRY = 0;
Value<atUint8> qRY = 0;
Value<atUint16> initRZ = 0;
Value<atUint8> qRZ = 0;
Value<atUint16> keyCount2 = 0;
Value<atUint16> initTX = 0;
Value<atUint8> qTX = 0;
Value<atUint16> initTY = 0;
Value<atUint8> qTY = 0;
Value<atUint16> initTZ = 0;
Value<atUint8> qTZ = 0;
void read(Athena::io::IStreamReader& reader)
{
id = reader.readUint32();
keyCount1 = reader.readUint16();
initRX = reader.readUint16();
qRX = reader.readUByte();
initRY = reader.readUint16();
qRY = reader.readUByte();
initRZ = reader.readUint16();
qRZ = reader.readUByte();
keyCount2 = reader.readUint16();
if (keyCount2)
{
initTX = reader.readUint16();
qTX = reader.readUByte();
initTY = reader.readUint16();
qTY = reader.readUByte();
initTZ = reader.readUint16();
qTZ = reader.readUByte();
}
}
void write(Athena::io::IStreamWriter& writer) const
{
writer.writeUint32(id);
writer.writeUint16(keyCount1);
writer.writeUint16(initRX);
writer.writeUByte(qRX);
writer.writeUint16(initRY);
writer.writeUByte(qRY);
writer.writeUint16(initRZ);
writer.writeUByte(qRZ);
writer.writeUint16(keyCount2);
if (keyCount2)
{
writer.writeUint16(initTX);
writer.writeUByte(qTX);
writer.writeUint16(initTY);
writer.writeUByte(qTY);
writer.writeUint16(initTZ);
writer.writeUByte(qTZ);
}
}
};
};
std::unique_ptr<IANIM> m_anim;
void read(Athena::io::IStreamReader& reader)
{
atUint32 version = reader.readUint32();
switch (version)
{
case 0:
m_anim.reset(new struct ANIM0);
m_anim->read(reader);
break;
case 2:
m_anim.reset(new struct ANIM2);
m_anim->read(reader);
break;
default:
Log.report(LogVisor::Error, "unrecognized ANIM version");
break;
}
}
void write(Athena::io::IStreamWriter& writer) const
{
writer.writeUint32(m_anim->m_version);
m_anim->write(writer);
}
};
}
}
#endif // _DNAMP1_ANIM_HPP_

42
DataSpec/DNAMP1/CINF.hpp Normal file
View File

@ -0,0 +1,42 @@
#ifndef _DNAMP1_CINF_HPP_
#define _DNAMP1_CINF_HPP_
#include "../DNACommon/DNACommon.hpp"
namespace Retro
{
namespace DNAMP1
{
struct CINF : BigDNA
{
DECL_DNA
Value<atUint32> boneCount;
struct Bone : BigDNA
{
DECL_DNA
Value<atUint32> id;
Value<atUint32> parentId;
Value<atVec3f> origin;
Value<atUint32> linkedCount;
Vector<atUint32, DNA_COUNT(linkedCount)> linked;
};
Vector<Bone, DNA_COUNT(boneCount)> bones;
Value<atUint32> boneIdCount;
Vector<atUint32, DNA_COUNT(boneIdCount)> boneIds;
Value<atUint32> nameCount;
struct Name : BigDNA
{
DECL_DNA
String<-1> name;
Value<atUint32> boneId;
};
Vector<Name, DNA_COUNT(nameCount)> names;
};
}
}
#endif // _DNAMP1_CINF_HPP_

View File

@ -1,144 +0,0 @@
#include <cstddef>
#include "CMDL.hpp"
#include "DNAMP1.hpp"
namespace Retro
{
namespace DNAMP1
{
bool CMDL::ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReader& reader)
{
reader.setEndian(Athena::BigEndian);
CMDL::Header head;
head.read(reader);
if (head.magic != 0xDEADBABE)
{
Log.report(LogVisor::Error, "invalid CMDL magic");
return false;
}
if (head.version != 2)
{
Log.report(LogVisor::Error, "invalid CMDL version for MP1");
return false;
}
/* Open Py Stream */
HECL::BlenderConnection::PyOutStream os = conn.beginPythonOut();
os << "import bmesh\n";
os << "bm = bmesh.new()\n";
for (size_t s=0 ; s<head.secCount ; ++s)
{
atUint64 secStart = reader.position();
std::unique_ptr<atVec3f[]> vertPos;
std::unique_ptr<atVec3f[]> vertNorm;
typedef atInt16 ShortVec3[3];
std::unique_ptr<ShortVec3[]> vertNormShort;
std::unique_ptr<atVec2f[]> vertUVs;
typedef atInt16 ShortVec2[2];
std::unique_ptr<ShortVec2[]> vertUVsShort;
bool visitedDLOffsets = false;
if (s < head.matSetCount)
{
MaterialSet matSet;
matSet.read(reader);
}
else
{
switch (s-head.matSetCount)
{
case 0:
{
/* Positions */
size_t vertCount = head.secSizes[s] / 12;
vertPos.reset(new atVec3f[vertCount]);
for (size_t i=0 ; i<vertCount ; ++i)
vertPos[i] = reader.readVec3f();
break;
}
case 1:
{
/* Normals */
if (head.flags.shortNormals())
{
size_t normCount = head.secSizes[s] / 6;
vertNormShort.reset(new ShortVec3[normCount]);
for (size_t i=0 ; i<normCount ; ++i)
{
vertNormShort[i][0] = reader.readInt16();
vertNormShort[i][1] = reader.readInt16();
vertNormShort[i][2] = reader.readInt16();
}
}
else
{
size_t normCount = head.secSizes[s] / 12;
vertNorm.reset(new atVec3f[normCount]);
for (size_t i=0 ; i<normCount ; ++i)
vertNorm[i] = reader.readVec3f();
}
break;
}
case 2:
{
/* Colors */
break;
}
case 3:
{
/* Float UVs */
size_t uvCount = head.secSizes[s] / 8;
vertUVs.reset(new atVec2f[uvCount]);
for (size_t i=0 ; i<uvCount ; ++i)
vertUVs[i] = reader.readVec2f();
break;
}
case 4:
{
/* Short UVs */
if (head.flags.shortUVs())
{
size_t uvCount = head.secSizes[s] / 4;
vertUVsShort.reset(new ShortVec2[uvCount]);
for (size_t i=0 ; i<uvCount ; ++i)
{
vertUVsShort[i][0] = reader.readInt16();
vertUVsShort[i][1] = reader.readInt16();
}
break;
}
/* DL Offsets (here or next section) */
visitedDLOffsets = true;
break;
}
default:
{
if (!visitedDLOffsets)
{
visitedDLOffsets = true;
break;
}
/* GX Display List (surface) */
SurfaceHeader sHead;
sHead.read(reader);
}
}
}
if (s < head.secCount - 1)
reader.seek(secStart + head.secSizes[s], Athena::Begin);
}
return true;
}
}
}

View File

@ -2,8 +2,9 @@
#define _DNAMP1_CMDL_HPP_
#include "../DNACommon/DNACommon.hpp"
#include "../DNACommon/CMDL.hpp"
#include "CMDLMaterials.hpp"
#include "BlenderConnection.hpp"
#include "DNAMP1.hpp"
namespace Retro
{
@ -12,50 +13,19 @@ namespace DNAMP1
struct CMDL
{
struct Header : BigDNA
{
DECL_DNA
Value<atUint32> magic;
Value<atUint32> version;
struct Flags : BigDNA
{
DECL_DNA
Value<atUint32> flags;
inline bool shortNormals() const {return (flags & 0x2) != 0;}
inline void setShortNormals(bool val) {flags &= ~0x2; flags |= val << 1;}
inline bool shortUVs() const {return (flags & 0x4) != 0;}
inline void setShortUVs(bool val) {flags &= ~0x4; flags |= val << 2;}
} flags;
Value<atVec3f> aabbMin;
Value<atVec3f> aabbMax;
Value<atUint32> secCount;
Value<atUint32> matSetCount;
Vector<atUint32, DNA_COUNT(secCount)> secSizes;
Align<32> align;
};
struct SurfaceHeader : BigDNA
{
DECL_DNA
Value<atVec3f> centroid;
Value<atUint32> matIdx;
Value<atInt16> qDiv;
Value<atUint16> dlSize;
Seek<8, Athena::Current> seek;
Value<atUint32> aabbSz;
Value<atVec3f> reflectionNormal;
Seek<DNA_COUNT(aabbSz), Athena::Current> seek2;
Align<32> align;
};
static bool ReadToBlender(HECL::BlenderConnection& conn, Athena::io::IStreamReader& reader);
static bool Extract(PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
static bool Extract(const SpecBase& dataSpec,
PAKEntryReadStream& rs,
const HECL::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry,
bool force)
{
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
if (!conn.createBlend(outPath.getAbsolutePath()))
return false;
return ReadToBlender(conn, rs);
DNACMDL::ReadCMDLToBlender<PAKRouter<PAKBridge>, MaterialSet, 2>
(conn, rs, pakRouter, entry, dataSpec.getMasterShaderPath());
return conn.saveBlend();
}
};

View File

@ -0,0 +1,729 @@
#include "CMDLMaterials.hpp"
#include "../DNAMP2/CMDLMaterials.hpp"
using Stream = HECL::BlenderConnection::PyOutStream;
namespace Retro
{
namespace DNAMP1
{
void MaterialSet::RegisterMaterialProps(Stream& out)
{
out << "bpy.types.Material.retro_depth_sort = bpy.props.BoolProperty(name='Retro: Transparent Depth Sort')\n"
"bpy.types.Material.retro_punchthrough_alpha = bpy.props.BoolProperty(name='Retro: Punchthrough Alpha')\n"
"bpy.types.Material.retro_samus_reflection = bpy.props.BoolProperty(name='Retro: Samus Reflection')\n"
"bpy.types.Material.retro_depth_write = bpy.props.BoolProperty(name='Retro: Depth Write')\n"
"bpy.types.Material.retro_samus_reflection_persp = bpy.props.BoolProperty(name='Retro: Samus Reflection Perspective')\n"
"bpy.types.Material.retro_shadow_occluder = bpy.props.BoolProperty(name='Retro: Shadow Occluder')\n"
"bpy.types.Material.retro_samus_reflection_indirect = bpy.props.BoolProperty(name='Retro: Samus Reflection Indirect Texture')\n"
"bpy.types.Material.retro_lightmapped = bpy.props.BoolProperty(name='Retro: Lightmapped')\n"
"bpy.types.Material.retro_lightmap = bpy.props.StringProperty(name='Retro: Lightmap')\n"
"bpy.types.Mesh.cmdl_material_count = bpy.props.IntProperty(name='CMDL: Material Count')\n"
"\n";
}
static void AddTexture(Stream& out, GX::TexGenSrc type, int mtxIdx, uint32_t texIdx)
{
char mtxLabel[64];
if (mtxIdx == -1)
strncpy(mtxLabel, "IDENTITY", 64);
else
snprintf(mtxLabel, 64, "MTX_%u", mtxIdx);
out.format("# Texture\n"
"tex_uv_node = new_nodetree.nodes.new('ShaderNodeGeometry')\n"
"tex_uv_node.label = '%s'\n"
"tex_node = new_nodetree.nodes.new('ShaderNodeTexture')\n"
"tex_node.label = 'Texture %u'\n"
"texture_nodes.append(tex_node)\n"
"gridder.place_node(tex_uv_node, 1)\n"
"gridder.place_node(tex_node, 1)\n"
"tex_uv_node.location[0] -= 120\n"
"tex_node.location[0] += 120\n"
"tex_node.location[1] += 176\n", mtxLabel, texIdx);
if (texIdx != 0xff)
out.format("tex_node.texture = tex_maps[%u]\n",
texIdx);
if (type == GX::TG_POS)
out.format("tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['View'], tex_node.inputs['Vector']))\n");
else if (type == GX::TG_NRM)
out.format("tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['Normal'], tex_node.inputs['Vector']))\n");
else if (type >= GX::TG_TEX0 && type <= GX::TG_TEX7) {
uint8_t texIdx = type - GX::TG_TEX0;
out.format("tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['UV'], tex_node.inputs['Vector']))\n"
"tex_uv_node.uv_layer = 'UV_%u'\n", texIdx);
}
out << "\n";
}
static void AddTextureAnim(Stream& out,
MaterialSet::Material::UVAnimation::Mode type,
unsigned idx, const float* vals)
{
switch (type)
{
case MaterialSet::Material::UVAnimation::ANIM_MV_INV_NOTRANS:
out.format("for link in list(tex_links):\n"
" if link.from_node.label == 'MTX_%u':\n"
" tex_links.remove(link)\n"
" soc_from = link.from_socket\n"
" soc_to = link.to_socket\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node.node_tree = bpy.data.node_groups['RWKUVMode0Node']\n"
" node.location[0] = link.from_node.location[0] + 50\n"
" node.location[1] = link.from_node.location[1] - 50\n"
" new_nodetree.links.remove(link)\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n",
idx);
break;
case MaterialSet::Material::UVAnimation::ANIM_MV_INV:
out.format("for link in list(tex_links):\n"
" if link.from_node.label == 'MTX_%u':\n"
" tex_links.remove(link)\n"
" soc_from = link.from_socket\n"
" soc_to = link.to_socket\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node.node_tree = bpy.data.node_groups['RWKUVMode1Node']\n"
" node.location[0] = link.from_node.location[0] + 50\n"
" node.location[1] = link.from_node.location[1] - 50\n"
" new_nodetree.links.remove(link)\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n",
idx);
break;
case MaterialSet::Material::UVAnimation::ANIM_SCROLL:
out.format("for link in list(tex_links):\n"
" if link.from_node.label == 'MTX_%u':\n"
" tex_links.remove(link)\n"
" soc_from = link.from_socket\n"
" soc_to = link.to_socket\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node.node_tree = bpy.data.node_groups['RWKUVMode2Node']\n"
" node.location[0] = link.from_node.location[0] + 50\n"
" node.location[1] = link.from_node.location[1] - 50\n"
" node.inputs[1].default_value = (%f,%f,0)\n"
" node.inputs[2].default_value = (%f,%f,0)\n"
" new_nodetree.links.remove(link)\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n",
idx, vals[0], vals[1], vals[2], vals[3]);
break;
case MaterialSet::Material::UVAnimation::ANIM_ROTATION:
out.format("for link in list(tex_links):\n"
" if link.from_node.label == 'MTX_%u':\n"
" tex_links.remove(link)\n"
" soc_from = link.from_socket\n"
" soc_to = link.to_socket\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node.node_tree = bpy.data.node_groups['RWKUVMode3Node']\n"
" node.location[0] = link.from_node.location[0] + 50\n"
" node.location[1] = link.from_node.location[1] - 50\n"
" node.inputs[1].default_value = %f\n"
" node.inputs[2].default_value = %f\n"
" new_nodetree.links.remove(link)\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n",
idx, vals[0], vals[1]);
break;
case MaterialSet::Material::UVAnimation::ANIM_HSTRIP:
out.format("for link in list(tex_links):\n"
" if link.from_node.label == 'MTX_%u':\n"
" tex_links.remove(link)\n"
" soc_from = link.from_socket\n"
" soc_to = link.to_socket\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node.node_tree = bpy.data.node_groups['RWKUVMode4Node']\n"
" node.location[0] = link.from_node.location[0] + 50\n"
" node.location[1] = link.from_node.location[1] - 50\n"
" node.inputs[1].default_value = %f\n"
" node.inputs[2].default_value = %f\n"
" node.inputs[3].default_value = %f\n"
" node.inputs[4].default_value = %f\n"
" new_nodetree.links.remove(link)\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n",
idx, vals[0], vals[1], vals[2], vals[3]);
break;
case MaterialSet::Material::UVAnimation::ANIM_VSTRIP:
out.format("for link in list(tex_links):\n"
" if link.from_node.label == 'MTX_%u':\n"
" tex_links.remove(link)\n"
" soc_from = link.from_socket\n"
" soc_to = link.to_socket\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node.node_tree = bpy.data.node_groups['RWKUVMode5Node']\n"
" node.location[0] = link.from_node.location[0] + 50\n"
" node.location[1] = link.from_node.location[1] - 50\n"
" node.inputs[1].default_value = %f\n"
" node.inputs[2].default_value = %f\n"
" node.inputs[3].default_value = %f\n"
" node.inputs[4].default_value = %f\n"
" new_nodetree.links.remove(link)\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n",
idx, vals[0], vals[1], vals[3], vals[2]);
break;
case MaterialSet::Material::UVAnimation::ANIM_MODEL:
out.format("for link in list(tex_links):\n"
" if link.from_node.label == 'MTX_%u':\n"
" tex_links.remove(link)\n"
" soc_from = link.from_socket\n"
" soc_to = link.to_socket\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node.node_tree = bpy.data.node_groups['RWKUVMode6Node']\n"
" node.location[0] = link.from_node.location[0] + 50\n"
" node.location[1] = link.from_node.location[1] - 50\n"
" new_nodetree.links.remove(link)\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n",
idx);
break;
case MaterialSet::Material::UVAnimation::ANIM_MODE_WHO_MUST_NOT_BE_NAMED:
out.format("for link in list(tex_links):\n"
" if link.from_node.label == 'MTX_%u':\n"
" tex_links.remove(link)\n"
" soc_from = link.from_socket\n"
" soc_to = link.to_socket\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node.node_tree = bpy.data.node_groups['RWKUVMode7Node']\n"
" node.location[0] = link.from_node.location[0] + 50\n"
" node.location[1] = link.from_node.location[1] - 50\n"
" node.inputs[1].default_value = %f\n"
" node.inputs[2].default_value = %f\n"
" new_nodetree.links.remove(link)\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n",
idx, vals[0], vals[1]);
break;
default:
break;
}
}
static void AddKcolor(Stream& out, const GX::Color& col, unsigned idx)
{
out.format("# KColor\n"
"kc_node = new_nodetree.nodes.new('ShaderNodeRGB')\n"
"kc_node.label = 'KColor %u'\n"
"kc_node.outputs['Color'].default_value[0] = %f\n"
"kc_node.outputs['Color'].default_value[1] = %f\n"
"kc_node.outputs['Color'].default_value[2] = %f\n"
"kc_node.outputs['Color'].default_value[3] = %f\n"
"gridder.place_node(kc_node, 1)\n"
"\n"
"ka_node = new_nodetree.nodes.new('ShaderNodeValue')\n"
"ka_node.label = 'KAlpha %u'\n"
"ka_node.outputs['Value'].default_value = %f\n"
"gridder.place_node(ka_node, 1)\n"
"\n"
"kcolor_nodes.append((kc_node,ka_node))\n"
"\n",
idx,
(float)col.r / (float)0xff, (float)col.g / (float)0xff,
(float)col.b / (float)0xff, (float)col.a / (float)0xff,
idx,
(float)col.a / (float)0xff);
}
static void add_link(Stream& out, const char* a, const char* b)
{
out.format("new_nodetree.links.new(%s, %s)\n", a, b);
}
enum CombinerType
{
COMB_ADD,
COMB_SUB,
COMB_MULT
};
static void AddColorCombiner(Stream& out, CombinerType type,
const char* a, const char* b, const char* v)
{
out << "combiner_node = new_nodetree.nodes.new('ShaderNodeMixRGB')\n"
"combiner_node.inputs[0].default_value = 1.0\n"
"gridder.place_node_right(combiner_node, 2, 0)\n";
if (type == COMB_ADD)
out << "combiner_node.blend_type = 'ADD'\n";
else if (type == COMB_SUB)
out << "combiner_node.blend_type = 'SUBTRACT'\n";
else if (type == COMB_MULT)
out << "combiner_node.blend_type = 'MULTIPLY'\n";
if (a) {
if (!strcmp(a, "ZERO"))
out << "combiner_node.inputs['Color1'].default_value = (0.0, 0.0, 0.0, 0.0)\n";
else if (!strcmp(a, "HALF"))
out << "combiner_node.inputs['Color1'].default_value = (0.5, 0.5, 0.5, 0.5)\n";
else if (!strcmp(a, "ONE"))
out << "combiner_node.inputs['Color1'].default_value = (1.0, 1.0, 1.0, 1.0)\n";
else if (!strlen(a))
{}
else
out.format("new_nodetree.links.new(%s, combiner_node.inputs['Color1'])\n", a);
}
if (b) {
if (!strcmp(b, "ZERO"))
out << "combiner_node.inputs['Color2'].default_value = (0.0, 0.0, 0.0, 0.0)\n";
else if (!strcmp(b, "HALF"))
out << "combiner_node.inputs['Color2'].default_value = (0.5, 0.5, 0.5, 0.5)\n";
else if (!strcmp(b, "ONE"))
out << "combiner_node.inputs['Color2'].default_value = (1.0, 1.0, 1.0, 1.0)\n";
else if (!strlen(b))
{}
else
out.format("new_nodetree.links.new(%s, combiner_node.inputs['Color2'])\n", b);
}
if (v)
out.format("new_nodetree.links.new(combiner_node.outputs['Color'], %s)\n", v);
out << "color_combiner_nodes.append(combiner_node)\n\n";
}
static void AddAlphaCombiner(Stream& out, enum CombinerType type,
const char* a, const char* b, const char* v)
{
out << "combiner_node = new_nodetree.nodes.new('ShaderNodeMath')\n"
"gridder.place_node_right(combiner_node, 2, 1)\n";
if (type == COMB_ADD)
out << "combiner_node.operation = 'ADD'\n";
else if (type == COMB_SUB)
out << "combiner_node.operation = 'SUBTRACT'\n";
else if (type == COMB_MULT)
out << "combiner_node.operation = 'MULTIPLY'\n";
if (a) {
if (!strcmp(a, "ZERO"))
out << "combiner_node.inputs[0].default_value = 0.0\n";
else if (!strcmp(a, "HALF"))
out << "combiner_node.inputs[0].default_value = 0.5\n";
else if (!strcmp(a, "ONE"))
out << "combiner_node.inputs[0].default_value = 1.0\n";
else
out.format("new_nodetree.links.new(%s, combiner_node.inputs[0])\n", a);
}
if (b) {
if (!strcmp(b, "ZERO"))
out << "combiner_node.inputs[1].default_value = 0.0\n";
else if (!strcmp(b, "HALF"))
out << "combiner_node.inputs[1].default_value = 0.5\n";
else if (!strcmp(b, "ONE"))
out << "combiner_node.inputs[1].default_value = 1.0\n";
else
out.format("new_nodetree.links.new(%s, combiner_node.inputs[1])\n", b);
}
if (v)
out.format("new_nodetree.links.new(combiner_node.outputs[0], %s)\n", v);
out << "alpha_combiner_nodes.append(combiner_node)\n\n";
}
static void AddLightmap(Stream& out, const char* tex, unsigned& c_combiner_idx)
{
out << "world_light_node = new_nodetree.nodes.new('ShaderNodeRGB')\n"
"gridder.place_node(world_light_node, 1)\n"
"world_light_node.label = 'WORLD_LIGHTING'\n"
"world_light_node.outputs[0].default_value = (1.0,1.0,1.0,1.0)\n";
AddColorCombiner(out, COMB_MULT, tex, "world_light_node.outputs[0]", nullptr);
AddColorCombiner(out, COMB_ADD,
"color_combiner_nodes[-1].outputs[0]",
"material_node.outputs['Color']", nullptr);
c_combiner_idx += 2;
}
static void TranslateColorSocket(char* socketOut, GX::TevColorArg arg,
GX::TevKColorSel kcolor,
const MaterialSet::Material::TEVStageTexInfo& stageTex,
char c_regs[4][64], char a_regs[4][64]) {
if (arg == GX::CC_ZERO)
strcpy(socketOut, "ZERO");
else if (arg == GX::CC_HALF)
strcpy(socketOut, "HALF");
else if (arg == GX::CC_ONE)
strcpy(socketOut, "ONE");
else if (arg == GX::CC_TEXC) {
if (stageTex.tcgSlot == 0xff)
strcpy(socketOut, "ONE");
else
sprintf(socketOut, "texture_nodes[%u].outputs['Color']", stageTex.tcgSlot);
} else if (arg == GX::CC_TEXA) {
if (stageTex.tcgSlot == 0xff)
strcpy(socketOut, "ONE");
else
sprintf(socketOut, "texture_nodes[%u].outputs['Value']", stageTex.tcgSlot);
} else if (arg == GX::CC_RASC)
strcpy(socketOut, "material_node.outputs['Color']");
else if (arg == GX::CC_RASA) {
strcpy(socketOut, "material_node.outputs['Alpha']");
} else if (arg == GX::CC_KONST) {
int kreg = (kcolor - GX::TEV_KCSEL_K0) % 4;
if (kcolor < GX::TEV_KCSEL_K0)
strcpy(socketOut, "ONE");
else if (kreg == 0)
strcpy(socketOut, "kcolor_nodes[0][0].outputs[0]");
else if (kreg == 1)
strcpy(socketOut, "kcolor_nodes[1][0].outputs[0]");
else if (kreg == 2)
strcpy(socketOut, "kcolor_nodes[2][0].outputs[0]");
else if (kreg == 3)
strcpy(socketOut, "kcolor_nodes[3][0].outputs[0]");
else
strcpy(socketOut, "ONE");
} else if (arg == GX::CC_CPREV)
strcpy(socketOut, c_regs[GX::TEVPREV]);
else if (arg == GX::CC_APREV) {
strcpy(socketOut, a_regs[GX::TEVPREV]);
} else if (arg == GX::CC_C0)
strcpy(socketOut, c_regs[GX::TEVREG0]);
else if (arg == GX::CC_A0) {
strcpy(socketOut, a_regs[GX::TEVREG0]);
} else if (arg == GX::CC_C1)
strcpy(socketOut, c_regs[GX::TEVREG1]);
else if (arg == GX::CC_A1) {
strcpy(socketOut, a_regs[GX::TEVREG1]);
} else if (arg == GX::CC_C2)
strcpy(socketOut, c_regs[GX::TEVREG2]);
else if (arg == GX::CC_A2) {
strcpy(socketOut, a_regs[GX::TEVREG2]);
}
}
static void TranslateAlphaSocket(char* socketOut, GX::TevAlphaArg arg,
GX::TevKAlphaSel kalpha,
const MaterialSet::Material::TEVStageTexInfo& stageTex,
char a_regs[4][64]) {
if (arg == GX::CA_ZERO)
strcpy(socketOut, "ZERO");
else if (arg == GX::CA_TEXA) {
if (stageTex.tcgSlot == 0xff)
strcpy(socketOut, "ONE");
else
sprintf(socketOut, "texture_nodes[%u].outputs['Value']", stageTex.tcgSlot);
} else if (arg == GX::CA_RASA)
strcpy(socketOut, "material_node.outputs['Alpha']");
else if (arg == GX::CA_KONST) {
int kreg = kalpha - GX::TEV_KASEL_K0_A;
if (kreg == 0)
strcpy(socketOut, "kcolor_nodes[0][1].outputs[0]");
else if (kreg == 1)
strcpy(socketOut, "kcolor_nodes[1][1].outputs[0]");
else if (kreg == 2)
strcpy(socketOut, "kcolor_nodes[2][1].outputs[0]");
else if (kreg == 3)
strcpy(socketOut, "kcolor_nodes[3][1].outputs[0]");
else
strcpy(socketOut, "ONE");
} else if (arg == GX::CA_APREV)
strcpy(socketOut, a_regs[GX::TEVPREV]);
else if (arg == GX::CA_A0)
strcpy(socketOut, a_regs[GX::TEVREG0]);
else if (arg == GX::CA_A1)
strcpy(socketOut, a_regs[GX::TEVREG1]);
else if (arg == GX::CA_A2)
strcpy(socketOut, a_regs[GX::TEVREG2]);
}
static void AddTEVStage(Stream& out, const MaterialSet::Material::TEVStage& stage,
const MaterialSet::Material::TEVStageTexInfo& stageTex,
char c_regs[4][64], char a_regs[4][64],
unsigned& c_combiner_idx, unsigned& a_combiner_idx)
{
char ca[64];
char cb[64];
char cc[64];
char cd[64];
TranslateColorSocket(ca, stage.colorInA(), stage.kColorIn(), stageTex, c_regs, a_regs);
TranslateColorSocket(cb, stage.colorInB(), stage.kColorIn(), stageTex, c_regs, a_regs);
TranslateColorSocket(cc, stage.colorInC(), stage.kColorIn(), stageTex, c_regs, a_regs);
TranslateColorSocket(cd, stage.colorInD(), stage.kColorIn(), stageTex, c_regs, a_regs);
char aa[64];
char ab[64];
char ac[64];
char ad[64];
TranslateAlphaSocket(aa, stage.alphaInA(), stage.kAlphaIn(), stageTex, a_regs);
TranslateAlphaSocket(ab, stage.alphaInB(), stage.kAlphaIn(), stageTex, a_regs);
TranslateAlphaSocket(ac, stage.alphaInC(), stage.kAlphaIn(), stageTex, a_regs);
TranslateAlphaSocket(ad, stage.alphaInD(), stage.kAlphaIn(), stageTex, a_regs);
/* Apply color optimizations */
unsigned c_tev_opts = 0;
if (stage.colorInA() == GX::CC_ZERO || stage.colorInC() == GX::CC_ONE)
c_tev_opts |= 1;
if (stage.colorInB() == GX::CC_ZERO || stage.colorInC() == GX::CC_ZERO)
c_tev_opts |= 2;
if (c_tev_opts & 1 || c_tev_opts & 2)
c_tev_opts |= 4;
if (stage.colorInD() == GX::CC_ZERO || (c_tev_opts & 7) == 7)
c_tev_opts |= 8;
if (!(c_tev_opts & 1))
{
/* A nodes */
AddColorCombiner(out, COMB_SUB, "ONE", ca, NULL);
++c_combiner_idx;
AddColorCombiner(out, COMB_MULT, "color_combiner_nodes[-1].outputs[0]", cc, NULL);
++c_combiner_idx;
}
if (!(c_tev_opts & 2))
{
/* B nodes */
AddColorCombiner(out, COMB_MULT, cb, cc, NULL);
++c_combiner_idx;
}
if (!(c_tev_opts & 4))
{
/* A+B node */
AddColorCombiner(out, COMB_ADD, "color_combiner_nodes[-2].outputs[0]", "color_combiner_nodes[-1].outputs[0]", NULL);
++c_combiner_idx;
}
if (!(c_tev_opts & 8))
{
/* +D node */
AddColorCombiner(out, COMB_ADD, "color_combiner_nodes[-1].outputs[0]", cd, NULL);
++c_combiner_idx;
}
/* Apply alpha optimizations */
unsigned a_tev_opts = 0;
if (stage.alphaInA() == GX::CA_ZERO)
a_tev_opts |= 1;
if (stage.alphaInB() == GX::CA_ZERO || stage.alphaInC() == GX::CA_ZERO)
a_tev_opts |= 2;
if (a_tev_opts & 1 || a_tev_opts & 2)
a_tev_opts |= 4;
if (stage.alphaInD() == GX::CA_ZERO || (a_tev_opts & 7) == 7)
a_tev_opts |= 8;
if (!(a_tev_opts & 1))
{
/* A nodes */
AddAlphaCombiner(out, COMB_SUB, "ONE", aa, NULL);
++a_combiner_idx;
AddAlphaCombiner(out, COMB_MULT, "alpha_combiner_nodes[-1].outputs[0]", ac, NULL);
++a_combiner_idx;
}
if (!(a_tev_opts & 2))
{
/* B nodes */
AddAlphaCombiner(out, COMB_MULT, ab, ac, NULL);
++a_combiner_idx;
}
if (!(a_tev_opts & 4))
{
/* A+B node */
AddAlphaCombiner(out, COMB_ADD, "alpha_combiner_nodes[-2].outputs[0]", "alpha_combiner_nodes[-1].outputs[0]", NULL);
++a_combiner_idx;
}
if (!(a_tev_opts & 8)) {
/* +D node */
AddAlphaCombiner(out, COMB_ADD, "alpha_combiner_nodes[-1].outputs[0]", ad, NULL);
++a_combiner_idx;
}
/* Update TEV regs */
if (c_tev_opts == 0xf)
{
if (stage.colorInD() != GX::CC_ZERO)
strncpy(c_regs[stage.colorOpOutReg()], cd, 64);
} else
snprintf(c_regs[stage.colorOpOutReg()], 64, "color_combiner_nodes[%u].outputs[0]", c_combiner_idx - 1);
if (a_tev_opts == 0xf)
{
if (stage.alphaInD() != GX::CA_ZERO)
strncpy(a_regs[stage.alphaOpOutReg()], ad, 64);
} else
snprintf(a_regs[stage.alphaOpOutReg()], 64, "alpha_combiner_nodes[%u].outputs[0]", a_combiner_idx - 1);
/* Row Break in gridder */
out << "gridder.row_break(2)\n";
}
template <class MAT>
void _ConstructMaterial(Stream& out,
const MAT& material,
unsigned groupIdx,
unsigned matIdx)
{
unsigned i;
out.format("new_material = bpy.data.materials.new('MAT_%u_%u')\n"
"new_material.use_shadows = True\n"
"new_material.use_transparent_shadows = True\n"
"new_material.diffuse_color = (1.0,1.0,1.0)\n"
"new_material.diffuse_intensity = 1.0\n"
"new_material.specular_intensity = 0.0\n"
"new_material.use_nodes = True\n"
"new_nodetree = new_material.node_tree\n"
"material_node = new_nodetree.nodes['Material']\n"
"final_node = new_nodetree.nodes['Output']\n"
"\n"
"gridder = hecl.Nodegrid(new_nodetree)\n"
"gridder.place_node(final_node, 3)\n"
"gridder.place_node(material_node, 0)\n"
"material_node.material = new_material\n"
"\n"
"texture_nodes = []\n"
"kcolor_nodes = []\n"
"color_combiner_nodes = []\n"
"alpha_combiner_nodes = []\n"
"tex_links = []\n"
"tev_reg_sockets = [None]*4\n"
"\n", groupIdx, matIdx);
/* Material Flags */
out.format("new_material.retro_depth_sort = %s\n"
"new_material.retro_punchthrough_alpha = %s\n"
"new_material.retro_samus_reflection = %s\n"
"new_material.retro_depth_write = %s\n"
"new_material.retro_samus_reflection_persp = %s\n"
"new_material.retro_shadow_occluder = %s\n"
"new_material.retro_samus_reflection_indirect = %s\n"
"new_material.retro_lightmapped = %s\n"
"new_material.game_settings.invisible = %s\n",
material.flags.depthSorting() ? "True" : "False",
material.flags.punchthroughAlpha() ? "True" : "False",
material.flags.samusReflection() ? "True" : "False",
material.flags.depthWrite() ? "True" : "False",
material.flags.samusReflectionSurfaceEye() ? "True" : "False",
material.flags.shadowOccluderMesh() ? "True" : "False",
material.flags.samusReflectionIndirectTexture() ? "True" : "False",
material.flags.lightmap() ? "True" : "False",
material.flags.shadowOccluderMesh() ? "True" : "False");
/* Texture Indices */
out << "tex_maps = []\n";
for (atUint32 idx : material.texureIdxs)
out.format("tex_maps.append(texmap_list[%u])\n", idx);
/* KColor entries */
if (material.flags.konstValuesEnabled())
{
unsigned i=0;
for (const GX::Color& col : material.konstColors)
AddKcolor(out, col, i++);
}
/* Blend factors */
using BlendFactor = MaterialSet::Material::BlendFactor;
if (material.blendDestFactor() != BlendFactor::GX_BL_ZERO)
{
if (material.blendDestFactor() == BlendFactor::GX_BL_ONE)
out << "new_material.game_settings.alpha_blend = 'ADD'\n"
"new_material.use_transparency = True\n"
"new_material.transparency_method = 'RAYTRACE'\n"
"new_material.alpha = 1.0\n";
else
out << "new_material.game_settings.alpha_blend = 'ALPHA'\n"
"new_material.use_transparency = True\n"
"new_material.transparency_method = 'RAYTRACE'\n"
"new_material.alpha = 1.0\n";
}
/* Color channels (for combining dynamic lighting) */
for (const MaterialSet::Material::ColorChannel& chan : material.colorChannels)
{
if (!chan.lighting())
out << "new_material.use_shadeless = True\n";
}
/* Add texture maps/tcgs */
unsigned addedTcgs = 0;
for (i=0 ; i<material.tevStageCount ; ++i)
{
if (material.tevStageTexInfo[i].tcgSlot != 0xff &&
!(addedTcgs >> material.tevStageTexInfo[i].tcgSlot & 1))
{
const MaterialSet::Material::TexCoordGen& tcg = material.tcgs[material.tevStageTexInfo[i].tcgSlot];
GX::TexMtx mtx = tcg.mtx();
int mtxIdx = -1;
if (mtx >= GX::TEXMTX0 && mtx <= GX::TEXMTX9)
mtxIdx = (mtx - GX::TEXMTX0) / 3;
AddTexture(out, tcg.source(), mtxIdx, material.tevStageTexInfo[i].texSlot);
addedTcgs |= 1 << material.tevStageTexInfo[i].tcgSlot;
}
}
/* TEV-emulation combiner-node index context */
unsigned c_combiner_idx = 0;
unsigned a_combiner_idx = 0;
/* Initialze TEV register sockets */
char c_regs[4][64] = {"ONE", "ONE", "ONE", "ONE"};
char a_regs[4][64] = {"ONE", "ONE", "ONE", "ONE"};
/* Has Lightmap? */
if (material.tevStages[0].colorInB() == GX::CC_C1)
{
if (material.tevStageTexInfo[0].texSlot != 0xff)
out << "new_material.retro_lightmap = tex_maps[0].name\n"
"tex_maps[0].image.use_fake_user = True\n";
AddLightmap(out, "texture_nodes[0].outputs['Color']", c_combiner_idx);
strncpy(c_regs[GX::TEVREG1], "world_light_node.outputs[0]", 64);
}
/* Add TEV stages */
for (i=0 ; i<material.tevStageCount ; ++i)
{
const MaterialSet::Material::TEVStage& stage = material.tevStages[i];
const MaterialSet::Material::TEVStageTexInfo& stage_tex = material.tevStageTexInfo[i];
AddTEVStage(out, stage, stage_tex, c_regs, a_regs, c_combiner_idx, a_combiner_idx);
}
/* Connect final prev register */
if (!strcmp(c_regs[GX::TEVPREV], "ONE"))
out << "final_node.inputs['Color'].default_value = (1.0,1.0,1.0,1.0)\n";
else
out.format("new_nodetree.links.new(%s, final_node.inputs['Color'])\n", c_regs[GX::TEVPREV]);
if (!strcmp(a_regs[GX::TEVPREV], "ONE"))
out << "final_node.inputs['Alpha'].default_value = 1.0\n";
else
out.format("new_nodetree.links.new(%s, final_node.inputs['Alpha'])\n", a_regs[GX::TEVPREV]);
/* Texmtx Animation Section */
i=0;
for (const MaterialSet::Material::UVAnimation& anim : material.uvAnims)
AddTextureAnim(out, anim.mode, i++, anim.vals);
}
void MaterialSet::ConstructMaterial(Stream& out,
const MaterialSet::Material& material,
unsigned groupIdx,
unsigned matIdx)
{_ConstructMaterial(out, material, groupIdx, matIdx);}
}
}
namespace Retro
{
namespace DNAMP2
{
void MaterialSet::ConstructMaterial(Stream& out,
const MaterialSet::Material& material,
unsigned groupIdx,
unsigned matIdx)
{Retro::DNAMP1::_ConstructMaterial(out, material, groupIdx, matIdx);}
}
}

View File

@ -1,8 +1,11 @@
#ifndef _DNAMP1_CMDL_MATERIALS_HPP_
#define _DNAMP1_CMDL_MATERIALS_HPP_
#include <BlenderConnection.hpp>
#include "../DNACommon/DNACommon.hpp"
#include "../DNACommon/GX.hpp"
#include "../DNACommon/CMDL.hpp"
#include "DNAMP1.hpp"
namespace Retro
{
@ -80,7 +83,24 @@ struct MaterialSet : BigDNA
inline void setTex5(GX::AttrType val) {vaFlags &= ~0xC0000; vaFlags |= atUint32(val) << 18;}
inline GX::AttrType tex6() const {return GX::AttrType(vaFlags >> 20 & 0x3);}
inline void setTex6(GX::AttrType val) {vaFlags &= ~0x300000; vaFlags |= atUint32(val) << 20;}
inline GX::AttrType pnMatIdx() const {return GX::AttrType(vaFlags >> 24 & 0x1);}
inline void setPnMatIdx(GX::AttrType val) {vaFlags &= ~0x1000000; vaFlags |= atUint32(val & 0x1) << 24;}
inline GX::AttrType tex0MatIdx() const {return GX::AttrType(vaFlags >> 25 & 0x1);}
inline void setTex0MatIdx(GX::AttrType val) {vaFlags &= ~0x2000000; vaFlags |= atUint32(val & 0x1) << 25;}
inline GX::AttrType tex1MatIdx() const {return GX::AttrType(vaFlags >> 26 & 0x1);}
inline void setTex1MatIdx(GX::AttrType val) {vaFlags &= ~0x4000000; vaFlags |= atUint32(val & 0x1) << 26;}
inline GX::AttrType tex2MatIdx() const {return GX::AttrType(vaFlags >> 27 & 0x1);}
inline void setTex2MatIdx(GX::AttrType val) {vaFlags &= ~0x8000000; vaFlags |= atUint32(val & 0x1) << 27;}
inline GX::AttrType tex3MatIdx() const {return GX::AttrType(vaFlags >> 28 & 0x1);}
inline void setTex3MatIdx(GX::AttrType val) {vaFlags &= ~0x10000000; vaFlags |= atUint32(val & 0x1) << 28;}
inline GX::AttrType tex4MatIdx() const {return GX::AttrType(vaFlags >> 29 & 0x1);}
inline void setTex4MatIdx(GX::AttrType val) {vaFlags &= ~0x20000000; vaFlags |= atUint32(val & 0x1) << 29;}
inline GX::AttrType tex5MatIdx() const {return GX::AttrType(vaFlags >> 30 & 0x1);}
inline void setTex5MatIdx(GX::AttrType val) {vaFlags &= ~0x40000000; vaFlags |= atUint32(val & 0x1) << 30;}
inline GX::AttrType tex6MatIdx() const {return GX::AttrType(vaFlags >> 31 & 0x1);}
inline void setTex6MatIdx(GX::AttrType val) {vaFlags &= ~0x80000000; vaFlags |= atUint32(val & 0x1) << 31;}
} vaFlags;
inline const VAFlags& getVAFlags() const {return vaFlags;}
Value<atUint32> groupIdx;
Vector<atUint32, DNA_COUNT(flags.konstValuesEnabled())> konstCount;
@ -178,6 +198,9 @@ struct MaterialSet : BigDNA
inline void setAlphaOpClamp(bool val) {acFlags &= ~0x100; acFlags |= atUint32(val) << 8;}
inline GX::TevRegID alphaOpOutReg() const {return GX::TevRegID(acFlags >> 9 & 0x3);}
inline void setAlphaOpOutReg(GX::TevRegID val) {acFlags &= ~0x600; acFlags |= atUint32(val) << 9;}
inline GX::TevKColorSel kColorIn() const {return GX::TevKColorSel(kcInput);}
inline GX::TevKAlphaSel kAlphaIn() const {return GX::TevKAlphaSel(kaInput);}
};
Vector<TEVStage, DNA_COUNT(tevStageCount)> tevStages;
struct TEVStageTexInfo : BigDNA
@ -199,14 +222,14 @@ struct MaterialSet : BigDNA
inline void setType(GX::TexGenType val) {flags &= ~0xf; flags |= atUint32(val);}
inline GX::TexGenSrc source() const {return GX::TexGenSrc(flags >> 4 & 0x1f);}
inline void setSource(GX::TexGenSrc val) {flags &= ~0x1f0; flags |= atUint32(val) << 4;}
inline GX::TexMtx mtxIdx() const {return GX::TexMtx(flags >> 9 & 0x1f + 30);}
inline void setMtxIdx(GX::TexMtx val) {flags &= ~0x3e00; flags |= (atUint32(val)-30) << 9;}
inline GX::TexMtx mtx() const {return GX::TexMtx(flags >> 9 & 0x1f + 30);}
inline void setMtx(GX::TexMtx val) {flags &= ~0x3e00; flags |= (atUint32(val)-30) << 9;}
inline bool normalize() const {return flags >> 14 & 0x1;}
inline void setNormalize(bool val) {flags &= ~0x4000; flags |= atUint32(val) << 14;}
inline GX::PTTexMtx postMtxIdx() const {return GX::PTTexMtx(flags >> 15 & 0x3f + 64);}
inline void setPostMtxIdx(GX::PTTexMtx val) {flags &= ~0x1f8000; flags |= (atUint32(val)-64) << 15;}
inline GX::PTTexMtx postMtx() const {return GX::PTTexMtx(flags >> 15 & 0x3f + 64);}
inline void setPostMtx(GX::PTTexMtx val) {flags &= ~0x1f8000; flags |= (atUint32(val)-64) << 15;}
};
Vector<TexCoordGen, DNA_COUNT(tcgCount)> tgcs;
Vector<TexCoordGen, DNA_COUNT(tcgCount)> tcgs;
Value<atUint32> uvAnimsSize;
Value<atUint32> uvAnimsCount;
@ -280,6 +303,18 @@ struct MaterialSet : BigDNA
};
Vector<Material, DNA_COUNT(head.materialCount)> materials;
static void RegisterMaterialProps(HECL::BlenderConnection::PyOutStream& out);
static void ConstructMaterial(HECL::BlenderConnection::PyOutStream& out,
const MaterialSet::Material& material,
unsigned groupIdx, unsigned matIdx);
inline void readToBlender(HECL::BlenderConnection::PyOutStream& os,
const PAKRouter<PAKBridge>& pakRouter,
const typename PAKRouter<PAKBridge>::EntryType& entry,
unsigned setIdx)
{
DNACMDL::ReadMaterialSetToBlender_1_2(os, *this, pakRouter, entry, setIdx);
}
};
}

View File

@ -1,11 +1,18 @@
make_dnalist(liblist
PAK
MLVL
CMDL
ANCS
ANIM
CINF
CSKR
MAPA
CMDLMaterials)
add_library(DNAMP1
DNAMP1.hpp DNAMP1.cpp
${liblist}
PAK.cpp
STRG.hpp STRG.cpp
CMDL.cpp)
ANCS.cpp
ANIM.cpp
CMDL.hpp
CMDLMaterials.cpp)

34
DataSpec/DNAMP1/CSKR.hpp Normal file
View File

@ -0,0 +1,34 @@
#ifndef _DNAMP1_CSKR_HPP_
#define _DNAMP1_CSKR_HPP_
#include "../DNACommon/DNACommon.hpp"
namespace Retro
{
namespace DNAMP1
{
struct CSKR : BigDNA
{
DECL_DNA
Value<atUint32> skinningRuleCount;
struct SkinningRule : BigDNA
{
DECL_DNA
Value<atUint32> weightCount;
struct Weight : BigDNA
{
DECL_DNA
Value<atUint32> boneId;
Value<float> weight;
};
Vector<Weight, DNA_COUNT(weightCount)> weights;
Value<atUint32> vertCount;
};
Vector<SkinningRule, DNA_COUNT(skinningRuleCount)> skinningRules;
};
}
}
#endif // _DNAMP1_CSKR_HPP_

View File

@ -6,6 +6,7 @@
#include "MLVL.hpp"
#include "../DNACommon/TXTR.hpp"
#include "CMDL.hpp"
#include "ANCS.hpp"
namespace Retro
{
@ -18,14 +19,11 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
{
NOD::AthenaPartReadStream rs(node.beginReadStream());
m_pak.read(rs);
}
HECL::SystemString PAKBridge::getLevelString() const
{
HECL::SystemString retval;
/* Append Level String */
for (const PAK::Entry& entry : m_pak.m_entries)
{
if (entry.type == Retro::MLVL)
if (entry.type == SBIG('MLVL'))
{
PAKEntryReadStream rs = entry.beginReadStream(m_node);
MLVL mlvl;
@ -36,25 +34,158 @@ HECL::SystemString PAKBridge::getLevelString() const
PAKEntryReadStream rs = nameEnt->beginReadStream(m_node);
STRG mlvlName;
mlvlName.read(rs);
if (retval.size())
retval += _S(", ");
retval += mlvlName.getSystemString(ENGL, 0);
if (m_levelString.size())
m_levelString += _S(", ");
m_levelString += mlvlName.getSystemString(FOURCC('ENGL'), 0);
}
}
}
}
UniqueResult PAKBridge::uniqueCheck(const PAK::Entry& entry)
{
UniqueResult::Type result = UniqueResult::UNIQUE_NOTFOUND;
bool foundOneLayer = false;
UniqueID32 areaId;
unsigned layerIdx;
for (const auto& pair : m_areaDeps)
{
unsigned l=0;
for (const auto& layer : pair.second.layers)
{
if (layer.resources.find(entry.id) != layer.resources.end())
{
if (foundOneLayer)
{
if (areaId == pair.first)
result = UniqueResult::UNIQUE_AREA;
else
return {UniqueResult::UNIQUE_LEVEL};
continue;
}
else
result = UniqueResult::UNIQUE_LAYER;
areaId = pair.first;
layerIdx = l;
foundOneLayer = true;
}
++l;
}
if (pair.second.resources.find(entry.id) != pair.second.resources.end())
{
if (foundOneLayer)
{
if (areaId == pair.first)
result = UniqueResult::UNIQUE_AREA;
else
return {UniqueResult::UNIQUE_LEVEL};
continue;
}
else
result = UniqueResult::UNIQUE_AREA;
areaId = pair.first;
foundOneLayer = true;
}
}
UniqueResult retval = {result};
if (result == UniqueResult::UNIQUE_LAYER || result == UniqueResult::UNIQUE_AREA)
{
const PAKBridge::Area& area = m_areaDeps[areaId];
retval.areaName = &area.name;
if (result == UniqueResult::UNIQUE_LAYER)
{
const PAKBridge::Area::Layer& layer = area.layers[layerIdx];
retval.layerName = &layer.name;
}
}
return retval;
}
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry)
static HECL::SystemString LayerName(const std::string& name)
{
#if HECL_UCS2
HECL::SystemString ret = HECL::UTF8ToWide(mlvl.layerNames[layerIdx++]);
#else
HECL::SystemString ret = name;
#endif
for (auto& ch : ret)
if (ch == _S('/') || ch == _S('\\'))
ch = _S('-');
return ret;
}
void PAKBridge::build()
{
/* First pass: build per-area/per-layer dependency map */
for (const PAK::Entry& entry : m_pak.m_entries)
{
if (entry.type == SBIG('MLVL'))
{
PAKEntryReadStream rs = entry.beginReadStream(m_node);
MLVL mlvl;
mlvl.read(rs);
m_areaDeps.reserve(mlvl.areaCount);
unsigned layerIdx = 0;
for (const MLVL::Area& area : mlvl.areas)
{
Area& areaDeps = m_areaDeps[area.areaMREAId];
const PAK::Entry* areaNameEnt = m_pak.lookupEntry(area.areaNameId);
if (areaNameEnt)
{
STRG areaName;
PAKEntryReadStream rs = areaNameEnt->beginReadStream(m_node);
areaName.read(rs);
areaDeps.name = areaName.getSystemString(FOURCC('ENGL'), 0);
}
if (areaDeps.name.empty())
{
#if HECL_UCS2
areaDeps.name = _S("MREA_") + HECL::UTF8ToWide(area.areaMREAId.toString());
#else
areaDeps.name = "MREA_" + area.areaMREAId.toString();
#endif
}
areaDeps.layers.reserve(area.depLayerCount-1);
unsigned r=0;
for (unsigned l=1 ; l<area.depLayerCount ; ++l)
{
areaDeps.layers.emplace_back();
Area::Layer& layer = areaDeps.layers.back();
layer.name = LayerName(mlvl.layerNames[layerIdx++]);
layer.resources.reserve(area.depLayers[l] - r);
for (; r<area.depLayers[l] ; ++r)
layer.resources.emplace(area.deps[r].id);
}
areaDeps.resources.reserve(area.depCount - r);
for (; r<area.depCount ; ++r)
areaDeps.resources.emplace(area.deps[r].id);
areaDeps.resources.emplace(area.areaMREAId);
}
}
}
/* Second pass: cross-compare uniqueness */
for (PAK::Entry& entry : m_pak.m_entries)
{
entry.unique = uniqueCheck(entry);
}
}
ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const PAK::Entry& entry)
{
switch (entry.type.toUint32())
{
case SBIG('STRG'):
return {STRG::Extract, ".as"};
return {STRG::Extract, nullptr, ".yaml"};
case SBIG('TXTR'):
return {TXTR::Extract, ".png"};
return {TXTR::Extract, nullptr, ".png"};
case SBIG('CMDL'):
return {CMDL::Extract, ".blend", 1};
return {nullptr, CMDL::Extract, ".blend", 1};
case SBIG('ANCS'):
return {nullptr, ANCS::Extract, nullptr};
case SBIG('MLVL'):
return {MLVL::Extract, nullptr, ".yaml"};
}
return {};
}

View File

@ -17,11 +17,26 @@ class PAKBridge
HECL::Database::Project& m_project;
const NOD::DiscBase::IPartition::Node& m_node;
PAK m_pak;
struct Area
{
HECL::SystemString name;
struct Layer
{
HECL::SystemString name;
std::unordered_set<UniqueID32> resources;
};
std::vector<Layer> layers;
std::unordered_set<UniqueID32> resources;
};
std::unordered_map<UniqueID32, Area> m_areaDeps;
HECL::SystemString m_levelString;
UniqueResult uniqueCheck(const PAK::Entry& entry);
public:
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
static ResExtractor LookupExtractor(const PAK::Entry& entry);
const std::string& getName() const {return m_node.getName();}
HECL::SystemString getLevelString() const;
void build();
static ResExtractor<PAKBridge> LookupExtractor(const PAK::Entry& entry);
inline const std::string& getName() const {return m_node.getName();}
inline const HECL::SystemString& getLevelString() const {return m_levelString;}
typedef PAK PAKType;
inline const PAKType& getPAK() const {return m_pak;}

81
DataSpec/DNAMP1/MAPA.hpp Normal file
View File

@ -0,0 +1,81 @@
#ifndef __DNAMP1_MAPA_HPP__
#define __DNAMP1_MAPA_HPP__
#include <vector>
#include "../DNACommon/DNACommon.hpp"
namespace Retro
{
namespace DNAMP1
{
struct MAPA : BigDNA
{
DECL_DNA
Value<atUint32> magic;
Value<atUint32> version;
Value<atUint32> unknown1;
Value<atUint32> unknown2;
Value<atVec3f> aabbMin;
Value<atVec3f> aabbMax;
Value<atUint32> mappableObjectCount;
Value<atUint32> vertexCount;
Value<atUint32> surfaceCount;
struct MappableObject : BigDNA
{
DECL_DNA
Value<atUint32> type;
Value<atUint32> unknown1;
Value<atUint16> unknown2;
Value<atUint16> id;
Seek<DNA_COUNT(4), Athena::Current> seek1;
Value<atVec4f> transform1;
Value<atVec4f> transform2;
Value<atVec4f> transform3;
Seek<DNA_COUNT(0x10), Athena::Current> seek2;
};
Vector<MappableObject, DNA_COUNT(mappableObjectCount)> mappableObjects;
Vector<atVec3f, DNA_COUNT(vertexCount)> vertices;
struct SurfaceHeader : BigDNA
{
DECL_DNA
Value<atVec3f> normal;
Value<atVec3f> center;
Value<atUint32> start;
Value<atUint32> end;
};
Vector<SurfaceHeader, DNA_COUNT(surfaceCount)> surfaceHeaders;
struct Surface : BigDNA
{
DECL_DNA
Value<atUint32> primitiveCount;
struct Primitive : BigDNA
{
DECL_DNA
Value<atUint32> type;
Value<atUint32> indexCount;
Vector<atUint32, DNA_COUNT(indexCount)> indices;
Align<4> align;
};
Vector<Primitive, DNA_COUNT(primitiveCount)> primitives;
Value<atUint32> borderCount;
struct Border : BigDNA
{
DECL_DNA
Value<atUint32> indexCount;
Vector<atUint32, DNA_COUNT(indexCount)> indices;
Align<4> align;
};
Vector<Border, DNA_COUNT(borderCount)> borders;
};
Vector<Surface, DNA_COUNT(surfaceCount)> surfaces;
};
}
}
#endif

View File

@ -8,9 +8,9 @@ namespace Retro
namespace DNAMP1
{
struct MLVL : BigDNA
struct MLVL : BigYAML
{
DECL_DNA
DECL_YAML
Value<atUint32> magic;
Value<atUint32> version;
UniqueID32 worldNameId;
@ -18,9 +18,9 @@ struct MLVL : BigDNA
UniqueID32 worldSkyboxId;
Value<atUint32> memRelayLinkCount;
struct MemRelayLink : BigDNA
struct MemRelayLink : BigYAML
{
DECL_DNA
DECL_YAML
Value<atUint32> memRelayId;
Value<atUint32> targetId;
Value<atUint16> msg;
@ -30,9 +30,9 @@ struct MLVL : BigDNA
Value<atUint32> areaCount;
Value<atUint32> unknown1;
struct Area : BigDNA
struct Area : BigYAML
{
DECL_DNA
DECL_YAML
UniqueID32 areaNameId;
Value<atVec4f> transformMtx[3];
Value<atVec3f> aabb[2];
@ -44,9 +44,9 @@ struct MLVL : BigDNA
Value<atUint32> padding;
Value<atUint32> depCount;
struct Dependency : BigDNA
struct Dependency : BigYAML
{
DECL_DNA
DECL_YAML
UniqueID32 id;
FourCC type;
};
@ -56,13 +56,13 @@ struct MLVL : BigDNA
Vector<atUint32, DNA_COUNT(depLayerCount)> depLayers;
Value<atUint32> dockCount;
struct Dock : BigDNA
struct Dock : BigYAML
{
DECL_DNA
DECL_YAML
Value<atUint32> endpointCount;
struct Endpoint : BigDNA
struct Endpoint : BigYAML
{
DECL_DNA
DECL_YAML
Value<atUint32> areaIdx;
Value<atUint32> dockIdx;
};
@ -80,9 +80,9 @@ struct MLVL : BigDNA
Value<atUint32> unknown3;
Value<atUint32> audioGroupCount;
struct AudioGroup : BigDNA
struct AudioGroup : BigYAML
{
DECL_DNA
DECL_YAML
Value<atUint32> unknown;
UniqueID32 agscId;
};
@ -90,9 +90,9 @@ struct MLVL : BigDNA
String<-1> unkString;
Value<atUint32> layerFlagCount;
struct LayerFlags : BigDNA
struct LayerFlags : BigYAML
{
DECL_DNA
DECL_YAML
Value<atUint32> layerCount;
Value<atUint64> flags;
};
@ -103,6 +103,16 @@ struct MLVL : BigDNA
Value<atUint32> layerNameOffsetCount;
Vector<atUint32, DNA_COUNT(layerNameOffsetCount)> layerNameOffsets;
static bool Extract(const SpecBase& dataspec, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
{
MLVL mlvl;
mlvl.read(rs);
FILE* fp = HECL::Fopen(outPath.getAbsolutePath().c_str(), _S("wb"));
mlvl.toYAMLFile(fp);
fclose(fp);
return true;
}
};
}

View File

@ -34,6 +34,7 @@ struct PAK : BigDNA
UniqueID32 id;
Value<atUint32> size;
Value<atUint32> offset;
UniqueResult unique;
std::unique_ptr<atUint8[]> getBuffer(const NOD::DiscBase::IPartition::Node& pak, atUint64& szOut) const;
inline PAKEntryReadStream beginReadStream(const NOD::DiscBase::IPartition::Node& pak, atUint64 off=0) const

View File

@ -111,71 +111,66 @@ void STRG::write(Athena::io::IStreamWriter& writer) const
}
}
bool STRG::readAngelScript(const AngelScript::asIScriptModule& in)
void STRG::fromYAML(Athena::io::YAMLDocReader& reader)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> wconv;
const Athena::io::YAMLNode* root = reader.getRootNode();
/* Validate pass */
for (AngelScript::asUINT i=0 ; i<in.GetGlobalVarCount() ; ++i)
/* Validate Pass */
if (root->m_type == YAML_MAPPING_NODE)
{
const char* name;
int typeId;
if (in.GetGlobalVar(i, &name, 0, &typeId) < 0)
continue;
if (typeId == ASTYPE_STRGLanguage.getTypeID())
for (const auto& lang : root->m_mapChildren)
{
if (strlen(name) != 4)
if (lang.first.size() != 4)
{
Log.report(LogVisor::Error, "STRG language string '%s' from %s must be exactly 4 characters", name, in.GetName());
return false;
Log.report(LogVisor::Warning, "STRG language string '%s' must be exactly 4 characters; skipping", lang.first.c_str());
return;
}
if (lang.second->m_type != YAML_SEQUENCE_NODE)
{
Log.report(LogVisor::Warning, "STRG language string '%s' must contain a sequence; skipping", lang.first.c_str());
return;
}
for (const auto& str : lang.second->m_seqChildren)
{
if (str->m_type != YAML_SCALAR_NODE)
{
Log.report(LogVisor::Warning, "STRG language '%s' must contain all scalars; skipping", lang.first.c_str());
return;
}
}
}
}
/* Read pass */
langs.clear();
for (AngelScript::asUINT i=0 ; i<in.GetGlobalVarCount() ; ++i)
else
{
const char* name;
int typeId;
if (in.GetGlobalVar(i, &name, 0, &typeId) < 0)
continue;
if (typeId == ASTYPE_STRGLanguage.getTypeID())
{
const std::vector<std::string*>& strsin = ASTYPE_STRGLanguage.vectorCast(in.GetAddressOfGlobalVar(i));
std::vector<std::wstring> strs;
for (const std::string* str : strsin)
strs.emplace_back(wconv.from_bytes(*str));
langs.emplace_back(FourCC(name), strs);
}
Log.report(LogVisor::Warning, "STRG must have a mapping root node; skipping");
return;
}
/* Read Pass */
langs.clear();
for (const auto& lang : root->m_mapChildren)
{
std::vector<std::wstring> strs;
for (const auto& str : lang.second->m_seqChildren)
strs.emplace_back(wconv.from_bytes(str->m_scalarString));
langs.emplace_back(FourCC(lang.first.c_str()), strs);
}
langMap.clear();
langMap.reserve(langs.size());
for (std::pair<FourCC, std::vector<std::wstring>>& item : langs)
for (auto& item : langs)
langMap.emplace(item.first, &item.second);
return true;
}
void STRG::writeAngelScript(std::ofstream& out) const
void STRG::toYAML(Athena::io::YAMLDocWriter& writer) const
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> wconv;
for (const std::pair<FourCC, std::vector<std::wstring>>& lang : langs)
for (const auto& lang : langs)
{
out << "STRG::Language " << lang.first.toString() << "({";
bool comma = false;
unsigned idx = 0;
writer.enterSubVector(lang.first.toString().c_str());
for (const std::wstring& str : lang.second)
{
if (comma)
out << ",";
out << "\n/* " << idx++ << " */ \"";
out << wconv.to_bytes(str);
out << "\"";
comma = true;
}
out << "\n});\n";
writer.writeWString(nullptr, str);
writer.leaveSubVector();
}
}

View File

@ -4,15 +4,17 @@
#include <unordered_map>
#include "../DNACommon/DNACommon.hpp"
#include "../DNACommon/STRG.hpp"
#include "DNAMP1.hpp"
namespace Retro
{
namespace DNAMP1
{
struct STRG : ISTRG, BigDNA
struct STRG : ISTRG
{
DECL_EXPLICIT_DNA
DECL_YAML
Delete expl;
void _read(Athena::io::IStreamReader& reader);
std::vector<std::pair<FourCC, std::vector<std::wstring>>> langs;
std::unordered_map<FourCC, std::vector<std::wstring>*> langMap;
@ -56,25 +58,22 @@ struct STRG : ISTRG, BigDNA
return HECL::SystemString();
}
bool readAngelScript(const AngelScript::asIScriptModule& in);
void writeAngelScript(std::ofstream& out) const;
static bool Extract(PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
static bool Extract(const SpecBase&, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
{
STRG strg;
strg.read(rs);
std::ofstream strgOut(outPath.getAbsolutePath());
strg.writeAngelScript(strgOut);
FILE* fp = HECL::Fopen(outPath.getAbsolutePath().c_str(), _S("wb"));
strg.toYAMLFile(fp);
fclose(fp);
return true;
}
static bool Cook(const HECL::ProjectPath& inPath, const HECL::ProjectPath& outPath)
{
STRG strg;
HECL::Database::ASUniqueModule mod = HECL::Database::ASUniqueModule::CreateFromPath(inPath);
if (!mod)
return false;
strg.readAngelScript(mod);
FILE* fp = HECL::Fopen(inPath.getAbsolutePath().c_str(), _S("rb"));
strg.fromYAMLFile(fp);
fclose(fp);
Athena::io::FileWriter ws(outPath.getAbsolutePath());
strg.write(ws);
return true;

35
DataSpec/DNAMP2/CMDL.hpp Normal file
View File

@ -0,0 +1,35 @@
#ifndef _DNAMP2_CMDL_HPP_
#define _DNAMP2_CMDL_HPP_
#include "../DNACommon/DNACommon.hpp"
#include "../DNACommon/CMDL.hpp"
#include "CMDLMaterials.hpp"
#include "DNAMP2.hpp"
namespace Retro
{
namespace DNAMP2
{
struct CMDL
{
static bool Extract(const SpecBase& dataSpec,
PAKEntryReadStream& rs,
const HECL::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter,
const DNAMP1::PAK::Entry& entry,
bool force)
{
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
if (!conn.createBlend(outPath.getAbsolutePath()))
return false;
DNACMDL::ReadCMDLToBlender<PAKRouter<PAKBridge>, MaterialSet, 4>
(conn, rs, pakRouter, entry, dataSpec.getMasterShaderPath());
return conn.saveBlend();
}
};
}
}
#endif // _DNAMP2_CMDL_HPP_

View File

@ -4,6 +4,7 @@
#include "../DNACommon/DNACommon.hpp"
#include "../DNACommon/GX.hpp"
#include "../DNAMP1/CMDLMaterials.hpp"
#include "DNAMP2.hpp"
namespace Retro
{
@ -22,14 +23,16 @@ struct MaterialSet : BigDNA
DNAMP1::MaterialSet::Material::Flags flags;
Value<atUint32> textureCount;
Vector<UniqueID32, DNA_COUNT(textureCount)> texureIdxs;
Vector<atUint32, DNA_COUNT(textureCount)> texureIdxs;
using VAFlags = DNAMP1::MaterialSet::Material::VAFlags;
DNAMP1::MaterialSet::Material::VAFlags vaFlags;
inline const VAFlags& getVAFlags() const {return vaFlags;}
Value<atUint32> unk0; /* MP2 only */
Value<atUint32> unk1; /* MP2 only */
Value<atUint32> groupIdx;
Value<atUint32> konstCount;
Vector<GX::Color, DNA_COUNT(konstCount)> konstColors;
Vector<atUint32, DNA_COUNT(flags.konstValuesEnabled())> konstCount;
Vector<GX::Color, DNA_COUNT(flags.konstValuesEnabled() ? konstCount[0] : 0)> konstColors;
Value<atUint16> _blendDstFac;
using BlendFactor = DNAMP1::MaterialSet::Material::BlendFactor;
@ -48,7 +51,7 @@ struct MaterialSet : BigDNA
Vector<DNAMP1::MaterialSet::Material::TEVStageTexInfo, DNA_COUNT(tevStageCount)> tevStageTexInfo;
Value<atUint32> tcgCount;
Vector<DNAMP1::MaterialSet::Material::TexCoordGen, DNA_COUNT(tcgCount)> tgcs;
Vector<DNAMP1::MaterialSet::Material::TexCoordGen, DNA_COUNT(tcgCount)> tcgs;
Value<atUint32> uvAnimsSize;
Value<atUint32> uvAnimsCount;
@ -56,6 +59,21 @@ struct MaterialSet : BigDNA
};
Vector<Material, DNA_COUNT(head.materialCount)> materials;
static inline void RegisterMaterialProps(HECL::BlenderConnection::PyOutStream& out)
{
DNAMP1::MaterialSet::RegisterMaterialProps(out);
}
static void ConstructMaterial(HECL::BlenderConnection::PyOutStream& out,
const MaterialSet::Material& material,
unsigned groupIdx, unsigned matIdx);
inline void readToBlender(HECL::BlenderConnection::PyOutStream& os,
const PAKRouter<PAKBridge>& pakRouter,
const typename PAKRouter<PAKBridge>::EntryType& entry,
unsigned setIdx)
{
DNACMDL::ReadMaterialSetToBlender_1_2(os, *this, pakRouter, entry, setIdx);
}
};
}

View File

@ -4,4 +4,5 @@ make_dnalist(liblist
add_library(DNAMP2
DNAMP2.hpp DNAMP2.cpp
${liblist}
CMDL.hpp
STRG.hpp STRG.cpp)

View File

@ -2,6 +2,7 @@
#include "DNAMP2.hpp"
#include "STRG.hpp"
#include "MLVL.hpp"
#include "CMDL.hpp"
#include "../DNACommon/TXTR.hpp"
namespace Retro
@ -16,14 +17,11 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
{
NOD::AthenaPartReadStream rs(node.beginReadStream());
m_pak.read(rs);
}
HECL::SystemString PAKBridge::getLevelString() const
{
HECL::SystemString retval;
/* Append Level String */
for (const DNAMP1::PAK::Entry& entry : m_pak.m_entries)
{
if (entry.type == Retro::MLVL)
if (entry.type == SBIG('MLVL'))
{
PAKEntryReadStream rs = entry.beginReadStream(m_node);
MLVL mlvl;
@ -34,23 +32,163 @@ HECL::SystemString PAKBridge::getLevelString() const
PAKEntryReadStream rs = nameEnt->beginReadStream(m_node);
STRG mlvlName;
mlvlName.read(rs);
if (retval.size())
retval += _S(", ");
retval += mlvlName.getSystemString(ENGL, 0);
if (m_levelString.size())
m_levelString += _S(", ");
m_levelString += mlvlName.getSystemString(FOURCC('ENGL'), 0);
}
}
}
}
UniqueResult PAKBridge::uniqueCheck(const DNAMP1::PAK::Entry& entry)
{
UniqueResult::Type result = UniqueResult::UNIQUE_NOTFOUND;
bool foundOneLayer = false;
UniqueID32 areaId;
unsigned layerIdx;
for (const auto& pair : m_areaDeps)
{
unsigned l=0;
for (const auto& layer : pair.second.layers)
{
if (layer.resources.find(entry.id) != layer.resources.end())
{
if (foundOneLayer)
{
if (areaId == pair.first)
result = UniqueResult::UNIQUE_AREA;
else
return {UniqueResult::UNIQUE_LEVEL};
continue;
}
else
result = UniqueResult::UNIQUE_LAYER;
areaId = pair.first;
layerIdx = l;
foundOneLayer = true;
}
++l;
}
if (pair.second.resources.find(entry.id) != pair.second.resources.end())
{
if (foundOneLayer)
{
if (areaId == pair.first)
result = UniqueResult::UNIQUE_AREA;
else
return {UniqueResult::UNIQUE_LEVEL};
continue;
}
else
result = UniqueResult::UNIQUE_AREA;
areaId = pair.first;
foundOneLayer = true;
}
}
UniqueResult retval = {result};
if (result == UniqueResult::UNIQUE_LAYER || result == UniqueResult::UNIQUE_AREA)
{
const PAKBridge::Area& area = m_areaDeps[areaId];
retval.areaName = &area.name;
if (result == UniqueResult::UNIQUE_LAYER)
{
const PAKBridge::Area::Layer& layer = area.layers[layerIdx];
retval.layerName = &layer.name;
}
}
return retval;
}
ResExtractor PAKBridge::LookupExtractor(const DNAMP1::PAK::Entry& entry)
static HECL::SystemString LayerName(const std::string& name)
{
#if HECL_UCS2
HECL::SystemString ret = HECL::UTF8ToWide(mlvl.layerNames[layerIdx++]);
#else
HECL::SystemString ret = name;
#endif
for (auto& ch : ret)
if (ch == _S('/') || ch == _S('\\'))
ch = _S('-');
return ret;
}
void PAKBridge::build()
{
/* First pass: build per-area/per-layer dependency map */
for (const DNAMP1::PAK::Entry& entry : m_pak.m_entries)
{
if (entry.type == SBIG('MLVL'))
{
PAKEntryReadStream rs = entry.beginReadStream(m_node);
MLVL mlvl;
mlvl.read(rs);
m_areaDeps.reserve(mlvl.areaCount);
unsigned layerIdx = 0;
for (const MLVL::Area& area : mlvl.areas)
{
Area& areaDeps = m_areaDeps[area.areaMREAId];
const DNAMP1::PAK::Entry* areaNameEnt = m_pak.lookupEntry(area.areaNameId);
if (areaNameEnt)
{
STRG areaName;
PAKEntryReadStream rs = areaNameEnt->beginReadStream(m_node);
areaName.read(rs);
areaDeps.name = areaName.getSystemString(FOURCC('ENGL'), 0);
}
if (areaDeps.name.empty())
{
areaDeps.name = area.internalAreaName;
#if HECL_UCS2
areaDeps.name = HECL::UTF8ToWide(area.internalAreaName);
#else
areaDeps.name = area.internalAreaName;
#endif
if (areaDeps.name.empty())
{
#if HECL_UCS2
areaDeps.name = _S("MREA_") + HECL::UTF8ToWide(area.areaMREAId.toString());
#else
areaDeps.name = "MREA_" + area.areaMREAId.toString();
#endif
}
}
areaDeps.layers.reserve(area.depLayerCount-1);
unsigned r=0;
for (unsigned l=1 ; l<area.depLayerCount ; ++l)
{
areaDeps.layers.emplace_back();
Area::Layer& layer = areaDeps.layers.back();
layer.name = LayerName(mlvl.layerNames[layerIdx++]);
layer.resources.reserve(area.depLayers[l] - r);
for (; r<area.depLayers[l] ; ++r)
layer.resources.emplace(area.deps[r].id);
}
areaDeps.resources.reserve(area.depCount - r);
for (; r<area.depCount ; ++r)
areaDeps.resources.emplace(area.deps[r].id);
areaDeps.resources.emplace(area.areaMREAId);
}
}
}
/* Second pass: cross-compare uniqueness */
for (DNAMP1::PAK::Entry& entry : m_pak.m_entries)
{
entry.unique = uniqueCheck(entry);
}
}
ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const DNAMP1::PAK::Entry& entry)
{
switch (entry.type.toUint32())
{
case SBIG('STRG'):
return {STRG::Extract, ".as"};
return {STRG::Extract, nullptr, ".yaml"};
case SBIG('TXTR'):
return {TXTR::Extract, ".png"};
return {TXTR::Extract, nullptr, ".png"};
case SBIG('CMDL'):
return {nullptr, CMDL::Extract, ".blend", 1};
}
return {};
}

View File

@ -17,11 +17,26 @@ class PAKBridge
HECL::Database::Project& m_project;
const NOD::DiscBase::IPartition::Node& m_node;
DNAMP1::PAK m_pak;
struct Area
{
HECL::SystemString name;
struct Layer
{
HECL::SystemString name;
std::unordered_set<UniqueID32> resources;
};
std::vector<Layer> layers;
std::unordered_set<UniqueID32> resources;
};
std::unordered_map<UniqueID32, Area> m_areaDeps;
HECL::SystemString m_levelString;
UniqueResult uniqueCheck(const DNAMP1::PAK::Entry& entry);
public:
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
static ResExtractor LookupExtractor(const DNAMP1::PAK::Entry& entry);
const std::string& getName() const {return m_node.getName();}
HECL::SystemString getLevelString() const;
void build();
static ResExtractor<PAKBridge> LookupExtractor(const DNAMP1::PAK::Entry& entry);
inline const std::string& getName() const {return m_node.getName();}
inline const HECL::SystemString& getLevelString() const {return m_levelString;}
typedef DNAMP1::PAK PAKType;
inline const PAKType& getPAK() const {return m_pak;}

View File

@ -70,6 +70,7 @@ struct MLVL : BigDNA
String<-1> internalAreaName;
};
Vector<Area, DNA_COUNT(areaCount)> areas;
UniqueID32 worldMap;
Value<atUint8> unknown2;

View File

@ -139,42 +139,86 @@ void STRG::write(Athena::io::IStreamWriter& writer) const
}
}
bool STRG::readAngelScript(const AngelScript::asIScriptModule& in)
{
return false;
}
void STRG::writeAngelScript(std::ofstream& out) const
void STRG::fromYAML(Athena::io::YAMLDocReader& reader)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> wconv;
for (const std::pair<FourCC, std::vector<std::wstring>>& lang : langs)
const Athena::io::YAMLNode* root = reader.getRootNode();
/* Validate Pass */
if (root->m_type == YAML_MAPPING_NODE)
{
out << "STRG::Language " << lang.first.toString() << "({";
bool comma = false;
unsigned idx = 0;
for (const std::wstring& str : lang.second)
for (const auto& lang : root->m_mapChildren)
{
if (comma)
out << ",";
out << "\n/* " << idx++ << " */ \"";
out << wconv.to_bytes(str);
out << "\"";
comma = true;
if (!lang.first.compare("names"))
continue;
if (lang.first.size() != 4)
{
Log.report(LogVisor::Warning, "STRG language string '%s' must be exactly 4 characters; skipping", lang.first.c_str());
return;
}
if (lang.second->m_type != YAML_SEQUENCE_NODE)
{
Log.report(LogVisor::Warning, "STRG language string '%s' must contain a sequence; skipping", lang.first.c_str());
return;
}
for (const auto& str : lang.second->m_seqChildren)
{
if (str->m_type != YAML_SCALAR_NODE)
{
Log.report(LogVisor::Warning, "STRG language '%s' must contain all scalars; skipping", lang.first.c_str());
return;
}
}
}
out << "\n});\n";
}
out << "STRG::Names NAMES({";
bool comma = false;
for (const std::pair<std::string, int32_t>& name : names)
else
{
if (comma)
out << ",";
out << "\n ";
comma = true;
out << "{\"" << name.first << "\", " << name.second << "}";
Log.report(LogVisor::Warning, "STRG must have a mapping root node; skipping");
return;
}
/* Read Pass */
langs.clear();
for (const auto& lang : root->m_mapChildren)
{
std::vector<std::wstring> strs;
for (const auto& str : lang.second->m_seqChildren)
strs.emplace_back(wconv.from_bytes(str->m_scalarString));
langs.emplace_back(FourCC(lang.first.c_str()), strs);
}
names.clear();
const Athena::io::YAMLNode* namesNode = root->findMapChild("names");
if (namesNode)
for (const auto& item : namesNode->m_mapChildren)
names[item.first] = Athena::io::NodeToVal<atInt32>(item.second.get());
langMap.clear();
langMap.reserve(langs.size());
for (std::pair<FourCC, std::vector<std::wstring>>& item : langs)
langMap.emplace(item.first, &item.second);
}
void STRG::toYAML(Athena::io::YAMLDocWriter& writer) const
{
for (const auto& lang : langs)
{
writer.enterSubVector(lang.first.toString().c_str());
for (const std::wstring& str : lang.second)
writer.writeWString(nullptr, str);
writer.leaveSubVector();
}
if (names.size())
{
writer.enterSubRecord("names");
for (const auto& name : names)
{
writer.enterSubRecord(name.first.c_str());
writer.writeInt32(nullptr, name.second);
writer.leaveSubRecord();
}
writer.leaveSubRecord();
}
out << "\n});\n";
}
}

View File

@ -10,9 +10,10 @@ namespace Retro
namespace DNAMP2
{
struct STRG : ISTRG, BigDNA
struct STRG : ISTRG
{
DECL_EXPLICIT_DNA
DECL_YAML
Delete expl;
void _read(Athena::io::IStreamReader& reader);
std::vector<std::pair<FourCC, std::vector<std::wstring>>> langs;
std::unordered_map<FourCC, std::vector<std::wstring>*> langMap;
@ -63,25 +64,22 @@ struct STRG : ISTRG, BigDNA
return HECL::SystemString();
}
bool readAngelScript(const AngelScript::asIScriptModule& in);
void writeAngelScript(std::ofstream& out) const;
static bool Extract(PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
static bool Extract(const SpecBase&, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
{
STRG strg;
strg.read(rs);
std::ofstream strgOut(outPath.getAbsolutePath());
strg.writeAngelScript(strgOut);
FILE* fp = HECL::Fopen(outPath.getAbsolutePath().c_str(), _S("wb"));
strg.toYAMLFile(fp);
fclose(fp);
return true;
}
static bool Cook(const HECL::ProjectPath& inPath, const HECL::ProjectPath& outPath)
{
STRG strg;
HECL::Database::ASUniqueModule mod = HECL::Database::ASUniqueModule::CreateFromPath(inPath);
if (!mod)
return false;
strg.readAngelScript(mod);
FILE* fp = HECL::Fopen(inPath.getAbsolutePath().c_str(), _S("rb"));
strg.fromYAMLFile(fp);
fclose(fp);
Athena::io::FileWriter ws(outPath.getAbsolutePath());
strg.write(ws);
return true;

35
DataSpec/DNAMP3/CMDL.hpp Normal file
View File

@ -0,0 +1,35 @@
#ifndef _DNAMP3_CMDL_HPP_
#define _DNAMP3_CMDL_HPP_
#include "../DNACommon/DNACommon.hpp"
#include "../DNACommon/CMDL.hpp"
#include "CMDLMaterials.hpp"
#include "DNAMP3.hpp"
namespace Retro
{
namespace DNAMP3
{
struct CMDL
{
static bool Extract(const SpecBase& dataSpec,
PAKEntryReadStream& rs,
const HECL::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry,
bool force)
{
HECL::BlenderConnection& conn = HECL::BlenderConnection::SharedConnection();
if (!conn.createBlend(outPath.getAbsolutePath()))
return false;
DNACMDL::ReadCMDLToBlender<PAKRouter<PAKBridge>, MaterialSet, 5>
(conn, rs, pakRouter, entry, dataSpec.getMasterShaderPath());
return conn.saveBlend();
}
};
}
}
#endif // _DNAMP3_CMDL_HPP_

View File

@ -0,0 +1,19 @@
#include "CMDLMaterials.hpp"
using Stream = HECL::BlenderConnection::PyOutStream;
namespace Retro
{
namespace DNAMP3
{
void MaterialSet::ConstructMaterial(Stream& out,
const MaterialSet::Material& material,
unsigned groupIdx,
unsigned matIdx)
{
}
}
}

View File

@ -0,0 +1,203 @@
#ifndef _DNAMP3_CMDL_MATERIALS_HPP_
#define _DNAMP3_CMDL_MATERIALS_HPP_
#include "../DNACommon/DNACommon.hpp"
#include "../DNACommon/GX.hpp"
#include "../DNAMP1/CMDLMaterials.hpp"
#include "DNAMP3.hpp"
namespace Retro
{
namespace DNAMP3
{
struct MaterialSet : BigDNA
{
DECL_DNA
Value<atUint32> materialCount;
struct Material : BigDNA
{
Delete expl;
using VAFlags = DNAMP1::MaterialSet::Material::VAFlags;
struct Header : BigDNA
{
DECL_DNA
Value<atUint32> size;
struct Flags : BigDNA
{
DECL_DNA
Value<atUint32> flags;
inline bool alphaBlending() const {return (flags & 0x8) != 0;}
inline void setAlphaBlending(bool enabled) {flags &= ~0x8; flags |= atUint32(enabled) << 3;}
inline bool punchthroughAlpha() const {return (flags & 0x10) != 0;}
inline void setPunchthroughAlpha(bool enabled) {flags &= ~0x10; flags |= atUint32(enabled) << 4;}
inline bool additiveBlending() const {return (flags & 0x20) != 0;}
inline void setAdditiveBlending(bool enabled) {flags &= ~0x20; flags |= atUint32(enabled) << 5;}
inline bool shadowOccluderMesh() const {return (flags & 0x100) != 0;}
inline void setShadowOccluderMesh(bool enabled) {flags &= ~0x100; flags |= atUint32(enabled) << 8;}
} flags;
Value<atUint32> groupIdx;
Value<atUint32> unk1;
DNAMP1::MaterialSet::Material::VAFlags vaFlags;
Value<atUint32> unk2;
Value<atUint32> unk3;
Value<atUint32> unk4;
} header;
inline const VAFlags& getVAFlags() const {return header.vaFlags;}
struct ISection : BigDNA
{
Delete expl;
enum Type
{
PASS = SBIG('PASS'),
CLR = SBIG('CLR '),
INT = SBIG('INT ')
} m_type;
ISection(Type type) : m_type(type) {}
};
struct SectionPASS : ISection
{
SectionPASS() : ISection(ISection::PASS) {}
DECL_DNA
Value<atUint32> size;
enum Subtype
{
DIFF = SBIG('DIFF'),
RIML = SBIG('RIML'),
BLOL = SBIG('BLOL'),
BLOD = SBIG('BLOD'),
CLR = SBIG('CLR '),
TRAN = SBIG('TRAN'),
INCA = SBIG('INCA'),
RFLV = SBIG('RFLV'),
RFLD = SBIG('RFLD'),
LRLD = SBIG('LRLD'),
LURD = SBIG('LURD'),
BLOI = SBIG('BLOI'),
XRAY = SBIG('XRAY'),
TOON = SBIG('TOON')
};
FourCC subtype;
struct Flags : BigDNA
{
DECL_DNA
Value<atUint32> flags;
} flags;
UniqueID64 txtrId;
Value<atUint32> uvSrc;
Value<atUint32> uvAnimSize;
struct UVAnimation : BigDNA
{
DECL_DNA
Value<atUint16> unk1;
Value<atUint16> unk2;
DNAMP1::MaterialSet::Material::UVAnimation anim;
};
Vector<UVAnimation, DNA_COUNT(uvAnimSize != 0)> uvAnim;
};
struct SectionCLR : ISection
{
SectionCLR() : ISection(ISection::CLR) {}
DECL_DNA
enum Subtype
{
CLR = SBIG('CLR '),
DIFB = SBIG('DIFB')
};
FourCC subtype;
GX::Color color;
};
struct SectionINT : ISection
{
SectionINT() : ISection(ISection::INT) {}
DECL_DNA
enum Subtype
{
OPAC = SBIG('OPAC'),
BLOD = SBIG('BLOD'),
BLOI = SBIG('BLOI'),
BNIF = SBIG('BNIF'),
XRBR = SBIG('XRBR')
};
FourCC subtype;
Value<atUint32> value;
};
struct SectionFactory : BigDNA
{
Delete expl;
std::unique_ptr<ISection> section;
void read(Athena::io::IStreamReader& reader)
{
FourCC type;
type.read(reader);
switch (type)
{
case ISection::PASS:
section.reset(new struct SectionPASS);
section->read(reader);
break;
case ISection::CLR:
section.reset(new struct SectionCLR);
section->read(reader);
break;
case ISection::INT:
section.reset(new struct SectionINT);
section->read(reader);
break;
default:
section.reset(nullptr);
break;
}
}
void write(Athena::io::IStreamWriter& writer) const
{
if (!section)
return;
writer.writeUBytes((atUint8*)&section->m_type, 4);
section->write(writer);
}
};
std::vector<SectionFactory> sections;
void read(Athena::io::IStreamReader& reader)
{
header.read(reader);
sections.clear();
do {
sections.emplace_back();
sections.back().read(reader);
} while (sections.back().section);
sections.pop_back();
}
void write(Athena::io::IStreamWriter& writer) const
{
header.write(writer);
for (const SectionFactory& section : sections)
section.write(writer);
writer.writeUBytes((atUint8*)"END ", 4);
}
};
Vector<Material, DNA_COUNT(materialCount)> materials;
static inline void RegisterMaterialProps(HECL::BlenderConnection::PyOutStream& out)
{
DNAMP1::MaterialSet::RegisterMaterialProps(out);
}
static void ConstructMaterial(HECL::BlenderConnection::PyOutStream& out,
const MaterialSet::Material& material,
unsigned groupIdx, unsigned matIdx);
inline void readToBlender(HECL::BlenderConnection::PyOutStream& os,
const PAKRouter<PAKBridge>& pakRouter,
const typename PAKRouter<PAKBridge>::EntryType& entry,
unsigned setIdx)
{
DNACMDL::ReadMaterialSetToBlender_3(os, *this, pakRouter, entry, setIdx);
}
};
}
}
#endif // _DNAMP3_CMDL_MATERIALS_HPP_

View File

@ -1,8 +1,11 @@
make_dnalist(liblist
PAK
MLVL)
MLVL
CMDLMaterials)
add_library(DNAMP3
DNAMP3.hpp DNAMP3.cpp
${liblist}
PAK.cpp
CMDL.hpp
CMDLMaterials.cpp
STRG.hpp STRG.cpp)

View File

@ -4,6 +4,7 @@
#include "DNAMP3.hpp"
#include "STRG.hpp"
#include "MLVL.hpp"
#include "CMDL.hpp"
#include "../DNACommon/TXTR.hpp"
namespace Retro
@ -18,14 +19,12 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
{
NOD::AthenaPartReadStream rs(node.beginReadStream());
m_pak.read(rs);
}
HECL::SystemString PAKBridge::getLevelString() const
{
/* Append Level String */
std::set<HECL::SystemString, CaseInsensitiveCompare> uniq;
for (const PAK::Entry& entry : m_pak.m_entries)
{
if (entry.type == Retro::MLVL)
if (entry.type == SBIG('MLVL'))
{
PAKEntryReadStream rs = entry.beginReadStream(m_node);
MLVL mlvl;
@ -36,30 +35,34 @@ HECL::SystemString PAKBridge::getLevelString() const
PAKEntryReadStream rs = nameEnt->beginReadStream(m_node);
STRG mlvlName;
mlvlName.read(rs);
uniq.insert(mlvlName.getSystemString(ENGL, 0));
uniq.insert(mlvlName.getSystemString(FOURCC('ENGL'), 0));
}
}
}
HECL::SystemString retval;
bool comma = false;
for (const HECL::SystemString& str : uniq)
{
if (comma)
retval += _S(", ");
m_levelString += _S(", ");
comma = true;
retval += str;
m_levelString += str;
}
return retval;
}
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry)
void PAKBridge::build()
{
}
ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const PAK::Entry& entry)
{
switch (entry.type.toUint32())
{
case SBIG('STRG'):
return {STRG::Extract, ".as"};
return {STRG::Extract, nullptr, ".yaml"};
case SBIG('TXTR'):
return {TXTR::Extract, ".png"};
return {TXTR::Extract, nullptr, ".png"};
case SBIG('CMDL'):
return {nullptr, CMDL::Extract, ".blend", 1};
}
return {};
}

View File

@ -17,11 +17,13 @@ class PAKBridge
HECL::Database::Project& m_project;
const NOD::DiscBase::IPartition::Node& m_node;
PAK m_pak;
HECL::SystemString m_levelString;
public:
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
static ResExtractor LookupExtractor(const PAK::Entry& entry);
const std::string& getName() const {return m_node.getName();}
HECL::SystemString getLevelString() const;
void build();
static ResExtractor<PAKBridge> LookupExtractor(const PAK::Entry& entry);
inline const std::string& getName() const {return m_node.getName();}
inline HECL::SystemString getLevelString() const {return m_levelString;}
typedef PAK PAKType;
inline const PAKType& getPAK() const {return m_pak;}

View File

@ -41,6 +41,7 @@ struct PAK : BigDNA
UniqueID64 id;
Value<atUint32> size;
Value<atUint32> offset;
UniqueResult unique;
std::unique_ptr<atUint8[]> getBuffer(const NOD::DiscBase::IPartition::Node& pak, atUint64& szOut) const;
inline PAKEntryReadStream beginReadStream(const NOD::DiscBase::IPartition::Node& pak, atUint64 off=0) const

View File

@ -86,6 +86,72 @@ void STRG::read(Athena::io::IStreamReader& reader)
_read(reader);
}
void STRG::fromYAML(Athena::io::YAMLDocReader& reader)
{
const Athena::io::YAMLNode* root = reader.getRootNode();
/* Validate Pass */
if (root->m_type == YAML_MAPPING_NODE)
{
for (const auto& lang : root->m_mapChildren)
{
if (!lang.first.compare("names"))
continue;
if (lang.first.size() != 4)
{
Log.report(LogVisor::Warning, "STRG language string '%s' must be exactly 4 characters; skipping", lang.first.c_str());
return;
}
if (lang.second->m_type != YAML_SEQUENCE_NODE)
{
Log.report(LogVisor::Warning,
"STRG language string '%s' must contain a sequence; skipping", lang.first.c_str());
return;
}
for (const auto& str : lang.second->m_seqChildren)
{
if (str->m_type != YAML_SCALAR_NODE)
{
Log.report(LogVisor::Warning, "STRG language '%s' must contain all scalars; skipping", lang.first.c_str());
return;
}
}
}
}
else
{
Log.report(LogVisor::Warning, "STRG must have a mapping root node; skipping");
return;
}
const Athena::io::YAMLNode* nameYAML = root->findMapChild("names");
names.clear();
if (nameYAML && nameYAML->m_type == YAML_MAPPING_NODE)
for (const auto& item : nameYAML->m_mapChildren)
if (item.second->m_type == YAML_SCALAR_NODE)
names[item.first] = Athena::io::NodeToVal<atInt32>(item.second.get());
langs.clear();
langs.reserve(root->m_mapChildren.size());
for (const auto& item : root->m_mapChildren)
{
if (!item.first.compare("names") || item.first.size() != 4 ||
item.second->m_type != YAML_SEQUENCE_NODE)
continue;
std::vector<std::string> strs;
for (const auto& node : item.second->m_seqChildren)
if (node->m_type == YAML_SCALAR_NODE)
strs.emplace_back(node->m_scalarString);
langs.emplace_back(std::make_pair(FourCC(item.first.c_str()), std::move(strs)));
}
langMap.clear();
langMap.reserve(langs.size());
for (std::pair<FourCC, std::vector<std::string>>& item : langs)
langMap.emplace(item.first, &item.second);
}
void STRG::write(Athena::io::IStreamWriter& writer) const
{
writer.setEndian(Athena::BigEndian);
@ -147,40 +213,27 @@ void STRG::write(Athena::io::IStreamWriter& writer) const
}
}
bool STRG::readAngelScript(const AngelScript::asIScriptModule& in)
void STRG::toYAML(Athena::io::YAMLDocWriter& writer) const
{
return false;
}
void STRG::writeAngelScript(std::ofstream& out) const
{
for (const std::pair<FourCC, std::vector<std::string>>& lang : langs)
for (const auto& item : langs)
{
out << "STRG::Language " << lang.first.toString() << "({";
bool comma = false;
unsigned idx = 0;
for (const std::string& str : lang.second)
writer.enterSubVector(item.first.toString().c_str());
for (const std::string& str : item.second)
writer.writeString(nullptr, str);
writer.leaveSubVector();
}
if (names.size())
{
writer.enterSubRecord("names");
for (const auto& item : names)
{
if (comma)
out << ",";
out << "\n/* " << idx++ << " */ \"";
out << str << "\"";
comma = true;
writer.enterSubRecord(item.first.c_str());
writer.writeInt32(nullptr, item.second);
writer.leaveSubRecord();
}
out << "\n});\n";
writer.leaveSubRecord();
}
out << "STRG::Names NAMES({";
bool comma = false;
for (const std::pair<std::string, int32_t>& name : names)
{
if (comma)
out << ",";
out << "\n ";
comma = true;
out << "{\"" << name.first << "\", " << name.second << "}";
}
out << "\n});\n";
}
}

View File

@ -10,9 +10,10 @@ namespace Retro
namespace DNAMP3
{
struct STRG : ISTRG, BigDNA
struct STRG : ISTRG
{
DECL_EXPLICIT_DNA
DECL_YAML
Delete expl;
void _read(Athena::io::IStreamReader& reader);
std::vector<std::pair<FourCC, std::vector<std::string>>> langs;
std::unordered_map<FourCC, std::vector<std::string>*> langMap;
@ -63,24 +64,21 @@ struct STRG : ISTRG, BigDNA
return HECL::SystemString();
}
bool readAngelScript(const AngelScript::asIScriptModule& in);
void writeAngelScript(std::ofstream& out) const;
static bool Extract(PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
static bool Extract(const SpecBase&, PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
{
std::unique_ptr<ISTRG> strg = LoadSTRG(rs);
std::ofstream strgOut(outPath.getAbsolutePath());
strg->writeAngelScript(strgOut);
FILE* fp = HECL::Fopen(outPath.getAbsolutePath().c_str(), _S("wb"));
strg->toYAMLFile(fp);
fclose(fp);
return true;
}
static bool Cook(const HECL::ProjectPath& inPath, const HECL::ProjectPath& outPath)
{
STRG strg;
HECL::Database::ASUniqueModule mod = HECL::Database::ASUniqueModule::CreateFromPath(inPath);
if (!mod)
return false;
strg.readAngelScript(mod);
FILE* fp = HECL::Fopen(inPath.getAbsolutePath().c_str(), _S("rb"));
strg.fromYAMLFile(fp);
fclose(fp);
Athena::io::FileWriter ws(outPath.getAbsolutePath());
strg.write(ws);
return true;

View File

@ -1,10 +1,12 @@
#include "SpecBase.hpp"
#include "Blender/BlenderSupport.hpp"
namespace Retro
{
bool SpecBase::canExtract(HECL::Database::Project& project,
const ExtractPassInfo& info, std::vector<ExtractReport>& reps)
static LogVisor::LogModule Log("Retro::SpecBase");
bool SpecBase::canExtract(const ExtractPassInfo& info, std::vector<ExtractReport>& reps)
{
m_disc = NOD::OpenDiscFromImage(info.srcpath.c_str(), m_isWii);
if (!m_disc)
@ -38,18 +40,19 @@ bool SpecBase::canExtract(HECL::Database::Project& project,
}
if (m_standalone)
return checkFromStandaloneDisc(project, *m_disc.get(), *regstr, info.extractArgs, reps);
return checkFromStandaloneDisc(*m_disc, *regstr, info.extractArgs, reps);
else
return checkFromTrilogyDisc(project, *m_disc.get(), *regstr, info.extractArgs, reps);
return checkFromTrilogyDisc(*m_disc, *regstr, info.extractArgs, reps);
}
void SpecBase::doExtract(HECL::Database::Project& project, const ExtractPassInfo& info,
FExtractProgress progress)
void SpecBase::doExtract(const ExtractPassInfo& info, FExtractProgress progress)
{
if (!Blender::BuildMasterShader(m_masterShader))
Log.report(LogVisor::FatalError, "Unable to build master shader blend");
if (m_isWii)
{
/* Extract update partition for repacking later */
const HECL::SystemString& target = project.getProjectRootPath().getAbsolutePath();
const HECL::SystemString& target = m_project.getProjectRootPath().getAbsolutePath();
NOD::DiscBase::IPartition* update = m_disc->getUpdatePartition();
if (update)
{
@ -69,29 +72,29 @@ void SpecBase::doExtract(HECL::Database::Project& project, const ExtractPassInfo
progress(_S("Trilogy Files"), 1, 1.0);
}
}
extractFromDisc(project, *m_disc.get(), info.force, progress);
extractFromDisc(*m_disc, info.force, progress);
}
bool SpecBase::canCook(const HECL::Database::Project& project, const CookTaskInfo& info)
bool SpecBase::canCook(const CookTaskInfo& info)
{
return false;
}
void SpecBase::doCook(const HECL::Database::Project& project, const CookTaskInfo& info)
void SpecBase::doCook(const CookTaskInfo& info)
{
}
bool SpecBase::canPackage(const HECL::Database::Project& project, const PackagePassInfo& info)
bool SpecBase::canPackage(const PackagePassInfo& info)
{
return false;
}
void SpecBase::gatherDependencies(const HECL::Database::Project& project, const PackagePassInfo& info,
void SpecBase::gatherDependencies(const PackagePassInfo& info,
std::unordered_set<HECL::ProjectPath>& implicitsOut)
{
}
void SpecBase::doPackage(const HECL::Database::Project& project, const PackagePassInfo& info)
void SpecBase::doPackage(const PackagePassInfo& info)
{
}

View File

@ -11,34 +11,31 @@ namespace Retro
struct SpecBase : HECL::Database::IDataSpec
{
bool canExtract(HECL::Database::Project& project, const ExtractPassInfo& info,
std::vector<ExtractReport>& reps);
void doExtract(HECL::Database::Project& project, const ExtractPassInfo& info, FExtractProgress progress);
bool canExtract(const ExtractPassInfo& info, std::vector<ExtractReport>& reps);
void doExtract(const ExtractPassInfo& info, FExtractProgress progress);
bool canCook(const HECL::Database::Project& project, const CookTaskInfo& info);
void doCook(const HECL::Database::Project& project, const CookTaskInfo& info);
bool canCook(const CookTaskInfo& info);
void doCook(const CookTaskInfo& info);
bool canPackage(const HECL::Database::Project& project, const PackagePassInfo& info);
void gatherDependencies(const HECL::Database::Project& project, const PackagePassInfo& info,
bool canPackage(const PackagePassInfo& info);
void gatherDependencies(const PackagePassInfo& info,
std::unordered_set<HECL::ProjectPath>& implicitsOut);
void doPackage(const HECL::Database::Project& project, const PackagePassInfo& info);
void doPackage(const PackagePassInfo& info);
virtual bool checkStandaloneID(const char* id) const=0;
virtual bool checkFromStandaloneDisc(HECL::Database::Project& project,
NOD::DiscBase& disc,
virtual bool checkFromStandaloneDisc(NOD::DiscBase& disc,
const HECL::SystemString& regstr,
const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)=0;
virtual bool checkFromTrilogyDisc(HECL::Database::Project& project,
NOD::DiscBase& disc,
virtual bool checkFromTrilogyDisc(NOD::DiscBase& disc,
const HECL::SystemString& regstr,
const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)=0;
virtual bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase& disc, bool force,
virtual bool extractFromDisc(NOD::DiscBase& disc, bool force,
FExtractProgress progress)=0;
virtual bool checkFromProject(HECL::Database::Project& proj)=0;
virtual bool readFromProject(HECL::Database::Project& proj)=0;
virtual bool checkFromProject()=0;
virtual bool readFromProject()=0;
virtual bool visitGameObjects(std::function<bool(const HECL::Database::ObjectBase&)>)=0;
struct ILevelSpec
@ -52,6 +49,14 @@ struct SpecBase : HECL::Database::IDataSpec
};
virtual bool visitLevels(std::function<bool(const ILevelSpec&)>)=0;
inline const HECL::ProjectPath& getMasterShaderPath() const {return m_masterShader;}
SpecBase(HECL::Database::Project& project)
: m_project(project),
m_masterShader(project.getProjectRootPath(), ".hecl/RetroMasterShader.blend") {}
protected:
HECL::Database::Project& m_project;
HECL::ProjectPath m_masterShader;
private:
std::unique_ptr<NOD::DiscBase> m_disc;
bool m_isWii;

View File

@ -20,7 +20,6 @@ struct SpecMP1 : SpecBase
return false;
}
bool doMP1 = false;
std::vector<const NOD::DiscBase::IPartition::Node*> m_nonPaks;
std::vector<DNAMP1::PAKBridge> m_paks;
std::map<std::string, DNAMP1::PAKBridge*, CaseInsensitiveCompare> m_orderedPaks;
@ -30,9 +29,10 @@ struct SpecMP1 : SpecBase
PAKRouter<DNAMP1::PAKBridge> m_pakRouter;
SpecMP1(HECL::Database::Project& project)
: m_workPath(project.getProjectRootPath(), _S("MP1")),
: SpecBase(project),
m_workPath(project.getProjectRootPath(), _S("MP1")),
m_cookPath(project.getProjectCookedPath(SpecEntMP1), _S("MP1")),
m_pakRouter(m_workPath, m_cookPath) {}
m_pakRouter(*this, m_workPath, m_cookPath) {}
void buildPaks(HECL::Database::Project& project,
NOD::DiscBase::IPartition::Node& root,
@ -116,13 +116,11 @@ struct SpecMP1 : SpecBase
}
}
bool checkFromStandaloneDisc(HECL::Database::Project& project,
NOD::DiscBase& disc,
bool checkFromStandaloneDisc(NOD::DiscBase& disc,
const HECL::SystemString& regstr,
const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)
{
doMP1 = true;
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
std::unique_ptr<uint8_t[]> dolBuf = partition->getDOLBuf();
const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19;
@ -141,18 +139,18 @@ struct SpecMP1 : SpecBase
/* Iterate PAKs and build level options */
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
buildPaks(project, root, args, rep);
buildPaks(m_project, root, args, rep);
return true;
}
bool checkFromTrilogyDisc(HECL::Database::Project& project,
NOD::DiscBase& disc,
bool checkFromTrilogyDisc(NOD::DiscBase& disc,
const HECL::SystemString& regstr,
const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)
{
std::vector<HECL::SystemString> mp1args;
bool doExtract = false;
if (args.size())
{
/* Needs filter */
@ -162,7 +160,7 @@ struct SpecMP1 : SpecBase
HECL::ToLower(lowerArg);
if (!lowerArg.compare(0, 3, _S("mp1")))
{
doMP1 = true;
doExtract = true;
size_t slashPos = arg.find(_S('/'));
if (slashPos == HECL::SystemString::npos)
slashPos = arg.find(_S('\\'));
@ -172,10 +170,10 @@ struct SpecMP1 : SpecBase
}
}
else
doMP1 = true;
doExtract = true;
if (!doMP1)
return true;
if (!doExtract)
return false;
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
@ -202,17 +200,13 @@ struct SpecMP1 : SpecBase
NOD::DiscBase::IPartition::Node::DirectoryIterator mp1It = root.find("MP1");
if (mp1It == root.end())
return false;
buildPaks(project, *mp1It, mp1args, rep);
buildPaks(m_project, *mp1It, mp1args, rep);
return true;
}
bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase&, bool force,
FExtractProgress progress)
bool extractFromDisc(NOD::DiscBase&, bool force, FExtractProgress progress)
{
if (!doMP1)
return true;
progress(_S("Indexing PAKs"), 2, 0.0);
m_pakRouter.build(m_paks, [&progress](float factor)
{
@ -230,14 +224,15 @@ struct SpecMP1 : SpecBase
}
progress(_S("MP1 Root"), 3, 1.0);
const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP1);
const HECL::ProjectPath& cookPath = m_project.getProjectCookedPath(SpecEntMP1);
cookPath.makeDir();
m_cookPath.makeDir();
int compIdx = 4;
prog = 0;
for (DNAMP1::PAKBridge& pak : m_paks)
for (std::pair<std::string, DNAMP1::PAKBridge*> pair : m_orderedPaks)
{
DNAMP1::PAKBridge& pak = *pair.second;
const std::string& name = pak.getName();
HECL::SystemStringView sysName(name);
@ -253,11 +248,11 @@ struct SpecMP1 : SpecBase
return true;
}
bool checkFromProject(HECL::Database::Project& proj)
bool checkFromProject()
{
return false;
}
bool readFromProject(HECL::Database::Project& proj)
bool readFromProject()
{
return false;
}

View File

@ -18,7 +18,6 @@ struct SpecMP2 : SpecBase
return false;
}
bool doMP2 = false;
std::vector<const NOD::DiscBase::IPartition::Node*> m_nonPaks;
std::vector<DNAMP2::PAKBridge> m_paks;
std::map<std::string, DNAMP2::PAKBridge*, CaseInsensitiveCompare> m_orderedPaks;
@ -28,12 +27,12 @@ struct SpecMP2 : SpecBase
PAKRouter<DNAMP2::PAKBridge> m_pakRouter;
SpecMP2(HECL::Database::Project& project)
: m_workPath(project.getProjectRootPath(), _S("MP2")),
: SpecBase(project),
m_workPath(project.getProjectRootPath(), _S("MP2")),
m_cookPath(project.getProjectCookedPath(SpecEntMP2), _S("MP2")),
m_pakRouter(m_workPath, m_cookPath) {}
m_pakRouter(*this, m_workPath, m_cookPath) {}
void buildPaks(HECL::Database::Project& project,
NOD::DiscBase::IPartition::Node& root,
void buildPaks(NOD::DiscBase::IPartition::Node& root,
const std::vector<HECL::SystemString>& args,
ExtractReport& rep)
{
@ -89,7 +88,7 @@ struct SpecMP2 : SpecBase
}
if (good)
m_paks.emplace_back(project, child);
m_paks.emplace_back(m_project, child);
}
}
@ -113,13 +112,11 @@ struct SpecMP2 : SpecBase
}
}
bool checkFromStandaloneDisc(HECL::Database::Project& project,
NOD::DiscBase& disc,
bool checkFromStandaloneDisc(NOD::DiscBase& disc,
const HECL::SystemString& regstr,
const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)
{
doMP2 = true;
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
std::unique_ptr<uint8_t[]> dolBuf = partition->getDOLBuf();
const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19;
@ -138,18 +135,18 @@ struct SpecMP2 : SpecBase
/* Iterate PAKs and build level options */
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
buildPaks(project, root, args, rep);
buildPaks(root, args, rep);
return true;
}
bool checkFromTrilogyDisc(HECL::Database::Project& project,
NOD::DiscBase& disc,
bool checkFromTrilogyDisc(NOD::DiscBase& disc,
const HECL::SystemString& regstr,
const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)
{
std::vector<HECL::SystemString> mp2args;
bool doExtract = false;
if (args.size())
{
/* Needs filter */
@ -159,7 +156,7 @@ struct SpecMP2 : SpecBase
HECL::ToLower(lowerArg);
if (!lowerArg.compare(0, 3, _S("mp2")))
{
doMP2 = true;
doExtract = true;
size_t slashPos = arg.find(_S('/'));
if (slashPos == HECL::SystemString::npos)
slashPos = arg.find(_S('\\'));
@ -169,10 +166,10 @@ struct SpecMP2 : SpecBase
}
}
else
doMP2 = true;
doExtract = true;
if (!doMP2)
return true;
if (!doExtract)
return false;
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
@ -199,17 +196,13 @@ struct SpecMP2 : SpecBase
NOD::DiscBase::IPartition::Node::DirectoryIterator mp2It = root.find("MP2");
if (mp2It == root.end())
return false;
buildPaks(project, *mp2It, mp2args, rep);
buildPaks(*mp2It, mp2args, rep);
return true;
}
bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase&, bool force,
FExtractProgress progress)
bool extractFromDisc(NOD::DiscBase&, bool force, FExtractProgress progress)
{
if (!doMP2)
return true;
progress(_S("Indexing PAKs"), 2, 0.0);
m_pakRouter.build(m_paks, [&progress](float factor)
{
@ -227,14 +220,15 @@ struct SpecMP2 : SpecBase
}
progress(_S("MP2 Root"), 3, 1.0);
const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP2);
const HECL::ProjectPath& cookPath = m_project.getProjectCookedPath(SpecEntMP2);
cookPath.makeDir();
m_cookPath.makeDir();
int compIdx = 4;
prog = 0;
for (DNAMP2::PAKBridge& pak : m_paks)
for (std::pair<std::string, DNAMP2::PAKBridge*> pair : m_orderedPaks)
{
DNAMP2::PAKBridge& pak = *pair.second;
const std::string& name = pak.getName();
HECL::SystemStringView sysName(name);
@ -250,11 +244,11 @@ struct SpecMP2 : SpecBase
return true;
}
bool checkFromProject(HECL::Database::Project& proj)
bool checkFromProject()
{
return false;
}
bool readFromProject(HECL::Database::Project& proj)
bool readFromProject()
{
return false;
}

View File

@ -39,15 +39,15 @@ struct SpecMP3 : SpecBase
PAKRouter<DNAMP3::PAKBridge> m_fePakRouter;
SpecMP3(HECL::Database::Project& project)
: m_workPath(project.getProjectRootPath(), _S("MP3")),
: SpecBase(project),
m_workPath(project.getProjectRootPath(), _S("MP3")),
m_cookPath(project.getProjectCookedPath(SpecEntMP3), _S("MP3")),
m_pakRouter(m_workPath, m_cookPath),
m_pakRouter(*this, m_workPath, m_cookPath),
m_feWorkPath(project.getProjectRootPath(), _S("fe")),
m_feCookPath(project.getProjectCookedPath(SpecEntMP3), _S("fe")),
m_fePakRouter(m_feWorkPath, m_feCookPath) {}
m_fePakRouter(*this, m_feWorkPath, m_feCookPath) {}
void buildPaks(HECL::Database::Project& project,
NOD::DiscBase::IPartition::Node& root,
void buildPaks(NOD::DiscBase::IPartition::Node& root,
const std::vector<HECL::SystemString>& args,
ExtractReport& rep,
bool fe)
@ -114,9 +114,9 @@ struct SpecMP3 : SpecBase
if (good)
{
if (fe)
m_fePaks.emplace_back(project, child);
m_fePaks.emplace_back(m_project, child);
else
m_paks.emplace_back(project, child);
m_paks.emplace_back(m_project, child);
}
}
}
@ -163,8 +163,7 @@ struct SpecMP3 : SpecBase
}
}
bool checkFromStandaloneDisc(HECL::Database::Project& project,
NOD::DiscBase& disc,
bool checkFromStandaloneDisc(NOD::DiscBase& disc,
const HECL::SystemString& regstr,
const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)
@ -188,13 +187,12 @@ struct SpecMP3 : SpecBase
/* Iterate PAKs and build level options */
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
buildPaks(project, root, args, rep, false);
buildPaks(root, args, rep, false);
return true;
}
bool checkFromTrilogyDisc(HECL::Database::Project& project,
NOD::DiscBase& disc,
bool checkFromTrilogyDisc(NOD::DiscBase& disc,
const HECL::SystemString& regstr,
const std::vector<HECL::SystemString>& args,
std::vector<ExtractReport>& reps)
@ -240,6 +238,9 @@ struct SpecMP3 : SpecBase
doMPTFE = true;
}
if (!doMP3 && !doMPTFE)
return false;
NOD::DiscGCN::IPartition* partition = disc.getDataPartition();
NOD::DiscBase::IPartition::Node& root = partition->getFSTRoot();
@ -269,7 +270,7 @@ struct SpecMP3 : SpecBase
NOD::DiscBase::IPartition::Node::DirectoryIterator mp3It = root.find("MP3");
if (mp3It == root.end())
return false;
buildPaks(project, *mp3It, mp3args, rep, false);
buildPaks(*mp3It, mp3args, rep, false);
}
/* MPT Frontend extract */
@ -298,14 +299,13 @@ struct SpecMP3 : SpecBase
NOD::DiscBase::IPartition::Node::DirectoryIterator feIt = root.find("fe");
if (feIt == root.end())
return false;
buildPaks(project, *feIt, feargs, rep, true);
buildPaks(*feIt, feargs, rep, true);
}
return true;
}
bool extractFromDisc(HECL::Database::Project& project, NOD::DiscBase&, bool force,
FExtractProgress progress)
bool extractFromDisc(NOD::DiscBase&, bool force, FExtractProgress progress)
{
int compIdx = 2;
int prog;
@ -318,7 +318,7 @@ struct SpecMP3 : SpecBase
});
progress(_S("Indexing PAKs"), compIdx++, 1.0);
HECL::ProjectPath mp3WorkPath(project.getProjectRootPath(), "MP3");
HECL::ProjectPath mp3WorkPath(m_project.getProjectRootPath(), "MP3");
mp3WorkPath.makeDir();
progress(_S("MP3 Root"), compIdx, 0.0);
prog = 0;
@ -329,14 +329,15 @@ struct SpecMP3 : SpecBase
}
progress(_S("MP3 Root"), compIdx++, 1.0);
const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP3);
const HECL::ProjectPath& cookPath = m_project.getProjectCookedPath(SpecEntMP3);
cookPath.makeDir();
HECL::ProjectPath mp3CookPath(cookPath, "MP3");
mp3CookPath.makeDir();
prog = 0;
for (DNAMP3::PAKBridge& pak : m_paks)
for (std::pair<std::string, DNAMP3::PAKBridge*> pair : m_orderedPaks)
{
DNAMP3::PAKBridge& pak = *pair.second;
m_pakRouter.enterPAKBridge(pak);
const std::string& name = pak.getName();
@ -371,13 +372,14 @@ struct SpecMP3 : SpecBase
}
progress(_S("fe Root"), compIdx++, 1.0);
const HECL::ProjectPath& cookPath = project.getProjectCookedPath(SpecEntMP3);
const HECL::ProjectPath& cookPath = m_project.getProjectCookedPath(SpecEntMP3);
cookPath.makeDir();
m_feCookPath.makeDir();
prog = 0;
for (DNAMP3::PAKBridge& pak : m_fePaks)
for (std::pair<std::string, DNAMP3::PAKBridge*> pair : m_feOrderedPaks)
{
DNAMP3::PAKBridge& pak = *pair.second;
const std::string& name = pak.getName();
HECL::SystemStringView sysName(name);
@ -394,11 +396,11 @@ struct SpecMP3 : SpecBase
return true;
}
bool checkFromProject(HECL::Database::Project& proj)
bool checkFromProject()
{
return false;
}
bool readFromProject(HECL::Database::Project& proj)
bool readFromProject()
{
return false;
}

2
NODLib

@ -1 +1 @@
Subproject commit ebb5c0743da7fafbe4c3945d0117700c43845425
Subproject commit 28ed4ba6afe64b1e529799bfb8d87c35fe868e4c