Database (.fdb)

You can use the tools collection from assembly to work with FDB files.

Note

There is a converter from fdb to sqlite available, see the Tools section. This file type has no relation to firebird database files of the same extension.

Note

It seems like:
  • Tables are sorted by their name in ascii representation. Uppercase letters then underscore then lowercase letters.

  • Tables themselves are hash maps. Use id % row_count to get the appropriate row_info, then follow the linked_row_info until all entries with that ID are found.

  • When the primary key is a string, a dedicated hash function is used to determin the index of the row_info slot.

  • Strings are stored spearately for each row, even if they have the same content. This makes for a great amount of redundancy in the file, but keeps editing simple.

Note

id:
fdb
file-extension:
fdb
endian:
le
imports:
  1. ../common/common

Sequence

  1. [u4] num_tables

  2. [u4] ofs_table

Instance tables

[table]

Position: ofs_table
repeat-expr: num_tables

Type table

Sequence

  1. [u4] ofs_table_desc

  2. [u4] ofs_hash_table

Instance table_desc

[table_description]

Position: ofs_table_desc

Instance hash_table

[hash_table]

Position: ofs_hash_table

Type table_description

Sequence

  1. [u4] num_columns

  2. [text] table_name

  3. [u4] ofs_columns

Instance columns

[column_description]

Position: ofs_columns
repeat-expr: num_columns

Type column_description

Sequence

  1. [u4:variant_type] data_type

  2. [text] column_name

Type hash_table

Sequence

  1. [u4] table_size

  2. [u4] ofs_buckets

Instance buckets

[hash_bucket]

Position: ofs_buckets
repeat-expr: table_size

Type hash_bucket

Sequence

  1. [u4] ofs_data

Instance data

if: ofs_data != 0xffffffff

[list_rows]

Position: ofs_data

Type list_rows

Sequence

  1. [u4] ofs_row_data

  2. [u4] ofs_next_data

Instance row_data

[row_data]

Position: ofs_row_data

Instance next_data

if: ofs_next_data != 0xffffffff

[list_rows]

Position: ofs_next_data

Type row_data

Sequence

  1. [u4] num_data

  2. [u4] ofs_data_array

Instance data_array

[variant_data]

Position: ofs_data_array
repeat-expr: num_data

Type variant_data

Sequence

  1. [u4:variant_type] data_type

  2. [switch-on:data_type] data

    Type Cases
    variant_type::null null_data
    variant_type::i32 s4
    variant_type::u32 u4
    variant_type::real f4
    variant_type::nvarchar text
    variant_type::bool common::bool
    variant_type::i64 i64
    variant_type::u64 u64
    variant_type::text text

Type null_data

Sequence

  1. [0, 0, 0, 0] null_data

Type i64

Sequence

  1. [u4] ofs_i64

Instance i64

[s8]

Position: ofs_i64

Type u64

Sequence

  1. [u4] ofs_u64

Instance u64

[u8]

Position: ofs_u64

Type text

Sequence

  1. [u4] ofs_text

Instance text

[strz]

Position: ofs_text
Encoding: ascii

Enum variant_type

0:
null
1:
i32
2:
u32
3:
real
4:
nvarchar
5:
bool
6:
i64
7:
u64
8:
text

Note

  • Address pointers can be -1 which most likely means an invalid address (just skip those)

  • Strings types (TEXT and VARCHAR) are always null-terminated (with some over allocated bytes afterwards it seems, apparently string length are filled to be modulo 4 = 0?)

  • Strings and int64 (BIGINT) types are always stored with an additional address pointer, like this: [pointer]->[data]

SQLite Conversion

lcdr’s tools rely on https://www.sqlite.org/datatype3.html#determination_of_column_affinity to assign the type of columns in SQLite while preserving the original type:

SQLITE_TYPE = {}
SQLITE_TYPE[0] = "none"
SQLITE_TYPE[1] = "int32"
SQLITE_TYPE[3] = "real"
SQLITE_TYPE[4] = "text_4"
SQLITE_TYPE[5] = "int_bool"
SQLITE_TYPE[6] = "int64"
SQLITE_TYPE[8] = "text_8"