void InitializeBspTags(IO.EndianReader s, Blam.CacheFile cache) { // Seek to the scenario's scenario_structure_bsps_block tag_block field s.Seek(items[0].Offset + 1444, System.IO.SeekOrigin.Begin); bspTags = new Item[s.ReadInt32()]; uint sbsp_offset = s.ReadPointer(); // Seek to the scenario_structure_bsps_block definitions s.Seek(sbsp_offset, System.IO.SeekOrigin.Begin); DatumIndex di = new DatumIndex(); CacheItemBase item = null; // Process each definition's runtime data for (int x = 0; x < bspTags.Length; x++) { s.Seek(28, System.IO.SeekOrigin.Current); di.Read(s); item = items[di.Index]; bspTags[x] = item as CacheItemBase; // Seek back to the beginning of the definition so the following stream code works s.Seek(-Halo1.Tags.scenario_structure_bsps_block.kSizeOf, System.IO.SeekOrigin.Current); // We're actually selectively reading scenario_structure_bsps_block fields here // The offset actually points to the bsp header, and the bsp comes after that header item.Offset = s.ReadInt32() + Halo1.Tags.scenario_structure_bsps_header.kSizeOf; item.Size = s.ReadInt32(); cache.BspAddressMasks.Add(s.ReadUInt32() - (uint)item.Offset); // won't count the header item.BspIndex = bspCount++; // Seek to the end of this definition, and thus, the start of the next definition s.Seek(20, System.IO.SeekOrigin.Current); } }
/// <summary> /// Stream the field from a buffer /// </summary> /// <param name="input"></param> public override void Read(IO.EndianReader input) { #if false//DEBUG if (Value < 5) { input.Seek(Value, System.IO.SeekOrigin.Current); } else { byte[] data = input.ReadBytes(Value); for (int x = 0, y = 0, z = 0; x < data.Length; x++) { if (data[x] != 0) { if (y == 0 || x == 0) { y = x; } z++; } else if (x != 0 && data[x - 1] != 0) { trace.WriteLine("Found data at offset 0x{0:X} covering 0x{1:X} bytes, in a pad field with a count of {1}", y, z, Value); } } data = null; } #else input.Seek(Value, System.IO.SeekOrigin.Current); #endif }
/// <summary> /// Takes the input stream and does any version specific /// data operations to get the tag manager ready for reading /// </summary> void CalculateVersion() { InputStream.Seek(60); InputStream.State = BlamLib.IO.EndianState.Little; uint group_tag = InputStream.ReadTagUInt(); BlamBuild bb = FromSignature(group_tag); #region Endian handling if (bb == BlamBuild.Halo1) { endianState = BlamLib.IO.EndianState.Big; } else { endianState = BlamLib.IO.EndianState.Little; } if (InputStream != null) { InputStream.State = endianState; } if (OutputStream != null) { OutputStream.State = endianState; } #endregion if (engine == BlamVersion.Unknown) { engine = bb.ToVersion(); ManageSetupTagGroup(); } }
/// <summary> /// Validates the cache's header for proper signatures /// </summary> /// <param name="s">cache stream</param> /// <param name="header_sizes">size of a single header</param> /// <remarks>Assumes the header is at the start of the stream</remarks> /// <returns>The header size entry that matches this cache or -1 if no matches</returns> internal static int ValidateHeader(IO.EndianReader s, params int[] header_sizes) { bool invalid = true; int result = -1; foreach (int header_size in header_sizes) { s.Seek(0); if (s.ReadTagUInt() == MiscGroups.head.ID && s.Length >= header_size) // it HAS to be more than the size of a header... { s.Seek(header_size - sizeof(uint)); if (s.ReadTagUInt() == MiscGroups.foot.ID) { invalid = false; result = header_size; } } if (!invalid) { break; } } if (invalid) { throw new InvalidCacheFileException(s.FileName); } return(result); }
public void Read(IO.EndianReader s) { mWidthType = (StringStorageWidthType)s.ReadByte(); mType = (StringStorageType)s.ReadByte(); mByteOrder = (Shell.EndianFormat)s.ReadByte(); s.Seek(sizeof(byte)); mFixedLength = s.ReadInt16(); s.Seek(sizeof(ushort)); }
static void Read(IO.EndianReader s, bool decrypt, IO.IEndianStreamable obj, long size = 0, ulong userKey = 0, Action <IO.EndianReader> readLeftovers = null) { if (!decrypt) { obj.Read(s); } else { using (var ms = new System.IO.MemoryStream()) using (var sout = new IO.EndianWriter(ms, Shell.EndianFormat.Big)) using (var decrypted = new IO.EndianReader(ms, Shell.EndianFormat.Big)) { long position = s.BaseStream.Position; var tea = new Security.Cryptography.PhxTEA(s, sout); tea.InitializeKey(Security.Cryptography.PhxTEA.kKeyGameFile, userKey); tea.Decrypt(size); decrypted.Seek(0); obj.Read(decrypted); if (readLeftovers != null) { readLeftovers(decrypted); } } } }
static void Write(IO.EndianWriter s, bool encrypt, IO.IEndianStreamable obj, long size = 0, ulong userKey = 0, Action <IO.EndianWriter> writeLeftovers = null) { if (!encrypt) { obj.Write(s); if (writeLeftovers != null) { writeLeftovers(s); } } else { using (var ms = new System.IO.MemoryStream()) using (var sin = new IO.EndianWriter(ms, Shell.EndianFormat.Big)) using (var encrypted = new IO.EndianReader(ms, Shell.EndianFormat.Big)) { obj.Write(sin); if (writeLeftovers != null) { writeLeftovers(sin); } encrypted.Seek(0); var tea = new Security.Cryptography.PhxTEA(encrypted, s); tea.InitializeKey(Security.Cryptography.PhxTEA.kKeyGameFile, userKey); tea.Encrypt(size); } } }
/// <summary> /// Read and preprocess header information /// </summary> private void ReadPreprocess() { char[] gtag; #region Endian Switch ushort us_temp = InputStream.ReadUInt16(); if (us_temp == HeaderEndian) { SetEndianState(IO.EndianState.Little); } else if (us_temp == HeaderEndianSwap) { SetEndianState(IO.EndianState.Big); } else { throw new Debug.ExceptionLog("File: Invalid switch! {0:X4}", us_temp); } #endregion #region Version us_temp = InputStream.ReadUInt16(); Debug.Assert.If(us_temp == HeaderVersion, "File: mismatching file version! [{0}, !{1}", HeaderVersion, us_temp); #endregion #region Signature gtag = InputStream.ReadTag(); Debug.Assert.If(TagGroup.Test(gtag, (char[])IO.TagGroups.BlamLib), "File: invalid signature! [{0} !{1}]", IO.TagGroups.BlamLib.TagToString(), new string(gtag)); #endregion flags.Reset((uint)(InputStream.ReadUInt16() << 16) | flags); #region Data Version us_temp = InputStream.ReadUInt16(); Debug.Assert.If(us_temp == data.Attribute.Version, "File: mismatching data version! [{0}, !{1}", data.Attribute.Version, us_temp); #endregion #region Data Signature gtag = InputStream.ReadTag(); Debug.Assert.If(TagGroup.Test(gtag, (char[])data.GroupTag), "File: invalid data signature! [{0} !{1}]", data.GroupTag.TagToString(), new string(gtag)); #endregion #region Data Directories InputStream.ReadInt32(); InputStream.ReadInt32(); #endregion #region Data InputStream.ReadInt32(); int data_offset = InputStream.ReadInt32(); #endregion Debug.Assert.If(InputStream.Position == HeaderSize, "File: Update header size! [{0}, !{1}]", HeaderSize, InputStream.Position); InputStream.Seek(data_offset); }
/// <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); } } }
/// <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> /// <remarks> /// Doesn't actually populate the collection from a debug stream. Instead it seeks the streams to directly after this set's /// data, assuming the streams have data which matches this collection. Also assumes the streams aren't filled with just static /// strings, but also contain dynamic string data as well (or else the seek operations this performs will exception) /// </remarks> public void FromDebugStream(IO.EndianReader offsets, IO.EndianReader buffer, bool is_packed) { int last_offset = sizeof(uint) * Count; offsets.Seek(last_offset); int last_offset_buffer = offsets.ReadInt32(); buffer.Seek(last_offset_buffer); }
public string ReadName(int nameOffset) { if (NameData == null || nameOffset >= NameData.Length) { throw new InvalidOperationException(nameOffset.ToString("X8")); } NameDataReader.Seek(nameOffset); return(NameDataReader.ReadString(Memory.Strings.StringStorage.CStringAscii)); }
internal static int ValidateHeaderAdjustEndian(IO.EndianReader s, params int[] header_sizes) { bool invalid = true; int result = -1; foreach (int header_size in header_sizes) { if (s.Length < header_size) { continue; // it HAS to be more than the size of a header... } s.Seek(0); uint head = s.ReadTagUInt(); if (head != MiscGroups.head.ID) { if (IO.ByteSwap.SwapUDWord(head) == MiscGroups.head.ID) { s.State = s.State.Invert(); } } s.Seek(header_size - sizeof(uint)); if (s.ReadTagUInt() == MiscGroups.foot.ID) { invalid = false; result = header_size; } if (!invalid) { break; } } if (invalid) { throw new InvalidCacheFileException(s.FileName); } return(result); }
public void ReadChildren(IO.EndianReader s) { if (mChildrenOffset.IsInvalidHandle) { return; } s.Seek((long)mChildrenOffset); for (int x = 0; x < ChildrenIndices.Capacity; x++) { ChildrenIndices.Add(s.ReadInt32()); } }
public void Build(IO.EndianReader stream, int offset, int count, int total_size) { stream.Seek(offset); int[] indicies = new int[count]; for (int x = 0; x < indicies.Length; x++) { indicies[x] = stream.ReadInt32(); } ValueLengths = new byte[count]; for (int x = count - 1, prev_offset = total_size; x >= 0; x--) { ValueLengths[x] = (byte)((prev_offset - indicies[x]) - 1); prev_offset = indicies[x]; } Count = count; }
public override void Read(IO.EndianReader s) { BlamVersion ver = (BlamVersion)s.ReadUInt16(); // HACK: this is a hack if (ver == BlamVersion.Halo1) { ver = BlamVersion.Halo1_CE; } Debug.Assert.If(ver == engine, "Engine version mismatch: expected {0}, but got {1}", engine, ver); s.Seek(2, System.IO.SeekOrigin.Current); #region FileNames int file_count = s.ReadInt32(); for (int x = 0; x < file_count; x++) { files.Add(s.ReadCString()); } #endregion }
public void ReadAttributes(XmbFile xmb, IO.EndianReader s) { if (mAttributesOffset.IsInvalidHandle) { return; } s.Seek((long)mAttributesOffset); for (int x = 0; x < Attributes.Capacity; x++) { XmbVariant k; XmbVariantSerialization.Read(s, out k); XmbVariant v; XmbVariantSerialization.Read(s, out v); var kv = new KeyValuePair <XmbVariant, XmbVariant>(k, v); Attributes.Add(kv); if (k.HasUnicodeData || v.HasUnicodeData) { xmb.mHasUnicodeStrings = true; } } }
/// <summary>Read a CString from an endian stream</summary> /// <param name="s">Endian stream to read from</param> /// <param name="length">Optional length specification</param> /// <param name="actualCount">On return, the actual character byte count, or -1 if all bytes are valid</param> /// <returns>The character's bytes for the string we're reading</returns> byte[] ReadStrCString(IO.EndianReader s, int length, out int actualCount) { byte[] bytes; actualCount = TypeExtensions.kNone; // complete string case // the user was nice and saved us some CPU trying to feel around for the null // because we don't have a fixed length to speed things up if (!mStorage.IsFixedLength && length > 0) { bytes = s.ReadBytes(GetMaxCleanByteCount(length)); s.Seek(mNullCharacterSize, System.IO.SeekOrigin.Current); } // F**K: figure out the length ourselves. Or maybe we're a fixed length CString... // in which case we'll ignore anything the user tried to tell us about the length else { using (var ms = new System.IO.MemoryStream(!mStorage.IsFixedLength ? 512 : mStorage.FixedLength)) { // The N-byte methods take care of reading past the // null character, no need to do it in this case. if (mNullCharacterSize == 1) { ReadCStringSingleByte(s, ms); } else { ReadCStringMultiByte(s, ms); } // We use ToArray instead of GetArray so all of [ms] can theoretically be disposed of bytes = ms.ToArray(); } } return(bytes); }
internal void ReadLanguageStrings(IO.EndianReader buffer, uint langBitmask = uint.MaxValue) { Contract.Assert(LanguageRegistry.NumberOfLanguages <= Bits.kInt32BitCount, nameof(langBitmask) + " is too small to actually be a language bitvector"); Contract.Assert(mLanguageOffsets != null); for (int x = 0; x < mLanguageOffsets.Length; x++) { int offset = mLanguageOffsets[x]; if (offset.IsNone()) { continue; } else if (!Bitwise.Flags.Test(langBitmask, 1U << x)) { continue; } buffer.Seek(offset); mLanguageStrings[x] = buffer.ReadString(Memory.Strings.StringStorage.CStringUtf8); } LanguageOffsetsDispose(); }
bool CacheRead(BlamLib.Blam.CacheFile c, out byte[] data) { data = null; ResourcePtr offset = GetOffset(0); int size = GetSize(0); var rsrc_cache = Program.Halo2.FromLocation(c as Halo2.CacheFile, offset); // the shared cache isn't loaded, break if (rsrc_cache == null) { return(false); } // get the input stream we need IO.EndianReader er = rsrc_cache.InputStream; // read the bitmap er.Seek(offset.Offset); data = er.ReadBytes(size); return(true); }
/// <summary>Moves the stream ahead by the sizeof a four character code (4 bytes)</summary> /// <param name="s"></param> /// <remarks>Doesn't actually read any data from the stream, only seeks forward</remarks> public override void Read(IO.EndianReader s) => s.Seek(sizeof(TagWord), System.IO.SeekOrigin.Current);
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); }
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 void Read(IO.EndianReader s) { var context = s.UserData as XmbFileContext; using (s.ReadSignatureWithByteSwapSupport(kSignature)) { if (context.PointerSize == Shell.ProcessorSize.x64) { // #HACK to deal with xmb files which weren't updated with new tools if (s.ByteOrder == Shell.EndianFormat.Big) { context.PointerSize = Shell.ProcessorSize.x32; } } s.VirtualAddressTranslationInitialize(context.PointerSize); Values.PtrHandle elements_offset_pos; if (context.PointerSize == Shell.ProcessorSize.x64) { s.Pad32(); } #region Initialize elements { int count = s.ReadInt32(); if (context.PointerSize == Shell.ProcessorSize.x64) { s.Pad32(); } s.ReadVirtualAddress(out elements_offset_pos); mElements = new List <Element>(count); } #endregion #region Initialize and read pool { int size = s.ReadInt32(); if (context.PointerSize == Shell.ProcessorSize.x64) { s.Pad32(); } Values.PtrHandle pool_offset_pos = s.ReadVirtualAddress(); s.Seek((long)pool_offset_pos); byte[] buffer = s.ReadBytes(size); mPool = new XmbVariantMemoryPool(buffer, s.ByteOrder); } #endregion if (context.PointerSize == Shell.ProcessorSize.x64) { s.Pad64(); } s.Seek((long)elements_offset_pos); for (int x = 0; x < mElements.Capacity; x++) { var e = new Element(); mElements.Add(e); e.Index = x; e.Read(this, context, s); } foreach (var e in mElements) { e.ReadAttributes(this, s); e.ReadChildren(s); } } }
/// <summary>Moves the stream ahead by the sizeof a eight character code (8 bytes) times the count of the <see cref="GroupTags"/></summary> /// <param name="s"></param> /// <remarks>Doesn't actually read any data from the stream, only seeks forward</remarks> public override void Read(IO.EndianReader s) { s.Seek(GroupTags.Count * sizeof(TagWord), System.IO.SeekOrigin.Current); }
/// <summary> /// Moves the stream ahead by the sizeof a four character code (4 bytes) times the count of the group tags /// </summary> /// <param name="s"></param> /// <remarks>Doesn't actually read any data from the stream, only seeks forward</remarks> public void Read(IO.EndianReader s) { s.Seek(groupTags.Length * 4, System.IO.SeekOrigin.Current); }
/// <summary> /// Moves the stream ahead by the sizeof /// a four character code (4 bytes) /// </summary> /// <param name="s"></param> /// <remarks>Doesn't actually read any data from the stream, only seeks forward</remarks> public void Read(IO.EndianReader s) { s.Seek(4, System.IO.SeekOrigin.Current); }
public void ExtractSounds(string path, System.IO.StreamWriter towav, IO.EndianReader pckReader, bool overwriteExisting = false) { Dictionary <uint, SoundBank.AkSoundBankHierarchyObjectBase> sounds; if (!mObjects.TryGetValue(SoundBank.HircType.Sound, out sounds)) { Debug.Trace.FilePackage.TraceInformation("{0} - No sounds to extract?", PackageFileName); return; } foreach (var kv in sounds) { var snd = kv.Value as SoundBank.AkSoundBankHierarchySound; if (snd.Name == null && mDupObjects.Contains(kv.Key)) { continue; } uint src_id = snd.Source.MediaInfo.SourceID; mUntouched.Remove(src_id); if (snd.Source.MediaInfo.FileID == 0) { towav.WriteLine("REM NoData {0}", snd.Name); continue; } string dir = null; string filename = (snd.Name ?? ("unknown_" + kv.Key.ToString("X8"))) + ".xma"; uint bank_id = snd.BankId; string bank_name; if (!Package.IdToName.TryGetValue(bank_id, out bank_name)) { bank_name = bank_id.ToString("X8"); } SoundBank.AkSoundBankData bank_data = null; bool streamed = snd.Source.StreamType != SoundBank.AkBankSourceData.SourceType.Data; if (!streamed) { bank_data = mIdToBank[bank_id].mData; } dir = System.IO.Path.Combine(path, bank_name); System.IO.Directory.CreateDirectory(dir); string full_path = System.IO.Path.Combine(dir, filename); towav.WriteLine("towav.exe {0}", full_path); if (System.IO.File.Exists(full_path) && !overwriteExisting) { continue; } using (var fs = System.IO.File.Create(full_path)) { if (streamed) { var file = Package.FindStreamedFileById(snd.Source.MediaInfo.FileID); pckReader.Seek(file.FileOffset); byte[] data = pckReader.ReadBytes((int)file.FileSize32); fs.Write(data, 0, data.Length); } else { fs.Write(bank_data.Buffer, (int)snd.Source.MediaInfo.FileOffset, (int)snd.Source.MediaInfo.MediaSize); } } } ////////////////////////////////////////////////////////////////////////// foreach (var kv in mUntouched) { var mr = kv.Value; string name = "unknown2_" + mr.Media.ID.ToString("X8"); if (mr.Media.Size == 0) { towav.WriteLine("REM NoData2 {0}", name); continue; } string filename = name + ".xma"; uint bank_id = mr.BankId; string bank_name; if (!Package.IdToName.TryGetValue(bank_id, out bank_name)) { bank_name = bank_id.ToString("X8"); } SoundBank.AkSoundBankData bank_data = mIdToBank[bank_id].mData; string dir = System.IO.Path.Combine(path, bank_name); System.IO.Directory.CreateDirectory(dir); string full_path = System.IO.Path.Combine(dir, filename); towav.WriteLine("towav.exe {0}", full_path); if (System.IO.File.Exists(full_path)) { continue; } using (var fs = System.IO.File.Create(full_path)) { fs.Write(bank_data.Buffer, (int)mr.Media.Offset, (int)mr.Media.Size); } } }
internal TI.Definition LoadResources(CacheFileGen3 c, cache_file_resource_gestalt_group owner, cache_file_resource_layout_table cache_layout, bool mega_hack) { if (Reference.Datum == DatumIndex.Null) { return(null); // this is a null entry } if (loadedResources != null) { return(loadedResources); // data already loaded, return } var rdf = c.GetCacheFileResourceDefinitionFactory(); // pre-process cache for resource loading cache_layout.BuildInteropData(); owner.BuildInteropData(); int resource_stream_definition_size = BlockSize.Value; // sound resource case hack bool use_sound_resource_hack = false; if (resource_stream_definition_size == 0) { Debug.Assert.If(ResourceType.Value == owner.resource_index_sound_resource_definition); resource_stream_definition_size = sound_resource_definition.kSizeOf; use_sound_resource_hack = true; } int resource_stream_size_required = resource_stream_definition_size; // base address to use on cache fixups, cache data will be appended // later on uint cache_required_base_address = 0, cache_optional_base_address = 0; byte[] resource_stream_data; #region create resource buffer { // Get our page segment data so we can build our resource buffer for loading int seg_index = SegmentIndex.Value; cache_file_resource_layout_table.page_segment_block page_segment = cache_layout.PageSegments[seg_index]; int required_size = page_segment.GetRequiredSize(); int optional_size = page_segment.GetOptionalSize(); cache_required_base_address = (uint)resource_stream_size_required; if (required_size > 0) { resource_stream_size_required += required_size; } if (optional_size > 0) // optional isn't always used so don't set the base address if it isn't { cache_optional_base_address = (uint)resource_stream_size_required; resource_stream_size_required += optional_size; } // get our definition data buffer resource_stream_data = new byte[resource_stream_size_required]; if (use_sound_resource_hack) // sound_resources don't occupy space in the resource-definition-data, so we have to create faux def data { int data_size = 0; if (required_size > 0) { data_size += required_size; } if (optional_size > 0) { data_size += optional_size; } if (data_size > 0) { rdf.InsertDataSizeIntoFauxDefinitionData(resource_stream_data, (uint)data_size); } } else { Array.Copy(owner.ResourceDefinitionData.Value, BlockOffset.Value, resource_stream_data, 0, resource_stream_definition_size); } { // get cache data and append it byte[] page_data = cache_layout.Pages[page_segment.RequiredPageIndex.Value].GetSegmentData(c, cache_layout, page_segment.RequiredSegmentOffset.Value); Array.Copy(page_data, 0, resource_stream_data, (int)cache_required_base_address, required_size); if (page_segment.OptionalPageIndex.Value >= 0 && cache_optional_base_address > 0) { page_data = cache_layout.Pages[page_segment.OptionalPageIndex.Value].GetSegmentData(c, cache_layout, page_segment.OptionalSegmentOffset.Value); Array.Copy(page_data, 0, resource_stream_data, (int)cache_optional_base_address, optional_size); } } } #endregion Util.OptionalValue optv = new Util.OptionalValue((byte)resource_fixup_type.Count); #region perform fixups using (var ms = new System.IO.MemoryStream(resource_stream_data, true)) { foreach (resource_fixup_block def in ResourceFixups) { uint address = (uint)def.Address.Value; uint address_offset = optv.GetValue(address); ms.Seek(def.BlockOffset.Value, System.IO.SeekOrigin.Begin); resource_fixup_type rft = (resource_fixup_type)optv.GetOption(address); switch (rft) { case resource_fixup_type.Data: break; case resource_fixup_type.CacheRequired: address_offset += cache_required_base_address; break; case resource_fixup_type.CacheOptional: address_offset += cache_optional_base_address; break; default: throw new Debug.Exceptions.UnreachableException(); } IO.ByteSwap.SwapUDWordAndWrite(address_offset, ms); // hack identifier for StructReference fields when the definition is at offset '0' // as that f***s with the init code //if (address_offset == 0) IO.ByteSwap.SwapUDWordAndWrite(1, ms); } // foreach (resource_definition_fixup_block def in ResourceDefinitionFixups) // { // } } #endregion #region create and stream definition using (var s = new IO.EndianReader(resource_stream_data, IO.EndianState.Big, null)) { int res_type = ResourceType.Value; if (res_type == owner.resource_index_render_geometry_api_resource_definition) { loadedResources = rdf.GenerateRenderGeometryApiResource(); } else if (res_type == owner.resource_index_bitmap_texture_interop_resource) { loadedResources = rdf.GenerateBitmapTextureInteropResource(); } else if (res_type == owner.resource_index_model_animation_tag_resource) { loadedResources = rdf.GenerateModelAnimationTagResource(); } // TODO: haven't quite figured this one out yet. Currently using hacked up code (see [use_sound_resource_hack]) else if (res_type == owner.resource_index_sound_resource_definition) { loadedResources = rdf.GenerateSoundResourceResource(); } else if (res_type == owner.resource_index_bitmap_texture_interleaved_interop_resource) { loadedResources = rdf.GenerateBitmapTextureInterleavedInteropResource(); } else if (res_type == owner.resource_index_structure_bsp_tag_resources) { loadedResources = rdf.GenerateStructureBspTagResource(); } // TODO: haven't figured this one out yet else if (res_type == owner.resource_index_structure_bsp_cache_file_tag_resources) { loadedResources = rdf.GenerateStructureBspCacheFileTagResource(); } else { throw new Debug.Exceptions.UnreachableException(); } s.Seek(optv.GetValue((uint)DefinitionOffset.Value)); IO.ITagStream ts = new resource_tag_stream(c, Reference.Datum, s); ts.Flags.Add( IO.ITagStreamFlags.UseStreamPositions | IO.ITagStreamFlags.DontStreamStringData | IO.ITagStreamFlags.DontStreamFieldSetHeader | IO.ITagStreamFlags.DontPostprocess); if (mega_hack) { ts.Flags.Add(IO.ITagStreamFlags.Halo3VertexBufferMegaHack); } loadedResources.Read(ts); } #endregion return(loadedResources); }