mirror of
https://github.com/WiiDatabase/nusdownloader.git
synced 2024-11-24 03:09:17 +01:00
401 lines
13 KiB
C#
401 lines
13 KiB
C#
|
/* This file is part of libWiiSharp
|
|||
|
* Copyright (C) 2009 Leathl
|
|||
|
*
|
|||
|
* libWiiSharp is free software: you can redistribute it and/or
|
|||
|
* modify it under the terms of the GNU General Public License as published
|
|||
|
* by the Free Software Foundation, either version 3 of the License, or
|
|||
|
* (at your option) any later version.
|
|||
|
*
|
|||
|
* libWiiSharp is distributed in the hope that it will be
|
|||
|
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|||
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
|
* GNU General Public License for more details.
|
|||
|
*
|
|||
|
* You should have received a copy of the GNU General Public License
|
|||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
*/
|
|||
|
|
|||
|
//Lz77 (de-)compression algorithm based on gbalzss by Andre Perrot (Thanks!)
|
|||
|
|
|||
|
using System;
|
|||
|
using System.IO;
|
|||
|
|
|||
|
namespace libWiiSharp
|
|||
|
{
|
|||
|
public class Lz77
|
|||
|
{
|
|||
|
private static uint lz77Magic = 0x4c5a3737;
|
|||
|
private const int N = 4096;
|
|||
|
private const int F = 18;
|
|||
|
private const int threshold = 2;
|
|||
|
private int[] leftSon = new int[N + 1];
|
|||
|
private int[] rightSon = new int[N + 257];
|
|||
|
private int[] dad = new int[N + 1];
|
|||
|
private ushort[] textBuffer = new ushort[N + 17];
|
|||
|
private int matchPosition = 0, matchLength = 0;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Lz77 Magic.
|
|||
|
/// </summary>
|
|||
|
public static uint Lz77Magic { get { return lz77Magic; } }
|
|||
|
|
|||
|
#region Public Functions
|
|||
|
/// <summary>
|
|||
|
/// Checks whether a file is Lz77 compressed or not.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static bool IsLz77Compressed(string file)
|
|||
|
{
|
|||
|
return IsLz77Compressed(File.ReadAllBytes(file));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Checks whether a file is Lz77 compressed or not.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static bool IsLz77Compressed(byte[] file)
|
|||
|
{
|
|||
|
Headers.HeaderType h = Headers.DetectHeader(file);
|
|||
|
return (Shared.Swap(BitConverter.ToUInt32(file, (int)h)) == lz77Magic) ;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Checks whether a file is Lz77 compressed or not.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static bool IsLz77Compressed(Stream file)
|
|||
|
{
|
|||
|
Headers.HeaderType h = Headers.DetectHeader(file);
|
|||
|
byte[] temp = new byte[4];
|
|||
|
file.Seek((long)h, SeekOrigin.Begin);
|
|||
|
file.Read(temp, 0, temp.Length);
|
|||
|
return (Shared.Swap(BitConverter.ToUInt32(temp, 0)) == lz77Magic);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Compresses a file using the Lz77 algorithm.
|
|||
|
/// </summary>
|
|||
|
/// <param name="inFile"></param>
|
|||
|
/// <param name="outFile"></param>
|
|||
|
public void Compress(string inFile, string outFile)
|
|||
|
{
|
|||
|
Stream compressedFile;
|
|||
|
|
|||
|
using (FileStream fsIn = new FileStream(inFile, FileMode.Open))
|
|||
|
compressedFile = compress(fsIn);
|
|||
|
|
|||
|
byte[] output = new byte[compressedFile.Length];
|
|||
|
compressedFile.Read(output, 0, output.Length);
|
|||
|
|
|||
|
if (File.Exists(outFile)) File.Delete(outFile);
|
|||
|
|
|||
|
using (FileStream fs = new FileStream(outFile, FileMode.Create))
|
|||
|
fs.Write(output, 0, output.Length);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Compresses the byte array using the Lz77 algorithm.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public byte[] Compress(byte[] file)
|
|||
|
{
|
|||
|
return ((MemoryStream)compress(new MemoryStream(file))).ToArray();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Compresses the stream using the Lz77 algorithm.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public Stream Compress(Stream file)
|
|||
|
{
|
|||
|
return compress(file);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Decompresses a file using the Lz77 algorithm.
|
|||
|
/// </summary>
|
|||
|
/// <param name="inFile"></param>
|
|||
|
/// <param name="outFile"></param>
|
|||
|
public void Decompress(string inFile, string outFile)
|
|||
|
{
|
|||
|
Stream compressedFile;
|
|||
|
|
|||
|
using (FileStream fsIn = new FileStream(inFile, FileMode.Open))
|
|||
|
compressedFile = decompress(fsIn);
|
|||
|
|
|||
|
byte[] output = new byte[compressedFile.Length];
|
|||
|
compressedFile.Read(output, 0, output.Length);
|
|||
|
|
|||
|
if (File.Exists(outFile)) File.Delete(outFile);
|
|||
|
|
|||
|
using (FileStream fs = new FileStream(outFile, FileMode.Create))
|
|||
|
fs.Write(output, 0, output.Length);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Decompresses the byte array using the Lz77 algorithm.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public byte[] Decompress(byte[] file)
|
|||
|
{
|
|||
|
return ((MemoryStream)decompress(new MemoryStream(file))).ToArray();
|
|||
|
}
|
|||
|
|
|||
|
public Stream Decompress(Stream file)
|
|||
|
{
|
|||
|
return decompress(file);
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Private Functions
|
|||
|
private Stream decompress(Stream inFile)
|
|||
|
{
|
|||
|
if (!Lz77.IsLz77Compressed(inFile)) return inFile;
|
|||
|
inFile.Seek(0, SeekOrigin.Begin);
|
|||
|
|
|||
|
int i, j, k, r, c, z;
|
|||
|
uint flags, decompressedSize, currentSize = 0;
|
|||
|
|
|||
|
Headers.HeaderType h = Headers.DetectHeader(inFile);
|
|||
|
|
|||
|
byte[] temp = new byte[8];
|
|||
|
inFile.Seek((int)h, SeekOrigin.Begin);
|
|||
|
inFile.Read(temp, 0, 8);
|
|||
|
|
|||
|
if (Shared.Swap(BitConverter.ToUInt32(temp, 0)) != lz77Magic)
|
|||
|
{ inFile.Dispose(); throw new Exception("Invaild Magic!"); }
|
|||
|
if (temp[4] != 0x10)
|
|||
|
{ inFile.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 (true)
|
|||
|
{
|
|||
|
flags <<= 1;
|
|||
|
z++;
|
|||
|
|
|||
|
if (z == 8)
|
|||
|
{
|
|||
|
if ((c = inFile.ReadByte()) == -1) break;
|
|||
|
|
|||
|
flags = (uint)c;
|
|||
|
z = 0;
|
|||
|
}
|
|||
|
|
|||
|
if ((flags & 0x80) == 0)
|
|||
|
{
|
|||
|
if ((c = inFile.ReadByte()) == inFile.Length - 1) break;
|
|||
|
if (currentSize < decompressedSize) outFile.WriteByte((byte)c);
|
|||
|
|
|||
|
textBuffer[r++] = (byte)c;
|
|||
|
r &= (N - 1);
|
|||
|
currentSize++;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if ((i = inFile.ReadByte()) == -1) break;
|
|||
|
if ((j = inFile.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;
|
|||
|
}
|
|||
|
|
|||
|
private Stream compress(Stream inFile)
|
|||
|
{
|
|||
|
if (Lz77.IsLz77Compressed(inFile)) return inFile;
|
|||
|
inFile.Seek(0, SeekOrigin.Begin);
|
|||
|
|
|||
|
int textSize = 0;
|
|||
|
int codeSize = 0;
|
|||
|
|
|||
|
int i, c, r, s, length, lastMatchLength, codeBufferPointer, mask;
|
|||
|
int[] codeBuffer = new int[17];
|
|||
|
|
|||
|
uint fileSize = ((Convert.ToUInt32(inFile.Length)) << 8) + 0x10;
|
|||
|
MemoryStream outFile = new MemoryStream();
|
|||
|
|
|||
|
outFile.Write(BitConverter.GetBytes(Shared.Swap(lz77Magic)), 0, 4);
|
|||
|
outFile.Write(BitConverter.GetBytes(fileSize), 0, 4);
|
|||
|
|
|||
|
InitTree();
|
|||
|
codeBuffer[0] = 0;
|
|||
|
codeBufferPointer = 1;
|
|||
|
mask = 0x80;
|
|||
|
s = 0;
|
|||
|
r = N - F;
|
|||
|
|
|||
|
for (i = s; i < r; i++) textBuffer[i] = 0xffff;
|
|||
|
|
|||
|
for (length = 0; length < F && (c = (int)inFile.ReadByte()) != -1; length++)
|
|||
|
textBuffer[r + length] = (ushort)c;
|
|||
|
|
|||
|
if ((textSize = length) == 0) return inFile;
|
|||
|
|
|||
|
for (i = 1; i <= F; i++) InsertNode(r - i);
|
|||
|
InsertNode(r);
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
if (matchLength > length) matchLength = length;
|
|||
|
|
|||
|
if (matchLength <= threshold)
|
|||
|
{
|
|||
|
matchLength = 1;
|
|||
|
codeBuffer[codeBufferPointer++] = textBuffer[r];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
codeBuffer[0] |= mask;
|
|||
|
|
|||
|
codeBuffer[codeBufferPointer++] = (char)
|
|||
|
(((r - matchPosition - 1) >> 8) & 0x0f) |
|
|||
|
((matchLength - (threshold + 1)) << 4);
|
|||
|
|
|||
|
codeBuffer[codeBufferPointer++] = (char)((r - matchPosition - 1) & 0xff);
|
|||
|
}
|
|||
|
|
|||
|
if ((mask >>= 1) == 0)
|
|||
|
{
|
|||
|
for (i = 0; i < codeBufferPointer; i++)
|
|||
|
outFile.WriteByte((byte)codeBuffer[i]);
|
|||
|
|
|||
|
codeSize += codeBufferPointer;
|
|||
|
codeBuffer[0] = 0; codeBufferPointer = 1;
|
|||
|
mask = 0x80;
|
|||
|
}
|
|||
|
|
|||
|
lastMatchLength = matchLength;
|
|||
|
for (i = 0; i < lastMatchLength && (c = (int)inFile.ReadByte()) != -1; i++)
|
|||
|
{
|
|||
|
DeleteNode(s);
|
|||
|
|
|||
|
textBuffer[s] = (ushort)c;
|
|||
|
if (s < F - 1) textBuffer[s + N] = (ushort)c;
|
|||
|
s = (s + 1) & (N - 1); r = (r + 1) & (N - 1);
|
|||
|
|
|||
|
InsertNode(r);
|
|||
|
}
|
|||
|
|
|||
|
while (i++ < lastMatchLength)
|
|||
|
{
|
|||
|
DeleteNode(s);
|
|||
|
|
|||
|
s = (s + 1) & (N - 1); r = (r + 1) & (N - 1);
|
|||
|
if (--length != 0) InsertNode(r);
|
|||
|
}
|
|||
|
} while (length > 0);
|
|||
|
|
|||
|
|
|||
|
if (codeBufferPointer > 1)
|
|||
|
{
|
|||
|
for (i = 0; i < codeBufferPointer; i++) outFile.WriteByte((byte)codeBuffer[i]);
|
|||
|
codeSize += codeBufferPointer;
|
|||
|
}
|
|||
|
|
|||
|
if (codeSize % 4 != 0)
|
|||
|
for (i = 0; i < 4 - (codeSize % 4); i++)
|
|||
|
outFile.WriteByte(0x00);
|
|||
|
|
|||
|
return outFile;
|
|||
|
}
|
|||
|
|
|||
|
private void InitTree()
|
|||
|
{
|
|||
|
int i;
|
|||
|
for (i = N + 1; i <= N + 256; i++) rightSon[i] = N;
|
|||
|
for (i = 0; i < N; i++) dad[i] = N;
|
|||
|
}
|
|||
|
|
|||
|
private void InsertNode(int r)
|
|||
|
{
|
|||
|
int i, p, cmp;
|
|||
|
cmp = 1;
|
|||
|
p = N + 1 + (textBuffer[r] == 0xffff ? 0 : textBuffer[r]);
|
|||
|
rightSon[r] = leftSon[r] = N; matchLength = 0;
|
|||
|
|
|||
|
for (; ; )
|
|||
|
{
|
|||
|
if (cmp >= 0)
|
|||
|
{
|
|||
|
if (rightSon[p] != N) p = rightSon[p];
|
|||
|
else { rightSon[p] = r; dad[r] = p; return; }
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (leftSon[p] != N) p = leftSon[p];
|
|||
|
else { leftSon[p] = r; dad[r] = p; return; }
|
|||
|
}
|
|||
|
|
|||
|
for (i = 1; i < F; i++)
|
|||
|
if ((cmp = textBuffer[r + i] - textBuffer[p + i]) != 0) break;
|
|||
|
|
|||
|
if (i > matchLength)
|
|||
|
{
|
|||
|
matchPosition = p;
|
|||
|
if ((matchLength = i) >= F) break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
dad[r] = dad[p]; leftSon[r] = leftSon[p]; rightSon[r] = rightSon[p];
|
|||
|
dad[leftSon[p]] = r; dad[rightSon[p]] = r;
|
|||
|
|
|||
|
if (rightSon[dad[p]] == p) rightSon[dad[p]] = r;
|
|||
|
else leftSon[dad[p]] = r;
|
|||
|
|
|||
|
dad[p] = N;
|
|||
|
}
|
|||
|
|
|||
|
private void DeleteNode(int p)
|
|||
|
{
|
|||
|
int q;
|
|||
|
|
|||
|
if (dad[p] == N) return;
|
|||
|
|
|||
|
if (rightSon[p] == N) q = leftSon[p];
|
|||
|
else if (leftSon[p] == N) q = rightSon[p];
|
|||
|
else
|
|||
|
{
|
|||
|
q = leftSon[p];
|
|||
|
|
|||
|
if (rightSon[q] != N)
|
|||
|
{
|
|||
|
do { q = rightSon[q]; } while (rightSon[q] != N);
|
|||
|
rightSon[dad[q]] = leftSon[q]; dad[leftSon[q]] = dad[q];
|
|||
|
leftSon[q] = leftSon[p]; dad[leftSon[p]] = q;
|
|||
|
}
|
|||
|
|
|||
|
rightSon[q] = rightSon[p]; dad[rightSon[p]] = q;
|
|||
|
}
|
|||
|
|
|||
|
dad[q] = dad[p];
|
|||
|
|
|||
|
if (rightSon[dad[p]] == p) rightSon[dad[p]] = q;
|
|||
|
else leftSon[dad[p]] = q;
|
|||
|
|
|||
|
dad[p] = N;
|
|||
|
}
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|