Paradox - Strukturen

1. Allgemeines

M, F, G, O und F Felder sind BLOB's (Binary Large Objects) und haben eine Variable Länge. Sie besitzen einen 10 Byte Header, der die Länge und Position des BLOB enthält und eine Modification Number, die intern von Paradox benutzt wird.

BLOB Felder werden im *.MB File gespeichert, wobei die ersten Bytes, deren Länge beim Erzeugen der Tabelle definiert werden, in der *.DB Tabelle gespiegelt werden. Wenn die Größe des BLOB kleiner als die definierte Länge in der *.DB Tabelle ist, dann werden die Daten nicht in der *.MB Tabelle gespeichert.

Header Format: LongInt für Offset

LongInt für Länge des BLOB

Word für Modification Number

Das Datum wird als Long Integer gespeichert. Es enthält die Anzahl der Tage seit dem 1.1.0001. Das Kleinste gültige Datum ist der 1.1.0100 (36160 = $0008D40). Beispiel: 4.5.1996 = 728783 = $000B1ECF. Paradox akzeptiert Daten zwischen 1.1.0100 und 31.12.9999.

2. DB File

Generelle Struktur

Organisation

First, Last und Free Blocks sind Integer Werte und stehen am Anfang von jedem Block. Der erste Block nach dem Header hat die Nummer 1. Alle Blocks haben die gleiche Länge. Siehe MaxTableSize. BlockOffset = BlockLength * ( BolckNumber - 1) + TableHeaderLength. Block 1 darf nie ein freier Block sein.

Die Größe eines Paradox File Headers ist immer 2048 Bytes. Der erste Teil (Offset $0000 bis $0077) hat eine Feste Länge. Der Rest hat eine Variable Länge, die von der Anzahl der Felder anhängt.

Einige der Informationen aus dem File Header scheinen nur benötigt zu werden, wenn Paradox im RAM läuft. Einige von diesen Feldern sind Pointer auf andere Felder im Header und sind nur zur Laufzeit gültig.

Die Datenblöcke sind in Records unterteilt. Die Größe eines Record wird bei der Erzeugung einer Tabelle definiert.

Die Darstellung der Zahlen im Header erfolgt im Little Endian Format (Low-Byte .. High-Byte).

Die Darstellung der Zahlen im Data Block erfolgt im Big Endian Format (High-Byte .. Low-Byte).

2.1 Common File Header

Common Header $0000..$0057

Offset Typ Bedeutung
0000 Integer RecordSize

Größe des Records in Bytes.

0002 Integer HeaderSize (always $0800) |

Die HeaderSize kann geändert werden um kleinere oder größere Header zu erzeugen. Borland's TUTULITY Programm gibt jedoch dann eine Fehlermeldung aus. Paradox, die BDE und die Paradox Engine arbeiten jedoch kommentarlos mit dieser Änderung.

0004 Byte FileType

00 = Ein Index Daten File: *.DB
01 = Ein Primär-Index File: *.PX
02 = Ein Nicht-Index Daten File: *.DB
03 = Ein Nicht-Inkrementierender Sekundär-Index File: *.Xnn
04 = Ein Sekundär-Index File: *.Ynn
05 = Ein Inkrementierender Sekündär-Index File: *.Xnn
06 = Ein Nicht-Inkrementierender Sekundär-Index File: *.XGn
07 = Ein Sekundär-Index File: *.YGn
08 = Ein Inkrementierender Sekündär-Index File: *.XGn

0005 Byte MaxTableSize

Dies ist die Größe eines Daten-Blocks. Wird angelegt, wenn die Tabelle erzeugt wird.
01 = 64M (Block Größe = $0400 Bytes)
02 = 128M (Block Größe = $0800 Bytes)
03 = 192M (Block Größe = $0C00 Bytes) nicht in 4.5
04 = 256M (Block Größe = $1000 Bytes)

0006 LongInt NumRecords

Anzahl der Records in diesem File.

000A Word UsedBlocks

Anzahl der benutzten Blöcke.

000C Word FileBlocks

Anzahl der Daten Blocks in diesem File. (Jeder Block ist ein Zusammenschluß von Records)

000E Word FirstBlock

Immer 1, es sei denn die Tabelle ist leer.

0010 Word LastBlock

Nummer des letzten belegten Blockes in der Tabelle.

0012 Word Unbekannt
0014 Byte ModifiedFlags1

Ein Rebuild der Tabelle ist erforderlich, wenn der Wert ungleich 0 ist.

0015 Byte IndexFieldNumber

In einem *.Xnn File eines Sekundär-Index, ist dies die Anzahl der Felder, die referenziert werden.

In allen anderen Files ist dieser Wert 0.

0016 Pointer PrimaryIndexWorkspace

Immer NIL bei DB Tabellen.

001A Pointer Unbekannt (Suspected Pointer)

Dieses Feld ist normalerweise ein NIL Pointer. Eine Änderung wurde beim Feldtyp BCD, in einer 5.0 Tabelle beobachtet. Wahrscheinlich ist es nur ein Arbeits Pointer.

001E

0020

  Unbekannt

Die Benutzung dieser Bytes wurde bisher nur in einem *.PX File beobachtet.

0021 Integer NumFields

Anzahl der Felder in dieser Tabelle.

0023 Integer PrimaryKeyFields

Anzahl der Felder, die einen Primär-Schlüssel haben.

0025 LongInt Encryption1

Position der Verschlüsselungs Information bei Paradox 3.0 und 3.5 (Hier steht $00000000, wenn keine Verschlüsselung vorliegt). Die Folge-Versionen speichern hier immer $FF00FF00 und den Verschlüsselungs-Code ab Offset $005C.

Primär- und Sekundär Index Files speichern ihre Schlüssel auch an dieser Stelle, sie enthalten jedoch häufig 0en, weil Die Paradox Engine und die BDE überhaupt keine Index Dateien verschlüsseln. Eine Verschlüsselung kann aber durch kopieren dieser Felder in die Index Dateien trotzdem durchgeführt werden.

0029 Byte SortOrder

$00: ASCII
$B7: International
$82: Norwegisch/Dänisch
$E6: Norwegisch/Dänisch (4.0)
$F0: Schwedisch/Finnisch

002A Byte ModifiedFlags2

Ein Rebuild der Tabelle ist erforderlich, wenn der Wert ungleich 0 ist.

002B Word Unbekannt (immer 0)
002D Byte ChangeCount1

Wird bei jedem File Header upgedate inkrementiert.

002E Byte ChangeCount2

Es ist noch nicht klar, wenn dieses Feld inkrementiert wird.

002F Byte Unbekannt
0030 ^pChar TableNamePtr

Dies ist ein Pointer auf TableNamePtr, welcher ein Pointer auf TableName ist.

Paradox benutzt dieses Feld, um einen schnelleren Zugriff auf TableName zu haben, weil auf diesen Teil der Tabelle sequentiell zugegriffen wird.

Wird nur im RAM benutzt.

0034 Pointer FldInfoPtr

Pointer auf die Liste der Feld ID's.

Dieser Pointer kann benutzt werden, um den Tabellen-Header im RAM zur Laufzeit zu erreichen. Für Paradox Version > 4.0 muß $0078 subtrahiert werden, für kleinere Versionen und *.PX und *.Ynn Dateien $0058.

0038 Byte WriteProtected

0 Schreibschutz Aus
1 Schreibschutz An

0039 Byte FileVersionID

$03 Version 3.0
$04 Version 3.5
$05..09 Version 4.x (meistens = $09)
$0A,$0B Version 5.x
$0C Version 7.x

003A Word MaxBlocks

Funktion ist unbekannt. Der Wert ist immer mit FileBlocks identisch (bei Offset $000C).

003C Byte Unbekannt
003D Byte AuxPasswords

Anzahl der Hilfs-Passworte für diese Tabelle.

003E Word Unbekannt
0040 Pointer CryptInfoStartPtr

Zeigt auf das CryptInfo Feld. Wenn keine Verschlüsselung durchgeführt wird, ist es immer NIL. Manchmal steht aber auch NIL drin, wenn die Tabelle verschlüsselt ist.

0044 Pointer CryptInfoEndPtr

Zeigt auf das Ende von CryptInfo. Wenn keine Verschlüsselung durchgeführt wird, ist es immer NIL.

0048 Byte Unbekannt
0049 LongInt AutoInc

Dieses Feld speichert den Wert, der beim Auto-Increment- Feld-Typ "+" der Tabelle benutzt wird.

004D Word FirstFreeBlock

Nummer des ersten freien Blocks

004F Byte IndexUpdateRequired
0050

0054

  Unbekannt
0055 Byte RefIntegrity

Ein Wert von $2? bedeutet, das für diese Tabelle ein Integritäts Test durchgeführt werden sollte.

0056 Byte Unbekannt
0057 Byte Unbekannt

2.2 Data File Header

Data File Header $0058..$0077

Offset Typ Bedeutung
0058 Integer Unbekannt (File Version ID?)

$0105..$0109 Version 4.x (meistens = $0109)
$010A, $010B Version 5.x (meistens = $010B)
$010C Version 7.0

005A Integer Unbekannt (File Version ID?)

Identisch mit $0058

005C LongInt Encryption2

Position der Verschlüsselungs Information bei Paradox 3.0 und 3.5 (Hier steht $00000000, wenn keine Verschlüsselung vorliegt). Die Folge-Versionen speichern hier immer $FF00FF00 und den Verschlüsselungs-Code ab Offset $005C.

Primär- und Sekundär Index Files speichern ihre Schlüssel auch an dieser Stelle, sie enthalten jedoch häufig 0en, weil Die Paradox Engine und die BDE überhaupt keine Index Dateien verschlüsseln.

Eine Verschlüsselung kann aber durch kopieren dieser Felder in die Index Dateien trotzdem durchgeführt werden.

0060 LongInt FileUpdateTime (nur bei Version 4.x)

Das Format ist identisch mit dem vom Packed-Date-Time.

0064 Integer HiFieldID

Diese Zahl ist immer NumFields + 1.

0066 Integer HiFieldIDinfo?

Dieser Wert ist identisch mit HiFieldID, seine Funktion ist jedoch nicht bekannt.

0068 Integer SometimesNumFields?

Hier steht meistens die Anzahl der Felder in der Tabelle, aber der Wert ist häufig 0. Warum ist nicht bekannt.

006A Integer DosGlobalCodePage

Dies ist der Global Code Page Wert, der beim Erzeugen der Tabelle angelegt wird.

$01B5 United States
$02E1 Greek 1
$0352 Multilingual (Latin I)
$0354 Eastern European (Latin II)
$0359 Turkish
$035C Portuguese
$035D Icelandic
$035F Canadian French
$0361 Nordic
$0365 Greek 2

Mehr Informationen sind im MS-DOS Technical Manual beim Interrupt $21, Funktion $66 zu finden.

006C

006F

Bytes Unbekannt
0070 Integer ChangeCount4
0072

0077

Bytes Unbekannt

2.3 Common File Header

Offset Bedeutung
0058 Paradox Tabellen Version 3.0 und 3.5
0058 Paradox .PX und .Ynn Index Files (alle Versionen)
0078 Paradox Tabellen Version 4.0 und höher
0078 Paradox .Xnn Index Files Version 4.0 und höher

Dies sind Sprungziele von Pointern, deshalb gibt es keinen fest spezifizierten Offset.

Common File Header

Offset Typ Bedeutung
---- Array FieldInfo

type TFldInfoRec = RECORD
  fType : byte;
  fSize : byte;
end;

Array[1..(NumFields)] of TFldInfoRec FieldInfo

Dies sind die Feld ID's für jedes Feld in der Tabelle:

  fType fSize(decimal)
  -------------------------------------
   $01    n   "A"  Alpha
   $02    4   "D"  Date
   $03    2   "S"  Short Integer
   $04    4   "I"  Long Integer
   $05    8   "$"  Currency
   $06    8   "N"  Number
   $09    1   "L"  Logical
   $0C    n   "M"  Memo BLOB
   $0D    n   "B"  Binary Large Object
   $0E    n   "F"  Formatted Memo BLOB
   $0F    n   "O"  OLE
   $10    n   "G"  Graphic BLOB
   $14    4   "T"  Time
   $15    8   "@"  Timestamp
   $16    4   "+"  Autoincrement
   $17   17*  "#"  BCD
   $18    n   "Y"  Bytes

---- pChar TableNamePtr

Pointer auf die Tebellennamen, wenn der Header im RAM ist.

---- Array FieldNamePtrArray

Array[1..(NumFields)] of pChar

Dies ist ein Feld auf die Pointer, welche die Feld Namen enthalten wenn Paradox oder die BDE läuft.

---- Array TableName

Array[1..79] of Char

Dies ist der Name des Files, wenn es erzeugt wurde. Der Name ist 79 Bytes lang, aufgefüllt mit 0en.

Häufig steht hier der Name RESTTEMP.DB.

---- Char[] FieldNames

Hier stehen die Feldanamen als ASCII Sequenz. 0 terminiert.

---- Record CryptInfo

Für verschlüsselte Tabellen stehen hier zusätzliche Informationen.

Tabellen mit Hilfs-Passworten haben hier 256 Bytes zur Verfügung. Über die Bedeutung gibt es keine Informationen. Verschlüsselte Tabellen ohne Hilfs-Passworte haben hier für jedes Feld 1 Byte zur Verfügung.

---- Array FieldNumbers

Array[1..(NumFields)] of Integer

Dies könnte eine Liste mit Feldnummern sein. Änderungen haben jedoch keine Wirkung. Sie scheinen nur während der Laufzeit im Speicher benutzt zu werden.

---- Char[] SortOrderID

Ein ASCII String der die Sort Order für diese Tabelle enthält ("ascii", "intl", usw.).

2.4 Data Block

Byte 1 bis 6 sind von der BDE Reserviert. Danach folgt eine Anzahl von Datenrecords. Die Anzahl ist von der Größe eines Records abhängig. Die Recordgröße kann aus den einzelnen Typenangaben jedes Tabelleneintrages berechnet werden. Da die Anzahl der Records nicht immer genau einen Block ausfüllen, befinden sich am Blockende eine unbestimmte Anzahl von Füllbytes.

0000 Word NextBlock

Nummer des nächsten logischen Blocks. Diese Serie von Pointer wird auch Verkettete Liste bezeichnet. Die BDE berechnet die physikalische Startadresse eines Blocks aus der Blocknummer multipliziert mit der Blockgröße okus der Größe des Headers.

0002 Word LastBlock

Nummer des letzten Blocks.

0004 Integer LastBlockPtr

Zeiger auf den Start des letzten Record in diesem Block.

Ein Zähler für die Anzahl der aktiven Records in diesem Block. Dieses Feld enthält eine 0, wenn nur ein Record vohanden ist und eine negative Zahl (0 - RecordSize) wenn kein Record da ist.

NumRecsInBlock = (LastBlockPtr / RecordSize) + 1

0006 Record 1. Record

Alpha Felder sind eine Folge von Zeichen, die mit 00 bis zur definierten Länge aufgefüllt sind. Alle anderen Felder werden im Big-Endian-Format angegeben. Bei positiven Zahlen ist immer das MSB gesetzt.

Short Integer: 1 = $80 $01, 512 = $82 $00

Real Zahlen werden immer als 8-Byte Double Typ gespeichert. Das MSB ist immer gesetzt.

Datums Felder enthalten die Anzahl der Tage seit 00.01.0000. Das MSB ist immer gesetzt.

---- Record n. Record
xxxx   Dummy

nn Bytes vom letzten Record bis zum Blockende. Anzahl Bytes sind sind immer kleiner als die Recordlänge.

3. MB File

Das MB File wird benutzt, um größere binäre Daten (Binary Large Object = BLOB) zu speichern.

Die Organistion der Daten ist sehr unterschiedlich zum Format der DB und PX Tabelle. Die Blöcke sind nicht als verkettete Liste organisiert und sie besitzen eine unterschiedliche Länge. Der Header ist immer 4kB groß. Die folgenden Blöcke haben immer eine Länge von einem vielfachen von 4kB.

Jeder Block beginnt mit folgenden 3 Bytes:

Offset Typ Bedeutung
0000 Byte RecordType

00 - Header Block
02 - Einzelner BLOB Block
03 - Untergeordneter Block
04 - Freier Block

0001 Word NumChunks

Anzahl der 4kB Chunks in diesem Block
Die maximale Größe ist $FFFF * $1000 = 65536 * 4096.
Das sind 256 MB. Die maximale Länge eines BLOB.

0003 Record Records

3.1 BLOB Header

Der BLOB Header ist der erste Block im MB File. Er ist 4096 Bytes groß und enthält folgende Felder:

BLOB Header

Offset Typ Bedeutung
0000 Byte RecordType

00 - Header Block

0001 Word BlockSize

Blockgröße dividiert mit 4. 1 für den Header.

0003 Word ModCount

Wird auf 1 gesetzt, wenn die Tabelle restruktiriert wird. Immer wenn beim BLOB ein update gemacht wird, wird dieses Feld um 1 erhöht. Warum, weiss keiner so genau.

0005

000A

Bytes Unbekannt
000B Word BaseSize

Basis Größe des Daten Blocks $1000 (Vermutung)

000D Word SubSize

Größe des untergeordneten Daten Blocks $1000 (Vermutung)

000F Byte Unbekannt
0010 Byte SubChunkSize

Größe des untergeordneten Chunks $10 (Vermutung)

0011 Word NumSubs

Anzahl der untergeordneten Chunks $0040 (Vermutung)

0013 Word SubThreshold

Grenze zwischen großen und kleinen BLOB's $0800
Große BLOB'S haben ihre eigenen Blocks.
Mehrer kleine BLOB's werden in einem Block gespeichert.

3.2 Einzelner BLOB Block

Ein einzelner BLOB Block kann irgendwo im MB File liegen.

Die Block Länge ist das kleinste vielfache von 4 kB, welche größer oder gleich der Länge vom BLOB ist.

BLOB Block

Offset Typ Bedeutung
0000 Byte RecordType

02 - Einzelner BLOB Block

0001 Word BlobSizeDiv4

Blockgröße dividiert mit 4

0003 LongInt BlobSize

Größe des BLOB in Byte

0007 Word ModNumber

Wird auf 1 gesetzt, wenn die Tabelle restrukturiert wird. Immer wenn beim BLOB ein update gemacht wird, wird dieses Feld um 1 erhöht. Warum, weiss keiner so genau.

0009 Blob Data

Blob Daten

3.3 Untergeordneter Block

Ein untergeordneter Block kann irgendwo im MB File liegen.

Bis zu 64 kurze BLOB's können in diesem Typ von Block liegen.

Ein untergeordneter Block ist 4kB Lang. Er hat einen 12 Bytes Header, gefolgt von einem Array von bis zu 64 5-Byte BLOB Pointern.

Ein DB Feld, welchen ein BLOB hat, enthält den Offset des Blocks und den Index auf den Pointer Array. Der Array Pointer zeigt dann auf die Daten.

Untergeordneter Block

Offset Typ Bedeutung
0000 Byte RecordType

03 - Untergeordneter Block

0001 Word BlobSizeDiv4

Blockgröße dividiert mit 4

0003

000B

Bytes Unbekannt
000C Word Array1
nn+0 Byte Daten Offset / 16

Der Offset zählt vom Blockanfang. Wenn er 0 ist, dann wurde der BLOB gelöscht und der Speicher ist/kann von einem anderen BLOB benutzt werden

nn+1 Byte Daten Länge / 16 (hochgerundet)
nn+2 Word Modifikations Nummer (Siehe ModNumber)
nn+4 Byte Daten Länge mod 16

Wenn der Wert 0 ist, dann ist der BLOB gelöscht und steht zur freien Vefügung. Für einen aktive BLOB kann der Wert zwischen $01 und $10 liegen.

0011 Word Array2
0016 Word Array..
0147 Word Array64
0150 Blob BlobData

Ein untergeordneter Block besteht aus 16 Byte Chunks. Der erste verfügbare Chunk beginnt beim Offset von $0150. Um den Offset zu bekommen muß das erste Bytes eines Pointer Eintrage mit 16 multipliziert werden. Das nächste Byte gibt die Anzahl der Chunks an. Das letzte Byte gibt die Anzahl der Bytes im letzten Chunk an.

Beispiel: 25 03 0F 00 07
Offset = $0250
Anzahl der Chunks = 3
Anzahl Bytes im letzten Chunk = 7
Datengröße = 2 * 16 + 7 = 39 Bytes
Modifikation Number = 15

3.4 Freier Block

Ein freier Block kann irgendwo im MB File liegen.

Die Blocklänge ist ein vielfaches von 4kB.

Wenn da meherer freie Blöcke sind, dann befindet sich die kombinierte Länge im ersten freien Block.

Freier Block

Offset Typ Bedeutung
0000 Byte RecordType

04 - Freier Block

0001 Word BlobSizeDiv4

Blockgröße dividiert mit 4

0003 Bytes Keine BLOB Daten

Wenn ein BLOB vom Typ 03 gelöscht wird, dann wird er zu einem BLOB vom Typ 04. Wenn alle BLOB's in einem Block vom Typ 02 gelöscht wurden, dann wird der Block zu einem freien Block.

Wird eine BLOB upgedatet, dann wird er nie an der gleichen Stelle gespeichert.

4. PX File

Generelle Struktur

Organisation

First, Last und Free Blocks sind Integer Werte und stehen am Anfang von jedem Block. Der erste Block nach dem Header hat die Nummer 1. Alle Blocks haben die gleiche Länge. Siehe MaxTableSize. BlockOffset = BlockLength * ( BolckNumber - 1) + TableHeaderLength. Block 1 darf nie eine freier Block sein.

Die Größe eines Paradox File Headers ist immer 2048 Bytes. Der erste Teil (Offset $0000 bis $0077) hat eine Feste Länge. Der Rest hat eine Variable Länge, die von der Anzahl der Felder anhängt.

Einige der Informationen aus dem File Header scheinen nur benötigt zu werden, wenn Paradox im RAM läuft. Einige von diesen Feldern sind Pointer auf andere Felder im Header und sind nur zur Laufzeit gültig.

Die Datenblöcke sind in Records unterteilt. Die Größe eines Record wird bei der Erzeugung einer Tabelle definiert.

Die Darstellung der Zahlen im Header erfolgt im Little Endian Format (Low-Byte .. High-Byte).

Das *.PX File wird angelegt, sobald der erste Record in die *.DB Tabelle geschrieben wird.

4.1 Common Header

Common Header $0000..$0057

Offset Typ Bedeutung
0000 Integer RecordSize

Größe des Records in Bytes.

0002 Integer HeaderSize (always $0800) |

Die HeaderSize kann geändert werden um kleinere oder größere Header zu erzeugen. Borland's TUTULITY Programm gibt jedoch dann eine Fehlermeldung aus. Paradox, die BDE und die Paradox Engine arbeiten jedoch kommentarlos mit dieser Änderung.

0004 Byte FileType

00 = Ein Index Daten File: *.DB
01 = Ein Primär-Index File: *.PX
02 = Ein Nicht-Index Daten File: *.DB
03 = Ein Nicht-Inkrementierender Sekundär-Index File: *.Xnn
04 = Ein Sekundär-Index File: *.Ynn
05 = Ein Inkrementierender Sekündär-Index File: *.Xnn
06 = Ein Nicht-Inkrementierender Sekundär-Index File: *.XGn
07 = Ein Sekundär-Index File: *.YGn
08 = Ein Inkrementierender Sekündär-Index File: *.XGn

0005 Byte MaxTableSize

Dies ist die Größe eines Daten-Blocks. Wird angelegt, wenn die Tabelle erzeugt wird.
01 = 64M (Block Größe = $0400 Bytes)
02 = 128M (Block Größe = $0800 Bytes)
03 = 192M (Block Größe = $0C00 Bytes) nicht in 4.5
04 = 256M (Block Größe = $1000 Bytes)

0006 LongInt NumRecords

Anzahl der Records in diesem File.

000A Word UsedBlocks

Anzahl der benutzten Blöcke.

000C Word FileBlocks

Anzahl der Daten Blocks in diesem File. (Jeder Block ist ein Zusammenschluß von Records)

000E Word FirstBlock

Immer 1, es sei denn die Tabelle ist leer.

0010 Word LastBlock

Nummer des letzten belegten Blockes in der Tabelle.

0012 Word Unbekannt
0014 Byte ModifiedFlags1

Ein Rebuild der Tabelle ist erforderlich, wenn der Wert ungleich 0 ist.

0015 Byte IndexFieldNumber

In einem *.Xnn File eines Sekundär-Index, ist dies die Anzahl der Felder, die referenziert werden.

In allen anderen Files ist dieser Wert 0.

0016 Pointer PrimaryIndexWorkspace

Pointer auf den Primär-Index File Header (im RAM).

001A Pointer Unbekannt (Suspected Pointer)

Dieses Feld ist normalerweise ein NIL Pointer. Eine Änderung wurde beim Feldtyp BCD, in einer 5.0 Tabelle beobachtet. Wahrscheinlich ist es nur ein Arbeits Pointer.

001E Word IndexBlockNumber

Block Nummer des Index Root.

0021 Integer NumFields

Anzahl der Felder in dieser Tabelle.

0023 Integer PrimaryKeyFields

Anzahl der Felder, die einen Primär-Schlüssel haben.

0025 LongInt Encryption1

Position der Verschlüsselungs Information bei Paradox 3.0 und 3.5 (Hier steht $00000000, wenn keine Verschlüsselung vorliegt). Die Folge-Versionen speichern hier immer $FF00FF00 und den Verschlüsselungs-Code ab Offset $005C.

Primär- und Sekundär Index Files speichern ihre Schlüssel auch an dieser Stelle, sie enthalten jedoch häufig 0en, weil Die Paradox Engine und die BDE überhaupt keine Index Dateien verschlüsseln. Eine Verschlüsselung kann aber durch kopieren dieser Felder in die Index Dateien trotzdem durchgeführt werden.

0029 Byte SortOrder

$00: ASCII
$B7: International
$82: Norwegisch/Dänisch
$E6: Norwegisch/Dänisch (4.0)
$F0: Schwedisch/Finnisch

002A Byte ModifiedFlags2

Ein Rebuild der Tabelle ist erforderlich, wenn der Wert ungleich 0 ist.

002B Word Unbekannt (immer 0)
002D Byte ChangeCount1

Wird bei jedem File Header upgedate inkrementiert.

002E Byte ChangeCount2

Es ist noch nicht klar, wenn dieses Feld inkrementiert wird.

002F Byte Unbekannt
0030 ^pChar TableNamePtr

Dies ist ein Pointer auf TableNamePtr, welcher ein Pointer auf TableName ist.

Paradox benutzt dieses Feld, um einen schnelleren Zugriff auf TableName zu haben, weil auf diesen Teil der Tabelle sequentiell zugegriffen wird.

Wird nur im RAM benutzt.

0034 Pointer FldInfoPtr

Pointer auf die Liste der Feld ID's.

Dieser Pointer kann benutzt werden, um den Tabellen-Header im RAM zur Laufzeit zu erreichen. Für Paradox Version > 4.0 muß $0078 subtrahiert werden, für kleinere Versionen und *.PX und *.Ynn Dateien $0058.

0038 Byte WriteProtected

0 Schreibschutz Aus
1 Schreibschutz An

0039 Byte FileVersionID

$03 Version 3.0
$04 Version 3.5
$05..09 Version 4.x (meistens = $09)
$0A,$0B Version 5.x
$0C Version 7.x

003A Word MaxBlocks

Funktion ist unbekannt. Der Wert ist immer mit FileBlocks identisch (bei Offset $000C).

003C Byte Unbekannt
003D Byte AuxPasswords

Anzahl der Hilfs-Passworte für diese Tabelle.

003E Word Unbekannt
0040 Pointer CryptInfoStartPtr

Zeigt auf das CryptInfo Feld. Wenn keine Verschlüsselung durchgeführt wird, ist es immer NIL. Manchmal steht aber auch NIL drin, wenn die Tabelle verschlüsselt ist.

0044 Pointer CryptInfoEndPtr

Zeigt auf das Ende von CryptInfo. Wenn keine Verschlüsselung durchgeführt wird, ist es immer NIL.

0048 Byte Unbekannt
0049 LongInt AutoInc

Dieses Feld speichert den Wert, der beim Auto-Increment- Feld-Typ "+" der Tabelle benutzt wird.

004D Word Unbekannt
004F Byte IndexUpdateRequired
0050

0054

  Unbekannt
0055 Byte RefIntegrity

Ein Wert von $2? bedeutet, das für diese Tabelle ein Integritäts Test durchgeführt werden sollte.

0056 Byte Unbekannt
0057 Byte Unbekannt

4.2 Data File Header

Gilt nur für Paradox Versionen >= 4.0

Data File Header $0058..$0077

Offset Typ Bedeutung
0058 Integer Unbekannt (File Version ID?)

$0105..$0109 Version 4.x (meistens = $0109)
$010A, $010B Version 5.x (meistens = $010B)
$010C Version 7.0

005A Integer Unbekannt (File Version ID?)

Identisch mit $0058

005C LongInt Encryption2

Position der Verschlüsselungs Information bei Paradox 3.0 und 3.5 (Hier steht $00000000, wenn keine Verschlüsselung vorliegt). Die Folge-Versionen speichern hier immer $FF00FF00 und den Verschlüsselungs-Code ab Offset $005C.

Primär- und Sekundär Index Files speichern ihre Schlüssel auch an dieser Stelle, sie enthalten jedoch häufig 0en, weil Die Paradox Engine und die BDE überhaupt keine Index Dateien verschlüsseln.

Eine Verschlüsselung kann aber durch kopieren dieser Felder in die Index Dateien trotzdem durchgeführt werden.

0060 LongInt FileUpdateTime (nur bei Version 4.x)

Das Format ist identisch mit dem vom Packed-Date-Time.

0064 Integer HiFieldID

Diese Zahl ist immer NumFields + 1.

0066 Integer HiFieldIDinfo?

Dieser Wert ist identisch mit HiFieldID, seine Funktion ist jedoch nicht bekannt.

0068 Integer SometimesNumFields?

Hier steht meistens die Anzahl der Felder in der Tabelle, aber der Wert ist häufig 0. Warum ist nicht bekannt.

006A Integer DosGlobalCodePage

Dies ist der Global Code Page Wert, der beim Erzeugen der Tabelle angelegt wird.

$01B5 United States
$02E1 Greek 1
$0352 Multilingual (Latin I)
$0354 Eastern European (Latin II)
$0359 Turkish
$035C Portuguese
$035D Icelandic
$035F Canadian French
$0361 Nordic
$0365 Greek 2

Mehr Informationen sind im MS-DOS Technical Manual beim Interrupt $21, Funktion $66 zu finden.

006C

006F

Bytes Unbekannt
0070 Integer ChangeCount4
0072

0077

Bytes Unbekannt

4.3 Data Block

Byte 1 bis 6 sind von der BDE Reserviert. Danach folgt eine Anzahl von Datenrecords. Die Anzahl ist von der Größe eines Records abhängig. Die Recordgröße kann aus den einzelnen Typenangaben jedes Tabelleneintrages berechnet werden. Da die Anzahl der Records nicht immer genau einen Block ausfüllen, befinden sich am Blockende eine unbestimmte Anzahl von Füllbytes.

Es sind nur die Felder enthalten, die mit einem Index markiert wurden.

0000 Word NextBlock

Nummer des nächsten logischen Blocks. Diese Serie von Pointer wird auch Verkettete Liste bezeichnet. Die BDE berechnet die physikalische Startadresse eines Blocks aus der Blocknummer multipliziert mit der Blockgröße okus der Größe des Headers.

0002 Word LastBlock

Nummer des letzten Blocks.

0004 Integer LastBlockPtr

Zeiger auf den Start des letzten Record in diesem Block.

Ein Zähler für die Anzahl der aktiven Records in diesem Block. Dieses Feld enthält eine 0, wenn nur ein Record vohanden ist und eine negative Zahl (0 - RecordSize) wenn kein Record da ist.

NumRecsInBlock = (LastBlockPtr / RecordSize) + 1

0006 Record 1. Record

0000 Inhalt des Records der Länge nn der einen Index besitzt. Hier stehen alle Records, die einen Index haben.
Anschliessend stehen 3 Integer:
nn+0

Word

Enthält die Blocknummer, die zum Key Feld gehört.
Beim Level 1 Block ist es die DB File Blocknummer
Bei einem höherem Level ist es die PX File Blocknummer.
nn+2 Word Statistik
nn+6 Word Unbekannt, da steht immer eine 0 drin.

---- Record 2. Record
---- Record n.Record

Die Index Blocks sind als Baumstruktur gespeichert.

Beispiel:
Wir haben 10000 Records.
Der Key ist ein Integer zwischen 1 und 10000.
1 Data Block enthält 10 Records.
1 Index Block enthält 10 Indexe. Es passen zwar wesentlich mehr rein, aber das Beispiel wird dadurch einfacher.

Wenn der erste Daten Record eingefügt wird, wird die Index Tabelle angelegt. Der Index Record enthält die Blocknummer des ersten Data Blocks (Block 1 in DB) und den Key des des ersten Records in diesem Daten Block (Key = 1).

Wenn Record 2 bis 10 eingefügt wird, werden sie im ersten Datenblock der DB abgelegt. Es wird kein weiterer Index Record angelegt.

Wenn Record 11 eingefügt wird, wird eine neuer Daten Block erzeugt. Es wird ein Index Record erzeugt, der den Key des des ersten Records des neuen Blocks (Key = 11) und die Blocknummer des zweiten Datenblocks enthält. Die Index Tabelle enthält nun zwei Records.

Wenn Daten Record 101 eingefügt wird, dann ist keine Platz mehr in diesem Block, und es wird eine neuer angelegt. Die Index Tabelle enthält jetzt zwei Blocks.

Jetzt wird ein Index Block erzeugt, der über einen Index die ersten beiden Indexblocks verwaltet. Dieser Block wird als Level 2 Index bezeichnet. Die ersten beiden Blocks werden als Level 1 Index bezeichnet. Der Lever 2 Index enthält den ersten Key von jeder Level 1 Index Tabelle. Die Struktur sieht jetzt folgendermaßen aus:

Level 2 Index    Level 1 Index

Key              Keys im Block
  1 -----------> 1, 11,21, 31, ..., 91
101 -----------> 101

Der Level 2 Record zeigt auf einen Block im Level 1 Index. Dieser Pointer ist die Blocknummer des Level 1 Index Blocks.

Wenn Record 1001 eingefügt wird, wird ein zweiter Level 2 Index Block erzeugt und ein Level 3 Index Block wird angelegt und die Level 2 Index Blocks zu verwalten.

Nachdem 10000 Record eingefügt wurden, besitzt die Struktur folgendes aussehen:

Um einen Record über den Index zu finden, startet man beim Root Level (Level 3) Index. Jetzt wird in jedem Index Level der Key gesucht, der kleiner oder gleich dem gesuchten Key ist. Die Blocknummer aus dem Level 1 Index zeigt dann auf die gesuchte Blocknummer im Data File.

Als Beispiel wird Record 185 gesucht:

5. X** File

Ein X** File enthält die Daten Records für einen Sekundären Index. Es gibt einen Record für jeden Record im DB File.

Ein X** file hat das gleiche logische Format wie ein DB File.

Ein X** Daten Record enthält das Sekundär Index Feld, gefolgt vom Primär Index Feld. Ein zusätzliche Typ S Feld mit der Bezeichnung 'Hint' ist das letzte Feld im Record. Alle Felder, außer 'Hint' sin im Record Key enthalten.

Beispiel:

Wenn der Daten Record den Key 'Custid' hat, und es wird ein Sekundärer Index 'Last Name' und 'First Name' definiert, dann enthält der X** Record vier Felder: [Custid], [Last Name], [First Name] und [Hint]. Die ersten drei Felder sind im Primär Index des X** Files.

[Hint] enthält die Block Nummer des Blocks aus dem DB File, welcher mit dem Index Record verbunden ist. Dies bedeutet, das der DB Record direkt angesprochen werden kann. Er braucht nicht über die Index Tabelle aus dem PX File adressiert werden.

[HINT] ist immer ein Feld vom Typ S definiert und wird von Paradox als ein Integer behandelt. Paradox weiß, das hier eine Block Nummer leigt.

Wenn mehr als 16 Sekundär Index Felder spezifiziert werden, dann werden nur die ersten 16 im Index eingefügt.

5.1 File Header

Common Header $0000..$0057

Offset Typ Bedeutung
0000 Integer RecordSize

Größe des Records in Bytes.

0002 Integer HeaderSize (always $0800) |

Die HeaderSize kann geändert werden um kleinere oder größere Header zu erzeugen. Borland's TUTULITY Programm gibt jedoch dann eine Fehlermeldung aus. Paradox, die BDE und die Paradox Engine arbeiten jedoch kommentarlos mit dieser Änderung.

0004 Byte FileType

00 = Ein Index Daten File: *.DB
01 = Ein Primär-Index File: *.PX
02 = Ein Nicht-Index Daten File: *.DB
03 = Ein Nicht-Inkrementierender Sekundär-Index File: *.Xnn
04 = Ein Sekundär-Index File: *.Ynn
05 = Ein Inkrementierender Sekündär-Index File: *.Xnn
06 = Ein Nicht-Inkrementierender Sekundär-Index File: *.XGn
07 = Ein Sekundär-Index File: *.YGn
08 = Ein Inkrementierender Sekündär-Index File: *.XGn

0005 Byte MaxTableSize

Dies ist die Größe eines Daten-Blocks. Wird angelegt, wenn die Tabelle erzeugt wird.
01 = 64M (Block Größe = $0400 Bytes)
02 = 128M (Block Größe = $0800 Bytes)
03 = 192M (Block Größe = $0C00 Bytes) nicht in 4.5
04 = 256M (Block Größe = $1000 Bytes)

0006 LongInt NumRecords

Anzahl der Records in diesem File.

000A Word UsedBlocks

Anzahl der benutzten Blöcke.

000C Word FileBlocks

Anzahl der Daten Blocks in diesem File. (Jeder Block ist ein Zusammenschluß von Records)

000E Word FirstBlock

Immer 1, es sei denn die Tabelle ist leer.

0010 Word LastBlock

Nummer des letzten belegten Blockes in der Tabelle.

0012 Word Unbekannt
0014 Byte ModifiedFlags1

Ein Rebuild der Tabelle ist erforderlich, wenn der Wert ungleich 0 ist.

0015 Byte IndexFieldNumber

In einem *.Xnn File eines Sekundär-Index, ist dies die Anzahl der Felder, die referenziert werden.

In allen anderen Files ist dieser Wert 0.

0016 Pointer PrimaryIndexWorkspace

Immer NIL bei DB Tabellen.

001A Pointer Unbekannt (Suspected Pointer)

Dieses Feld ist normalerweise ein NIL Pointer. Eine Änderung wurde beim Feldtyp BCD, in einer 5.0 Tabelle beobachtet. Wahrscheinlich ist es nur ein Arbeits Pointer.

001E

0020

Unbekannt

Die Benutzung dieser Bytes wurde bisher nur in einem *.PX File beobachtet.

0021 Integer NumFields

Anzahl der Felder in dieser Tabelle.

0023 Integer PrimaryKeyFields

Anzahl der Felder, die einen Primär-Schlüssel haben.

0025 LongInt Encryption1

Position der Verschlüsselungs Information bei Paradox 3.0 und 3.5 (Hier steht $00000000, wenn keine Verschlüsselung vorliegt). Die Folge-Versionen speichern hier immer $FF00FF00 und den Verschlüsselungs-Code ab Offset $005C.

Primär- und Sekundär Index Files speichern ihre Schlüssel auch an dieser Stelle, sie enthalten jedoch häufig 0en, weil Die Paradox Engine und die BDE überhaupt keine Index Dateien verschlüsseln. Eine Verschlüsselung kann aber durch kopieren dieser Felder in die Index Dateien trotzdem durchgeführt werden.

0029 Byte SortOrder

$00: ASCII
$B7: International
$82: Norwegisch/Dänisch
$E6: Norwegisch/Dänisch (4.0)
$F0: Schwedisch/Finnisch

002A Byte ModifiedFlags2

Ein Rebuild der Tabelle ist erforderlich, wenn der Wert ungleich 0 ist.

002B Word Unbekannt (immer 0)
002D Byte ChangeCount1

Wird bei jedem File Header upgedate inkrementiert.

002E Byte ChangeCount2

Es ist noch nicht klar, wenn dieses Feld inkrementiert wird.

002F Byte Unbekannt
0030 ^pChar TableNamePtr

Dies ist ein Pointer auf TableNamePtr, welcher ein Pointer auf TableName ist.

Paradox benutzt dieses Feld, um einen schnelleren Zugriff auf TableName zu haben, weil auf diesen Teil der Tabelle sequentiell zugegriffen wird.

Wird nur im RAM benutzt.

0034 Pointer FldInfoPtr

Pointer auf die Liste der Feld ID's.

Dieser Pointer kann benutzt werden, um den Tabellen-Header im RAM zur Laufzeit zu erreichen. Für Paradox Version > 4.0 muß $0078 subtrahiert werden, für kleinere Versionen und *.PX und *.Ynn Dateien $0058.

0038 Byte WriteProtected

0 Schreibschutz Aus
1 Schreibschutz An

0039 Byte FileVersionID

$03 Version 3.0
$04 Version 3.5
$05..09 Version 4.x (meistens = $09)
$0A,$0B Version 5.x
$0C Version 7.x

003A Word MaxBlocks

Funktion ist unbekannt. Der Wert ist immer mit FileBlocks identisch (bei Offset $000C).

003C Byte Unbekannt
003D Byte AuxPasswords

Anzahl der Hilfs-Passworte für diese Tabelle.

003E Word Unbekannt
0040 Pointer CryptInfoStartPtr

Zeigt auf das CryptInfo Feld. Wenn keine Verschlüsselung durchgeführt wird, ist es immer NIL. Manchmal steht aber auch NIL drin, wenn die Tabelle verschlüsselt ist.

0044 Pointer CryptInfoEndPtr

Zeigt auf das Ende von CryptInfo. Wenn keine Verschlüsselung durchgeführt wird, ist es immer NIL.

0048 Byte Unbekannt
0049 LongInt AutoInc

Dieses Feld speichert den Wert, der beim Auto-Increment- Feld-Typ "+" der Tabelle benutzt wird.

004D Word FirstFreeBlock

Nummer des ersten freien Blocks

004F Byte IndexUpdateRequired
0050

0054

  Unbekannt
0055 Byte RefIntegrity

Ein Wert von $2? bedeutet, das für diese Tabelle ein Integritäts Test durchgeführt werden sollte.

0056 Byte Unbekannt
0057 Byte Unbekannt
0058 Bytes Start des Feld-Beschreibungs-Array

Das Feld-Beschreibungs-Array enthält 2 Bytes für jedes Feld.

Feldnamen beginnen bei einem Offset von 120 ($78) plus 83 Bytes plus 6 Bytes für die Feldnummern. Die Feldnamen entsprechen der Reihenfolge der Feldnummern. Jeder Feldname ist ein nullterminierter String.

Bei einem zusammengesetzten Index (mehr als 1 Sekundär Index Feld) hat das Feld den gleichen Namen wie im DB File.

Bei einem einfachen Index (nur 1 Sekundär Index Feld) wird der Name durch 'Sec Key' ersetzt.

Der Primär Key Feldname ligt hinter den Sekundär Feldnamen. 'Hint' liegt hinter den Primär Key Feldnamen.

Direkt hinter dem nullterminierten 'Hint' liegt eine Reihe von nn 2-Byte Integers, wobei nn die Anzahl der Felder in diesem Record sind. Der erste Integer Wert enthält die Anzahl der Felder im Sekundären Index.

Direkt hinter den Integers, liegt ein nullterminierter String der die Sort Order enthält, z.B 'ascii'.

Im X** Header Block wird kein Sekundär Index Daten Record gespeichert.

5.2 Daten Block

Der Datenblock hat das gleiche Format wie der Datenblock vom DB File:

Byte 1 bis 6 sind von der BDE Reserviert. Danach folgt eine Anzahl von Datenrecords. Die Anzahl ist von der Größe eines Records abhängig. Die Recordgröße kann aus den einzelnen Typenangaben jedes Tabelleneintrages berechnet werden. Da die Anzahl der Records nicht immer genau einen Block ausfüllen, befinden sich am Blockende eine unbestimmte Anzahl von Füllbytes.

0000 Word NextBlock

Nummer des nächsten logischen Blocks. Diese Serie von Pointer wird auch Verkettete Liste bezeichnet. Die BDE berechnet die physikalische Startadresse eines Blocks aus der Blocknummer multipliziert mit der Blockgröße okus der Größe des Headers.

0002 Word LastBlock

Nummer des letzten Blocks.

0004 Integer LastBlockPtr

Zeiger auf den Start des letzten Record in diesem Block.

Ein Zähler für die Anzahl der aktiven Records in diesem Block. Dieses Feld enthält eine 0, wenn nur ein Record vohanden ist und eine negative Zahl (0 - RecordSize) wenn kein Record da ist.

NumRecsInBlock = (LastBlockPtr / RecordSize) + 1

0006 Record 1. Record

Alpha Felder sind eine Folge von Zeichen, die mit 00 bis zur definierten Länge aufgefüllt sind. Alle anderen Felder werden im Big-Endian-Format angegeben. Bei positiven Zahlen ist immer das MSB gesetzt.

Short Integer: 1 = $80 $01, 512 = $82 $00

Real Zahlen werden immer als 8-Byte Double Typ gespeichert. Das MSB ist immer gesetzt.

Datums Felder enthalten die Anzahl der Tage seit 00.01.0000. Das MSB ist immer gesetzt.

---- Record n. Record
xxxx Bytes Dummy

nn Bytes vom letzten Record bis zum Blockende. Anzahl Bytes sind sind immer kleiner als die Recordlänge.

6. Y** File

Das Y** File ist der Primär Indes für ein X** File.

Sein logisches Format ist identisch mit dem vom PX File.

6.1 File Header

Die Beschreibung ist identisch mit der vom PX File.

Die Länge des Index Records ist um 6 Bytes größer als die Summe der Länge der Key Felder.

In diesem Block werden keine Index Records gespeichert.

6.2 Index Block

Es ist das gleiche Format wie der Index Block beim PX File.