X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Ffiles.c;h=795aeda2befb595fc04508083404fd91e729275f;hb=fb957ec8be35cfc2fb77827c5ae02a8e952ef82d;hp=d7f59a722369cc7e0e82bfa2d46ed293fe8527b8;hpb=2adec83bef3e75ccb9af305cb44693c467c4d665;p=rocksndiamonds.git diff --git a/src/files.c b/src/files.c index d7f59a72..795aeda2 100644 --- a/src/files.c +++ b/src/files.c @@ -20,6 +20,7 @@ #include "init.h" #include "tools.h" #include "tape.h" +#include "config.h" #define ENABLE_UNUSED_CODE 0 /* currently unused functions */ #define ENABLE_HISTORIC_CHUNKS 0 /* only for historic reference */ @@ -1318,6 +1319,8 @@ filetype_id_list[] = { LEVEL_FILE_TYPE_DX, "DX" }, { LEVEL_FILE_TYPE_SB, "SB" }, { LEVEL_FILE_TYPE_DC, "DC" }, + { LEVEL_FILE_TYPE_MM, "MM" }, + { LEVEL_FILE_TYPE_MM, "DF" }, { -1, NULL }, }; @@ -1603,6 +1606,7 @@ static void setLevelInfoToDefaults_Level(struct LevelInfo *level) level->native_em_level = &native_em_level; level->native_sp_level = &native_sp_level; + level->native_mm_level = &native_mm_level; level->file_version = FILE_VERSION_ACTUAL; level->game_version = GAME_VERSION_ACTUAL; @@ -2118,6 +2122,16 @@ static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi) if (fileExists(lfi->filename)) return; } + else if (leveldir_current->level_filetype != NULL) + { + int filetype = getFiletypeFromID(leveldir_current->level_filetype); + + /* check for specified native level file with standard file name */ + setLevelFileInfo_FormatLevelFilename(lfi, filetype, + "%03d.%s", nr, LEVELFILE_EXTENSION); + if (fileExists(lfi->filename)) + return; + } /* check for native Rocks'n'Diamonds level file */ setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND, @@ -3193,6 +3207,9 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level, Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename); + if (!setup.editor.use_template_for_new_levels) + return; + /* if level file not found, try to initialize level data from template */ filename = getGlobalLevelTemplateFilename(); @@ -3660,7 +3677,8 @@ 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; + boolean num_invalid_elements = 0; + int i, j, x, y; level->fieldx = level_sp->width; level->fieldy = level_sp->height; @@ -3673,20 +3691,39 @@ void CopyNativeLevel_SP_to_RND(struct LevelInfo *level) int element_new = getMappedElement(map_element_SP_to_RND(element_old)); if (element_new == EL_UNKNOWN) - Error(ERR_WARN, "invalid element %d at position %d, %d", + { + num_invalid_elements++; + + Error(ERR_DEBUG, "invalid element %d at position %d, %d", element_old, x, y); + } level->field[x][y] = element_new; } } + if (num_invalid_elements > 0) + Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements, + (!options.debug ? " (use '--debug' for more details)" : "")); + for (i = 0; i < MAX_PLAYERS; i++) level->initial_player_gravity[i] = (header->InitialGravity == 1 ? TRUE : FALSE); + /* skip leading spaces */ for (i = 0; i < SP_LEVEL_NAME_LEN; i++) - level->name[i] = header->LevelTitle[i]; - level->name[SP_LEVEL_NAME_LEN] = '\0'; + if (header->LevelTitle[i] != ' ') + break; + + /* copy level title */ + for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++) + level->name[j] = header->LevelTitle[i]; + level->name[j] = '\0'; + + /* cut trailing spaces */ + for (; j > 0; j--) + if (level->name[j - 1] == ' ' && level->name[j] == '\0') + level->name[j - 1] = '\0'; level->gems_needed = header->InfotronsNeeded; @@ -3771,10 +3808,20 @@ static void CopyNativeTape_RND_to_SP(struct LevelInfo *level) level_sp->header.DemoRandomSeed = tape.random_seed; demo->length = 0; + for (i = 0; i < tape.length; i++) { int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]); int demo_repeat = tape.pos[i].delay; + int demo_entries = (demo_repeat + 15) / 16; + + if (demo->length + demo_entries >= SP_MAX_TAPE_LEN) + { + Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d", + SP_MAX_TAPE_LEN); + + break; + } for (j = 0; j < demo_repeat / 16; j++) demo->data[demo->length++] = 0xf0 | demo_action; @@ -3783,8 +3830,6 @@ static void CopyNativeTape_RND_to_SP(struct LevelInfo *level) demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action; } - demo->data[demo->length++] = 0xff; - demo->is_available = TRUE; } @@ -3804,22 +3849,104 @@ static void CopyNativeTape_SP_to_RND(struct LevelInfo *level) return; 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; TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename)); - for (i = 0; i < demo->length - 1; i++) + tape.counter = 0; + tape.pos[tape.counter].delay = 0; + + for (i = 0; i < demo->length; i++) { int demo_action = demo->data[i] & 0x0f; int demo_repeat = (demo->data[i] & 0xf0) >> 4; + int tape_action = map_key_SP_to_RND(demo_action); + int tape_repeat = demo_repeat + 1; + byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 }; + boolean success = 0; + int j; + + for (j = 0; j < tape_repeat; j++) + success = TapeAddAction(action); + + if (!success) + { + Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d", + MAX_TAPE_LEN); - tape.pos[i].action[0] = map_key_SP_to_RND(demo_action); - tape.pos[i].delay = demo_repeat + 1; + break; + } } - tape.length_frames = GetTapeLengthFrames(); - tape.length_seconds = GetTapeLengthSeconds(); + TapeHaltRecording(); +} + + +/* ------------------------------------------------------------------------- */ +/* functions for loading MM level */ +/* ------------------------------------------------------------------------- */ + +void CopyNativeLevel_RND_to_MM(struct LevelInfo *level) +{ + struct LevelInfo_MM *level_mm = level->native_mm_level; + int x, y; + + level_mm->file_version = level->file_version; + level_mm->game_version = level->game_version; + level_mm->encoding_16bit_field = level->encoding_16bit_field; + + level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH); + level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT); + + level_mm->time = level->time; + level_mm->kettles_needed = level->gems_needed; + level_mm->auto_count_kettles = FALSE; + level_mm->laser_red = FALSE; + level_mm->laser_green = FALSE; + level_mm->laser_blue = TRUE; + + strcpy(level_mm->name, level->name); + strcpy(level_mm->author, level->author); + + level_mm->score[SC_PACMAN] = level->score[SC_PACMAN]; + level_mm->score[SC_KEY] = level->score[SC_PACMAN]; + level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS]; + + level_mm->amoeba_speed = level->amoeba_speed; + level_mm->time_fuse = 0; + + for (x = 0; x < level->fieldx; x++) + for (y = 0; y < level->fieldy; y++) + level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]); +} + +void CopyNativeLevel_MM_to_RND(struct LevelInfo *level) +{ + struct LevelInfo_MM *level_mm = level->native_mm_level; + int x, y; + + level->file_version = level_mm->file_version; + level->game_version = level_mm->game_version; + level->encoding_16bit_field = level_mm->encoding_16bit_field; + + level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX); + level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY); + + level->time = level_mm->time; + level->gems_needed = level_mm->kettles_needed; + + strcpy(level->name, level_mm->name); + strcpy(level->author, level_mm->author); + + level->score[SC_PACMAN] = level_mm->score[SC_PACMAN]; + level->score[SC_KEY] = level_mm->score[SC_PACMAN]; + level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS]; + + level->amoeba_speed = level_mm->amoeba_speed; + + for (x = 0; x < level->fieldx; x++) + for (y = 0; y < level->fieldy; y++) + level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]); } @@ -5852,12 +5979,22 @@ static void LoadLevelFromFileInfo_SP(struct LevelInfo *level, level->no_valid_file = TRUE; } +static void LoadLevelFromFileInfo_MM(struct LevelInfo *level, + struct LevelFileInfo *level_file_info, + boolean level_info_only) +{ + if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only)) + 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); + else if (level->game_engine_type == GAME_ENGINE_TYPE_MM) + CopyNativeLevel_RND_to_MM(level); } void CopyNativeLevel_Native_to_RND(struct LevelInfo *level) @@ -5866,6 +6003,8 @@ void CopyNativeLevel_Native_to_RND(struct LevelInfo *level) CopyNativeLevel_EM_to_RND(level); else if (level->game_engine_type == GAME_ENGINE_TYPE_SP) CopyNativeLevel_SP_to_RND(level); + else if (level->game_engine_type == GAME_ENGINE_TYPE_MM) + CopyNativeLevel_MM_to_RND(level); } void SaveNativeLevel(struct LevelInfo *level) @@ -5910,6 +6049,11 @@ static void LoadLevelFromFileInfo(struct LevelInfo *level, level->game_engine_type = GAME_ENGINE_TYPE_SP; break; + case LEVEL_FILE_TYPE_MM: + LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only); + level->game_engine_type = GAME_ENGINE_TYPE_MM; + break; + case LEVEL_FILE_TYPE_DC: LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only); break; @@ -7062,7 +7206,8 @@ static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element) return chunk_size; } -static void SaveLevelFromFilename(struct LevelInfo *level, char *filename) +static void SaveLevelFromFilename(struct LevelInfo *level, char *filename, + boolean save_as_template) { int chunk_size; int i; @@ -7126,7 +7271,7 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename) } /* if not using template level, check for non-default custom/group elements */ - if (!level->use_custom_template) + if (!level->use_custom_template || save_as_template) { for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) { @@ -7162,14 +7307,14 @@ void SaveLevel(int nr) { char *filename = getDefaultLevelFilename(nr); - SaveLevelFromFilename(&level, filename); + SaveLevelFromFilename(&level, filename, FALSE); } void SaveLevelTemplate() { char *filename = getLocalLevelTemplateFilename(); - SaveLevelFromFilename(&level, filename); + SaveLevelFromFilename(&level, filename, TRUE); } boolean SaveLevelChecked(int nr) @@ -7343,7 +7488,16 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape) for (i = 0; i < tape->length; i++) { if (i >= MAX_TAPE_LEN) + { + Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d", + MAX_TAPE_LEN); + + // tape too large; read and ignore remaining tape data from this chunk + for (;i < tape->length; i++) + ReadUnusedBytesFromFile(file, tape->num_participating_players + 1); + break; + } for (j = 0; j < MAX_PLAYERS; j++) { @@ -7944,41 +8098,42 @@ void SaveScore(int nr) #define SETUP_TOKEN_TEAM_MODE 14 #define SETUP_TOKEN_HANDICAP 15 #define SETUP_TOKEN_SKIP_LEVELS 16 -#define SETUP_TOKEN_TIME_LIMIT 17 -#define SETUP_TOKEN_FULLSCREEN 18 -#define SETUP_TOKEN_WINDOW_SCALING_PERCENT 19 -#define SETUP_TOKEN_WINDOW_SCALING_QUALITY 20 -#define SETUP_TOKEN_SCREEN_RENDERING_MODE 21 -#define SETUP_TOKEN_ASK_ON_ESCAPE 22 -#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 23 -#define SETUP_TOKEN_QUICK_SWITCH 24 -#define SETUP_TOKEN_INPUT_ON_FOCUS 25 -#define SETUP_TOKEN_PREFER_AGA_GRAPHICS 26 -#define SETUP_TOKEN_GAME_FRAME_DELAY 27 -#define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 28 -#define SETUP_TOKEN_SMALL_GAME_GRAPHICS 29 -#define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 30 -#define SETUP_TOKEN_GRAPHICS_SET 31 -#define SETUP_TOKEN_SOUNDS_SET 32 -#define SETUP_TOKEN_MUSIC_SET 33 -#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 34 -#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 35 -#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 36 -#define SETUP_TOKEN_VOLUME_SIMPLE 37 -#define SETUP_TOKEN_VOLUME_LOOPS 38 -#define SETUP_TOKEN_VOLUME_MUSIC 39 -#define SETUP_TOKEN_TOUCH_CONTROL_TYPE 40 -#define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 41 -#define SETUP_TOKEN_TOUCH_DROP_DISTANCE 42 - -#define NUM_GLOBAL_SETUP_TOKENS 43 +#define SETUP_TOKEN_INCREMENT_LEVELS 17 +#define SETUP_TOKEN_TIME_LIMIT 18 +#define SETUP_TOKEN_FULLSCREEN 19 +#define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20 +#define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21 +#define SETUP_TOKEN_SCREEN_RENDERING_MODE 22 +#define SETUP_TOKEN_ASK_ON_ESCAPE 23 +#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24 +#define SETUP_TOKEN_QUICK_SWITCH 25 +#define SETUP_TOKEN_INPUT_ON_FOCUS 26 +#define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27 +#define SETUP_TOKEN_GAME_FRAME_DELAY 28 +#define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29 +#define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30 +#define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31 +#define SETUP_TOKEN_GRAPHICS_SET 32 +#define SETUP_TOKEN_SOUNDS_SET 33 +#define SETUP_TOKEN_MUSIC_SET 34 +#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35 +#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36 +#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37 +#define SETUP_TOKEN_VOLUME_SIMPLE 38 +#define SETUP_TOKEN_VOLUME_LOOPS 39 +#define SETUP_TOKEN_VOLUME_MUSIC 40 +#define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41 +#define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42 +#define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43 + +#define NUM_GLOBAL_SETUP_TOKENS 44 /* editor setup */ -#define SETUP_TOKEN_EDITOR_EL_CHARS 0 -#define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS 1 -#define SETUP_TOKEN_EDITOR_EL_CUSTOM 2 -#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 3 -#define SETUP_TOKEN_EDITOR_EL_DYNAMIC 4 +#define SETUP_TOKEN_EDITOR_EL_CLASSIC 0 +#define SETUP_TOKEN_EDITOR_EL_CUSTOM 1 +#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2 +#define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3 +#define SETUP_TOKEN_EDITOR_EL_HEADLINES 4 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5 #define NUM_EDITOR_SETUP_TOKENS 6 @@ -8056,24 +8211,26 @@ void SaveScore(int nr) /* internal setup */ #define SETUP_TOKEN_INT_PROGRAM_TITLE 0 -#define SETUP_TOKEN_INT_PROGRAM_AUTHOR 1 -#define SETUP_TOKEN_INT_PROGRAM_EMAIL 2 -#define SETUP_TOKEN_INT_PROGRAM_WEBSITE 3 -#define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 4 -#define SETUP_TOKEN_INT_PROGRAM_COMPANY 5 -#define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 6 -#define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 7 -#define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 8 -#define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 9 -#define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 10 -#define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 11 -#define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 12 -#define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 13 -#define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 14 -#define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 15 -#define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 16 - -#define NUM_INTERNAL_SETUP_TOKENS 17 +#define SETUP_TOKEN_INT_PROGRAM_VERSION 1 +#define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2 +#define SETUP_TOKEN_INT_PROGRAM_EMAIL 3 +#define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4 +#define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5 +#define SETUP_TOKEN_INT_PROGRAM_COMPANY 6 +#define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7 +#define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8 +#define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9 +#define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10 +#define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11 +#define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12 +#define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13 +#define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14 +#define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15 +#define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16 +#define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17 +#define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18 + +#define NUM_INTERNAL_SETUP_TOKENS 19 /* debug setup */ #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0 @@ -8098,8 +8255,9 @@ void SaveScore(int nr) #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21 +#define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22 -#define NUM_DEBUG_SETUP_TOKENS 22 +#define NUM_DEBUG_SETUP_TOKENS 23 /* options setup */ #define SETUP_TOKEN_OPTIONS_VERBOSE 0 @@ -8136,6 +8294,7 @@ static struct TokenInfo global_setup_tokens[] = { TYPE_SWITCH, &si.team_mode, "team_mode" }, { TYPE_SWITCH, &si.handicap, "handicap" }, { TYPE_SWITCH, &si.skip_levels, "skip_levels" }, + { TYPE_SWITCH, &si.increment_levels, "increment_levels" }, { TYPE_SWITCH, &si.time_limit, "time_limit" }, { TYPE_SWITCH, &si.fullscreen, "fullscreen" }, { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" }, @@ -8166,11 +8325,11 @@ static struct TokenInfo global_setup_tokens[] = static struct TokenInfo editor_setup_tokens[] = { - { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" }, - { TYPE_SWITCH, &sei.el_steel_chars, "editor.el_steel_chars" }, + { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" }, { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" }, { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" }, { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" }, + { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" }, { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" }, }; @@ -8184,6 +8343,8 @@ static struct TokenInfo editor_cascade_setup_tokens[] = { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" }, { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" }, { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" }, + { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" }, + { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" }, { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" }, { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" }, { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" }, @@ -8248,6 +8409,7 @@ static struct TokenInfo system_setup_tokens[] = static struct TokenInfo internal_setup_tokens[] = { { TYPE_STRING, &sxi.program_title, "program_title" }, + { TYPE_STRING, &sxi.program_version, "program_version" }, { TYPE_STRING, &sxi.program_author, "program_author" }, { TYPE_STRING, &sxi.program_email, "program_email" }, { TYPE_STRING, &sxi.program_website, "program_website" }, @@ -8262,6 +8424,7 @@ static struct TokenInfo internal_setup_tokens[] = { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" }, { TYPE_STRING, &sxi.default_level_series, "default_level_series" }, { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" }, + { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" }, { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" }, { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" }, }; @@ -8290,6 +8453,7 @@ static struct TokenInfo debug_setup_tokens[] = { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" }, { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"}, { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" }, + { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" }, }; static struct TokenInfo options_setup_tokens[] = @@ -8334,6 +8498,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) si->team_mode = FALSE; si->handicap = TRUE; si->skip_levels = TRUE; + si->increment_levels = TRUE; si->time_limit = TRUE; si->fullscreen = FALSE; si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT; @@ -8373,15 +8538,24 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) si->editor.el_supaplex = TRUE; si->editor.el_diamond_caves = TRUE; si->editor.el_dx_boulderdash = TRUE; + + si->editor.el_mirror_magic = TRUE; + si->editor.el_deflektor = TRUE; + si->editor.el_chars = TRUE; si->editor.el_steel_chars = TRUE; + + si->editor.el_classic = TRUE; si->editor.el_custom = TRUE; - si->editor.el_headlines = TRUE; - si->editor.el_user_defined = FALSE; - si->editor.el_dynamic = TRUE; + si->editor.el_user_defined = FALSE; + si->editor.el_dynamic = TRUE; + + si->editor.el_headlines = TRUE; + + si->editor.show_element_token = FALSE; - si->editor.show_element_token = FALSE; + si->editor.use_template_for_new_levels = TRUE; si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME; si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME; @@ -8434,6 +8608,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE; si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING); + si->internal.program_version = getStringCopy(getProgramRealVersionString()); si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING); si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING); si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING); @@ -8452,6 +8627,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET); si->internal.choose_from_top_leveldir = FALSE; + si->internal.show_scaling_in_title = TRUE; si->internal.default_window_width = WIN_XSIZE_DEFAULT; si->internal.default_window_height = WIN_YSIZE_DEFAULT; @@ -8481,6 +8657,8 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY; si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY; + si->debug.show_frames_per_second = FALSE; + si->options.verbose = FALSE; #if defined(PLATFORM_ANDROID) @@ -8499,6 +8677,9 @@ static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si) si->editor_cascade.el_dc = TRUE; si->editor_cascade.el_dx = TRUE; + si->editor_cascade.el_mm = TRUE; + si->editor_cascade.el_df = TRUE; + si->editor_cascade.el_chars = FALSE; si->editor_cascade.el_steel_chars = FALSE; si->editor_cascade.el_ce = FALSE; @@ -8508,6 +8689,60 @@ static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si) si->editor_cascade.el_dynamic = FALSE; } +#define MAX_HIDE_SETUP_TOKEN_SIZE 20 + +static char *getHideSetupToken(void *setup_value) +{ + static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE]; + + if (setup_value != NULL) + snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value); + + return hide_setup_token; +} + +static void setHideSetupEntry(void *setup_value_raw) +{ + /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */ + void *setup_value = setup_value_raw - (void *)&si + (void *)&setup; + + char *hide_setup_token = getHideSetupToken(setup_value); + + if (setup_value != NULL) + setHashEntry(hide_setup_hash, hide_setup_token, ""); +} + +boolean hideSetupEntry(void *setup_value) +{ + char *hide_setup_token = getHideSetupToken(setup_value); + + return (setup_value != NULL && + getHashEntry(hide_setup_hash, hide_setup_token) != NULL); +} + +static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash, + struct TokenInfo *token_info, + int token_nr, char *token_text) +{ + char *token_hide_text = getStringCat2(token_text, ".hide"); + char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text); + + /* set the value of this setup option in the setup option structure */ + setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text)); + + /* check if this setup option should be hidden in the setup menu */ + if (token_hide_value != NULL && get_boolean_from_string(token_hide_value)) + setHideSetupEntry(token_info[token_nr].value); +} + +static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash, + struct TokenInfo *token_info, + int token_nr) +{ + setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr, + token_info[token_nr].text); +} + static void decodeSetupFileHash(SetupFileHash *setup_file_hash) { int i, pnr; @@ -8515,25 +8750,25 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash) if (!setup_file_hash) return; + if (hide_setup_hash == NULL) + hide_setup_hash = newSetupFileHash(); + /* global setup */ si = setup; for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++) - setSetupInfo(global_setup_tokens, i, - getHashEntry(setup_file_hash, global_setup_tokens[i].text)); + setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i); setup = si; /* editor setup */ sei = setup.editor; for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++) - setSetupInfo(editor_setup_tokens, i, - getHashEntry(setup_file_hash,editor_setup_tokens[i].text)); + setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i); setup.editor = sei; /* shortcut setup */ ssi = setup.shortcut; for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++) - setSetupInfo(shortcut_setup_tokens, i, - getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text)); + setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i); setup.shortcut = ssi; /* player setup */ @@ -8549,8 +8784,8 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash) char full_token[100]; sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text); - setSetupInfo(player_setup_tokens, i, - getHashEntry(setup_file_hash, full_token)); + setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i, + full_token); } setup.input[pnr] = sii; } @@ -8558,29 +8793,25 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash) /* system setup */ syi = setup.system; for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++) - setSetupInfo(system_setup_tokens, i, - getHashEntry(setup_file_hash, system_setup_tokens[i].text)); + setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i); setup.system = syi; /* internal setup */ sxi = setup.internal; for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++) - setSetupInfo(internal_setup_tokens, i, - getHashEntry(setup_file_hash, internal_setup_tokens[i].text)); + setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i); setup.internal = sxi; /* debug setup */ sdi = setup.debug; for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++) - setSetupInfo(debug_setup_tokens, i, - getHashEntry(setup_file_hash, debug_setup_tokens[i].text)); + setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i); setup.debug = sdi; /* options setup */ soi = setup.options; for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++) - setSetupInfo(options_setup_tokens, i, - getHashEntry(setup_file_hash, options_setup_tokens[i].text)); + setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i); setup.options = soi; } @@ -8612,7 +8843,7 @@ void LoadSetupFromFilename(char *filename) } else { - Error(ERR_WARN, "using default setup values"); + Error(ERR_DEBUG, "using default setup values"); } } @@ -8678,6 +8909,55 @@ void LoadSetup_EditorCascade() free(filename); } +static void addGameControllerMappingToHash(SetupFileHash *mappings_hash, + char *mapping_line) +{ + char mapping_guid[MAX_LINE_LEN]; + char *mapping_start, *mapping_end; + + // get GUID from game controller mapping line: copy complete line + strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1); + mapping_guid[MAX_LINE_LEN - 1] = '\0'; + + // get GUID from game controller mapping line: cut after GUID part + mapping_start = strchr(mapping_guid, ','); + if (mapping_start != NULL) + *mapping_start = '\0'; + + // cut newline from game controller mapping line + mapping_end = strchr(mapping_line, '\n'); + if (mapping_end != NULL) + *mapping_end = '\0'; + + // add mapping entry to game controller mappings hash + setHashEntry(mappings_hash, mapping_guid, mapping_line); +} + +static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash, + char *filename) +{ + FILE *file; + + if (!(file = fopen(filename, MODE_READ))) + { + Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename); + + return; + } + + while (!feof(file)) + { + char line[MAX_LINE_LEN]; + + if (!fgets(line, MAX_LINE_LEN, file)) + break; + + addGameControllerMappingToHash(mappings_hash, line); + } + + fclose(file); +} + void SaveSetup() { char *filename = getSetupFilename(); @@ -8787,6 +9067,47 @@ void SaveSetup_EditorCascade() free(filename); } +static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash, + char *filename) +{ + FILE *file; + + if (!(file = fopen(filename, MODE_WRITE))) + { + Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename); + + return; + } + + BEGIN_HASH_ITERATION(mappings_hash, itr) + { + fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr)); + } + END_HASH_ITERATION(mappings_hash, itr) + + fclose(file); +} + +void SaveSetup_AddGameControllerMapping(char *mapping) +{ + char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME); + SetupFileHash *mappings_hash = newSetupFileHash(); + + InitUserDataDirectory(); + + // load existing personal game controller mappings + LoadSetup_ReadGameControllerMappings(mappings_hash, filename); + + // add new mapping to personal game controller mappings + addGameControllerMappingToHash(mappings_hash, mapping); + + // save updated personal game controller mappings + SaveSetup_WriteGameControllerMappings(mappings_hash, filename); + + freeSetupFileHash(mappings_hash); + free(filename); +} + void LoadCustomElementDescriptions() { char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);