mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-07-16 02:56:44 -04:00
Added FSO.Files for use with the API server
Don't ask me. FreeSO is the prime example of dependency hell.
This commit is contained in:
parent
4b5e584eeb
commit
8fec258215
104 changed files with 14653 additions and 163 deletions
419
server/tso.files/FAR3/Decompresser.cs
Executable file
419
server/tso.files/FAR3/Decompresser.cs
Executable file
|
@ -0,0 +1,419 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace FSO.Files.FAR3
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a decompresser that can decompress files in a FAR3
|
||||
/// archive. If you have some kind of need to understand this code, go to:
|
||||
/// http://wiki.niotso.org/RefPack
|
||||
/// The code in this class was ported from DBPF4J:
|
||||
/// http://sc4dbpf4j.cvs.sourceforge.net/viewvc/sc4dbpf4j/DBPF4J/
|
||||
/// </summary>
|
||||
public class Decompresser
|
||||
{
|
||||
private long m_CompressedSize = 0;
|
||||
private long m_DecompressedSize = 0;
|
||||
|
||||
public long DecompressedSize
|
||||
{
|
||||
get { return m_DecompressedSize; }
|
||||
set { m_DecompressedSize = value; }
|
||||
}
|
||||
|
||||
public long CompressedSize
|
||||
{
|
||||
get { return m_CompressedSize; }
|
||||
set { m_CompressedSize = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies data from source to destination array.<br>
|
||||
/// The copy is byte by byte from srcPos to destPos and given length.
|
||||
/// </summary>
|
||||
/// <param name="Src">The source array.</param>
|
||||
/// <param name="SrcPos">The source Position.</param>
|
||||
/// <param name="Dest">The destination array.</param>
|
||||
/// <param name="DestPos">The destination Position.</param>
|
||||
/// <param name="Length">The length.</param>
|
||||
private void ArrayCopy2(byte[] Src, int SrcPos, ref byte[] Dest, int DestPos, long Length)
|
||||
{
|
||||
if (Dest.Length < DestPos + Length)
|
||||
{
|
||||
byte[] DestExt = new byte[(int)(DestPos + Length)];
|
||||
Array.Copy(Dest, 0, DestExt, 0, Dest.Length);
|
||||
Dest = DestExt;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Length/* - 1*/; i++)
|
||||
Dest[DestPos + i] = Src[SrcPos + i];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies data from array at destPos-srcPos to array at destPos.
|
||||
/// </summary>
|
||||
/// <param name="array">The array.</param>
|
||||
/// <param name="srcPos">The Position to copy from (reverse from end of array!)</param>
|
||||
/// <param name="destPos">The Position to copy to.</param>
|
||||
/// <param name="length">The length of data to copy.</param>
|
||||
private void OffsetCopy(ref byte[] array, int srcPos, int destPos, long length)
|
||||
{
|
||||
srcPos = destPos - srcPos;
|
||||
|
||||
if (array.Length < destPos + length)
|
||||
{
|
||||
byte[] NewArray = new byte[(int)(destPos + length)];
|
||||
Array.Copy(array, 0, NewArray, 0, array.Length);
|
||||
array = NewArray;
|
||||
}
|
||||
|
||||
for (int i = 0; i < length /*- 1*/; i++)
|
||||
{
|
||||
array[destPos + i] = array[srcPos + i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compresses data and returns it as an array of bytes.
|
||||
/// Assumes that the array of bytes passed contains
|
||||
/// uncompressed data.
|
||||
/// </summary>
|
||||
/// <param name="Data">The data to be compressed.</param>
|
||||
/// <returns>An array of bytes with compressed data.</returns>
|
||||
public byte[] Compress(byte[] Data)
|
||||
{
|
||||
// if data is big enough for compress
|
||||
if (Data.Length > 6)
|
||||
{
|
||||
// some Compression Data
|
||||
const int MAX_OFFSET = 0x20000;
|
||||
const int MAX_COPY_COUNT = 0x404;
|
||||
// used to finetune the lookup (small values increase the
|
||||
// compression for Big Files)
|
||||
const int QFS_MAXITER = 0x80;
|
||||
|
||||
// contains the latest offset for a combination of two
|
||||
// characters
|
||||
Dictionary<int, ArrayList> cmpmap2 = new Dictionary<int, ArrayList>();
|
||||
|
||||
// will contain the compressed data (maximal size =
|
||||
// uncompressedSize+MAX_COPY_COUNT)
|
||||
byte[] cData = new byte[Data.Length + MAX_COPY_COUNT];
|
||||
|
||||
// init some vars
|
||||
int writeIndex = 9; // leave 9 bytes for the header
|
||||
int lastReadIndex = 0;
|
||||
ArrayList indexList = null;
|
||||
int copyOffset = 0;
|
||||
int copyCount = 0;
|
||||
int index = -1;
|
||||
bool end = false;
|
||||
|
||||
// begin main compression loop
|
||||
while (index < Data.Length - 3)
|
||||
{
|
||||
// get all Compression Candidates (list of offsets for all
|
||||
// occurances of the current 3 bytes)
|
||||
do
|
||||
{
|
||||
index++;
|
||||
if (index >= Data.Length - 2)
|
||||
{
|
||||
end = true;
|
||||
break;
|
||||
}
|
||||
int mapindex = Data[index] + (Data[index + 1] << 8)
|
||||
+ (Data[index + 2] << 16);
|
||||
|
||||
indexList = cmpmap2[mapindex];
|
||||
if (indexList == null)
|
||||
{
|
||||
indexList = new ArrayList();
|
||||
cmpmap2.Add(mapindex, indexList);
|
||||
}
|
||||
indexList.Add(index);
|
||||
} while (index < lastReadIndex);
|
||||
if (end)
|
||||
break;
|
||||
|
||||
// find the longest repeating byte sequence in the index
|
||||
// List (for offset copy)
|
||||
int offsetCopyCount = 0;
|
||||
int loopcount = 1;
|
||||
while ((loopcount < indexList.Count) && (loopcount < QFS_MAXITER))
|
||||
{
|
||||
int foundindex = (int) indexList[(indexList.Count - 1) - loopcount];
|
||||
if ((index - foundindex) >= MAX_OFFSET)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
loopcount++;
|
||||
copyCount = 3;
|
||||
|
||||
while ((Data.Length > index + copyCount)&& (Data[index + copyCount] == Data[foundindex + copyCount]) && (copyCount < MAX_COPY_COUNT))
|
||||
{
|
||||
copyCount++;
|
||||
}
|
||||
|
||||
if (copyCount > offsetCopyCount)
|
||||
{
|
||||
offsetCopyCount = copyCount;
|
||||
copyOffset = index - foundindex;
|
||||
}
|
||||
}
|
||||
|
||||
// check if we can compress this
|
||||
// In FSH Tool stand additionally this:
|
||||
if (offsetCopyCount > Data.Length - index)
|
||||
{
|
||||
offsetCopyCount = index - Data.Length;
|
||||
}
|
||||
if (offsetCopyCount <= 2)
|
||||
{
|
||||
offsetCopyCount = 0;
|
||||
}
|
||||
else if ((offsetCopyCount == 3) && (copyOffset > 0x400))
|
||||
{ // 1024
|
||||
offsetCopyCount = 0;
|
||||
}
|
||||
else if ((offsetCopyCount == 4) && (copyOffset > 0x4000))
|
||||
{ // 16384
|
||||
offsetCopyCount = 0;
|
||||
}
|
||||
|
||||
// this is offset-compressable? so do the compression
|
||||
if (offsetCopyCount > 0)
|
||||
{
|
||||
// plaincopy
|
||||
|
||||
// In FSH Tool stand this (A):
|
||||
while (index - lastReadIndex >= 4)
|
||||
{
|
||||
copyCount = (index - lastReadIndex) / 4 - 1;
|
||||
if (copyCount > 0x1B)
|
||||
{
|
||||
copyCount = 0x1B;
|
||||
}
|
||||
cData[writeIndex++] = (byte)(0xE0 + copyCount);
|
||||
copyCount = 4 * copyCount + 4;
|
||||
|
||||
ArrayCopy2(Data, lastReadIndex, ref cData, writeIndex, copyCount);
|
||||
lastReadIndex += copyCount;
|
||||
writeIndex += copyCount;
|
||||
}
|
||||
|
||||
// offsetcopy
|
||||
copyCount = index - lastReadIndex;
|
||||
copyOffset--;
|
||||
if ((offsetCopyCount <= 0x0A) && (copyOffset < 0x400))
|
||||
{
|
||||
cData[writeIndex++] = (byte) (((copyOffset >> 8) << 5)
|
||||
+ ((offsetCopyCount - 3) << 2) + copyCount);
|
||||
cData[writeIndex++] = (byte)(copyOffset & 0xff);
|
||||
}
|
||||
else if ((offsetCopyCount <= 0x43) && (copyOffset < 0x4000))
|
||||
{
|
||||
cData[writeIndex++] = (byte)(0x80 + (offsetCopyCount - 4));
|
||||
cData[writeIndex++] = (byte) ((copyCount << 6) + (copyOffset >> 8));
|
||||
cData[writeIndex++] = (byte) (copyOffset & 0xff);
|
||||
}
|
||||
else if ((offsetCopyCount <= MAX_COPY_COUNT) && (copyOffset < MAX_OFFSET))
|
||||
{
|
||||
cData[writeIndex++] = (byte)(0xc0
|
||||
+ ((copyOffset >> 16) << 4)
|
||||
+ (((offsetCopyCount - 5) >> 8) << 2) + copyCount);
|
||||
cData[writeIndex++] = (byte)((copyOffset >> 8) & 0xff);
|
||||
cData[writeIndex++] = (byte)(copyOffset & 0xff);
|
||||
cData[writeIndex++] = (byte)((offsetCopyCount - 5) & 0xff);
|
||||
}
|
||||
|
||||
// do the offset copy
|
||||
ArrayCopy2(Data, lastReadIndex, ref cData, writeIndex, copyCount);
|
||||
writeIndex += copyCount;
|
||||
lastReadIndex += copyCount;
|
||||
lastReadIndex += offsetCopyCount;
|
||||
}
|
||||
}
|
||||
|
||||
// add the End Record
|
||||
index = Data.Length;
|
||||
// in FSH Tool stand the same as above (A)
|
||||
while (index - lastReadIndex >= 4)
|
||||
{
|
||||
copyCount = (index - lastReadIndex) / 4 - 1;
|
||||
|
||||
if (copyCount > 0x1B)
|
||||
copyCount = 0x1B;
|
||||
|
||||
cData[writeIndex++] = (byte)(0xE0 + copyCount);
|
||||
copyCount = 4 * copyCount + 4;
|
||||
|
||||
ArrayCopy2(Data, lastReadIndex, ref cData, writeIndex, copyCount);
|
||||
lastReadIndex += copyCount;
|
||||
writeIndex += copyCount;
|
||||
}
|
||||
|
||||
copyCount = index - lastReadIndex;
|
||||
cData[writeIndex++] = (byte) (0xfc + copyCount);
|
||||
ArrayCopy2(Data, lastReadIndex, ref cData, writeIndex, copyCount);
|
||||
writeIndex += copyCount;
|
||||
lastReadIndex += copyCount;
|
||||
|
||||
MemoryStream DataStream = new MemoryStream();
|
||||
BinaryWriter Writer = new BinaryWriter(DataStream);
|
||||
|
||||
// write the header for the compressed data
|
||||
// set the compressed size
|
||||
Writer.Write((uint)writeIndex);
|
||||
m_CompressedSize = writeIndex;
|
||||
// set the MAGICNUMBER
|
||||
Writer.Write((ushort)0xFB10);
|
||||
// set the decompressed size
|
||||
byte[] revData = BitConverter.GetBytes(Data.Length);
|
||||
Writer.Write((revData[2] << 16) | (revData[1] << 8) | revData[0]);
|
||||
Writer.Write(cData);
|
||||
|
||||
//Avoid nasty swearing here!
|
||||
Writer.Flush();
|
||||
|
||||
m_DecompressedSize = Data.Length;
|
||||
|
||||
return DataStream.ToArray();
|
||||
}
|
||||
|
||||
return Data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses data and returns it as an
|
||||
/// uncompressed array of bytes.
|
||||
/// </summary>
|
||||
/// <param name="Data">The data to decompress.</param>
|
||||
/// <returns>An uncompressed array of bytes.</returns>
|
||||
public byte[] Decompress(byte[] Data)
|
||||
{
|
||||
|
||||
MemoryStream MemData = new MemoryStream(Data);
|
||||
BinaryReader Reader = new BinaryReader(MemData);
|
||||
|
||||
if (Data.Length > 6)
|
||||
{
|
||||
byte[] DecompressedData = new byte[(int)m_DecompressedSize];
|
||||
int DataPos = 0;
|
||||
|
||||
int Pos = 0;
|
||||
long Control1 = 0;
|
||||
|
||||
while (Control1 != 0xFC && Pos < Data.Length)
|
||||
{
|
||||
Control1 = Data[Pos];
|
||||
Pos++;
|
||||
|
||||
if (Pos == Data.Length)
|
||||
break;
|
||||
|
||||
if (Control1 >= 0 && Control1 <= 127)
|
||||
{
|
||||
// 0x00 - 0x7F
|
||||
long control2 = Data[Pos];
|
||||
Pos++;
|
||||
long numberOfPlainText = (Control1 & 0x03);
|
||||
ArrayCopy2(Data, Pos, ref DecompressedData, DataPos, numberOfPlainText);
|
||||
DataPos += (int)numberOfPlainText;
|
||||
Pos += (int)numberOfPlainText;
|
||||
|
||||
if (DataPos == (DecompressedData.Length))
|
||||
break;
|
||||
|
||||
int offset = (int)(((Control1 & 0x60) << 3) + (control2) + 1);
|
||||
long numberToCopyFromOffset = ((Control1 & 0x1C) >> 2) + 3;
|
||||
OffsetCopy(ref DecompressedData, offset, DataPos, numberToCopyFromOffset);
|
||||
DataPos += (int)numberToCopyFromOffset;
|
||||
|
||||
if (DataPos == (DecompressedData.Length))
|
||||
break;
|
||||
}
|
||||
else if ((Control1 >= 128 && Control1 <= 191))
|
||||
{
|
||||
// 0x80 - 0xBF
|
||||
long control2 = Data[Pos];
|
||||
Pos++;
|
||||
long control3 = Data[Pos];
|
||||
Pos++;
|
||||
|
||||
long numberOfPlainText = (control2 >> 6) & 0x03;
|
||||
ArrayCopy2(Data, Pos, ref DecompressedData, DataPos, numberOfPlainText);
|
||||
DataPos += (int)numberOfPlainText;
|
||||
Pos += (int)numberOfPlainText;
|
||||
|
||||
if (DataPos == (DecompressedData.Length))
|
||||
break;
|
||||
|
||||
int offset = (int)(((control2 & 0x3F) << 8) + (control3) + 1);
|
||||
long numberToCopyFromOffset = (Control1 & 0x3F) + 4;
|
||||
OffsetCopy(ref DecompressedData, offset, DataPos, numberToCopyFromOffset);
|
||||
DataPos += (int)numberToCopyFromOffset;
|
||||
|
||||
if (DataPos == (DecompressedData.Length))
|
||||
break;
|
||||
}
|
||||
else if (Control1 >= 192 && Control1 <= 223)
|
||||
{
|
||||
// 0xC0 - 0xDF
|
||||
long numberOfPlainText = (Control1 & 0x03);
|
||||
long control2 = Data[Pos];
|
||||
Pos++;
|
||||
long control3 = Data[Pos];
|
||||
Pos++;
|
||||
long control4 = Data[Pos];
|
||||
Pos++;
|
||||
ArrayCopy2(Data, Pos, ref DecompressedData, DataPos, numberOfPlainText);
|
||||
DataPos += (int)numberOfPlainText;
|
||||
Pos += (int)numberOfPlainText;
|
||||
|
||||
if (DataPos == (DecompressedData.Length))
|
||||
break;
|
||||
|
||||
int offset = (int)(((Control1 & 0x10) << 12) + (control2 << 8) + (control3) + 1);
|
||||
long numberToCopyFromOffset = ((Control1 & 0x0C) << 6) + (control4) + 5;
|
||||
OffsetCopy(ref DecompressedData, offset, DataPos, numberToCopyFromOffset);
|
||||
DataPos += (int)numberToCopyFromOffset;
|
||||
|
||||
if (DataPos == (DecompressedData.Length))
|
||||
break;
|
||||
}
|
||||
else if (Control1 >= 224 && Control1 <= 251)
|
||||
{
|
||||
// 0xE0 - 0xFB
|
||||
long numberOfPlainText = ((Control1 & 0x1F) << 2) + 4;
|
||||
ArrayCopy2(Data, Pos, ref DecompressedData, DataPos, numberOfPlainText);
|
||||
DataPos += (int)numberOfPlainText;
|
||||
Pos += (int)numberOfPlainText;
|
||||
|
||||
if (DataPos == (DecompressedData.Length))
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
long numberOfPlainText = (Control1 & 0x03);
|
||||
ArrayCopy2(Data, Pos, ref DecompressedData, DataPos, numberOfPlainText);
|
||||
|
||||
DataPos += (int)numberOfPlainText;
|
||||
Pos += (int)numberOfPlainText;
|
||||
|
||||
if (DataPos == (DecompressedData.Length))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return DecompressedData;
|
||||
}
|
||||
|
||||
//No data to decompress
|
||||
return Data;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue