mirror of
https://github.com/Barubary/dsdecmp.git
synced 2024-11-16 15:49:24 +01:00
C#: implemented a compression algorithm for the LZE/Le compression scheme. (although it appears to perform worse than the original compression algorithm, it should still work)
This commit is contained in:
parent
4302517e58
commit
a30a7ac3e3
@ -7,24 +7,58 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoldenSunDD", "GoldenSunDD\
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuminousArc", "LuminousArc\LuminousArc.csproj", "{4BD8DF5C-E971-45D1-B170-340D22DDB351}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuminousArc", "LuminousArc\LuminousArc.csproj", "{4BD8DF5C-E971-45D1-B170-340D22DDB351}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tester", "Tester\Tester.csproj", "{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|Mixed Platforms = Debug|Mixed Platforms
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|Mixed Platforms = Release|Mixed Platforms
|
||||||
|
Release|x86 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Release|Any CPU.Build.0 = Release|Any CPU
|
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Release|Any CPU.Build.0 = Release|Any CPU
|
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Release|Any CPU.Build.0 = Release|Any CPU
|
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||||
|
{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
|
||||||
|
{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}.Debug|Mixed Platforms.Build.0 = Debug|x86
|
||||||
|
{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}.Release|Any CPU.ActiveCfg = Release|x86
|
||||||
|
{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}.Release|Mixed Platforms.ActiveCfg = Release|x86
|
||||||
|
{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}.Release|Mixed Platforms.Build.0 = Release|x86
|
||||||
|
{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}.Release|x86.Build.0 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -11,7 +11,7 @@ namespace DSDecmp
|
|||||||
class NewestProgram
|
class NewestProgram
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
public static string PluginFolder = "../../../PluginDistro/Debug";
|
public static string PluginFolder = "./Plugins/Debug";
|
||||||
#else
|
#else
|
||||||
public static string PluginFolder = "./Plugins";
|
public static string PluginFolder = "./Plugins";
|
||||||
#endif
|
#endif
|
||||||
@ -299,7 +299,6 @@ namespace DSDecmp
|
|||||||
inputData = new byte[inStream.Length];
|
inputData = new byte[inStream.Length];
|
||||||
inStream.Read(inputData, 0, inputData.Length);
|
inStream.Read(inputData, 0, inputData.Length);
|
||||||
}
|
}
|
||||||
bool compressed = false;
|
|
||||||
|
|
||||||
#region try to compress
|
#region try to compress
|
||||||
|
|
||||||
@ -315,7 +314,6 @@ namespace DSDecmp
|
|||||||
{
|
{
|
||||||
outStr.WriteTo(output);
|
outStr.WriteTo(output);
|
||||||
}
|
}
|
||||||
compressed = true;
|
|
||||||
if (format is CompositeFormat)
|
if (format is CompositeFormat)
|
||||||
Console.Write((format as CompositeFormat).LastUsedCompressFormatString);
|
Console.Write((format as CompositeFormat).LastUsedCompressFormatString);
|
||||||
else
|
else
|
||||||
@ -323,12 +321,7 @@ namespace DSDecmp
|
|||||||
Console.WriteLine("-compressed " + input + " to " + outputFile);
|
Console.WriteLine("-compressed " + input + " to " + outputFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception) { }
|
catch (Exception ex)
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
if (!compressed)
|
|
||||||
{
|
{
|
||||||
#region copy or print and continue
|
#region copy or print and continue
|
||||||
|
|
||||||
@ -337,11 +330,20 @@ namespace DSDecmp
|
|||||||
Copy(input, outputFile);
|
Copy(input, outputFile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Console.WriteLine("Could not " + format.ShortFormatString + "-compress " + input + ".");
|
{
|
||||||
|
Console.WriteLine("Could not " + format.ShortFormatString + "-compress " + input + ";");
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine(ex.StackTrace);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
catch (FileNotFoundException)
|
catch (FileNotFoundException)
|
||||||
{
|
{
|
||||||
Console.WriteLine("The file " + input + " does not exist.");
|
Console.WriteLine("The file " + input + " does not exist.");
|
||||||
|
@ -14,19 +14,21 @@ namespace DSDecmp
|
|||||||
/// starting at oldPtr. Takes O(inLength * oldLength) = O(n^2) time.
|
/// starting at oldPtr. Takes O(inLength * oldLength) = O(n^2) time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="newPtr">The start of the data that needs to be compressed.</param>
|
/// <param name="newPtr">The start of the data that needs to be compressed.</param>
|
||||||
/// <param name="newLength">The number of bytes that still need to be compressed.</param>
|
/// <param name="newLength">The number of bytes that still need to be compressed.
|
||||||
|
/// (or: the maximum number of bytes that _may_ be compressed into one block)</param>
|
||||||
/// <param name="oldPtr">The start of the raw file.</param>
|
/// <param name="oldPtr">The start of the raw file.</param>
|
||||||
/// <param name="oldLength">The number of bytes already compressed.</param>
|
/// <param name="oldLength">The number of bytes already compressed.</param>
|
||||||
/// <param name="disp">The offset of the start of the longest block to refer to.</param>
|
/// <param name="disp">The offset of the start of the longest block to refer to.</param>
|
||||||
|
/// <param name="minDisp">The minimum allowed value for 'disp'.</param>
|
||||||
/// <returns>The length of the longest sequence of bytes that can be copied from the already decompressed data.</returns>
|
/// <returns>The length of the longest sequence of bytes that can be copied from the already decompressed data.</returns>
|
||||||
public static unsafe int GetOccurrenceLength(byte* newPtr, int newLength, byte* oldPtr, int oldLength, out int disp)
|
public static unsafe int GetOccurrenceLength(byte* newPtr, int newLength, byte* oldPtr, int oldLength, out int disp, int minDisp = 1)
|
||||||
{
|
{
|
||||||
disp = 0;
|
disp = 0;
|
||||||
if (newLength == 0)
|
if (newLength == 0)
|
||||||
return 0;
|
return 0;
|
||||||
int maxLength = 0;
|
int maxLength = 0;
|
||||||
// try every possible 'disp' value (disp = oldLength - i)
|
// try every possible 'disp' value (disp = oldLength - i)
|
||||||
for (int i = 0; i < oldLength - 1; i++)
|
for (int i = 0; i < oldLength - minDisp; i++)
|
||||||
{
|
{
|
||||||
// work from the start of the old data to the end, to mimic the original implementation's behaviour
|
// work from the start of the old data to the end, to mimic the original implementation's behaviour
|
||||||
// (and going from start to end or from end to start does not influence the compression ratio anyway)
|
// (and going from start to end or from end to start does not influence the compression ratio anyway)
|
||||||
@ -48,6 +50,10 @@ namespace DSDecmp
|
|||||||
{
|
{
|
||||||
maxLength = currentLength;
|
maxLength = currentLength;
|
||||||
disp = oldLength - i;
|
disp = oldLength - i;
|
||||||
|
|
||||||
|
// if we cannot do better anyway, stop trying.
|
||||||
|
if (maxLength == newLength)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return maxLength;
|
return maxLength;
|
||||||
|
@ -29,9 +29,15 @@ namespace GameFormats
|
|||||||
|
|
||||||
public override bool SupportsCompression
|
public override bool SupportsCompression
|
||||||
{
|
{
|
||||||
get { return false; }
|
get { return true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool lookAhead = false;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets if, when compressing using this format, the optimal compression scheme should be used.
|
||||||
|
/// </summary>
|
||||||
|
public static bool LookAhead { get { return lookAhead; } set { lookAhead = value; } }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An LZE / Le file consists of the following:
|
* An LZE / Le file consists of the following:
|
||||||
- A six byte header
|
- A six byte header
|
||||||
@ -97,6 +103,10 @@ namespace GameFormats
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Method: Decompress(instream, inLength, outstream)
|
||||||
|
/// <summary>
|
||||||
|
/// Decompresses the given stream using the LZE/Le compression format.
|
||||||
|
/// </summary>
|
||||||
public override long Decompress(System.IO.Stream instream, long inLength, System.IO.Stream outstream)
|
public override long Decompress(System.IO.Stream instream, long inLength, System.IO.Stream outstream)
|
||||||
{
|
{
|
||||||
long readBytes = 0;
|
long readBytes = 0;
|
||||||
@ -191,7 +201,7 @@ namespace GameFormats
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
case 1:
|
case 1:
|
||||||
#region 1 -> compact LZ10-like format
|
#region 1 -> compact LZ10/RLE-like format
|
||||||
{
|
{
|
||||||
#region Get length and displacement('disp') values from next byte
|
#region Get length and displacement('disp') values from next byte
|
||||||
// there are < 2 bytes available when the end is at most 1 byte away
|
// there are < 2 bytes available when the end is at most 1 byte away
|
||||||
@ -268,6 +278,7 @@ namespace GameFormats
|
|||||||
default:
|
default:
|
||||||
throw new Exception("BUG: Mask is not 2 bits long!");
|
throw new Exception("BUG: Mask is not 2 bits long!");
|
||||||
}
|
}
|
||||||
|
|
||||||
outstream.Flush();
|
outstream.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,22 +292,206 @@ namespace GameFormats
|
|||||||
|
|
||||||
return decompressedSize;
|
return decompressedSize;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
private int MaskRsh(int value, int mask)
|
/// <summary>
|
||||||
|
/// Checks if the given aguments have the '-opt' option, which makes this format
|
||||||
|
/// compress using (near-)optimal compression instead of the original compression algorithm.
|
||||||
|
/// </summary>
|
||||||
|
public override int ParseCompressionOptions(string[] args)
|
||||||
{
|
{
|
||||||
int maskCopy = mask;
|
LookAhead = false;
|
||||||
int masked = value & mask;
|
if (args.Length > 0)
|
||||||
if (maskCopy == 0)
|
if (args[0] == "-opt")
|
||||||
return masked;
|
|
||||||
while ((maskCopy & 1) == 0)
|
|
||||||
{
|
{
|
||||||
masked >>= 1;
|
LookAhead = true;
|
||||||
maskCopy >>= 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return masked;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int Compress(System.IO.Stream instream, long inLength, System.IO.Stream outstream)
|
public unsafe override int Compress(System.IO.Stream instream, long inLength, System.IO.Stream outstream)
|
||||||
|
{
|
||||||
|
// block type 0: stores at most 3+0xF = 0x12 = 18 bytes (in 2 bytes)
|
||||||
|
// block type 1: stores at most 2+0x3F = 0x41 = 65 bytes (in 1 byte)
|
||||||
|
// block type 2: 1 raw byte
|
||||||
|
// block type 3: 3 raw bytes
|
||||||
|
|
||||||
|
if (LookAhead)
|
||||||
|
return CompressWithLA(instream, inLength, outstream);
|
||||||
|
|
||||||
|
|
||||||
|
// save the input data in an array to prevent having to go back and forth in a file
|
||||||
|
byte[] indata = new byte[inLength];
|
||||||
|
int numReadBytes = instream.Read(indata, 0, (int)inLength);
|
||||||
|
if (numReadBytes != inLength)
|
||||||
|
throw new StreamTooShortException();
|
||||||
|
|
||||||
|
// write the compression head first
|
||||||
|
outstream.WriteByte((byte)'L');
|
||||||
|
outstream.WriteByte((byte)'e');
|
||||||
|
outstream.WriteByte((byte)(inLength & 0xFF));
|
||||||
|
outstream.WriteByte((byte)((inLength >> 8) & 0xFF));
|
||||||
|
outstream.WriteByte((byte)((inLength >> 16) & 0xFF));
|
||||||
|
outstream.WriteByte((byte)((inLength >> 24) & 0xFF));
|
||||||
|
|
||||||
|
int compressedLength = 6;
|
||||||
|
|
||||||
|
fixed (byte* instart = &indata[0])
|
||||||
|
{
|
||||||
|
// we do need to buffer the output, as the first byte indicates which blocks are compressed.
|
||||||
|
// this version does not use a look-ahead, so we do not need to buffer more than 4 blocks at a time.
|
||||||
|
// (a block is at most 3 bytes long)
|
||||||
|
byte[] outbuffer = new byte[4 * 3 + 1];
|
||||||
|
outbuffer[0] = 0;
|
||||||
|
int bufferlength = 1, bufferedBlocks = 0;
|
||||||
|
int readBytes = 0;
|
||||||
|
|
||||||
|
int cacheByte = -1;
|
||||||
|
|
||||||
|
while (readBytes < inLength)
|
||||||
|
{
|
||||||
|
#region If 4 blocks are bufferd, write them and reset the buffer
|
||||||
|
// we can only buffer 4 blocks at a time.
|
||||||
|
if (bufferedBlocks == 4)
|
||||||
|
{
|
||||||
|
outstream.Write(outbuffer, 0, bufferlength);
|
||||||
|
compressedLength += bufferlength;
|
||||||
|
// reset the buffer
|
||||||
|
outbuffer[0] = 0;
|
||||||
|
bufferlength = 1;
|
||||||
|
bufferedBlocks = 0;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// type 0: 3 <= len <= 18; 5 <= disp <= 0x1004
|
||||||
|
// type 1: 2 <= len <= 65; 1 <= disp <= 4
|
||||||
|
// type 2: 1 raw byte
|
||||||
|
// type 3: 3 raw bytes
|
||||||
|
|
||||||
|
// check if we can compress it using type 1 first (only 1 byte-long block)
|
||||||
|
int disp;
|
||||||
|
int oldLength = Math.Min(readBytes, 0x1004);
|
||||||
|
int length = LZUtil.GetOccurrenceLength(instart + readBytes, (int)Math.Min(inLength - readBytes, 65),
|
||||||
|
instart + readBytes - oldLength, oldLength, out disp, 1);
|
||||||
|
if (disp >= 1 && ((disp <= 4 && length >= 2) || (disp >= 5 && length >= 3)))
|
||||||
|
{
|
||||||
|
if (cacheByte >= 0)
|
||||||
|
{
|
||||||
|
// write a single raw byte block (the previous byte could not be the start of any compressed block)
|
||||||
|
outbuffer[bufferlength++] = (byte)(cacheByte & 0xFF);
|
||||||
|
outbuffer[0] |= (byte)(2 << (bufferedBlocks * 2));
|
||||||
|
cacheByte = -1;
|
||||||
|
bufferedBlocks++;
|
||||||
|
// the block set may be full; just retry this iteration.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (disp >= 5)
|
||||||
|
{
|
||||||
|
#region compress using type 0
|
||||||
|
|
||||||
|
// type 0: store len/disp in 2 bytes:
|
||||||
|
// AB CD, with len = C + 3, disp = DAB + 5
|
||||||
|
|
||||||
|
// make sure we do not try to compress more than fits into the block
|
||||||
|
length = Math.Min(length, 0xF + 3);
|
||||||
|
|
||||||
|
readBytes += length;
|
||||||
|
|
||||||
|
outbuffer[bufferlength++] = (byte)((disp - 5) & 0xFF);
|
||||||
|
outbuffer[bufferlength] = (byte)(((disp - 5) >> 8) & 0xF);
|
||||||
|
outbuffer[bufferlength++] |= (byte)(((length - 3) & 0xF) << 4);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
else // 1 <= disp <= 4
|
||||||
|
{
|
||||||
|
#region compress using type 1
|
||||||
|
|
||||||
|
// type 1: store len/disp in 1 byte:
|
||||||
|
// ABCDEFGH, wih len = ABCDEF + 2, disp = GH + 1
|
||||||
|
|
||||||
|
readBytes += length;
|
||||||
|
|
||||||
|
outbuffer[bufferlength] = (byte)(((length - 2) << 2) & 0xFC);
|
||||||
|
outbuffer[bufferlength] |= (byte)((disp - 1) & 0x3);
|
||||||
|
bufferlength++;
|
||||||
|
|
||||||
|
outbuffer[0] |= (byte)(1 << (bufferedBlocks * 2));
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (cacheByte < 0)
|
||||||
|
{
|
||||||
|
// first fail? remember byte, try to compress starting at next byte
|
||||||
|
cacheByte = *(instart + (readBytes++));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 2 consecutive fails -> store 3 raw bytes (type 3) if possible.
|
||||||
|
if (inLength - readBytes >= 2)
|
||||||
|
{
|
||||||
|
outbuffer[bufferlength++] = (byte)(cacheByte & 0xFF);
|
||||||
|
outbuffer[bufferlength++] = *(instart + (readBytes++));
|
||||||
|
outbuffer[bufferlength++] = *(instart + (readBytes++));
|
||||||
|
outbuffer[0] |= (byte)(3 << (bufferedBlocks * 2));
|
||||||
|
cacheByte = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// there are only two bytes remaining (incl. the cched byte)
|
||||||
|
// so write the cached byte first as single raw byte.
|
||||||
|
// keep the next/last byte as new cache, since the block buffer may be full.
|
||||||
|
outbuffer[bufferlength++] = (byte)(cacheByte & 0xFF);
|
||||||
|
outbuffer[0] |= (byte)(2 << (bufferedBlocks * 2));
|
||||||
|
cacheByte = *(instart + (readBytes++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferedBlocks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there may be one cache-byte left.
|
||||||
|
if (cacheByte >= 0)
|
||||||
|
{
|
||||||
|
// if the current set of blocks is full, empty it first
|
||||||
|
if (bufferedBlocks == 4)
|
||||||
|
{
|
||||||
|
#region empty block buffer
|
||||||
|
|
||||||
|
outstream.Write(outbuffer, 0, bufferlength);
|
||||||
|
compressedLength += bufferlength;
|
||||||
|
// reset the buffer
|
||||||
|
outbuffer[0] = 0;
|
||||||
|
bufferlength = 1;
|
||||||
|
bufferedBlocks = 0;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
outbuffer[bufferlength++] = (byte)(cacheByte & 0xFF);
|
||||||
|
cacheByte = -1;
|
||||||
|
outbuffer[0] |= (byte)(2 << (bufferedBlocks * 2));
|
||||||
|
bufferedBlocks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy any remaining blocks to the output
|
||||||
|
if (bufferedBlocks > 0)
|
||||||
|
{
|
||||||
|
outstream.Write(outbuffer, 0, bufferlength);
|
||||||
|
compressedLength += bufferlength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return compressedLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe int CompressWithLA(Stream instream, long inLength, Stream outstream)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
@ -30,6 +31,7 @@
|
|||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
@ -211,16 +211,18 @@
|
|||||||
Utility class for compression using LZ-like compression schemes.
|
Utility class for compression using LZ-like compression schemes.
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:DSDecmp.LZUtil.GetOccurrenceLength(System.Byte*,System.Int32,System.Byte*,System.Int32,System.Int32@)">
|
<member name="M:DSDecmp.LZUtil.GetOccurrenceLength(System.Byte*,System.Int32,System.Byte*,System.Int32,System.Int32@,System.Int32)">
|
||||||
<summary>
|
<summary>
|
||||||
Determine the maximum size of a LZ-compressed block starting at newPtr, using the already compressed data
|
Determine the maximum size of a LZ-compressed block starting at newPtr, using the already compressed data
|
||||||
starting at oldPtr. Takes O(inLength * oldLength) = O(n^2) time.
|
starting at oldPtr. Takes O(inLength * oldLength) = O(n^2) time.
|
||||||
</summary>
|
</summary>
|
||||||
<param name="newPtr">The start of the data that needs to be compressed.</param>
|
<param name="newPtr">The start of the data that needs to be compressed.</param>
|
||||||
<param name="newLength">The number of bytes that still need to be compressed.</param>
|
<param name="newLength">The number of bytes that still need to be compressed.
|
||||||
|
(or: the maximum number of bytes that _may_ be compressed into one block)</param>
|
||||||
<param name="oldPtr">The start of the raw file.</param>
|
<param name="oldPtr">The start of the raw file.</param>
|
||||||
<param name="oldLength">The number of bytes already compressed.</param>
|
<param name="oldLength">The number of bytes already compressed.</param>
|
||||||
<param name="disp">The offset of the start of the longest block to refer to.</param>
|
<param name="disp">The offset of the start of the longest block to refer to.</param>
|
||||||
|
<param name="minDisp">The minimum allowed value for 'disp'.</param>
|
||||||
<returns>The length of the longest sequence of bytes that can be copied from the already decompressed data.</returns>
|
<returns>The length of the longest sequence of bytes that can be copied from the already decompressed data.</returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="T:DSDecmp.Formats.Nitro.NitroCFormat">
|
<member name="T:DSDecmp.Formats.Nitro.NitroCFormat">
|
||||||
|
Binary file not shown.
Binary file not shown.
27
CSharp/Tester/Program.cs
Normal file
27
CSharp/Tester/Program.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using GameFormats;
|
||||||
|
|
||||||
|
namespace Tester
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Start");
|
||||||
|
|
||||||
|
//new LuminousArc().Compress("D:/tile molester/DSLazy/NDS_UNPACK_LUMARC/test/atcbg_dec.imb", "D:/tile molester/DSLazy/NDS_UNPACK_LUMARC/test/atcbg_dec_cmp.imb");
|
||||||
|
//new LuminousArc().Decompress("D:/tile molester/DSLazy/NDS_UNPACK_LUMARC/test/atcbg_dec_cmp.imb", "D:/tile molester/DSLazy/NDS_UNPACK_LUMARC/test/atcbg_dec_cmp_dec.imb");
|
||||||
|
|
||||||
|
new LuminousArc().Compress("D:/tile molester/DSLazy/NDS_UNPACK_LUMARC/test/o_lmoji00_dec.bin", "D:/tile molester/DSLazy/NDS_UNPACK_LUMARC/test/o_lmoji00_dec_cmp.bin");
|
||||||
|
Console.WriteLine();
|
||||||
|
new LuminousArc().Decompress("D:/tile molester/DSLazy/NDS_UNPACK_LUMARC/test/o_lmoji00_dec_cmp.bin", "D:/tile molester/DSLazy/NDS_UNPACK_LUMARC/test/o_lmoji00_dec_cmp_dec.bin");
|
||||||
|
|
||||||
|
|
||||||
|
Console.WriteLine("Success?");
|
||||||
|
Console.ReadLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
CSharp/Tester/Properties/AssemblyInfo.cs
Normal file
36
CSharp/Tester/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("Tester")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Tester")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2011")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("abe40de2-686c-4757-83ff-375f54a21490")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
71
CSharp/Tester/Tester.csproj
Normal file
71
CSharp/Tester/Tester.csproj
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||||
|
<ProductVersion>8.0.30703</ProductVersion>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<ProjectGuid>{A4FABF4B-59F2-4D4B-9012-FF177980EAB7}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Tester</RootNamespace>
|
||||||
|
<AssemblyName>Tester</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||||
|
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\DSDecmp\DSDecmp.csproj">
|
||||||
|
<Project>{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}</Project>
|
||||||
|
<Name>DSDecmp</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\GoldenSunDD\GoldenSunDD.csproj">
|
||||||
|
<Project>{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}</Project>
|
||||||
|
<Name>GoldenSunDD</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\LuminousArc\LuminousArc.csproj">
|
||||||
|
<Project>{4BD8DF5C-E971-45D1-B170-340D22DDB351}</Project>
|
||||||
|
<Name>LuminousArc</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
Loading…
Reference in New Issue
Block a user