Документирование внутренней структуры базы данных CTree v4 .. синтаксический анализ двоичного файла?
У меня есть очень старая (около 1993 года, база данных Faircom CTree v4), которая использовалась ныне несуществующим производителем испытательного оборудования. У меня нет доступа к исходной конфигурации, но есть только две таблицы (два файла), а затем два других совпадающих файла, которые, как я предполагаю, являются их индексными файлами-аналогами.
Я пытаюсь разобрать эти старые файлы в новой программе на C#, чтобы загрузить данные для других целей, поэтому я смотрю на двоичный файл .... Я вижу, как они структурированы, есть заголовок и множество указателей (это было написано и скомпилирован с помощью Turbo C), и я даже вижу там свои данные в виде текста.
двоичный снимок изображения #1
Взяв то, что показано выше, вы можете видеть, что смещение данных записи - это второе слово в заголовке основного файла. Теперь мы переходим к конкретной структуре записи ...
двоичный снимок изображения #2
Здесь показаны 4 записи - каждая разного цвета
1-е слово - два совпадающих байта, обозначающих тип записи? (FA,FB,FC,FD,FE,FF= конец)
2-е слово - ЕСТЬ длина записи (после 6 байтов заголовка).
3-е слово - неизвестно (иногда соответствует 2-му слову, иногда нулю, иногда что-то другое)
Актуальные вопросы для этого поста:
- Похоже, что мне нужно будет определить поля для записей самостоятельно, проанализировав несколько разных файлов данных, чтобы определить длину. Кто-нибудь знает, существуют ли бесплатные инструменты, которые могут «понять» структуры баз данных Faircom? Я связался с Faircom, и они помогут мне, если я куплю контракт на поддержку, но я совершенно не ожидал, что буду тратить на это деньги, но, возможно, мне придется это сделать.
- Я попытался найти заголовки 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, это может дать некоторую разумность. Спасибо за внимание!