public static List <BlobImage> GetBlobsFrom(FileInfo[] files) { List <BlobImage> result = new List <BlobImage>(files.Length); foreach (FileInfo file in files) { BlobFile blob = new BlobFile(); using (Stream s = file.Open(FileMode.Open, FileAccess.Read)) { blob.Deserialize(s); foreach (var entry in blob.Entries) { BlobImage image = new BlobImage(); image.BlobReference = new BlobReference(); image.BlobReference.Id = entry.Name; image.BlobReference.FileName = file.Name; s.Seek(entry.Offset, SeekOrigin.Begin); byte[] data = new byte[entry.Size]; s.Read(data, 0, data.Length); Bitmap b = null; try { b = GenerateBitmapFromCfs(new MemoryStream(data), file.Name); } catch (Exception) { b = null; } if (b == null) { continue; } image.Image = b; result.Add(image); } } } return(result); }
private void openToolStripMenuItem_Click(object sender, EventArgs e) { var dialog = new OpenFileDialog(); dialog.Filter = "Infantry Online Blob File (*.blo)|*.blo"; if (dialog.ShowDialog() == DialogResult.OK) { blob = new BlobFile(); fileName = dialog.SafeFileName; filePath = dialog.FileName; blobStream = dialog.OpenFile(); blob.Deserialize(blobStream); blobListBox.DataSource = blob.Entries; UpdateMainWindowTitle(); } }
private LoadedBlobFile LoadBlobFile(string path) { using (var fs = File.OpenRead(path)) { var memoryStream = new MemoryStream(); fs.CopyTo(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); var blob = new BlobFile(); blob.Deserialize(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); return(new LoadedBlobFile { BlobName = Path.GetFileName(path), BlobFile = blob, Stream = memoryStream }); } }
public static void Main(string[] args) { var xmlSettings = new XmlWriterSettings() { Indent = true, NewLineChars = "\r\n", NewLineHandling = NewLineHandling.Replace, }; var configBasePath = Path.Combine(GetExecutablePath(), "serializers", "resources"); var config = Configuration.Load(configBasePath); string parseName = null; var mode = Mode.Unknown; var showHelp = false; var options = new OptionSet() { { "b|xml2bin", "convert xml to bin", v => mode = v != null ? Mode.Import : mode }, { "x|bin2xml", "convert bin to xml", v => mode = v != null ? Mode.Export : mode }, { "p|parse=", "override parse name", v => parseName = v }, { "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; } // try to figure out what they want to do if (mode == Mode.Unknown && extras.Count >= 1) { var testPath = extras[0]; if (Directory.Exists(testPath) == true) { mode = Mode.Import; } else if (File.Exists(testPath) == true) { mode = Mode.Export; } } if (extras.Count < 1 || extras.Count > 2 || showHelp == true || mode == Mode.Unknown) { Console.WriteLine("Usage: {0} [OPTIONS]+ -x input_bin [output_dir]", GetExecutableName()); Console.WriteLine(" {0} [OPTIONS]+ -b input_dir [output_bin]", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } if (mode == Mode.Export) { var inputPath = extras[0]; var outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(inputPath, null); using (var input = File.OpenRead(inputPath)) { Console.WriteLine("Loading bin..."); var blob = new BlobFile(); blob.Deserialize(input); if (parseName == null) { parseName = Path.GetFileNameWithoutExtension(inputPath); } var parse = config.GetParse(parseName); if (parse == null) { Console.WriteLine( "Don't know how to handle '{0}' with a hash of '{1}'.", parseName, blob.ParseHash); return; } var target = parse.GetTarget(blob.ParseHash); if (target == null) { Console.WriteLine( "Don't know how to handle '{0}' with a hash of '{1}'.", parseName, blob.ParseHash); return; } var version = target.FirstVersion(); if (version == null) { Console.WriteLine( "No support for '{0}' with a hash of '{1}'.", parseName, blob.ParseHash); return; } var assemblyPath = Path.Combine( GetExecutablePath(), "serializers", "assemblies", version + ".dll"); if (File.Exists(assemblyPath) == false) { Console.WriteLine( "Assembly '{0}' appears to be missing!", Path.GetFileName(assemblyPath)); return; } var assembly = Assembly.LoadFrom(assemblyPath); var type = assembly.GetType(target.Class); if (type == null) { Console.WriteLine( "Assembly '{0}' does not expose '{1}'!", Path.GetFileName(assemblyPath), target.Class); return; } var resource = new Resource() { Parse = parseName, ParseHash = blob.ParseHash, }; foreach (var file in blob.Files) { resource.Files.Add(new Resource.FileEntry() { Name = file.Name, Timestamp = file.Timestamp, }); } foreach (var dependency in blob.Dependencies) { resource.Dependencies.Add(new Resource.DependencyEntry() { Type = dependency.Type, Name = dependency.Name, Hash = dependency.Hash, }); } var loadResource = typeof(BlobDataReader) .GetMethod("LoadResource", BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(type); Console.WriteLine("Loading entries..."); Func <int, string> getFileNameFromIndex = i => { if (i < 0 || i >= resource.Files.Count) { throw new KeyNotFoundException($"file index {i} is out of range"); } return(resource.Files[i].Name); }; var list = (IList)loadResource.Invoke( null, new object[] { input, parse.IsClient, parse.IsServer, getFileNameFromIndex }); var entries = list.Cast <object>(); var listType = typeof(List <>).MakeGenericType(type); Console.WriteLine("Saving entries to XML..."); switch (parse.Mode.ToLowerInvariant()) { case "single": { var serializer = new DataContractSerializer(listType); const string entryName = "entries.xml"; resource.Entries.Add(entryName); var entryPath = Path.Combine(outputPath, entryName); if (File.Exists(entryPath) == true) { throw new InvalidOperationException(); } var entryParentPath = Path.GetDirectoryName(entryPath); if (string.IsNullOrEmpty(entryParentPath) == false) { Directory.CreateDirectory(entryParentPath); } using (var output = File.Create(entryPath)) { var localList = (IList)Activator.CreateInstance(listType); foreach (var entry in entries) { localList.Add(entry); } var writer = XmlWriter.Create(output, xmlSettings); serializer.WriteStartObject(writer, listType); writer.WriteAttributeString("xmlns", "c", "", "http://datacontract.gib.me/cryptic"); //writer.WriteAttributeString("xmlns", "i", "", "http://www.w3.org/2001/XMLSchema-instance"); writer.WriteAttributeString( "xmlns", "a", "", "http://schemas.microsoft.com/2003/10/Serialization/Arrays"); //writer.WriteAttributeString("xmlns", "s", "", "http://datacontract.gib.me/startrekonline"); serializer.WriteObjectContent(writer, localList); serializer.WriteEndObject(writer); writer.Flush(); } break; } case "file": { var serializer = new DataContractSerializer(listType); var fileNameFieldName = "FileName"; if (string.IsNullOrEmpty(target.FileNameKey) == false) { fileNameFieldName = target.FileNameKey; } var fileNameField = type.GetField( fileNameFieldName, BindingFlags.Public | BindingFlags.Instance); if (fileNameField == null) { Console.WriteLine("Class '{0}' does not expose '{1}'!", target.Class, fileNameFieldName); return; } var uniqueFileNames = entries .Select(i => (string)fileNameField.GetValue(i)) .Distinct(); foreach (var fileName in uniqueFileNames) { var entryName = fileName; entryName = entryName.Replace('/', '\\'); entryName = Path.ChangeExtension(entryName, ".xml"); resource.Entries.Add(entryName); var entryPath = Path.Combine(outputPath, entryName); if (File.Exists(entryPath) == true) { throw new InvalidOperationException(); } var entryParentPath = Path.GetDirectoryName(entryPath); if (string.IsNullOrEmpty(entryParentPath) == false) { Directory.CreateDirectory(entryParentPath); } using (var output = File.Create(entryPath)) { var localEntries = (IList)Activator.CreateInstance(listType); string name = fileName; foreach (var entry in entries .Where(e => (string)(fileNameField.GetValue(e)) == name)) { localEntries.Add(entry); } var writer = XmlWriter.Create(output, xmlSettings); serializer.WriteStartObject(writer, listType); writer.WriteAttributeString("xmlns", "c", "", "http://datacontract.gib.me/cryptic"); //writer.WriteAttributeString("xmlns", "i", "", "http://www.w3.org/2001/XMLSchema-instance"); writer.WriteAttributeString( "xmlns", "a", "", "http://schemas.microsoft.com/2003/10/Serialization/Arrays"); //writer.WriteAttributeString("xmlns", "s", "", "http://datacontract.gib.me/startrekonline"); serializer.WriteObjectContent(writer, localEntries); serializer.WriteEndObject(writer); writer.Flush(); } } break; } case "name": { var serializer = new DataContractSerializer(type); if (string.IsNullOrEmpty(target.Key) == true) { Console.WriteLine("No key set for '{0}'!", parseName); return; } var keyField = type.GetField( target.Key, BindingFlags.Public | BindingFlags.Instance); if (keyField == null) { Console.WriteLine("Class '{0}' does not expose '{1}'!", target.Class, target.Key); return; } foreach (var entry in entries) { var entryName = Path.ChangeExtension((string)keyField.GetValue(entry), ".xml"); resource.Entries.Add(entryName); var entryPath = Path.Combine(outputPath, entryName); if (File.Exists(entryPath) == true) { throw new InvalidOperationException(); } var entryParentPath = Path.GetDirectoryName(entryPath); if (string.IsNullOrEmpty(entryParentPath) == false) { Directory.CreateDirectory(entryParentPath); } using (var output = File.Create(entryPath)) { var writer = XmlWriter.Create(output, xmlSettings); serializer.WriteStartObject(writer, entry); writer.WriteAttributeString("xmlns", "c", "", "http://datacontract.gib.me/cryptic"); //writer.WriteAttributeString("xmlns", "i", "", "http://www.w3.org/2001/XMLSchema-instance"); writer.WriteAttributeString( "xmlns", "a", "", "http://schemas.microsoft.com/2003/10/Serialization/Arrays"); //writer.WriteAttributeString("xmlns", "s", "", "http://datacontract.gib.me/startrekonline"); serializer.WriteObjectContent(writer, entry); serializer.WriteEndObject(writer); writer.Flush(); } } break; } case "entry": { var serializer = new DataContractSerializer(type); var fileNameFieldName = "FileName"; if (string.IsNullOrEmpty(target.FileNameKey) == false) { fileNameFieldName = target.FileNameKey; } var fileNameField = type.GetField( fileNameFieldName, BindingFlags.Public | BindingFlags.Instance); if (fileNameField == null) { Console.WriteLine("Class '{0}' does not expose '{1}'!", target.Class, fileNameFieldName); return; } if (string.IsNullOrEmpty(target.Key) == true) { Console.WriteLine("No key set for '{0}'!", parseName); return; } var keyField = type.GetField( target.Key, BindingFlags.Public | BindingFlags.Instance); if (keyField == null) { Console.WriteLine("Class '{0}' does not expose '{1}'!", target.Class, target.Key); return; } foreach (var entry in entries) { var entryName = (string)fileNameField.GetValue(entry); entryName = entryName.Replace('/', '\\'); entryName = Path.ChangeExtension( Path.Combine(entryName, (string)keyField.GetValue(entry)), ".xml"); resource.Entries.Add(entryName); var entryPath = Path.Combine(outputPath, entryName); if (File.Exists(entryPath) == true) { throw new InvalidOperationException(); } var entryParentPath = Path.GetDirectoryName(entryPath); if (string.IsNullOrEmpty(entryParentPath) == false) { Directory.CreateDirectory(entryParentPath); } using (var output = File.Create(entryPath)) { var writer = XmlWriter.Create(output, xmlSettings); serializer.WriteStartObject(writer, entry); writer.WriteAttributeString("xmlns", "c", "", "http://datacontract.gib.me/cryptic"); //writer.WriteAttributeString("xmlns", "i", "", "http://www.w3.org/2001/XMLSchema-instance"); writer.WriteAttributeString( "xmlns", "a", "", "http://schemas.microsoft.com/2003/10/Serialization/Arrays"); //writer.WriteAttributeString("xmlns", "s", "", "http://datacontract.gib.me/startrekonline"); serializer.WriteObjectContent(writer, entry); serializer.WriteEndObject(writer); writer.Flush(); } } break; } default: { throw new NotSupportedException(); } } Console.WriteLine("Saving index..."); using (var output = File.Create(Path.Combine(outputPath, "@resource.xml"))) { var writer = XmlWriter.Create(output, xmlSettings); var serializer = new XmlSerializer(typeof(Resource)); serializer.Serialize(writer, resource); writer.Flush(); } } } else if (mode == Mode.Import) { var inputPath = extras[0]; var outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(inputPath, ".bin"); Console.WriteLine("Loading index..."); Resource resource; using (var input = File.OpenRead(Path.Combine(inputPath, "@resource.xml"))) { var reader = XmlReader.Create(input); var serializer = new XmlSerializer(typeof(Resource)); resource = (Resource)serializer.Deserialize(reader); } var schema = config.GetParse(resource.Parse); if (schema == null) { Console.WriteLine("Don't know how to handle '{0}'!", resource.Parse); return; } var target = schema.GetTarget(resource.ParseHash); if (target == null) { Console.WriteLine( "Don't know how to handle '{0}' with a hash of {1}.", resource.Parse, resource.ParseHash); return; } var version = target.FirstVersion(); if (version == null) { Console.WriteLine( "No support for '{0}' with a hash of {1:X8}.", resource.Parse, resource.ParseHash); return; } var assemblyPath = Path.Combine( GetExecutablePath(), "serializers", "assemblies", version + ".dll"); if (File.Exists(assemblyPath) == false) { Console.WriteLine( "Assembly '{0}' appears to be missing!", Path.GetFileName(assemblyPath)); return; } var assembly = Assembly.LoadFrom(assemblyPath); var type = assembly.GetType(target.Class); if (type == null) { Console.WriteLine( "Assembly '{0}' does not expose '{1}'!", Path.GetFileName(assemblyPath), target.Class); return; } var blob = new BlobFile() { ParseHash = resource.ParseHash, }; foreach (var file in resource.Files) { blob.Files.Add(new Blob.FileEntry() { Name = file.Name, Timestamp = file.Timestamp, }); } foreach (var dependency in resource.Dependencies) { blob.Dependencies.Add(new Blob.DependencyEntry() { Type = dependency.Type, Name = dependency.Name, Hash = dependency.Hash, }); } var listType = typeof(List <>).MakeGenericType(type); var entries = (IList)Activator.CreateInstance(listType); Console.WriteLine("Loading entries from XML..."); switch (schema.Mode.ToLowerInvariant()) { case "single": case "file": { var serializer = new DataContractSerializer(listType); foreach (var entryName in resource.Entries) { var entryPath = Path.IsPathRooted(entryName) == true ? entryName : Path.Combine(inputPath, entryName); using (var input = File.OpenRead(entryPath)) { var reader = XmlReader.Create(input); var localEntries = (IList)serializer.ReadObject(reader); foreach (var entry in localEntries) { entries.Add(entry); } } } break; } case "name": case "entry": { var serializer = new DataContractSerializer(type); foreach (var entryName in resource.Entries) { var entryPath = Path.IsPathRooted(entryName) == true ? entryName : Path.Combine(inputPath, entryName); using (var input = File.OpenRead(entryPath)) { var reader = XmlReader.Create(input); var entry = serializer.ReadObject(reader); entries.Add(entry); } } break; } default: { throw new NotSupportedException(); } } if (string.IsNullOrEmpty(target.Key) == false) { var keyField = type.GetField( target.Key, BindingFlags.Public | BindingFlags.Instance); if (keyField == null) { Console.WriteLine("Class '{0}' does not expose '{1}'!", target.Class, target.Key); return; } Console.WriteLine("Sorting entries..."); var sortedEntries = entries .Cast <object>() .OrderBy(keyField.GetValue) .ToList(); entries.Clear(); foreach (var entry in sortedEntries) { entries.Add(entry); } Func <string, int> getIndexFromFileName = s => blob.Files.FindIndex(fe => fe.Name == s); Console.WriteLine("Saving entries..."); var saveResource = typeof(BlobDataWriter) .GetMethod("SaveResource", BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(type); using (var output = File.Create(outputPath)) { blob.Serialize(output); saveResource.Invoke( null, new object[] { entries, output, schema.IsClient, schema.IsServer, getIndexFromFileName }); } } } else { throw new InvalidOperationException(); } }
public static void Main(string[] args) { var xmlSettings = new XmlWriterSettings() { Indent = true, NewLineChars = "\r\n", NewLineHandling = NewLineHandling.Replace, }; var configBasePath = Path.Combine(GetExecutablePath(), "serializers", "objects"); var config = Configuration.Load(configBasePath); string schemaName = null; var mode = Mode.Unknown; var showHelp = false; var options = new OptionSet() { { "b|xml2bin", "convert xml to bin", v => mode = v != null ? Mode.Import : mode }, { "x|bin2xml", "convert bin to xml", v => mode = v != null ? Mode.Export : mode }, { "s|schema=", "override schema name", v => schemaName = v }, { "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; } // try to figure out what they want to do if (mode == Mode.Unknown && extras.Count >= 1) { var testPath = extras[0]; if (Directory.Exists(testPath) == true) { mode = Mode.Import; } else if (File.Exists(testPath) == true) { mode = Mode.Export; } } if (extras.Count < 1 || extras.Count > 2 || showHelp == true || mode == Mode.Unknown) { Console.WriteLine("Usage: {0} [OPTIONS]+ -x input_bin [output_xml]", GetExecutableName()); Console.WriteLine(" {0} [OPTIONS]+ -b input_xml [output_bin]", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } if (mode == Mode.Export) { var inputPath = extras[0]; var outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(inputPath, ".xml"); using (var input = File.OpenRead(inputPath)) { Console.WriteLine("Loading bin..."); var blob = new BlobFile(); blob.Deserialize(input); if (schemaName == null) { schemaName = Path.GetFileNameWithoutExtension(inputPath); } var schema = config.GetSchema(schemaName); if (schema == null) { Console.WriteLine("Don't know how to handle '{0}' with a hash of '{1}'.", schemaName, blob.ParserHash); return; } var target = schema.GetTarget(blob.ParserHash); if (target == null) { Console.WriteLine("Don't know how to handle '{0}' with a hash of '{1}'.", schemaName, blob.ParserHash); return; } var version = target.FirstVersion(); if (version == null) { Console.WriteLine("No support for '{0}' with a hash of '{1}'.", schemaName, blob.ParserHash); return; } var assemblyPath = Path.Combine(GetExecutablePath(), "serializers", "assemblies", version + ".dll"); if (File.Exists(assemblyPath) == false) { Console.WriteLine("Assembly '{0}' appears to be missing!", Path.GetFileName(assemblyPath)); return; } var assembly = Assembly.LoadFrom(assemblyPath); var type = assembly.GetType(target.Class); if (type == null) { Console.WriteLine("Assembly '{0}' does not expose '{1}'!", Path.GetFileName(assemblyPath), target.Class); return; } var resource = new Resource { Schema = schemaName, ParserHash = blob.ParserHash, }; foreach (var file in blob.Files) { resource.Files.Add(new Resource.FileEntry() { Name = file.Name, Timestamp = file.Timestamp, }); } foreach (var dependency in blob.Dependencies) { resource.Dependencies.Add(new Resource.DependencyEntry() { Type = dependency.Type, Name = dependency.Name, Hash = dependency.Hash, }); } Func <int, string> getFileNameFromIndex = i => { if (i < 0 || i >= resource.Files.Count) { throw new KeyNotFoundException("file index " + i.ToString(CultureInfo.InvariantCulture) + " is out of range"); } return(resource.Files[i].Name); }; var loadObject = typeof(BlobDataReader) .GetMethod("LoadObject", BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(type); var data = loadObject.Invoke( null, new object[] { input, schema.IsClient, schema.IsServer, getFileNameFromIndex }); Console.WriteLine("Saving object to XML..."); using (var output = File.Create(outputPath)) { var writer = XmlWriter.Create(output, xmlSettings); writer.WriteStartDocument(); writer.WriteStartElement("object"); writer.WriteStartElement("data"); var objectWriter = XmlWriter.Create(writer, xmlSettings); var objectSerializer = new DataContractSerializer(type); objectSerializer.WriteStartObject(objectWriter, type); objectWriter.WriteAttributeString("xmlns", "c", "", "http://datacontract.gib.me/cryptic"); //objectWriter.WriteAttributeString("xmlns", "i", "", "http://www.w3.org/2001/XMLSchema-instance"); objectWriter.WriteAttributeString("xmlns", "a", "", "http://schemas.microsoft.com/2003/10/Serialization/Arrays"); //objectWriter.WriteAttributeString("xmlns", "s", "", "http://datacontract.gib.me/startrekonline"); objectSerializer.WriteObjectContent(objectWriter, data); objectSerializer.WriteEndObject(objectWriter); objectWriter.Flush(); writer.WriteEndElement(); var resourceWriter = XmlWriter.Create(writer, xmlSettings); var resourceSerializer = new XmlSerializer(typeof(Resource)); resourceSerializer.Serialize(resourceWriter, resource); resourceWriter.Flush(); writer.WriteEndElement(); writer.WriteEndDocument(); writer.Flush(); } } } else if (mode == Mode.Import) { var inputPath = extras[0]; var outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(inputPath, ".bin"); Console.WriteLine("Loading XML..."); var blob = new BlobFile(); using (var input = File.OpenRead(inputPath)) { var doc = new XPathDocument(input); var nav = doc.CreateNavigator(); var resourceNode = nav.SelectSingleNode("/object/resource"); if (resourceNode == null) { throw new InvalidOperationException(); } var resourceSerializer = new XmlSerializer(typeof(Resource)); var resource = (Resource)resourceSerializer.Deserialize(resourceNode.ReadSubtree()); var schema = config.GetSchema(resource.Schema); if (schema == null) { Console.WriteLine("Don't know how to handle '{0}'!", resource.Schema); return; } var target = schema.GetTarget(resource.ParserHash); if (target == null) { Console.WriteLine("Don't know how to handle '{0}' with a hash of {1}.", resource.Schema, resource.ParserHash); return; } var version = target.FirstVersion(); if (version == null) { Console.WriteLine("No support for '{0}' with a hash of {1:X8}.", resource.Schema, resource.ParserHash); return; } var assemblyPath = Path.Combine(GetExecutablePath(), "serializers", "assemblies", version + ".dll"); if (File.Exists(assemblyPath) == false) { Console.WriteLine("Assembly '{0}' appears to be missing!", Path.GetFileName(assemblyPath)); return; } var assembly = Assembly.LoadFrom(assemblyPath); var type = assembly.GetType(target.Class); if (type == null) { Console.WriteLine("Assembly '{0}' does not expose '{1}'!", Path.GetFileName(assemblyPath), target.Class); return; } blob.ParserHash = resource.ParserHash; foreach (var file in resource.Files) { blob.Files.Add(new Blob.FileEntry() { Name = file.Name, Timestamp = file.Timestamp, }); } foreach (var dependency in resource.Dependencies) { blob.Dependencies.Add(new Blob.DependencyEntry() { Type = dependency.Type, Name = dependency.Name, Hash = dependency.Hash, }); } var objectNode = nav.SelectSingleNode("/object/data"); if (objectNode == null) { throw new InvalidOperationException(); } objectNode.MoveToFirstChild(); var subtree = objectNode.ReadSubtree(); var objectSerializer = new DataContractSerializer(type); var data = objectSerializer.ReadObject(subtree); Func <string, int> getIndexFromFileName = s => blob.Files.FindIndex(fe => fe.Name == s); Console.WriteLine("Saving object..."); var saveResource = typeof(BlobDataWriter) .GetMethod("SaveObject", BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(type); using (var output = File.Create(outputPath)) { blob.Serialize(output); saveResource.Invoke( null, new[] { data, output, schema.IsClient, schema.IsServer, getIndexFromFileName }); } } } else { throw new InvalidOperationException(); } }
static void Main(string[] args) { string infPath; if (args.Length == 0) { infPath = Directory.GetCurrentDirectory(); } else { infPath = args[0]; } var md5 = MD5.Create(); var extractLocation = Path.Combine(infPath, "BloExtraction"); if (Directory.Exists(extractLocation)) { Directory.CreateDirectory(extractLocation); } Console.SetCursorPosition(0, 0); Console.Write("Blo Extractor"); Console.SetCursorPosition(0, 2); Console.Write("Working directory: " + infPath); foreach (var inputPath in Directory.GetFiles(infPath, "*.blo")) { var bloName = Path.GetFileName(inputPath); var bloLocation = Path.Combine(extractLocation, bloName); if (!Directory.Exists(bloLocation)) { Directory.CreateDirectory(bloLocation); } using (var input = File.OpenRead(inputPath)) { var blob = new BlobFile(); blob.Deserialize(input); Console.SetCursorPosition(0, 3); Console.Write(String.Format("Current blo: {0}", bloName).PadRight(Console.WindowWidth)); foreach (var cfsEntry in blob.Entries) { input.Seek(cfsEntry.Offset, SeekOrigin.Begin); var data = new byte[cfsEntry.Size]; input.Read(data, 0, data.Length); var hash = md5.ComputeHash(data); var friendlyHash = BitConverter .ToString(hash) .Replace("-", "") .ToLowerInvariant(); Console.SetCursorPosition(0, 4); Console.Write(String.Format("Current cfs: {0}", cfsEntry.Name).PadRight(Console.WindowWidth)); //Output the cfs file string cfsFile = Path.Combine(bloLocation, cfsEntry.Name); using (var output = File.Create(cfsFile)) { output.Write(data, 0, data.Length); } //Output a settings file using (var outputinfo = File.CreateText(Path.Combine(bloLocation, cfsEntry.Name + "-info.txt"))) { outputinfo.WriteLine("name=" + cfsEntry.Name); outputinfo.WriteLine("size=" + cfsEntry.Size); outputinfo.WriteLine("md5=" + friendlyHash); outputinfo.WriteLine("blo=" + bloName); outputinfo.WriteLine("newname=none"); outputinfo.WriteLine("newblo=none"); } //Lets try to create a sample image file, too if (Path.GetExtension(cfsEntry.Name) != ".cfs") { continue; } string previewFile = Path.Combine(bloLocation, cfsEntry.Name + "-preview.png"); Helpers.GenerateCFSPreview(cfsFile, previewFile, true); } } } Console.SetCursorPosition(0, 6); Console.Write("Blo Extraction complete..."); Console.ReadKey(); }
public static void Main(string[] args) { bool showHelp = false; bool resourceMapping = true; bool generateBlobs = false; bool stripDumbBlobs = false; var options = new OptionSet() { { "no-resource-mapping", "don't do resource mapping for older level files", v => resourceMapping = v == null }, { "g|generate-blobs", "generate blobs for older level files when resource mapping fails", v => generateBlobs = v != null }, { "s|strip-lvb-blob-references", "strip references to .lvb.blo blob files", v => stripDumbBlobs = v != null }, { "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_lvl [output_map]", 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, ".map"); string lvbPath = Path.ChangeExtension(inputPath, ".lvb"); string lvbName = Path.GetFileName(lvbPath); var level = new LevelFile(); using (var input = File.OpenRead(inputPath)) { level.Deserialize(input); } if (stripDumbBlobs == true) { for (int i = 0; i < level.Floors.Count; i++) { var floor = level.Floors[i]; if (floor.FileName != null && floor.FileName.EndsWith(".lvb.blo") == true) { floor.FileName = null; level.Floors[i] = floor; } } for (int i = 0; i < level.Objects.Count; i++) { var obj = level.Objects[i]; if (obj.FileName != null && obj.FileName.EndsWith(".lvb.blo") == true) { obj.FileName = null; level.Objects[i] = obj; } } } var hasLevelBlob = level.Floors.Any(f => f.FileName == null) == true || level.Objects.Any(o => o.FileName == null) == true; var remap = new Dictionary <string, string>(); if (hasLevelBlob == true) { // TODO: decrap this block of code var resources = resourceMapping == true ? LoadBloTable() : new SortedDictionary <string, string>(); if (File.Exists(lvbPath) == false) { Console.WriteLine("Could not open '{0}'!", lvbPath); Console.WriteLine(); Console.WriteLine("Mapping of LVB resources to real locations isn't going to happen."); } else { using (var lvb = File.OpenRead(lvbPath)) { var levelBlob = new BlobFile(); levelBlob.Deserialize(lvb); var md5 = MD5.Create(); var unknownEntries = new List <BlobFile.Entry>(); foreach (var entry in levelBlob.Entries) { lvb.Seek(entry.Offset, SeekOrigin.Begin); var data = new byte[entry.Size]; if (lvb.Read(data, 0, data.Length) != data.Length) { throw new FormatException(); } var hash = md5.ComputeHash(data); var friendlyHash = BitConverter .ToString(hash) .Replace("-", "") .ToLowerInvariant(); if (resources.ContainsKey(friendlyHash) == true) { remap[entry.Name] = resources[friendlyHash]; } else { Console.WriteLine("Could not find real location for '{0},{1}'.", lvbName, entry.Name); unknownEntries.Add(entry); } } if (unknownEntries.Count > 0) { Console.WriteLine("Could not find all of the level blob resources."); if (generateBlobs == true) { // ReSharper disable JoinDeclarationAndInitializer string floorBlobName; // ReSharper restore JoinDeclarationAndInitializer floorBlobName = "f_!"; floorBlobName += Path.GetFileNameWithoutExtension(inputPath); floorBlobName = Path.ChangeExtension(floorBlobName, ".blo"); // ReSharper disable JoinDeclarationAndInitializer string floorBlobPath; // ReSharper restore JoinDeclarationAndInitializer floorBlobPath = Path.GetDirectoryName(inputPath); if (floorBlobPath == null) { throw new InvalidOperationException(); } floorBlobPath = Path.Combine(floorBlobPath, floorBlobName); Console.WriteLine("Creating '{0}'...", floorBlobPath); using (var output = File.Create(floorBlobPath)) { var floorBlob = new BlobFile { Version = 2, }; // generate fake entries var floors = unknownEntries .Where(f => f.Name.StartsWith("f")) .ToArray(); foreach (var floor in floors) { floorBlob.Entries.Add(new BlobFile.Entry() { Name = floor.Name, }); } floorBlob.Serialize(output); // generate real entries floorBlob.Entries.Clear(); foreach (var floor in floors) { Console.WriteLine(" adding '{0}'", floor.Name); lvb.Seek(floor.Offset, SeekOrigin.Begin); floorBlob.Entries.Add(new BlobFile.Entry() { Name = floor.Name, Offset = output.Position, Size = floor.Size, }); output.WriteFromStream(lvb, floor.Size); remap[floor.Name] = string.Format("{0},{1}", floorBlobName, floor.Name); } output.Seek(0, SeekOrigin.Begin); floorBlob.Serialize(output); } // ReSharper disable JoinDeclarationAndInitializer string objectBlobName; // ReSharper restore JoinDeclarationAndInitializer objectBlobName = "o_!"; objectBlobName += Path.GetFileNameWithoutExtension(inputPath); objectBlobName = Path.ChangeExtension(objectBlobName, ".blo"); // ReSharper disable JoinDeclarationAndInitializer string objectBlobPath; // ReSharper restore JoinDeclarationAndInitializer objectBlobPath = Path.GetDirectoryName(inputPath); if (objectBlobPath == null) { throw new InvalidOperationException(); } objectBlobPath = Path.Combine(objectBlobPath, objectBlobName); Console.WriteLine("Creating '{0}'...", objectBlobPath); using (var output = File.Create(objectBlobPath)) { var objectBlob = new BlobFile { Version = 2, }; // generate fake entries var objects = unknownEntries .Where(o => o.Name.StartsWith("o")) .ToArray(); foreach (var obj in objects) { objectBlob.Entries.Add(new BlobFile.Entry() { Name = obj.Name, }); } objectBlob.Serialize(output); // generate real entries objectBlob.Entries.Clear(); foreach (var obj in objects) { Console.WriteLine(" adding '{0}'", obj.Name); lvb.Seek(obj.Offset, SeekOrigin.Begin); objectBlob.Entries.Add(new BlobFile.Entry() { Name = obj.Name, Offset = output.Position, Size = obj.Size, }); output.WriteFromStream(lvb, obj.Size); remap[obj.Name] = string.Format("{0},{1}", objectBlobName, obj.Name); } output.Seek(0, SeekOrigin.Begin); objectBlob.Serialize(output); } } } } } } using (var output = File.Create(outputPath)) { var header = new Map.Header { Version = 9, Width = level.Width, Height = level.Height, EntityCount = level.Entities.Count, PhysicsLow = new short[32], }; // not right? DERP //header.OffsetX = level.OffsetX; //header.OffsetY = level.OffsetY; Array.Copy(level.PhysicsLow, header.PhysicsLow, level.PhysicsLow.Length); header.PhysicsHigh = new short[32]; Array.Copy(level.PhysicsHigh, header.PhysicsHigh, level.PhysicsHigh.Length); header.LightColorWhite = level.LightColorWhite; header.LightColorRed = level.LightColorRed; header.LightColorGreen = level.LightColorGreen; header.LightColorBlue = level.LightColorBlue; output.WriteStructure(header); for (int i = 0; i < 8192; i++) { if (i < level.TerrainIds.Length) { output.WriteValueU8((byte)level.TerrainIds[i]); } else { output.WriteValueU8(0); } } for (int i = 0; i < 2048; i++) { var reference = new Map.BlobReference(); if (i < level.Floors.Count) { var floor = level.Floors[i]; if (floor.FileName == null) { if (remap.ContainsKey(floor.Id) == true) { reference.Path = remap[floor.Id]; } else { reference.Path = string.Format("{0},{1}", lvbName, floor.Id); } } else { reference.Path = string.Format("{0},{1}", floor.FileName, floor.Id); } } output.WriteStructure(reference); } var tiles = new byte[level.Width * level.Height * 4]; // ReSharper disable UnusedVariable int offset = 0; // ReSharper restore UnusedVariable for (int i = 0, j = 0; i < level.Tiles.Length; i++, j += 4) { tiles[j + 0] = level.Tiles[i].BitsA; tiles[j + 0] &= 0x7F; tiles[j + 1] = 0; tiles[j + 2] = level.Tiles[i].BitsC; tiles[j + 3] = level.Tiles[i].BitsB; } using (var rle = new MemoryStream()) { rle.WriteRLE(tiles, 4, level.Tiles.Length, false); rle.Position = 0; output.WriteValueS32((int)rle.Length); output.WriteFromStream(rle, rle.Length); } foreach (var t in level.Entities) { output.WriteStructure(t); var obj = level.Objects[t.ObjectId]; var reference = new Map.BlobReference(); if (obj.FileName == null) { if (remap.ContainsKey(obj.Id) == true) { reference.Path = remap[obj.Id]; } else { reference.Path = string.Format("{0},{1}", lvbName, obj.Id); } } else { reference.Path = string.Format("{0},{1}", obj.FileName, obj.Id); } output.WriteStructure(reference); } } }
public static void Main(string[] args) { 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 < 0 || extras.Count > 1 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ [input_directory]", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } var directoryPath = extras.Count == 0 ? Directory.GetCurrentDirectory() : extras[0]; var md5 = MD5.Create(); var resources = new SortedDictionary <string, List <ResourceLocation> >(); foreach (var inputPath in Directory.GetFiles(directoryPath, "*.blo")) { var fileName = Path.GetFileName(inputPath); if (fileName == null) { continue; } if (fileName.StartsWith("o_") == false && fileName.StartsWith("f_") == false) { continue; } if (fileName.EndsWith(".lvb.blo") == true) { continue; } Console.WriteLine("Processing '{0}'...", Path.GetFileName(inputPath)); using (var input = File.OpenRead(inputPath)) { var blob = new BlobFile(); blob.Deserialize(input); foreach (var entry in blob.Entries) { Console.WriteLine(" Hashing '{0}'", entry.Name); input.Seek(entry.Offset, SeekOrigin.Begin); var data = new byte[entry.Size]; if (input.Read(data, 0, data.Length) != data.Length) { throw new FormatException(); } var hash = md5.ComputeHash(data); var friendlyHash = BitConverter .ToString(hash) .Replace("-", "") .ToLowerInvariant(); if (resources.ContainsKey(friendlyHash) == false) { resources[friendlyHash] = new List <ResourceLocation>(); } resources[friendlyHash].Add( new ResourceLocation() { FileName = fileName.ToLowerInvariant(), Id = entry.Name.ToLowerInvariant(), }); } } } var custom = LoadBloTableCustom(); // ReSharper disable JoinDeclarationAndInitializer string outputPath; // ReSharper restore JoinDeclarationAndInitializer outputPath = Path.GetDirectoryName(GetExecutablePath()); if (outputPath == null) { throw new InvalidOperationException(); } outputPath = Path.Combine(outputPath, "blotable.xml"); var settings = new XmlWriterSettings() { Indent = true, }; using (var xml = XmlWriter.Create(outputPath, settings)) { xml.WriteStartDocument(); xml.WriteStartElement("resources"); xml.WriteStartElement("auto"); foreach (var resource in resources) { xml.WriteStartElement("resource"); xml.WriteAttributeString("hash", resource.Key); foreach (var location in resource.Value) { xml.WriteStartElement("source"); xml.WriteAttributeString("filename", location.FileName); xml.WriteAttributeString("id", location.Id); xml.WriteEndElement(); } xml.WriteEndElement(); } xml.WriteEndElement(); if (custom.Count > 0) { xml.WriteStartElement("custom"); foreach (var resource in custom) { xml.WriteStartElement("resource"); xml.WriteAttributeString("hash", resource.Key); foreach (var location in resource.Value) { xml.WriteStartElement("source"); xml.WriteAttributeString("filename", location.FileName); xml.WriteAttributeString("id", location.Id); xml.WriteEndElement(); } xml.WriteEndElement(); } xml.WriteEndElement(); } xml.WriteEndElement(); xml.WriteEndDocument(); } }