public static Dictionary <string, IEnumerable <UnitSeatMapping> > LoadAllSeatMappings(string path, EngineDatabase db, out EngineDescription engineInfo) { Dictionary <string, IEnumerable <UnitSeatMapping> > result = new Dictionary <string, IEnumerable <UnitSeatMapping> >(); string fileName = Path.GetFileNameWithoutExtension(path); using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { var reader = new EndianReader(stream, Endian.BigEndian); var cache = CacheFileLoader.LoadCacheFile(reader, fileName, db, out engineInfo); if (cache.Type != CacheFileType.Shared && cache.Type != CacheFileType.SinglePlayerShared && cache.ScriptFiles.Length > 0) { foreach (var file in cache.ScriptFiles) { if (file is ScnrScriptFile scnr) { var mappings = SeatMappingNameExtractor.ExtractScnrSeatMappings(scnr, reader, engineInfo.ScriptInfo); if (mappings.Any()) { result[file.Name] = mappings; } } } } } return(result); }
private static CompressionState DetermineState(IReader reader, EngineDatabase engineDb, out EngineType type) { // Set the reader's endianness based upon the file's header magic reader.SeekTo(0); byte[] headerMagic = reader.ReadBlock(4); reader.Endianness = CacheFileLoader.DetermineCacheFileEndianness(headerMagic); // Load engine version info var version = new CacheFileVersionInfo(reader); type = version.Engine; if (version.Engine == EngineType.SecondGeneration) { // Load build info var engineInfo = engineDb.FindEngineByVersion(version.BuildString); if (engineInfo == null) { return(CompressionState.Null); } if (!engineInfo.UsesCompression) { return(CompressionState.Null); } return(AnalyzeSecondGen(reader, engineInfo)); } else { return(CompressionState.Null); } }
private static void DumpSharedResources(string mapPath, EngineDatabase db) { ICacheFile cacheFile; ResourceTable resources; using (var reader = new EndianReader(File.OpenRead(mapPath), Endian.BigEndian)) { cacheFile = CacheFileLoader.LoadCacheFile(reader, db); resources = cacheFile.Resources.LoadResourceTable(reader); } using (var output = new StreamWriter(Path.ChangeExtension(mapPath, ".txt"))) { output.WriteLine("Shared resources referenced by {0}:", Path.GetFileName(mapPath)); output.WriteLine(); output.WriteLine("Rsrc Datum Map File Class Tag"); output.WriteLine("---------- -------- ----- ---"); foreach (Resource resource in resources.Resources.Where(r => r.Location != null && r.ParentTag != null)) { // If either page has a null file path, then it's shared ResourcePointer loc = resource.Location; string primaryFile = (loc.PrimaryPage != null) ? loc.PrimaryPage.FilePath : null; string secondaryFile = (loc.SecondaryPage != null) ? loc.SecondaryPage.FilePath : null; if (primaryFile != null || secondaryFile != null) { string className = CharConstant.ToString(resource.ParentTag.Class.Magic); string name = cacheFile.FileNames.GetTagName(resource.ParentTag) ?? resource.ParentTag.Index.ToString(); string fileName = primaryFile ?? secondaryFile; fileName = fileName.Substring(fileName.IndexOf('\\') + 1); output.WriteLine("{0} {1, -12} {2} {3}", resource.Index, fileName, className, name); } } } }
private KeyValuePair <ICacheFile, EngineDescription> LoadMap(string path, out IReader reader) { reader = new EndianReader(File.OpenRead(path), Endian.BigEndian); var cacheFile = CacheFileLoader.LoadCacheFile(reader, path, App.AssemblyStorage.AssemblySettings.DefaultDatabase, out EngineDescription buildInfo); return (new KeyValuePair <ICacheFile, EngineDescription>(cacheFile, buildInfo)); }
public IEnumerator Load(string source, Func <T, IEnumerator> finish = null, Action <string> error = null, Action <float, string> progress = null) { finishLoad = finish; errorLoad = error; progressLoad = progress; cacheSource = CacheFileLoader.CacheStreamKey(source); isInProgress = true; yield return(childLoader.Load(source)); }
public static async Task Create(Dictionary <string, string> arguments, Dictionary <string, string> options) { await Task.Delay(1); var cacheFilePath = arguments["cache"]; var projectDirectory = arguments["project"]; // Check file exists if (cacheFilePath == null || !File.Exists(cacheFilePath)) { throw new ComposerException("Cache doesn't exist"); } // Create project directory // TODO: think about logic if directory already exists if (!Directory.Exists(projectDirectory)) { Directory.CreateDirectory(projectDirectory); } // Create Project var project = new Models.Project { Title = "Example Halo Project", Version = "1.0.0", Description = "Simple project for testing the tool", Properties = new ProjectProperties { TagsFolder = "tags", GitEnabled = true } }; using (var stream = File.OpenRead(cacheFilePath)) using (var reader = new EndianReader(stream, Endian.BigEndian)) { EngineDescription engineDescription = null; EngineDatabase engineDatabase = XMLEngineDatabaseLoader.LoadDatabase("data/formats/engines.xml"); var cacheFile = CacheFileLoader.LoadCacheFile(reader, engineDatabase, out engineDescription); var stringIdTrie = new Trie(); if (cacheFile.StringIDs != null) { stringIdTrie.AddRange(cacheFile.StringIDs); } if (cacheFile.TagClasses.Any()) { LoadTags(project, cacheFile, projectDirectory, stringIdTrie); } } File.WriteAllText(Path.Combine(projectDirectory, "project.json"), JsonConvert.SerializeObject(project, Formatting.Indented)); }
public static IEnumerable <ICacheFile> LoadAllMaps(string[] paths, EngineDatabase db) { List <ICacheFile> result = new List <ICacheFile>(); foreach (string path in paths) { using var stream = new FileStream(path, FileMode.Open, FileAccess.Read); var reader = new EndianReader(stream, Endian.BigEndian); var cache = CacheFileLoader.LoadCacheFile(reader, Path.GetFileNameWithoutExtension(path), db); result.Add(cache); } return(result); }
private static ScriptTable LoadScripts(string path, EngineDatabase db, out EngineDescription desc) { using (var stream = File.OpenRead(path)) { EngineDescription engine; var reader = new EndianReader(stream, Endian.BigEndian); var cache = CacheFileLoader.LoadCacheFile(reader, Path.GetFileName(path), db, out engine); desc = engine; var scripts = cache.ScriptFiles[0].LoadScripts(reader); return(scripts); } }
public static bool applyPatch(ZipArchiveEntry zippedPatchFile, string patchFileName, string unmoddedMapPath, string outputPath) { createTmpDir(); try { zippedPatchFile.ExtractToFile(Config.modpack_dir + @"\tmp\" + patchFileName); } catch (IOException) { rmTmpDir(); createTmpDir(); zippedPatchFile.ExtractToFile(Config.modpack_dir + @"\tmp\" + patchFileName); } Patch currentPatch = LoadPatch(Config.modpack_dir + @"\tmp\" + patchFileName); // Copy the original map to the destination path IO.CopyFile(unmoddedMapPath, outputPath, true); //if modpack has written to unmoddedmap, take from backups // Open the destination map using (var stream = new EndianStream(File.Open(outputPath, FileMode.Open, FileAccess.ReadWrite), Endian.BigEndian)) { EngineDatabase engineDb = XMLEngineDatabaseLoader.LoadDatabase("Formats/Engines.xml"); ICacheFile cacheFile; try { cacheFile = CacheFileLoader.LoadCacheFile(stream, engineDb); } catch (NotSupportedException nse) { form1.showMsg("Error patching '" + patchFileName + "':" + nse.Message, "Error"); return(false); } if (!string.IsNullOrEmpty(currentPatch.BuildString) && cacheFile.BuildString != currentPatch.BuildString) { form1.showMsg("Unable to patch. That patch is for a map with a build version of " + currentPatch.BuildString + ", and the unmodified map file doesn't match that.", "Error"); return(false); } if (currentPatch.MapInternalName == null) { // Because Ascension doesn't include this, and ApplyPatch() will complain otherwise currentPatch.MapInternalName = cacheFile.InternalName; } // Apply the patch! try { PatchApplier.ApplyPatch(currentPatch, cacheFile, stream); } catch (ArgumentException ae) { form1.showMsg("There was an issue applying the patch file '" + patchFileName + "': " + ae.Message, "Info"); return(false); } } rmTmpDir(); return(true); }
private KeyValuePair <ICacheFile, EngineDescription> LoadMap(string path, out IReader reader) { reader = new EndianReader(File.OpenRead(path), Endian.BigEndian); reader.SeekTo(0); byte[] headerMagic = reader.ReadBlock(4); reader.Endianness = CacheFileLoader.DetermineCacheFileEndianness(headerMagic); var versionInfo = new CacheFileVersionInfo(reader); EngineDescription buildInfo = App.AssemblyStorage.AssemblySettings.DefaultDatabase.FindEngineByVersion(versionInfo.BuildString); return (new KeyValuePair <ICacheFile, EngineDescription>(new ThirdGenCacheFile(reader, buildInfo, path, versionInfo.BuildString), buildInfo)); }
public override IEnumerator BeforeLoad(string source) { //create query string from url memoryKey = CacheFileLoader.CacheStreamKey(source); //query from image dictionary //if we have a texture with this key //we can take it and break; if (memoryData.ContainsKey(memoryKey)) { InnerFinishLoad(memoryData[memoryKey]); yield break; } }
public static Dictionary <string, ScriptTable> LoadAllScriptFiles(string path, EngineDatabase db, out EngineDescription engineInfo) { Dictionary <string, ScriptTable> result = new Dictionary <string, ScriptTable>(); using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { var reader = new EndianReader(stream, Endian.BigEndian); var cache = CacheFileLoader.LoadCacheFile(reader, path, db, out engineInfo); if (cache.Type != CacheFileType.Shared && cache.Type != CacheFileType.SinglePlayerShared && cache.ScriptFiles.Length > 0) { foreach (var file in cache.ScriptFiles) { result.Add(file.Name, file.LoadScripts(reader)); } } } return(result); }
private void StartGeneration() { _isWorking = true; btnInputFolder.IsEnabled = btnOutputFolder.IsEnabled = btnGeneratePlugins.IsEnabled = false; MaskingPage.Visibility = Visibility.Visible; List <MapEntry> generatorMaps = GeneratorMaps.Where(m => m.IsSelected).ToList(); string outputPath = txtOutputFolder.Text; EngineDescription picked = null; string firstMap = generatorMaps.First().LocalMapPath; using (FileStream fs = File.OpenRead(firstMap)) { using (EndianReader reader = new EndianReader(fs, Endian.BigEndian)) { var matches = CacheFileLoader.FindEngineDescriptions(reader, App.AssemblyStorage.AssemblySettings.DefaultDatabase); if (matches.Count > 1) { picked = MetroEnginePicker.Show(firstMap, matches); } } } var worker = new BackgroundWorker(); worker.DoWork += (o, args) => worker_DoWork(o, args, generatorMaps, outputPath, worker, picked); worker.WorkerReportsProgress = true; worker.ProgressChanged += worker_ProgressChanged; worker.RunWorkerCompleted += worker_RunWorkerCompleted; worker.RunWorkerAsync(); }
// Patch Applying private void btnApplyPatch_Click(object sender, RoutedEventArgs e) { try { // Check the user isn't completly retarded if (!CheckAllApplyMandatoryFields() || currentPatch == null) { return; } // Check the output name if (cacheOutputName != "") { if (Path.GetFileNameWithoutExtension(txtApplyPatchOutputMap.Text) != cacheOutputName) { if (MetroMessageBox.Show("Warning", "This patch suggests to use the filename \"" + cacheOutputName + ".map\" to save this map. This filename may be required in order for the map to work correctly.\r\n\r\nAre you sure you want to save this map as \"" + Path.GetFileName(txtApplyPatchOutputMap.Text) + "\"?", MetroMessageBox.MessageBoxButtons.OkCancel) != MetroMessageBox.MessageBoxResult.OK) { Close(); return; } } } // Paths string unmoddedMapPath = txtApplyPatchUnmodifiedMap.Text; string outputPath = txtApplyPatchOutputMap.Text; // Copy the original map to the destination path File.Copy(unmoddedMapPath, outputPath, true); // Open the destination map using (var stream = new EndianStream(File.Open(outputPath, FileMode.Open, FileAccess.ReadWrite), Endian.BigEndian)) { EngineDatabase engineDb = XMLEngineDatabaseLoader.LoadDatabase("Formats/Engines.xml"); ICacheFile cacheFile = CacheFileLoader.LoadCacheFile(stream, outputPath, engineDb); if (currentPatch.MapInternalName != null && cacheFile.InternalName != currentPatch.MapInternalName) { MetroMessageBox.Show("Unable to apply patch", "Hold on there! That patch is for " + currentPatch.MapInternalName + ".map, and the unmodified map file you selected doesn't seem to match that. Find the correct file and try again."); return; } if (!string.IsNullOrEmpty(currentPatch.BuildString) && cacheFile.BuildString != currentPatch.BuildString) { MetroMessageBox.Show("Unable to apply patch", "Hold on there! That patch is for a map with a build version of" + currentPatch.BuildString + ", and the unmodified map file you selected doesn't seem to match that. Find the correct file and try again."); return; } // Apply the patch! if (currentPatch.MapInternalName == null) { currentPatch.MapInternalName = cacheFile.InternalName; } // Because Ascension doesn't include this, and ApplyPatch() will complain otherwise PatchApplier.ApplyPatch(currentPatch, cacheFile, stream); // Check for blf snaps if (cbApplyPatchBlfExtraction.IsChecked != null && (PatchApplicationPatchExtra.Visibility == Visibility.Visible && (bool)cbApplyPatchBlfExtraction.IsChecked)) { string extractDir = Path.GetDirectoryName(outputPath); string blfDirectory = Path.Combine(extractDir, "images"); string infDirectory = Path.Combine(extractDir, "info"); if (!Directory.Exists(blfDirectory)) { Directory.CreateDirectory(blfDirectory); } if (!Directory.Exists(infDirectory)) { Directory.CreateDirectory(infDirectory); } string infPath = Path.Combine(infDirectory, Path.GetFileName(currentPatch.CustomBlfContent.MapInfoFileName)); File.WriteAllBytes(infPath, currentPatch.CustomBlfContent.MapInfo); foreach (BlfContainerEntry blfContainerEntry in currentPatch.CustomBlfContent.BlfContainerEntries) { string blfPath = Path.Combine(blfDirectory, Path.GetFileName(blfContainerEntry.FileName)); File.WriteAllBytes(blfPath, blfContainerEntry.BlfContainer); } } } MetroMessageBox.Show("Patch Applied!", "Your patch has been applied successfully. Have fun!"); } catch (Exception ex) { MetroException.Show(ex); } }
private void btnCreatePatchModifiedMap_Click(object sender, RoutedEventArgs e) { var ofd = new OpenFileDialog { Title = "Assembly - Select a Modified Map file", Filter = "Blam Cache Files|*.map" }; if (ofd.ShowDialog() != DialogResult.OK) { return; } txtCreatePatchModifiedMap.Text = ofd.FileName; txtCreatePatchOutputName.Text = Path.GetFileNameWithoutExtension(ofd.FileName); var fileStream = new FileStream(ofd.FileName, FileMode.Open); byte[] headerMagic = new byte[4]; fileStream.Read(headerMagic, 0, 4); var endian = CacheFileLoader.DetermineCacheFileEndianness(headerMagic); var cacheStream = new EndianStream(fileStream, endian); var versionInfo = new CacheFileVersionInfo(cacheStream); _buildInfo = App.AssemblyStorage.AssemblySettings.DefaultDatabase.FindEngineByVersion(versionInfo.BuildString); if (_buildInfo != null && _buildInfo.Name != null) { switch (_buildInfo.Name) { case "Halo 2 Vista": cboxCreatePatchTargetGame.SelectedIndex = (int)TargetGame.Halo2Vista; break; case "Halo 3: ODST": cboxCreatePatchTargetGame.SelectedIndex = (int)TargetGame.Halo3ODST; break; default: if (_buildInfo.Name.Contains("MCC")) { cboxCreatePatchTargetGame.SelectedIndex = (int)TargetGame.MCC; } else if (_buildInfo.Name.Contains("Halo 3")) { cboxCreatePatchTargetGame.SelectedIndex = (int)TargetGame.Halo3; } else if (_buildInfo.Name.Contains("Halo: Reach")) { cboxCreatePatchTargetGame.SelectedIndex = (int)TargetGame.HaloReach; } else if (_buildInfo.Name.Contains("Halo 4")) { cboxCreatePatchTargetGame.SelectedIndex = (int)TargetGame.Halo4; } else { cboxCreatePatchTargetGame.SelectedIndex = 6; // Other } break; } } cacheStream.Dispose(); }
// Patch Creation private void btnCreatePatch_Click(object sender, RoutedEventArgs e) { try { // Check the user isn't completly retarded if (!CheckAllCreateMandatoryFields()) { return; } // Check the user isn't a skid if (!CheckAllCreateMetaFilesExists()) { return; } // Paths string cleanMapPath = txtCreatePatchUnModifiedMap.Text; string moddedMapPath = txtCreatePatchModifiedMap.Text; string outputPath = txtCreatePatchOutputPatch.Text; string previewImage = txtCreatePatchPreviewImage.Text; // Details string author = txtCreatePatchContentAuthor.Text; string desc = txtCreatePatchContentDescription.Text; string name = txtCreatePatchContentName.Text; string outputName = txtCreatePatchOutputName.Text; // Make dat patch var patch = new Patch { Author = author, Description = desc, Name = name, OutputName = outputName, Screenshot = String.IsNullOrEmpty(previewImage) ? null : File.ReadAllBytes(previewImage), BuildString = _buildInfo.Version, PC = String.IsNullOrEmpty(_buildInfo.GameExecutable) ? false : true }; EndianReader originalReader = null; EndianReader newReader = null; try { originalReader = new EndianReader(File.OpenRead(cleanMapPath), Endian.BigEndian); newReader = new EndianReader(File.OpenRead(moddedMapPath), Endian.BigEndian); ICacheFile originalFile = CacheFileLoader.LoadCacheFile(originalReader, cleanMapPath, App.AssemblyStorage.AssemblySettings.DefaultDatabase); ICacheFile newFile = CacheFileLoader.LoadCacheFile(newReader, moddedMapPath, App.AssemblyStorage.AssemblySettings.DefaultDatabase); if (cbCreatePatchHasCustomMeta.IsChecked != null && (bool)cbCreatePatchHasCustomMeta.IsChecked && cboxCreatePatchTargetGame.SelectedIndex < 4) { var targetGame = (TargetGame)cboxCreatePatchTargetGame.SelectedIndex; byte[] mapInfo = File.ReadAllBytes(txtCreatePatchMapInfo.Text); var mapInfoFileInfo = new FileInfo(txtCreatePatchMapInfo.Text); FileInfo blfFileInfo; patch.CustomBlfContent = new BlfContent(mapInfoFileInfo.FullName, mapInfo, targetGame); #region Blf Data if (PatchCreationBlfOption0.Visibility == Visibility.Visible) { blfFileInfo = new FileInfo(txtCreatePatchblf0.Text); patch.CustomBlfContent.BlfContainerEntries.Add(new BlfContainerEntry(blfFileInfo.Name, File.ReadAllBytes(blfFileInfo.FullName))); } if (PatchCreationBlfOption1.Visibility == Visibility.Visible) { blfFileInfo = new FileInfo(txtCreatePatchblf1.Text); patch.CustomBlfContent.BlfContainerEntries.Add(new BlfContainerEntry(blfFileInfo.Name, File.ReadAllBytes(blfFileInfo.FullName))); } if (PatchCreationBlfOption2.Visibility == Visibility.Visible) { blfFileInfo = new FileInfo(txtCreatePatchblf2.Text); patch.CustomBlfContent.BlfContainerEntries.Add(new BlfContainerEntry(blfFileInfo.Name, File.ReadAllBytes(blfFileInfo.FullName))); } if (PatchCreationBlfOption3.Visibility == Visibility.Visible) { blfFileInfo = new FileInfo(txtCreatePatchblf3.Text); patch.CustomBlfContent.BlfContainerEntries.Add(new BlfContainerEntry(blfFileInfo.Name, File.ReadAllBytes(blfFileInfo.FullName))); } #endregion } PatchBuilder.BuildPatch(originalFile, originalReader, newFile, newReader, patch); } finally { if (originalReader != null) { originalReader.Dispose(); } if (newReader != null) { newReader.Dispose(); } } IWriter output = new EndianWriter(File.Open(outputPath, FileMode.Create, FileAccess.Write), Endian.BigEndian); AssemblyPatchWriter.WritePatch(patch, output); output.Dispose(); MetroMessageBox.Show("Patch Created!", "Your patch has been created in the designated location. Happy sailing, modder!"); } catch (Exception ex) { MetroException.Show(ex); } }
private static void Main(string[] args) { if (args.Length != 3) { Console.WriteLine("Usage: mapexpand <map file> <section> <page count>"); Console.WriteLine(); Console.WriteLine("Available sections:"); Console.WriteLine(" stringidindex"); Console.WriteLine(" stringiddata"); Console.WriteLine(" tagnameindex"); Console.WriteLine(" tagnamedata"); Console.WriteLine(" resource"); Console.WriteLine(" tag"); return; } int pageCount; if (!int.TryParse(args[2], out pageCount) || pageCount <= 0) { Console.WriteLine("The page count must be a positive integer."); return; } Console.WriteLine("Reading..."); var stream = new EndianStream(File.Open(args[0], FileMode.Open, FileAccess.ReadWrite), Endian.BigEndian); var database = XMLEngineDatabaseLoader.LoadDatabase("Formats/Engines.xml"); var buildInfo = CacheFileLoader.FindEngineDescription(stream, database); if (buildInfo.Engine != EngineType.ThirdGeneration) { Console.WriteLine("Only third-generation map files are supported."); return; } var cacheFile = new ThirdGenCacheFile(stream, buildInfo, args[0]); FileSegmentGroup area; FileSegment section; int pageSize; switch (args[1]) { case "stringidindex": area = cacheFile.StringArea; section = cacheFile.StringIDIndexTable; pageSize = 0x1000; break; case "stringiddata": area = cacheFile.StringArea; section = cacheFile.StringIDDataTable; pageSize = 0x1000; break; case "tagnameindex": area = cacheFile.StringArea; section = cacheFile.FileNameIndexTable; pageSize = 0x1000; break; case "tagnamedata": area = cacheFile.StringArea; section = cacheFile.FileNameDataTable; pageSize = 0x1000; break; case "resource": area = null; section = cacheFile.RawTable; pageSize = 0x1000; break; case "tag": area = cacheFile.MetaArea; section = cacheFile.MetaArea.Segments[0]; pageSize = 0x10000; break; default: Console.WriteLine("Invalid section name: \"{0}\"", args[1]); return; } Console.WriteLine("- Engine version: {0}", buildInfo.BuildVersion); Console.WriteLine("- Internal name: {0}", cacheFile.InternalName); Console.WriteLine("- Scenario name: {0}", cacheFile.ScenarioName); Console.WriteLine(); Console.WriteLine("Injecting empty pages..."); var injectSize = pageCount * pageSize; section.Resize(section.Size + injectSize, stream); Console.WriteLine("Adjusting the header..."); cacheFile.SaveChanges(stream); stream.Dispose(); Console.WriteLine(); var offset = section.Offset; if (section.ResizeOrigin == SegmentResizeOrigin.End) { offset += section.ActualSize - injectSize; } if (area != null) { Console.WriteLine("Successfully injected 0x{0:X} bytes at 0x{1:X} (offset 0x{2:X}).", injectSize, area.BasePointer, offset); } else { Console.WriteLine("Successfully injected 0x{0:X} bytes at offset 0x{1:X}.", injectSize, offset); } }
// TODO (Dragon): support CEA 360 with LZX private static CompressionState DetermineState(IReader reader, EngineDatabase engineDb, out EngineDescription engineInfo, out StructureValueCollection headerValues) { headerValues = null; // not all compressed maps have a decompressed header // so we handle that here try { // Load engine version info engineInfo = CacheFileLoader.FindEngineDescription(reader, engineDb); } catch (ArgumentException e) // map had no header, assume its CEA { using (MemoryStream ms_header_out = new MemoryStream()) { // first chunk offset is at 0x4 reader.SeekTo(0x4); int first_chunk_offset = reader.ReadInt32(); int second_chunk_offset = reader.ReadInt32(); int first_chunk_size = second_chunk_offset - first_chunk_offset - 6; reader.SeekTo(first_chunk_offset); // CEA 360 stores an 0xFF, use it for ID byte cea_360_ff_byte = reader.ReadByte(); if (cea_360_ff_byte == 0xFF) // CEA 360 { // TODO (Dragon): decompress first chunk to get the header with lzx throw new InvalidOperationException("Assembly does not support CEA 360 decompression (missing LZX)"); } else // assume CEA MCC { reader.SeekTo(first_chunk_offset + 6); byte[] first_chunk_bytes = reader.ReadBlock(first_chunk_size); using (MemoryStream ms_header_comp = new MemoryStream(first_chunk_bytes)) { //ms_header_comp.Write(first_chunk_bytes, 0, first_chunk_size); using (DeflateStream ds = new DeflateStream(ms_header_comp, CompressionMode.Decompress)) { ds.CopyTo(ms_header_out); } } } EndianReader header_reader = new EndianReader(ms_header_out, Endian.LittleEndian); engineInfo = CacheFileLoader.FindEngineDescription(header_reader, engineDb); } } // if engine wasnt set its because we couldnt read a proper header, throw an exception if (engineInfo == null || !engineInfo.UsesCompression) { return(CompressionState.Null); } if (engineInfo.Engine == EngineType.FirstGeneration) { return(AnalyzeFirstGen(reader, engineInfo, out headerValues)); } else if (engineInfo.Engine == EngineType.SecondGeneration) { return(AnalyzeSecondGen(reader, engineInfo, out headerValues)); } else { return(CompressionState.Null); } }
// TODO (Dragon): support CEA 360 with LZX private static CompressionState DetermineState(IReader reader, EngineDatabase engineDb, out EngineType type, out EngineDescription engineInfo) { CacheFileVersionInfo version = null; // not all compressed maps have a decompressed header // so we handle that here try { // Attempt to set the reader's endianness based upon the file's header magic reader.SeekTo(0); byte[] headerMagic = reader.ReadBlock(4); reader.Endianness = CacheFileLoader.DetermineCacheFileEndianness(headerMagic); // Load engine version info version = new CacheFileVersionInfo(reader); } catch (ArgumentException e) // map had no header, assume its CEA { using (MemoryStream ms_header_out = new MemoryStream()) { // first chunk offset is at 0x4 reader.SeekTo(0x4); int first_chunk_offset = reader.ReadInt32(); int second_chunk_offset = reader.ReadInt32(); int first_chunk_size = second_chunk_offset - first_chunk_offset - 6; reader.SeekTo(first_chunk_offset); // CEA 360 stores an 0xFF, use it for ID byte cea_360_ff_byte = reader.ReadByte(); if (cea_360_ff_byte == 0xFF) // CEA 360 { // TODO (Dragon): decompress first chunk to get the header with lzx throw new InvalidOperationException("assembly does not support CEA 360 decompression (missing LZX)"); } else // assume CEA MCC { reader.SeekTo(first_chunk_offset + 6); byte[] first_chunk_bytes = reader.ReadBlock(first_chunk_size); using (MemoryStream ms_header_comp = new MemoryStream(first_chunk_bytes)) { //ms_header_comp.Write(first_chunk_bytes, 0, first_chunk_size); using (DeflateStream ds = new DeflateStream(ms_header_comp, CompressionMode.Decompress)) { ds.CopyTo(ms_header_out); } } } EndianReader header_reader = new EndianReader(ms_header_out, Endian.LittleEndian); version = new CacheFileVersionInfo(header_reader); } } // if version wasnt set its because we couldnt read a proper header, throw an exception if (version == null) { throw new NullReferenceException("Failed to create CacheFileVersionInfo from map header"); } type = version.Engine; engineInfo = engineDb.FindEngineByVersion(version.BuildString); if (version.Engine == EngineType.FirstGeneration) { if (engineInfo == null) { return(CompressionState.Null); } if (!engineInfo.UsesCompression) { return(CompressionState.Null); } return(AnalyzeFirstGen(reader, engineInfo)); } else if (version.Engine == EngineType.SecondGeneration) { if (engineInfo == null) { return(CompressionState.Null); } if (!engineInfo.UsesCompression) { return(CompressionState.Null); } return(AnalyzeSecondGen(reader, engineInfo)); } else { return(CompressionState.Null); } }
static void Main(string[] args) { if (args.Length != 3) { Console.WriteLine("Usage: BlockAlignmentScanner <map dir> <in plugin dir> <out plugin dir>"); return; } var mapDir = args[0]; var inDir = args[1]; var outDir = args[2]; Console.WriteLine("Loading plugins..."); var pluginsByClass = new Dictionary <string, XDocument>(); foreach (var pluginPath in Directory.EnumerateFiles(inDir, "*.xml")) { Console.WriteLine("- {0}", pluginPath); var document = XDocument.Load(pluginPath); var className = Path.GetFileNameWithoutExtension(pluginPath); pluginsByClass[className] = document; } Console.WriteLine("Loading engine database..."); var exeDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var dbPath = Path.Combine(exeDir, "Formats", "Engines.xml"); var db = XMLEngineDatabaseLoader.LoadDatabase(dbPath); Console.WriteLine("Processing maps..."); var alignsByElem = new Dictionary <XElement, int>(); foreach (var mapPath in Directory.EnumerateFiles(mapDir, "*.map")) { Console.WriteLine("- {0}", Path.GetFileName(mapPath)); using (var reader = new EndianReader(File.OpenRead(mapPath), Endian.BigEndian)) { var map = CacheFileLoader.LoadCacheFile(reader, db); var visitedTagBlocks = new HashSet <uint>(); foreach (var tag in map.Tags) { if (tag == null || tag.Class == null || tag.MetaLocation == null) { continue; } // Get the plugin for the tag var className = CharConstant.ToString(tag.Class.Magic); XDocument plugin; if (!pluginsByClass.TryGetValue(className, out plugin)) { continue; } // Process it var baseOffset = tag.MetaLocation.AsOffset(); var baseElement = plugin.Element("plugin"); DetectAlignment(map, reader, baseOffset, baseElement, alignsByElem, visitedTagBlocks); } } } Console.WriteLine("Adjusting plugins..."); foreach (var align in alignsByElem) { if (align.Value != 4) { Console.WriteLine("- \"{0}\" -> align 0x{1:X}", align.Key.Attribute("name").Value, align.Value); align.Key.SetAttributeValue(XName.Get("align"), "0x" + align.Value.ToString("X")); } } if (!Directory.Exists(outDir)) { Directory.CreateDirectory(outDir); } Console.WriteLine("Saving plugins..."); foreach (var plugin in pluginsByClass) { var outPath = Path.Combine(outDir, plugin.Key + ".xml"); Console.WriteLine("- {0}", outPath); var settings = new XmlWriterSettings(); settings.Indent = true; settings.IndentChars = "\t"; using (var writer = XmlWriter.Create(outPath, settings)) plugin.Value.Save(writer); } }
private static void Main(string[] args) { var ofd = new OpenFileDialog(); ofd.Title = "Open Cache File"; ofd.Filter = "Blam Cache Files|*.map"; if (ofd.ShowDialog() != DialogResult.OK) { return; } var sfd = new SaveFileDialog(); sfd.Title = "Save String Dump"; sfd.Filter = "Text Files|*.txt"; if (sfd.ShowDialog() != DialogResult.OK) { return; } string mapPath = ofd.FileName; string dumpPath = sfd.FileName; EngineDatabase engineDb = XMLEngineDatabaseLoader.LoadDatabase("Formats/Engines.xml"); ICacheFile cacheFile; LanguagePack locales; using (IReader reader = new EndianReader(File.OpenRead(mapPath), Endian.BigEndian)) { Console.WriteLine("Loading cache file..."); cacheFile = CacheFileLoader.LoadCacheFile(reader, mapPath, engineDb); Console.WriteLine("Loading locales..."); locales = cacheFile.Languages.LoadLanguage(GameLanguage.English, reader); } var output = new StreamWriter(dumpPath); output.WriteLine("Input file: {0}.map", cacheFile.InternalName); output.WriteLine(); // Sort locales by stringID var localesById = new List <LocalizedString>(); localesById.AddRange(locales.StringLists.SelectMany(l => l.Strings).Where(s => s != null && s.Value != null)); localesById.Sort((x, y) => x.Key.Value.CompareTo(y.Key.Value)); // Dump locales Console.WriteLine("Dumping locales..."); output.WriteLine("---------------"); output.WriteLine("English Locales"); output.WriteLine("---------------"); foreach (LocalizedString str in localesById) { if (str != null) { output.WriteLine("{0} = \"{1}\"", str.Key, str.Value); } } output.WriteLine(); // Dump stringIDs Console.WriteLine("Dumping stringIDs..."); output.WriteLine("---------"); output.WriteLine("StringIDs"); output.WriteLine("---------"); int index = 0; foreach (string str in cacheFile.StringIDs) { if (str != null) { output.WriteLine("0x{0:X} = \"{1}\"", index, str); } else { output.WriteLine("0x{0:X} = (null)", index); } index++; } output.Close(); Console.WriteLine("Done!"); }