C#: Disabled LZ-Overlay from 'decompressing' files that have four 0-bytes at the end. Also implemented the command-line input, and added a new release build to the bin/ folder.

This commit is contained in:
barubary 2011-05-15 15:16:04 +00:00
parent 591c44b309
commit 3bf20290a3
3 changed files with 494 additions and 7 deletions

View File

@ -59,7 +59,7 @@ namespace DSDecmp.Formats
uint extraSize = IOUtils.ToNDSu32(header, header.Length - 4);
if (extraSize == 0)
return true;
return false; // do not decompress whenevr the last 4 bytes are 0; too many files have that.
// if the extrasize is nonzero, the minimum header length is 8 bytes
if (header.Length < 8)
return false;

View File

@ -1,12 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
using DSDecmp.Formats.Nitro;
using DSDecmp.Formats;
using System.IO;
namespace DSDecmp
{
public static class NewProgram
{
public static void Main3(string[] args)
/// <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)
{
if (args.Length == 0)
{
@ -16,7 +35,102 @@ namespace DSDecmp
#endif
return;
}
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);
#if DEBUG
Console.ReadLine();
@ -27,12 +141,14 @@ namespace DSDecmp
{
Console.WriteLine("DSDecmp - Decompressor for compression formats used on the NDS - by Barubary");
Console.WriteLine();
Console.WriteLine("Usage:\tDSDecmp (-c FORMAT (FORMATOPTS)) (-ge) input (output)");
Console.WriteLine("Usage:\tDSDecmp (-c FORMAT FORMATOPT*) (-ge) input (output)");
Console.WriteLine();
Console.WriteLine("Without the -c modifier, DSDecmp will decompress the input file to the output");
Console.WriteLine("file. If the output file is a directory, the output file will be placed in that");
Console.WriteLine("directory with the same filename as the original file. The extension will be");
Console.WriteLine("appended with a format-specific extension.");
Console.WriteLine("If the output is a nonexistent file or directory, it is assumed to be a");
Console.WriteLine("directory iff there is no '.' in the name.");
Console.WriteLine("The input can also be a directory. In that case, it would be the same as");
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");
@ -56,15 +172,20 @@ namespace DSDecmp
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");
Console.WriteLine(" ratio.");
Console.WriteLine(" ratio. Will never compress using lzovl.");
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();
Console.WriteLine("The following format options are available:");
Console.WriteLine(" lz10, lz11 and lzovl:");
Console.WriteLine("The following format options (FORMATOPT) are available:");
Console.WriteLine(" lz10, lz11, lzovl, gba* and nds*:");
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.");
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");
Console.WriteLine("is the input directory appended with '_cmp'.");
Console.WriteLine();
Console.WriteLine("Supplying the -ge modifier together with the -c modifier, the extension of the");
Console.WriteLine("compressed files will be extended with the 'FORMAT' value that always results");
@ -73,5 +194,371 @@ namespace DSDecmp
Console.WriteLine("extended with .cdat");
}
#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))
{
if (!Directory.Exists(input + "_cmp"))
Directory.CreateDirectory(input + "_cmp");
foreach (string file in Directory.GetFiles(input))
{
Compress(file, input + "_cmp", format, guessExtension);
}
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;
if (Directory.Exists(output))
{
output = CombinePaths(output, Path.GetFileName(input));
}
if (Path.GetExtension(output) == ".dat")
output = RemoveExtension(output);
if (guessExtension)
output += "." + compressedFormat.ToString().ToLower();
else
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))
{
if (!Directory.Exists(input + "_dec"))
Directory.CreateDirectory(input + "_dec");
foreach (string file in Directory.GetFiles(input))
{
Decompress(file, input + "_dec", guessExtension);
}
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;
}
if (Directory.Exists(output))
{
output = CombinePaths(output, Path.GetFileName(input));
}
switch (Path.GetExtension(output))
{
case ".cdat":
case ".lz10":
case ".lz11":
case ".lzovl":
case ".rle":
case ".huff4":
case ".huff8":
output = RemoveExtension(output);
break;
}
byte[] outData = decompressedData.ToArray();
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";
}
else
output += ".dat";
using (FileStream outStream = File.Create(output))
{
outStream.Write(outData, 0, outData.Length);
Console.WriteLine(usedFormat.ToString() + "-decompressed " + input);
}
}
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);
}
}
}

Binary file not shown.