mirror of
https://github.com/kwsch/pk3DS.git
synced 2026-04-05 08:25:00 -05:00
186 lines
6.9 KiB
C#
186 lines
6.9 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace pk3DS.Core.CTR
|
|
{
|
|
public static class FileFormat
|
|
{
|
|
internal const string defaultExtension = "bin";
|
|
internal static readonly string[] validEXT = {"BCH",};
|
|
|
|
public static string Guess(string path)
|
|
{
|
|
string ext;
|
|
using (BinaryReader br = new BinaryReader(File.OpenRead(path)))
|
|
ext = Guess(br);
|
|
return ext;
|
|
}
|
|
public static string Guess(byte[] data)
|
|
{
|
|
string ext;
|
|
using (BinaryReader br = new BinaryReader(new MemoryStream(data)))
|
|
ext = Guess(br);
|
|
return ext;
|
|
}
|
|
public static string Guess(MemoryStream ms, bool start = true)
|
|
{
|
|
string ext;
|
|
using (BinaryReader br = new BinaryReader(ms))
|
|
ext = Guess(br, start);
|
|
return ext;
|
|
}
|
|
public static string Guess(BinaryReader br, bool start = true)
|
|
{
|
|
long position = br.BaseStream.Position; // Store current position to reset after.
|
|
|
|
if (start) // Seek to top of stream if requested
|
|
br.BaseStream.Position = 0;
|
|
|
|
// Guess Extension
|
|
string ext;
|
|
if (GuessMini(br, out ext))
|
|
Console.WriteLine("Mini Packed File detected, extension type " + ext);
|
|
else if (GuessHeaderedDARC(br, out ext))
|
|
Console.WriteLine("Headered DARC File detected, extension type " + ext);
|
|
else if (GuessBCLIM(br, out ext))
|
|
Console.WriteLine("BCLIM File detected, extension type " + ext);
|
|
else if (GuessLZ11(br, out ext))
|
|
Console.WriteLine("LZ11 Compressed File detected, extension type " + ext);
|
|
else if (Guess4CHAR(br, out ext))
|
|
Console.WriteLine("4CHAR File detected, extension type " + ext);
|
|
else if (Guess3CHAR(br, out ext))
|
|
Console.WriteLine("3CHAR File detected, extension type " + ext);
|
|
else ext = defaultExtension; // default
|
|
|
|
// Return BaseStream position to the start.
|
|
br.BaseStream.Position = position;
|
|
return "." + ext;
|
|
}
|
|
|
|
public static bool GuessMini(BinaryReader br, out string ext)
|
|
{
|
|
long position = br.BaseStream.Position; // Store current position to reset after.
|
|
ext = ""; // Reset extension
|
|
try
|
|
{
|
|
// check for 2char container extensions
|
|
ushort magic = br.ReadUInt16();
|
|
ushort count = br.ReadUInt16();
|
|
br.BaseStream.Position = 4 + 4 * count;
|
|
if (br.ReadUInt32() == br.BaseStream.Length)
|
|
{
|
|
ext += (char)magic & 0xFF;
|
|
ext += (char)magic << 8;
|
|
}
|
|
}
|
|
catch { }
|
|
// Return BaseStream position to the start.
|
|
br.BaseStream.Position = position;
|
|
|
|
return ext.Length > 0;
|
|
}
|
|
public static bool GuessHeaderedDARC(BinaryReader br, out string ext)
|
|
{
|
|
long position = br.BaseStream.Position; // Store current position to reset after.
|
|
ext = ""; // Reset extension
|
|
try
|
|
{
|
|
byte[] magic = Encoding.ASCII.GetBytes(br.ReadChars(4));
|
|
int count = BitConverter.ToUInt16(magic, 0);
|
|
br.BaseStream.Position = position + 4 + 0x40 * count;
|
|
uint tableval = br.ReadUInt32();
|
|
br.BaseStream.Position += 0x20 * tableval;
|
|
while (br.PeekChar() == 0) // seek forward
|
|
br.ReadByte();
|
|
if (br.ReadUInt32() == 0x63726164)
|
|
ext = "darc";
|
|
}
|
|
catch { }
|
|
// Return BaseStream position to the start.
|
|
br.BaseStream.Position = position;
|
|
|
|
return ext.Length > 0;
|
|
}
|
|
public static bool GuessBCLIM(BinaryReader br, out string ext)
|
|
{
|
|
long position = br.BaseStream.Position; // Store current position to reset after.
|
|
ext = ""; // Reset extension
|
|
try
|
|
{
|
|
br.BaseStream.Position = br.BaseStream.Length - 0x28;
|
|
if (br.ReadUInt32() == 0x4D494C43)
|
|
{
|
|
br.BaseStream.Position = br.BaseStream.Length - 0x4;
|
|
if (br.ReadUInt32() == br.BaseStream.Length - 0x28)
|
|
ext = "bclim";
|
|
}
|
|
}
|
|
catch { }
|
|
// Return BaseStream position to the start.
|
|
br.BaseStream.Position = position;
|
|
|
|
return ext.Length > 0;
|
|
}
|
|
public static bool GuessLZ11(BinaryReader br, out string ext)
|
|
{
|
|
long position = br.BaseStream.Position; // Store current position to reset after.
|
|
ext = ""; // Reset extension
|
|
try
|
|
{
|
|
int type = br.PeekChar();
|
|
if (type != 0x11)
|
|
return false;
|
|
byte[] sizeBytes = new byte[3];
|
|
br.Read(sizeBytes, 0, 3);
|
|
|
|
int decompressedSize = sizeBytes[0] | sizeBytes[1] << 8 | sizeBytes[2];
|
|
if (decompressedSize > br.BaseStream.Length && decompressedSize < br.BaseStream.Length * 10) // assuming 10x compression isn't feasible
|
|
ext = "lz"; // really weak LZ detection, at most 16MB
|
|
}
|
|
catch { }
|
|
br.BaseStream.Position = position;
|
|
return ext.Length > 0;
|
|
}
|
|
public static bool Guess4CHAR(BinaryReader br, out string ext)
|
|
{
|
|
long position = br.BaseStream.Position; // Store current position to reset after.
|
|
ext = ""; // Reset extension
|
|
try
|
|
{
|
|
byte[] magic = Encoding.ASCII.GetBytes(br.ReadChars(4));
|
|
|
|
Regex r = new Regex("^[a-zA-Z0-9]*$");
|
|
ext = Encoding.ASCII.GetString(magic);
|
|
// Return BaseStream position to the start.
|
|
br.BaseStream.Position = position;
|
|
|
|
return r.IsMatch(ext) && ext.Length == 4;
|
|
}
|
|
catch { }
|
|
br.BaseStream.Position = position;
|
|
return false;
|
|
}
|
|
public static bool Guess3CHAR(BinaryReader br, out string ext)
|
|
{
|
|
long position = br.BaseStream.Position; // Store current position to reset after.
|
|
ext = ""; // Reset extension
|
|
try
|
|
{
|
|
byte[] magic = Encoding.ASCII.GetBytes(br.ReadChars(3));
|
|
|
|
ext = Encoding.ASCII.GetString(magic);
|
|
// Return BaseStream position to the start.
|
|
br.BaseStream.Position = position;
|
|
|
|
return validEXT.Contains(ext);
|
|
}
|
|
catch { }
|
|
br.BaseStream.Position = position;
|
|
return false;
|
|
}
|
|
}
|
|
}
|