private LoadedResourceCache GetResourceCache(PageableResource resource) { if (!resource.GetLocation(out var location)) { return(null); } if (!LoadedResourceCaches.TryGetValue(location, out LoadedResourceCache cache)) { var file = new FileInfo(Path.Combine(Directory.FullName, ResourceCacheNames[location])); if (!file.Exists && file.Name == "resources_b.dat") { file = new FileInfo(Path.Combine(Directory.FullName, "video.dat")); } using (var stream = file.OpenRead()) { cache = new LoadedResourceCache { File = file, Cache = new ResourceCache(stream) }; } } return(cache); }
public object Deserialize(PageableResource pageable) { switch (pageable.Resource.ResourceType) { case TagResourceTypeGen3.Animation: return(Deserialize <ModelAnimationTagResource>(pageable)); case TagResourceTypeGen3.Bink: return(Deserialize <BinkResource>(pageable)); case TagResourceTypeGen3.Bitmap: case TagResourceTypeGen3.BitmapInterleaved: return(Deserialize <BitmapTextureInteropResource>(pageable)); case TagResourceTypeGen3.Collision: return(Deserialize <StructureBspTagResources>(pageable)); case TagResourceTypeGen3.Pathfinding: return(Deserialize <StructureBspCacheFileTagResources>(pageable)); case TagResourceTypeGen3.RenderGeometry: return(Deserialize <RenderGeometryApiResourceDefinition>(pageable)); case TagResourceTypeGen3.Sound: return(Deserialize <SoundResourceDefinition>(pageable)); default: throw new NotSupportedException(pageable.Resource.ResourceType.ToString()); } }
private ResourceCacheHaloOnline GetResourceCache(PageableResource resource, out ResourceLocation location) { if (!resource.GetLocation(out location)) { return(null); } return(GetResourceCache(location)); }
/// <summary> /// Extracts raw, compressed resource data. /// </summary> /// <param name="resource">The resource.</param> /// <returns>The raw, compressed resource data.</returns> public byte[] ExtractRawResource(PageableResource resource) { if (resource == null) { throw new ArgumentNullException("resource"); } var cache = GetResourceCache(resource); using (var stream = cache.File.OpenRead()) return(cache.Cache.ExtractRaw(stream, resource.Page.Index, resource.Page.CompressedBlockSize)); }
/// <summary> /// Adds raw, pre-compressed resource data to a cache. /// </summary> /// <param name="resource">The resource reference to initialize.</param> /// <param name="data">The pre-compressed data to store.</param> public void AddRawResource(PageableResource resource, byte[] data) { if (resource == null) { throw new ArgumentNullException("resource"); } resource.DisableChecksum(); var cache = GetResourceCache(resource, out var location); using (var stream = OpenCacheReadWrite(location)) resource.Page.Index = cache.AddRaw(stream, data); }
/// <summary> /// Replaces a resource with raw, pre-compressed data. /// </summary> /// <param name="resource">The resource whose data should be replaced. On success, the reference will be adjusted to account for the new data.</param> /// <param name="data">The raw, pre-compressed data to use.</param> public void ReplaceRawResource(PageableResource resource, byte[] data) { if (resource == null) { throw new ArgumentNullException("resource"); } resource.DisableChecksum(); var cache = GetResourceCache(resource); using (var stream = cache.File.Open(FileMode.Open, FileAccess.ReadWrite)) cache.Cache.ImportRaw(stream, resource.Page.Index, data); }
/// <summary> /// Extracts and decompresses the data for a resource from the current cache. /// </summary> /// <param name="inStream"></param> /// <param name="pageable">The resource.</param> /// <param name="outStream">The stream to write the extracted data to.</param> /// <exception cref="System.ArgumentException">Thrown if the output stream is not open for writing.</exception> /// <exception cref="System.InvalidOperationException">Thrown if the file containing the resource has not been loaded.</exception> public void ExtractResource(Stream inStream, PageableResource pageable, Stream outStream) { if (pageable == null) { throw new ArgumentNullException("resource"); } if (!outStream.CanWrite) { throw new ArgumentException("The output stream is not open for writing", "outStream"); } var cache = GetResourceCache(pageable); cache.Cache.Decompress(inStream, pageable.Page.Index, pageable.Page.CompressedBlockSize, outStream); }
/// <summary> /// Extracts and decompresses the data for a resource from the current cache. /// </summary> /// <param name="pageable">The resource.</param> /// <param name="outStream">The stream to write the extracted data to.</param> /// <exception cref="System.ArgumentException">Thrown if the output stream is not open for writing.</exception> /// <exception cref="System.InvalidOperationException">Thrown if the file containing the resource has not been loaded.</exception> public override void ExtractResource(PageableResource pageable, Stream outStream) { if (pageable == null) { throw new ArgumentNullException("resource"); } if (!outStream.CanWrite) { throw new ArgumentException("The output stream is not open for writing", "outStream"); } var cache = GetResourceCache(pageable); using (var stream = cache.File.OpenRead()) cache.Cache.Decompress(stream, pageable.Page.Index, pageable.Page.CompressedBlockSize, outStream); }
private PageableResource ConvertPageableResource(ModPackage modPack, PageableResource resource) { if (resource.Page.Index == -1) { return(resource); } var resourceStream = new MemoryStream(); modPack.Resources.Decompress(modPack.ResourcesStream, resource.Page.Index, resource.Page.CompressedBlockSize, resourceStream); resourceStream.Position = 0; resource.ChangeLocation(ResourceLocation.ResourcesB); resource.Page.OldFlags &= ~OldRawPageFlags.InMods; CacheContext.ResourceCaches.AddResource(resource, resourceStream); return(resource); }
private PageableResource ConvertResource(PageableResource resource, GameCacheContextHaloOnline srcCacheContext, GameCacheContextHaloOnline destCacheContext) { if (resource == null || resource.Page.Index < 0 || !resource.GetLocation(out var location)) { return(null); } Console.WriteLine("- Copying resource {0} in {1}...", resource.Page.Index, location); var data = srcCacheContext.ExtractRawResource(resource); var newLocation = FixResourceLocation(location, srcCacheContext.Version, destCacheContext.Version); resource.ChangeLocation(newLocation); destCacheContext.AddRawResource(resource, data); return(resource); }
private TagResourceReference CreateResource <T>(T resourceDefinition, ResourceLocation location, TagResourceTypeGen3 resourceType) { var resourceReference = new TagResourceReference(); var pageableResource = new PageableResource(); pageableResource.Page = new ResourcePage(); pageableResource.Resource = new ResourceData(); pageableResource.ChangeLocation(location); pageableResource.Resource.Unknown2 = 1; pageableResource.Resource.ResourceType = resourceType; resourceReference.HaloOnlinePageableResource = pageableResource; var definitionStream = new MemoryStream(); var dataStream = new MemoryStream(); using (var definitionWriter = new EndianWriter(definitionStream, EndianFormat.LittleEndian)) using (var dataWriter = new EndianWriter(dataStream, EndianFormat.LittleEndian)) { var context = new ResourceDefinitionSerializationContext(dataWriter, definitionWriter, CacheAddressType.Definition); var serializer = new ResourceSerializer(Cache.Version); serializer.Serialize(context, resourceDefinition); var data = dataStream.ToArray(); var definitionData = definitionStream.ToArray(); dataStream.Position = 0; pageableResource.DisableChecksum(); dataStream.Position = 0; AddResource(pageableResource, dataStream); // add resource definition and fixups pageableResource.Resource.DefinitionData = definitionData; pageableResource.Resource.FixupLocations = context.FixupLocations; pageableResource.Resource.DefinitionAddress = context.MainStructOffset; pageableResource.Resource.InteropLocations = context.InteropLocations; } return(resourceReference); }
/// <summary> /// Compresses and replaces the data for a resource. /// </summary> /// <param name="resource">The resource whose data should be replaced. On success, the reference will be adjusted to account for the new data.</param> /// <param name="dataStream">The stream to read the new data from.</param> /// <exception cref="System.ArgumentException">Thrown if the input stream is not open for reading.</exception> public void ReplaceResource(PageableResource resource, Stream dataStream) { if (resource == null) { throw new ArgumentNullException("resource"); } if (!dataStream.CanRead) { throw new ArgumentException("The input stream is not open for reading", "dataStream"); } var cache = GetResourceCache(resource); using (var stream = cache.File.Open(FileMode.Open, FileAccess.ReadWrite)) { var dataSize = (int)(dataStream.Length - dataStream.Position); var data = new byte[dataSize]; dataStream.Read(data, 0, dataSize); var compressedSize = cache.Cache.Compress(stream, resource.Page.Index, data); resource.Page.CompressedBlockSize = compressedSize; resource.Page.UncompressedBlockSize = (uint)dataSize; resource.DisableChecksum(); } }
/// <summary> /// Adds a new pageable_resource to the current cache. /// </summary> /// <param name="resource">The pageable_resource to add.</param> /// <param name="dataStream">The stream to read the resource data from.</param> /// <exception cref="System.ArgumentNullException">resource</exception> /// <exception cref="System.ArgumentException">The input stream is not open for reading;dataStream</exception> public void AddResource(PageableResource resource, Stream dataStream) { if (resource == null) { throw new ArgumentNullException("resource"); } if (!dataStream.CanRead) { throw new ArgumentException("The input stream is not open for reading", "dataStream"); } var cache = GetResourceCache(resource, out var location); using (var stream = OpenCacheReadWrite(location)) { var dataSize = (int)(dataStream.Length - dataStream.Position); var data = new byte[dataSize]; dataStream.Read(data, 0, dataSize); resource.Page.Index = cache.Add(stream, data, out uint compressedSize); resource.Page.CompressedBlockSize = compressedSize; resource.Page.UncompressedBlockSize = (uint)dataSize; resource.DisableChecksum(); } }
public T Deserialize <T>(PageableResource pageable) => Deserialize <T>(new ResourceSerializationContext(this, pageable));
private void AddTags(HashSet <int> tagIndices) { using (var srcTagStream = CacheContext.OpenTagCacheRead()) { 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, ResourceCache>(); var srcResourceStreams = new Dictionary <ResourceLocation, Stream>(); foreach (var value in Enum.GetValues(typeof(ResourceLocation))) { ResourceCache resourceCache = null; var location = (ResourceLocation)value; if (location == ResourceLocation.None) { continue; } try { resourceCache = CacheContext.GetResourceCache(location); } catch (FileNotFoundException) { continue; } srcResourceCaches[location] = resourceCache; srcResourceStreams[location] = CacheContext.OpenResourceCacheRead(location); } for (var tagIndex = 0; tagIndex < CacheContext.TagCache.Index.Count; tagIndex++) { if (!tagIndices.Contains(tagIndex)) { ModPackage.Tags.AllocateTag(); continue; } var srcTag = CacheContext.GetTag(tagIndex); var destTag = ModPackage.Tags.AllocateTag(srcTag.Group, srcTag.Name); using (var tagDataStream = new MemoryStream(CacheContext.TagCache.ExtractTagRaw(srcTagStream, 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 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 = 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 RawPage { 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); } ModPackage.Tags.SetTagDataRaw(ModPackage.TagsStream, destTag, tagDataStream.ToArray()); } } ModPackage.Tags.UpdateTagOffsets(new BinaryWriter(ModPackage.TagsStream, Encoding.Default, true)); } }
private static bool ExtractResource(ScenarioStructureBsp Definition, HaloOnlineCacheContext CacheContext, IReadOnlyList <string> args) { if (args.Count < 1) { return(false); } var resourceType = ""; var filePath = ""; var cachePath = "resources.dat"; resourceType = args[1].ToLower(); // geometry2 filePath = args[2]; // sc140_000_geometry2.raw PageableResource resource = null; switch (resourceType) { case "geometry": resource = Definition.Geometry.Resource; break; case "geometry2": resource = Definition.Geometry2.Resource; break; case "collisionbspresource": resource = Definition.CollisionBspResource; break; case "pathfindingresource": resource = Definition.PathfindingResource; break; default: Console.WriteLine($"Invalid sbsp resource type."); return(false); } if (resource == null || resource.Page.Index < 0 || !resource.GetLocation(out var location)) { Console.WriteLine("Resource is null."); return(false); } try { using (var stream = File.OpenRead(CacheContext.TagCacheFile.DirectoryName + "\\" + cachePath)) { var cache = new ResourceCache(stream); using (var outStream = File.Open(filePath, FileMode.Create, FileAccess.Write)) { cache.Decompress(stream, resource.Page.Index, resource.Page.CompressedBlockSize, outStream); Console.WriteLine("Wrote 0x{0:X} bytes to {1}.", outStream.Position, filePath); } } } catch (Exception ex) { Console.WriteLine("Failed to extract resource: {0}", ex.Message); } return(true); }
private PageableResource ConvertPageableResource(Stream sourceStream, Stream destStream, PageableResource resource) { if (resource.GetLocation(out var location) && location == ResourceLocation.ResourcesB) { resource.ChangeLocation(ResourceLocation.Resources); var resourceData = ResourcesB.ExtractRaw(sourceStream, resource.Page.Index, resource.Page.CompressedBlockSize); resource.Page.Index = Resources.AddRaw(destStream, resourceData); } return(resource); }
private PageableResource ConvertPageableResource(ModPackageExtended sourceModPack, ModPackageExtended destModPack, PageableResource resource) { if (resource.Page.Index == -1) { return(resource); } var resourceData = sourceModPack.Resources.ExtractRaw(sourceModPack.ResourcesStream, resource.Page.Index, resource.Page.CompressedBlockSize); resource.Page.Index = destModPack.Resources.Add(destModPack.ResourcesStream, resourceData, out resource.Page.CompressedBlockSize); return(resource); }
public override object Execute(List <string> args) { if (args.Count != 0) { return(false); } using (var cacheStream = CacheContext.OpenTagCacheReadWrite()) using (var reader = new EndianReader(cacheStream)) using (var writer = new EndianWriter(cacheStream)) { var retainedTags = new HashSet <int>(); LoadTagDependencies(CacheContext.TagCache.Index.FindFirstInGroup("cfgt").Index, ref retainedTags); foreach (var scnr in CacheContext.TagCache.Index.FindAllInGroup("scnr")) { LoadTagDependencies(scnr.Index, ref retainedTags); } var resourceIndices = new Dictionary <ResourceLocation, Dictionary <int, PageableResource> > { [ResourceLocation.Audio] = new Dictionary <int, PageableResource>(), [ResourceLocation.Lightmaps] = 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>() }; // // Set up source resource caches // var srcResourceCaches = new Dictionary <ResourceLocation, ResourceCache>(); var srcResourceStreams = new Dictionary <ResourceLocation, Stream>(); foreach (var value in Enum.GetValues(typeof(ResourceLocation))) { ResourceCache resourceCache = null; var location = (ResourceLocation)value; if (location == ResourceLocation.None) { continue; } try { resourceCache = CacheContext.GetResourceCache(location); } catch (FileNotFoundException) { continue; } srcResourceCaches[location] = resourceCache; srcResourceStreams[location] = CacheContext.OpenResourceCacheRead(location); } // // Set up destination resource caches // var destDirectory = new DirectoryInfo("new"); var destResourceCaches = new Dictionary <ResourceLocation, ResourceCache>(); var destResourceStreams = new Dictionary <ResourceLocation, Stream>(); foreach (var entry in srcResourceStreams) { var resourceCache = CacheContext.CreateResourceCache(destDirectory, entry.Key); destResourceCaches[entry.Key] = resourceCache; destResourceStreams[entry.Key] = File.Open(Path.Combine(destDirectory.FullName, CacheContext.ResourceCacheNames[entry.Key]), FileMode.Open, FileAccess.ReadWrite); } // // Copy any used resources to the new resource caches // for (var i = 0; i < CacheContext.TagCache.Index.Count; i++) { var tag = CacheContext.TagCache.Index[i]; if (tag == null) { continue; } if (!retainedTags.Contains(i)) { var tagName = tag?.Name ?? $"0x{tag.Index:X4}"; var tagGroupName = CacheContext.GetString(tag.Group.Name); Console.Write($"Nulling {tagName}.{tagGroupName}..."); CacheContext.TagCache.NullTag(cacheStream, tag); Console.WriteLine("done."); continue; } foreach (var resourcePointerOffset in tag.ResourcePointerOffsets) { reader.BaseStream.Position = tag.HeaderOffset + resourcePointerOffset; var resourcePointer = reader.ReadUInt32(); if (resourcePointer == 0) { continue; } var resourceOffset = tag.PointerToOffset(resourcePointer); reader.BaseStream.Position = tag.HeaderOffset + resourceOffset + 2; var locationFlags = (OldRawPageFlags)reader.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; reader.BaseStream.Position = tag.HeaderOffset + resourceOffset + 4; var resourceIndex = reader.ReadInt32(); var compressedSize = reader.ReadUInt32(); if (resourceIndex == -1) { continue; } PageableResource pageable = null; if (resourceIndices[resourceLocation].ContainsKey(resourceIndex)) { pageable = resourceIndices[resourceLocation][resourceIndex]; } else { var newResourceIndex = destResourceCaches[resourceLocation].AddRaw( destResourceStreams[resourceLocation], srcResourceCaches[resourceLocation].ExtractRaw( srcResourceStreams[resourceLocation], resourceIndex, compressedSize)); pageable = resourceIndices[resourceLocation][resourceIndex] = new PageableResource { Page = new RawPage { OldFlags = resourceLocation == ResourceLocation.Audio ? OldRawPageFlags.InAudio : resourceLocation == ResourceLocation.Resources ? OldRawPageFlags.InResources : resourceLocation == ResourceLocation.Textures ? OldRawPageFlags.InTextures : resourceLocation == ResourceLocation.TexturesB ? OldRawPageFlags.InTexturesB : OldRawPageFlags.InResourcesB, Index = newResourceIndex } }; } reader.BaseStream.Position = tag.HeaderOffset + resourceOffset + 2; writer.Write((byte)pageable.Page.OldFlags); reader.BaseStream.Position = tag.HeaderOffset + resourceOffset + 4; writer.Write(pageable.Page.Index); } } // // Close resource streams // foreach (var entry in srcResourceStreams) { entry.Value.Close(); } foreach (var entry in destResourceStreams) { entry.Value.Close(); } } return(true); }
/// <summary> /// Adds a new pageable_resource to the current cache. /// </summary> /// <param name="pageable">The <see cref="PageableResource"/> to add.</param> /// <param name="stream">The <see cref="Stream"/> to read the resource data from.</param> public abstract void AddResource(PageableResource pageable, Stream stream);
private PageableResource ConvertBitmap(Bitmap bitmap, Dictionary <ResourceLocation, Stream> resourceStreams, int imageIndex, string tagName) { var image = bitmap.Images[imageIndex]; BaseBitmap baseBitmap = BitmapConverter.ConvertGen3Bitmap(BlamCache, bitmap, imageIndex, BlamCache.Version); if (baseBitmap == null) { return(null); } // fix type enum if (baseBitmap.Type == BitmapType.Array) { baseBitmap.Type = BitmapType.Texture3D; } SetTagData(baseBitmap, image); var dataSize = baseBitmap.Data.Length; var resource = new PageableResource { Page = new RawPage(), Resource = new TagResourceGen3 { ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), ResourceType = TagResourceTypeGen3.Bitmap, Unknown2 = 1 } }; using (var dataStream = new MemoryStream(baseBitmap.Data)) { var bitmapResource = new Bitmap.BitmapResource { Resource = resource, Unknown4 = 0 }; var resourceContext = new ResourceSerializationContext(CacheContext, resource); // Create new definition var resourceDefinition = new BitmapTextureInteropResource { Texture = new TagStructureReference <BitmapTextureInteropResource.BitmapDefinition> { Definition = new BitmapTextureInteropResource.BitmapDefinition { Data = new TagData(), UnknownData = new TagData(), } } }; SetResourceDefinitionData(baseBitmap, image, resourceDefinition.Texture.Definition); // // Serialize the new resource definition // var location = bitmap.Usage == 2 ? ResourceLocation.TexturesB : // bump maps ResourceLocation.Textures; // everything else resource.ChangeLocation(location); if (resource == null) { throw new ArgumentNullException("resource"); } if (!dataStream.CanRead) { throw new ArgumentException("The input stream is not open for reading", "dataStream"); } var cache = CacheContext.GetResourceCache(location); if (!resourceStreams.ContainsKey(location)) { resourceStreams[location] = FlagIsSet(PortingFlags.Memory) ? new MemoryStream() : (Stream)CacheContext.OpenResourceCacheReadWrite(location); if (FlagIsSet(PortingFlags.Memory)) { using (var resourceStream = CacheContext.OpenResourceCacheRead(location)) resourceStream.CopyTo(resourceStreams[location]); } } dataSize = (int)(dataStream.Length - dataStream.Position); var data = new byte[dataSize]; dataStream.Read(data, 0, dataSize); resource.Page.Index = cache.Add(resourceStreams[location], data, out uint compressedSize); resource.Page.CompressedBlockSize = compressedSize; resource.Page.UncompressedBlockSize = (uint)dataSize; resource.DisableChecksum(); CacheContext.Serializer.Serialize(resourceContext, resourceDefinition); } return(resource); }
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 ResourceSerializationContext(GameCacheContext cacheContext, PageableResource resource) { CacheContext = cacheContext; Resource = resource; }
public void Serialize(PageableResource pageable, object definition) => Serialize(new ResourceSerializationContext(this, pageable), definition);
public override object Execute(List <string> args) { if (args.Count < 1 || args.Count > 5) { return(false); } bool promptTags = false; bool promptMaps = false; int? fromIndex = null; int? toIndex = null; string forCache = null; while (args.Count != 1) { switch (args[0].ToLower()) { case "prompttags": promptTags = true; args.RemoveRange(0, 1); break; case "promptmaps": promptMaps = true; args.RemoveRange(0, 1); break; case "from:": if (CacheContext.TryGetTag(args[1], out var fromInstance) && fromInstance != null) { fromIndex = fromInstance.Index; } args.RemoveRange(0, 2); break; case "to:": if (CacheContext.TryGetTag(args[1], out var toInstance) && toInstance != null) { toIndex = toInstance.Index; } args.RemoveRange(0, 2); break; case "forCache:": forCache = args[1]; args.RemoveRange(0, 2); break; default: throw new ArgumentException(args[0]); } } if (!promptTags && !promptMaps && !fromIndex.HasValue && !toIndex.HasValue) { promptTags = true; promptMaps = true; } if (fromIndex.HasValue && !toIndex.HasValue) { toIndex = CacheContext.TagCache.Index.NonNull().Last().Index; } var packageFile = new FileInfo(args[0]); var tagIndices = new HashSet <int>(); var mapFiles = new HashSet <string>(); string line = null; if (fromIndex.HasValue && toIndex.HasValue) { foreach (var entry in Enumerable.Range(fromIndex.Value, toIndex.Value - fromIndex.Value)) { if (!tagIndices.Contains(entry) && CacheContext.GetTag(entry) != null) { tagIndices.Add(entry); } } } if (promptTags) { Console.WriteLine("Please specify the tags to be used (enter an empty line to finish):"); while ((line = Console.ReadLine().TrimStart().TrimEnd()) != "") { if (CacheContext.TryGetTag(line, out var instance) && instance != null && !tagIndices.Contains(instance.Index)) { tagIndices.Add(instance.Index); } } } if (promptMaps) { Console.WriteLine("Please specify the .map files to be used (enter an empty line to finish):"); while ((line = Console.ReadLine().TrimStart().TrimEnd()) != "") { var mapFile = new FileInfo(line); if (mapFile.Exists && mapFile.Extension == ".map" && !mapFiles.Contains(mapFile.FullName)) { mapFiles.Add(mapFile.FullName); } } } using (var srcTagStream = CacheContext.OpenTagCacheRead()) using (var destTagsStream = new MemoryStream()) using (var destResourceStream = new MemoryStream()) { var destTagCache = CacheContext.CreateTagCache(destTagsStream); var destResourceCache = CacheContext.CreateResourceCache(destResourceStream); 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, ResourceCache>(); var srcResourceStreams = new Dictionary <ResourceLocation, Stream>(); foreach (var value in Enum.GetValues(typeof(ResourceLocation))) { ResourceCache resourceCache = null; var location = (ResourceLocation)value; if (location == ResourceLocation.None) { continue; } try { resourceCache = CacheContext.GetResourceCache(location); } catch (FileNotFoundException) { continue; } srcResourceCaches[location] = resourceCache; srcResourceStreams[location] = CacheContext.OpenResourceCacheRead(location); } for (var tagIndex = 0; tagIndex < CacheContext.TagCache.Index.Count; tagIndex++) { if (!tagIndices.Contains(tagIndex)) { destTagCache.AllocateTag(); continue; } var srcTag = CacheContext.GetTag(tagIndex); var destTag = destTagCache.AllocateTag(srcTag.Group, srcTag.Name); using (var tagDataStream = new MemoryStream(CacheContext.TagCache.ExtractTagRaw(srcTagStream, srcTag))) using (var tagDataReader = new EndianReader(tagDataStream)) using (var tagDataWriter = new EndianWriter(tagDataStream)) { var resourcePointerOffsets = new HashSet <uint>(); foreach (var resourcePointerOffset in 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 = 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 = destResourceCache.AddRaw( destResourceStream, srcResourceCaches[resourceLocation].ExtractRaw( srcResourceStreams[resourceLocation], resourceIndex, compressedSize)); pageable = resourceIndices[resourceLocation][resourceIndex] = new PageableResource { Page = new RawPage { 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); } destTagCache.SetTagDataRaw(destTagsStream, destTag, tagDataStream.ToArray()); } } destTagCache.UpdateTagOffsets(new BinaryWriter(destTagsStream, Encoding.Default, true)); if (!packageFile.Directory.Exists) { packageFile.Directory.Create(); } using (var packageStream = packageFile.Create()) using (var writer = new BinaryWriter(packageStream)) { // // reserve header space // writer.Write(new byte[48]); // // write tag cache // var tagCacheOffset = (uint)packageStream.Position; destTagsStream.Position = 0; StreamUtil.Copy(destTagsStream, packageStream, (int)destTagsStream.Length); StreamUtil.Align(packageStream, 4); // // write tag names table // var names = new Dictionary <int, string>(); foreach (var entry in destTagCache.Index) { if (entry != null && entry.Name != null) { names[entry.Index] = entry.Name; } } var tagNamesTableOffset = (uint)packageStream.Position; var tagNamesTableCount = names.Count; foreach (var entry in names) { writer.Write(entry.Key); var chars = new char[256]; for (var i = 0; i < entry.Value.Length; i++) { chars[i] = entry.Value[i]; } writer.Write(chars); } // // write resource cache // var resourceCacheOffset = (uint)packageStream.Position; destResourceStream.Position = 0; StreamUtil.Copy(destResourceStream, packageStream, (int)destResourceStream.Length); StreamUtil.Align(packageStream, 4); // // write map file table // var mapFileTableOffset = (uint)packageStream.Position; var mapFileTableCount = mapFiles.Count; var mapFileInfo = new List <(uint, uint)>(); writer.Write(new byte[8 * mapFileTableCount]); foreach (var entry in mapFiles) { var mapFile = new FileInfo(entry); using (var mapFileStream = mapFile.OpenRead()) { mapFileInfo.Add(((uint)packageStream.Position, (uint)mapFileStream.Length)); StreamUtil.Copy(mapFileStream, packageStream, (int)mapFileStream.Length); StreamUtil.Align(packageStream, 4); } } packageStream.Position = mapFileTableOffset; foreach (var entry in mapFileInfo) { writer.Write(entry.Item1); writer.Write(entry.Item2); } // // calculate package sha1 // packageStream.Position = 48; var packageSha1 = new SHA1Managed().ComputeHash(packageStream); // // update package header // packageStream.Position = 0; writer.Write(new Tag("mod!")); writer.Write(packageSha1); writer.Write(tagCacheOffset); writer.Write(tagNamesTableCount == 0 ? 0 : tagNamesTableOffset); writer.Write(tagNamesTableCount); writer.Write(resourceCacheOffset); writer.Write(mapFileTableCount == 0 ? 0 : mapFileTableOffset); writer.Write(mapFileTableCount); } } return(true); }
public void InjectDds(TagSerializer serializer, TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream ddsStream, ResourceLocation location = ResourceLocation.Textures) { var resource = bitmap.Resources[imageIndex].Resource; var newResource = (resource == null); ResourceSerializationContext resourceContext; BitmapTextureInteropResource definition; if (newResource) { // Create a new resource reference resource = new PageableResource { Page = new RawPage(), Resource = new TagResourceGen3 { ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), ResourceType = TagResourceTypeGen3.Bitmap, Unknown2 = 1 } }; bitmap.Resources[imageIndex].Resource = resource; resourceContext = new ResourceSerializationContext(CacheContext, resource); definition = new BitmapTextureInteropResource { Texture = new TagStructureReference <BitmapTextureInteropResource.BitmapDefinition> { Definition = new BitmapTextureInteropResource.BitmapDefinition { Data = new TagData(), UnknownData = new TagData(), } } }; } else { // Deserialize the old definition resourceContext = new ResourceSerializationContext(CacheContext, resource); definition = deserializer.Deserialize <BitmapTextureInteropResource>(resourceContext); } if (definition.Texture == null || definition.Texture.Definition == null) { throw new ArgumentException("Invalid bitmap definition"); } var texture = definition.Texture.Definition; var imageData = bitmap.Images[imageIndex]; // Read the DDS header and modify the definition to match var dds = DdsHeader.Read(ddsStream); var dataSize = (int)(ddsStream.Length - ddsStream.Position); texture.Data = new TagData(dataSize, new CacheResourceAddress(CacheResourceAddressType.Resource, 0)); texture.Width = (short)dds.Width; texture.Height = (short)dds.Height; texture.Depth = (sbyte)Math.Max(1, dds.Depth); texture.MipmapCount = (sbyte)Math.Max(1, dds.MipMapCount); texture.Type = BitmapDdsFormatDetection.DetectType(dds); texture.D3DFormat = (int)((dds.D3D10Format != DxgiFormat.Bc5UNorm) ? dds.FourCc : DdsFourCc.FromString("ATI2")); texture.Format = BitmapDdsFormatDetection.DetectFormat(dds); // Set flags based on the format switch (texture.Format) { case BitmapFormat.Dxt1: case BitmapFormat.Dxt3: case BitmapFormat.Dxt5: case BitmapFormat.Dxn: texture.Flags = BitmapFlags.Compressed; break; default: texture.Flags = BitmapFlags.None; break; } if ((texture.Width & (texture.Width - 1)) == 0 && (texture.Height & (texture.Height - 1)) == 0) { texture.Flags |= BitmapFlags.PowerOfTwoDimensions; } // If creating a new image, then add a new resource, otherwise replace the existing one if (newResource) { resource.ChangeLocation(location); CacheContext.AddResource(resource, ddsStream); } else { CacheContext.ReplaceResource(resource, ddsStream); } // Serialize the new resource definition serializer.Serialize(resourceContext, definition); // Modify the image data in the bitmap tag to match the definition imageData.Width = texture.Width; imageData.Height = texture.Height; imageData.Depth = texture.Depth; imageData.Type = texture.Type; imageData.Format = texture.Format; imageData.Flags = texture.Flags; imageData.MipmapCount = (sbyte)(texture.MipmapCount - 1); imageData.DataOffset = texture.Data.Address.Offset; imageData.DataSize = texture.Data.Size; imageData.Curve = (BitmapImageCurve)texture.Curve; }
/// <summary> /// Extracts and decompresses the data for a <see cref="PageableResource"/> from the current cache. /// </summary> /// <param name="resource">The <see cref="PageableResource"/> to extract.</param> /// <param name="stream">The <see cref="Stream"/> to write the extracted data to.</param> public abstract void ExtractResource(PageableResource resource, Stream stream);