ADDED LICENSE Index: LICENSE ================================================================== --- LICENSE +++ LICENSE @@ -0,0 +1,4 @@ +Free Hero Mesh is released to public domain. + +Puzzle sets may have their own copyright which might or might not differ +from that of the Free Hero Mesh program itself. ADDED colorpalette Index: colorpalette ================================================================== --- colorpalette +++ colorpalette @@ -0,0 +1,21 @@ +C020FF +000000 222222 333333 444444 555555 666666 777777 888888 999999 AAAAAA BBBBBB CCCCCC DDDDDD EEEEEE FFFFFF +281400 412300 5F3200 842100 A05000 C35F14 E1731E FF8232 FF9141 FFA050 FFAF5F FFBE73 FFD282 FFE191 FFF0A0 +321E1E 412220 5F2830 823040 A03A4C BE4658 E15464 FF6670 FF7F7B FF8E7F FF9F7F FFAF7F FFBF7F FFCF7F FFDF7F +280D0D 401515 602020 802A2A A03535 C04040 E04A4A FF5555 FF6764 FF6F64 FF7584 FF849D FF94B7 FF9FD1 FFAEEA +901400 A02000 B03000 C04000 D05000 E06000 F07000 FF8000 FF9000 FFA000 FFB000 FFC000 FFD000 FFE000 FFF000 +280000 400000 600000 800000 A00000 C00000 E00000 FF0000 FF2828 FF4040 FF6060 FF8080 FFA0A0 FFC0C0 FFE0E0 +280028 400040 600060 800080 A000A0 C000C0 E000E0 FF00FF FF28FF FF40FF FF60FF FF80FF FFA0FF FFC0FF FFE0FF +281428 402040 603060 804080 A050A0 C060C0 E070E0 FF7CFF FF8CFF FF9CFF FFACFF FFBCFF FFCCFF FFDCFF FFECFF +280050 350566 420A7C 4F0F92 5C14A8 6919BE 761ED4 8323EA 9028FF A040FF B060FF C080FF D0A0FF E0C0FF F0E0FF +000028 000040 000060 000080 0000A0 0000C0 0000E0 0000FF 0A28FF 284AFF 466AFF 678AFF 87AAFF A7CAFF C7EBFF +0F1E1E 142323 193232 1E4141 285050 325F5F 377373 418282 469191 50A0A0 5AAFAF 5FC3C3 69D2D2 73E1E1 78F0F0 +002828 004040 006060 008080 00A0A0 00C0C0 00E0E0 00FFFF 28FFFF 40FFFF 60FFFF 80FFFF A0FFFF C0FFFF E0FFFF +002800 004000 006000 008000 00A000 00C000 00E000 00FF00 28FF28 40FF40 60FF60 80FF80 A0FFA0 C0FFC0 E0FFE0 +002110 234123 325F32 418241 50A050 5FC35F 73E173 85FF7A 91FF6E A0FF5F B4FF50 C3FF41 D2FF32 E1FF23 F0FF0F +282800 404000 606000 808000 A0A000 C0C000 E0E000 FFFF00 FFFF28 FFFF40 FFFF60 FFFF80 FFFFA0 FFFFC0 FFFFE0 + +442100 00FF55 0055FF FF5500 55FF00 FF0055 5500FF CA8B25 F078F0 F0F078 FF7F00 DD6D01 7AFF00 111111 + +000000 0000AA 00AA00 00AAAA AA0000 AA00AA AAAA00 AAAAAA +555555 5555FF 55FF55 55FFFF FF5555 FF55FF FFFF55 FFFFFF ADDED fileform1.txt Index: fileform1.txt ================================================================== --- fileform1.txt +++ fileform1.txt @@ -0,0 +1,589 @@ +** 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*] 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. + +[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). + +[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] 0=ObjTopAt, 1=ObjBottomAt, 2=VolumeAt, 3=HeightAt, 4=Delta + +[39*] Self + +[40] ObjClassAt + +[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 + +[68/1*] Move(Self, ...); minor code is 255 + +[69/2*] Move; minor code is 255 + +[80] SendMessage + +[82] 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). + +[85/2] Move; minor code is 255 + +[88] 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 + +[100] CallSub + +[101] Goto + +[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 + +[110] 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: + +* Number of levels (WORD) + +* Two null bytes (meaing unknown) + +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 mbtofhm.c Index: mbtofhm.c ================================================================== --- mbtofhm.c +++ mbtofhm.c @@ -0,0 +1,716 @@ +#if 0 +gcc -s -O2 -o ./mbtofhm -Wno-unused-result mbtofhm.c +exit +#endif + +// This program is part of Free Hero Mesh and is public domain. + +#include +#include +#include +#include "names.h" + +#define fatal(...) do{ fprintf(stderr,__VA_ARGS__); exit(1); }while(0) + +// Pictures +static unsigned char*pict; +static unsigned short*picalloc; +static int npic; +static unsigned short sizes[8]; +static unsigned char*inpic; +static unsigned char*outpic; +static unsigned char*inpicstr; +static unsigned char*inpicabovestr; +static int outpiclen; +static char picavail[512]; + +// Classes +typedef struct { + char*name; + char*desc; + unsigned char attr[62]; + short npic; + unsigned short pic[128]; + short nvars; + unsigned char*vars; + short nsubslbl; + unsigned char*subslbl; + unsigned char*subscode; + short nmsgslbl; + unsigned char*msgslbl; + unsigned short nmsgs; + unsigned char**msgscode; +} ClassInfo; +static int nusermsg; +static char**usermsg; +static ClassInfo*class[512]; + +// Levels +static int nlevels; +static unsigned short*levelid; + +// Sounds +static int nsounds; +static char**sounds; +static unsigned short*soundid; + +// Common +static char*basename; +static char*nam; +static unsigned long haroffs; + +static void*alloc(unsigned int s) { + void*o=malloc(s); + if(s && !o) fatal("Allocation failed\n"); + return o; +} +static void*my_realloc(void*p,unsigned int s) { + void*o=realloc(p,s); + if(s && !o) fatal("Reallocation failed\n"); + return o; +} +#define Allocate(x,y) (x=alloc((y)*sizeof(*(x)))) +#define Reallocate(x,y) (x=my_realloc((x),(y)*sizeof(*(x)))) + +static void hamarc_begin(FILE*fp) { + fwrite(nam,1,strlen(nam)+1,fp); + fwrite("\0\0\0",4,1,fp); + haroffs=ftell(fp); +} + +static void hamarc_end(FILE*fp) { + unsigned long end=ftell(fp); + fseek(fp,haroffs-4,SEEK_SET); + fputc((end-haroffs)>>16,fp); + fputc((end-haroffs)>>24,fp); + fputc((end-haroffs)>>0,fp); + fputc((end-haroffs)>>8,fp); + fseek(fp,end,SEEK_SET); +} + +static inline void read_header(void) { + unsigned char buf[30]; + int i,s; + fread(buf,1,30,stdin); + if(memcmp("MESH",buf+2,4)) fatal("Unrecognized file format\n"); + for(i=0;i7225?7225:am; + outpic[outpiclen++]=ch+n/85-1; + if(le>0) memcpy(outpic+outpiclen,inpicstr+st,le0) outpiclen+=le=homo && (ca>=hetero || homo>1)) { + // Output a copy-above run + out_run(170,ca,0,0); + i+=ca; + } else if(homo>1) { + // Output a homogeneous run + out_run(0,homo-1,i,1); + i+=homo; + } else { + // Output a heterogeneous run + if(hetero>85) hetero=85; + out_run(85,hetero,i,hetero); + i+=hetero; + } + if(outpiclen>=ms) return ms+1; + } + return outpiclen; +} + +static inline void do_pictures(void) { + FILE*fp; + int i,j,k,s,t,x,y,z; + char meth[8]; + int siz[8]; + sprintf(nam,"%s.xclass",basename); + fp=fopen(nam,"w"); + if(!fp) fatal("Cannot open file '%s' for writing\n",nam); + for(i=0;i>1); + fputc(t>>16,fp); fputc(t>>24,fp); fputc(t,fp); fputc(t>>8,fp); + fputc((*meth<<4)|z,fp); + for(z=0;z<8;z++) { + if(!sizes[z]) break; + fputc(sizes[z],fp); + } + for(z=1;z<8;z+=2) { + if(!sizes[z]) break; + fputc(meth[z]|(z==7?0xF0:meth[z+1]<<4),fp); + } + for(z=k=0;z<8;z++) { + x=sizes[z]; + if(!x) break; + for(y=0;y=512) fatal("Class number %d out of range\n",id); + if(class[id]) fatal("Duplicate class number %d\n",id); + Allocate(class[id],1); + class[id]->name=name; + i=fgetc(stdin); + if(i==255) { + i=fgetc(stdin); + i|=fgetc(stdin)<<8; + } + fread(Allocate(class[id]->desc,i+1),1,i,stdin); + class[id]->desc[i]=0; + fread(class[id]->attr,1,62,stdin); + j=fgetc(stdin); + j|=fgetc(stdin)<<8; + class[id]->npic=j; + for(i=0;ipic[i]=k; + } + if(j>128) class[id]->npic=128; + j=fgetc(stdin); + j|=fgetc(stdin)<<8; + class[id]->nvars=j; + fread(Allocate(class[id]->vars,j<<3),j,8,stdin); + j=fgetc(stdin); + j|=fgetc(stdin)<<8; + class[id]->nsubslbl=j; + fread(Allocate(class[id]->subslbl,10*j),j,10,stdin); + fread(buf,1,2,stdin); + k=buf[0]|(buf[1]<<8); + Allocate(class[id]->subscode,k<<1); + class[id]->subscode[0]=i; + class[id]->subscode[1]=j; + fread(class[id]->subscode+2,2,k-1,stdin); + j=fgetc(stdin); + j|=fgetc(stdin)<<8; + class[id]->nmsgslbl=j; + fread(Allocate(class[id]->msgslbl,10*j),j,10,stdin); + class[id]->nmsgs=0; + class[id]->msgscode=0; + for(;;) { + fread(buf,1,4,stdin); + k=buf[2]|(buf[3]<<8); + if(!k) break; + i=class[id]->nmsgs++; + Allocate(Reallocate(class[id]->msgscode,class[id]->nmsgs)[i],k<<1); + memcpy(class[id]->msgscode[i],buf,4); + fread(class[id]->msgscode[i]+4,k-2,2,stdin); + } +} + +static inline void do_classes(void) { + int n; + n=fgetc(stdin); + n|=fgetc(stdin)<<8; + while(n--) read_one_class(); +} + +static inline void out_classes(void) { + FILE*fp; + ClassInfo*c; + int i,j,n; + sprintf(nam,"%s.class",basename); + fp=fopen(nam,"w"); + if(!fp) fatal("Cannot open file '%s' for writing\n",nam); + fprintf(fp,"; Note: This file was automatically converted.\n; Remove this notice if you have corrected it.\n"); + for(n=0;n<512;n++) if(c=class[n]) { + fprintf(fp,"\n($%s\n Compatible",c->name); + if(c->attr[1]&1) fprintf(fp," Input"); + if(c->attr[1]&128) fprintf(fp," Player"); + if(!strcmp(c->name,"Quiz")) fprintf(fp," Quiz"); + fprintf(fp,"\n (Image"); + for(j=i=0;inpic;i++) { + fprintf(fp," \"%d\"",c->pic[i]); + if(picavail[c->pic[i]]) j++; + } + fprintf(fp,")\n"); + if(!j) { + fprintf(fp," (DefaultImage ())\n"); + } else if(j!=c->npic) { + fprintf(fp," (DefaultImage"); + for(i=0;inpic;i++) if(picavail[c->pic[i]]) fprintf(fp," %d",i); + fprintf(fp,")\n"); + } + //TODO: Description + if(c->attr[2] || c->attr[3]) fprintf(fp," (Misc4 0x%04X)\n",c->attr[2]|(c->attr[3]<<8)); + if(c->attr[4] || c->attr[5]) fprintf(fp," (Misc5 0x%04X)\n",c->attr[4]|(c->attr[5]<<8)); + if(c->attr[6] || c->attr[7]) fprintf(fp," (Misc6 0x%04X)\n",c->attr[6]|(c->attr[7]<<8)); + if(c->attr[8] || c->attr[9]) fprintf(fp," (Misc7 0x%04X)\n",c->attr[8]|(c->attr[9]<<8)); + if(c->attr[50] || c->attr[51]) fprintf(fp," (Temperature %d)\n",c->attr[50]|(c->attr[51]<<8)); + if(c->attr[10]) { + if(c->attr[10]==0x55) { + fprintf(fp," Shovable\n"); + } else { + fprintf(fp," (Shovable"); + if(c->attr[10]&0xAA) { + // I don't know why, but sometimes this happens. + fprintf(fp," 0x%02X",c->attr[10]); + } else { + if(c->attr[10]&0x01) fprintf(fp," E"); + if(c->attr[10]&0x04) fprintf(fp," N"); + if(c->attr[10]&0x10) fprintf(fp," W"); + if(c->attr[10]&0x40) fprintf(fp," S"); + } + fprintf(fp,")\n"); + } + } + if(!c->attr[12] && !c->attr[13] && !c->attr[15] && !c->attr[16] && !(c->attr[14]&~4)) { + if(c->attr[14]) fprintf(fp," (Arrivals InPlace)\n"); + } else { + fprintf(fp," (Arrivals\n"); + for(i=0;i<5;i++) { + fprintf(fp," "); + for(j=0;j<5;j++) fprintf(fp," %c",(c->attr[i+12]<attr[17] && !c->attr[18] && !c->attr[20] && !c->attr[21] && !(c->attr[19]&~4)) { + if(c->attr[19]) fprintf(fp," (Departures InPlace)\n"); + } else { + fprintf(fp," (Departures\n"); + for(i=0;i<5;i++) { + fprintf(fp," "); + for(j=0;j<5;j++) fprintf(fp," %c",(c->attr[i+17]<attr[22] || c->attr[23]) fprintf(fp," (Density %d)\n",c->attr[22]|(c->attr[23]<<8)); + if(c->attr[24] || c->attr[25]) fprintf(fp," (Volume %d)\n",c->attr[24]|(c->attr[25]<<8)); + if(c->attr[26] || c->attr[27]) fprintf(fp," (Strength %d)\n",c->attr[26]|(c->attr[27]<<8)); + if(c->attr[28] || c->attr[29]) fprintf(fp," (Weight %d)\n",c->attr[28]|(c->attr[29]<<8)); + if(c->attr[46] || c->attr[47]) fprintf(fp," (Height %d)\n",c->attr[46]|(c->attr[47]<<8)); + if(c->attr[48] || c->attr[49]) fprintf(fp," (Climb %d)\n",c->attr[48]|(c->attr[49]<<8)); + //TODO: Shape + if(memcmp(c->attr+30,"\0\0\0\0\0\0\0\0",8)) { + if(memcmp(c->attr+30,c->attr+32,2) || memcmp(c->attr+30,c->attr+34,4)) { + fprintf(fp," (Hard"); + if(c->attr[30] || c->attr[31]) fprintf(fp," (E %d)",c->attr[30]|(c->attr[31]<<8)); + if(c->attr[32] || c->attr[33]) fprintf(fp," (N %d)",c->attr[32]|(c->attr[33]<<8)); + if(c->attr[34] || c->attr[35]) fprintf(fp," (W %d)",c->attr[34]|(c->attr[35]<<8)); + if(c->attr[36] || c->attr[37]) fprintf(fp," (S %d)",c->attr[36]|(c->attr[37]<<8)); + fprintf(fp,")\n"); + } else { + fprintf(fp," (Hard %d)\n",c->attr[30]|(c->attr[31]<<8)); + } + } + if(memcmp(c->attr+38,"\0\0\0\0\0\0\0\0",8)) { + if(memcmp(c->attr+38,c->attr+40,2) || memcmp(c->attr+38,c->attr+42,4)) { + fprintf(fp," (Sharp"); + if(c->attr[38] || c->attr[39]) fprintf(fp," (E %d)",c->attr[38]|(c->attr[39]<<8)); + if(c->attr[40] || c->attr[41]) fprintf(fp," (N %d)",c->attr[40]|(c->attr[41]<<8)); + if(c->attr[42] || c->attr[43]) fprintf(fp," (W %d)",c->attr[42]|(c->attr[43]<<8)); + if(c->attr[44] || c->attr[45]) fprintf(fp," (S %d)",c->attr[44]|(c->attr[45]<<8)); + fprintf(fp,")\n"); + } else { + fprintf(fp," (Sharp %d)\n",c->attr[38]|(c->attr[39]<<8)); + } + } + //TODO: Class codes + if(c->subscode[1] || c->subscode[0]>2) { + fprintf(fp," (SUBS\n ; Not implemented yet!\n )\n"); + } + for(i=0;inmsgs;i++) { + fprintf(fp," ("); + j=c->msgscode[i][0]|(c->msgscode[i][1]<<8); + if(j<20) fprintf(fp,"%s",standard_message_names[j]); + else if(j-20>8,fp); // Level code + fputc(29,fp); fputc(21,fp); // Width/height + i=fgetc(stdin); + i|=fgetc(stdin)<<8; + // fputc(17,fp); // Select proportional font + while(i--) { + switch(j=fgetc(stdin)) { + case 14: + i-=4; + j=fgetc(stdin); + j|=fgetc(stdin)<<8; + if(j>=512 || !class[j]) { + fgetc(stdin); fgetc(stdin); + break; + } + fputc(14,fp); + fputs(class[j]->name,fp); + j=fgetc(stdin); + j|=fgetc(stdin)<<8; + fprintf(fp,":%d\\",j); + break; + default: fputc(j,fp); + } + } + fread(mru,1,32,stdin); // Skip border colours + fread(buf,1,2,stdin); // Skip border colours + mru[0x04]=mru[0x14]=255; + n=fgetc(stdin); + n|=fgetc(stdin)<<8; + x=q=0; + y=1; + // Free Hero Mesh level format for objects: + // * bit flags (or 0xFF for end): + // bit7 = MRU (omit everything but position) + // bit6 = Next position + // bit5 = New X position + // bit4 = New Y position + // bit3 = Has MiscVars + // bit2-bit0 = LastDir (should be RLE in case of MRU?) + // * new X if applicable + // * new Y if applicable + // * class (one-based; add 0x8000 for image 0) (two bytes) + // * image (one byte) + // * data types (if has MiscVars): + // bit7-bit6 = How many (0=has Misc2 and Misc3, not Misc1) + // bit5-bit4 = Misc3 type + // bit3-bit2 = Misc2 type + // bit1-bit0 = Misc1 type + // * misc data (variable size) + while(n--) { + fread(buf,1,16,stdin); + ++*buf; + if(!*buf) ++buf[1]; + mru[0x06]=mru[0x16]=buf[6]; + mru[0x08]=mru[0x18]=buf[8]; + i=0; + if(buf[6]==x+1) i|=0x40; else if(buf[6]!=x) i|=0x20; + if(buf[8]!=y) i|=0x10; + if(x==buf[6] && y==buf[8]) q+=16; else q=0; + if(q<32 && !memcmp(mru+q,buf,16)) { + i|=0x80; + } else { + i|=buf[4]&7; + if(buf[5] || memcmp(buf+10,"\0\0\0\0\0\0",6)) i|=0x08; + if(q<32) memcpy(mru+q,buf,16); + } + fputc(i,fp); + x=buf[6]; + y=buf[8]; + if(i&0x20) fputc(x,fp); + if(i&0x10) fputc(y,fp); + if(i<0x80) { + if(buf[2]) { + fwrite(buf,1,3,fp); + } else { + fputc(*buf,fp); + fputc(buf[1]|0x80,fp); + } + } + if(i&0x08) { + i=buf[5]&0x3F; + if(buf[14] || buf[15]) i|=0xC0; + else if(buf[12] || buf[13]) i|=0x80; + else i|=0x40; + if(i>=0xC0 && !buf[10] && !buf[11]) i&=0x3F; + if(i&0xC0) { + if((i&0x03)==0x02 && (buf[11] || buf[10]>20)) { + j=buf[10]|(buf[11]<<8); + fputc(j+236,fp); fputc((j+236)>>8,fp); + } else { + fwrite(buf+10,1,2,fp); + } + } + if((i&0xC0)!=0x40) { + if((i&0x0C)==0x08 && (buf[13] || buf[12]>20)) { + j=buf[12]|(buf[13]<<8); + fputc(j+236,fp); fputc((j+236)>>8,fp); + } else { + fwrite(buf+12,1,2,fp); + } + } + if(!(0xC0&~i) || !(i&0xC0)) { + if((i&0x03)==0x02 && (buf[15] || buf[14]>20)) { + j=buf[14]|(buf[15]<<8); + fputc(j+236,fp); fputc((j+236)>>8,fp); + } else { + fwrite(buf+14,1,2,fp); + } + } + } + } + fputc(255,fp); // End of objects + n=fgetc(stdin); + n|=fgetc(stdin)<<8; + while(n--) { + i=fgetc(stdin); + i|=fgetc(stdin)<<8; + // fputc(17,fp); // Select proportional font + while(i--) { + switch(j=fgetc(stdin)) { + case 14: + i-=4; + j=fgetc(stdin); + j|=fgetc(stdin)<<8; + if(j>=512 || !class[j]) { + fgetc(stdin); fgetc(stdin); + break; + } + fputc(14,fp); + fputs(class[j]->name,fp); + j=fgetc(stdin); + j|=fgetc(stdin)<<8; + fprintf(fp,":%d\\",j); + break; + default: fputc(j,fp); + } + } + } + hamarc_end(fp); +} + +static inline void do_levels(void) { + int i,n; + FILE*fp; + sprintf(nam,"%s.level",basename); + n=fgetc(stdin); + n|=fgetc(stdin)<<8; + fgetc(stdin); fgetc(stdin); // Unknown meaning + if(!n) return; + nlevels=n; + Allocate(levelid,nlevels); + fp=fopen(nam,"w"); + if(!fp) fatal("Cannot open file '%s' for writing\n",nam); + strcpy(nam,"CLASS.DEF"); + hamarc_begin(fp); + for(i=0;i<512;i++) if(class[i]) { + fputc(i+1,fp); fputc((i+1)>>8,fp); + fwrite(class[i]->name,1,strlen(class[i]->name)+1,fp); + } + fputc(0,fp); fputc(0,fp); + for(i=0;i>8,fp); + fwrite(usermsg[i],1,strlen(usermsg[i])+1,fp); + } + hamarc_end(fp); + for(i=0;i>8,fp); + } + hamarc_end(fp); + fclose(fp); +} + +static void one_solution(FILE*fp) { + char buf[9]; + int i,n; + i=fgetc(stdin); + i|=fgetc(stdin)<<8; + n=fgetc(stdin); + n|=fgetc(stdin)<<8; + sprintf(nam,"%d.SOL",i); + hamarc_begin(fp); + fputc(0,fp); fputc(0,fp); // Level version + fread(buf,1,9,stdin); + if(*buf) { + fputc(1,fp); // bit0=has user name, bit1=has timestamp + for(i=0;i<9;i++) { + fputc(buf[i],fp); + if(!buf[i]) break; + } + } else { + fputc(0,fp); // has neither user name nor timestamp + } + while(n--) fputc(fgetc(stdin),fp); + hamarc_end(fp); +} + +static inline void do_solutions(void) { + int n; + FILE*fp; + sprintf(nam,"%s.solution",basename); + n=fgetc(stdin); + n|=fgetc(stdin)<<8; + if(!n) return; + fp=fopen(nam,"w"); + if(!fp) fatal("Cannot open file '%s' for writing\n",nam); + while(n--) one_solution(fp); + fclose(fp); +} + +static inline void do_user_sounds(void) { + int i,j,n; + FILE*fp; + n=fgetc(stdin); + n|=fgetc(stdin)<<8; + if(!n) return; + nsounds=n; + sprintf(nam,"%s.xclass",basename); + fp=fopen(nam,"r+"); + if(!fp) fatal("Cannot open file '%s' for reading/writing\n",nam); + fseek(fp,0,SEEK_END); + Allocate(sounds,n); + Allocate(soundid,n); + while(n--) { + i=fgetc(stdin); + i|=fgetc(stdin)<<8; + j=fgetc(stdin); + fread(nam,1,j,stdin); + nam[j]=0; + sounds[n]=strdup(nam); + if(!sounds[n]) fatal("String duplication failed\n"); + strcpy(nam+j,".WAV"); + soundid[n]=i; + hamarc_begin(fp); + i=fgetc(stdin); + i|=fgetc(stdin)<<8; + while(i--) fputc(fgetc(stdin),fp); + hamarc_end(fp); + } + fclose(fp); +} + +int main(int argc,char**argv) { + if(argc!=2) fatal("usage: %s basename < inputfile\n",argv[0]); + basename=argv[1]; + Allocate(nam,strlen(basename)+256); + read_header(); + do_pictures(); + fread(picavail,1,512,stdin); + do_user_messages(); + do_classes(); + do_levels(); + if(nlevels) do_solutions(); + do_user_sounds(); + out_classes(); + return 0; +} ADDED names.h Index: names.h ================================================================== --- names.h +++ names.h @@ -0,0 +1,147 @@ +// Auto-generated! Do not modify directly! +#define MSG_INIT 0 +#define MSG_CREATE 1 +#define MSG_DESTROY 2 +#define MSG_BEGIN_TURN 3 +#define MSG_ARRIVED 4 +#define MSG_DEPARTED 5 +#define MSG_LASTIMAGE 6 +#define MSG_MOVED 7 +#define MSG_JUMPED 8 +#define MSG_KEY 9 +#define MSG_MOVING 10 +#define MSG_SUNK 11 +#define MSG_FLOATED 12 +#define MSG_PLAYERMOVING 13 +#define MSG_HIT 14 +#define MSG_HITBY 15 +#define MSG_DESTROYED 16 +#define MSG_CREATED 17 +#define MSG_POSTINIT 18 +#define MSG_END_TURN 19 +#define MSG_CLEANUP 20 +static const char*const standard_message_names[]={ + "INIT", + "CREATE", + "DESTROY", + "BEGIN_TURN", + "ARRIVED", + "DEPARTED", + "LASTIMAGE", + "MOVED", + "JUMPED", + "KEY", + "MOVING", + "SUNK", + "FLOATED", + "PLAYERMOVING", + "HIT", + "HITBY", + "DESTROYED", + "CREATED", + "POSTINIT", + "END_TURN", + "CLEANUP", +}; +#define SND_SPLASH 0 +#define SND_POUR 1 +#define SND_DOOR 2 +#define SND_GLASS 3 +#define SND_BANG 4 +#define SND_UNHH 5 +#define SND_UH_OH 6 +#define SND_FROG 7 +#define SND_THWIT 8 +#define SND_KLINKK 9 +#define SND_POWER 10 +#define SND_KLECK 11 +#define SND_CLICK 12 +#define SND_SMALL_POP 13 +#define SND_DINK 14 +#define SND_TICK 15 +#define SND_CHYEW 16 +#define SND_CHEEP 17 +#define SND_ANHH 18 +#define SND_BRRRT 19 +#define SND_BRRREEET 20 +#define SND_BWEEP 21 +#define SND_DRLRLRINK 22 +#define SND_FFFFTT 23 +#define SND_WAHOO 24 +#define SND_YEEHAW 25 +#define SND_OLDPHONE 26 +#define SND_RATTLE 27 +#define SND_BEEDEEP 28 +#define SND_THMP_thmp 29 +#define SND_BEDOINGNG 30 +#define SND_HEARTBEAT 31 +#define SND_LOCK 32 +#define SND_TAHTASHH 33 +#define SND_BOOOM 34 +#define SND_VACUUM 35 +#define SND_RATCHET2 36 +#define SND_DYUPE 37 +#define SND_UNCORK 38 +#define SND_BOUNCE 39 +#define SND_JAYAYAYNG 40 +#define SND_DEEP_POP 41 +#define SND_RATCHET1 42 +#define SND_GLISSANT 43 +#define SND_BUZZER 44 +#define SND_FAROUT 45 +#define SND_KEWEL 46 +#define SND_WHACK 47 +#define SND_STEAM 48 +#define SND_HAWK 49 +static const char*const standard_sound_names[]={ + "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", +}; ADDED names.js Index: names.js ================================================================== --- names.js +++ names.js @@ -0,0 +1,90 @@ +// Program to generate names.h file. +// Public domain. +"use strict"; +const standard_message_names=` + // Original + 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 + // New + 20 = CLEANUP +`.split("\n").map(x=>/^ *([0-9]+) = ([^ ]*) *$/.exec(x)).filter(x=>x); +const standard_sound_names=[]; +` + 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 +`.replace(/[A-Za-z_0-9]+/g,x=>standard_sound_names.push(x)); +console.log("// Auto-generated! Do not modify directly!"); +standard_message_names.forEach(([a,b,c])=>console.log("#define MSG_"+c+" "+b)); +console.log("static const char*const standard_message_names[]={"); +standard_message_names.forEach(([a,b,c])=>console.log(" \""+c+"\",")); +console.log("};"); +standard_sound_names.forEach((x,y)=>console.log("#define SND_"+x+" "+y)); +console.log("static const char*const standard_sound_names[]={"); +standard_sound_names.forEach(x=>console.log(" \""+x+"\",")); +console.log("};"); ADDED notes Index: notes ================================================================== --- notes +++ notes @@ -0,0 +1,34 @@ +Some notes + + +=== File formats === + +Each puzzle set in Free Hero Mesh is stored as four files: +*.xclass (Hamster archive) - Pictures and user sounds +*.class (Text) - Class definitions (attributes and codes) +*.level (Hamster archive) - Levels +*.solution (Hamster archive) - Solutions to levels + +An idea is to use either Hamster archive or SQLite for the .level and +.solution files. I decided to use Hamster archive, but here are some of +the advantages and disadvantages of each approach: + +Advantages of using Hamster archive: +* It is a simple file format. +* You can use the "har" program to extract and alter lumps. +* There will be less bugs because it is simpler than SQLite. + +Disadvantages of using Hamster archive: +* It is necessary to rewrite the entire file when it changes. + +Advantages of using SQLite: +* You can read parts of the file at a time. +* You can alter records without having to rewrite everything. +* SQL can now be used for user queries and user scripts. +* You can use sqlite3 command shell to deal with the file. + +Disadvantages of using SQLite: +* There will be extra overhead due to b-trees and other stuff. +* It may be necessary to deal with untrusted database schemas (unsure). +* Now SQLite must be included in this program. + ADDED readpicture.c Index: readpicture.c ================================================================== --- readpicture.c +++ readpicture.c @@ -0,0 +1,110 @@ +#if 0 +gcc -s -O2 -o ./readpicture -Wno-unused-result readpicture.c +exit +#endif + +// This program is part of Free Hero Mesh and is public domain. + +#include +#include +#include + +typedef struct { + unsigned char x[8]; +} Color; + +static unsigned char buf[32]; +static int size,num,meth; +static Color pal[256]; +static unsigned char*pic; + +static inline void load_palette(void) { + int i; + FILE*fp=fopen("colorpalette","r"); + if(!fp) exit(1); + for(i=0;i<256;i++) { + fscanf(fp,"%2hhx%2hhx%2hhx",pal[i].x+0,pal[i].x+2,pal[i].x+4); + pal[i].x[1]=pal[i].x[0]; + pal[i].x[3]=pal[i].x[2]; + pal[i].x[5]=pal[i].x[4]; + pal[i].x[7]=pal[i].x[6]=i?255:0; + } + fclose(fp); +} + +static void load_picture(int id) { + int c,n,t,x,y; + unsigned char*p; + meth=(id?buf[(*buf&15)+1+((id-1)>>1)]>>(id&1?0:4):*buf>>4)&15; + size=buf[id+1]; + pic=realloc(pic,size*size); + if(!pic) { + fprintf(stderr,"Allocation failed\n"); + exit(1); + } + if(meth==15) { + fread(pic,size,size,stdin); + return; + } + p=pic; + n=t=0; + y=size*size; + while(y--) { + if(!n) { + n=fgetc(stdin); + if(n<85) { + // Homogeneous run + n++; + x=fgetc(stdin); + if(t==1 && x==c) n*=85; else n++; + c=x; + t=1; + } else if(n<170) { + // Heterogeneous run + n-=84; + t=2; + } else { + // Copy-above run + n-=169; + if(t==3) n*=85; + t=3; + } + } + n--; + if(t==2) c=fgetc(stdin); + if(t==3) c=p-pic>=size?p[-size]:0; + *p++=c; + } +} + +static inline void out_picture(void) { + int x,y; + //fprintf(stderr,"%d\n",meth); + if(meth==5 || meth==6) meth^=3; + for(y=0;y>1),stdin); + for(i=0;i<=num;i++) load_picture(i); + fwrite("farbfeld",1,8,stdout); + putchar(0); putchar(0); + putchar(size>>8); putchar(size); + putchar(0); putchar(0); + putchar(size>>8); putchar(size); + out_picture(); + return 0; +} +