1

我有一个非常旧的(大约 1993 年,v4 Faircom CTree 数据库),被一家现已解散的测试设备制造商使用。我无法访问原始配置,但似乎只有两个表(两个文件),然后是另外两个匹配的文件,我认为它们是它们的兄弟索引文件。

我试图在较新的 C# 程序中解析这些旧文件以加载数据以用于其他目的,所以我查看二进制文件....我看到它们的结构,有一个标题和各种指针(这是写的并用 Turbo C 编译),我什至在其中看到我的数据作为文本。

二进制快照图片#1

以上面显示的内容,您可以看到记录数据的偏移量是主文件头的第二个单词。现在我们进入记录特定结构......

二进制快照图片#2

此处显示 4 条记录 - 每条记录都有不同的颜色

第一个字 - 表示记录类型的两个匹配字节?(FA,FB,FC,FD,FE,FF=结束)

第 2 个字 - 是记录的长度(在 6 个标头字节之后)。

第三个词 - 未知(有时与第二个词匹配,有时为零,有时与其他词匹配)

这篇文章的实际问题:

  1. 看来我需要自己通过解析一些不同的数据文件来确定记录的字段以计算长度。有谁知道是否有可以“理解”Faircom 数据库结构的免费工具?我已经联系了 Faircom,如果我购买了支持合同,他们会帮助我,但我并不完全期望在这方面花钱,但我可能不得不这样做。
  2. 我试图找到 C 头文件来获取这些旧版本中发出的二进制文件,但我就是找不到这么低的东西。任何人都对 Faircom DB 内部或超低级参考信息有任何指示或知识。了解那些 0xFA-0xFF 枚举也很方便。
4

1 回答 1

0

好的,只是为了总结一下,这就是我登陆的地方。最后,我只是自己将二进制文件解析为相关记录,因为我没有使用任何 Faircom 东西所需的原始模式文件. 重新创建文件的任务与在 C# 中解析二进制文件所花费的时间一样多,因为这非常简单。

我确实看到 0xFA 记录似乎是“真实”记录,而 0xFD 记录表示它们已被“删除”。如果您更改了文件但尚未保存,原始程序确实能够“还原”文件,因此 faircom 数据库结构在 1994 年就允许它成为相当交钥匙的工具。

其他可能特定于或可能不特定于我的架构的一般观察结果...我有以下数据类型...字符串、字符串数组、字节数组、数字

我创建了一个看起来像这样的小 SchemaDefinition..

schema = new CTreeSchema();
schema.Columns.Add(new CTreeColumn("Ordinal", CTreeColumnType.Number, 4));
schema.Columns.Add(new CTreeColumn("Unknown3", CTreeColumnType.Number, 4));
schema.Columns.Add(new CTreeColumn("Unknown4", CTreeColumnType.Number, 4));
schema.Columns.Add(new CTreeColumn("Name", CTreeColumnType.String, 0));
schema.Columns.Add(new CTreeColumn("ExtendedData", CTreeColumnType.StringArray, 0));

然后我用这样的东西解析它......

public CTreeRecord(CTreeSchema schema, byte[] bytes)
    {
        _internalBytes = bytes;
        RecordTypeId = bytes[0];
        int byteIndex = 6;
        foreach(var i in schema.Columns)
        {
            int endIndex = 0;
            switch (i.Type)
            {
                case CTreeColumn.CTreeColumnType.String:
                    if (i.Length == 0)
                    {
                        //null terminated
                        endIndex = Array.IndexOf<byte>(bytes, 0x00, byteIndex);
                        i.Value = Encoding.ASCII.GetString(bytes.Skip(byteIndex).Take(endIndex - byteIndex).ToArray());
                        byteIndex += (1+ endIndex - byteIndex);
                    }
                    if (i.Length > 0)
                    {
                        //specific length
                        throw new NotSupportedException("Static Length String columns not supported.");
                        //byteIndex += i.Length;
                    }
                    break;
                case CTreeColumn.CTreeColumnType.StringArray:
                    List<string> headerStrings = new List<string>();
                    endIndex = Array.IndexOf<byte>(bytes, 0x00, byteIndex);
                    byte[] arrayBytes = bytes.Skip(byteIndex).Take(endIndex - byteIndex).ToArray();
                    byteIndex += (1 + endIndex - byteIndex);
                    List<byte[]> headerBytes = SplitByteString(arrayBytes, 0x01, false);
                    foreach (byte[] b in headerBytes)
                    {
                        headerStrings.Add(Encoding.ASCII.GetString(b));
                    }
                    i.Value = headerStrings;
                    break;
                case CTreeColumn.CTreeColumnType.ByteArray:
                    List<byte[]> byteArray = new List<byte[]>();
                    for (int a = 0; a < i.Length; a++)
                    {
                        endIndex = Array.IndexOf<byte>(bytes, 0x00, byteIndex);
                        byteArray.Add(bytes.Skip(byteIndex).Take(endIndex - byteIndex).ToArray());
                        byteIndex += (1 + endIndex - byteIndex);
                    }
                    i.Value = byteArray;
                    break;
                case CTreeColumn.CTreeColumnType.Number:
                    switch (i.Length)
                    {
                        case 2:
                            i.Value = BitConverter.ToUInt16(bytes, byteIndex);
                            break;
                        case 4:
                            i.Value = BitConverter.ToUInt32(bytes, byteIndex);
                            break;
                    }
                    byteIndex += i.Length;
                    break;
                case CTreeColumn.CTreeColumnType.Date:
                    throw new NotSupportedException("Date columns not supported.");
                    break;
            }
            Columns.Add(i);
        }
    }

    private List<byte[]> SplitByteString(byte[] bytes, byte splitValue, bool removeEmptyEntries)
    {
        List<byte[]> splitBytes = new List<byte[]>();
        int currentIndex = 0;
        while (currentIndex < bytes.Length)
        {
            int splitIndex = Array.IndexOf<byte>(bytes, splitValue, currentIndex);
            if (splitIndex >= 0)
            {
                //found one
                int currentLength = splitIndex - currentIndex;
                if (!(currentLength == 0 && removeEmptyEntries))
                {
                    splitBytes.Add(bytes.Skip(currentIndex).Take(currentLength).ToArray());
                    currentIndex += (1 + currentLength);
                }
                else
                {
                    currentIndex++;
                }
            }
            else
            {
                //not found, just take until end now..
                splitBytes.Add(bytes.Skip(currentIndex).Take(bytes.Length - currentIndex).ToArray());
                currentIndex += (bytes.Length - currentIndex);
            }
            
        }
        return splitBytes;
    }

总而言之,它非常丑陋并且非常特定于这个应用程序,但是如果有人必须处理 C Faircom 数据库文件,这可能会提供一些理智。

于 2021-06-16T04:43:32.897 回答