/// <summary> /// Stream the field from a buffer /// </summary> /// <param name="input"></param> public override void Read(IO.EndianReader input) { if (StringType == StringType.Normal) { Value = input.ReadTagString(); } else if (StringType == StringType.Unicode) { Value = input.ReadUnicodeString(Length); } else if (StringType == StringType.Ascii) { Value = input.ReadAsciiString(Length); } else if (StringType == StringType.Halo1Profile) { Value = input.ReadUnicodeString(12); } else if (StringType == StringType.Halo2Profile) { Value = input.ReadUnicodeString(16); } else if (StringType == StringType.CString) { Value = input.ReadCString(); } }
/// <summary> /// Stream the field header data from a tag stream /// </summary> /// <param name="ts"></param> /// <exception cref="Exceptions.InvalidStringId"></exception> public override void ReadHeader(IO.ITagStream ts) { IO.EndianReader s = ts.GetInputStream(); headerOffset = s.PositionUnsigned; // nifty for debugging OwnerId = ts.OwnerId; string value = null; try { if (fieldType == FieldType.OldStringId && ts.Flags.Test(IO.ITagStreamFlags.Halo2OldFormat_StringId)) { value = s.ReadAsciiString(28); // max of 28 characters in the string id in old builds } var owner = Program.GetTagIndex(OwnerId); Handle.Read(s, owner.StringIds.Definition.Description); if (value != null /*&& Handle != Blam.StringID.Null*/) { owner.StringIds.TryAndGetStringId(value, out Handle); Handle = new Blam.StringId(Handle.Description, Handle.Index, Handle.Length, -1); // HACK used to tell Read that we already read the string data (as this is an old halo 2 tag) } } catch (Exception ex) { throw new Exceptions.InvalidStringId(ex, base.headerOffset, uint.MaxValue, ts, Handle.Length, value); } }
/// <summary></summary> /// <param name="offsets">Buffer containing the offsets of the string values in <paramref name="buffer"/></param> /// <param name="buffer">Buffer containing the string values</param> /// <param name="is_packed">If true, reads the strings as null terminated strings, else as 128 character strings</param> /// <param name="count">Number of dynamic strings for <paramref name="set_index"/></param> /// <remarks> /// Doesn't use <paramref name="offsets"/> for reading the strings, assumes the strings are in sequential order in <paramref name="buffer"/>. /// /// Ignores <see cref="IsReadOnly"/>; will always add new IDs. /// </remarks> public void FromDebugStream(IO.EndianReader offsets, IO.EndianReader buffer, bool is_packed, int count) { if (count <= 0) { throw new ArgumentOutOfRangeException("count", count, "Must be greater than zero"); } // Assume the strings are in sequential order in [buffer] offsets.Seek(sizeof(uint) * count, System.IO.SeekOrigin.Current); int set_index = InitialId.Set, initial_index = InitialId.Index; InitializeSet(count); for (int x = 0; x < count; x++) { var str = is_packed ? buffer.ReadCString() : buffer.ReadAsciiString(128); var sid = mOwner.Definition.Description.Generate(initial_index + x, str.Length, set_index); m_set.Set.Add(sid, str); int hc = str.GetHashCode(); StringId first_sid; if (m_set.SetLookup.TryGetValue(hc, out first_sid)) { Debug.Warn.If(false, "Hash collision! '{0}' ({1}) collides with '{2}' ({3})", m_set.Set[first_sid], first_sid.ToString(), str, sid.ToString()); } else { m_set.SetLookup.Add(str.GetHashCode(), sid); } } }
public override void Read(IO.EndianReader s) { int k_local_sizeof = Blam.CacheFile.ValidateHeader(s, kSizeOf); s.Seek(4); version = s.ReadInt32(); if (version != 11 && version != 12) { throw new InvalidCacheFileException(s.FileName); } fileLength = s.ReadInt32(); s.ReadInt32(); tagIndexAddress = s.ReadUInt32(); memoryBufferOffset = s.ReadInt32(); memoryBufferSize = s.ReadInt32(); sourceFile = s.ReadAsciiString(256); build = s.ReadTagString(); cacheType = (Blam.CacheType)s.ReadInt16(); sharedType = (Cache.SharedType)s.ReadInt16(); s.ReadBool(); s.ReadBool(); // false if it belongs to a untracked build s.ReadBool(); // PATCH: this is '3' in main menu patches s.ReadByte(); // appears to be an ODST-only field s.ReadInt32(); s.ReadInt32(); s.ReadInt32(); s.ReadInt32(); s.ReadInt32(); #region string id table // 0x158 stringIdsCount = s.ReadInt32(); stringIdsBufferSize = s.ReadInt32(); stringIdIndicesOffset = s.ReadInt32(); stringIdsBufferOffset = s.ReadInt32(); #endregion #region filetimes? // pretty sure this is a flags field // used to tell which of the following 64bit values // are used. Damn sure this are FILETIME structures, but // hex workshop doesn't like them so I can't be for sure... needsShared = s.ReadInt32() != 0; // just a little 'hack' if you will. if zero, the map is self reliant, so no worries Filetime.dwHighDateTime = s.ReadInt32(); Filetime.dwLowDateTime = s.ReadInt32(); if (s.ReadInt32() != 0) { flags.Add(Halo3.CacheHeaderFlags.DependsOnMainMenu); } s.ReadInt32(); if (s.ReadInt32() != 0) { flags.Add(Halo3.CacheHeaderFlags.DependsOnShared); } s.ReadInt32(); if (s.ReadInt32() != 0) { flags.Add(Halo3.CacheHeaderFlags.DependsOnCampaign); } s.ReadInt32(); #endregion name = s.ReadTagString(); s.ReadInt32(); scenarioPath = s.ReadAsciiString(256); // PATCH: this is -1 in main menu patches s.ReadInt32(); // minor version, normally not used #region tag paths tagNamesCount = s.ReadInt32(); tagNamesBufferOffset = s.ReadInt32(); // cstring buffer tagNamesBufferSize = s.ReadInt32(); // cstring buffer total size in bytes tagNameIndicesOffset = s.ReadInt32(); TagsUnknown1Count = s.ReadInt32(); TagsUnknown1Offset = s.ReadInt32(); // PATCH: zero for non-patch data TagsUnknown2Count = s.ReadInt32(); TagsUnknown2Offset = s.ReadInt32(); #endregion checksum = s.ReadUInt32(); // 0x2D4 s.Seek(32, System.IO.SeekOrigin.Current); // these bytes are always the same. first 8 changed in Halo4 baseAddress = s.ReadUInt32(); // expected base address xdkVersion = s.ReadInt32(); // xdk version #region memory partitions // 0x300 // memory partitions memoryPartitions = new Partition[6]; memoryPartitions[0].BaseAddress = s.ReadUInt32(); // cache resource buffer memoryPartitions[0].Size = s.ReadInt32(); // readonly memoryPartitions[1].BaseAddress = s.ReadUInt32(); // cache gestalt resource buffer memoryPartitions[1].Size = s.ReadInt32(); memoryPartitions[2].BaseAddress = s.ReadUInt32(); // global tags buffer (cache sound tags likes this memory space too) memoryPartitions[2].Size = s.ReadInt32(); memoryPartitions[3].BaseAddress = s.ReadUInt32(); // shared tag blocks? (havok data likes this memory space too) memoryPartitions[3].Size = s.ReadInt32(); memoryPartitions[4].BaseAddress = s.ReadUInt32(); // address memoryPartitions[4].Size = s.ReadInt32(); // readonly memoryPartitions[5].BaseAddress = s.ReadUInt32(); // map tags buffer memoryPartitions[5].Size = s.ReadInt32(); #endregion int count = s.ReadInt32(); s.Seek(4 + 8, System.IO.SeekOrigin.Current); // these bytes are always the same // if there is a hash in the header, this is the ONLY // place where it can be s.Seek(20 /*SHA1*/ + 40 + 256 /*RSA*/, System.IO.SeekOrigin.Current); // ??? // 0x47C cacheInterop.Read(s); cacheInterop.PostprocessForCacheRead(k_local_sizeof); s.Seek(16, System.IO.SeekOrigin.Current); // GUID?, these bytes are always the same. ODST is different from Halo 3 // PATCH: main menu patches have a single entry (where stock has none) #region blah 1 // 0x4AC // campaign has a shit load of these // but shared doesn't nor mainmenu // I compared the sc110 french and english and both have the SAME counts and element data. So // I don't think this is a hash or something. At least, if it is, it's not runtime relative so // nothing we have to worry about s.ReadInt16(); // I've only seen this be two different values (besides zero). count = s.ReadInt16(); // I think the above specifies the size of the structure this count represents? s.ReadInt32(); // seems to always be zero CompressionGuid = new Guid(s.ReadBytes(16)); s.Seek(count * 28, System.IO.SeekOrigin.Current); // seek past the elements // dword // long // buffer [0x14] (probably a sha1 hash) s.Seek((3600 - count) * 28, System.IO.SeekOrigin.Current); // seek past the unused elements #endregion // PATCH: main menu patches have a count of '1' but doesn't appear to have #region blah 2 s.Seek(sizeof(uint) + 0x4E9C, System.IO.SeekOrigin.Current); #if false { // 0x18E94 // going to punt and just assume there is a max count of 13 of these possible // maybe related to bsp\'zones'? const int blah2_sizeof = 0x60C; count = (int)(s.ReadUInt32() >> 24); // did someone forget to f*****g byte swap something? s.Seek(count * blah2_sizeof, System.IO.SeekOrigin.Current); // seek past the elements s.Seek((13 - count) * blah2_sizeof, System.IO.SeekOrigin.Current); // seek past the unused elements } #endif #endregion s.Seek(712 + sizeof(uint), System.IO.SeekOrigin.Current); ReadPostprocessForInterop(); if (!cacheInterop.IsNull) { int debug_mask = (int)cacheInterop[CacheSectionType.Debug].AddressMask; if (TagsUnknown1Offset != 0) { TagsUnknown1Offset -= debug_mask; } if (TagsUnknown2Offset != 0) { TagsUnknown2Offset -= debug_mask; } } ReadPostprocessForBaseAddresses(s); }