private static void Main(string[] args) { Endian endian = Endian.Little; bool showHelp = false; bool verbose = false; var options = new OptionSet() { { "v|verbose", "be verbose", v => verbose = v != null }, { "l|little-endian", "little-endian mode (default)", v => GetOptionValue(ref endian, v, Endian.Little) }, { "b|big-endian", "big-endian mode", v => GetOptionValue(ref endian, v, Endian.Big) }, { "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 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ output_par input_directory+", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Pack files from input directories into a PARC file."); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } var inputPaths = new List <string>(); string outputPath; if (extras.Count == 1) { inputPaths.Add(extras[0]); outputPath = extras[0] + ".par"; } else { outputPath = extras[0]; inputPaths.AddRange(extras.Skip(1)); } var pendingEntries = new SortedDictionary <string, KeyValuePair <string, string> >(); if (verbose == true) { Console.WriteLine("Finding files..."); } foreach (var relativePath in inputPaths) { string inputPath = Path.GetFullPath(relativePath); if (inputPath.EndsWith(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) == true) { inputPath = inputPath.Substring(0, inputPath.Length - 1); } foreach (string path in Directory.GetFiles(inputPath, "*", SearchOption.AllDirectories)) { string fullPath = Path.GetFullPath(path); string partPath = fullPath.Substring(inputPath.Length + 1) .Replace(Path.DirectorySeparatorChar, '\\') .Replace(Path.AltDirectorySeparatorChar, '\\'); var partKey = partPath.ToLowerInvariant(); KeyValuePair <string, string> previousPair; if (pendingEntries.TryGetValue(partKey, out previousPair) == true) { if (verbose == true) { Console.WriteLine("Ignoring duplicate of {0}:", previousPair.Key); Console.WriteLine(" Previously added from: {0}", previousPair.Value); } else { Console.WriteLine("Ignoring duplicate of {0}!", previousPair.Key); } continue; } pendingEntries[partKey] = new KeyValuePair <string, string>(partPath, fullPath); } } const int alignment = 2048; var archive = new ArchiveFile() { Endian = endian, }; using (var output = File.Create(outputPath)) { var headerSize = ArchiveFile.EstimateHeaderSize(pendingEntries.Keys); output.Position = headerSize; long current = 0; long total = pendingEntries.Count; var padding = total.ToString(CultureInfo.InvariantCulture).Length; foreach (var kv in pendingEntries.Values) { var partPath = kv.Key; var fullPath = kv.Value; current++; if (verbose == true) { Console.WriteLine( "[{0}/{1}] {2}", current.ToString(CultureInfo.InvariantCulture).PadLeft(padding), total, partPath); } using (var input = File.OpenRead(fullPath)) { var dataSize = (uint)input.Length; output.Position = output.Position.Align(alignment); if (output.Position > 0xfFFFFFFFFL) { throw new InvalidOperationException("unsupported data offset"); } ArchiveFile.FileEntry fileEntry; fileEntry.Path = partPath; fileEntry.IsCompressed = false; fileEntry.DataUncompressedSize = dataSize; fileEntry.DataCompressedSize = dataSize; fileEntry.DataOffset = output.Position; archive.Entries.Add(fileEntry); if (dataSize > 0) { output.WriteFromStream(input, dataSize); } } } output.SetLength(output.Position.Align(alignment)); // pad file ending output.Position = 0; archive.Serialize(output); if (output.Position != headerSize) { throw new InvalidOperationException("header estimation mismatch"); } } }