public static void Main(string[] args) { bool showHelp = false; string currentProject = null; bool littleEndian = true; var options = new OptionSet() { { "h|help", "show this message and exit", v => showHelp = v != null }, { "l|little-endian", "operate in little-endian mode", v => littleEndian = v != null ? true : littleEndian }, { "b|big-endian", "operate in big-endian mode", v => littleEndian = v != null ? false : littleEndian }, { "p|project=", "override current project", v => currentProject = v }, }; 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 != 0 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } Console.WriteLine("Loading project..."); var manager = ProjectData.Manager.Load(currentProject); if (manager.ActiveProject == null) { Console.WriteLine("Nothing to do: no active project loaded."); return; } var project = manager.ActiveProject; var hashes = manager.LoadLists( "*.filelist", s => s.HashFileName(), s => s.ToLowerInvariant()); var installPath = project.InstallPath; var listsPath = project.ListsPath; if (installPath == null) { Console.WriteLine("Could not detect install path."); return; } else if (listsPath == null) { Console.WriteLine("Could not detect lists path."); return; } Console.WriteLine("Searching for archives..."); var inputPaths = new List <string>(); inputPaths.AddRange(Directory.GetFiles(installPath, "*.000", SearchOption.AllDirectories)); var outputPaths = new List <string>(); var fileAlignment = manager.GetSetting <uint>("bigfile_alignment", 0x7FF00000); Console.WriteLine("Processing..."); foreach (var inputPath in inputPaths) { var outputPath = GetListPath(installPath, inputPath); if (outputPath == null) { throw new InvalidOperationException(); } Console.WriteLine(outputPath); outputPath = Path.Combine(listsPath, outputPath); if (outputPaths.Contains(outputPath) == true) { throw new InvalidOperationException(); } outputPaths.Add(outputPath); var big = new BigFileV1(); big.LittleEndian = littleEndian; big.FileAlignment = fileAlignment; if (File.Exists(inputPath + ".bak") == true) { using (var input = File.OpenRead(inputPath + ".bak")) { big.Deserialize(input); } } else { using (var input = File.OpenRead(inputPath)) { big.Deserialize(input); } } var localBreakdown = new Breakdown(); var names = new List <string>(); foreach (var nameHash in big.Entries.Select(e => e.NameHash).Distinct()) { var name = hashes[nameHash]; if (name != null) { if (names.Contains(name) == false) { names.Add(name); localBreakdown.Known++; } } localBreakdown.Total++; } names.Sort(); Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); using (var output = new StreamWriter(outputPath)) { output.WriteLine("; {0}", localBreakdown); foreach (string name in names) { output.WriteLine(name); } } } }
public static void Main(string[] args) { bool showHelp = false; string currentProject = null; bool littleEndian = true; var options = new OptionSet() { { "h|help", "show this message and exit", v => showHelp = v != null }, { "l|little-endian", "operate in little-endian mode", v => littleEndian = v != null ? true : littleEndian }, { "b|big-endian", "operate in big-endian mode", v => littleEndian = v != null ? false : littleEndian }, { "p|project=", "override current project", v => currentProject = v }, }; 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 != 0 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } Console.WriteLine("Loading project..."); var manager = ProjectData.Manager.Load(currentProject); if (manager.ActiveProject == null) { Console.WriteLine("Nothing to do: no active project loaded."); return; } var project = manager.ActiveProject; var hashes = manager.LoadLists( "*.filelist", s => s.HashFileName(), s => s.ToLowerInvariant()); var installPath = project.InstallPath; var listsPath = project.ListsPath; if (installPath == null) { Console.WriteLine("Could not detect install path."); return; } else if (listsPath == null) { Console.WriteLine("Could not detect lists path."); return; } Console.WriteLine("Searching for archives..."); var inputPaths = new List<string>(); inputPaths.AddRange(Directory.GetFiles(installPath, "*.000", SearchOption.AllDirectories)); var outputPaths = new List<string>(); var fileAlignment = manager.GetSetting<uint>("bigfile_alignment", 0x7FF00000); Console.WriteLine("Processing..."); foreach (var inputPath in inputPaths) { var outputPath = GetListPath(installPath, inputPath); if (outputPath == null) { throw new InvalidOperationException(); } Console.WriteLine(outputPath); outputPath = Path.Combine(listsPath, outputPath); if (outputPaths.Contains(outputPath) == true) { throw new InvalidOperationException(); } outputPaths.Add(outputPath); var big = new BigFileV1(); big.LittleEndian = littleEndian; big.FileAlignment = fileAlignment; if (File.Exists(inputPath + ".bak") == true) { using (var input = File.OpenRead(inputPath + ".bak")) { big.Deserialize(input); } } else { using (var input = File.OpenRead(inputPath)) { big.Deserialize(input); } } var localBreakdown = new Breakdown(); var names = new List<string>(); foreach (var nameHash in big.Entries.Select(e => e.NameHash).Distinct()) { var name = hashes[nameHash]; if (name != null) { if (names.Contains(name) == false) { names.Add(name); localBreakdown.Known++; } } localBreakdown.Total++; } names.Sort(); Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); using (var output = new StreamWriter(outputPath)) { output.WriteLine("; {0}", localBreakdown); foreach (string name in names) { output.WriteLine(name); } } } }
public static void Main(string[] args) { bool verbose = true; bool showHelp = false; var options = new OptionSet() { { "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_directory [output_archive]", GetExecutableName()); Console.WriteLine("Pack directory into an archive."); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } string inputPath = extras[0]; string outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(inputPath, ".000"); if (Directory.Exists(inputPath) == true) { string testPath = Path.Combine(inputPath, "bigfile.xml"); if (File.Exists(testPath) == true) { inputPath = testPath; } } var outdir = Path.GetDirectoryName(outputPath); if (!Directory.Exists(outdir)) Directory.CreateDirectory(outdir); var entries = new List<MyEntry>(); var big = new BigFileV1(); using (var input = File.Open( inputPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { var doc = new XPathDocument(input); var nav = doc.CreateNavigator(); var root = nav.SelectSingleNode("/files"); var _fileAlignment = root.GetAttribute("alignment", ""); if (string.IsNullOrEmpty(_fileAlignment) == true) { throw new FormatException("alignment cannot be null or empty"); } big.FileAlignment = uint.Parse(_fileAlignment, NumberStyles.AllowHexSpecifier); var _endian = root.GetAttribute("endian", ""); switch (_endian.ToLowerInvariant()) { case "big": big.LittleEndian = false; break; case "little": default: big.LittleEndian = true; break; } //big.BasePath = root.GetAttribute("basepath", "") ?? "PC-W"; var nodes = root.Select("entry"); while (nodes.MoveNext() == true) { var node = nodes.Current; var _hash = node.GetAttribute("hash", ""); if (string.IsNullOrEmpty(_hash) == true) { throw new FormatException("entry hash cannot be null or empty"); } var _locale = node.GetAttribute("locale", ""); if (string.IsNullOrEmpty(_locale) == true) { throw new FormatException("entry locale cannot be null or empty"); } var path = node.Value; if (string.IsNullOrEmpty(path) == true) { throw new FormatException("entry path cannot be null or empty"); } if (Path.IsPathRooted(path) == false) { path = Path.Combine(Path.GetDirectoryName(inputPath), path); path = Path.GetFullPath(path); } entries.Add(new MyEntry() { NameHash = uint.Parse(_hash, NumberStyles.AllowHexSpecifier), UncompressedSize = 0, Offset = 0, Locale = uint.Parse(_locale, NumberStyles.AllowHexSpecifier), CompressedSize = 0, Path = path, }); } } uint? currentBigFile = null; Stream data = null; var headerSize = (uint)BigFileV1.EstimateHeaderSize(entries.Count); var firstOffset = headerSize / 2048; var maxBlocksPerFile = big.FileAlignment / 2048; var globalOffset = 0u; var localOffset = firstOffset; var entryBigFile = 0u; foreach (var entry in entries) { if (verbose == true) { Console.WriteLine(Path.GetFileName(entry.Path)); } using (var input = File.OpenRead(entry.Path)) { var length = (uint)input.Length; var blockCount = length.Align(2048) / 2048; if (blockCount > maxBlocksPerFile) { Console.WriteLine("'{0}' can't fit in the archive! (writing as much as possible)", entry.Path); blockCount = maxBlocksPerFile; length = blockCount * 2048; } if (localOffset + blockCount > maxBlocksPerFile) { localOffset = 0; globalOffset += maxBlocksPerFile; entryBigFile++; } if (currentBigFile.HasValue == false || currentBigFile.Value != entryBigFile) { if (data != null) { data.Close(); data = null; } currentBigFile = entryBigFile; data = File.Create(Path.ChangeExtension(outputPath, "." + currentBigFile.Value.ToString().PadLeft(3, '0'))); } data.Seek(localOffset * 2048, SeekOrigin.Begin); data.WriteFromStream(input, length); entry.UncompressedSize = length; entry.Offset = globalOffset + localOffset; big.Entries.Add(entry); localOffset += blockCount; } } if (data != null) { data.Close(); } using (var output = File.OpenWrite(Path.ChangeExtension(outputPath, ".000"))) { big.Serialize(output); } }
public static void Main(string[] args) { bool showHelp = false; bool? extractUnknowns = null; bool overwriteFiles = false; bool verbose = true; string currentProject = null; bool littleEndian = true; var options = new OptionSet() { { "o|overwrite", "overwrite existing files", v => overwriteFiles = v != null }, { "nu|no-unknowns", "don't extract unknown files", v => extractUnknowns = v != null ? false : extractUnknowns }, { "ou|only-unknowns", "only extract unknown files", v => extractUnknowns = v != null ? true : extractUnknowns }, { "l|little-endian", "operate in little-endian mode", v => littleEndian = v != null ? true : littleEndian }, { "b|big-endian", "operate in big-endian mode", v => littleEndian = v != null ? false : littleEndian }, { "v|verbose", "be verbose", v => verbose = v != null }, { "h|help", "show this message and exit", v => showHelp = v != null }, { "p|project=", "override current project", v => currentProject = v }, }; 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 || Is000(extras[0]) == false) { Console.WriteLine("Usage: {0} [OPTIONS]+ input_file.000 [output_dir]", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } string inputPath = extras[0]; string outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(inputPath, null) + "_unpack"; string bigPathSuffix; var bigPathBase = GetBasePath(inputPath, out bigPathSuffix); var manager = ProjectData.Manager.Load(currentProject); if (manager.ActiveProject == null) { Console.WriteLine("Warning: no active project loaded."); } var big = new BigFileV1(); big.Endianness = littleEndian ? Endian.Little : Endian.Big; big.FileAlignment = manager.GetSetting <uint>("bigfile_alignment", 0x7FF00000); var compressionType = manager.GetSetting <CompressionType>("compression_type", CompressionType.None); using (var input = File.OpenRead(inputPath)) { big.Deserialize(input); } if (big.Entries.Any(e => e.CompressedSize != 0) == true && compressionType == CompressionType.None) { throw new InvalidOperationException("compressed entries not supported"); } var test = big.Entries.Where(e => e.CompressedSize != 0).ToArray(); var hashes = manager.LoadLists( "*.filelist", s => s.HashFileName(), s => s.ToLowerInvariant()); Directory.CreateDirectory(outputPath); var settings = new XmlWriterSettings(); settings.Indent = true; using (var xml = XmlWriter.Create( Path.Combine(outputPath, "bigfile.xml"), settings)) { xml.WriteStartDocument(); xml.WriteStartElement("files"); xml.WriteAttributeString("endian", big.Endianness == Endian.Little ? "little" : "big"); xml.WriteAttributeString("alignment", big.FileAlignment.ToString("X8")); Stream data = null; uint? currentBigFile = null; uint? lastLocale = null; var maxBlocksPerFile = big.FileAlignment / 2048; { long current = 0; long total = big.Entries.Count; foreach (var entry in big.Entries.OrderBy(e => e.Offset)) { current++; var entryBigFile = entry.Offset / maxBlocksPerFile; var entryOffset = (entry.Offset % maxBlocksPerFile) * 2048; if (currentBigFile.HasValue == false || currentBigFile.Value != entryBigFile) { if (data != null) { data.Close(); data = null; } currentBigFile = entryBigFile; var bigPath = string.Format("{0}.{1}{2}", bigPathBase, currentBigFile.Value.ToString().PadLeft(3, '0'), bigPathSuffix); if (verbose == true) { Console.WriteLine(bigPath); } data = File.OpenRead(bigPath); } string name = hashes[entry.NameHash]; if (name == null) { if (extractUnknowns.HasValue == true && extractUnknowns.Value == false) { continue; } string extension; // detect type { var guess = new byte[64]; int read = 0; if (entry.UncompressedSize > 0) { if (entry.CompressedSize != 0) { data.Seek(entryOffset, SeekOrigin.Begin); if (compressionType == CompressionType.Zlib) { var zlib = new InflaterInputStream(data); read = zlib.Read(guess, 0, (int)Math.Min( entry.UncompressedSize, guess.Length)); } else { throw new NotSupportedException(); } } else { data.Seek(entryOffset, SeekOrigin.Begin); read = data.Read(guess, 0, (int)Math.Min( entry.UncompressedSize, guess.Length)); } } extension = FileExtensions.Detect( guess, Math.Min(guess.Length, read)); } name = entry.NameHash.ToString("X8"); name = Path.ChangeExtension(name, "." + extension); name = Path.Combine(extension, name); name = Path.Combine("__UNKNOWN", name); } else { if (extractUnknowns.HasValue == true && extractUnknowns.Value == true) { continue; } name = name.Replace("/", "\\"); if (name.StartsWith("\\") == true) { name = name.Substring(1); } } if (entry.Locale == 0xFFFFFFFF) { name = Path.Combine("default", name); } else { name = Path.Combine(entry.Locale.ToString("X8"), name); } var entryPath = Path.Combine(outputPath, name); Directory.CreateDirectory(Path.GetDirectoryName(entryPath)); if (lastLocale.HasValue == false || lastLocale.Value != entry.Locale) { xml.WriteComment(string.Format(" {0} = {1} ", entry.Locale.ToString("X8"), ((Big.Locale)entry.Locale))); lastLocale = entry.Locale; } xml.WriteStartElement("entry"); xml.WriteAttributeString("hash", entry.NameHash.ToString("X8")); xml.WriteAttributeString("locale", entry.Locale.ToString("X8")); xml.WriteValue(name); xml.WriteEndElement(); if (overwriteFiles == false && File.Exists(entryPath) == true) { continue; } if (verbose == true) { Console.WriteLine("[{0}/{1}] {2}", current, total, name); } using (var output = File.Create(entryPath)) { if (entry.UncompressedSize > 0) { if (entry.CompressedSize != 0) { data.Seek(entryOffset, SeekOrigin.Begin); if (compressionType == CompressionType.Zlib) { using (var temp = data.ReadToMemoryStream(entry.CompressedSize)) { var zlib = new InflaterInputStream(temp); output.WriteFromStream(zlib, entry.UncompressedSize); } } else { throw new NotSupportedException(); } } else { data.Seek(entryOffset, SeekOrigin.Begin); output.WriteFromStream(data, entry.UncompressedSize); } } } } } if (data != null) { data.Close(); } xml.WriteEndElement(); xml.WriteEndDocument(); xml.Flush(); } }
public static void Main(string[] args) { bool showHelp = false; bool? extractUnknowns = null; bool overwriteFiles = false; bool verbose = true; string currentProject = null; bool littleEndian = true; var options = new OptionSet() { { "o|overwrite", "overwrite existing files", v => overwriteFiles = v != null }, { "nu|no-unknowns", "don't extract unknown files", v => extractUnknowns = v != null ? false : extractUnknowns }, { "ou|only-unknowns", "only extract unknown files", v => extractUnknowns = v != null ? true : extractUnknowns }, { "l|little-endian", "operate in little-endian mode", v => littleEndian = v != null ? true : littleEndian }, { "b|big-endian", "operate in big-endian mode", v => littleEndian = v != null ? false : littleEndian }, { "v|verbose", "be verbose", v => verbose = v != null }, { "h|help", "show this message and exit", v => showHelp = v != null }, { "p|project=", "override current project", v => currentProject = v }, }; 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 || Is000(extras[0]) == false) { Console.WriteLine("Usage: {0} [OPTIONS]+ input_file.000 [output_dir]", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } string inputPath = extras[0]; string outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(inputPath, null) + "_unpack"; string bigPathSuffix; var bigPathBase = GetBasePath(inputPath, out bigPathSuffix); var manager = ProjectData.Manager.Load(currentProject); if (manager.ActiveProject == null) { Console.WriteLine("Warning: no active project loaded."); } var big = new BigFileV1(); big.LittleEndian = littleEndian; big.FileAlignment = manager.GetSetting<uint>("bigfile_alignment", 0x7FF00000); var compressionType = manager.GetSetting<CompressionType>("compression_type", CompressionType.None); using (var input = File.OpenRead(inputPath)) { big.Deserialize(input); } if (big.Entries.Any(e => e.CompressedSize != 0) == true && compressionType == CompressionType.None) { throw new InvalidOperationException("compressed entries not supported"); } var test = big.Entries.Where(e => e.CompressedSize != 0).ToArray(); var hashes = manager.LoadLists( "*.filelist", s => s.HashFileName(), s => s.ToLowerInvariant()); Directory.CreateDirectory(outputPath); var settings = new XmlWriterSettings(); settings.Indent = true; using (var xml = XmlWriter.Create( Path.Combine(outputPath, "bigfile.xml"), settings)) { xml.WriteStartDocument(); xml.WriteStartElement("files"); xml.WriteAttributeString("endian", big.LittleEndian == true ? "little" : "big"); xml.WriteAttributeString("alignment", big.FileAlignment.ToString("X8")); Stream data = null; uint? currentBigFile = null; uint? lastLocale = null; var maxBlocksPerFile = big.FileAlignment / 2048; { long current = 0; long total = big.Entries.Count; foreach (var entry in big.Entries.OrderBy(e => e.Offset)) { current++; var entryBigFile = entry.Offset / maxBlocksPerFile; var entryOffset = (entry.Offset % maxBlocksPerFile) * 2048; if (currentBigFile.HasValue == false || currentBigFile.Value != entryBigFile) { if (data != null) { data.Close(); data = null; } currentBigFile = entryBigFile; var bigPath = string.Format("{0}.{1}{2}", bigPathBase, currentBigFile.Value.ToString().PadLeft(3, '0'), bigPathSuffix); if (verbose == true) { Console.WriteLine(bigPath); } data = File.OpenRead(bigPath); } string name = hashes[entry.NameHash]; if (name == null) { if (extractUnknowns.HasValue == true && extractUnknowns.Value == false) { continue; } string extension; // detect type { var guess = new byte[64]; int read = 0; if (entry.UncompressedSize > 0) { if (entry.CompressedSize != 0) { data.Seek(entryOffset, SeekOrigin.Begin); if (compressionType == CompressionType.Zlib) { var zlib = new InflaterInputStream(data); read = zlib.Read(guess, 0, (int)Math.Min( entry.UncompressedSize, guess.Length)); } else { throw new NotSupportedException(); } } else { data.Seek(entryOffset, SeekOrigin.Begin); read = data.Read(guess, 0, (int)Math.Min( entry.UncompressedSize, guess.Length)); } } extension = FileExtensions.Detect( guess, Math.Min(guess.Length, read)); } name = entry.NameHash.ToString("X8"); name = Path.ChangeExtension(name, "." + extension); name = Path.Combine(extension, name); name = Path.Combine("__UNKNOWN", name); } else { if (extractUnknowns.HasValue == true && extractUnknowns.Value == true) { continue; } name = name.Replace("/", "\\"); if (name.StartsWith("\\") == true) { name = name.Substring(1); } } if (entry.Locale == 0xFFFFFFFF) { name = Path.Combine("default", name); } else { name = Path.Combine(entry.Locale.ToString("X8"), name); } var entryPath = Path.Combine(outputPath, name); Directory.CreateDirectory(Path.GetDirectoryName(entryPath)); if (lastLocale.HasValue == false || lastLocale.Value != entry.Locale) { xml.WriteComment(string.Format(" {0} = {1} ", entry.Locale.ToString("X8"), ((Big.Locale)entry.Locale))); lastLocale = entry.Locale; } xml.WriteStartElement("entry"); xml.WriteAttributeString("hash", entry.NameHash.ToString("X8")); xml.WriteAttributeString("locale", entry.Locale.ToString("X8")); xml.WriteValue(name); xml.WriteEndElement(); if (overwriteFiles == false && File.Exists(entryPath) == true) { continue; } if (verbose == true) { Console.WriteLine("[{0}/{1}] {2}", current, total, name); } using (var output = File.Create(entryPath)) { if (entry.UncompressedSize > 0) { if (entry.CompressedSize != 0) { data.Seek(entryOffset, SeekOrigin.Begin); if (compressionType == CompressionType.Zlib) { using (var temp = data.ReadToMemoryStream(entry.CompressedSize)) { var zlib = new InflaterInputStream(temp); output.WriteFromStream(zlib, entry.UncompressedSize); } } else { throw new NotSupportedException(); } } else { data.Seek(entryOffset, SeekOrigin.Begin); output.WriteFromStream(data, entry.UncompressedSize); } } } } } if (data != null) { data.Close(); } xml.WriteEndElement(); xml.WriteEndDocument(); xml.Flush(); } }