File: pxformat.txt

package info (click to toggle)
pxlib 0.6.9-1
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,684 kB
  • sloc: ansic: 5,373; sh: 4,873; makefile: 127
file content (1099 lines) | stat: -rw-r--r-- 63,991 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
 The PARADOX File Structure                         Compiled by Randy Beck
 ==========================                               rb@randybeck.com
                                                  http://www.randybeck.com

    I haven't made changes to this description for a long while, but not
    much has changed.  The latest revision concerns the tableName field,
    which I've discovered has lengthened in Paradox 7.

    I'm not aware of any changes after Paradox 7.  But if anybody knows of
    anything different in Paradox 8 and 9 then please let me know.

    I should say here that many of the fields are listed here as pointers
    while the file is in memory.  I first made this connection back when
    Paradox 4 was still the state of the art.  I have not verified that
    this remains true, but a quick glance at some of the fields makes me
    think it's probably still true.


 This document details the binary file format for Paradox data files.
 There are still a few unknowns, but the important items are covered.

 I refer to the older table format as 3.0 tables and the newer ones as 4.0
 through 7.0, but I believe that most of the 3.0 information may also apply
 to the earlier versions.

 Pascal terms are used to describe data types:
     byte is 1 byte unsigned;
     integer is a 2-byte signed integer;
     word is a 2-byte unsigned integer;
     longint is a 4-byte signed integer;
     char is a 1-byte character;
     pchar is a pointer to a character;
     ^ modifies any type definition to a pointer to that type;
     ^pchar is a pointer to a pointer to a character.

 All pointers are 4-byte pointers.

 Please send additions and corrections to:

     Randy Beck
     P.O. Box 540433
     Orlando, FL 32854-0433
     USA

     email: rb@randybeck.com

 Distribute this freely, but please leave my name and address
 intact so that others may add information.




 GENERAL FILE STRUCTURE
 ======================

    ============================
    |     Header               |
    |     Data Block 0         |
    |     Data Block 1         |
    |     Data Block 2         |
    |     ...                  |
    |     Data Block n         |
    ============================

 The size of the Paradox file header is usually 2048 bytes (see headerSize
 at file offset 0002).  The first portion (offsets 0000 to $0057) has fixed
 field locations.  The next section ($0058 to $0077) was new to Paradox 4.0
 data files.  The rest varies in size -- depending upon the upon the number
 of fields -- and is listed sequentially.

 Some of the information in the file header seems to be needed only while
 it is being used by Paradox in RAM.  A few of these fields are pointers to
 other fields in the header, and are valid only during run-time.  They are
 listed here anyway, although their meaning is subject to change and they
 serve little purpose for third-party software.

 The data area which follows is divided into record blocks.  These use 1024,
 2048, 3072 or 4096 bytes each, depending upon the maximum table size set
 when the table was created.  The field structure of the data area is itself
 unusual in that each data field is arranged in hi-byte to low-byte order.

 Note:  This hi-byte to low-byte arrangement only applies to the user data
 in the table.  Everything else uses the normal low-byte to hi-byte format.

 The structure of primary and secondary index files generally follows that
 of Paradox version 3 data files.




 Paradox Common File Header  -- offsets 0000 to 0057
 ==========================

 With some noted exceptions, this part of the description is common to data
 and index files.  Offsets in this list are given in hexadecimal.  Other
 numbers should be assumed as listed in decimal format unless preceded by a
 dollar sign ('$').

 offset  type        usage
==============================================================================
| 0000 | integer     recordSize                                              |
|      |                                                                     |
|      |        This is the size of a user record in this table.             |
|      |                                                                     |
|      |        For primary index files, each "record" is actually the       |
|      |        field or fields in the index, plus three integers which      |
|      |        are not referenced in the header.                            |
|      |                                                                     |
|      |        Secondary index files also have additional fields, but       |
|      |        these are listed in the header.                              |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0002 | integer     headerSize (always $0800)                               |
|      |                                                                     |
|      |        You can change headerSize, and move the data blocks          |
|      |        accordingly, to create larger or smaller table headers.      |
|      |        Borland's TUTILITY program would flag an error, but          |
|      |        Paradox, the Borland Database Engine and the Paradox         |
|      |        Engine will all still work with these tables.                |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0004 | byte        fileType                                                |
|      |                                                                     |
|      |           0 = this is an indexed .DB data file                      |
|      |           1 = this is a primary index .PX file                      |
|      |           2 = this is a non-indexed .DB data file                   |
|      |           3 = this is a non-incrementing secondary index .Xnn file  |
|      |           4 = this is a secondary index .Ynn file (inc or non-inc)  |
|      |           5 = this is an incrementing secondary index .Xnn file     |
|      |           6 = this is a non-incrementing secondary index .XGn file  |
|      |           7 = this is a secondary index .YGn file (inc or non inc)  |
|      |           8 = this is an incrementing secondary index .XGn file     |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0005 | byte        maxTableSize                                            |
|      |                                                                     |
|      |        This is the "maximum table size" determined when this        |
|      |        table was created.  It really indicates the size of each     |
|      |        block of records in the data section of the table.           |
|      |                                                                     |
|      |           1 =   64M    (block size = $0400 bytes)                   |
|      |           2 =  128M    (block size = $0800 bytes)                   |
|      |           3 =  192M    (block size = $0C00 bytes)                   |
|      |           4 =  256M    (block size = $1000 bytes)                   |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0006 | longint     numRecords                                              |
|      |                                                                     |
|      |        This is the number of records in this file.                  |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 000A | word        nextBlock                                               |
|      |                                                                     |
|      |        I'm not certain what this really is, but it seems to be      |
|      |        the same as fileBlocks unless there is an empty block in     |
|      |        the table.                                                   |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 000C | word        fileBlocks                                              |
|      |                                                                     |
|      |        This is the number of data blocks in the file.               |
|      |        (Each "block" is a cluster of records.)                      |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 000E | word        firstBlock                                              |
|      |                                                                     |
|      |        Always 1 unless the table is empty.                          |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0010 | word        lastBlock                                               |
|      |                                                                     |
|      |        This works out to the number of blocks that the table        |
|      |        would contain if every block was packed.                     |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0012 | word        unknown                                                 |
|      |                                                                     |
|      |        The value of this field seems to the change when records     |
|      |        or blocks have been added to the table, but I still haven't  |
|      |        figured it out.                                              |
|      |        Uwe: This field and 0x2C must be set correctly for a         |
|      |        .PX file (see note at 0x2C). This field is less critical.    |
|      |        Uwe: This field in a .PX file and a .DB file seem to be      |
|      |        related. The value in the .PX file is in most cases 1 larger |
|      |        than in the .DB. The value in the .DB file itself is seldom  |
|      |        larger than 19.                                              | 
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0014 | byte        modifiedFlags1                                          |
|      |                                                                     |
|      |        A rebuild is required if this is not zero.                   |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0015 | byte        indexFieldNumber                                        |
|      |                                                                     |
|      |        In the .Xnn file of a secondary index, this is the number    |
|      |        of the field it is referencing.                              |
|      |                                                                     |
|      |        This will be zero in the other files.                        |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0016 | pointer     primaryIndexWorkspace                                   |
|      |                                                                     |
|      |        Pointer to the primary index file header (in RAM).           |
|      |        This will be a NIL if there is no primary index.             |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 001A | pointer     unknown  (suspected pointer)                            |
|      |                                                                     |
|      |        This field is usually a NIL pointer.  I've only seen it      |
|      |        used in 5.0 tables with BCD field types.  It is probably     |
|      |        just a workspace pointer.                                    |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 001E | word        indexRoot    (Added 14.01.04 by Uwe)                    | 
|      |        Block number of index root (check paradox4.txt)              |
|      |        Only used in .PX files.                                      |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0020 | byte       numIndexLevels   (Added 14.01.04 by Uwe)                 |
|      |        Number of index levels (check paradox4.txt)                  |
|      |        Only used in .PX files.                                      |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0021 | integer     numFields                                               |
|      |                                                                     |
|      |        This is the number of fields in the table.  If this is an    |
|      |        index file, then it would only be the number of fields in    |
|      |        this index.                                                  |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0023 | integer     primaryKeyFields                                        |
|      |                                                                     |
|      |        This is the number of fields in the file's primary key.      |
|      |        It will be a zero for .PX and .Ynn files; and 2 for .Xnn     |
|      |        secondary index files.                                       |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0025 | longint     encryption1                                             |
|      |                                                                     |
|      |        This was where the encryption information was stored for     |
|      |        versions 3.0 and 3.5.  (It was a zero if not encrypted.)     |
|      |                                                                     |
|      |        Subsequent versions store the value $FF00FF00 here, and      |
|      |        move the encryption code to offset $005C.  Even so, these    |
|      |        newer versions still maintain this information at both       |
|      |        locations while working in RAM.                              |
|      |                                                                     |
|      |        Primary and .Ynn secondary index files always use this       |
|      |        field to store the encryption code, but it is often a        |
|      |        zero because the Paradox Engine and the Borland Database     |
|      |        Engine do not always encrypt index files.  You can encrypt   |
|      |        unencrypted index files by following these steps:            |
|      |                                                                     |
|      |           Begin with an empty encrypted table;                      |
|      |           Truncate the index files to headerSize;                   |
|      |           Zeroize nextBlock, fileBlocks, firstBlock and lastBlock;  |
|      |           Copy four bytes from the .DB data file's encryption1      |
|      |               field (for version 3), or the encryption2 field       |
|      |               (for versions 4 and above) into the encryption1       |
|      |               or encryption2 field of the index file;               |
|      |           Test thoroughly.                                          |
|      |                                                                     |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0029 | byte        sortOrder                                               |
|      |                                                                     |
|      |           $00:  ASCII                                               |
|      |           $B7:  International                                       |
|      |           $82:  Norwegian/Danish                                    |
|      |           $E6:  Norwegian/Danish (4.0)                              |
|      |           $F0:  Swedish/Finnish                                     |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 002A | byte        modifiedFlags2                                          |
|      |                                                                     |
|      |        A rebuild is required if this is not zero.                   |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 002B...002C        unknown    (always 0)                                   |
|      |        0x2C has a value in .PX files. If it is not set Paradox will |
|      |        quit with a message like 'the index is not valid anymore'.   |
|      |        See also 0x12. 0x2C increments whenever a change happend     |
|      |        to the .PX file. If you add 2 new records to the .DB file    |
|      |        this will increment by two. If you delete 2 records it will  |
|      |        also increment by two. If you change a primary key field     |
|      |        if will increment for each changed field. If you change a    |
|      |        field and change it back it will be incremented by two.      |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 002D | byte        changeCount1                                            |
|      |                                                                     |
|      |        This is incremented whenever the file header is updated.     |
|      |        Uwe: Set this to 2 in the .DB file if you plan to provide    |
|      |        a primary index file. Usually a 1 is sufficient, but it      |
|      |        seems like the Paradox software will use the index file only |
|      |        if this is not set to 2 (maybe higher). The error msg will   |
|      |        be something like 'index is not up to date'. An explanation  |
|      |        for that could be, that a file whose header has not at least |
|      |        been update once can't have a primary index.                 |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 002E | byte        changeCount2                                            |
|      |                                                                     |
|      |        I'm not certain when this is incremented.                    |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 002F | byte        unknown                                                 |
|      |                                                                     |
|      |        Always zero (Uwe)                                            |
+------+---------------------------------------------------------------------+
| 0030 | ^pchar      tableNamePtrPtr                                         |
|      |                                                                     |
|      |        This is a pointer to tableNamePtr, which is a pointer to     |
|      |        tableName.  Paradox uses this field to gain faster access    |
|      |        to tableName because that part of the header is accessed     |
|      |        sequentially.                                                |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0034 | pointer     fldInfoPtr                                              |
|      |                                                                     |
|      |        Pointer to the list of field identifiers.  This is listed    |
|      |        in the accompanying Pascal record definition as a            |
|      |        PFldInfoRec type.                                            |
|      |                                                                     |
|      |        You can use this pointer value to locate the table header    |
|      |        in memory during run time.  Just subtract $0078 from this    |
|      |        value (for 4.0+ tables), or $0058 (for .PX and .Ynn index    |
|      |        files and version 3.0 tables).                               |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0038 | byte        writeProtected                                          |
|      |                                                                     |
|      |           0        write protection OFF                             |
|      |           1        write protection ON                              |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0039 | byte        fileVersionID                                           |
|      |                                                                     |
|      |           $03      version 3.0                                      |
|      |           $04      version 3.5                                      |
|      |           $05..09  version 4.x   (usually = $09)                    |
|      |           $0A,$0B  version 5.x                                      |
|      |           $0C      version 7.x                                      |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 003A | word        maxBlocks                                               |
|      |                                                                     |
|      |        I don't know what this is for.  It is usually the same       |
|      |        as fileBlocks (at offset 000C).                              |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 003C | byte        unknown                                                 |
+------+---------------------------------------------------------------------+
| 003D | byte        auxPasswords                                            |
|      |                                                                     |
|      |        Number of auxiliary passwords assigned to the table.         |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 003E...003F        unknown (Uwe: this field is always $1f0f in .DB files)  |
+------+---------------------------------------------------------------------+
| 0040 | pointer     cryptInfoStartPtr                                       |
|      |                                                                     |
|      |        Points to cryptInfo field.  It is always NIL when not        |
|      |        encrypted.  It is sometimes NIL even when encrypted.         |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0044 | pointer     cryptInfoEndPtr                                         |
|      |                                                                     |
|      |        Points to end of cryptInfo.  This is NIL if not encrypted.   |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0048 | byte        unknown                                                 |
|      |                                                                     |
|      |        Always zero (Uwe)                                            |
+------+---------------------------------------------------------------------+
| 0049 | longint     autoInc                                                 |
|      |                                                                     |
|      |        This long integer stores the value used for the next auto    |
|      |        incrementing field in tables with a "+" autoincrementing     |
|      |        field type.                                                  |
|      |                                                                     |
|      |        Formerly used as a modification count.                       |
|      |                                                                     |
|      |        My thanks to Orlando Ruiz for informing me of this change.   |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 004D | word        firstFreeBlock  (Uwe: check paradox4.txt)               |
+------+---------------------------------------------------------------------+
| 004F | byte        indexUpdateRequired                                     |
+------+---------------------------------------------------------------------+
| 0050 | byte        unknown                                                 |
|      |                                                                     |
|      |        Always zero (Uwe)                                            |
+------+---------------------------------------------------------------------+
| 0051 | integer     realHeaderSize                                          |
|      |                                                                     |
|      |        Added by Uwe 20.01.2003                                      |
|      |        The exact size of bytes in the header.                       |
|      |                                                                     |
|      |        In .PX files it can be calculated by adding:                 |
|      |          0x58               (size of header)                        |
|      |        + numFields * 2      (Field type and size)                   |
|      |        + 4                  (tableNamePtr)                          |
|      |        + 79 or 261          (len of tablename depending on version) |
|      |                                                                     |
|      |        In .DB files it can be calculated by adding:                 |
|      |          0x78               (size of header)                        |
|      |        + numFields * 2      (Field type and size)                   |
|      |        + 4                  (tableNamePtr)                          |
|      |        + numFields * 4      (fieldNamePtrs)                         |
|      |        + numFields * 2      (fieldNumbers)                          |
|      |        + sum(len(fieldname) (Space used by field names)             |
|      |        + 79 or 261          (len of tablename depending on version) |
|      |        + len(sortOrderID)   (don't forget the trailing zero)        |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0053 | byte        unknown                                                 |
|      |                                                                     |
|      |        Always zero (Uwe)                                            |
+------+---------------------------------------------------------------------+
| 0054 | byte        unknown                                                 |
|      |                                                                     |
|      |        Always zero (Uwe)                                            |
+------+---------------------------------------------------------------------+
| 0055 | byte        refIntegrity                                            |
|      |                                                                     |
|      |        A value here (=2?) denotes that this table uses              |
|      |        referential integrity checks.                                |
|      |                                                                     |
|      |                                                                     |
|      |             inxDirection (sec'y index file only)                    |
|      |                                                                     |
|      |        Secondary .Xnn index files of v7.0 tables use this           |
|      |        field to indicate sort order direction:                      |
|      |                                                                     |
|      |           $01      ascending sort                                   |
|      |           $11      descending sort                                  |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0056 | byte        unknown                                                 |
|      |                                                                     |
|      |        In .DB files this is always 0x20, in .PX files it is         |
|      |        zero. (Uwe)                                                  |
+------+---------------------------------------------------------------------+
| 0057 | byte        unknown                                                 |
|      |                                                                     |
|      |        Always zero (Uwe)                                            |
==============================================================================





 Paradox 4+ Data File Header -- offsets 0058 to 0077
 ===========================

 This part of the description applies only to .DB data files and .Xnn index
 files for Paradox versions 4.0 and later.

 offset  type        usage
==============================================================================
| 0058 | integer     unknown  (file version ID?)                             |
|      |                                                                     |
|      |           $0105..$0109  version 4.x   (usually = $0109)             |
|      |           $010A, $010B  version 5.x   (usually = $010B)             |
|      |           $010C         version 7.0                                 |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 005A | integer     unknown  (file version ID?)                             |
|      |                                                                     |
|      |        same values as at 0058                                       |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 005C | longint     encryption2                                             |
|      |                                                                     |
|      |        This will be zero if not encrypted.                          |
|      |        See encryption1 at offset 0025.                              |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0060 | longint     fileUpdateTime (4.x only)                               |
|      |                                                                     |
|      |        Format similar to a packed date and time.                    |
|      |        I don't know what this does in 5.0 tables.                   |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0064 | integer     hiFieldID                                               |
|      |                                                                     |
|      |        This number is always numFields + 1.                         |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0066 | integer     hiFieldIDinfo?                                          |
|      |                                                                     |
|      |        This is related to hiFieldID (above), but I don't really     |
|      |        know what it's for.                                          |
|      |        Uwe: This value plus 0x58 is the offset to the start of      |
|      |        the fieldNumbers.                                            |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0068 | integer     sometimesNumFields?                                     |
|      |                                                                     |
|      |        This is sometimes the number of fields in the table, but     |
|      |        is often just a zero.  I don't know why.                     |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 006A | integer     dosGlobalCodePage                                       |
|      |                                                                     |
|      |        This was the Global Code Page when this table was created.   |
|      |                                                                     |
|      |           $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                                           |
|      |                                                                     |
|      |        Refer to an MS-DOS technical reference about interrupt $21,  |
|      |        function $66, for more information about this.               |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 006C...006D        unknown                                                 |
|      |                                                                     |
|      |        0x6C is sometimes 88. (Uwe)                                  |
+------+---------------------------------------------------------------------+
| 006E | integer     unknown                                                 |
|      |                                                                     |
|      |        Uwe: This value + 0x60-1 is the number of bytes actually     |
|      |        used in the header (see realHeaderSize).                     |
+------+---------------------------------------------------------------------+
| 0070 | integer     changeCount4                                            |
|      |                                                                     |
|      |        Uwe: This value changes when a record is deleted but not     |
|      |        when a record is changed. It is incremented when you         |
|      |        delete a record and change in to 'none modify' modus in      |
|      |        the Paradox software. It increments a second time when you   |
|      |        leave the Paradox software. Maybe it counts both, header     |
|      |        changes and record deletion.                                 |
+------+---------------------------------------------------------------------+
| 0072...0077        unknown                                                 |
|      |                                                                     |
|      |        Uwe: 0x74 seems to be another changeCount. It increments     |
|      |        when a record changes but not when a record was deleted.     |
|      |                                                                     |
|      |        0x74 and 0x75 has sometimes a value != zero                  |
|      |        All other fields are always zero (Uwe)                       |
==============================================================================





 Paradox Common File Header  -- continued
 ==========================

 The file header continues sequentially.  Since the number of fields varies,
 there are no subsequent fixed offsets.

 This section begins where the previous section left off:

 offset
==============================================================================
| 0058   Paradox tables version 3.0 and 3.5                                  |
| 0058   Paradox .PX and .Ynn index files (any listed version)               |
| 0078   Paradox tables version 4.0 and above                                |
| 0078   Paradox .Xnn index files version 4.0 and above                      |
==============================================================================

         type                                   usage
==============================================================================
| ---- | array[1..(numFields)] of TFldInfoRec   fieldInfo                    |
|      |                                                                     |
|      |        type  TFldInfoRec  = RECORD                                  |
|      |                  fType   : byte;                                    |
|      |                  fSize   : byte;                                    |
|      |              end;                                                   |
|      |                                                                     |
|      |        These are the field identifiers for each field in the        |
|      |        table:                                                       |
|      |                                                                     |
|      |           fType  fSize(decimal)                                     |
|      |           -------------------------                                 |
|      |            $01     v   "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     v   "M"  Memo BLOb                               |
|      |            $0D     v   "B"  Binary Large Object                     |
|      |            $0E     v   "F"  Formatted Memo BLOb                     |
|      |            $0F     v   "O"  OLE                                     |
|      |            $10     v   "G"  Graphic BLOb                            |
|      |            $14     4   "T"  Time                                    |
|      |            $15     8   "@"  Timestamp                               |
|      |            $16     4   "+"  Autoincrement                           |
|      |            $17    17*  "#"  BCD                                     |
|      |            $18     v   "Y"  Bytes                                   |
|      |                                                                     |
|      |        The fSize given for BCD fields is not used for field size.   |
|      |        Instead, fSize denotes the number of digits following the    |
|      |        decimal point.  BCD fields are always 17 bytes long.         |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| ---- | pchar                                  tableNamePtr                 |
|      |                                                                     |
|      |        Pointer to tableName when header is in RAM.                  |
|      |                                                                     |
+======+=====================================================================+
| ---- | array[1..(numFields)] of pchar         fieldNamePtrArray            |
|      |                                                                     |
|      |      * These pointers are not present in the index files. *         |
|      |                                                                     |
|      |        This is an array of pointers that reference the field names  |
|      |        when Paradox (or one of the engines) is running.  The size   |
|      |        of this array depends upon the number of fields.             |
|      |                                                                     |
+======+=====================================================================+
| ---- | array[1..(length varies)] of char      tableName                    |
|      |                                                                     |
|      |        This was the name this file was assigned when created.       |
|      |                                                                     |
|      |        Most tables will use 79 bytes, padded with zeroes.           |
|      |        This was extended with Paradox 7 to 271 bytes.               |
|      |        Uwe: It looks like it is 261 and not 271.                    |
|      |                                                                     |
==============================================================================
|                                                                            |
|     The rest of this information does not apply to .PX and .Ynn files.     |
|                                                                            |
==============================================================================
| ---- | char[]                                 fieldNames                   |
|      |                                                                     |
|      |        These are the ASCIIZ field name(s), arranged sequentially.   |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| ---- | record                                 cryptInfo                    |
|      |                                                                     |
|      |        Encrypted tables would have additional data here.            |
|      |                                                                     |
|      |        Tables with auxiliary passwords would have 256 bytes here    |
|      |        (about which I have no information).                         |
|      |        Encrypted tables without auxiliary passwords would have      |
|      |        one byte for each field here.                                |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| ---- | array[1..(numFields)] of integer       fieldNumbers                 |
|      |                                                                     |
|      |        These seem to be field numbers.  Changing these numbers      |
|      |        in my limited experiments seemed to cause no change in       |
|      |        behavior.  TUTILITY didn't seem to mind either.              |
|      |                                                                     |
|      |        I define this as an array, but the size is determined by     |
|      |        the number of fields in the table.                           |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| ---- | char[]      sortOrderID                                             |
|      |                                                                     |
|      |        An ASCIIZ string representing the sort order for this        |
|      |        table ("ascii", "intl", etc.).                               |
|      |                                                                     |
==============================================================================






 Paradox Data Blocks
 ===================

 The data area begins at offset headerSize (usually $0800).  It is divided
 into blocks of 1024, 2048, 3072 or 4096 bytes -- depending upon the maximum
 table size set when the table was created.

 The entire data block will be encrypted if the table is encrypted.

  byte   type      usage
==============================================================================
| 0000 | word      nextBlock (block number + ???)                            |
|      |                                                                     |
|      |        I don't know what this does.                                 |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0002 | word      blockNumber                                               |
|      |                                                                     |
|      |        The first block is numbered zero.                            |
|      |                                                                     |
+------+---------------------------------------------------------------------+
| 0004 | integer   addDataSize                                               |
|      |                                                                     |
|      |        This represents the amount of data in this block -- in       |
|      |        addition to one record length.                               |
|      |                                                                     |
|      |        This will be a zero if there is one record in this block,    |
|      |        and a negative number if there are no records.               |
|      |                                                                     |
|      |           numRecsInBlock = (addDataSize / recordSize) + 1           |
|      |                                                                     |
|      |        Uwe: I have seen DB files with bogus addDataSize which were  |
|      |        both too large or too small. This is an indication of        |
|      |        records being deleted without removing the record from the   |
|      |        file.                                                        |
|      |                                                                     |
==============================================================================
| 0006.......      fileData                                                  |
|                                                                            |
|               Block size varies according to maxTableSize (at 0005):       |
|                  maxTableSize =  1  (64M):   block size = $0400 bytes      |
|                  maxTableSize =  2 (128M):   block size = $0800 bytes      |
|                  maxTableSize =  3 (192M):   block size = $0C00 bytes      |
|                  maxTableSize =  4 (256M):   block size = $1000 bytes      |
|                                                                            |
|                                                                            |
|   The records in the data area are arranged in field order.  Alpha fields  |
|   are arrays of characters, padded with zeroes.  Other fields seem to be   |
|   arranged in hi-byte to low-byte order, with the first byte's high bit    |
|   set for positive numbers.                                                |
|                                                                            |
|   For example, the number 1 stored as a 2-byte Short integer field is      |
|   arranged this way:  $80, 01.  And the integer 256 would be stored as:    |
|   $81, 00.                                                                 |
|                                                                            |
|   Floating point Number and currency fields are 8-byte DOUBLE types,       |
|   hi-byte to low-byte, with the first byte's high bit set.                 |
|                                                                            |
|   Date fields are stored as the number of days since JAN-0-0000, in high-  |
|   byte to low-byte order with the first byte's high bit set.               |
|                                                                            |
|                                                                            |
|   If you are wondering why the first byte's high bit is set for positive   |
|   numbers, recall from the Paradox Engine's documentation that a "blank"   |
|   SHORT (2-byte) integer is assigned the value of $8000.  By using the     |
|   hi-byte to low-byte format, and by reversing the first byte's high bit,  |
|   the value $8000 is converted to all zeroes.  So a record stored as all   |
|   zeroes is actually all blanks.                                           |
|                                                                            |
|                                                                            |
|   The records in primary index files contain the field or fields in the    |
|   index -- plus three integers that are not described in the header's      |
|   fieldInfo area.                                                          |
|                                                                            |
|                                                                            |
==============================================================================





 Sample Program with Paradox File Header Defined as a Pascal Record
 ==================================================================

 The rest of this text may be copied to a separate file as source code.


Program PXFMT;

(*
    This program will list the field values in the given table.

      Usage:  PXFMT <tablename.db>

    The file name's extension is mandatory.  It also works with index
    files, and displays the index record's three additional fields.

    It will stop if there are any errors.
 *)

uses  Dos, Crt, Objects;

const
    { Paradox codes for field types }
    pxfAlpha        = $01;
    pxfDate         = $02;
    pxfShort        = $03;
    pxfLong         = $04;
    pxfCurrency     = $05;
    pxfNumber       = $06;
    pxfLogical      = $09;
    pxfMemoBLOb     = $0C;
    pxfBLOb         = $0D;
    pxfFmtMemoBLOb  = $0E;
    pxfOLE          = $0F;
    pxfGraphic      = $10;
    pxfTime         = $14;
    pxfTimestamp    = $15;
    pxfAutoInc      = $16;
    pxfBCD          = $17;
    pxfBytes        = $18;


type
    { field information record used in TPxHeader below }
    PFldInfoRec         = ^TFldInfoRec;
    TFldInfoRec         =  RECORD
        fType   : byte;
        fSize   : byte;
    end;


    PPxHeader           = ^TPxHeader;
    TPxHeader           =  RECORD
        recordSize              :  word;
        headerSize              :  word;
        fileType                :  byte;
        maxTableSize            :  byte;
        numRecords              :  longint;
        nextBlock               :  word;
        fileBlocks              :  word;
        firstBlock              :  word;
        lastBlock               :  word;
        unknown12x13            :  word;
        modifiedFlags1          :  byte;
        indexFieldNumber	:  byte;
        primaryIndexWorkspace   :  pointer;
        unknownPtr1A            :  pointer;
        unknown1Ex20            :  array[$001E..$0020] of byte;
        numFields               :  integer;
        primaryKeyFields        :  integer;
        encryption1             :  longint;
        sortOrder               :  byte;
        modifiedFlags2          :  byte;
        unknown2Bx2C            :  array[$002B..$002C] of byte;
        changeCount1            :  byte;
        changeCount2            :  byte;
        unknown2F               :  byte;
        tableNamePtrPtr         : ^pchar;
        fldInfoPtr              :  PFldInfoRec;
        writeProtected          :  byte;
        fileVersionID           :  byte;
        maxBlocks               :  word;
        unknown3C               :  byte;
        auxPasswords            :  byte;
        unknown3Ex3F            :  array[$003E..$003F] of byte;
        cryptInfoStartPtr       :  pointer;
        cryptInfoEndPtr         :  pointer;
        unknown48               :  byte;
        autoInc                 :  longint;
        unknown4Dx4E            :  array[$004D..$004E] of byte;
        indexUpdateRequired     :  byte;
        unknown50x54            :  array[$0050..$0054] of byte;
        refIntegrity            :  byte;
        unknown56x57            :  array[$0056..$0057] of byte;
        case INTEGER of
          3:   (fieldInfo35     :  array[1..255] of TFldInfoRec);
          4:   (fileVerID2      :  integer;
                fileVerID3      :  integer;
                encryption2     :  longint;
                fileUpdateTime  :  longint;  { 4.0 only }
                hiFieldID       :  word;
                hiFieldIDinfo   :  word;
                sometimesNumFields:integer;
                dosCodePage     :  word;
                unknown6Cx6F    :  array[$006C..$006F] of byte;
                changeCount4    :  integer;
                unknown72x77    :  array[$0072..$0077] of byte;
                fieldInfo       :  array[1..255] of TFldInfoRec);

      { This is only the first part of the file header.  The last field
        is described as an array of 255 elements, but its size is really
        determined by the number of fields in the table.  The actual
        table header has more information that follows. }

    end;


    PDataBlock  = ^TDataBlock;
    TDataBlock  =  RECORD
        nextBlock     : word;
        blockNumber   : word;
        addDataSize   : integer;
        fileData      : array[0..$0FF9] of byte;
        { fileData size varies according to maxTableSize }
    end;




procedure ConvertPxField(var N;  F: PFldInfoRec);
{ This will convert both ways, but blanks will be turned to zeroes. }
{ Warning:  Not all field types are converted. }
type TNRec= array[0..16] of byte;
var  i    : integer;
     size : integer;
     NRec : TNRec;

    function ItsBlank : boolean;
    var  i : integer;
    begin
      ItsBlank := TRUE;
      For i := 0 to pred(size) do If TNRec(N)[i] <> 0 then ItsBlank := FALSE;
    end;

begin
  If F^.fType = pxfBCD then { BCD field size value not used for field size }
    size := 17
   else
    size := F^.fSize;
  If (F^.fType in [pxfDate..pxfNumber, pxfTime..pxfAutoInc]) and
     not ItsBlank  { leave blank fields as all zeroes }
   then
    begin
    TNRec(N)[0] := TNRec(N)[0] xor $80;
    For i := 0 to pred(size) do
      NRec[pred(size-i)] := TNRec(N)[i];
    Move(NRec, N, size);
    end;
end;


procedure ConvertPxRecord(Hdr: PPxHeader; P: pointer);
const  IndexF : TFldInfoRec = (fType: pxfShort;  fSize: sizeof(INTEGER));
var  i    : integer;
     F    : PFldInfoRec;
begin
  F := Hdr^.fldInfoPtr;  { begin with the first field identifier }
  For i := 1 to Hdr^.numFields do
    begin
    ConvertPxField(P^, F);
    If F^.fType = pxfBCD then { BCD field size value not used for field size }
      Inc(ptrrec(P).ofs, 17)
     else
      Inc(ptrrec(P).ofs, F^.fSize);
    Inc(ptrrec(F).ofs, sizeof(F^));
    end;
  If Hdr^.fileType = 1 then  { convert primary index information }
    begin
    For i := 1 to 3 do
      begin
      ConvertPxField(P^, @IndexF);
      Inc(ptrrec(P).ofs, 2);
      end;
    end;
end;


procedure WritePxField(var N;  F: PFldInfoRec);
{ not all field types are supported here }
var  i    : integer;
     A    : string;
begin
  Case F^.fType of
    pxfAlpha, pxfMemoBLOb:
      begin
      Move(N, A[1], F^.fSize);
      A[0] := char(F^.fSize);
      For i := length(A) downto 1 do
        If (A[i] = #0) then A[0] := char(pred(i));
      write('"', A, '"');
      end;
    pxfShort:             write(integer(N));
    pxfLong, pxfAutoInc:  write(longint(N));
    pxfCurrency:          write('$', double(N):1:2);
    pxfNumber:            write(double(N):1:3);

    { the rest of the field types are not translated }
    pxfDate:              write('<Date:',longint(N),'>');
    pxfLogical:           write('<Logical:',byte(N),'>');
    pxfBLOb:              write('<BLOb>');
    pxfFmtMemoBLOb:       write('<FormattedBLOb>');
    pxfOLE:               write('<OLE>');
    pxfGraphic:           write('<Graphic>');
    pxfTime:              write('<Time:',longint(N),'>');
    pxfTimestamp:         write('<TimeStamp:',longint(N),'>');
    pxfBCD:               write('<BCD>');
    pxfBytes:             write('<Bytes>');
   else                   write('<unknown>');
    end;
end;


procedure WritePxRecord(Hdr: PPxHeader; P: pointer);
const  IndexF : TFldInfoRec = (fType: pxfShort;  fSize: sizeof(INTEGER));
var  i    : integer;
     F    : PFldInfoRec;
begin
  F := Hdr^.fldInfoPtr;  { begin with the first field identifier }
  For i := 1 to Hdr^.numFields do
    begin
    If i > 1 then write(', ');
    WritePxField(P^, F);
    If F^.fType = pxfBCD then { BCD field size value not used for field size }
      Inc(ptrrec(P).ofs, 17)
     else
      Inc(ptrrec(P).ofs, F^.fSize);
    Inc(ptrrec(F).ofs, sizeof(F^));
    end;
  If Hdr^.fileType = 1 then  { display primary index information }
    begin
    For i := 1 to 3 do
      begin
      If i = 1 then write(';  index fields: ') else write(', ');
      WritePxField(P^, @IndexF);
      Inc(ptrrec(P).ofs, 2);
      end;
    end;
  writeln;
end;


procedure ReadBlock(var S: TStream; Hdr: PPxHeader; var AData );
begin
  S.Read(AData, Hdr^.maxTableSize * $0400)
end;


procedure SeekBlock(var S: TStream; Hdr: PPxHeader; ABlock: word);
var  L   : longint;
begin
  L := ABlock;
  L := (L * Hdr^.maxTableSize * $0400) + Hdr^.headerSize;
  S.Seek(L);
end;



procedure ReadAllRecords(var S: TStream);
var  i      : integer;
     num,z  : word;
     Block  : PDataBlock;
     F      : TFldInfoRec;
     Hdr    : PPxHeader;

    function  FileFormatIsOK : boolean;
    begin
      FileFormatIsOK := (Hdr^.maxTableSize >= 1) and (Hdr^.maxTableSize <= 4)
    end;

    function  FileIsEncrypted : boolean;
    begin
      If (Hdr^.fileVersionID <= 4) or not (Hdr^.fileType in [0,2,3,5]) then
        FileIsEncrypted := (Hdr^.encryption1 <> 0)
       else
        FileIsEncrypted := (Hdr^.encryption2 <> 0)
    end;

begin
  New(Hdr);
  S.Seek(0);
  S.Read(Hdr^, sizeof(Hdr^));
  If (S.Status = stOK) and FileFormatIsOK then
    begin

    { assign the header's fldInfoPtr field }
    If (Hdr^.fileVersionID <= 4) or not (Hdr^.fileType in [0,2,3,5]) then
      Hdr^.fldInfoPtr := addr(Hdr^.fieldInfo35)
     else
      Hdr^.fldInfoPtr := addr(Hdr^.fieldInfo);

    If FileIsEncrypted then
      writeln('This file is encrypted.')
     else
      begin
      New(Block);
      num := 0;
      While (S.Status = stOK) and (num < Hdr^.fileBlocks) do
        begin
        SeekBlock(S, Hdr, num);
        ReadBlock(S, Hdr, Block^);
        If (S.Status = stOK) and (Block^.addDataSize >= 0) then
          begin
          z := 0;
          For i := 0 to (Block^.addDataSize div Hdr^.recordSize) do
            begin
            ConvertPxRecord(Hdr, addr(Block^.fileData[z]));
            WritePxRecord(Hdr, addr(Block^.fileData[z]));
            Inc(z, Hdr^.recordSize);
            end;
          end;
        Inc(num);
        end;

      Dispose(Block);
      end;

    end;
end;


var  Stream   : TBufStream;

Begin
  Assign(Output, '');
  Rewrite(Output);
  Stream.Init(paramstr(1), stOpenRead, 4096);
  ReadAllRecords(Stream);
  If Stream.Status <> stOK then
    writeln(^M^J'Error:  Status=', Stream.Status, ';  Error=', Stream.ErrorInfo);
  Stream.Done;
End.

--------
Found at http://www.password-crackers.com/crack2.html

Try following universal passwords for different Win/DOS versions: jIGGAe or nx66ppx