public static void Main(string[] args) { bool showHelp = false; string currentProject = null; var options = new OptionSet() { { "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 != 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>(); 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 BigFileV2(); 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; bool? extractUnknowns = null; bool overwriteFiles = false; bool verbose = true; string currentProject = null; 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 }, { "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 || Path.GetExtension(extras[0]) != ".000") { 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"; var manager = ProjectData.Manager.Load(currentProject); if (manager.ActiveProject == null) { Console.WriteLine("Warning: no active project loaded."); } var big = new BigFileV2(); using (var input = File.OpenRead(inputPath)) { big.Deserialize(input); } 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("basepath", big.BasePath); 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 = Path.ChangeExtension(inputPath, "." + currentBigFile.Value.ToString().PadLeft(3, '0')); 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) { 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) { data.Seek(entryOffset, SeekOrigin.Begin); output.WriteFromStream(data, entry.UncompressedSize); } } } } if (data != null) { data.Close(); } xml.WriteEndElement(); xml.WriteEndDocument(); xml.Flush(); } }