More fixes and additions
Archives now use a link list for archive infos and treenodes. Handling replaced treenodes is easier and fixes renaming issues if a file was opened and renamed. More LM2 archive improvements, with more chunk loading.
This commit is contained in:
parent
1059113cee
commit
1429a00178
Binary file not shown.
@ -5,8 +5,24 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library.IO;
|
||||
|
||||
namespace FirstPlugin
|
||||
namespace FirstPlugin.LuigisMansion.DarkMoon
|
||||
{
|
||||
public class ChunkEntry
|
||||
{
|
||||
public uint Unknown1;
|
||||
public uint ChunkOffset;
|
||||
public DataType ChunkType;
|
||||
public uint ChunkSubCount;
|
||||
public uint Unknown3;
|
||||
}
|
||||
|
||||
public class ChunkSubEntry
|
||||
{
|
||||
public SubDataType ChunkType;
|
||||
public uint ChunkSize;
|
||||
public uint ChunkOffset;
|
||||
}
|
||||
|
||||
//Table consists of 2 chunk entry lists that define how the .data reads sections
|
||||
public class LM2_ChunkTable
|
||||
{
|
||||
@ -18,28 +34,6 @@ namespace FirstPlugin
|
||||
public List<ChunkEntry> ChunkEntries = new List<ChunkEntry>();
|
||||
public List<ChunkSubEntry> ChunkSubEntries = new List<ChunkSubEntry>();
|
||||
|
||||
public class ChunkEntry
|
||||
{
|
||||
public uint Unknown1;
|
||||
public uint ChunkOffset;
|
||||
public uint ChunkType;
|
||||
public uint ChunkSubCount;
|
||||
public uint Unknown3;
|
||||
}
|
||||
|
||||
public class ChunkSubEntry
|
||||
{
|
||||
public uint ChunkSize;
|
||||
public DataType ChunkType;
|
||||
public uint Unknown;
|
||||
}
|
||||
|
||||
public enum DataType : uint
|
||||
{
|
||||
TextureHeader = 0x0201B501,
|
||||
TextureData = 0x1701B502,
|
||||
}
|
||||
|
||||
public void Read(FileReader tableReader)
|
||||
{
|
||||
tableReader.SetByteOrder(false);
|
||||
@ -52,8 +46,8 @@ namespace FirstPlugin
|
||||
|
||||
entry.Unknown1 = tableReader.ReadUInt32(); //8
|
||||
entry.ChunkOffset = tableReader.ReadUInt32(); //The chunk offset in the file. Relative to the first chunk position in the file
|
||||
entry.ChunkType = tableReader.ReadUInt32(); //The type of chunk. 0x8701B5 for example for texture info
|
||||
entry.ChunkSubCount = tableReader.ReadUInt32();
|
||||
entry.ChunkType = tableReader.ReadEnum<DataType>(false); //The type of chunk. 0x8701B5 for example for texture info
|
||||
entry.ChunkSubCount = tableReader.ReadUInt32(); //Uncertain about this. 2 for textures (info + block). Some sections however use large numbers.
|
||||
|
||||
//This increases by 2 each chunk info, however the starting value is not 0
|
||||
//Note the last entry does not have this
|
||||
@ -71,9 +65,9 @@ namespace FirstPlugin
|
||||
while (!tableReader.EndOfStream && tableReader.Position <= tableReader.BaseStream.Length - 12)
|
||||
{
|
||||
ChunkSubEntry subEntry = new ChunkSubEntry();
|
||||
subEntry.ChunkType = tableReader.ReadEnum<DataType>(false); //The type of chunk. 0x8701B5 for example for texture info
|
||||
subEntry.ChunkType = tableReader.ReadEnum<SubDataType>(false); //The type of chunk. 0x8701B5 for example for texture info
|
||||
subEntry.ChunkSize = tableReader.ReadUInt32();
|
||||
subEntry.Unknown = tableReader.ReadUInt32();
|
||||
subEntry.ChunkOffset = tableReader.ReadUInt32();
|
||||
ChunkSubEntries.Add(subEntry);
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ using Toolbox.Library.IO;
|
||||
using Toolbox.Library.Forms;
|
||||
using System.Drawing;
|
||||
|
||||
namespace FirstPlugin
|
||||
namespace FirstPlugin.LuigisMansion.DarkMoon
|
||||
{
|
||||
//Parse info based on https://github.com/TheFearsomeDzeraora/LM2L
|
||||
public class LM2_DICT : TreeNodeFile, IFileFormat, IArchiveFile
|
||||
@ -47,7 +47,7 @@ namespace FirstPlugin
|
||||
}
|
||||
}
|
||||
|
||||
public List<FileEntry> files = new List<FileEntry>();
|
||||
public List<ChunkDataEntry> files = new List<ChunkDataEntry>();
|
||||
public IEnumerable<ArchiveFileInfo> Files => files;
|
||||
|
||||
public void ClearFiles() { files.Clear(); }
|
||||
@ -55,6 +55,23 @@ namespace FirstPlugin
|
||||
public bool IsCompressed = false;
|
||||
|
||||
public LM2_ChunkTable ChunkTable;
|
||||
public List<FileEntry> fileEntries = new List<FileEntry>();
|
||||
|
||||
//The click event will load the viewport and any models found in the file if there are any
|
||||
public override void OnClick(TreeView treeview)
|
||||
{
|
||||
if (modelFolder.Nodes.Count != 0)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
TreeNode textureFolder = new TreeNode("Textures");
|
||||
TreeNode modelFolder = new TreeNode("Models");
|
||||
|
||||
public void Load(System.IO.Stream stream)
|
||||
{
|
||||
@ -71,23 +88,22 @@ namespace FirstPlugin
|
||||
reader.SeekBegin(0x2C);
|
||||
byte[] Unknowns = reader.ReadBytes((int)FileCount);
|
||||
|
||||
TreeNode tableNodes = new TreeNode("File Section Entries");
|
||||
|
||||
long FileTablePos = reader.Position;
|
||||
for (int i = 0; i < FileCount; i++)
|
||||
{
|
||||
var file = new FileEntry(this);
|
||||
file.Text = $"entry {i}";
|
||||
file.Read(reader);
|
||||
fileEntries.Add(file);
|
||||
tableNodes.Nodes.Add(file);
|
||||
|
||||
//Skip the file table entries as those are not needed to be loaded
|
||||
if (file.FileType != 0 && i > 1)
|
||||
{
|
||||
file.FileName = $"File {i} (FileType: ({file.FileType}) Unknowns: {file.Unknown2} {file.Unknown3})";
|
||||
files.Add(file);
|
||||
}
|
||||
//The first file stores a chunk layout
|
||||
//The second one seems to be a duplicate?
|
||||
if (i == 0)
|
||||
{
|
||||
using (var tableReader = new FileReader(file.FileData))
|
||||
using (var tableReader = new FileReader(file.GetData()))
|
||||
{
|
||||
ChunkTable = new LM2_ChunkTable();
|
||||
ChunkTable.Read(tableReader);
|
||||
@ -97,68 +113,120 @@ namespace FirstPlugin
|
||||
|
||||
TreeNode list1 = new TreeNode("Entry List 1");
|
||||
TreeNode list2 = new TreeNode("Entry List 2 ");
|
||||
debugFolder.Nodes.Add(tableNodes);
|
||||
debugFolder.Nodes.Add(list1);
|
||||
debugFolder.Nodes.Add(list2);
|
||||
|
||||
foreach (var chunk in ChunkTable.ChunkEntries)
|
||||
{
|
||||
list1.Nodes.Add($"ChunkType {chunk.ChunkType.ToString("X")} ChunkOffset {chunk.ChunkOffset} Unknown1 {chunk.Unknown1} ChunkSubCount {chunk.ChunkSubCount} Unknown3 {chunk.Unknown3}");
|
||||
list1.Nodes.Add($"ChunkType {chunk.ChunkType} ChunkOffset {chunk.ChunkOffset} Unknown1 {chunk.Unknown1} ChunkSubCount {chunk.ChunkSubCount} Unknown3 {chunk.Unknown3}");
|
||||
}
|
||||
foreach (var chunk in ChunkTable.ChunkSubEntries)
|
||||
{
|
||||
list2.Nodes.Add($"ChunkType {chunk.ChunkType} ChunkSize {chunk.ChunkSize} Unknown {chunk.Unknown}");
|
||||
list2.Nodes.Add($"ChunkType {chunk.ChunkType} ChunkSize {chunk.ChunkSize} Unknown {chunk.ChunkOffset}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Now go through each file and format and connect the headers and blocks
|
||||
uint ImageIndex = 0;
|
||||
//Set an instance of our current data
|
||||
//Chunks are in order, so you build off of when an instance gets loaded
|
||||
TexturePOWE currentTexture = new TexturePOWE();
|
||||
LM2_Model currentModel = new LM2_Model();
|
||||
|
||||
List<TexturePOWE> Textures = new List<TexturePOWE>();
|
||||
//Each part of the file is divided into multiple file/section entries
|
||||
//The first entry being the chunk table parsed before this
|
||||
//The second file being a duplicate (sometimes slightly larger than the first)
|
||||
//The third file stores texture headers, while the fourth one usually has the rest of the main data
|
||||
//Any additional ones currently are unknown how they work. Some of which have unknown compression aswell
|
||||
|
||||
TreeNode textureFolder = new TreeNode("Textures");
|
||||
byte[] File002Data = fileEntries[2].GetData(); //Get the third file
|
||||
byte[] File003Data = fileEntries[3].GetData(); //Get the fourth file
|
||||
|
||||
for (int i = 0; i < files.Count; i++)
|
||||
int chunkId = 0;
|
||||
uint ImageHeaderIndex = 0;
|
||||
uint modelIndex = 0;
|
||||
foreach (var chunk in ChunkTable.ChunkSubEntries)
|
||||
{
|
||||
if (files[i].FileType == FileEntry.DataType.Texture)
|
||||
{
|
||||
if (files[i].Unknown3 == 1) //Info
|
||||
{
|
||||
//Read the info
|
||||
using (var textureReader = new FileReader(files[i].FileData))
|
||||
{
|
||||
int headerIndex = 0;
|
||||
while (!textureReader.EndOfStream && textureReader.ReadUInt32() == TexturePOWE.Identifier)
|
||||
{
|
||||
var texture = new TexturePOWE();
|
||||
texture.ImageKey = "texture";
|
||||
texture.SelectedImageKey = texture.ImageKey;
|
||||
texture.Index = ImageIndex;
|
||||
texture.Read(textureReader);
|
||||
texture.Text = $"Texture {headerIndex++}";
|
||||
textureFolder.Nodes.Add(texture);
|
||||
Textures.Add(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
else //Block
|
||||
{
|
||||
uint Offset = 0;
|
||||
foreach (var tex in Textures)
|
||||
{
|
||||
if (tex.Index == ImageIndex)
|
||||
{
|
||||
tex.ImageData = Utils.SubArray(files[i].FileData, Offset, tex.ImageSize);
|
||||
}
|
||||
var chunkEntry = new ChunkDataEntry(this, chunk);
|
||||
chunkEntry.DataFile = File003Data;
|
||||
chunkEntry.FileName = $"Chunk {chunk.ChunkType} {chunkId++}";
|
||||
files.Add(chunkEntry);
|
||||
|
||||
Offset += tex.ImageSize;
|
||||
switch (chunk.ChunkType)
|
||||
{
|
||||
case SubDataType.TextureHeader:
|
||||
chunkEntry.DataFile = File002Data;
|
||||
|
||||
//Read the info
|
||||
using (var textureReader = new FileReader(chunkEntry.FileData))
|
||||
{
|
||||
currentTexture = new TexturePOWE();
|
||||
currentTexture.ImageKey = "texture";
|
||||
currentTexture.SelectedImageKey = currentTexture.ImageKey;
|
||||
currentTexture.Index = ImageHeaderIndex;
|
||||
currentTexture.Read(textureReader);
|
||||
currentTexture.Text = $"Texture {ImageHeaderIndex}";
|
||||
textureFolder.Nodes.Add(currentTexture);
|
||||
|
||||
ImageHeaderIndex++;
|
||||
}
|
||||
ImageIndex++;
|
||||
}
|
||||
break;
|
||||
case SubDataType.TextureData:
|
||||
currentTexture.ImageData = chunkEntry.FileData;
|
||||
break;
|
||||
case SubDataType.ModelStart:
|
||||
currentModel.ModelInfo = new LM2_ModelInfo();
|
||||
currentModel.Text = $"Model {modelIndex}";
|
||||
currentModel.ModelInfo.Data = chunkEntry.FileData;
|
||||
modelFolder.Nodes.Add(currentModel);
|
||||
|
||||
modelIndex++;
|
||||
break;
|
||||
case SubDataType.VertexStartPointers:
|
||||
using (var vtxPtrReader = new FileReader(chunkEntry.FileData))
|
||||
{
|
||||
while (!vtxPtrReader.EndOfStream)
|
||||
currentModel.VertexBufferPointers.Add(vtxPtrReader.ReadUInt32());
|
||||
}
|
||||
break;
|
||||
case SubDataType.SubmeshInfo:
|
||||
int MeshCount = chunkEntry.FileData.Length / 0x28;
|
||||
using (var meshReader = new FileReader(chunkEntry.FileData))
|
||||
{
|
||||
for (uint i = 0; i < MeshCount; i++)
|
||||
{
|
||||
LM2_Mesh mesh = new LM2_Mesh();
|
||||
mesh.Read(meshReader);
|
||||
currentModel.Meshes.Add(mesh);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SubDataType.ModelTransform:
|
||||
using (var transformReader = new FileReader(chunkEntry.FileData))
|
||||
currentModel.Transform = transformReader.ReadMatrix4();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (LM2_Model model in modelFolder.Nodes)
|
||||
{
|
||||
for (int i = 0; i < model.VertexBufferPointers.Count; i++)
|
||||
{
|
||||
LM2_Mesh mesh = model.Meshes[i];
|
||||
|
||||
RenderableMeshWrapper genericObj = new RenderableMeshWrapper();
|
||||
genericObj.Mesh = mesh;
|
||||
genericObj.Text = $"Mesh {i}";
|
||||
model.Nodes.Add(genericObj);
|
||||
}
|
||||
}
|
||||
|
||||
if (modelFolder.Nodes.Count > 0)
|
||||
Nodes.Add(modelFolder);
|
||||
|
||||
if (textureFolder.Nodes.Count > 0)
|
||||
Nodes.Add(textureFolder);
|
||||
}
|
||||
@ -184,102 +252,16 @@ namespace FirstPlugin
|
||||
return false;
|
||||
}
|
||||
|
||||
public class TexturePOWE : STGenericTexture
|
||||
{
|
||||
public static readonly uint Identifier = 0xE977D350;
|
||||
|
||||
public uint Index { get; set; }
|
||||
|
||||
public uint ID { get; set; }
|
||||
public uint ImageSize { get; set; }
|
||||
public uint ID2 { get; set; }
|
||||
|
||||
public byte[] ImageData { get; set; }
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
PlatformSwizzle = PlatformSwizzle.Platform_3DS;
|
||||
|
||||
ID = reader.ReadUInt32();
|
||||
ImageSize = reader.ReadUInt32();
|
||||
ID2 = reader.ReadUInt32();
|
||||
reader.Seek(0x8);
|
||||
Width = reader.ReadUInt16();
|
||||
Height = reader.ReadUInt16();
|
||||
reader.Seek(3);
|
||||
var numMips = reader.ReadByte();
|
||||
reader.Seek(0x14);
|
||||
byte FormatCtr = reader.ReadByte();
|
||||
reader.Seek(3);
|
||||
|
||||
MipCount = 1;
|
||||
Format = CTR_3DS.ConvertPICAToGenericFormat((CTR_3DS.PICASurfaceFormat)FormatCtr);
|
||||
}
|
||||
|
||||
public override void OnClick(TreeView treeview)
|
||||
{
|
||||
ImageEditorBase editor = (ImageEditorBase)LibraryGUI.GetActiveContent(typeof(ImageEditorBase));
|
||||
if (editor == null)
|
||||
{
|
||||
editor = new ImageEditorBase();
|
||||
editor.Dock = DockStyle.Fill;
|
||||
|
||||
LibraryGUI.LoadEditor(editor);
|
||||
}
|
||||
editor.Text = Text;
|
||||
editor.LoadProperties(this.GenericProperties);
|
||||
editor.LoadImage(this);
|
||||
}
|
||||
|
||||
public override bool CanEdit { get; set; } = false;
|
||||
|
||||
public override void SetImageData(Bitmap bitmap, int ArrayLevel)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0)
|
||||
{
|
||||
return ImageData;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class FileEntry : ArchiveFileInfo
|
||||
public class ChunkDataEntry : ArchiveFileInfo
|
||||
{
|
||||
public byte[] DataFile;
|
||||
public LM2_DICT ParentDictionary { get; set; }
|
||||
public ChunkSubEntry Entry;
|
||||
|
||||
public uint Offset;
|
||||
public uint DecompressedSize;
|
||||
public uint CompressedSize;
|
||||
public DataType FileType;
|
||||
public byte Unknown2;
|
||||
public byte Unknown3; //Possibly the effect? 0 for image block, 1 for info
|
||||
|
||||
public enum DataType : ushort
|
||||
public ChunkDataEntry(LM2_DICT dict, ChunkSubEntry entry)
|
||||
{
|
||||
Texture = 0x80,
|
||||
ParentDictionary = dict;
|
||||
Entry = entry;
|
||||
}
|
||||
|
||||
public override byte[] FileData
|
||||
@ -291,6 +273,27 @@ namespace FirstPlugin
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] GetData( )
|
||||
{
|
||||
using (var reader = new FileReader(DataFile))
|
||||
{
|
||||
reader.SeekBegin(Entry.ChunkOffset);
|
||||
return reader.ReadBytes((int)Entry.ChunkSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class FileEntry : TreeNodeFile, IContextMenuNode
|
||||
{
|
||||
public LM2_DICT ParentDictionary { get; set; }
|
||||
|
||||
public uint Offset;
|
||||
public uint DecompressedSize;
|
||||
public uint CompressedSize;
|
||||
public ushort Unknown1;
|
||||
public byte Unknown2;
|
||||
public byte Unknown3; //Possibly the effect? 0 for image block, 1 for info
|
||||
|
||||
public FileEntry(LM2_DICT dict)
|
||||
{
|
||||
ParentDictionary = dict;
|
||||
@ -301,7 +304,7 @@ namespace FirstPlugin
|
||||
Offset = reader.ReadUInt32();
|
||||
DecompressedSize = reader.ReadUInt32();
|
||||
CompressedSize = reader.ReadUInt32();
|
||||
FileType = reader.ReadEnum<DataType>(false);
|
||||
Unknown1 = reader.ReadUInt16();
|
||||
Unknown2 = reader.ReadByte();
|
||||
Unknown3 = reader.ReadByte();
|
||||
}
|
||||
@ -319,7 +322,39 @@ namespace FirstPlugin
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] GetData()
|
||||
public ToolStripItem[] GetContextMenuItems()
|
||||
{
|
||||
List<ToolStripItem> Items = new List<ToolStripItem>();
|
||||
Items.Add(new STToolStipMenuItem("Export Raw Data", null, Export, Keys.Control | Keys.E));
|
||||
return Items.ToArray();
|
||||
}
|
||||
|
||||
private void Export(object sender, EventArgs args)
|
||||
{
|
||||
SaveFileDialog sfd = new SaveFileDialog();
|
||||
sfd.FileName = Text;
|
||||
sfd.Filter = "Raw Data (*.*)|*.*";
|
||||
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
System.IO.File.WriteAllBytes(sfd.FileName, GetData());
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnClick(TreeView treeView)
|
||||
{
|
||||
HexEditor editor = (HexEditor)LibraryGUI.GetActiveContent(typeof(HexEditor));
|
||||
if (editor == null)
|
||||
{
|
||||
editor = new HexEditor();
|
||||
LibraryGUI.LoadEditor(editor);
|
||||
}
|
||||
editor.Text = Text;
|
||||
editor.Dock = DockStyle.Fill;
|
||||
editor.LoadData(GetData());
|
||||
}
|
||||
|
||||
public byte[] GetData()
|
||||
{
|
||||
byte[] Data = new byte[DecompressedSize];
|
||||
|
||||
|
28
File_Format_Library/FileFormats/Archives/LM2/LM2_Enums.cs
Normal file
28
File_Format_Library/FileFormats/Archives/LM2/LM2_Enums.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FirstPlugin.LuigisMansion.DarkMoon
|
||||
{
|
||||
public enum DataType : uint
|
||||
{
|
||||
Texture = 0x8701B500,
|
||||
}
|
||||
|
||||
public enum SubDataType : uint
|
||||
{
|
||||
ModelStart = 0x1201B006,
|
||||
SubmeshInfo = 0x1201B003, //Or polygon groups?
|
||||
VertexStartPointers = 0x1201B004,
|
||||
ModelTransform = 0x1301B001, //Matrix4x4. 0x40 in size
|
||||
MeshBuffers = 0x1301B005, //vertex and index buffer
|
||||
MaterialName = 0x1201B333,
|
||||
MeshIndexTable = 0x12017105,
|
||||
MessageData = 0x12027020,
|
||||
ShaderData = 0x1401B400,
|
||||
TextureHeader = 0x0201B501,
|
||||
TextureData = 0x1701B502,
|
||||
}
|
||||
}
|
120
File_Format_Library/FileFormats/Archives/LM2/LM2_Model.cs
Normal file
120
File_Format_Library/FileFormats/Archives/LM2/LM2_Model.cs
Normal file
@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
using Toolbox.Library.Forms;
|
||||
using OpenTK;
|
||||
|
||||
namespace FirstPlugin.LuigisMansion.DarkMoon
|
||||
{
|
||||
public class LM2_Model : TreeNodeCustom
|
||||
{
|
||||
public LM2_ModelInfo ModelInfo;
|
||||
public List<LM2_Mesh> Meshes = new List<LM2_Mesh>();
|
||||
public List<uint> VertexBufferPointers = new List<uint>();
|
||||
public Matrix4 Transform { get; set; }
|
||||
|
||||
public override void OnClick(TreeView treeView)
|
||||
{
|
||||
STPropertyGrid editor = (STPropertyGrid)LibraryGUI.GetActiveContent(typeof(STPropertyGrid));
|
||||
if (editor == null)
|
||||
{
|
||||
editor = new STPropertyGrid();
|
||||
LibraryGUI.LoadEditor(editor);
|
||||
}
|
||||
editor.Text = Text;
|
||||
editor.Dock = DockStyle.Fill;
|
||||
editor.LoadProperty(this, OnPropertyChanged);
|
||||
}
|
||||
|
||||
public void OnPropertyChanged() { }
|
||||
}
|
||||
|
||||
public class LM2_ModelInfo
|
||||
{
|
||||
public byte[] Data;
|
||||
}
|
||||
|
||||
public class RenderableMeshWrapper : STGenericObject
|
||||
{
|
||||
public LM2_Mesh Mesh { get; set; }
|
||||
|
||||
public override void OnClick(TreeView treeView)
|
||||
{
|
||||
STPropertyGrid editor = (STPropertyGrid)LibraryGUI.GetActiveContent(typeof(STPropertyGrid));
|
||||
if (editor == null)
|
||||
{
|
||||
editor = new STPropertyGrid();
|
||||
LibraryGUI.LoadEditor(editor);
|
||||
}
|
||||
editor.Text = Text;
|
||||
editor.Dock = DockStyle.Fill;
|
||||
editor.LoadProperty(Mesh, OnPropertyChanged);
|
||||
}
|
||||
|
||||
public void OnPropertyChanged() { }
|
||||
}
|
||||
|
||||
public class LM2_IndexList
|
||||
{
|
||||
public short[] UnknownIndices { get; set; }
|
||||
|
||||
public uint Unknown { get; set; }
|
||||
|
||||
public short[] UnknownIndices2 { get; set; }
|
||||
|
||||
public uint[] Unknown2 { get; set; }
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
UnknownIndices = reader.ReadInt16s(4);
|
||||
Unknown = reader.ReadUInt32();
|
||||
UnknownIndices2 = reader.ReadInt16s(8);
|
||||
Unknown2 = reader.ReadUInt32s(6); //Increases by 32 each entry
|
||||
}
|
||||
}
|
||||
|
||||
public class LM2_Mesh
|
||||
{
|
||||
public uint IndexStartOffset { get; private set; } //relative to buffer start
|
||||
public ushort IndexCount { get; private set; } //divide by 3 to get face count
|
||||
public ushort IndexFormat { get; private set; } //0x0 - ushort, 0x8000 - byte
|
||||
|
||||
public ushort BufferStride { get; private set; }
|
||||
public ushort Unknown { get; private set; }
|
||||
public ushort Unknown2 { get; private set; }
|
||||
public ulong DataFormat { get; private set; }
|
||||
public uint Unknown4 { get; private set; }
|
||||
public uint Unknown5 { get; private set; }
|
||||
public uint Unknown6 { get; private set; }
|
||||
public ushort VertexCount { get; private set; }
|
||||
public ushort Unknown7 { get; private set; }
|
||||
public uint HashID { get; private set; }
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
IndexStartOffset = reader.ReadUInt32();
|
||||
IndexCount = reader.ReadUInt16();
|
||||
IndexFormat = reader.ReadUInt16();
|
||||
BufferStride = reader.ReadUInt16();
|
||||
Unknown = reader.ReadUInt16();
|
||||
Unknown2 = reader.ReadUInt16();
|
||||
DataFormat = reader.ReadUInt64();
|
||||
Unknown4 = reader.ReadUInt32();
|
||||
Unknown5 = reader.ReadUInt32();
|
||||
Unknown6 = reader.ReadUInt32();
|
||||
VertexCount = reader.ReadUInt16();
|
||||
Unknown7 = reader.ReadUInt16(); //0x100
|
||||
HashID = reader.ReadUInt32(); //0x100
|
||||
}
|
||||
|
||||
public class FormatInfo
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
99
File_Format_Library/FileFormats/Archives/LM2/TexturePOWE.cs
Normal file
99
File_Format_Library/FileFormats/Archives/LM2/TexturePOWE.cs
Normal file
@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Drawing;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
using Toolbox.Library.Forms;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace FirstPlugin.LuigisMansion.DarkMoon
|
||||
{
|
||||
public class TexturePOWE : STGenericTexture
|
||||
{
|
||||
public static readonly uint Identifier = 0xE977D350;
|
||||
|
||||
public uint Index { get; set; }
|
||||
|
||||
public uint ID { get; set; }
|
||||
public uint ImageSize { get; set; }
|
||||
public uint ID2 { get; set; }
|
||||
|
||||
public byte[] ImageData { get; set; }
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
//Magic and ID not pointed to for sub entries so just skip them for now
|
||||
// uint magic = reader.ReadUInt32();
|
||||
// if (magic != Identifier)
|
||||
// throw new Exception($"Invalid texture header magic! Expected {Identifier.ToString("x")}. Got {Identifier.ToString("x")}");
|
||||
// ID = reader.ReadUInt32();
|
||||
|
||||
PlatformSwizzle = PlatformSwizzle.Platform_3DS;
|
||||
|
||||
ImageSize = reader.ReadUInt32();
|
||||
ID2 = reader.ReadUInt32();
|
||||
reader.Seek(0x8);
|
||||
Width = reader.ReadUInt16();
|
||||
Height = reader.ReadUInt16();
|
||||
reader.Seek(3);
|
||||
var numMips = reader.ReadByte();
|
||||
reader.Seek(0x14);
|
||||
byte FormatCtr = reader.ReadByte();
|
||||
reader.Seek(3);
|
||||
|
||||
MipCount = 1;
|
||||
Format = CTR_3DS.ConvertPICAToGenericFormat((CTR_3DS.PICASurfaceFormat)FormatCtr);
|
||||
}
|
||||
|
||||
public override void OnClick(TreeView treeview)
|
||||
{
|
||||
ImageEditorBase editor = (ImageEditorBase)LibraryGUI.GetActiveContent(typeof(ImageEditorBase));
|
||||
if (editor == null)
|
||||
{
|
||||
editor = new ImageEditorBase();
|
||||
editor.Dock = DockStyle.Fill;
|
||||
|
||||
LibraryGUI.LoadEditor(editor);
|
||||
}
|
||||
editor.Text = Text;
|
||||
editor.LoadProperties(this.GenericProperties);
|
||||
editor.LoadImage(this);
|
||||
}
|
||||
|
||||
public override bool CanEdit { get; set; } = false;
|
||||
|
||||
public override void SetImageData(Bitmap bitmap, int ArrayLevel)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0)
|
||||
{
|
||||
return ImageData;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -206,6 +206,9 @@
|
||||
<Compile Include="FileFormats\Archives\IGA_PAK.cs" />
|
||||
<Compile Include="FileFormats\Archives\LM2\LM2_ChunkTable.cs" />
|
||||
<Compile Include="FileFormats\Archives\LM2\LM2_DICT.cs" />
|
||||
<Compile Include="FileFormats\Archives\LM2\LM2_Enums.cs" />
|
||||
<Compile Include="FileFormats\Archives\LM2\LM2_Model.cs" />
|
||||
<Compile Include="FileFormats\Archives\LM2\TexturePOWE.cs" />
|
||||
<Compile Include="FileFormats\Archives\LZARC.cs" />
|
||||
<Compile Include="FileFormats\Archives\ME01.cs" />
|
||||
<Compile Include="FileFormats\Archives\MKGPDX_PAC.cs" />
|
||||
|
@ -7,6 +7,7 @@ using Toolbox.Library;
|
||||
using Toolbox.Library.Forms;
|
||||
using Toolbox.Library.IO;
|
||||
using FirstPlugin.Forms;
|
||||
using FirstPlugin.LuigisMansion.DarkMoon;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
|
@ -540,8 +540,8 @@ namespace Toolbox.Library.Forms
|
||||
|
||||
private void treeViewCustom1_DragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
var file = GetActiveArchive();
|
||||
if (file == null || !file.CanReplaceFiles)
|
||||
var root = GetActiveArchive();
|
||||
if (root == null || !root.ArchiveFile.CanReplaceFiles)
|
||||
return;
|
||||
|
||||
Point pt = treeViewCustom1.PointToClient(new Point(e.X, e.Y));
|
||||
@ -557,10 +557,15 @@ namespace Toolbox.Library.Forms
|
||||
// e.Effect = DragDropEffects.None;
|
||||
}
|
||||
|
||||
private IArchiveFile GetActiveArchive()
|
||||
private ArchiveRootNodeWrapper GetActiveArchive()
|
||||
{
|
||||
if (treeViewCustom1.SelectedNode != null && treeViewCustom1.SelectedNode is ArchiveBase)
|
||||
return ((ArchiveBase)treeViewCustom1.SelectedNode).ArchiveFile;
|
||||
var node = treeViewCustom1.SelectedNode;
|
||||
if (node != null && node is ArchiveRootNodeWrapper)
|
||||
return (ArchiveRootNodeWrapper)node;
|
||||
if (node != null && node is ArchiveFileWrapper)
|
||||
return ((ArchiveFileWrapper)node).RootNode;
|
||||
if (node != null && node is ArchiveFolderNodeWrapper)
|
||||
return ((ArchiveFolderNodeWrapper)node).RootNode;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -183,13 +183,15 @@ namespace Toolbox.Library
|
||||
}
|
||||
|
||||
|
||||
public static void AddFiles(TreeNode parentNode, IArchiveFile archiveFile, string[] Files)
|
||||
public static void AddFiles(TreeNode parentNode, ArchiveRootNodeWrapper rootNode, string[] Files)
|
||||
{
|
||||
var archiveFile = rootNode.ArchiveFile;
|
||||
|
||||
if (Files == null || Files.Length <= 0 || !archiveFile.CanAddFiles) return;
|
||||
|
||||
for (int i = 0; i < Files.Length; i++)
|
||||
{
|
||||
var File = ArchiveFileWrapper.FromPath(Files[i], archiveFile);
|
||||
var File = ArchiveFileWrapper.FromPath(Files[i], rootNode);
|
||||
string FileName = Path.GetFileName(Files[i]);
|
||||
|
||||
//Don't add the root file name
|
||||
@ -209,9 +211,9 @@ namespace Toolbox.Library
|
||||
if (HasAddedFile)
|
||||
{
|
||||
if (parentNode is ArchiveRootNodeWrapper)
|
||||
((ArchiveRootNodeWrapper)parentNode).FileNodes.Add(File);
|
||||
((ArchiveRootNodeWrapper)parentNode).AddFileNode(File);
|
||||
if (parentNode is ArchiveFolderNodeWrapper)
|
||||
((ArchiveRootNodeWrapper)parentNode).FileNodes.Add(File);
|
||||
((ArchiveRootNodeWrapper)parentNode).AddFileNode(File);
|
||||
|
||||
parentNode.Nodes.Add(File);
|
||||
}
|
||||
|
@ -147,6 +147,50 @@ namespace Toolbox.Library.IO
|
||||
return new Syroot.IOExtension.Half(ReadUInt16());
|
||||
}
|
||||
|
||||
public Matrix4 ReadMatrix4(bool SwapRows = false)
|
||||
{
|
||||
Matrix4 mat4 = new Matrix4();
|
||||
if (SwapRows)
|
||||
{
|
||||
mat4.M11 = ReadSingle();
|
||||
mat4.M21 = ReadSingle();
|
||||
mat4.M31 = ReadSingle();
|
||||
mat4.M41 = ReadSingle();
|
||||
mat4.M12 = ReadSingle();
|
||||
mat4.M22 = ReadSingle();
|
||||
mat4.M32 = ReadSingle();
|
||||
mat4.M42 = ReadSingle();
|
||||
mat4.M13 = ReadSingle();
|
||||
mat4.M23 = ReadSingle();
|
||||
mat4.M33 = ReadSingle();
|
||||
mat4.M43 = ReadSingle();
|
||||
mat4.M14 = ReadSingle();
|
||||
mat4.M24 = ReadSingle();
|
||||
mat4.M34 = ReadSingle();
|
||||
mat4.M44 = ReadSingle();
|
||||
}
|
||||
else
|
||||
{
|
||||
mat4.M11 = ReadSingle();
|
||||
mat4.M12 = ReadSingle();
|
||||
mat4.M13 = ReadSingle();
|
||||
mat4.M14 = ReadSingle();
|
||||
mat4.M21 = ReadSingle();
|
||||
mat4.M22 = ReadSingle();
|
||||
mat4.M23 = ReadSingle();
|
||||
mat4.M24 = ReadSingle();
|
||||
mat4.M31 = ReadSingle();
|
||||
mat4.M32 = ReadSingle();
|
||||
mat4.M33 = ReadSingle();
|
||||
mat4.M34 = ReadSingle();
|
||||
mat4.M41 = ReadSingle();
|
||||
mat4.M42 = ReadSingle();
|
||||
mat4.M43 = ReadSingle();
|
||||
mat4.M44 = ReadSingle();
|
||||
}
|
||||
return mat4;
|
||||
}
|
||||
|
||||
public void SeekBegin(uint Offset) { Seek(Offset, SeekOrigin.Begin); }
|
||||
public void SeekBegin(int Offset) { Seek(Offset, SeekOrigin.Begin); }
|
||||
public void SeekBegin(long Offset) { Seek(Offset, SeekOrigin.Begin); }
|
||||
|
@ -183,7 +183,8 @@ namespace Toolbox.Library
|
||||
//Wrapper for the archive file itself
|
||||
public class ArchiveRootNodeWrapper : ArchiveBase, IContextMenuNode
|
||||
{
|
||||
public List<ArchiveFileWrapper> FileNodes = new List<ArchiveFileWrapper>();
|
||||
//A list that links archive file infos to treenodes of varying types
|
||||
public List<Tuple<ArchiveFileInfo, TreeNode>> FileNodes = new List<Tuple<ArchiveFileInfo, TreeNode>>();
|
||||
|
||||
public virtual object PropertyDisplay { get; set; }
|
||||
|
||||
@ -195,6 +196,11 @@ namespace Toolbox.Library
|
||||
PropertyDisplay = new GenericArchiveProperties(archiveFile, text);
|
||||
}
|
||||
|
||||
public void AddFileNode(ArchiveFileWrapper fileWrapper)
|
||||
{
|
||||
FileNodes.Add(Tuple.Create(fileWrapper.ArchiveFileInfo, (TreeNode)fileWrapper));
|
||||
}
|
||||
|
||||
public ToolStripItem[] GetContextMenuItems()
|
||||
{
|
||||
var ToolStrips = new ToolStripItem[]
|
||||
@ -232,7 +238,7 @@ namespace Toolbox.Library
|
||||
|
||||
if (ofd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
TreeHelper.AddFiles(this, ArchiveFile, ofd.FileNames);
|
||||
TreeHelper.AddFiles(this, this, ofd.FileNames);
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,7 +248,9 @@ namespace Toolbox.Library
|
||||
return;
|
||||
|
||||
for (int i = 0; i < FileNodes.Count; i++)
|
||||
FileNodes[i].ArchiveFileInfo.FileName = SetFullPath(FileNodes[i], this);
|
||||
{
|
||||
FileNodes[i].Item1.FileName = SetFullPath(FileNodes[i].Item2, this);
|
||||
}
|
||||
}
|
||||
|
||||
private static string SetFullPath(TreeNode node, TreeNode root)
|
||||
@ -411,7 +419,7 @@ namespace Toolbox.Library
|
||||
}
|
||||
else if (node is ArchiveFileInfo)
|
||||
{
|
||||
ArchiveFileWrapper wrapperFile = new ArchiveFileWrapper(node.Name, (ArchiveFileInfo)node, archiveFile);
|
||||
ArchiveFileWrapper wrapperFile = new ArchiveFileWrapper(node.Name, (ArchiveFileInfo)node, this);
|
||||
wrapperFile.Name = node.Name;
|
||||
parent.Nodes.Add(wrapperFile);
|
||||
}
|
||||
@ -468,11 +476,11 @@ namespace Toolbox.Library
|
||||
|
||||
if (rootIndex == roots.Length - 1)
|
||||
{
|
||||
ArchiveFileWrapper wrapperFile = new ArchiveFileWrapper(parentName, node, archiveFile);
|
||||
ArchiveFileWrapper wrapperFile = new ArchiveFileWrapper(parentName, node, this);
|
||||
wrapperFile.Name = nodeName;
|
||||
parentNode.Nodes.Add(wrapperFile);
|
||||
parentNode = wrapperFile;
|
||||
FileNodes.Add(wrapperFile);
|
||||
AddFileNode(wrapperFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -557,7 +565,7 @@ namespace Toolbox.Library
|
||||
ofd.Multiselect = true;
|
||||
|
||||
if (ofd.ShowDialog() == DialogResult.OK) {
|
||||
TreeHelper.AddFiles(this, ArchiveFile, ofd.FileNames);
|
||||
TreeHelper.AddFiles(this, RootNode, ofd.FileNames);
|
||||
}
|
||||
}
|
||||
|
||||
@ -618,11 +626,14 @@ namespace Toolbox.Library
|
||||
//Wrapper for files
|
||||
public class ArchiveFileWrapper : ArchiveBase, IContextMenuNode
|
||||
{
|
||||
public ArchiveRootNodeWrapper RootNode;
|
||||
|
||||
public virtual ArchiveFileInfo ArchiveFileInfo { get; set; }
|
||||
|
||||
public ArchiveFileWrapper(string text, ArchiveFileInfo archiveFileInfo, IArchiveFile archiveFile) : base(archiveFile)
|
||||
public ArchiveFileWrapper(string text, ArchiveFileInfo archiveFileInfo, ArchiveRootNodeWrapper rootNode) : base(rootNode.ArchiveFile)
|
||||
{
|
||||
Text = text;
|
||||
RootNode = rootNode;
|
||||
// ReloadMenus(archiveFile);
|
||||
|
||||
ArchiveFileInfo = archiveFileInfo;
|
||||
@ -705,12 +716,12 @@ namespace Toolbox.Library
|
||||
else return "";
|
||||
}
|
||||
|
||||
public static ArchiveFileWrapper FromPath(string FilePath, IArchiveFile archiveFile)
|
||||
public static ArchiveFileWrapper FromPath(string FilePath, ArchiveRootNodeWrapper rootNode)
|
||||
{
|
||||
var ArchiveFileInfo = new ArchiveFileInfo();
|
||||
ArchiveFileInfo.FileName = FilePath;
|
||||
ArchiveFileInfo.FileData = File.ReadAllBytes(FilePath);
|
||||
return new ArchiveFileWrapper(Path.GetFileName(FilePath), ArchiveFileInfo, archiveFile);
|
||||
return new ArchiveFileWrapper(Path.GetFileName(FilePath), ArchiveFileInfo, rootNode);
|
||||
}
|
||||
|
||||
public ToolStripItem[] GetContextMenuItems()
|
||||
@ -781,12 +792,12 @@ namespace Toolbox.Library
|
||||
OpenFormDialog(file);
|
||||
}
|
||||
else if (file is TreeNode)
|
||||
ReplaceNode(this.Parent, this, (TreeNode)file);
|
||||
ReplaceNode(this.Parent, this, (TreeNode)file, RootNode);
|
||||
else if (file is IArchiveFile)
|
||||
{
|
||||
var FileRoot = new ArchiveRootNodeWrapper(file.FileName, (IArchiveFile)file);
|
||||
FileRoot.FillTreeNodes();
|
||||
ReplaceNode(this.Parent, this, FileRoot);
|
||||
ReplaceNode(this.Parent, this, FileRoot, RootNode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -847,15 +858,20 @@ namespace Toolbox.Library
|
||||
editor.UpdateEditor();
|
||||
}
|
||||
|
||||
public static void ReplaceNode(TreeNode node, TreeNode replaceNode, TreeNode NewNode)
|
||||
public static void ReplaceNode(TreeNode node, ArchiveFileWrapper replaceNode, TreeNode NewNode, ArchiveRootNodeWrapper rootNode)
|
||||
{
|
||||
if (NewNode == null)
|
||||
return;
|
||||
|
||||
var fileInfo = replaceNode.ArchiveFileInfo;
|
||||
|
||||
int index = node.Nodes.IndexOf(replaceNode);
|
||||
node.Nodes.RemoveAt(index);
|
||||
node.Nodes.Insert(index, NewNode);
|
||||
|
||||
rootNode.FileNodes.RemoveAt(index);
|
||||
rootNode.FileNodes.Insert(index, Tuple.Create(fileInfo, NewNode));
|
||||
|
||||
NewNode.ImageKey = replaceNode.ImageKey;
|
||||
NewNode.SelectedImageKey = replaceNode.SelectedImageKey;
|
||||
NewNode.Text = replaceNode.Text;
|
||||
|
Loading…
Reference in New Issue
Block a user