mirror of
https://github.com/KillzXGaming/Switch-Toolbox
synced 2024-11-22 04:23:09 +00:00
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
23 changed files with 12339 additions and 225 deletions
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 Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
using System.IO;
|
||||
|
||||
namespace DKCTF
|
||||
{
|
||||
|
@ -50,101 +51,98 @@ namespace DKCTF
|
|||
|
||||
public void ClearFiles() { files.Clear(); }
|
||||
|
||||
private PakHeader Header;
|
||||
private List<DirectoryAssetEntry> Directories = new List<DirectoryAssetEntry>();
|
||||
private List<MetaOffsetEntry> MetaOffsets = new List<MetaOffsetEntry>();
|
||||
private List<CNameTagEntry> FileNameEntries = new List<CNameTagEntry>();
|
||||
//For file searching
|
||||
public Dictionary<string, FileEntry> ModelFiles = new Dictionary<string, FileEntry>();
|
||||
public Dictionary<string, FileEntry> SkeletonFiles = new Dictionary<string, FileEntry>();
|
||||
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)
|
||||
{
|
||||
Directories.Clear();
|
||||
MetaOffsets.Clear();
|
||||
FileNameEntries.Clear();
|
||||
PACK pack = new PACK(stream);
|
||||
|
||||
using (var reader = new FileReader(stream, true))
|
||||
for (int i = 0; i < pack.Assets.Count; i++)
|
||||
{
|
||||
reader.SetByteOrder(true);
|
||||
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);
|
||||
}
|
||||
string ext = pack.Assets[i].Type.ToLower();
|
||||
|
||||
for (int i = 0; i < Directories.Count; i++)
|
||||
{
|
||||
files.Add(new FileEntry()
|
||||
FileEntry file = new FileEntry();
|
||||
file.ParentArchive = this;
|
||||
file.ArchiveStream = stream;
|
||||
file.AssetEntry = pack.Assets[i];
|
||||
|
||||
string dir = pack.Assets[i].Type;
|
||||
if (DirectoryLabels.ContainsKey(dir))
|
||||
dir = DirectoryLabels[dir];
|
||||
|
||||
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)
|
||||
{
|
||||
FileData = new byte[0],
|
||||
FileDataStream = Directories[i].Data,
|
||||
FileName = Directories[i].Type,
|
||||
});
|
||||
|
||||
/* var file = STFileLoader.OpenFileFormat(subStream, Directories[i].ToString());
|
||||
if (file != null && file is TreeNodeFile)
|
||||
{
|
||||
Nodes.Add((TreeNodeFile)file);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}*/
|
||||
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)
|
||||
{
|
||||
if (chunk.ChunkType != "ADIR")
|
||||
throw new Exception("Unexpected type! Expected ADIR, got " + chunk.ChunkType);
|
||||
|
||||
long pos = reader.Position;
|
||||
|
||||
reader.SeekBegin(pos + chunk.DataOffset);
|
||||
uint numEntries = reader.ReadUInt32();
|
||||
for (int i = 0; i < numEntries; i++)
|
||||
foreach (var c in CharFiles)
|
||||
{
|
||||
DirectoryAssetEntry entry = new DirectoryAssetEntry();
|
||||
entry.Read(reader);
|
||||
Directories.Add(entry);
|
||||
SkeletonFiles[c.Value.SkeletonFileID.ToString()].FileName = $"Characters/{c.Value.Name}/Models/{c.Value.SkeletonFileID}.skel";
|
||||
|
||||
foreach (var m in c.Value.Models)
|
||||
{
|
||||
if (ModelFiles.ContainsKey(m.FileID.ToString()))
|
||||
ModelFiles[m.FileID.ToString()].FileName = $"Characters/{c.Value.Name}/Models/{m.Name}.smdl";
|
||||
}
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
reader.SeekBegin(pos + chunk.DataSize);
|
||||
}
|
||||
|
||||
private void ReadMetaChunk(FileReader reader, CChunkDescriptor chunk)
|
||||
{
|
||||
if (chunk.ChunkType != "META")
|
||||
throw new Exception("Unexpected type! Expected META, got " + chunk.ChunkType);
|
||||
long pos = reader.Position;
|
||||
reader.SeekBegin(pos + chunk.DataOffset);
|
||||
uint numEntries = reader.ReadUInt32();
|
||||
for (int i = 0; i < numEntries; i++)
|
||||
foreach (var file in files)
|
||||
{
|
||||
MetaOffsetEntry entry = reader.ReadStruct< MetaOffsetEntry>();
|
||||
MetaOffsets.Add(entry);
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
reader.SeekBegin(pos + chunk.DataSize);
|
||||
files = files.OrderBy(x => x.FileName).ToList();
|
||||
}
|
||||
|
||||
private void ReadFileNameChunk(FileReader reader, CChunkDescriptor chunk)
|
||||
Dictionary<string, string> DirectoryLabels = new Dictionary<string, string>()
|
||||
{
|
||||
if (chunk.ChunkType != "STRG")
|
||||
throw new Exception("Unexpected type! Expected STRG, got " + chunk.ChunkType);
|
||||
{ "CHAR", "Characters" },
|
||||
{ "CMDL", "Static Models" },
|
||||
{ "SMDL", "Skinned Models" },
|
||||
{ "TXTR", "Textures" },
|
||||
{ "MTRL", "Shaders" },
|
||||
{ "CSMP", "AudioSample" },
|
||||
{ "CAUD", "AudioData" },
|
||||
{ "GENP", "Gpsys" },
|
||||
{ "ANIM", "Animations" },
|
||||
{ "XFRM", "Xfpsys" },
|
||||
{ "WMDL", "World Models" },
|
||||
{ "DCLN", "Collision Models" },
|
||||
{ "CLSN", "Collision Static Models" },
|
||||
};
|
||||
|
||||
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()
|
||||
{
|
||||
|
@ -168,53 +166,80 @@ namespace DKCTF
|
|||
|
||||
public class FileEntry : ArchiveFileInfo
|
||||
{
|
||||
public PACK.DirectoryAssetEntry AssetEntry;
|
||||
|
||||
}
|
||||
public PAK ParentArchive;
|
||||
|
||||
public class DirectoryAssetEntry
|
||||
{
|
||||
public string Type;
|
||||
public CObjectId FileID;
|
||||
public long MetaPointer;
|
||||
|
||||
public long Offset;
|
||||
public long Size;
|
||||
public Stream SubData;
|
||||
|
||||
public SubStream Data;
|
||||
public Stream ArchiveStream;
|
||||
|
||||
public void Read(FileReader reader)
|
||||
public override byte[] FileData
|
||||
{
|
||||
Type = reader.ReadString(4, Encoding.ASCII);
|
||||
FileID = reader.ReadStruct<CObjectId>();
|
||||
Offset = reader.ReadInt64();
|
||||
Size = reader.ReadInt64();
|
||||
get
|
||||
{
|
||||
List<byte[]> Data = new List<byte[]>();
|
||||
|
||||
Data = new SubStream(reader.BaseStream, Offset,Size);
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AssetEntry.Type == "TXTR")
|
||||
{
|
||||
var txt = new TXTR();
|
||||
return txt.CreateUncompressedFile(Utils.CombineByteArray(Data.ToArray()));
|
||||
}
|
||||
|
||||
|
||||
return Utils.CombineByteArray(Data.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
public override IFileFormat OpenFile()
|
||||
{
|
||||
return $"{FileID.Guid.Part4.ToString()}.{Type}";
|
||||
var pak = this.ParentArchive;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public class MetaOffsetEntry
|
||||
{
|
||||
public CObjectTag ObjectTag;
|
||||
public uint FileOffset;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public class PakHeader
|
||||
{
|
||||
CFormDescriptor PackForm;
|
||||
CFormDescriptor TocForm;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public class CNameTagEntry
|
||||
{
|
||||
public CObjectTag ObjectTag;
|
||||
public string Name;
|
||||
}
|
||||
}
|
||||
|
|
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>();
|
||||
|
||||
|
||||
foreach (var file in Directory.GetFiles(Path.Combine(Runtime.ExecutableDir, "Lib", "MTVertexFormats")))
|
||||
{
|
||||
LoadPresets(file);
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace CafeLibrary.M2
|
|||
private Texture FindTextureParam(uint[] parameters, int id)
|
||||
{
|
||||
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 null;
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ namespace FirstPlugin.LuigisMansion3
|
|||
fileEntries.Add(file);
|
||||
|
||||
if (file.DecompressedSize > 0)
|
||||
{
|
||||
{
|
||||
file.Text = $"entry {i}";
|
||||
|
||||
if (i < 52)
|
||||
|
|
|
@ -245,6 +245,17 @@
|
|||
<Compile Include="FileFormats\BSMAT\NoFormattingConverter.cs" />
|
||||
<Compile Include="FileFormats\Byaml\XmlByamlConverter.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\FINF.cs" />
|
||||
<Compile Include="FileFormats\Font\BXFNT\FontKerningTable.cs" />
|
||||
|
@ -350,7 +361,7 @@
|
|||
<Compile Include="FileFormats\Archives\VIBS.cs" />
|
||||
<Compile Include="FileFormats\Audio\Archives\AudioCommon.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\PAK.cs" />
|
||||
<Compile Include="FileFormats\Effects\ESET.cs" />
|
||||
|
|
|
@ -343,6 +343,8 @@ namespace FirstPlugin
|
|||
Formats.Add(typeof(BFRES));
|
||||
Formats.Add(typeof(MT_TEX));
|
||||
Formats.Add(typeof(MT_Model));
|
||||
Formats.Add(typeof(DKCTF.CModel));
|
||||
Formats.Add(typeof(DKCTF.CTexture));
|
||||
Formats.Add(typeof(BCSV));
|
||||
Formats.Add(typeof(TVOL));
|
||||
Formats.Add(typeof(BTI));
|
||||
|
|
|
@ -48,8 +48,15 @@ namespace Toolbox.Library
|
|||
BatchFormatExport form = new BatchFormatExport(Formats);
|
||||
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)
|
||||
tex.SaveDDS(folderPath + '\\' + tex.Text + ".dds");
|
||||
else if (form.Index == 1)
|
||||
|
|
|
@ -13,6 +13,11 @@ namespace Toolbox.Library.IO
|
|||
int 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 override string ToString()
|
||||
{
|
||||
return Encoding.ASCII.GetString(BitConverter.GetBytes(value));
|
||||
}
|
||||
}
|
||||
|
||||
[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">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Lib\PakFileIDs\PakContents.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Lib\Plugins\Blank.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
Loading…
Reference in a new issue