Esempio n. 1
0
        /// <summary>
        /// Compresses the provided stream into the other stream, if the map was previously decompressed.
        /// If the provided stream was not previously decompressed, it will simply copy and return false
        /// </summary>
        /// <returns>A boolean indicating if compression was performed</returns>
        public static bool Compress(Stream decompressed, Stream compressTo)
        {
            if (decompressed.CanSeek == false)
            {
                throw new NotSupportedException("Must be able to seek on decompressed");
            }
            if (compressTo.CanSeek == false)
            {
                throw new NotSupportedException("Must be able to seek on compressTo");
            }

            var fourCC = decompressed.ReadUInt32At(0);

            if (fourCC == RealFourCC)
            {
                decompressed.Position = 0;
                decompressed.CopyTo(compressTo);
                return(false);
            }

            if (fourCC != DecompressedFourCC)
            {
                throw new Exception("Provided decompressed map stream was not valid for compression");
            }

            decompressed.Position = 0;
            compressTo.Position   = 0;

            var header = new Span <byte>(new byte[BlamSerializer.SizeOf <H2mccMapHeader>()]);

            // Copy header
            decompressed.Read(header);
            compressTo.WriteUInt32(RealFourCC);
            compressTo.Write(header.Slice(4));

            // Write empty compression info until we're done
            var compressionSections = new Span <byte>(new byte[8192]);

            compressTo.Write(compressionSections);

            var sections = new List <H2mccCompressionSections.CompressionSection>();

            var chunk = new Span <byte>(new byte[CompressionChunkSize]);

            // Compress chunks, write to compressTo
            while (decompressed.Position < decompressed.Length - 1)
            {
                var bytesToTake = Math.Min(CompressionChunkSize, decompressed.Length - decompressed.Position);
                var readBytes   = decompressed.Read(chunk);

                Debug.Assert(readBytes == bytesToTake);

                using var compressed = new MemoryStream();
                using (var compressor = new DeflateStream(compressed, CompressionLevel.Optimal, true))
                {
                    compressor.Write(chunk.ToArray(), 0, readBytes);
                }

                compressed.Seek(0, SeekOrigin.Begin);

                var section = new H2mccCompressionSections.CompressionSection((int)compressed.Length + 2, (uint)compressTo.Position);
                sections.Add(section);

                // Write magic bytes
                compressTo.Write(BitConverter.GetBytes((ushort)5416));

                compressed.CopyTo(compressTo);
            }

            // Go back and write compression section info
            compressTo.Seek(BlamSerializer.SizeOf <H2mccMapHeader>(), SeekOrigin.Begin);
            foreach (var section in sections)
            {
                compressTo.WriteInt32(section.Count);
                compressTo.WriteUInt32(section.Offset);
            }

            return(true);
        }
Esempio n. 2
0
        private ResolvedTagPropertyInfo ResolvePropertyInfo(TagIndexEntry tagInfo, string propertyPath)
        {
            var topTagType = TagFactory.GetTypeForTag(tagInfo.Tag);

            var steps = PropertyAccessorParser.ExtractProperties(propertyPath);

            var offset = 0;

            var stepType = topTagType;

            foreach (var step in steps)
            {
                var prop = stepType.GetProperty(step.PropertyName);

                if (prop == null)
                {
                    throw new Exception($"Couldn't find property '{step.PropertyName}' on type '{stepType}'");
                }

                if (step.AccessType == PropertyAccessorParser.PropertyAccessType.Normal)
                {
                    offset  += BlamSerializer.StartsAt(stepType, step.PropertyName);
                    stepType = prop.PropertyType;
                }
                else if (step.AccessType == PropertyAccessorParser.PropertyAccessType.ElementAccess)
                {
                    if (prop.PropertyType.IsArray == false || step.ElementArgument is not int)
                    {
                        throw new NotSupportedException("Only arrays are currently supported for element access");
                    }

                    var elementSize   = BlamSerializer.SizeOf(prop.PropertyType.GetElementType());
                    var elementOffset = (elementSize * ((int)step.ElementArgument));

                    if (prop.GetCustomAttribute <ReferenceArrayAttribute>() != null)
                    {
                        var startsAt = BlamSerializer.StartsAt(stepType, step.PropertyName);

                        // Read element array base offset
                        var baseOffset = new SecondaryOffset(this.originalMap, this.mapToPatch.ReadInt32At(tagInfo.Offset.Value + offset + startsAt + 4));

                        // baseOffset is the absolute offset, need to subtract tag offset and prior property offsets to get relative
                        offset += baseOffset.Value - tagInfo.Offset.Value - offset + elementOffset;
                    }
                    else if (prop.GetCustomAttribute <PrimitiveArrayAttribute>() != null)
                    {
                        offset += elementOffset;
                    }
                    else
                    {
                        throw new Exception("Only primitive and reference arrays are supported");
                    }

                    stepType = prop.PropertyType.GetElementType();
                }
            }

            return(new ResolvedTagPropertyInfo()
            {
                RelativeOffset = offset,
                PropertyType = stepType
            });
        }
Esempio n. 3
0
        public static void PatchMap(H2mccMap scene, Stream map, string patchFilePath)
        {
            var scenarioStart = scene.TagIndex[scene.IndexHeader.Scenario].Offset.Value;
            var nodesStart    = BlamSerializer.StartsAt <ScenarioTag>(s => s.ScriptSyntaxNodes);
            var nodeCount     = map.ReadUInt32At(scenarioStart + nodesStart);
            var nodeOffset    = (int)map.ReadUInt32At(scenarioStart + nodesStart + 4) - scene.SecondaryMagic;

            var nodeSize = BlamSerializer.SizeOf <ScenarioTag.ScriptSyntaxNode>();

            var patchLines = File.ReadAllLines(patchFilePath);

            foreach (var line in patchLines)
            {
                if (string.IsNullOrWhiteSpace(line))
                {
                    continue;
                }

                if (ShouldPatchFrom(scene, line, out var patch))
                {
                    Console.WriteLine($"\t Patching {scene.Header.Name} [{patch.Index}]");
                    var patchStart = nodeOffset + patch.Index * nodeSize;

                    // Fixup next node's check value. We never change check values, so we can
                    // re-use the 'old' nodes here to get that info
                    if (patch.NodeData.NextIndex == ushort.MaxValue)
                    {
                        patch.NodeData.NextCheckval = ushort.MaxValue;
                    }
                    else
                    {
                        var nextNode = scene.Scenario.ScriptSyntaxNodes[patch.NodeData.NextIndex];
                        patch.NodeData.NextCheckval = nextNode.Checkval;
                    }

                    // Fixup next node's check value for scope/invocation nodes
                    if ((patch.NodeData.NodeType == NodeType.BuiltinInvocation || patch.NodeData.NodeType == NodeType.ScriptInvocation))
                    {
                        if (patch.NodeData.NodeData_H16 == ushort.MaxValue)
                        {
                            patch.NodeData.NodeData_32 = patch.NodeData.NodeData_H16 | ((uint)ushort.MaxValue) << 16;
                        }
                        else
                        {
                            var nextNode = scene.Scenario.ScriptSyntaxNodes[patch.NodeData.NodeData_H16];
                            patch.NodeData.NodeData_32 = patch.NodeData.NodeData_H16 | ((uint)nextNode.Checkval) << 16;
                        }
                    }

                    //map.WriteUInt16At(patchStart + 0, patch.NodeData.Checkval);
                    map.WriteUInt16At(patchStart + 2, patch.NodeData.OperationId);
                    map.WriteUInt16At(patchStart + 4, (ushort)patch.NodeData.DataType);
                    map.WriteUInt16At(patchStart + 6, (ushort)patch.NodeData.NodeType);
                    map.WriteUInt16At(patchStart + 8, patch.NodeData.NextIndex);
                    map.WriteUInt16At(patchStart + 10, patch.NodeData.NextCheckval);
                    map.WriteUInt16At(patchStart + 12, patch.NodeData.NodeString);
                    //map.WriteUInt16At(patchStart + 14, patch.NodeData.ValueH);
                    map.WriteUInt32At(patchStart + 16, patch.NodeData.NodeData_32);
                }
            }
        }