2011-04-21 15:58:07 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text;
|
2011-05-15 17:16:04 +02:00
|
|
|
|
using DSDecmp.Formats.Nitro;
|
|
|
|
|
using DSDecmp.Formats;
|
|
|
|
|
using System.IO;
|
2011-04-21 15:58:07 +02:00
|
|
|
|
|
|
|
|
|
namespace DSDecmp
|
|
|
|
|
{
|
|
|
|
|
public static class NewProgram
|
|
|
|
|
{
|
2011-05-15 17:16:04 +02:00
|
|
|
|
/// <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)
|
|
|
|
|
LZ10,
|
|
|
|
|
LZ11,
|
|
|
|
|
HUFF4,
|
|
|
|
|
HUFF8,
|
|
|
|
|
RLE,
|
|
|
|
|
HUFF,
|
|
|
|
|
NDS,
|
|
|
|
|
GBA,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void Main(string[] args)
|
2011-04-21 15:58:07 +02:00
|
|
|
|
{
|
|
|
|
|
if (args.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
PrintUsage();
|
|
|
|
|
#if DEBUG
|
|
|
|
|
Console.ReadLine();
|
|
|
|
|
#endif
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-05-15 17:16:04 +02:00
|
|
|
|
|
|
|
|
|
int argIndex = 0;
|
|
|
|
|
bool compress = false;
|
|
|
|
|
Formats compressFormat = Formats.NDS;
|
|
|
|
|
|
|
|
|
|
#region check for the -c option and its parameter(s)
|
|
|
|
|
if (args[argIndex].Equals("-c"))
|
|
|
|
|
{
|
|
|
|
|
argIndex++;
|
|
|
|
|
compress = true;
|
|
|
|
|
|
|
|
|
|
if (args.Length < argIndex + 2)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("A compression format and input file is required in order to compress.");
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
PrintUsage();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
switch (args[argIndex].ToLower())
|
|
|
|
|
{
|
|
|
|
|
case "lz10": compressFormat = Formats.LZ10; break;
|
|
|
|
|
case "lz11": compressFormat = Formats.LZ11; break;
|
|
|
|
|
case "lzovl": compressFormat = Formats.LZOVL; break;
|
|
|
|
|
case "rle": compressFormat = Formats.RLE; break;
|
|
|
|
|
case "huff4": compressFormat = Formats.HUFF4; break;
|
|
|
|
|
case "huff8": compressFormat = Formats.HUFF8; break;
|
|
|
|
|
case "huff": compressFormat = Formats.HUFF; break;
|
|
|
|
|
case "gba*": compressFormat = Formats.GBA; break;
|
|
|
|
|
case "nds*": compressFormat = Formats.NDS; break;
|
|
|
|
|
default:
|
|
|
|
|
Console.WriteLine("Unknown compression format " + args[argIndex]);
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
PrintUsage();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
argIndex++;
|
|
|
|
|
// handle the format options
|
|
|
|
|
switch (compressFormat)
|
|
|
|
|
{
|
|
|
|
|
case Formats.LZ10:
|
|
|
|
|
case Formats.GBA:
|
|
|
|
|
if (args[argIndex].Equals("-opt"))
|
|
|
|
|
{
|
|
|
|
|
LZ10.LookAhead = true;
|
|
|
|
|
argIndex++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case Formats.LZ11:
|
|
|
|
|
if (args[argIndex].Equals("-opt"))
|
|
|
|
|
{
|
|
|
|
|
LZ11.LookAhead = true;
|
|
|
|
|
argIndex++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case Formats.LZOVL:
|
|
|
|
|
if (args[argIndex].Equals("-opt"))
|
|
|
|
|
{
|
|
|
|
|
LZOvl.LookAhead = true;
|
|
|
|
|
argIndex++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case Formats.NDS:
|
|
|
|
|
if (args[argIndex].Equals("-opt"))
|
|
|
|
|
{
|
|
|
|
|
LZ10.LookAhead = true;
|
|
|
|
|
LZ11.LookAhead = true;
|
|
|
|
|
LZOvl.LookAhead = true;
|
|
|
|
|
argIndex++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
if (args.Length < argIndex + 1)
|
|
|
|
|
throw new ArgumentException("No input file given.");
|
|
|
|
|
|
|
|
|
|
bool guessExtension = false;
|
|
|
|
|
if (args[argIndex].Equals("-ge"))
|
|
|
|
|
{
|
|
|
|
|
guessExtension = true;
|
|
|
|
|
argIndex++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args.Length < argIndex + 1)
|
|
|
|
|
throw new ArgumentException("No input file given.");
|
|
|
|
|
|
|
|
|
|
string input = args[argIndex++];
|
|
|
|
|
string output = null;
|
|
|
|
|
if (args.Length > argIndex)
|
|
|
|
|
output = args[argIndex++];
|
|
|
|
|
|
|
|
|
|
if (compress)
|
|
|
|
|
Compress(input, output, compressFormat, guessExtension);
|
|
|
|
|
else
|
|
|
|
|
Decompress(input, output, guessExtension);
|
2011-04-21 15:58:07 +02:00
|
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
Console.ReadLine();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void PrintUsage()
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("DSDecmp - Decompressor for compression formats used on the NDS - by Barubary");
|
|
|
|
|
Console.WriteLine();
|
2011-05-15 17:16:04 +02:00
|
|
|
|
Console.WriteLine("Usage:\tDSDecmp (-c FORMAT FORMATOPT*) (-ge) input (output)");
|
2011-04-21 15:58:07 +02:00
|
|
|
|
Console.WriteLine();
|
|
|
|
|
Console.WriteLine("Without the -c modifier, DSDecmp will decompress the input file to the output");
|
2011-05-15 17:49:21 +02:00
|
|
|
|
Console.WriteLine(" file. If the output file is a directory, the output file will be placed in");
|
|
|
|
|
Console.WriteLine(" that directory with the same filename as the original file. The extension will");
|
|
|
|
|
Console.WriteLine(" be appended with a format-specific extension.");
|
2011-04-21 15:58:07 +02:00
|
|
|
|
Console.WriteLine("The input can also be a directory. In that case, it would be the same as");
|
2011-05-15 17:49:21 +02:00
|
|
|
|
Console.WriteLine(" calling DSDecmp for every non-directory in the given directory with the same");
|
|
|
|
|
Console.WriteLine(" options, with one exception; the output is by default the input folder, but");
|
|
|
|
|
Console.WriteLine(" with '_dec' appended.");
|
|
|
|
|
Console.WriteLine("If the output does not exist, it is assumed to be the same type as the input");
|
|
|
|
|
Console.WriteLine(" (file or directory).");
|
2011-04-21 15:58:07 +02:00
|
|
|
|
Console.WriteLine("If there is no output file given, it is assumed to be the directory of the");
|
2011-05-15 17:49:21 +02:00
|
|
|
|
Console.WriteLine(" input file.");
|
2011-04-21 15:58:07 +02:00
|
|
|
|
Console.WriteLine();
|
|
|
|
|
Console.WriteLine("With the -ge option, instead of a format-specific extension, the extension");
|
2011-05-15 17:49:21 +02:00
|
|
|
|
Console.WriteLine(" will be guessed from the first four bytes of the output file. Only");
|
|
|
|
|
Console.WriteLine(" non-accented letters or numbers are considered in those four bytes.");
|
2011-04-21 15:58:07 +02:00
|
|
|
|
Console.WriteLine();
|
|
|
|
|
Console.WriteLine("With the -c option, the input is compressed instead of decompressed. FORMAT");
|
|
|
|
|
Console.WriteLine("indicates the desired compression format, and can be one of:");
|
|
|
|
|
Console.WriteLine(" --- formats built-in in the NDS ---");
|
|
|
|
|
Console.WriteLine(" lz10 - 'default' LZ-compression format.");
|
|
|
|
|
Console.WriteLine(" lz11 - LZ-compression format better suited for files with long repetitions.");
|
|
|
|
|
Console.WriteLine(" lzovl - LZ-compression used in 'overlay files'.");
|
|
|
|
|
Console.WriteLine(" rle - Run-Length Encoding 'compression'.");
|
|
|
|
|
Console.WriteLine(" huff4 - Huffman compression with 4-bit sized data blocks.");
|
|
|
|
|
Console.WriteLine(" huff8 - Huffman compression with 8-bit sized data blocks.");
|
|
|
|
|
Console.WriteLine(" --- utility 'formats' ---");
|
|
|
|
|
Console.WriteLine(" huff - The Huffman compression that gives the bext compression ratio.");
|
|
|
|
|
Console.WriteLine(" nds* - The built-in compression format that gives the best compression");
|
2011-05-15 17:16:04 +02:00
|
|
|
|
Console.WriteLine(" ratio. Will never compress using lzovl.");
|
2011-04-21 15:58:07 +02:00
|
|
|
|
Console.WriteLine(" gba* - The built-in compression format that gives the best compression");
|
|
|
|
|
Console.WriteLine(" ratio, and is also supported by the GBA.");
|
|
|
|
|
Console.WriteLine();
|
2011-05-15 17:16:04 +02:00
|
|
|
|
Console.WriteLine("The following format options (FORMATOPT) are available:");
|
|
|
|
|
Console.WriteLine(" lz10, lz11, lzovl, gba* and nds*:");
|
2011-05-14 16:19:11 +02:00
|
|
|
|
Console.WriteLine(" -opt : employs a better compression algorithm to boost the compression");
|
|
|
|
|
Console.WriteLine(" ratio. Not using this option will result in using the algorithm");
|
|
|
|
|
Console.WriteLine(" originally used to compress the game files.");
|
2011-05-15 17:16:04 +02:00
|
|
|
|
Console.WriteLine(" Using this option for the gba* and nds* will only have effect on");
|
|
|
|
|
Console.WriteLine(" the lz10, lz11 and lzovl algorithms.");
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
Console.WriteLine("If the input is a directory when the -c option, the default output directory");
|
2011-05-15 17:49:21 +02:00
|
|
|
|
Console.WriteLine(" is the input directory appended with '_cmp'.");
|
2011-05-14 16:19:11 +02:00
|
|
|
|
Console.WriteLine();
|
2011-04-21 15:58:07 +02:00
|
|
|
|
Console.WriteLine("Supplying the -ge modifier together with the -c modifier, the extension of the");
|
2011-05-15 17:49:21 +02:00
|
|
|
|
Console.WriteLine(" compressed files will be extended with the 'FORMAT' value that always results");
|
|
|
|
|
Console.WriteLine(" in that particualr format (so 'lz11', 'rle', etc).");
|
2011-04-21 15:58:07 +02:00
|
|
|
|
Console.WriteLine("If the -ge modifier is not present, the extension of compressed files will be");
|
2011-05-15 17:49:21 +02:00
|
|
|
|
Console.WriteLine(" extended with .cdat");
|
2011-04-21 15:58:07 +02:00
|
|
|
|
|
|
|
|
|
}
|
2011-05-15 17:16:04 +02:00
|
|
|
|
|
|
|
|
|
#region compression methods
|
|
|
|
|
|
|
|
|
|
private static void Compress(string input, string output, Formats format, bool guessExtension)
|
|
|
|
|
{
|
|
|
|
|
if (!File.Exists(input) && !Directory.Exists(input))
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Cannot compress a file or directory that does not exist (" + input + ")");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set the default value of the output
|
|
|
|
|
if (string.IsNullOrEmpty(output))
|
|
|
|
|
{
|
|
|
|
|
if (Directory.Exists(input))
|
|
|
|
|
{
|
2011-05-15 17:49:21 +02:00
|
|
|
|
string newDir = Path.GetFullPath(input) + "_cmp";
|
|
|
|
|
if (!Directory.Exists(newDir))
|
|
|
|
|
Directory.CreateDirectory(newDir);
|
2011-05-15 17:16:04 +02:00
|
|
|
|
foreach (string file in Directory.GetFiles(input))
|
|
|
|
|
{
|
2011-05-15 17:49:21 +02:00
|
|
|
|
Compress(file, newDir, format, guessExtension);
|
2011-05-15 17:16:04 +02:00
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!guessExtension)
|
|
|
|
|
output = input; // the .cdat extension is added automatically
|
|
|
|
|
else
|
|
|
|
|
output = Path.GetDirectoryName(input);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Directory.Exists(input))
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists(output))
|
|
|
|
|
Directory.CreateDirectory(output);
|
|
|
|
|
foreach (string file in Directory.GetFiles(input))
|
|
|
|
|
{
|
|
|
|
|
Compress(file, output, format, guessExtension);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// compress the input
|
|
|
|
|
MemoryStream compressedData = new MemoryStream();
|
|
|
|
|
Formats compressedFormat;
|
|
|
|
|
int outsize = DoCompress(input, compressedData, format, out compressedFormat);
|
|
|
|
|
if (outsize < 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2011-05-15 17:49:21 +02:00
|
|
|
|
bool mustAppendExt = !Directory.Exists(output) && !File.Exists(output);
|
2011-05-15 17:16:04 +02:00
|
|
|
|
if (Directory.Exists(output))
|
|
|
|
|
{
|
|
|
|
|
output = CombinePaths(output, Path.GetFileName(input));
|
|
|
|
|
}
|
2011-05-15 17:49:21 +02:00
|
|
|
|
if (mustAppendExt && Path.GetExtension(output) == ".dat")
|
2011-05-15 17:16:04 +02:00
|
|
|
|
output = RemoveExtension(output);
|
|
|
|
|
if (guessExtension)
|
|
|
|
|
output += "." + compressedFormat.ToString().ToLower();
|
2011-05-15 17:49:21 +02:00
|
|
|
|
else if (mustAppendExt)
|
2011-05-15 17:16:04 +02:00
|
|
|
|
output += ".cdat";
|
|
|
|
|
|
|
|
|
|
using (FileStream outStream = File.Create(output))
|
|
|
|
|
{
|
|
|
|
|
compressedData.WriteTo(outStream);
|
|
|
|
|
Console.WriteLine(compressedFormat.ToString() + "-compressed " + input + " to " + output);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int DoCompress(string infile, MemoryStream output, Formats format, out Formats actualFormat)
|
|
|
|
|
{
|
|
|
|
|
CompressionFormat fmt = null;
|
|
|
|
|
switch (format)
|
|
|
|
|
{
|
|
|
|
|
case Formats.LZ10: fmt = new LZ10(); break;
|
|
|
|
|
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.HUFF:
|
|
|
|
|
return CompressHuff(infile, output, out actualFormat);
|
|
|
|
|
case Formats.GBA:
|
|
|
|
|
return CompressGBA(infile, output, out actualFormat);
|
|
|
|
|
case Formats.NDS:
|
|
|
|
|
return CompressNDS(infile, output, out actualFormat);
|
|
|
|
|
default:
|
|
|
|
|
throw new Exception("Unhandled compression format " + format);
|
|
|
|
|
}
|
|
|
|
|
actualFormat = format;
|
|
|
|
|
|
|
|
|
|
using (FileStream inStream = File.OpenRead(infile))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return fmt.Compress(inStream, inStream.Length, output);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception s)
|
|
|
|
|
{
|
|
|
|
|
// any exception generated by compression is a fatal exception
|
|
|
|
|
Console.WriteLine(s.Message);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int CompressHuff(string infile, MemoryStream output, out Formats actualFormat)
|
|
|
|
|
{
|
|
|
|
|
return CompressBest(infile, output, out actualFormat, Formats.HUFF4, Formats.HUFF8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int CompressGBA(string infile, MemoryStream output, out Formats actualFormat)
|
|
|
|
|
{
|
|
|
|
|
return CompressBest(infile, output, out actualFormat, Formats.HUFF4, Formats.HUFF8, Formats.LZ10, Formats.RLE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int CompressNDS(string infile, MemoryStream output, out Formats actualFormat)
|
|
|
|
|
{
|
|
|
|
|
return CompressBest(infile, output, out actualFormat, Formats.HUFF4, Formats.HUFF8, Formats.LZ10, Formats.LZ11, Formats.RLE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int CompressBest(string infile, MemoryStream output, out Formats actualFormat, params Formats[] formats)
|
|
|
|
|
{
|
|
|
|
|
// only read the input data once from the file.
|
|
|
|
|
byte[] inputData;
|
|
|
|
|
using (FileStream inStream = File.OpenRead(infile))
|
|
|
|
|
{
|
|
|
|
|
inputData = new byte[inStream.Length];
|
|
|
|
|
inStream.Read(inputData, 0, inputData.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MemoryStream bestOutput = null;
|
|
|
|
|
int minCompSize = int.MaxValue;
|
|
|
|
|
actualFormat = Formats.GBA;
|
|
|
|
|
foreach (Formats format in formats)
|
|
|
|
|
{
|
|
|
|
|
#region compress the file in each format, and save the best one
|
|
|
|
|
|
|
|
|
|
MemoryStream currentOutput = new MemoryStream();
|
|
|
|
|
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.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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int currentOutSize;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using (MemoryStream inStream = new MemoryStream(inputData))
|
|
|
|
|
{
|
|
|
|
|
currentOutSize = realFormat.Compress(inStream, inStream.Length, currentOutput);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (InputTooLargeException i)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(i.Message);
|
|
|
|
|
actualFormat = format;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (currentOutSize < minCompSize)
|
|
|
|
|
{
|
|
|
|
|
bestOutput = currentOutput;
|
|
|
|
|
minCompSize = currentOutSize;
|
|
|
|
|
actualFormat = format;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bestOutput == null)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("The file could not be compressed in any format.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
bestOutput.WriteTo(output);
|
|
|
|
|
return minCompSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region decompression methods
|
|
|
|
|
|
|
|
|
|
private static void Decompress(string input, string output, bool guessExtension)
|
|
|
|
|
{
|
|
|
|
|
if (!File.Exists(input) && !Directory.Exists(input))
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Cannot decompress a file or directory that does not exist (" + input + ")");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set the default value of the output
|
|
|
|
|
if (string.IsNullOrEmpty(output))
|
|
|
|
|
{
|
|
|
|
|
if (Directory.Exists(input))
|
|
|
|
|
{
|
2011-05-15 17:49:21 +02:00
|
|
|
|
string newDir = Path.GetFullPath(input) + "_dec";
|
|
|
|
|
if (!Directory.Exists(newDir))
|
|
|
|
|
Directory.CreateDirectory(newDir);
|
2011-05-15 17:16:04 +02:00
|
|
|
|
foreach (string file in Directory.GetFiles(input))
|
|
|
|
|
{
|
2011-05-15 17:49:21 +02:00
|
|
|
|
Decompress(file, newDir, guessExtension);
|
2011-05-15 17:16:04 +02:00
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!guessExtension)
|
|
|
|
|
output = input; // '.dat' gets added automatically if -ge is not given
|
|
|
|
|
else
|
|
|
|
|
output = Path.GetDirectoryName(input);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Directory.Exists(input))
|
|
|
|
|
{
|
|
|
|
|
if (File.Exists(output))
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Cannot decompress a folder to a single file.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!Directory.Exists(output))
|
|
|
|
|
Directory.CreateDirectory(output);
|
|
|
|
|
foreach (string file in Directory.GetFiles(input))
|
|
|
|
|
{
|
|
|
|
|
Decompress(file, output, guessExtension);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] inData;
|
|
|
|
|
using (FileStream inStream = File.OpenRead(input))
|
|
|
|
|
{
|
|
|
|
|
inData = new byte[inStream.Length];
|
|
|
|
|
inStream.Read(inData, 0, inData.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MemoryStream decompressedData = new MemoryStream();
|
|
|
|
|
long decSize = -1;
|
|
|
|
|
Formats usedFormat = Formats.NDS;
|
|
|
|
|
// just try all formats, and stop once one has been found that can decompress it.
|
|
|
|
|
foreach (Formats f in Enum.GetValues(typeof(Formats)))
|
|
|
|
|
{
|
|
|
|
|
using (MemoryStream inStream = new MemoryStream(inData))
|
|
|
|
|
{
|
|
|
|
|
decSize = Decompress(inStream, decompressedData, f);
|
|
|
|
|
if (decSize >= 0)
|
|
|
|
|
{
|
|
|
|
|
usedFormat = f;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (decSize < 0)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Could not decompress " + input + "; no matching compression method found.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-15 17:49:21 +02:00
|
|
|
|
bool mustAppendExt = !Directory.Exists(output) && !File.Exists(output);
|
|
|
|
|
|
2011-05-15 17:16:04 +02:00
|
|
|
|
if (Directory.Exists(output))
|
|
|
|
|
{
|
|
|
|
|
output = CombinePaths(output, Path.GetFileName(input));
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-15 17:49:21 +02:00
|
|
|
|
byte[] outData = decompressedData.ToArray();
|
|
|
|
|
if (mustAppendExt)
|
2011-05-15 17:16:04 +02:00
|
|
|
|
{
|
2011-05-15 17:49:21 +02:00
|
|
|
|
switch (Path.GetExtension(output))
|
|
|
|
|
{
|
|
|
|
|
case ".cdat":
|
|
|
|
|
case ".lz10":
|
|
|
|
|
case ".lz11":
|
|
|
|
|
case ".lzovl":
|
|
|
|
|
case ".rle":
|
|
|
|
|
case ".huff4":
|
|
|
|
|
case ".huff8":
|
|
|
|
|
output = RemoveExtension(output);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-05-15 17:16:04 +02:00
|
|
|
|
}
|
|
|
|
|
if (guessExtension)
|
|
|
|
|
{
|
|
|
|
|
string ext = "";
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
{
|
|
|
|
|
if ((outData[i] >= 'a' && outData[i] <= 'z')
|
|
|
|
|
|| (outData[i] >= 'A' && outData[i] <= 'Z')
|
|
|
|
|
|| char.IsDigit((char)outData[i]))
|
|
|
|
|
ext += (char)outData[i];
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (ext.Length > 0)
|
|
|
|
|
output += "." + ext;
|
|
|
|
|
else
|
|
|
|
|
output += ".dat";
|
|
|
|
|
}
|
2011-05-15 17:49:21 +02:00
|
|
|
|
else if(mustAppendExt)
|
2011-05-15 17:16:04 +02:00
|
|
|
|
output += ".dat";
|
|
|
|
|
|
|
|
|
|
using (FileStream outStream = File.Create(output))
|
|
|
|
|
{
|
|
|
|
|
outStream.Write(outData, 0, outData.Length);
|
2011-05-15 17:49:21 +02:00
|
|
|
|
Console.WriteLine(usedFormat.ToString() + "-decompressed " + input + " to " + output);
|
2011-05-15 17:16:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static long Decompress(MemoryStream inputStream, MemoryStream output, Formats 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))
|
|
|
|
|
return -1;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return realFormat.Decompress(inputStream, inputStream.Length, output);
|
|
|
|
|
}
|
|
|
|
|
catch (TooMuchInputException e)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(e.Message);
|
|
|
|
|
return output.Length;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Could not decompress using the " + format.ToString() + " format; " + e.Message);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
private static string CombinePaths(string dir, string file)
|
|
|
|
|
{
|
|
|
|
|
if (Path.IsPathRooted(file))
|
|
|
|
|
return file;
|
|
|
|
|
if (!dir.EndsWith(Path.DirectorySeparatorChar + "")
|
|
|
|
|
&& !dir.EndsWith(Path.AltDirectorySeparatorChar + ""))
|
|
|
|
|
return dir + Path.DirectorySeparatorChar + file;
|
|
|
|
|
else
|
|
|
|
|
return dir + file;
|
|
|
|
|
}
|
|
|
|
|
private static string RemoveExtension(string path)
|
|
|
|
|
{
|
|
|
|
|
return Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(path);
|
|
|
|
|
}
|
2011-04-21 15:58:07 +02:00
|
|
|
|
}
|
|
|
|
|
}
|