X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame_sp%2Ffile.c;h=a1ff3e80cc7769d03aa9e38c67a1dca064c936bf;hp=f7b833c10474fd26003d51b86b67c49a8599ff20;hb=64e7c54dce6ea8c063f04198c64c5057d751c928;hpb=4a8368abd71c718621838773eed914b5bbd4e1d8 diff --git a/src/game_sp/file.c b/src/game_sp/file.c index f7b833c1..a1ff3e80 100644 --- a/src/game_sp/file.c +++ b/src/game_sp/file.c @@ -3,17 +3,17 @@ #include "global.h" -/* ------------------------------------------------------------------------- */ -/* functions for loading Supaplex level */ -/* ------------------------------------------------------------------------- */ +// ---------------------------------------------------------------------------- +// functions for loading Supaplex level +// ---------------------------------------------------------------------------- -void setTapeInfoToDefaults_SP() +static void setTapeInfoToDefaults_SP(void) { native_sp_level.demo.is_available = FALSE; native_sp_level.demo.length = 0; } -void setLevelInfoToDefaults_SP() +void setLevelInfoToDefaults_SP(void) { LevelInfoType *header = &native_sp_level.header; char *empty_title = "-------- EMPTY --------"; @@ -28,7 +28,7 @@ void setLevelInfoToDefaults_SP() for (y = 0; y < native_sp_level.height; y++) native_sp_level.playfield[x][y] = fiSpace; - /* copy string (without terminating '\0' character!) */ + // copy string (without terminating '\0' character!) for (i = 0; i < SP_LEVEL_NAME_LEN; i++) header->LevelTitle[i] = empty_title[i]; @@ -51,75 +51,18 @@ void setLevelInfoToDefaults_SP() port->FreezeEnemies = 0; } - /* set raw header bytes (used for subsequent buffer zone) to "hardware" */ + // set raw header bytes (used for subsequent buffer zone) to "hardware" for (i = 0; i < SP_HEADER_SIZE; i++) native_sp_level.header_raw_bytes[i] = 0x20; setTapeInfoToDefaults_SP(); } -void copyInternalEngineVars_SP() +void copyInternalEngineVars_SP(void) { - char *preceding_playfield_memory[] = - { - "95 89 95 89 95 89 3b 8a 3b 8a 3b 8a 3b 8a 3b 8a", // |......;.;.;.;.;.| - "3b 8a 3b 8a 3b 8a e8 8a e8 8a e8 8a e8 8a e8 8a", // |;.;.;.è.è.è.è.è.| - "e8 8a e8 8a e8 8a b1 8b b1 8b b1 8b b1 8b b1 8b", // |è.è.è.±.±.±.±.±.| - "b1 8b b1 8b b1 8b 85 8c 85 8c 85 8c 85 8c 85 8c", // |±.±.±...........| - "85 8c 85 8c 85 8c 5b 8d 5b 8d 5b 8d 5b 8d 5b 8d", // |......[.[.[.[.[.| - "5b 8d 5b 8d 5b 8d 06 8e 06 8e 06 8e 06 8e 06 8e", // |[.[.[...........| - "06 8e 06 8e 06 8e ac 8e ac 8e ac 8e ac 8e ac 8e", // |......¬.¬.¬.¬.¬.| - "ac 8e ac 8e ac 8e 59 8f 59 8f 59 8f 59 8f 59 8f", // |¬.¬.¬.Y.Y.Y.Y.Y.| - "59 8f 59 8f 59 8f 00 00 70 13 00 00 00 00 e8 17", // |Y.Y.Y...p.....è.| - "00 00 00 00 00 00 69 38 00 00 00 00 00 00 00 00", // |......i8........| - "00 00 00 00 00 00 00 00 d0 86 00 00 b2 34 00 00", // |........Ð...²4..| - "00 00 00 00 00 00 8f 8b 1d 34 00 00 00 00 00 00", // |.........4......| - "00 00 00 00 23 39 09 09 00 0c 00 08 00 58 00 00", // |....#9.......X..| - "00 00 00 25 77 06 7f 00 00 00 01 00 00 00 00 00", // |...%w...........| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 ec 06 26 05 00 00 00", // |.........ì.&....| - "00 00 00 01 00 00 00 00 31 32 33 34 35 36 37 38", // |........12345678| - "39 30 2d 00 08 00 51 57 45 52 54 59 55 49 4f 50", // |90-...QWERTYUIOP| - "00 00 0a 00 41 53 44 46 47 48 4a 4b 4c 00 00 00", // |....ASDFGHJKL...| - "00 00 5a 58 43 56 42 4e 4d 00 00 00 00 00 00 20", // |..ZXCVBNM...... | - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 2e 00 1e 00 31 00 14 00 39 00", // |..........1...9.| - "1f 00 14 00 18 00 ff ff 01 00 01 4c 45 56 45 4c", // |......ÿÿ...LEVEL| - "53 2e 44 41 54 00 00 00 00 00 00 00 00 00 00 00", // |S.DAT...........| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", // |................| - - NULL - }; - int preceding_buffer_size = 0; int count; int i, x, y; -#if 1 - for (i = 0; preceding_playfield_memory[i] != NULL; i++) - preceding_buffer_size += 8; /* eight 16-bit integer values */ -#endif - - /* needed for engine snapshots */ - game_sp.preceding_buffer_size = preceding_buffer_size; - LInfo = native_sp_level.header; FieldWidth = native_sp_level.width; @@ -129,30 +72,19 @@ void copyInternalEngineVars_SP() FieldMax = (FieldWidth * FieldHeight) + HeaderSize - 1; LevelMax = (FieldWidth * FieldHeight) - 1; - FileMax = FieldMax + native_sp_level.demo.length; - - PlayField8 = REDIM_1D(sizeof(byte), 0, FileMax + 1 - 1); - DisPlayField = REDIM_1D(sizeof(byte), 0, FieldMax + 1 - 1); -#if 1 - PlayField16 = REDIM_1D(sizeof(int), -preceding_buffer_size, FieldMax); -#else - PlayField16 = REDIM_1D(sizeof(int), -FieldWidth, FieldMax); -#endif + // initialize preceding playfield buffer + for (i = -game_sp.preceding_buffer_size; i < 0; i++) + PlayField16[i] = 0; -#if 1 - -#if 0 - /* fill preceding playfield buffer zone with (indestructible) "hardware" */ - for (i = -FieldWidth; i < 0; i++) - PlayField16[i] = 0x20; -#endif + // initialize preceding playfield buffer + for (i = -SP_MAX_PLAYFIELD_WIDTH; i < 0; i++) + PlayField8[i] = 0; -#if 1 count = 0; - for (i = 0; preceding_playfield_memory[i] != NULL; i++) + for (i = 0; game_sp.preceding_buffer[i] != NULL; i++) { - char *s = preceding_playfield_memory[i]; - boolean hi_byte = FALSE; /* little endian data => start with low byte */ + char *s = game_sp.preceding_buffer[i]; + boolean hi_byte = FALSE; // little endian data => start with low byte while (s[0] != '\0' && s[1] != '\0') { @@ -163,7 +95,7 @@ void copyInternalEngineVars_SP() if (hi_byte) byte <<= 8; - PlayField16[-preceding_buffer_size + count] |= byte; + PlayField16[-game_sp.preceding_buffer_size + count] |= byte; if (hi_byte) count++; @@ -176,14 +108,13 @@ void copyInternalEngineVars_SP() s++; } } -#endif count = 0; for (y = 0; y < native_sp_level.height; y++) for (x = 0; x < native_sp_level.width; x++) PlayField8[count++] = native_sp_level.playfield[x][y]; - /* add raw header bytes to subsequent playfield buffer zone */ + // add raw header bytes to subsequent playfield buffer zone for (i = 0; i < SP_HEADER_SIZE; i++) PlayField8[count++] = native_sp_level.header_raw_bytes[i]; @@ -194,83 +125,18 @@ void copyInternalEngineVars_SP() PlayField8[i] = 0; } -#if 0 - { - static int x = 0; - - if (x == 1) - { - printf("++++++++"); - printf("----------\n"); - for (i = 0; i < preceding_buffer_size + FieldMax; i++) - { - int x = PlayField16[-preceding_buffer_size + i]; - - printf("%c%c", x & 0xff, x >> 8); - } - printf("----------\n"); - exit(0); - } - - x++; - } -#endif - -#else - - for (i = 0; y = 0; y < native_sp_level.height; y++) - { - for (x = 0; x < native_sp_level.width; x++) - { - PlayField8[i] = native_sp_level.playfield[x][y]; - - PlayField16[i] = PlayField8[i]; - DisPlayField[i] = PlayField8[i]; - PlayField8[i] = 0; - - i++; - } - } - -#endif - if (native_sp_level.demo.is_available) - { DemoAvailable = True; - PlayField8[FieldMax + 1] = native_sp_level.demo.level_nr; - - for (i = 0; i < native_sp_level.demo.length; i++) - PlayField8[FieldMax + i + 2] = native_sp_level.demo.data[i]; - } - - AnimationPosTable = REDIM_1D(sizeof(int), 0, LevelMax - 2 * FieldWidth); - AnimationSubTable = REDIM_1D(sizeof(byte), 0, LevelMax - 2 * FieldWidth); - TerminalState = REDIM_1D(sizeof(byte), 0, FieldMax + 1 - 1); - - DemoPointer = FieldMax + 1; - DemoOffset = DemoPointer; - DemoKeyRepeatCounter = 0; - GravityFlag = LInfo.InitialGravity; FreezeZonks = LInfo.InitialFreezeZonks; -#if 1 - /* this is set by main game tape code to native random generator directly */ -#else - -#if 1 - printf("::: file.c: copyInternalEngineVars_SP(): RandomSeed = LInfo.DemoRandomSeed\n"); -#endif - - RandomSeed = LInfo.DemoRandomSeed; - -#endif - LevelLoaded = True; + + // random seed set by main game tape code to native random generator seed } -static void LoadNativeLevelFromFileStream_SP(FILE *file, int width, int height, +static void LoadNativeLevelFromFileStream_SP(File *file, int width, int height, boolean demo_available) { LevelInfoType *header = &native_sp_level.header; @@ -279,29 +145,38 @@ static void LoadNativeLevelFromFileStream_SP(FILE *file, int width, int height, /* for details of the Supaplex level format, see Herman Perk's Supaplex documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */ - native_sp_level.width = width; - native_sp_level.height = height; + native_sp_level.width = MIN(width, SP_MAX_PLAYFIELD_WIDTH); + native_sp_level.height = MIN(height, SP_MAX_PLAYFIELD_HEIGHT); - /* read level playfield (width * height == 60 * 24 tiles == 1440 bytes) */ - for (y = 0; y < native_sp_level.height; y++) - for (x = 0; x < native_sp_level.width; x++) - native_sp_level.playfield[x][y] = getFile8Bit(file); + // read level playfield (width * height == 60 * 24 tiles == 1440 bytes) + // (MPX levels may have non-standard playfield size -- check max. size) + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + byte element = getFile8Bit(file); + + if (x < SP_MAX_PLAYFIELD_WIDTH && + y < SP_MAX_PLAYFIELD_HEIGHT) + native_sp_level.playfield[x][y] = element; + } + } - /* read level header (96 bytes) */ + // read level header (96 bytes) - ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */ + ReadUnusedBytesFromFile(file, 4); // (not used by Supaplex engine) - /* initial gravity: 1 == "on", anything else (0) == "off" */ + // initial gravity: 1 == "on", anything else (0) == "off" header->InitialGravity = getFile8Bit(file); - /* SpeedFixVersion XOR 0x20 */ + // SpeedFixVersion XOR 0x20 header->Version = getFile8Bit(file); - /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */ + // level title in uppercase letters, padded with dashes ("-") (23 bytes) for (i = 0; i < SP_LEVEL_NAME_LEN; i++) header->LevelTitle[i] = getFile8Bit(file); - /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */ + // initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" header->InitialFreezeZonks = getFile8Bit(file); /* number of infotrons needed; 0 means that Supaplex will count the total @@ -309,14 +184,10 @@ static void LoadNativeLevelFromFileStream_SP(FILE *file, int width, int height, (a multiple of 256 infotrons will result in "0 infotrons needed"!) */ header->InfotronsNeeded = getFile8Bit(file); - /* number of special ("gravity") port entries below (maximum 10 allowed) */ + // number of special ("gravity") port entries below (maximum 10 allowed) header->SpecialPortCount = getFile8Bit(file); -#if 0 - printf("::: num_special_ports == %d\n", header->SpecialPortCount); -#endif - - /* database of properties of up to 10 special ports (6 bytes per port) */ + // database of properties of up to 10 special ports (6 bytes per port) for (i = 0; i < SP_MAX_SPECIAL_PORTS; i++) { SpecialPortType *port = &header->SpecialPort[i]; @@ -326,45 +197,30 @@ static void LoadNativeLevelFromFileStream_SP(FILE *file, int width, int height, the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice of what may be expected: Supaplex works with a game field in memory which is 2 bytes per tile) */ - port->PortLocation = getFile16BitBE(file); /* yes, big endian */ - -#if 0 - { - int port_x = (port->PortLocation / 2) % SP_PLAYFIELD_WIDTH; - int port_y = (port->PortLocation / 2) / SP_PLAYFIELD_WIDTH; + port->PortLocation = getFile16BitBE(file); // yes, big endian - printf("::: %d: port_location == %d => (%d, %d)\n", - i, port->PortLocation, port_x, port_y); - } -#endif - - /* change gravity: 1 == "turn on", anything else (0) == "turn off" */ + // change gravity: 1 == "turn on", anything else (0) == "turn off" port->Gravity = getFile8Bit(file); - /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */ + // "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" port->FreezeZonks = getFile8Bit(file); - /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */ + // "freeze enemies": 1 == "turn on", anything else (0) == "turn off" port->FreezeEnemies = getFile8Bit(file); - ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */ + ReadUnusedBytesFromFile(file, 1); // (not used by Supaplex engine) } - /* SpeedByte XOR Highbyte(RandomSeed) */ + // SpeedByte XOR Highbyte(RandomSeed) header->SpeedByte = getFile8Bit(file); - /* CheckSum XOR SpeedByte */ + // CheckSum XOR SpeedByte header->CheckSumByte = getFile8Bit(file); - /* random seed used for recorded demos */ - header->DemoRandomSeed = getFile16BitLE(file); /* yes, little endian */ - // header->DemoRandomSeed = getFile16BitBE(file); /* !!! TEST ONLY !!! */ - -#if 0 - printf("::: file.c: DemoRandomSeed == %d\n", header->DemoRandomSeed); -#endif + // random seed used for recorded demos + header->DemoRandomSeed = getFile16BitLE(file); // yes, little endian - /* auto-determine number of infotrons if it was stored as "0" -- see above */ + // auto-determine number of infotrons if it was stored as "0" -- see above if (header->InfotronsNeeded == 0) { for (x = 0; x < native_sp_level.width; x++) @@ -372,47 +228,48 @@ static void LoadNativeLevelFromFileStream_SP(FILE *file, int width, int height, if (native_sp_level.playfield[x][y] == fiInfotron) header->InfotronsNeeded++; - header->InfotronsNeeded &= 0xff; /* only use low byte -- see above */ + header->InfotronsNeeded &= 0xff; // only use low byte -- see above } - /* read raw level header bytes (96 bytes) */ + // read raw level header bytes (96 bytes) - fseek(file, -(SP_HEADER_SIZE), SEEK_CUR); /* rewind file */ + seekFile(file, -(SP_HEADER_SIZE), SEEK_CUR); // rewind file for (i = 0; i < SP_HEADER_SIZE; i++) - native_sp_level.header_raw_bytes[i] = fgetc(file); + native_sp_level.header_raw_bytes[i] = getByteFromFile(file); - /* also load demo tape, if available (only in single level files) */ + // also load demo tape, if available (only in single level files) if (demo_available) { int level_nr = getFile8Bit(file); - level_nr &= 0x7f; /* clear highest bit */ + level_nr &= 0x7f; // clear highest bit level_nr = (level_nr < 1 ? 1 : level_nr > 111 ? 111 : level_nr); native_sp_level.demo.level_nr = level_nr; - for (i = 0; i < SP_MAX_TAPE_LEN && !feof(file); i++) + for (i = 0; i < SP_MAX_TAPE_LEN && !checkEndOfFile(file); i++) { native_sp_level.demo.data[i] = getFile8Bit(file); - if (native_sp_level.demo.data[i] == 0xff) /* "end of demo" byte */ - { - i++; - + if (native_sp_level.demo.data[i] == 0xff) // "end of demo" byte break; - } } + if (i >= SP_MAX_TAPE_LEN) + Error(ERR_WARN, "SP demo truncated: size exceeds maximum SP demo size %d", + SP_MAX_TAPE_LEN); + native_sp_level.demo.length = i; native_sp_level.demo.is_available = (native_sp_level.demo.length > 0); } } -boolean LoadNativeLevel_SP(char *filename, int level_pos) +boolean LoadNativeLevel_SP(char *filename, int level_pos, + boolean level_info_only) { - FILE *file; + File *file; int i, l, x, y; char name_first, name_last; struct LevelInfo_SP multipart_level; @@ -430,13 +287,14 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) int level_width = SP_STD_PLAYFIELD_WIDTH; int level_height = SP_STD_PLAYFIELD_HEIGHT; - /* always start with reliable default values */ + // always start with reliable default values setLevelInfoToDefaults_SP(); copyInternalEngineVars_SP(); - if (!(file = fopen(filename, MODE_READ))) + if (!(file = openFile(filename, MODE_READ))) { - Error(ERR_WARN, "cannot open file '%s' -- using empty level", filename); + if (!level_info_only) + Error(ERR_WARN, "cannot open file '%s' -- using empty level", filename); return FALSE; } @@ -494,7 +352,7 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) ldesc->Width = getFile16BitLE(file); ldesc->Height = getFile16BitLE(file); - ldesc->OffSet = getFile32BitLE(file); /* starts with 1, not with 0 */ + ldesc->OffSet = getFile32BitLE(file); // starts with 1, not with 0 ldesc->Size = getFile32BitLE(file); } @@ -504,8 +362,8 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) file_seek_pos = mpx_level_desc[level_pos].OffSet - 1; } - /* position file stream to the requested level (in case of level package) */ - if (fseek(file, file_seek_pos, SEEK_SET) != 0) + // position file stream to the requested level (in case of level package) + if (seekFile(file, file_seek_pos, SEEK_SET) != 0) { Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename); @@ -525,7 +383,7 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) LoadNativeLevelFromFileStream_SP(file, level_width, level_height, demo_available); - /* check if this level is a part of a bigger multi-part level */ + // check if this level is a part of a bigger multi-part level if (is_single_level_file) break; @@ -543,20 +401,20 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) if (is_multipart_level) { - /* correct leading multipart level meta information in level name */ + // correct leading multipart level meta information in level name for (i = 0; i < SP_LEVEL_NAME_LEN && header->LevelTitle[i] == name_first; i++) header->LevelTitle[i] = '-'; - /* correct trailing multipart level meta information in level name */ + // correct trailing multipart level meta information in level name for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && header->LevelTitle[i] == name_last; i--) header->LevelTitle[i] = '-'; } - /* ---------- check for normal single level ---------- */ + // ---------- check for normal single level ---------- if (!reading_multipart_level && !is_multipart_level) { @@ -566,7 +424,7 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) break; } - /* ---------- check for empty level (unused multi-part) ---------- */ + // ---------- check for empty level (unused multi-part) ---------- if (!reading_multipart_level && is_multipart_level && !is_first_part) { @@ -579,7 +437,7 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) break; } - /* ---------- check for finished multi-part level ---------- */ + // ---------- check for finished multi-part level ---------- if (reading_multipart_level && (!is_multipart_level || @@ -593,16 +451,16 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) break; } - /* ---------- here we have one part of a multi-part level ---------- */ + // ---------- here we have one part of a multi-part level ---------- reading_multipart_level = TRUE; - if (is_first_part) /* start with first part of new multi-part level */ + if (is_first_part) // start with first part of new multi-part level { - /* copy level info structure from first part */ + // copy level info structure from first part multipart_level = native_sp_level; - /* clear playfield of new multi-part level */ + // clear playfield of new multi-part level for (x = 0; x < SP_MAX_PLAYFIELD_WIDTH; x++) for (y = 0; y < SP_MAX_PLAYFIELD_HEIGHT; y++) multipart_level.playfield[x][y] = fiSpace; @@ -616,11 +474,6 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) multipart_xpos = (int)(name_first - '0'); multipart_ypos = (int)(name_last - '0'); -#if 0 - printf("----------> part (%d/%d) of multi-part level '%s'\n", - multipart_xpos, multipart_ypos, multipart_level.header.LevelTitle); -#endif - if (multipart_xpos * SP_STD_PLAYFIELD_WIDTH > SP_MAX_PLAYFIELD_WIDTH || multipart_ypos * SP_STD_PLAYFIELD_HEIGHT > SP_MAX_PLAYFIELD_HEIGHT) { @@ -634,7 +487,7 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) multipart_level.height = MAX(multipart_level.height, multipart_ypos * SP_STD_PLAYFIELD_HEIGHT); - /* copy level part at the right position of multi-part level */ + // copy level part at the right position of multi-part level for (x = 0; x < SP_STD_PLAYFIELD_WIDTH; x++) { for (y = 0; y < SP_STD_PLAYFIELD_HEIGHT; y++) @@ -648,7 +501,7 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) } } - fclose(file); + closeFile(file); if (use_empty_level) { @@ -678,12 +531,12 @@ void SaveNativeLevel_SP(char *filename) return; } - /* write level playfield (width * height == 60 * 24 tiles == 1440 bytes) */ + // write level playfield (width * height == 60 * 24 tiles == 1440 bytes) for (y = 0; y < native_sp_level.height; y++) for (x = 0; x < native_sp_level.width; x++) putFile8Bit(file, native_sp_level.playfield[x][y]); - /* write level header (96 bytes) */ + // write level header (96 bytes) WriteUnusedBytesToFile(file, 4); @@ -713,7 +566,7 @@ void SaveNativeLevel_SP(char *filename) putFile8Bit(file, header->CheckSumByte); putFile16BitLE(file, header->DemoRandomSeed); - /* also save demo tape, if available */ + // also save demo tape, if available if (native_sp_level.demo.is_available) { @@ -721,6 +574,8 @@ void SaveNativeLevel_SP(char *filename) for (i = 0; i < native_sp_level.demo.length; i++) putFile8Bit(file, native_sp_level.demo.data[i]); + + putFile8Bit(file, 0xff); // "end of demo" byte } fclose(file);