+#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)