コード例 #1
0
        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);
                }
            }
        }