Add import functionality for the Wii's U8 Compressed Archive and Archived Font files. (#675)
* U8: Implemented LZ77 Type 11 & Type 10 Decompression. BXFNT: Implemented Wii's BRFNA files. * Removed accidental "using" inclusions in LZ77_WII
This commit is contained in:
parent
14fa40193a
commit
07c209bf17
Binary file not shown.
@ -7,6 +7,7 @@ using Toolbox;
|
||||
using System.Windows.Forms;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
using System.IO;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
@ -16,7 +17,7 @@ namespace FirstPlugin
|
||||
|
||||
public bool CanSave { get; set; }
|
||||
public string[] Description { get; set; } = new string[] { "U8" };
|
||||
public string[] Extension { get; set; } = new string[] { "*.u8"};
|
||||
public string[] Extension { get; set; } = new string[] { "*.u8", "*.arc", "*.cmparc"};
|
||||
public string FileName { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public IFileInfo IFileInfo { get; set; }
|
||||
@ -28,14 +29,30 @@ namespace FirstPlugin
|
||||
|
||||
private readonly uint BEMagic = 0x55AA382D;
|
||||
private readonly uint LEMagic = 0x2D38AA55;
|
||||
private int LZType = 0x0, LZSize = 0;
|
||||
|
||||
public bool Identify(System.IO.Stream stream)
|
||||
public bool Identify(Stream stream)
|
||||
{
|
||||
using (var reader = new Toolbox.Library.IO.FileReader(stream, true))
|
||||
using (var reader = new FileReader(stream, true))
|
||||
{
|
||||
reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian;
|
||||
byte LZCheck = reader.ReadByte();
|
||||
if (LZCheck == 0x11 || LZCheck == 0x10)
|
||||
{
|
||||
LZType = LZCheck;
|
||||
|
||||
// For the Wii's U8 ARC files compressed with LZ77 Type 10 or Type 11, the decompiled file size is written in little endian.
|
||||
reader.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian;
|
||||
LZSize = reader.ReadInt32();
|
||||
reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian;
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Position = 0;
|
||||
}
|
||||
|
||||
uint signature = reader.ReadUInt32();
|
||||
|
||||
reader.Position = 0;
|
||||
return signature == BEMagic || signature == LEMagic;
|
||||
}
|
||||
@ -65,9 +82,25 @@ namespace FirstPlugin
|
||||
|
||||
private bool IsBigEndian = false;
|
||||
|
||||
public void Load(System.IO.Stream stream)
|
||||
public void Load(Stream stream)
|
||||
{
|
||||
using (var reader = new FileReader(stream))
|
||||
SubStream sub;
|
||||
if (LZType == 0x11 || LZType == 0x10)
|
||||
{
|
||||
sub = new SubStream(stream, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
sub = null;
|
||||
}
|
||||
|
||||
Stream dataStream =
|
||||
LZType == 0x11 ? new MemoryStream(LZ77_WII.Decompress11(sub.ToArray(), LZSize)) :
|
||||
LZType == 0x10 ? new MemoryStream(LZ77_WII.Decompress10LZ(sub.ToArray(), LZSize)) : stream;
|
||||
|
||||
dataStream.Position = 0;
|
||||
|
||||
using (var reader = new FileReader(dataStream))
|
||||
{
|
||||
reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian;
|
||||
|
||||
|
@ -20,8 +20,8 @@ namespace FirstPlugin
|
||||
public FileType FileType { get; set; } = FileType.Font;
|
||||
|
||||
public bool CanSave { get; set; }
|
||||
public string[] Description { get; set; } = new string[] { "Cafe Font", "CTR Font", "Revolution Font" };
|
||||
public string[] Extension { get; set; } = new string[] { "*.bffnt", "*.bcfnt", "*.brfnt", };
|
||||
public string[] Description { get; set; } = new string[] { "Cafe Font", "CTR Font", "Revolution Font", "Revolution Archived Font" };
|
||||
public string[] Extension { get; set; } = new string[] { "*.bffnt", "*.bcfnt", "*.brfnt", "*.brfna" };
|
||||
public string FileName { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public IFileInfo IFileInfo { get; set; }
|
||||
@ -33,7 +33,8 @@ namespace FirstPlugin
|
||||
return reader.CheckSignature(4, "FFNT") ||
|
||||
reader.CheckSignature(4, "CFNT") ||
|
||||
reader.CheckSignature(4, "RFNT") ||
|
||||
reader.CheckSignature(4, "TNFR");
|
||||
reader.CheckSignature(4, "TNFR") ||
|
||||
reader.CheckSignature(4, "RFNA");
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +102,8 @@ namespace FirstPlugin
|
||||
}
|
||||
else if (bffnt.Platform == FFNT.PlatformType.Cafe)
|
||||
{
|
||||
for (int s = 0; s < tglp.SheetDataList.Count; s++) {
|
||||
for (int s = 0; s < tglp.SheetDataList.Count; s++)
|
||||
{
|
||||
var surface = new Gx2ImageBlock();
|
||||
surface.Text = $"Sheet_{s}";
|
||||
surface.Load(tglp, s);
|
||||
@ -206,6 +208,7 @@ namespace FirstPlugin
|
||||
public ushort HeaderSize;
|
||||
public uint Version { get; set; }
|
||||
|
||||
public GLGR GlyphGroup { get; set; }
|
||||
public FINF FontSection { get; set; }
|
||||
public FontKerningTable KerningTable { get; set; }
|
||||
|
||||
@ -226,7 +229,7 @@ namespace FirstPlugin
|
||||
reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian;
|
||||
|
||||
Signature = reader.ReadString(4, Encoding.ASCII);
|
||||
if (Signature != "FFNT" && Signature != "CFNT" && Signature != "RFNT" && Signature != "TNFR")
|
||||
if (Signature != "FFNT" && Signature != "CFNT" && Signature != "RFNT" && Signature != "TNFR" && Signature != "RFNA")
|
||||
throw new Exception($"Invalid signature {Signature}! Expected FFNT or CFNT or RFNT.");
|
||||
|
||||
BOM = reader.ReadUInt16();
|
||||
@ -237,7 +240,8 @@ namespace FirstPlugin
|
||||
|
||||
//Parse header first and check the version
|
||||
//Brfnt uses a slightly different header structure
|
||||
if (Signature == "RFNT" || Signature == "TNFR") {
|
||||
if (Signature == "RFNT" || Signature == "TNFR" || Signature == "RFNA")
|
||||
{
|
||||
Version = reader.ReadUInt16();
|
||||
uint FileSize = reader.ReadUInt32();
|
||||
HeaderSize = reader.ReadUInt16();
|
||||
@ -265,13 +269,24 @@ namespace FirstPlugin
|
||||
|
||||
if (Signature == "CFNT")
|
||||
Platform = PlatformType.Ctr;
|
||||
if (Signature == "RFNT" || Signature == "TNFR")
|
||||
if (Signature == "RFNT" || Signature == "TNFR" || Signature == "RFNA")
|
||||
Platform = PlatformType.Wii;
|
||||
|
||||
Console.WriteLine($"Platform {Platform}");
|
||||
|
||||
reader.Seek(HeaderSize, SeekOrigin.Begin);
|
||||
if (Signature == "RFNA")
|
||||
{
|
||||
GlyphGroup = new GLGR();
|
||||
GlyphGroup.Read(reader);
|
||||
// It's needed to take off 22 because of the included header length in SectionSize.
|
||||
reader.Seek(GlyphGroup.SectionSize - 0x16, SeekOrigin.Current);
|
||||
}
|
||||
FontSection = new FINF();
|
||||
if (GlyphGroup != null)
|
||||
{
|
||||
FontSection.GlyphGroup = GlyphGroup;
|
||||
}
|
||||
FontSection.Read(reader, this);
|
||||
|
||||
//Check for any unread blocks
|
||||
@ -347,7 +362,8 @@ namespace FirstPlugin
|
||||
|
||||
writer.SeekBegin(HeaderSize);
|
||||
FontSection.Write(writer, this);
|
||||
if (KerningTable != null) {
|
||||
if (KerningTable != null)
|
||||
{
|
||||
BlockCounter++;
|
||||
KerningTable.Write(writer, this);
|
||||
}
|
||||
@ -392,33 +408,33 @@ namespace FirstPlugin
|
||||
float YScale = (fontHeight / TextureGlyph.CellWidth);
|
||||
float height = (TextureGlyph.SheetHeight - 2) / TextureGlyph.ColumnCount;
|
||||
|
||||
/* int pos = 0;
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
char character = text[i];
|
||||
/* int pos = 0;
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
char character = text[i];
|
||||
|
||||
int charWidth = (int)FontInfo.DefaultCharWidth;
|
||||
int glyphWidth = (int)FontInfo.DefaultGlyphWidth;
|
||||
int leftWidth = (int)FontInfo.DefaultLeftWidth;
|
||||
int charWidth = (int)FontInfo.DefaultCharWidth;
|
||||
int glyphWidth = (int)FontInfo.DefaultGlyphWidth;
|
||||
int leftWidth = (int)FontInfo.DefaultLeftWidth;
|
||||
|
||||
if (FontInfo.CodeMapDictionary.ContainsKey(character))
|
||||
{
|
||||
var idx = FontInfo.CodeMapDictionary[character];
|
||||
if (idx == 0xFFFF) continue;
|
||||
var charWidthInfo = GetCharWidthInfoByIndex(FontInfo, (ushort)idx);
|
||||
if (FontInfo.CodeMapDictionary.ContainsKey(character))
|
||||
{
|
||||
var idx = FontInfo.CodeMapDictionary[character];
|
||||
if (idx == 0xFFFF) continue;
|
||||
var charWidthInfo = GetCharWidthInfoByIndex(FontInfo, (ushort)idx);
|
||||
|
||||
charWidth = charWidthInfo.CharWidth;
|
||||
glyphWidth = charWidthInfo.GlyphWidth;
|
||||
leftWidth = charWidthInfo.Left;
|
||||
}
|
||||
charWidth = charWidthInfo.CharWidth;
|
||||
glyphWidth = charWidthInfo.GlyphWidth;
|
||||
leftWidth = charWidthInfo.Left;
|
||||
}
|
||||
|
||||
|
||||
/* Bitmap b = new Bitmap(width, height);
|
||||
using (Graphics g = Graphics.FromImage(b))
|
||||
{
|
||||
g.DrawImage();
|
||||
}
|
||||
}*/
|
||||
/* Bitmap b = new Bitmap(width, height);
|
||||
using (Graphics g = Graphics.FromImage(b))
|
||||
{
|
||||
g.DrawImage();
|
||||
}
|
||||
}*/
|
||||
|
||||
if (bitmapFont == null)
|
||||
bitmapFont = GetBitmapFont(true);
|
||||
|
@ -23,6 +23,7 @@ namespace FirstPlugin
|
||||
public byte DefaultGlyphWidth { get; set; }
|
||||
public byte DefaultCharWidth { get; set; }
|
||||
public CharacterCode CharEncoding { get; set; }
|
||||
public GLGR GlyphGroup;
|
||||
public TGLP TextureGlyph;
|
||||
public CMAP CodeMap;
|
||||
public CWDH CharacterWidth;
|
||||
|
37
File_Format_Library/FileFormats/Font/BXFNT/GLGR.cs
Normal file
37
File_Format_Library/FileFormats/Font/BXFNT/GLGR.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library.IO;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public class GLGR
|
||||
{
|
||||
public uint SectionSize;
|
||||
public uint SheetSize;
|
||||
public uint HeaderSize = 0x20;
|
||||
public ushort GlyphsPerSheet;
|
||||
public ushort SetCount;
|
||||
public ushort SheetCount;
|
||||
public ushort CWDHCount;
|
||||
public ushort CMAPCount;
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
string Signature = reader.ReadString(4, Encoding.ASCII);
|
||||
if (Signature != "GLGR")
|
||||
{
|
||||
throw new Exception($"Invalid signature {Signature}! Expected GLGR.");
|
||||
}
|
||||
SectionSize = reader.ReadUInt32();
|
||||
SheetSize = reader.ReadUInt32();
|
||||
GlyphsPerSheet = reader.ReadUInt16();
|
||||
SetCount = reader.ReadUInt16();
|
||||
SheetCount = reader.ReadUInt16();
|
||||
CWDHCount = reader.ReadUInt16();
|
||||
CMAPCount = reader.ReadUInt16();
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ namespace FirstPlugin
|
||||
public byte CellWidth { get; set; }
|
||||
public byte CellHeight { get; set; }
|
||||
public byte MaxCharWidth { get; set; }
|
||||
public byte SheetCount { get; private set; }
|
||||
public ushort SheetCount { get; private set; }
|
||||
public uint SheetSize { get; set; }
|
||||
public ushort BaseLinePos { get; set; }
|
||||
public ushort Format { get; set; }
|
||||
@ -53,7 +53,16 @@ namespace FirstPlugin
|
||||
BaseLinePos = reader.ReadByte();
|
||||
MaxCharWidth = reader.ReadByte();
|
||||
SheetSize = reader.ReadUInt32();
|
||||
SheetCount = (byte)reader.ReadUInt16();
|
||||
SheetCount = reader.ReadUInt16();
|
||||
if (header.Signature == "RFNA")
|
||||
{
|
||||
reader.ReadByte(); // No clue what the value is for
|
||||
Format = reader.ReadByte();
|
||||
}
|
||||
else
|
||||
{
|
||||
Format = reader.ReadUInt16();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -61,9 +70,9 @@ namespace FirstPlugin
|
||||
MaxCharWidth = reader.ReadByte();
|
||||
SheetSize = reader.ReadUInt32();
|
||||
BaseLinePos = reader.ReadUInt16();
|
||||
Format = reader.ReadUInt16();
|
||||
}
|
||||
|
||||
Format = reader.ReadUInt16();
|
||||
RowCount = reader.ReadUInt16();
|
||||
ColumnCount = reader.ReadUInt16();
|
||||
SheetWidth = reader.ReadUInt16();
|
||||
@ -74,7 +83,18 @@ namespace FirstPlugin
|
||||
{
|
||||
for (int i = 0; i < SheetCount; i++)
|
||||
{
|
||||
SheetDataList.Add(reader.ReadBytes((int)SheetSize));
|
||||
byte[] decompedData;
|
||||
uint compSheetSize = 0;
|
||||
if (header.Signature == "RFNA") // .brfna files have their texture sheets compressed with Huffman.
|
||||
{
|
||||
compSheetSize = reader.ReadUInt32();
|
||||
decompedData = Huffman_WII.DecompressHuffman(reader.ReadBytes((int)compSheetSize), (int)SheetSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
decompedData = reader.ReadBytes((int)SheetSize);
|
||||
}
|
||||
SheetDataList.Add(decompedData);
|
||||
if (SheetDataList[i].Length != SheetSize)
|
||||
throw new Exception("SheetSize mis match!");
|
||||
}
|
||||
|
@ -273,6 +273,7 @@
|
||||
<Compile Include="FileFormats\Font\BXFNT\CMAP.cs" />
|
||||
<Compile Include="FileFormats\Font\BXFNT\FINF.cs" />
|
||||
<Compile Include="FileFormats\Font\BXFNT\FontKerningTable.cs" />
|
||||
<Compile Include="FileFormats\Font\BXFNT\GLGR.cs" />
|
||||
<Compile Include="FileFormats\Font\BXFNT\Images\CtrImageBlock.cs" />
|
||||
<Compile Include="FileFormats\Font\BXFNT\CWDH.cs" />
|
||||
<Compile Include="FileFormats\Font\BXFNT\Images\Gx2ImageBlock.cs" />
|
||||
|
260
Switch_Toolbox_Library/Compression/Huffman_WII.cs
Normal file
260
Switch_Toolbox_Library/Compression/Huffman_WII.cs
Normal file
@ -0,0 +1,260 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library.IO;
|
||||
|
||||
namespace Toolbox.Library
|
||||
{
|
||||
public class Huffman_WII
|
||||
{
|
||||
//Ported from
|
||||
//https://github.com/Barubary/dsdecmp/blob/4ddd87206bacf4ce7d803b40ff3bd2663327b083/CSharp/DSDecmp/Program.cs#L417
|
||||
//(Modified to use byte arrays instead of file paths)
|
||||
//Copyright (c) 2010 Nick Kraayenbrink
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in
|
||||
//all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
//THE SOFTWARE.
|
||||
public static byte[] DecompressHuffman(byte[] input, int decompSize)
|
||||
{
|
||||
|
||||
FileReader br = new FileReader(new MemoryStream(input), true);
|
||||
|
||||
byte firstByte = br.ReadByte();
|
||||
|
||||
int dataSize = firstByte & 0x0F;
|
||||
|
||||
if ((firstByte & 0xF0) != 0x20)
|
||||
throw new InvalidDataException(String.Format("Invalid huffman comressed file; invalid tag {0:x}", firstByte));
|
||||
|
||||
//Console.WriteLine("Data size: {0:x}", dataSize);
|
||||
if (dataSize != 8 && dataSize != 4)
|
||||
throw new InvalidDataException(String.Format("Unhandled dataSize {0:x}", dataSize));
|
||||
|
||||
int decomp_size = 0;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
decomp_size |= br.ReadByte() << (i * 8);
|
||||
}
|
||||
|
||||
byte treeSize = br.ReadByte();
|
||||
HuffTreeNode.maxInpos = 4 + (treeSize + 1) * 2;
|
||||
|
||||
Console.WriteLine("Tree Size: {0:x}", treeSize);
|
||||
Console.WriteLine("Tee end: 0x{0:X}", HuffTreeNode.maxInpos);
|
||||
|
||||
HuffTreeNode rootNode = new HuffTreeNode();
|
||||
rootNode.parseData(br);
|
||||
|
||||
br.BaseStream.Position = 4 + (treeSize + 1) * 2; // go to start of coded bitstream.
|
||||
// read all data
|
||||
uint[] indata = new uint[(br.BaseStream.Length - br.BaseStream.Position) / 4];
|
||||
for (int i = 0; i < indata.Length; i++)
|
||||
indata[i] = br.ReadUInt32();
|
||||
|
||||
long curr_size = 0;
|
||||
decomp_size *= dataSize == 8 ? 1 : 2;
|
||||
byte[] outdata = new byte[decomp_size];
|
||||
|
||||
int idx = -1;
|
||||
string codestr = "";
|
||||
LinkedList<byte> code = new LinkedList<byte>();
|
||||
int value;
|
||||
|
||||
// Overwriting the likely nonsense read with the proper intended texture size.
|
||||
decomp_size = decompSize;
|
||||
|
||||
while (curr_size < decomp_size)
|
||||
{
|
||||
try
|
||||
{
|
||||
string newstr = uint_to_bits(indata[++idx]);
|
||||
codestr += newstr;
|
||||
Console.WriteLine("next uint: " + newstr);
|
||||
}
|
||||
catch (IndexOutOfRangeException e)
|
||||
{
|
||||
throw new IndexOutOfRangeException("not enough data.", e);
|
||||
}
|
||||
while (codestr.Length > 0 && curr_size < decompSize) // Need to force stop at the max decompSize, otherwise we get IndexOutOfRange Exceptions.
|
||||
{
|
||||
code.AddFirst(byte.Parse(codestr[0] + ""));
|
||||
//Console.Write(code.First.Value);
|
||||
codestr = codestr.Remove(0, 1);
|
||||
if (rootNode.getValue(code.Last, out value))
|
||||
{
|
||||
//Console.WriteLine(" -> "+value.ToString("X"));
|
||||
try
|
||||
{
|
||||
outdata[curr_size++] = (byte)value;
|
||||
}
|
||||
catch (IndexOutOfRangeException ex)
|
||||
{
|
||||
if (code.First.Value != 0)
|
||||
throw ex;
|
||||
}
|
||||
code.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
br.Close();
|
||||
|
||||
byte[] realout;
|
||||
if (dataSize == 4)
|
||||
{
|
||||
realout = new byte[decomp_size / 2];
|
||||
for (int i = 0; i < decomp_size / 2; i++)
|
||||
{
|
||||
if ((outdata[i * 2] & 0xF0) > 0
|
||||
|| (outdata[i * 2 + 1] & 0xF0) > 0)
|
||||
throw new Exception("first 4 bits of data should be 0 if dataSize = 4");
|
||||
realout[i] = (byte)((outdata[i * 2] << 4) | outdata[i * 2 + 1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
realout = outdata;
|
||||
}
|
||||
Console.WriteLine("Huffman decompressed.");
|
||||
return realout;
|
||||
}
|
||||
|
||||
private static string byte_to_bits(byte b)
|
||||
{
|
||||
string o = "";
|
||||
for (int i = 0; i < 8; i++)
|
||||
o = (((b & (1 << i)) >> i) & 1) + o;
|
||||
return o;
|
||||
}
|
||||
private static string uint_to_bits(uint u)
|
||||
{
|
||||
string o = "";
|
||||
for (int i = 3; i > -1; i--)
|
||||
o += byte_to_bits((byte)((u & (0xFF << (i * 8))) >> (i * 8)));
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
//Ported from
|
||||
//https://github.com/Barubary/dsdecmp/blob/4ddd87206bacf4ce7d803b40ff3bd2663327b083/CSharp/DSDecmp/Program.cs#L1367
|
||||
class HuffTreeNode
|
||||
{
|
||||
internal static int maxInpos = 0;
|
||||
internal HuffTreeNode node0, node1;
|
||||
internal int data = -1; // [-1,0xFF]
|
||||
/// <summary>
|
||||
/// To get a value, provide the last node of a list of bytes < 2.
|
||||
/// the list will be read from back to front.
|
||||
/// </summary>
|
||||
internal bool getValue(LinkedListNode<byte> code, out int value)
|
||||
{
|
||||
value = data;
|
||||
if (code == null)
|
||||
return node0 == null && node1 == null && data >= 0;
|
||||
|
||||
if (code.Value > 1)
|
||||
throw new Exception(String.Format("the list should be a list of bytes < 2. got:{0:g}", code.Value));
|
||||
|
||||
byte c = code.Value;
|
||||
bool retVal;
|
||||
HuffTreeNode n = c == 0 ? node0 : node1;
|
||||
retVal = n != null && n.getValue(code.Previous, out value);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
internal int this[string code]
|
||||
{
|
||||
get
|
||||
{
|
||||
LinkedList<byte> c = new LinkedList<byte>();
|
||||
foreach (char ch in code)
|
||||
c.AddFirst((byte)ch);
|
||||
int val = 1;
|
||||
return this.getValue(c.Last, out val) ? val : -1;
|
||||
}
|
||||
}
|
||||
|
||||
internal void parseData(BinaryReader br)
|
||||
{
|
||||
/*
|
||||
* Tree Table (list of 8bit nodes, starting with the root node)
|
||||
Root Node and Non-Data-Child Nodes are:
|
||||
Bit0-5 Offset to next child node,
|
||||
Next child node0 is at (CurrentAddr AND NOT 1)+Offset*2+2
|
||||
Next child node1 is at (CurrentAddr AND NOT 1)+Offset*2+2+1
|
||||
Bit6 Node1 End Flag (1=Next child node is data)
|
||||
Bit7 Node0 End Flag (1=Next child node is data)
|
||||
Data nodes are (when End Flag was set in parent node):
|
||||
Bit0-7 Data (upper bits should be zero if Data Size is less than 8)
|
||||
*/
|
||||
this.node0 = new HuffTreeNode();
|
||||
this.node1 = new HuffTreeNode();
|
||||
long currPos = br.BaseStream.Position;
|
||||
byte b = br.ReadByte();
|
||||
long offset = b & 0x3F;
|
||||
bool end0 = (b & 0x80) > 0, end1 = (b & 0x40) > 0;
|
||||
|
||||
// parse data for node0
|
||||
br.BaseStream.Position = (currPos - (currPos & 1)) + offset * 2 + 2;
|
||||
if (br.BaseStream.Position < maxInpos)
|
||||
{
|
||||
if (end0)
|
||||
node0.data = br.ReadByte();
|
||||
else
|
||||
node0.parseData(br);
|
||||
}
|
||||
|
||||
// parse data for node1
|
||||
br.BaseStream.Position = (currPos - (currPos & 1)) + offset * 2 + 2 + 1;
|
||||
if (br.BaseStream.Position < maxInpos)
|
||||
{
|
||||
if (end1)
|
||||
node1.data = br.ReadByte();
|
||||
else
|
||||
node1.parseData(br);
|
||||
}
|
||||
|
||||
// reset position
|
||||
br.BaseStream.Position = currPos;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (data < 0 && node0 != null && node1 != null)
|
||||
return "<" + node0.ToString() + ", " + node1.ToString() + ">";
|
||||
else
|
||||
return String.Format("[{0:x}]", data);
|
||||
}
|
||||
|
||||
internal int Depth
|
||||
{
|
||||
get
|
||||
{
|
||||
if (data < 0)
|
||||
return 0;
|
||||
else
|
||||
return 1 + Math.Max(node0.Depth, node1.Depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -140,6 +140,64 @@ namespace Toolbox.Library
|
||||
return outdata;
|
||||
}
|
||||
|
||||
//Ported from
|
||||
//https://github.com/Barubary/dsdecmp/blob/master/Java/JavaDSDecmp.java#L27
|
||||
//Rewrote to C#
|
||||
public static byte[] Decompress10LZ(byte[] in_data, int decomp_size)
|
||||
{
|
||||
byte[] out_data = new byte[decomp_size];
|
||||
int curr_size = 0, flags, disp, n, b, cdest;
|
||||
bool flag;
|
||||
|
||||
var reader = new FileReader(new MemoryStream(in_data), true);
|
||||
|
||||
while (curr_size < decomp_size)
|
||||
{
|
||||
try { flags = reader.ReadByte(); }
|
||||
catch (EndOfStreamException ex) { throw ex; }
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
flag = (flags & (0x80 >> i)) > 0;
|
||||
if (flag)
|
||||
{
|
||||
disp = 0;
|
||||
try { b = reader.ReadByte(); }
|
||||
catch (EndOfStreamException ex) { throw new InvalidDataException("Incomplete data", ex); }
|
||||
n = b >> 4;
|
||||
disp = (b & 0x0F) << 8;
|
||||
try { disp |= reader.ReadByte(); }
|
||||
catch (EndOfStreamException ex) { throw new InvalidDataException("Incomplete data", ex); }
|
||||
n += 3;
|
||||
cdest = curr_size;
|
||||
Console.WriteLine(string.Format("disp: 0x{0:x}", disp));
|
||||
if (disp > curr_size) { throw new InvalidDataException("Cannot go back more than already written"); }
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
out_data[curr_size++] = out_data[cdest - disp - 1 + j];
|
||||
}
|
||||
if (curr_size > decomp_size) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
try { b = reader.ReadByte(); }
|
||||
catch(EndOfStreamException ex)
|
||||
{
|
||||
Console.Error.WriteLine("Incomplete data, " + ex);
|
||||
break;
|
||||
}
|
||||
try { out_data[curr_size++] = (byte)b; }
|
||||
catch(IndexOutOfRangeException ex)
|
||||
{ if (b == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out_data;
|
||||
}
|
||||
|
||||
|
||||
public static byte[] Decompress(byte[] input, bool useMagic = true)
|
||||
{
|
||||
|
@ -231,6 +231,7 @@
|
||||
<Compile Include="Compression\Formats\ZlibGZ.cs" />
|
||||
<Compile Include="Compression\Formats\Zstb.cs" />
|
||||
<Compile Include="Compression\Formats\Zstb_Kirby.cs" />
|
||||
<Compile Include="Compression\Huffman_WII.cs" />
|
||||
<Compile Include="Compression\LZ77_WII.cs" />
|
||||
<Compile Include="Compression\7ZIP\LZMA\LzmaBase.cs" />
|
||||
<Compile Include="Compression\7ZIP\LZMA\LzmaDecoder.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user