/// <summary>
 /// Creates a tag serialization context which serializes data into a tag.
 /// </summary>
 /// <param name="stream">The stream to write to.</param>
 /// <param name="cache">The cache file to write to.</param>
 /// <param name="stringIds">The stringID source to use.</param>
 /// <param name="tag">The tag to overwrite.</param>
 public TagSerializationContext(Stream stream, TagCache cache, StringIDCache stringIds, TagInstance tag)
 {
     _stream    = stream;
     _cache     = cache;
     _stringIds = stringIds;
     Tag        = tag;
 }
        public ImportResourceCommand(OpenTagCache info) : base(
                CommandFlags.Inherit,

                "importresource",
                "Import as a new resource for a specified tag.",

                "importresource <cache file> <tag> <input file>\n",

                "For bitm, it only works with permutations. The path must end with a folder, and not a file.\n")
        {
            _info      = info;
            _cache     = info.Cache;
            _fileInfo  = info.CacheFile;
            _stringIds = info.StringIDs;
        }
Example #3
0
        public StringIDCommand(OpenTagCache info) : base(
                CommandFlags.Inherit,

                "stringid",
                "Add, look up, or find stringID values",

                "stringid add <string>\n" +
                "stringid get <id>\n" +
                "stringid list [filter]",

                "\"stringid add\" will add a new stringID.\n" +
                "\"stringid get\" will display the string corresponding to an ID value.\n" +
                "\"stringid list\" will list stringIDs, optionally filtering them.")
        {
            _info      = info;
            _stringIds = info.StringIDs;
        }
Example #4
0
        public static Tag ParseGroupTag(StringIDCache stringIDs, string groupName)
        {
            if (TagStructureTypes.IsGroupTag(groupName))
            {
                return(new Tag(groupName));
            }

            foreach (var pair in TagGroup.Instances)
            {
                if (groupName == stringIDs.GetString(pair.Value.Name))
                {
                    return(pair.Value.Tag);
                }
            }

            return(Tag.Null);
        }
Example #5
0
        public override bool Execute(List <string> args)
        {
            if (args.Count != 1)
            {
                return(false);
            }

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

            if (!destDir.Exists)
            {
                Write("Destination directory does not exist. Create it? [y/n] ");
                var answer = ReadLine().ToLower();

                if (answer.Length == 0 || !(answer.StartsWith("y") || answer.StartsWith("n")))
                {
                    return(false);
                }

                if (answer.StartsWith("y"))
                {
                    destDir.Create();
                }
                else
                {
                    return(false);
                }
            }

            WriteLine($"Generating cache files in \"{destDir.FullName}\"...");

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

            Write($"Generating {destTagsFile.FullName}...");
            using (var tagCacheStream = destTagsFile.Create())
                using (var writer = new BinaryWriter(tagCacheStream))
                {
                    writer.Write((int)0);                   // padding
                    writer.Write((int)0);                   // tag list offset
                    writer.Write((int)0);                   // tag count
                    writer.Write((int)0);                   // padding
                    writer.Write((long)130713360239499012); // timestamp
                    writer.Write((long)0);                  // padding
                }
            WriteLine("done.");

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

            Write($"Generating {destStringIDsFile.FullName}...");
            using (var stringIDCacheStream = destStringIDsFile.Create())
                using (var writer = new BinaryWriter(stringIDCacheStream))
                {
                    writer.Write((int)0); // string count
                    writer.Write((int)0); // data size
                }
            WriteLine("done.");

            var resourceCachePaths = new string[]
            {
                Combine(destDir.FullName, "audio.dat"),
                Combine(destDir.FullName, "resources.dat"),
                Combine(destDir.FullName, "textures.dat"),
                Combine(destDir.FullName, "textures_b.dat"),
                Combine(destDir.FullName, "video.dat")
            };

            foreach (var resourceCachePath in resourceCachePaths)
            {
                Write($"Generating {resourceCachePath}...");
                using (var resourceCacheStream = File.Create(resourceCachePath))
                    using (var writer = new BinaryWriter(resourceCacheStream))
                    {
                        writer.Write((int)0); // padding
                        writer.Write((int)0); // table offset
                        writer.Write((int)0); // resource count
                        writer.Write((int)0); // padding
                    }
                WriteLine("done.");
            }

            var dependencies = new Dictionary <int, TagInstance>();

            LoadTagDependencies(0, ref dependencies);
            LoadTagDependencies(0x16, ref dependencies);
            LoadTagDependencies(0x27D7, ref dependencies);

            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
            };

            using (Stream srcStream = Info.OpenCacheRead(), destStream = destInfo.OpenCacheReadWrite())
            {
                var maxDependency = dependencies.Keys.Max();
                for (var i = 0; i <= maxDependency; i++)
                {
                    var srcTag = Info.Cache.Tags[i];
                    if (srcTag == null)
                    {
                        destInfo.Cache.AllocateTag();
                        continue;
                    }

                    var srcData = Info.Cache.ExtractTagRaw(srcStream, srcTag);

                    var destTag = destInfo.Cache.AllocateTag(srcTag.Group);
                    destInfo.Cache.SetTagDataRaw(destStream, destTag, srcData);

                    srcData = new byte[0];
                }
            }

            WriteLine($"Done generating cache files in \"{destDir.FullName}\".");

            return(true);
        }
Example #6
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);
        }
Example #7
0
        /// <summary>
        /// Filters a set of localized strings and prepares them for display.
        /// </summary>
        /// <param name="unic">The string list.</param>
        /// <param name="stringIds">The string ID cache to use.</param>
        /// <param name="strings">The strings to display.</param>
        /// <param name="language">The language to display strings from.</param>
        /// <param name="filter">The filter to match strings and stringIDs against. Can be <c>null</c> to display everything.</param>
        /// <returns>The strings to print.</returns>
        public static List <DisplayString> PrepareForDisplay(MultilingualUnicodeStringList unic, StringIDCache stringIds, IEnumerable <LocalizedString> strings, GameLanguage language, string filter)
        {
            // Filter the input strings
            var display = new List <DisplayString>();

            foreach (var localizedString in strings)
            {
                var str = unic.GetString(localizedString, language);
                if (str == null)
                {
                    continue;
                }
                var stringId = stringIds.GetString(localizedString.StringID);
                if (filter != null && !str.Contains(filter) && !stringId.Contains(filter))
                {
                    continue;
                }
                display.Add(new DisplayString
                {
                    StringID = stringId,
                    String   = str
                });
            }
            display.Sort((a, b) => String.Compare(a.StringID, b.StringID, StringComparison.Ordinal));
            return(display);
        }
Example #8
0
        public static List <Tag> ParseGroupTags(StringIDCache stringIDs, IEnumerable <string> classNames)
        {
            var searchClasses = classNames.Select(a => ParseGroupTag(stringIDs, a)).ToList();

            return((searchClasses.Any(c => c.Value == -1)) ? null : searchClasses);
        }
        public override bool Execute(List <string> args)
        {
            if (args.Count != 4)
            {
                return(false);
            }

            var sourceTag = ParseTagIndex(Info, args[0]);

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

            var inputCsv = new FileInfo(args[1]);

            if (!inputCsv.Exists)
            {
                WriteLine($"Input csv file does not exist: {inputCsv.FullName}");
                return(false);
            }

            var outputCsv = new FileInfo(args[2]);

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

            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);
            }

            TagVersionMap tagMap;

            using (var reader = new StreamReader(inputCsv.OpenRead()))
                tagMap = ParseTagVersionMap(reader);

            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);

            return(true);
        }
Example #10
0
        static void Main(string[] args)
        {
            CultureInfo.DefaultThreadCurrentCulture = CultureInfo.GetCultureInfo("en-US");
            ConsoleHistory.Initialize();

            // Get the file path from the first argument
            // If no argument is given, load tags.dat
            var filePath = (args.Length > 0) ? args[0] : "tags.dat";

            // If there are extra arguments, use them to automatically execute a command
            List <string> autoexecCommand = null;

            if (args.Length > 1)
            {
                autoexecCommand = args.Skip(1).ToList();
            }

            if (autoexecCommand == null)
            {
                Console.WriteLine("Tag Tool [{0}]", Assembly.GetExecutingAssembly().GetName().Version);
                Console.WriteLine();
                Console.WriteLine("Please report any bugs and feature requests at");
                Console.WriteLine("<https://github.com/camden-smallwood/TagTool/issues>.");
                Console.WriteLine();
                Console.Write("Reading tags...");
            }

            // Load the tag cache
            FileInfo fileInfo = null;
            TagCache cache    = null;

            try
            {
                fileInfo = new FileInfo(filePath);
                using (var stream = fileInfo.Open(FileMode.Open, FileAccess.Read))
                    cache = new TagCache(stream);
            }
            catch (Exception e)
            {
                Console.WriteLine("ERROR: " + e.Message);
                ConsoleHistory.Dump("hott_*_tags_init.log");
                return;
            }

            if (autoexecCommand == null)
            {
                Console.WriteLine("{0} tags loaded.", cache.Tags.Count);
            }

            // Version detection
            DefinitionSet closestVersion;
            var           version = Definition.Detect(cache, out closestVersion);

            if (version != DefinitionSet.Unknown)
            {
                if (autoexecCommand == null)
                {
                    var buildDate = DateTime.FromFileTime(cache.Timestamp);
                    Console.WriteLine("- Detected target engine version {0}.", Definition.GetVersionString(closestVersion));
                    Console.WriteLine("- This cache file was built on {0} at {1}.", buildDate.ToShortDateString(), buildDate.ToShortTimeString());
                }
            }
            else
            {
                Console.WriteLine("WARNING: The cache file's version was not recognized!");
                Console.WriteLine("Using the closest known version {0}.", Definition.GetVersionString(closestVersion));
                version = closestVersion;
            }

            // Load stringIDs
            Console.Write("Reading stringIDs...");
            var           stringIdPath = Path.Combine(fileInfo.DirectoryName ?? "", "string_ids.dat");
            var           resolver     = StringIDResolverFactory.Create(version);
            StringIDCache stringIds    = null;

            try
            {
                using (var stream = File.OpenRead(stringIdPath))
                    stringIds = new StringIDCache(stream, resolver);
            }
            catch (IOException)
            {
                Console.WriteLine("Warning: unable to open string_ids.dat!");
                Console.WriteLine("Commands which require stringID values will be unavailable.");
            }
            catch (Exception e)
            {
                Console.WriteLine("ERROR: " + e.Message);
                ConsoleHistory.Dump("hott_*_string_ids_init.log");
                return;
            }

            if (autoexecCommand == null && stringIds != null)
            {
                Console.WriteLine("{0} strings loaded.", stringIds.Strings.Count);
                Console.WriteLine();
            }

            var info = new OpenTagCache
            {
                Cache         = cache,
                CacheFile     = fileInfo,
                StringIDs     = stringIds,
                StringIDsFile = (stringIds != null) ? new FileInfo(stringIdPath) : null,
                Version       = version,
                Serializer    = new TagSerializer(version),
                Deserializer  = new TagDeserializer(version),
            };

            var tagNamesPath = "TagNames\\tagnames_" + Definition.GetVersionString(version) + ".csv";

            if (File.Exists(tagNamesPath))
            {
                using (var tagNamesStream = File.Open(tagNamesPath, FileMode.Open, FileAccess.Read))
                {
                    var reader = new StreamReader(tagNamesStream);

                    while (!reader.EndOfStream)
                    {
                        var line           = reader.ReadLine();
                        var separatorIndex = line.IndexOf(',');
                        var indexString    = line.Substring(2, separatorIndex - 2);

                        int tagIndex;
                        if (!int.TryParse(indexString, NumberStyles.HexNumber, null, out tagIndex))
                        {
                            tagIndex = -1;
                        }

                        if (tagIndex < 0 || tagIndex >= cache.Tags.Count)
                        {
                            continue;
                        }

                        var nameString = line.Substring(separatorIndex + 1);

                        if (nameString.Contains(" "))
                        {
                            var lastSpaceIndex = nameString.LastIndexOf(' ');
                            nameString = nameString.Substring(lastSpaceIndex + 1, nameString.Length - lastSpaceIndex - 1);
                        }

                        info.TagNames[tagIndex] = nameString;
                    }

                    reader.Close();
                }
            }

            foreach (var tag in info.Cache.Tags)
            {
                if (tag != null && !info.TagNames.ContainsKey(tag.Index))
                {
                    info.TagNames[tag.Index] = $"0x{tag.Index:X4}";
                }
            }

            // Create command context
            var contextStack = new CommandContextStack();
            var tagsContext  = TagCacheContextFactory.Create(contextStack, info);

            contextStack.Push(tagsContext);

            // If autoexecuting a command, just run it and return
            if (autoexecCommand != null)
            {
                if (!ExecuteCommand(contextStack.Context, autoexecCommand))
                {
                    Console.WriteLine("Unrecognized command: {0}", autoexecCommand[0]);
                }
                return;
            }

            Console.WriteLine("Enter \"help\" to list available commands. Enter \"exit\" to quit.");
            while (true)
            {
                // Read and parse a command
                Console.WriteLine();
                Console.Write("{0}> ", contextStack.GetPath());
                var commandLine = Console.ReadLine();
                if (commandLine == null)
                {
                    break;
                }
                string redirectFile;
                var    commandArgs = ArgumentParser.ParseCommand(commandLine, out redirectFile);
                if (commandArgs.Count == 0)
                {
                    continue;
                }

                // If "exit" or "quit" is given, pop the current context
                if (commandArgs[0] == "exit" || commandArgs[0] == "quit")
                {
                    if (!contextStack.Pop())
                    {
                        break; // No more contexts - quit
                    }
                    continue;
                }

                // Handle redirection
                var          oldOut         = Console.Out;
                StreamWriter redirectWriter = null;
                if (redirectFile != null)
                {
                    redirectWriter = new StreamWriter(File.Open(redirectFile, FileMode.Create, FileAccess.Write));
                    Console.SetOut(redirectWriter);
                }

                // Try to execute it
                if (!ExecuteCommand(contextStack.Context, commandArgs))
                {
                    Console.WriteLine("Unrecognized command: {0}", commandArgs[0]);
                    Console.WriteLine("Use \"help\" to list available commands.");
                }

                // Undo redirection
                if (redirectFile != null)
                {
                    Console.SetOut(oldOut);
                    redirectWriter.Dispose();
                    Console.WriteLine("Wrote output to {0}.", redirectFile);
                }
            }
        }