1
0
mirror of synced 2024-12-01 02:27:22 +01:00

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:
KillzXGaming 2019-07-22 15:06:49 -04:00
parent 1059113cee
commit 1429a00178
12 changed files with 539 additions and 192 deletions

Binary file not shown.

View File

@ -5,8 +5,24 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Toolbox.Library.IO; 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 //Table consists of 2 chunk entry lists that define how the .data reads sections
public class LM2_ChunkTable public class LM2_ChunkTable
{ {
@ -18,28 +34,6 @@ namespace FirstPlugin
public List<ChunkEntry> ChunkEntries = new List<ChunkEntry>(); public List<ChunkEntry> ChunkEntries = new List<ChunkEntry>();
public List<ChunkSubEntry> ChunkSubEntries = new List<ChunkSubEntry>(); 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) public void Read(FileReader tableReader)
{ {
tableReader.SetByteOrder(false); tableReader.SetByteOrder(false);
@ -52,8 +46,8 @@ namespace FirstPlugin
entry.Unknown1 = tableReader.ReadUInt32(); //8 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.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.ChunkType = tableReader.ReadEnum<DataType>(false); //The type of chunk. 0x8701B5 for example for texture info
entry.ChunkSubCount = tableReader.ReadUInt32(); 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 //This increases by 2 each chunk info, however the starting value is not 0
//Note the last entry does not have this //Note the last entry does not have this
@ -71,9 +65,9 @@ namespace FirstPlugin
while (!tableReader.EndOfStream && tableReader.Position <= tableReader.BaseStream.Length - 12) while (!tableReader.EndOfStream && tableReader.Position <= tableReader.BaseStream.Length - 12)
{ {
ChunkSubEntry subEntry = new ChunkSubEntry(); 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.ChunkSize = tableReader.ReadUInt32();
subEntry.Unknown = tableReader.ReadUInt32(); subEntry.ChunkOffset = tableReader.ReadUInt32();
ChunkSubEntries.Add(subEntry); ChunkSubEntries.Add(subEntry);
} }
} }

View File

@ -10,7 +10,7 @@ using Toolbox.Library.IO;
using Toolbox.Library.Forms; using Toolbox.Library.Forms;
using System.Drawing; using System.Drawing;
namespace FirstPlugin namespace FirstPlugin.LuigisMansion.DarkMoon
{ {
//Parse info based on https://github.com/TheFearsomeDzeraora/LM2L //Parse info based on https://github.com/TheFearsomeDzeraora/LM2L
public class LM2_DICT : TreeNodeFile, IFileFormat, IArchiveFile 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 IEnumerable<ArchiveFileInfo> Files => files;
public void ClearFiles() { files.Clear(); } public void ClearFiles() { files.Clear(); }
@ -55,6 +55,23 @@ namespace FirstPlugin
public bool IsCompressed = false; public bool IsCompressed = false;
public LM2_ChunkTable ChunkTable; 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) public void Load(System.IO.Stream stream)
{ {
@ -71,23 +88,22 @@ namespace FirstPlugin
reader.SeekBegin(0x2C); reader.SeekBegin(0x2C);
byte[] Unknowns = reader.ReadBytes((int)FileCount); byte[] Unknowns = reader.ReadBytes((int)FileCount);
TreeNode tableNodes = new TreeNode("File Section Entries");
long FileTablePos = reader.Position; long FileTablePos = reader.Position;
for (int i = 0; i < FileCount; i++) for (int i = 0; i < FileCount; i++)
{ {
var file = new FileEntry(this); var file = new FileEntry(this);
file.Text = $"entry {i}";
file.Read(reader); 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 first file stores a chunk layout
//The second one seems to be a duplicate? //The second one seems to be a duplicate?
if (i == 0) if (i == 0)
{ {
using (var tableReader = new FileReader(file.FileData)) using (var tableReader = new FileReader(file.GetData()))
{ {
ChunkTable = new LM2_ChunkTable(); ChunkTable = new LM2_ChunkTable();
ChunkTable.Read(tableReader); ChunkTable.Read(tableReader);
@ -97,68 +113,120 @@ namespace FirstPlugin
TreeNode list1 = new TreeNode("Entry List 1"); TreeNode list1 = new TreeNode("Entry List 1");
TreeNode list2 = new TreeNode("Entry List 2 "); TreeNode list2 = new TreeNode("Entry List 2 ");
debugFolder.Nodes.Add(tableNodes);
debugFolder.Nodes.Add(list1); debugFolder.Nodes.Add(list1);
debugFolder.Nodes.Add(list2); debugFolder.Nodes.Add(list2);
foreach (var chunk in ChunkTable.ChunkEntries) 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) 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 //Set an instance of our current data
uint ImageIndex = 0; //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) var chunkEntry = new ChunkDataEntry(this, chunk);
{ chunkEntry.DataFile = File003Data;
if (files[i].Unknown3 == 1) //Info chunkEntry.FileName = $"Chunk {chunk.ChunkType} {chunkId++}";
files.Add(chunkEntry);
switch (chunk.ChunkType)
{ {
case SubDataType.TextureHeader:
chunkEntry.DataFile = File002Data;
//Read the info //Read the info
using (var textureReader = new FileReader(files[i].FileData)) using (var textureReader = new FileReader(chunkEntry.FileData))
{ {
int headerIndex = 0; currentTexture = new TexturePOWE();
while (!textureReader.EndOfStream && textureReader.ReadUInt32() == TexturePOWE.Identifier) currentTexture.ImageKey = "texture";
currentTexture.SelectedImageKey = currentTexture.ImageKey;
currentTexture.Index = ImageHeaderIndex;
currentTexture.Read(textureReader);
currentTexture.Text = $"Texture {ImageHeaderIndex}";
textureFolder.Nodes.Add(currentTexture);
ImageHeaderIndex++;
}
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))
{ {
var texture = new TexturePOWE(); while (!vtxPtrReader.EndOfStream)
texture.ImageKey = "texture"; currentModel.VertexBufferPointers.Add(vtxPtrReader.ReadUInt32());
texture.SelectedImageKey = texture.ImageKey; }
texture.Index = ImageIndex; break;
texture.Read(textureReader); case SubDataType.SubmeshInfo:
texture.Text = $"Texture {headerIndex++}"; int MeshCount = chunkEntry.FileData.Length / 0x28;
textureFolder.Nodes.Add(texture); using (var meshReader = new FileReader(chunkEntry.FileData))
Textures.Add(texture); {
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;
} }
else //Block
{
uint Offset = 0;
foreach (var tex in Textures)
{
if (tex.Index == ImageIndex)
{
tex.ImageData = Utils.SubArray(files[i].FileData, Offset, tex.ImageSize);
} }
Offset += tex.ImageSize; foreach (LM2_Model model in modelFolder.Nodes)
} {
ImageIndex++; 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) if (textureFolder.Nodes.Count > 0)
Nodes.Add(textureFolder); Nodes.Add(textureFolder);
} }
@ -184,102 +252,16 @@ namespace FirstPlugin
return false; return false;
} }
public class TexturePOWE : STGenericTexture public class ChunkDataEntry : ArchiveFileInfo
{
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 byte[] DataFile;
public LM2_DICT ParentDictionary { get; set; } public LM2_DICT ParentDictionary { get; set; }
public ChunkSubEntry Entry;
public uint Offset; public ChunkDataEntry(LM2_DICT dict, ChunkSubEntry entry)
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
{ {
Texture = 0x80, ParentDictionary = dict;
Entry = entry;
} }
public override byte[] FileData 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) public FileEntry(LM2_DICT dict)
{ {
ParentDictionary = dict; ParentDictionary = dict;
@ -301,7 +304,7 @@ namespace FirstPlugin
Offset = reader.ReadUInt32(); Offset = reader.ReadUInt32();
DecompressedSize = reader.ReadUInt32(); DecompressedSize = reader.ReadUInt32();
CompressedSize = reader.ReadUInt32(); CompressedSize = reader.ReadUInt32();
FileType = reader.ReadEnum<DataType>(false); Unknown1 = reader.ReadUInt16();
Unknown2 = reader.ReadByte(); Unknown2 = reader.ReadByte();
Unknown3 = 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]; byte[] Data = new byte[DecompressedSize];

View 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,
}
}

View 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
{
}
}
}

View 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,
};
}
}
}
}

View File

@ -206,6 +206,9 @@
<Compile Include="FileFormats\Archives\IGA_PAK.cs" /> <Compile Include="FileFormats\Archives\IGA_PAK.cs" />
<Compile Include="FileFormats\Archives\LM2\LM2_ChunkTable.cs" /> <Compile Include="FileFormats\Archives\LM2\LM2_ChunkTable.cs" />
<Compile Include="FileFormats\Archives\LM2\LM2_DICT.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\LZARC.cs" />
<Compile Include="FileFormats\Archives\ME01.cs" /> <Compile Include="FileFormats\Archives\ME01.cs" />
<Compile Include="FileFormats\Archives\MKGPDX_PAC.cs" /> <Compile Include="FileFormats\Archives\MKGPDX_PAC.cs" />

View File

@ -7,6 +7,7 @@ using Toolbox.Library;
using Toolbox.Library.Forms; using Toolbox.Library.Forms;
using Toolbox.Library.IO; using Toolbox.Library.IO;
using FirstPlugin.Forms; using FirstPlugin.Forms;
using FirstPlugin.LuigisMansion.DarkMoon;
namespace FirstPlugin namespace FirstPlugin
{ {

View File

@ -540,8 +540,8 @@ namespace Toolbox.Library.Forms
private void treeViewCustom1_DragOver(object sender, DragEventArgs e) private void treeViewCustom1_DragOver(object sender, DragEventArgs e)
{ {
var file = GetActiveArchive(); var root = GetActiveArchive();
if (file == null || !file.CanReplaceFiles) if (root == null || !root.ArchiveFile.CanReplaceFiles)
return; return;
Point pt = treeViewCustom1.PointToClient(new Point(e.X, e.Y)); Point pt = treeViewCustom1.PointToClient(new Point(e.X, e.Y));
@ -557,10 +557,15 @@ namespace Toolbox.Library.Forms
// e.Effect = DragDropEffects.None; // e.Effect = DragDropEffects.None;
} }
private IArchiveFile GetActiveArchive() private ArchiveRootNodeWrapper GetActiveArchive()
{ {
if (treeViewCustom1.SelectedNode != null && treeViewCustom1.SelectedNode is ArchiveBase) var node = treeViewCustom1.SelectedNode;
return ((ArchiveBase)treeViewCustom1.SelectedNode).ArchiveFile; 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; return null;
} }

View File

@ -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; if (Files == null || Files.Length <= 0 || !archiveFile.CanAddFiles) return;
for (int i = 0; i < Files.Length; i++) 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]); string FileName = Path.GetFileName(Files[i]);
//Don't add the root file name //Don't add the root file name
@ -209,9 +211,9 @@ namespace Toolbox.Library
if (HasAddedFile) if (HasAddedFile)
{ {
if (parentNode is ArchiveRootNodeWrapper) if (parentNode is ArchiveRootNodeWrapper)
((ArchiveRootNodeWrapper)parentNode).FileNodes.Add(File); ((ArchiveRootNodeWrapper)parentNode).AddFileNode(File);
if (parentNode is ArchiveFolderNodeWrapper) if (parentNode is ArchiveFolderNodeWrapper)
((ArchiveRootNodeWrapper)parentNode).FileNodes.Add(File); ((ArchiveRootNodeWrapper)parentNode).AddFileNode(File);
parentNode.Nodes.Add(File); parentNode.Nodes.Add(File);
} }

View File

@ -147,6 +147,50 @@ namespace Toolbox.Library.IO
return new Syroot.IOExtension.Half(ReadUInt16()); 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(uint Offset) { Seek(Offset, SeekOrigin.Begin); }
public void SeekBegin(int Offset) { Seek(Offset, SeekOrigin.Begin); } public void SeekBegin(int Offset) { Seek(Offset, SeekOrigin.Begin); }
public void SeekBegin(long Offset) { Seek(Offset, SeekOrigin.Begin); } public void SeekBegin(long Offset) { Seek(Offset, SeekOrigin.Begin); }

View File

@ -183,7 +183,8 @@ namespace Toolbox.Library
//Wrapper for the archive file itself //Wrapper for the archive file itself
public class ArchiveRootNodeWrapper : ArchiveBase, IContextMenuNode 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; } public virtual object PropertyDisplay { get; set; }
@ -195,6 +196,11 @@ namespace Toolbox.Library
PropertyDisplay = new GenericArchiveProperties(archiveFile, text); PropertyDisplay = new GenericArchiveProperties(archiveFile, text);
} }
public void AddFileNode(ArchiveFileWrapper fileWrapper)
{
FileNodes.Add(Tuple.Create(fileWrapper.ArchiveFileInfo, (TreeNode)fileWrapper));
}
public ToolStripItem[] GetContextMenuItems() public ToolStripItem[] GetContextMenuItems()
{ {
var ToolStrips = new ToolStripItem[] var ToolStrips = new ToolStripItem[]
@ -232,7 +238,7 @@ namespace Toolbox.Library
if (ofd.ShowDialog() == DialogResult.OK) 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; return;
for (int i = 0; i < FileNodes.Count; i++) 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) private static string SetFullPath(TreeNode node, TreeNode root)
@ -411,7 +419,7 @@ namespace Toolbox.Library
} }
else if (node is ArchiveFileInfo) 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; wrapperFile.Name = node.Name;
parent.Nodes.Add(wrapperFile); parent.Nodes.Add(wrapperFile);
} }
@ -468,11 +476,11 @@ namespace Toolbox.Library
if (rootIndex == roots.Length - 1) if (rootIndex == roots.Length - 1)
{ {
ArchiveFileWrapper wrapperFile = new ArchiveFileWrapper(parentName, node, archiveFile); ArchiveFileWrapper wrapperFile = new ArchiveFileWrapper(parentName, node, this);
wrapperFile.Name = nodeName; wrapperFile.Name = nodeName;
parentNode.Nodes.Add(wrapperFile); parentNode.Nodes.Add(wrapperFile);
parentNode = wrapperFile; parentNode = wrapperFile;
FileNodes.Add(wrapperFile); AddFileNode(wrapperFile);
} }
else else
{ {
@ -557,7 +565,7 @@ namespace Toolbox.Library
ofd.Multiselect = true; ofd.Multiselect = true;
if (ofd.ShowDialog() == DialogResult.OK) { 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 //Wrapper for files
public class ArchiveFileWrapper : ArchiveBase, IContextMenuNode public class ArchiveFileWrapper : ArchiveBase, IContextMenuNode
{ {
public ArchiveRootNodeWrapper RootNode;
public virtual ArchiveFileInfo ArchiveFileInfo { get; set; } 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; Text = text;
RootNode = rootNode;
// ReloadMenus(archiveFile); // ReloadMenus(archiveFile);
ArchiveFileInfo = archiveFileInfo; ArchiveFileInfo = archiveFileInfo;
@ -705,12 +716,12 @@ namespace Toolbox.Library
else return ""; else return "";
} }
public static ArchiveFileWrapper FromPath(string FilePath, IArchiveFile archiveFile) public static ArchiveFileWrapper FromPath(string FilePath, ArchiveRootNodeWrapper rootNode)
{ {
var ArchiveFileInfo = new ArchiveFileInfo(); var ArchiveFileInfo = new ArchiveFileInfo();
ArchiveFileInfo.FileName = FilePath; ArchiveFileInfo.FileName = FilePath;
ArchiveFileInfo.FileData = File.ReadAllBytes(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() public ToolStripItem[] GetContextMenuItems()
@ -781,12 +792,12 @@ namespace Toolbox.Library
OpenFormDialog(file); OpenFormDialog(file);
} }
else if (file is TreeNode) else if (file is TreeNode)
ReplaceNode(this.Parent, this, (TreeNode)file); ReplaceNode(this.Parent, this, (TreeNode)file, RootNode);
else if (file is IArchiveFile) else if (file is IArchiveFile)
{ {
var FileRoot = new ArchiveRootNodeWrapper(file.FileName, (IArchiveFile)file); var FileRoot = new ArchiveRootNodeWrapper(file.FileName, (IArchiveFile)file);
FileRoot.FillTreeNodes(); FileRoot.FillTreeNodes();
ReplaceNode(this.Parent, this, FileRoot); ReplaceNode(this.Parent, this, FileRoot, RootNode);
} }
} }
@ -847,15 +858,20 @@ namespace Toolbox.Library
editor.UpdateEditor(); 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) if (NewNode == null)
return; return;
var fileInfo = replaceNode.ArchiveFileInfo;
int index = node.Nodes.IndexOf(replaceNode); int index = node.Nodes.IndexOf(replaceNode);
node.Nodes.RemoveAt(index); node.Nodes.RemoveAt(index);
node.Nodes.Insert(index, NewNode); node.Nodes.Insert(index, NewNode);
rootNode.FileNodes.RemoveAt(index);
rootNode.FileNodes.Insert(index, Tuple.Create(fileInfo, NewNode));
NewNode.ImageKey = replaceNode.ImageKey; NewNode.ImageKey = replaceNode.ImageKey;
NewNode.SelectedImageKey = replaceNode.SelectedImageKey; NewNode.SelectedImageKey = replaceNode.SelectedImageKey;
NewNode.Text = replaceNode.Text; NewNode.Text = replaceNode.Text;