mirror of https://github.com/AxioDL/metaforce.git
Merge branch 'master' of https://github.com/RetroView/RetroCommon
This commit is contained in:
commit
cdd7b45394
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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_
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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_
|
|
@ -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_
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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_
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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);}
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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_
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -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;}
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -4,4 +4,5 @@ make_dnalist(liblist
|
|||
add_library(DNAMP2
|
||||
DNAMP2.hpp DNAMP2.cpp
|
||||
${liblist}
|
||||
CMDL.hpp
|
||||
STRG.hpp STRG.cpp)
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -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;}
|
||||
|
|
|
@ -70,6 +70,7 @@ struct MLVL : BigDNA
|
|||
|
||||
String<-1> internalAreaName;
|
||||
};
|
||||
Vector<Area, DNA_COUNT(areaCount)> areas;
|
||||
|
||||
UniqueID32 worldMap;
|
||||
Value<atUint8> unknown2;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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*)§ion->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_
|
|
@ -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)
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -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;}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
2
NODLib
|
@ -1 +1 @@
|
|||
Subproject commit ebb5c0743da7fafbe4c3945d0117700c43845425
|
||||
Subproject commit 28ed4ba6afe64b1e529799bfb8d87c35fe868e4c
|
Loading…
Reference in New Issue