private static void WriteLocaleChanges(Patch patch, IWriter writer) { if (patch.LanguageChanges.Count == 0) return; long startPos = writer.Position; writer.WriteInt32(AssemblyPatchBlockID.Locl); writer.WriteUInt32(0); // Size filled in later writer.WriteByte(0); // Version 0 // Write change data for each language writer.WriteByte((byte)patch.LanguageChanges.Count); foreach (LanguageChange language in patch.LanguageChanges) { writer.WriteByte(language.LanguageIndex); // Write the change data for each string in the language writer.WriteInt32(language.LocaleChanges.Count); foreach (LocaleChange change in language.LocaleChanges) { writer.WriteUInt16((ushort)change.Index); writer.WriteUTF8(change.NewValue); } } // Fill in the block size long endPos = writer.Position; writer.SeekTo(startPos + 4); writer.WriteUInt32((uint)(endPos - startPos)); writer.SeekTo(endPos); }
private void makePatch_Click(object sender, EventArgs e) { Patch patch = new Patch(); patch.Name = patchName.Text; patch.Description = patchDescription.Text; patch.Author = patchAuthor.Text; IReader originalReader = new EndianReader(File.OpenRead(unmoddedPath.Text), Endian.BigEndian); IReader newReader = new EndianReader(File.OpenRead(moddedPath.Text), Endian.BigEndian); ThirdGenVersionInfo version = new ThirdGenVersionInfo(originalReader); BuildInfoLoader loader = new BuildInfoLoader(XDocument.Load(@"Formats\SupportedBuilds.xml"), @"Formats\"); BuildInformation buildInfo = loader.LoadBuild(version.BuildString); ThirdGenCacheFile originalFile = new ThirdGenCacheFile(originalReader, buildInfo, version.BuildString); ThirdGenCacheFile newFile = new ThirdGenCacheFile(newReader, buildInfo, version.BuildString); MetaComparer.CompareMeta(originalFile, originalReader, newFile, newReader, patch); LocaleComparer.CompareLocales(originalFile, originalReader, newFile, newReader, patch); originalReader.Close(); newReader.Close(); IWriter output = new EndianWriter(File.OpenWrite(outPath.Text), Endian.BigEndian); AssemblyPatchWriter.WritePatch(patch, output); output.Close(); MessageBox.Show("Done!"); }
/// <summary> /// Compares the tag meta between two cache files and adds the results to a patch. /// </summary> /// <param name="originalFile">The unmodified cache file.</param> /// <param name="originalReader">A stream open on the unmodified cache file.</param> /// <param name="newFile">The modified cache file.</param> /// <param name="newReader">A stream open on the modified cache file.</param> /// <param name="output">The Patch to add results to.</param> public static void CompareMeta(ICacheFile originalFile, IReader originalReader, ICacheFile newFile, IReader newReader, Patch output) { // TODO: Handle files with expanded meta partitions uint address = originalFile.Info.MetaBase.AsAddress(); uint offset = originalFile.Info.MetaBase.AsOffset(); uint endOffset = offset + originalFile.Info.MetaSize; const int BufferSize = 0x1000; byte[] oldBuffer = new byte[BufferSize]; byte[] newBuffer = new byte[BufferSize]; int diffStart = 0; uint diffAddress = 0; int diffSize = 0; originalReader.SeekTo(offset); newReader.SeekTo(offset); while (offset < endOffset) { // Read the meta in large blocks and then compare the blocks originalReader.ReadBlock(oldBuffer, 0, BufferSize); newReader.ReadBlock(newBuffer, 0, BufferSize); for (int i = 0; i < oldBuffer.Length; i++) { if (oldBuffer[i] != newBuffer[i]) { if (diffSize == 0) { diffStart = i; diffAddress = (uint)(address + i); } diffSize++; } else if (diffSize > 0) { // Copy the differing bytes to a buffer byte[] diff = new byte[diffSize]; Array.Copy(newBuffer, diffStart, diff, 0, diffSize); // Export the change data MetaChange change = new MetaChange((uint)(address + i), diff); output.MetaChanges.Add(change); diffSize = 0; } } offset += BufferSize; address += BufferSize; } }
public static void WritePatch(Patch patch, IWriter writer) { writer.WriteInt32(AssemblyPatchMagic); writer.WriteUInt32(0); // File size filled in later writer.WriteByte(0); // No compression WriteBlocks(patch, writer); // Fill in the file size long fileSize = writer.Position; writer.SeekTo(4); writer.WriteUInt32((uint)fileSize - 8); writer.SeekTo(fileSize); }
private static void ReadPatchInfo(IReader reader, Patch output) { byte version = reader.ReadByte(); // Version 0 (all versions) output.MapID = reader.ReadInt32(); output.MapInternalName = reader.ReadAscii(); output.Name = reader.ReadUTF16(); output.Description = reader.ReadUTF16(); output.Author = reader.ReadUTF16(); int screenshotLength = reader.ReadInt32(); if (screenshotLength > 0) output.Screenshot = reader.ReadBlock(screenshotLength); }
/// <summary> /// Compares the locale data of two cache files and adds the results to a patch. /// </summary> /// <param name="originalFile">The unmodified cache file.</param> /// <param name="originalReader">A stream open on the unmodified cache file.</param> /// <param name="newFile">The modified cache file.</param> /// <param name="newReader">A stream open on the modified cache file.</param> /// <param name="output">The Patch to add results to.</param> public static void CompareLocales(ICacheFile originalFile, IReader originalReader, ICacheFile newFile, IReader newReader, Patch output) { if (originalFile.Languages.Count != newFile.Languages.Count) throw new InvalidOperationException("Cannot compare locales between cache files with different language counts."); // Compare each language for (int i = 0; i < originalFile.Languages.Count; i++) { // Compare the strings in the two language LanguageChange change = CompareLanguages((byte)i, originalFile.Languages[i], originalReader, newFile.Languages[i], newReader); // Only add the info if anything actually changed between the two languages if (change.LocaleChanges.Count > 0) output.LanguageChanges.Add(change); } }
private static void WriteMetaChanges(Patch patch, IWriter writer) { if (patch.MetaChanges.Count == 0) return; long startPos = writer.Position; writer.WriteInt32(AssemblyPatchBlockID.Meta); writer.WriteUInt32(0); // Size filled in later writer.WriteByte(0); // Version 0 // Filter meta changes by size (as a file size optimization) List<MetaChange> fourByteChanges = new List<MetaChange>(); List<MetaChange> otherChanges = new List<MetaChange>(); foreach (MetaChange change in patch.MetaChanges) { if (change.Data.Length == 4) fourByteChanges.Add(change); else otherChanges.Add(change); } // Write 4-byte changes writer.WriteUInt32((uint)fourByteChanges.Count); foreach (MetaChange change in fourByteChanges) { writer.WriteUInt32(change.Address); writer.WriteBlock(change.Data); } // Write other changes writer.WriteUInt32((uint)otherChanges.Count); foreach (MetaChange change in otherChanges) { writer.WriteUInt32(change.Address); writer.WriteUInt32((uint)change.Data.Length); writer.WriteBlock(change.Data); } // Fill in the block size long endPos = writer.Position; writer.SeekTo(startPos + 4); writer.WriteUInt32((uint)(endPos - startPos)); writer.SeekTo(endPos); }
private static void ReadMetaChanges(IReader reader, Patch output) { byte version = reader.ReadByte(); // Version 0 (all versions) uint num4ByteChanges = 0; for (uint i = 0; i < num4ByteChanges; i++) { uint address = reader.ReadUInt32(); byte[] data = reader.ReadBlock(4); output.MetaChanges.Add(new MetaChange(address, data)); } uint numChanges = 0; for (uint i = 0; i < numChanges; i++) { uint address = reader.ReadUInt32(); uint dataSize = reader.ReadUInt32(); byte[] data = reader.ReadBlock((int)dataSize); output.MetaChanges.Add(new MetaChange(address, data)); } }
private static Patch ReadBlocks(IReader reader, uint offset, uint endOffset) { Patch result = new Patch(); while (offset < endOffset) { reader.SeekTo(offset); int blockId = reader.ReadInt32(); uint size = reader.ReadUInt32(); switch (blockId) { case AssemblyPatchBlockID.Titl: ReadPatchInfo(reader, result); break; case AssemblyPatchBlockID.Meta: ReadMetaChanges(reader, result); break; } // Skip to the next block offset += size; } return result; }
private static void WriteBlocks(Patch patch, IWriter writer) { WritePatchInfo(patch, writer); WriteMetaChanges(patch, writer); WriteLocaleChanges(patch, writer); }
private static void WritePatchInfo(Patch patch, IWriter writer) { long startPos = writer.Position; writer.WriteInt32(AssemblyPatchBlockID.Titl); writer.WriteUInt32(0); // Size filled in later writer.WriteByte(0); // Version 0 writer.WriteInt32(patch.MapID); if (patch.MapInternalName != null) writer.WriteAscii(patch.MapInternalName); else writer.WriteByte(0); writer.WriteUTF16(patch.Name); writer.WriteUTF16(patch.Description); writer.WriteUTF16(patch.Author); if (patch.Screenshot != null) { writer.WriteInt32(patch.Screenshot.Length); writer.WriteBlock(patch.Screenshot); } else { writer.WriteInt32(0); } // Fill in the block size long endPos = writer.Position; writer.SeekTo(startPos + 4); writer.WriteUInt32((uint)(endPos - startPos)); writer.SeekTo(endPos); }