Free Hero Mesh

Check-in [b152314d28]
Login
This is a mirror of the main repository for Free Hero Mesh. New tickets and changes will not be accepted at this mirror.
Overview
Comment:Initial commit with files included.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: b152314d288df3e91af70a58b36ae7764a4c86fc
User & Date: user on 2018-03-05 09:32:23
Other Links: manifest | tags
Context
2018-03-08
05:50
A change in file format, and notes about a hybrid approach check-in: a4f844ac56 user: user tags: trunk
2018-03-05
09:32
Initial commit with files included. check-in: b152314d28 user: user tags: trunk
09:03
initial empty check-in check-in: 1f3a1f5ef3 user: user tags: trunk
Changes

Added LICENSE version [71ed6913b8].





1
2
3
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 version [96e46c5fe1].






















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 version [a949aa6944].














































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
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<nsizes;s++) {
    for(i=0;i<nalloc;i++) {
      for(y=0;y<sizes[s];y++) {
        for(j=0;j<16;j++) {
          @< Read (sizes[s]) bytes from file into
             scanline y of size s of picture 16*i+j
             (one byte per pixel; zero = transparent) @>
        }
      }
    }
  }
  // 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 version [2264eccb2d].













































































































































































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;i<buf[10];i++) sizes[i]=buf[i+i+12]|(buf[i+i+13]<<8);
  npic=buf[28]|(buf[29]<<8);
  Allocate(picalloc,npic);
  for(i=0;i<npic;i++) {
    fread(buf,1,2,stdin);
    picalloc[i]=buf[0]|(buf[1]<<8);
  }
  for(i=s=0;i<8;i++) s+=sizes[i]*sizes[i];
  Allocate(pict,i=s*npic*16);
  fread(pict,1,i,stdin);
  for(i=s=0;i<8;i++) if(s<sizes[i]) s=sizes[i];
  s*=s;
  Allocate(inpic,s);
  Allocate(outpic,s+256);
  Allocate(inpicstr,s+4);
  Allocate(inpicabovestr,s+4);
}

static void out_run(int ch,int am,int st,int le) {
  int n=am%85?:85;
  outpic[outpiclen++]=ch+n-1;
  if(le) memcpy(outpic+outpiclen,inpicstr+st,le<n?le:n);
  if(le) outpiclen+=le<n?le:n;
  if(ch) st+=n,le-=n;
  while(am-=n) {
    n=am>7225?7225:am;
    outpic[outpiclen++]=ch+n/85-1;
    if(le>0) memcpy(outpic+outpiclen,inpicstr+st,le<n?le:n);
    if(le>0) outpiclen+=le<n?le:n;
    if(ch) st+=n,le-=n;
  }
}

static int one_picture(int ps,int me) {
  int ms=ps*ps;
  int i=0;
  int w,x,y,z;
  int ca,homo,hetero;
  for(y=0;y<ps;y++) for(x=0;x<ps;x++) {
    if(me&1) x=ps-x-1;
    if(me&2) y=ps-y-1;
    inpicstr[i++]=inpic[me&4?x*ps+y:y*ps+x];
    if(me&1) x=ps-x-1;
    if(me&2) y=ps-y-1;
  }
  for(i=0;i<ms;i++) inpicabovestr[i]=i<ps?0:inpicstr[i-ps];
  inpicabovestr[i]=255; inpicstr[i++]=254;
  inpicabovestr[i]=253; inpicstr[i++]=252;
  outpiclen=i=0;
  while(i<ms) {
    ca=0;
    while(i+ca<ms && inpicstr[i+ca]==inpicabovestr[i+ca]) ca++;
    homo=1;
    while(i+homo<ms && inpicstr[i+homo]==inpicstr[i]) homo++;
    hetero=1;
    while(i+hetero<ms) {
      if(inpicstr[i+hetero]==inpicabovestr[i+hetero] && inpicstr[i+hetero+1]==inpicabovestr[i+hetero+1]) break;
      if(inpicstr[i+hetero]==inpicstr[i+hetero+1] && i+hetero==ms-1) break;
      if(inpicstr[i+hetero]==inpicstr[i+hetero+1] && inpicstr[i+hetero]==inpicstr[i+hetero+2]) break;
      hetero++;
    }
    if(ca>=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<npic;i++) {
    for(j=0;j<16;j++) {
      if(picalloc[i]&(1<<j)) {
        fwrite(nam,sprintf(nam,"%d.IMG",i*16+j)+1,1,fp);
        for(z=k=t=0;z<8;z++) {
          x=sizes[z];
          if(!x) break;
          for(y=0;y<x;y++) memcpy(inpic+y*x,pict+k+(i*x+y)*x*16+j*x,x);
          meth[z]=15;
          siz[z]=x*x;
          for(y=0;y<8;y++) {
            s=one_picture(x,y);
            if(s<siz[z]) siz[z]=s,meth[z]=y;
          }
          t+=siz[z];
          k+=x*x*16*npic;
        }
        if(z!=8) meth[z]=15;
        t+=z+1+(z>>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<x;y++) memcpy(inpic+y*x,pict+k+(i*x+y)*x*16+j*x,x);
          if(meth[z]==15) fwrite(inpic,x,x,fp);
          else fwrite(outpic,1,one_picture(x,meth[z]),fp);
          k+=x*x*16*npic;
        }
      }
    }
  }
  fclose(fp);
}

static void unprefix_messages(const char*pre,int n) {
  int i,j;
  for(i=0;i<nusermsg;i++) {
    if(usermsg[i] && !strncmp(pre,usermsg[i],n)) {
      for(j=0;j<nusermsg;j++) if(j!=i && usermsg[j] && !strcmp(usermsg[i],usermsg[j])) goto skip;
      memmove(usermsg[i],usermsg[i]+n,strlen(usermsg[i])+1-n);
    }
    skip: ;
  }
}

static inline void do_user_messages(void) {
  int i,j;
  i=fgetc(stdin);
  nusermsg=i|(fgetc(stdin)<<8);
  Allocate(usermsg,nusermsg);
  for(i=0;i<nusermsg;i++) {
    j=fgetc(stdin);
    if(j) {
      Allocate(usermsg[i],j+1);
      fread(usermsg[i],1,j,stdin);
      usermsg[i][j]=0;
    } else {
      usermsg[i]=0;
    }
  }
  unprefix_messages("MSG_",4);
  unprefix_messages("USR_",4);
  unprefix_messages("WTP_",4);
  unprefix_messages("MSG_",4);
}

static void read_one_class(void) {
  unsigned char buf[4];
  int i,j,k,id;
  char*name;
  i=fgetc(stdin);
  Allocate(name,i+1);
  fread(name,1,i,stdin);
  name[i]=0;
  id=fgetc(stdin);
  id|=fgetc(stdin)<<8;
  if(id>=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;i<j;i++) {
    k=fgetc(stdin);
    k|=fgetc(stdin)<<8;
    if(i<128) class[id]->pic[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;i<c->npic;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;i<c->npic;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]<<j)&16?'1':'0');
        fputc('\n',fp);
      }
      fprintf(fp,"  )\n");
    }
    if(!c->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]<<j)&16?'1':'0');
        fputc('\n',fp);
      }
      fprintf(fp,"  )\n");
    }
    if(c->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;i<c->nmsgs;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<nusermsg && usermsg[j-20]) fprintf(fp,"#%s",usermsg[j-20]);
      else fprintf(fp,"#%d?",j);
      fprintf(fp,"\n    ; Not implemented yet!\n  )\n");
    }
    fprintf(fp,")\n");
  }
  fclose(fp);
}

static void one_level(FILE*fp,int ord) {
  unsigned char buf[16];
  unsigned char mru[32];
  int i,j,x,y,n,q;
  i=fgetc(stdin);
  i|=fgetc(stdin)<<8;
  levelid[ord]=i;
  sprintf(nam,"%d.LVL",i);
  hamarc_begin(fp);
  fputc(0,fp); fputc(0,fp); // Level version
  fputc(ord,fp); fputc(ord>>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<nusermsg;i++) if(usermsg[i]) {
    fputc(i,fp); fputc((i+256)>>8,fp);
    fwrite(usermsg[i],1,strlen(usermsg[i])+1,fp);
  }
  hamarc_end(fp);
  for(i=0;i<n;i++) one_level(fp,i);
  strcpy(nam,"LEVEL.IDX");
  hamarc_begin(fp);
  for(i=0;i<n;i++) {
    fputc(levelid[i],fp);
    fputc(levelid[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 version [e0fea38138].




















































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// 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 version [db084a2327].



























































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// 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 version [477e17bf7e].



































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 version [528626beff].















































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#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 <stdio.h>
#include <stdlib.h>
#include <string.h>

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<size;y++) for(x=0;x<size;x++) {
    if(meth&1) x=size-x-1;
    if(meth&2) y=size-y-1;
    fwrite(pal[pic[meth&4?x*size+y:y*size+x]].x,1,8,stdout);
    if(meth&1) x=size-x-1;
    if(meth&2) y=size-y-1;
  }
}

int main(int argc,char**argv) {
  int i;
  if(argc!=2) return 1;
  load_palette();
  num=strtol(argv[1],0,0);
  *buf=fgetc(stdin);
  i=*buf&15;
  fread(buf+1,1,i+(i>>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;
}