Ejemplo n.º 1
0
        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();
            }
        }
Ejemplo n.º 2
0
        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();
            }
        }
Ejemplo n.º 3
0
        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);
                }
            }
        }