mirror of
https://github.com/kwsch/pk3DS.git
synced 2026-10-03 06:16:55 -05:00
Refactoring & Code Integration from other projects
Added DARC Batch by SciresM Added png2bclim conversion by Kaphotics/SciresM Really really old code; just using for some quick tool integration.
This commit is contained in:
940
pk3DS/ARCUtil.cs
Normal file
940
pk3DS/ARCUtil.cs
Normal file
@@ -0,0 +1,940 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace pk3DS
|
||||
{
|
||||
public partial class ARC
|
||||
{
|
||||
// Multi Type Archive Handling
|
||||
internal static Boolean onefile = true;
|
||||
internal static SARC analyzeSARC(string path)
|
||||
{
|
||||
SARC sarc = new SARC
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(path),
|
||||
FilePath = Path.GetDirectoryName(path),
|
||||
Extension = Path.GetExtension(path)
|
||||
};
|
||||
BinaryReader br = new BinaryReader(File.OpenRead(path));
|
||||
sarc.valid = true;
|
||||
sarc.Signature = new string(br.ReadChars(4));
|
||||
if (sarc.Signature != "SARC")
|
||||
{
|
||||
sarc.valid = false;
|
||||
return sarc;
|
||||
}
|
||||
sarc.HeaderSize = br.ReadUInt16();
|
||||
sarc.Endianness = br.ReadUInt16();
|
||||
sarc.FileSize = br.ReadUInt32();
|
||||
sarc.DataOffset = br.ReadUInt32();
|
||||
sarc.Unknown = br.ReadUInt32();
|
||||
sarc.SFat = new SFAT { Signature = new string(br.ReadChars(4)) };
|
||||
if (sarc.SFat.Signature != "SFAT")
|
||||
{
|
||||
sarc.valid = false;
|
||||
return sarc;
|
||||
}
|
||||
sarc.SFat.HeaderSize = br.ReadUInt16();
|
||||
sarc.SFat.EntryCount = br.ReadUInt16();
|
||||
sarc.SFat.HashMult = br.ReadUInt32();
|
||||
sarc.SFat.Entries = new List<SFATEntry>();
|
||||
for (int i = 0; i < sarc.SFat.EntryCount; i++)
|
||||
{
|
||||
SFATEntry s = new SFATEntry
|
||||
{
|
||||
FileNameHash = br.ReadUInt32(),
|
||||
FileNameOffset = br.ReadUInt32(),
|
||||
FileDataStart = br.ReadUInt32(),
|
||||
FileDataEnd = br.ReadUInt32()
|
||||
};
|
||||
sarc.SFat.Entries.Add(s);
|
||||
}
|
||||
sarc.SFnt = new SFNT { Signature = new string(br.ReadChars(4)) };
|
||||
if (sarc.SFnt.Signature != "SFNT")
|
||||
{
|
||||
sarc.valid = false;
|
||||
return sarc;
|
||||
}
|
||||
sarc.SFnt.HeaderSize = br.ReadUInt16();
|
||||
sarc.SFnt.Unknown = br.ReadUInt16();
|
||||
sarc.SFnt.StringOffset = (uint)br.BaseStream.Position;
|
||||
return sarc;
|
||||
}
|
||||
internal static ShuffleARC AnalyzeShuffle(string path)
|
||||
{
|
||||
ShuffleARC sharc = new ShuffleARC
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(path),
|
||||
FilePath = Path.GetDirectoryName(path),
|
||||
Extension = Path.GetExtension(path)
|
||||
};
|
||||
BinaryReader br = new BinaryReader(File.OpenRead(path));
|
||||
if (br.ReadUInt32() != 0xB)
|
||||
{
|
||||
br.BaseStream.Seek(0x100, SeekOrigin.Begin);
|
||||
if (br.ReadUInt32() != 0xB)
|
||||
{
|
||||
sharc.valid = false;
|
||||
return sharc;
|
||||
}
|
||||
sharc.add100 = true;
|
||||
}
|
||||
uint magic = br.ReadUInt32();
|
||||
if (magic.ToString("X8") != sharc.FileName)
|
||||
{
|
||||
Console.WriteLine("Sharc mismatch - " + magic.ToString("X8") + "," + sharc.FileName);
|
||||
sharc.valid = false;
|
||||
return sharc;
|
||||
}
|
||||
sharc.valid = true;
|
||||
br.ReadUInt32();
|
||||
br.ReadUInt32();
|
||||
sharc.FileCount = br.ReadUInt32();
|
||||
br.ReadUInt32();
|
||||
sharc.Files = new List<ShuffleFile>();
|
||||
for (int i = 0; i < sharc.FileCount; i++)
|
||||
{
|
||||
br.BaseStream.Seek(0x8, SeekOrigin.Current);
|
||||
ShuffleFile sf = new ShuffleFile
|
||||
{
|
||||
Length = br.ReadUInt32(),
|
||||
Offset = br.ReadUInt32() + (uint)(sharc.add100 ? 0x100 : 0)
|
||||
};
|
||||
br.BaseStream.Seek(0x10, SeekOrigin.Current);
|
||||
sharc.Files.Add(sf);
|
||||
}
|
||||
return sharc;
|
||||
}
|
||||
internal static GAR analyzeGAR(string path)
|
||||
{
|
||||
GAR gar = new GAR
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(path),
|
||||
FilePath = Path.GetDirectoryName(path),
|
||||
Extension = Path.GetExtension(path)
|
||||
};
|
||||
BinaryReader br = new BinaryReader(File.OpenRead(path));
|
||||
long len = br.BaseStream.Length;
|
||||
gar.Magic = br.ReadUInt32();
|
||||
gar.FileLength = br.ReadUInt32();
|
||||
if (gar.Magic != 0x02524147 || gar.FileLength != len)
|
||||
{
|
||||
gar.valid = false;
|
||||
return gar;
|
||||
}
|
||||
gar.valid = true;
|
||||
gar.Unknown = br.ReadUInt32();
|
||||
gar.HeaderLength = br.ReadUInt32();
|
||||
gar.FileMetaOffset = br.ReadUInt32();
|
||||
gar.FileOffsetsOffset = br.ReadUInt32();
|
||||
br.BaseStream.Seek(0x1C, SeekOrigin.Current);
|
||||
gar.FileCountOffset = br.ReadUInt32();
|
||||
gar.CTXBOffset = br.ReadUInt32();
|
||||
br.BaseStream.Seek(gar.FileOffsetsOffset, SeekOrigin.Begin);
|
||||
gar.DataOffset = br.ReadUInt32();
|
||||
gar.FileCount = (gar.DataOffset - gar.FileOffsetsOffset) / 4;
|
||||
br.BaseStream.Seek(gar.FileMetaOffset, SeekOrigin.Begin);
|
||||
gar.Files = new List<GARFile>();
|
||||
for (int i = 0; i < gar.FileCount; i++)
|
||||
{
|
||||
GARFile gf = new GARFile
|
||||
{
|
||||
Length = br.ReadUInt32(),
|
||||
NOffset = br.ReadUInt32(),
|
||||
NWEOffset = br.ReadUInt32()
|
||||
};
|
||||
gar.Files.Add(gf);
|
||||
}
|
||||
for (int i = 0; i < gar.FileCount; i++)
|
||||
{
|
||||
br.BaseStream.Seek(gar.Files[i].NOffset, SeekOrigin.Begin);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (char c = br.ReadChar(); c != (char)0; c = br.ReadChar())
|
||||
{
|
||||
sb.Append(c);
|
||||
}
|
||||
gar.Files[i].Name = sb.ToString();
|
||||
br.BaseStream.Seek(gar.Files[i].NWEOffset, SeekOrigin.Begin);
|
||||
sb = new StringBuilder();
|
||||
for (char c = br.ReadChar(); c != (char)0; c = br.ReadChar())
|
||||
{
|
||||
sb.Append(c);
|
||||
}
|
||||
gar.Files[i].NameWithExtension = sb.ToString();
|
||||
}
|
||||
br.BaseStream.Seek(gar.FileOffsetsOffset, SeekOrigin.Begin);
|
||||
if (gar.Files.Count > 0)
|
||||
{
|
||||
gar.Files[0].Offset = gar.DataOffset;
|
||||
br.ReadUInt32();
|
||||
}
|
||||
for (int i = 1; i < gar.FileCount; i++)
|
||||
{
|
||||
gar.Files[i].Offset = br.ReadUInt32();
|
||||
}
|
||||
return gar;
|
||||
}
|
||||
internal static DARC analyze(string path)
|
||||
{
|
||||
DARC darc = new DARC
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(path),
|
||||
FilePath = Path.GetDirectoryName(path),
|
||||
Extension = Path.GetExtension(path)
|
||||
};
|
||||
BinaryReader br = new BinaryReader(File.OpenRead(path));
|
||||
long len = br.BaseStream.Length;
|
||||
darc.Magic = br.ReadUInt32();
|
||||
UInt32 m = darc.Magic;
|
||||
darc.HeaderOffset = 0;
|
||||
while (m != 0x63726164 && darc.HeaderOffset < len - 4)
|
||||
{
|
||||
m = br.ReadUInt32();
|
||||
darc.HeaderOffset += 4;
|
||||
}
|
||||
if (darc.HeaderOffset >= len - 4)
|
||||
{
|
||||
darc.valid = false;
|
||||
return darc;
|
||||
}
|
||||
darc.Magic = m;
|
||||
darc.valid = true;
|
||||
darc.BOM = br.ReadUInt16();
|
||||
darc.HeaderLength = br.ReadUInt16();
|
||||
darc.Unknown = br.ReadUInt32();
|
||||
darc.totalLength = br.ReadUInt32();
|
||||
darc.TableOffset = br.ReadUInt32(); //from start of file
|
||||
darc.TableOffset += darc.HeaderOffset;
|
||||
darc.TableLength = br.ReadUInt32();
|
||||
darc.DataOffset = br.ReadUInt32();
|
||||
FileTable ft = new FileTable();
|
||||
br.BaseStream.Seek(darc.TableOffset + 8, SeekOrigin.Begin);
|
||||
int count = br.ReadByte();
|
||||
ft.Files = new List<DarcFile>();
|
||||
ft.FileNames = new List<string>();
|
||||
br.BaseStream.Seek(darc.TableOffset, SeekOrigin.Begin);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
DarcFile file = new DarcFile
|
||||
{
|
||||
NameOffset = br.ReadUInt16(),
|
||||
Parent = br.ReadByte(),
|
||||
Folder = br.ReadByte(),
|
||||
Offset = br.ReadUInt32() + darc.HeaderOffset,
|
||||
Length = br.ReadUInt32()
|
||||
};
|
||||
DarcFile f2 = file;
|
||||
ft.Files.Add(f2);
|
||||
}
|
||||
|
||||
uint NameTableOffset = (uint)br.BaseStream.Position;
|
||||
for (int i = 0; i < ft.Files.Count; i++)
|
||||
{
|
||||
br.BaseStream.Seek(NameTableOffset + ft.Files[i].NameOffset, SeekOrigin.Begin);
|
||||
MemoryStream stream = new MemoryStream();
|
||||
for (byte fb = br.ReadByte(), sb = br.ReadByte();
|
||||
fb != 0 || sb != 0;
|
||||
fb = br.ReadByte(), sb = br.ReadByte())
|
||||
{
|
||||
stream.WriteByte(fb);
|
||||
stream.WriteByte(sb);
|
||||
}
|
||||
ft.FileNames.Add(Encoding.Unicode.GetString(stream.ToArray()));
|
||||
stream.Close();
|
||||
}
|
||||
darc.Files = ft;
|
||||
darc.FileName = Path.GetFileNameWithoutExtension(path);
|
||||
darc.FilePath = Path.GetDirectoryName(path);
|
||||
darc.Extension = Path.GetExtension(path);
|
||||
return darc;
|
||||
}
|
||||
internal static FARC analyzeFARC(string path)
|
||||
{
|
||||
FARC farc = new FARC
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(path),
|
||||
FilePath = Path.GetDirectoryName(path),
|
||||
Extension = Path.GetExtension(path)
|
||||
};
|
||||
BinaryReader br = new BinaryReader(File.OpenRead(path));
|
||||
long len = br.BaseStream.Length;
|
||||
farc.Magic = br.ReadUInt32();
|
||||
UInt32 m = farc.Magic;
|
||||
farc.HeaderOffset = 0;
|
||||
while (m != 0x43524146 && farc.HeaderOffset < len - 4) //FARC
|
||||
{
|
||||
m = br.ReadUInt32();
|
||||
farc.HeaderOffset += 4;
|
||||
}
|
||||
if (farc.HeaderOffset >= len - 4)
|
||||
{
|
||||
farc.valid = false;
|
||||
return farc;
|
||||
}
|
||||
farc.Magic = m;
|
||||
farc.valid = true;
|
||||
br.BaseStream.Seek(farc.HeaderOffset + 0x24, SeekOrigin.Begin);
|
||||
farc.SirOffset = br.ReadUInt32() + farc.HeaderOffset;
|
||||
br.ReadUInt32(); //unk
|
||||
farc.DataOffset = br.ReadUInt32() + farc.HeaderOffset;
|
||||
br.BaseStream.Seek(farc.SirOffset, SeekOrigin.Begin);
|
||||
farc.SirMagic = br.ReadUInt32();
|
||||
if (farc.SirMagic != 0x30524953)
|
||||
{
|
||||
farc.valid = false;
|
||||
return farc;
|
||||
}
|
||||
farc.MetaPointer = farc.SirOffset + br.ReadUInt32();
|
||||
br.BaseStream.Seek(farc.MetaPointer, SeekOrigin.Begin);
|
||||
farc.TableOffset = farc.SirOffset + br.ReadUInt32();
|
||||
farc.FileCount = br.ReadUInt32();
|
||||
br.BaseStream.Seek(farc.TableOffset, SeekOrigin.Begin);
|
||||
FARCFileTable ft = new FARCFileTable
|
||||
{
|
||||
Files = new List<FARCFile>(),
|
||||
FileNames = new List<string>()
|
||||
};
|
||||
for (int i = 0; i < farc.FileCount; i++)
|
||||
{
|
||||
FARCFile file = new FARCFile
|
||||
{
|
||||
NameOffset = br.ReadUInt32(),
|
||||
Offset = br.ReadUInt32(),
|
||||
Length = br.ReadUInt32()
|
||||
};
|
||||
br.ReadUInt32(); //align to 0x10
|
||||
FARCFile f2 = file;
|
||||
ft.Files.Add(f2);
|
||||
}
|
||||
for (int i = 0; i < farc.FileCount; i++)
|
||||
{
|
||||
br.BaseStream.Seek(ft.Files[i].NameOffset + farc.SirOffset, SeekOrigin.Begin);
|
||||
MemoryStream stream = new MemoryStream();
|
||||
int firstByte = 1, secondByte = 1;
|
||||
while (firstByte != 0 || secondByte != 0)
|
||||
{
|
||||
firstByte = br.ReadByte();
|
||||
secondByte = br.ReadByte();
|
||||
stream.WriteByte((byte) firstByte);
|
||||
stream.WriteByte((byte) secondByte);
|
||||
}
|
||||
ft.FileNames.Add(Encoding.Unicode.GetString(stream.ToArray()));
|
||||
stream.Close();
|
||||
}
|
||||
farc.Files = ft;
|
||||
farc.FileName = Path.GetFileNameWithoutExtension(path);
|
||||
farc.FilePath = Path.GetDirectoryName(path);
|
||||
farc.Extension = Path.GetExtension(path);
|
||||
return farc;
|
||||
}
|
||||
internal static string Interpret(string path)
|
||||
{
|
||||
string fn = Path.GetFileName(path);
|
||||
if (fn == "save0.bin" || fn == "save1.bin" || fn == "save2.bin")
|
||||
{
|
||||
return FixMajoraChecksum(path);
|
||||
}
|
||||
if (fn.StartsWith("message") && (fn.EndsWith("_US.bin") || fn.EndsWith("_UK.bin")))
|
||||
{
|
||||
return ParseShuffleText(path);
|
||||
}
|
||||
DARC darc = analyze(path);
|
||||
FARC farc = new FARC();
|
||||
GAR gar = new GAR();
|
||||
SARC sarc = new SARC();
|
||||
ShuffleARC sharc = new ShuffleARC();
|
||||
if (!darc.valid) farc = analyzeFARC(path);
|
||||
if (!farc.valid) gar = analyzeGAR(path);
|
||||
if (!gar.valid) sharc = AnalyzeShuffle(path);
|
||||
if (!sharc.valid) sarc = analyzeSARC(path);
|
||||
|
||||
string ret = "";
|
||||
if (darc.valid)
|
||||
{
|
||||
ret += "Header Offset: " + darc.HeaderOffset + Environment.NewLine + "File Count: " + darc.Files.Files.Count + Environment.NewLine;
|
||||
int extracted = 0;
|
||||
int folder = 0;
|
||||
for (int i = 0; i < darc.Files.Files.Count; i++)
|
||||
{
|
||||
if (darc.Files.Files[i].Folder > 0) { folder++; }
|
||||
else
|
||||
{
|
||||
extracted++;
|
||||
string dir = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + darc.FileName + Path.DirectorySeparatorChar;
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
string outPath = dir + darc.Files.FileNames[i];
|
||||
var fs = File.OpenRead(path);
|
||||
fs.Seek(darc.Files.Files[i].Offset, SeekOrigin.Begin);
|
||||
byte[] fileBuffer = new byte[darc.Files.Files[i].Length];
|
||||
fs.Read(fileBuffer, 0, fileBuffer.Length);
|
||||
fs.Close();
|
||||
File.WriteAllBytes(outPath, fileBuffer);
|
||||
}
|
||||
}
|
||||
ret += "Extracted " + extracted + " files";
|
||||
if (folder > 0)
|
||||
{
|
||||
ret += ", did not extract " + folder + " folders";
|
||||
}
|
||||
ret += "." + Environment.NewLine + "Open a .DARC/.SARC/.FARC/.GAR/Shuffle Archive file (or drag/drop).";
|
||||
}
|
||||
else if (farc.valid)
|
||||
{
|
||||
ret += "Header Offset: " + farc.HeaderOffset + Environment.NewLine;
|
||||
int extracted = 0;
|
||||
for (int i = 0; i < farc.Files.Files.Count; i++)
|
||||
{
|
||||
extracted++;
|
||||
string dir = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + "FARC_" + farc.FileName + Path.DirectorySeparatorChar;
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
string outPath = dir + farc.Files.FileNames[i];
|
||||
var fs = File.OpenRead(farc.FilePath + "\\" + farc.FileName + farc.Extension);
|
||||
fs.Seek(farc.Files.Files[i].Offset + farc.DataOffset, SeekOrigin.Begin);
|
||||
byte[] fileBuffer = new byte[farc.Files.Files[i].Length];
|
||||
fs.Read(fileBuffer, 0, fileBuffer.Length);
|
||||
fs.Close();
|
||||
File.WriteAllBytes(outPath, fileBuffer);
|
||||
}
|
||||
ret += "Extracted " + extracted + " files";
|
||||
ret += "." + Environment.NewLine + ".DARC/.FARC/.SARC/.GAR/Shuffle Archive file (or drag/drop).";
|
||||
}
|
||||
else if (gar.valid)
|
||||
{
|
||||
ret += "New GAR with " + gar.FileCount + " files." + Environment.NewLine;
|
||||
string dir = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + gar.FileName + Path.DirectorySeparatorChar;
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
for (int i = 0; i < gar.FileCount; i++)
|
||||
{
|
||||
var fs = File.OpenRead(path);
|
||||
fs.Seek(gar.Files[i].Offset, SeekOrigin.Begin);
|
||||
byte[] fileBuffer = new byte[gar.Files[i].Length];
|
||||
fs.Read(fileBuffer, 0, fileBuffer.Length);
|
||||
fs.Close();
|
||||
File.WriteAllBytes(dir + gar.Files[i].NameWithExtension, fileBuffer);
|
||||
ret += "Extracted " + gar.Files[i].NameWithExtension + " (Offset: " + gar.Files[i].Offset.ToString("X8") + ", Len: " + gar.Files[i].Length.ToString("X8") + ")." + Environment.NewLine;
|
||||
}
|
||||
ret += Environment.NewLine;
|
||||
}
|
||||
else if (sharc.valid)
|
||||
{
|
||||
ret += "New Shuffle Archive with " + sharc.FileCount + " files." + Environment.NewLine;
|
||||
string dir = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + sharc.FileName + "_" + Path.DirectorySeparatorChar;
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
Console.WriteLine("Making dir: " + dir);
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
|
||||
string diglen = "".PadLeft((int)(Math.Log10(sharc.FileCount) + 1), '0');
|
||||
for (int i = 0; i < sharc.FileCount; i++)
|
||||
{
|
||||
var fs = File.OpenRead(path);
|
||||
fs.Seek(sharc.Files[i].Offset, SeekOrigin.Begin);
|
||||
byte[] fileBuffer = new byte[sharc.Files[i].Length];
|
||||
fs.Read(fileBuffer, 0, fileBuffer.Length);
|
||||
fs.Close();
|
||||
uint check = 0;
|
||||
for (int j = 0; j < fileBuffer.Length; j += 4)
|
||||
check += BitConverter.ToUInt32(fileBuffer, j);
|
||||
Console.WriteLine(i.ToString(diglen) + ": " + check.ToString("X8"));
|
||||
File.WriteAllBytes(dir + i.ToString(diglen) + ".zip", fileBuffer);
|
||||
ret += "Extracted " + i.ToString(diglen) + " (Offset: " + sharc.Files[i].Offset.ToString("X8") + ", Len: " + sharc.Files[i].Length.ToString("X8") + ")." + Environment.NewLine;
|
||||
}
|
||||
ret += Environment.NewLine;
|
||||
}
|
||||
else if (sarc.valid)
|
||||
{
|
||||
ret = "New SARC with " + sarc.SFat.EntryCount + " files." + Environment.NewLine;
|
||||
string dir = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + sarc.FileName + "_" + Path.DirectorySeparatorChar;
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
Console.WriteLine("Making dir: " + dir);
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
|
||||
foreach (SFATEntry t in sarc.SFat.Entries)
|
||||
{
|
||||
var fs = File.OpenRead(path);
|
||||
uint FileLen = t.FileDataEnd - t.FileDataStart;
|
||||
string FileName = "";
|
||||
fs.Seek(t.FileDataStart + sarc.DataOffset, SeekOrigin.Begin);
|
||||
byte[] fileBuffer = new byte[FileLen];
|
||||
fs.Read(fileBuffer, 0, (int)FileLen);
|
||||
fs.Seek(sarc.SFnt.StringOffset, SeekOrigin.Begin);
|
||||
fs.Seek((t.FileNameOffset & 0x00FFFFFF) * 4, SeekOrigin.Current);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (char c = (char)fs.ReadByte(); c != 0; c = (char)fs.ReadByte())
|
||||
{
|
||||
sb.Append(c);
|
||||
}
|
||||
FileName = sb.ToString().Replace('/', Path.DirectorySeparatorChar);
|
||||
fs.Close();
|
||||
string FileDir = Path.GetDirectoryName(dir + FileName) + Path.DirectorySeparatorChar;
|
||||
if (!Directory.Exists(FileDir))
|
||||
{
|
||||
Console.WriteLine("Making dir: " + FileDir);
|
||||
Directory.CreateDirectory(FileDir);
|
||||
}
|
||||
File.WriteAllBytes(dir + FileName, fileBuffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = "Not a valid .DARC/.FARC/.SARC/.GAR/Shuffle Archive file";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Mini Packing Util
|
||||
internal static void packMini(string path, string ident, string fileName, string outExt = null, string outFolder = null)
|
||||
{
|
||||
if (outFolder == null) outFolder = path;
|
||||
if (outExt == null) outExt = ".bin";
|
||||
// Create new Binary with the relevant header bytes
|
||||
byte[] data = new byte[4];
|
||||
data[0] = (byte)ident[0];
|
||||
data[1] = (byte)ident[1];
|
||||
string[] files = Directory.GetFiles(path);
|
||||
Array.Copy(BitConverter.GetBytes((ushort)files.Length), 0, data, 2, 2);
|
||||
|
||||
int count = files.Length;
|
||||
int dataOffset = 4 + 4 + count * 4;
|
||||
|
||||
// Start the data filling.
|
||||
using (MemoryStream dataout = new MemoryStream())
|
||||
using (MemoryStream offsetMap = new MemoryStream())
|
||||
using (BinaryWriter bd = new BinaryWriter(dataout))
|
||||
using (BinaryWriter bo = new BinaryWriter(offsetMap))
|
||||
{
|
||||
// For each file...
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Write File Offset
|
||||
uint fileOffset = (uint)(dataout.Position + dataOffset);
|
||||
bo.Write(fileOffset);
|
||||
|
||||
// Write File to Stream
|
||||
bd.Write(File.ReadAllBytes(files[i]));
|
||||
|
||||
// Pad the Data MemoryStream with Zeroes until len%4=0;
|
||||
while (dataout.Length % 4 != 0)
|
||||
bd.Write((byte)0);
|
||||
// File Offset will be updated as the offset is based off of the Data length.
|
||||
|
||||
// Delete the File
|
||||
File.Delete(files[i]);
|
||||
}
|
||||
// Cap the File
|
||||
bo.Write((uint)(dataout.Position + dataOffset));
|
||||
|
||||
using (var newPack = File.Create(Path.Combine(outFolder, fileName + outExt)))
|
||||
using (var header = new MemoryStream(data))
|
||||
{
|
||||
header.WriteTo(newPack);
|
||||
offsetMap.WriteTo(newPack);
|
||||
dataout.WriteTo(newPack);
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static byte[] packMini(byte[][] fileData, string ident)
|
||||
{
|
||||
// Create new Binary with the relevant header bytes
|
||||
byte[] data = new byte[4];
|
||||
data[0] = (byte)ident[0];
|
||||
data[1] = (byte)ident[1];
|
||||
Array.Copy(BitConverter.GetBytes((ushort)fileData.Length), 0, data, 2, 2);
|
||||
|
||||
int count = fileData.Length;
|
||||
int dataOffset = 4 + 4 + count * 4;
|
||||
|
||||
// Start the data filling.
|
||||
using (MemoryStream dataout = new MemoryStream())
|
||||
using (MemoryStream offsetMap = new MemoryStream())
|
||||
using (BinaryWriter bd = new BinaryWriter(dataout))
|
||||
using (BinaryWriter bo = new BinaryWriter(offsetMap))
|
||||
{
|
||||
// For each file...
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Write File Offset
|
||||
uint fileOffset = (uint)(dataout.Position + dataOffset);
|
||||
bo.Write(fileOffset);
|
||||
|
||||
// Write File to Stream
|
||||
bd.Write(fileData[i]);
|
||||
|
||||
// Pad the Data MemoryStream with Zeroes until len%4=0;
|
||||
while (dataout.Length % 4 != 0)
|
||||
bd.Write((byte)0);
|
||||
// File Offset will be updated as the offset is based off of the Data length.
|
||||
}
|
||||
// Cap the File
|
||||
bo.Write((uint)(dataout.Position + dataOffset));
|
||||
|
||||
using (var newPack = new MemoryStream())
|
||||
using (var header = new MemoryStream(data))
|
||||
{
|
||||
header.WriteTo(newPack);
|
||||
offsetMap.WriteTo(newPack);
|
||||
dataout.WriteTo(newPack);
|
||||
return newPack.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static void unpackMini(string path, string ident, string outFolder = null)
|
||||
{
|
||||
if (outFolder == null) outFolder = Path.GetDirectoryName(path);
|
||||
|
||||
using (var s = new FileStream(path, FileMode.Open))
|
||||
using (var br = new BinaryReader(s))
|
||||
{
|
||||
string fx = new string(br.ReadChars(2));
|
||||
|
||||
if (fx != ident) return;
|
||||
|
||||
ushort count = br.ReadUInt16();
|
||||
string namePad = "D" + Math.Ceiling(Math.Log10(count));
|
||||
uint[] offsets = new uint[count + 1];
|
||||
for (int i = 0; i < count; i++)
|
||||
offsets[i] = br.ReadUInt32();
|
||||
|
||||
uint length = br.ReadUInt32();
|
||||
offsets[offsets.Length - 1] = length;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
br.BaseStream.Seek(offsets[i], SeekOrigin.Begin);
|
||||
using (MemoryStream dataout = new MemoryStream())
|
||||
{
|
||||
byte[] data = new byte[0];
|
||||
s.CopyTo(dataout, (int)offsets[i]);
|
||||
int len = (int)offsets[i + 1] - (int)offsets[i];
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
data = dataout.ToArray();
|
||||
Array.Resize(ref data, len);
|
||||
}
|
||||
string newFile = Path.Combine(outFolder, i.ToString(namePad) + ".bin");
|
||||
File.WriteAllBytes(newFile, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
File.Delete(path); // File is unpacked.
|
||||
}
|
||||
internal static byte[][] unpackMini(byte[] fileData, string ident)
|
||||
{
|
||||
using (var s = new MemoryStream(fileData))
|
||||
using (var br = new BinaryReader(s))
|
||||
{
|
||||
string fx = new string(br.ReadChars(2));
|
||||
|
||||
if (fx != ident) return null;
|
||||
|
||||
ushort count = br.ReadUInt16();
|
||||
byte[][] returnData = new byte[count][];
|
||||
|
||||
uint[] offsets = new uint[count + 1];
|
||||
for (int i = 0; i < count; i++)
|
||||
offsets[i] = br.ReadUInt32();
|
||||
|
||||
uint length = br.ReadUInt32();
|
||||
offsets[offsets.Length - 1] = length;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
br.BaseStream.Seek(offsets[i], SeekOrigin.Begin);
|
||||
using (MemoryStream dataout = new MemoryStream())
|
||||
{
|
||||
byte[] data = new byte[0];
|
||||
s.CopyTo(dataout, (int)offsets[i]);
|
||||
int len = (int)offsets[i + 1] - (int)offsets[i];
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
data = dataout.ToArray();
|
||||
Array.Resize(ref data, len);
|
||||
}
|
||||
returnData[i] = data;
|
||||
}
|
||||
}
|
||||
return returnData;
|
||||
}
|
||||
}
|
||||
|
||||
// Generic Utility
|
||||
internal static string FixMajoraChecksum(string path)
|
||||
{
|
||||
byte[] data = File.ReadAllBytes(path);
|
||||
Array.Copy(BitConverter.GetBytes((ushort)0), 0, data, 0x1A88, 2);
|
||||
CRC16 crc = new CRC16();
|
||||
ushort val = crc.ComputeChecksum(data);
|
||||
val ^= 0x903B;
|
||||
Array.Copy(BitConverter.GetBytes(val), 0, data, 0x1A88, 2);
|
||||
File.WriteAllBytes(path, data);
|
||||
return "Corrected Majora Checksum to " + val.ToString("X4");
|
||||
}
|
||||
internal static string ParseShuffleText(string path)
|
||||
{
|
||||
ShuffleText st = new ShuffleText
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(path),
|
||||
FilePath = Path.GetDirectoryName(path),
|
||||
Extension = Path.GetExtension(path)
|
||||
};
|
||||
Console.WriteLine(st.FilePath);
|
||||
BinaryReader br = new BinaryReader(File.OpenRead(path));
|
||||
br.BaseStream.Seek(0xC, SeekOrigin.Begin);
|
||||
uint StringDataLen = br.ReadUInt32();
|
||||
st.StringMetaOffset = br.ReadUInt32();
|
||||
st.StringMetaLen = br.ReadUInt32();
|
||||
st.StringCount = st.StringMetaLen / 4;
|
||||
Console.WriteLine(st.StringCount);
|
||||
br.BaseStream.Seek(st.StringMetaOffset, SeekOrigin.Begin);
|
||||
st.offsets = new List<uint>();
|
||||
for (int i = 0; i < st.StringCount; i++)
|
||||
{
|
||||
st.offsets.Add(br.ReadUInt32());
|
||||
}
|
||||
string ret = "Dumped Offsets.";
|
||||
st.strings = new List<string>();
|
||||
for (int i = 0; i < st.StringCount; i++)
|
||||
{
|
||||
br.BaseStream.Seek(st.offsets[i], SeekOrigin.Begin);
|
||||
uint len = (i < st.StringCount - 1)
|
||||
? st.offsets[i + 1] - st.offsets[i]
|
||||
: StringDataLen + 0x40 - st.offsets[i];
|
||||
byte[] data = br.ReadBytes((int)len);
|
||||
st.strings.Add(Encoding.Unicode.GetString(data).Replace((char)0, ' ').Replace((char)0xa, ' '));
|
||||
}
|
||||
ret += Environment.NewLine + "Dumped Strings.";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (string t in st.strings)
|
||||
sb.AppendLine(t);
|
||||
|
||||
string newfilename = st.FilePath + Path.DirectorySeparatorChar + st.FileName + ".txt";
|
||||
File.WriteAllText(newfilename, sb.ToString());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public struct FARC
|
||||
{
|
||||
public UInt32 Magic;
|
||||
public UInt32 SirMagic;
|
||||
public UInt32 SirOffset;
|
||||
public UInt32 HeaderOffset;
|
||||
public UInt32 MetaPointer; //from start of file
|
||||
public UInt32 NamesOffset;
|
||||
public UInt32 TableOffset; //from start of file
|
||||
public UInt32 DataOffset; //from start of file
|
||||
public UInt32 FileCount;
|
||||
public FARCFileTable Files;
|
||||
|
||||
public string FileName;
|
||||
public string FilePath;
|
||||
public string Extension;
|
||||
public Boolean valid;
|
||||
}
|
||||
public struct FARCFileTable
|
||||
{
|
||||
public List<FARCFile> Files;
|
||||
public List<string> FileNames;
|
||||
}
|
||||
public struct FARCFile
|
||||
{
|
||||
public uint NameOffset;
|
||||
public uint Offset;
|
||||
public uint Length;
|
||||
|
||||
public string Name;
|
||||
}
|
||||
|
||||
public class SARC
|
||||
{
|
||||
public string Signature;
|
||||
public ushort HeaderSize = 0x14;
|
||||
public ushort Endianness;
|
||||
public uint FileSize;
|
||||
public uint DataOffset;
|
||||
public uint Unknown;
|
||||
public SFAT SFat;
|
||||
public SFNT SFnt;
|
||||
|
||||
public string FileName;
|
||||
public string FilePath;
|
||||
public string Extension;
|
||||
public bool valid;
|
||||
}
|
||||
public class SFAT
|
||||
{
|
||||
public string Signature;
|
||||
public ushort HeaderSize;
|
||||
public ushort EntryCount;
|
||||
public uint HashMult;
|
||||
public List<SFATEntry> Entries;
|
||||
}
|
||||
public class SFATEntry
|
||||
{
|
||||
public uint FileNameHash;
|
||||
public uint FileNameOffset;
|
||||
public uint FileDataStart;
|
||||
public uint FileDataEnd;
|
||||
}
|
||||
public class SFNT
|
||||
{
|
||||
public string Signature;
|
||||
public ushort HeaderSize;
|
||||
public ushort Unknown;
|
||||
public uint StringOffset;
|
||||
}
|
||||
|
||||
public class ShuffleARC
|
||||
{
|
||||
public uint magic; //0xB
|
||||
public uint FileNameCheck;
|
||||
public uint unk;
|
||||
public uint unk2;
|
||||
public uint FileCount;
|
||||
public uint padding;
|
||||
public List<ShuffleFile> Files;
|
||||
|
||||
public string FileName;
|
||||
public string FilePath;
|
||||
public string Extension;
|
||||
public bool add100;
|
||||
public bool valid;
|
||||
}
|
||||
public class ShuffleFile
|
||||
{
|
||||
public uint Offset;
|
||||
public uint Length;
|
||||
}
|
||||
public class ShuffleText
|
||||
{
|
||||
public uint StringMetaOffset;
|
||||
public uint StringMetaLen;
|
||||
public uint StringCount;
|
||||
public List<uint> offsets;
|
||||
public List<String> strings;
|
||||
|
||||
public string FileName;
|
||||
public string FilePath;
|
||||
public string Extension;
|
||||
public bool valid;
|
||||
}
|
||||
|
||||
public class GAR
|
||||
{
|
||||
public uint Magic; //0x02524146 "GAR"
|
||||
public uint FileLength;
|
||||
public uint Unknown;
|
||||
public uint HeaderLength;
|
||||
public uint FileMetaOffset;
|
||||
public uint FileOffsetsOffset;
|
||||
public uint FileCountOffset;
|
||||
public uint CTXBOffset; //Filecount = (CTXBOffset-FileCountOffset)/4;
|
||||
public uint DataOffset;
|
||||
|
||||
public uint FileCount;
|
||||
public List<GARFile> Files;
|
||||
|
||||
public string FileName;
|
||||
public string FilePath;
|
||||
public string Extension;
|
||||
public bool valid;
|
||||
}
|
||||
public class GARFile
|
||||
{
|
||||
public string NameWithExtension;
|
||||
public string Name;
|
||||
public uint NOffset;
|
||||
public uint NWEOffset;
|
||||
public uint Offset;
|
||||
public uint Length;
|
||||
}
|
||||
|
||||
public struct DARC
|
||||
{
|
||||
public UInt32 HeaderOffset; // Where is header in file?
|
||||
|
||||
public UInt32 Magic; // 0x64617263 "darc"
|
||||
public UInt16 BOM; // 0xFFFE
|
||||
public UInt16 HeaderLength; // HeaderLength - 0x1C
|
||||
public UInt32 Unknown; // 0x10000000
|
||||
public UInt32 totalLength; // Total Length of file
|
||||
public UInt32 TableOffset; // Offset from Start of File
|
||||
public UInt32 TableLength; // Table Length
|
||||
public UInt32 DataOffset; // Data Offset
|
||||
|
||||
public FileTable Files;
|
||||
|
||||
public string FileName;
|
||||
public string FilePath;
|
||||
public string Extension;
|
||||
public Boolean valid;
|
||||
}
|
||||
public struct FileTable
|
||||
{
|
||||
public List<DarcFile> Files;
|
||||
public List<string> FileNames;
|
||||
}
|
||||
public struct DarcFile
|
||||
{
|
||||
public ushort NameOffset; //
|
||||
public byte Parent;
|
||||
public byte Folder;
|
||||
public uint Offset;
|
||||
public uint Length;
|
||||
|
||||
public string Name;
|
||||
}
|
||||
|
||||
public class CRC16
|
||||
{
|
||||
private const ushort polynomial = 0xA001;
|
||||
private ushort[] table = new ushort[256];
|
||||
|
||||
public ushort ComputeChecksum(byte[] bytes)
|
||||
{
|
||||
return bytes.Aggregate<byte, ushort>(0, (current, t) => (ushort) ((current >> 8) ^ table[(current ^ t)]));
|
||||
}
|
||||
|
||||
public byte[] ComputeChecksumBytes(byte[] bytes)
|
||||
{
|
||||
ushort crc = ComputeChecksum(bytes);
|
||||
return BitConverter.GetBytes(crc);
|
||||
}
|
||||
|
||||
public CRC16()
|
||||
{
|
||||
for (ushort i = 0; i < table.Length; ++i)
|
||||
{
|
||||
ushort value = 0;
|
||||
ushort temp = i;
|
||||
for (byte j = 0; j < 8; ++j)
|
||||
{
|
||||
if (((value ^ temp) & 0x0001) != 0)
|
||||
value = (ushort) ((value >> 1) ^ polynomial);
|
||||
else
|
||||
value >>= 1;
|
||||
|
||||
temp >>= 1;
|
||||
}
|
||||
table[i] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,7 +274,7 @@ public static bool garcUnpack(string garcPath, string outPath, bool skipDecompre
|
||||
|
||||
// Sub-classes
|
||||
#region GARC Class & Struct
|
||||
public class ARC
|
||||
public partial class ARC
|
||||
{
|
||||
public static GARC unpackGARC(string path)
|
||||
{
|
||||
|
||||
@@ -513,7 +513,7 @@ private void B_OPower_Click(object sender, EventArgs e)
|
||||
// Extra Tools
|
||||
private void L_SubTools_Click(object sender, EventArgs e)
|
||||
{
|
||||
new Tools().ShowDialog();
|
||||
new ToolsUI().ShowDialog();
|
||||
}
|
||||
|
||||
// GARC Requests
|
||||
|
||||
@@ -8,7 +8,7 @@ public partial class Moves : Form
|
||||
{
|
||||
public Moves()
|
||||
{
|
||||
Util.unpackMini(Directory.GetFiles("move")[0], "WD");
|
||||
ARC.unpackMini(Directory.GetFiles("move")[0], "WD");
|
||||
|
||||
movelist[0] = "";
|
||||
sortedmoves = (string[])movelist.Clone();
|
||||
@@ -162,7 +162,7 @@ private void setEntry()
|
||||
private void formClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
setEntry();
|
||||
Util.packMini("move", "WD", "0", ".bin", "move");
|
||||
ARC.packMini("move", "WD", "0", ".bin", "move");
|
||||
}
|
||||
|
||||
private void B_RandAll_Click(object sender, EventArgs e)
|
||||
@@ -186,12 +186,12 @@ private void B_RandAll_Click(object sender, EventArgs e)
|
||||
}
|
||||
internal static int[] getTypes()
|
||||
{
|
||||
Util.unpackMini(Directory.GetFiles("move")[0], "WD");
|
||||
ARC.unpackMini(Directory.GetFiles("move")[0], "WD");
|
||||
string[] f2 = Directory.GetFiles("move");
|
||||
int[] moveTypes = new int[f2.Length];
|
||||
for (int i = 0; i < f2.Length; i++)
|
||||
moveTypes[i] = File.ReadAllBytes(f2[i])[0];
|
||||
Util.packMini("move", "WD", "0", ".bin", "move");
|
||||
ARC.packMini("move", "WD", "0", ".bin", "move");
|
||||
return moveTypes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ private void getEntry()
|
||||
if (entry < 0) return;
|
||||
RTB_F.Text = RTB_O.Text = richTextBox2.Text = RTB_T.Text = string.Empty;
|
||||
byte[] raw = File.ReadAllBytes(filepaths[entry]);
|
||||
locationData = Util.unpackMini(raw, "ZO");
|
||||
locationData = ARC.unpackMini(raw, "ZO");
|
||||
if (locationData == null) return;
|
||||
|
||||
RichTextBox[] rtba = {RTB_MapInfo, RTB_OWSC, RTB_MapSC, RTB_Encounter, RTB_File5};
|
||||
|
||||
52
pk3DS/Tools/ToolsUI.Designer.cs
generated
52
pk3DS/Tools/ToolsUI.Designer.cs
generated
@@ -33,32 +33,37 @@ private void InitializeComponent()
|
||||
this.PB_BCLIM = new System.Windows.Forms.Panel();
|
||||
this.CHK_PNG = new System.Windows.Forms.CheckBox();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.panel1 = new System.Windows.Forms.Panel();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.comboBox1 = new System.Windows.Forms.ComboBox();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// PB_Unpack
|
||||
//
|
||||
this.PB_Unpack.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.PB_Unpack.Location = new System.Drawing.Point(12, 25);
|
||||
this.PB_Unpack.Name = "PB_Unpack";
|
||||
this.PB_Unpack.Size = new System.Drawing.Size(150, 67);
|
||||
this.PB_Unpack.Size = new System.Drawing.Size(300, 67);
|
||||
this.PB_Unpack.TabIndex = 0;
|
||||
//
|
||||
// L_DARCMini
|
||||
//
|
||||
this.L_DARCMini.AutoSize = true;
|
||||
this.L_DARCMini.Location = new System.Drawing.Point(12, 9);
|
||||
this.L_DARCMini.Location = new System.Drawing.Point(9, 9);
|
||||
this.L_DARCMini.Name = "L_DARCMini";
|
||||
this.L_DARCMini.Size = new System.Drawing.Size(109, 13);
|
||||
this.L_DARCMini.Size = new System.Drawing.Size(145, 13);
|
||||
this.L_DARCMini.TabIndex = 1;
|
||||
this.L_DARCMini.Text = "DARC && Mini Unpack";
|
||||
this.L_DARCMini.Text = "GARC, DARC && Mini Unpack";
|
||||
//
|
||||
// PB_BCLIM
|
||||
//
|
||||
this.PB_BCLIM.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.PB_BCLIM.Location = new System.Drawing.Point(15, 283);
|
||||
this.PB_BCLIM.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.PB_BCLIM.Location = new System.Drawing.Point(12, 226);
|
||||
this.PB_BCLIM.Name = "PB_BCLIM";
|
||||
this.PB_BCLIM.Size = new System.Drawing.Size(300, 67);
|
||||
this.PB_BCLIM.Size = new System.Drawing.Size(300, 124);
|
||||
this.PB_BCLIM.TabIndex = 1;
|
||||
this.PB_BCLIM.Paint += new System.Windows.Forms.PaintEventHandler(this.PB_BCLIM_Paint);
|
||||
//
|
||||
@@ -66,7 +71,7 @@ private void InitializeComponent()
|
||||
//
|
||||
this.CHK_PNG.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.CHK_PNG.AutoSize = true;
|
||||
this.CHK_PNG.Location = new System.Drawing.Point(218, 266);
|
||||
this.CHK_PNG.Location = new System.Drawing.Point(103, 209);
|
||||
this.CHK_PNG.Name = "CHK_PNG";
|
||||
this.CHK_PNG.Size = new System.Drawing.Size(97, 17);
|
||||
this.CHK_PNG.TabIndex = 2;
|
||||
@@ -76,17 +81,45 @@ private void InitializeComponent()
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(12, 267);
|
||||
this.label1.Location = new System.Drawing.Point(9, 210);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(88, 13);
|
||||
this.label1.TabIndex = 3;
|
||||
this.label1.Text = "BCLIM Converter";
|
||||
//
|
||||
// panel1
|
||||
//
|
||||
this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.panel1.Location = new System.Drawing.Point(168, 124);
|
||||
this.panel1.Name = "panel1";
|
||||
this.panel1.Size = new System.Drawing.Size(144, 67);
|
||||
this.panel1.TabIndex = 5;
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Location = new System.Drawing.Point(9, 108);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(77, 13);
|
||||
this.label4.TabIndex = 7;
|
||||
this.label4.Text = "Folder Repack";
|
||||
//
|
||||
// comboBox1
|
||||
//
|
||||
this.comboBox1.FormattingEnabled = true;
|
||||
this.comboBox1.Location = new System.Drawing.Point(12, 124);
|
||||
this.comboBox1.Name = "comboBox1";
|
||||
this.comboBox1.Size = new System.Drawing.Size(150, 21);
|
||||
this.comboBox1.TabIndex = 8;
|
||||
//
|
||||
// ToolsUI
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(324, 362);
|
||||
this.Controls.Add(this.comboBox1);
|
||||
this.Controls.Add(this.label4);
|
||||
this.Controls.Add(this.panel1);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.CHK_PNG);
|
||||
this.Controls.Add(this.PB_BCLIM);
|
||||
@@ -109,6 +142,9 @@ private void InitializeComponent()
|
||||
private System.Windows.Forms.Panel PB_BCLIM;
|
||||
private System.Windows.Forms.CheckBox CHK_PNG;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Panel panel1;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private System.Windows.Forms.ComboBox comboBox1;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,25 @@ private void tabMain_DragDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||||
string path = files[0]; // open first D&D
|
||||
openQuick(path);
|
||||
|
||||
if (sender == PB_Unpack)
|
||||
openARC(path);
|
||||
else if (sender == PB_BCLIM)
|
||||
openIMG(path);
|
||||
else if (sender == panel1)
|
||||
saveARC(path);
|
||||
}
|
||||
private void openQuick(string path, bool recursing = false)
|
||||
|
||||
private void saveARC(string path)
|
||||
{
|
||||
int type = comboBox1.SelectedIndex;
|
||||
Util.Alert("Not implemented." + Environment.NewLine + path);
|
||||
}
|
||||
private void openIMG(string path)
|
||||
{
|
||||
Util.Alert("Not implemented." + Environment.NewLine + path);
|
||||
}
|
||||
private void openARC(string path, bool recursing = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -52,13 +68,13 @@ private void openQuick(string path, bool recursing = false)
|
||||
if (fi.Length == length)
|
||||
{
|
||||
// Fetch Mini File Contents
|
||||
Util.unpackMini(path, fx, newFolder);
|
||||
ARC.unpackMini(path, fx, newFolder);
|
||||
// Recurse throught the extracted contents if they extract successfully
|
||||
if (Directory.Exists(newFolder))
|
||||
foreach (string file in Directory.GetFiles(newFolder))
|
||||
openQuick(file, true);
|
||||
openARC(file, true);
|
||||
}
|
||||
else if (false)
|
||||
else if (false) // DARC
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
179
pk3DS/Util.cs
179
pk3DS/Util.cs
@@ -596,184 +596,7 @@ internal static int IndexOfBytes(byte[] array, byte[] pattern, int startIndex, i
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Mini Packing Util
|
||||
internal static void packMini(string path, string ident, string fileName, string outExt = null, string outFolder = null)
|
||||
{
|
||||
if (outFolder == null) outFolder = path;
|
||||
if (outExt == null) outExt = ".bin";
|
||||
// Create new Binary with the relevant header bytes
|
||||
byte[] data = new byte[4];
|
||||
data[0] = (byte)ident[0];
|
||||
data[1] = (byte)ident[1];
|
||||
string[] files = Directory.GetFiles(path);
|
||||
Array.Copy(BitConverter.GetBytes((ushort)files.Length), 0, data, 2, 2);
|
||||
|
||||
int count = files.Length;
|
||||
int dataOffset = 4 + 4 + count * 4;
|
||||
|
||||
// Start the data filling.
|
||||
using (MemoryStream dataout = new MemoryStream())
|
||||
using (MemoryStream offsetMap = new MemoryStream())
|
||||
using (BinaryWriter bd = new BinaryWriter(dataout))
|
||||
using (BinaryWriter bo = new BinaryWriter(offsetMap))
|
||||
{
|
||||
// For each file...
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Write File Offset
|
||||
uint fileOffset = (uint)(dataout.Position + dataOffset);
|
||||
bo.Write(fileOffset);
|
||||
|
||||
// Write File to Stream
|
||||
bd.Write(File.ReadAllBytes(files[i]));
|
||||
|
||||
// Pad the Data MemoryStream with Zeroes until len%4=0;
|
||||
while (dataout.Length % 4 != 0)
|
||||
bd.Write((byte)0);
|
||||
// File Offset will be updated as the offset is based off of the Data length.
|
||||
|
||||
// Delete the File
|
||||
File.Delete(files[i]);
|
||||
}
|
||||
// Cap the File
|
||||
bo.Write((uint)(dataout.Position + dataOffset));
|
||||
|
||||
using (var newPack = File.Create(Path.Combine(outFolder, fileName + outExt)))
|
||||
using (var header = new MemoryStream(data))
|
||||
{
|
||||
header.WriteTo(newPack);
|
||||
offsetMap.WriteTo(newPack);
|
||||
dataout.WriteTo(newPack);
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static byte[] packMini(byte[][] fileData, string ident)
|
||||
{
|
||||
// Create new Binary with the relevant header bytes
|
||||
byte[] data = new byte[4];
|
||||
data[0] = (byte)ident[0];
|
||||
data[1] = (byte)ident[1];
|
||||
Array.Copy(BitConverter.GetBytes((ushort)fileData.Length), 0, data, 2, 2);
|
||||
|
||||
int count = fileData.Length;
|
||||
int dataOffset = 4 + 4 + count * 4;
|
||||
|
||||
// Start the data filling.
|
||||
using (MemoryStream dataout = new MemoryStream())
|
||||
using (MemoryStream offsetMap = new MemoryStream())
|
||||
using (BinaryWriter bd = new BinaryWriter(dataout))
|
||||
using (BinaryWriter bo = new BinaryWriter(offsetMap))
|
||||
{
|
||||
// For each file...
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Write File Offset
|
||||
uint fileOffset = (uint)(dataout.Position + dataOffset);
|
||||
bo.Write(fileOffset);
|
||||
|
||||
// Write File to Stream
|
||||
bd.Write(fileData[i]);
|
||||
|
||||
// Pad the Data MemoryStream with Zeroes until len%4=0;
|
||||
while (dataout.Length % 4 != 0)
|
||||
bd.Write((byte)0);
|
||||
// File Offset will be updated as the offset is based off of the Data length.
|
||||
}
|
||||
// Cap the File
|
||||
bo.Write((uint)(dataout.Position + dataOffset));
|
||||
|
||||
using (var newPack = new MemoryStream())
|
||||
using (var header = new MemoryStream(data))
|
||||
{
|
||||
header.WriteTo(newPack);
|
||||
offsetMap.WriteTo(newPack);
|
||||
dataout.WriteTo(newPack);
|
||||
return newPack.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static void unpackMini(string path, string ident, string outFolder = null)
|
||||
{
|
||||
if (outFolder == null) outFolder = Path.GetDirectoryName(path);
|
||||
|
||||
using (var s = new FileStream(path, FileMode.Open))
|
||||
using (var br = new BinaryReader(s))
|
||||
{
|
||||
string fx = new string(br.ReadChars(2));
|
||||
|
||||
if (fx != ident) return;
|
||||
|
||||
ushort count = br.ReadUInt16();
|
||||
string namePad = "D" + Math.Ceiling(Math.Log10(count));
|
||||
uint[] offsets = new uint[count + 1];
|
||||
for (int i = 0; i < count; i++)
|
||||
offsets[i] = br.ReadUInt32();
|
||||
|
||||
uint length = br.ReadUInt32();
|
||||
offsets[offsets.Length - 1] = length;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
br.BaseStream.Seek(offsets[i], SeekOrigin.Begin);
|
||||
using (MemoryStream dataout = new MemoryStream())
|
||||
{
|
||||
byte[] data = new byte[0];
|
||||
s.CopyTo(dataout, (int)offsets[i]);
|
||||
int len = (int)offsets[i + 1] - (int)offsets[i];
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
data = dataout.ToArray();
|
||||
Array.Resize(ref data, len);
|
||||
}
|
||||
string newFile = Path.Combine(outFolder, i.ToString(namePad) + ".bin");
|
||||
File.WriteAllBytes(newFile, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
File.Delete(path); // File is unpacked.
|
||||
}
|
||||
internal static byte[][] unpackMini(byte[] fileData, string ident)
|
||||
{
|
||||
using (var s = new MemoryStream(fileData))
|
||||
using (var br = new BinaryReader(s))
|
||||
{
|
||||
string fx = new string(br.ReadChars(2));
|
||||
|
||||
if (fx != ident) return null;
|
||||
|
||||
ushort count = br.ReadUInt16();
|
||||
byte[][] returnData = new byte[count][];
|
||||
|
||||
uint[] offsets = new uint[count + 1];
|
||||
for (int i = 0; i < count; i++)
|
||||
offsets[i] = br.ReadUInt32();
|
||||
|
||||
uint length = br.ReadUInt32();
|
||||
offsets[offsets.Length - 1] = length;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
br.BaseStream.Seek(offsets[i], SeekOrigin.Begin);
|
||||
using (MemoryStream dataout = new MemoryStream())
|
||||
{
|
||||
byte[] data = new byte[0];
|
||||
s.CopyTo(dataout, (int)offsets[i]);
|
||||
int len = (int)offsets[i + 1] - (int)offsets[i];
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
data = dataout.ToArray();
|
||||
Array.Resize(ref data, len);
|
||||
}
|
||||
returnData[i] = data;
|
||||
}
|
||||
}
|
||||
return returnData;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Misc
|
||||
internal static string getHexString(byte[] data)
|
||||
{
|
||||
|
||||
@@ -50,8 +50,10 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ARCUtil.cs" />
|
||||
<Compile Include="BLZ.cs" />
|
||||
<Compile Include="ExeFS.cs" />
|
||||
<Compile Include="png2bclim.cs" />
|
||||
<Compile Include="Randomizer.cs" />
|
||||
<Compile Include="RomFS.cs" />
|
||||
<Compile Include="Subforms\RSWE.cs">
|
||||
@@ -172,6 +174,12 @@
|
||||
<Compile Include="Subforms\XYWE.designer.cs">
|
||||
<DependentUpon>XYWE.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Tools\ToolsUI.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Tools\ToolsUI.Designer.cs">
|
||||
<DependentUpon>ToolsUI.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Util.cs" />
|
||||
<Compile Include="Subforms\xytext.cs">
|
||||
<SubType>Form</SubType>
|
||||
@@ -249,6 +257,9 @@
|
||||
<EmbeddedResource Include="Subforms\XYWE.resx">
|
||||
<DependentUpon>XYWE.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Tools\ToolsUI.resx">
|
||||
<DependentUpon>ToolsUI.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
|
||||
740
pk3DS/png2bclim.cs
Normal file
740
pk3DS/png2bclim.cs
Normal file
@@ -0,0 +1,740 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
|
||||
namespace pk3DS
|
||||
{
|
||||
class png2bclim
|
||||
{
|
||||
internal static void openFile(string path, bool autosave = false, bool crop = true, char format = 'X')
|
||||
{
|
||||
// Handle file
|
||||
if (!File.Exists(path)) throw new Exception("Can only accept files, not folders");
|
||||
string ext = Path.GetExtension(path);
|
||||
if (ext == ".png")
|
||||
makeBCLIM(path, format);
|
||||
else if (ext == ".bin" || ext == ".bclim")
|
||||
makeBMP(path, autosave, crop);
|
||||
}
|
||||
|
||||
internal static Image makeBCLIM(string path, char fc)
|
||||
{
|
||||
byte[] byteArray = File.ReadAllBytes(path);
|
||||
using (Stream BitmapStream = new MemoryStream(byteArray)) // Open the file, even if it is in use.
|
||||
{
|
||||
Image img = Image.FromStream(BitmapStream);
|
||||
Bitmap mBitmap = new Bitmap(img);
|
||||
// Pixel format acquired. Now to determine how we want to save the data.
|
||||
MemoryStream ms = new MemoryStream();
|
||||
int bclimformat = 7; // Init to default (for X)
|
||||
|
||||
if (fc == 'X')
|
||||
write16BitColorPalette(mBitmap, ref ms);
|
||||
else
|
||||
{
|
||||
bclimformat = Convert.ToInt16(fc.ToString(), 16);
|
||||
try
|
||||
{
|
||||
writeGeneric(bclimformat, mBitmap, ref ms);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.Media.SystemSounds.Beep.Play();
|
||||
System.Diagnostics.Debug.WriteLine(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
long datalength = ms.Length;
|
||||
// Write the CLIM + imag data.
|
||||
using (BinaryWriter bw = new BinaryWriter(ms))
|
||||
{
|
||||
bw.Write((uint)0x4D494C43); // CLIM
|
||||
bw.Write((ushort)0xFEFF); // BOM
|
||||
bw.Write((uint)0x14);
|
||||
bw.Write((ushort)0x0202); // 2 2
|
||||
bw.Write((uint)(datalength+0x28));
|
||||
bw.Write((uint)1);
|
||||
bw.Write((uint)0x67616D69);
|
||||
bw.Write((uint)0x10);
|
||||
bw.Write((ushort)mBitmap.Width);
|
||||
bw.Write((ushort)mBitmap.Height);
|
||||
bw.Write((uint)bclimformat);
|
||||
bw.Write((uint)datalength);
|
||||
}
|
||||
string fp = Path.GetFileNameWithoutExtension(path);
|
||||
fp = "new_" + fp.Substring(fp.IndexOf('_') + 1);
|
||||
string pp = Path.GetDirectoryName(path);
|
||||
string newPath = Path.Combine(pp, fp + ".bclim");
|
||||
File.WriteAllBytes(newPath, ms.ToArray());
|
||||
|
||||
return makeBMP(newPath);
|
||||
}
|
||||
}
|
||||
internal static Image makeBMP(string path, bool autosave = false, bool crop = true)
|
||||
{
|
||||
CLIM bclim = BCLIM.analyze(path);
|
||||
if (bclim.Magic != 0x4D494C43)
|
||||
{
|
||||
System.Media.SystemSounds.Beep.Play();
|
||||
return null;
|
||||
}
|
||||
|
||||
// Interpret data.
|
||||
int f = bclim.FileFormat;
|
||||
if (f > 13)
|
||||
{
|
||||
System.Media.SystemSounds.Exclamation.Play();
|
||||
return null;
|
||||
}
|
||||
|
||||
Bitmap img = new Bitmap(1,1);
|
||||
if (f == 7 && BitConverter.ToUInt16(bclim.Data, 0) == 2)
|
||||
// PKM XY Format 7 (Color Palette)
|
||||
img = getIMG_XY7(bclim);
|
||||
else if (f == 10 || f == 11) {
|
||||
Util.Alert("ETC Images not supported."); return null;
|
||||
}
|
||||
else
|
||||
img = getIMG(bclim);
|
||||
|
||||
Rectangle cropRect = new Rectangle(0, 0, bclim.Width, bclim.Height);
|
||||
Bitmap CropBMP = new Bitmap(cropRect.Width, cropRect.Height);
|
||||
using (Graphics g = Graphics.FromImage(CropBMP))
|
||||
{
|
||||
g.DrawImage(img,
|
||||
new Rectangle(0, 0, CropBMP.Width, CropBMP.Height),
|
||||
cropRect,
|
||||
GraphicsUnit.Pixel);
|
||||
}
|
||||
if (!autosave) return !crop ? img : CropBMP;
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
//error will throw from here
|
||||
CropBMP.Save(ms, ImageFormat.Png);
|
||||
byte[] data = ms.ToArray();
|
||||
File.WriteAllBytes(bclim.FilePath + "\\" + bclim.FileName + ".png", data);
|
||||
}
|
||||
return !crop ? img : CropBMP;
|
||||
}
|
||||
// Bitmap Data Writing
|
||||
internal static Bitmap getIMG(CLIM bclim)
|
||||
{
|
||||
// New Image
|
||||
Bitmap img = new Bitmap(nlpo2(gcm(bclim.Width,8)), nlpo2(gcm(bclim.Height,8)));
|
||||
int f = bclim.FileFormat;
|
||||
int area = img.Width * img.Height;
|
||||
if (f == 9 && area > bclim.Data.Length/4)
|
||||
{
|
||||
img = new Bitmap(gcm(bclim.Width, 8), gcm(bclim.Height, 8));
|
||||
area = img.Width * img.Height;
|
||||
}
|
||||
// Coordinates
|
||||
// Colors
|
||||
// Tiles Per Width
|
||||
int p = gcm(img.Width, 8) / 8;
|
||||
if (p == 0) p = 1;
|
||||
|
||||
// Build Image
|
||||
using (Stream BitmapStream = new MemoryStream(bclim.Data))
|
||||
using (BinaryReader br = new BinaryReader(BitmapStream))
|
||||
for (uint i = 0; i < area; i++) // for every pixel
|
||||
{
|
||||
uint x;
|
||||
uint y;
|
||||
d2xy(i % 64, out x, out y);
|
||||
uint tile = i / 64;
|
||||
|
||||
// Shift Tile Coordinate into Tilemap
|
||||
x += (uint)(tile % p) * 8;
|
||||
y += (uint)(tile / p) * 8;
|
||||
|
||||
// Get Color
|
||||
Color c;
|
||||
switch (f)
|
||||
{
|
||||
case 0x0: // L8 // 8bit/1 byte
|
||||
case 0x1: // A8
|
||||
case 0x2: // LA4
|
||||
c = DecodeColor(br.ReadByte(), f);
|
||||
break;
|
||||
case 0x3: // LA8 // 16bit/2 byte
|
||||
case 0x4: // HILO8
|
||||
case 0x5: // RGB565
|
||||
case 0x8: // RGBA4444
|
||||
case 0x7: // RGBA5551
|
||||
c = DecodeColor(br.ReadUInt16(), f);
|
||||
break;
|
||||
case 0x6: // RGB8: // 24bit
|
||||
byte[] data = br.ReadBytes(3); Array.Resize(ref data, 4);
|
||||
c = DecodeColor(BitConverter.ToUInt32(data, 0), f);
|
||||
break;
|
||||
case 0x9: // RGBA8888
|
||||
c = DecodeColor(br.ReadUInt32(), f);
|
||||
break;
|
||||
case 0xC: // L4
|
||||
case 0xD: // A4 // 4bit - Do 2 pixels at a time.
|
||||
uint val = br.ReadByte();
|
||||
img.SetPixel((int)x, (int)y, DecodeColor(val & 0xF, f)); // lowest bits for the low pixel
|
||||
i++; x++;
|
||||
c = DecodeColor(val >> 4, f); // highest bits for the high pixel
|
||||
break;
|
||||
default: throw new Exception("Invalid FileFormat.");
|
||||
}
|
||||
img.SetPixel((int)x, (int)y, c);
|
||||
}
|
||||
return img;
|
||||
}
|
||||
internal static Bitmap getIMG_XY7(CLIM bclim)
|
||||
{
|
||||
Bitmap img = new Bitmap(bclim.BaseSize, bclim.BaseSize);
|
||||
using (Stream BitmapStream = new MemoryStream(bclim.Data))
|
||||
using (BinaryReader br = new BinaryReader(BitmapStream))
|
||||
{
|
||||
// Fetch Color stuff.
|
||||
if (br.ReadUInt16() != 2) return null;
|
||||
ushort colors = br.ReadUInt16();
|
||||
Color[] ca = new Color[colors];
|
||||
for (int i = 0; i < colors; i++)
|
||||
ca[i] = DecodeColor(br.ReadUInt16(), 7);
|
||||
|
||||
// Coordinates
|
||||
// Colors
|
||||
// Tiles Per Width
|
||||
int p = gcm(img.Width, 8) / 8;
|
||||
if (p == 0) p = 1;
|
||||
|
||||
for (uint i = 0; i < bclim.BaseSize * bclim.BaseSize; i++) // for every pixel
|
||||
{
|
||||
uint x;
|
||||
uint y;
|
||||
d2xy(i % 64, out x, out y);
|
||||
uint tile = i / 64;
|
||||
|
||||
// Shift Tile Coordinate into Tilemap
|
||||
x += (uint)(tile % p) * 8;
|
||||
y += (uint)(tile / p) * 8;
|
||||
|
||||
byte val = br.ReadByte();
|
||||
if (colors <= 0x10) // Handle 2 pixels at a time
|
||||
{
|
||||
img.SetPixel((int)x, (int)y, ca[val >> 4]);
|
||||
x++; i++; val &= 0xF;
|
||||
img.SetPixel((int)x, (int)y, ca[val]);
|
||||
}
|
||||
else //1bpp instead of .5, handle 2 pixels at a time the same way for no reason
|
||||
{
|
||||
img.SetPixel((int)x, (int)y, ca[val]);
|
||||
x++; i++; val = br.ReadByte();
|
||||
img.SetPixel((int)x, (int)y, ca[val]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
// BCLIM Data Writing
|
||||
internal static int write16BitColorPalette(Bitmap img, ref MemoryStream ms)
|
||||
{
|
||||
using (Stream pixelcolors = new MemoryStream())
|
||||
using (BinaryWriter bz = new BinaryWriter(pixelcolors))
|
||||
{
|
||||
// Set up our basis.
|
||||
bool under16colors = false;
|
||||
int colors = getColorCount(img);
|
||||
Color[] pcs = new Color[colors];
|
||||
if (colors < 16) under16colors = true;
|
||||
uint div = 1;
|
||||
if (under16colors)
|
||||
div = 2;
|
||||
|
||||
if (colors > 70) throw new Exception("Too many colors");
|
||||
|
||||
// Set up a new reverse image to build into.
|
||||
int w = gcm(img.Width, 8);
|
||||
int h = gcm(img.Height, 8);
|
||||
w = Math.Max(nlpo2(w), nlpo2(h));
|
||||
h = w;
|
||||
byte[] pixelarray = new Byte[w * h];
|
||||
|
||||
const int colorformat = 2;
|
||||
int ctr = 1;
|
||||
|
||||
pcs[0] = Color.FromArgb(0, 0xFF, 0xFF, 0xFF);
|
||||
|
||||
int p = gcm(w, 8) / 8;
|
||||
if (p == 0) p = 1;
|
||||
int d = 0;
|
||||
for (uint i = 0; i < pixelarray.Length; i++)
|
||||
{
|
||||
d = (int)(i / div);
|
||||
// Get Tile Coordinate
|
||||
uint x;
|
||||
uint y;
|
||||
d2xy(i % 64, out x, out y);
|
||||
|
||||
// Get Shift Tile
|
||||
uint tile = i / 64;
|
||||
|
||||
// Shift Tile Coordinate into Tilemap
|
||||
x += (uint)(tile % p) * 8;
|
||||
y += (uint)(tile / p) * 8;
|
||||
if (x >= img.Width || y >= img.Height) // Don't try to access any pixel data outside of our bounds.
|
||||
{ i++; continue; } // Goto next tile.
|
||||
|
||||
// Get Color of Pixel
|
||||
Color c = img.GetPixel((int)x, (int)y);
|
||||
|
||||
// Color Table Building Logic
|
||||
int index = Array.IndexOf(pcs, c);
|
||||
if (c.A == 0) index = 0;
|
||||
if (index < 0) // If new color
|
||||
{ pcs[ctr] = c; index = ctr; ctr++; } // Add it to color list
|
||||
|
||||
// Add pixel to pixeldata
|
||||
if (under16colors) index = index << 4;
|
||||
pixelarray[i / div] = (byte)index;
|
||||
if (!under16colors) continue;
|
||||
|
||||
c = img.GetPixel((int)x + 1, (int)y);
|
||||
index = Array.IndexOf(pcs, c);
|
||||
if (c.A == 0) index = 0;
|
||||
if (index < 0) // If new color
|
||||
{ pcs[ctr] = c; index = ctr; ctr++; }
|
||||
pixelarray[i / div] |= (byte)(index);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Write Intro
|
||||
bz.Write((ushort)colorformat); bz.Write((ushort)ctr);
|
||||
// Write Colors
|
||||
for (int i = 0; i < ctr; i++)
|
||||
bz.Write(GetRGBA5551(pcs[i])); // Write byte array.
|
||||
// Write Pixel Data
|
||||
for (uint i = 0; i < d; i++)
|
||||
bz.Write(pixelarray[i]);
|
||||
// Write Padding
|
||||
while (pixelcolors.Length < nlpo2((int)pixelcolors.Length))
|
||||
bz.Write((byte)0);
|
||||
// Copy to main CLIM.
|
||||
pixelcolors.Position = 0; pixelcolors.CopyTo(ms);
|
||||
}
|
||||
return 7;
|
||||
}
|
||||
internal static void writeGeneric(int format, Bitmap img, ref MemoryStream ms)
|
||||
{
|
||||
int w = img.Width;
|
||||
int h = img.Height;
|
||||
|
||||
// square format. Yay.
|
||||
w = h = Math.Max(nlpo2(w),nlpo2(h));
|
||||
if (format == 0 && Math.Min(img.Width, img.Height) > 64)
|
||||
{
|
||||
w = nlpo2(img.Width);
|
||||
h = nlpo2(img.Height);
|
||||
}
|
||||
using (MemoryStream mz = new MemoryStream())
|
||||
using (BinaryWriter bz = new BinaryWriter(mz))
|
||||
{
|
||||
int p = gcm(w, 8) / 8;
|
||||
if (p == 0) p = 1;
|
||||
for (uint i = 0; i < w * h; i++)
|
||||
{
|
||||
uint x;
|
||||
uint y;
|
||||
d2xy(i % 64, out x, out y);
|
||||
|
||||
// Get Shift Tile
|
||||
uint tile = i / 64;
|
||||
|
||||
// Shift Tile Coordinate into Tilemap
|
||||
x += (uint)(tile % p) * 8;
|
||||
y += (uint)(tile / p) * 8;
|
||||
|
||||
// Don't write data
|
||||
Color c;
|
||||
if (x >= img.Width || y >= img.Height)
|
||||
{ c = Color.FromArgb(0, 0, 0, 0); }
|
||||
else
|
||||
{ c = img.GetPixel((int)x, (int)y); if (c.A == 0) c = Color.FromArgb(0, 86, 86, 86); }
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case 0: bz.Write(GetL8(c)); break; // L8
|
||||
case 1: bz.Write(GetA8(c)); break; // A8
|
||||
case 2: bz.Write(GetLA4(c)); break; // LA4(4)
|
||||
case 3: bz.Write(GetLA8(c)); break; // LA8(8)
|
||||
case 4: bz.Write(GetHILO8(c)); break; // HILO8
|
||||
case 5: bz.Write(GetRGB565(c)); break; // RGB565
|
||||
case 6:
|
||||
{
|
||||
bz.Write(c.B);
|
||||
bz.Write(c.G);
|
||||
bz.Write(c.R); break;
|
||||
}
|
||||
case 7: bz.Write(GetRGBA5551(c)); break; // RGBA5551
|
||||
case 8: bz.Write(GetRGBA4444(c)); break; // RGBA4444
|
||||
case 9: bz.Write(GetRGBA8888(c)); break; // RGBA8
|
||||
case 10: throw new Exception("ETC1 not supported.");
|
||||
case 11: throw new Exception("ETC1A4 not supported.");
|
||||
case 12:
|
||||
{
|
||||
byte val = (byte)(GetL8(c) / 0x11); // First Pix // L4
|
||||
{ c = img.GetPixel((int)x, (int)y); if (c.A == 0) c = Color.FromArgb(0, 0, 0, 0); }
|
||||
val |= (byte)((GetL8(c) / 0x11) << 4); i++;
|
||||
bz.Write(val); break;
|
||||
}
|
||||
case 13:
|
||||
{
|
||||
byte val = (byte)(GetA8(c) / 0x11); // First Pix // L4
|
||||
{ c = img.GetPixel((int)x, (int)y); }
|
||||
val |= (byte)((GetA8(c) / 0x11) << 4); i++;
|
||||
bz.Write(val); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (mz.Length < nlpo2((int)mz.Length)) // pad
|
||||
bz.Write((byte)0);
|
||||
mz.Position = 0;
|
||||
mz.CopyTo(ms);
|
||||
}
|
||||
}
|
||||
internal static int getColorCount(Bitmap img)
|
||||
{
|
||||
Color[] colors = new Color[img.Width * img.Height];
|
||||
int colorct = 1;
|
||||
|
||||
for (int i = 0; i < colors.Length; i++)
|
||||
{
|
||||
Color c = img.GetPixel(i % img.Width, i / img.Width);
|
||||
int index = Array.IndexOf(colors, c);
|
||||
if (c.A == 0) index = 0;
|
||||
if (index >= 0) continue;
|
||||
|
||||
colors[colorct] = c;
|
||||
colorct++;
|
||||
}
|
||||
return colorct;
|
||||
}
|
||||
|
||||
|
||||
internal static int[] Convert5To8 = { 0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39,
|
||||
0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B,
|
||||
0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD,
|
||||
0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF };
|
||||
|
||||
private static Color DecodeColor(uint val, int format)
|
||||
{
|
||||
int alpha = 0xFF, red, green, blue;
|
||||
switch (format)
|
||||
{
|
||||
case 0: // L8
|
||||
return Color.FromArgb(alpha, (byte)val, (byte)val, (byte)val);
|
||||
case 1: // A8
|
||||
return Color.FromArgb((byte)val, alpha, alpha, alpha);
|
||||
case 2: // LA4
|
||||
red = (byte)(val >> 4);
|
||||
alpha = (byte)(val & 0x0F);
|
||||
return Color.FromArgb(alpha, red, red, red);
|
||||
case 3: // LA8
|
||||
red = (byte)((val >> 8 & 0xFF));
|
||||
alpha = (byte)(val & 0xFF);
|
||||
return Color.FromArgb(alpha, red, red, red);
|
||||
case 4: // HILO8
|
||||
red = (byte)(val >> 8);
|
||||
green = (byte)(val & 0xFF);
|
||||
return Color.FromArgb(alpha, red, green, 0xFF);
|
||||
case 5: // RGB565
|
||||
red = Convert5To8[(val >> 11) & 0x1F];
|
||||
green = (byte)(((val >> 5) & 0x3F) * 4);
|
||||
blue = Convert5To8[val & 0x1F];
|
||||
return Color.FromArgb(alpha, red, green, blue);
|
||||
case 6: // RGB8
|
||||
red = (byte)((val >> 16) & 0xFF);
|
||||
green = (byte)((val >> 8) & 0xFF);
|
||||
blue = (byte)(val & 0xFF);
|
||||
return Color.FromArgb(alpha, red, green, blue);
|
||||
case 7: // RGBA5551
|
||||
red = Convert5To8[(val >> 11) & 0x1F];
|
||||
green = Convert5To8[(val >> 6) & 0x1F];
|
||||
blue = Convert5To8[(val >> 1) & 0x1F];
|
||||
alpha = (val & 0x0001) == 1 ? 0xFF : 0x00;
|
||||
return Color.FromArgb(alpha, red, green, blue);
|
||||
case 8: // RGBA4444
|
||||
alpha = (byte)(0x11 * (val & 0xf));
|
||||
red = (byte)(0x11 * ((val >> 12) & 0xf));
|
||||
green = (byte)(0x11 * ((val >> 8) & 0xf));
|
||||
blue = (byte)(0x11 * ((val >> 4) & 0xf));
|
||||
return Color.FromArgb(alpha, red, green, blue);
|
||||
case 9: // RGBA8888
|
||||
red = (byte)((val >> 24) & 0xFF);
|
||||
green = (byte)((val >> 16) & 0xFF);
|
||||
blue = (byte)((val >> 8) & 0xFF);
|
||||
alpha = (byte)(val & 0xFF);
|
||||
return Color.FromArgb(alpha, red, green, blue);
|
||||
// case 10:
|
||||
// case 11:
|
||||
case 12: // L4
|
||||
return Color.FromArgb(alpha, (byte)(val * 0x11), (byte)(val * 0x11), (byte)(val * 0x11));
|
||||
case 13: // A4
|
||||
return Color.FromArgb((byte)(val * 0x11), alpha, alpha, alpha);
|
||||
default:
|
||||
return Color.White;
|
||||
}
|
||||
}
|
||||
|
||||
// Color Conversion
|
||||
internal static byte GetL8(Color c)
|
||||
{
|
||||
byte red = c.R;
|
||||
byte green = c.G;
|
||||
byte blue = c.B;
|
||||
// Luma (Y’) = 0.299 R’ + 0.587 G’ + 0.114 B’ from wikipedia
|
||||
return (byte)(((0x4CB2 * red + 0x9691 * green + 0x1D3E * blue) >> 16) & 0xFF);
|
||||
} // L8
|
||||
internal static byte GetA8(Color c)
|
||||
{
|
||||
return c.A;
|
||||
} // A8
|
||||
internal static byte GetLA4(Color c)
|
||||
{
|
||||
return (byte)((c.A / 0x11) + (c.R / 0x11) << 4);
|
||||
} // LA4
|
||||
internal static ushort GetLA8(Color c)
|
||||
{
|
||||
return (ushort)((c.A) + ((c.R) << 8));
|
||||
} // LA8
|
||||
internal static ushort GetHILO8(Color c)
|
||||
{
|
||||
return (ushort)((c.G) + ((c.R) << 8));
|
||||
} // HILO8
|
||||
internal static ushort GetRGB565(Color c)
|
||||
{
|
||||
int val = 0;
|
||||
// val += c.A >> 8; // unused
|
||||
val += convert8to5(c.B) >> 3;
|
||||
val += (c.G >> 2) << 5;
|
||||
val += convert8to5(c.R) << 10;
|
||||
return (ushort)val;
|
||||
} // RGB565
|
||||
// RGB8
|
||||
internal static ushort GetRGBA5551(Color c)
|
||||
{
|
||||
int val = 0;
|
||||
val += (byte)(c.A > 0x80 ? 1 : 0);
|
||||
val += convert8to5(c.R) << 11;
|
||||
val += convert8to5(c.G) << 6;
|
||||
val += convert8to5(c.B) << 1;
|
||||
ushort v = (ushort)val;
|
||||
|
||||
return v;
|
||||
}// RGBA5551
|
||||
internal static ushort GetRGBA4444(Color c)
|
||||
{
|
||||
int val = 0;
|
||||
val += (c.A / 0x11);
|
||||
val += ((c.B / 0x11) << 4);
|
||||
val += ((c.G / 0x11) << 8);
|
||||
val += ((c.R / 0x11) << 12);
|
||||
return (ushort)val;
|
||||
}// RGBA4444
|
||||
internal static uint GetRGBA8888(Color c) // RGBA8888
|
||||
{
|
||||
uint val = 0;
|
||||
val += c.A;
|
||||
val += (uint)(c.B << 8);
|
||||
val += (uint)(c.G << 16);
|
||||
val += (uint)(c.R << 24);
|
||||
return val;
|
||||
}
|
||||
|
||||
// Unit Conversion
|
||||
internal static byte convert8to5(int colorval)
|
||||
{
|
||||
|
||||
byte[] Convert8to5 = { 0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39,
|
||||
0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B,
|
||||
0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD,
|
||||
0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF };
|
||||
byte i = 0;
|
||||
while (colorval > Convert8to5[i]) i++;
|
||||
return i;
|
||||
}
|
||||
internal static UInt32 DM2X(UInt32 code)
|
||||
{
|
||||
return C11(code >> 0);
|
||||
}
|
||||
internal static UInt32 DM2Y(UInt32 code)
|
||||
{
|
||||
return C11(code >> 1);
|
||||
}
|
||||
internal static UInt32 C11(UInt32 x)
|
||||
{
|
||||
x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
|
||||
x = (x ^ (x >> 1)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10
|
||||
x = (x ^ (x >> 2)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
|
||||
x = (x ^ (x >> 4)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
|
||||
x = (x ^ (x >> 8)) & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210
|
||||
return x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Greatest common multiple (to round up)
|
||||
/// </summary>
|
||||
/// <param name="n">Number to round-up.</param>
|
||||
/// <param name="m">Multiple to round-up to.</param>
|
||||
/// <returns>Rounded up number.</returns>
|
||||
internal static int gcm(int n, int m)
|
||||
{
|
||||
return ((n + m - 1) / m) * m;
|
||||
}
|
||||
/// <summary>
|
||||
/// Next Largest Power of 2
|
||||
/// </summary>
|
||||
/// <param name="x">Input to round up to next 2^n</param>
|
||||
/// <returns>2^n > x && x > 2^(n-1) </returns>
|
||||
internal static int nlpo2(int x)
|
||||
{
|
||||
x--; // comment out to always take the next biggest power of two, even if x is already a power of two
|
||||
x |= (x >> 1);
|
||||
x |= (x >> 2);
|
||||
x |= (x >> 4);
|
||||
x |= (x >> 8);
|
||||
x |= (x >> 16);
|
||||
return (x+1);
|
||||
}
|
||||
|
||||
// Morton Translation
|
||||
/// <summary>
|
||||
/// Combines X/Y Coordinates to a decimal ordinate.
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
internal static uint xy2d(uint x, uint y)
|
||||
{
|
||||
x &= 0x0000ffff;
|
||||
y &= 0x0000ffff;
|
||||
x |= (x << 8);
|
||||
y |= (y << 8);
|
||||
x &= 0x00ff00ff;
|
||||
y &= 0x00ff00ff;
|
||||
x |= (x << 4);
|
||||
y |= (y << 4);
|
||||
x &= 0x0f0f0f0f;
|
||||
y &= 0x0f0f0f0f;
|
||||
x |= (x << 2);
|
||||
y |= (y << 2);
|
||||
x &= 0x33333333;
|
||||
y &= 0x33333333;
|
||||
x |= (x << 1);
|
||||
y |= (y << 1);
|
||||
x &= 0x55555555;
|
||||
y &= 0x55555555;
|
||||
return x | (y << 1);
|
||||
}
|
||||
/// <summary>
|
||||
/// Decimal Ordinate In to X / Y Coordinate Out
|
||||
/// </summary>
|
||||
/// <param name="d">Loop integer which will be decoded to X/Y</param>
|
||||
/// <param name="x">Output X coordinate</param>
|
||||
/// <param name="y">Output Y coordinate</param>
|
||||
internal static void d2xy(uint d, out uint x, out uint y)
|
||||
{
|
||||
x = d;
|
||||
y = (x >> 1);
|
||||
x &= 0x55555555;
|
||||
y &= 0x55555555;
|
||||
x |= (x >> 1);
|
||||
y |= (y >> 1);
|
||||
x &= 0x33333333;
|
||||
y &= 0x33333333;
|
||||
x |= (x >> 2);
|
||||
y |= (y >> 2);
|
||||
x &= 0x0f0f0f0f;
|
||||
y &= 0x0f0f0f0f;
|
||||
x |= (x >> 4);
|
||||
y |= (y >> 4);
|
||||
x &= 0x00ff00ff;
|
||||
y &= 0x00ff00ff;
|
||||
x |= (x >> 8);
|
||||
y |= (y >> 8);
|
||||
x &= 0x0000ffff;
|
||||
y &= 0x0000ffff;
|
||||
}
|
||||
|
||||
public class BCLIM
|
||||
{
|
||||
public static CLIM analyze(string path)
|
||||
{
|
||||
CLIM bclim = new CLIM
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(path),
|
||||
FilePath = Path.GetDirectoryName(path),
|
||||
Extension = Path.GetExtension(path)
|
||||
};
|
||||
byte[] byteArray = File.ReadAllBytes(path);
|
||||
using (BinaryReader br = new BinaryReader(new MemoryStream(byteArray)))
|
||||
{
|
||||
br.BaseStream.Seek(br.BaseStream.Length - 0x28, SeekOrigin.Begin);
|
||||
bclim.Magic = br.ReadUInt32();
|
||||
|
||||
bclim.BOM = br.ReadUInt16();
|
||||
bclim.CLIMLength = br.ReadUInt32();
|
||||
bclim.TileWidth = 2 << br.ReadByte();
|
||||
bclim.TileHeight = 2 << br.ReadByte();
|
||||
bclim.totalLength = br.ReadUInt32();
|
||||
bclim.Count = br.ReadUInt32();
|
||||
|
||||
bclim.imag = br.ReadChars(4);
|
||||
bclim.imagLength = br.ReadUInt32();
|
||||
bclim.Width = br.ReadUInt16();
|
||||
bclim.Height = br.ReadUInt16();
|
||||
bclim.FileFormat = br.ReadInt32();
|
||||
bclim.dataLength = br.ReadUInt32();
|
||||
|
||||
bclim.BaseSize = Math.Max(nlpo2(bclim.Width), nlpo2(bclim.Height));
|
||||
|
||||
br.BaseStream.Seek(0, SeekOrigin.Begin);
|
||||
bclim.Data = br.ReadBytes((int)bclim.dataLength);
|
||||
|
||||
return bclim;
|
||||
}
|
||||
}
|
||||
}
|
||||
public struct CLIM
|
||||
{
|
||||
public UInt32 Magic; // CLIM = 0x4D494C43
|
||||
public UInt16 BOM; // 0xFFFE
|
||||
public UInt32 CLIMLength; // HeaderLength - 14
|
||||
public int TileWidth; // 1<<[[n]]
|
||||
public int TileHeight; // 1<<[[n]]
|
||||
public UInt32 totalLength; // Total Length of file
|
||||
public UInt32 Count; // "1" , guessing it's just Count.
|
||||
|
||||
public char[] imag; // imag = 0x67616D69
|
||||
public UInt32 imagLength; // HeaderLength - 10
|
||||
public UInt16 Width; // Final Dimensions
|
||||
public UInt16 Height; // Final Dimensions
|
||||
public Int32 FileFormat; // ??
|
||||
public UInt32 dataLength; // Pixel Data Region Length
|
||||
|
||||
public byte[] Data;
|
||||
|
||||
public Int32 BaseSize;
|
||||
|
||||
//// Contained Data
|
||||
//public int ColorFormat;
|
||||
//public int ColorCount;
|
||||
//public Color[] Colors;
|
||||
|
||||
//public int[] Pixels;
|
||||
|
||||
public string FileName;
|
||||
public string FilePath;
|
||||
public string Extension;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user