static void FixupTagInstanceHeaderName(CacheFile cache, CacheItem instance, int name_offset, IO.EndianReader s) { s.Seek(name_offset); instance.TagNameOffset = s.PositionUnsigned; instance.ReferenceName = cache.References.AddOptimized(instance.GroupTag, s.ReadCString()); }
public override void Write(BlamLib.IO.EndianWriter s) { CacheFile cf = s.Owner as CacheFile; if (cf.EngineVersion == BlamVersion.Halo2_PC) { return; } if (cf.EngineVersion == BlamVersion.Halo2_Alpha) { return; } s.WriteTag((char[])MiscGroups.head); s.Write(8); s.Write(fileLength); s.Write(0); s.Write(offsetToIndex); s.Write(indexStreamSize); s.Write(tagBufferSize); s.Write(0); // needs to be calc'd s.Write(new byte[256]); s.Write("02.09.27.09809", false); s.Write((int)cacheType); s.Write(0); // needs to be calc'd s.Write(0); // needs to be calc'd s.Write(0); s.Write(0); s.Write(0); // needs to be calc'd s.Write(0); // needs to be calc'd s.Write(0); // needs to be calc'd s.Write(/*stringIdsBufferAlignedOffset*/ 0); s.Write(stringIdsCount); s.Write(stringIdsBufferSize); s.Write(stringIdIndicesOffset); s.Write(stringIdsBufferOffset); s.Write(0); // 4 bools s.Write(Filetime.dwHighDateTime); s.Write(Filetime.dwHighDateTime); // mainmenu s.Write(SharedFiletimes[0].dwHighDateTime); s.Write(SharedFiletimes[0].dwHighDateTime); // shared s.Write(SharedFiletimes[1].dwHighDateTime); s.Write(SharedFiletimes[1].dwHighDateTime); // shared sp s.Write(SharedFiletimes[2].dwHighDateTime); s.Write(SharedFiletimes[2].dwHighDateTime); s.Write(name, false); s.Write(0); s.Write(scenarioPath, 256); s.Write(Convert.ToInt32(needsShared)); s.Write(tagNamesCount); s.Write(tagNamesBufferOffset); s.Write(tagNamesBufferSize); s.Write(tagNameIndicesOffset); s.Write(0); // checksum s.Write(new byte[1320]); s.WriteTag((char[])MiscGroups.foot); }
public override void Read(BlamLib.IO.EndianReader s) { CacheFile cache = s.Owner as CacheFile; tagsOffset = (uint)(cache.Header.OffsetToIndex + cache.HeaderHalo2.IndexStreamSize); bool is_alpha = cache.EngineVersion == BlamVersion.Halo2_Alpha; bool is_echo = cache.EngineVersion == BlamVersion.Halo2_Epsilon; bool is_pc = cache.EngineVersion == BlamVersion.Halo2_PC; bool is_mp = cache.HeaderHalo2.CacheType == CacheType.Multiplayer; Managers.BlamDefinition bdef = Program.GetManager(cache.EngineVersion); uint tags_addressmask; if (!is_pc) { cache.AddressMask = bdef[cache.EngineVersion].CacheTypes.BaseAddress - (uint)cache.Header.OffsetToIndex; tags_addressmask = (uint)(cache.Header.OffsetToIndex + cache.HeaderHalo2.IndexStreamSize); } else { // pc maps use virtual addresses which are actually offsets relative to // the start of the tag memory buffer. since these are offsets and not actually // addresses which we would normally mask the base off of, we have to do some // number magic so our subtraction operations actually end up working in reverse // to get the correct file offset tags_addressmask = 0 - (uint)cache.Header.OffsetToIndex; cache.AddressMask = tags_addressmask; } #region version dependant loading if (is_alpha) { groupTagsAddress = address = s.ReadUInt32(); groupTagsCount = 0; scenario.Read(s); s.ReadInt32(); // crc tagCount = s.ReadInt32(); items = new CacheItem[tagCount]; s.ReadInt32(); // 'tags' } else if (is_pc) { groupTagsAddress = s.ReadPointer(); // offset (relative to the tag index offset) groupTagsCount = s.ReadInt32(); uint offset = s.ReadPointer(); // offset (relative to the tag index offset) to the tag entries scenario.Read(s); gameGlobals.Read(s); s.ReadInt32(); // crc tagCount = s.ReadInt32(); items = new CacheItem[tagCount]; s.ReadInt32(); // 'tags' s.Seek(offset /*- 32, System.IO.SeekOrigin.Current*/); // go to the first tag entry } else { groupTagsAddress = s.ReadUInt32(); groupTagsCount = s.ReadInt32(); address = s.ReadUInt32(); scenario.Read(s); gameGlobals.Read(s); s.ReadInt32(); // crc tagCount = s.ReadInt32(); items = new CacheItem[tagCount]; s.ReadInt32(); // 'tags' s.Seek(groupTagsCount * 12, System.IO.SeekOrigin.Current); // go to the first tag entry } this.groupTagsOffset = this.groupTagsAddress - s.BaseAddress; #endregion CacheItem item; uint temp_pos = 0; uint sbsp_offset = 0; DatumIndex[] ltmps = null; Tags.scenario_structure_bsp_reference_block bsp_block = new Tags.scenario_structure_bsp_reference_block(); if (is_pc) // MP maps need this adjustment { cache.AddressMask = tags_addressmask += cache.HeaderHalo2.PcFields.VirtualAddress; } for (int x = 0; x < items.Length; x++) { item = new CacheItem(); items[x] = item; if (is_alpha) { item.ReadAlpha(s); } else { item.Read(s); } if (item.Location == ItemLocation.Unknown) { items[x] = CacheItem.Null; } else if (is_pc && item.HasExternalData) { HasExternalTags = true; } } if (!is_pc) { // While the tag definitions come right after the tag header in a cache file, when // finally loaded into game memory this isn't the case. The 'stream' size of the tag // header is how much actual memory is used by map's generated the tag header, but the // map's tag header may not utilize the entire memory space dedicated to it in game memory. // So, the game would read the tag header data using the offset and 'stream' size data from // the cache header, then it would seek to offset+stream_size to get to the tag definitions // which it would then read into game memory at the memory location defined by 'address' tags_addressmask = items[0].Address - tags_addressmask; cache.AddressMask = tags_addressmask; for (int x = 0; x < items.Length; x++) { item = items[x]; item.Offset = (int)(item.Address - tags_addressmask); #region on scnr tag if (!is_alpha && !is_echo && TagGroups.scnr.ID == item.GroupTag.ID) { temp_pos = s.PositionUnsigned; //if (is_alpha || is_echo) // s.Seek(item.Offset + 828); //else s.Seek(item.Offset + 528); bspTags = new Item[s.ReadInt32()]; sbsp_offset = s.ReadPointer(); ltmps = new DatumIndex[bspTags.Length]; s.Seek(temp_pos); } #endregion #region on sbsp tag else if (!is_alpha && !is_echo && TagGroups.sbsp.ID == item.GroupTag.ID) { temp_pos = s.PositionUnsigned; s.Seek(sbsp_offset + (uint)(bspCount * Halo2.Tags.scenario_structure_bsp_reference_block.kRuntimeSizeOf)); bspTags[bspCount] = item; bsp_block.Read(cache); if (bsp_block.RuntimeOffset != 0) { item.Offset = bsp_block.RuntimeOffset; item.Size = bsp_block.RuntimeSize; item.Address = (uint)bsp_block.RuntimeAddress.Value; //cache.BspAddressMasks.Add((uint)(item.Address - item.Offset)); } ltmps[bspCount] = bsp_block.Lightmap.Datum; item.BspIndex = bspCount++; s.Seek(temp_pos); } #endregion } } #region alpha tag name code if (is_alpha) { // following the tag datums in alpha builds is the tag names buffer foreach (Halo2.CacheItem ci in items) { ci.TagNameOffset = s.PositionUnsigned; ci.ReferenceName = cache.References.AddOptimized(ci.GroupTag, s.ReadCString()); } } #endregion #region retail tag name & bsp offset fixup code else { // Build the absolute tag name offsets s.Seek(cache.HeaderHalo2.TagNameIndicesOffset); int[] offsets = new int[tagCount]; for (int x = 0; x < offsets.Length; x++) { int offset = s.ReadInt32(); // Offset will be -1 if the tag in question is 'null' if (offset != -1) { offset += cache.HeaderHalo2.TagNamesBufferOffset; } offsets[x] = offset; } // Fixup all tag instances which are named for (int x = 0; x < tagCount; x++) { if (offsets[x] != -1) { FixupTagInstanceHeaderName(cache, items[x], offsets[x], s); } } // PC maps store all zones in the tag memory, they don't need to swap out and thus don't // need any fix ups (durrr, PCs have loltons of RAM) if (!is_pc && !is_echo) { var head = new Halo2.Tags.scenario_structure_bsps_header(); foreach (CacheItem tmp_item in bspTags) { s.Seek(tmp_item.Offset); head.Read(cache); // bsp uint bsp_address_mask = head.FixupBspInstanceHeader(tmp_item, s.Position); cache.BspAddressMasks.Add(bsp_address_mask); // ltmp DatumIndex ltmp_datum = ltmps[tmp_item.BspIndex]; if (ltmp_datum != DatumIndex.Null) { head.FixupLightmapInstanceHeader(this.items[ltmp_datum.Index], tmp_item); } } } } #endregion }
public override void Read(BlamLib.IO.EndianReader s) { bool is_alpha; bool is_pc = false; Blam.CacheFile.ValidateHeader(s, 0x800); #region figure out if this is a alpha map s.Seek(0x120); is_alpha = s.ReadTagString() == "02.06.28.07902"; #endregion #region figure out if this is a pc map if (!is_alpha) { s.Seek(0x24); int test = s.ReadInt32(); if (test == -1 || (test != 0 && test < s.Length)) { is_pc = true; } } #endregion s.Seek(4); version = s.ReadInt32(); fileLength = s.ReadInt32(); s.ReadInt32(); offsetToIndex = s.ReadInt32(); indexStreamSize = s.ReadInt32(); tagBufferSize = s.ReadInt32(); // size of the tag data, excluding the bsp data s.ReadInt32(); // max size: 0x3200000, indexStreamSize+tagBufferSize if (is_pc) { PcFields.VirtualAddress = s.ReadUInt32(); // virtual base address PcFields.TagDependencyGraphOffset = s.ReadInt32(); // buffer offset (starts right before tag index, 512 byte aligned). not shared? should equal -1 PcFields.TagDependencyGraphSize = s.ReadUInt32(); // buffer size. not shared? should be zero. Max: 0x100000 } sourceFile = s.ReadAsciiString(256); build = s.ReadTagString(); cacheType = (Blam.CacheType)s.ReadInt32(); // SP Filesize Max: 0x11800000 // MP Filesize Max: 0x5000000 // Mainmenu Filesize Max: 0x20800000 // Shared Filesize Max: 0xB400000 // Single Player Shared Filesize Max: 0x20800000 s.ReadInt32(); // cache resource (tag) crc s.ReadInt32(); // boolean, set to true in pc when building cache s.ReadInt32(); s.ReadInt32(); s.ReadInt32(); // count of some sort? only on the xbox // offset, then size // (size+0xFFF) & 0xFFFFF000 // test that with 0x1FF, if true, or with 0x1FF then add 1 s.ReadInt32(); // begins with 0x200 bytes of a chunk of the tag filenames table and ends with the same s.ReadInt32(); // has something to do with bitmaps. the bigger the file, the bigger this is #region string id // each string this buffer is aligned to 128 characters (the max length + 1 of a string_id) /*stringIdsBufferAlignedOffset = */ s.ReadInt32(); stringIdsCount = s.ReadInt32(); stringIdsBufferSize = s.ReadInt32(); stringIdIndicesOffset = s.ReadInt32(); stringIdsBufferOffset = s.ReadInt32(); #endregion s.ReadInt32(); // 4 bools Filetime.dwHighDateTime = s.ReadInt32(); Filetime.dwLowDateTime = s.ReadInt32(); /*MainmenuFiletime.dwHighDateTime*/ SharedFiletimes[0].dwHighDateTime = s.ReadInt32(); /*MainmenuFiletime.dwLowDateTime = */ SharedFiletimes[0].dwLowDateTime = s.ReadInt32(); /*SharedFiletime.dwHighDateTime*/ SharedFiletimes[1].dwHighDateTime = s.ReadInt32(); /*SharedFiletime.dwLowDateTime*/ SharedFiletimes[1].dwLowDateTime = s.ReadInt32(); /*SharedSingleplayerFiletime.dwHighDateTime*/ SharedFiletimes[2].dwHighDateTime = s.ReadInt32(); /*SharedSingleplayerFiletime.dwLowDateTime*/ SharedFiletimes[2].dwLowDateTime = s.ReadInt32(); name = s.ReadTagString(); s.ReadInt32(); // dword, but unused scenarioPath = s.ReadAsciiString(256); // minor version needsShared = s.ReadInt32() == 1; // doesn't exist on the pc #region tag names tagNamesCount = s.ReadInt32(); // not used in alpha tagNamesBufferOffset = s.ReadInt32(); // not used in alpha tagNamesBufferSize = s.ReadInt32(); // not used in alpha tagNameIndicesOffset = s.ReadInt32(); // not used in alpha #endregion if (is_pc) { // s_cache_language_pack PcFields.LanguagePacksOffset = s.ReadInt32(); // offset, shared database = -1 // sizeof(s_cache_language_pack) * LanguageType.kMax PcFields.LanguagePacksSizeOf = s.ReadUInt32(); // sizeof, shared database = 0 // secondary sound gestalt PcFields.SecondarySoundGestalt.Read(s); // shared database = -1 // fast geometry load region PcFields.FastLoadGeometryBlockOffset = s.ReadInt32(); // offset to a cache block resource PcFields.FastLoadGeometryBlockSize = s.ReadUInt32(); // sizeof entire cache block resources partition. max size: 0x4000000 checksum = s.ReadUInt32(); // another xor checksum // sbsp // - data 0x28C: global_structure_physics_struct->MoppCode // - data 0x2BC: global_structure_physics_struct->BreakableSurfacesMoppCode // - block 0xD4: structure_bsp_cluster_block // * data 0xC4: "Collision mopp Code" // * block 0x54: structure_bsp_cluster_data_block_new // - data 0x3C: global_geometry_section_struct->"Visibility mopp Code" // phmo // - data 0xE8: "mopp codes" // coll // - block 0x28: collision_model_region_block // * block 0x04: collision_model_permutation_block // - block 0x10: collision_bsp_physics_block // * data 0x64: "mopp Code Data" // mode // - block 0x30: render_model_section_block // * block 0x34: render_model_section_data_block // - data 0x3C: global_geometry_section_struct->"Visibility mopp Code" // ltmp // - block 0x80: structure_lightmap_group_block // * block 0x30: lightmap_geometry_section_block // - block 0x54: lightmap_geometry_section_cache_data_block // * data 0x3C: global_geometry_section_struct->"Visibility mopp Code" // * block 0x48: lightmap_geometry_section_block // - block 0x54: lightmap_geometry_section_cache_data_block // * data 0x3C: global_geometry_section_struct->"Visibility mopp Code" PcFields.MoppCodesChecksum = s.ReadUInt32(); // shared databases = 0 s.Seek(1284 + sizeof(uint), System.IO.SeekOrigin.Current); } else { checksum = s.ReadUInt32(); s.Seek(1320 + sizeof(uint), System.IO.SeekOrigin.Current); } CacheFile cf = s.Owner as CacheFile; if (is_pc) { cf.EngineVersion = BlamVersion.Halo2_PC; } else if (build == "02.08.28.09214") { cf.EngineVersion = BlamVersion.Halo2_Epsilon; } else if (is_alpha) { cf.EngineVersion = BlamVersion.Halo2_Alpha; } else { cf.EngineVersion = BlamVersion.Halo2_Xbox; } }