Esempio n. 1
0
        public override bool Execute(List<string> args)
        {
            if (args.Count != 1)
                return false;

            var outDir = args[0];
            Directory.CreateDirectory(outDir);

            Console.WriteLine("Loading resource caches...");
            var resourceManager = new ResourceDataManager();
            try
            {
                resourceManager.LoadCachesFromDirectory(_info.CacheFile.DirectoryName);
            }
            catch
            {
                Console.WriteLine("Unable to load the resource .dat files.");
                Console.WriteLine("Make sure that they all exist and are valid.");
                return true;
            }

            var extractor = new BitmapDdsExtractor(resourceManager);
            var count = 0;
            using (var tagsStream = _info.OpenCacheRead())
            {
                foreach (var tag in _info.Cache.Tags.FindAllInGroup("bitm"))
                {
                    Console.Write("Extracting ");
                    TagPrinter.PrintTagShort(tag);

                    try
                    {
                        var tagContext = new TagSerializationContext(tagsStream, _info.Cache, _info.StringIDs, tag);
                        var bitmap = _info.Deserializer.Deserialize<TagDefinitions.Bitmap>(tagContext);
                        var ddsOutDir = outDir;
                        if (bitmap.Images.Count > 1)
                        {
                            ddsOutDir = Path.Combine(outDir, tag.Index.ToString("X8"));
                            Directory.CreateDirectory(ddsOutDir);
                        }
                        for (var i = 0; i < bitmap.Images.Count; i++)
                        {
                            var outPath = Path.Combine(ddsOutDir,
                                ((bitmap.Images.Count > 1) ? i.ToString() : tag.Index.ToString("X8")) + ".dds");
                            using (var outStream = File.Open(outPath, FileMode.Create, FileAccess.Write))
                            {
                                extractor.ExtractDds(_info.Deserializer, bitmap, i, outStream);
                            }
                        }
                        count++;
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("ERROR: Failed to extract bitmap: " + ex.Message);
                    }
                }
            }
            Console.WriteLine("Extracted {0} bitmaps.", count);
            return true;
        }
        private bool ImportBitmapResource(TagInstance tagIndex, string cachePath, string dataPath)
        {
            Bitmap        bitmap;
            ResourceCache resourceCache;
            uint          compressedSize = 0;

            using (var cacheStream = _info.OpenCacheRead())
            {
                var tagContext = new TagSerializationContext(cacheStream, _info.Cache, _info.StringIDs, tagIndex);
                bitmap = _info.Deserializer.Deserialize <Bitmap>(tagContext);
            }
            using (var stream = File.Open(_info.CacheFile.DirectoryName + "\\" + cachePath, FileMode.Open, FileAccess.ReadWrite))
            {
                int imageIndex = 0;
                foreach (string file in Directory.EnumerateFiles(dataPath, "*.bitm"))
                {
                    byte[] inBitmapData = File.ReadAllBytes(file);
                    resourceCache = new ResourceCache(stream);
                    bitmap.Resources[imageIndex].Resource.Index          = resourceCache.Add(stream, inBitmapData, out compressedSize);
                    bitmap.Resources[imageIndex].Resource.CompressedSize = compressedSize;
                    imageIndex++;
                }
            }
            using (var cacheStream = _fileInfo.Open(FileMode.Open, FileAccess.ReadWrite))
            {
                var context = new TagSerializationContext(cacheStream, _cache, _stringIds, tagIndex);
                _info.Serializer.Serialize(context, bitmap);
            }
            Console.WriteLine("{1}: Imported 0x{0}.", compressedSize, tagIndex);
            return(true);
        }
        private static void EditVFilesList(CommandContext context, OpenTagCache info, TagInstance tag)
        {
            VFilesList vfsl;

            using (var stream = info.OpenCacheRead())
                vfsl = info.Deserializer.Deserialize<VFilesList>(
                    new TagSerializationContext(stream, info.Cache, info.StringIds, tag));

            VFilesContextFactory.Populate(context, info, tag, vfsl);
        }
Esempio n. 4
0
        private static void EditVFilesList(CommandContext context, OpenTagCache info, TagInstance tag)
        {
            VFilesList vfsl;

            using (var stream = info.OpenCacheRead())
                vfsl = info.Deserializer.Deserialize <VFilesList>(
                    new TagSerializationContext(stream, info.Cache, info.StringIDs, tag));

            VFilesContextFactory.Populate(context, info, tag, vfsl);
        }
Esempio n. 5
0
        private static void EditRenderModel(CommandContext context, OpenTagCache info, TagInstance tag)
        {
            RenderModel renderModel;

            using (var stream = info.OpenCacheRead())
                renderModel = info.Deserializer.Deserialize <RenderModel>(
                    new TagSerializationContext(stream, info.Cache, info.StringIDs, tag));

            RenderModelContextFactory.Populate(context, info, tag, renderModel);
        }
Esempio n. 6
0
        private static void EditScenario(CommandContext context, OpenTagCache info, TagInstance tag)
        {
            Scenario scenario;

            using (var stream = info.OpenCacheRead())
                scenario = info.Deserializer.Deserialize <Scenario>(
                    new TagSerializationContext(stream, info.Cache, info.StringIDs, tag));

            ScnrContextFactory.Populate(context, info, tag, scenario);
        }
Esempio n. 7
0
        private static void EditAnimation(CommandContext context, OpenTagCache info, TagInstance tag)
        {
            ModelAnimationGraph animation;

            using (var stream = info.OpenCacheRead())
                animation = info.Deserializer.Deserialize <ModelAnimationGraph>(
                    new TagSerializationContext(stream, info.Cache, info.StringIDs, tag));

            AnimationContextFactory.Populate(context, info, tag, animation);
        }
Esempio n. 8
0
        private static void EditMultilingualUnicodeStringList(CommandContext context, OpenTagCache info, TagInstance tag)
        {
            MultilingualUnicodeStringList unic;

            using (var stream = info.OpenCacheRead())
                unic = info.Deserializer.Deserialize <MultilingualUnicodeStringList>(
                    new TagSerializationContext(stream, info.Cache, info.StringIDs, tag));

            UnicodeContextFactory.Populate(context, info, tag, unic);
        }
Esempio n. 9
0
        private static void EditBitmap(CommandContext context, OpenTagCache info, TagInstance tag)
        {
            Bitmap bitmap;

            using (var stream = info.OpenCacheRead())
                bitmap = info.Deserializer.Deserialize <Bitmap>(
                    new TagSerializationContext(stream, info.Cache, info.StringIDs, tag));

            BitmapContextFactory.Populate(context, info, tag, bitmap);
        }
Esempio n. 10
0
        public override bool Execute(List <string> args)
        {
            if (args.Count != 1 && args.Count != 2)
            {
                return(false);
            }
            GameLanguage language;

            if (!ArgumentParser.ParseLanguage(args[0], out language))
            {
                return(false);
            }
            var filter = (args.Count == 2) ? args[1] : null;

            var found = false;

            using (var stream = _info.OpenCacheRead())
            {
                foreach (var unicTag in _info.Cache.Tags.FindAllInGroup("unic"))
                {
                    var unic    = _info.Deserializer.Deserialize <MultilingualUnicodeStringList>(new TagSerializationContext(stream, _info.Cache, _info.StringIDs, unicTag));
                    var strings = LocalizedStringPrinter.PrepareForDisplay(unic, _info.StringIDs, unic.Strings, language, filter);
                    if (strings.Count == 0)
                    {
                        continue;
                    }
                    if (found)
                    {
                        Console.WriteLine();
                    }
                    Console.WriteLine("Strings found in {0:X8}.unic:", unicTag.Index);
                    LocalizedStringPrinter.PrintStrings(strings);
                    found = true;
                }
            }
            if (!found)
            {
                Console.Error.WriteLine("No strings found.");
            }

            return(true);
        }
Esempio n. 11
0
        public override bool Execute(List <string> args)
        {
            if (args.Count != 4)
            {
                return(false);
            }
            var srcTag = ArgumentParser.ParseTagIndex(_info, args[0]);

            if (srcTag == null)
            {
                return(false);
            }
            var csvPath    = args[1];
            var csvOutPath = args[2];
            var targetDir  = args[3];

            // Load the CSV
            Console.WriteLine("Reading {0}...", csvPath);
            TagVersionMap tagMap;

            using (var reader = new StreamReader(File.OpenRead(csvPath)))
                tagMap = TagVersionMap.ParseTagVersionMap(reader);

            // Load destination files
            Console.WriteLine("Loading the target tags.dat...");
            var destCachePath = Path.Combine(targetDir, "tags.dat");
            var destInfo      = new OpenTagCache {
                CacheFile = new FileInfo(destCachePath)
            };

            using (var stream = destInfo.OpenCacheRead())
                destInfo.Cache = new TagCache(stream);

            // Do version detection
            DefinitionSet guessedVersion;

            destInfo.Version = Definition.Detect(destInfo.Cache, out guessedVersion);
            if (destInfo.Version == DefinitionSet.Unknown)
            {
                Console.WriteLine("Unrecognized target version!");
                return(true);
            }
            Console.WriteLine("- Detected version {0}", Definition.GetVersionString(destInfo.Version));

            if (_info.Version != DefinitionSet.HaloOnline498295 && destInfo.Version != DefinitionSet.HaloOnline106708)
            {
                Console.Error.WriteLine("Conversion is only supported from 11.1.498295 Live to 1.106708 cert_ms23.");
                return(true);
            }

            // Set up version-specific objects
            destInfo.Serializer   = new TagSerializer(destInfo.Version);
            destInfo.Deserializer = new TagDeserializer(destInfo.Version);

            // Load stringIDs
            Console.WriteLine("Loading the target string_ids.dat...");

            var resolver = StringIDResolverFactory.Create(destInfo.Version);

            var destStringIDsPath = Path.Combine(targetDir, "string_ids.dat");

            destInfo.StringIDsFile = new FileInfo(destStringIDsPath);

            using (var stream = destInfo.StringIDsFile.OpenRead())
                destInfo.StringIDs = new StringIDCache(stream, resolver);

            // Load resources for the target build
            Console.WriteLine("Loading target resources...");
            var destResources = new ResourceDataManager();

            destResources.LoadCachesFromDirectory(destInfo.CacheFile.DirectoryName);

            // Load resources for our build
            Console.WriteLine("Loading source resources...");
            var srcResources = new ResourceDataManager();

            srcResources.LoadCachesFromDirectory(_info.CacheFile.DirectoryName);

            Console.WriteLine();
            Console.WriteLine("CONVERTING FROM VERSION {0} TO {1}", Definition.GetVersionString(_info.Version), Definition.GetVersionString(destInfo.Version));
            Console.WriteLine();

            TagInstance resultTag;

            using (Stream srcStream = _info.OpenCacheRead(), destStream = destInfo.OpenCacheReadWrite())
                resultTag = ConvertTag(srcTag, _info, srcStream, srcResources, destInfo, destStream, destResources, tagMap);

            Console.WriteLine();
            Console.WriteLine("Repairing decal systems...");
            FixDecalSystems(destInfo, resultTag.Index);

            Console.WriteLine();
            Console.WriteLine("Saving stringIDs...");
            using (var stream = destInfo.StringIDsFile.Open(FileMode.Open, FileAccess.ReadWrite))
                destInfo.StringIDs.Save(stream);

            Console.WriteLine("Writing {0}...", csvOutPath);
            using (var stream = new StreamWriter(File.Open(csvOutPath, FileMode.Create, FileAccess.ReadWrite)))
                tagMap.WriteCsv(stream);

            // Uncomment this to add the new tag as a dependency to cfgt to make testing easier

            /*using (var stream = destInfo.OpenCacheReadWrite())
             * {
             *  destInfo.Cache.Tags[0].Dependencies.Add(resultTag.Index);
             *  destInfo.Cache.UpdateTag(stream, destInfo.Cache.Tags[0]);
             * }*/

            Console.WriteLine();
            Console.WriteLine("All done! The converted tag is:");
            TagPrinter.PrintTagShort(resultTag);
            return(true);
        }
Esempio n. 12
0
        public static CommandContext Create(CommandContextStack stack, OpenTagCache info, TagInstance tag)
        {
            var groupName = info.StringIDs.GetString(tag.Group.Name);

            var tagName = $"0x{tag.Index:X4}";

            if (info.TagNames.ContainsKey(tag.Index))
            {
                tagName = info.TagNames[tag.Index];
                tagName = $"(0x{tag.Index:X4}) {tagName.Substring(tagName.LastIndexOf('\\') + 1)}";
            }

            var context = new CommandContext(stack.Context,
                                             string.Format("{0}.{1}", tagName, groupName));

            switch (tag.Group.Tag.ToString())
            {
            case "vfsl":     // vfiles_list
                EditVFilesList(context, info, tag);
                break;

            case "unic":     // multilingual_unicode_string_list
                EditMultilingualUnicodeStringList(context, info, tag);
                break;

            case "bitm":     // bitmap
                EditBitmap(context, info, tag);
                break;

            case "hlmt":     // model
                EditModel(context, info, tag);
                break;

            case "mode":     // render_model
                EditRenderModel(context, info, tag);
                break;

            case "jmad":
                EditAnimation(context, info, tag);
                break;

            case "rm  ":     // render_method
            case "rmsh":     // shader
            case "rmd ":     // shader_decal
            case "rmfl":     // shader_foliage
            case "rmhg":     // shader_halogram
            case "rmss":     // shader_screen
            case "rmtr":     // shader_terrain
            case "rmw ":     // shader_water
            case "rmzo":     // shader_zonly
            case "rmcs":     // shader_custom
                EditRenderMethod(context, info, tag);
                break;

            case "scnr":
                EditScenario(context, info, tag);
                break;
            }

            object value = null;

            using (var stream = info.OpenCacheRead())
                value = info.Deserializer.Deserialize(
                    new TagSerializationContext(stream, info.Cache, info.StringIDs, tag),
                    TagStructureTypes.FindByGroupTag(tag.Group.Tag));

            var structure = new TagStructureInfo(
                TagStructureTypes.FindByGroupTag(tag.Group.Tag));

            context.AddCommand(new ListFieldsCommand(info, structure, value));
            context.AddCommand(new SetFieldCommand(stack, info, tag, structure, value));
            context.AddCommand(new EditBlockCommand(stack, info, tag, value));
            context.AddCommand(new AddToCommand(stack, info, tag, structure, value));
            context.AddCommand(new RemoveFromCommand(stack, info, tag, structure, value));
            context.AddCommand(new CopyElementsCommand(stack, info, tag, structure, value));
            context.AddCommand(new PasteElementsCommand(stack, info, tag, structure, value));
            context.AddCommand(new SaveChangesCommand(info, tag, value));
            context.AddCommand(new ExitToCommand(stack));

            return(context);
        }
Esempio n. 13
0
        public override bool Execute(List <string> args)
        {
            if (args.Count != 2)
            {
                return(false);
            }

            var destDir = new DirectoryInfo(args[1]);

            if (!destDir.Exists)
            {
                WriteLine($"Destination cache directory does not exist: {destDir.FullName}");
                return(false);
            }

            var destTagsFile = new FileInfo(Combine(destDir.FullName, "tags.dat"));

            if (!destTagsFile.Exists)
            {
                WriteLine($"Destination tag cache file does not exist: {destTagsFile.FullName}");
                return(false);
            }

            var destStringIDsFile = new FileInfo(Combine(destDir.FullName, "string_ids.dat"));

            if (!destStringIDsFile.Exists)
            {
                WriteLine($"Destination string id cache file does not exist: {destStringIDsFile.FullName}");
                return(false);
            }

            var destResourcesFile = new FileInfo(Combine(destDir.FullName, "resources.dat"));

            if (!destResourcesFile.Exists)
            {
                WriteLine($"Destination resource cache file does not exist: {destResourcesFile.FullName}");
                return(false);
            }

            var destTexturesFile = new FileInfo(Combine(destDir.FullName, "textures.dat"));

            if (!destTexturesFile.Exists)
            {
                WriteLine($"Destination texture cache file does not exist: {destTexturesFile.FullName}");
                return(false);
            }

            var destTexturesBFile = new FileInfo(Combine(destDir.FullName, "textures_b.dat"));

            if (!destTexturesBFile.Exists)
            {
                WriteLine($"Destination texture cache file does not exist: {destTexturesBFile.FullName}");
                return(false);
            }

            var destAudioFile = new FileInfo(Combine(destDir.FullName, "audio.dat"));

            if (!destAudioFile.Exists)
            {
                WriteLine($"Destination audio cache file does not exist: {destAudioFile.FullName}");
                return(false);
            }

            TagCache destTagCache;

            using (var stream = destTagsFile.OpenRead())
                destTagCache = new TagCache(stream);

            DefinitionSet guessedVersion;
            var           destVersion = Detect(destTagCache, out guessedVersion);

            if (destVersion == Unknown)
            {
                WriteLine($"Unrecognized target version! (guessed {GetVersionString(guessedVersion)})");
                return(true);
            }

            WriteLine($"Destination cache version: {GetVersionString(destVersion)}");

            StringIDCache destStringIDCache;

            using (var stream = destStringIDsFile.OpenRead())
                destStringIDCache = new StringIDCache(stream, Create(destVersion));

            var destResources = new ResourceDataManager();

            destResources.LoadCachesFromDirectory(destDir.FullName);

            var srcResources = new ResourceDataManager();

            srcResources.LoadCachesFromDirectory(Info.CacheFile.DirectoryName);

            var destSerializer   = new TagSerializer(destVersion);
            var destDeserializer = new TagDeserializer(destVersion);

            var destInfo = new OpenTagCache
            {
                Cache         = destTagCache,
                CacheFile     = destTagsFile,
                StringIDs     = destStringIDCache,
                StringIDsFile = destStringIDsFile,
                Version       = destVersion,
                Serializer    = destSerializer,
                Deserializer  = destDeserializer
            };

            var destTag = ParseTagIndex(destInfo, args[0]);

            if (destTag == null || !destTag.IsInGroup(new Tag("mode")))
            {
                WriteLine("Destination tag must be of group 'mode'.");
                return(false);
            }

            RenderModel destDefinition;

            using (var destStream = destInfo.OpenCacheRead())
            {
                var context = new TagSerializationContext(destStream, destInfo.Cache, destInfo.StringIDs, destTag);
                destDefinition = destInfo.Deserializer.Deserialize <RenderModel>(context);
            }

            using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream())
            {
                // First extract the model data
                srcResources.Extract(Definition.Geometry.Resource, inStream);

                // Now open source and destination vertex streams
                inStream.Position = 0;
                var inVertexStream  = VertexStreamFactory.Create(Info.Version, inStream);
                var outVertexStream = VertexStreamFactory.Create(destInfo.Version, outStream);

                // Deserialize the definition data
                var resourceContext = new ResourceSerializationContext(Definition.Geometry.Resource);
                var definition      = Info.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext);

                // Convert each vertex buffer
                foreach (var buffer in definition.VertexBuffers)
                {
                    TagConverter.ConvertVertexBuffer(buffer.Definition, inStream, inVertexStream, outStream, outVertexStream);
                }

                // Copy each index buffer over
                foreach (var buffer in definition.IndexBuffers)
                {
                    if (buffer.Definition.Data.Size == 0)
                    {
                        continue;
                    }
                    inStream.Position = buffer.Definition.Data.Address.Offset;
                    buffer.Definition.Data.Address = new ResourceAddress(ResourceAddressType.Resource, (int)outStream.Position);
                    var bufferData = new byte[buffer.Definition.Data.Size];
                    inStream.Read(bufferData, 0, bufferData.Length);
                    outStream.Write(bufferData, 0, bufferData.Length);
                    StreamUtil.Align(outStream, 4);
                }

                destInfo.Serializer.Serialize(resourceContext, definition);

                outStream.Position = 0;
                destResources.Replace(destDefinition.Geometry.Resource, outStream);
            }

            return(true);
        }
        public override bool Execute(List <string> args)
        {
            if (args.Count != 2)
            {
                return(false);
            }
            var             type   = args[0];
            var             outDir = args[1];
            TagLayoutWriter writer;

            switch (type)
            {
            case "csharp":
                writer = new CSharpLayoutWriter();
                break;

            case "cpp":
                writer = new CppLayoutWriter();
                break;

            default:
                return(false);
            }
            Directory.CreateDirectory(outDir);
            var count = 0;

            using (var stream = _info.OpenCacheRead())
            {
                foreach (var groupTag in _cache.Tags.NonNull().Select(t => t.Group.Tag).Distinct())
                {
                    TagLayoutGuess layout  = null;
                    TagInstance    lastTag = null;
                    foreach (var tag in _cache.Tags.FindAllInGroup(groupTag))
                    {
                        Console.Write("Analyzing ");
                        TagPrinter.PrintTagShort(tag);

                        lastTag = tag;
                        var analyzer  = new TagAnalyzer(_cache);
                        var data      = _cache.ExtractTag(stream, tag);
                        var tagLayout = analyzer.Analyze(data);
                        if (layout != null)
                        {
                            layout.Merge(tagLayout);
                        }
                        else
                        {
                            layout = tagLayout;
                        }
                    }
                    if (layout != null && lastTag != null)
                    {
                        Console.WriteLine("Writing {0} layout", groupTag);
                        var name      = _info.StringIds.GetString(lastTag.Group.Name);
                        var tagLayout = LayoutGuessFinalizer.MakeLayout(layout, name, groupTag);
                        var path      = Path.Combine(outDir, writer.GetSuggestedFileName(tagLayout));
                        writer.WriteLayout(tagLayout, path);
                        count++;
                    }
                }
            }
            Console.WriteLine("Successfully generated {0} layouts!", count);
            return(true);
        }
        public override bool Execute(List <string> args)
        {
            if (args.Count < 2)
            {
                return(false);
            }
            var outputPath = args[0];

            // Load each file and do version detection
            var infos = new List <OpenTagCache>();

            foreach (var path in args.Skip(1))
            {
                Console.WriteLine("Loading {0}...", path);

                // Load the cache file
                var info = new OpenTagCache {
                    CacheFile = new FileInfo(path)
                };
                using (var stream = info.OpenCacheRead())
                    info.Cache = new TagCache(stream);

                // Do version detection, and don't accept the closest version
                // because that might not work
                EngineVersion closestVersion;
                info.Version = VersionDetection.DetectVersion(info.Cache, out closestVersion);
                if (info.Version == EngineVersion.Unknown)
                {
                    Console.WriteLine("- Unrecognized version! Ignoring.");
                    continue;
                }
                info.Deserializer = new TagDeserializer(info.Version);
                infos.Add(info);
            }

            var result = new TagVersionMap();

            using (var baseStream = _info.OpenCacheRead())
            {
                // Get the scenario tags for this cache
                Console.WriteLine("Finding base scenario tags...");
                var baseScenarios = FindScenarios(_info, baseStream);
                var baseVersion   = _info.Version;
                var baseTagData   = new Dictionary <int, object>();
                foreach (var scenario in baseScenarios)
                {
                    baseTagData[scenario.Tag.Index] = scenario.Data;
                }

                // Now compare with each of the other caches
                foreach (var info in infos)
                {
                    using (var stream = info.OpenCacheRead())
                    {
                        Console.WriteLine("Finding scenario tags in {0}...", info.CacheFile.FullName);

                        // Get the scenario tags and connect them to the base tags
                        var scenarios     = FindScenarios(info, stream);
                        var tagsToCompare = new Queue <QueuedTag>();
                        for (var i = 0; i < scenarios.Count; i++)
                        {
                            tagsToCompare.Enqueue(scenarios[i]);
                            if (i < baseScenarios.Count)
                            {
                                result.Add(baseVersion, baseScenarios[i].Tag.Index, info.Version, scenarios[i].Tag.Index);
                            }
                        }

                        // Process each tag in the queue, enqueuing all of its dependencies as well
                        while (tagsToCompare.Count > 0)
                        {
                            // Get the tag and its data
                            var tag = tagsToCompare.Dequeue();
                            TagPrinter.PrintTagShort(tag.Tag);
                            var data = tag.Data;
                            if (data == null)
                            {
                                // No data yet - deserialize it
                                var context = new TagSerializationContext(stream, info.Cache, info.StringIds, tag.Tag);
                                var type    = TagStructureTypes.FindByGroupTag(tag.Tag.Group.Tag);
                                data = info.Deserializer.Deserialize(context, type);
                            }

                            // Now get the data for the base tag
                            var baseTag = result.Translate(info.Version, tag.Tag.Index, baseVersion);
                            if (baseTag == -1 || _info.Cache.Tags[baseTag].Group.Tag != tag.Tag.Group.Tag)
                            {
                                continue;
                            }
                            object baseData;
                            if (!baseTagData.TryGetValue(baseTag, out baseData))
                            {
                                // No data yet - deserialize it
                                var context = new TagSerializationContext(baseStream, _info.Cache, _info.StringIds, _info.Cache.Tags[baseTag]);
                                var type    = TagStructureTypes.FindByGroupTag(tag.Tag.Group.Tag);
                                baseData             = _info.Deserializer.Deserialize(context, type);
                                baseTagData[baseTag] = baseData;
                            }

                            // Compare the two blocks
                            CompareBlocks(baseData, baseVersion, data, info.Version, result, tagsToCompare);
                        }
                    }
                }
            }

            // Write out the CSV
            Console.WriteLine("Writing results...");
            using (var writer = new StreamWriter(File.Open(outputPath, FileMode.Create, FileAccess.Write)))
                result.WriteCsv(writer);

            Console.WriteLine("Done!");
            return(true);
        }
        private static void EditMultilingualUnicodeStringList(CommandContext context, OpenTagCache info, TagInstance tag)
        {
            MultilingualUnicodeStringList unic;

            using (var stream = info.OpenCacheRead())
                unic = info.Deserializer.Deserialize<MultilingualUnicodeStringList>(
                    new TagSerializationContext(stream, info.Cache, info.StringIds, tag));

            UnicodeContextFactory.Populate(context, info, tag, unic);
        }
        private static void EditScenario(CommandContext context, OpenTagCache info, TagInstance tag)
        {
            Scenario scenario;

            using (var stream = info.OpenCacheRead())
                scenario = info.Deserializer.Deserialize<Scenario>(
                    new TagSerializationContext(stream, info.Cache, info.StringIds, tag));

            ScnrContextFactory.Populate(context, info, tag, scenario);
        }
        public static CommandContext Create(CommandContextStack stack, OpenTagCache info, TagInstance tag)
        {
            var groupName = info.StringIds.GetString(tag.Group.Name);

            var context = new CommandContext(stack.Context,
                string.Format("0x{0:X4}.{1}", tag.Index, groupName));

            switch (tag.Group.Tag.ToString())
            {
                case "vfsl": // vfiles_list
                    EditVFilesList(context, info, tag);
                    break;

                case "unic": // multilingual_unicode_string_list
                    EditMultilingualUnicodeStringList(context, info, tag);
                    break;

                case "bitm": // bitmap
                    EditBitmap(context, info, tag);
                    break;

                case "hlmt": // model
                    EditModel(context, info, tag);
                    break;

                case "mode": // render_model
                    EditRenderModel(context, info, tag);
                    break;

                case "rm  ": // render_method
                case "rmsh": // shader
                case "rmd ": // shader_decal
                case "rmfl": // shader_foliage
                case "rmhg": // shader_halogram
                case "rmss": // shader_screen
                case "rmtr": // shader_terrain
                case "rmw ": // shader_water
                case "rmzo": // shader_zonly
                case "rmcs": // shader_custom
                    EditRenderMethod(context, info, tag);
                    break;

                case "scnr":
                    EditScenario(context, info, tag);
                    break;
            }

            object value = null;

            using (var stream = info.OpenCacheRead())
                value = info.Deserializer.Deserialize(
                    new TagSerializationContext(stream, info.Cache, info.StringIds, tag),
                    TagStructureTypes.FindByGroupTag(tag.Group.Tag));

            var structure = new TagStructureInfo(
                TagStructureTypes.FindByGroupTag(tag.Group.Tag));

            context.AddCommand(new ListFieldsCommand(info, structure, value));
            context.AddCommand(new SetFieldCommand(stack, info, tag, structure, value));
            context.AddCommand(new EditBlockCommand(stack, info, tag, value));
            context.AddCommand(new AddToBlockCommand(stack, info, tag, structure, value));
            context.AddCommand(new RemoveFromBlockCommand(stack, info, tag, structure, value));
            context.AddCommand(new SaveChangesCommand(info, tag, value));
            context.AddCommand(new ExitToCommand(stack));

            return context;
        }
        private static void EditRenderModel(CommandContext context, OpenTagCache info, TagInstance tag)
        {
            RenderModel renderModel;

            using (var stream = info.OpenCacheRead())
                renderModel = info.Deserializer.Deserialize<RenderModel>(
                    new TagSerializationContext(stream, info.Cache, info.StringIds, tag));

            RenderModelContextFactory.Populate(context, info, tag, renderModel);
        }
        private static void EditBitmap(CommandContext context, OpenTagCache info, TagInstance tag)
        {
            Bitmap bitmap;

            using (var stream = info.OpenCacheRead())
                bitmap = info.Deserializer.Deserialize<Bitmap>(
                    new TagSerializationContext(stream, info.Cache, info.StringIds, tag));

            BitmapContextFactory.Populate(context, info, tag, bitmap);
        }
        public override bool Execute(List<string> args)
        {
            if (args.Count < 2)
                return false;
            var outputPath = args[0];

            // Load each file and do version detection
            var infos = new List<OpenTagCache>();
            foreach (var path in args.Skip(1))
            {
                Console.WriteLine("Loading {0}...", path);

                // Load the cache file
                var info = new OpenTagCache { CacheFile = new FileInfo(path) };
                using (var stream = info.OpenCacheRead())
                    info.Cache = new TagCache(stream);

                // Do version detection, and don't accept the closest version
                // because that might not work
                EngineVersion closestVersion;
                info.Version = VersionDetection.DetectVersion(info.Cache, out closestVersion);
                if (info.Version == EngineVersion.Unknown)
                {
                    Console.WriteLine("- Unrecognized version! Ignoring.");
                    continue;
                }
                info.Deserializer = new TagDeserializer(info.Version);
                infos.Add(info);
            }

            var result = new TagVersionMap();
            using (var baseStream = _info.OpenCacheRead())
            {
                // Get the scenario tags for this cache
                Console.WriteLine("Finding base scenario tags...");
                var baseScenarios = FindScenarios(_info, baseStream);
                var baseVersion = _info.Version;
                var baseTagData = new Dictionary<int, object>();
                foreach (var scenario in baseScenarios)
                    baseTagData[scenario.Tag.Index] = scenario.Data;

                // Now compare with each of the other caches
                foreach (var info in infos)
                {
                    using (var stream = info.OpenCacheRead())
                    {
                        Console.WriteLine("Finding scenario tags in {0}...", info.CacheFile.FullName);

                        // Get the scenario tags and connect them to the base tags
                        var scenarios = FindScenarios(info, stream);
                        var tagsToCompare = new Queue<QueuedTag>();
                        for (var i = 0; i < scenarios.Count; i++)
                        {
                            tagsToCompare.Enqueue(scenarios[i]);
                            result.Add(baseVersion, baseScenarios[i].Tag.Index, info.Version, scenarios[i].Tag.Index);
                        }

                        // Process each tag in the queue, enqueuing all of its dependencies as well
                        while (tagsToCompare.Count > 0)
                        {
                            // Get the tag and its data
                            var tag = tagsToCompare.Dequeue();
                            TagPrinter.PrintTagShort(tag.Tag);
                            var data = tag.Data;
                            if (data == null)
                            {
                                // No data yet - deserialize it
                                var context = new TagSerializationContext(stream, info.Cache, info.StringIds, tag.Tag);
                                var type = TagStructureTypes.FindByGroupTag(tag.Tag.Group.Tag);
                                data = info.Deserializer.Deserialize(context, type);
                            }

                            // Now get the data for the base tag
                            var baseTag = result.Translate(info.Version, tag.Tag.Index, baseVersion);
                            if (baseTag == -1 || _info.Cache.Tags[baseTag].Group.Tag != tag.Tag.Group.Tag)
                                continue;
                            object baseData;
                            if (!baseTagData.TryGetValue(baseTag, out baseData))
                            {
                                // No data yet - deserialize it
                                var context = new TagSerializationContext(baseStream, _info.Cache, _info.StringIds, _info.Cache.Tags[baseTag]);
                                var type = TagStructureTypes.FindByGroupTag(tag.Tag.Group.Tag);
                                baseData = _info.Deserializer.Deserialize(context, type);
                                baseTagData[baseTag] = baseData;
                            }

                            // Compare the two blocks
                            CompareBlocks(baseData, baseVersion, data, info.Version, result, tagsToCompare);
                        }
                    }
                }
            }

            // Write out the CSV
            Console.WriteLine("Writing results...");
            using (var writer = new StreamWriter(File.Open(outputPath, FileMode.Create, FileAccess.Write)))
                result.WriteCsv(writer);

            Console.WriteLine("Done!");
            return true;
        }
        public static CommandContext Create(CommandContextStack stack, OpenTagCache info, TagInstance tag)
        {
            var groupName = info.StringIDs.GetString(tag.Group.Name);

            var tagName = $"0x{tag.Index:X4}";

            if (info.TagNames.ContainsKey(tag.Index))
            {
                tagName = info.TagNames[tag.Index];
                tagName = $"(0x{tag.Index:X4}) {tagName.Substring(tagName.LastIndexOf('\\') + 1)}";
            }

            var context = new CommandContext(stack.Context,
                string.Format("{0}.{1}", tagName, groupName));

            object value = null;

            using (var stream = info.OpenCacheRead())
                value = info.Deserializer.Deserialize(
                    new TagSerializationContext(stream, info.Cache, info.StringIDs, tag),
                    TagStructureTypes.FindByGroupTag(tag.Group.Tag));

            switch (tag.Group.Tag.ToString())
            {
                case "vfsl": // vfiles_list
                    VFilesContextFactory.Populate(context, info, tag, (VFilesList)value);
                    break;

                case "unic": // multilingual_unicode_string_list
                    UnicodeContextFactory.Populate(context, info, tag, (MultilingualUnicodeStringList)value);
                    break;

                case "bitm": // bitmap
                    BitmapContextFactory.Populate(context, info, tag, (Bitmap)value);
                    break;

                case "hlmt": // model
                    ModelContextFactory.Populate(context, info, tag, (Model)value);
                    break;

                case "mode": // render_model
                    RenderModelContextFactory.Populate(context, info, tag, (RenderModel)value);
                    break;

                case "jmad":
                    AnimationContextFactory.Populate(context, info, tag, (ModelAnimationGraph)value);
                    break;

                case "rm  ": // render_method
                case "rmsh": // shader
                case "rmd ": // shader_decal
                case "rmfl": // shader_foliage
                case "rmhg": // shader_halogram
                case "rmss": // shader_screen
                case "rmtr": // shader_terrain
                case "rmw ": // shader_water
                case "rmzo": // shader_zonly
                case "rmcs": // shader_custom
                    RenderMethodContextFactory.Populate(context, info, tag, (RenderMethod)value);
                    break;

                case "scnr":
                    ScnrContextFactory.Populate(context, info, tag, (Scenario)value);
                    break;

                case "sbsp":
                    BSPContextFactory.Populate(context, info, tag, (ScenarioStructureBsp)value);
                    break;
            }

            var structure = new TagStructureInfo(
                TagStructureTypes.FindByGroupTag(tag.Group.Tag));

            context.AddCommand(new ListFieldsCommand(info, structure, value));
            context.AddCommand(new SetFieldCommand(stack, info, tag, structure, value));
            context.AddCommand(new EditBlockCommand(stack, info, tag, value));
            context.AddCommand(new AddToCommand(stack, info, tag, structure, value));
            context.AddCommand(new RemoveFromCommand(stack, info, tag, structure, value));
            context.AddCommand(new CopyElementsCommand(stack, info, tag, structure, value));
            context.AddCommand(new PasteElementsCommand(stack, info, tag, structure, value));
            context.AddCommand(new SaveChangesCommand(info, tag, value));
            context.AddCommand(new ExitToCommand(stack));

            return context;
        }