public static void Main(string[] args) { bool verbose = false; bool overwriteFiles = true; bool showHelp = false; bool debugMode = false; OptionSet options = new OptionSet() { { "v|verbose", "be verbose (list files)", v => verbose = v != null }, { "d|debug", "debug mode", v => debugMode = v != null }, { "o|overwrite", "overwrite files if they already exist", v => overwriteFiles = 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_str [output_directory]", GetExecutableName()); Console.WriteLine("Unpack specified stream set."); 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) + "_unpacked"; Stream input = File.OpenRead(inputPath); Directory.CreateDirectory(outputPath); var settings = new XmlWriterSettings(); settings.Indent = true; using (var xml = XmlWriter.Create( Path.Combine(outputPath, "@archive.xml"), settings)) { xml.WriteStartDocument(); xml.WriteStartElement("streams"); var set = new StreamSetFile(); set.Deserialize(input); int counter = 0; for (int i = 0; i < set.Contents.Count;) { var headerInfo = set.Contents[i]; if (headerInfo.Type != StreamSet.ContentType.Header) { //throw new FormatException("excepted header"); i++; continue; } input.Seek(headerInfo.Offset, SeekOrigin.Begin); var fileInfo = new StreamSet.FileInfo(); fileInfo.Deserialize(input, set.LittleEndian); if (input.Position > headerInfo.Offset + headerInfo.Size) { throw new FormatException("read too much header data?"); } /* * if (Enum.IsDefined(typeof(StreamSet.FileBuild), fileInfo.Build) == false) * { * throw new FormatException("unsupported build type " + ((uint)fileInfo.Build).ToString("X8")); * }*/ /*else if (fileInfo.Type != fileInfo.Type2) * { * throw new FormatException("type hashes don't match"); * } * else if (fileInfo.Type != fileInfo.TypeName.HashFileName()) * { * throw new FormatException("type name hash and type hash don't match"); * }*/ string fileName; fileName = counter.ToString("D4") + "_" + fileInfo.GetSaneFileName(); counter++; fileName = Path.Combine(fileInfo.TypeName, fileName); i++; Console.WriteLine("{0}", fileInfo.FileName); string outputName = Path.Combine(outputPath, fileName); if (overwriteFiles == false && File.Exists(outputName) == true) { continue; } xml.WriteStartElement("stream"); //xml.WriteAttributeString("build", fileInfo.Build.ToString()); if (Enum.IsDefined(typeof(StreamSet.FileBuild), fileInfo.Build) == false) { xml.WriteAttributeString("build", ((uint)fileInfo.Build).ToString("X8")); } else { xml.WriteAttributeString("build", fileInfo.Build.ToString()); } xml.WriteAttributeString("alignment", fileInfo.Alignment.ToString("X4")); xml.WriteAttributeString("flags", fileInfo.Flags.ToString("X4")); xml.WriteAttributeString("type", fileInfo.Type.ToString("X8")); xml.WriteAttributeString("u0C", fileInfo.Unknown0C.ToString("X8")); xml.WriteAttributeString("type2", fileInfo.Type2.ToString("X8")); xml.WriteAttributeString("u14", fileInfo.Unknown14.ToString("X8")); xml.WriteAttributeString("u18", fileInfo.Unknown18.ToString("X8")); xml.WriteAttributeString("base_name", fileInfo.BaseName); xml.WriteAttributeString("file_name", fileInfo.FileName); xml.WriteAttributeString("type_name", fileInfo.TypeName); if (debugMode == false) { xml.WriteString(fileName); } if (debugMode == true) { xml.WriteStartElement("blocks"); } Directory.CreateDirectory(Path.GetDirectoryName(outputName)); using (var output = File.Create(outputName)) { uint readSize = 0; while (readSize < fileInfo.TotalSize) { uint leftSize = fileInfo.TotalSize - readSize; var dataInfo = set.Contents[i]; if (dataInfo.Type != StreamSet.ContentType.Data && dataInfo.Type != StreamSet.ContentType.CompressedData) { throw new InvalidOperationException(); } input.Seek(dataInfo.Offset, SeekOrigin.Begin); if (debugMode == true) { xml.WriteStartElement("block"); } if (dataInfo.Type == StreamSet.ContentType.CompressedData) { if (debugMode == true) { xml.WriteAttributeString("type", "compressed"); } var compressedSize = input.ReadValueU32(set.LittleEndian); if (4 + compressedSize > dataInfo.Size) { throw new InvalidOperationException(); } var compressedStream = input.ReadToMemoryStream(compressedSize); var compressedData = Gibbed.RefPack.Decompression.Decompress( compressedStream); uint writeSize = Math.Min(leftSize, (uint)compressedData.Length); output.Write(compressedData, 0, (int)writeSize); readSize += writeSize; if (debugMode == true) { xml.WriteAttributeString("offset", dataInfo.Offset.ToString("X8")); xml.WriteAttributeString("tsize", (dataInfo.Size + 12).ToString()); xml.WriteAttributeString("size", dataInfo.Size.ToString()); xml.WriteAttributeString("csize", compressedSize.ToString()); xml.WriteAttributeString("usize1", writeSize.ToString()); xml.WriteAttributeString("usize2", compressedData.Length.ToString()); } } else { if (debugMode == true) { xml.WriteAttributeString("type", "uncompressed"); } uint writeSize = Math.Min(leftSize, dataInfo.Size); output.WriteFromStream(input, writeSize); readSize += writeSize; if (debugMode == true) { xml.WriteAttributeString("offset", dataInfo.Offset.ToString("X8")); xml.WriteAttributeString("tsize", (dataInfo.Size + 12).ToString()); xml.WriteAttributeString("size", dataInfo.Size.ToString()); } } if (debugMode == true) { xml.WriteEndElement(); } ++i; } } if (debugMode == true) { xml.WriteEndElement(); } xml.WriteEndElement(); } xml.WriteEndElement(); xml.WriteEndDocument(); xml.Flush(); } }
public static void Main(string[] args) { bool verbose = false; bool overwriteFiles = true; bool showHelp = false; bool debugMode = false; OptionSet options = new OptionSet() { { "v|verbose", "be verbose (list files)", v => verbose = v != null }, { "d|debug", "debug mode", v => debugMode = v != null }, { "o|overwrite", "overwrite files if they already exist", v => overwriteFiles = 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_str [output_directory]", GetExecutableName()); Console.WriteLine("Unpack specified stream set."); 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) + "_unpacked"; Stream input = File.OpenRead(inputPath); Directory.CreateDirectory(outputPath); var settings = new XmlWriterSettings(); settings.Indent = true; using (var xml = XmlWriter.Create( Path.Combine(outputPath, "@archive.xml"), settings)) { xml.WriteStartDocument(); xml.WriteStartElement("streams"); var set = new StreamSetFile(); set.Deserialize(input); int counter = 0; for (int i = 0; i < set.Contents.Count; ) { var headerInfo = set.Contents[i]; if (headerInfo.Type != StreamSet.ContentType.Header) { //throw new FormatException("excepted header"); i++; continue; } input.Seek(headerInfo.Offset, SeekOrigin.Begin); var fileInfo = new StreamSet.FileInfo(); fileInfo.Deserialize(input, set.LittleEndian); if (input.Position > headerInfo.Offset + headerInfo.Size) { throw new FormatException("read too much header data?"); } /* if (Enum.IsDefined(typeof(StreamSet.FileBuild), fileInfo.Build) == false) { throw new FormatException("unsupported build type " + ((uint)fileInfo.Build).ToString("X8")); }*/ /*else if (fileInfo.Type != fileInfo.Type2) { throw new FormatException("type hashes don't match"); } else if (fileInfo.Type != fileInfo.TypeName.HashFileName()) { throw new FormatException("type name hash and type hash don't match"); }*/ string fileName; fileName = counter.ToString("D4") + "_" + fileInfo.GetSaneFileName(); counter++; fileName = Path.Combine(fileInfo.TypeName, fileName); i++; Console.WriteLine("{0}", fileInfo.FileName); string outputName = Path.Combine(outputPath, fileName); if (overwriteFiles == false && File.Exists(outputName) == true) { continue; } xml.WriteStartElement("stream"); //xml.WriteAttributeString("build", fileInfo.Build.ToString()); if (Enum.IsDefined(typeof(StreamSet.FileBuild), fileInfo.Build) == false) { xml.WriteAttributeString("build", ((uint)fileInfo.Build).ToString("X8")); } else { xml.WriteAttributeString("build", fileInfo.Build.ToString()); } xml.WriteAttributeString("alignment", fileInfo.Alignment.ToString("X4")); xml.WriteAttributeString("flags", fileInfo.Flags.ToString("X4")); xml.WriteAttributeString("type", fileInfo.Type.ToString("X8")); xml.WriteAttributeString("u0C", fileInfo.Unknown0C.ToString("X8")); xml.WriteAttributeString("type2", fileInfo.Type2.ToString("X8")); xml.WriteAttributeString("u14", fileInfo.Unknown14.ToString("X8")); xml.WriteAttributeString("u18", fileInfo.Unknown18.ToString("X8")); xml.WriteAttributeString("base_name", fileInfo.BaseName); xml.WriteAttributeString("file_name", fileInfo.FileName); xml.WriteAttributeString("type_name", fileInfo.TypeName); if (debugMode == false) { xml.WriteString(fileName); } if (debugMode == true) { xml.WriteStartElement("blocks"); } Directory.CreateDirectory(Path.GetDirectoryName(outputName)); using (var output = File.Create(outputName)) { uint readSize = 0; while (readSize < fileInfo.TotalSize) { uint leftSize = fileInfo.TotalSize - readSize; var dataInfo = set.Contents[i]; if (dataInfo.Type != StreamSet.ContentType.Data && dataInfo.Type != StreamSet.ContentType.CompressedData) { throw new InvalidOperationException(); } input.Seek(dataInfo.Offset, SeekOrigin.Begin); if (debugMode == true) { xml.WriteStartElement("block"); } if (dataInfo.Type == StreamSet.ContentType.CompressedData) { if (debugMode == true) { xml.WriteAttributeString("type", "compressed"); } var compressedSize = input.ReadValueU32(set.LittleEndian); if (4 + compressedSize > dataInfo.Size) { throw new InvalidOperationException(); } var compressedStream = input.ReadToMemoryStream(compressedSize); var compressedData = Gibbed.RefPack.Decompression.Decompress( compressedStream); uint writeSize = Math.Min(leftSize, (uint)compressedData.Length); output.Write(compressedData, 0, (int)writeSize); readSize += writeSize; if (debugMode == true) { xml.WriteAttributeString("offset", dataInfo.Offset.ToString("X8")); xml.WriteAttributeString("tsize", (dataInfo.Size + 12).ToString()); xml.WriteAttributeString("size", dataInfo.Size.ToString()); xml.WriteAttributeString("csize", compressedSize.ToString()); xml.WriteAttributeString("usize1", writeSize.ToString()); xml.WriteAttributeString("usize2", compressedData.Length.ToString()); } } else { if (debugMode == true) { xml.WriteAttributeString("type", "uncompressed"); } uint writeSize = Math.Min(leftSize, dataInfo.Size); output.WriteFromStream(input, writeSize); readSize += writeSize; if (debugMode == true) { xml.WriteAttributeString("offset", dataInfo.Offset.ToString("X8")); xml.WriteAttributeString("tsize", (dataInfo.Size + 12).ToString()); xml.WriteAttributeString("size", dataInfo.Size.ToString()); } } if (debugMode == true) { xml.WriteEndElement(); } ++i; } } if (debugMode == true) { xml.WriteEndElement(); } xml.WriteEndElement(); } xml.WriteEndElement(); xml.WriteEndDocument(); xml.Flush(); } }