private static void CompressLzo(ref Entry entry,
                                        Stream input,
                                        Stream output)
        {
            var uncompressedData = input.ReadBytes((uint)input.Length);
            var uncompressedSize = (uint)uncompressedData.Length;

            var compressedData = new byte[uncompressedData.Length +
                                          (uncompressedData.Length / 16) + 64 + 3];
            var actualCompressedSize = compressedData.Length;

            var result = LZO.Compress(uncompressedData,
                                      0,
                                      uncompressedData.Length,
                                      compressedData,
                                      0,
                                      ref actualCompressedSize);

            if (result != LZO.ErrorCode.Success)
            {
                throw new InvalidOperationException("compression error " + result.ToString());
            }

            if (actualCompressedSize < uncompressedSize)
            {
                entry.CompressionScheme = CompressionScheme.LZO1x;
                entry.UncompressedSize  = uncompressedSize;
                entry.CompressedSize    = (uint)actualCompressedSize;
                output.Write(compressedData, 0, actualCompressedSize);
            }
            else
            {
                input.Seek(0, SeekOrigin.Begin);
                entry.CompressionScheme = CompressionScheme.None;
                entry.UncompressedSize  = 0;
                entry.CompressedSize    = (uint)input.Length;
                output.WriteFromStream(input, input.Length);
            }
        }
        private static void Main(string[] args)
        {
            bool showHelp = false;
            bool verbose  = false;

            var options = new OptionSet()
            {
                { "v|verbose", "be verbose", v => verbose = v != null },
                { "h|help", "show this message and exit", v => showHelp = v != null },
            };

            List <string> extras;

            try
            {
                extras = options.Parse(args);
            }
            catch (OptionException e)
            {
                Console.Write("{0}: ", GetExecutableName());
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `{0} --help' for more information.", GetExecutableName());
                return;
            }

            if (extras.Count < 1 || extras.Count > 2 || showHelp == true)
            {
                Console.WriteLine("Usage: {0} [OPTIONS]+ input_dir [output_tms]", GetExecutableName());
                Console.WriteLine();
                Console.WriteLine("Options:");
                options.WriteOptionDescriptions(Console.Out);
                return;
            }

            string baseInputPath = extras[0];
            string outputPath    = extras.Count > 1 ? extras[1] : Path.ChangeExtension(baseInputPath + "_packed", ".tms");

            var endian = Endian.Little;

            var inputPaths = Directory.GetFiles(baseInputPath);

            byte[] uncompressedBytes;
            using (var data = new MemoryStream())
            {
                foreach (var inputPath in inputPaths)
                {
                    {
                        var inputName      = Path.GetFileName(inputPath) ?? "";
                        var inputNameBytes = Encoding.ASCII.GetBytes(inputName);
                        Array.Resize(ref inputNameBytes, inputNameBytes.Length + 1);
                        data.WriteValueS32(inputNameBytes.Length, endian);
                        data.WriteBytes(inputNameBytes);
                    }

                    {
                        var inputText          = File.ReadAllText(inputPath);
                        var inputTextIsUnicode = inputText.Any(c => c > 127);

                        if (inputTextIsUnicode == false)
                        {
                            var inputTextBytes = Encoding.ASCII.GetBytes(inputText);
                            Array.Resize(ref inputTextBytes, inputTextBytes.Length + 1);
                            data.WriteValueS32(inputTextBytes.Length, endian);
                            data.WriteBytes(inputTextBytes);
                        }
                        else
                        {
                            var inputTextBytes = Encoding.Unicode.GetBytes(inputText);
                            Array.Resize(ref inputTextBytes, inputTextBytes.Length + 2);
                            data.WriteValueS32(-(inputTextBytes.Length / 2), endian);
                            data.WriteBytes(inputTextBytes);
                        }
                    }
                }

                data.Flush();

                uncompressedBytes = (byte[])data.GetBuffer().Clone();
                Array.Resize(ref uncompressedBytes, (int)data.Length);
            }

            var compressedBytes = new byte[uncompressedBytes.Length +
                                           (uncompressedBytes.Length / 16) + 64 + 3];
            var actualCompressedSize = compressedBytes.Length;

            var result = LZO.Compress(uncompressedBytes,
                                      0,
                                      uncompressedBytes.Length,
                                      compressedBytes,
                                      0,
                                      ref actualCompressedSize);

            if (result != LZO.ErrorCode.Success)
            {
                throw new SaveCorruptionException(string.Format("LZO compression failure ({0})", result));
            }

            Array.Resize(ref compressedBytes, actualCompressedSize);

            using (var output = File.Create(outputPath))
            {
                output.WriteValueS32(uncompressedBytes.Length, endian);
                output.WriteValueS32(inputPaths.Length, endian);
                output.WriteValueU32(0x9E2A83C1, endian);
                output.WriteValueU32(0x00020000, endian);
                output.WriteValueS32(compressedBytes.Length, endian);
                output.WriteValueS32(uncompressedBytes.Length, endian);
                output.WriteValueS32(compressedBytes.Length, endian);
                output.WriteValueS32(uncompressedBytes.Length, endian);
                output.WriteBytes(compressedBytes);
            }
        }