*level = li; /* copy temporary buffer back to level data */
setLevelInfoToDefaults_EM();
+ setLevelInfoToDefaults_SP();
level->native_em_level = &native_em_level;
+ level->native_sp_level = &native_sp_level;
level->file_version = FILE_VERSION_ACTUAL;
level->game_version = GAME_VERSION_ACTUAL;
static int getFileTypeFromBasename(char *basename)
{
+ /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
+
static char *filename = NULL;
struct stat file_status;
/* ---------- try to determine file type from filename ---------- */
/* check for typical filename of a Supaplex level package file */
+#if 1
+ if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
+ return LEVEL_FILE_TYPE_SP;
+#else
if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
strncmp(basename, "LEVELS.D", 8) == 0))
return LEVEL_FILE_TYPE_SP;
+#endif
/* check for typical filename of a Diamond Caves II level package file */
- if (strSuffix(basename, ".dc") ||
- strSuffix(basename, ".dc2"))
+ if (strSuffixLower(basename, ".dc") ||
+ strSuffixLower(basename, ".dc2"))
return LEVEL_FILE_TYPE_DC;
/* ---------- try to determine file type from filesize ---------- */
return LEVEL_FILE_TYPE_UNKNOWN;
}
+static boolean checkForPackageFromBasename(char *basename)
+{
+ /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
+ !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
+
+ return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
+}
+
static char *getSingleLevelBasename(int nr)
{
static char basename[MAX_FILENAME_LEN];
setLevelFileInfo_FormatLevelFilename(lfi, filetype,
leveldir_current->level_filename, nr);
+
+ lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
+
if (fileExists(lfi->filename))
return;
}
}
}
-static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
- struct LevelFileInfo *level_file_info)
-{
- if (!LoadNativeLevel_EM(level_file_info->filename))
- level->no_valid_file = TRUE;
-}
-
-void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
-{
- if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
- CopyNativeLevel_RND_to_EM(level);
-}
-
-void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
-{
- if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
- CopyNativeLevel_EM_to_RND(level);
-}
-
/* ------------------------------------------------------------------------- */
/* functions for loading SP level */
/* ------------------------------------------------------------------------- */
+#if 0
+
#define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
#define SP_LEVEL_SIZE 1536
#define SP_LEVEL_XSIZE 60
}
/* position file stream to the requested level inside the level package */
- if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
+ if (level_file_info->packed &&
+ fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
{
level->no_valid_file = TRUE;
*level = multipart_level;
}
+#endif
+
+void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
+{
+ struct LevelInfo_SP *level_sp = level->native_sp_level;
+ LevelInfoType *header = &level_sp->header;
+ int i, x, y;
+
+ level_sp->width = level->fieldx;
+ level_sp->height = level->fieldy;
+
+ for (x = 0; x < level->fieldx; x++)
+ {
+ for (y = 0; y < level->fieldy; y++)
+ {
+ int element_old = level->field[x][y];
+ int element_new;
+
+ if (element_old >= EL_SP_START &&
+ element_old <= EL_SP_END)
+ element_new = element_old - EL_SP_START;
+ else if (element_old == EL_EMPTY_SPACE)
+ element_new = 0x00;
+ else if (element_old == EL_INVISIBLE_WALL)
+ element_new = 0x28;
+ else
+ element_new = 0x20; /* map unknown elements to yellow "hardware" */
+
+ level_sp->playfield[x][y] = element_new;
+ }
+ }
+
+ header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
+
+ for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
+ header->LevelTitle[i] = level->name[i];
+ /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
+
+ header->InfotronsNeeded = level->gems_needed;
+
+ /* !!! ADD SPECIAL PORT DATABASE STUFF !!! */
+}
+
+void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
+{
+ struct LevelInfo_SP *level_sp = level->native_sp_level;
+ LevelInfoType *header = &level_sp->header;
+ int i, x, y;
+
+ level->fieldx = level_sp->width;
+ level->fieldy = level_sp->height;
+
+ for (x = 0; x < level->fieldx; x++)
+ {
+ for (y = 0; y < level->fieldy; y++)
+ {
+ int element_old = level_sp->playfield[x][y];
+ int element_new;
+
+ if (element_old <= 0x27)
+ element_new = getMappedElement(EL_SP_START + element_old);
+ else if (element_old == 0x28)
+ element_new = EL_INVISIBLE_WALL;
+ else
+ {
+ Error(ERR_WARN, "invalid element %d at position %d, %d",
+ element_old, x, y);
+
+ element_new = EL_UNKNOWN;
+ }
+
+ level->field[x][y] = element_new;
+ }
+ }
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ level->initial_player_gravity[i] =
+ (header->InitialGravity == 1 ? TRUE : FALSE);
+
+ for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
+ level->name[i] = header->LevelTitle[i];
+ level->name[SP_LEVEL_NAME_LEN] = '\0';
+
+ level->gems_needed = header->InfotronsNeeded;
+
+ for (i = 0; i < header->SpecialPortCount; i++)
+ {
+ SpecialPortType *port = &header->SpecialPort[i];
+ int port_location = port->PortLocation;
+ int gravity = port->Gravity;
+ int port_x, port_y, port_element;
+
+ port_x = (port_location / 2) % level->fieldx;
+ port_y = (port_location / 2) / level->fieldx;
+
+ if (port_x < 0 || port_x >= level->fieldx ||
+ port_y < 0 || port_y >= level->fieldy)
+ {
+ Error(ERR_WARN, "special port position (%d, %d) out of bounds",
+ port_x, port_y);
+
+ continue;
+ }
+
+ port_element = level->field[port_x][port_y];
+
+ if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
+ port_element > EL_SP_GRAVITY_PORT_UP)
+ {
+ Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
+
+ continue;
+ }
+
+ /* change previous (wrong) gravity inverting special port to either
+ gravity enabling special port or gravity disabling special port */
+ level->field[port_x][port_y] +=
+ (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
+ EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
+ }
+
+ /* change special gravity ports without database entries to normal ports */
+ for (x = 0; x < level->fieldx; x++)
+ for (y = 0; y < level->fieldy; y++)
+ if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
+ level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
+ level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
+
+ level->time = 0; /* no time limit */
+ level->amoeba_speed = 0;
+ level->time_magic_wall = 0;
+ level->time_wheel = 0;
+ level->amoeba_content = EL_EMPTY;
+
+#if 1
+ /* original Supaplex does not use score values -- use default values */
+#else
+ for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
+ level->score[i] = 0;
+#endif
+
+ /* there are no yamyams in supaplex levels */
+ for (i = 0; i < level->num_yamyam_contents; i++)
+ for (x = 0; x < 3; x++)
+ for (y = 0; y < 3; y++)
+ level->yamyam_content[i].e[x][y] = EL_EMPTY;
+}
+
+static void setTapeInfoToDefaults();
+
+static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
+{
+ struct LevelInfo_SP *level_sp = level->native_sp_level;
+ struct DemoInfo_SP *demo = &level_sp->demo;
+ int i;
+
+ /* always start with reliable default values */
+ setTapeInfoToDefaults();
+
+ tape.level_nr = demo->level_nr; /* (currently not used) */
+ tape.length = demo->length - 1; /* without "end of demo" byte */
+ tape.random_seed = level_sp->header.DemoRandomSeed;
+
+ // tape.date = <SET FROM FILE DATE OF *.SP FILE>
+
+ for (i = 0; i < demo->length - 1; i++)
+ {
+ int demo_action = demo->data[i] & 0x0f;
+ int demo_repeat = (demo->data[i] & 0xf0) >> 4;
+
+ tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
+ tape.pos[i].delay = demo_repeat + 1;
+ }
+
+ tape.length_seconds = GetTapeLength();
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* functions for loading DC level */
+/* ------------------------------------------------------------------------- */
#define DC_LEVEL_HEADER_SIZE 344
#endif
+/* ------------------------------------------------------------------------- */
+/* functions for handling native levels */
+/* ------------------------------------------------------------------------- */
+
+static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
+ struct LevelFileInfo *level_file_info)
+{
+ if (!LoadNativeLevel_EM(level_file_info->filename))
+ level->no_valid_file = TRUE;
+}
+
+static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
+ struct LevelFileInfo *level_file_info)
+{
+ int pos = 0;
+
+ /* determine position of requested level inside level package */
+ if (level_file_info->packed)
+ pos = level_file_info->nr - leveldir_current->first_level;
+
+ if (!LoadNativeLevel_SP(level_file_info->filename, pos))
+ level->no_valid_file = TRUE;
+}
+
+void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
+{
+ if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
+ CopyNativeLevel_RND_to_EM(level);
+ else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
+ CopyNativeLevel_RND_to_SP(level);
+}
+
+void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
+{
+ if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
+ CopyNativeLevel_EM_to_RND(level);
+ else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
+ CopyNativeLevel_SP_to_RND(level);
+}
+
+
/* ------------------------------------------------------------------------- */
/* functions for loading generic level */
/* ------------------------------------------------------------------------- */
case LEVEL_FILE_TYPE_SP:
LoadLevelFromFileInfo_SP(level, level_file_info);
-#if 1
level->game_engine_type = GAME_ENGINE_TYPE_SP;
-#endif
break;
case LEVEL_FILE_TYPE_DC:
char *filename = getSolutionTapeFilename(nr);
LoadTapeFromFilename(filename);
+
+#if 1
+ if (TAPE_IS_EMPTY(tape) &&
+ level.game_engine_type == GAME_ENGINE_TYPE_SP &&
+ level.native_sp_level->demo.is_available)
+ CopyNativeTape_SP_to_RND(&level);
+#endif
}
static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)