/* functions for loading Supaplex level */
/* ------------------------------------------------------------------------- */
+void setTapeInfoToDefaults_SP()
+{
+ native_sp_level.demo.is_available = FALSE;
+ native_sp_level.demo.length = 0;
+}
+
void setLevelInfoToDefaults_SP()
{
LevelInfoType *header = &native_sp_level.header;
char *empty_title = "-------- EMPTY --------";
int i, x, y;
- native_sp_level.width = SP_PLAYFIELD_WIDTH;
- native_sp_level.height = SP_PLAYFIELD_HEIGHT;
+ native_sp_level.game_sp = &game_sp;
+
+ native_sp_level.width = SP_STD_PLAYFIELD_WIDTH;
+ native_sp_level.height = SP_STD_PLAYFIELD_HEIGHT;
for (x = 0; x < native_sp_level.width; x++)
for (y = 0; y < native_sp_level.height; y++)
for (i = 0; i < SP_HEADER_SIZE; i++)
native_sp_level.header_raw_bytes[i] = 0x20;
- native_sp_level.demo.is_available = FALSE;
- native_sp_level.demo.length = 0;
+ setTapeInfoToDefaults_SP();
}
void copyInternalEngineVars_SP()
{
- int i, x, y;
int count;
+ int i, x, y;
LInfo = native_sp_level.header;
FieldMax = (FieldWidth * FieldHeight) + HeaderSize - 1;
LevelMax = (FieldWidth * FieldHeight) - 1;
- FileMax = FieldMax + native_sp_level.demo.length;
+#if 0
+ /* (add one byte for the level number stored as first byte of demo data) */
+ FileMax = FieldMax + native_sp_level.demo.length + 1;
+#endif
+
+#if 0
+ PlayField8 = REDIM_1D(sizeof(byte), 0, FileMax);
+ DisPlayField = REDIM_1D(sizeof(byte), 0, FieldMax);
+ PlayField16 = REDIM_1D(sizeof(int), -game_sp.preceding_buffer_size, FieldMax);
+#endif
+
+ /* initialize preceding playfield buffer */
+ for (i = -game_sp.preceding_buffer_size; i < 0; i++)
+ PlayField16[i] = 0;
- PlayField8 = REDIM_1D(sizeof(byte), 0, FileMax + 1 - 1);
- DisPlayField = REDIM_1D(sizeof(byte), 0, FieldMax + 1 - 1);
- PlayField16 = REDIM_1D(sizeof(int), -FieldWidth, FieldMax);
+ /* initialize preceding playfield buffer */
+ for (i = -SP_MAX_PLAYFIELD_WIDTH; i < 0; i++)
+ PlayField8[i] = 0;
-#if 1
+ count = 0;
+ for (i = 0; game_sp.preceding_buffer[i] != NULL; i++)
+ {
+ 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')
+ {
+ int hi_nibble = s[0] - (s[0] > '9' ? 'a' - 10 : '0');
+ int lo_nibble = s[1] - (s[1] > '9' ? 'a' - 10 : '0');
+ int byte = (hi_nibble << 4) | lo_nibble;
+
+ if (hi_byte)
+ byte <<= 8;
+
+ PlayField16[-game_sp.preceding_buffer_size + count] |= byte;
+
+ if (hi_byte)
+ count++;
+
+ hi_byte = !hi_byte;
+
+ s += 2;
+
+ while (*s == ' ')
+ s++;
+ }
+ }
count = 0;
for (y = 0; y < native_sp_level.height; y++)
PlayField8[i] = 0;
}
-#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;
+#if 0
+ /* !!! NEVER USED !!! */
PlayField8[FieldMax + 1] = native_sp_level.demo.level_nr;
+ /* !!! NEVER USED !!! */
for (i = 0; i < native_sp_level.demo.length; i++)
- PlayField8[FieldMax + i + 2] = native_sp_level.demo.data[i];
+ PlayField8[FieldMax + 2 + i] = native_sp_level.demo.data[i];
+#endif
}
+#if 0
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;
+ TerminalState = REDIM_1D(sizeof(byte), 0, FieldMax);
+#endif
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;
}
-static void LoadNativeLevelFromFileStream_SP(FILE *file, boolean demo_available)
+#if 1
+
+static void LoadNativeLevelFromFileStream_SP(File *file, int width, int height,
+ boolean demo_available)
{
LevelInfoType *header = &native_sp_level.header;
int i, x, y;
/* 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 = SP_PLAYFIELD_WIDTH;
- native_sp_level.height = SP_PLAYFIELD_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);
+ /* (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) */
/* 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) */
for (i = 0; i < SP_MAX_SPECIAL_PORTS; i++)
{
which is 2 bytes per tile) */
port->PortLocation = getFile16BitBE(file); /* yes, big endian */
-#if 0
+ /* 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" */
+ port->FreezeZonks = getFile8Bit(file);
+
+ /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
+ port->FreezeEnemies = getFile8Bit(file);
+
+ ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
+ }
+
+ /* SpeedByte XOR Highbyte(RandomSeed) */
+ header->SpeedByte = getFile8Bit(file);
+
+ /* CheckSum XOR SpeedByte */
+ header->CheckSumByte = getFile8Bit(file);
+
+ /* 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 */
+ if (header->InfotronsNeeded == 0)
+ {
+ for (x = 0; x < native_sp_level.width; x++)
+ for (y = 0; y < native_sp_level.height; y++)
+ if (native_sp_level.playfield[x][y] == fiInfotron)
+ header->InfotronsNeeded++;
+
+ header->InfotronsNeeded &= 0xff; /* only use low byte -- see above */
+ }
+
+ /* read raw level header bytes (96 bytes) */
+
+ seekFile(file, -(SP_HEADER_SIZE), SEEK_CUR); /* rewind file */
+ for (i = 0; i < SP_HEADER_SIZE; i++)
+ native_sp_level.header_raw_bytes[i] = getByteFromFile(file);
+
+ /* 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 = (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 && !checkEndOfFile(file); i++)
{
- int port_x = (port->PortLocation / 2) % SP_PLAYFIELD_WIDTH;
- int port_y = (port->PortLocation / 2) / SP_PLAYFIELD_WIDTH;
+ native_sp_level.demo.data[i] = getFile8Bit(file);
- printf("::: %d: port_location == %d => (%d, %d)\n",
- i, port->PortLocation, port_x, port_y);
+ if (native_sp_level.demo.data[i] == 0xff) /* "end of demo" byte */
+ {
+ i++;
+
+ break;
+ }
}
-#endif
+
+ native_sp_level.demo.length = i;
+ native_sp_level.demo.is_available = (native_sp_level.demo.length > 0);
+ }
+}
+
+#else
+
+static void LoadNativeLevelFromFileStream_SP(FILE *file, int width, int height,
+ boolean demo_available)
+{
+ LevelInfoType *header = &native_sp_level.header;
+ int i, x, y;
+
+ /* 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 = 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) */
+ /* (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) */
+
+ ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
+
+ /* initial gravity: 1 == "on", anything else (0) == "off" */
+ header->InitialGravity = getFile8Bit(file);
+
+ /* SpeedFixVersion XOR 0x20 */
+ header->Version = getFile8Bit(file);
+
+ /* 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" */
+ header->InitialFreezeZonks = getFile8Bit(file);
+
+ /* number of infotrons needed; 0 means that Supaplex will count the total
+ amount of infotrons in the level and use the low byte of that number
+ (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) */
+ header->SpecialPortCount = getFile8Bit(file);
+
+ /* 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];
+
+ /* high and low byte of the location of a special port; if (x, y) are the
+ coordinates of a port in the field and (0, 0) is the top-left corner,
+ 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 */
/* change gravity: 1 == "turn on", anything else (0) == "turn off" */
port->Gravity = 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
/* auto-determine number of infotrons if it was stored as "0" -- see above */
if (header->InfotronsNeeded == 0)
for (i = 0; i < SP_HEADER_SIZE; i++)
native_sp_level.header_raw_bytes[i] = fgetc(file);
- /* also load demo tape, if available */
+ /* also load demo tape, if available (only in single level files) */
if (demo_available)
{
}
}
-boolean LoadNativeLevel_SP(char *filename, int pos)
+#endif
+
+#if 1
+
+boolean LoadNativeLevel_SP(char *filename, int level_pos,
+ boolean level_info_only)
+{
+ File *file;
+ int i, l, x, y;
+ char name_first, name_last;
+ struct LevelInfo_SP multipart_level;
+ int multipart_xpos, multipart_ypos;
+ boolean is_multipart_level;
+ boolean is_first_part;
+ boolean reading_multipart_level = FALSE;
+ boolean use_empty_level = FALSE;
+ LevelInfoType *header = &native_sp_level.header;
+ boolean is_single_level_file = (strSuffixLower(filename, ".sp") ||
+ strSuffixLower(filename, ".mpx"));
+ boolean demo_available = is_single_level_file;
+ boolean is_mpx_file = strSuffixLower(filename, ".mpx");
+ int file_seek_pos = level_pos * SP_STD_LEVEL_SIZE;
+ int level_width = SP_STD_PLAYFIELD_WIDTH;
+ int level_height = SP_STD_PLAYFIELD_HEIGHT;
+
+ /* always start with reliable default values */
+ setLevelInfoToDefaults_SP();
+ copyInternalEngineVars_SP();
+
+ if (!(file = openFile(filename, MODE_READ)))
+ {
+ if (!level_info_only)
+ Error(ERR_WARN, "cannot open file '%s' -- using empty level", filename);
+
+ return FALSE;
+ }
+
+ if (is_mpx_file)
+ {
+ char mpx_chunk_name[4 + 1];
+ int mpx_version;
+ int mpx_level_count;
+ LevelDescriptor *mpx_level_desc;
+
+ getFileChunkBE(file, mpx_chunk_name, NULL);
+
+ if (!strEqual(mpx_chunk_name, "MPX "))
+ {
+ Error(ERR_WARN, "cannot find MPX ID in file '%s' -- using empty level",
+ filename);
+
+ return FALSE;
+ }
+
+ mpx_version = getFile16BitLE(file);
+
+ if (mpx_version != 1)
+ {
+ Error(ERR_WARN, "unknown MPX version in file '%s' -- using empty level",
+ filename);
+
+ return FALSE;
+ }
+
+ mpx_level_count = getFile16BitLE(file);
+
+ if (mpx_level_count < 1)
+ {
+ Error(ERR_WARN, "no MPX levels found in file '%s' -- using empty level",
+ filename);
+
+ return FALSE;
+ }
+
+ if (level_pos >= mpx_level_count)
+ {
+ Error(ERR_WARN, "MPX level not found in file '%s' -- using empty level",
+ filename);
+
+ return FALSE;
+ }
+
+ mpx_level_desc = checked_calloc(mpx_level_count * sizeof(LevelDescriptor));
+
+ for (i = 0; i < mpx_level_count; i++)
+ {
+ LevelDescriptor *ldesc = &mpx_level_desc[i];
+
+ ldesc->Width = getFile16BitLE(file);
+ ldesc->Height = getFile16BitLE(file);
+ ldesc->OffSet = getFile32BitLE(file); /* starts with 1, not with 0 */
+ ldesc->Size = getFile32BitLE(file);
+ }
+
+ level_width = mpx_level_desc[level_pos].Width;
+ level_height = mpx_level_desc[level_pos].Height;
+
+ file_seek_pos = mpx_level_desc[level_pos].OffSet - 1;
+ }
+
+ /* 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);
+
+ return FALSE;
+ }
+
+ /* there exist Supaplex level package files with multi-part levels which
+ can be detected as follows: instead of leading and trailing dashes ('-')
+ to pad the level name, they have leading and trailing numbers which are
+ the x and y coordinations of the current part of the multi-part level;
+ if there are '?' characters instead of numbers on the left or right side
+ of the level name, the multi-part level consists of only horizontal or
+ vertical parts */
+
+ for (l = level_pos; l < SP_NUM_LEVELS_PER_PACKAGE; l++)
+ {
+ LoadNativeLevelFromFileStream_SP(file, level_width, level_height,
+ demo_available);
+
+ /* check if this level is a part of a bigger multi-part level */
+
+ if (is_single_level_file)
+ break;
+
+ name_first = header->LevelTitle[0];
+ name_last = header->LevelTitle[SP_LEVEL_NAME_LEN - 1];
+
+ is_multipart_level =
+ ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
+ (name_last == '?' || (name_last >= '0' && name_last <= '9')));
+
+ is_first_part =
+ ((name_first == '?' || name_first == '1') &&
+ (name_last == '?' || name_last == '1'));
+
+ if (is_multipart_level)
+ {
+ /* 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 */
+ for (i = SP_LEVEL_NAME_LEN - 1;
+ i >= 0 && header->LevelTitle[i] == name_last;
+ i--)
+ header->LevelTitle[i] = '-';
+ }
+
+ /* ---------- check for normal single level ---------- */
+
+ if (!reading_multipart_level && !is_multipart_level)
+ {
+ /* the current level is simply a normal single-part level, and we are
+ not reading a multi-part level yet, so return the level as it is */
+
+ break;
+ }
+
+ /* ---------- check for empty level (unused multi-part) ---------- */
+
+ if (!reading_multipart_level && is_multipart_level && !is_first_part)
+ {
+ /* this is a part of a multi-part level, but not the first part
+ (and we are not already reading parts of a multi-part level);
+ in this case, use an empty level instead of the single part */
+
+ use_empty_level = TRUE;
+
+ break;
+ }
+
+ /* ---------- check for finished multi-part level ---------- */
+
+ if (reading_multipart_level &&
+ (!is_multipart_level ||
+ !strEqualN(header->LevelTitle, multipart_level.header.LevelTitle,
+ SP_LEVEL_NAME_LEN)))
+ {
+ /* we are already reading parts of a multi-part level, but this level is
+ either not a multi-part level, or a part of a different multi-part
+ level; in both cases, the multi-part level seems to be complete */
+
+ break;
+ }
+
+ /* ---------- 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 */
+ {
+ /* copy level info structure from first part */
+ multipart_level = native_sp_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;
+ }
+
+ if (name_first == '?')
+ name_first = '1';
+ if (name_last == '?')
+ name_last = '1';
+
+ 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)
+ {
+ Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
+
+ break;
+ }
+
+ multipart_level.width = MAX(multipart_level.width,
+ multipart_xpos * SP_STD_PLAYFIELD_WIDTH);
+ multipart_level.height = MAX(multipart_level.height,
+ multipart_ypos * SP_STD_PLAYFIELD_HEIGHT);
+
+ /* 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++)
+ {
+ int start_x = (multipart_xpos - 1) * SP_STD_PLAYFIELD_WIDTH;
+ int start_y = (multipart_ypos - 1) * SP_STD_PLAYFIELD_HEIGHT;
+
+ multipart_level.playfield[start_x + x][start_y + y] =
+ native_sp_level.playfield[x][y];
+ }
+ }
+ }
+
+ closeFile(file);
+
+ if (use_empty_level)
+ {
+ setLevelInfoToDefaults_SP();
+
+ Error(ERR_WARN, "single part of multi-part level -- using empty level");
+ }
+
+ if (reading_multipart_level)
+ native_sp_level = multipart_level;
+
+ copyInternalEngineVars_SP();
+
+ return TRUE;
+}
+
+#else
+
+boolean LoadNativeLevel_SP(char *filename, int level_pos,
+ boolean level_info_only)
{
FILE *file;
int i, l, x, y;
boolean reading_multipart_level = FALSE;
boolean use_empty_level = FALSE;
LevelInfoType *header = &native_sp_level.header;
- boolean demo_available = (strSuffix(filename, ".sp") ||
- strSuffix(filename, ".SP"));
+ boolean is_single_level_file = (strSuffixLower(filename, ".sp") ||
+ strSuffixLower(filename, ".mpx"));
+ boolean demo_available = is_single_level_file;
+ boolean is_mpx_file = strSuffixLower(filename, ".mpx");
+ int file_seek_pos = level_pos * SP_STD_LEVEL_SIZE;
+ int level_width = SP_STD_PLAYFIELD_WIDTH;
+ int level_height = SP_STD_PLAYFIELD_HEIGHT;
/* always start with reliable default values */
setLevelInfoToDefaults_SP();
if (!(file = fopen(filename, MODE_READ)))
{
- Error(ERR_WARN, "cannot open level '%s' -- using empty level", filename);
+ if (!level_info_only)
+ Error(ERR_WARN, "cannot open file '%s' -- using empty level", filename);
return FALSE;
}
+ if (is_mpx_file)
+ {
+ char mpx_chunk_name[4 + 1];
+ int mpx_version;
+ int mpx_level_count;
+ LevelDescriptor *mpx_level_desc;
+
+ getFileChunkBE(file, mpx_chunk_name, NULL);
+
+ if (!strEqual(mpx_chunk_name, "MPX "))
+ {
+ Error(ERR_WARN, "cannot find MPX ID in file '%s' -- using empty level",
+ filename);
+
+ return FALSE;
+ }
+
+ mpx_version = getFile16BitLE(file);
+
+ if (mpx_version != 1)
+ {
+ Error(ERR_WARN, "unknown MPX version in file '%s' -- using empty level",
+ filename);
+
+ return FALSE;
+ }
+
+ mpx_level_count = getFile16BitLE(file);
+
+ if (mpx_level_count < 1)
+ {
+ Error(ERR_WARN, "no MPX levels found in file '%s' -- using empty level",
+ filename);
+
+ return FALSE;
+ }
+
+ if (level_pos >= mpx_level_count)
+ {
+ Error(ERR_WARN, "MPX level not found in file '%s' -- using empty level",
+ filename);
+
+ return FALSE;
+ }
+
+ mpx_level_desc = checked_calloc(mpx_level_count * sizeof(LevelDescriptor));
+
+ for (i = 0; i < mpx_level_count; i++)
+ {
+ LevelDescriptor *ldesc = &mpx_level_desc[i];
+
+ ldesc->Width = getFile16BitLE(file);
+ ldesc->Height = getFile16BitLE(file);
+ ldesc->OffSet = getFile32BitLE(file); /* starts with 1, not with 0 */
+ ldesc->Size = getFile32BitLE(file);
+ }
+
+ level_width = mpx_level_desc[level_pos].Width;
+ level_height = mpx_level_desc[level_pos].Height;
+
+ 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, pos * SP_LEVEL_SIZE, SEEK_SET) != 0)
+ if (fseek(file, file_seek_pos, SEEK_SET) != 0)
{
Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
of the level name, the multi-part level consists of only horizontal or
vertical parts */
- for (l = pos; l < SP_NUM_LEVELS_PER_PACKAGE; l++)
+ for (l = level_pos; l < SP_NUM_LEVELS_PER_PACKAGE; l++)
{
- LoadNativeLevelFromFileStream_SP(file, demo_available);
+ LoadNativeLevelFromFileStream_SP(file, level_width, level_height,
+ demo_available);
/* check if this level is a part of a bigger multi-part level */
+ if (is_single_level_file)
+ break;
+
name_first = header->LevelTitle[0];
name_last = header->LevelTitle[SP_LEVEL_NAME_LEN - 1];
((name_first == '?' || name_first == '1') &&
(name_last == '?' || name_last == '1'));
- /* 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 */
- for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && header->LevelTitle[i] == name_last; i--)
- header->LevelTitle[i] = '-';
+ if (is_multipart_level)
+ {
+ /* 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 */
+ for (i = SP_LEVEL_NAME_LEN - 1;
+ i >= 0 && header->LevelTitle[i] == name_last;
+ i--)
+ header->LevelTitle[i] = '-';
+ }
/* ---------- check for normal single level ---------- */
multipart_xpos, multipart_ypos, multipart_level.header.LevelTitle);
#endif
- if (multipart_xpos * SP_PLAYFIELD_WIDTH > SP_MAX_PLAYFIELD_WIDTH ||
- multipart_ypos * SP_PLAYFIELD_HEIGHT > SP_MAX_PLAYFIELD_HEIGHT)
+ if (multipart_xpos * SP_STD_PLAYFIELD_WIDTH > SP_MAX_PLAYFIELD_WIDTH ||
+ multipart_ypos * SP_STD_PLAYFIELD_HEIGHT > SP_MAX_PLAYFIELD_HEIGHT)
{
Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
}
multipart_level.width = MAX(multipart_level.width,
- multipart_xpos * SP_PLAYFIELD_WIDTH);
+ multipart_xpos * SP_STD_PLAYFIELD_WIDTH);
multipart_level.height = MAX(multipart_level.height,
- multipart_ypos * SP_PLAYFIELD_HEIGHT);
+ multipart_ypos * SP_STD_PLAYFIELD_HEIGHT);
/* copy level part at the right position of multi-part level */
- for (x = 0; x < SP_PLAYFIELD_WIDTH; x++)
+ for (x = 0; x < SP_STD_PLAYFIELD_WIDTH; x++)
{
- for (y = 0; y < SP_PLAYFIELD_HEIGHT; y++)
+ for (y = 0; y < SP_STD_PLAYFIELD_HEIGHT; y++)
{
- int start_x = (multipart_xpos - 1) * SP_PLAYFIELD_WIDTH;
- int start_y = (multipart_ypos - 1) * SP_PLAYFIELD_HEIGHT;
+ int start_x = (multipart_xpos - 1) * SP_STD_PLAYFIELD_WIDTH;
+ int start_y = (multipart_ypos - 1) * SP_STD_PLAYFIELD_HEIGHT;
multipart_level.playfield[start_x + x][start_y + y] =
native_sp_level.playfield[x][y];
return TRUE;
}
+
+#endif
+
+void SaveNativeLevel_SP(char *filename)
+{
+ LevelInfoType *header = &native_sp_level.header;
+ FILE *file;
+ int i, x, y;
+
+ if (!(file = fopen(filename, MODE_WRITE)))
+ {
+ Error(ERR_WARN, "cannot save native level file '%s'", filename);
+
+ return;
+ }
+
+ /* 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) */
+
+ WriteUnusedBytesToFile(file, 4);
+
+ putFile8Bit(file, header->InitialGravity);
+ putFile8Bit(file, header->Version);
+
+ for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
+ putFile8Bit(file, header->LevelTitle[i]);
+
+ putFile8Bit(file, header->InitialFreezeZonks);
+ putFile8Bit(file, header->InfotronsNeeded);
+ putFile8Bit(file, header->SpecialPortCount);
+
+ for (i = 0; i < SP_MAX_SPECIAL_PORTS; i++)
+ {
+ SpecialPortType *port = &header->SpecialPort[i];
+
+ putFile16BitBE(file, port->PortLocation);
+ putFile8Bit(file, port->Gravity);
+ putFile8Bit(file, port->FreezeZonks);
+ putFile8Bit(file, port->FreezeEnemies);
+
+ WriteUnusedBytesToFile(file, 1);
+ }
+
+ putFile8Bit(file, header->SpeedByte);
+ putFile8Bit(file, header->CheckSumByte);
+ putFile16BitLE(file, header->DemoRandomSeed);
+
+ /* also save demo tape, if available */
+
+ if (native_sp_level.demo.is_available)
+ {
+ putFile8Bit(file, native_sp_level.demo.level_nr);
+
+ for (i = 0; i < native_sp_level.demo.length; i++)
+ putFile8Bit(file, native_sp_level.demo.data[i]);
+ }
+
+ fclose(file);
+}