Exemplo n.º 1
0
        /// <summary>
        /// Attempts to get a tag of a specific type from the current cache.
        /// </summary>
        /// <typeparam name="T">The type of the tag definition.</typeparam>
        /// <param name="name">The name of the tag.</param>
        /// <param name="result">The resulting tag.</param>
        /// <returns>True if the tag was found, false otherwise.</returns>
        public bool TryGetTag <T>(string name, out CachedTagInstance result) where T : TagStructure
        {
            if (name == "none" || name == "null")
            {
                result = null;
                return(true);
            }

            if (Tags.TagDefinition.Types.Values.Contains(typeof(T)))
            {
                var groupTag = Tags.TagDefinition.Types.First((KeyValuePair <Tag, Type> entry) => entry.Value == typeof(T)).Key;

                foreach (var instance in TagCache.Index)
                {
                    if (instance is null)
                    {
                        continue;
                    }

                    if (instance.IsInGroup(groupTag) && instance.Name == name)
                    {
                        result = instance;
                        return(true);
                    }
                }
            }

            result = null;
            return(false);
        }
Exemplo n.º 2
0
 /// <summary>
 /// Fixes tag offsets after a resize operation.
 /// </summary>
 /// <param name="startOffset">The offset where the resize operation took place.</param>
 /// <param name="sizeDelta">The amount to add to each tag offset after the start offset.</param>
 /// <param name="ignore">A tag to ignore.</param>
 private void FixTagOffsets(long startOffset, long sizeDelta, CachedTagInstance ignore)
 {
     foreach (var adjustTag in _tags.Where(t => t != null && t != ignore && t.HeaderOffset >= startOffset))
     {
         adjustTag.HeaderOffset += sizeDelta;
     }
 }
Exemplo n.º 3
0
        /// <summary>
        /// Reads a tag's data from the file.
        /// </summary>
        /// <param name="stream">The stream to read from.</param>
        /// <param name="tag">The tag to read.</param>
        /// <returns>The data that was read.</returns>
        public CachedTagData ExtractTag(Stream stream, CachedTagInstance tag)
        {
            if (tag == null)
            {
                throw new ArgumentNullException(nameof(tag));
            }
            else if (tag.HeaderOffset < 0)
            {
                throw new ArgumentException("The tag is not in the cache file");
            }

            // Build the description info and get the data offset
            var data = BuildTagData(stream, tag, out uint dataOffset);

            // Read the tag data
            stream.Position = tag.HeaderOffset + dataOffset;
            data.Data       = new byte[tag.TotalSize - dataOffset];
            stream.Read(data.Data, 0, data.Data.Length);

            // Correct pointers
            using (var dataWriter = new BinaryWriter(new MemoryStream(data.Data)))
            {
                foreach (var fixup in data.PointerFixups)
                {
                    dataWriter.BaseStream.Position = fixup.WriteOffset;
                    dataWriter.Write(tag.OffsetToPointer(fixup.TargetOffset));
                }
            }
            return(data);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Allocates a new tag at the end of the tag list without updating the file.
        /// You can give the tag data by using one of the overwrite functions.
        /// </summary>
        /// <param name="type">The tag's type information.</param>
        /// <param name="name">The name of the tag instance.</param>
        /// <returns>The allocated tag.</returns>
        public CachedTagInstance AllocateTag(TagGroup type, string name = null)
        {
            var tagIndex = _tags.Count;
            var tag      = new CachedTagInstance(tagIndex, type, name);

            _tags.Add(tag);
            return(tag);
        }
Exemplo n.º 5
0
 public void Serialize(Stream stream, CachedTagInstance instance, object definition)
 {
     if (!ModifiedTags.Contains(instance.Index))
     {
         SignalModifiedTag(instance.Index);
     }
     Serializer.Serialize(new TagSerializationContext(stream, this, instance), definition);
 }
Exemplo n.º 6
0
        /// <summary>
        /// Attempts to get a tag from the current cache.
        /// </summary>
        /// <param name="index">The index of the tag.</param>
        /// <param name="instance">The tag at the specified index from the current cache.</param>
        /// <returns>true if the index is within the range of the tag cache, false otherwise.</returns>
        public bool TryGetTag(int index, out CachedTagInstance instance)
        {
            if (index < 0 || index >= TagCache.Index.Count)
            {
                instance = null;
                return(false);
            }

            instance = TagCache.Index[index];
            return(true);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Duplicates a tag.
        /// </summary>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="tag">The tag to duplicate.</param>
        /// <returns>The new tag.</returns>
        public CachedTagInstance DuplicateTag(Stream stream, CachedTagInstance tag)
        {
            if (tag == null)
            {
                throw new ArgumentNullException(nameof(tag));
            }

            // Just extract the tag and add it back
            var result = AllocateTag(tag.Group);

            SetTagDataRaw(stream, result, ExtractTagRaw(stream, tag));
            return(result);
        }
Exemplo n.º 8
0
        /// <summary>
        /// Builds a description for a tag's data without extracting anything.
        /// </summary>
        /// <param name="stream">The stream to read from.</param>
        /// <param name="tag">The tag to read.</param>
        /// <param name="dataOffset">On return, this will contain the offset of the tag's data relative to its header.</param>
        /// <returns>The description that was built. </returns>
        private static CachedTagData BuildTagData(Stream stream, CachedTagInstance tag, out uint dataOffset)
        {
            var data = new CachedTagData
            {
                Group            = tag.Group,
                MainStructOffset = tag.DefinitionOffset,
            };

            foreach (var dependency in tag.Dependencies)
            {
                data.Dependencies.Add(dependency);
            }

            // Read pointer fixups
            var reader = new BinaryReader(stream);

            foreach (var pointerOffset in tag.PointerOffsets)
            {
                reader.BaseStream.Position = tag.HeaderOffset + pointerOffset;
                data.PointerFixups.Add(new CachedTagData.PointerFixup
                {
                    WriteOffset  = pointerOffset,
                    TargetOffset = tag.PointerToOffset(reader.ReadUInt32()),
                });
            }

            // Find the start of the tag's data by finding the offset of the first block which is pointed to by something
            // We CAN'T just calculate a header size here because we don't know for sure if there's padding and how big it is
            var startOffset = tag.DefinitionOffset;

            foreach (var fixup in data.PointerFixups)
            {
                startOffset = Math.Min(startOffset, Math.Min(fixup.WriteOffset, fixup.TargetOffset));
            }

            // Now convert all offsets into relative ones
            foreach (var fixup in data.PointerFixups)
            {
                fixup.WriteOffset  -= startOffset;
                fixup.TargetOffset -= startOffset;
            }

            data.ResourcePointerOffsets.AddRange(tag.ResourcePointerOffsets.Select(offset => offset - startOffset));

            data.TagReferenceOffsets.AddRange(tag.TagReferenceOffsets.Select(offset => offset - startOffset));

            data.MainStructOffset -= startOffset;
            dataOffset             = startOffset;

            return(data);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Overwrites a tag's data.
        /// </summary>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="tag">The tag to overwrite.</param>
        /// <param name="data">The data to store.</param>
        public void SetTagData(Stream stream, CachedTagInstance tag, CachedTagData data)
        {
            if (tag == null)
            {
                throw new ArgumentNullException(nameof(tag));
            }
            else if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }
            else if (data.Group == TagGroup.None)
            {
                throw new ArgumentException("Cannot assign a tag to a null tag group");
            }
            else if (data.Data == null)
            {
                throw new ArgumentException("The tag data buffer is null");
            }

            // Ensure the data fits
            var headerSize        = CachedTagInstance.CalculateHeaderSize(data);
            var alignedHeaderSize = (uint)((headerSize + 0xF) & ~0xF);

            if (tag.HeaderOffset < 0)
            {
                tag.HeaderOffset = GetNewTagOffset(tag.Index);
            }
            var alignedLength = (data.Data.Length + 0xF) & ~0xF;

            ResizeBlock(stream, tag, tag.HeaderOffset, tag.TotalSize, alignedHeaderSize + alignedLength);
            tag.TotalSize = alignedHeaderSize + alignedLength;
            tag.Update(data, alignedHeaderSize);

            // Write in the new header and data
            stream.Position = tag.HeaderOffset;
            var writer = new BinaryWriter(stream);

            tag.WriteHeader(writer);
            StreamUtil.Fill(stream, 0, (int)(alignedHeaderSize - headerSize));
            stream.Write(data.Data, 0, data.Data.Length);
            StreamUtil.Fill(stream, 0, alignedLength - data.Data.Length);

            // Correct pointers
            foreach (var fixup in data.PointerFixups)
            {
                writer.BaseStream.Position = tag.HeaderOffset + alignedHeaderSize + fixup.WriteOffset;
                writer.Write(tag.OffsetToPointer(alignedHeaderSize + fixup.TargetOffset));
            }

            UpdateTagOffsets(writer);
        }
Exemplo n.º 10
0
        /// <summary>
        /// Reads the tags.dat file.
        /// </summary>
        /// <param name="reader">The stream to read from.</param>
        /// <param name="names">The dictionary of tag instance names.</param>
        private void Load(BinaryReader reader, Dictionary <int, string> names)
        {
            // Read file header
            reader.BaseStream.Position = 0x4;
            var tagListOffset = reader.ReadInt32(); // 0x4 uint32 offset table offset
            var tagCount      = reader.ReadInt32(); // 0x8 uint32 number of tags

            reader.BaseStream.Position = 0x10;
            Timestamp = reader.ReadInt64();         // 0x10 FILETIME timestamp

            // Read tag offset list
            var headerOffsets = new uint[tagCount];

            reader.BaseStream.Position = tagListOffset;
            for (var i = 0; i < tagCount; i++)
            {
                headerOffsets[i] = reader.ReadUInt32();
            }

            // Read each tag
            for (var i = 0; i < tagCount; i++)
            {
                if (headerOffsets[i] == 0)
                {
                    // Offset of 0 = null tag
                    _tags.Add(null);
                    continue;
                }

                string name = null;

                if (names.ContainsKey(i))
                {
                    name = names[i];
                }

                var tag = new CachedTagInstance(i, name)
                {
                    HeaderOffset = headerOffsets[i]
                };
                _tags.Add(tag);

                reader.BaseStream.Position = tag.HeaderOffset;
                tag.ReadHeader(reader);
            }
        }
Exemplo n.º 11
0
        /// <summary>
        /// Reads a tag's raw data from the file, including its header.
        /// </summary>
        /// <param name="stream">The stream to read from.</param>
        /// <param name="tag">The tag to read.</param>
        /// <returns>The data that was read.</returns>
        public byte[] ExtractTagRaw(Stream stream, CachedTagInstance tag)
        {
            if (tag == null)
            {
                throw new ArgumentNullException(nameof(tag));
            }
            else if (tag.HeaderOffset < 0)
            {
                throw new ArgumentException("The tag is not in the cache file");
            }

            var result = new byte[tag.TotalSize];

            stream.Position = tag.HeaderOffset;
            stream.Read(result, 0, result.Length);

            return(result);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Resizes a block of data in the file.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="tag">The tag that the block belongs to, if any.</param>
        /// <param name="startOffset">The offset where the block to resize begins at.</param>
        /// <param name="oldSize">The current size of the block to resize.</param>
        /// <param name="newSize">The new size of the block.</param>
        /// <exception cref="System.ArgumentException">Cannot resize a block to a negative size</exception>
        private void ResizeBlock(Stream stream, CachedTagInstance tag, long startOffset, long oldSize, long newSize)
        {
            if (newSize < 0)
            {
                throw new ArgumentException("Cannot resize a block to a negative size");
            }
            else if (oldSize == newSize)
            {
                return;
            }

            var oldEndOffset = startOffset + oldSize;
            var sizeDelta    = newSize - oldSize;

            StreamUtil.Copy(stream, oldEndOffset, oldEndOffset + sizeDelta, stream.Length - oldEndOffset);

            FixTagOffsets(oldEndOffset, sizeDelta, tag);
        }
Exemplo n.º 13
0
        public bool TryAllocateTag(out CachedTagInstance result, Type type, string name = null)
        {
            result = null;

            try
            {
                var structure = TagStructure.GetTagStructureInfo(type, Version).Structure;

                if (structure == null)
                {
                    Console.WriteLine($"TagStructure attribute not found for type \"{type.Name}\".");
                    return(false);
                }

                var groupTag = new Tag(structure.Tag);

                if (!TagGroup.Instances.ContainsKey(groupTag))
                {
                    Console.WriteLine($"TagGroup not found for type \"{type.Name}\" ({structure.Tag}).");
                    return(false);
                }

                result = TagCache.AllocateTag(TagGroup.Instances[groupTag], name);

                if (result == null)
                {
                    return(false);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"{e.GetType().Name}: {e.Message}");
                return(false);
            }

            return(true);
        }
Exemplo n.º 14
0
        /// <summary>
        /// Overwrites a tag's raw data, including its header.
        /// </summary>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="tag">The tag to overwrite.</param>
        /// <param name="data">The data to overwrite the tag with.</param>
        /// <exception cref="System.ArgumentNullException">tag</exception>
        public void SetTagDataRaw(Stream stream, CachedTagInstance tag, byte[] data)
        {
            if (tag == null)
            {
                throw new ArgumentNullException(nameof(tag));
            }

            // Ensure the data fits
            if (tag.HeaderOffset < 0)
            {
                tag.HeaderOffset = GetNewTagOffset(tag.Index);
            }
            ResizeBlock(stream, tag, tag.HeaderOffset, tag.TotalSize, data.Length);
            tag.TotalSize = data.Length;

            // Write the data
            stream.Position = tag.HeaderOffset;
            stream.Write(data, 0, data.Length);

            // Re-parse it
            stream.Position = tag.HeaderOffset;
            tag.ReadHeader(new BinaryReader(stream));
            UpdateTagOffsets(new BinaryWriter(stream));
        }
Exemplo n.º 15
0
        /// <summary>
        /// Attempts to get a tag by parsing its group name.
        /// </summary>
        /// <param name="name">The full name of the tag.</param>
        /// <param name="result">The resulting tag.</param>
        /// <returns>True if the tag was found, false otherwise.</returns>
        public bool TryGetTag(string name, out CachedTagInstance result)
        {
            if (name.Length == 0)
            {
                result = null;
                return(false);
            }

            if (name == "null")
            {
                result = null;
                return(true);
            }

            if (name == "*")
            {
                if (TagCache.Index.Count == 0)
                {
                    result = null;
                    return(false);
                }

                result = TagCache.Index.Last();
                return(true);
            }

            if (name.StartsWith("*."))
            {
                if (!name.TrySplit('.', out var startNamePieces) || !TryParseGroupTag(startNamePieces[1], out var starGroupTag))
                {
                    result = null;
                    return(false);
                }

                result = TagCache.Index.Last(tag => tag.IsInGroup(starGroupTag));
                return(true);
            }

            if (name.StartsWith("0x"))
            {
                name = name.Substring(2);

                if (name.TrySplit('.', out var hexNamePieces))
                {
                    name = hexNamePieces[0];
                }

                if (!int.TryParse(name, NumberStyles.HexNumber, null, out int tagIndex) || !TagCache.Index.Contains(tagIndex))
                {
                    result = null;
                    return(false);
                }

                result = TagCache.Index[tagIndex];
                return(true);
            }

            if (!name.TrySplit('.', out var namePieces) || !TryParseGroupTag(namePieces[1], out var groupTag))
            {
                throw new Exception($"Invalid tag name: {name}");
            }

            var tagName = namePieces[0];

            foreach (var instance in TagCache.Index)
            {
                if (instance is null)
                {
                    continue;
                }

                if (instance.IsInGroup(groupTag) && instance.Name == tagName)
                {
                    result = instance;
                    return(true);
                }
            }

            result = null;
            return(false);
        }
Exemplo n.º 16
0
 public T Deserialize <T>(Stream stream, CachedTagInstance instance) =>
 Deserialize <T>(new TagSerializationContext(stream, this, instance));
Exemplo n.º 17
0
 public object Deserialize(Stream stream, CachedTagInstance instance) =>
 Deserialize(new TagSerializationContext(stream, this, instance), Tags.TagDefinition.Find(instance.Group.Tag));
Exemplo n.º 18
0
 public void Serialize(Stream stream, CachedTagInstance instance, object definition) =>
 Serializer.Serialize(new TagSerializationContext(stream, this, instance), definition);
Exemplo n.º 19
0
 public void NullTag(Stream stream, CachedTagInstance tag)
 {
     Index[tag.Index] = null;
     SetTagDataRaw(stream, tag, new byte[] { });
 }