/// <summary> /// Loads a cache file from a stream. /// </summary> /// <param name="reader">The stream to read from.</param> /// <param name="infoLoader">The BuildInfoLoader responsible for loading build information for the cache file.</param> /// <param name="buildInfo">The variable to store build information to.</param> /// <returns>The cache file that was loaded.</returns> /// <exception cref="ArgumentException">Thrown if the cache file is invalid.</exception> /// <exception cref="NotSupportedException">Thrown if the cache file's target engine is not supported.</exception> public static ICacheFile LoadCacheFile(IReader reader, BuildInfoLoader infoLoader, out BuildInformation buildInfo) { // Set the reader's endianness based upon the file's header magic reader.SeekTo(0); byte[] headerMagic = reader.ReadBlock(4); reader.Endianness = DetermineCacheFileEndianness(headerMagic); // Load engine version info CacheFileVersionInfo version = new CacheFileVersionInfo(reader); if (version.Engine != EngineType.SecondGeneration && version.Engine != EngineType.ThirdGeneration) throw new NotSupportedException("Engine not supported"); // Load build info buildInfo = infoLoader.LoadBuild(version.BuildString); if (buildInfo == null) throw new NotSupportedException("Engine version \"" + version.BuildString + "\" not supported"); // Load the cache file depending upon the engine version switch (version.Engine) { case EngineType.SecondGeneration: return new SecondGenCacheFile(reader, buildInfo, version.BuildString); case EngineType.ThirdGeneration: return new ThirdGenCacheFile(reader, buildInfo, version.BuildString); default: throw new NotSupportedException("Engine not supported"); } }
static void Main(string[] args) { if (args.Length != 2) { Console.WriteLine("Usage: mapexpand <map file> <page count>"); Console.WriteLine("Pages are multiples of 0x10000 bytes."); return; } int pageCount; if (!int.TryParse(args[1], out pageCount) || pageCount <= 0) { Console.WriteLine("The page count must be a positive integer."); return; } Console.WriteLine("Reading..."); EndianStream stream = new EndianStream(File.Open(args[0], FileMode.Open, FileAccess.ReadWrite), Endian.BigEndian); CacheFileVersionInfo version = new CacheFileVersionInfo(stream); if (version.Engine != EngineType.ThirdGeneration) { Console.WriteLine("Only third-generation map files are supported."); return; } BuildInfoLoader infoLoader = new BuildInfoLoader(XDocument.Load("Formats/SupportedBuilds.xml"), "Formats/"); BuildInformation buildInfo = infoLoader.LoadBuild(version.BuildString); ThirdGenCacheFile cacheFile = new ThirdGenCacheFile(stream, buildInfo, version.BuildString); Console.WriteLine("- Engine version: {0}", version.BuildString); Console.WriteLine("- Internal name: {0}", cacheFile.InternalName); Console.WriteLine("- Scenario name: {0}", cacheFile.ScenarioName); Console.WriteLine("- File size: 0x{0:X}", cacheFile.FileSize); Console.WriteLine("- Virtual size: 0x{0:X}", cacheFile.MetaArea.Size); for (int i = 0; i < cacheFile.Partitions.Length; i++) { var partition = cacheFile.Partitions[i]; if (partition.BasePointer != null) Console.WriteLine(" - Partition {0} at 0x{1:X}-0x{2:X} (size=0x{3:X})", i, partition.BasePointer.AsPointer(), partition.BasePointer.AsPointer() + partition.Size - 1, partition.Size); } Console.WriteLine("- Meta pointer mask: 0x{0:X}", cacheFile.MetaArea.PointerMask); Console.WriteLine("- Locale pointer mask: 0x{0:X}", (uint)-cacheFile.LocaleArea.PointerMask); Console.WriteLine("- String pointer mask: 0x{0:X}", cacheFile.StringArea.PointerMask); Console.WriteLine(); Console.WriteLine("Injecting empty pages..."); int injectSize = pageCount * 0x10000; Console.WriteLine("- Start address: 0x{0:X} (offset 0x{1:X})", cacheFile.MetaArea.BasePointer - injectSize, cacheFile.MetaArea.Offset); cacheFile.MetaArea.Resize(cacheFile.MetaArea.Size + injectSize, stream); Console.WriteLine(); Console.WriteLine("Adjusting the header..."); cacheFile.SaveChanges(stream); Console.WriteLine(); Console.WriteLine("Successfully injected 0x{0:X} bytes at 0x{1:X} (offset 0x{2:X}).", injectSize, cacheFile.MetaArea.BasePointer, cacheFile.MetaArea.Offset); stream.Close(); }