/// <summary> /// Applies all patches to a file /// </summary> /// <param name="patchEntry"></param> /// <param name="indexContainer"></param> /// <param name="input"></param> /// <param name="output"></param> /// <returns></returns> public bool ApplyPatch(PatchEntry patchEntry, Indices.IndexContainer indexContainer, Stream input, Stream output) { if (indexContainer == null || input == null || output == null || patchEntry == null) { return(false); } if (patchEntry.Records.Count == 0) { return(false); } // get the correct order - just in case patchEntry.Records.Sort((x, y) => x.Ordinal - y.Ordinal); // iterate the patches for (int i = 0; i < patchEntry.Records.Count; i++) { // if applying more than one the previous output is required // as patches are incremental if (i > 0) { input = output; } using (var patch = indexContainer.OpenPatch(patchEntry.Records[i].PatchEKey)) { if (patch == null) { return(false); } ZBSPatch.Apply(input, patch, output); } } return(true); }
private void Read(Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (!stream.CanRead || stream.Length <= 1) { throw new NotSupportedException($"Unable to read PatchFile stream"); } using (var reader = new BinaryReader(stream)) { PatchHeader.Read(reader); // read the patch entries int pageSize = 1 << PatchHeader.PageSize; long length = reader.BaseStream.Length; foreach (var offset in GetOffsets(reader)) { reader.BaseStream.Position = offset; // buffer the page then read the entries to minimise file reads using (var ms = new MemoryStream(reader.ReadBytes(pageSize))) using (var br = new BinaryReader(ms)) { var block = new PatchEntry(); while (block.Read(br, PatchHeader)) { _PatchEntries[block.CKey] = block; block = new PatchEntry(); } } } #region Unknown Data // Unknown = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position)); /* * // the following is unknown entries that comprise of: * // a keysize byte followed by either a CKey or Ekey * // the key type relevant size (decompressed/compressed) * // and optionally an ESpec string * // blocks are always a pair of 112 bytes then 160, Encoding Entry then Content Entry * // * // struct encodin_entry { * // char _unk0[4]; // always 0 * // char KeySize; * // char Hash[KeySize]; * // char _unk15[7]; * // uint32_t some_offset; * // * // // 0x44 CompressedSize; * // } * // * // struct content_entry { * // char _unk0[4]; // always 0 * // char KeySize; * // char Hash[KeySize]; * // char _unk15[7]; * // uint32_t some_offset; * // * // // 0x34 DeompressedSize; * // // 0x6C partial ESpec always ends in 0xA0 * // } */ #endregion Checksum = stream.MD5Hash(); } }
/// <summary> /// Returns a PatchEntry by CKey /// </summary> /// <param name="ckey"></param> /// <param name="entry"></param> /// <returns></returns> public bool TryGet(MD5Hash ckey, out PatchEntry entry) => _PatchEntries.TryGetValue(ckey, out entry);