public static void Main(string[] args) { bool verbose = false; string filterPattern = null; bool overwriteFiles = false; bool dontUseFullPaths = false; bool showHelp = false; var options = new OptionSet() { { "v|verbose", "be verbose (list files)", v => verbose = v != null }, { "f|filter=", "only extract files using pattern", v => filterPattern = v }, { "o|overwrite", "overwrite files if they already exist", v => overwriteFiles = v != null }, { "nf|no-full-paths", "don't extract using full paths", v => dontUseFullPaths = v != null }, { "h|help", "show this message and exit", v => showHelp = v != null }, }; List <string> extra; try { extra = 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 (extra.Count < 1 || extra.Count > 2 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ input_sarc [output_directory]", GetExecutableName()); Console.WriteLine("Unpack specified small archive."); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } string inputPath = extra[0]; string outputPath = extra.Count > 1 ? extra[1] : Path.ChangeExtension(inputPath, null) + "_unpack"; string tmpOutputPath = extra.Count > 1 ? extra[1] : Path.ChangeExtension(inputPath, ".sarc"); Regex filter = null; if (string.IsNullOrEmpty(filterPattern) == false) { filter = new Regex(filterPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); } using (var temp = File.OpenRead(inputPath)) using (var cool = CreateCoolArchiveStream(temp)) { //if (cool != null) //{ // using (var output = File.Create(tmpOutputPath)) // { // var res = cool.ReadBytes((uint)cool.Length); // output.WriteBytes(res); // cool.Position = 0; // } //} var input = cool ?? temp; var smallArchive = new SmallArchiveFile(); smallArchive.Deserialize(input); long current = 0; long total = smallArchive.Entries.Count; var padding = total.ToString(CultureInfo.InvariantCulture).Length; Directory.CreateDirectory(outputPath); var xmlPath = Path.Combine(outputPath, "@files.xml"); var xmlSettings = new XmlWriterSettings() { Indent = true, }; using (var xml = XmlWriter.Create(xmlPath, xmlSettings)) { xml.WriteStartDocument(); xml.WriteStartElement("files"); foreach (var entry in smallArchive.Entries) { current++; if (string.IsNullOrEmpty(entry.Name) == true) { throw new InvalidOperationException(); } var entryName = entry.Name; //if (filter != null && filter.IsMatch(entryName) == false) //{ // continue; //} var entryPath = entryName; if (entryPath[0] == '/' || entryPath[0] == '\\') { entryPath = entryPath.Substring(1); } entryPath = entryPath.Replace('/', Path.DirectorySeparatorChar); if (dontUseFullPaths == true) { entryPath = Path.GetFileName(entryPath); } entryPath = Path.Combine(outputPath, entryPath); //if (overwriteFiles == false && File.Exists(entryName) == true) //{ // continue; //} if (verbose == true) { Console.WriteLine("[{0}/{1}] {2}", current.ToString(CultureInfo.InvariantCulture).PadRight(padding), total, entryName); } if (entry.Offset == 0) { xml.WriteStartElement("file"); xml.WriteStartAttribute("name"); xml.WriteValue(entryName); xml.WriteEndAttribute(); xml.WriteStartAttribute("size"); xml.WriteValue(entry.Size); xml.WriteEndAttribute(); xml.WriteEndElement(); } else { xml.WriteStartElement("file"); xml.WriteStartAttribute("name"); xml.WriteValue(entryName); xml.WriteEndAttribute(); xml.WriteValue(GetRelativePathForFile(xmlPath, entryPath)); xml.WriteEndElement(); var parentOutputPath = Path.GetDirectoryName(entryPath); if (string.IsNullOrEmpty(parentOutputPath) == false) { Directory.CreateDirectory(parentOutputPath); } using (var output = File.Create(entryPath)) { input.Seek(entry.Offset, SeekOrigin.Begin); output.WriteFromStream(input, entry.Size); } } } Console.WriteLine("exported {0} files", current); xml.WriteEndElement(); xml.WriteEndDocument(); } } }
public static void Main(string[] args) { const int alignment = 4; var endian = Endian.Little; bool verbose = false; bool compress = false; bool showHelp = false; var options = new OptionSet { { "v|verbose", "be verbose (list files)", v => verbose = v != null }, { "l|little-endian", "write in little endian mode", v => { if (v != null) { endian = Endian.Little; } } }, { "b|big-endian", "write in big endian mode", v => { if (v != null) { endian = Endian.Big; } } }, { "c|compress", "compress small archive with zlib.", v => compress = v != null }, { "h|help", "show this message and exit", v => showHelp = v != null } }; List <string> extra; try { extra = 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 (extra.Count < 1 || extra.Count > 2 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ input_directory [output_sarc]", GetExecutableName()); Console.WriteLine("Pack specified directory."); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } string inputPath = extra[0]; string outputPath = extra.Count > 1 ? extra[1] : inputPath + ".sarc"; using (var output = File.Create(outputPath)) { Stream data = output; if (compress == true) { data = new DeflaterOutputStream(output, new Deflater(Deflater.BEST_SPEED, false)); } var paths = new List <string>(); paths.AddRange(Directory.GetFiles(inputPath, "*", SearchOption.AllDirectories)); paths.Sort(new NameComparer()); int headerSize = 0; // ReSharper disable LoopCanBeConvertedToQuery foreach (string path in paths) // ReSharper restore LoopCanBeConvertedToQuery { headerSize += 4 + (Path.GetFileName(path) ?? "").Length + 4 + 4; } headerSize = headerSize.Align(16); // TODO: rewrite this terrible, terrible code. var smallArchive = new SmallArchiveFile(); int offset = 16 + headerSize; foreach (string path in paths) { smallArchive.Entries.Add(new SmallArchiveFile.Entry() { Name = Path.GetFileName(path), Offset = (uint)offset, Size = (uint)((new FileInfo(path)).Length), }); offset += (int)((new FileInfo(path)).Length); offset = offset.Align(alignment); } smallArchive.Endian = endian; smallArchive.Serialize(data); var buffer = new byte[0x4000]; foreach (string path in paths) { if (verbose == true) { Console.WriteLine("Adding {0}...", Path.GetFileName(path)); } int total = 0; using (var input = File.OpenRead(path)) { while (true) { int read = input.Read(buffer, 0, 0x4000); if (read == 0) { break; } data.Write(buffer, 0, read); total += read; } } int dummySize = total.Align(alignment) - total; if (dummySize > 0) { var dummyBlock = new byte[dummySize]; data.Write(dummyBlock, 0, dummySize); } } var deflaterOutputStream = data as DeflaterOutputStream; if (deflaterOutputStream != null) { (deflaterOutputStream).Finish(); } data.Flush(); } }
public static void Main(string[] args) { var endian = Endian.Little; bool verbose = false; bool compress = false; bool showHelp = false; var options = new OptionSet { { "v|verbose", "be verbose (list files)", v => verbose = v != null }, { "l|little-endian", "write in little endian mode", v => SetOption(v, ref endian, Endian.Little) }, { "b|big-endian", "write in big endian mode", v => SetOption(v, ref endian, Endian.Big) }, { "c|compress", "compress small archive with zlib.", v => compress = v != null }, { "h|help", "show this message and exit", v => showHelp = v != null } }; List <string> extra; try { extra = 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 (extra.Count < 1 || extra.Count > 2 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ input_directory [output_sarc]", GetExecutableName()); Console.WriteLine("Pack specified directory."); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } var inputPath = Path.GetFullPath(extra[0]); string xmlPath; if (Directory.Exists(inputPath) == true) { xmlPath = Path.Combine(inputPath, "@files.xml"); } else { xmlPath = inputPath; inputPath = Path.GetDirectoryName(inputPath); } var outputPath = extra.Count > 1 ? extra[1] : inputPath + ".sarc"; var pendingEntries = new List <PendingEntry>(); using (var xml = File.OpenRead(xmlPath)) { var doc = new XPathDocument(xml); var nav = doc.CreateNavigator(); var rawFiles = nav.Select("/files/file"); while (rawFiles.MoveNext() == true) { var rawFile = rawFiles.Current; if (rawFile.MoveToAttribute("name", "") == false) { throw new FormatException(); } var entryName = rawFile.Value; rawFile.MoveToParent(); if (rawFile.MoveToAttribute("size", "") == true) { uint entrySize; if (uint.TryParse(rawFile.Value, out entrySize) == false) { throw new FormatException(); } pendingEntries.Add(new PendingEntry() { Name = entryName, Size = entrySize, }); rawFile.MoveToParent(); continue; } string entryPath; if (Path.IsPathRooted(rawFile.Value) == false) { entryPath = Path.Combine(inputPath, rawFile.Value); } else { entryPath = rawFile.Value; } pendingEntries.Add(new PendingEntry() { Name = entryName, Path = entryPath, }); } } using (var output = File.Create(outputPath)) { var headerSize = SmallArchiveFile.EstimateHeaderSize(pendingEntries.Select(pe => pe.Name)); var smallArchive = new SmallArchiveFile(); output.Position = headerSize; foreach (var pendingEntry in pendingEntries) { if (pendingEntry.Size != null) { smallArchive.Entries.Add(new SmallArchiveFile.Entry(pendingEntry.Name, 0, pendingEntry.Size.Value)); continue; } using (var input = File.OpenRead(pendingEntry.Path)) { output.Position = output.Position.Align(4); smallArchive.Entries.Add(new SmallArchiveFile.Entry(pendingEntry.Name, (uint)output.Position, (uint)input.Length)); output.WriteFromStream(input, input.Length); } } output.Position = 0; smallArchive.Endian = endian; smallArchive.Serialize(output); } }
private void HandleStream(Stream file, string type) { file.Position = 0; if (type == "ADF") { // Got some AAF file bool inc = false; try { var adf = new AdfFile(); try { adf.Deserialize(file); ++ADFReadCount; } catch { inc = true; ++ADFSkipCount; } ADFExtractStrings(adf); } catch { if (!inc) { ++ADFSkipCount; } } } else if (type == "RTPC") { // Got some RTPC file try { var propertyContainerFile = new PropertyContainerFile(); propertyContainerFile.Deserialize(file); RTPCExtractStrings(propertyContainerFile); ++RTPCReadCount; } catch { ++RTPCSkipCount; } } else if (type == "AAF") { // Got some AAF Archive using (var cool = CreateCoolArchiveStream(file)) { var input = cool ?? file; var smallArchive = new SmallArchiveFile(); smallArchive.Deserialize(input); foreach (var entry in smallArchive.Entries) { this.AddString(entry.Name); if (entry.Offset == 0) { continue; } ++this.FileCount; this.PrintProgress(); input.Position = entry.Offset; string subtype = this.GetType(input); if (string.IsNullOrEmpty(subtype)) { continue; } using (var entryStream = entry.ReadToMemoryStream(input)) { this.HandleStream(entryStream, subtype); } } ++this.AAFExtractCount; } } }
private void HandleStream(Stream file, string type) { // flush if the limit is reached if (this._EntryCount > 300000) { this.flush(); } //file.Position = 0; if (type == "ADF") { // Got some AAF file bool inc = false; try { var adf = new AdfFile(); try { adf.Deserialize(file); ++ADFReadCount; } catch { inc = true; ++ADFSkipCount; } ADFExtractStrings(adf); } catch { if (!inc) { ++ADFSkipCount; } } } else if (type == "RTPC") { // Got some RTPC file try { var propertyContainerFile = new PropertyContainerFile(); propertyContainerFile.Deserialize(file); RTPCExtractStrings(propertyContainerFile); ++RTPCReadCount; } catch { ++RTPCSkipCount; } } else if (type == "AAF") { // Got some AAF Archive using (var cool = CreateCoolArchiveStream(file)) { var input = cool ?? file; var smallArchive = new SmallArchiveFile(); smallArchive.Deserialize(input); this._CurrentFile.Add(null); foreach (var entry in smallArchive.Entries) { this.AddString(entry.Name); if (entry.Offset == 0) { continue; } ++this.FileCount; this.PrintProgress(); input.Position = entry.Offset; string subtype = this.GetType(input); if (string.IsNullOrEmpty(subtype)) { continue; } // insert the name this._CurrentFile[this._CurrentFile.Count - 1] = entry.Name; this.UpdateCurrentFile(); // process the file using (var entryStream = entry.ReadToMemoryStream(input)) { // I know that this copies a copy of a memory (that's a stupid thing) // but without this the memories goes crazy and if I force GC Collect // that's even worse. So a little copy doesn't look bad in front of // the horrors produced when you don't have it. // to see what it's like, replace entryStream with input. this.HandleStream(entryStream, subtype); } } this._CurrentFile.RemoveAt(this._CurrentFile.Count - 1); ++this.AAFExtractCount; } } }
public static void Main(string[] args) { bool verbose = false; bool overwriteFiles = false; bool decompress = false; bool listing = false; bool showHelp = false; var options = new OptionSet() { { "v|verbose", "be verbose (list files)", v => verbose = v != null }, { "l|list", "just list files (don't extract)", v => listing = v != null }, { "o|overwrite", "overwrite files if they already exist", v => overwriteFiles = v != null }, { "d|decompress", "decompress a zlib compressed small archive.", v => decompress = v != null }, { "h|help", "show this message and exit", v => showHelp = v != null }, }; List <string> extra; try { extra = 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 (extra.Count < 1 || extra.Count > 2 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ input_sarc [output_directory]", GetExecutableName()); Console.WriteLine("Unpack specified small archive."); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } string inputPath = extra[0]; string outputPath = extra.Count > 1 ? extra[1] : Path.ChangeExtension(inputPath, null) + "_unpack"; using (var input = File.OpenRead(inputPath)) { if (input.ReadValueU8() == 0x78) { if (verbose == true) { Console.WriteLine("Detected compressed SARC."); } decompress = true; } input.Seek(-1, SeekOrigin.Current); Stream data; if (decompress == false) { data = input; } else { var decompressed = new MemoryStream(); //var zlib = new ZlibStream(input, CompressionMode.Decompress); var zlib = new InflaterInputStream(input); var buffer = new byte[0x4000]; while (true) { int read = zlib.Read(buffer, 0, buffer.Length); if (read < 0) { throw new InvalidOperationException("zlib error"); } if (read == 0) { break; } decompressed.Write(buffer, 0, read); } zlib.Close(); input.Close(); decompressed.Position = 0; data = decompressed; } if (listing == false) { Directory.CreateDirectory(outputPath); } var smallArchive = new SmallArchiveFile(); smallArchive.Deserialize(data); long counter = 0; long skipped = 0; long totalCount = smallArchive.Entries.Count; if (verbose == true) { Console.WriteLine("{0} files in small archive.", totalCount); } foreach (var entry in smallArchive.Entries) { counter++; var entryName = Path.GetFileName(entry.Name); if (entryName == null) { throw new InvalidOperationException(); } var entryPath = Path.Combine(outputPath, entryName); if (overwriteFiles == false && File.Exists(entryPath) == true) { if (verbose == true) { Console.WriteLine("{1:D4}/{2:D4} !! {0}", entry.Name, counter, totalCount); } skipped++; continue; } if (verbose == true || listing == true) { Console.WriteLine("{1:D4}/{2:D4} => {0}", entry.Name, counter, totalCount); } if (listing == false) { using (var output = File.Create(entryPath)) { data.Seek(entry.Offset, SeekOrigin.Begin); output.WriteFromStream(data, entry.Size); } } } if (verbose == true && skipped > 0) { Console.WriteLine("{0} files not overwritten.", skipped); } } }