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 GravityFlag = LInfo.InitialGravity;
252 FreezeZonks = LInfo.InitialFreezeZonks;
255 /* this is set by main game tape code to native random generator directly */
259 printf("::: file.c: copyInternalEngineVars_SP(): RandomSeed = LInfo.DemoRandomSeed\n");
262 RandomSeed = LInfo.DemoRandomSeed;
269 static void LoadNativeLevelFromFileStream_SP(FILE *file, int width, int height,
270 boolean demo_available)
272 LevelInfoType *header = &native_sp_level.header;
275 /* for details of the Supaplex level format, see Herman Perk's Supaplex
276 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
278 native_sp_level.width = width;
279 native_sp_level.height = height;
281 /* read level playfield (width * height == 60 * 24 tiles == 1440 bytes) */
282 for (y = 0; y < native_sp_level.height; y++)
283 for (x = 0; x < native_sp_level.width; x++)
284 native_sp_level.playfield[x][y] = getFile8Bit(file);
286 /* read level header (96 bytes) */
288 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
290 /* initial gravity: 1 == "on", anything else (0) == "off" */
291 header->InitialGravity = getFile8Bit(file);
293 /* SpeedFixVersion XOR 0x20 */
294 header->Version = getFile8Bit(file);
296 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
297 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
298 header->LevelTitle[i] = getFile8Bit(file);
300 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
301 header->InitialFreezeZonks = getFile8Bit(file);
303 /* number of infotrons needed; 0 means that Supaplex will count the total
304 amount of infotrons in the level and use the low byte of that number
305 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
306 header->InfotronsNeeded = getFile8Bit(file);
308 /* number of special ("gravity") port entries below (maximum 10 allowed) */
309 header->SpecialPortCount = getFile8Bit(file);
312 printf("::: num_special_ports == %d\n", header->SpecialPortCount);
315 /* database of properties of up to 10 special ports (6 bytes per port) */
316 for (i = 0; i < SP_MAX_SPECIAL_PORTS; i++)
318 SpecialPortType *port = &header->SpecialPort[i];
320 /* high and low byte of the location of a special port; if (x, y) are the
321 coordinates of a port in the field and (0, 0) is the top-left corner,
322 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
323 of what may be expected: Supaplex works with a game field in memory
324 which is 2 bytes per tile) */
325 port->PortLocation = getFile16BitBE(file); /* yes, big endian */
329 int port_x = (port->PortLocation / 2) % SP_PLAYFIELD_WIDTH;
330 int port_y = (port->PortLocation / 2) / SP_PLAYFIELD_WIDTH;
332 printf("::: %d: port_location == %d => (%d, %d)\n",
333 i, port->PortLocation, port_x, port_y);
337 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
338 port->Gravity = getFile8Bit(file);
340 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
341 port->FreezeZonks = getFile8Bit(file);
343 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
344 port->FreezeEnemies = getFile8Bit(file);
346 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
349 /* SpeedByte XOR Highbyte(RandomSeed) */
350 header->SpeedByte = getFile8Bit(file);
352 /* CheckSum XOR SpeedByte */
353 header->CheckSumByte = getFile8Bit(file);
355 /* random seed used for recorded demos */
356 header->DemoRandomSeed = getFile16BitLE(file); /* yes, little endian */
357 // header->DemoRandomSeed = getFile16BitBE(file); /* !!! TEST ONLY !!! */
360 printf("::: file.c: DemoRandomSeed == %d\n", header->DemoRandomSeed);
363 /* auto-determine number of infotrons if it was stored as "0" -- see above */
364 if (header->InfotronsNeeded == 0)
366 for (x = 0; x < native_sp_level.width; x++)
367 for (y = 0; y < native_sp_level.height; y++)
368 if (native_sp_level.playfield[x][y] == fiInfotron)
369 header->InfotronsNeeded++;
371 header->InfotronsNeeded &= 0xff; /* only use low byte -- see above */
374 /* read raw level header bytes (96 bytes) */
376 fseek(file, -(SP_HEADER_SIZE), SEEK_CUR); /* rewind file */
377 for (i = 0; i < SP_HEADER_SIZE; i++)
378 native_sp_level.header_raw_bytes[i] = fgetc(file);
380 /* also load demo tape, if available (only in single level files) */
384 int level_nr = getFile8Bit(file);
386 level_nr &= 0x7f; /* clear highest bit */
387 level_nr = (level_nr < 1 ? 1 :
388 level_nr > 111 ? 111 : level_nr);
390 native_sp_level.demo.level_nr = level_nr;
392 for (i = 0; i < SP_MAX_TAPE_LEN && !feof(file); i++)
394 native_sp_level.demo.data[i] = getFile8Bit(file);
396 if (native_sp_level.demo.data[i] == 0xff) /* "end of demo" byte */
404 native_sp_level.demo.length = i;
405 native_sp_level.demo.is_available = (native_sp_level.demo.length > 0);
409 boolean LoadNativeLevel_SP(char *filename, int level_pos)
413 char name_first, name_last;
414 struct LevelInfo_SP multipart_level;
415 int multipart_xpos, multipart_ypos;
416 boolean is_multipart_level;
417 boolean is_first_part;
418 boolean reading_multipart_level = FALSE;
419 boolean use_empty_level = FALSE;
420 LevelInfoType *header = &native_sp_level.header;
421 boolean is_single_level_file = (strSuffixLower(filename, ".sp") ||
422 strSuffixLower(filename, ".mpx"));
423 boolean demo_available = is_single_level_file;
424 boolean is_mpx_file = strSuffixLower(filename, ".mpx");
425 int file_seek_pos = level_pos * SP_STD_LEVEL_SIZE;
426 int level_width = SP_STD_PLAYFIELD_WIDTH;
427 int level_height = SP_STD_PLAYFIELD_HEIGHT;
429 /* always start with reliable default values */
430 setLevelInfoToDefaults_SP();
431 copyInternalEngineVars_SP();
433 if (!(file = fopen(filename, MODE_READ)))
435 Error(ERR_WARN, "cannot open file '%s' -- using empty level", filename);
442 char mpx_chunk_name[4 + 1];
445 LevelDescriptor *mpx_level_desc;
447 getFileChunkBE(file, mpx_chunk_name, NULL);
449 if (!strEqual(mpx_chunk_name, "MPX "))
451 Error(ERR_WARN, "cannot find MPX ID in file '%s' -- using empty level",
457 mpx_version = getFile16BitLE(file);
459 if (mpx_version != 1)
461 Error(ERR_WARN, "unknown MPX version in file '%s' -- using empty level",
467 mpx_level_count = getFile16BitLE(file);
469 if (mpx_level_count < 1)
471 Error(ERR_WARN, "no MPX levels found in file '%s' -- using empty level",
477 if (level_pos >= mpx_level_count)
479 Error(ERR_WARN, "MPX level not found in file '%s' -- using empty level",
485 mpx_level_desc = checked_calloc(mpx_level_count * sizeof(LevelDescriptor));
487 for (i = 0; i < mpx_level_count; i++)
489 LevelDescriptor *ldesc = &mpx_level_desc[i];
491 ldesc->Width = getFile16BitLE(file);
492 ldesc->Height = getFile16BitLE(file);
493 ldesc->OffSet = getFile32BitLE(file); /* starts with 1, not with 0 */
494 ldesc->Size = getFile32BitLE(file);
497 level_width = mpx_level_desc[level_pos].Width;
498 level_height = mpx_level_desc[level_pos].Height;
500 file_seek_pos = mpx_level_desc[level_pos].OffSet - 1;
503 /* position file stream to the requested level (in case of level package) */
504 if (fseek(file, file_seek_pos, SEEK_SET) != 0)
506 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
511 /* there exist Supaplex level package files with multi-part levels which
512 can be detected as follows: instead of leading and trailing dashes ('-')
513 to pad the level name, they have leading and trailing numbers which are
514 the x and y coordinations of the current part of the multi-part level;
515 if there are '?' characters instead of numbers on the left or right side
516 of the level name, the multi-part level consists of only horizontal or
519 for (l = level_pos; l < SP_NUM_LEVELS_PER_PACKAGE; l++)
521 LoadNativeLevelFromFileStream_SP(file, level_width, level_height,
524 /* check if this level is a part of a bigger multi-part level */
526 if (is_single_level_file)
529 name_first = header->LevelTitle[0];
530 name_last = header->LevelTitle[SP_LEVEL_NAME_LEN - 1];
533 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
534 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
537 ((name_first == '?' || name_first == '1') &&
538 (name_last == '?' || name_last == '1'));
540 if (is_multipart_level)
542 /* correct leading multipart level meta information in level name */
544 i < SP_LEVEL_NAME_LEN && header->LevelTitle[i] == name_first;
546 header->LevelTitle[i] = '-';
548 /* correct trailing multipart level meta information in level name */
549 for (i = SP_LEVEL_NAME_LEN - 1;
550 i >= 0 && header->LevelTitle[i] == name_last;
552 header->LevelTitle[i] = '-';
555 /* ---------- check for normal single level ---------- */
557 if (!reading_multipart_level && !is_multipart_level)
559 /* the current level is simply a normal single-part level, and we are
560 not reading a multi-part level yet, so return the level as it is */
565 /* ---------- check for empty level (unused multi-part) ---------- */
567 if (!reading_multipart_level && is_multipart_level && !is_first_part)
569 /* this is a part of a multi-part level, but not the first part
570 (and we are not already reading parts of a multi-part level);
571 in this case, use an empty level instead of the single part */
573 use_empty_level = TRUE;
578 /* ---------- check for finished multi-part level ---------- */
580 if (reading_multipart_level &&
581 (!is_multipart_level ||
582 !strEqualN(header->LevelTitle, multipart_level.header.LevelTitle,
585 /* we are already reading parts of a multi-part level, but this level is
586 either not a multi-part level, or a part of a different multi-part
587 level; in both cases, the multi-part level seems to be complete */
592 /* ---------- here we have one part of a multi-part level ---------- */
594 reading_multipart_level = TRUE;
596 if (is_first_part) /* start with first part of new multi-part level */
598 /* copy level info structure from first part */
599 multipart_level = native_sp_level;
601 /* clear playfield of new multi-part level */
602 for (x = 0; x < SP_MAX_PLAYFIELD_WIDTH; x++)
603 for (y = 0; y < SP_MAX_PLAYFIELD_HEIGHT; y++)
604 multipart_level.playfield[x][y] = fiSpace;
607 if (name_first == '?')
609 if (name_last == '?')
612 multipart_xpos = (int)(name_first - '0');
613 multipart_ypos = (int)(name_last - '0');
616 printf("----------> part (%d/%d) of multi-part level '%s'\n",
617 multipart_xpos, multipart_ypos, multipart_level.header.LevelTitle);
620 if (multipart_xpos * SP_STD_PLAYFIELD_WIDTH > SP_MAX_PLAYFIELD_WIDTH ||
621 multipart_ypos * SP_STD_PLAYFIELD_HEIGHT > SP_MAX_PLAYFIELD_HEIGHT)
623 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
628 multipart_level.width = MAX(multipart_level.width,
629 multipart_xpos * SP_STD_PLAYFIELD_WIDTH);
630 multipart_level.height = MAX(multipart_level.height,
631 multipart_ypos * SP_STD_PLAYFIELD_HEIGHT);
633 /* copy level part at the right position of multi-part level */
634 for (x = 0; x < SP_STD_PLAYFIELD_WIDTH; x++)
636 for (y = 0; y < SP_STD_PLAYFIELD_HEIGHT; y++)
638 int start_x = (multipart_xpos - 1) * SP_STD_PLAYFIELD_WIDTH;
639 int start_y = (multipart_ypos - 1) * SP_STD_PLAYFIELD_HEIGHT;
641 multipart_level.playfield[start_x + x][start_y + y] =
642 native_sp_level.playfield[x][y];
651 setLevelInfoToDefaults_SP();
653 Error(ERR_WARN, "single part of multi-part level -- using empty level");
656 if (reading_multipart_level)
657 native_sp_level = multipart_level;
659 copyInternalEngineVars_SP();
664 void SaveNativeLevel_SP(char *filename)
666 LevelInfoType *header = &native_sp_level.header;
670 if (!(file = fopen(filename, MODE_WRITE)))
672 Error(ERR_WARN, "cannot save native level file '%s'", filename);
677 /* write level playfield (width * height == 60 * 24 tiles == 1440 bytes) */
678 for (y = 0; y < native_sp_level.height; y++)
679 for (x = 0; x < native_sp_level.width; x++)
680 putFile8Bit(file, native_sp_level.playfield[x][y]);
682 /* write level header (96 bytes) */
684 WriteUnusedBytesToFile(file, 4);
686 putFile8Bit(file, header->InitialGravity);
687 putFile8Bit(file, header->Version);
689 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
690 putFile8Bit(file, header->LevelTitle[i]);
692 putFile8Bit(file, header->InitialFreezeZonks);
693 putFile8Bit(file, header->InfotronsNeeded);
694 putFile8Bit(file, header->SpecialPortCount);
696 for (i = 0; i < SP_MAX_SPECIAL_PORTS; i++)
698 SpecialPortType *port = &header->SpecialPort[i];
700 putFile16BitBE(file, port->PortLocation);
701 putFile8Bit(file, port->Gravity);
702 putFile8Bit(file, port->FreezeZonks);
703 putFile8Bit(file, port->FreezeEnemies);
705 WriteUnusedBytesToFile(file, 1);
708 putFile8Bit(file, header->SpeedByte);
709 putFile8Bit(file, header->CheckSumByte);
710 putFile16BitLE(file, header->DemoRandomSeed);
712 /* also save demo tape, if available */
714 if (native_sp_level.demo.is_available)
716 putFile8Bit(file, native_sp_level.demo.level_nr);
718 for (i = 0; i < native_sp_level.demo.length; i++)
719 putFile8Bit(file, native_sp_level.demo.data[i]);