Index: README ================================================================== --- README +++ README @@ -96,10 +96,13 @@ * bindings.doc: Describes the configuration of key/mouse bindings. * class.doc: This document describes the Free Hero Mesh programming language. Read this if you want to define your own classes of objects. + +* codepage.doc: Describes the use of code pages and the file format of the +codepage.har file. * commandline.doc: Describes the command-line arguments. The man page also describes the command-line arguments, although the commandline.doc file has a more elaborate description. @@ -106,18 +109,60 @@ * config.doc: Describes the configuration options for Free Hero Mesh. * export.doc: The level export format is described in this file; it can be used for importing as well as exporting. +* man6/heromesh.6: Man page; includes information about command-line +arguments, environment variables, and link to further documentation. + +* man6/heromesh.str: Must be installed in the same directory as the +man6/heromesh.6 file. The distributor should change this to point to +the correct directories where the files are stored; these directories +will be mentioned in the man page + +* misc/mbform.doc: Describes the .mb file format for Hero Mesh. (Not +directly relevant to Free Hero Mesh. It contains information that was +used for writing the converter, and may be of interest to some readers +for other purposes, but is neither used nor does it contain anything +that is needed for users or developers of Free Hero Mesh.) + * picedit.doc: Describes the picture editor. -* puzzleset.doc: Describes wat makes up a puzzle set. +* puzzleset.doc: Describes what makes up a puzzle set. * sql.doc: This file lists and documents each of the SQL functions and SQL tables that are available. This is not relevant to programming the rules of the game; it is used for user customization. + +=== Other files === + +Free Hero Mesh also includes the following other files, which are not the +source code files, compilation files, or documentation files: + +* codepage.har: Contains fonts for code pages. Code page 437 is included +inside of the executable file and does not need this file; this file is +only needed for code pages other than 437. + +* default.heromeshrc: An example configuration file which you can customize +for your own use. You should at least set the proper paths to the files. + +* mbtofhm.c: Converter from Everett Kaser's MESH engine into Free Hero +Mesh. Give the base name of the output files as the command-line argument, +and receives the .mb file from stdin. + +* misc/har.c: A small C program for dealing with Hamster archives. This is +not properly a part of Free Hero Mesh (and is not needed in order to use +or compile Free Hero Mesh), but is included since it is likely to be useful +for some users. + +* misc/sokoban.tar.gz: Example puzzle set. + +(The files in the misc/ directory are not part of Free Hero Mesh nor are +they needed by Free Hero Mesh, but are provided for convenience. They can +safely be ignored or deleted if you are not using them.) + === Community/discussion === For discussion of Free Hero Mesh, use the NNTP server. This is a newsgroup in the zzo38computer.org NNTP server, named: DELETED fileform1.txt Index: fileform1.txt ================================================================== --- fileform1.txt +++ fileform1.txt @@ -1,633 +0,0 @@ -** Hero Mesh File Format ** - -This describes the file format of the Windows version of Hero Mesh. (Free -Hero Mesh uses a different file format described in a different file.) - -All numbers are small-endian and unsigned. WORD is 16-bits, and DWORD is -32-bits, and BYTE is 8-bits. - - -=== Header === - -At the beginning of the file is the following: - -Offset Type Description -0 WORD File version -2 CHAR(4) ASCII characters "MESH" -6 DWORD Puzzle set number -10 WORD Number of picture sizes -12 WORD(8) Picture sizes -28 WORD Number of words in picture allocation table - -File version is 15 for Hero Mesh version 1.1c, and is 16 for Hero Mesh -version 2.0. Other versions (if any) are unknown. The rest of this -document is applicable to file version 16 (differences, if any, are -unknown; file version 15 does not seem to have any differences as far -as I can tell). - -Puzzle set number is a number that can be read by the class codes but -otherwise does nothing. It is used by the Hero Hearts puzzle sets to -determine which puzzle set to link to after completing the last level. - -The picture sizes are all square; the numbers given are the width (which -is equal to the height) of that picture size. For example, if it says 24 -then the pictures are 24x24. - -After this comes the picture allocation table, which specifies which -pictures are allocated. The first word of the allocation table is for the -first sixteen pictures; the low bit is for the first picture, and the high -bit is for the sixteenth picture. - - -=== Pictures === - -Picture data comes next. The encoding is like this C code: - - // nsizes = number of picture sizes (1 to 8; normally 3) - // nalloc = number of words in picture allocation table - // sizes[s] = width or height of picture size s (where s is 0 to 7) - for(s=0;s - } - } - } - } - // Unallocated pictures are still read, even though they aren't used. - -(This may also be considered as one large picture per size; where the -pictures are tile sets with sixteen tiles across.) - -Next comes the picture availability table, which controls which pictures -are available for selection in the level editor. There are always 512 -bytes, one byte per picture. The byte value is 1 if it is available or 0 -if it is not available. - - -=== User Message Table === - -Next is the user message table. It starts with a WORD which is the number -of user messages. User message numbers start with 20; messages 0 to 19 are -the built-in messages. For each user message there is a BYTE giving the -length of the message name, followed by the message name. (There seems to -be always one extra message name at the end which is blank and is unused. -The reason for this is currently unknown.) If the name is empty then that -user message does not exist (it may have been deleted). - -The standard messages are as follows: - 0 = INIT - 1 = CREATE - 2 = DESTROY - 3 = BEGIN_TURN - 4 = ARRIVED - 5 = DEPARTED - 6 = LASTIMAGE - 7 = MOVED - 8 = JUMPED - 9 = KEY - 10 = MOVING - 11 = SUNK - 12 = FLOATED - 13 = PLAYERMOVING - 14 = HIT - 15 = HITBY - 16 = DESTROYED - 17 = CREATED - 18 = POSTINIT - 19 = END_TURN - - -=== Classes === - -After the user message table comes the classes. This starts with a WORD -specifying the number of classes. Classes then follow. - -Each class consists of the following: - -* Name: A BYTE giving the length of the name, followed by the name. - -* ID number: A WORD giving the zero-based class ID number (class ID -numbers are sometimes one-based, but here they are zero-based). - -* Description: One or three bytes for length, followed by the plain text -(using CRLF for line endings). If length is less than 255 then it is one -byte. Otherwise, the first byte is 255 and then a WORD follows which gives -the length of the description. The description isn't null terminated. Any -escapes in the description are interpreted at runtime; they are not stored -with the control codes mentioned for special texts. - -* Attributes: See attribute table below. - -* Number of pictures (WORD) - -* List of pictures. Each is a WORD, and is a zero-based number of the -picture from the picture area; after this, all further picture numbers in -the file are zero-based indexing into this table. - -* Number of user variables (WORD) - -* User variable names; each is eight bytes long and is padded with nulls. -(Variable names are actually limited to seven characters.) - -* Number of subroutine labels (WORD) - -* Label names; each is ten bytes long, and is a eight-byte null terminated -string, with junk in the remaining bytes; the final two bytes are a word -address into the sobroutine or message section. - -* Subroutine program codes - -* Number of message labels (WORD) (these aren't the message names; these -are labels outside of the subroutine section). - -* Message label names; stored like the subroutine label names. - -* Message program codes - -Attribute Table - 0 BYTE ??? - 1 BYTE Flag - 2 WORD Misc4 - 4 WORD Misc5 - 6 WORD Misc6 - 8 WORD Misc7 - 10 WORD Shovable - 12 BYTE(5) Arrivals - 17 BYTE(5) Departures - 22 WORD Density - 24 WORD Volume - 26 WORD Strength - 28 WORD Weight - 30 WORD HardE - 32 WORD HardN - 34 WORD HardW - 36 WORD HardS - 38 WORD SharpE - 40 WORD SharpN - 42 WORD SharpW - 44 WORD SharpS - 46 WORD Height - 48 WORD Climb - 50 WORD Temperature - 52 WORD Shape - 54 WORD ??? - 56 WORD ??? - 58 WORD ??? - 60 WORD ??? - -The flag byte has bit0 set for "receives keys" and has bit7 set for "is -the player". Other flags (if any) are unknown, they appear to be unset. - -The arrivals and departures are stored each as five bytes, each byte for -one row, with the first byte corresponding to the top row. In each byte, -the rightmost column is the low bit; only the low 5-bits are used. (This -is not the same as the representation used in class codes!) - -The last four attributes seem to have something to do with the program -length, but it is unknown. However, it is not necessary to read these in -order to figure out the program length (see below for details). - - -=== Program === - -Each class has program codes; this section describes how the program codes -are encoded in the file. - -The first WORD of the subroutine section is the number of WORDs that make -up the the subroutine section, including the count itself. After that one -WORD header comes the instructions. - -Each message block in the message section then consists of the header and -then the instructions. The header of each message block consists of two -WORDs, the first being the message number, and the second being the length -(in WORDs) of this message block, including the header. - -The program is terminated by a header with both WORDs zero. - -Program instructions are executed using a stack-based virtual machine. -Each instruction is normally one WORD; see below section for the opcodes. - - -=== Program Opcodes === - -The first byte is opcode byte (the major code). The second byte is the -minor code, the meaning depending on the opcode. In the descriptions -below, if it says / and a number, it is how many taken from stack. If -there is * then it pushes to stack, ! means end block, - means no effect -on data stack. - -[1*] Local variables: 0=Class, 1=Temperature, 2=Shape, 4=Xloc, 5=Yloc, -6=LastDir, 7=CurImage, 8=Inertia, 9=Misc1, 10=Misc2, 11=Misc3, 12=Misc4, -13=Misc5, 14=Misc6, 15=Misc7, 16=Arrived, 17=Departed, 18=Arrivals, -19=Departures, 32=Busy, 33=Invisible, 34=UserSignal, 35=UserState, -36=KeyCleared, 37=IsPlayer, 38=Destroyed, 39=Stealthy, 40=VisualOnly, -48=Msg, 49=MsgFrom, 50=MsgArg1, 51=MsgArg2, 64=Density, 65=Volume, -66=Strength, 67=Weight, 68=Distance, 69=Height, 70=Climb, 72=HardE, -73=HardN, 74=HardW, 75=HardS, 76=SharpE, 77=SharpN, 78=SharpW, 79=SharpS, -80=ShapeE, 81=ShapeN, 82=ShapeW, 83=ShapeS, 84=Shovable - -[2/1*] Retrieve local variable from other object. Local variable numbers -are the same as opcode 1. - -[3/1] Assignment to standard local variables of current object. - -[4/2] Assignment to standard local variable of other object (the first -(top) value taken from the stack is the value, and second is the object). - -[5*] User-defined local variable; the second byte is zero-based local -variable number. - -[6/1] Assignment to user-defined variable; the second byte is the -zero-based local variable number to write to. - -[7*] Short decimal constant; the second byte is the 8-bit value. - -[8*] Long decimal constant. The next two words are the small-endian 32-bit -number that it represents. - -[9*] Short class constant; the second byte is a zero-based class number -(the class number is one-based at runtime). - -[11*] Message constant; the second byte is the message number. - -[13*] String literal. Follow by one WORD giving number of bytes of string -(including null terminator, always even), and then null-terminated special -text. There may be two null terminators if needed to make it even. - -[14*] Direction constant (0-7 for absolute, 8-15 for relative) - -[16/1*] Unary operator: 0=negative, 1=bitwise NOT, 2=logical NOT - -[17/2*] Binary operator: 0=multiply, 1=divide, 2=modulo - -[18/2*] Binary operator: 0=add, 1=subtract, 2=AND, 3=OR, 4=XOR - -[19/2*] Bit shift operator: 0=left, 1=right - -[20/2*] Comparison operator: 0=equal, 1=unequal, 2=less, 3=greater, 4=less -or equal, 5=greater or equal - -[21/2*] Logical operator: 0=AND, 1=OR, 2=XOR (not short-circuiting) - -[32] ObjDir - -[34] ObjAbove - -[36] ObjBelow - -[38/2*] 0=ObjTopAt, 1=ObjBottomAt, 2=VolumeAt, 3=HeightAt, 4=Delta - -[39*] Self - -[40/3*] ObjClassAt - -[47*] Array reference; next word is the array address. - -[48*] Key - -[49*] Animation constant: 0=STOP, 1=ONCE, 2=LOOP, 8=OSC - -[50*] Keyboard constant: See section about key codes. - -[51*] Short hexadecimal constant; second byte is the 8-bit value. - -[52*] Long hexadecimal constant. Next two words are the small-endian -32-bit number that it represents. - -[53*] Global variable: 0=Level, 1=LevelCount, 2=AltImage, 3=ExplainDeath, -4=GlobalBool, 5=PuzzleSetNumber, 6=MoveNumber - -[54] XDir - -[56] YDir - -[58] 0=NewX, 1=NewY - -[59*] Bit constant 0-31; the second byte is the number 0-31, and at -runtime it is replaced by the relevant 32-bit number. - -[60*] Sound constant (see section about user sounds) - -[61] 0=ClassCount/1* (I don't know what this does), 1=GetArray - -[64/3*] SendMessage (to Self) - -[65/4*] SendMessage - -[66/3*] Broadcast; second byte is zero-based class number - -[67/4*] Broadcast - -[68/1*] Move(Self, ...); minor code is 255 to take the value from the -stack, or is another number for a direction constant (in this latter case -the same bug occurs as with opcode 84). - -[69/2*] Move; minor code is same as opcode 68, but the bug with opcode 84 -does not occur, regardless of the minor code. - -[70/2*] JumpTo(Self, ...) - -[71/3*] JumpTo - -[72/5*] Create - -[74*] Destroy(Self) - -[75/1*] Destroy - -[80/3] SendMessage(Self, ...) - -[81/4] SendMessage - -[82/3] Broadcast; second byte is zero-based class number - -[83/4] Broadcast - -[84] Move(Self, dir). The second byte is the direction to move. Unlike -the normal Move() function, this one adds Strength to Inertia instead of -setting Inertia equal to Strength (this seems to be a bug). If the minor -code is 255 then takes direction from stack and does not have this bug. - -[85] Move. The second byte is the same as for opcode 84; if 255 then it -takes from the stack (before taking the object to move). In this case the -bug mentioned above does not occur regardless of what the minor code is. - -[88/5] Create - -[96] Comment. Has one WORD giving length of the comment text (including -the null terminator), and then the plain text of the comment (with CRLF -line endings), null terminated. - -[97] Popup setting. Second byte is 0 for PopupColor or 1 for PopupLoc. - -[98] Destroy(Self) - -[99/1] Destroy - -[100] CallSub - -[101-] Goto; next WORD is address to branch to unconditionally - -[102] Return. Second byte is 0 for implicit end of SUBS block, 1 for -return from a subroutine, 2 for the end of a message block. - -[103-] Return short constant. Second byte is return value. - -[104/1] Return - -[105/1] If. Second byte is zero for block-if or one for inline-if. The -next word is the number of words to skip (including the count itself) if -the condition is false. - -[106] Else - -[107/3] ImageSeq (seems to do nothing?) - -[108/3] ImageLoop (seems to do nothing?) - -[109] PopUp. Values taken from the stack is one plus the minor code. - -[110/2] JumpTo(Self, ...) - -[111/3] JumpTo - -[112] Sound - -[114] Array operations. Second byte: 0 = definition of an array, 1 = -SetArray, 2 = InitArray. - -[126] Animate - -[127] Link - -[128] GotoLevel - -[129-] 0=WinLevel, 1=LocateMe, 2=IgnoreKey, 3=PopupMisc, 4=UpdateScreen -(I don't know what UpdateScreen means?) - -[130] FlushClass - -[131] FlushObj - -[132] SetInventory - -[133] DelInventory - -[134] ForEachObjAt - -[240] Trace - - -=== Levels === - -After all classes are the levels. Before the levels comes a header of four -bytes that consists of: - -* Number of levels (WORD) - -* Two null bytes with unknown meaning - -Each level consists of: - -* Zero-based level number (WORD) - -* Level description length, including the null terminator (WORD) - -* Level description; a null-terminated special text - -* Border colours, outer to inner (BYTE(32)) - -* Background colour (BYTE) - -* Null byte (maybe the high byte of the background colour) - -* Number of objects (WORD) - -* Objects, in ordinary progressive television order; within each cell they -go bottom to top. See information about object records below. - -* Number of level strings (WORD) - -* Level strings; each one WORD length (the length includes the null -terminator), followed by the null-terminated special text. - -An object record is sixteen bytes long, and consists of: - 0 WORD Class (zero-based) - 2 WORD CurImage (zero-based) - 4 BYTE LastDir (zero-based; 0=east, 1=northeast, etc) - 5 BYTE Data types for Misc vars - 6 WORD X coordinate (one-based) - 8 WORD Y coordinate (one-based) - 10 WORD Misc1 - 12 WORD Misc2 - 14 WORD Misc3 - -The data types are as follows: - 0 = Number - 1 = Class (one-based) - 2 = Message - 3 = String -Data type for Misc1 is in bit1 and bit0, data type for Misc2 is in bit3 -and bit2, data type for Misc3 is in bit5 and bit4. Bit7 and bit6 are -always zero. - -The data type is used only in the editor and is irrelevant at run time. - - -=== Special Text === - -Level data may contain special text. This is text using the following -control codes (escapes have already been interpreted): - -1 = Black (\0) -2 = Blue (\1) -3 = Green (\2) -4 = Cyan (\3) -5 = Red (\4) -6 = Purple (\5) -7 = Yellow (\6) -8 = White (\7) -10 = Line break (\n) -11 = Left (\l) -12 = Center (\c) -14 = Picture (\i); followed by two WORDs: zero-based class and image -15 = Horizontal line (\b) -16 = Quiz button (\q); followed by one byte key code - - -=== Solutions === - -After the levels come the solutions. There is first one WORD which is the -number of solutions, and then the solutions. Each solution consists of: - -* Zero-based level number (WORD) - -* Number of keys in solution (WORD) - -* User name (BYTE(9)); the user name is null terminated, although there -may be junk after the null terminator. - -* Key codes; one byte each. - - -=== User Sounds === - -Finally, the user sounds come last. One WORD gives the number of user -sounds. And then each sound consists of: - -* Sound ID number (zero-based) (WORD) - -* Name length (BYTE) - -* Name (not terminated; length is given instead) - -* Data length (WORD) - -* Data (a RIFF WAVE file) - -The built-in sounds are as follows: - SPLASH - POUR - DOOR - GLASS - BANG - UNHH - UH_OH - FROG - THWIT - KLINKK - POWER - KLECK - CLICK - SMALL_POP - DINK - TICK - CHYEW - CHEEP - ANHH - BRRRT - BRRREEET - BWEEP - DRLRLRINK - FFFFTT - WAHOO - YEEHAW - OLDPHONE - RATTLE - BEEDEEP - THMP_thmp - BEDOINGNG - HEARTBEAT - LOCK - TAHTASHH - BOOOM - VACUUM - RATCHET2 - DYUPE - UNCORK - BOUNCE - JAYAYAYNG - DEEP_POP - RATCHET1 - GLISSANT - BUZZER - FAROUT - KEWEL - WHACK - STEAM - HAWK - - -=== Key codes === - - 8 BACK - 9 TAB - 12 CENTER (number pad 5 when numlock is off) - 13 ENTER - 16 SHIFT - 17 CTRL - 19 BREAK - 20 CAPSLOCK - 32 SPACE - 33 PGUP - 34 PGDN - 35 END - 36 HOME - 37 LEFT - 38 UP - 39 RIGHT - 40 DOWN - 48 0 (the top row zero) - 57 9 (the top row nine; 1-8 are in between 0 and 9) - 65 A - 90 Z (letters B-Y are in between A and Z) - 96 NUMPAD0 - 105 NUMPAD9 (number pad 1-8 are in between) - 106 MULTIPLY (the number pad "*" key) - 110 DECIMAL (the number pad "." key) - 111 DIVIDE (the number pad "/" key) - 120 F9 - 121 F10 - 122 F11 - 123 F12 - 144 NUMLOCK - 145 SCRLOCK - 186 SEMICOLON - 187 EQUALS - 188 COMMA - 189 MINUS - 190 PERIOD - 191 SLASH - 192 TILDE - 219 OBRACKET - 220 BACKSLASH - 221 CBRACKET - 222 QUOTE - -The number pad and main enter keys have the same key code. When num lock -is off, the number pad keys have the same codes as the other arrow keys. -The NUMPAD codes are only when numlock is on. ADDED misc/har.c Index: misc/har.c ================================================================== --- misc/har.c +++ misc/har.c @@ -0,0 +1,145 @@ +#if 0 +gcc -s -O2 -o ~/bin/har -Wno-unused-result har.c +exit +#endif + +/* + Hamster archiver + Public domain + + c = Create + d = Delete + l = Lowercase + o = Extract to stdout + r = Rename + s = Safe + t = List + u = Uppercase + x = Extract +*/ + +#include +#include +#include + +#define fatal(...) do { fprintf(stderr,__VA_ARGS__); putchar('\n'); exit(1); } while(0) + +static const char safe[128]={ + [0]=1, + ['0'...'9']=1, + ['A'...'Z']=1, + ['a'...'z']=1, + ['-']=1, + ['_']=1, + ['.']=1, + ['/']=1, + ['+']=1, +}; + +static char opt[128]; +static char name[256]; +static unsigned long size; +static int argc; +static char**argv; + +static void process_lump(int a) { + int n=0; + int d=1; + int c,o; + FILE*fp; + FILE*in; + if(opt['c']) { + strcpy(name,argv[a]); + in=fopen(argv[a+opt['r']],"r"); + if(!in) { + perror(argv[a+opt['r']]); + fatal("Cannot open file for input"); + } + if(opt['u']) { + for(n=0;name[n];n++) if(name[n]>='a' && name[n]<='z') name[n]+='A'-'a'; + } else if(opt['l']) { + for(n=0;name[n];n++) if(name[n]>='A' && name[n]<='Z') name[n]+='a'-'A'; + } + fseek(in,0,SEEK_END); + size=ftell(in); + fseek(in,0,SEEK_SET); + o=1; + } else { + in=stdin; + for(;;) { + c=fgetc(stdin); + if(c==EOF) exit(0); + if(opt['u'] && c>='a' && c<='z') c+='A'-'a'; else if(opt['l'] && c>='A' && c<='Z') c+='a'-'A'; + if(opt['s']) { + if(c=='/') d=1; else if(c && c!='.') d=0; + if((c&~127) || !safe[c] || (c=='.' && n && name[n-1]=='.') || (c=='/' && d)) c='_'; + } + name[n++]=c; + if(!c) break; + if(n==254) fatal("Name too long: %s",name); + } + if(opt['s']) { + if(!n) name[1]=0,n=1; + if(d) name[n-1]='_'; + } + size=fgetc(stdin)<<16; + size|=fgetc(stdin)<<24; + size|=fgetc(stdin); + size|=fgetc(stdin)<<8; + if(argc<3) { + o=1; + } else { + o=0; + for(n=2;n>16); + putchar(size>>24); + putchar(size); + putchar(size>>8); + } + } + while(size--) fputc(fgetc(in),fp); + if(opt['x']) fclose(fp); + } else { + while(size--) fgetc(in); + } + if(opt['c']) fclose(in); +} + +int main(int Argc,char**Argv) { + char*t; + int a; + argc=Argc; + argv=Argv; + if(argc>1) for(t=argv[1];*t;) opt[*t++&127]=1; + if(opt['c']) { + for(a=2;a + } + } + } + } + // Unallocated pictures are still read, even though they aren't used. + +(This may also be considered as one large picture per size; where the +pictures are tile sets with sixteen tiles across.) + +Next comes the picture availability table, which controls which pictures +are available for selection in the level editor. There are always 512 +bytes, one byte per picture. The byte value is 1 if it is available or 0 +if it is not available. + + +=== User Message Table === + +Next is the user message table. It starts with a WORD which is the number +of user messages. User message numbers start with 20; messages 0 to 19 are +the built-in messages. For each user message there is a BYTE giving the +length of the message name, followed by the message name. (There seems to +be always one extra message name at the end which is blank and is unused. +The reason for this is currently unknown.) If the name is empty then that +user message does not exist (it may have been deleted). + +The standard messages are as follows: + 0 = INIT + 1 = CREATE + 2 = DESTROY + 3 = BEGIN_TURN + 4 = ARRIVED + 5 = DEPARTED + 6 = LASTIMAGE + 7 = MOVED + 8 = JUMPED + 9 = KEY + 10 = MOVING + 11 = SUNK + 12 = FLOATED + 13 = PLAYERMOVING + 14 = HIT + 15 = HITBY + 16 = DESTROYED + 17 = CREATED + 18 = POSTINIT + 19 = END_TURN + + +=== Classes === + +After the user message table comes the classes. This starts with a WORD +specifying the number of classes. Classes then follow. + +Each class consists of the following: + +* Name: A BYTE giving the length of the name, followed by the name. + +* ID number: A WORD giving the zero-based class ID number (class ID +numbers are sometimes one-based, but here they are zero-based). + +* Description: One or three bytes for length, followed by the plain text +(using CRLF for line endings). If length is less than 255 then it is one +byte. Otherwise, the first byte is 255 and then a WORD follows which gives +the length of the description. The description isn't null terminated. Any +escapes in the description are interpreted at runtime; they are not stored +with the control codes mentioned for special texts. + +* Attributes: See attribute table below. + +* Number of pictures (WORD) + +* List of pictures. Each is a WORD, and is a zero-based number of the +picture from the picture area; after this, all further picture numbers in +the file are zero-based indexing into this table. + +* Number of user variables (WORD) + +* User variable names; each is eight bytes long and is padded with nulls. +(Variable names are actually limited to seven characters.) + +* Number of subroutine labels (WORD) + +* Label names; each is ten bytes long, and is a eight-byte null terminated +string, with junk in the remaining bytes; the final two bytes are a word +address into the sobroutine or message section. + +* Subroutine program codes + +* Number of message labels (WORD) (these aren't the message names; these +are labels outside of the subroutine section). + +* Message label names; stored like the subroutine label names. + +* Message program codes + +Attribute Table + 0 BYTE ??? + 1 BYTE Flag + 2 WORD Misc4 + 4 WORD Misc5 + 6 WORD Misc6 + 8 WORD Misc7 + 10 WORD Shovable + 12 BYTE(5) Arrivals + 17 BYTE(5) Departures + 22 WORD Density + 24 WORD Volume + 26 WORD Strength + 28 WORD Weight + 30 WORD HardE + 32 WORD HardN + 34 WORD HardW + 36 WORD HardS + 38 WORD SharpE + 40 WORD SharpN + 42 WORD SharpW + 44 WORD SharpS + 46 WORD Height + 48 WORD Climb + 50 WORD Temperature + 52 WORD Shape + 54 WORD ??? + 56 WORD ??? + 58 WORD ??? + 60 WORD ??? + +The flag byte has bit0 set for "receives keys" and has bit7 set for "is +the player". Other flags (if any) are unknown, they appear to be unset. + +The arrivals and departures are stored each as five bytes, each byte for +one row, with the first byte corresponding to the top row. In each byte, +the rightmost column is the low bit; only the low 5-bits are used. (This +is not the same as the representation used in class codes!) + +The last four attributes seem to have something to do with the program +length, but it is unknown. However, it is not necessary to read these in +order to figure out the program length (see below for details). + + +=== Program === + +Each class has program codes; this section describes how the program codes +are encoded in the file. + +The first WORD of the subroutine section is the number of WORDs that make +up the the subroutine section, including the count itself. After that one +WORD header comes the instructions. + +Each message block in the message section then consists of the header and +then the instructions. The header of each message block consists of two +WORDs, the first being the message number, and the second being the length +(in WORDs) of this message block, including the header. + +The program is terminated by a header with both WORDs zero. + +Program instructions are executed using a stack-based virtual machine. +Each instruction is normally one WORD; see below section for the opcodes. + + +=== Program Opcodes === + +The first byte is opcode byte (the major code). The second byte is the +minor code, the meaning depending on the opcode. In the descriptions +below, if it says / and a number, it is how many taken from stack. If +there is * then it pushes to stack, ! means end block, - means no effect +on data stack. + +[1*] Local variables: 0=Class, 1=Temperature, 2=Shape, 4=Xloc, 5=Yloc, +6=LastDir, 7=CurImage, 8=Inertia, 9=Misc1, 10=Misc2, 11=Misc3, 12=Misc4, +13=Misc5, 14=Misc6, 15=Misc7, 16=Arrived, 17=Departed, 18=Arrivals, +19=Departures, 32=Busy, 33=Invisible, 34=UserSignal, 35=UserState, +36=KeyCleared, 37=IsPlayer, 38=Destroyed, 39=Stealthy, 40=VisualOnly, +48=Msg, 49=MsgFrom, 50=MsgArg1, 51=MsgArg2, 64=Density, 65=Volume, +66=Strength, 67=Weight, 68=Distance, 69=Height, 70=Climb, 72=HardE, +73=HardN, 74=HardW, 75=HardS, 76=SharpE, 77=SharpN, 78=SharpW, 79=SharpS, +80=ShapeE, 81=ShapeN, 82=ShapeW, 83=ShapeS, 84=Shovable + +[2/1*] Retrieve local variable from other object. Local variable numbers +are the same as opcode 1. + +[3/1] Assignment to standard local variables of current object. + +[4/2] Assignment to standard local variable of other object (the first +(top) value taken from the stack is the value, and second is the object). + +[5*] User-defined local variable; the second byte is zero-based local +variable number. + +[6/1] Assignment to user-defined variable; the second byte is the +zero-based local variable number to write to. + +[7*] Short decimal constant; the second byte is the 8-bit value. + +[8*] Long decimal constant. The next two words are the small-endian 32-bit +number that it represents. + +[9*] Short class constant; the second byte is a zero-based class number +(the class number is one-based at runtime). + +[11*] Message constant; the second byte is the message number. + +[13*] String literal. Follow by one WORD giving number of bytes of string +(including null terminator, always even), and then null-terminated special +text. There may be two null terminators if needed to make it even. + +[14*] Direction constant (0-7 for absolute, 8-15 for relative) + +[16/1*] Unary operator: 0=negative, 1=bitwise NOT, 2=logical NOT + +[17/2*] Binary operator: 0=multiply, 1=divide, 2=modulo + +[18/2*] Binary operator: 0=add, 1=subtract, 2=AND, 3=OR, 4=XOR + +[19/2*] Bit shift operator: 0=left, 1=right + +[20/2*] Comparison operator: 0=equal, 1=unequal, 2=less, 3=greater, 4=less +or equal, 5=greater or equal + +[21/2*] Logical operator: 0=AND, 1=OR, 2=XOR (not short-circuiting) + +[32] ObjDir + +[34] ObjAbove + +[36] ObjBelow + +[38/2*] 0=ObjTopAt, 1=ObjBottomAt, 2=VolumeAt, 3=HeightAt, 4=Delta + +[39*] Self + +[40/3*] ObjClassAt + +[47*] Array reference; next word is the array address. + +[48*] Key + +[49*] Animation constant: 0=STOP, 1=ONCE, 2=LOOP, 8=OSC + +[50*] Keyboard constant: See section about key codes. + +[51*] Short hexadecimal constant; second byte is the 8-bit value. + +[52*] Long hexadecimal constant. Next two words are the small-endian +32-bit number that it represents. + +[53*] Global variable: 0=Level, 1=LevelCount, 2=AltImage, 3=ExplainDeath, +4=GlobalBool, 5=PuzzleSetNumber, 6=MoveNumber + +[54] XDir + +[56] YDir + +[58] 0=NewX, 1=NewY + +[59*] Bit constant 0-31; the second byte is the number 0-31, and at +runtime it is replaced by the relevant 32-bit number. + +[60*] Sound constant (see section about user sounds) + +[61] 0=ClassCount/1* (I don't know what this does), 1=GetArray + +[64/3*] SendMessage (to Self) + +[65/4*] SendMessage + +[66/3*] Broadcast; second byte is zero-based class number + +[67/4*] Broadcast + +[68/1*] Move(Self, ...); minor code is 255 to take the value from the +stack, or is another number for a direction constant (in this latter case +the same bug occurs as with opcode 84). + +[69/2*] Move; minor code is same as opcode 68, but the bug with opcode 84 +does not occur, regardless of the minor code. + +[70/2*] JumpTo(Self, ...) + +[71/3*] JumpTo + +[72/5*] Create + +[74*] Destroy(Self) + +[75/1*] Destroy + +[80/3] SendMessage(Self, ...) + +[81/4] SendMessage + +[82/3] Broadcast; second byte is zero-based class number + +[83/4] Broadcast + +[84] Move(Self, dir). The second byte is the direction to move. Unlike +the normal Move() function, this one adds Strength to Inertia instead of +setting Inertia equal to Strength (this seems to be a bug). If the minor +code is 255 then takes direction from stack and does not have this bug. + +[85] Move. The second byte is the same as for opcode 84; if 255 then it +takes from the stack (before taking the object to move). In this case the +bug mentioned above does not occur regardless of what the minor code is. + +[88/5] Create + +[96] Comment. Has one WORD giving length of the comment text (including +the null terminator), and then the plain text of the comment (with CRLF +line endings), null terminated. + +[97] Popup setting. Second byte is 0 for PopupColor or 1 for PopupLoc. + +[98] Destroy(Self) + +[99/1] Destroy + +[100] CallSub + +[101-] Goto; next WORD is address to branch to unconditionally + +[102] Return. Second byte is 0 for implicit end of SUBS block, 1 for +return from a subroutine, 2 for the end of a message block. + +[103-] Return short constant. Second byte is return value. + +[104/1] Return + +[105/1] If. Second byte is zero for block-if or one for inline-if. The +next word is the number of words to skip (including the count itself) if +the condition is false. + +[106] Else + +[107/3] ImageSeq (seems to do nothing?) + +[108/3] ImageLoop (seems to do nothing?) + +[109] PopUp. Values taken from the stack is one plus the minor code. + +[110/2] JumpTo(Self, ...) + +[111/3] JumpTo + +[112] Sound + +[114] Array operations. Second byte: 0 = definition of an array, 1 = +SetArray, 2 = InitArray. + +[126] Animate + +[127] Link + +[128] GotoLevel + +[129-] 0=WinLevel, 1=LocateMe, 2=IgnoreKey, 3=PopupMisc, 4=UpdateScreen +(I don't know what UpdateScreen means?) + +[130] FlushClass + +[131] FlushObj + +[132] SetInventory + +[133] DelInventory + +[134] ForEachObjAt + +[240] Trace + + +=== Levels === + +After all classes are the levels. Before the levels comes a header of four +bytes that consists of: + +* Number of levels (WORD) + +* Two null bytes with unknown meaning + +Each level consists of: + +* Zero-based level number (WORD) + +* Level description length, including the null terminator (WORD) + +* Level description; a null-terminated special text + +* Border colours, outer to inner (BYTE(32)) + +* Background colour (BYTE) + +* Null byte (maybe the high byte of the background colour) + +* Number of objects (WORD) + +* Objects, in ordinary progressive television order; within each cell they +go bottom to top. See information about object records below. + +* Number of level strings (WORD) + +* Level strings; each one WORD length (the length includes the null +terminator), followed by the null-terminated special text. + +An object record is sixteen bytes long, and consists of: + 0 WORD Class (zero-based) + 2 WORD CurImage (zero-based) + 4 BYTE LastDir (zero-based; 0=east, 1=northeast, etc) + 5 BYTE Data types for Misc vars + 6 WORD X coordinate (one-based) + 8 WORD Y coordinate (one-based) + 10 WORD Misc1 + 12 WORD Misc2 + 14 WORD Misc3 + +The data types are as follows: + 0 = Number + 1 = Class (one-based) + 2 = Message + 3 = String +Data type for Misc1 is in bit1 and bit0, data type for Misc2 is in bit3 +and bit2, data type for Misc3 is in bit5 and bit4. Bit7 and bit6 are +always zero. + +The data type is used only in the editor and is irrelevant at run time. + + +=== Special Text === + +Level data may contain special text. This is text using the following +control codes (escapes have already been interpreted): + +1 = Black (\0) +2 = Blue (\1) +3 = Green (\2) +4 = Cyan (\3) +5 = Red (\4) +6 = Purple (\5) +7 = Yellow (\6) +8 = White (\7) +10 = Line break (\n) +11 = Left (\l) +12 = Center (\c) +14 = Picture (\i); followed by two WORDs: zero-based class and image +15 = Horizontal line (\b) +16 = Quiz button (\q); followed by one byte key code + + +=== Solutions === + +After the levels come the solutions. There is first one WORD which is the +number of solutions, and then the solutions. Each solution consists of: + +* Zero-based level number (WORD) + +* Number of keys in solution (WORD) + +* User name (BYTE(9)); the user name is null terminated, although there +may be junk after the null terminator. + +* Key codes; one byte each. + + +=== User Sounds === + +Finally, the user sounds come last. One WORD gives the number of user +sounds. And then each sound consists of: + +* Sound ID number (zero-based) (WORD) + +* Name length (BYTE) + +* Name (not terminated; length is given instead) + +* Data length (WORD) + +* Data (a RIFF WAVE file) + +The built-in sounds are as follows: + SPLASH + POUR + DOOR + GLASS + BANG + UNHH + UH_OH + FROG + THWIT + KLINKK + POWER + KLECK + CLICK + SMALL_POP + DINK + TICK + CHYEW + CHEEP + ANHH + BRRRT + BRRREEET + BWEEP + DRLRLRINK + FFFFTT + WAHOO + YEEHAW + OLDPHONE + RATTLE + BEEDEEP + THMP_thmp + BEDOINGNG + HEARTBEAT + LOCK + TAHTASHH + BOOOM + VACUUM + RATCHET2 + DYUPE + UNCORK + BOUNCE + JAYAYAYNG + DEEP_POP + RATCHET1 + GLISSANT + BUZZER + FAROUT + KEWEL + WHACK + STEAM + HAWK + + +=== Key codes === + + 8 BACK + 9 TAB + 12 CENTER (number pad 5 when numlock is off) + 13 ENTER + 16 SHIFT + 17 CTRL + 19 BREAK + 20 CAPSLOCK + 32 SPACE + 33 PGUP + 34 PGDN + 35 END + 36 HOME + 37 LEFT + 38 UP + 39 RIGHT + 40 DOWN + 48 0 (the top row zero) + 57 9 (the top row nine; 1-8 are in between 0 and 9) + 65 A + 90 Z (letters B-Y are in between A and Z) + 96 NUMPAD0 + 105 NUMPAD9 (number pad 1-8 are in between) + 106 MULTIPLY (the number pad "*" key) + 110 DECIMAL (the number pad "." key) + 111 DIVIDE (the number pad "/" key) + 120 F9 + 121 F10 + 122 F11 + 123 F12 + 144 NUMLOCK + 145 SCRLOCK + 186 SEMICOLON + 187 EQUALS + 188 COMMA + 189 MINUS + 190 PERIOD + 191 SLASH + 192 TILDE + 219 OBRACKET + 220 BACKSLASH + 221 CBRACKET + 222 QUOTE + +The number pad and main enter keys have the same key code. When num lock +is off, the number pad keys have the same codes as the other arrow keys. +The NUMPAD codes are only when numlock is on.