Example #1
0
        public void Load(FileInfo file)
        {
            if (!file.Exists)
            {
                throw new FileNotFoundException(file.FullName);
            }

            if (file.Length < typeof(ModPackageHeader).GetSize())
            {
                throw new FormatException(file.FullName);
            }

            using (var stream = file.OpenRead())
                using (var reader = new EndianReader(stream, leaveOpen: true))
                {
                    var dataContext  = new DataSerializationContext(reader);
                    var deserializer = new TagDeserializer(CacheVersion.HaloOnline106708);

                    Header = deserializer.Deserialize <ModPackageHeader>(dataContext);

                    ReadMetadataSection(reader, dataContext, deserializer);
                    ReadTagsSection(reader, dataContext, deserializer);
                    ReadTagNamesSection(reader, dataContext, deserializer);
                    ReadResourcesSection(reader);
                    ReadMapFilesSection(reader);
                    ReadCampaignFileSection(reader);
                    ReadStringIdSection(reader);
                    ReadFontSection(reader);
                    ReadFileEntries(reader, dataContext, deserializer);

                    int tagCacheCount = TagCachesStreams.Count;

                    TagCaches = new List <TagCacheHaloOnline>();
                    for (int i = 0; i < tagCacheCount; i++)
                    {
                        TagCaches.Add(new TagCacheHaloOnline(TagCachesStreams[i], TagCacheNames[i]));
                    }

                    Resources = new ResourceCacheHaloOnline(CacheVersion.HaloOnline106708, ResourcesStream);
                }
        }
Example #2
0
        public ModPackage(FileInfo file = null)
        {
            if (file != null)
            {
                Load(file);
            }
            else
            {
                // init a single cache
                var tagStream = new MemoryStream();
                TagCachesStreams.Add(tagStream);

                var names = new Dictionary <int, string>();
                var tags  = new TagCacheHaloOnline(tagStream, names);
                TagCaches = new List <TagCacheHaloOnline>();
                TagCaches.Add(tags);
                TagCacheNames.Add(names);
                Files = new Dictionary <string, Stream>();

                Resources           = new ResourceCacheHaloOnline(CacheVersion.HaloOnline106708, ResourcesStream);
                Header.SectionTable = new ModPackageSectionTable();
            }
        }
        private void AddTags(HashSet <int> tagIndices)
        {
            // define current cache tags, names
            var modTagCache  = ModPackage.TagCaches[0];
            var modTagNames  = ModPackage.TagCacheNames[0];
            var modTagStream = ModPackage.TagCachesStreams[0];

            using (var srcTagStream = CacheContext.OpenCacheRead())
            {
                var resourceIndices = new Dictionary <ResourceLocation, Dictionary <int, PageableResource> >
                {
                    [ResourceLocation.Audio]        = new Dictionary <int, PageableResource>(),
                    [ResourceLocation.Lightmaps]    = new Dictionary <int, PageableResource>(),
                    [ResourceLocation.Mods]         = new Dictionary <int, PageableResource>(),
                    [ResourceLocation.RenderModels] = new Dictionary <int, PageableResource>(),
                    [ResourceLocation.Resources]    = new Dictionary <int, PageableResource>(),
                    [ResourceLocation.ResourcesB]   = new Dictionary <int, PageableResource>(),
                    [ResourceLocation.Textures]     = new Dictionary <int, PageableResource>(),
                    [ResourceLocation.TexturesB]    = new Dictionary <int, PageableResource>()
                };

                var srcResourceCaches  = new Dictionary <ResourceLocation, ResourceCacheHaloOnline>();
                var srcResourceStreams = new Dictionary <ResourceLocation, Stream>();

                foreach (var value in Enum.GetValues(typeof(ResourceLocation)))
                {
                    ResourceCacheHaloOnline resourceCache = null;
                    var location = (ResourceLocation)value;

                    if (location == ResourceLocation.None)
                    {
                        continue;
                    }

                    try
                    {
                        resourceCache = CacheContext.ResourceCaches.GetResourceCache(location);
                    }
                    catch (FileNotFoundException)
                    {
                        continue;
                    }

                    srcResourceCaches[location]  = resourceCache;
                    srcResourceStreams[location] = CacheContext.ResourceCaches.OpenCacheRead(location);
                }

                for (var tagIndex = 0; tagIndex < CacheContext.TagCache.Count; tagIndex++)
                {
                    var srcTag = CacheContext.TagCache.GetTag(tagIndex);

                    if (srcTag == null)
                    {
                        modTagCache.AllocateTag(new TagTool.Tags.TagGroup());
                        continue;
                    }

                    if (!tagIndices.Contains(tagIndex))
                    {
                        var emptyTag      = modTagCache.AllocateTag(srcTag.Group, srcTag.Name);
                        var cachedTagData = new CachedTagData();
                        cachedTagData.Data  = new byte[0];
                        cachedTagData.Group = emptyTag.Group;
                        modTagCache.SetTagData(modTagStream, (CachedTagHaloOnline)emptyTag, cachedTagData);
                        continue;
                    }

                    var destTag = modTagCache.AllocateTag(srcTag.Group, srcTag.Name);

                    using (var tagDataStream = new MemoryStream(CacheContext.TagCacheGenHO.ExtractTagRaw(srcTagStream, (CachedTagHaloOnline)srcTag)))
                        using (var tagDataReader = new EndianReader(tagDataStream, leaveOpen: true))
                            using (var tagDataWriter = new EndianWriter(tagDataStream, leaveOpen: true))
                            {
                                var resourcePointerOffsets = new HashSet <uint>();

                                foreach (var resourcePointerOffset in ((CachedTagHaloOnline)srcTag).ResourcePointerOffsets)
                                {
                                    if (resourcePointerOffset == 0 || resourcePointerOffsets.Contains(resourcePointerOffset))
                                    {
                                        continue;
                                    }

                                    resourcePointerOffsets.Add(resourcePointerOffset);

                                    tagDataStream.Position = resourcePointerOffset;
                                    var resourcePointer = tagDataReader.ReadUInt32();

                                    if (resourcePointer == 0)
                                    {
                                        continue;
                                    }

                                    var resourceOffset = ((CachedTagHaloOnline)srcTag).PointerToOffset(resourcePointer);

                                    tagDataReader.BaseStream.Position = resourceOffset + 2;
                                    var locationFlags = (OldRawPageFlags)tagDataReader.ReadByte();

                                    var resourceLocation =
                                        locationFlags.HasFlag(OldRawPageFlags.InAudio) ?
                                        ResourceLocation.Audio :
                                        locationFlags.HasFlag(OldRawPageFlags.InResources) ?
                                        ResourceLocation.Resources :
                                        locationFlags.HasFlag(OldRawPageFlags.InResourcesB) ?
                                        ResourceLocation.ResourcesB :
                                        locationFlags.HasFlag(OldRawPageFlags.InTextures) ?
                                        ResourceLocation.Textures :
                                        locationFlags.HasFlag(OldRawPageFlags.InTexturesB) ?
                                        ResourceLocation.TexturesB :
                                        ResourceLocation.ResourcesB;

                                    tagDataReader.BaseStream.Position = resourceOffset + 4;
                                    var resourceIndex  = tagDataReader.ReadInt32();
                                    var compressedSize = tagDataReader.ReadUInt32();

                                    if (resourceIndex == -1)
                                    {
                                        continue;
                                    }

                                    PageableResource pageable = null;

                                    if (resourceIndices[resourceLocation].ContainsKey(resourceIndex))
                                    {
                                        pageable = resourceIndices[resourceLocation][resourceIndex];
                                    }
                                    else
                                    {
                                        var newResourceIndex = ModPackage.Resources.AddRaw(
                                            ModPackage.ResourcesStream,
                                            srcResourceCaches[resourceLocation].ExtractRaw(
                                                srcResourceStreams[resourceLocation],
                                                resourceIndex,
                                                compressedSize));

                                        pageable = resourceIndices[resourceLocation][resourceIndex] = new PageableResource
                                        {
                                            Page = new ResourcePage
                                            {
                                                OldFlags = OldRawPageFlags.InMods,
                                                Index    = newResourceIndex
                                            }
                                        };
                                    }

                                    tagDataReader.BaseStream.Position = resourceOffset + 2;
                                    tagDataWriter.Write((byte)pageable.Page.OldFlags);

                                    tagDataReader.BaseStream.Position = resourceOffset + 4;
                                    tagDataWriter.Write(pageable.Page.Index);
                                }

                                modTagCache.SetTagDataRaw(modTagStream, (CachedTagHaloOnline)destTag, tagDataStream.ToArray());
                            }
                }

                foreach (var stream in srcResourceStreams.Values)
                {
                    if (stream != null)
                    {
                        stream.Dispose();
                    }
                }

                modTagCache.UpdateTagOffsets(new EndianWriter(modTagStream));
            }
        }
Example #4
0
        public override object Execute(List <string> args)
        {
            if (args.Count < 2)
            {
                return(false);
            }

            var fieldName      = args[0];
            var fieldNameLow   = fieldName.ToLower();
            var fieldNameSnake = fieldName.ToSnakeCase();

            var previousContext   = ContextStack.Context;
            var previousOwner     = Owner;
            var previousStructure = Structure;

            if (fieldName.Contains("."))
            {
                var lastIndex = fieldName.LastIndexOf('.');
                var blockName = fieldName.Substring(0, lastIndex);
                fieldName      = fieldName.Substring(lastIndex + 1, (fieldName.Length - lastIndex) - 1);
                fieldNameLow   = fieldName.ToLower();
                fieldNameSnake = fieldName.ToSnakeCase();

                var command = new EditBlockCommand(ContextStack, Cache, Tag, Owner);

                if (command.Execute(new List <string> {
                    blockName
                }).Equals(false))
                {
                    while (ContextStack.Context != previousContext)
                    {
                        ContextStack.Pop();
                    }
                    Owner     = previousOwner;
                    Structure = previousStructure;
                    return(false);
                }

                command = (ContextStack.Context.GetCommand("EditBlock") as EditBlockCommand);

                Owner     = command.Owner;
                Structure = command.Structure;

                if (Owner == null)
                {
                    while (ContextStack.Context != previousContext)
                    {
                        ContextStack.Pop();
                    }
                    Owner     = previousOwner;
                    Structure = previousStructure;
                    return(false);
                }
            }

            var field = TagStructure.GetTagFieldEnumerable(Structure)
                        .Find(f =>
                              f.Name == fieldName ||
                              f.Name.ToLower() == fieldNameLow ||
                              f.Name.ToSnakeCase() == fieldNameSnake);

            if (field == null)
            {
                Console.WriteLine("ERROR: {0} does not contain a field named \"{1}\".", Structure.Types[0].Name, fieldName);
                while (ContextStack.Context != previousContext)
                {
                    ContextStack.Pop();
                }
                Owner     = previousOwner;
                Structure = previousStructure;
                return(false);
            }

            var fieldType  = field.FieldType;
            var fieldAttrs = field.GetCustomAttributes(typeof(TagFieldAttribute), false);
            var fieldAttr  = fieldAttrs?.Length < 1 ? new TagFieldAttribute() : (TagFieldAttribute)fieldAttrs[0];
            var fieldInfo  = new TagFieldInfo(field, fieldAttr, uint.MaxValue, uint.MaxValue);
            var fieldValue = ParseArgs(Cache, field.FieldType, fieldInfo, args.Skip(1).ToList());

            if (fieldValue != null && fieldValue.Equals(false))
            {
                while (ContextStack.Context != previousContext)
                {
                    ContextStack.Pop();
                }
                Owner     = previousOwner;
                Structure = previousStructure;
                return(false);
            }

            if (Cache is GameCacheHaloOnlineBase && field.FieldType == typeof(PageableResource))
            {
                var haloOnlineGameCache = (GameCacheHaloOnlineBase)Cache;

                var ownerValue = field.GetValue(Owner);

                if (fieldValue == null)
                {
                    field.SetValue(Owner, null);
                }
                else if (ownerValue is PageableResource pageable)
                {
                    var newLocation = ResourceLocation.None;

                    FileInfo resourceFile = null;

                    switch (fieldValue)
                    {
                    case FileInfo file:
                        if (!pageable.GetLocation(out newLocation))
                        {
                            newLocation = ResourceLocation.ResourcesB;
                        }
                        resourceFile = file;
                        break;

                    case ValueTuple <ResourceLocation, FileInfo> tuple:
                        newLocation  = tuple.Item1;
                        resourceFile = tuple.Item2;
                        break;

                    default:
                        throw new FormatException(fieldValue.ToString());
                    }

                    ResourceCacheHaloOnline oldCache = null;

                    if (pageable.GetLocation(out var oldLocation))
                    {
                        oldCache = haloOnlineGameCache.ResourceCaches.GetResourceCache(oldLocation);
                    }

                    var newCache = haloOnlineGameCache.ResourceCaches.GetResourceCache(newLocation);

                    var data = File.ReadAllBytes(resourceFile.FullName);

                    pageable.Page.UncompressedBlockSize = (uint)data.Length;

                    if (oldLocation == newLocation && pageable.Page.Index != -1)
                    {
                        using (var stream = haloOnlineGameCache.ResourceCaches.OpenCacheReadWrite(oldLocation))
                        {
                            pageable.Page.CompressedBlockSize = oldCache.Compress(stream, pageable.Page.Index, data);
                        }
                    }
                    else
                    {
                        using (var destStream = haloOnlineGameCache.ResourceCaches.OpenCacheReadWrite(newLocation))
                        {
                            pageable.Page.Index = newCache.Add(destStream, data, out pageable.Page.CompressedBlockSize);
                        }

                        pageable.ChangeLocation(newLocation);
                    }

                    pageable.DisableChecksum();

                    field.SetValue(Owner, fieldValue = pageable);
                }
            }
            else
            {
                field.SetValue(Owner, fieldValue);
            }

            var typeString =
                fieldType.IsGenericType ?
                $"{fieldType.Name}<{fieldType.GenericTypeArguments[0].Name}>" :
                fieldType.Name;

            string valueString;

#if !DEBUG
            try
            {
#endif
            if (fieldValue == null)
            {
                valueString = "null";
            }
            else if (fieldType == typeof(StringId))
            {
                valueString = Cache.StringTable.GetString((StringId)fieldValue);
            }
            else if (fieldType == typeof(CachedTag))
            {
                var instance = (CachedTag)fieldValue;

                var tagName = instance?.Name ?? $"0x{instance.Index:X4}";

                valueString = $"[0x{instance.Index:X4}] {tagName}.{Cache.StringTable.GetString(instance.Group.Name)}";
            }
            else if (fieldType == typeof(TagFunction))
            {
                var function = (TagFunction)fieldValue;
                valueString = "";
                foreach (var datum in function.Data)
                {
                    valueString += datum.ToString("X2");
                }
            }
            else if (fieldType == typeof(PageableResource))
            {
                var pageable = (PageableResource)fieldValue;
                pageable.GetLocation(out var location);
                valueString = pageable == null ? "null" : $"{{ Location: {location}, Index: 0x{pageable.Page.Index:X4}, CompressedSize: 0x{pageable.Page.CompressedBlockSize:X8} }}";
            }
            else if (fieldInfo.FieldType.IsArray && fieldInfo.Attribute.Length != 0)
            {
                valueString = fieldValue == null ? "null" : $"[{fieldInfo.Attribute.Length}] {{ ";
                var valueArray = (Array)fieldValue;

                if (fieldValue != null)
                {
                    for (var i = 0; i < fieldInfo.Attribute.Length; i++)
                    {
                        valueString += $"{valueArray.GetValue(i)}{((i + 1) < fieldInfo.Attribute.Length ? "," : "")} ";
                    }

                    valueString += "}";
                }
            }
            else if (fieldType.GetInterface(typeof(IList).Name) != null)
            {
                valueString =
                    ((IList)fieldValue).Count != 0 ?
                    $"{{...}}[{((IList)fieldValue).Count}]" :
                    "null";
            }
            else
            {
                valueString = fieldValue.ToString();
            }
#if !DEBUG
        }

        catch (Exception e)
        {
            valueString = $"<ERROR MESSAGE=\"{e.Message}\" />";
        }
#endif

            var fieldFullName     = $"{field.DeclaringType.FullName}.{field.Name}".Replace("+", ".");
            var documentationNode = EditTagContextFactory.Documentation.SelectSingleNode($"//member[starts-with(@name, 'F:{fieldFullName}')]");

            Console.WriteLine("{0}: {1} = {2} {3}", field.Name, typeString, valueString,
                              documentationNode != null ?
                              $":: {documentationNode.FirstChild.InnerText.Replace("\r\n", "").TrimStart().TrimEnd()}" :
                              "");

            while (ContextStack.Context != previousContext)
            {
                ContextStack.Pop();
            }
            Owner     = previousOwner;
            Structure = previousStructure;

            return(true);
        }