by Christof Wollenhaupt, Foxpert
This article is work in progress...
|0x00||0x02||FE F2 FF: file type identification. VFP FXP file|
|0x1C||0x4D||Code header2023 foxpert GmbH|
|0x4E||...||Code blocks containing the main code, followed by all procedures and methods|
|Class construction code|
|Class definition table|
|Line length information block|
|File name block|
|File footer block|
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|
The code header starts at position 0x1C
|0x0D||WORD||number of procedures in the file|
|0x0F||WORD||number of classes in the file|
|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
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).
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 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|
|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).|
|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|
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|
Commands are identified by a single byte as shown in the following list:
|55||end of procedure|
|A3||add protected method|
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
|0xEA||extended function. The actual function code follows.|
This list contains all those functions that are available through the 0xEA (extended function) code:
Clauses share the same value range as expressions and functions:
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:
|0x10||Operator =, assignment 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.|