summaryrefslogtreecommitdiff
path: root/docview/src/IPFFileFormatUnit.pas
blob: 7d6aade7965e51e6f2658de1ef80f2af969f823c (plain)
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
Unit IPFFileFormatUnit;

{$mode objfpc}{$H+}

interface

// Definition of IPF file header and other structures

uses
  SysUtils;

type
  uint32  = longword;       // 4 bytes
  uint16  = word;           // 2 bytes
  uint8   = byte;           // 1 byte
  pUInt16 = ^uint16;
  pUInt32 = ^uint32;
  pUInt8  = ^uint8;
  Unsigned_31 = 0 .. (1 shl 31) - 1;  // 31-bit type
  Unsigned_4  = 0 .. (1 shl 4) - 1;   // 4-bit type
  Unsigned_1  = 0 .. (1 shl 1) - 1;   // 1-bit type


  PCharArray  = packed array[ 0..0 ] of PCHar;
  UInt32Array = packed array[ 0..0 ] of UInt32;
  UInt16Array = packed array[ 0..0 ] of UInt16;
  UInt8Array  = packed array[ 0..0 ] of UInt8;

  PCharArrayPointer   = ^PCharArray;
  UInt32ArrayPointer  = ^UInt32Array;
  UInt16ArrayPointer  = ^UInt16Array;
  UInt8ArrayPointer   = ^UInt8Array;

  TBooleanArray = array[ 0..0 ] of boolean;
  BooleanArrayPointer = ^TBooleanArray;


  EHelpFileException = class( Exception )
  end;


  EWindowsHelpFormatException = class( Exception )
  end;


  TProgressCallback = procedure(n, outof: integer; AMessage: string) of object;


const
  ErrorCorruptHelpFile = 'Corrupt help file, or something similar';

const
  INF_HEADER_ID = 'HSP';

Type
  THelpFileHeader = packed record
    ID: array[0..2] of ansichar; // ID magic word "HSP"
    flags: uint8;         // probably a flag word...
                          // [0x01] bit 0: set if INF style file
                          // [0x10] bit 4: set if HLP style file
                          // patching this byte allows reading HLP files
                          // using the VIEW command, while help files
                          // seem to work with INF settings here as well.
    hdrsize: uint16;      // total size of header
    version_hi: uint8;
    version_lo: uint8;
    ntoc: uint16;         // number of entries in the tocarray
    tocstart: uint32;     // file offset of the start of the toc
    toclen: uint32;       // number of bytes in file occupied by the toc
    tocoffsetsstart: uint32;     // file offset of the start of array of toc offsets
    nres: uint16;         // number of panels with ressource numbers
    resstart: uint32;     // 32 bit file offset of ressource number table
    nname: uint16;        // number of panels with textual name
    namestart: uint32;    // 32 bit file offset to panel name table
    nindex: uint16;       // number of index entries
    indexstart: uint32;   // 32 bit file offset to index table
    indexlen: uint32;     // size of index table
    icmdCount: uint16;    // number of icmd index items
    icmdOffset: uint32;   // file offset to icmd index items
    icmdSize: uint32;     // size of icmd index table
    searchstart: uint32;  // 31 bit file offset of full text search table
                          // Note: top bit indicates 16 or 8 bit search record!
    searchlen: uint32;    // size of full text search table
    nslots: uint16;       // number of "slots"
    slotsstart: uint32;   // file offset of the slots array
    dictlen: uint32;      // number of bytes occupied by the "dictionary"
    ndict: uint16;        // number of entries in the dictionary
    dictstart: uint32;    // file offset of the start of the dictionary
    imgstart: uint32;     // file offset of image data
    maxCVTIndex: byte;    // highest index inside panel's local dictionary,
                          // always seems to be 245
    nlsstart: uint32;     // 32 bit file offset of NLS table
    nlslen: uint32;       // size of NLS table
    extstart: uint32;     // 32 bit file offset of extended data block
    reserved: array[ 0..11 ] of byte; // for future use. set to zero.
    title: array[ 0..47 ] of ansichar;    // ASCII title of database
  end;
  TPHelpFileHeader = ^THelpFileHeader;

  TExtendedHelpFileHeader = packed record
    NumFontEntry: uint16;             // FONT TABLE:   Number entries
    FontTableOffset: uint32;          // FONT TABLE:   Offset in file
    NumDataBase: uint16;              // DATA BASE:    Number of files
    DataBaseOffset: uint32;           // DATA BASE:    Offset in file
    DataBaseSize: uint32;             // DATA BASE:    Size in bytes
    EntryInGNameTable: uint16;        // GLOBAL NAMES: Number entries
    HelpPanelGNameTblOffset: uint32;  // GLOBAL NAMES: Offset in file
    StringsOffset: uint32;            // STRINGS : Offset in file
    StringsSize: uint16;              // STRINGS : Total bytes of all strings
    ChildPagesOffset: uint32;         // CHILDPAGES : Offset in file
    ChildPagesSize: uint32;           // CHILDPAGES : Total bytes of all strings
    NumGIndexEntry: uint32;           // Total number of Global Index items
    CtrlOffset: uint32;               // CTRL BUTTONS : offset in file
    CtrlSize: uint32;                 // CTRL BUTTONS : size in bytes
    Reserved: array[0..3] of uint32;  // For future use. Set to zero
  end;
  TPExtendedHelpFileHeader = ^TExtendedHelpFileHeader;

Type
  TTOCEntryStart = packed record
    length: uint8; // length of the entry including this byte (but not including extended data)
    flags: uint8; // flag byte, description folows (MSB first)
                  // bit8 haschildren;  // following nodes are a higher level
                  // bit7 hidden;       // this entry doesn't appear in VIEW.EXE's
                                        // presentation of the toc
                  // bit6 extended;     // extended entry format
                  // bit5 stuff;        // ??
                  // int4 level;        // nesting level
    numSlots: uint8; // number of "slots" occupied by the text for
                                // this toc entry
  end;
  pTTOCEntryStart = ^TTOCEntryStart;

  TExtendedTOCEntry = packed record
    w1: uint8;
               // bit 3: Window controls are specified
               // bit 2: Viewport
               // bit 1: Size is specified.
               // bit 0: Position is specified.
    w2: uint8;
               // bit 3:
               // bit 2: Group is specified.
               // bit 1
               // bit 0: Clear (all windows before display)
  end;
  pExtendedTOCEntry = ^TExtendedTOCEntry;

  TTOCEntryOffsetArray =  packed array[ 0..0 ] of uint32;
  pTTOCEntryOffsetArray = ^TTOCEntryOffsetArray;

const
  TOCEntryExtended      = $20; { extended entry format }
  TOCEntryHidden        = $40; { this entry doesn't appear in VIEW.EXE's presentation of the toc }
  TOCEntryHasChildren   = $80; { following nodes are a higher level }
  TOCEntryLevelMask     = $0f;

type
  THelpXYPair = packed record
    Flags: uint8;
    X: uint16;
    Y: uint16;
  end;
  pHelpXYPair = ^THelpXYPair;

  TSlotHeader = packed record
    stuff: uint8;             // always 0??
    localdictpos: uint32;     // file offset of the local dictionary
    nlocaldict: uint8;        // number of entries in the local dict
    ntext: uint16;            // number of bytes in the text
  end;
  pSlotHeader = ^TSlotHeader;

  THelpFontSpec = packed record
    FaceName: array[ 0..32 ] of ansichar;
    Height: uint16;
    Width: uint16;
    Codepage: uint16;
  end;
  pTHelpFontSpec = ^THelpFontSpec;

  TNlsHeader = packed record
    NlsSize: uint16;
    NlsType: uint8;
    NlsFormat: uint8;
  end;

  TNlsCountryDef = packed record
    { if the following is true then...
      NlsHeader.size = 10
      NlsHeader.type = NLSRecType.CONTROL
      NlsHeader.format = 0
    }
    Value: uint16;      // =256
    Code: uint16;       // country code
    Page: uint16;       // code page
    Reserved: uint16;
  end;

  // Single-byte character set
  TSbcsNlsGrammerDef = packed record
    { if the following is true then...
      NlsHeader.size = 36
      NlsHeader.type = NLSRecType.WORD || NLSRecType.GRAPHIC
      NlsHeader.format = 0
    }
    bits: array[0..31] of uint8;  // high-order bits first
  end;

  TPanelControls = packed record
    ControlCount: uint16;   // number of ControlDef records
    GroupCount: uint16;     // number of GroupDef records
    GroupIndex: uint16;     // for cover page
    Reserved: uint16;
  end;

  TControlDef = packed record
    CtrlType: uint16;       // type of control
    ResourceID: uint16;     // resource id (panel) it belongs to
    { variable length data follows, contains button text }
    // DictString: array of char;
  end;

  TControlGroupDef = packed record
    Count: uint16;          // number of indexes into ControlDef records
    { variable length data follows }
    // index[count] of type uint16
  end;

// List of IPF escape codes. 

const
  // Basic byte codes
  IPF_END_PARA = $fa;
  IPF_CENTER = $fb;
  IPF_INVERT_SPACING = $fc;
  IPF_LINEBREAK = $fd;
  IPF_SPACE = $fe;
  IPF_ESC = $ff; // followed by one of the ecXXX codes below

  // FF XX
  ecSetLeftMargin = $02;
  ecHighlight1 = $04; // hp1,2,3,5,6,7
  ecLinkStart = $05;
  ecFootnoteLinkStart = $07;
  ecLinkEnd = $08;
  ecStartCharGraphics = $0b;
  ecEndCharGraphics = $0c;
  ecHighlight2 = $0d; // hp4,8,9
  ecImage = $0e;
  ecLinkedImage = $0f;
  ecProgramLink = $10;
  ecSetLeftMarginNewLine = $11;
  ecSetLeftMarginFit = $12;
  ecForegroundColor = $13;
  ecBackgroundColor = $14;
  ecFontChange = $19;
  ecStartLines = $1a;
  ecEndLines = $1b;
  ecSetLeftMarginHere = $1c;
  ecStartLinkByResourceID = $1d;
  ecExternalLink = $1f;

  // Subescape codes of
  HPART_DEFINE = 0;
  HPART_PT_HDREF = 1;
  HPART_PT_FNREF = 2;
  HPART_PT_SPREF = 3;
  HPART_HDREF = 4;
  HPART_FNREF = 5;
  HPART_SPREF = 6;
  HPART_LAUNCH = 7;
  HPART_PT_LAUNCH = 8;
  HPART_INFORM = 9;
  HPART_PT_INFORM = 10;
  // ?? 11 ??
  HPART_EXTERN_PT_HDREF = 12;
  HPART_EXTERN_PT_SPREF = 13;
  HPART_EXTERN_HDREF = 14;
  HPART_EXTERN_SPREF = 15;
  HPART_GLOBAL_HDREF = 16;
  HPART_GLOBAL_PT_HDREF = 17;

// -----------------------------------------------------------
// Operations on Int32 arrays, used for searching
// These could be optimised heavily if needed.
procedure AllocUInt32Array( Var pArray: UInt32ArrayPointer;
                            Size: longint );
procedure FreeUInt32Array( Var pArray: UInt32ArrayPointer;
                           Size: longint );

procedure FillUInt32Array( pArray: UInt32ArrayPointer;
                           Size: longint;
                           Value: UInt32 );

procedure AddUInt32Array( pSource: UInt32ArrayPointer;
                          pDest: UInt32ArrayPointer;
                          Size: longint );

// Dest = Dest + source * Multiplier
procedure AddMultConstUInt32Array( pSource: UInt32ArrayPointer;
                                   Multiplier: longint;
                                   pDest: UInt32ArrayPointer;
                                   Size: longint );

procedure AndUInt32Array( pSource: UInt32ArrayPointer;
                          pDest: UInt32ArrayPointer;
                          Size: longint );

// If both source and dest > 0 then
//   add source to dest
procedure AndAddUInt32Array( pSource: UInt32ArrayPointer;
                             pDest: UInt32ArrayPointer;
                             Size: longint );

// if Source > 0 then dest is set to 0
procedure AndNotUInt32Array( pSource: UInt32ArrayPointer;
                             pDest: UInt32ArrayPointer;
                             Size: longint );

// dest = dest or source;
// if source > 0  then set dest to  > 0
procedure OrUInt32Array( pSource: UInt32ArrayPointer;
                         pDest: UInt32ArrayPointer;
                         Size: longint );

// if source = 0 then dest set to >0
procedure NotOrUInt32Array( pSource: UInt32ArrayPointer;
                            pDest: UInt32ArrayPointer;
                            Size: longint );

procedure CopyUInt32Array( pSource: UInt32ArrayPointer;
                           pDest: UInt32ArrayPointer;
                           Size: longint );

procedure ClearUInt32Array( pArray: UInt32ArrayPointer;
                            Size: longint );
procedure SetUInt32Array( pArray: UInt32ArrayPointer;
                          Size: longint );

// returns the result of ORing every array element.
// Can be useful for debugging e.g. seeing at a glance
// if any element is non-zero
function OrAllUInt32Array( pArray: UInt32ArrayPointer;
                           Size: longint ): longint;


Implementation


// Operations on int32 arrays
// -----------------------------------------------------------

procedure AllocUInt32Array( Var pArray: UInt32ArrayPointer;
                            Size: longint );
begin
  GetMem( pArray,
          Size
          * sizeof( UInt32 ) );
end;

procedure FreeUInt32Array( Var pArray: UInt32ArrayPointer;
                           Size: longint );
begin
  FreeMem( pArray,
           Size
           * sizeof( UInt32 ) );
end;

// This is a nice fast implementation of filling an
// array of dwords (Int32/longword)
procedure FillUInt32Array( pArray: UInt32ArrayPointer;
                           Size: longint;
                           Value: UInt32 );
var
  i: integer;
begin
  assert( Size > 0 );
  if Size < 1 then
    Exit;
  for i := 0 to Size-1 do
  begin
    pArray^[i] := Value;
  end;
end;

procedure ClearUInt32Array( pArray: UInt32ArrayPointer;
                            Size: longint );
begin
  FillUInt32Array( pArray, Size, 0 );
end;

procedure SetUInt32Array( pArray: UInt32ArrayPointer;
                          Size: longint );
begin
  FillUInt32Array( pArray, Size, $ffffffff );
end;

procedure AddUInt32Array( pSource: UInt32ArrayPointer;
                          pDest: UInt32ArrayPointer;
                          Size: longint );
var
  i: longint;
begin
  for i := 0 to Size - 1 do
    inc( pDest^[ i ], pSource^[ i ] );
end;

procedure AddMultConstUInt32Array( pSource: UInt32ArrayPointer;
                                   Multiplier: longint;
                                   pDest: UInt32ArrayPointer;
                                   Size: longint );
var
  i: longint;
begin
  for i := 0 to Size - 1 do
    inc( pDest^[ i ], pSource^[ i ] * Multiplier );
end;

procedure OrUInt32Array( pSource: UInt32ArrayPointer;
                         pDest: UInt32ArrayPointer;
                         Size: longint );
var
  i: longint;
begin
  for i := 0 to Size - 1 do
    pDest^[ i ] := pDest^[ i ] or pSource^[ i ];
end;

procedure CopyUInt32Array( pSource: UInt32ArrayPointer;
                           pDest: UInt32ArrayPointer;
                           Size: longint );
begin
  Move(pSource^, PDest^, Size * SizeOf(LongInt));
end;

procedure NotOrUInt32Array( pSource: UInt32ArrayPointer;
                            pDest: UInt32ArrayPointer;
                            Size: longint );
var
  i: longint;
begin
  for i := 0 to Size - 1 do
    if pSource^[ i ] = 0 then
      pDest^[ i ] := 1;
end;

procedure AndUInt32Array( pSource: UInt32ArrayPointer;
                          pDest: UInt32ArrayPointer;
                          Size: longint );
var
  i: longint;
begin
  for i := 0 to Size - 1 do
    pDest^[ i ] := pDest^[ i ] and pSource^[ i ];
end;

procedure AndAddUInt32Array( pSource: UInt32ArrayPointer;
                             pDest: UInt32ArrayPointer;
                             Size: longint );
var
  i: longint;
begin
  for i := 0 to Size - 1 do
    if     ( pSource^[ i ] > 0 )
       and ( pDest^[ i ] > 0 ) then
      inc( pDest^[ i ], pSource^[ i ] )
    else
      pDest^[ i ] := 0;
end;

procedure AndNotUInt32Array( pSource: UInt32ArrayPointer;
                             pDest: UInt32ArrayPointer;
                             Size: longint );
var
  i: longint;
begin
  for i := 0 to Size - 1 do
    if pSource^[ i ] > 0 then
      pDest^[ i ] := 0;
end;

function OrAllUInt32Array( pArray: UInt32ArrayPointer;
                           Size: longint ): longint;
var
  i: longint;
begin
  Result := 0;
  for i := 0 to Size - 1 do
    Result := Result or pArray^[ i ];
end;


end.