public static bool UpdateEntry(BMG bmg, int entryIndex, byte[] data, string text) { try { var entry = bmg.INF_Section.Items[entryIndex]; var sizeDelta = data.Length - (int)entry.Length; if (sizeDelta != 0) { for (var i = entryIndex + 1; i < bmg.INF_Section.Items.Length; i++) { bmg.INF_Section.Items[i].Text_Offset = (uint)(bmg.INF_Section.Items[i].Text_Offset + sizeDelta); } } entry.Text = text; entry.Length = (uint)data.Length; entry.Data = data; bmg.INF_Section.Items[entryIndex] = entry; return(true); } catch { return(false); } }
public static async void SaveStrings(string FilePath) { BMG bmg = await Decode(FilePath, null); using (StreamWriter Writer = File.CreateText(Path.GetDirectoryName(FilePath) + "/" + Path.GetFileNameWithoutExtension(FilePath) + "_Output.txt")) { foreach (string Message in bmg.DAT_Section.Strings) { Writer.WriteLine(Message); } Writer.Flush(); Writer.Close(); } }
public static async Task <BMG> Decode(string path, List <System.Drawing.Color> colors, Delegate reportProgressFunc = null) { using (var fileStream = new FileStream(path, FileMode.Open)) { Stream stream = fileStream; // TODO: Remove the MainWindow static variable reference. if (MainWindow.SelectedCharacterSet == CharacterSet.WildWorld && LZ77.IsLz77Compressed(fileStream)) { Debug.WriteLine($"In file size: {fileStream.Length:X}"); stream = new LZ77().Decompress(fileStream); stream.Seek(0, SeekOrigin.Begin); // TEST var recompress = new LZ77().Compress(stream); Debug.WriteLine($"Recompressed file size: {recompress.Length:X}"); stream.Seek(0, SeekOrigin.Begin); // Test write /*using (var fStream = new FileStream(Path.Combine(Path.GetDirectoryName(path), "decompressTest.bmg"), * FileMode.Create)) * { * var data = ((MemoryStream)recompress).ToArray(); * fStream.Write(data, 0, data.Length); * recompress.Close(); * }*/ } using (var reader = new BinaryReader(stream)) { #if DEBUG var watch = Stopwatch.StartNew(); watch.Start(); #endif var bmg = new BMG { FileType = new string(reader.ReadChars(8)), Size = reader.ReadUInt32().Reverse() }; // Use size to determine endianness if (bmg.Size != 0 && bmg.Size > reader.BaseStream.Length) { bmg.IsLittleEndian = true; } // Confirm Size isn't 0 if (bmg.Size == 0) { //Console.WriteLine("BMG Size was zero. Setting it to the filesize."); //bmg.Size = (ulong)Reader.BaseStream.Length; } bmg.SectionCount = reader.ReadUInt32(); bmg.Encoding = reader.ReadUInt32(); if (!bmg.IsLittleEndian) { bmg.SectionCount = bmg.SectionCount.Reverse(); bmg.Encoding = bmg.Encoding.Reverse(); } // Debug Lines Console.WriteLine("BMG Section Count: " + bmg.SectionCount); Console.WriteLine("UTF16: " + (bmg.Encoding == 0x02000000)); // Create our Text Info Section (INF) reader.BaseStream.Position = 0x20; bmg.INF_Section.SectionType = new string(reader.ReadChars(4)); bmg.INF_Section.Size = reader.ReadUInt32(); bmg.INF_Section.MessageCount = reader.ReadUInt16(); bmg.INF_Section.INF_Size = reader.ReadUInt16(); bmg.INF_Section.Unknown = reader.ReadUInt32(); if (!bmg.IsLittleEndian) { bmg.INF_Section.Size = bmg.INF_Section.Size.Reverse(); bmg.INF_Section.MessageCount = bmg.INF_Section.MessageCount.Reverse(); bmg.INF_Section.INF_Size = bmg.INF_Section.INF_Size.Reverse(); bmg.INF_Section.Unknown = bmg.INF_Section.Unknown.Reverse(); } bmg.INF_Section.Items = new BMG_INF_Item[bmg.INF_Section.MessageCount]; //Debug Lines Console.WriteLine("INF Size: 0x" + bmg.INF_Section.Size.ToString("X")); Console.WriteLine("INF Message Count: " + bmg.INF_Section.MessageCount); Console.WriteLine("INF CharSize: 0x" + bmg.INF_Section.INF_Size.ToString("X")); // Load our Text Info Items reader.BaseStream.Position = 0x30; for (var i = 0; i < bmg.INF_Section.MessageCount; i++) { var item = new BMG_INF_Item { Text_Offset = bmg.IsLittleEndian ? reader.ReadUInt32() : reader.ReadUInt32().Reverse() }; bmg.INF_Section.Items[i] = item; if (bmg.INF_Section.INF_Size > 4) { // TODO: This is a hack. We should figure out what the additional size means reader.BaseStream.Seek(bmg.INF_Section.INF_Size - 4, SeekOrigin.Current); } } // Create our Text Data Section (DAT) reader.BaseStream.Position = bmg.Size == 0 ? bmg.INF_Section.Size : bmg.INF_Section.Size + 0x20; // + 0x20 for the bgm header if (Encoding.ASCII.GetString(reader.ReadBytes(4)) != "DAT1") { var dat1Found = false; while (!dat1Found) { if (Encoding.ASCII.GetString(reader.ReadBytes(4)) != "DAT1") { continue; } reader.BaseStream.Position -= 4; Debug.WriteLine("Found DAT1: 0x" + reader.BaseStream.Position.ToString("X")); dat1Found = true; } } else { reader.BaseStream.Position -= 4; } bmg.DAT_Section.Offset = (int)reader.BaseStream.Position; bmg.DAT_Section.SectionType = Encoding.ASCII.GetString(reader.ReadBytes(4)); bmg.DAT_Section.Size = reader.ReadUInt32(); if (!bmg.IsLittleEndian) { bmg.DAT_Section.Size = bmg.DAT_Section.Size.Reverse(); } bmg.DAT_Section.Strings = new string[bmg.INF_Section.MessageCount]; long stringStartOffset = bmg.DAT_Section.Offset + 0x8; await Task.Run(() => { // TODO: Move this static reference out of here. var parser = Parser.GetParser(MainWindow.SelectedCharacterSet); for (var i = 0; i < bmg.INF_Section.MessageCount; i++) { reader.BaseStream.Position = stringStartOffset + bmg.INF_Section.Items[i].Text_Offset; long endingOffset; if (i == bmg.INF_Section.MessageCount - 1) { endingOffset = bmg.DAT_Section.Size > 0 ? bmg.DAT_Section.Offset + bmg.DAT_Section.Size : reader.BaseStream.Length; } else { endingOffset = stringStartOffset + bmg.INF_Section.Items[i + 1].Text_Offset; } var startingOffset = reader.BaseStream.Position; // TODO: Wild World has a case where if the next INF entry is 0, the message id? or something is set to the next value after that // This means that each entry is 0xC in size max. var readSize = (int)(endingOffset - startingOffset); if (readSize < 0) { Console.WriteLine($"Read size is less than 0 for entry {i:X4}"); } bmg.INF_Section.Items[i].Data = reader.ReadBytes(readSize); bmg.INF_Section.Items[i].Text = MainWindow.SelectedCharacterSet == CharacterSet.DoubutsuNoMoriPlus ? TextUtility.Decode(bmg.INF_Section.Items[i].Data, colors) : parser.Decode(bmg.INF_Section.Items[i].Data); bmg.INF_Section.Items[i].Length = (uint)(endingOffset - startingOffset); if (reportProgressFunc != null && i % 50 == 0) { Application.Current.Dispatcher.Invoke(new Action(() => reportProgressFunc.DynamicInvoke(i, bmg.INF_Section.MessageCount))); } } return(bmg); }); if (reportProgressFunc != null) { Application.Current.Dispatcher.Invoke(new Action(() => reportProgressFunc.DynamicInvoke(bmg.INF_Section.MessageCount, bmg.INF_Section.MessageCount))); } #if DEBUG watch.Stop(); Debug.WriteLine($"Decode time elapsed: {watch.ElapsedMilliseconds} ms"); #endif return(bmg); } } }
public static bool Write(BMG File, string Path) { try { if (System.IO.File.Exists(Path)) { System.IO.File.Delete(Path); } using (BinaryWriter Writer = new BinaryWriter(new FileStream(Path, FileMode.OpenOrCreate))) { // Write BMG Header Writer.Write(Encoding.ASCII.GetBytes("MESGbmg1")); // File Identifier Writer.Write(BitConverter.GetBytes(File.Size).Reverse().ToArray()); Writer.Write(BitConverter.GetBytes(File.SectionCount).Reverse().ToArray()); Writer.Write(BitConverter.GetBytes(File.Encoding).Reverse().ToArray()); Writer.Write(new byte[0xC]); // Padding // Calculate INF Section Size File.INF_Section.Size = 0x10 + File.INF_Section.INF_Size * (uint)File.INF_Section.Items.Length; // Assure that the INF Section is aligned to a 32 byte grid. if (File.INF_Section.Size % 32 != 0) { File.INF_Section.Size += 32 - File.INF_Section.Size % 32; } // Write INF Header Writer.Write(Encoding.ASCII.GetBytes("INF1")); Writer.Write(BitConverter.GetBytes(File.INF_Section.Size).Reverse().ToArray()); Writer.Write(BitConverter.GetBytes(File.INF_Section.MessageCount).Reverse().ToArray()); Writer.Write(BitConverter.GetBytes(File.INF_Section.INF_Size).Reverse().ToArray()); Writer.Write(BitConverter.GetBytes(File.INF_Section.Unknown).Reverse().ToArray()); // Write INF data for (int i = 0; i < File.INF_Section.Items.Length; i++) { Writer.Write(BitConverter.GetBytes(File.INF_Section.Items[i].Text_Offset).Reverse().ToArray()); } // Add padding as needed Writer.Write(new byte[File.DAT_Section.Offset - Writer.BaseStream.Position]); // Set the correct DAT size. File.DAT_Section.Size = 8 + File.INF_Section.Items[0].Text_Offset + File.INF_Section.Items.Aggregate((uint)0, (current, b) => current + (uint)b.Data.Length); // Write DAT Header Writer.Write(Encoding.ASCII.GetBytes("DAT1")); Writer.Write(BitConverter.GetBytes(File.DAT_Section.Size).Reverse().ToArray()); // Write padding to data Writer.Write(new byte[File.INF_Section.Items[0].Text_Offset]); // Write DAT data for (int i = 0; i < File.INF_Section.Items.Length; i++) { Writer.Write(File.INF_Section.Items[i].Data); } Writer.Flush(); } return(true); } catch { return(false); } }