6 /* ------------------------------------------------------------------------- */
7 /* functions for loading Supaplex level */
8 /* ------------------------------------------------------------------------- */
10 void setTapeInfoToDefaults_SP()
12 native_sp_level.demo.is_available = FALSE;
13 native_sp_level.demo.length = 0;
16 void setLevelInfoToDefaults_SP()
18 LevelInfoType *header = &native_sp_level.header;
19 char *empty_title = "-------- EMPTY --------";
22 native_sp_level.game_sp = &game_sp;
24 native_sp_level.width = SP_STD_PLAYFIELD_WIDTH;
25 native_sp_level.height = SP_STD_PLAYFIELD_HEIGHT;
27 for (x = 0; x < native_sp_level.width; x++)
28 for (y = 0; y < native_sp_level.height; y++)
29 native_sp_level.playfield[x][y] = fiSpace;
31 /* copy string (without terminating '\0' character!) */
32 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
33 header->LevelTitle[i] = empty_title[i];
35 header->InitialGravity = 0;
37 header->InitialFreezeZonks = 0;
38 header->InfotronsNeeded = 0;
39 header->SpecialPortCount = 0;
40 header->SpeedByte = 0;
41 header->CheckSumByte = 0;
42 header->DemoRandomSeed = 0;
44 for (i = 0; i < SP_MAX_SPECIAL_PORTS; i++)
46 SpecialPortType *port = &header->SpecialPort[i];
48 port->PortLocation = 0;
50 port->FreezeZonks = 0;
51 port->FreezeEnemies = 0;
54 /* set raw header bytes (used for subsequent buffer zone) to "hardware" */
55 for (i = 0; i < SP_HEADER_SIZE; i++)
56 native_sp_level.header_raw_bytes[i] = 0x20;
58 setTapeInfoToDefaults_SP();
61 void copyInternalEngineVars_SP()
63 char *preceding_playfield_memory[] =
65 "95 89 95 89 95 89 3b 8a 3b 8a 3b 8a 3b 8a 3b 8a", // |......;.;.;.;.;.|
66 "3b 8a 3b 8a 3b 8a e8 8a e8 8a e8 8a e8 8a e8 8a", // |;.;.;.è.è.è.è.è.|
67 "e8 8a e8 8a e8 8a b1 8b b1 8b b1 8b b1 8b b1 8b", // |è.è.è.±.±.±.±.±.|
68 "b1 8b b1 8b b1 8b 85 8c 85 8c 85 8c 85 8c 85 8c", // |±.±.±...........|
69 "85 8c 85 8c 85 8c 5b 8d 5b 8d 5b 8d 5b 8d 5b 8d", // |......[.[.[.[.[.|
70 "5b 8d 5b 8d 5b 8d 06 8e 06 8e 06 8e 06 8e 06 8e", // |[.[.[...........|
71 "06 8e 06 8e 06 8e ac 8e ac 8e ac 8e ac 8e ac 8e", // |......¬.¬.¬.¬.¬.|
72 "ac 8e ac 8e ac 8e 59 8f 59 8f 59 8f 59 8f 59 8f", // |¬.¬.¬.Y.Y.Y.Y.Y.|
73 "59 8f 59 8f 59 8f 00 00 70 13 00 00 00 00 e8 17", // |Y.Y.Y...p.....è.|
74 "00 00 00 00 00 00 69 38 00 00 00 00 00 00 00 00", // |......i8........|
75 "00 00 00 00 00 00 00 00 d0 86 00 00 b2 34 00 00", // |........Ð...²4..|
76 "00 00 00 00 00 00 8f 8b 1d 34 00 00 00 00 00 00", // |.........4......|
77 "00 00 00 00 23 39 09 09 00 0c 00 08 00 58 00 00", // |....#9.......X..|
78 "00 00 00 25 77 06 7f 00 00 00 01 00 00 00 00 00", // |...%w...........|
79 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
80 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
81 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
82 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
83 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
84 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
85 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
86 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
87 "00 00 00 00 00 00 00 00 00 ec 06 26 05 00 00 00", // |.........ì.&....|
88 "00 00 00 01 00 00 00 00 31 32 33 34 35 36 37 38", // |........12345678|
89 "39 30 2d 00 08 00 51 57 45 52 54 59 55 49 4f 50", // |90-...QWERTYUIOP|
90 "00 00 0a 00 41 53 44 46 47 48 4a 4b 4c 00 00 00", // |....ASDFGHJKL...|
91 "00 00 5a 58 43 56 42 4e 4d 00 00 00 00 00 00 20", // |..ZXCVBNM...... |
92 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
93 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
94 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
95 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
96 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
97 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
98 "00 00 00 00 00 00 2e 00 1e 00 31 00 14 00 39 00", // |..........1...9.|
99 "1f 00 14 00 18 00 ff ff 01 00 01 4c 45 56 45 4c", // |......ÿÿ...LEVEL|
100 "53 2e 44 41 54 00 00 00 00 00 00 00 00 00 00 00", // |S.DAT...........|
101 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
102 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
103 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
104 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
105 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
106 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
107 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................|
111 int preceding_buffer_size = 0;
116 for (i = 0; preceding_playfield_memory[i] != NULL; i++)
117 preceding_buffer_size += 8; /* eight 16-bit integer values */
120 /* needed for engine snapshots */
121 game_sp.preceding_buffer_size = preceding_buffer_size;
123 LInfo = native_sp_level.header;
125 FieldWidth = native_sp_level.width;
126 FieldHeight = native_sp_level.height;
129 FieldMax = (FieldWidth * FieldHeight) + HeaderSize - 1;
130 LevelMax = (FieldWidth * FieldHeight) - 1;
132 FileMax = FieldMax + native_sp_level.demo.length;
134 PlayField8 = REDIM_1D(sizeof(byte), 0, FileMax + 1 - 1);
135 DisPlayField = REDIM_1D(sizeof(byte), 0, FieldMax + 1 - 1);
137 PlayField16 = REDIM_1D(sizeof(int), -preceding_buffer_size, FieldMax);
139 PlayField16 = REDIM_1D(sizeof(int), -FieldWidth, FieldMax);
145 /* fill preceding playfield buffer zone with (indestructible) "hardware" */
146 for (i = -FieldWidth; i < 0; i++)
147 PlayField16[i] = 0x20;
152 for (i = 0; preceding_playfield_memory[i] != NULL; i++)
154 char *s = preceding_playfield_memory[i];
155 boolean hi_byte = FALSE; /* little endian data => start with low byte */
157 while (s[0] != '\0' && s[1] != '\0')
159 int hi_nibble = s[0] - (s[0] > '9' ? 'a' - 10 : '0');
160 int lo_nibble = s[1] - (s[1] > '9' ? 'a' - 10 : '0');
161 int byte = (hi_nibble << 4) | lo_nibble;
166 PlayField16[-preceding_buffer_size + count] |= byte;
182 for (y = 0; y < native_sp_level.height; y++)
183 for (x = 0; x < native_sp_level.width; x++)
184 PlayField8[count++] = native_sp_level.playfield[x][y];
186 /* add raw header bytes to subsequent playfield buffer zone */
187 for (i = 0; i < SP_HEADER_SIZE; i++)
188 PlayField8[count++] = native_sp_level.header_raw_bytes[i];
190 for (i = 0; i < count; i++)
192 PlayField16[i] = PlayField8[i];
193 DisPlayField[i] = PlayField8[i];
204 printf("----------\n");
205 for (i = 0; i < preceding_buffer_size + FieldMax; i++)
207 int x = PlayField16[-preceding_buffer_size + i];
209 printf("%c%c", x & 0xff, x >> 8);
211 printf("----------\n");
221 for (i = 0; y = 0; y < native_sp_level.height; y++)
223 for (x = 0; x < native_sp_level.width; x++)
225 PlayField8[i] = native_sp_level.playfield[x][y];
227 PlayField16[i] = PlayField8[i];
228 DisPlayField[i] = PlayField8[i];
237 if (native_sp_level.demo.is_available)
239 DemoAvailable = True;
241 PlayField8[FieldMax + 1] = native_sp_level.demo.level_nr;
243 for (i = 0; i < native_sp_level.demo.length; i++)
244 PlayField8[FieldMax + i + 2] = native_sp_level.demo.data[i];
247 AnimationPosTable = REDIM_1D(sizeof(int), 0, LevelMax - 2 * FieldWidth);
248 AnimationSubTable = REDIM_1D(sizeof(byte), 0, LevelMax - 2 * FieldWidth);
249 TerminalState = REDIM_1D(sizeof(byte), 0, FieldMax + 1 - 1);
251 DemoPointer = FieldMax + 1;
252 DemoOffset = DemoPointer;
253 DemoKeyRepeatCounter = 0;
255 GravityFlag = LInfo.InitialGravity;
256 FreezeZonks = LInfo.InitialFreezeZonks;
259 /* this is set by main game tape code to native random generator directly */
263 printf("::: file.c: copyInternalEngineVars_SP(): RandomSeed = LInfo.DemoRandomSeed\n");
266 RandomSeed = LInfo.DemoRandomSeed;
273 static void LoadNativeLevelFromFileStream_SP(FILE *file, int width, int height,
274 boolean demo_available)
276 LevelInfoType *header = &native_sp_level.header;
279 /* for details of the Supaplex level format, see Herman Perk's Supaplex
280 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
282 native_sp_level.width = width;
283 native_sp_level.height = height;
285 /* read level playfield (width * height == 60 * 24 tiles == 1440 bytes) */
286 for (y = 0; y < native_sp_level.height; y++)
287 for (x = 0; x < native_sp_level.width; x++)
288 native_sp_level.playfield[x][y] = getFile8Bit(file);
290 /* read level header (96 bytes) */
292 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
294 /* initial gravity: 1 == "on", anything else (0) == "off" */
295 header->InitialGravity = getFile8Bit(file);
297 /* SpeedFixVersion XOR 0x20 */
298 header->Version = getFile8Bit(file);
300 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
301 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
302 header->LevelTitle[i] = getFile8Bit(file);
304 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
305 header->InitialFreezeZonks = getFile8Bit(file);
307 /* number of infotrons needed; 0 means that Supaplex will count the total
308 amount of infotrons in the level and use the low byte of that number
309 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
310 header->InfotronsNeeded = getFile8Bit(file);
312 /* number of special ("gravity") port entries below (maximum 10 allowed) */
313 header->SpecialPortCount = getFile8Bit(file);
316 printf("::: num_special_ports == %d\n", header->SpecialPortCount);
319 /* database of properties of up to 10 special ports (6 bytes per port) */
320 for (i = 0; i < SP_MAX_SPECIAL_PORTS; i++)
322 SpecialPortType *port = &header->SpecialPort[i];
324 /* high and low byte of the location of a special port; if (x, y) are the
325 coordinates of a port in the field and (0, 0) is the top-left corner,
326 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
327 of what may be expected: Supaplex works with a game field in memory
328 which is 2 bytes per tile) */
329 port->PortLocation = getFile16BitBE(file); /* yes, big endian */
333 int port_x = (port->PortLocation / 2) % SP_PLAYFIELD_WIDTH;
334 int port_y = (port->PortLocation / 2) / SP_PLAYFIELD_WIDTH;
336 printf("::: %d: port_location == %d => (%d, %d)\n",
337 i, port->PortLocation, port_x, port_y);
341 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
342 port->Gravity = getFile8Bit(file);
344 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
345 port->FreezeZonks = getFile8Bit(file);
347 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
348 port->FreezeEnemies = getFile8Bit(file);
350 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
353 /* SpeedByte XOR Highbyte(RandomSeed) */
354 header->SpeedByte = getFile8Bit(file);
356 /* CheckSum XOR SpeedByte */
357 header->CheckSumByte = getFile8Bit(file);
359 /* random seed used for recorded demos */
360 header->DemoRandomSeed = getFile16BitLE(file); /* yes, little endian */
361 // header->DemoRandomSeed = getFile16BitBE(file); /* !!! TEST ONLY !!! */
364 printf("::: file.c: DemoRandomSeed == %d\n", header->DemoRandomSeed);
367 /* auto-determine number of infotrons if it was stored as "0" -- see above */
368 if (header->InfotronsNeeded == 0)
370 for (x = 0; x < native_sp_level.width; x++)
371 for (y = 0; y < native_sp_level.height; y++)
372 if (native_sp_level.playfield[x][y] == fiInfotron)
373 header->InfotronsNeeded++;
375 header->InfotronsNeeded &= 0xff; /* only use low byte -- see above */
378 /* read raw level header bytes (96 bytes) */
380 fseek(file, -(SP_HEADER_SIZE), SEEK_CUR); /* rewind file */
381 for (i = 0; i < SP_HEADER_SIZE; i++)
382 native_sp_level.header_raw_bytes[i] = fgetc(file);
384 /* also load demo tape, if available (only in single level files) */
388 int level_nr = getFile8Bit(file);
390 level_nr &= 0x7f; /* clear highest bit */
391 level_nr = (level_nr < 1 ? 1 :
392 level_nr > 111 ? 111 : level_nr);
394 native_sp_level.demo.level_nr = level_nr;
396 for (i = 0; i < SP_MAX_TAPE_LEN && !feof(file); i++)
398 native_sp_level.demo.data[i] = getFile8Bit(file);
400 if (native_sp_level.demo.data[i] == 0xff) /* "end of demo" byte */
408 native_sp_level.demo.length = i;
409 native_sp_level.demo.is_available = (native_sp_level.demo.length > 0);
413 boolean LoadNativeLevel_SP(char *filename, int level_pos)
417 char name_first, name_last;
418 struct LevelInfo_SP multipart_level;
419 int multipart_xpos, multipart_ypos;
420 boolean is_multipart_level;
421 boolean is_first_part;
422 boolean reading_multipart_level = FALSE;
423 boolean use_empty_level = FALSE;
424 LevelInfoType *header = &native_sp_level.header;
425 boolean is_single_level_file = (strSuffixLower(filename, ".sp") ||
426 strSuffixLower(filename, ".mpx"));
427 boolean demo_available = is_single_level_file;
428 boolean is_mpx_file = strSuffixLower(filename, ".mpx");
429 int file_seek_pos = level_pos * SP_STD_LEVEL_SIZE;
430 int level_width = SP_STD_PLAYFIELD_WIDTH;
431 int level_height = SP_STD_PLAYFIELD_HEIGHT;
433 /* always start with reliable default values */
434 setLevelInfoToDefaults_SP();
435 copyInternalEngineVars_SP();
437 if (!(file = fopen(filename, MODE_READ)))
439 Error(ERR_WARN, "cannot open file '%s' -- using empty level", filename);
446 char mpx_chunk_name[4 + 1];
449 LevelDescriptor *mpx_level_desc;
451 getFileChunkBE(file, mpx_chunk_name, NULL);
453 if (!strEqual(mpx_chunk_name, "MPX "))
455 Error(ERR_WARN, "cannot find MPX ID in file '%s' -- using empty level",
461 mpx_version = getFile16BitLE(file);
463 if (mpx_version != 1)
465 Error(ERR_WARN, "unknown MPX version in file '%s' -- using empty level",
471 mpx_level_count = getFile16BitLE(file);
473 if (mpx_level_count < 1)
475 Error(ERR_WARN, "no MPX levels found in file '%s' -- using empty level",
481 if (level_pos >= mpx_level_count)
483 Error(ERR_WARN, "MPX level not found in file '%s' -- using empty level",
489 mpx_level_desc = checked_calloc(mpx_level_count * sizeof(LevelDescriptor));
491 for (i = 0; i < mpx_level_count; i++)
493 LevelDescriptor *ldesc = &mpx_level_desc[i];
495 ldesc->Width = getFile16BitLE(file);
496 ldesc->Height = getFile16BitLE(file);
497 ldesc->OffSet = getFile32BitLE(file); /* starts with 1, not with 0 */
498 ldesc->Size = getFile32BitLE(file);
501 level_width = mpx_level_desc[level_pos].Width;
502 level_height = mpx_level_desc[level_pos].Height;
504 file_seek_pos = mpx_level_desc[level_pos].OffSet - 1;
507 /* position file stream to the requested level (in case of level package) */
508 if (fseek(file, file_seek_pos, SEEK_SET) != 0)
510 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
515 /* there exist Supaplex level package files with multi-part levels which
516 can be detected as follows: instead of leading and trailing dashes ('-')
517 to pad the level name, they have leading and trailing numbers which are
518 the x and y coordinations of the current part of the multi-part level;
519 if there are '?' characters instead of numbers on the left or right side
520 of the level name, the multi-part level consists of only horizontal or
523 for (l = level_pos; l < SP_NUM_LEVELS_PER_PACKAGE; l++)
525 LoadNativeLevelFromFileStream_SP(file, level_width, level_height,
528 /* check if this level is a part of a bigger multi-part level */
530 if (is_single_level_file)
533 name_first = header->LevelTitle[0];
534 name_last = header->LevelTitle[SP_LEVEL_NAME_LEN - 1];
537 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
538 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
541 ((name_first == '?' || name_first == '1') &&
542 (name_last == '?' || name_last == '1'));
544 if (is_multipart_level)
546 /* correct leading multipart level meta information in level name */
548 i < SP_LEVEL_NAME_LEN && header->LevelTitle[i] == name_first;
550 header->LevelTitle[i] = '-';
552 /* correct trailing multipart level meta information in level name */
553 for (i = SP_LEVEL_NAME_LEN - 1;
554 i >= 0 && header->LevelTitle[i] == name_last;
556 header->LevelTitle[i] = '-';
559 /* ---------- check for normal single level ---------- */
561 if (!reading_multipart_level && !is_multipart_level)
563 /* the current level is simply a normal single-part level, and we are
564 not reading a multi-part level yet, so return the level as it is */
569 /* ---------- check for empty level (unused multi-part) ---------- */
571 if (!reading_multipart_level && is_multipart_level && !is_first_part)
573 /* this is a part of a multi-part level, but not the first part
574 (and we are not already reading parts of a multi-part level);
575 in this case, use an empty level instead of the single part */
577 use_empty_level = TRUE;
582 /* ---------- check for finished multi-part level ---------- */
584 if (reading_multipart_level &&
585 (!is_multipart_level ||
586 !strEqualN(header->LevelTitle, multipart_level.header.LevelTitle,
589 /* we are already reading parts of a multi-part level, but this level is
590 either not a multi-part level, or a part of a different multi-part
591 level; in both cases, the multi-part level seems to be complete */
596 /* ---------- here we have one part of a multi-part level ---------- */
598 reading_multipart_level = TRUE;
600 if (is_first_part) /* start with first part of new multi-part level */
602 /* copy level info structure from first part */
603 multipart_level = native_sp_level;
605 /* clear playfield of new multi-part level */
606 for (x = 0; x < SP_MAX_PLAYFIELD_WIDTH; x++)
607 for (y = 0; y < SP_MAX_PLAYFIELD_HEIGHT; y++)
608 multipart_level.playfield[x][y] = fiSpace;
611 if (name_first == '?')
613 if (name_last == '?')
616 multipart_xpos = (int)(name_first - '0');
617 multipart_ypos = (int)(name_last - '0');
620 printf("----------> part (%d/%d) of multi-part level '%s'\n",
621 multipart_xpos, multipart_ypos, multipart_level.header.LevelTitle);
624 if (multipart_xpos * SP_STD_PLAYFIELD_WIDTH > SP_MAX_PLAYFIELD_WIDTH ||
625 multipart_ypos * SP_STD_PLAYFIELD_HEIGHT > SP_MAX_PLAYFIELD_HEIGHT)
627 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
632 multipart_level.width = MAX(multipart_level.width,
633 multipart_xpos * SP_STD_PLAYFIELD_WIDTH);
634 multipart_level.height = MAX(multipart_level.height,
635 multipart_ypos * SP_STD_PLAYFIELD_HEIGHT);
637 /* copy level part at the right position of multi-part level */
638 for (x = 0; x < SP_STD_PLAYFIELD_WIDTH; x++)
640 for (y = 0; y < SP_STD_PLAYFIELD_HEIGHT; y++)
642 int start_x = (multipart_xpos - 1) * SP_STD_PLAYFIELD_WIDTH;
643 int start_y = (multipart_ypos - 1) * SP_STD_PLAYFIELD_HEIGHT;
645 multipart_level.playfield[start_x + x][start_y + y] =
646 native_sp_level.playfield[x][y];
655 setLevelInfoToDefaults_SP();
657 Error(ERR_WARN, "single part of multi-part level -- using empty level");
660 if (reading_multipart_level)
661 native_sp_level = multipart_level;
663 copyInternalEngineVars_SP();
668 void SaveNativeLevel_SP(char *filename)
670 LevelInfoType *header = &native_sp_level.header;
674 if (!(file = fopen(filename, MODE_WRITE)))
676 Error(ERR_WARN, "cannot save native level file '%s'", filename);
681 /* write level playfield (width * height == 60 * 24 tiles == 1440 bytes) */
682 for (y = 0; y < native_sp_level.height; y++)
683 for (x = 0; x < native_sp_level.width; x++)
684 putFile8Bit(file, native_sp_level.playfield[x][y]);
686 /* write level header (96 bytes) */
688 WriteUnusedBytesToFile(file, 4);
690 putFile8Bit(file, header->InitialGravity);
691 putFile8Bit(file, header->Version);
693 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
694 putFile8Bit(file, header->LevelTitle[i]);
696 putFile8Bit(file, header->InitialFreezeZonks);
697 putFile8Bit(file, header->InfotronsNeeded);
698 putFile8Bit(file, header->SpecialPortCount);
700 for (i = 0; i < SP_MAX_SPECIAL_PORTS; i++)
702 SpecialPortType *port = &header->SpecialPort[i];
704 putFile16BitBE(file, port->PortLocation);
705 putFile8Bit(file, port->Gravity);
706 putFile8Bit(file, port->FreezeZonks);
707 putFile8Bit(file, port->FreezeEnemies);
709 WriteUnusedBytesToFile(file, 1);
712 putFile8Bit(file, header->SpeedByte);
713 putFile8Bit(file, header->CheckSumByte);
714 putFile16BitLE(file, header->DemoRandomSeed);
716 /* also save demo tape, if available */
718 if (native_sp_level.demo.is_available)
720 putFile8Bit(file, native_sp_level.demo.level_nr);
722 for (i = 0; i < native_sp_level.demo.length; i++)
723 putFile8Bit(file, native_sp_level.demo.data[i]);