/// <summary> /// Verifies if the stream points to a valid blf start chunk and set the endian format. /// </summary> /// <param name="reader"></param> /// <returns></returns> private bool IsValid(EndianReader reader) { var position = reader.Position; try { Format = FindChunkEndianFormat(reader); reader.Format = Format; } catch (Exception e) { Console.WriteLine($"BLF file is invalid: {e.Message}"); return(false); } var deserializer = new TagDeserializer(Version); var dataContext = new DataSerializationContext(reader); var header = (BlfChunkHeader)deserializer.Deserialize(dataContext, typeof(BlfChunkHeader)); reader.SeekTo(position); if (header.Signature == "_blf") { return(true); } else { return(false); } }
public CacheFile(GameCache cacheContext, FileInfo file, CacheVersion version, bool memory) { CacheContext = cacheContext; File = file; Version = version; Deserializer = new TagDeserializer(Version); Stream = memory ? new MemoryStream() : (Stream)file.OpenRead(); if (memory) { using (var fileStream = file.OpenRead()) { fileStream.Seek(0, SeekOrigin.Begin); fileStream.CopyTo(Stream); } } Reader = new EndianReader(Stream, EndianFormat.LittleEndian); Reader.SeekTo(0); if (Reader.ReadTag() == "daeh") { Reader.Format = EndianFormat.BigEndian; } Reader.SeekTo(0); Header = Deserializer.Deserialize <CacheFileHeader>(new DataSerializationContext(Reader)); }
public void Read(EndianReader reader) { EndianFormat = DetectEndianFormat(reader); MapVersion = GetMapFileVersion(reader); Version = DetectCacheVersion(reader); var deserializer = new TagDeserializer(Version); reader.SeekTo(0); var dataContext = new DataSerializationContext(reader); Header = (CacheFileHeader)deserializer.Deserialize(dataContext, typeof(CacheFileHeader)); // temporary code until map file format cleanup if (MapVersion == CacheFileVersion.HaloOnline) { var mapFileHeaderSize = (int)TagStructure.GetTagStructureInfo(typeof(CacheFileHeader), Version).TotalSize; // Seek to the blf reader.SeekTo(mapFileHeaderSize); // Read blf MapFileBlf = new Blf(Version); if (!MapFileBlf.Read(reader)) { MapFileBlf = null; } } }
public void ExtractDds(TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream outStream) { // TODO: Make sure 3D textures and cube maps work // Deserialize the resource definition and verify it var resource = bitmap.Resources[imageIndex]; var resourceContext = new ResourceSerializationContext(resource.Resource); var definition = deserializer.Deserialize <BitmapTextureResourceDefinition>(resourceContext); if (definition.Texture == null || definition.Texture.Definition == null) { throw new ArgumentException("Invalid bitmap definition"); } var dataReference = definition.Texture.Definition.Data; if (dataReference.Address.Type != ResourceAddressType.Resource) { throw new InvalidOperationException("Invalid resource data address"); } var header = CreateDdsHeader(definition); var resourceDataStream = new MemoryStream(); _resourceManager.Extract(resource.Resource, resourceDataStream); header.WriteTo(outStream); resourceDataStream.Position = dataReference.Address.Offset; StreamUtil.Copy(resourceDataStream, outStream, dataReference.Size); }
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 <ModPackageHeaderExtended>(dataContext); ReadMetadataSection(reader, dataContext, deserializer); ReadTagsSection(reader); ReadTagNamesSection(reader, dataContext, deserializer); ReadResourcesSection(reader); ReadMapFilesSection(reader); ReadCampaignFileSection(reader); Tags = new TagCache(TagsStream, TagNames); Resources = new ResourceCache(ResourcesStream); } }
public GameCacheHaloOnline(DirectoryInfo directory) { Directory = directory; TagsFile = new FileInfo(Path.Combine(directory.FullName, "tags.dat")); TagNamesFile = new FileInfo(Path.Combine(directory.FullName, "tag_list.csv")); StringIdCacheFile = new FileInfo(Path.Combine(directory.FullName, "string_ids.dat")); Endianness = EndianFormat.LittleEndian; var names = TagCacheHaloOnline.LoadTagNames(TagNamesFile.FullName); using (var stream = TagsFile.OpenRead()) TagCacheGenHO = new TagCacheHaloOnline(stream, names); if (CacheVersion.Unknown == (Version = CacheVersionDetection.DetectFromTimestamp(TagCacheGenHO.Header.CreationTime, out var closestVersion))) { Version = closestVersion; } using (var stream = StringIdCacheFile.OpenRead()) StringTableHaloOnline = new StringTableHaloOnline(Version, stream); DisplayName = Version.ToString(); Deserializer = new TagDeserializer(Version); Serializer = new TagSerializer(Version); ResourceCaches = new ResourceCachesHaloOnline(this); }
public void InjectDds(Bitmap bitmap, int imageIndex, Stream ddsStream) { // Deserialize the old definition var resourceContext = new ResourceSerializationContext(bitmap.Resources[imageIndex].Resource); var definition = TagDeserializer.Deserialize <BitmapTextureResourceDefinition>(resourceContext); if (definition.Texture == null || definition.Texture.Definition == null) { throw new ArgumentException("Invalid bitmap definition"); } var texture = definition.Texture.Definition; // 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 ResourceDataReference(dataSize, new ResourceAddress(ResourceAddressType.Resource, 0)); texture.Width = (short)dds.Width; texture.Height = (short)dds.Height; texture.Depth = (sbyte)Math.Max(1, dds.Depth); texture.Levels = (sbyte)Math.Max(1, dds.MipMapCount); texture.Type = BitmapDdsFormatDetection.DetectType(dds); texture.D3DFormatUnused = (int)((dds.D3D10Format != DxgiFormat.Bc5UNorm) ? dds.FourCc : DdsFourCc.FromString("ATI2")); texture.Format = BitmapDdsFormatDetection.DetectFormat(dds); // Set flags based on the format switch (definition.Texture.Definition.Format) { case BitmapFormat.Dxt1: case BitmapFormat.Dxt3: case BitmapFormat.Dxt5: case BitmapFormat.Dxn: definition.Texture.Definition.Flags = BitmapFlags.Compressed; break; default: definition.Texture.Definition.Flags = BitmapFlags.None; break; } // Inject the resource data _resourceManager.Replace(bitmap.Resources[imageIndex].Resource, ddsStream); // Serialize the new resource definition TagSerializer.Serialize(resourceContext, definition); // Modify the image data in the bitmap tag to match the definition var imageData = bitmap.Images[imageIndex]; 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.Levels - 1); imageData.DataOffset = texture.Data.Address.Offset; imageData.DataSize = texture.Data.Size; }
private void Read(Stream stream) { // don't use using{}, we want to maintain the stream open. reader/writers will automatically close the stream when done in an using. var reader = new EndianReader(stream, EndianFormat.LittleEndian); stream.Position = 0; var addresses = new List <uint>(); var sizes = new List <uint>(); var dataContext = new DataSerializationContext(reader); var deserializer = new TagDeserializer(Version); Header = deserializer.Deserialize <ResourceCacheHaloOnlineHeader>(dataContext); reader.SeekTo(Header.ResourceTableOffset); // read all resource offsets if (Header.ResourceCount == 0) { return; } for (var i = 0; i < Header.ResourceCount; i++) { var address = reader.ReadUInt32(); if (!addresses.Contains(address) && (address != uint.MaxValue)) { addresses.Add(address); } Resources.Add(new Resource { Offset = address }); } // compute chunk sizes addresses.Sort((a, b) => a.CompareTo(b)); for (var i = 0; i < addresses.Count - 1; i++) { sizes.Add(addresses[i + 1] - addresses[i]); } sizes.Add(Header.ResourceTableOffset - addresses.Last()); foreach (var resource in Resources) { if (resource.Offset == uint.MaxValue) { continue; } resource.ChunkSize = sizes[addresses.IndexOf(resource.Offset)]; } }
public GameCacheModPackage(ModPackage modPackage, FileInfo file) { BaseModPackage = modPackage; Directory = file.Directory; DisplayName = BaseModPackage.Metadata.Name + ".pak"; Version = CacheVersion.HaloOnline106708; Endianness = EndianFormat.LittleEndian; Deserializer = new TagDeserializer(Version); Serializer = new TagSerializer(Version); }
private void EditHlmtTag(HaloTag tag) { Model model; using (var stream = _fileInfo.OpenRead()) model = TagDeserializer.Deserialize <Model>(new TagSerializationContext(stream, _cache, tag)); var context = HlmtContextFactory.Create(_stack.Context, _fileInfo, _cache, _stringIds, tag, model); _stack.Push(context); }
private void EditBitmTag(HaloTag tag) { Bitmap bitmap; using (var stream = _fileInfo.OpenRead()) bitmap = TagDeserializer.Deserialize <Bitmap>(new TagSerializationContext(stream, _cache, tag)); var context = BitmContextFactory.Create(_stack.Context, _fileInfo, _cache, tag, bitmap); _stack.Push(context); }
private void EditVfslTag(HaloTag tag) { VFilesList vfsl; using (var stream = _fileInfo.OpenRead()) vfsl = TagDeserializer.Deserialize <VFilesList>(new TagSerializationContext(stream, _cache, tag)); var context = VfslContextFactory.Create(_stack.Context, _fileInfo, _cache, tag, vfsl); _stack.Push(context); }
private void EditUnicTag(HaloTag tag) { MultilingualUnicodeStringList unic; using (var stream = _fileInfo.OpenRead()) unic = TagDeserializer.Deserialize <MultilingualUnicodeStringList>(new TagSerializationContext(stream, _cache, tag)); var context = UnicContextFactory.Create(_stack.Context, _fileInfo, _cache, tag, unic, _stringIds); _stack.Push(context); }
private MapFile GenerateMapFile(CachedTagInstance scenarioTag, Blf mapInfo = null) { MapFile map = new MapFile(); var header = new MapFileHeader(); Scenario scnr; using (var stream = CacheContext.OpenTagCacheRead()) { var deserializer = new TagDeserializer(CacheContext.Version); scnr = (Scenario)CacheContext.Deserialize(stream, scenarioTag); } map.Version = CacheContext.Version; map.EndianFormat = EndianFormat.LittleEndian; map.MapVersion = MapFileVersion.HaloOnline; header.HeadTag = new Tag("head"); header.FootTag = new Tag("foot"); header.Version = (int)map.MapVersion; header.Build = CacheVersionDetection.GetBuildName(CacheContext.Version); switch (scnr.MapType) { case ScenarioMapType.MainMenu: header.CacheType = CacheFileType.MainMenu; break; case ScenarioMapType.SinglePlayer: header.CacheType = CacheFileType.Campaign; break; case ScenarioMapType.Multiplayer: header.CacheType = CacheFileType.Multiplayer; break; } header.SharedType = CacheFileSharedType.None; header.MapId = scnr.MapId; header.ScenarioTagIndex = scenarioTag.Index; header.Name = scenarioTag.Name.Split('\\').Last(); header.ScenarioPath = scenarioTag.Name; map.Header = header; header.FileLength = 0x3390; if (mapInfo != null) { if (mapInfo.ContentFlags.HasFlag(BlfFileContentFlags.StartOfFile) && mapInfo.ContentFlags.HasFlag(BlfFileContentFlags.EndOfFile) && mapInfo.ContentFlags.HasFlag(BlfFileContentFlags.Scenario)) { map.MapFileBlf = mapInfo; } } return(map); }
public void InjectDds(TagSerializer serializer, TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream ddsStream) { // Deserialize the old definition var resourceContext = new ResourceSerializationContext(bitmap.Resources[imageIndex].Resource); var definition = deserializer.Deserialize<BitmapTextureResourceDefinition>(resourceContext); if (definition.Texture == null || definition.Texture.Definition == null) throw new ArgumentException("Invalid bitmap definition"); var texture = definition.Texture.Definition; // 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 ResourceDataReference(dataSize, new ResourceAddress(ResourceAddressType.Resource, 0)); texture.Width = (short)dds.Width; texture.Height = (short)dds.Height; texture.Depth = (sbyte)Math.Max(1, dds.Depth); texture.Levels = (sbyte)Math.Max(1, dds.MipMapCount); texture.Type = BitmapDdsFormatDetection.DetectType(dds); texture.D3DFormatUnused = (int)((dds.D3D10Format != DxgiFormat.Bc5UNorm) ? dds.FourCc : DdsFourCc.FromString("ATI2")); texture.Format = BitmapDdsFormatDetection.DetectFormat(dds); // Set flags based on the format switch (definition.Texture.Definition.Format) { case BitmapFormat.Dxt1: case BitmapFormat.Dxt3: case BitmapFormat.Dxt5: case BitmapFormat.Dxn: definition.Texture.Definition.Flags = BitmapFlags.Compressed; break; default: definition.Texture.Definition.Flags = BitmapFlags.None; break; } // Inject the resource data _resourceManager.Replace(bitmap.Resources[imageIndex].Resource, ddsStream); // Serialize the new resource definition serializer.Serialize(resourceContext, definition); // Modify the image data in the bitmap tag to match the definition var imageData = bitmap.Images[imageIndex]; 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.Levels - 1); imageData.DataOffset = texture.Data.Address.Offset; imageData.DataSize = texture.Data.Size; }
public TagCacheGen1(EndianReader reader, MapFile mapFile) { var tagDataSectionOffset = mapFile.Header.TagsHeaderAddress32; reader.SeekTo(tagDataSectionOffset); var dataContext = new DataSerializationContext(reader); var deserializer = new TagDeserializer(mapFile.Version); Header = deserializer.Deserialize <TagCacheGen1Header>(dataContext); if (mapFile.Version == CacheVersion.HaloXbox) { BaseTagAddress = 0x803A6000; } else { BaseTagAddress = 0x40440000; } // // Read all tags offsets are all broken, need some proper look // reader.SeekTo(Header.CachedTagArrayAddress - BaseTagAddress + tagDataSectionOffset); for (int i = 0; i < Header.TagCount; i++) { var group = new TagGroup() { Tag = new Tag(reader.ReadInt32()), ParentTag = new Tag(reader.ReadInt32()), GrandparentTag = new Tag(reader.ReadInt32()) }; var tagID = reader.ReadUInt32(); var tagPathNameAddress = reader.ReadUInt32(); var currentPos = reader.Position; string name = ""; if (tagPathNameAddress != 0) { reader.SeekTo(tagPathNameAddress - BaseTagAddress + tagDataSectionOffset); name = reader.ReadNullTerminatedString(); reader.SeekTo(currentPos); } var tagDataAddress = reader.ReadUInt32(); var weird2 = reader.ReadUInt32(); var unused = reader.ReadUInt32(); Tags.Add(new CachedTagGen1((int)(tagID & 0xFFFF), tagID, group, tagDataAddress, name)); } }
public GameCacheGen3(MapFile mapFile, FileInfo file) { BaseMapFile = mapFile; Version = BaseMapFile.Version; CacheFile = file; Deserializer = new TagDeserializer(Version); Serializer = new TagSerializer(Version); Endianness = BaseMapFile.EndianFormat; var interop = mapFile.Header.SectionTable; DisplayName = mapFile.Header.Name + ".map"; Directory = file.Directory; using (var cacheStream = OpenCacheRead()) using (var reader = new EndianReader(cacheStream, Endianness)) { StringTableGen3 = new StringTableGen3(reader, BaseMapFile); TagCacheGen3 = new TagCacheGen3(reader, BaseMapFile, StringTableGen3); ResourceCacheGen3 = new ResourceCacheGen3(this); if (TagCacheGen3.Instances.Count > 0) { if (BaseMapFile.Header.SectionTable.Sections[(int)CacheFileSectionType.LocalizationSection].Size == 0) { LocaleTables = new List <LocaleTable>(); } else { var globals = Deserialize <Globals>(cacheStream, TagCacheGen3.GlobalInstances["matg"]); LocaleTables = LocalesTableGen3.CreateLocalesTable(reader, BaseMapFile, globals); } } } // unused but kept for future uses switch (Version) { case CacheVersion.Halo3Beta: case CacheVersion.Halo3Retail: case CacheVersion.Halo3ODST: NetworkKey = ""; break; case CacheVersion.HaloReach: NetworkKey = "SneakerNetReigns"; break; } }
public override void LoadResourceTags() { TagDeserializer deserializer = new TagDeserializer(Version); foreach (IndexItem item in IndexItems) { if (item.GroupTag == "play") { CacheFile cacheFile = this; var blamContext = new CacheSerializationContext(ref cacheFile, item); if (cacheFile.File.FullName != File.FullName) { throw new InvalidOperationException(); } ResourceLayoutTable = deserializer.Deserialize <CacheFileResourceLayoutTable>(blamContext); break; } } foreach (IndexItem item in IndexItems) { if (item.GroupTag == "zone") { CacheFile cacheFile = this; var blamContext = new CacheSerializationContext(ref cacheFile, item); if (cacheFile.File.FullName != File.FullName) { throw new InvalidOperationException(); } ResourceGestalt = deserializer.Deserialize <CacheFileResourceGestalt>(blamContext); foreach (var tagresource in ResourceGestalt.TagResources) { foreach (var fixup in tagresource.ResourceFixups) { fixup.Offset = (int)(fixup.Address.Value & 0x0FFFFFFF); fixup.Type = (int)(fixup.Address.Value >> 28) & 0xF; fixup.RawAddress = (int)fixup.Address.Value; } } break; } } }
public GameCacheModPackage(FileInfo file) { ModPackageFile = file; Version = CacheVersion.HaloOnline106708; Endianness = EndianFormat.LittleEndian; Deserializer = new TagDeserializer(Version); Serializer = new TagSerializer(Version); Directory = file.Directory; // load mod package BaseModPackage = new ModPackage(file); DisplayName = BaseModPackage.Metadata.Name + ".pak"; ResourceCaches = new ResourceCachesModPackage(BaseModPackage); TagCacheGenHO = new TagCacheHaloOnline(BaseModPackage.TagCachesStreams[0], BaseModPackage.TagCacheNames[0]); StringTableHaloOnline = BaseModPackage.StringTable; }
public GameCacheGen1(MapFile mapFile, FileInfo file) { BaseMapFile = mapFile; CacheFile = file; Version = BaseMapFile.Version; CacheFile = file; Deserializer = new TagDeserializer(Version); Serializer = new TagSerializer(Version); Endianness = BaseMapFile.EndianFormat; DisplayName = mapFile.Header.NameOld + ".map"; Directory = file.Directory; using (var cacheStream = OpenCacheRead()) using (var reader = new EndianReader(cacheStream, Endianness)) { TagCacheGen1 = new TagCacheGen1(reader, mapFile); } }
public static byte[] ConvertHkpMoppData(CacheVersion sourceVersion, CacheVersion destVersion, byte[] data) { if (data == null || data.Length == 0) { return(data); } byte[] result; using (var inputReader = new EndianReader(new MemoryStream(data), CacheVersionDetection.IsLittleEndian(sourceVersion) ? EndianFormat.LittleEndian : EndianFormat.BigEndian)) using (var outputStream = new MemoryStream()) using (var outputWriter = new EndianWriter(outputStream, CacheVersionDetection.IsLittleEndian(destVersion) ? EndianFormat.LittleEndian : EndianFormat.BigEndian)) { var dataContext = new DataSerializationContext(inputReader, outputWriter); var deserializer = new TagDeserializer(sourceVersion); var serializer = new TagSerializer(destVersion); while (!inputReader.EOF) { var header = deserializer.Deserialize <HkpMoppCode>(dataContext); var dataSize = header.ArrayBase.GetCapacity(); var alignedSize = dataSize + 0xf & ~0xf; var nextOffset = inputReader.Position + alignedSize; List <byte> moppCodes = new List <byte>(); for (int j = 0; j < header.ArrayBase.GetCapacity(); j++) { moppCodes.Add(inputReader.ReadByte()); } inputReader.SeekTo(nextOffset); moppCodes = ConvertMoppCodes(sourceVersion, destVersion, moppCodes); serializer.Serialize(dataContext, header); for (int j = 0; j < moppCodes.Count; j++) { outputWriter.Write(moppCodes[j]); } StreamUtil.Align(outputStream, 0x10); } result = outputStream.ToArray(); } return(result); }
public override bool Execute(List <string> args) { if (args.Count != 1 && args.Count != 2) { return(false); } GameLanguage language; if (!ArgumentParser.ParseLanguage(args[0], out language)) { return(false); } var filter = (args.Count == 2) ? args[1] : null; var found = false; using (var stream = _fileInfo.OpenRead()) { foreach (var unicTag in _cache.Tags.FindAllByClass("unic")) { var unic = TagDeserializer.Deserialize <MultilingualUnicodeStringList>(new TagSerializationContext(stream, _cache, unicTag)); var strings = LocalizedStringPrinter.PrepareForDisplay(unic, _stringIds, unic.Strings, language, filter); if (strings.Count == 0) { continue; } if (found) { Console.WriteLine(); } Console.WriteLine("Strings found in {0:X8}.unic:", unicTag.Index); LocalizedStringPrinter.PrintStrings(strings); found = true; } } if (!found) { Console.Error.WriteLine("No strings found."); } return(true); }
public HaloOnlineCacheContext(DirectoryInfo directory) : base(directory) { var tagNames = LoadTagNames(); using (var stream = OpenTagCacheRead()) TagCache = new TagCache(stream, tagNames); if (CacheVersion.Unknown == (Version = CacheVersionDetection.DetectFromTagCache(TagCache, out var closestVersion))) { Version = closestVersion; } Deserializer = new TagDeserializer(Version == CacheVersion.Unknown ? closestVersion : Version); Serializer = new TagSerializer(Version == CacheVersion.Unknown ? closestVersion : Version); StringIdResolver stringIdResolver = null; if (CacheVersionDetection.Compare(Version, CacheVersion.HaloOnline700123) >= 0) { stringIdResolver = new StringIdResolverMS30(); } else if (CacheVersionDetection.Compare(Version, CacheVersion.HaloOnline498295) >= 0) { stringIdResolver = new StringIdResolverMS28(); } else { stringIdResolver = new StringIdResolverMS23(); } using (var stream = OpenStringIdCacheRead()) StringIdCache = new StringIdCache(stream, stringIdResolver); TagGroup.Instances[new Tag("obje")] = new TagGroup(new Tag("obje"), Tag.Null, Tag.Null, GetStringId("object")); TagGroup.Instances[new Tag("item")] = new TagGroup(new Tag("item"), new Tag("obje"), Tag.Null, GetStringId("item")); TagGroup.Instances[new Tag("devi")] = new TagGroup(new Tag("devi"), new Tag("obje"), Tag.Null, GetStringId("device")); TagGroup.Instances[new Tag("unit")] = new TagGroup(new Tag("unit"), new Tag("obje"), Tag.Null, GetStringId("unit")); TagGroup.Instances[new Tag("rm ")] = new TagGroup(new Tag("rm "), Tag.Null, Tag.Null, GetStringId("render_method")); TagGroup.Instances[new Tag("test")] = new TagGroup(new Tag("test"), Tag.Null, Tag.Null, GetStringId("test_blah")); }
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 void ExtractDds(TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream outStream) { // TODO: Make sure 3D textures and cube maps work // Deserialize the resource definition and verify it var resource = bitmap.Resources[imageIndex]; var resourceContext = new ResourceSerializationContext(resource.Resource); var definition = deserializer.Deserialize<BitmapTextureResourceDefinition>(resourceContext); if (definition.Texture == null || definition.Texture.Definition == null) throw new ArgumentException("Invalid bitmap definition"); var dataReference = definition.Texture.Definition.Data; if (dataReference.Address.Type != ResourceAddressType.Resource) throw new InvalidOperationException("Invalid resource data address"); var header = CreateDdsHeader(definition); var resourceDataStream = new MemoryStream(); _resourceManager.Extract(resource.Resource, resourceDataStream); header.WriteTo(outStream); resourceDataStream.Position = dataReference.Address.Offset; StreamUtil.Copy(resourceDataStream, outStream, dataReference.Size); }
private void ReadTagsSection(EndianReader reader, DataSerializationContext context, TagDeserializer deserializer) { var section = GetSectionHeader(reader, ModPackageSection.Tags); if (!GoToSectionHeaderOffset(reader, section)) { return; } var entry = new GenericSectionEntry(reader); var cacheCount = entry.Count; TagCachesStreams = new List <Stream>(); CacheNames = new List <string>(); for (int i = 0; i < cacheCount; i++) { var tagStream = new MemoryStream(); reader.BaseStream.Position = entry.TableOffset + 0x28 * i + section.Offset; var tableEntry = deserializer.Deserialize <CacheTableEntry>(context); CacheNames.Add(tableEntry.CacheName); reader.BaseStream.Position = tableEntry.Offset + section.Offset; if (section.Size > int.MaxValue) { throw new Exception("Tag cache size not supported"); } int size = (int)section.Size; byte[] data = new byte[size]; reader.Read(data, 0, size); tagStream.Write(data, 0, size); tagStream.Position = 0; TagCachesStreams.Add(tagStream); } }
public void InjectDds(TagSerializer serializer, TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream ddsStream) { var resource = bitmap.Resources[imageIndex].Resource; var newResource = (resource == null); ResourceSerializationContext resourceContext; BitmapTextureResourceDefinition definition; if (newResource) { // Create a new resource reference resource = new ResourceReference { DefinitionFixups = new List<ResourceDefinitionFixup>(), D3DObjectFixups = new List<D3DObjectFixup>(), Type = 1, // TODO: Map out this type enum instead of using numbers Unknown68 = 1 }; bitmap.Resources[imageIndex].Resource = resource; resourceContext = new ResourceSerializationContext(resource); definition = new BitmapTextureResourceDefinition { Texture = new D3DPointer<BitmapTextureResourceDefinition.BitmapDefinition> { Definition = new BitmapTextureResourceDefinition.BitmapDefinition() } }; } else { // Deserialize the old definition resourceContext = new ResourceSerializationContext(resource); definition = deserializer.Deserialize<BitmapTextureResourceDefinition>(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 ResourceDataReference(dataSize, new ResourceAddress(ResourceAddressType.Resource, 0)); texture.Width = (short)dds.Width; texture.Height = (short)dds.Height; texture.Depth = (sbyte)Math.Max(1, dds.Depth); texture.Levels = (sbyte)Math.Max(1, dds.MipMapCount); texture.Type = BitmapDdsFormatDetection.DetectType(dds); texture.D3DFormatUnused = (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) _resourceManager.Add(resource, ResourceLocation.Textures, ddsStream); else _resourceManager.Replace(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.Levels - 1); imageData.DataOffset = texture.Data.Address.Offset; imageData.DataSize = texture.Data.Size; imageData.Unknown15 = texture.Unknown35; }
public void InjectDds(TagSerializer serializer, TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream ddsStream) { var resource = bitmap.Resources[imageIndex].Resource; var newResource = (resource == null); ResourceSerializationContext resourceContext; BitmapTextureResourceDefinition definition; if (newResource) { // Create a new resource reference resource = new ResourceReference { DefinitionFixups = new List <ResourceDefinitionFixup>(), D3DObjectFixups = new List <D3DObjectFixup>(), Type = 1, // TODO: Map out this type enum instead of using numbers Unknown68 = 1 }; bitmap.Resources[imageIndex].Resource = resource; resourceContext = new ResourceSerializationContext(resource); definition = new BitmapTextureResourceDefinition { Texture = new D3DPointer <BitmapTextureResourceDefinition.BitmapDefinition> { Definition = new BitmapTextureResourceDefinition.BitmapDefinition() } }; } else { // Deserialize the old definition resourceContext = new ResourceSerializationContext(resource); definition = deserializer.Deserialize <BitmapTextureResourceDefinition>(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 ResourceDataReference(dataSize, new ResourceAddress(ResourceAddressType.Resource, 0)); texture.Width = (short)dds.Width; texture.Height = (short)dds.Height; texture.Depth = (sbyte)Math.Max(1, dds.Depth); texture.Levels = (sbyte)Math.Max(1, dds.MipMapCount); texture.Type = BitmapDdsFormatDetection.DetectType(dds); texture.D3DFormatUnused = (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) { _resourceManager.Add(resource, ResourceLocation.Textures, ddsStream); } else { _resourceManager.Replace(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.Levels - 1); imageData.DataOffset = texture.Data.Address.Offset; imageData.DataSize = texture.Data.Size; imageData.Unknown15 = texture.Unknown35; }
public override bool Execute(List <string> args) { if (args.Count != 2) { return(false); } var destDir = new DirectoryInfo(args[1]); if (!destDir.Exists) { WriteLine($"Destination cache directory does not exist: {destDir.FullName}"); return(false); } var destTagsFile = new FileInfo(Combine(destDir.FullName, "tags.dat")); if (!destTagsFile.Exists) { WriteLine($"Destination tag cache file does not exist: {destTagsFile.FullName}"); return(false); } var destStringIDsFile = new FileInfo(Combine(destDir.FullName, "string_ids.dat")); if (!destStringIDsFile.Exists) { WriteLine($"Destination string id cache file does not exist: {destStringIDsFile.FullName}"); return(false); } var destResourcesFile = new FileInfo(Combine(destDir.FullName, "resources.dat")); if (!destResourcesFile.Exists) { WriteLine($"Destination resource cache file does not exist: {destResourcesFile.FullName}"); return(false); } var destTexturesFile = new FileInfo(Combine(destDir.FullName, "textures.dat")); if (!destTexturesFile.Exists) { WriteLine($"Destination texture cache file does not exist: {destTexturesFile.FullName}"); return(false); } var destTexturesBFile = new FileInfo(Combine(destDir.FullName, "textures_b.dat")); if (!destTexturesBFile.Exists) { WriteLine($"Destination texture cache file does not exist: {destTexturesBFile.FullName}"); return(false); } var destAudioFile = new FileInfo(Combine(destDir.FullName, "audio.dat")); if (!destAudioFile.Exists) { WriteLine($"Destination audio cache file does not exist: {destAudioFile.FullName}"); return(false); } TagCache destTagCache; using (var stream = destTagsFile.OpenRead()) destTagCache = new TagCache(stream); DefinitionSet guessedVersion; var destVersion = Detect(destTagCache, out guessedVersion); if (destVersion == Unknown) { WriteLine($"Unrecognized target version! (guessed {GetVersionString(guessedVersion)})"); return(true); } WriteLine($"Destination cache version: {GetVersionString(destVersion)}"); StringIDCache destStringIDCache; using (var stream = destStringIDsFile.OpenRead()) destStringIDCache = new StringIDCache(stream, Create(destVersion)); var destResources = new ResourceDataManager(); destResources.LoadCachesFromDirectory(destDir.FullName); var srcResources = new ResourceDataManager(); srcResources.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); var destSerializer = new TagSerializer(destVersion); var destDeserializer = new TagDeserializer(destVersion); var destInfo = new OpenTagCache { Cache = destTagCache, CacheFile = destTagsFile, StringIDs = destStringIDCache, StringIDsFile = destStringIDsFile, Version = destVersion, Serializer = destSerializer, Deserializer = destDeserializer }; var destTag = ParseTagIndex(destInfo, args[0]); if (destTag == null || !destTag.IsInGroup(new Tag("mode"))) { WriteLine("Destination tag must be of group 'mode'."); return(false); } RenderModel destDefinition; using (var destStream = destInfo.OpenCacheRead()) { var context = new TagSerializationContext(destStream, destInfo.Cache, destInfo.StringIDs, destTag); destDefinition = destInfo.Deserializer.Deserialize <RenderModel>(context); } using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream()) { // First extract the model data srcResources.Extract(Definition.Geometry.Resource, inStream); // Now open source and destination vertex streams inStream.Position = 0; var inVertexStream = VertexStreamFactory.Create(Info.Version, inStream); var outVertexStream = VertexStreamFactory.Create(destInfo.Version, outStream); // Deserialize the definition data var resourceContext = new ResourceSerializationContext(Definition.Geometry.Resource); var definition = Info.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext); // Convert each vertex buffer foreach (var buffer in definition.VertexBuffers) { TagConverter.ConvertVertexBuffer(buffer.Definition, inStream, inVertexStream, outStream, outVertexStream); } // Copy each index buffer over foreach (var buffer in definition.IndexBuffers) { if (buffer.Definition.Data.Size == 0) { continue; } inStream.Position = buffer.Definition.Data.Address.Offset; buffer.Definition.Data.Address = new ResourceAddress(ResourceAddressType.Resource, (int)outStream.Position); var bufferData = new byte[buffer.Definition.Data.Size]; inStream.Read(bufferData, 0, bufferData.Length); outStream.Write(bufferData, 0, bufferData.Length); StreamUtil.Align(outStream, 4); } destInfo.Serializer.Serialize(resourceContext, definition); outStream.Position = 0; destResources.Replace(destDefinition.Geometry.Resource, outStream); } return(true); }
private void ReadFileEntries(EndianReader reader, ISerializationContext context, TagDeserializer deserializer) { Files = new Dictionary <string, Stream>(); var section = GetSectionHeader(reader, ModPackageSection.Files); if (!GoToSectionHeaderOffset(reader, section)) { return; } var fileTable = new GenericSectionEntry(reader); reader.BaseStream.Position = fileTable.TableOffset + section.Offset; for (int i = 0; i < fileTable.Count; i++) { var tableEntry = deserializer.Deserialize <FileTableEntry>(context); var stream = new MemoryStream(); StreamUtil.Copy(reader.BaseStream, stream, tableEntry.Size); Files.Add(tableEntry.Path, stream); } }
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; }
private void ReadTagNamesSection(EndianReader reader, DataSerializationContext context, TagDeserializer deserializer) { var section = GetSectionHeader(reader, ModPackageSection.TagNames); if (!GoToSectionHeaderOffset(reader, section)) { return; } var entry = new GenericSectionEntry(reader); var cacheCount = entry.Count; TagCacheNames = new List <Dictionary <int, string> >(); for (int i = 0; i < cacheCount; i++) { var nameDict = new Dictionary <int, string>(); reader.BaseStream.Position = entry.TableOffset + 0x8 * i + section.Offset; var tagNamesTableEntry = new GenericTableEntry(reader); if (tagNamesTableEntry.Size == 0) { throw new Exception("invalid tag name table entry size!"); } reader.BaseStream.Position = tagNamesTableEntry.Offset + section.Offset; var tagNamesHeader = new GenericSectionEntry(reader); reader.BaseStream.Position = tagNamesHeader.TableOffset + section.Offset; for (int j = 0; j < tagNamesHeader.Count; j++) { var tagNamesEntry = deserializer.Deserialize <ModPackageTagNamesEntry>(context); nameDict.Add(tagNamesEntry.TagIndex, tagNamesEntry.Name); } TagCacheNames.Add(nameDict); } }
public override bool Execute(List<string> args) { if (args.Count != 1) return false; var destDir = new DirectoryInfo(args[0]); if (!destDir.Exists) { Write("Destination directory does not exist. Create it? [y/n] "); var answer = ReadLine().ToLower(); if (answer.Length == 0 || !(answer.StartsWith("y") || answer.StartsWith("n"))) return false; if (answer.StartsWith("y")) destDir.Create(); else return false; } WriteLine($"Generating cache files in \"{destDir.FullName}\"..."); var destTagsFile = new FileInfo(Combine(destDir.FullName, "tags.dat")); Write($"Generating {destTagsFile.FullName}..."); using (var tagCacheStream = destTagsFile.Create()) using (var writer = new BinaryWriter(tagCacheStream)) { writer.Write((int)0); // padding writer.Write((int)0); // tag list offset writer.Write((int)0); // tag count writer.Write((int)0); // padding writer.Write((long)130713360239499012); // timestamp writer.Write((long)0); // padding } WriteLine("done."); var destStringIDsFile = new FileInfo(Combine(destDir.FullName, "string_ids.dat")); Write($"Generating {destStringIDsFile.FullName}..."); using (var stringIDCacheStream = destStringIDsFile.Create()) using (var writer = new BinaryWriter(stringIDCacheStream)) { writer.Write((int)0); // string count writer.Write((int)0); // data size } WriteLine("done."); var resourceCachePaths = new string[] { Combine(destDir.FullName, "audio.dat"), Combine(destDir.FullName, "resources.dat"), Combine(destDir.FullName, "textures.dat"), Combine(destDir.FullName, "textures_b.dat"), Combine(destDir.FullName, "video.dat") }; foreach (var resourceCachePath in resourceCachePaths) { Write($"Generating {resourceCachePath}..."); using (var resourceCacheStream = File.Create(resourceCachePath)) using (var writer = new BinaryWriter(resourceCacheStream)) { writer.Write((int)0); // padding writer.Write((int)0); // table offset writer.Write((int)0); // resource count writer.Write((int)0); // padding } WriteLine("done."); } var dependencies = new Dictionary<int, TagInstance>(); LoadTagDependencies(0, ref dependencies); LoadTagDependencies(0x16, ref dependencies); LoadTagDependencies(0x27D7, ref dependencies); var destResourcesFile = new FileInfo(Combine(destDir.FullName, "resources.dat")); if (!destResourcesFile.Exists) { WriteLine($"Destination resource cache file does not exist: {destResourcesFile.FullName}"); return false; } var destTexturesFile = new FileInfo(Combine(destDir.FullName, "textures.dat")); if (!destTexturesFile.Exists) { WriteLine($"Destination texture cache file does not exist: {destTexturesFile.FullName}"); return false; } var destTexturesBFile = new FileInfo(Combine(destDir.FullName, "textures_b.dat")); if (!destTexturesBFile.Exists) { WriteLine($"Destination texture cache file does not exist: {destTexturesBFile.FullName}"); return false; } var destAudioFile = new FileInfo(Combine(destDir.FullName, "audio.dat")); if (!destAudioFile.Exists) { WriteLine($"Destination audio cache file does not exist: {destAudioFile.FullName}"); return false; } TagCache destTagCache; using (var stream = destTagsFile.OpenRead()) destTagCache = new TagCache(stream); DefinitionSet guessedVersion; var destVersion = Detect(destTagCache, out guessedVersion); if (destVersion == Unknown) { WriteLine($"Unrecognized target version! (guessed {GetVersionString(guessedVersion)})"); return true; } WriteLine($"Destination cache version: {GetVersionString(destVersion)}"); StringIDCache destStringIDCache; using (var stream = destStringIDsFile.OpenRead()) destStringIDCache = new StringIDCache(stream, Create(destVersion)); var destResources = new ResourceDataManager(); destResources.LoadCachesFromDirectory(destDir.FullName); var srcResources = new ResourceDataManager(); srcResources.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); var destSerializer = new TagSerializer(destVersion); var destDeserializer = new TagDeserializer(destVersion); var destInfo = new OpenTagCache { Cache = destTagCache, CacheFile = destTagsFile, StringIDs = destStringIDCache, StringIDsFile = destStringIDsFile, Version = destVersion, Serializer = destSerializer, Deserializer = destDeserializer }; using (Stream srcStream = Info.OpenCacheRead(), destStream = destInfo.OpenCacheReadWrite()) { var maxDependency = dependencies.Keys.Max(); for (var i = 0; i <= maxDependency; i++) { var srcTag = Info.Cache.Tags[i]; if (srcTag == null) { destInfo.Cache.AllocateTag(); continue; } var srcData = Info.Cache.ExtractTagRaw(srcStream, srcTag); var destTag = destInfo.Cache.AllocateTag(srcTag.Group); destInfo.Cache.SetTagDataRaw(destStream, destTag, srcData); srcData = new byte[0]; } } WriteLine($"Done generating cache files in \"{destDir.FullName}\"."); return true; }
public override bool Execute(List <string> args) { if (args.Count != 1) { return(false); } var destDir = new DirectoryInfo(args[0]); if (!destDir.Exists) { Write("Destination directory does not exist. Create it? [y/n] "); var answer = ReadLine().ToLower(); if (answer.Length == 0 || !(answer.StartsWith("y") || answer.StartsWith("n"))) { return(false); } if (answer.StartsWith("y")) { destDir.Create(); } else { return(false); } } WriteLine($"Generating cache files in \"{destDir.FullName}\"..."); var destTagsFile = new FileInfo(Combine(destDir.FullName, "tags.dat")); Write($"Generating {destTagsFile.FullName}..."); using (var tagCacheStream = destTagsFile.Create()) using (var writer = new BinaryWriter(tagCacheStream)) { writer.Write((int)0); // padding writer.Write((int)0); // tag list offset writer.Write((int)0); // tag count writer.Write((int)0); // padding writer.Write((long)130713360239499012); // timestamp writer.Write((long)0); // padding } WriteLine("done."); var destStringIDsFile = new FileInfo(Combine(destDir.FullName, "string_ids.dat")); Write($"Generating {destStringIDsFile.FullName}..."); using (var stringIDCacheStream = destStringIDsFile.Create()) using (var writer = new BinaryWriter(stringIDCacheStream)) { writer.Write((int)0); // string count writer.Write((int)0); // data size } WriteLine("done."); var resourceCachePaths = new string[] { Combine(destDir.FullName, "audio.dat"), Combine(destDir.FullName, "resources.dat"), Combine(destDir.FullName, "textures.dat"), Combine(destDir.FullName, "textures_b.dat"), Combine(destDir.FullName, "video.dat") }; foreach (var resourceCachePath in resourceCachePaths) { Write($"Generating {resourceCachePath}..."); using (var resourceCacheStream = File.Create(resourceCachePath)) using (var writer = new BinaryWriter(resourceCacheStream)) { writer.Write((int)0); // padding writer.Write((int)0); // table offset writer.Write((int)0); // resource count writer.Write((int)0); // padding } WriteLine("done."); } var dependencies = new Dictionary <int, TagInstance>(); LoadTagDependencies(0, ref dependencies); LoadTagDependencies(0x16, ref dependencies); LoadTagDependencies(0x27D7, ref dependencies); var destResourcesFile = new FileInfo(Combine(destDir.FullName, "resources.dat")); if (!destResourcesFile.Exists) { WriteLine($"Destination resource cache file does not exist: {destResourcesFile.FullName}"); return(false); } var destTexturesFile = new FileInfo(Combine(destDir.FullName, "textures.dat")); if (!destTexturesFile.Exists) { WriteLine($"Destination texture cache file does not exist: {destTexturesFile.FullName}"); return(false); } var destTexturesBFile = new FileInfo(Combine(destDir.FullName, "textures_b.dat")); if (!destTexturesBFile.Exists) { WriteLine($"Destination texture cache file does not exist: {destTexturesBFile.FullName}"); return(false); } var destAudioFile = new FileInfo(Combine(destDir.FullName, "audio.dat")); if (!destAudioFile.Exists) { WriteLine($"Destination audio cache file does not exist: {destAudioFile.FullName}"); return(false); } TagCache destTagCache; using (var stream = destTagsFile.OpenRead()) destTagCache = new TagCache(stream); DefinitionSet guessedVersion; var destVersion = Detect(destTagCache, out guessedVersion); if (destVersion == Unknown) { WriteLine($"Unrecognized target version! (guessed {GetVersionString(guessedVersion)})"); return(true); } WriteLine($"Destination cache version: {GetVersionString(destVersion)}"); StringIDCache destStringIDCache; using (var stream = destStringIDsFile.OpenRead()) destStringIDCache = new StringIDCache(stream, Create(destVersion)); var destResources = new ResourceDataManager(); destResources.LoadCachesFromDirectory(destDir.FullName); var srcResources = new ResourceDataManager(); srcResources.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); var destSerializer = new TagSerializer(destVersion); var destDeserializer = new TagDeserializer(destVersion); var destInfo = new OpenTagCache { Cache = destTagCache, CacheFile = destTagsFile, StringIDs = destStringIDCache, StringIDsFile = destStringIDsFile, Version = destVersion, Serializer = destSerializer, Deserializer = destDeserializer }; using (Stream srcStream = Info.OpenCacheRead(), destStream = destInfo.OpenCacheReadWrite()) { var maxDependency = dependencies.Keys.Max(); for (var i = 0; i <= maxDependency; i++) { var srcTag = Info.Cache.Tags[i]; if (srcTag == null) { destInfo.Cache.AllocateTag(); continue; } var srcData = Info.Cache.ExtractTagRaw(srcStream, srcTag); var destTag = destInfo.Cache.AllocateTag(srcTag.Group); destInfo.Cache.SetTagDataRaw(destStream, destTag, srcData); srcData = new byte[0]; } } WriteLine($"Done generating cache files in \"{destDir.FullName}\"."); return(true); }
public override bool Execute(List<string> args) { if (args.Count != 2) return false; // // Verify the xbox scenario_structure_bsp tag // var bspName = args[0]; var newTagIndex = ArgumentParser.ParseTagIndex(Info, args[1]); CacheFile.IndexItem item = null; Console.WriteLine("Verifying xbox tag..."); foreach (var tag in BlamCache.IndexItems) { if (tag.ClassCode == "sbsp" && tag.Filename == bspName) { item = tag; break; } } if (item == null) { Console.WriteLine("Xbox tag does not exist: " + args[0]); return false; } // // Load the xbox scenario_structure_bsp tag // var xboxContext = new XboxSerializationContext(item); var deserializer = new TagDeserializer(BlamCache.Version); var sbsp = deserializer.Deserialize<ScenarioStructureBsp>(xboxContext); // blank resources that will be generated/converted from ODST automatically later var Resource1 = new ResourceReference() { Index = 1337, Owner = newTagIndex }; // render geo 1 var Resource2 = new ResourceReference() { Index = 1337, Owner = newTagIndex }; // render geo 2 var Resource3 = new ResourceReference() { Index = 1337, Owner = newTagIndex }; // collision BSP tag data var Resource4 = new ResourceReference() { Index = 1337, Owner = newTagIndex }; // other tag data and shit (unknown raw 3rd in halo 3, etc.) // Set the resource refs in the tag to the blank ones we just made sbsp.Geometry.Resource = Resource1; sbsp.Geometry2.Resource = Resource2; sbsp.CollisionBSPResource = Resource3; sbsp.Resource4 = Resource4; using (var cacheStream = Info.CacheFile.Open(FileMode.Open, FileAccess.ReadWrite)) { Console.WriteLine("Writing ElDewrito tag to tag " + newTagIndex+"..."); var context = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, Info.Cache.Tags[newTagIndex.Index]); Info.Serializer.Serialize(context, sbsp); } return true; }