解析BMD文件格式结构

时间:2016-12-10 13:57:59

标签: c# .net vb.net reverse-engineering file-format

首先也是最重要的一点,我想澄清一下,在这个问题上没有任何恶意,所有这一切的目的都是为了对文件中包含的单词和短语进行语言翻译。格式,就像那样。

其次,我正在寻找任何类型的帮助,比如只是一个可以帮助我继续的解释,或者可能是一个C#或VB.NET代码,说明了解决这个问题的方法。

我正在处理这个问题的文件是:

其文件名为 datBootsHelp.bmd ,其文件格式为 BMD ,这是一个特权,但是,存在某些 wikis 的信息。< / p>

BMD 文件格式在标题后面包含两个表,我需要programaticaly解析然后表示存储在第二个表中的信息,以便能够将其导出到带有人的纯文本文件 - 可读语法然后翻译导出的文本。

这是我的意思(部分)表格的一个例子: enter image description here

在下面的下图中,我将展示此文件格式的已知规范,以及我的其他评论,包括我所做的进展以及我获得的分析 datBootsHelp.bmd 我在这个帖子的开头分享了。

enter image description here

标有绿色标记的行是我设法定位并以编程方式解析而没有问题的值,红叉的行是尚未行的行,带有问号的行是偏移量给我带来了麻烦,所以它阻止我正确使用红十字的价值...

我遇到的问题是我无法确定在第一个表的末尾分隔哪个偏移量,第二个表的开头,也不知道哪个是确切的长度(计算零/空字节,它(第二个表格的字段/文本),也就是说,我无法理解文本标识符的开始位置,文本标识的长度/文本容量,文本描述的开始位置以及文本长度/文本容量。

如果我手工分析和比较每个人的每个领域,试图找出字段的固定长度,它给我不同的长度,所以我不蹲任何东西......但这只是意味着我分析这是错误的,因为它当然必须有最大长度。

据我所知,第一个表以偏移 814 0x32C )结束,如上图所示。在下图中,我从该偏移量开始选择,直到找到第一个文本标识符的第一个字母的开头...并且我在中间有2个字节( A4 1D )不知道他们是什么,但我认为&#34;未使用&#34;为此目的:

enter image description here

最后,如果有帮助的话,我将在这里分享用VB.NET编写的源代码的基本部分,我开发了算法来读取头数据并解析第一个表。

''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Represents the BMD File Format.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <remarks>
''' <see href="http://datacrystal.romhacking.net/wiki/Persona_3_and_4/BMD_%28File_Format%29"/>
''' </remarks>
''' ----------------------------------------------------------------------------------------------------
Public NotInheritable Class BMDFormat

#Region " Private Fields "

    ''' <summary>
    ''' Specifies the offset where the header begins.
    ''' </summary>
    Private ReadOnly HeaderOffset As Integer = &H0

    ''' <summary>
    ''' Specifies the offset where the non-text table begins. This is the first table in the BMD file format.
    ''' </summary>
    Private ReadOnly TableNonTextOffset As Integer = &H24

    ''' <summary>
    ''' Specifies the offset where the text table begins. This is the second table in the BMD file format.
    ''' </summary>
    Private TableTextOffset As Integer ' The value is determined later, at the end of the TableNonTextOffset table.

    ''' <summary>
    ''' Contains the offset location and length of the BMD header offsets.
    ''' </summary>
    Private ReadOnly HeaderOffsets As New Dictionary(Of String, KeyValuePair(Of Integer, Integer))(StringComparer.Ordinal) From {
        {"ChunkID", New KeyValuePair(Of Integer, Integer)(&H0, Nothing)},
        {"Size", New KeyValuePair(Of Integer, Integer)(&H4, &H5)},
        {"MagicIdentifier", New KeyValuePair(Of Integer, Integer)(&H8, &HB)},
        {"EndOfTextTable", New KeyValuePair(Of Integer, Integer)(&H10, &H11)},
        {"OffsetsAmount", New KeyValuePair(Of Integer, Integer)(&H14, &H15)},
        {"EntriesAmount", New KeyValuePair(Of Integer, Integer)(&H18, &H19)}
    } ' Friendly-Name, {Start-Offset, End-Offset}

#End Region

#Region " Properties "

    ''' <summary>
    ''' Gets the raw byte-data of the BMD file.
    ''' </summary>
    Public ReadOnly Property RawData As Byte()
        Get
            Return Me.rawDataB
        End Get
    End Property
    Private ReadOnly rawDataB As Byte()

    ''' <summary>
    ''' Gets the Chunk ID of the file format.
    ''' </summary>
    Public ReadOnly Property ChunkID As Byte
        Get
            Return Me.chunkIDB
        End Get
    End Property
    Private chunkIDB As Byte

    ''' <summary>
    ''' Gets the size of the file.
    ''' </summary>
    Public ReadOnly Property Size As Short
        Get
            Return Me.sizeB
        End Get
    End Property
    Private sizeB As Short

    ''' <summary>
    ''' Gets the Magic Identifier of the file format.
    ''' </summary>
    Public ReadOnly Property MagicIdentifier As Byte()
        Get
            Return Me.magicIdentifierB
        End Get
    End Property
    Private magicIdentifierB As Byte()

    ''' <summary>
    ''' Gets the end of text table of the file format.
    ''' </summary>
    Public ReadOnly Property EndOfTextTable As Short
        Get
            Return Me.endOfTextTableB
        End Get
    End Property
    Private endOfTextTableB As Short

    ''' <summary>
    ''' Gets the amount of offsets in offset table of the file format.
    ''' </summary>
    Public ReadOnly Property OffsetsAmount As Short
        Get
            Return Me.offsetsAmountB
        End Get
    End Property
    Private offsetsAmountB As Short

    ''' <summary>
    ''' Gets the amount of entries of the file format.
    ''' </summary>
    Public ReadOnly Property EntriesAmount As Short
        Get
            Return Me.entriesAmountB
        End Get
    End Property
    Private entriesAmountB As Short

#End Region

#Region " Constructors "

    Private Sub New()
    End Sub

    Public Sub New(ByVal filepath As String)
        Me.New(File.ReadAllBytes(filepath))
    End Sub

    Public Sub New(ByVal file As FileInfo)
        Me.New(file.FullName)
    End Sub

    Public Sub New(ByVal raw As Byte())
        Me.rawDataB = raw
        Me.ReadHeader()
    End Sub

#End Region

#Region " Private Methods "

    ''' <summary>
    ''' Reads the BMD header.
    ''' </summary>
    Private Sub ReadHeader()

        Dim chunkID As KeyValuePair(Of Integer, Integer) = Me.HeaderOffsets("ChunkID")
        Dim size As KeyValuePair(Of Integer, Integer) = Me.HeaderOffsets("Size")
        Dim magicIdentifier As KeyValuePair(Of Integer, Integer) = Me.HeaderOffsets("MagicIdentifier")
        Dim endOfTextTable As KeyValuePair(Of Integer, Integer) = Me.HeaderOffsets("EndOfTextTable")
        Dim offsetsAmount As KeyValuePair(Of Integer, Integer) = Me.HeaderOffsets("OffsetsAmount")
        Dim entriesAmount As KeyValuePair(Of Integer, Integer) = Me.HeaderOffsets("EntriesAmount")

        Me.magicIdentifierB = Me.GetBytes(Me.HeaderOffset, magicIdentifier.Key, magicIdentifier.Value)
        Me.chunkIDB = Me.GetByte(Me.HeaderOffset, chunkID.Key)
        Me.sizeB = BitConverter.ToInt16(Me.GetBytes(Me.HeaderOffset, size.Key, size.Value), 0)
        Me.endOfTextTableB = BitConverter.ToInt16(Me.GetBytes(Me.HeaderOffset, endOfTextTable.Key, endOfTextTable.Value), 0)
        Me.offsetsAmountB = BitConverter.ToInt16(Me.GetBytes(Me.HeaderOffset, offsetsAmount.Key, offsetsAmount.Value), 0)
        Me.entriesAmountB = BitConverter.ToInt16(Me.GetBytes(Me.HeaderOffset, entriesAmount.Key, entriesAmount.Value), 0)

        Me.TableTextOffset = Me.TableNonTextOffset + (8 * (entriesAmountB - 1)) + 2

        Me.ReadNonTextTable()
        Me.ReadTextTable()

    End Sub

    ''' <summary>
    ''' Reads the non-text table of the BMD file format.
    ''' <para></para>
    ''' This table seems not useful.
    ''' </summary>
    Private Sub ReadNonTextTable()

        For x As Integer = 0 To (entriesAmountB - 1)

            Dim offset As Integer = Me.TableNonTextOffset + (8 * x)
            Dim data As Byte() = Me.GetBytes(offset, 0, 1)
            Dim value As Short = BitConverter.ToInt16(data, 0)

#If DEBUG Then
            Debug.WriteLine(String.Format("Entry.Index.: {0}", (x + 1)))
            Debug.WriteLine(String.Format("Start.Offset: DEC={0,-4} HEX=0x{1}", offset, offset.ToString("X")))
            Debug.WriteLine(String.Format("Raw.Bytes...: {0}", String.Join(" ", From b As Byte In data Select b.ToString("X"))))
            Debug.WriteLine(String.Format("Int16.Value.: {0}", value))
            Debug.WriteLine(String.Empty)
#End If

        Next

    End Sub

    ''' <summary>
    ''' Reads the text table of the BMD file format.
    ''' <para></para>
    ''' This table contains the items identifiers and their descriptions for further translation.
    ''' </summary>
    Private Sub ReadTextTable()

        For x As Integer = 0 To ...
            ' ...?
        Next

    End Sub

    Private Function GetByte(ByVal start As Integer, ByVal offset As Integer) As Byte
        Return Buffer.GetByte(array:=Me.RawData, index:=start + offset)
    End Function

    Private Function GetBytes(ByVal start As Integer, ByVal from As Integer, ByVal [to] As Integer) As Byte()
        Return Me.rawDataB.Skip(start + from).Take(([to] - from) + 1).ToArray()
    End Function

#End Region

End Class

这是来自在线.NET转换器的C#源代码等效(未经测试):

using Microsoft.VisualBasic;

/// ----------------------------------------------------------------------------------------------------
/// <summary>
/// Represents the BMD File Format.
/// </summary>
/// ----------------------------------------------------------------------------------------------------
/// <remarks>
/// <see href="http://datacrystal.romhacking.net/wiki/Persona_3_and_4/BMD_%28File_Format%29"/>
/// </remarks>
/// ----------------------------------------------------------------------------------------------------
public sealed class BMDFormat {

    #region " Private Fields "

    ///<summary>
    /// Specifies the offset where the header begins.
    ///</summary>
    private readonly int HeaderOffset = 0x0;

    ///<summary>
    /// Specifies the offset where the non-text table begins. This is the first table in the BMD file format.
    ///</summary>
    private readonly int TableNonTextOffset = 0x24;

    ///<summary>
    /// Specifies the offset where the text table begins. This is the second table in the BMD file format.
    ///</summary>
    private int TableTextOffset; // The value is determined later, at the end of the TableNonTextOffset table.

    ///<summary>
    /// Contains the offset location and length of the BMD header offsets.
    ///</summary>
    private readonly Dictionary<string, KeyValuePair<int, int>> HeaderOffsets = new Dictionary<string, KeyValuePair<int, int>>(StringComparer.Ordinal) {
        {"ChunkID", new KeyValuePair<int, int>(0x0, null)},
        {"Size", new KeyValuePair<int, int>(0x4, 0x5)},
        {"MagicIdentifier", new KeyValuePair<int, int>(0x8, 0xb)},
        {"EndOfTextTable", new KeyValuePair<int, int>(0x10, 0x11)},
        {"OffsetsAmount", new KeyValuePair<int, int>(0x14, 0x15)},
        {"EntriesAmount", new KeyValuePair<int, int>(0x18, 0x19)}
    }; // Friendly-Name, {Start-Offset, End-Offset}

    #endregion

    #region " Properties "

    /// <summary>
    /// Gets the raw byte-data of the BMD file.
    /// </summary>
    public byte[] RawData {
        get { return this.rawDataB; }
    }
    private readonly byte[] rawDataB;

    /// <summary>
    /// Gets the Chunk ID of the file format.
    /// </summary>
    public byte ChunkID {
        get { return this.chunkIDB; }
    }
    private byte chunkIDB;

    /// <summary>
    /// Gets the size of the file.
    /// </summary>
    public short Size {
        get { return this.sizeB; }
    }
    private short sizeB;

    /// <summary>
    /// Gets the Magic Identifier of the file format.
    /// </summary>
    public byte[] MagicIdentifier {
        get { return this.magicIdentifierB; }
    }
    private byte[] magicIdentifierB;

    /// <summary>
    /// Gets the end of text table of the file format.
    /// </summary>
    public short EndOfTextTable {
        get { return this.endOfTextTableB; }
    }
    private short endOfTextTableB;

    /// <summary>
    /// Gets the amount of offsets in offset table of the file format.
    /// </summary>
    public short OffsetsAmount {
        get { return this.offsetsAmountB; }
    }
    private short offsetsAmountB;

    /// <summary>
    /// Gets the amount of entries of the file format.
    /// </summary>
    public short EntriesAmount {
        get { return this.entriesAmountB; }
    }
    private short entriesAmountB;

    #endregion

    #region " Constructors "

    private BMDFormat() {}
    public BMDFormat(string filepath) : this(File.ReadAllBytes(filepath)) {}
    public BMDFormat(FileInfo file) : this(file.FullName) {}

    public BMDFormat(byte[] raw) {
        this.rawDataB = raw;
        this.ReadHeader();
    }

    #endregion

    #region " Private Methods "

    ///<summary>
    /// Reads the BMD header.
    ///</summary>
    private void ReadHeader() {
        KeyValuePair<int, int> chunkID = this.HeaderOffsets("ChunkID");
        KeyValuePair<int, int> size = this.HeaderOffsets("Size");
        KeyValuePair<int, int> magicIdentifier = this.HeaderOffsets("MagicIdentifier");
        KeyValuePair<int, int> endOfTextTable = this.HeaderOffsets("EndOfTextTable");
        KeyValuePair<int, int> offsetsAmount = this.HeaderOffsets("OffsetsAmount");
        KeyValuePair<int, int> entriesAmount = this.HeaderOffsets("EntriesAmount");

        this.magicIdentifierB = this.GetBytes(this.HeaderOffset, magicIdentifier.Key, magicIdentifier.Value);
        this.chunkIDB = this.GetByte(this.HeaderOffset, chunkID.Key);
        this.sizeB = BitConverter.ToInt16(this.GetBytes(this.HeaderOffset, size.Key, size.Value), 0);
        this.endOfTextTableB = BitConverter.ToInt16(this.GetBytes(this.HeaderOffset, endOfTextTable.Key, endOfTextTable.Value), 0);
        this.offsetsAmountB = BitConverter.ToInt16(this.GetBytes(this.HeaderOffset, offsetsAmount.Key, offsetsAmount.Value), 0);
        this.entriesAmountB = BitConverter.ToInt16(this.GetBytes(this.HeaderOffset, entriesAmount.Key, entriesAmount.Value), 0);

        this.TableTextOffset = this.TableNonTextOffset + (8 * (entriesAmountB - 1)) + 2;

        this.ReadNonTextTable();
        this.ReadTextTable();
    }

    ///<summary>
    /// Reads the non-text table of the BMD file format.
    /// <para></para>
    /// This table seems not useful.
    ///</summary>
    private void ReadNonTextTable() {
        for (int x = 0; x <= (entriesAmountB - 1); x++) {
            int offset = this.TableNonTextOffset + (8 * x);
            byte[] data = this.GetBytes(offset, 0, 1);
            short value = BitConverter.ToInt16(data, 0);

            #if DEBUG
            Debug.WriteLine(string.Format("Entry.Index.: {0}", (x + 1)));
            Debug.WriteLine(string.Format("Start.Offset: DEC={0,-4} HEX=0x{1}", offset, offset.ToString("X")));
            Debug.WriteLine(string.Format("Raw.Bytes...: {0}", string.Join(" ", from b in datab.ToString("X"))));
            Debug.WriteLine(string.Format("Int16.Value.: {0}", value));
            Debug.WriteLine(string.Empty);
            #endif
        }
    }

    ///<summary>
    /// Reads the text table of the BMD file format.
    /// <para></para>
    /// This table contains the items identifiers and their descriptions for further translation.
    ///</summary>
    private void ReadTextTable() {
        for (int x = 0; x <= 0; x++) {
            // ...?
        }
    }

    private byte GetByte(int start, int offset) {
        return Buffer.GetByte(array: this.RawData, index: start + offset);
    }

    private byte[] GetBytes(int start, int @from, int to) {
        return this.rawDataB.Skip(start + @from).Take((to - @from) + 1).ToArray();
    }

    #endregion

}

//=======================================================
//Service provided by Telerik (www.telerik.com)
//=======================================================

0 个答案:

没有答案