Add support for DKCTF Wii U and Switch files.
Supports loading rigged models and viewing textures for both the Wii U and Switch versions of the game. Keep in mind the Switch version lacks LZSS 3 byte compression and will be missing vertex data for certain models.
This commit is contained in:
parent
5f3cde8d57
commit
2cc2269bab
175
File_Format_Library/FileFormats/DKCTF/CCharacter.cs
Normal file
175
File_Format_Library/FileFormats/DKCTF/CCharacter.cs
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Toolbox;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Toolbox.Library;
|
||||||
|
using Toolbox.Library.IO;
|
||||||
|
using Toolbox.Library.Forms;
|
||||||
|
using Toolbox.Library.Rendering;
|
||||||
|
using OpenTK;
|
||||||
|
using CafeLibrary.M2;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
public class CCharacter : TreeNodeFile
|
||||||
|
{
|
||||||
|
public FileType FileType { get; set; } = FileType.Model;
|
||||||
|
|
||||||
|
public bool CanSave { get; set; }
|
||||||
|
public string[] Description { get; set; } = new string[] { "Character Model" };
|
||||||
|
public string[] Extension { get; set; } = new string[] { "*.char" };
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public string FilePath { get; set; }
|
||||||
|
public IFileInfo IFileInfo { get; set; }
|
||||||
|
|
||||||
|
public bool Identify(System.IO.Stream stream)
|
||||||
|
{
|
||||||
|
using (var reader = new Toolbox.Library.IO.FileReader(stream, true))
|
||||||
|
{
|
||||||
|
bool IsForm = reader.CheckSignature(4, "RFRM");
|
||||||
|
bool FormType = reader.CheckSignature(4, "CHAR", 20);
|
||||||
|
|
||||||
|
return IsForm && FormType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type[] Types
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<Type> types = new List<Type>();
|
||||||
|
return types.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check for the viewport in the object editor
|
||||||
|
//This is attached to it to load multiple file formats within the object editor to the viewer
|
||||||
|
Viewport viewport
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var editor = LibraryGUI.GetObjectEditor();
|
||||||
|
return editor.GetViewport();
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var editor = LibraryGUI.GetObjectEditor();
|
||||||
|
editor.LoadViewport(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DrawablesLoaded = false;
|
||||||
|
public override void OnClick(TreeView treeView)
|
||||||
|
{
|
||||||
|
//Make sure opengl is enabled
|
||||||
|
if (Runtime.UseOpenGL)
|
||||||
|
{
|
||||||
|
//Open the viewport
|
||||||
|
if (viewport == null)
|
||||||
|
{
|
||||||
|
viewport = new Viewport(ObjectEditor.GetDrawableContainers());
|
||||||
|
viewport.Dock = DockStyle.Fill;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Make sure to load the drawables only once so set it to true!
|
||||||
|
if (!DrawablesLoaded)
|
||||||
|
{
|
||||||
|
ObjectEditor.AddContainer(DrawableContainer);
|
||||||
|
DrawablesLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reload which drawable to display
|
||||||
|
viewport.ReloadDrawables(DrawableContainer);
|
||||||
|
LibraryGUI.LoadEditor(viewport);
|
||||||
|
|
||||||
|
viewport.Text = Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<STGenericObject> ExportableMeshes => Model.Objects;
|
||||||
|
public IEnumerable<STGenericMaterial> ExportableMaterials => Model.Materials;
|
||||||
|
public IEnumerable<STGenericTexture> ExportableTextures => TextureList;
|
||||||
|
public STSkeleton ExportableSkeleton => Model.GenericSkeleton;
|
||||||
|
|
||||||
|
public GenericModelRenderer Renderer;
|
||||||
|
|
||||||
|
public DrawableContainer DrawableContainer = new DrawableContainer();
|
||||||
|
|
||||||
|
public List<STGenericTexture> TextureList = new List<STGenericTexture>();
|
||||||
|
|
||||||
|
public CHAR CharData { get; set; }
|
||||||
|
|
||||||
|
public STGenericModel Model;
|
||||||
|
|
||||||
|
TreeNode modelFolder = new TreeNode("Models");
|
||||||
|
|
||||||
|
TreeNode skeletonFolder = new STTextureFolder("Skeleton");
|
||||||
|
|
||||||
|
public void Load(System.IO.Stream stream)
|
||||||
|
{
|
||||||
|
Text = FileName;
|
||||||
|
|
||||||
|
Renderer = new GenericModelRenderer();
|
||||||
|
CharData = new CHAR(stream);
|
||||||
|
|
||||||
|
|
||||||
|
Nodes.Add(modelFolder);
|
||||||
|
Nodes.Add(skeletonFolder);
|
||||||
|
|
||||||
|
|
||||||
|
Model = ToGeneric();
|
||||||
|
|
||||||
|
DrawableContainer = new DrawableContainer();
|
||||||
|
DrawableContainer.Name = FileName;
|
||||||
|
DrawableContainer.Drawables.Add(Renderer);
|
||||||
|
DrawableContainer.Drawables.Add(Model.GenericSkeleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadModels(PAK pakFile)
|
||||||
|
{
|
||||||
|
var skeleton = pakFile.SkeletonFiles[CharData.SkeletonFileID.ToString()];
|
||||||
|
var skel = new SKEL(new MemoryStream(skeleton.FileData));
|
||||||
|
|
||||||
|
skeletonFolder.Nodes.Clear();
|
||||||
|
foreach (var bone in skel.JointNames)
|
||||||
|
{
|
||||||
|
TreeNode boneNode = new TreeNode(bone);
|
||||||
|
skeletonFolder.Nodes.Add(boneNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var model in CharData.Models)
|
||||||
|
{
|
||||||
|
var mod = pakFile.ModelFiles[model.FileID.ToString()].OpenFile() as CModel;
|
||||||
|
mod.LoadTextures(pakFile.TextureFiles);
|
||||||
|
|
||||||
|
modelFolder.Nodes.Add(mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unload()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(System.IO.Stream stream)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public STGenericModel ToGeneric()
|
||||||
|
{
|
||||||
|
var model = new STGenericModel();
|
||||||
|
model.GenericSkeleton = new STSkeleton();
|
||||||
|
|
||||||
|
List<GenericRenderedObject> meshes = new List<GenericRenderedObject>();
|
||||||
|
List<STGenericMaterial> materials = new List<STGenericMaterial>();
|
||||||
|
|
||||||
|
model.Objects = meshes;
|
||||||
|
model.Name = this.FileName;
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
300
File_Format_Library/FileFormats/DKCTF/CModel.cs
Normal file
300
File_Format_Library/FileFormats/DKCTF/CModel.cs
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Toolbox;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Toolbox.Library;
|
||||||
|
using Toolbox.Library.IO;
|
||||||
|
using Toolbox.Library.Forms;
|
||||||
|
using Toolbox.Library.Rendering;
|
||||||
|
using OpenTK;
|
||||||
|
using CafeLibrary.M2;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
public class CModel : TreeNodeFile, IFileFormat, IExportableModel
|
||||||
|
{
|
||||||
|
public FileType FileType { get; set; } = FileType.Model;
|
||||||
|
|
||||||
|
public bool CanSave { get; set; }
|
||||||
|
public string[] Description { get; set; } = new string[] { "CMDL" };
|
||||||
|
public string[] Extension { get; set; } = new string[] { "*.cmdl", "*.smdl", "*.wmdl" };
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public string FilePath { get; set; }
|
||||||
|
public IFileInfo IFileInfo { get; set; }
|
||||||
|
|
||||||
|
public bool Identify(System.IO.Stream stream)
|
||||||
|
{
|
||||||
|
using (var reader = new Toolbox.Library.IO.FileReader(stream, true))
|
||||||
|
{
|
||||||
|
bool IsForm = reader.CheckSignature(4, "RFRM");
|
||||||
|
bool FormType = reader.CheckSignature(4, "CMDL", 20) ||
|
||||||
|
reader.CheckSignature(4, "WMDL", 20) ||
|
||||||
|
reader.CheckSignature(4, "SMDL", 20);
|
||||||
|
|
||||||
|
return IsForm && FormType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type[] Types
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<Type> types = new List<Type>();
|
||||||
|
return types.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check for the viewport in the object editor
|
||||||
|
//This is attached to it to load multiple file formats within the object editor to the viewer
|
||||||
|
Viewport viewport
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var editor = LibraryGUI.GetObjectEditor();
|
||||||
|
return editor.GetViewport();
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var editor = LibraryGUI.GetObjectEditor();
|
||||||
|
editor.LoadViewport(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DrawablesLoaded = false;
|
||||||
|
public override void OnClick(TreeView treeView)
|
||||||
|
{
|
||||||
|
//Make sure opengl is enabled
|
||||||
|
if (Runtime.UseOpenGL)
|
||||||
|
{
|
||||||
|
//Open the viewport
|
||||||
|
if (viewport == null)
|
||||||
|
{
|
||||||
|
viewport = new Viewport(ObjectEditor.GetDrawableContainers());
|
||||||
|
viewport.Dock = DockStyle.Fill;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Make sure to load the drawables only once so set it to true!
|
||||||
|
if (!DrawablesLoaded)
|
||||||
|
{
|
||||||
|
ObjectEditor.AddContainer(DrawableContainer);
|
||||||
|
DrawablesLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reload which drawable to display
|
||||||
|
viewport.ReloadDrawables(DrawableContainer);
|
||||||
|
LibraryGUI.LoadEditor(viewport);
|
||||||
|
|
||||||
|
viewport.Text = Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<STGenericObject> ExportableMeshes => Model.Objects;
|
||||||
|
public IEnumerable<STGenericMaterial> ExportableMaterials => Model.Materials;
|
||||||
|
public IEnumerable<STGenericTexture> ExportableTextures => Renderer.Textures;
|
||||||
|
public STSkeleton ExportableSkeleton => Model.GenericSkeleton;
|
||||||
|
|
||||||
|
public GenericModelRenderer Renderer;
|
||||||
|
|
||||||
|
public DrawableContainer DrawableContainer = new DrawableContainer();
|
||||||
|
|
||||||
|
public List<STGenericTexture> TextureList = new List<STGenericTexture>();
|
||||||
|
|
||||||
|
public CMDL ModelData { get; set; }
|
||||||
|
|
||||||
|
public STGenericModel Model;
|
||||||
|
|
||||||
|
public SKEL SkeletonData;
|
||||||
|
|
||||||
|
public void LoadSkeleton(SKEL skeleton)
|
||||||
|
{
|
||||||
|
SkeletonData = skeleton;
|
||||||
|
Model = ToGeneric();
|
||||||
|
|
||||||
|
Model.GenericSkeleton = SkeletonData.ToGenericSkeleton();
|
||||||
|
|
||||||
|
DrawableContainer.Drawables.Add(Model.GenericSkeleton);
|
||||||
|
|
||||||
|
foreach (var bone in Model.GenericSkeleton.bones)
|
||||||
|
{
|
||||||
|
if (bone.Parent == null)
|
||||||
|
skeletonFolder.Nodes.Add(bone);
|
||||||
|
}
|
||||||
|
|
||||||
|
Renderer.Skeleton = Model.GenericSkeleton;
|
||||||
|
//Reload meshes with updated bone IDs
|
||||||
|
Renderer.Meshes.Clear();
|
||||||
|
foreach (GenericRenderedObject mesh in Model.Objects)
|
||||||
|
Renderer.Meshes.Add(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadTextures(Dictionary<string, FileEntry> Textures)
|
||||||
|
{
|
||||||
|
texFolder.Nodes.Clear();
|
||||||
|
foreach (var mat in ModelData.Materials)
|
||||||
|
{
|
||||||
|
foreach (var texMap in mat.Textures)
|
||||||
|
{
|
||||||
|
string guid = texMap.Value.FileID.ToString();
|
||||||
|
if (texFolder.Nodes.ContainsKey(guid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Textures[guid].FileFormat == null)
|
||||||
|
Textures[guid].OpenFile();
|
||||||
|
|
||||||
|
var tex = Textures[guid].FileFormat as CTexture;
|
||||||
|
|
||||||
|
TreeNode t = new TreeNode(guid);
|
||||||
|
t.ImageKey = "texture";
|
||||||
|
t.SelectedImageKey = "texture";
|
||||||
|
t.Tag = tex;
|
||||||
|
texFolder.Nodes.Add(t);
|
||||||
|
|
||||||
|
if (texMap.Key == "DIFT")
|
||||||
|
{
|
||||||
|
tex.Text = guid;
|
||||||
|
|
||||||
|
if (tex.RenderableTex == null)
|
||||||
|
tex.LoadOpenGLTexture();
|
||||||
|
Renderer.Textures.Add(tex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode texFolder = new STTextureFolder("Textures");
|
||||||
|
TreeNode skeletonFolder = new STTextureFolder("Skeleton");
|
||||||
|
|
||||||
|
public void Load(System.IO.Stream stream)
|
||||||
|
{
|
||||||
|
Text = FileName;
|
||||||
|
|
||||||
|
Renderer = new GenericModelRenderer();
|
||||||
|
ModelData = new CMDL(stream);
|
||||||
|
|
||||||
|
TreeNode meshFolder = new TreeNode("Meshes");
|
||||||
|
Nodes.Add(meshFolder);
|
||||||
|
|
||||||
|
Nodes.Add(texFolder);
|
||||||
|
|
||||||
|
Nodes.Add(skeletonFolder);
|
||||||
|
|
||||||
|
Model = ToGeneric();
|
||||||
|
|
||||||
|
foreach (GenericRenderedObject mesh in Model.Objects)
|
||||||
|
{
|
||||||
|
Renderer.Meshes.Add(mesh);
|
||||||
|
meshFolder.Nodes.Add(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var tex in TextureList)
|
||||||
|
{
|
||||||
|
Renderer.Textures.Add(tex);
|
||||||
|
texFolder.Nodes.Add(tex);
|
||||||
|
}
|
||||||
|
Renderer.Skeleton = Model.GenericSkeleton;
|
||||||
|
|
||||||
|
DrawableContainer = new DrawableContainer();
|
||||||
|
DrawableContainer.Name = FileName;
|
||||||
|
DrawableContainer.Drawables.Add(Renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unload()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(System.IO.Stream stream)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public STGenericModel ToGeneric()
|
||||||
|
{
|
||||||
|
var model = new STGenericModel();
|
||||||
|
model.Name = this.FileName;
|
||||||
|
|
||||||
|
model.GenericSkeleton = new STSkeleton();
|
||||||
|
if (SkeletonData != null)
|
||||||
|
model.GenericSkeleton = SkeletonData.ToGenericSkeleton();
|
||||||
|
|
||||||
|
List<GenericRenderedObject> meshes = new List<GenericRenderedObject>();
|
||||||
|
List<STGenericMaterial> materials = new List<STGenericMaterial>();
|
||||||
|
|
||||||
|
foreach (var mat in ModelData.Materials)
|
||||||
|
{
|
||||||
|
STGenericMaterial genericMaterial = new STGenericMaterial();
|
||||||
|
genericMaterial.Text = mat.Name;
|
||||||
|
materials.Add(genericMaterial);
|
||||||
|
|
||||||
|
foreach (var tex in mat.Textures)
|
||||||
|
{
|
||||||
|
var type = STGenericMatTexture.TextureType.Unknown;
|
||||||
|
if (tex.Key == "DIFT") type = STGenericMatTexture.TextureType.Diffuse;
|
||||||
|
if (tex.Key == "NMAP") type = STGenericMatTexture.TextureType.Normal;
|
||||||
|
if (tex.Key == "SPCT") type = STGenericMatTexture.TextureType.Specular;
|
||||||
|
|
||||||
|
genericMaterial.TextureMaps.Add(new STGenericMatTexture()
|
||||||
|
{
|
||||||
|
Type = type,
|
||||||
|
Name = tex.Value.FileID.ToString(),
|
||||||
|
WrapModeS = STTextureWrapMode.Repeat,
|
||||||
|
WrapModeT = STTextureWrapMode.Repeat,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var mesh in ModelData.Meshes)
|
||||||
|
{
|
||||||
|
var mat = materials[mesh.Header.MaterialIndex];
|
||||||
|
|
||||||
|
GenericRenderedObject genericMesh = new GenericRenderedObject();
|
||||||
|
genericMesh.Text = "Mesh_" + mat.Name;
|
||||||
|
meshes.Add(genericMesh);
|
||||||
|
|
||||||
|
foreach (var vert in mesh.Vertices)
|
||||||
|
{
|
||||||
|
Vertex v = new Vertex();
|
||||||
|
v.pos = vert.Position;
|
||||||
|
v.nrm = vert.Normal;
|
||||||
|
v.uv0 = vert.TexCoord0;
|
||||||
|
v.uv1 = vert.TexCoord1;
|
||||||
|
v.uv2 = vert.TexCoord2;
|
||||||
|
v.tan = vert.Tangent;
|
||||||
|
v.col = vert.Color;
|
||||||
|
|
||||||
|
if (SkeletonData != null)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
var weight = vert.BoneWeights[j];
|
||||||
|
var boneIndex = (int)vert.BoneIndices[j];
|
||||||
|
if (weight == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
v.boneWeights.Add(weight);
|
||||||
|
v.boneIds.Add(SkeletonData.SkinnedBonesRemap[boneIndex]);;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
genericMesh.vertices.Add(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
var poly = new STGenericPolygonGroup();
|
||||||
|
poly.Material = mat;
|
||||||
|
poly.MaterialIndex = mesh.Header.MaterialIndex;
|
||||||
|
genericMesh.PolygonGroups.Add(poly);
|
||||||
|
|
||||||
|
foreach (var id in mesh.Indices)
|
||||||
|
poly.faces.Add((int)id);
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Materials = materials;
|
||||||
|
model.Objects = meshes;
|
||||||
|
model.Name = this.FileName;
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
267
File_Format_Library/FileFormats/DKCTF/CTexture.cs
Normal file
267
File_Format_Library/FileFormats/DKCTF/CTexture.cs
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Toolbox;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Toolbox.Library;
|
||||||
|
using Toolbox.Library.Forms;
|
||||||
|
using Toolbox.Library.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
public class CTexture : STGenericTexture, IFileFormat, IContextMenuNode
|
||||||
|
{
|
||||||
|
public FileType FileType { get; set; } = FileType.Image;
|
||||||
|
|
||||||
|
public bool CanSave { get; set; }
|
||||||
|
public string[] Description { get; set; } = new string[] { "TXTR" };
|
||||||
|
public string[] Extension { get; set; } = new string[] { "*.txtr" };
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public string FilePath { get; set; }
|
||||||
|
public IFileInfo IFileInfo { get; set; }
|
||||||
|
|
||||||
|
public bool Identify(System.IO.Stream stream)
|
||||||
|
{
|
||||||
|
using (var reader = new Toolbox.Library.IO.FileReader(stream, true))
|
||||||
|
{
|
||||||
|
bool IsForm = reader.CheckSignature(4, "RFRM");
|
||||||
|
bool FormType = reader.CheckSignature(4, "TXTR", 20);
|
||||||
|
|
||||||
|
return IsForm && FormType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type[] Types
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<Type> types = new List<Type>();
|
||||||
|
return types.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanEdit { get; set; } = false;
|
||||||
|
|
||||||
|
public override TEX_FORMAT[] SupportedFormats
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new TEX_FORMAT[]
|
||||||
|
{
|
||||||
|
TEX_FORMAT.B5G6R5_UNORM,
|
||||||
|
TEX_FORMAT.R8G8_UNORM,
|
||||||
|
TEX_FORMAT.B5G5R5A1_UNORM,
|
||||||
|
TEX_FORMAT.B4G4R4A4_UNORM,
|
||||||
|
TEX_FORMAT.LA8,
|
||||||
|
TEX_FORMAT.HIL08,
|
||||||
|
TEX_FORMAT.L8,
|
||||||
|
TEX_FORMAT.A8_UNORM,
|
||||||
|
TEX_FORMAT.LA4,
|
||||||
|
TEX_FORMAT.A4,
|
||||||
|
TEX_FORMAT.ETC1_UNORM,
|
||||||
|
TEX_FORMAT.ETC1_A4,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TXTR Header;
|
||||||
|
|
||||||
|
public override void OnClick(TreeView treeView)
|
||||||
|
{
|
||||||
|
UpdateEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateEditor()
|
||||||
|
{
|
||||||
|
ImageEditorBase editor = (ImageEditorBase)LibraryGUI.GetActiveContent(typeof(ImageEditorBase));
|
||||||
|
if (editor == null)
|
||||||
|
{
|
||||||
|
editor = new ImageEditorBase();
|
||||||
|
editor.Dock = DockStyle.Fill;
|
||||||
|
LibraryGUI.LoadEditor(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.LoadProperties(GenericProperties);
|
||||||
|
editor.LoadImage(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToolStripItem[] GetContextMenuItems()
|
||||||
|
{
|
||||||
|
List<ToolStripItem> Items = new List<ToolStripItem>();
|
||||||
|
Items.Add(new ToolStripMenuItem("Save", null, SaveAction, Keys.Control | Keys.S));
|
||||||
|
Items.AddRange(base.GetContextMenuItems());
|
||||||
|
return Items.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveAction(object sender, EventArgs args)
|
||||||
|
{
|
||||||
|
SaveFileDialog sfd = new SaveFileDialog();
|
||||||
|
sfd.Filter = Utils.GetAllFilters(this);
|
||||||
|
sfd.FileName = FileName;
|
||||||
|
|
||||||
|
if (sfd.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
STFileSaver.SaveFileFormat(this, sfd.FileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FillEditor(UserControl control)
|
||||||
|
{
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.Width = Width;
|
||||||
|
prop.Height = Height;
|
||||||
|
prop.Depth = Depth;
|
||||||
|
prop.MipCount = MipCount;
|
||||||
|
prop.ArrayCount = ArrayCount;
|
||||||
|
prop.ImageSize = (uint)Header.BufferData.Length;
|
||||||
|
prop.Format = Format;
|
||||||
|
|
||||||
|
((ImageEditorBase)control).LoadImage(this);
|
||||||
|
((ImageEditorBase)control).LoadProperties(prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Load(System.IO.Stream stream)
|
||||||
|
{
|
||||||
|
PlatformSwizzle = PlatformSwizzle.Platform_Switch;
|
||||||
|
CanSave = true;
|
||||||
|
CanReplace = true;
|
||||||
|
Text = FileName;
|
||||||
|
this.ImageKey = "texture";
|
||||||
|
this.SelectedImageKey = "texture";
|
||||||
|
|
||||||
|
Header = new TXTR(stream);
|
||||||
|
|
||||||
|
if (Header.IsSwitch)
|
||||||
|
PlatformSwizzle = PlatformSwizzle.Platform_WiiU;
|
||||||
|
|
||||||
|
Width = Header.TextureHeader.Width;
|
||||||
|
Height = Header.TextureHeader.Height;
|
||||||
|
MipCount = (uint)Header.MipSizes.Length;
|
||||||
|
Format = FormatList[Header.TextureHeader.Format];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(System.IO.Stream stream)
|
||||||
|
{
|
||||||
|
using (var writer = new FileWriter(stream, true))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Replace(string FileName)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void SetImageData(System.Drawing.Bitmap bitmap, int ArrayLevel)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0, int DepthLevel = 0)
|
||||||
|
{
|
||||||
|
if (Header.IsSwitch)
|
||||||
|
return TegraX1Swizzle.GetImageData(this, Header.BufferData, ArrayLevel, MipLevel, DepthLevel);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var tex = this.Header.TextureHeader;
|
||||||
|
|
||||||
|
uint bpp = GetBytesPerPixel(Format);
|
||||||
|
|
||||||
|
GX2.GX2Surface surf = new GX2.GX2Surface();
|
||||||
|
surf.bpp = bpp;
|
||||||
|
surf.height = tex.Height;
|
||||||
|
surf.width = tex.Width;
|
||||||
|
surf.aa = (uint)GX2.GX2AAMode.GX2_AA_MODE_1X;
|
||||||
|
surf.alignment = 8192;
|
||||||
|
surf.imageSize = (uint)Header.BufferData.Length;
|
||||||
|
|
||||||
|
if (Header.Meta != null)
|
||||||
|
surf.alignment = Header.Meta.BaseAlignment;
|
||||||
|
surf.depth = 1;
|
||||||
|
surf.dim = (uint)GX2.GX2SurfaceDimension.DIM_2D;
|
||||||
|
surf.format = (uint)Bfres.Structs.FTEX.ConvertToGx2Format(Format);
|
||||||
|
surf.use = (uint)GX2.GX2SurfaceUse.USE_TEXTURE;
|
||||||
|
surf.data = Header.BufferData;
|
||||||
|
surf.numMips = 1;
|
||||||
|
surf.mipOffset = new uint[0];
|
||||||
|
surf.mipData = Header.BufferData;
|
||||||
|
surf.tileMode = (int)GX2.GX2TileMode.MODE_2D_TILED_THIN1;
|
||||||
|
|
||||||
|
var surfOut = GX2.getSurfaceInfo((GX2.GX2SurfaceFormat)Format, surf.width, surf.height, 1, 1, surf.tileMode, 0, 0);
|
||||||
|
surf.pitch = surfOut.pitch;
|
||||||
|
|
||||||
|
var swizzlePattern = 0xd0000 | tex.Swizzle << 8;
|
||||||
|
|
||||||
|
surf.swizzle = swizzlePattern;
|
||||||
|
|
||||||
|
Console.WriteLine("");
|
||||||
|
Console.WriteLine("// ----- GX2Surface Info ----- ");
|
||||||
|
Console.WriteLine(" dim = " + surf.dim);
|
||||||
|
Console.WriteLine(" width = " + surf.width);
|
||||||
|
Console.WriteLine(" height = " + surf.height);
|
||||||
|
Console.WriteLine(" depth = " + surf.depth);
|
||||||
|
Console.WriteLine(" numMips = " + surf.numMips);
|
||||||
|
Console.WriteLine(" format = " + surf.format);
|
||||||
|
Console.WriteLine(" aa = " + surf.aa);
|
||||||
|
Console.WriteLine(" use = " + surf.use);
|
||||||
|
Console.WriteLine(" imageSize = " + surf.imageSize);
|
||||||
|
Console.WriteLine(" mipSize = " + surf.mipSize);
|
||||||
|
Console.WriteLine(" tileMode = " + surf.tileMode);
|
||||||
|
Console.WriteLine(" swizzle = " + surf.swizzle);
|
||||||
|
Console.WriteLine(" alignment = " + surf.alignment);
|
||||||
|
Console.WriteLine(" pitch = " + surf.pitch);
|
||||||
|
Console.WriteLine(" bits per pixel = " + (surf.bpp << 3));
|
||||||
|
Console.WriteLine(" bytes per pixel = " + surf.bpp);
|
||||||
|
Console.WriteLine(" data size = " + surf.data.Length);
|
||||||
|
Console.WriteLine(" mip size = " + surf.mipData.Length);
|
||||||
|
Console.WriteLine(" realSize = " + surf.imageSize);
|
||||||
|
|
||||||
|
|
||||||
|
return GX2.Decode(surf, ArrayLevel, MipLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class STextureHeader
|
||||||
|
{
|
||||||
|
public uint Type;
|
||||||
|
public uint Format;
|
||||||
|
public uint Width;
|
||||||
|
public uint Height;
|
||||||
|
public uint Depth;
|
||||||
|
public uint TileMode;
|
||||||
|
public uint Swizzle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<uint, TEX_FORMAT> FormatList = new Dictionary<uint, TEX_FORMAT>()
|
||||||
|
{
|
||||||
|
{ 12, TEX_FORMAT.R8G8B8A8_UNORM },
|
||||||
|
{ 13, TEX_FORMAT.R8G8B8A8_UNORM_SRGB },
|
||||||
|
|
||||||
|
{ 20, TEX_FORMAT.BC1_UNORM },
|
||||||
|
{ 21, TEX_FORMAT.BC1_UNORM_SRGB },
|
||||||
|
{ 22, TEX_FORMAT.BC2_UNORM },
|
||||||
|
{ 23, TEX_FORMAT.BC2_UNORM_SRGB },
|
||||||
|
{ 24, TEX_FORMAT.BC3_UNORM },
|
||||||
|
{ 25, TEX_FORMAT.BC3_UNORM_SRGB },
|
||||||
|
{ 26, TEX_FORMAT.BC4_UNORM },
|
||||||
|
{ 27, TEX_FORMAT.BC4_SNORM },
|
||||||
|
{ 28, TEX_FORMAT.BC5_UNORM },
|
||||||
|
{ 29, TEX_FORMAT.BC5_SNORM },
|
||||||
|
{ 30, TEX_FORMAT.R11G11B10_FLOAT },
|
||||||
|
{ 31, TEX_FORMAT.R32_FLOAT },
|
||||||
|
{ 32, TEX_FORMAT.R16G16_FLOAT },
|
||||||
|
{ 33, TEX_FORMAT.R8G8_UNORM },
|
||||||
|
|
||||||
|
{ 54, TEX_FORMAT.ASTC_5x4_UNORM },
|
||||||
|
{ 55, TEX_FORMAT.ASTC_5x5_UNORM },
|
||||||
|
{ 56, TEX_FORMAT.ASTC_6x5_UNORM },
|
||||||
|
{ 57, TEX_FORMAT.ASTC_6x6_UNORM },
|
||||||
|
{ 58, TEX_FORMAT.ASTC_8x5_UNORM },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,105 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Toolbox.Library.IO;
|
|
||||||
|
|
||||||
namespace DKCTF
|
|
||||||
{
|
|
||||||
//Documentation from https://github.com/Kinnay/Nintendo-File-Formats/wiki/DKCTF-Types#cformdescriptor
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
public class CFormDescriptor
|
|
||||||
{
|
|
||||||
public Magic Magic = "RFRM";
|
|
||||||
public ulong DataSize;
|
|
||||||
public ulong Unknown;
|
|
||||||
public Magic FormType;
|
|
||||||
public uint VersionA;
|
|
||||||
public uint VersionB;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
public class CChunkDescriptor
|
|
||||||
{
|
|
||||||
public Magic ChunkType;
|
|
||||||
public long DataSize;
|
|
||||||
public uint Unknown;
|
|
||||||
public long DataOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
public class CMayaSpline
|
|
||||||
{
|
|
||||||
public Magic ChunkType;
|
|
||||||
public ulong DataSize;
|
|
||||||
public uint Unknown;
|
|
||||||
public ulong DataOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
public class CMayaSplineKnot
|
|
||||||
{
|
|
||||||
public float Time;
|
|
||||||
public float Value;
|
|
||||||
public ETangentType TangentType1;
|
|
||||||
public ETangentType TangentType2;
|
|
||||||
public float FieldC;
|
|
||||||
public float Field10;
|
|
||||||
public float Field14;
|
|
||||||
public float Field18;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
public class CObjectTag
|
|
||||||
{
|
|
||||||
public Magic Type;
|
|
||||||
public CGuid Objectid;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
public class CObjectId
|
|
||||||
{
|
|
||||||
public CGuid Guid;
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Guid.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
public class CGuid
|
|
||||||
{
|
|
||||||
public uint Part1;
|
|
||||||
public ushort Part2;
|
|
||||||
public ushort Part3;
|
|
||||||
public ulong Part4;
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"{Part1}|{Part2}|{Part3}|{Part4}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ETangentType
|
|
||||||
{
|
|
||||||
Linear,
|
|
||||||
Flat,
|
|
||||||
Smooth,
|
|
||||||
Step,
|
|
||||||
Clamped,
|
|
||||||
Fixed,
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum EInfinityType
|
|
||||||
{
|
|
||||||
Constant,
|
|
||||||
Linear,
|
|
||||||
Cycle,
|
|
||||||
CycleRelative,
|
|
||||||
Oscillate,
|
|
||||||
}
|
|
||||||
}
|
|
119
File_Format_Library/FileFormats/DKCTF/FileData/BufferHelper.cs
Normal file
119
File_Format_Library/FileFormats/DKCTF/FileData/BufferHelper.cs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Toolbox.Library.IO;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a helper for loading vertex and index buffer information.
|
||||||
|
/// </summary>
|
||||||
|
internal class BufferHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an index list from the provided buffer and formatting info.
|
||||||
|
/// </summary>
|
||||||
|
public static uint[] LoadIndexBuffer(byte[] buffer, CMDL.IndexFormat format, bool isLittleEndian)
|
||||||
|
{
|
||||||
|
var stride = GetIndexStride(format);
|
||||||
|
uint[] indices = new uint[buffer.Length / stride];
|
||||||
|
|
||||||
|
using (var reader = new FileReader(buffer))
|
||||||
|
{
|
||||||
|
reader.SetByteOrder(!isLittleEndian); //switch is little endianness
|
||||||
|
|
||||||
|
if (format == CMDL.IndexFormat.Uint16)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < indices.Length; i++)
|
||||||
|
indices[i] = reader.ReadUInt16();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < indices.Length; i++)
|
||||||
|
indices[i] = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a vertex list from the provided buffer and descriptor info.
|
||||||
|
/// </summary>
|
||||||
|
public static List<CMDL.CVertex> LoadVertexBuffer(byte[] buffer, CMDL.VertexBuffer vertexBuffer, bool isLittleEndian)
|
||||||
|
{
|
||||||
|
List<CMDL.CVertex> vertices = new List<CMDL.CVertex>();
|
||||||
|
|
||||||
|
using (var reader = new FileReader(buffer))
|
||||||
|
{
|
||||||
|
reader.SetByteOrder(!isLittleEndian); //switch is little endianness
|
||||||
|
|
||||||
|
for (int i = 0; i < vertexBuffer.VertexCount; i++) {
|
||||||
|
CMDL.CVertex vertex = new CMDL.CVertex();
|
||||||
|
vertices.Add(vertex);
|
||||||
|
|
||||||
|
foreach (var comp in vertexBuffer.Components) {
|
||||||
|
reader.SeekBegin(comp.Offset + i * comp.Stride);
|
||||||
|
switch (comp.Type)
|
||||||
|
{
|
||||||
|
case CMDL.EVertexComponent.in_position:
|
||||||
|
vertex.Position = ReadData(reader, comp.Format).Xyz;
|
||||||
|
break;
|
||||||
|
case CMDL.EVertexComponent.in_normal:
|
||||||
|
vertex.Normal = ReadData(reader, comp.Format).Xyz;
|
||||||
|
break;
|
||||||
|
case CMDL.EVertexComponent.in_texCoord0:
|
||||||
|
vertex.TexCoord0 = ReadData(reader, comp.Format).Xy;
|
||||||
|
break;
|
||||||
|
case CMDL.EVertexComponent.in_texCoord1:
|
||||||
|
vertex.TexCoord1 = ReadData(reader, comp.Format).Xy;
|
||||||
|
break;
|
||||||
|
case CMDL.EVertexComponent.in_texCoord2:
|
||||||
|
vertex.TexCoord2 = ReadData(reader, comp.Format).Xy;
|
||||||
|
break;
|
||||||
|
case CMDL.EVertexComponent.in_boneWeights:
|
||||||
|
vertex.BoneWeights = ReadData(reader, comp.Format);
|
||||||
|
break;
|
||||||
|
case CMDL.EVertexComponent.in_boneIndices:
|
||||||
|
vertex.BoneIndices = ReadData(reader, comp.Format);
|
||||||
|
break;
|
||||||
|
case CMDL.EVertexComponent.in_color:
|
||||||
|
vertex.Color = ReadData(reader, comp.Format);
|
||||||
|
break;
|
||||||
|
case CMDL.EVertexComponent.in_tangent0:
|
||||||
|
vertex.Tangent = ReadData(reader, comp.Format);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vector4 ReadData(FileReader reader, CMDL.VertexFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case CMDL.VertexFormat.Format_16_16_HalfSingle: return new Vector4(reader.ReadHalfSingle(), reader.ReadHalfSingle(), 0, 0);
|
||||||
|
case CMDL.VertexFormat.Format_32_32_32_Single: return new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), 0);
|
||||||
|
case CMDL.VertexFormat.Format_16_16_16_HalfSingle: return new Vector4(
|
||||||
|
reader.ReadHalfSingle(),reader.ReadHalfSingle(),
|
||||||
|
reader.ReadHalfSingle(), reader.ReadHalfSingle());
|
||||||
|
case CMDL.VertexFormat.Format_8_8_8_8_Uint:
|
||||||
|
return new Vector4(
|
||||||
|
reader.ReadByte(), reader.ReadByte(),
|
||||||
|
reader.ReadByte(), reader.ReadByte());
|
||||||
|
}
|
||||||
|
return new Vector4();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetIndexStride(CMDL.IndexFormat format)
|
||||||
|
{
|
||||||
|
if (format == CMDL.IndexFormat.Uint32) return 4;
|
||||||
|
else return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
134
File_Format_Library/FileFormats/DKCTF/FileData/CHAR.cs
Normal file
134
File_Format_Library/FileFormats/DKCTF/FileData/CHAR.cs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Toolbox.Library.IO;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a file format for storing character/actor data including skeleton, model and animation information.
|
||||||
|
/// </summary>
|
||||||
|
public class CHAR : FileForm
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public CObjectId SkeletonFileID;
|
||||||
|
|
||||||
|
public List<CCharacterModelSet> Models = new List<CCharacterModelSet>();
|
||||||
|
public List<CAnimationInfo> Animations = new List<CAnimationInfo>();
|
||||||
|
|
||||||
|
public bool IsSwitch;
|
||||||
|
|
||||||
|
public CHAR() { }
|
||||||
|
|
||||||
|
public CHAR(System.IO.Stream stream) : base(stream)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Read(FileReader reader)
|
||||||
|
{
|
||||||
|
reader.ReadStruct<CAssetHeader>();
|
||||||
|
reader.ReadStruct<SInfo>();
|
||||||
|
//Dumb hack atm
|
||||||
|
bool IsSwitch = false;
|
||||||
|
using (reader.TemporarySeek(reader.Position, System.IO.SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
uint len = reader.ReadUInt32();
|
||||||
|
if (len < 30)
|
||||||
|
IsSwitch = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Name = IOFileExtension.ReadFixedString(reader, IsSwitch);
|
||||||
|
|
||||||
|
SkeletonFileID = reader.ReadStruct<CObjectId>();
|
||||||
|
|
||||||
|
uint numModels = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < numModels; i++)
|
||||||
|
{
|
||||||
|
Models.Add(new CCharacterModelSet()
|
||||||
|
{
|
||||||
|
Name = IOFileExtension.ReadFixedString(reader, IsSwitch),
|
||||||
|
FileID = reader.ReadStruct<CObjectId>(),
|
||||||
|
BoundingBox = reader.ReadStruct<CAABox>(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
uint numAnimations = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < numAnimations; i++)
|
||||||
|
{
|
||||||
|
Animations.Add(new CAnimationInfo()
|
||||||
|
{
|
||||||
|
Name = IOFileExtension.ReadFixedString(reader, IsSwitch),
|
||||||
|
FileID = reader.ReadStruct<CObjectId>(),
|
||||||
|
field_1c = reader.ReadUInt32(),
|
||||||
|
field_20 = reader.ReadUInt32(),
|
||||||
|
field_24 = reader.ReadUInt16(),
|
||||||
|
field_26 = reader.ReadUInt16(),
|
||||||
|
field_28 = reader.ReadBoolean(),
|
||||||
|
BoundingBox = reader.ReadStruct<CAABox>(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class SInfo
|
||||||
|
{
|
||||||
|
public byte field_0;
|
||||||
|
public byte field_1;
|
||||||
|
public byte field_2;
|
||||||
|
public byte field_3;
|
||||||
|
public byte field_4;
|
||||||
|
public byte field_5;
|
||||||
|
public byte field_6;
|
||||||
|
public byte field_7;
|
||||||
|
public byte field_8;
|
||||||
|
public byte field_9;
|
||||||
|
public byte field_A;
|
||||||
|
public byte field_B;
|
||||||
|
public byte field_C;
|
||||||
|
public byte field_D;
|
||||||
|
public byte field_E;
|
||||||
|
public byte field_F;
|
||||||
|
|
||||||
|
public ushort flags1;
|
||||||
|
public byte flags2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CCharacterModelSet
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public CObjectId FileID;
|
||||||
|
public CAABox BoundingBox;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CAnimationInfo
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public CObjectId FileID;
|
||||||
|
|
||||||
|
public uint field_1c;
|
||||||
|
public uint field_20;
|
||||||
|
public ushort field_24;
|
||||||
|
public ushort field_26;
|
||||||
|
public bool field_28;
|
||||||
|
|
||||||
|
public CAABox BoundingBox;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
549
File_Format_Library/FileFormats/DKCTF/FileData/CMDL.cs
Normal file
549
File_Format_Library/FileFormats/DKCTF/FileData/CMDL.cs
Normal file
@ -0,0 +1,549 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Toolbox.Library.IO;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a model file format for loading mesh and material data.
|
||||||
|
/// </summary>
|
||||||
|
public class CMDL : FileForm
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The meshes of the model used to display the model.
|
||||||
|
/// </summary>
|
||||||
|
public List<CMesh> Meshes = new List<CMesh>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The materials of the model for rendering the mesh.
|
||||||
|
/// </summary>
|
||||||
|
public List<CMaterial> Materials = new List<CMaterial>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The vertex buffer list to read the buffer attributes.
|
||||||
|
/// </summary>
|
||||||
|
List<VertexBuffer> VertexBuffers = new List<VertexBuffer>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The index buffer list to read the index buffer data.
|
||||||
|
/// </summary>
|
||||||
|
List<CGraphicsIndexBufferToken> IndexBuffer = new List<CGraphicsIndexBufferToken>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines which variant of the file to parse. Switch reads strings and materials differently.
|
||||||
|
/// </summary>
|
||||||
|
bool IsSwitch => this.FileHeader.FormType == "SMDL" && this.FileHeader.VersionA >= 0x3A ||
|
||||||
|
this.FileHeader.FormType == "CMDL" && this.FileHeader.VersionA >= 0x35;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The meta data header for parsing gpu buffers and decompressing.
|
||||||
|
/// </summary>
|
||||||
|
SMetaData Meta;
|
||||||
|
|
||||||
|
public CMDL() { }
|
||||||
|
|
||||||
|
public CMDL(System.IO.Stream stream) : base(stream)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ReadMetaData(FileReader reader)
|
||||||
|
{
|
||||||
|
Meta = new SMetaData();
|
||||||
|
Meta.Unknown = reader.ReadUInt32();
|
||||||
|
Meta.GPUOffset = reader.ReadUInt32();
|
||||||
|
Meta.ReadBufferInfo = IOFileExtension.ReadList<SReadBufferInfo>(reader);
|
||||||
|
Meta.VertexBuffers = IOFileExtension.ReadList<SBufferInfo>(reader);
|
||||||
|
Meta.IndexBuffers = IOFileExtension.ReadList<SBufferInfo>(reader);
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteMetaData(FileWriter writer)
|
||||||
|
{
|
||||||
|
writer.Write(Meta.GPUOffset);
|
||||||
|
writer.Write(Meta.Unknown);
|
||||||
|
IOFileExtension.WriteList(writer, Meta.ReadBufferInfo);
|
||||||
|
IOFileExtension.WriteList(writer, Meta.VertexBuffers);
|
||||||
|
IOFileExtension.WriteList(writer, Meta.IndexBuffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ReadChunk(FileReader reader, CChunkDescriptor chunk)
|
||||||
|
{
|
||||||
|
switch (chunk.ChunkType)
|
||||||
|
{
|
||||||
|
case "CMDL":
|
||||||
|
break;
|
||||||
|
case "WMDL":
|
||||||
|
break;
|
||||||
|
case "SMDL":
|
||||||
|
reader.ReadUInt32(); //unk
|
||||||
|
break;
|
||||||
|
case "HEAD":
|
||||||
|
reader.ReadStruct<SModelHeader>();
|
||||||
|
break;
|
||||||
|
case "MTRL":
|
||||||
|
if (IsSwitch)
|
||||||
|
ReadMaterials(reader);
|
||||||
|
else
|
||||||
|
ReadMaterialsU(reader);
|
||||||
|
break;
|
||||||
|
case "MESH":
|
||||||
|
ReadMesh(reader);
|
||||||
|
break;
|
||||||
|
case "VBUF":
|
||||||
|
ReadVertexBuffer(reader);
|
||||||
|
break;
|
||||||
|
case "IBUF":
|
||||||
|
ReadIndexBuffer(reader);
|
||||||
|
break;
|
||||||
|
case "GPU ":
|
||||||
|
|
||||||
|
long startPos = reader.Position;
|
||||||
|
for (int i = 0; i < Meta.IndexBuffers.Count; i++)
|
||||||
|
{
|
||||||
|
var buffer = Meta.IndexBuffers[i];
|
||||||
|
//First buffer or specific buffer
|
||||||
|
var info = Meta.ReadBufferInfo[(int)buffer.ReadBufferIndex];
|
||||||
|
//Seek into the buffer region
|
||||||
|
reader.SeekBegin(info.Offset + buffer.Offset);
|
||||||
|
|
||||||
|
//Decompress
|
||||||
|
var data = IOFileExtension.DecompressedBuffer(reader, buffer.CompressedSize, buffer.DecompressedSize, IsSwitch);
|
||||||
|
// if (buffer.DecompressedSize != data.Length)
|
||||||
|
// throw new Exception();
|
||||||
|
|
||||||
|
//All indices
|
||||||
|
var indices = BufferHelper.LoadIndexBuffer(data, this.IndexBuffer[i].IndexType, IsSwitch);
|
||||||
|
|
||||||
|
//Read
|
||||||
|
foreach (var mesh in Meshes)
|
||||||
|
{
|
||||||
|
if (mesh.Header.IndexBufferIndex == i)
|
||||||
|
{
|
||||||
|
//Select indices to use
|
||||||
|
mesh.Indices = indices.Skip((int)mesh.Header.IndexStart).Take((int)mesh.Header.IndexCount).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Meta.VertexBuffers.Count; i++)
|
||||||
|
{
|
||||||
|
var vertexInfo = VertexBuffers[i];
|
||||||
|
|
||||||
|
var buffer = Meta.VertexBuffers[i];
|
||||||
|
//First buffer or specific buffer
|
||||||
|
var info = Meta.ReadBufferInfo[(int)buffer.ReadBufferIndex];
|
||||||
|
//Seek into the buffer region
|
||||||
|
reader.SeekBegin(info.Offset + buffer.Offset);
|
||||||
|
|
||||||
|
using (reader.TemporarySeek(reader.Position, System.IO.SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
reader.SetByteOrder(false);
|
||||||
|
var type = reader.ReadUInt32();
|
||||||
|
reader.SetByteOrder(true);
|
||||||
|
|
||||||
|
if (type != 13)
|
||||||
|
{
|
||||||
|
byte[] b = reader.ReadBytes((int)buffer.CompressedSize - 4);
|
||||||
|
System.IO.File.WriteAllBytes($"{Toolbox.Library.Runtime.ExecutableDir}\\VBuffer_{type}_{i}_{buffer.DecompressedSize}.bin", b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Decompress
|
||||||
|
var data = IOFileExtension.DecompressedBuffer(reader, buffer.CompressedSize, buffer.DecompressedSize, IsSwitch);
|
||||||
|
if (buffer.DecompressedSize != data.Length)
|
||||||
|
throw new Exception();
|
||||||
|
|
||||||
|
var vertices = BufferHelper.LoadVertexBuffer(data, vertexInfo,IsSwitch);
|
||||||
|
|
||||||
|
//Read
|
||||||
|
foreach (var mesh in Meshes)
|
||||||
|
{
|
||||||
|
if (mesh.Header.VertexBufferIndex == i)
|
||||||
|
{
|
||||||
|
//Only use the vertices referenced in the indices
|
||||||
|
//Some meshes use the same big buffer and can add too many unecessary vertices
|
||||||
|
mesh.SetupVertices(vertices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startPos += buffer.CompressedSize;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadMaterialsU(FileReader reader)
|
||||||
|
{
|
||||||
|
uint numMaterials = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < numMaterials; i++)
|
||||||
|
{
|
||||||
|
CMaterial material = new CMaterial();
|
||||||
|
Materials.Add(material);
|
||||||
|
material.Name = reader.ReadZeroTerminatedString();
|
||||||
|
material.ID = reader.ReadStruct<CObjectId>();
|
||||||
|
material.Type = reader.ReadStruct<Magic>();
|
||||||
|
material.Flags = reader.ReadUInt32();
|
||||||
|
uint numData = reader.ReadUInt32();
|
||||||
|
|
||||||
|
//Actual data type data
|
||||||
|
for (int j = 0; j < numData; j++)
|
||||||
|
{
|
||||||
|
var dtype = reader.ReadStruct<Magic>();
|
||||||
|
uint dformat = reader.ReadUInt32();
|
||||||
|
|
||||||
|
Console.WriteLine($"dtype {dtype} {dformat}");
|
||||||
|
|
||||||
|
switch (dformat)
|
||||||
|
{
|
||||||
|
case 0: //Texture
|
||||||
|
material.Textures.Add(dtype, reader.ReadStruct<CMaterialTextureTokenData>());
|
||||||
|
break;
|
||||||
|
case 1: //Color
|
||||||
|
material.Colors.Add(dtype, reader.ReadStruct<Color4f>());
|
||||||
|
break;
|
||||||
|
case 2: //Scaler
|
||||||
|
material.Scalars.Add(dtype, reader.ReadSingle());
|
||||||
|
break;
|
||||||
|
case 3: //int
|
||||||
|
material.Int.Add(dtype, reader.ReadInt32());
|
||||||
|
break;
|
||||||
|
case 4: //CLayeredTextureData
|
||||||
|
{
|
||||||
|
reader.ReadUInt32();
|
||||||
|
reader.ReadSingles(4); //color
|
||||||
|
reader.ReadSingles(4); //color
|
||||||
|
reader.ReadSingles(4); //color
|
||||||
|
reader.ReadByte(); //Flags
|
||||||
|
var texture1 = reader.ReadStruct<CObjectId>();
|
||||||
|
if (!texture1.IsZero())
|
||||||
|
reader.ReadStruct<STextureUsageInfo>();
|
||||||
|
var texture2 = reader.ReadStruct<CObjectId>();
|
||||||
|
if (!texture2.IsZero())
|
||||||
|
reader.ReadStruct<STextureUsageInfo>();
|
||||||
|
var texture3 = reader.ReadStruct<CObjectId>();
|
||||||
|
if (!texture3.IsZero())
|
||||||
|
reader.ReadStruct<STextureUsageInfo>();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 5: //int4
|
||||||
|
material.Int4.Add(dtype, reader.ReadInt32s(4));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception($"Unsupported material type {dformat}!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadMaterials(FileReader reader)
|
||||||
|
{
|
||||||
|
uint numMaterials = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < numMaterials; i++)
|
||||||
|
{
|
||||||
|
CMaterial material = new CMaterial();
|
||||||
|
Materials.Add(material);
|
||||||
|
|
||||||
|
uint size = reader.ReadUInt32();
|
||||||
|
material.Name = reader.ReadString((int)size, true);
|
||||||
|
material.ID = reader.ReadStruct<CObjectId>();
|
||||||
|
material.Type = reader.ReadStruct<Magic>();
|
||||||
|
material.Flags = reader.ReadUInt32();
|
||||||
|
uint numData = reader.ReadUInt32();
|
||||||
|
|
||||||
|
//A list of data types
|
||||||
|
for (int j = 0; j < numData; j++)
|
||||||
|
{
|
||||||
|
var dtype = reader.ReadStruct<Magic>();
|
||||||
|
var dformat = reader.ReadStruct<Magic>();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Actual data type data
|
||||||
|
for (int j = 0; j < numData; j++)
|
||||||
|
{
|
||||||
|
var dtype = reader.ReadStruct<Magic>();
|
||||||
|
var dformat = reader.ReadStruct<Magic>();
|
||||||
|
|
||||||
|
Console.WriteLine($"dtype {dtype} {dformat}");
|
||||||
|
|
||||||
|
switch (dformat)
|
||||||
|
{
|
||||||
|
case "TXTR": //Texture
|
||||||
|
material.Textures.Add(dtype, reader.ReadStruct<CMaterialTextureTokenData>());
|
||||||
|
break;
|
||||||
|
case "COLR": //Color
|
||||||
|
material.Colors.Add(dtype, reader.ReadStruct<Color4f>());
|
||||||
|
break;
|
||||||
|
case "SCLR": //Scaler
|
||||||
|
material.Scalars.Add(dtype, reader.ReadSingle());
|
||||||
|
break;
|
||||||
|
case "INT ": //int
|
||||||
|
material.Int.Add(dtype, reader.ReadInt32());
|
||||||
|
break;
|
||||||
|
case "INT4": //int4
|
||||||
|
material.Int4.Add(dtype, reader.ReadInt32s(4));
|
||||||
|
break;
|
||||||
|
case "CPLX": //CLayeredTextureData
|
||||||
|
{
|
||||||
|
reader.ReadUInt32();
|
||||||
|
reader.ReadSingles(4); //color
|
||||||
|
reader.ReadSingles(4); //color
|
||||||
|
reader.ReadSingles(4); //color
|
||||||
|
reader.ReadByte(); //Flags
|
||||||
|
var texture1 = reader.ReadStruct<CObjectId>();
|
||||||
|
if (!texture1.IsZero())
|
||||||
|
reader.ReadStruct<STextureUsageInfo>();
|
||||||
|
var texture2 = reader.ReadStruct<CObjectId>();
|
||||||
|
if (!texture2.IsZero())
|
||||||
|
reader.ReadStruct<STextureUsageInfo>();
|
||||||
|
var texture3 = reader.ReadStruct<CObjectId>();
|
||||||
|
if (!texture3.IsZero())
|
||||||
|
reader.ReadStruct<STextureUsageInfo>();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception($"Unsupported material type {dformat}!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadMesh(FileReader reader)
|
||||||
|
{
|
||||||
|
uint numMeshes = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < numMeshes; i++)
|
||||||
|
Meshes.Add(new CMesh()
|
||||||
|
{
|
||||||
|
Header = reader.ReadStruct<CRenderMesh>(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadVertexBuffer(FileReader reader)
|
||||||
|
{
|
||||||
|
uint numBuffers = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < numBuffers; i++)
|
||||||
|
{
|
||||||
|
VertexBuffer vertexBuffer = new VertexBuffer();
|
||||||
|
vertexBuffer.VertexCount = reader.ReadUInt32();
|
||||||
|
|
||||||
|
uint numAttributes = reader.ReadUInt32();
|
||||||
|
|
||||||
|
for (int j = 0; j < numAttributes; j++)
|
||||||
|
vertexBuffer.Components.Add(reader.ReadStruct<SVertexDataComponent>());
|
||||||
|
VertexBuffers.Add(vertexBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadIndexBuffer(FileReader reader)
|
||||||
|
{
|
||||||
|
uint numBuffers = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < numBuffers; i++)
|
||||||
|
IndexBuffer.Add(reader.ReadStruct<CGraphicsIndexBufferToken>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VertexBuffer
|
||||||
|
{
|
||||||
|
public List<SVertexDataComponent> Components = new List<SVertexDataComponent>();
|
||||||
|
|
||||||
|
public uint VertexCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CVertex
|
||||||
|
{
|
||||||
|
public Vector3 Position;
|
||||||
|
public Vector3 Normal;
|
||||||
|
public Vector2 TexCoord0;
|
||||||
|
public Vector2 TexCoord1;
|
||||||
|
public Vector2 TexCoord2;
|
||||||
|
|
||||||
|
public Vector4 BoneWeights = new Vector4(1, 0, 0, 0);
|
||||||
|
public Vector4 BoneIndices = new Vector4(0);
|
||||||
|
|
||||||
|
public Vector4 Color = Vector4.One;
|
||||||
|
|
||||||
|
public Vector4 Tangent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CMaterial
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
|
||||||
|
public CObjectId ID { get; set; }
|
||||||
|
|
||||||
|
public uint Flags { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, CMaterialTextureTokenData> Textures = new Dictionary<string, CMaterialTextureTokenData>();
|
||||||
|
|
||||||
|
public Dictionary<string, float> Scalars = new Dictionary<string, float>();
|
||||||
|
public Dictionary<string, int> Int = new Dictionary<string, int>();
|
||||||
|
public Dictionary<string, int[]> Int4 = new Dictionary<string, int[]>();
|
||||||
|
|
||||||
|
public Dictionary<string, Color4f> Colors = new Dictionary<string, Color4f>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CMesh
|
||||||
|
{
|
||||||
|
public CRenderMesh Header;
|
||||||
|
|
||||||
|
public List<CVertex> Vertices = new List<CVertex>();
|
||||||
|
|
||||||
|
public uint[] Indices;
|
||||||
|
|
||||||
|
public void SetupVertices(List<CVertex> vertices)
|
||||||
|
{
|
||||||
|
//Here we optmize the vertices to only use the vertices used by the mesh rather than use one giant list
|
||||||
|
List<CVertex> vertexList = new List<CVertex>();
|
||||||
|
List<uint> remappedIndices = new List<uint>();
|
||||||
|
for (int i = 0; i < Indices.Length; i++)
|
||||||
|
{
|
||||||
|
remappedIndices.Add((uint)vertexList.Count);
|
||||||
|
vertexList.Add(vertices[(int)Indices[i]]);
|
||||||
|
}
|
||||||
|
this.Vertices = vertexList;
|
||||||
|
this.Indices = remappedIndices.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SSkinnedModelHeader : CChunkDescriptor
|
||||||
|
{
|
||||||
|
public uint Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class CRenderMesh
|
||||||
|
{
|
||||||
|
public PrimtiiveType PrimtiiveMode;
|
||||||
|
public ushort MaterialIndex;
|
||||||
|
public byte VertexBufferIndex;
|
||||||
|
public byte IndexBufferIndex;
|
||||||
|
public uint IndexStart;
|
||||||
|
public uint IndexCount;
|
||||||
|
public ushort field_10;
|
||||||
|
public byte field_12;
|
||||||
|
public byte field_13;
|
||||||
|
public byte field_14;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class CMaterialTextureTokenData
|
||||||
|
{
|
||||||
|
public CObjectId FileID;
|
||||||
|
public STextureUsageInfo UsageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class STextureUsageInfo
|
||||||
|
{
|
||||||
|
public uint Flags;
|
||||||
|
public uint TextureFilter;
|
||||||
|
public uint TextureWrapX;
|
||||||
|
public uint TextureWrapY;
|
||||||
|
public uint TextureWrapZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class SModelHeader
|
||||||
|
{
|
||||||
|
public uint NumOpaqueMeshes;
|
||||||
|
public uint Num1PassTranslucentMeshes;
|
||||||
|
public uint Num2PassTranslucentMeshes;
|
||||||
|
public uint Num1BitMeshes;
|
||||||
|
public uint NumAdditiveMeshes;
|
||||||
|
public CAABox BoundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class CGraphicsIndexBufferToken
|
||||||
|
{
|
||||||
|
public IndexFormat IndexType;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class SVertexDataComponent
|
||||||
|
{
|
||||||
|
public uint field_0;
|
||||||
|
public uint Offset;
|
||||||
|
public uint Stride;
|
||||||
|
public VertexFormat Format;
|
||||||
|
public EVertexComponent Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum IndexFormat
|
||||||
|
{
|
||||||
|
Uint16 = 1,
|
||||||
|
Uint32 = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PrimtiiveType
|
||||||
|
{
|
||||||
|
Triangles = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum VertexFormat
|
||||||
|
{
|
||||||
|
Byte = 0,
|
||||||
|
Format_16_16_HalfSingle = 20,
|
||||||
|
Format_8_8_8_8_Uint = 22,
|
||||||
|
Format_16_16_16_HalfSingle = 34,
|
||||||
|
Format_32_32_32_Single = 37,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EVertexComponent
|
||||||
|
{
|
||||||
|
in_position,
|
||||||
|
in_normal,
|
||||||
|
in_tangent0,
|
||||||
|
in_tangent1,
|
||||||
|
in_texCoord0,
|
||||||
|
in_texCoord1,
|
||||||
|
in_texCoord2,
|
||||||
|
in_texCoord3,
|
||||||
|
in_color,
|
||||||
|
in_boneIndices,
|
||||||
|
in_boneWeights,
|
||||||
|
in_bakedLightingCoord,
|
||||||
|
in_bakedLightingTangent,
|
||||||
|
in_vertInstanceColor,
|
||||||
|
//3x4 matrices
|
||||||
|
in_vertTransform0,
|
||||||
|
in_vertTransform1,
|
||||||
|
in_vertTransform2,
|
||||||
|
//3x4 matrices for instancing
|
||||||
|
in_vertTransformIT0,
|
||||||
|
in_vertTransformIT1,
|
||||||
|
in_vertTransformIT2,
|
||||||
|
in_lastPosition,
|
||||||
|
in_currentPosition,
|
||||||
|
}
|
||||||
|
|
||||||
|
//Meta data from PAK archive
|
||||||
|
|
||||||
|
public class SMetaData
|
||||||
|
{
|
||||||
|
public uint Unknown;
|
||||||
|
public uint GPUOffset;
|
||||||
|
public List<SReadBufferInfo> ReadBufferInfo = new List<SReadBufferInfo>();
|
||||||
|
public List<SBufferInfo> VertexBuffers = new List<SBufferInfo>();
|
||||||
|
public List<SBufferInfo> IndexBuffers = new List<SBufferInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class SReadBufferInfo
|
||||||
|
{
|
||||||
|
public uint Size;
|
||||||
|
public uint Offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class SBufferInfo
|
||||||
|
{
|
||||||
|
public uint ReadBufferIndex;
|
||||||
|
public uint Offset;
|
||||||
|
public uint CompressedSize;
|
||||||
|
public uint DecompressedSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
316
File_Format_Library/FileFormats/DKCTF/FileData/Common.cs
Normal file
316
File_Format_Library/FileFormats/DKCTF/FileData/Common.cs
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Toolbox.Library.IO;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the base of a file format.
|
||||||
|
/// </summary>
|
||||||
|
public class FileForm
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The form header containing the type and version of the file.
|
||||||
|
/// </summary>
|
||||||
|
public CFormDescriptor FileHeader;
|
||||||
|
|
||||||
|
public FileForm() { }
|
||||||
|
|
||||||
|
public FileForm(Stream stream, bool leaveOpen = false)
|
||||||
|
{
|
||||||
|
using (var reader = new FileReader(stream, leaveOpen))
|
||||||
|
{
|
||||||
|
reader.SetByteOrder(true);
|
||||||
|
FileHeader = reader.ReadStruct<CFormDescriptor>();
|
||||||
|
Read(reader);
|
||||||
|
AfterLoad();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the file by looping through chunks..
|
||||||
|
/// </summary>
|
||||||
|
public virtual void Read(FileReader reader)
|
||||||
|
{
|
||||||
|
var endPos = ReadMetaFooter(reader);
|
||||||
|
|
||||||
|
while (reader.BaseStream.Position < endPos)
|
||||||
|
{
|
||||||
|
var chunk = reader.ReadStruct<CChunkDescriptor>();
|
||||||
|
var pos = reader.Position;
|
||||||
|
|
||||||
|
reader.SeekBegin(pos + chunk.DataOffset);
|
||||||
|
ReadChunk(reader, chunk);
|
||||||
|
|
||||||
|
reader.SeekBegin(pos + chunk.DataSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a specified chunk.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void ReadChunk(FileReader reader, CChunkDescriptor chunk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads meta data information within the pak archive.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void ReadMetaData(FileReader reader)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes meta data information within the pak archive.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void WriteMetaData(FileWriter writer)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes after the file has been fully read.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void AfterLoad()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the tool created footer containing meta data used for decompressing buffer data.
|
||||||
|
/// </summary>
|
||||||
|
public long ReadMetaFooter(FileReader reader)
|
||||||
|
{
|
||||||
|
using (reader.TemporarySeek(reader.BaseStream.Length - 12, SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
if (reader.ReadString(4, Encoding.ASCII) != "META")
|
||||||
|
return reader.BaseStream.Length;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
using (reader.TemporarySeek(reader.BaseStream.Length - 12, SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
reader.ReadSignature(4, "META");
|
||||||
|
reader.ReadString(4); //type of file
|
||||||
|
uint size = reader.ReadUInt32(); //size of meta data
|
||||||
|
//Seek back to meta data
|
||||||
|
reader.SeekBegin(reader.Position - size);
|
||||||
|
//Read meta data
|
||||||
|
ReadMetaData(reader);
|
||||||
|
|
||||||
|
return reader.BaseStream.Length - size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a footer to a file for accessing meta data information outside a .pak archive.
|
||||||
|
/// </summary>
|
||||||
|
public static byte[] WriteMetaFooter(FileReader reader, uint metaOffset, string type)
|
||||||
|
{
|
||||||
|
//Magic + meta offset first
|
||||||
|
var mem = new MemoryStream();
|
||||||
|
using (var writer = new FileWriter(mem))
|
||||||
|
{
|
||||||
|
writer.SetByteOrder(true);
|
||||||
|
|
||||||
|
reader.SeekBegin(metaOffset);
|
||||||
|
var file = GetFileForm(type);
|
||||||
|
file.ReadMetaData(reader);
|
||||||
|
file.WriteMetaData(writer);
|
||||||
|
|
||||||
|
//Write footer header last
|
||||||
|
writer.WriteSignature("META");
|
||||||
|
writer.WriteSignature(type);
|
||||||
|
writer.Write((uint)(writer.BaseStream.Length + 4)); //size
|
||||||
|
}
|
||||||
|
return mem.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Creates file instances for read/writing meta entries from pak archives
|
||||||
|
static FileForm GetFileForm(string type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "CMDL": return new CMDL();
|
||||||
|
case "SMDL": return new CMDL();
|
||||||
|
case "WMDL": return new CMDL();
|
||||||
|
case "TXTR": return new TXTR();
|
||||||
|
}
|
||||||
|
return new FileForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Documentation from https://github.com/Kinnay/Nintendo-File-Formats/wiki/DKCTF-Types#cformdescriptor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the header of a file format.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class CFormDescriptor
|
||||||
|
{
|
||||||
|
public Magic Magic = "RFRM";
|
||||||
|
public ulong DataSize;
|
||||||
|
public ulong Unknown;
|
||||||
|
public Magic FormType; //File type identifier
|
||||||
|
public uint VersionA;
|
||||||
|
public uint VersionB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the header of a chunk of a file form.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class CChunkDescriptor
|
||||||
|
{
|
||||||
|
public Magic ChunkType;
|
||||||
|
public long DataSize;
|
||||||
|
public uint Unknown;
|
||||||
|
public long DataOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class CMayaSpline
|
||||||
|
{
|
||||||
|
public Magic ChunkType;
|
||||||
|
public ulong DataSize;
|
||||||
|
public uint Unknown;
|
||||||
|
public ulong DataOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class CMayaSplineKnot
|
||||||
|
{
|
||||||
|
public float Time;
|
||||||
|
public float Value;
|
||||||
|
public ETangentType TangentType1;
|
||||||
|
public ETangentType TangentType2;
|
||||||
|
public float FieldC;
|
||||||
|
public float Field10;
|
||||||
|
public float Field14;
|
||||||
|
public float Field18;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag data for an object providing the type and id.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class CObjectTag
|
||||||
|
{
|
||||||
|
public Magic Type;
|
||||||
|
public CObjectId Objectid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A header for asset data providing a type and version number.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class CAssetHeader
|
||||||
|
{
|
||||||
|
public ushort TypeID;
|
||||||
|
public ushort Version;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores a unique ID for a given object to identify it.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct CObjectId
|
||||||
|
{
|
||||||
|
public CGuid Guid;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Guid.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsZero()
|
||||||
|
{
|
||||||
|
return Guid.Part1 == 0 &&
|
||||||
|
Guid.Part2 == 0 &&
|
||||||
|
Guid.Part3 == 0 &&
|
||||||
|
Guid.Part4 == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An axis aligned bounding box with a min and max position value.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class CAABox
|
||||||
|
{
|
||||||
|
public Vector3f Min;
|
||||||
|
public Vector3f Max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A vector with X/Y/Z axis values.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class Vector3f
|
||||||
|
{
|
||||||
|
public float X;
|
||||||
|
public float Y;
|
||||||
|
public float Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A color struct of RGBA values.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class Color4f
|
||||||
|
{
|
||||||
|
public float R;
|
||||||
|
public float G;
|
||||||
|
public float B;
|
||||||
|
public float A;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A 128 bit guid for identifying objects.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct CGuid
|
||||||
|
{
|
||||||
|
public uint Part1;
|
||||||
|
public ushort Part2;
|
||||||
|
public ushort Part3;
|
||||||
|
public ulong Part4;
|
||||||
|
|
||||||
|
public Guid ToGUID()
|
||||||
|
{
|
||||||
|
var bytes = BitConverter.GetBytes(Part4).Reverse().ToArray();
|
||||||
|
return new Guid(Part1, Part2, Part3, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() //Represented based on output guids in demo files
|
||||||
|
{
|
||||||
|
return ToGUID().ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ETangentType
|
||||||
|
{
|
||||||
|
Linear,
|
||||||
|
Flat,
|
||||||
|
Smooth,
|
||||||
|
Step,
|
||||||
|
Clamped,
|
||||||
|
Fixed,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EInfinityType
|
||||||
|
{
|
||||||
|
Constant,
|
||||||
|
Linear,
|
||||||
|
Cycle,
|
||||||
|
CycleRelative,
|
||||||
|
Oscillate,
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,205 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Toolbox.Library.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
internal class IOFileExtension
|
||||||
|
{
|
||||||
|
public static void WriteList<T>(FileWriter writer, List<T> list)
|
||||||
|
{
|
||||||
|
writer.Write(list.Count);
|
||||||
|
for (int i = 0; i < list.Count; i++)
|
||||||
|
writer.WriteStruct(list[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReadFixedString(FileReader reader, bool isSwitch = true)
|
||||||
|
{
|
||||||
|
if (!isSwitch)
|
||||||
|
return reader.ReadZeroTerminatedString();
|
||||||
|
|
||||||
|
uint len = reader.ReadUInt32();
|
||||||
|
return reader.ReadString((int)len, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<T> ReadList<T>(FileReader reader)
|
||||||
|
{
|
||||||
|
List<T> list = new List<T>();
|
||||||
|
|
||||||
|
uint count = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
list.Add(reader.ReadStruct<T>());
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CObjectId ReadID(FileReader reader)
|
||||||
|
{
|
||||||
|
return new CObjectId()
|
||||||
|
{
|
||||||
|
Guid = new CGuid()
|
||||||
|
{
|
||||||
|
Part1 = reader.ReadUInt32(),
|
||||||
|
Part2 = reader.ReadUInt16(),
|
||||||
|
Part3 = reader.ReadUInt16(),
|
||||||
|
Part4 = reader.ReadUInt64(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
public static byte[] DecompressedBuffer(FileReader reader, uint compSize, uint decompSize, bool isSwitch = true)
|
||||||
|
{
|
||||||
|
reader.SetByteOrder(false);
|
||||||
|
CompressionType type = (CompressionType)reader.ReadUInt32();
|
||||||
|
reader.SetByteOrder(true);
|
||||||
|
|
||||||
|
Console.WriteLine($"type {type}");
|
||||||
|
// File.WriteAllBytes($"Buffer{type}.bin", reader.ReadBytes((int)(compSize - 4)));
|
||||||
|
|
||||||
|
var data = reader.ReadBytes((int)compSize - 4);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case CompressionType.None:
|
||||||
|
return reader.ReadBytes((int)decompSize);
|
||||||
|
//LZSS with byte, short, and uint types
|
||||||
|
case CompressionType.LZSS_8: return DecompressLZSS(data, 1, decompSize);
|
||||||
|
case CompressionType.LZSS_16: return DecompressLZSS(data, 2, decompSize);
|
||||||
|
case CompressionType.LZSS_32: return DecompressLZSS(data, 3, decompSize);
|
||||||
|
case CompressionType.ZLib:
|
||||||
|
return STLibraryCompression.ZLIB.Decompress(data);
|
||||||
|
default:
|
||||||
|
return new byte[decompSize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CompressionType //8 = byte, 16 = short, 32 = uint32
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
LZSS_8 = 0x1,
|
||||||
|
LZSS_16 = 0x2,
|
||||||
|
LZSS_32 = 0x3,
|
||||||
|
ArithmeticStream_LZSS_8 = 0x4,
|
||||||
|
ArithmeticStream_LZSS_16 = 0x5,
|
||||||
|
ArithmeticStream_LZSS_32 = 0x6,
|
||||||
|
LZSS_8_3Byte = 0x7,
|
||||||
|
LZSS_16_3Byte = 0x8,
|
||||||
|
LZSS_32_3Byte = 0x9,
|
||||||
|
ArithmeticStream_LZSS_8_3Byte = 0xA,
|
||||||
|
ArithmeticStream_LZSS_16_3Byte = 0xB,
|
||||||
|
ArithmeticStream_LZSS_32_3Byte = 0xC,
|
||||||
|
ZLib = 0xD,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] DecompressLZSS(byte[] input, int mode, uint decompressedLength)
|
||||||
|
{
|
||||||
|
byte[] decomp = new byte[decompressedLength];
|
||||||
|
|
||||||
|
int src = 0;
|
||||||
|
int dst = 0;
|
||||||
|
|
||||||
|
// Otherwise, start preparing for decompression.
|
||||||
|
byte header_byte = 0;
|
||||||
|
byte group = 0;
|
||||||
|
|
||||||
|
while (src < input.Length && dst < decompressedLength)
|
||||||
|
{
|
||||||
|
// group will start at 8 and decrease by 1 with each data chunk read.
|
||||||
|
// When group reaches 0, we read a new header byte and reset it to 8.
|
||||||
|
if (group == 0)
|
||||||
|
{
|
||||||
|
header_byte = input[src++];
|
||||||
|
group = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// header_byte will be shifted left one bit for every data group read, so 0x80 always corresponds to the current data group.
|
||||||
|
// If 0x80 is set, then we read back from the decompressed buffer.
|
||||||
|
if ((header_byte & 0x80) != 0)
|
||||||
|
{
|
||||||
|
byte[] bytes = new byte[] { input[src++], input[src++] };
|
||||||
|
uint count = 0, length = 0;
|
||||||
|
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case 1: //byte
|
||||||
|
count = (uint)((bytes[0] >> 4) + 3);
|
||||||
|
length = (uint)(((bytes[0] & 0xF) << 0x8) | bytes[1]);
|
||||||
|
break;
|
||||||
|
case 2: //short
|
||||||
|
count = (uint)((bytes[0] >> 4) + 2);
|
||||||
|
length = (uint)((((bytes[0] & 0xF) << 0x8) | bytes[1]) << 1);
|
||||||
|
break;
|
||||||
|
case 3: //uint
|
||||||
|
count = (uint)((bytes[0] >> 4) + 1);
|
||||||
|
length = (uint)((((bytes[0] & 0xF) << 0x8) | bytes[1]) << 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// With the count and length calculated, we'll set a pointer to where we want to read back data from:
|
||||||
|
int seek = (dst - (int)length);
|
||||||
|
|
||||||
|
// count refers to how many byte groups to read back; the size of one byte group varies depending on mode
|
||||||
|
for (uint yb = 0; yb < count; yb++)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case 1: //byte
|
||||||
|
decomp[dst++] = decomp[(int)seek++];
|
||||||
|
break;
|
||||||
|
case 2: //short
|
||||||
|
for (uint b = 0; b < 2; b++)
|
||||||
|
decomp[dst++] = decomp[(int)seek++];
|
||||||
|
break;
|
||||||
|
case 3: //uint
|
||||||
|
for (uint b = 0; b < 4; b++)
|
||||||
|
decomp[dst++] = decomp[(int)seek++];
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If 0x80 is not set, then we read one byte group directly from the compressed buffer.
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case 1: //byte
|
||||||
|
decomp[dst++] = input[src++];
|
||||||
|
break;
|
||||||
|
case 2: //short
|
||||||
|
for (uint b = 0; b < 2; b++)
|
||||||
|
decomp[dst++] = input[src++];
|
||||||
|
break;
|
||||||
|
case 3: //uint
|
||||||
|
for (uint b = 0; b < 4; b++)
|
||||||
|
decomp[dst++] = input[src++];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header_byte <<= 1;
|
||||||
|
group--;
|
||||||
|
}
|
||||||
|
return decomp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] DecompressArithmeticStream(byte[] input, int mode, uint decompressedLength)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] DecompressLZSS3Bytes(byte[] input, int mode, uint decompressedLength)
|
||||||
|
{
|
||||||
|
byte[] decomp = new byte[decompressedLength];
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
|
||||||
|
return decomp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
152
File_Format_Library/FileFormats/DKCTF/FileData/PACK.cs
Normal file
152
File_Format_Library/FileFormats/DKCTF/FileData/PACK.cs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Toolbox.Library.IO;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an package file format for storing file data.
|
||||||
|
/// </summary>
|
||||||
|
public class PACK : FileForm
|
||||||
|
{
|
||||||
|
public CFormDescriptor TocHeader;
|
||||||
|
|
||||||
|
public List<DirectoryAssetEntry> Assets = new List<DirectoryAssetEntry>();
|
||||||
|
public List<CNameTagEntry> NameTagEntries = new List<CNameTagEntry>();
|
||||||
|
public Dictionary<string, uint> MetaOffsets = new Dictionary<string, uint>();
|
||||||
|
|
||||||
|
public long MetaDataOffset;
|
||||||
|
|
||||||
|
public PACK() { }
|
||||||
|
|
||||||
|
public PACK(System.IO.Stream stream) : base(stream, true)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Read(FileReader reader)
|
||||||
|
{
|
||||||
|
TocHeader = reader.ReadStruct<CFormDescriptor>();
|
||||||
|
long p = reader.Position;
|
||||||
|
|
||||||
|
while (reader.BaseStream.Position < p + (long)TocHeader.DataSize)
|
||||||
|
{
|
||||||
|
var chunk = reader.ReadStruct<CChunkDescriptor>();
|
||||||
|
var pos = reader.Position;
|
||||||
|
|
||||||
|
reader.SeekBegin(pos + chunk.DataOffset);
|
||||||
|
ReadChunk(reader, chunk);
|
||||||
|
|
||||||
|
reader.SeekBegin(pos + chunk.DataSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ReadChunk(FileReader reader, CChunkDescriptor chunk)
|
||||||
|
{
|
||||||
|
switch (chunk.ChunkType)
|
||||||
|
{
|
||||||
|
case "TOCC":
|
||||||
|
TocHeader = reader.ReadStruct<CFormDescriptor>();
|
||||||
|
break;
|
||||||
|
case "ADIR":
|
||||||
|
ReadAssetDirectoryChunk(reader);
|
||||||
|
break;
|
||||||
|
case "META":
|
||||||
|
ReadMetaChunk(reader);
|
||||||
|
break;
|
||||||
|
case "STRG":
|
||||||
|
ReadFileNameChunk(reader);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadAssetDirectoryChunk(FileReader reader)
|
||||||
|
{
|
||||||
|
uint numEntries = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < numEntries; i++)
|
||||||
|
{
|
||||||
|
DirectoryAssetEntry entry = new DirectoryAssetEntry();
|
||||||
|
entry.Read(reader);
|
||||||
|
Assets.Add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadMetaChunk(FileReader reader)
|
||||||
|
{
|
||||||
|
MetaDataOffset = reader.Position + 4;
|
||||||
|
|
||||||
|
uint numEntries = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < numEntries; i++)
|
||||||
|
{
|
||||||
|
var id = IOFileExtension.ReadID(reader);
|
||||||
|
|
||||||
|
uint offset = reader.ReadUInt32();
|
||||||
|
MetaOffsets.Add(id.ToString(), offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadFileNameChunk(FileReader reader)
|
||||||
|
{
|
||||||
|
uint numEntries = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < numEntries; i++)
|
||||||
|
{
|
||||||
|
string type = reader.ReadString(4, Encoding.ASCII);
|
||||||
|
var id = IOFileExtension.ReadID(reader);
|
||||||
|
|
||||||
|
string name = reader.ReadZeroTerminatedString();
|
||||||
|
|
||||||
|
// reader.Align(4);
|
||||||
|
NameTagEntries.Add(new CNameTagEntry()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
FileID = new CObjectTag()
|
||||||
|
{
|
||||||
|
Type = type,
|
||||||
|
Objectid = id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DirectoryAssetEntry
|
||||||
|
{
|
||||||
|
public string Type;
|
||||||
|
public CObjectId FileID;
|
||||||
|
|
||||||
|
public long Offset;
|
||||||
|
public long Size;
|
||||||
|
|
||||||
|
public void Read(FileReader reader)
|
||||||
|
{
|
||||||
|
Type = reader.ReadString(4, Encoding.ASCII);
|
||||||
|
FileID = IOFileExtension.ReadID(reader);
|
||||||
|
Offset = reader.ReadInt64();
|
||||||
|
Size = reader.ReadInt64();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class MetaOffsetEntry
|
||||||
|
{
|
||||||
|
public CObjectId FileID;
|
||||||
|
public uint FileOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class PakHeader
|
||||||
|
{
|
||||||
|
CFormDescriptor PackForm;
|
||||||
|
CFormDescriptor TocForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CNameTagEntry
|
||||||
|
{
|
||||||
|
public CObjectTag FileID;
|
||||||
|
public string Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
223
File_Format_Library/FileFormats/DKCTF/FileData/SKEL.cs
Normal file
223
File_Format_Library/FileFormats/DKCTF/FileData/SKEL.cs
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Toolbox.Library.IO;
|
||||||
|
using OpenTK;
|
||||||
|
using Toolbox.Library;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a skeletal file format.
|
||||||
|
/// </summary>
|
||||||
|
public class SKEL : FileForm
|
||||||
|
{
|
||||||
|
public List<string> JointNames = new List<string>();
|
||||||
|
public List<string> JointNamesVis = new List<string>();
|
||||||
|
|
||||||
|
public List<JointSet> JointSets = new List<JointSet>();
|
||||||
|
|
||||||
|
public List<BoneCoord> JointCoords = new List<BoneCoord>();
|
||||||
|
|
||||||
|
public byte[] SkinnedBonesRemap = new byte[0];
|
||||||
|
public byte[] BoneParentingIndices = new byte[0];
|
||||||
|
|
||||||
|
CAnimationAttrData AnimationAttributes = null;
|
||||||
|
|
||||||
|
CSkelMap SkelMap = null;
|
||||||
|
|
||||||
|
public SKEL() { }
|
||||||
|
|
||||||
|
public SKEL(System.IO.Stream stream) : base(stream)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Read(FileReader reader)
|
||||||
|
{
|
||||||
|
reader.ReadStruct<CAssetHeader>(); //version 0x9e22
|
||||||
|
|
||||||
|
//CSkelLayout
|
||||||
|
|
||||||
|
//CJointNameArray
|
||||||
|
JointNames = ReadCJointNameArray(reader);
|
||||||
|
|
||||||
|
ushort numTotalJoints = reader.ReadUInt16(); //Includes vis and attribute nodes
|
||||||
|
ushort numJoints = reader.ReadUInt16(); //Normal joint list with coordinates associated
|
||||||
|
ushort numSkinnedJoints = reader.ReadUInt16();
|
||||||
|
ushort numJointSets = reader.ReadUInt16();
|
||||||
|
ushort numUnkE = reader.ReadUInt16();
|
||||||
|
bool hasSkeletonMap = reader.ReadBoolean();
|
||||||
|
//CSkelMap
|
||||||
|
if (hasSkeletonMap)
|
||||||
|
{
|
||||||
|
SkelMap = new CSkelMap();
|
||||||
|
|
||||||
|
ushort numRemap = reader.ReadUInt16();
|
||||||
|
byte numUnk1 = reader.ReadByte();
|
||||||
|
byte numUnk2 = reader.ReadByte();
|
||||||
|
SkelMap.JointIndices = reader.ReadBytes(numRemap);
|
||||||
|
SkelMap.Unk2 = reader.ReadUInt16s(numRemap * 2);
|
||||||
|
SkelMap.Unk3 = reader.ReadUInt32s(numUnk1);
|
||||||
|
SkelMap.Unk4 = reader.ReadInt16s(numUnk2);
|
||||||
|
SkelMap.Flag = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
bool hasAnimationAttributes = reader.ReadBoolean();
|
||||||
|
//CAnimationAttrData
|
||||||
|
if (hasAnimationAttributes)
|
||||||
|
{
|
||||||
|
bool hasVisibilityNameGroup = reader.ReadBoolean();
|
||||||
|
if (hasVisibilityNameGroup)
|
||||||
|
{
|
||||||
|
JointNamesVis = ReadCJointNameArray(reader);
|
||||||
|
}
|
||||||
|
uint stateParam1 = reader.ReadUInt32();
|
||||||
|
uint stateParam2 = reader.ReadUInt32();
|
||||||
|
|
||||||
|
bool hasAnimationAttributeData = reader.ReadBoolean();
|
||||||
|
if (hasAnimationAttributeData)
|
||||||
|
{
|
||||||
|
AnimationAttributes = new CAnimationAttrData();
|
||||||
|
|
||||||
|
AnimationAttributes.Joints = ReadCJointNameArray(reader);
|
||||||
|
uint numAttributes = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < numAttributes; i++)
|
||||||
|
{
|
||||||
|
CAnimAttrInfo att = new CAnimAttrInfo();
|
||||||
|
|
||||||
|
att.Flag = reader.ReadUInt32();
|
||||||
|
if (att.Flag == 1)
|
||||||
|
{
|
||||||
|
att.Value1 = reader.ReadSingle();
|
||||||
|
att.Value2 = reader.ReadSingle();
|
||||||
|
}
|
||||||
|
AnimationAttributes.Attributes.Add(att);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BoneParentingIndices = reader.ReadBytes((int)numJoints);
|
||||||
|
SkinnedBonesRemap = reader.ReadBytes((int)numSkinnedJoints);
|
||||||
|
byte[] unkA = reader.ReadBytes((int)numTotalJoints);
|
||||||
|
byte[] unkE = reader.ReadBytes((int)numUnkE);
|
||||||
|
uint[] unkD = reader.ReadUInt32s((int)numJointSets);
|
||||||
|
|
||||||
|
for (int i = 0; i < numJoints; i++)
|
||||||
|
{
|
||||||
|
var rot = reader.ReadQuaternion();
|
||||||
|
JointCoords.Add(new BoneCoord()
|
||||||
|
{
|
||||||
|
Rotation = new Quaternion(rot.Y, rot.Z, rot.W, rot.X),
|
||||||
|
Scale = reader.ReadVec3(),
|
||||||
|
Position = reader.ReadVec3(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (int i = 0; i < numJointSets; i++)
|
||||||
|
{
|
||||||
|
JointSet jointSet = new JointSet();
|
||||||
|
|
||||||
|
jointSet.unk1 = reader.ReadUInt32();
|
||||||
|
uint jointSetCount = reader.ReadUInt32();
|
||||||
|
jointSet.unk2 = reader.ReadUInt32s(8);
|
||||||
|
jointSet.JointIndices = reader.ReadBytes((int)jointSetCount);
|
||||||
|
|
||||||
|
JointSets.Add(jointSet);
|
||||||
|
}
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public STSkeleton ToGenericSkeleton()
|
||||||
|
{
|
||||||
|
STSkeleton skeleton = new STSkeleton();
|
||||||
|
for (int i = 0; i < this.JointCoords.Count; i++)
|
||||||
|
{
|
||||||
|
var remap = SkelMap == null ? i : SkelMap.JointIndices[i];
|
||||||
|
var parentID = this.BoneParentingIndices[i];
|
||||||
|
var name = this.JointNames[i];
|
||||||
|
var coord = this.JointCoords[i];
|
||||||
|
|
||||||
|
skeleton.bones.Add(new STBone(skeleton)
|
||||||
|
{
|
||||||
|
RotationType = STBone.BoneRotationType.Quaternion,
|
||||||
|
Text = name,
|
||||||
|
Position = coord.Position,
|
||||||
|
Rotation = coord.Rotation,
|
||||||
|
Scale = coord.Scale,
|
||||||
|
parentIndex = parentID == 255 ? -1 : parentID,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
skeleton.reset();
|
||||||
|
skeleton.update();
|
||||||
|
return skeleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Matrix4 CalculateLocalMatrix(BoneCoord coord, int id)
|
||||||
|
{
|
||||||
|
if (BoneParentingIndices[id] == 255)
|
||||||
|
return CreateWorldMatrix(coord);
|
||||||
|
|
||||||
|
var parentMatrix = CreateWorldMatrix(JointCoords[BoneParentingIndices[id]]);
|
||||||
|
return parentMatrix.Inverted();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Matrix4 CreateWorldMatrix(BoneCoord coord)
|
||||||
|
{
|
||||||
|
return Matrix4.CreateScale(coord.Scale) *
|
||||||
|
Matrix4.CreateFromQuaternion(coord.Rotation) *
|
||||||
|
Matrix4.CreateTranslation(coord.Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> ReadCJointNameArray(FileReader reader)
|
||||||
|
{
|
||||||
|
List<string> joints = new List<string>();
|
||||||
|
|
||||||
|
uint field_0 = reader.ReadUInt32();
|
||||||
|
uint count = reader.ReadUInt32();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
joints.Add(IOFileExtension.ReadFixedString(reader, true));
|
||||||
|
}
|
||||||
|
uint unk2 = reader.ReadUInt32();
|
||||||
|
|
||||||
|
return joints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BoneCoord
|
||||||
|
{
|
||||||
|
public Vector3 Position;
|
||||||
|
public Vector3 Scale;
|
||||||
|
public Quaternion Rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CSkelMap
|
||||||
|
{
|
||||||
|
public byte[] JointIndices;
|
||||||
|
public ushort[] Unk2;
|
||||||
|
public uint[] Unk3;
|
||||||
|
public short[] Unk4;
|
||||||
|
public uint Flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CAnimationAttrData
|
||||||
|
{
|
||||||
|
public List<string> Joints = new List<string>();
|
||||||
|
public List<CAnimAttrInfo> Attributes = new List<CAnimAttrInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CAnimAttrInfo
|
||||||
|
{
|
||||||
|
public uint Flag;
|
||||||
|
public float Value1;
|
||||||
|
public float Value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JointSet
|
||||||
|
{
|
||||||
|
public uint unk1;
|
||||||
|
public uint[] unk2;
|
||||||
|
public byte[] JointIndices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
154
File_Format_Library/FileFormats/DKCTF/FileData/TXTR.cs
Normal file
154
File_Format_Library/FileFormats/DKCTF/FileData/TXTR.cs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Toolbox.Library.IO;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a texture file format.
|
||||||
|
/// </summary>
|
||||||
|
internal class TXTR : FileForm
|
||||||
|
{
|
||||||
|
public STextureHeader TextureHeader;
|
||||||
|
|
||||||
|
public SMetaData Meta;
|
||||||
|
|
||||||
|
public byte[] BufferData;
|
||||||
|
|
||||||
|
public uint[] MipSizes = new uint[0];
|
||||||
|
|
||||||
|
public uint TextureSize { get; set; }
|
||||||
|
|
||||||
|
public uint Unknown { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public bool IsSwitch => this.FileHeader.VersionA >= 0x0F;
|
||||||
|
|
||||||
|
public TXTR() { }
|
||||||
|
|
||||||
|
public TXTR(System.IO.Stream stream) : base(stream)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] CreateUncompressedFile(byte[] fileData)
|
||||||
|
{
|
||||||
|
var mem = new MemoryStream();
|
||||||
|
using (var writer = new FileWriter(mem))
|
||||||
|
using (var reader = new FileReader(fileData))
|
||||||
|
{
|
||||||
|
writer.SetByteOrder(true);
|
||||||
|
reader.SetByteOrder(true);
|
||||||
|
|
||||||
|
FileHeader = reader.ReadStruct<CFormDescriptor>();
|
||||||
|
ReadMetaFooter(reader);
|
||||||
|
|
||||||
|
reader.Position = 0;
|
||||||
|
byte[] textureInfo = reader.ReadBytes((int)Meta.GPUOffset + 24);
|
||||||
|
|
||||||
|
long pos = reader.BaseStream.Position - 24;
|
||||||
|
|
||||||
|
var buffer = Meta.BufferInfo[0];
|
||||||
|
reader.Seek(buffer.Offset);
|
||||||
|
|
||||||
|
byte[] BufferData = IOFileExtension.DecompressedBuffer(reader, buffer.CompressedSize, buffer.DecompressedSize, IsSwitch);
|
||||||
|
|
||||||
|
writer.Write(textureInfo);
|
||||||
|
writer.Write(BufferData);
|
||||||
|
|
||||||
|
using (writer.TemporarySeek(pos + 4, SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
writer.Write((long)BufferData.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mem.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ReadChunk(FileReader reader, CChunkDescriptor chunk)
|
||||||
|
{
|
||||||
|
switch (chunk.ChunkType)
|
||||||
|
{
|
||||||
|
case "HEAD":
|
||||||
|
TextureHeader = reader.ReadStruct<STextureHeader>();
|
||||||
|
uint numMips = reader.ReadUInt32();
|
||||||
|
MipSizes = reader.ReadUInt32s((int)numMips);
|
||||||
|
TextureSize = reader.ReadUInt32();
|
||||||
|
Unknown = reader.ReadUInt32();
|
||||||
|
break;
|
||||||
|
case "GPU ":
|
||||||
|
if (Meta != null)
|
||||||
|
{
|
||||||
|
var buffer = Meta.BufferInfo[0];
|
||||||
|
|
||||||
|
reader.Seek(buffer.Offset);
|
||||||
|
BufferData = IOFileExtension.DecompressedBuffer(reader, buffer.CompressedSize, buffer.DecompressedSize, IsSwitch);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BufferData = reader.ReadBytes((int)chunk.DataSize);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ReadMetaData(FileReader reader)
|
||||||
|
{
|
||||||
|
Meta = new SMetaData();
|
||||||
|
Meta.Unknown = reader.ReadUInt32();
|
||||||
|
Meta.AllocCategory = reader.ReadUInt32();
|
||||||
|
Meta.GPUOffset = reader.ReadUInt32();
|
||||||
|
Meta.BaseAlignment = reader.ReadUInt32();
|
||||||
|
Meta.GPUDataStart = reader.ReadUInt32();
|
||||||
|
Meta.GPUDataSize = reader.ReadUInt32();
|
||||||
|
Meta.BufferInfo = IOFileExtension.ReadList<SCompressedBufferInfo>(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteMetaData(FileWriter writer)
|
||||||
|
{
|
||||||
|
writer.Write(Meta.Unknown);
|
||||||
|
writer.Write(Meta.AllocCategory);
|
||||||
|
writer.Write(Meta.GPUOffset);
|
||||||
|
writer.Write(Meta.BaseAlignment);
|
||||||
|
writer.Write(Meta.GPUDataStart);
|
||||||
|
writer.Write(Meta.GPUDataSize);
|
||||||
|
IOFileExtension.WriteList(writer, Meta.BufferInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class STextureHeader
|
||||||
|
{
|
||||||
|
public uint Type;
|
||||||
|
public uint Format;
|
||||||
|
public uint Width;
|
||||||
|
public uint Height;
|
||||||
|
public uint Depth;
|
||||||
|
public uint TileMode;
|
||||||
|
public uint Swizzle;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Meta data from PAK archive
|
||||||
|
public class SMetaData
|
||||||
|
{
|
||||||
|
public uint Unknown; //4
|
||||||
|
public uint AllocCategory;
|
||||||
|
public uint GPUOffset;
|
||||||
|
public uint BaseAlignment;
|
||||||
|
public uint GPUDataStart;
|
||||||
|
public uint GPUDataSize;
|
||||||
|
public List<SCompressedBufferInfo> BufferInfo = new List<SCompressedBufferInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public class SCompressedBufferInfo
|
||||||
|
{
|
||||||
|
public uint DecompressedSize;
|
||||||
|
public uint CompressedSize;
|
||||||
|
public uint Offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Toolbox.Library;
|
using Toolbox.Library;
|
||||||
using Toolbox.Library.IO;
|
using Toolbox.Library.IO;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace DKCTF
|
namespace DKCTF
|
||||||
{
|
{
|
||||||
@ -50,101 +51,98 @@ namespace DKCTF
|
|||||||
|
|
||||||
public void ClearFiles() { files.Clear(); }
|
public void ClearFiles() { files.Clear(); }
|
||||||
|
|
||||||
private PakHeader Header;
|
//For file searching
|
||||||
private List<DirectoryAssetEntry> Directories = new List<DirectoryAssetEntry>();
|
public Dictionary<string, FileEntry> ModelFiles = new Dictionary<string, FileEntry>();
|
||||||
private List<MetaOffsetEntry> MetaOffsets = new List<MetaOffsetEntry>();
|
public Dictionary<string, FileEntry> SkeletonFiles = new Dictionary<string, FileEntry>();
|
||||||
private List<CNameTagEntry> FileNameEntries = new List<CNameTagEntry>();
|
public Dictionary<string, FileEntry> TextureFiles = new Dictionary<string, FileEntry>();
|
||||||
|
public Dictionary<string, CHAR> CharFiles = new Dictionary<string, CHAR>();
|
||||||
|
public Dictionary<string, FileEntry> AnimFiles = new Dictionary<string, FileEntry>();
|
||||||
|
|
||||||
public void Load(System.IO.Stream stream)
|
public void Load(System.IO.Stream stream)
|
||||||
{
|
{
|
||||||
Directories.Clear();
|
PACK pack = new PACK(stream);
|
||||||
MetaOffsets.Clear();
|
|
||||||
FileNameEntries.Clear();
|
|
||||||
|
|
||||||
using (var reader = new FileReader(stream, true))
|
for (int i = 0; i < pack.Assets.Count; i++)
|
||||||
{
|
{
|
||||||
reader.SetByteOrder(true);
|
string ext = pack.Assets[i].Type.ToLower();
|
||||||
Header = reader.ReadStruct<PakHeader>();
|
|
||||||
var ADIRChunk = reader.ReadStruct<CChunkDescriptor>();
|
|
||||||
ReadAssetDirectoryChunk(reader, ADIRChunk);
|
|
||||||
var METAChunk = reader.ReadStruct<CChunkDescriptor>();
|
|
||||||
ReadMetaChunk(reader, METAChunk);
|
|
||||||
var STRGChunk = reader.ReadStruct<CChunkDescriptor>();
|
|
||||||
ReadFileNameChunk(reader, STRGChunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < Directories.Count; i++)
|
FileEntry file = new FileEntry();
|
||||||
{
|
file.ParentArchive = this;
|
||||||
files.Add(new FileEntry()
|
file.ArchiveStream = stream;
|
||||||
{
|
file.AssetEntry = pack.Assets[i];
|
||||||
FileData = new byte[0],
|
|
||||||
FileDataStream = Directories[i].Data,
|
|
||||||
FileName = Directories[i].Type,
|
|
||||||
});
|
|
||||||
|
|
||||||
/* var file = STFileLoader.OpenFileFormat(subStream, Directories[i].ToString());
|
string dir = pack.Assets[i].Type;
|
||||||
if (file != null && file is TreeNodeFile)
|
if (DirectoryLabels.ContainsKey(dir))
|
||||||
{
|
dir = DirectoryLabels[dir];
|
||||||
Nodes.Add((TreeNodeFile)file);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
}*/
|
file.FileName = $"{dir}/{pack.Assets[i].FileID}.{ext}";
|
||||||
|
file.SubData = new SubStream(stream, pack.Assets[i].Offset, pack.Assets[i].Size);
|
||||||
|
|
||||||
|
if (pack.MetaOffsets.ContainsKey(pack.Assets[i].FileID.ToString()))
|
||||||
|
file.MetaPointer = pack.MetaDataOffset + pack.MetaOffsets[pack.Assets[i].FileID.ToString()];
|
||||||
|
files.Add(file);
|
||||||
|
|
||||||
|
switch (file.AssetEntry.Type)
|
||||||
|
{
|
||||||
|
case "SMDL": ModelFiles.Add(file.AssetEntry.FileID.ToString(), file); break;
|
||||||
|
case "TXTR": TextureFiles.Add(file.AssetEntry.FileID.ToString(), file); break;
|
||||||
|
case "SKEL": SkeletonFiles.Add(file.AssetEntry.FileID.ToString(), file); break;
|
||||||
|
case "ANIM": AnimFiles.Add(file.AssetEntry.FileID.ToString(), file); break;
|
||||||
|
case "CHAR":
|
||||||
|
var c = new CHAR(new MemoryStream(file.FileData));
|
||||||
|
file.FileName = $"Characters/{c.Name}/{c.Name}.char";
|
||||||
|
CharFiles.Add(file.AssetEntry.FileID.ToString(), c);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReadAssetDirectoryChunk(FileReader reader, CChunkDescriptor chunk)
|
foreach (var c in CharFiles)
|
||||||
{
|
{
|
||||||
if (chunk.ChunkType != "ADIR")
|
SkeletonFiles[c.Value.SkeletonFileID.ToString()].FileName = $"Characters/{c.Value.Name}/Models/{c.Value.SkeletonFileID}.skel";
|
||||||
throw new Exception("Unexpected type! Expected ADIR, got " + chunk.ChunkType);
|
|
||||||
|
|
||||||
long pos = reader.Position;
|
foreach (var m in c.Value.Models)
|
||||||
|
|
||||||
reader.SeekBegin(pos + chunk.DataOffset);
|
|
||||||
uint numEntries = reader.ReadUInt32();
|
|
||||||
for (int i = 0; i < numEntries; i++)
|
|
||||||
{
|
{
|
||||||
DirectoryAssetEntry entry = new DirectoryAssetEntry();
|
if (ModelFiles.ContainsKey(m.FileID.ToString()))
|
||||||
entry.Read(reader);
|
ModelFiles[m.FileID.ToString()].FileName = $"Characters/{c.Value.Name}/Models/{m.Name}.smdl";
|
||||||
Directories.Add(entry);
|
}
|
||||||
|
foreach (var m in c.Value.Animations)
|
||||||
|
{
|
||||||
|
if (AnimFiles.ContainsKey(m.FileID.ToString()))
|
||||||
|
AnimFiles[m.FileID.ToString()].FileName = $"Characters/{c.Value.Name}/Animations/{m.Name}.anim";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
if (PakFileList.GuiToFilePath.ContainsKey(file.AssetEntry.FileID.ToString()))
|
||||||
|
{
|
||||||
|
file.FileName = "_LabeledFiles/" + PakFileList.GuiToFilePath[file.AssetEntry.FileID.ToString()];
|
||||||
|
//Organize the data type folders for easier access.
|
||||||
|
if (file.AssetEntry.Type == "SMDL") file.FileName = file.FileName.Replace("exportData", "models");
|
||||||
|
if (file.AssetEntry.Type == "CMDL") file.FileName = file.FileName.Replace("exportData", "models");
|
||||||
|
if (file.AssetEntry.Type == "TXTR") file.FileName = file.FileName.Replace("exportData", "textures");
|
||||||
|
if (file.AssetEntry.Type == "ANIM") file.FileName = file.FileName.Replace("exportData", "animations");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files = files.OrderBy(x => x.FileName).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.SeekBegin(pos + chunk.DataSize);
|
Dictionary<string, string> DirectoryLabels = new Dictionary<string, string>()
|
||||||
}
|
|
||||||
|
|
||||||
private void ReadMetaChunk(FileReader reader, CChunkDescriptor chunk)
|
|
||||||
{
|
{
|
||||||
if (chunk.ChunkType != "META")
|
{ "CHAR", "Characters" },
|
||||||
throw new Exception("Unexpected type! Expected META, got " + chunk.ChunkType);
|
{ "CMDL", "Static Models" },
|
||||||
long pos = reader.Position;
|
{ "SMDL", "Skinned Models" },
|
||||||
reader.SeekBegin(pos + chunk.DataOffset);
|
{ "TXTR", "Textures" },
|
||||||
uint numEntries = reader.ReadUInt32();
|
{ "MTRL", "Shaders" },
|
||||||
for (int i = 0; i < numEntries; i++)
|
{ "CSMP", "AudioSample" },
|
||||||
{
|
{ "CAUD", "AudioData" },
|
||||||
MetaOffsetEntry entry = reader.ReadStruct< MetaOffsetEntry>();
|
{ "GENP", "Gpsys" },
|
||||||
MetaOffsets.Add(entry);
|
{ "ANIM", "Animations" },
|
||||||
}
|
{ "XFRM", "Xfpsys" },
|
||||||
|
{ "WMDL", "World Models" },
|
||||||
|
{ "DCLN", "Collision Models" },
|
||||||
|
{ "CLSN", "Collision Static Models" },
|
||||||
|
};
|
||||||
|
|
||||||
reader.SeekBegin(pos + chunk.DataSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReadFileNameChunk(FileReader reader, CChunkDescriptor chunk)
|
|
||||||
{
|
|
||||||
if (chunk.ChunkType != "STRG")
|
|
||||||
throw new Exception("Unexpected type! Expected STRG, got " + chunk.ChunkType);
|
|
||||||
|
|
||||||
long pos = reader.Position;
|
|
||||||
reader.SeekBegin(pos + chunk.DataOffset);
|
|
||||||
uint numEntries = reader.ReadUInt32();
|
|
||||||
for (int i = 0; i < numEntries; i++)
|
|
||||||
{
|
|
||||||
// CNameTagEntry entry = reader.ReadStruct<CNameTagEntry>();
|
|
||||||
// FileNameEntries.Add(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
reader.SeekBegin(pos + chunk.DataSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Unload()
|
public void Unload()
|
||||||
{
|
{
|
||||||
@ -168,53 +166,80 @@ namespace DKCTF
|
|||||||
|
|
||||||
public class FileEntry : ArchiveFileInfo
|
public class FileEntry : ArchiveFileInfo
|
||||||
{
|
{
|
||||||
|
public PACK.DirectoryAssetEntry AssetEntry;
|
||||||
|
|
||||||
|
public PAK ParentArchive;
|
||||||
|
|
||||||
|
public long MetaPointer;
|
||||||
|
|
||||||
|
public Stream SubData;
|
||||||
|
|
||||||
|
public Stream ArchiveStream;
|
||||||
|
|
||||||
|
public override byte[] FileData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<byte[]> Data = new List<byte[]>();
|
||||||
|
|
||||||
|
using (var reader = new FileReader(SubData, true))
|
||||||
|
{
|
||||||
|
Data.Add(reader.ReadBytes((int)reader.BaseStream.Length));
|
||||||
|
if (MetaPointer != null)
|
||||||
|
{
|
||||||
|
using (var r = new FileReader(ArchiveStream, true)) {
|
||||||
|
r.SetByteOrder(true);
|
||||||
|
|
||||||
|
Data.Add(FileForm.WriteMetaFooter(r, (uint)MetaPointer, AssetEntry.Type));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DirectoryAssetEntry
|
|
||||||
{
|
|
||||||
public string Type;
|
|
||||||
public CObjectId FileID;
|
|
||||||
|
|
||||||
public long Offset;
|
|
||||||
public long Size;
|
|
||||||
|
|
||||||
public SubStream Data;
|
|
||||||
|
|
||||||
public void Read(FileReader reader)
|
|
||||||
{
|
|
||||||
Type = reader.ReadString(4, Encoding.ASCII);
|
|
||||||
FileID = reader.ReadStruct<CObjectId>();
|
|
||||||
Offset = reader.ReadInt64();
|
|
||||||
Size = reader.ReadInt64();
|
|
||||||
|
|
||||||
Data = new SubStream(reader.BaseStream, Offset,Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"{FileID.Guid.Part4.ToString()}.{Type}";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
if (AssetEntry.Type == "TXTR")
|
||||||
public class MetaOffsetEntry
|
|
||||||
{
|
{
|
||||||
public CObjectTag ObjectTag;
|
var txt = new TXTR();
|
||||||
public uint FileOffset;
|
return txt.CreateUncompressedFile(Utils.CombineByteArray(Data.ToArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
public class PakHeader
|
return Utils.CombineByteArray(Data.ToArray());
|
||||||
{
|
}
|
||||||
CFormDescriptor PackForm;
|
|
||||||
CFormDescriptor TocForm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
public override IFileFormat OpenFile()
|
||||||
public class CNameTagEntry
|
|
||||||
{
|
{
|
||||||
public CObjectTag ObjectTag;
|
var pak = this.ParentArchive;
|
||||||
public string Name;
|
|
||||||
|
var file = base.OpenFile();
|
||||||
|
if (file is CModel)
|
||||||
|
{
|
||||||
|
((CModel)file).LoadTextures(pak.TextureFiles);
|
||||||
|
|
||||||
|
FileEntry GetSkeleton()
|
||||||
|
{
|
||||||
|
foreach (var c in pak.CharFiles)
|
||||||
|
{
|
||||||
|
foreach (var m in c.Value.Models)
|
||||||
|
{
|
||||||
|
if (AssetEntry.FileID.ToString() == m.FileID.ToString())
|
||||||
|
return pak.SkeletonFiles[c.Value.SkeletonFileID.ToString()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var skelFile = GetSkeleton();
|
||||||
|
if (skelFile != null)
|
||||||
|
{
|
||||||
|
var skel = new SKEL(new MemoryStream(skelFile.FileData));
|
||||||
|
((CModel)file).LoadSkeleton(skel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (file is CCharacter)
|
||||||
|
((CCharacter)file).LoadModels(pak);
|
||||||
|
|
||||||
|
this.FileFormat = file;
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
File_Format_Library/FileFormats/DKCTF/PakFileList.cs
Normal file
44
File_Format_Library/FileFormats/DKCTF/PakFileList.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Toolbox.Library;
|
||||||
|
|
||||||
|
namespace DKCTF
|
||||||
|
{
|
||||||
|
internal class PakFileList
|
||||||
|
{
|
||||||
|
static Dictionary<string, string> _filePaths = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
public static Dictionary<string, string> GuiToFilePath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_filePaths.Count == 0)
|
||||||
|
Load();
|
||||||
|
return _filePaths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Load()
|
||||||
|
{
|
||||||
|
string path = Path.Combine(Runtime.ExecutableDir, "Lib", "PakFileIDs", "PakContents.txt");
|
||||||
|
using (var reader = new StreamReader(path))
|
||||||
|
{
|
||||||
|
reader.ReadLine(); //headers
|
||||||
|
while (!reader.EndOfStream)
|
||||||
|
{
|
||||||
|
var line = reader.ReadLine();
|
||||||
|
var items = line.Split('\t');
|
||||||
|
if (items.Length != 4)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var id = items[2].Trim();
|
||||||
|
if (!_filePaths.ContainsKey(id))
|
||||||
|
_filePaths.Add(id, items[3].Trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,6 @@ namespace CafeLibrary.M2
|
|||||||
{
|
{
|
||||||
attGroups = new Dictionary<uint, Shader.AttributeGroup>();
|
attGroups = new Dictionary<uint, Shader.AttributeGroup>();
|
||||||
|
|
||||||
|
|
||||||
foreach (var file in Directory.GetFiles(Path.Combine(Runtime.ExecutableDir, "Lib", "MTVertexFormats")))
|
foreach (var file in Directory.GetFiles(Path.Combine(Runtime.ExecutableDir, "Lib", "MTVertexFormats")))
|
||||||
{
|
{
|
||||||
LoadPresets(file);
|
LoadPresets(file);
|
||||||
|
@ -118,7 +118,7 @@ namespace CafeLibrary.M2
|
|||||||
private Texture FindTextureParam(uint[] parameters, int id)
|
private Texture FindTextureParam(uint[] parameters, int id)
|
||||||
{
|
{
|
||||||
var index = parameters[id - 1] - 1;
|
var index = parameters[id - 1] - 1;
|
||||||
if (index < Textures.Count)
|
if (index < Textures.Count) //Index before texture hash. Sometimes not always the case?
|
||||||
return Textures[(int)index];
|
return Textures[(int)index];
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -245,6 +245,17 @@
|
|||||||
<Compile Include="FileFormats\BSMAT\NoFormattingConverter.cs" />
|
<Compile Include="FileFormats\BSMAT\NoFormattingConverter.cs" />
|
||||||
<Compile Include="FileFormats\Byaml\XmlByamlConverter.cs" />
|
<Compile Include="FileFormats\Byaml\XmlByamlConverter.cs" />
|
||||||
<Compile Include="FileFormats\Byaml\YamlByamlConverter.cs" />
|
<Compile Include="FileFormats\Byaml\YamlByamlConverter.cs" />
|
||||||
|
<Compile Include="FileFormats\DKCTF\CCharacter.cs" />
|
||||||
|
<Compile Include="FileFormats\DKCTF\CModel.cs" />
|
||||||
|
<Compile Include="FileFormats\DKCTF\FileData\BufferHelper.cs" />
|
||||||
|
<Compile Include="FileFormats\DKCTF\FileData\CMDL.cs" />
|
||||||
|
<Compile Include="FileFormats\DKCTF\CTexture.cs" />
|
||||||
|
<Compile Include="FileFormats\DKCTF\FileData\PACK.cs" />
|
||||||
|
<Compile Include="FileFormats\DKCTF\FileData\CHAR.cs" />
|
||||||
|
<Compile Include="FileFormats\DKCTF\FileData\SKEL.cs" />
|
||||||
|
<Compile Include="FileFormats\DKCTF\FileData\TXTR.cs" />
|
||||||
|
<Compile Include="FileFormats\DKCTF\FileData\IOFileExtension.cs" />
|
||||||
|
<Compile Include="FileFormats\DKCTF\PakFileList.cs" />
|
||||||
<Compile Include="FileFormats\Font\BXFNT\CMAP.cs" />
|
<Compile Include="FileFormats\Font\BXFNT\CMAP.cs" />
|
||||||
<Compile Include="FileFormats\Font\BXFNT\FINF.cs" />
|
<Compile Include="FileFormats\Font\BXFNT\FINF.cs" />
|
||||||
<Compile Include="FileFormats\Font\BXFNT\FontKerningTable.cs" />
|
<Compile Include="FileFormats\Font\BXFNT\FontKerningTable.cs" />
|
||||||
@ -350,7 +361,7 @@
|
|||||||
<Compile Include="FileFormats\Archives\VIBS.cs" />
|
<Compile Include="FileFormats\Archives\VIBS.cs" />
|
||||||
<Compile Include="FileFormats\Audio\Archives\AudioCommon.cs" />
|
<Compile Include="FileFormats\Audio\Archives\AudioCommon.cs" />
|
||||||
<Compile Include="FileFormats\Collision\KclMonoscript.cs" />
|
<Compile Include="FileFormats\Collision\KclMonoscript.cs" />
|
||||||
<Compile Include="FileFormats\DKCTF\Common.cs" />
|
<Compile Include="FileFormats\DKCTF\FileData\Common.cs" />
|
||||||
<Compile Include="FileFormats\DKCTF\MSBT.cs" />
|
<Compile Include="FileFormats\DKCTF\MSBT.cs" />
|
||||||
<Compile Include="FileFormats\DKCTF\PAK.cs" />
|
<Compile Include="FileFormats\DKCTF\PAK.cs" />
|
||||||
<Compile Include="FileFormats\Effects\ESET.cs" />
|
<Compile Include="FileFormats\Effects\ESET.cs" />
|
||||||
|
@ -343,6 +343,8 @@ namespace FirstPlugin
|
|||||||
Formats.Add(typeof(BFRES));
|
Formats.Add(typeof(BFRES));
|
||||||
Formats.Add(typeof(MT_TEX));
|
Formats.Add(typeof(MT_TEX));
|
||||||
Formats.Add(typeof(MT_Model));
|
Formats.Add(typeof(MT_Model));
|
||||||
|
Formats.Add(typeof(DKCTF.CModel));
|
||||||
|
Formats.Add(typeof(DKCTF.CTexture));
|
||||||
Formats.Add(typeof(BCSV));
|
Formats.Add(typeof(BCSV));
|
||||||
Formats.Add(typeof(TVOL));
|
Formats.Add(typeof(TVOL));
|
||||||
Formats.Add(typeof(BTI));
|
Formats.Add(typeof(BTI));
|
||||||
|
@ -48,8 +48,15 @@ namespace Toolbox.Library
|
|||||||
BatchFormatExport form = new BatchFormatExport(Formats);
|
BatchFormatExport form = new BatchFormatExport(Formats);
|
||||||
if (form.ShowDialog() == DialogResult.OK)
|
if (form.ShowDialog() == DialogResult.OK)
|
||||||
{
|
{
|
||||||
foreach (STGenericTexture tex in Nodes)
|
foreach (TreeNode node in Nodes)
|
||||||
{
|
{
|
||||||
|
STGenericTexture tex = null;
|
||||||
|
if (node is STGenericTexture) tex = (STGenericTexture)node;
|
||||||
|
if (node.Tag is STGenericTexture) tex = (STGenericTexture)node.Tag;
|
||||||
|
|
||||||
|
if (tex == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (form.Index == 0)
|
if (form.Index == 0)
|
||||||
tex.SaveDDS(folderPath + '\\' + tex.Text + ".dds");
|
tex.SaveDDS(folderPath + '\\' + tex.Text + ".dds");
|
||||||
else if (form.Index == 1)
|
else if (form.Index == 1)
|
||||||
|
@ -13,6 +13,11 @@ namespace Toolbox.Library.IO
|
|||||||
int value;
|
int value;
|
||||||
public static implicit operator string(Magic magic) => Encoding.ASCII.GetString(BitConverter.GetBytes(magic.value));
|
public static implicit operator string(Magic magic) => Encoding.ASCII.GetString(BitConverter.GetBytes(magic.value));
|
||||||
public static implicit operator Magic(string s) => new Magic { value = BitConverter.ToInt32(Encoding.ASCII.GetBytes(s), 0) };
|
public static implicit operator Magic(string s) => new Magic { value = BitConverter.ToInt32(Encoding.ASCII.GetBytes(s), 0) };
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Encoding.ASCII.GetString(BitConverter.GetBytes(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
9529
Toolbox/Lib/PakFileIDs/PakContents.txt
Normal file
9529
Toolbox/Lib/PakFileIDs/PakContents.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -558,6 +558,9 @@
|
|||||||
<Content Include="Lib\OpenTK.GLControl.dll">
|
<Content Include="Lib\OpenTK.GLControl.dll">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="Lib\PakFileIDs\PakContents.txt">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="Lib\Plugins\Blank.txt">
|
<Content Include="Lib\Plugins\Blank.txt">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
Loading…
Reference in New Issue
Block a user