2007-08Aug-10

by Christof Wollenhaupt, Foxpert

This article is work in progress...

FXP structure

2023 foxpert GmbH
0x00 0x02 FE F2 FF: file type identification. VFP FXP file
0x03 0x1B File header
0x1C 0x4D Code header
0x4E ... Code blocks containing the main code, followed by all procedures and methods
    Procedure headers
    Class construction code
    Class definition table
    Line length information block
    File name block
    File footer block

File header

The division into file and code header has been derieced from the code offsets in the procedure headers block.

0x00 6 Bytes Unknown. Seems to be 20 02 01 00 00 00
0x05 LONG Position of file footer block
0x09 LONG Position of file name block

Code header

The code header starts at position 0x1C

0x0D WORD number of procedures in the file
0x0F WORD number of classes in the file
0x11 LONG 0x00000025
0x15 LONG Position of procedure header list. Add 0x29 to get the file position.
0x19 LONG Position of class header list. Add 0x29 to get the file position.
0x1D LONG Position of unknown block. Add 0x29 to get the file position.
0x21 LONG number of code lines in the line length block.
0x25 LONG Position of line number block. Add 0x29 to get the file position.
0x2A WORD File stamp of the PRG file using the FAT format. Since this format only supports date up to the year 2107, VFP ceases to dectect program changes in 2108:
Bits 0–4: 2-second count, valid value range 0–29 inclusive (0 – 58 seconds).
Bits 5–10: Minutes, valid value range 0–59 inclusive.
Bits 11–15: Hours, valid value range 0–23 inclusive.
0x2C WORD Year part of the file stamp:
Bits 0–4: Day of month, valid value range 1-31 inclusive.
Bits 5–8: Month of year, 1 = January, valid value range 1–12 inclusive.
Bits 9–15: Count of years from 1980, valid value range 0–127 inclusive (1980–2107).

Code blocks

There's one code block for the FXP at the beginning (0x4E) followed by one code block for each procedure in the file.

nn nn   Length of the following code line area
    Code lines. Repeated for each line. Every code line in the sources is compiled into one tokenized code line. Code lines without compilable code are ignored.
  nn nn Length of line these two bytes
  nn VFP command (see the list in section Commands).

Most commands have a number of parameters. The precise format differs. However, most commands start either with an expression or a command clause.

  FE Expression ends. Most statements end with this code, even if they usually don't take expression parameters.
nn nn   Number of entries in the following name table list
    Name list for a single code block. The name list contains all variable, field, UDF function names, etc. that are used in the code. The following entry is repeated for each name.
  nn nn Length of a name
  cc cc... Name of a variable, function, etc...

Procedure headers

Procedure headers directly follow the last code block. There's one header for every procedure. That is, the number of procedure headers is one less than the number of code blocks.

WORD Length of the following procedure name
cc cc... name of the procedure
LONG Position of the code. To get the file position, add 0x29
00 00 unknown
nn nn class number to which this procedure belongs. Class numbers start at 0. Procedures that don't belong to a class use -1 (0xFFFF).

Class header

0x00 WORD length of class name
  STRING class name. The name is not terminated with 0x00
  WORD length of parent class name
  STRING name of parent class.If an OF clause has been specified to define the parent class location, the class name is followed by a colon which is followed by the location.
  LONG Postion of class construction code. Add 0x29 to get the file position
  WORD unknown
  WORD unknown

Class construction code

Line length information block

This block contains informations about the physical lines in the FXP. For each code line it list's the type (executable, not executable) as well as the number of lines. For each line there's a WORD. Here are some samples:

D1 executable code spanning one line
D2 executable code spanning two lines
31 not executable statement of one line
32 two lines that are not executable.

All empty lines and comments before a line belong to the line. A comment followed by a variable assignment is stored as D2. A variable assignment followed by a comment is D1 31. There's one entry for every code line. Therefore each procedure has at least one 31 entry for the ENDPROC that indicates the end of a file.

The first nibble seems to indicate the type of line or the number of parameters, ...

D1 line with a variable assignment (var=value)
31 line with a statement like PROCEDURE or ENDPROC that is not executable
71 Command such as DO...
41 Line with BROWSE
B1 Line with USE
A1 Line with ? and one parameter
E1 Line with ? and two parameters
21 01 Line with ? and three parameters

File name block

The file name block contains details about the PRG name and location.

String Base directory. This is the same directory into which the FXP file has originally been compiled. The string is terminated with a single 0x00.
String Name of the FXP file. The string is determinated with a single 0x00.
String Name and full location of the source file, usually the PRG. This string is determinated with 0x00.

File footer block

The file footer block is the very last block in a prg.

0x00 5 bytes Always 00 29 00 00 00
0x05 LONG Position of the file name block
0x09 LONG unknown. Seems to be 0x00000000
0x0D LONG Length of base directory entry in file name block including the terminating 0x00
0x11 LONG 0x00000000
0x15 LONG 0x00000000

Commands

Commands are identified by a single byte as shown in the following list:

02 ?
03 ??
18 DO
1B ELSE
1D ENDDO
1E ENDIF
25 IF
34 PARAMETERS
35 PRIVATE
37 PUBLIC
38 QUIT
42 RETURN
4A STORE
51 USE
54 variable assignment
55 end of procedure
84 FOR
85 ENDFOR
99 function call
A2 add method
A3 add protected method
AE LOCAL
B0 CD

Functions

Functions are also identified by a single byte code. To deal with more than 255 functions, Microsoft added an escape code (0xEA) that provides another 255 function. This is enough to handle all functions available in FoxPro. Function codes are only used in expressions. The following list contains all regular functions

0x3E LEN
0x5A STR
0xB2 PADR
0xEA extended function. The actual function code follows.

This list contains all those functions that are available through the 0xEA (extended function) code:

0x4E CREATEOBJECT
0xF0 STREXTRACT

Clauses

Clauses share the same value range as expressions and functions:

0x14 FORM
0x16 IN
ox28 TO
0x2B WHILE
0x51 AS
0xD1 WITH

Expressions

Expressions are stored in inverse polish notation. A calculation like lnSum + 5 is stored as:

lnSum 5 +

Expressions must be treated as a stream. The first byte identifies an expression element. However, depending on the type, multiple bytes follow. The following list contains expression elements. Expression codes share the same range of values as functions because both can be mxed:

0x06 Operator Add
0x07 Comma
0x0A Operator Not
0x0D Operator <
0x0E Operator <=
0x0F Operator !=
0x10 Operator =, assignment operator
0x11 Operator >=
0x12 Operator >
0x14 Operator ==
0x43 A parameter list starts. Visual FoxPro uses this as a marker. Any number of expression can follow. When VFP encounters a function code as defined in the list above, the function searches the stack for this opcode. Any expression inbetween is treated as a parameter.
0xD9 A string literal. The next two bytes hold the length of the string in characters. The following bytes contain the actual string. An interesting aspect here is that technically, VFP could store string literals up to 65.535 characters long.
0xE9 An Int32 constant. The next byte indicates the number of digits. This is followed by four bytes containing the actual data.
0xF4 An alias follows. For VFP everything that is followed by a period is an alias (with some exeception). Code such loForm.lblName.Caption is stored as alias "LOFORM", alias "LBLNAME", field name "CAPTION".
0xF8 An Int8 constant. The next byte indicates the number of digits. The following byte contains the actual data. For instance F8 02 14 is the representation of the decimal number 20.
0xF9 An Int16 constant. The following byte indicates the number of digits. The next two bytes hold the actual data.
0xFA A double constant. The next byte holds the total number of digits including the decimal point. The after byte after that one defines the number of digits of the fractional part. These two numbers are identical to the definition of numeric fields in a table. A value in a N(10,3) field would be stored in program code as FA 0A 03. The last eight bytes contain the actual floating point value in the IEEE format used by the computer.
0xFB Starts a name expression. Everything until the FD opcode belongs to the name. VFP uses this opcode when a command expects a name and the source code doesn't contain any expression.