mirror of
https://github.com/Barubary/dsdecmp.git
synced 2025-02-20 05:12:47 +01:00
C#: A step into a new system, where game-specific formats can be defined as plugins, preventing users from having to download DSDecmp itself again and again. Such plugins must be stores in a 'Plugins' folder in the same location as the DSDecmp.exe.
To keep the base program mobile, all native NDS formats do not require plugins. The 8-bit Huffman format cannot be used to compress at the moment; thanks to CUE a bug has been found where the offset-field of a node overflows, corrupting the compressed file.
This commit is contained in:
parent
c1994426f3
commit
d64137221a
@ -1,8 +1,12 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual C# Express 2010
|
||||
# Visual Studio 2010
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DSDecmp", "DSDecmp\DSDecmp.csproj", "{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoldenSunDD", "GoldenSunDD\GoldenSunDD.csproj", "{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuminousArc", "LuminousArc\LuminousArc.csproj", "{4BD8DF5C-E971-45D1-B170-340D22DDB351}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -13,6 +17,14 @@ Global
|
||||
{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}.Debug|Any CPU.Build.0 = Debug|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
|
||||
{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}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}.Release|Any CPU.Build.0 = Release|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}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4BD8DF5C-E971-45D1-B170-340D22DDB351}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -36,6 +36,7 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DocumentationFile>bin\Release\DSDecmp.XML</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
@ -44,17 +45,19 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Exceptions\InputTooLargeException.cs" />
|
||||
<Compile Include="Exceptions\NotEnoughDataException.cs" />
|
||||
<Compile Include="Exceptions\StreamTooShortException.cs" />
|
||||
<Compile Include="Exceptions\TooMuchInputException.cs" />
|
||||
<Compile Include="Formats\CompressionFormat.cs" />
|
||||
<Compile Include="Formats\GameSpecific\GoldenSunDD.cs" />
|
||||
<Compile Include="Formats\LZOvl.cs" />
|
||||
<Compile Include="Formats\CompositeFormat.cs" />
|
||||
<Compile Include="Formats\Nitro\CompositeFormats.cs" />
|
||||
<Compile Include="Formats\Nitro\Huffman.cs" />
|
||||
<Compile Include="Formats\Nitro\LZ10.cs" />
|
||||
<Compile Include="Formats\Nitro\LZ11.cs" />
|
||||
<Compile Include="Formats\LZOvl.cs" />
|
||||
<Compile Include="Formats\Nitro\NitroCFormat.cs" />
|
||||
<Compile Include="Exceptions\NotEnoughDataException.cs" />
|
||||
<Compile Include="Formats\Nitro\RLE.cs" />
|
||||
<Compile Include="NewestProgram.cs" />
|
||||
<Compile Include="NewProgram.cs" />
|
||||
<Compile Include="TestProgram.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
@ -62,6 +65,7 @@
|
||||
<Compile Include="Utils\LZUtil.cs" />
|
||||
<Compile Include="Utils\SimpleReversePrioQueue.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildBinPath)\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.
|
||||
|
@ -4,8 +4,15 @@ using System.Text;
|
||||
|
||||
namespace DSDecmp
|
||||
{
|
||||
/// <summary>
|
||||
/// An exception indicating that the file cannot be compressed, because the decompressed size
|
||||
/// cannot be represented in the current compression format.
|
||||
/// </summary>
|
||||
public class InputTooLargeException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new exception that indicates that the input is too big to be compressed.
|
||||
/// </summary>
|
||||
public InputTooLargeException()
|
||||
: base("The compression ratio is not high enough to fit the input "
|
||||
+ "in a single compressed file.") { }
|
||||
|
@ -11,6 +11,9 @@ namespace DSDecmp
|
||||
/// </summary>
|
||||
public class StreamTooShortException : EndOfStreamException
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new exception that indicates that the stream was shorter than the given input length.
|
||||
/// </summary>
|
||||
public StreamTooShortException()
|
||||
: base("The end of the stream was reached "
|
||||
+ "before the given amout of data was read.")
|
||||
|
@ -4,6 +4,10 @@ using System.Text;
|
||||
|
||||
namespace DSDecmp
|
||||
{
|
||||
/// <summary>
|
||||
/// An exception indication that the input has more data than required in order
|
||||
/// to decompress it. This may indicate that more sub-files are present in the file.
|
||||
/// </summary>
|
||||
public class TooMuchInputException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
|
148
CSharp/DSDecmp/Formats/CompositeFormat.cs
Normal file
148
CSharp/DSDecmp/Formats/CompositeFormat.cs
Normal file
@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace DSDecmp.Formats
|
||||
{
|
||||
public abstract class CompositeFormat : CompressionFormat
|
||||
{
|
||||
private List<CompressionFormat> formats;
|
||||
|
||||
protected CompositeFormat(IEnumerable<CompressionFormat> formats)
|
||||
{
|
||||
this.formats = new List<CompressionFormat>(formats);
|
||||
}
|
||||
protected CompositeFormat(params CompressionFormat[] formats)
|
||||
{
|
||||
this.formats = new List<CompressionFormat>(formats);
|
||||
}
|
||||
|
||||
public override bool Supports(System.IO.Stream stream, long inLength)
|
||||
{
|
||||
foreach (CompositeFormat fmt in this.formats)
|
||||
{
|
||||
if (fmt.Supports(stream, inLength))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override long Decompress(System.IO.Stream instream, long inLength, System.IO.Stream outstream)
|
||||
{
|
||||
byte[] inputData = new byte[instream.Length];
|
||||
instream.Read(inputData, 0, inputData.Length);
|
||||
|
||||
foreach (CompressionFormat format in this.formats)
|
||||
{
|
||||
if (!format.SupportsDecompression)
|
||||
continue;
|
||||
using (MemoryStream input = new MemoryStream(inputData))
|
||||
{
|
||||
if (!format.Supports(input, inputData.Length))
|
||||
continue;
|
||||
MemoryStream output = new MemoryStream();
|
||||
try
|
||||
{
|
||||
long decLength = format.Decompress(input, inputData.Length, output);
|
||||
if (decLength > 0)
|
||||
{
|
||||
output.WriteTo(outstream);
|
||||
return decLength;
|
||||
}
|
||||
}
|
||||
catch (Exception) { continue; }
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidDataException("Input cannot be decompressed using the " + this.ShortFormatString + " formats.");
|
||||
}
|
||||
|
||||
public string LastUsedCompressFormatString { get; private set; }
|
||||
public override int Compress(System.IO.Stream instream, long inLength, System.IO.Stream outstream)
|
||||
{
|
||||
// only read the input data once from the file.
|
||||
byte[] inputData = new byte[instream.Length];
|
||||
instream.Read(inputData, 0, inputData.Length);
|
||||
|
||||
MemoryStream bestOutput = null;
|
||||
string bestFormatString = "";
|
||||
int minCompSize = int.MaxValue;
|
||||
foreach (CompressionFormat format in formats)
|
||||
{
|
||||
if (!format.SupportsCompression)
|
||||
continue;
|
||||
|
||||
#region compress the file in each format, and save the best one
|
||||
|
||||
MemoryStream currentOutput = new MemoryStream();
|
||||
int currentOutSize;
|
||||
try
|
||||
{
|
||||
using (MemoryStream input = new MemoryStream(inputData))
|
||||
{
|
||||
currentOutSize = format.Compress(input, input.Length, currentOutput);
|
||||
}
|
||||
}
|
||||
catch (InputTooLargeException i)
|
||||
{
|
||||
Console.WriteLine(i.Message);
|
||||
bestFormatString = format.ShortFormatString;
|
||||
return -1;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (currentOutSize < minCompSize)
|
||||
{
|
||||
bestOutput = currentOutput;
|
||||
minCompSize = currentOutSize;
|
||||
bestFormatString = format.ShortFormatString;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
if (bestOutput == null)
|
||||
return -1;
|
||||
bestOutput.WriteTo(outstream);
|
||||
this.LastUsedCompressFormatString = bestFormatString;
|
||||
return minCompSize;
|
||||
}
|
||||
|
||||
public override int ParseCompressionOptions(string[] args)
|
||||
{
|
||||
// try each option on each of the formats.
|
||||
// each pass over the formats lets them try to consume the options.
|
||||
// if one or more formats consume at least one option, the maximum number
|
||||
// of consumed options is treated as 'handled'; they are ignored in the
|
||||
// next pass. This continues until none of the formats consume the next
|
||||
// value in the options.
|
||||
|
||||
int totalOptionCount = 0;
|
||||
bool usedOption = true;
|
||||
while (usedOption)
|
||||
{
|
||||
usedOption = false;
|
||||
if (args.Length <= totalOptionCount)
|
||||
break;
|
||||
int maxOptionCount = 0;
|
||||
string[] subArray = new string[args.Length - totalOptionCount];
|
||||
Array.Copy(args, totalOptionCount, subArray, 0, subArray.Length);
|
||||
foreach (CompressionFormat format in this.formats)
|
||||
{
|
||||
int optCount = format.ParseCompressionOptions(subArray);
|
||||
maxOptionCount = Math.Max(optCount, maxOptionCount);
|
||||
}
|
||||
|
||||
if (maxOptionCount > 0)
|
||||
{
|
||||
totalOptionCount += maxOptionCount;
|
||||
usedOption = true;
|
||||
}
|
||||
}
|
||||
return totalOptionCount;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace DSDecmp.Formats
|
||||
namespace DSDecmp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for compression formats.
|
||||
/// Base class for all compression formats.
|
||||
/// </summary>
|
||||
public abstract class CompressionFormat
|
||||
{
|
||||
@ -86,7 +86,7 @@ namespace DSDecmp.Formats
|
||||
/// </summary>
|
||||
/// <param name="infile">The file to compress.</param>
|
||||
/// <param name="outfile">The file to write the compressed data to.</param>
|
||||
/// <returns>The size of the compressed file.</returns>
|
||||
/// <returns>The size of the compressed file. If -1, the file could not be compressed.</returns>
|
||||
public int Compress(string infile, string outfile)
|
||||
{
|
||||
// make sure the output directory exists
|
||||
@ -108,7 +108,35 @@ namespace DSDecmp.Formats
|
||||
/// <param name="instream">The stream to read plaintext data from.</param>
|
||||
/// <param name="inLength">The length of the plaintext data.</param>
|
||||
/// <param name="outstream">The stream to write the compressed data to.</param>
|
||||
/// <returns>The size of the compressed stream.</returns>
|
||||
/// <returns>The size of the compressed stream. If -1, the file could not be compressed.</returns>
|
||||
public abstract int Compress(Stream instream, long inLength, Stream outstream);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a short string identifying this compression format.
|
||||
/// </summary>
|
||||
public abstract string ShortFormatString { get; }
|
||||
/// <summary>
|
||||
/// Gets a short description of this compression format (used in the program usage).
|
||||
/// </summary>
|
||||
public abstract string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets if this format supports compressing a file.
|
||||
/// </summary>
|
||||
public abstract bool SupportsCompression { get; }
|
||||
/// <summary>
|
||||
/// Gets if this format supports decompressing a file.
|
||||
/// </summary>
|
||||
public virtual bool SupportsDecompression { get { return true; } }
|
||||
/// <summary>
|
||||
/// Gets the value that must be given on the command line in order to compress using this format.
|
||||
/// </summary>
|
||||
public abstract string CompressionFlag { get; }
|
||||
/// <summary>
|
||||
/// Parses any input specific for this format. Does nothing by default.
|
||||
/// </summary>
|
||||
/// <param name="args">Any arguments that may be used by the format.</param>
|
||||
/// <returns>The number of input arguments consumed by this format.</returns>
|
||||
public virtual int ParseCompressionOptions(string[] args) { return 0; }
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using DSDecmp.Utils;
|
||||
|
||||
namespace DSDecmp.Formats
|
||||
{
|
||||
@ -12,8 +11,28 @@ namespace DSDecmp.Formats
|
||||
/// Note that the last 12 bytes should not be included in the 'inLength' argument when
|
||||
/// decompressing arm9.bin. This is done automatically if a file is given instead of a stream.
|
||||
/// </summary>
|
||||
public class LZOvl : CompressionFormat
|
||||
public sealed class LZOvl : CompressionFormat
|
||||
{
|
||||
public override string ShortFormatString
|
||||
{
|
||||
get { return "LZ-Ovl"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "Reverse LZ format, mainly used in 'overlay' files of NDS games."; }
|
||||
}
|
||||
|
||||
public override string CompressionFlag
|
||||
{
|
||||
get { return "lzovl"; }
|
||||
}
|
||||
|
||||
public override bool SupportsCompression
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
private static bool lookAhead = false;
|
||||
/// <summary>
|
||||
/// Sets the flag that determines if 'look-ahead'/DP should be used when compressing
|
||||
@ -25,6 +44,17 @@ namespace DSDecmp.Formats
|
||||
set { lookAhead = value; }
|
||||
}
|
||||
|
||||
public override int ParseCompressionOptions(string[] args)
|
||||
{
|
||||
if (args.Length > 0)
|
||||
if (args[0] == "-opt")
|
||||
{
|
||||
LookAhead = true;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#region Method: Supports(string file)
|
||||
public override bool Supports(string file)
|
||||
{
|
||||
|
58
CSharp/DSDecmp/Formats/Nitro/CompositeFormats.cs
Normal file
58
CSharp/DSDecmp/Formats/Nitro/CompositeFormats.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace DSDecmp.Formats.Nitro
|
||||
{
|
||||
public class CompositeGBAFormat : CompositeFormat
|
||||
{
|
||||
public CompositeGBAFormat()
|
||||
: base(new Huffman4(), new Huffman8(), new LZ10()) { }
|
||||
|
||||
public override string ShortFormatString
|
||||
{
|
||||
get { return "GBA"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "All formats natively supported by the GBA."; }
|
||||
}
|
||||
|
||||
public override bool SupportsCompression
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override string CompressionFlag
|
||||
{
|
||||
get { return "gba*"; }
|
||||
}
|
||||
}
|
||||
|
||||
public class CompositeNDSFormat : CompositeFormat
|
||||
{
|
||||
public CompositeNDSFormat()
|
||||
: base(new Huffman4(), new Huffman8(), new LZ10(), new LZ11()) { }
|
||||
|
||||
public override string ShortFormatString
|
||||
{
|
||||
get { return "NDS"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "All formats natively supported by the NDS."; }
|
||||
}
|
||||
|
||||
public override bool SupportsCompression
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override string CompressionFlag
|
||||
{
|
||||
get { return "nds*"; }
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using DSDecmp.Utils;
|
||||
|
||||
namespace DSDecmp.Formats.Nitro
|
||||
{
|
||||
@ -10,29 +9,24 @@ namespace DSDecmp.Formats.Nitro
|
||||
/// Compressor and decompressor for the Huffman format used in many of the games for the
|
||||
/// newer Nintendo consoles and handhelds.
|
||||
/// </summary>
|
||||
public class Huffman : NitroCFormat
|
||||
public abstract class Huffman : NitroCFormat
|
||||
{
|
||||
public enum BlockSize : byte { FOURBIT = 0x24, EIGHTBIT = 0x28 }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the block size used when using the Huffman format to compress.
|
||||
/// </summary>
|
||||
public static BlockSize CompressBlockSize { get; set; }
|
||||
public BlockSize CompressBlockSize { get; set; }
|
||||
|
||||
static Huffman()
|
||||
public override bool SupportsCompression
|
||||
{
|
||||
CompressBlockSize = BlockSize.EIGHTBIT;
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public Huffman() : base(0) { }
|
||||
|
||||
public override bool Supports(System.IO.Stream stream, long inLength)
|
||||
public Huffman(BlockSize blockSize)
|
||||
: base((byte)blockSize)
|
||||
{
|
||||
base.magicByte = (byte)BlockSize.FOURBIT;
|
||||
if (base.Supports(stream, inLength))
|
||||
return true;
|
||||
base.magicByte = (byte)BlockSize.EIGHTBIT;
|
||||
return base.Supports(stream, inLength);
|
||||
this.CompressBlockSize = blockSize;
|
||||
}
|
||||
|
||||
#region Decompression method
|
||||
@ -63,10 +57,7 @@ namespace DSDecmp.Formats.Nitro
|
||||
long readBytes = 0;
|
||||
|
||||
byte type = (byte)instream.ReadByte();
|
||||
BlockSize blockSize = BlockSize.FOURBIT;
|
||||
if (type != (byte)blockSize)
|
||||
blockSize = BlockSize.EIGHTBIT;
|
||||
if (type != (byte)blockSize)
|
||||
if (type != (byte)this.CompressBlockSize)
|
||||
throw new InvalidDataException("The provided stream is not a valid Huffman "
|
||||
+ "compressed stream (invalid type 0x" + type.ToString("X") + "); unknown block size.");
|
||||
byte[] sizeBytes = new byte[3];
|
||||
@ -147,7 +138,7 @@ namespace DSDecmp.Formats.Nitro
|
||||
#endregion
|
||||
|
||||
#region write the data in the current node (when possible)
|
||||
switch (blockSize)
|
||||
switch (this.CompressBlockSize)
|
||||
{
|
||||
case BlockSize.EIGHTBIT:
|
||||
{
|
||||
@ -175,7 +166,7 @@ namespace DSDecmp.Formats.Nitro
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Exception("Unknown block size " + blockSize.ToString());
|
||||
throw new Exception("Unknown block size " + this.CompressBlockSize.ToString());
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -200,18 +191,265 @@ namespace DSDecmp.Formats.Nitro
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override int Compress(Stream instream, long inLength, Stream outstream)
|
||||
#region Utility method: GetLowest(leafQueue, nodeQueue, out prio)
|
||||
/// <summary>
|
||||
/// Gets the tree node with the lowest priority (frequency) from the leaf and node queues.
|
||||
/// If the priority is the same for both head items in the queues, the node from the leaf queue is picked.
|
||||
/// </summary>
|
||||
protected HuffTreeNode GetLowest(SimpleReversedPrioQueue<int, HuffTreeNode> leafQueue, SimpleReversedPrioQueue<int, HuffTreeNode> nodeQueue, out int prio)
|
||||
{
|
||||
switch (CompressBlockSize)
|
||||
if (leafQueue.Count == 0)
|
||||
return nodeQueue.Dequeue(out prio);
|
||||
else if (nodeQueue.Count == 0)
|
||||
return leafQueue.Dequeue(out prio);
|
||||
else
|
||||
{
|
||||
case BlockSize.FOURBIT:
|
||||
return Compress4(instream, inLength, outstream);
|
||||
case BlockSize.EIGHTBIT:
|
||||
return Compress8(instream, inLength, outstream);
|
||||
default:
|
||||
throw new Exception("Unhandled BlockSize " + CompressBlockSize);
|
||||
int leafPrio, nodePrio;
|
||||
leafQueue.Peek(out leafPrio);
|
||||
nodeQueue.Peek(out nodePrio);
|
||||
// pick a node from the leaf queue when the priorities are equal.
|
||||
if (leafPrio <= nodePrio)
|
||||
return leafQueue.Dequeue(out prio);
|
||||
else
|
||||
return nodeQueue.Dequeue(out prio);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Utility class: HuffTreeNode
|
||||
/// <summary>
|
||||
/// A single node in a Huffman tree.
|
||||
/// </summary>
|
||||
public class HuffTreeNode
|
||||
{
|
||||
/// <summary>
|
||||
/// The data contained in this node. May not mean anything when <code>isData == false</code>
|
||||
/// </summary>
|
||||
private byte data;
|
||||
/// <summary>
|
||||
/// A flag indicating if this node has been filled.
|
||||
/// </summary>
|
||||
private bool isFilled;
|
||||
/// <summary>
|
||||
/// The data contained in this node. May not mean anything when <code>isData == false</code>.
|
||||
/// Throws a NullReferenceException when this node has not been defined (ie: reference was outside the
|
||||
/// bounds of the tree definition)
|
||||
/// </summary>
|
||||
public byte Data
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.isFilled) throw new NullReferenceException("Reference to an undefined node in the huffman tree.");
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// A flag indicating if this node contains data. If not, this is not a leaf node.
|
||||
/// </summary>
|
||||
private bool isData;
|
||||
/// <summary>
|
||||
/// Returns true if this node represents data.
|
||||
/// </summary>
|
||||
public bool IsData { get { return this.isData; } }
|
||||
|
||||
/// <summary>
|
||||
/// The child of this node at side 0
|
||||
/// </summary>
|
||||
private HuffTreeNode child0;
|
||||
/// <summary>
|
||||
/// The child of this node at side 0
|
||||
/// </summary>
|
||||
public HuffTreeNode Child0 { get { return this.child0; } }
|
||||
/// <summary>
|
||||
/// The child of this node at side 1
|
||||
/// </summary>
|
||||
private HuffTreeNode child1;
|
||||
/// <summary>
|
||||
/// The child of this node at side 1
|
||||
/// </summary>
|
||||
public HuffTreeNode Child1 { get { return this.child1; } }
|
||||
/// <summary>
|
||||
/// The parent node of this node.
|
||||
/// </summary>
|
||||
public HuffTreeNode Parent { get; private set; }
|
||||
/// <summary>
|
||||
/// Determines if this is the Child0 of the parent node. Assumes there is a parent.
|
||||
/// </summary>
|
||||
public bool IsChild0 { get { return this.Parent.child0 == this; } }
|
||||
/// <summary>
|
||||
/// Determines if this is the Child1 of the parent node. Assumes there is a parent.
|
||||
/// </summary>
|
||||
public bool IsChild1 { get { return this.Parent.child1 == this; } }
|
||||
|
||||
private int depth;
|
||||
/// <summary>
|
||||
/// Get or set the depth of this node. Will not be set automatically, but
|
||||
/// will be set recursively (the depth of all child nodes will be updated when this is set).
|
||||
/// </summary>
|
||||
public int Depth
|
||||
{
|
||||
get { return this.depth; }
|
||||
set
|
||||
{
|
||||
this.depth = value;
|
||||
// recursively set the depth of the child nodes.
|
||||
if (!this.isData)
|
||||
{
|
||||
this.child0.Depth = this.depth + 1;
|
||||
this.child1.Depth = this.depth + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the size of the sub-tree with this node as root.
|
||||
/// </summary>
|
||||
public int Size
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.IsData)
|
||||
return 1;
|
||||
return 1 + this.child0.Size + this.child1.Size;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a seuqnce over the nodes of the sub-tree with this node as root in a pre-order fashion. (Root-Left-Right)
|
||||
/// </summary>
|
||||
public IEnumerable<HuffTreeNode> PreOrderTraversal
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return this;
|
||||
if (!this.IsData)
|
||||
{
|
||||
foreach (HuffTreeNode c in this.child0.PreOrderTraversal)
|
||||
yield return c;
|
||||
foreach (HuffTreeNode c in this.child1.PreOrderTraversal)
|
||||
yield return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually creates a new node for a huffman tree.
|
||||
/// </summary>
|
||||
/// <param name="data">The data for this node.</param>
|
||||
/// <param name="isData">If this node represents data.</param>
|
||||
/// <param name="child0">The child of this node on the 0 side.</param>
|
||||
/// <param name="child1">The child of this node on the 1 side.</param>
|
||||
public HuffTreeNode(byte data, bool isData, HuffTreeNode child0, HuffTreeNode child1)
|
||||
{
|
||||
this.data = data;
|
||||
this.isData = isData;
|
||||
this.child0 = child0;
|
||||
this.child1 = child1;
|
||||
this.isFilled = true;
|
||||
if (!isData)
|
||||
{
|
||||
this.child0.Parent = this;
|
||||
this.child1.Parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new node in the Huffman tree.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from. It is assumed that there is (at least)
|
||||
/// one more byte available to read.</param>
|
||||
/// <param name="isData">If this node is a data-node.</param>
|
||||
/// <param name="relOffset">The offset of this node in the source data, relative to the start
|
||||
/// of the compressed file.</param>
|
||||
/// <param name="maxStreamPos">The indicated end of the huffman tree. If the stream is past
|
||||
/// this position, the tree is invalid.</param>
|
||||
public HuffTreeNode(Stream stream, bool isData, long relOffset, long maxStreamPos)
|
||||
{
|
||||
/*
|
||||
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)
|
||||
*/
|
||||
|
||||
if (stream.Position >= maxStreamPos)
|
||||
{
|
||||
// this happens when part of the tree is unused.
|
||||
this.isFilled = false;
|
||||
return;
|
||||
}
|
||||
this.isFilled = true;
|
||||
int readData = stream.ReadByte();
|
||||
if (readData < 0)
|
||||
throw new StreamTooShortException();
|
||||
this.data = (byte)readData;
|
||||
|
||||
this.isData = isData;
|
||||
|
||||
if (!this.isData)
|
||||
{
|
||||
int offset = this.data & 0x3F;
|
||||
bool zeroIsData = (this.data & 0x80) > 0;
|
||||
bool oneIsData = (this.data & 0x40) > 0;
|
||||
|
||||
// off AND NOT 1 == off XOR (off AND 1)
|
||||
long zeroRelOffset = (relOffset ^ (relOffset & 1)) + offset * 2 + 2;
|
||||
|
||||
long currStreamPos = stream.Position;
|
||||
// position the stream right before the 0-node
|
||||
stream.Position += (zeroRelOffset - relOffset) - 1;
|
||||
// read the 0-node
|
||||
this.child0 = new HuffTreeNode(stream, zeroIsData, zeroRelOffset, maxStreamPos);
|
||||
this.child0.Parent = this;
|
||||
// the 1-node is directly behind the 0-node
|
||||
this.child1 = new HuffTreeNode(stream, oneIsData, zeroRelOffset + 1, maxStreamPos);
|
||||
this.child1.Parent = this;
|
||||
|
||||
// reset the stream position to right behind this node's data
|
||||
stream.Position = currStreamPos;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (this.isData)
|
||||
{
|
||||
return "<" + this.data.ToString("X2") + ">";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "[" + this.child0.ToString() + "," + this.child1.ToString() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class Huffman4 : Huffman
|
||||
{
|
||||
public override string ShortFormatString
|
||||
{
|
||||
get { return "Huffman-4"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "Huffman compression scheme using 4-bit datablocks."; }
|
||||
}
|
||||
|
||||
public override string CompressionFlag
|
||||
{
|
||||
get { return "huff4"; }
|
||||
}
|
||||
|
||||
public Huffman4()
|
||||
: base(BlockSize.FOURBIT) { }
|
||||
|
||||
#region 4-bit block size Compression method
|
||||
/// <summary>
|
||||
@ -221,7 +459,7 @@ namespace DSDecmp.Formats.Nitro
|
||||
/// <param name="inLength">The length of the input stream.</param>
|
||||
/// <param name="outstream">The stream to write the decompressed data to.</param>
|
||||
/// <returns>The size of the decompressed data.</returns>
|
||||
private int Compress4(Stream instream, long inLength, Stream outstream)
|
||||
public override int Compress(Stream instream, long inLength, Stream outstream)
|
||||
{
|
||||
if (inLength > 0xFFFFFF)
|
||||
throw new InputTooLargeException();
|
||||
@ -375,6 +613,27 @@ namespace DSDecmp.Formats.Nitro
|
||||
return compressedLength;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class Huffman8 : Huffman
|
||||
{
|
||||
public override string ShortFormatString
|
||||
{
|
||||
get { return "Huffman-8"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "Huffman compression scheme using 8-bit datablocks."; }
|
||||
}
|
||||
|
||||
public override string CompressionFlag
|
||||
{
|
||||
get { return "huff8"; }
|
||||
}
|
||||
|
||||
public Huffman8()
|
||||
: base(BlockSize.EIGHTBIT) { }
|
||||
|
||||
#region 8-bit block size Compression method
|
||||
/// <summary>
|
||||
@ -384,7 +643,7 @@ namespace DSDecmp.Formats.Nitro
|
||||
/// <param name="inLength">The length of the input stream.</param>
|
||||
/// <param name="outstream">The stream to write the decompressed data to.</param>
|
||||
/// <returns>The size of the decompressed data.</returns>
|
||||
private int Compress8(Stream instream, long inLength, Stream outstream)
|
||||
public override int Compress(Stream instream, long inLength, Stream outstream)
|
||||
{
|
||||
if (inLength > 0xFFFFFF)
|
||||
throw new InputTooLargeException();
|
||||
@ -452,9 +711,33 @@ namespace DSDecmp.Formats.Nitro
|
||||
outstream.WriteByte((byte)((nodeCount - 1) / 2));
|
||||
compressedLength++;
|
||||
|
||||
// use a breadth-first traversal to store the tree, such that we do not need to store/calculate the side of each sub-tree.
|
||||
LinkedList<HuffTreeNode> printQueue = new LinkedList<HuffTreeNode>();
|
||||
printQueue.AddLast(root);
|
||||
// use a breadth-first traversal to store the tree, such that we do not need to store/calculate the size of each sub-tree.
|
||||
// NO! BF results in an ordering that may overflow the offset field. use pre-order instead (Self-Left-Right)
|
||||
foreach (HuffTreeNode node in root.PreOrderTraversal)
|
||||
{
|
||||
if (node.Parent == null) // root node.
|
||||
{
|
||||
// bits 0-5: 'offset' = # nodes in queue left
|
||||
// bit 6: node1 end flag
|
||||
// bit 7: node0 end flag
|
||||
byte data = 0;
|
||||
if (node.Child0.IsData)
|
||||
data |= 0x80;
|
||||
if (node.Child1.IsData)
|
||||
data |= 0x40;
|
||||
outstream.WriteByte(data);
|
||||
}
|
||||
if (node.IsData)
|
||||
continue;
|
||||
else
|
||||
{
|
||||
// bits 0-5: 'offset': if this is left node, 0. if right node
|
||||
// bit 6: node1 end flag
|
||||
// bit 7: node0 end flag
|
||||
}
|
||||
compressedLength++;
|
||||
}
|
||||
/*
|
||||
while (printQueue.Count > 0)
|
||||
{
|
||||
HuffTreeNode node = printQueue.First.Value;
|
||||
@ -476,11 +759,11 @@ namespace DSDecmp.Formats.Nitro
|
||||
data |= 0x40;
|
||||
outstream.WriteByte(data);
|
||||
|
||||
printQueue.AddLast(node.Child0);
|
||||
printQueue.AddLast(node.Child1);
|
||||
printQueue.AddFirst(node.Child1);
|
||||
printQueue.AddFirst(node.Child0);
|
||||
}
|
||||
compressedLength++;
|
||||
}
|
||||
}/**/
|
||||
|
||||
#endregion
|
||||
|
||||
@ -530,211 +813,31 @@ namespace DSDecmp.Formats.Nitro
|
||||
return compressedLength;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tree node with the lowest priority (frequency) from the leaf and node queues.
|
||||
/// If the priority is the same for both head items in the queues, the node from the leaf queue is picked.
|
||||
/// </summary>
|
||||
private HuffTreeNode GetLowest(SimpleReversedPrioQueue<int, HuffTreeNode> leafQueue, SimpleReversedPrioQueue<int, HuffTreeNode> nodeQueue, out int prio)
|
||||
public class HuffmanAny : CompositeFormat
|
||||
{
|
||||
public HuffmanAny()
|
||||
: base(new Huffman4(), new Huffman8()) { }
|
||||
|
||||
public override string ShortFormatString
|
||||
{
|
||||
if (leafQueue.Count == 0)
|
||||
return nodeQueue.Dequeue(out prio);
|
||||
else if (nodeQueue.Count == 0)
|
||||
return leafQueue.Dequeue(out prio);
|
||||
else
|
||||
{
|
||||
int leafPrio, nodePrio;
|
||||
leafQueue.Peek(out leafPrio);
|
||||
nodeQueue.Peek(out nodePrio);
|
||||
// pick a node from the leaf queue when the priorities are equal.
|
||||
if (leafPrio <= nodePrio)
|
||||
return leafQueue.Dequeue(out prio);
|
||||
else
|
||||
return nodeQueue.Dequeue(out prio);
|
||||
}
|
||||
get { return "Huffman"; }
|
||||
}
|
||||
|
||||
#region Utility class: HuffTreeNode
|
||||
/// <summary>
|
||||
/// A single node in a Huffman tree.
|
||||
/// </summary>
|
||||
public class HuffTreeNode
|
||||
public override string Description
|
||||
{
|
||||
/// <summary>
|
||||
/// The data contained in this node. May not mean anything when <code>isData == false</code>
|
||||
/// </summary>
|
||||
private byte data;
|
||||
/// <summary>
|
||||
/// A flag indicating if this node has been filled.
|
||||
/// </summary>
|
||||
private bool isFilled;
|
||||
/// <summary>
|
||||
/// The data contained in this node. May not mean anything when <code>isData == false</code>.
|
||||
/// Throws a NullReferenceException when this node has not been defined (ie: reference was outside the
|
||||
/// bounds of the tree definition)
|
||||
/// </summary>
|
||||
public byte Data
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.isFilled) throw new NullReferenceException("Reference to an undefined node in the huffman tree.");
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// A flag indicating if this node contains data. If not, this is not a leaf node.
|
||||
/// </summary>
|
||||
private bool isData;
|
||||
/// <summary>
|
||||
/// Returns true if this node represents data.
|
||||
/// </summary>
|
||||
public bool IsData { get { return this.isData; } }
|
||||
|
||||
/// <summary>
|
||||
/// The child of this node at side 0
|
||||
/// </summary>
|
||||
private HuffTreeNode child0;
|
||||
/// <summary>
|
||||
/// The child of this node at side 0
|
||||
/// </summary>
|
||||
public HuffTreeNode Child0 { get { return this.child0; } }
|
||||
/// <summary>
|
||||
/// The child of this node at side 1
|
||||
/// </summary>
|
||||
private HuffTreeNode child1;
|
||||
/// <summary>
|
||||
/// The child of this node at side 1
|
||||
/// </summary>
|
||||
public HuffTreeNode Child1 { get { return this.child1; } }
|
||||
/// <summary>
|
||||
/// The parent node of this node.
|
||||
/// </summary>
|
||||
public HuffTreeNode Parent { get; private set; }
|
||||
/// <summary>
|
||||
/// Determines if this is the Child0 of the parent node. Assumes there is a parent.
|
||||
/// </summary>
|
||||
public bool IsChild0 { get { return this.Parent.child0 == this; } }
|
||||
/// <summary>
|
||||
/// Determines if this is the Child1 of the parent node. Assumes there is a parent.
|
||||
/// </summary>
|
||||
public bool IsChild1 { get { return this.Parent.child1 == this; } }
|
||||
|
||||
private int depth;
|
||||
/// <summary>
|
||||
/// Get or set the depth of this node. Will not be set automatically, but
|
||||
/// will be set recursively (the depth of all child nodes will be updated when this is set).
|
||||
/// </summary>
|
||||
public int Depth
|
||||
{
|
||||
get { return this.depth; }
|
||||
set
|
||||
{
|
||||
this.depth = value;
|
||||
// recursively set the depth of the child nodes.
|
||||
if (!this.isData)
|
||||
{
|
||||
this.child0.Depth = this.depth + 1;
|
||||
this.child1.Depth = this.depth + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually creates a new node for a huffman tree.
|
||||
/// </summary>
|
||||
/// <param name="data">The data for this node.</param>
|
||||
/// <param name="isData">If this node represents data.</param>
|
||||
/// <param name="child0">The child of this node on the 0 side.</param>
|
||||
/// <param name="child1">The child of this node on the 1 side.</param>
|
||||
public HuffTreeNode(byte data, bool isData, HuffTreeNode child0, HuffTreeNode child1)
|
||||
{
|
||||
this.data = data;
|
||||
this.isData = isData;
|
||||
this.child0 = child0;
|
||||
this.child1 = child1;
|
||||
this.isFilled = true;
|
||||
if (!isData)
|
||||
{
|
||||
this.child0.Parent = this;
|
||||
this.child1.Parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new node in the Huffman tree.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from. It is assumed that there is (at least)
|
||||
/// one more byte available to read.</param>
|
||||
/// <param name="isData">If this node is a data-node.</param>
|
||||
/// <param name="relOffset">The offset of this node in the source data, relative to the start
|
||||
/// of the compressed file.</param>
|
||||
/// <param name="maxStreamPos">The indicated end of the huffman tree. If the stream is past
|
||||
/// this position, the tree is invalid.</param>
|
||||
public HuffTreeNode(Stream stream, bool isData, long relOffset, long maxStreamPos)
|
||||
{
|
||||
/*
|
||||
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)
|
||||
*/
|
||||
|
||||
if (stream.Position >= maxStreamPos)
|
||||
{
|
||||
// this happens when part of the tree is unused.
|
||||
this.isFilled = false;
|
||||
return;
|
||||
}
|
||||
this.isFilled = true;
|
||||
int readData = stream.ReadByte();
|
||||
if (readData < 0)
|
||||
throw new StreamTooShortException();
|
||||
this.data = (byte)readData;
|
||||
|
||||
this.isData = isData;
|
||||
|
||||
if (!this.isData)
|
||||
{
|
||||
int offset = this.data & 0x3F;
|
||||
bool zeroIsData = (this.data & 0x80) > 0;
|
||||
bool oneIsData = (this.data & 0x40) > 0;
|
||||
|
||||
// off AND NOT 1 == off XOR (off AND 1)
|
||||
long zeroRelOffset = (relOffset ^ (relOffset & 1)) + offset * 2 + 2;
|
||||
|
||||
long currStreamPos = stream.Position;
|
||||
// position the stream right before the 0-node
|
||||
stream.Position += (zeroRelOffset - relOffset) - 1;
|
||||
// read the 0-node
|
||||
this.child0 = new HuffTreeNode(stream, zeroIsData, zeroRelOffset, maxStreamPos);
|
||||
this.child0.Parent = this;
|
||||
// the 1-node is directly behind the 0-node
|
||||
this.child1 = new HuffTreeNode(stream, oneIsData, zeroRelOffset + 1, maxStreamPos);
|
||||
this.child1.Parent = this;
|
||||
|
||||
// reset the stream position to right behind this node's data
|
||||
stream.Position = currStreamPos;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (this.isData)
|
||||
{
|
||||
return "<" + this.data.ToString("X2") + ">";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "[" + this.child0.ToString() + "," + this.child1.ToString() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
get { return "Either the Huffman-4 or Huffman-8 format."; }
|
||||
}
|
||||
|
||||
public override bool SupportsCompression
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override string CompressionFlag
|
||||
{
|
||||
get { return "huff"; }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using DSDecmp.Utils;
|
||||
|
||||
namespace DSDecmp.Formats.Nitro
|
||||
{
|
||||
/// <summary>
|
||||
/// Compressor and decompressor for the LZ-0x10 format used in many of the games for the
|
||||
/// newer Nintendo consoles and handhelds.
|
||||
/// </summary>
|
||||
public class LZ10 : NitroCFormat
|
||||
public sealed class LZ10 : NitroCFormat
|
||||
{
|
||||
public override string ShortFormatString
|
||||
{
|
||||
get { return "LZ-10"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "Common LZ-type compression used in many post-GBC Nintendo games."; }
|
||||
}
|
||||
|
||||
public override string CompressionFlag
|
||||
{
|
||||
get { return "lz10"; }
|
||||
}
|
||||
|
||||
public override bool SupportsCompression
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
private static bool lookAhead = false;
|
||||
/// <summary>
|
||||
/// Sets the flag that determines if 'look-ahead'/DP should be used when compressing
|
||||
@ -25,6 +43,17 @@ namespace DSDecmp.Formats.Nitro
|
||||
|
||||
public LZ10() : base(0x10) { }
|
||||
|
||||
public override int ParseCompressionOptions(string[] args)
|
||||
{
|
||||
if (args.Length > 0)
|
||||
if (args[0] == "-opt")
|
||||
{
|
||||
LookAhead = true;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#region 'Original' Decompression method
|
||||
/// <summary>
|
||||
/// Decompress a stream that is compressed in the LZ-10 format.
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using DSDecmp.Utils;
|
||||
|
||||
namespace DSDecmp.Formats.Nitro
|
||||
{
|
||||
@ -10,8 +9,27 @@ namespace DSDecmp.Formats.Nitro
|
||||
/// Compressor and decompressor for the LZ-0x11 format used in many of the games for the
|
||||
/// newer Nintendo consoles and handhelds.
|
||||
/// </summary>
|
||||
public class LZ11 : NitroCFormat
|
||||
public sealed class LZ11 : NitroCFormat
|
||||
{
|
||||
public override string ShortFormatString
|
||||
{
|
||||
get { return "LZ-11"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "Variant of the LZ-0x10 format to support longer repetitions."; }
|
||||
}
|
||||
|
||||
public override string CompressionFlag
|
||||
{
|
||||
get { return "lz11"; }
|
||||
}
|
||||
|
||||
public override bool SupportsCompression
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
private static bool lookAhead = false;
|
||||
/// <summary>
|
||||
@ -26,6 +44,18 @@ namespace DSDecmp.Formats.Nitro
|
||||
|
||||
public LZ11() : base(0x11) { }
|
||||
|
||||
public override int ParseCompressionOptions(string[] args)
|
||||
{
|
||||
LookAhead = false;
|
||||
if (args.Length > 0)
|
||||
if (args[0] == "-opt")
|
||||
{
|
||||
LookAhead = true;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#region Decompression method
|
||||
public override long Decompress(Stream instream, long inLength, Stream outstream)
|
||||
{
|
||||
|
@ -9,8 +9,28 @@ namespace DSDecmp.Formats.Nitro
|
||||
/// Compressor and decompressor for the RLE format used in several of the games for the
|
||||
/// newer Nintendo consoles and handhelds.
|
||||
/// </summary>
|
||||
public class RLE : NitroCFormat
|
||||
public sealed class RLE : NitroCFormat
|
||||
{
|
||||
public override string ShortFormatString
|
||||
{
|
||||
get { return "RLE"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "Run-Length Encoding used in some modern Nintendo games."; }
|
||||
}
|
||||
|
||||
public override string CompressionFlag
|
||||
{
|
||||
get { return "rle"; }
|
||||
}
|
||||
|
||||
public override bool SupportsCompression
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public RLE() : base(0x30) { }
|
||||
|
||||
public override long Decompress(Stream instream, long inLength, Stream outstream)
|
||||
|
@ -1,20 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using DSDecmp.Formats.Nitro;
|
||||
using DSDecmp.Formats;
|
||||
using System.IO;
|
||||
|
||||
namespace DSDecmp
|
||||
{
|
||||
|
||||
public static class NewProgram
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The formats allowed when compressing a file.
|
||||
/// </summary>
|
||||
public enum Formats
|
||||
{
|
||||
LZOVL, // keep this as the first one, as only the end of a file may be LZ-ovl-compressed (and overlay files are oftenly double-compressed)
|
||||
LZOVL, // keep this as the first one, as only the end of a file may be LZ-ovl-compressed (and overlay files are oftenly double-compressed) (it needs to be attempted first when decompressing)
|
||||
LZ10,
|
||||
LZ11,
|
||||
HUFF4,
|
||||
@ -22,10 +25,10 @@ namespace DSDecmp
|
||||
RLE,
|
||||
HUFF,
|
||||
NDS,
|
||||
GBA,
|
||||
GBA
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
public static void MainNewOld(string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
@ -275,8 +278,8 @@ namespace DSDecmp
|
||||
case Formats.LZ11: fmt = new LZ11(); break;
|
||||
case Formats.LZOVL: fmt = new LZOvl(); break;
|
||||
case Formats.RLE: fmt = new RLE(); break;
|
||||
case Formats.HUFF4: Huffman.CompressBlockSize = Huffman.BlockSize.FOURBIT; fmt = new Huffman(); break;
|
||||
case Formats.HUFF8: Huffman.CompressBlockSize = Huffman.BlockSize.EIGHTBIT; fmt = new Huffman(); break;
|
||||
case Formats.HUFF4: fmt = new Huffman4(); break;
|
||||
case Formats.HUFF8: fmt = new Huffman8(); break;
|
||||
case Formats.HUFF:
|
||||
return CompressHuff(infile, output, out actualFormat);
|
||||
case Formats.GBA:
|
||||
@ -339,12 +342,15 @@ namespace DSDecmp
|
||||
CompressionFormat realFormat = null;
|
||||
switch (format)
|
||||
{
|
||||
case Formats.HUFF4: Huffman.CompressBlockSize = Huffman.BlockSize.FOURBIT; realFormat = new Huffman(); break;
|
||||
case Formats.HUFF8: Huffman.CompressBlockSize = Huffman.BlockSize.EIGHTBIT; realFormat = new Huffman(); break;
|
||||
case Formats.HUFF4: realFormat = new Huffman4(); break;
|
||||
case Formats.HUFF8: realFormat = new Huffman8(); break;
|
||||
case Formats.LZ10: realFormat = new LZ10(); break;
|
||||
case Formats.LZ11: realFormat = new LZ11(); break;
|
||||
case Formats.LZOVL: realFormat = new LZOvl(); break;
|
||||
case Formats.RLE: realFormat = new RLE(); break;
|
||||
default:
|
||||
Console.WriteLine("Unsupported single format: "+format);
|
||||
continue;
|
||||
}
|
||||
|
||||
int currentOutSize;
|
||||
@ -450,7 +456,7 @@ namespace DSDecmp
|
||||
{
|
||||
using (MemoryStream inStream = new MemoryStream(inData))
|
||||
{
|
||||
decSize = Decompress(inStream, decompressedData, f);
|
||||
decSize = Decompress(inStream, decompressedData, null);
|
||||
if (decSize >= 0)
|
||||
{
|
||||
usedFormat = f;
|
||||
@ -515,29 +521,13 @@ namespace DSDecmp
|
||||
|
||||
}
|
||||
|
||||
private static long Decompress(MemoryStream inputStream, MemoryStream output, Formats format)
|
||||
private static long Decompress(MemoryStream inputStream, MemoryStream output, CompressionFormat format)
|
||||
{
|
||||
CompressionFormat realFormat = null;
|
||||
switch (format)
|
||||
{
|
||||
case Formats.HUFF:
|
||||
realFormat = new Huffman(); break;
|
||||
case Formats.LZ10:
|
||||
realFormat = new LZ10(); break;
|
||||
case Formats.LZ11:
|
||||
realFormat = new LZ11(); break;
|
||||
case Formats.LZOVL:
|
||||
realFormat = new LZOvl(); break;
|
||||
case Formats.RLE:
|
||||
realFormat = new RLE(); break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
if (!realFormat.Supports(inputStream, inputStream.Length))
|
||||
if (!format.Supports(inputStream, inputStream.Length))
|
||||
return -1;
|
||||
try
|
||||
{
|
||||
return realFormat.Decompress(inputStream, inputStream.Length, output);
|
||||
return format.Decompress(inputStream, inputStream.Length, output);
|
||||
}
|
||||
catch (TooMuchInputException e)
|
||||
{
|
||||
@ -546,7 +536,7 @@ namespace DSDecmp
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Could not decompress using the " + format.ToString() + " format; " + e.Message);
|
||||
Console.WriteLine("Could not decompress using the " + format.ShortFormatString + " format; " + e.Message);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
563
CSharp/DSDecmp/NewestProgram.cs
Normal file
563
CSharp/DSDecmp/NewestProgram.cs
Normal file
@ -0,0 +1,563 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using DSDecmp.Formats;
|
||||
using DSDecmp.Formats.Nitro;
|
||||
|
||||
namespace DSDecmp
|
||||
{
|
||||
class NewestProgram
|
||||
{
|
||||
#if DEBUG
|
||||
public static string PluginFolder = "../../../PluginDistro/Debug";
|
||||
#else
|
||||
public static string PluginFolder = "./Plugins";
|
||||
#endif
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// I/O input:
|
||||
// file -> read from file, save to file
|
||||
// folder [-co] -> read all files from folder, save to folder_dec or folder_cmp (when decomp or comp resp.) (Same filenames)
|
||||
// file newfile -> read from file, save to newfile
|
||||
// folderin folderout [-co] -> read all files from folderin, save to folderout. (Same filenames)
|
||||
// file1 file2 ... -> read file1, file2, etc
|
||||
// file1 file2 ... folderout [-co] -> read file1, file2, etc, save to folderout. (same filenames)
|
||||
// -> when -co is present, all files that could not be (de)compressed will be copied instead.
|
||||
|
||||
// preambles:
|
||||
// <nothing> -> decompress input to output using first matched format
|
||||
// -d [-ge] -> decompress input to output using first matched format. If -ge, then guess the extension based on first 4 bytes.
|
||||
// -d [-ge] -f <format> -> decompress input to output using the indicated format. If -ge, then guess the extension based on first 4 bytes.
|
||||
// -c <format> [opt1 opt2 ...] -> compress input to output using the specified format and its options.
|
||||
|
||||
// built-in formats:
|
||||
// lz10 -> LZ-0x10, found in >= GBA
|
||||
// lz11 -> LZ-0x11, found in >= NDS
|
||||
// lzovl -> LZ-Ovl/Overlay / backwards LZ, found mostly in NDS overlay files.
|
||||
// huff4 -> 4-bit Huffman, found in >= GBA
|
||||
// huff8 -> 8-bit Huffman, found in >= GBA
|
||||
// huff -> any Huffman format.
|
||||
// gba* -> any format natively supported by the GBA
|
||||
// nds* -> any format natively supported by the NDS, but not LZ-Ovl
|
||||
// when compressing, the best format of the selected set is used. when decompression,
|
||||
// only the formats in the selected set are used.
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
PrintUsage();
|
||||
//Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] == "-c")
|
||||
{
|
||||
if (args.Length <= 2) { Console.WriteLine("Too few arguments."); return; }
|
||||
CompressionFormat format = FirstOrDefault(GetFormat(args[1]));
|
||||
if (format == null) { return; }
|
||||
|
||||
string[] ioArgs = new string[args.Length - 2];
|
||||
Array.Copy(args, 2, ioArgs, 0, ioArgs.Length);
|
||||
|
||||
int optionCount = format.ParseCompressionOptions(ioArgs);
|
||||
string[] realIoArgs = new string[ioArgs.Length - optionCount];
|
||||
Array.Copy(ioArgs, optionCount, realIoArgs, 0, realIoArgs.Length);
|
||||
|
||||
Compress(realIoArgs, format);
|
||||
}
|
||||
else if (args[0] == "-d")
|
||||
{
|
||||
if (args.Length <= 1) { PrintUsage(); return; }
|
||||
int ioIdx = 1;
|
||||
bool guessExtension = false;
|
||||
if (args[ioIdx] == "-ge")
|
||||
{
|
||||
guessExtension = true;
|
||||
ioIdx++;
|
||||
}
|
||||
IEnumerable<CompressionFormat> formats = GetAllFormats(false); // we do not need the built-in composite formats to decompress.
|
||||
if (args[ioIdx] == "-f")
|
||||
{
|
||||
if (args.Length <= ioIdx + 2) { Console.WriteLine("Too few arguments."); return; }
|
||||
formats = GetFormat(args[ioIdx + 1]);
|
||||
ioIdx += 2;
|
||||
}
|
||||
if (formats == null) { return; }
|
||||
if (args.Length <= ioIdx) { Console.WriteLine("Too few arguments."); return; }
|
||||
|
||||
string[] ioArgs = new string[args.Length - ioIdx];
|
||||
Array.Copy(args, ioIdx, ioArgs, 0, ioArgs.Length);
|
||||
|
||||
Decompress(ioArgs, formats, guessExtension);
|
||||
}
|
||||
else
|
||||
{
|
||||
Decompress(args, GetAllFormats(false), false);
|
||||
}
|
||||
}
|
||||
|
||||
#region Usage printer
|
||||
private static void PrintUsage()
|
||||
{
|
||||
Console.WriteLine("DSDecmp - Decompressor for compression formats used on the NDS - by Barubary");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Usage:\tDSDecmp FMTARGS IOARGS");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("IOARGS can be:");
|
||||
Console.WriteLine("-------------------------------------------------------------------------------");
|
||||
Console.WriteLine("file -> read the file, overwrite it.");
|
||||
Console.WriteLine("folder [-co] -> read all files from folder, save to folder_dec");
|
||||
Console.WriteLine(" or folder_cmp.");
|
||||
Console.WriteLine("file newfile -> read the file, save it to newfile.");
|
||||
Console.WriteLine(" (newfile cannot exist yet)");
|
||||
Console.WriteLine("folderin folderout [-co] -> read all files from folderin, save to folderout.");
|
||||
Console.WriteLine("file1 file2 ... -> read file1, file2, etc; overwrite them.");
|
||||
Console.WriteLine("file1 file2 ... folderout [-co] -> read file1, file2, etc; save to folderout.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("When -co is present, all files that could not be handled will be copied to the");
|
||||
Console.WriteLine(" indicated output folder.");
|
||||
Console.WriteLine("-------------------------------------------------------------------------------");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("FMTARGS can be:");
|
||||
Console.WriteLine("-------------------------------------------------------------------------------");
|
||||
Console.WriteLine("<nothing> -> try to decompress input to output.");
|
||||
Console.WriteLine("-d [-ge] -> try to decompress input to output.");
|
||||
Console.WriteLine("-d [-ge] -f <format> -> try to decompress input to output, using fiven format");
|
||||
Console.WriteLine("-c <format> [opt1 ...] -> compress intput to output using given format ");
|
||||
Console.WriteLine(" and options.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("When -ge is present, the extension of the output file will be determined by");
|
||||
Console.WriteLine(" the first 4 bytes of the decompressed data. (of those are alphanuemric ASCII");
|
||||
Console.WriteLine(" characters).");
|
||||
Console.WriteLine("-------------------------------------------------------------------------------");
|
||||
Console.WriteLine("Supported formats:");
|
||||
Console.WriteLine("<format> -> description");
|
||||
foreach (CompressionFormat fmt in GetAllFormats(true))
|
||||
{
|
||||
Console.WriteLine(fmt.CompressionFlag.PadRight(7, ' ') + "-> " + fmt.Description);
|
||||
}
|
||||
Console.WriteLine("-------------------------------------------------------------------------------");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Method: Decompress(string[] ioArgs, IEnumerable<CompressionFormat> formats)
|
||||
private static void Decompress(string[] ioArgs, IEnumerable<CompressionFormat> formats, bool guessExtension)
|
||||
{
|
||||
string[] inputFiles;
|
||||
string outputDir;
|
||||
bool copyErrors;
|
||||
if (!ParseIOArguments(ioArgs, false, out inputFiles, out outputDir, out copyErrors))
|
||||
return;
|
||||
|
||||
foreach (string input in inputFiles)
|
||||
{
|
||||
string outputFile = outputDir ?? IOUtils.GetParent(input);
|
||||
if (Directory.Exists(outputDir))
|
||||
outputFile = Path.Combine(outputFile + Path.DirectorySeparatorChar, Path.GetFileName(input));
|
||||
|
||||
try
|
||||
{
|
||||
// read the file only once.
|
||||
byte[] inputData;
|
||||
using (Stream inStream = File.OpenRead(input))
|
||||
{
|
||||
inputData = new byte[inStream.Length];
|
||||
inStream.Read(inputData, 0, inputData.Length);
|
||||
}
|
||||
bool decompressed = false;
|
||||
foreach (CompressionFormat format in formats)
|
||||
{
|
||||
#region try to decompress using the current format
|
||||
|
||||
using (MemoryStream inStr = new MemoryStream(inputData),
|
||||
outStr = new MemoryStream())
|
||||
{
|
||||
if (!format.Supports(inStr, input.Length))
|
||||
continue;
|
||||
try
|
||||
{
|
||||
long decompSize = format.Decompress(inStr, inputData.Length, outStr);
|
||||
if (decompSize < 0)
|
||||
continue;
|
||||
if (guessExtension)
|
||||
{
|
||||
string outFileName = Path.GetFileNameWithoutExtension(outputFile);
|
||||
outStr.Position = 0;
|
||||
byte[] magic = new byte[4];
|
||||
outStr.Read(magic, 0, 4);
|
||||
outStr.Position = 0;
|
||||
outFileName += "." + GuessExtension(magic, Path.GetExtension(outputFile).Substring(1));
|
||||
outputFile = outputFile.Replace(Path.GetFileName(outputFile), outFileName);
|
||||
}
|
||||
using (FileStream output = File.Create(outputFile))
|
||||
{
|
||||
outStr.WriteTo(output);
|
||||
}
|
||||
decompressed = true;
|
||||
Console.WriteLine(format.ShortFormatString + "-decompressed " + input + " to " + outputFile);
|
||||
break;
|
||||
}
|
||||
catch (TooMuchInputException tmie)
|
||||
{
|
||||
// a TMIE is fine. let the user know and continue saving the decompressed data.
|
||||
Console.WriteLine(tmie.Message);
|
||||
if (guessExtension)
|
||||
{
|
||||
string outFileName = Path.GetFileNameWithoutExtension(outputFile);
|
||||
outStr.Position = 0;
|
||||
byte[] magic = new byte[4];
|
||||
outStr.Read(magic, 0, 4);
|
||||
outStr.Position = 0;
|
||||
outFileName += "." + GuessExtension(magic, Path.GetExtension(outputFile).Substring(1));
|
||||
outputFile = outputFile.Replace(Path.GetFileName(outputFile), outFileName);
|
||||
}
|
||||
using (FileStream output = File.Create(outputFile))
|
||||
{
|
||||
outStr.WriteTo(output);
|
||||
}
|
||||
decompressed = true;
|
||||
Console.WriteLine(format.ShortFormatString + "-decompressed " + input + " to " + outputFile);
|
||||
break;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
if (!decompressed)
|
||||
{
|
||||
#region copy or print and continue
|
||||
|
||||
if (copyErrors)
|
||||
{
|
||||
Copy(input, outputFile);
|
||||
}
|
||||
else
|
||||
Console.WriteLine("No suitable decompressor found for " + input + ".");
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
Console.WriteLine("The file " + input + " does not exist.");
|
||||
continue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Could not load file " + input + ";");
|
||||
Console.WriteLine(ex.Message);
|
||||
#if DEBUG
|
||||
Console.WriteLine(ex.StackTrace);
|
||||
#endif
|
||||
}
|
||||
} // end foreach input
|
||||
}
|
||||
#endregion Method: Decompress
|
||||
|
||||
#region Method: Compress
|
||||
private static void Compress(string[] ioArgs, CompressionFormat format)
|
||||
{
|
||||
string[] inputFiles;
|
||||
string outputDir;
|
||||
bool copyErrors;
|
||||
if (!ParseIOArguments(ioArgs, true, out inputFiles, out outputDir, out copyErrors))
|
||||
return;
|
||||
|
||||
foreach (string input in inputFiles)
|
||||
{
|
||||
string outputFile = outputDir ?? IOUtils.GetParent(input);
|
||||
if (Directory.Exists(outputDir))
|
||||
outputFile = Path.Combine(outputFile + Path.DirectorySeparatorChar, Path.GetFileName(input));
|
||||
|
||||
try
|
||||
{
|
||||
// read the file only once.
|
||||
byte[] inputData;
|
||||
using (Stream inStream = File.OpenRead(input))
|
||||
{
|
||||
inputData = new byte[inStream.Length];
|
||||
inStream.Read(inputData, 0, inputData.Length);
|
||||
}
|
||||
bool compressed = false;
|
||||
|
||||
#region try to compress
|
||||
|
||||
using (MemoryStream inStr = new MemoryStream(inputData),
|
||||
outStr = new MemoryStream())
|
||||
{
|
||||
try
|
||||
{
|
||||
long compSize = format.Compress(inStr, inputData.Length, outStr);
|
||||
if (compSize > 0)
|
||||
{
|
||||
using (FileStream output = File.Create(outputFile))
|
||||
{
|
||||
outStr.WriteTo(output);
|
||||
}
|
||||
compressed = true;
|
||||
Console.WriteLine(format.ShortFormatString + "-compressed " + input + " to " + outputFile);
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
if (!compressed)
|
||||
{
|
||||
#region copy or print and continue
|
||||
|
||||
if (copyErrors)
|
||||
{
|
||||
Copy(input, outputFile);
|
||||
}
|
||||
else
|
||||
Console.WriteLine("Could not " + format.ShortFormatString + "-compress " + input + ".");
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
Console.WriteLine("The file " + input + " does not exist.");
|
||||
continue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Could not load file " + input + ";");
|
||||
Console.WriteLine(ex.Message);
|
||||
#if DEBUG
|
||||
Console.WriteLine(ex.StackTrace);
|
||||
#endif
|
||||
}
|
||||
} // end foreach input
|
||||
}
|
||||
#endregion Method: Compress
|
||||
|
||||
#region Method: ParseIOArguments
|
||||
/// <summary>
|
||||
/// Parses the IO arguments of the input.
|
||||
/// </summary>
|
||||
/// <param name="ioArgs">The arguments to parse.</param>
|
||||
/// <param name="inputFiles">The files to handle as input.</param>
|
||||
/// <param name="outputDir">The directory to save the handled files in. If this is null,
|
||||
/// the files should be overwritten. If this does not exist, it is the output file
|
||||
/// (the input may only contain one file if that si the case).</param>
|
||||
/// <param name="copyErrors">If files that cannot be handled (properly) should be copied to the output directory.</param>
|
||||
/// <returns>True iff parsing of the arguments succeeded.</returns>
|
||||
private static bool ParseIOArguments(string[] ioArgs, bool compress, out string[] inputFiles, out string outputDir, out bool copyErrors)
|
||||
{
|
||||
inputFiles = null;
|
||||
// when null, output dir = input dir. if it does not exist, it is the output file (only possible when only one input file).
|
||||
outputDir = null;
|
||||
copyErrors = false;
|
||||
|
||||
#region check if the -co flag is present
|
||||
if (ioArgs.Length > 0 && ioArgs[ioArgs.Length - 1] == "-co")
|
||||
{
|
||||
string[] newIoArgs = new string[ioArgs.Length - 1];
|
||||
Array.Copy(ioArgs, newIoArgs, newIoArgs.Length);
|
||||
ioArgs = newIoArgs;
|
||||
copyErrors = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
switch (ioArgs.Length)
|
||||
{
|
||||
case 0:
|
||||
Console.WriteLine("No input file given.");
|
||||
return false;
|
||||
case 1:
|
||||
if (Directory.Exists(ioArgs[0]))
|
||||
{
|
||||
inputFiles = Directory.GetFiles(ioArgs[0]);
|
||||
if (compress)
|
||||
outputDir = Path.GetFullPath(ioArgs[0]) + "_cmp";
|
||||
else
|
||||
outputDir = Path.GetFullPath(ioArgs[0]) + "_dec";
|
||||
if (!Directory.Exists(outputDir))
|
||||
Directory.CreateDirectory(outputDir);
|
||||
break;
|
||||
}
|
||||
else if (File.Exists(ioArgs[0]))
|
||||
{
|
||||
inputFiles = ioArgs;
|
||||
outputDir = null;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("The file " + ioArgs[0] + " does not exist.");
|
||||
return false;
|
||||
}
|
||||
case 2:
|
||||
if (Directory.Exists(ioArgs[0]))
|
||||
{
|
||||
inputFiles = Directory.GetFiles(ioArgs[0]);
|
||||
outputDir = ioArgs[1];
|
||||
if (!Directory.Exists(outputDir))
|
||||
Directory.CreateDirectory(outputDir);
|
||||
break;
|
||||
}
|
||||
else if (File.Exists(ioArgs[0]))
|
||||
{
|
||||
if (File.Exists(ioArgs[1]))
|
||||
{
|
||||
inputFiles = ioArgs;
|
||||
outputDir = null;
|
||||
break;
|
||||
}
|
||||
else// if (Directory.Exists(ioArgs[1]))
|
||||
// both nonexisting file and existing directory is handled the same.
|
||||
{
|
||||
inputFiles = new string[] { ioArgs[0] };
|
||||
outputDir = ioArgs[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("The file " + ioArgs[0] + " does not exist.");
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
if (File.Exists(ioArgs[ioArgs.Length - 1]))
|
||||
{
|
||||
inputFiles = ioArgs;
|
||||
outputDir = null;
|
||||
break;
|
||||
}
|
||||
else //if (Directory.Exists(ioArgs[ioArgs.Length - 1]))
|
||||
// both existing and nonexisting directories are fine.
|
||||
{
|
||||
outputDir = ioArgs[ioArgs.Length - 1];
|
||||
inputFiles = new string[ioArgs.Length - 1];
|
||||
Array.Copy(ioArgs, inputFiles, inputFiles.Length);
|
||||
|
||||
// but we must make sure the output directory exists.
|
||||
if (!Directory.Exists(outputDir))
|
||||
Directory.CreateDirectory(outputDir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion ParseIOArguments
|
||||
|
||||
private static string GuessExtension(byte[] magic, string defaultExt)
|
||||
{
|
||||
string ext = "";
|
||||
for (int i = 0; i < magic.Length && i < 4; i++)
|
||||
{
|
||||
if ((magic[i] >= 'a' && magic[i] <= 'z') || (magic[i] >= 'A' && magic[i] <= 'Z')
|
||||
|| char.IsDigit((char)magic[i]))
|
||||
{
|
||||
ext += (char)magic[i];
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (ext.Length <= 1)
|
||||
return defaultExt;
|
||||
return ext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the source file to the destination path.
|
||||
/// </summary>
|
||||
private static void Copy(string sourcefile, string destfile)
|
||||
{
|
||||
if (Path.GetFullPath(sourcefile) == Path.GetFullPath(destfile))
|
||||
return;
|
||||
File.Copy(sourcefile, destfile);
|
||||
Console.WriteLine("Copied " + sourcefile + " to " + destfile);
|
||||
}
|
||||
|
||||
#region Format sequence getters
|
||||
/// <summary>
|
||||
/// Gets the compression format corresponding to the given format string.
|
||||
/// </summary>
|
||||
private static IEnumerable<CompressionFormat> GetFormat(string formatstring)
|
||||
{
|
||||
if (formatstring == null)
|
||||
yield break;
|
||||
foreach (CompressionFormat fmt in GetAllFormats(true))
|
||||
if (fmt.CompressionFlag == formatstring)
|
||||
{
|
||||
yield return fmt;
|
||||
yield break;
|
||||
}
|
||||
Console.WriteLine("No such compression format: " + formatstring);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a sequence over all compression formats currently supported; both built-in and plugin-based.
|
||||
/// </summary>
|
||||
private static IEnumerable<CompressionFormat> GetAllFormats(bool alsoBuiltInCompositeFormats)
|
||||
{
|
||||
foreach (CompressionFormat fmt in GetBuiltInFormats(alsoBuiltInCompositeFormats))
|
||||
yield return fmt;
|
||||
foreach (CompressionFormat fmt in GetPluginFormats())
|
||||
yield return fmt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a sequence over all built-in compression formats.
|
||||
/// </summary>
|
||||
/// <param name="alsoCompositeFormats">If the built-in composite formats should also be part of the sequence.</param>
|
||||
private static IEnumerable<CompressionFormat> GetBuiltInFormats(bool alsoCompositeFormats)
|
||||
{
|
||||
yield return new LZOvl();
|
||||
yield return new LZ10();
|
||||
yield return new LZ11();
|
||||
yield return new Huffman4();
|
||||
yield return new Huffman8();
|
||||
yield return new RLE();
|
||||
if (alsoCompositeFormats)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a sequence over all formats that can be used from plugins.
|
||||
/// </summary>
|
||||
private static IEnumerable<CompressionFormat> GetPluginFormats()
|
||||
{
|
||||
string pluginPath = Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName;
|
||||
pluginPath = Path.Combine(pluginPath, PluginFolder);
|
||||
if (System.IO.Directory.Exists(pluginPath))
|
||||
{
|
||||
foreach (CompressionFormat fmt in IOUtils.LoadCompressionPlugins(pluginPath))
|
||||
yield return fmt;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Plugin folder " + pluginPath + " is not present; only built-in formats are supported.");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first item from the given sequence, or the default value of the type in the sequence
|
||||
/// if it is empty.
|
||||
/// </summary>
|
||||
private static T FirstOrDefault<T>(IEnumerable<T> sequence)
|
||||
{
|
||||
if (sequence != null)
|
||||
{
|
||||
IEnumerator<T> enumerator = sequence.GetEnumerator();
|
||||
if (enumerator.MoveNext())
|
||||
return enumerator.Current;
|
||||
}
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
}
|
@ -48,7 +48,14 @@ namespace DSDecmp
|
||||
static bool AllowOVL = true;
|
||||
static bool ForceOVL = false;
|
||||
|
||||
/*
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine(-14 % 10);
|
||||
Console.ReadLine();
|
||||
}/**/
|
||||
|
||||
public static void Main1(string[] args)
|
||||
{
|
||||
|
||||
if (args.Length == 0) { Usage(); return; }
|
||||
@ -452,7 +459,8 @@ namespace DSDecmp
|
||||
byte treeSize = br.ReadByte();
|
||||
HuffTreeNode.maxInpos = 4 + (treeSize + 1) * 2;
|
||||
|
||||
//Console.WriteLine("Tree Size: {0:x}", treeSize);
|
||||
Console.WriteLine("Tree Size: {0:x}", treeSize);
|
||||
Console.WriteLine("Tee end: 0x{0:X}", HuffTreeNode.maxInpos);
|
||||
|
||||
HuffTreeNode rootNode = new HuffTreeNode();
|
||||
rootNode.parseData(br);
|
||||
@ -476,11 +484,16 @@ namespace DSDecmp
|
||||
string codestr = "";
|
||||
LinkedList<byte> code = new LinkedList<byte>();
|
||||
int value;
|
||||
|
||||
decomp_size = 0x100;
|
||||
|
||||
while (curr_size < decomp_size)
|
||||
{
|
||||
try
|
||||
{
|
||||
codestr += uint_to_bits(indata[++idx]);
|
||||
string newstr = uint_to_bits(indata[++idx]);
|
||||
codestr += newstr;
|
||||
Console.WriteLine("next uint: "+newstr);
|
||||
}
|
||||
catch (IndexOutOfRangeException e)
|
||||
{
|
||||
@ -489,9 +502,11 @@ namespace DSDecmp
|
||||
while (codestr.Length > 0)
|
||||
{
|
||||
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;
|
||||
@ -505,6 +520,7 @@ namespace DSDecmp
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (codestr.Length > 0 || idx < indata.Length-1)
|
||||
{
|
||||
while (idx < indata.Length-1)
|
||||
@ -512,7 +528,9 @@ namespace DSDecmp
|
||||
codestr = codestr.Replace("0", "");
|
||||
if (codestr.Length > 0)
|
||||
Console.WriteLine("too much data; str={0:s}, idx={1:g}/{2:g}", codestr, idx, indata.Length);
|
||||
}
|
||||
}/**/
|
||||
|
||||
br.Close();
|
||||
|
||||
byte[] realout;
|
||||
if (dataSize == 4)
|
||||
@ -1426,7 +1444,7 @@ namespace DSDecmp
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (data < 0)
|
||||
if (data < 0 && node0 != null && node1 != null)
|
||||
return "<" + node0.ToString() + ", " + node1.ToString() + ">";
|
||||
else
|
||||
return String.Format("[{0:x}]", data);
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using DSDecmp.Formats.Nitro;
|
||||
using System.IO;
|
||||
|
||||
namespace DSDecmp
|
||||
@ -9,10 +8,44 @@ namespace DSDecmp
|
||||
class TestProgram
|
||||
{
|
||||
|
||||
public static void Main2(string[] args)
|
||||
public static void MainTest(string[] args)
|
||||
{
|
||||
/*
|
||||
new RLE().Compress("tests/rle/testdata.dat", "tests/rle/cmp/testdata.rle.dat");
|
||||
new RLE().Decompress("tests/rle/cmp/testdata.rle.dat", "tests/rle/dec/testdata.elr.dat");
|
||||
/**/
|
||||
|
||||
//Program.Main1(new string[] { "tests/huff/00.dat", "tests/huff/dec2/" });
|
||||
//Console.WriteLine("-----------------------------------------------------");
|
||||
//new Huffman().Decompress("tests/huff/00.dat", "tests/huff/dec/00.ffuh.dat");
|
||||
/**/
|
||||
//new LZ11().Decompress("tests/lz11/game_over_NCGR.cdat", "tests/lz11/dec/game_over.11zl.NCGR");
|
||||
|
||||
//new LZOvl().Decompress("tests/lzovl/overlay_0001.bin", "tests/lzovl/dec/overlay_0001.dat");
|
||||
|
||||
//new LZ10().Decompress("tests/lz10/npc002_LZ.bin", "tests/lz10/dec/npc002.narc");
|
||||
//LZ10.LookAhead = true;
|
||||
//new LZ10().Compress("tests/lz10/dec/npc002.narc", "tests/lz10/cmp/npc002_d.narc.lz");
|
||||
//new LZ10().Decompress("tests/lz10/cmp/npc002_d.narc.lz", "tests/lz10/cmpdec/npc002.narc");
|
||||
|
||||
//LZ11.LookAhead = true;
|
||||
//new LZ11().Compress("tests/lz11/dec/game_over.11zl.NCGR", "tests/lz11/cmp/game_over.NCGR2.lz11");
|
||||
//new LZ11().Decompress("tests/lz11/cmp/game_over.NCGR2.lz11", "tests/lz11/cmpdec/game_over.NCGR");
|
||||
|
||||
//LZOvl.LookAhead = true;
|
||||
//new LZOvl().Compress("tests/lzovl/dec/overlay_0001.dat", "tests/lzovl/cmp/overlay_0001b.bin");
|
||||
//new LZOvl().Decompress("tests/lzovl/cmp/overlay_0001b.bin", "tests/lzovl/cmpdec/overlay_0001.dat");
|
||||
|
||||
//Huffman.CompressBlockSize = Huffman.BlockSize.FOURBIT;
|
||||
//new Huffman().Compress("tests/huff/dec/00.ffuh.dat", "tests/huff/cmp/00.huff4");
|
||||
//new Huffman().Decompress("tests/huff/cmp/00.huff4", "tests/huff/cmpdec/00.dat");
|
||||
//new Huffman().Compress("tests/huff/test.dat", "tests/huff/cmp/test.huff");
|
||||
//new Huffman().Decompress("tests/huff/cmp/test.huff", "tests/huff/cmpdec/test.dat");
|
||||
|
||||
//new LZOvl().Decompress("tests/lzovl2/overlay_0001.bin", "tests/lzovl2/overlay_0001.dat");
|
||||
|
||||
//new LuminousArc().Decompress("tests/Le/advimg00.imb", "tests/Le/dec/advimg00.imb");
|
||||
|
||||
Console.WriteLine("Success?");
|
||||
Console.ReadLine();
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
|
||||
namespace DSDecmp
|
||||
{
|
||||
@ -9,6 +11,8 @@ namespace DSDecmp
|
||||
/// </summary>
|
||||
public static class IOUtils
|
||||
{
|
||||
|
||||
#region byte[] <-> (u)int
|
||||
/// <summary>
|
||||
/// Returns a 4-byte unsigned integer as used on the NDS converted from four bytes
|
||||
/// at a specified position in a byte array.
|
||||
@ -66,5 +70,78 @@ namespace DSDecmp
|
||||
| (buffer[offset + 1] << 8)
|
||||
| (buffer[offset + 2] << 16));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Plugin loading
|
||||
/// <summary>
|
||||
/// (Attempts to) load compression formats from the given file.
|
||||
/// </summary>
|
||||
/// <param name="file">The dll file to load.</param>
|
||||
/// <param name="printFailures">If formats without an empty contrsuctor should get a print.</param>
|
||||
/// <returns>A list with an instance of all compression formats found in the given dll file.</returns>
|
||||
/// <exception cref="FileNotFoundException">If the given file does not exist.</exception>
|
||||
/// <exception cref="FileLoadException">If the file could not be loaded.</exception>
|
||||
/// <exception cref="BadImageFormatException">If the file is not a valid assembly, or the loaded
|
||||
/// assembly is compiled with a higher version of .NET.</exception>
|
||||
internal static IEnumerable<CompressionFormat> LoadCompressionPlugin(string file, bool printFailures = false)
|
||||
{
|
||||
if (file == null)
|
||||
throw new FileNotFoundException("A null-path cannot be loaded.");
|
||||
List<CompressionFormat> newFormats = new List<CompressionFormat>();
|
||||
|
||||
string fullPath = Path.GetFullPath(file);
|
||||
|
||||
Assembly dll = Assembly.LoadFile(fullPath);
|
||||
foreach (Type dllType in dll.GetTypes())
|
||||
{
|
||||
if (dllType.IsSubclassOf(typeof(CompressionFormat))
|
||||
&& !dllType.IsAbstract)
|
||||
{
|
||||
try
|
||||
{
|
||||
newFormats.Add(Activator.CreateInstance(dllType) as CompressionFormat);
|
||||
}
|
||||
catch (MissingMethodException)
|
||||
{
|
||||
if (printFailures)
|
||||
Console.WriteLine(dllType + " is a compression format, but does not have a parameterless constructor. Format cannot be loaded from " + fullPath + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newFormats;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all compression formats found in the given folder.
|
||||
/// </summary>
|
||||
/// <param name="folder">The folder to load plugins from.</param>
|
||||
/// <returns>A list with an instance of all compression formats found in the given folder.</returns>
|
||||
internal static IEnumerable<CompressionFormat> LoadCompressionPlugins(string folder)
|
||||
{
|
||||
List<CompressionFormat> formats = new List<CompressionFormat>();
|
||||
|
||||
foreach (string file in Directory.GetFiles(folder))
|
||||
{
|
||||
try
|
||||
{
|
||||
formats.AddRange(LoadCompressionPlugin(file, false));
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
return formats;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to the parent directory of the given path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to get the parent directory path of.</param>
|
||||
/// <returns>The full path to the parent directory of teh given path.</returns>
|
||||
public static string GetParent(string path)
|
||||
{
|
||||
return Directory.GetParent(path).FullName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace DSDecmp.Utils
|
||||
namespace DSDecmp
|
||||
{
|
||||
internal static class LZUtil
|
||||
/// <summary>
|
||||
/// Utility class for compression using LZ-like compression schemes.
|
||||
/// </summary>
|
||||
public static class LZUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Determine the maximum size of a LZ-compressed block starting at newPtr, using the already compressed data
|
||||
@ -16,7 +19,7 @@ namespace DSDecmp.Utils
|
||||
/// <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>
|
||||
/// <returns>The length of the longest sequence of bytes that can be copied from the already decompressed data.</returns>
|
||||
internal 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)
|
||||
{
|
||||
disp = 0;
|
||||
if (newLength == 0)
|
||||
|
@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace DSDecmp.Utils
|
||||
namespace DSDecmp
|
||||
{
|
||||
/// <summary>
|
||||
/// Very simplistic implementation of a priority queue that returns items with lowest priority first.
|
||||
@ -17,14 +17,25 @@ namespace DSDecmp.Utils
|
||||
private SortedDictionary<TPrio, LinkedList<TValue>> items;
|
||||
private int itemCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of items in this queue.
|
||||
/// </summary>
|
||||
public int Count { get { return this.itemCount; } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new, empty reverse priority queue.
|
||||
/// </summary>
|
||||
public SimpleReversedPrioQueue()
|
||||
{
|
||||
this.items = new SortedDictionary<TPrio, LinkedList<TValue>>();
|
||||
this.itemCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues the given value, using the given priority.
|
||||
/// </summary>
|
||||
/// <param name="priority">The priority of the value.</param>
|
||||
/// <param name="value">The value to enqueue.</param>
|
||||
public void Enqueue(TPrio priority, TValue value)
|
||||
{
|
||||
if (!this.items.ContainsKey(priority))
|
||||
@ -33,6 +44,12 @@ namespace DSDecmp.Utils
|
||||
this.itemCount++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value with the lowest priority from this queue, without dequeueing the value.
|
||||
/// </summary>
|
||||
/// <param name="priority">The priority of the returned value.</param>
|
||||
/// <returns>The current value with the lowest priority.</returns>
|
||||
/// <exception cref="IndexOutOfRangeException">If there are no items left in this queue.</exception>
|
||||
public TValue Peek(out TPrio priority)
|
||||
{
|
||||
if (this.itemCount == 0)
|
||||
@ -45,6 +62,12 @@ namespace DSDecmp.Utils
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeues the current value at the head of thisreverse priority queue.
|
||||
/// </summary>
|
||||
/// <param name="priority">The priority of the dequeued value.</param>
|
||||
/// <returns>The dequeued value, that used to be at the head of this queue.</returns>
|
||||
/// <exception cref="IndexOutOfRangeException">If this queue does not contain any items.</exception>
|
||||
public TValue Dequeue(out TPrio priority)
|
||||
{
|
||||
if (this.itemCount == 0)
|
||||
|
@ -2,11 +2,33 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using DSDecmp;
|
||||
|
||||
namespace DSDecmp.Formats.GameSpecific
|
||||
namespace GameFormats
|
||||
{
|
||||
public class GoldenSunDD : CompressionFormat
|
||||
{
|
||||
|
||||
public override string ShortFormatString
|
||||
{
|
||||
get { return "GSDD"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "A variant of the LZ-0x11 scheme found in Golden Sun: Dark Dawn."; }
|
||||
}
|
||||
|
||||
public override string CompressionFlag
|
||||
{
|
||||
get { return "gsdd"; }
|
||||
}
|
||||
|
||||
public override bool SupportsCompression
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool Supports(System.IO.Stream stream, long inLength)
|
||||
{
|
||||
long streamStart = stream.Position;
|
||||
@ -50,11 +72,7 @@ namespace DSDecmp.Formats.GameSpecific
|
||||
*
|
||||
* for each chunk:
|
||||
* - first byte determines which blocks are compressed
|
||||
* - block i is compressed iff:
|
||||
* - the i'th MSB is the last 1-bit in the byte
|
||||
* - OR the i'th MSB is a 0-bit, not directly followed by other 0-bits.
|
||||
* - note that there will never be more than one 0-bit before any 1-bit in this byte
|
||||
* (look at the corresponding code, it may clarify this a bit more)
|
||||
* multiply by -1 to get the proper flags (1->compressed, 0->raw)
|
||||
* - then come 8 blocks:
|
||||
* - a non-compressed block is simply one single byte
|
||||
* - a compressed block can have 3 sizes:
|
||||
@ -112,6 +130,7 @@ namespace DSDecmp.Formats.GameSpecific
|
||||
// determine which blocks are compressed
|
||||
int b = 0;
|
||||
expandedFlags = new bool[8];
|
||||
// flags = -flags
|
||||
while (flags > 0)
|
||||
{
|
||||
bool bit = (flags & 0x80) > 0;
|
57
CSharp/GoldenSunDD/GoldenSunDD.csproj
Normal file
57
CSharp/GoldenSunDD/GoldenSunDD.csproj
Normal file
@ -0,0 +1,57 @@
|
||||
<?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)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{8CE72663-0036-4A94-BD70-99AFE7CEEC0C}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GoldenSunDD</RootNamespace>
|
||||
<AssemblyName>GoldenSunDD</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\PluginDistro\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\PluginDistro\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="GoldenSunDD.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DSDecmp\DSDecmp.csproj">
|
||||
<Project>{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}</Project>
|
||||
<Name>DSDecmp</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>
|
36
CSharp/GoldenSunDD/Properties/AssemblyInfo.cs
Normal file
36
CSharp/GoldenSunDD/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("GoldenSunDD")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GoldenSunDD")]
|
||||
[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("15c63bf0-dd3c-4ef2-b3c4-6c6278475ec9")]
|
||||
|
||||
// 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")]
|
304
CSharp/LuminousArc/LuminousArc.cs
Normal file
304
CSharp/LuminousArc/LuminousArc.cs
Normal file
@ -0,0 +1,304 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using DSDecmp;
|
||||
|
||||
namespace GameFormats
|
||||
{
|
||||
/// <summary>
|
||||
/// Compressor/decompressor for the LZE format found in Luminous Arc games. Format specification by Roger Pepitone; http://pastebin.com/qNgSB2f9
|
||||
/// </summary>
|
||||
public class LuminousArc : CompressionFormat
|
||||
{
|
||||
|
||||
public override string ShortFormatString
|
||||
{
|
||||
get { return "LZE/Le"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "A variant of an LZ/RLE scheme found in Luminous Arc games."; }
|
||||
}
|
||||
|
||||
public override string CompressionFlag
|
||||
{
|
||||
get { return "Le"; }
|
||||
}
|
||||
|
||||
public override bool SupportsCompression
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/*
|
||||
* An LZE / Le file consists of the following:
|
||||
- A six byte header
|
||||
- A series of blocks
|
||||
|
||||
The header consists of:
|
||||
- 2 bytes: 0x4c, 0x65 ("Le" in ASCII)
|
||||
- 4 bytes: the size of the uncompressed data in little-endian format
|
||||
|
||||
Each block consists of:
|
||||
- 1 byte: the types for the following mini-records
|
||||
(2 bits per type, stored with the first type at the least
|
||||
significant bit)
|
||||
- 4 mini-records
|
||||
|
||||
|
||||
Each mini-record consists of:
|
||||
- If its type is 0:
|
||||
-- 2 bytes BYTE1 BYTE2: Write (3 + (BYTE2 >> 4)) bytes from
|
||||
back (5 + (BYTE1 | ((BYTE2 & 0xf) << 8))) to output
|
||||
- If its type is 1:
|
||||
-- 1 byte BYTE1: Write (2 + (BYTE >> 2)) bytes from
|
||||
back (1 + (BYTE & 3)) to output
|
||||
- If its type is 2:
|
||||
-- 1 byte: (copied to output stream)
|
||||
- If its type is 3:
|
||||
-- 3 bytes: (copied to output stream)
|
||||
|
||||
|
||||
The last block may go over the end; if so, ignore any excess data.
|
||||
*/
|
||||
|
||||
#region Method: Supports(Stream, inLength)
|
||||
/// <summary>
|
||||
/// Determines if this format may potentially be used to decompress the given stream.
|
||||
/// Does not guarantee success when returning true, but does guarantee failure when returning false.
|
||||
/// </summary>
|
||||
public override bool Supports(System.IO.Stream stream, long inLength)
|
||||
{
|
||||
long streamStart = stream.Position;
|
||||
try
|
||||
{
|
||||
if (inLength <= 6) // min 6 byte header
|
||||
return false;
|
||||
|
||||
byte[] header = new byte[2];
|
||||
stream.Read(header, 0, 2);
|
||||
if (header[0] != 'L' || header[1] != 'e')
|
||||
return false;
|
||||
|
||||
byte[] outLength = new byte[4];
|
||||
stream.Read(outLength, 0, 4);
|
||||
if (IOUtils.ToNDSu32(outLength, 0) == 0)
|
||||
return inLength == 6;
|
||||
|
||||
// as long as the magic is OK, anything else is fine for now. (for this superficial check)
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
stream.Position = streamStart;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override long Decompress(System.IO.Stream instream, long inLength, System.IO.Stream outstream)
|
||||
{
|
||||
long readBytes = 0;
|
||||
|
||||
byte[] magic = new byte[2];
|
||||
instream.Read(magic, 0, 2);
|
||||
if (magic[0] != 'L' || magic[1] != 'e')
|
||||
throw new InvalidDataException("The provided stream is not a valid LZE (Le) "
|
||||
+ "compressed stream (invalid magic '" + (char)magic[0] + (char)magic[1] + "')");
|
||||
byte[] sizeBytes = new byte[4];
|
||||
instream.Read(sizeBytes, 0, 4);
|
||||
uint decompressedSize = IOUtils.ToNDSu32(sizeBytes, 0);
|
||||
readBytes += 4;
|
||||
|
||||
// the maximum 'DISP-5' is 0xFFF.
|
||||
int bufferLength = 0xFFF + 5;
|
||||
byte[] buffer = new byte[bufferLength];
|
||||
int bufferOffset = 0;
|
||||
|
||||
|
||||
int currentOutSize = 0;
|
||||
int flags = 0, mask = 3;
|
||||
while (currentOutSize < decompressedSize)
|
||||
{
|
||||
// (throws when requested new flags byte is not available)
|
||||
#region Update the mask. If all flag bits have been read, get a new set.
|
||||
// the current mask is the mask used in the previous run. So if it masks the
|
||||
// last flag bit, get a new flags byte.
|
||||
if (mask == 3)
|
||||
{
|
||||
if (readBytes >= inLength)
|
||||
throw new NotEnoughDataException(currentOutSize, decompressedSize);
|
||||
flags = instream.ReadByte(); readBytes++;
|
||||
if (flags < 0)
|
||||
throw new StreamTooShortException();
|
||||
mask = 0xC0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask >>= 2;
|
||||
flags >>= 2;
|
||||
}
|
||||
#endregion
|
||||
|
||||
switch (flags & 0x3)
|
||||
{
|
||||
case 0:
|
||||
#region 0 -> LZ10-like format
|
||||
{
|
||||
#region Get length and displacement('disp') values from next 2 bytes
|
||||
// there are < 2 bytes available when the end is at most 1 byte away
|
||||
if (readBytes + 1 >= inLength)
|
||||
{
|
||||
// make sure the stream is at the end
|
||||
if (readBytes < inLength)
|
||||
{
|
||||
instream.ReadByte(); readBytes++;
|
||||
}
|
||||
throw new NotEnoughDataException(currentOutSize, decompressedSize);
|
||||
}
|
||||
int byte1 = instream.ReadByte(); readBytes++;
|
||||
int byte2 = instream.ReadByte(); readBytes++;
|
||||
if (byte2 < 0)
|
||||
throw new StreamTooShortException();
|
||||
|
||||
// the number of bytes to copy
|
||||
int length = byte2 >> 4;
|
||||
length += 3;
|
||||
|
||||
// from where the bytes should be copied (relatively)
|
||||
int disp = ((byte2 & 0x0F) << 8) | byte1;
|
||||
disp += 5;
|
||||
|
||||
if (disp > currentOutSize)
|
||||
throw new InvalidDataException("Cannot go back more than already written. "
|
||||
+ "DISP = 0x" + disp.ToString("X") + ", #written bytes = 0x" + currentOutSize.ToString("X")
|
||||
+ " at 0x" + (instream.Position - 2).ToString("X"));
|
||||
#endregion
|
||||
|
||||
int bufIdx = bufferOffset + bufferLength - disp;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
byte next = buffer[bufIdx % bufferLength];
|
||||
bufIdx++;
|
||||
outstream.WriteByte(next);
|
||||
buffer[bufferOffset] = next;
|
||||
bufferOffset = (bufferOffset + 1) % bufferLength;
|
||||
}
|
||||
currentOutSize += length;
|
||||
|
||||
break;
|
||||
}
|
||||
#endregion
|
||||
case 1:
|
||||
#region 1 -> compact LZ10-like format
|
||||
{
|
||||
#region Get length and displacement('disp') values from next byte
|
||||
// there are < 2 bytes available when the end is at most 1 byte away
|
||||
if (readBytes >= inLength)
|
||||
{
|
||||
throw new NotEnoughDataException(currentOutSize, decompressedSize);
|
||||
}
|
||||
int b = instream.ReadByte(); readBytes++;
|
||||
if (b < 0)
|
||||
throw new StreamTooShortException();
|
||||
|
||||
// the number of bytes to copy
|
||||
int length = b >> 2;
|
||||
length += 2;
|
||||
|
||||
// from where the bytes should be copied (relatively)
|
||||
int disp = (b & 0x03);
|
||||
disp += 1;
|
||||
|
||||
if (disp > currentOutSize)
|
||||
throw new InvalidDataException("Cannot go back more than already written. "
|
||||
+ "DISP = 0x" + disp.ToString("X") + ", #written bytes = 0x" + currentOutSize.ToString("X")
|
||||
+ " at 0x" + (instream.Position - 1).ToString("X"));
|
||||
#endregion
|
||||
|
||||
int bufIdx = bufferOffset + bufferLength - disp;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
byte next = buffer[bufIdx % bufferLength];
|
||||
bufIdx++;
|
||||
outstream.WriteByte(next);
|
||||
buffer[bufferOffset] = next;
|
||||
bufferOffset = (bufferOffset + 1) % bufferLength;
|
||||
}
|
||||
currentOutSize += length;
|
||||
break;
|
||||
}
|
||||
#endregion
|
||||
case 2:
|
||||
#region 2 -> copy 1 byte
|
||||
{
|
||||
if (readBytes >= inLength)
|
||||
throw new NotEnoughDataException(currentOutSize, decompressedSize);
|
||||
int next = instream.ReadByte(); readBytes++;
|
||||
if (next < 0)
|
||||
throw new StreamTooShortException();
|
||||
|
||||
currentOutSize++;
|
||||
outstream.WriteByte((byte)next);
|
||||
buffer[bufferOffset] = (byte)next;
|
||||
bufferOffset = (bufferOffset + 1) % bufferLength;
|
||||
break;
|
||||
}
|
||||
#endregion
|
||||
case 3:
|
||||
#region 3 -> copy 3 bytes
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (readBytes >= inLength)
|
||||
throw new NotEnoughDataException(currentOutSize, decompressedSize);
|
||||
int next = instream.ReadByte(); readBytes++;
|
||||
if (next < 0)
|
||||
throw new StreamTooShortException();
|
||||
|
||||
currentOutSize++;
|
||||
outstream.WriteByte((byte)next);
|
||||
buffer[bufferOffset] = (byte)next;
|
||||
bufferOffset = (bufferOffset + 1) % bufferLength;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endregion
|
||||
default:
|
||||
throw new Exception("BUG: Mask is not 2 bits long!");
|
||||
}
|
||||
outstream.Flush();
|
||||
}
|
||||
|
||||
if (readBytes < inLength)
|
||||
{
|
||||
// the input may be 4-byte aligned.
|
||||
if ((readBytes ^ (readBytes & 3)) + 4 < inLength)
|
||||
throw new TooMuchInputException(readBytes, inLength);
|
||||
// (this happens rather often for Le files?)
|
||||
}
|
||||
|
||||
return decompressedSize;
|
||||
}
|
||||
|
||||
private int MaskRsh(int value, int mask)
|
||||
{
|
||||
int maskCopy = mask;
|
||||
int masked = value & mask;
|
||||
if (maskCopy == 0)
|
||||
return masked;
|
||||
while ((maskCopy & 1) == 0)
|
||||
{
|
||||
masked >>= 1;
|
||||
maskCopy >>= 1;
|
||||
}
|
||||
return masked;
|
||||
}
|
||||
|
||||
public override int Compress(System.IO.Stream instream, long inLength, System.IO.Stream outstream)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
57
CSharp/LuminousArc/LuminousArc.csproj
Normal file
57
CSharp/LuminousArc/LuminousArc.csproj
Normal file
@ -0,0 +1,57 @@
|
||||
<?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)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{4BD8DF5C-E971-45D1-B170-340D22DDB351}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>LuminousArc</RootNamespace>
|
||||
<AssemblyName>LuminousArc</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\PluginDistro\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\PluginDistro\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="LuminousArc.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DSDecmp\DSDecmp.csproj">
|
||||
<Project>{E6F419F9-D6B5-4BE7-99BB-97C48C927FF3}</Project>
|
||||
<Name>DSDecmp</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>
|
36
CSharp/LuminousArc/Properties/AssemblyInfo.cs
Normal file
36
CSharp/LuminousArc/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("LuminousArc")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("LuminousArc")]
|
||||
[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("d74a7ef8-8f65-4ed0-ae05-a4a97130f047")]
|
||||
|
||||
// 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")]
|
647
CSharp/PluginDistro/DSDecmp.xml
Normal file
647
CSharp/PluginDistro/DSDecmp.xml
Normal file
@ -0,0 +1,647 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>DSDecmp</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:DSDecmp.TooMuchInputException">
|
||||
<summary>
|
||||
An exception indication that the input has more data than required in order
|
||||
to decompress it. This may indicate that more sub-files are present in the file.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.TooMuchInputException.#ctor(System.Int64,System.Int64)">
|
||||
<summary>
|
||||
Creates a new exception indicating that the input has more data than necessary for
|
||||
decompressing th stream. It may indicate that other data is present after the compressed
|
||||
stream.
|
||||
</summary>
|
||||
<param name="readBytes">The number of bytes read by the decompressor.</param>
|
||||
<param name="totLength">The indicated length of the input stream.</param>
|
||||
</member>
|
||||
<member name="P:DSDecmp.TooMuchInputException.ReadBytes">
|
||||
<summary>
|
||||
Gets the number of bytes read by the decompressed to decompress the stream.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.InputTooLargeException">
|
||||
<summary>
|
||||
An exception indicating that the file cannot be compressed, because the decompressed size
|
||||
cannot be represented in the current compression format.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.InputTooLargeException.#ctor">
|
||||
<summary>
|
||||
Creates a new exception that indicates that the input is too big to be compressed.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.IOUtils">
|
||||
<summary>
|
||||
Class for I/O-related utility methods.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.IOUtils.ToNDSu32(System.Byte[],System.Int32)">
|
||||
<summary>
|
||||
Returns a 4-byte unsigned integer as used on the NDS converted from four bytes
|
||||
at a specified position in a byte array.
|
||||
</summary>
|
||||
<param name="buffer">The source of the data.</param>
|
||||
<param name="offset">The location of the data in the source.</param>
|
||||
<returns>The indicated 4 bytes converted to uint</returns>
|
||||
</member>
|
||||
<member name="M:DSDecmp.IOUtils.ToNDSs32(System.Byte[],System.Int32)">
|
||||
<summary>
|
||||
Returns a 4-byte signed integer as used on the NDS converted from four bytes
|
||||
at a specified position in a byte array.
|
||||
</summary>
|
||||
<param name="buffer">The source of the data.</param>
|
||||
<param name="offset">The location of the data in the source.</param>
|
||||
<returns>The indicated 4 bytes converted to int</returns>
|
||||
</member>
|
||||
<member name="M:DSDecmp.IOUtils.FromNDSu32(System.UInt32)">
|
||||
<summary>
|
||||
Converts a u32 value into a sequence of bytes that would make ToNDSu32 return
|
||||
the given input value.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.IOUtils.ToNDSu24(System.Byte[],System.Int32)">
|
||||
<summary>
|
||||
Returns a 3-byte integer as used in the built-in compression
|
||||
formats in the DS, convrted from three bytes at a specified position in a byte array,
|
||||
</summary>
|
||||
<param name="buffer">The source of the data.</param>
|
||||
<param name="offset">The location of the data in the source.</param>
|
||||
<returns>The indicated 3 bytes converted to an integer.</returns>
|
||||
</member>
|
||||
<member name="M:DSDecmp.IOUtils.LoadCompressionPlugin(System.String,System.Boolean)">
|
||||
<summary>
|
||||
(Attempts to) load compression formats from the given file.
|
||||
</summary>
|
||||
<param name="file">The dll file to load.</param>
|
||||
<param name="printFailures">If formats without an empty contrsuctor should get a print.</param>
|
||||
<returns>A list with an instance of all compression formats found in the given dll file.</returns>
|
||||
<exception cref="T:System.IO.FileNotFoundException">If the given file does not exist.</exception>
|
||||
<exception cref="T:System.IO.FileLoadException">If the file could not be loaded.</exception>
|
||||
<exception cref="T:System.BadImageFormatException">If the file is not a valid assembly, or the loaded
|
||||
assembly is compiled with a higher version of .NET.</exception>
|
||||
</member>
|
||||
<member name="M:DSDecmp.IOUtils.LoadCompressionPlugins(System.String)">
|
||||
<summary>
|
||||
Loads all compression formats found in the given folder.
|
||||
</summary>
|
||||
<param name="folder">The folder to load plugins from.</param>
|
||||
<returns>A list with an instance of all compression formats found in the given folder.</returns>
|
||||
</member>
|
||||
<member name="M:DSDecmp.IOUtils.GetParent(System.String)">
|
||||
<summary>
|
||||
Gets the full path to the parent directory of the given path.
|
||||
</summary>
|
||||
<param name="path">The path to get the parent directory path of.</param>
|
||||
<returns>The full path to the parent directory of teh given path.</returns>
|
||||
</member>
|
||||
<member name="T:DSDecmp.CompressionFormat">
|
||||
<summary>
|
||||
Base class for all compression formats.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.CompressionFormat.Supports(System.String)">
|
||||
<summary>
|
||||
Checks if the decompressor for this format supports the given file. Assumes the
|
||||
file exists. Returns false when it is certain that the given file is not supported.
|
||||
False positives may occur, as this method should not do any decompression, and
|
||||
may mis-interpret a similar file format as compressed.
|
||||
</summary>
|
||||
<param name="file">The name of the file to check.</param>
|
||||
<returns>False if the file can certainly not be decompressed using this decompressor.
|
||||
True if the file may potentially be decompressed using this decompressor.</returns>
|
||||
</member>
|
||||
<member name="M:DSDecmp.CompressionFormat.Supports(System.IO.Stream,System.Int64)">
|
||||
<summary>
|
||||
Checks if the decompressor for this format supports the data from the given stream.
|
||||
Returns false when it is certain that the given data is not supported.
|
||||
False positives may occur, as this method should not do any decompression, and may
|
||||
mis-interpret a similar data format as compressed.
|
||||
</summary>
|
||||
<param name="stream">The stream that may or may not contain compressed data. The
|
||||
position of this stream may change during this call, but will be returned to its
|
||||
original position when the method returns.</param>
|
||||
<param name="inLength">The length of the input stream.</param>
|
||||
<returns>False if the data can certainly not be decompressed using this decompressor.
|
||||
True if the data may potentially be decompressed using this decompressor.</returns>
|
||||
</member>
|
||||
<member name="M:DSDecmp.CompressionFormat.Decompress(System.String,System.String)">
|
||||
<summary>
|
||||
Decompresses the given file, writing the deocmpressed data to the given output file.
|
||||
The output file will be overwritten if it already exists.
|
||||
Assumes <code>Supports(infile)</code> returns <code>true</code>.
|
||||
</summary>
|
||||
<param name="infile">The file to decompress.</param>
|
||||
<param name="outfile">The target location of the decompressed file.</param>
|
||||
</member>
|
||||
<member name="M:DSDecmp.CompressionFormat.Decompress(System.IO.Stream,System.Int64,System.IO.Stream)">
|
||||
<summary>
|
||||
Decompresses the given stream, writing the decompressed data to the given output stream.
|
||||
Assumes <code>Supports(instream)</code> returns <code>true</code>.
|
||||
After this call, the input stream will be positioned at the end of the compressed stream,
|
||||
or at the initial position + <code>inLength</code>, whichever comes first.
|
||||
</summary>
|
||||
<param name="instream">The stream to decompress. At the end of this method, the position
|
||||
of this stream is directly after the compressed data.</param>
|
||||
<param name="inLength">The length of the input data. Not necessarily all of the
|
||||
input data may be read (if there is padding, for example), however never more than
|
||||
this number of bytes is read from the input stream.</param>
|
||||
<param name="outstream">The stream to write the decompressed data to.</param>
|
||||
<returns>The length of the output data.</returns>
|
||||
<exception cref="T:DSDecmp.NotEnoughDataException">When the given length of the input data
|
||||
is not enough to properly decompress the input.</exception>
|
||||
</member>
|
||||
<member name="M:DSDecmp.CompressionFormat.Compress(System.String,System.String)">
|
||||
<summary>
|
||||
Compresses the given input file, and writes the compressed data to the given
|
||||
output file.
|
||||
</summary>
|
||||
<param name="infile">The file to compress.</param>
|
||||
<param name="outfile">The file to write the compressed data to.</param>
|
||||
<returns>The size of the compressed file. If -1, the file could not be compressed.</returns>
|
||||
</member>
|
||||
<member name="M:DSDecmp.CompressionFormat.Compress(System.IO.Stream,System.Int64,System.IO.Stream)">
|
||||
<summary>
|
||||
Compresses the next <code>inLength</code> bytes from the input stream,
|
||||
and writes the compressed data to the given output stream.
|
||||
</summary>
|
||||
<param name="instream">The stream to read plaintext data from.</param>
|
||||
<param name="inLength">The length of the plaintext data.</param>
|
||||
<param name="outstream">The stream to write the compressed data to.</param>
|
||||
<returns>The size of the compressed stream. If -1, the file could not be compressed.</returns>
|
||||
</member>
|
||||
<member name="M:DSDecmp.CompressionFormat.ParseCompressionOptions(System.String[])">
|
||||
<summary>
|
||||
Parses any input specific for this format. Does nothing by default.
|
||||
</summary>
|
||||
<param name="args">Any arguments that may be used by the format.</param>
|
||||
<returns>The number of input arguments consumed by this format.</returns>
|
||||
</member>
|
||||
<member name="P:DSDecmp.CompressionFormat.ShortFormatString">
|
||||
<summary>
|
||||
Gets a short string identifying this compression format.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.CompressionFormat.Description">
|
||||
<summary>
|
||||
Gets a short description of this compression format (used in the program usage).
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.CompressionFormat.SupportsCompression">
|
||||
<summary>
|
||||
Gets if this format supports compressing a file.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.CompressionFormat.SupportsDecompression">
|
||||
<summary>
|
||||
Gets if this format supports decompressing a file.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.CompressionFormat.CompressionFlag">
|
||||
<summary>
|
||||
Gets the value that must be given on the command line in order to compress using this format.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.LZUtil">
|
||||
<summary>
|
||||
Utility class for compression using LZ-like compression schemes.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.LZUtil.GetOccurrenceLength(System.Byte*,System.Int32,System.Byte*,System.Int32,System.Int32@)">
|
||||
<summary>
|
||||
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.
|
||||
</summary>
|
||||
<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="oldPtr">The start of the raw file.</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>
|
||||
<returns>The length of the longest sequence of bytes that can be copied from the already decompressed data.</returns>
|
||||
</member>
|
||||
<member name="T:DSDecmp.Formats.Nitro.NitroCFormat">
|
||||
<summary>
|
||||
Base class for Nitro-based decompressors. Uses the 1-byte magic and 3-byte decompression
|
||||
size format.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:DSDecmp.Formats.Nitro.NitroCFormat.SkipLargePlaintexts">
|
||||
<summary>
|
||||
If true, Nitro Decompressors will not decompress files that have a decompressed
|
||||
size (plaintext size) larger than MaxPlaintextSize.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:DSDecmp.Formats.Nitro.NitroCFormat.MaxPlaintextSize">
|
||||
<summary>
|
||||
The maximum allowed size of the decompressed file (plaintext size) allowed for Nitro
|
||||
Decompressors. Only used when SkipLargePlaintexts = true.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:DSDecmp.Formats.Nitro.NitroCFormat.magicByte">
|
||||
<summary>
|
||||
The first byte of every file compressed with the format for this particular
|
||||
Nitro Dcompressor instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.StreamTooShortException">
|
||||
<summary>
|
||||
An exception thrown by the compression or decompression function, indicating that the
|
||||
given input length was too large for the given input stream.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.StreamTooShortException.#ctor">
|
||||
<summary>
|
||||
Creates a new exception that indicates that the stream was shorter than the given input length.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.Formats.Nitro.LZ11">
|
||||
<summary>
|
||||
Compressor and decompressor for the LZ-0x11 format used in many of the games for the
|
||||
newer Nintendo consoles and handhelds.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.Nitro.LZ11.CompressWithLA(System.IO.Stream,System.Int64,System.IO.Stream)">
|
||||
<summary>
|
||||
Variation of the original compression method, making use of Dynamic Programming to 'look ahead'
|
||||
and determine the optimal 'length' values for the compressed blocks. Is not 100% optimal,
|
||||
as the flag-bytes are not taken into account.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.Nitro.LZ11.GetOptimalCompressionLengths(System.Byte*,System.Int32,System.Int32[]@,System.Int32[]@)">
|
||||
<summary>
|
||||
Gets the optimal compression lengths for each start of a compressed block using Dynamic Programming.
|
||||
This takes O(n^2) time, although in practice it will often be O(n^3) since one of the constants is 0x10110
|
||||
(the maximum length of a compressed block)
|
||||
</summary>
|
||||
<param name="indata">The data to compress.</param>
|
||||
<param name="inLength">The length of the data to compress.</param>
|
||||
<param name="lengths">The optimal 'length' of the compressed blocks. For each byte in the input data,
|
||||
this value is the optimal 'length' value. If it is 1, the block should not be compressed.</param>
|
||||
<param name="disps">The 'disp' values of the compressed blocks. May be 0, in which case the
|
||||
corresponding length will never be anything other than 1.</param>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.LZ11.LookAhead">
|
||||
<summary>
|
||||
Sets the flag that determines if 'look-ahead'/DP should be used when compressing
|
||||
with the LZ-11 format. The default is false, which is what is used in the original
|
||||
implementation.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.Formats.Nitro.LZ10">
|
||||
<summary>
|
||||
Compressor and decompressor for the LZ-0x10 format used in many of the games for the
|
||||
newer Nintendo consoles and handhelds.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.Nitro.LZ10.Decompress(System.IO.Stream,System.Int64,System.IO.Stream)">
|
||||
<summary>
|
||||
Decompress a stream that is compressed in the LZ-10 format.
|
||||
</summary>
|
||||
<param name="instream">The compressed stream.</param>
|
||||
<param name="inLength">The length of the input stream.</param>
|
||||
<param name="outstream">The output stream, where the decompressed data is written to.</param>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.Nitro.LZ10.CompressWithLA(System.IO.Stream,System.Int64,System.IO.Stream)">
|
||||
<summary>
|
||||
Variation of the original compression method, making use of Dynamic Programming to 'look ahead'
|
||||
and determine the optimal 'length' values for the compressed blocks. Is not 100% optimal,
|
||||
as the flag-bytes are not taken into account.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.Nitro.LZ10.GetOptimalCompressionLengths(System.Byte*,System.Int32,System.Int32[]@,System.Int32[]@)">
|
||||
<summary>
|
||||
Gets the optimal compression lengths for each start of a compressed block using Dynamic Programming.
|
||||
This takes O(n^2) time.
|
||||
</summary>
|
||||
<param name="indata">The data to compress.</param>
|
||||
<param name="inLength">The length of the data to compress.</param>
|
||||
<param name="lengths">The optimal 'length' of the compressed blocks. For each byte in the input data,
|
||||
this value is the optimal 'length' value. If it is 1, the block should not be compressed.</param>
|
||||
<param name="disps">The 'disp' values of the compressed blocks. May be 0, in which case the
|
||||
corresponding length will never be anything other than 1.</param>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.LZ10.LookAhead">
|
||||
<summary>
|
||||
Sets the flag that determines if 'look-ahead'/DP should be used when compressing
|
||||
with the LZ-10 format. The default is false, which is what is used in the original
|
||||
implementation.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.Formats.Nitro.RLE">
|
||||
<summary>
|
||||
Compressor and decompressor for the RLE format used in several of the games for the
|
||||
newer Nintendo consoles and handhelds.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.Formats.LZOvl">
|
||||
<summary>
|
||||
The LZ-Overlay compression format. Compresses part of the file from end to start.
|
||||
Is used for the 'overlay' files in NDS games, as well as arm9.bin.
|
||||
Note that the last 12 bytes should not be included in the 'inLength' argument when
|
||||
decompressing arm9.bin. This is done automatically if a file is given instead of a stream.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.LZOvl.CompressNormal(System.IO.Stream,System.Int64,System.IO.Stream)">
|
||||
<summary>
|
||||
Compresses the given input stream with the LZ-Ovl compression, but compresses _forward_
|
||||
instad of backwards.
|
||||
</summary>
|
||||
<param name="instream">The input stream to compress.</param>
|
||||
<param name="inLength">The length of the input stream.</param>
|
||||
<param name="outstream">The stream to write to.</param>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.LZOvl.CompressWithLA(System.IO.Stream,System.Int64,System.IO.Stream)">
|
||||
<summary>
|
||||
Variation of the original compression method, making use of Dynamic Programming to 'look ahead'
|
||||
and determine the optimal 'length' values for the compressed blocks. Is not 100% optimal,
|
||||
as the flag-bytes are not taken into account.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.LZOvl.GetOptimalCompressionLengths(System.Byte*,System.Int32,System.Int32[]@,System.Int32[]@)">
|
||||
<summary>
|
||||
Gets the optimal compression lengths for each start of a compressed block using Dynamic Programming.
|
||||
This takes O(n^2) time.
|
||||
</summary>
|
||||
<param name="indata">The data to compress.</param>
|
||||
<param name="inLength">The length of the data to compress.</param>
|
||||
<param name="lengths">The optimal 'length' of the compressed blocks. For each byte in the input data,
|
||||
this value is the optimal 'length' value. If it is 1, the block should not be compressed.</param>
|
||||
<param name="disps">The 'disp' values of the compressed blocks. May be less than 3, in which case the
|
||||
corresponding length will never be anything other than 1.</param>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.LZOvl.GetOptimalCompressionPartLength(System.Int32[])">
|
||||
<summary>
|
||||
Gets the 'optimal' length of the compressed part of the file.
|
||||
Or rather: the length in such a way that compressing any more will not
|
||||
result in a shorter file.
|
||||
</summary>
|
||||
<param name="blocklengths">The lengths of the compressed blocks, as gotten from GetOptimalCompressionLengths.</param>
|
||||
<returns>The 'optimal' length of the compressed part of the file.</returns>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.LZOvl.LookAhead">
|
||||
<summary>
|
||||
Sets the flag that determines if 'look-ahead'/DP should be used when compressing
|
||||
with the LZ-Ovl format. The default is false, which is what is used in the original
|
||||
implementation.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.NewProgram.Formats">
|
||||
<summary>
|
||||
The formats allowed when compressing a file.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.NewestProgram.ParseIOArguments(System.String[],System.Boolean,System.String[]@,System.String@,System.Boolean@)">
|
||||
<summary>
|
||||
Parses the IO arguments of the input.
|
||||
</summary>
|
||||
<param name="ioArgs">The arguments to parse.</param>
|
||||
<param name="inputFiles">The files to handle as input.</param>
|
||||
<param name="outputDir">The directory to save the handled files in. If this is null,
|
||||
the files should be overwritten. If this does not exist, it is the output file
|
||||
(the input may only contain one file if that si the case).</param>
|
||||
<param name="copyErrors">If files that cannot be handled (properly) should be copied to the output directory.</param>
|
||||
<returns>True iff parsing of the arguments succeeded.</returns>
|
||||
</member>
|
||||
<member name="M:DSDecmp.NewestProgram.Copy(System.String,System.String)">
|
||||
<summary>
|
||||
Copies the source file to the destination path.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.NewestProgram.GetFormat(System.String)">
|
||||
<summary>
|
||||
Gets the compression format corresponding to the given format string.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.NewestProgram.GetAllFormats(System.Boolean)">
|
||||
<summary>
|
||||
Gets a sequence over all compression formats currently supported; both built-in and plugin-based.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.NewestProgram.GetBuiltInFormats(System.Boolean)">
|
||||
<summary>
|
||||
Gets a sequence over all built-in compression formats.
|
||||
</summary>
|
||||
<param name="alsoCompositeFormats">If the built-in composite formats should also be part of the sequence.</param>
|
||||
</member>
|
||||
<member name="M:DSDecmp.NewestProgram.GetPluginFormats">
|
||||
<summary>
|
||||
Gets a sequence over all formats that can be used from plugins.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.NewestProgram.FirstOrDefault``1(System.Collections.Generic.IEnumerable{``0})">
|
||||
<summary>
|
||||
Gets the first item from the given sequence, or the default value of the type in the sequence
|
||||
if it is empty.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.NotEnoughDataException">
|
||||
<summary>
|
||||
An exception that is thrown by the decompression functions when there
|
||||
is not enough data available in order to properly decompress the input.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.NotEnoughDataException.#ctor(System.Int64,System.Int64)">
|
||||
<summary>
|
||||
Creates a new NotEnoughDataException.
|
||||
</summary>
|
||||
<param name="currentOutSize">The actual number of written bytes.</param>
|
||||
<param name="totalOutSize">The desired number of written bytes.</param>
|
||||
</member>
|
||||
<member name="P:DSDecmp.NotEnoughDataException.WrittenLength">
|
||||
<summary>
|
||||
Gets the actual number of written bytes.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.NotEnoughDataException.DesiredLength">
|
||||
<summary>
|
||||
Gets the number of bytes that was supposed to be written.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.SimpleReversedPrioQueue`2">
|
||||
<summary>
|
||||
Very simplistic implementation of a priority queue that returns items with lowest priority first.
|
||||
This is not the most efficient implementation, but required the least work while using the classes
|
||||
from the .NET collections, and without requiring importing another dll or several more class files
|
||||
in order to make it work.
|
||||
</summary>
|
||||
<typeparam name="TPrio">The type of the priority values.</typeparam>
|
||||
<typeparam name="TValue">The type of item to put into the queue.</typeparam>
|
||||
</member>
|
||||
<member name="M:DSDecmp.SimpleReversedPrioQueue`2.#ctor">
|
||||
<summary>
|
||||
Creates a new, empty reverse priority queue.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.SimpleReversedPrioQueue`2.Enqueue(`0,`1)">
|
||||
<summary>
|
||||
Enqueues the given value, using the given priority.
|
||||
</summary>
|
||||
<param name="priority">The priority of the value.</param>
|
||||
<param name="value">The value to enqueue.</param>
|
||||
</member>
|
||||
<member name="M:DSDecmp.SimpleReversedPrioQueue`2.Peek(`0@)">
|
||||
<summary>
|
||||
Gets the current value with the lowest priority from this queue, without dequeueing the value.
|
||||
</summary>
|
||||
<param name="priority">The priority of the returned value.</param>
|
||||
<returns>The current value with the lowest priority.</returns>
|
||||
<exception cref="T:System.IndexOutOfRangeException">If there are no items left in this queue.</exception>
|
||||
</member>
|
||||
<member name="M:DSDecmp.SimpleReversedPrioQueue`2.Dequeue(`0@)">
|
||||
<summary>
|
||||
Dequeues the current value at the head of thisreverse priority queue.
|
||||
</summary>
|
||||
<param name="priority">The priority of the dequeued value.</param>
|
||||
<returns>The dequeued value, that used to be at the head of this queue.</returns>
|
||||
<exception cref="T:System.IndexOutOfRangeException">If this queue does not contain any items.</exception>
|
||||
</member>
|
||||
<member name="P:DSDecmp.SimpleReversedPrioQueue`2.Count">
|
||||
<summary>
|
||||
Gets the number of items in this queue.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.Formats.Nitro.Huffman">
|
||||
<summary>
|
||||
Compressor and decompressor for the Huffman format used in many of the games for the
|
||||
newer Nintendo consoles and handhelds.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.Nitro.Huffman.GetLowest(DSDecmp.SimpleReversedPrioQueue{System.Int32,DSDecmp.Formats.Nitro.Huffman.HuffTreeNode},DSDecmp.SimpleReversedPrioQueue{System.Int32,DSDecmp.Formats.Nitro.Huffman.HuffTreeNode},System.Int32@)">
|
||||
<summary>
|
||||
Gets the tree node with the lowest priority (frequency) from the leaf and node queues.
|
||||
If the priority is the same for both head items in the queues, the node from the leaf queue is picked.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.Huffman.CompressBlockSize">
|
||||
<summary>
|
||||
Sets the block size used when using the Huffman format to compress.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode">
|
||||
<summary>
|
||||
A single node in a Huffman tree.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.data">
|
||||
<summary>
|
||||
The data contained in this node. May not mean anything when <code>isData == false</code>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.isFilled">
|
||||
<summary>
|
||||
A flag indicating if this node has been filled.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.isData">
|
||||
<summary>
|
||||
A flag indicating if this node contains data. If not, this is not a leaf node.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.child0">
|
||||
<summary>
|
||||
The child of this node at side 0
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.child1">
|
||||
<summary>
|
||||
The child of this node at side 1
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.#ctor(System.Byte,System.Boolean,DSDecmp.Formats.Nitro.Huffman.HuffTreeNode,DSDecmp.Formats.Nitro.Huffman.HuffTreeNode)">
|
||||
<summary>
|
||||
Manually creates a new node for a huffman tree.
|
||||
</summary>
|
||||
<param name="data">The data for this node.</param>
|
||||
<param name="isData">If this node represents data.</param>
|
||||
<param name="child0">The child of this node on the 0 side.</param>
|
||||
<param name="child1">The child of this node on the 1 side.</param>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.#ctor(System.IO.Stream,System.Boolean,System.Int64,System.Int64)">
|
||||
<summary>
|
||||
Creates a new node in the Huffman tree.
|
||||
</summary>
|
||||
<param name="stream">The stream to read from. It is assumed that there is (at least)
|
||||
one more byte available to read.</param>
|
||||
<param name="isData">If this node is a data-node.</param>
|
||||
<param name="relOffset">The offset of this node in the source data, relative to the start
|
||||
of the compressed file.</param>
|
||||
<param name="maxStreamPos">The indicated end of the huffman tree. If the stream is past
|
||||
this position, the tree is invalid.</param>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.Data">
|
||||
<summary>
|
||||
The data contained in this node. May not mean anything when <code>isData == false</code>.
|
||||
Throws a NullReferenceException when this node has not been defined (ie: reference was outside the
|
||||
bounds of the tree definition)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.IsData">
|
||||
<summary>
|
||||
Returns true if this node represents data.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.Child0">
|
||||
<summary>
|
||||
The child of this node at side 0
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.Child1">
|
||||
<summary>
|
||||
The child of this node at side 1
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.Parent">
|
||||
<summary>
|
||||
The parent node of this node.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.IsChild0">
|
||||
<summary>
|
||||
Determines if this is the Child0 of the parent node. Assumes there is a parent.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.IsChild1">
|
||||
<summary>
|
||||
Determines if this is the Child1 of the parent node. Assumes there is a parent.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.Depth">
|
||||
<summary>
|
||||
Get or set the depth of this node. Will not be set automatically, but
|
||||
will be set recursively (the depth of all child nodes will be updated when this is set).
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.Size">
|
||||
<summary>
|
||||
Calculates the size of the sub-tree with this node as root.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DSDecmp.Formats.Nitro.Huffman.HuffTreeNode.PreOrderTraversal">
|
||||
<summary>
|
||||
Returns a seuqnce over the nodes of the sub-tree with this node as root in a pre-order fashion. (Root-Left-Right)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.Nitro.Huffman4.Compress(System.IO.Stream,System.Int64,System.IO.Stream)">
|
||||
<summary>
|
||||
Applies Huffman compression with a datablock size of 4 bits.
|
||||
</summary>
|
||||
<param name="instream">The stream to compress.</param>
|
||||
<param name="inLength">The length of the input stream.</param>
|
||||
<param name="outstream">The stream to write the decompressed data to.</param>
|
||||
<returns>The size of the decompressed data.</returns>
|
||||
</member>
|
||||
<member name="M:DSDecmp.Formats.Nitro.Huffman8.Compress(System.IO.Stream,System.Int64,System.IO.Stream)">
|
||||
<summary>
|
||||
Applies Huffman compression with a datablock size of 8 bits.
|
||||
</summary>
|
||||
<param name="instream">The stream to compress.</param>
|
||||
<param name="inLength">The length of the input stream.</param>
|
||||
<param name="outstream">The stream to write the decompressed data to.</param>
|
||||
<returns>The size of the decompressed data.</returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
BIN
CSharp/PluginDistro/GoldenSunDD.dll
Normal file
BIN
CSharp/PluginDistro/GoldenSunDD.dll
Normal file
Binary file not shown.
BIN
CSharp/PluginDistro/LuminousArc.dll
Normal file
BIN
CSharp/PluginDistro/LuminousArc.dll
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user