mirror of
https://github.com/KillzXGaming/Switch-Toolbox
synced 2024-11-22 12:33:12 +00:00
07c209bf17
* U8: Implemented LZ77 Type 11 & Type 10 Decompression. BXFNT: Implemented Wii's BRFNA files. * Removed accidental "using" inclusions in LZ77_WII
268 lines
10 KiB
C#
268 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using Toolbox.Library.IO;
|
|
|
|
namespace Toolbox.Library
|
|
{
|
|
public class LZ77_WII
|
|
{
|
|
private readonly static int N = 4096;
|
|
private readonly static ushort[] textBuffer = new ushort[N + 17];
|
|
private readonly static int F = 18;
|
|
private static readonly int threshold = 2;
|
|
|
|
//Ported from
|
|
//https://github.com/mistydemeo/quickbms/blob/5752a6a2a38e16211952553fcffa59570855ac42/included/nintendo.c#L58
|
|
// various code from DSDecmp: http://code.google.com/p/dsdecmp/
|
|
// original code of unlz77wii_raw10 from "Hector Martin <marcan@marcansoft.com>" http://wiibrew.org/wiki/Wii.py
|
|
// ported to C by Luigi Auriemma
|
|
public static byte[] Decompress11(byte[] input, int decomp_size)
|
|
{
|
|
int i, j, disp = 0, len = 0, cdest;
|
|
byte b1, bt, b2, b3, flags;
|
|
int threshold = 1;
|
|
bool flag = false;
|
|
|
|
int inputOffset = 0;
|
|
int curr_size = 0;
|
|
|
|
byte[] outdata = new byte[decomp_size];
|
|
|
|
while (curr_size < decomp_size)
|
|
{
|
|
if (inputOffset >= input.Length) break;
|
|
|
|
flags = input[inputOffset++];
|
|
for (i = 0; i < 8 && curr_size < decomp_size; i++)
|
|
{
|
|
flag = (flags & (0x80 >> i)) > 0;
|
|
if (flag)
|
|
{
|
|
if (inputOffset > input.Length) break;
|
|
b1 = input[inputOffset++];
|
|
|
|
switch ((int)(b1 >> 4))
|
|
{
|
|
//#region case 0
|
|
case 0:
|
|
{
|
|
// ab cd ef
|
|
// =>
|
|
// len = abc + 0x11 = bc + 0x11
|
|
// disp = def
|
|
|
|
len = b1 << 4;
|
|
if (inputOffset >= input.Length) break;
|
|
bt = input[inputOffset++];
|
|
len |= bt >> 4;
|
|
len += 0x11;
|
|
|
|
disp = (bt & 0x0F) << 8;
|
|
if (inputOffset > input.Length) break;
|
|
b2 = input[inputOffset++];
|
|
disp |= b2;
|
|
break;
|
|
}
|
|
//#endregion
|
|
|
|
//#region case 1
|
|
case 1:
|
|
{
|
|
// ab cd ef gh
|
|
// =>
|
|
// len = bcde + 0x111
|
|
// disp = fgh
|
|
// 10 04 92 3F => disp = 0x23F, len = 0x149 + 0x11 = 0x15A
|
|
|
|
if (inputOffset + 3 > input.Length) break;
|
|
bt = input[inputOffset++];
|
|
b2 = input[inputOffset++];
|
|
b3 = input[inputOffset++];
|
|
|
|
len = (b1 & 0xF) << 12; // len = b000
|
|
len |= bt << 4; // len = bcd0
|
|
len |= (b2 >> 4); // len = bcde
|
|
len += 0x111; // len = bcde + 0x111
|
|
disp = (b2 & 0x0F) << 8; // disp = f
|
|
disp |= b3; // disp = fgh
|
|
break;
|
|
}
|
|
//#endregion
|
|
|
|
//#region other
|
|
default:
|
|
{
|
|
// ab cd
|
|
// =>
|
|
// len = a + threshold = a + 1
|
|
// disp = bcd
|
|
|
|
len = (b1 >> 4) + threshold;
|
|
|
|
disp = (b1 & 0x0F) << 8;
|
|
if (inputOffset >= input.Length) break;
|
|
|
|
b2 = input[inputOffset++];
|
|
disp |= b2;
|
|
break;
|
|
}
|
|
//#endregion
|
|
}
|
|
|
|
if (disp > curr_size)
|
|
return null;
|
|
|
|
cdest = curr_size;
|
|
|
|
for (j = 0; j < len && curr_size < decomp_size; j++)
|
|
outdata[curr_size++] = outdata[cdest - disp - 1 + j];
|
|
|
|
if (curr_size > decomp_size)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (inputOffset >= input.Length) break;
|
|
outdata[curr_size++] = input[inputOffset++];
|
|
|
|
if (curr_size > decomp_size)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
if (input == null) throw new ArgumentNullException(nameof(input));
|
|
using (var reader = new FileReader(new MemoryStream(input), true))
|
|
{
|
|
reader.SetByteOrder(true);
|
|
reader.ReadSignature(4, "LZ77");
|
|
|
|
reader.Position = 0;
|
|
|
|
int i, j, k, r, c, z;
|
|
uint flags, decompressedSize, currentSize = 0;
|
|
|
|
byte[] temp = new byte[8];
|
|
reader.Read(temp, 0, 8);
|
|
|
|
if (temp[4] != 0x10)
|
|
{ reader.Dispose(); throw new Exception("Unsupported Compression Type!"); }
|
|
|
|
decompressedSize = (BitConverter.ToUInt32(temp, 4)) >> 8;
|
|
|
|
for (i = 0; i < N - F; i++) textBuffer[i] = 0xdf;
|
|
r = N - F; flags = 7; z = 7;
|
|
|
|
MemoryStream outFile = new MemoryStream();
|
|
while (outFile.Length < decompressedSize)
|
|
{
|
|
flags <<= 1;
|
|
z++;
|
|
|
|
if (z == 8)
|
|
{
|
|
if ((c = reader.ReadByte()) == -1) break;
|
|
|
|
flags = (uint)c;
|
|
z = 0;
|
|
}
|
|
|
|
if ((flags & 0x80) == 0)
|
|
{
|
|
if ((c = reader.ReadByte()) == reader.Length - 1) break;
|
|
if (currentSize < decompressedSize) outFile.WriteByte((byte)c);
|
|
|
|
textBuffer[r++] = (byte)c;
|
|
r &= (N - 1);
|
|
currentSize++;
|
|
}
|
|
else
|
|
{
|
|
if ((i = reader.ReadByte()) == -1) break;
|
|
if ((j = reader.ReadByte()) == -1) break;
|
|
|
|
j = j | ((i << 8) & 0xf00);
|
|
i = ((i >> 4) & 0x0f) + threshold;
|
|
for (k = 0; k <= i; k++)
|
|
{
|
|
c = textBuffer[(r - j - 1) & (N - 1)];
|
|
if (currentSize < decompressedSize) outFile.WriteByte((byte)c); textBuffer[r++] = (byte)c; r &= (N - 1); currentSize++;
|
|
}
|
|
}
|
|
}
|
|
return outFile.ToArray();
|
|
}
|
|
return input;
|
|
}
|
|
}
|
|
}
|