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