Документирование внутренней структуры базы данных CTree v4 .. синтаксический анализ двоичного файла?

У меня есть очень старая (около 1993 года, база данных Faircom CTree v4), которая использовалась ныне несуществующим производителем испытательного оборудования. У меня нет доступа к исходной конфигурации, но есть только две таблицы (два файла), а затем два других совпадающих файла, которые, как я предполагаю, являются их индексными файлами-аналогами.

Я пытаюсь разобрать эти старые файлы в новой программе на C#, чтобы загрузить данные для других целей, поэтому я смотрю на двоичный файл .... Я вижу, как они структурированы, есть заголовок и множество указателей (это было написано и скомпилирован с помощью Turbo C), и я даже вижу там свои данные в виде текста.

двоичный снимок изображения #1

Взяв то, что показано выше, вы можете видеть, что смещение данных записи - это второе слово в заголовке основного файла. Теперь мы переходим к конкретной структуре записи ...

двоичный снимок изображения #2

Здесь показаны 4 записи - каждая разного цвета

1-е слово - два совпадающих байта, обозначающих тип записи? (FA,FB,FC,FD,FE,FF= конец)

2-е слово - ЕСТЬ длина записи (после 6 байтов заголовка).

3-е слово - неизвестно (иногда соответствует 2-му слову, иногда нулю, иногда что-то другое)

Актуальные вопросы для этого поста:

  1. Похоже, что мне нужно будет определить поля для записей самостоятельно, проанализировав несколько разных файлов данных, чтобы определить длину. Кто-нибудь знает, существуют ли бесплатные инструменты, которые могут «понять» структуры баз данных Faircom? Я связался с Faircom, и они помогут мне, если я куплю контракт на поддержку, но я совершенно не ожидал, что буду тратить на это деньги, но, возможно, мне придется это сделать.
  2. Я попытался найти заголовки C, чтобы добраться до двоичного файла, испускаемого в этих старых версиях, но я просто не могу найти материал на таком низком уровне. У кого-нибудь есть какие-либо указатели или знания о внутреннем устройстве Faircom DB или справочная информация сверхнизкого уровня. Понять, что это за перечисления 0xFA-0xFF, тоже было бы полезно ...

Продвигаясь вперёд с этим, я отчитаюсь. Пожалуйста, задавайте любые вопросы. благодарю вас!

JA

1 ответ

Решение

Хорошо, просто чтобы подвести итог, вот где я приземлился ... в конце концов, я просто сам проанализировал двоичный файл на соответствующие записи, потому что у меня не было исходных файлов схемы, которые были бы необходимы для использования любого материала Faircom . Задача воссоздания файлов заняла бы столько же времени, как и анализ двоичного файла в C#, поскольку это довольно просто.

Я действительно видел, что записи 0xFA оказались «настоящими» записями, в то время как записи 0xFD означали, что они были «удалены». В исходной программе действительно была возможность «вернуть» файл, если вы его изменили, но еще не сохранили, поэтому структуры базы данных Faircom позволили сделать это уже в 1994 году под ключ.

Другие общие наблюдения, которые могут быть или не быть специфичными для моей схемы ... У меня были следующие типы данных ... String, StringArray, ByteArray, Number

Я создал небольшое определение 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, это может дать некоторую разумность. Спасибо за внимание!

Другие вопросы по тегам