X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Ffiles.c;h=497d12c87f8941ee063762c3dda96738adad977c;hp=4fd17038204a1dce81521c71a3f83df4aa0d4735;hb=14801844faf14be284c590b66f030c6bf7cea5c2;hpb=40d5b9f612f42fc1c5bc3cbedefe642e3295acf9 diff --git a/src/files.c b/src/files.c index 4fd17038..497d12c8 100644 --- a/src/files.c +++ b/src/files.c @@ -57,7 +57,7 @@ #define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */ #define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */ -#define TAPE_CHUNK_HEAD_UNUSED 3 /* unused tape header bytes */ +#define TAPE_CHUNK_HEAD_UNUSED 2 /* unused tape header bytes */ #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x)) #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE) @@ -244,6 +244,12 @@ static struct LevelFileConfigInfo chunk_config_INFO[] = &li.auto_exit_sokoban, FALSE }, + { + -1, -1, + TYPE_BOOLEAN, CONF_VALUE_8_BIT(10), + &li.auto_count_gems, FALSE + }, + { -1, -1, -1, -1, @@ -800,16 +806,69 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] = &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS }, - /* ---------- unused values ----------------------------------------------- */ + { + EL_MM_MCDUFFIN, -1, + TYPE_BOOLEAN, CONF_VALUE_8_BIT(1), + &li.mm_laser_red, FALSE + }, + { + EL_MM_MCDUFFIN, -1, + TYPE_BOOLEAN, CONF_VALUE_8_BIT(2), + &li.mm_laser_green, FALSE + }, + { + EL_MM_MCDUFFIN, -1, + TYPE_BOOLEAN, CONF_VALUE_8_BIT(3), + &li.mm_laser_blue, TRUE + }, { - EL_UNKNOWN, SAVE_CONF_NEVER, + EL_DF_LASER, -1, + TYPE_BOOLEAN, CONF_VALUE_8_BIT(1), + &li.df_laser_red, TRUE + }, + { + EL_DF_LASER, -1, + TYPE_BOOLEAN, CONF_VALUE_8_BIT(2), + &li.df_laser_green, TRUE + }, + { + EL_DF_LASER, -1, + TYPE_BOOLEAN, CONF_VALUE_8_BIT(3), + &li.df_laser_blue, FALSE + }, + + { + EL_MM_FUSE_ACTIVE, -1, + TYPE_INTEGER, CONF_VALUE_16_BIT(1), + &li.mm_time_fuse, 25 + }, + { + EL_MM_BOMB, -1, + TYPE_INTEGER, CONF_VALUE_16_BIT(1), + &li.mm_time_bomb, 75 + }, + { + EL_MM_GRAY_BALL, -1, + TYPE_INTEGER, CONF_VALUE_16_BIT(1), + &li.mm_time_ball, 75 + }, + { + EL_MM_STEEL_BLOCK, -1, TYPE_INTEGER, CONF_VALUE_16_BIT(1), - &li.score[SC_UNKNOWN_14], 10 + &li.mm_time_block, 75 }, + { + EL_MM_LIGHTBALL, -1, + TYPE_INTEGER, CONF_VALUE_16_BIT(1), + &li.score[SC_ELEM_BONUS], 10 + }, + + /* ---------- unused values ----------------------------------------------- */ + { EL_UNKNOWN, SAVE_CONF_NEVER, - TYPE_INTEGER, CONF_VALUE_16_BIT(2), + TYPE_INTEGER, CONF_VALUE_16_BIT(1), &li.score[SC_UNKNOWN_15], 10 }, @@ -1319,6 +1378,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 }, }; @@ -1601,9 +1662,11 @@ static void setLevelInfoToDefaults_Level(struct LevelInfo *level) setLevelInfoToDefaults_EM(); setLevelInfoToDefaults_SP(); + setLevelInfoToDefaults_MM(); 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; @@ -1634,6 +1697,9 @@ static void setLevelInfoToDefaults_Level(struct LevelInfo *level) BorderElement = EL_STEELWALL; + /* detect custom elements when loading them */ + level->file_has_custom_elements = FALSE; + /* set all bug compatibility flags to "false" => do not emulate this bug */ level->use_action_after_change_bug = FALSE; @@ -1915,6 +1981,26 @@ static int getFileTypeFromBasename(char *basename) return LEVEL_FILE_TYPE_UNKNOWN; } +static int getFileTypeFromMagicBytes(char *filename, int type) +{ + File *file; + + if ((file = openFile(filename, MODE_READ))) + { + char chunk_name[CHUNK_ID_LEN + 1]; + + getFileChunkBE(file, chunk_name, NULL); + + if (strEqual(chunk_name, "MMII") || + strEqual(chunk_name, "MIRR")) + type = LEVEL_FILE_TYPE_MM; + + closeFile(file); + } + + return type; +} + static boolean checkForPackageFromBasename(char *basename) { /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!! @@ -2119,6 +2205,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, @@ -2171,6 +2267,9 @@ static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi) { if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN) lfi->type = getFileTypeFromBasename(lfi->basename); + + if (lfi->type == LEVEL_FILE_TYPE_RND) + lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type); } static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr) @@ -2545,6 +2644,8 @@ static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level) element_info[element].push_delay_random = 8; } + level->file_has_custom_elements = TRUE; + return chunk_size; } @@ -2571,6 +2672,8 @@ static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level) Error(ERR_WARN, "invalid custom element number %d", element); } + level->file_has_custom_elements = TRUE; + return chunk_size; } @@ -2662,6 +2765,8 @@ static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level) ei->modified_settings = TRUE; } + level->file_has_custom_elements = TRUE; + return chunk_size; } @@ -2810,6 +2915,8 @@ static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level) /* mark this custom element as modified */ ei->modified_settings = TRUE; + level->file_has_custom_elements = TRUE; + return chunk_size; } @@ -2854,6 +2961,8 @@ static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level) /* mark this group element as modified */ element_info[element].modified_settings = TRUE; + level->file_has_custom_elements = TRUE; + return chunk_size; } @@ -3146,6 +3255,8 @@ static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level) break; } + level->file_has_custom_elements = TRUE; + return real_chunk_size; } @@ -3171,6 +3282,8 @@ static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level) *ei = xx_ei; *group = xx_group; + level->file_has_custom_elements = TRUE; + return real_chunk_size; } @@ -3664,7 +3777,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; @@ -3677,20 +3791,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; @@ -3775,10 +3908,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; @@ -3787,8 +3930,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; } @@ -3808,22 +3949,117 @@ 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; - tape.pos[i].action[0] = map_key_SP_to_RND(demo_action); - tape.pos[i].delay = demo_repeat + 1; + 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); + + 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->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 = level->auto_count_gems; + + level_mm->laser_red = level->mm_laser_red; + level_mm->laser_green = level->mm_laser_green; + level_mm->laser_blue = level->mm_laser_blue; + + strcpy(level_mm->name, level->name); + strcpy(level_mm->author, level->author); + + level_mm->score[SC_EMERALD] = level->score[SC_EMERALD]; + level_mm->score[SC_PACMAN] = level->score[SC_PACMAN]; + level_mm->score[SC_KEY] = level->score[SC_KEY]; + level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS]; + level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS]; + + level_mm->amoeba_speed = level->amoeba_speed; + level_mm->time_fuse = level->mm_time_fuse; + level_mm->time_bomb = level->mm_time_bomb; + level_mm->time_ball = level->mm_time_ball; + level_mm->time_block = level->mm_time_block; + + for (x = 0; x < level->fieldx; x++) + for (y = 0; y < level->fieldy; y++) + Ur[x][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->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; + level->auto_count_gems = level_mm->auto_count_kettles; + + level->mm_laser_red = level_mm->laser_red; + level->mm_laser_green = level_mm->laser_green; + level->mm_laser_blue = level_mm->laser_blue; + + strcpy(level->name, level_mm->name); + + /* only overwrite author from 'levelinfo.conf' if author defined in level */ + if (!strEqual(level_mm->author, ANONYMOUS_NAME)) + strcpy(level->author, level_mm->author); + + level->score[SC_EMERALD] = level_mm->score[SC_EMERALD]; + level->score[SC_PACMAN] = level_mm->score[SC_PACMAN]; + level->score[SC_KEY] = level_mm->score[SC_KEY]; + level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS]; + level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS]; + + level->amoeba_speed = level_mm->amoeba_speed; + level->mm_time_fuse = level_mm->time_fuse; + level->mm_time_bomb = level_mm->time_bomb; + level->mm_time_ball = level_mm->time_ball; + level->mm_time_block = level_mm->time_block; + + 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]); } @@ -5856,12 +6092,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) @@ -5870,6 +6116,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) @@ -5914,6 +6162,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; @@ -6125,9 +6378,25 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename) level->em_explodes_by_fire = TRUE; } -static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) +static void LoadLevel_InitStandardElements(struct LevelInfo *level) { - int i, j, x, y; + int i, x, y; + + /* map elements that have changed in newer versions */ + level->amoeba_content = getMappedElementByVersion(level->amoeba_content, + level->game_version); + for (i = 0; i < MAX_ELEMENT_CONTENTS; i++) + for (x = 0; x < 3; x++) + for (y = 0; y < 3; y++) + level->yamyam_content[i].e[x][y] = + getMappedElementByVersion(level->yamyam_content[i].e[x][y], + level->game_version); + +} + +static void LoadLevel_InitCustomElements(struct LevelInfo *level) +{ + int i, j; /* map custom element change events that have changed in newer versions (these following values were accidentally changed in version 3.0.1) @@ -6241,19 +6510,30 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) } } - /* map elements that have changed in newer versions */ - level->amoeba_content = getMappedElementByVersion(level->amoeba_content, - level->game_version); - for (i = 0; i < MAX_ELEMENT_CONTENTS; i++) - for (x = 0; x < 3; x++) - for (y = 0; y < 3; y++) - level->yamyam_content[i].e[x][y] = - getMappedElementByVersion(level->yamyam_content[i].e[x][y], - level->game_version); + /* set some other uninitialized values of custom elements in older levels */ + if (level->game_version < VERSION_IDENT(3,1,0,0)) + { + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + + element_info[element].access_direction = MV_ALL_DIRECTIONS; + + element_info[element].explosion_delay = 17; + element_info[element].ignition_delay = 8; + } + } +} + +static void LoadLevel_InitElements(struct LevelInfo *level, char *filename) +{ + LoadLevel_InitStandardElements(level); + + if (level->file_has_custom_elements) + LoadLevel_InitCustomElements(level); /* initialize element properties for level editor etc. */ InitElementPropertiesEngine(level->game_version); - InitElementPropertiesAfterLoading(level->game_version); InitElementPropertiesGfxElement(); } @@ -7301,6 +7581,8 @@ static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape) } } + tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE); + ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED); engine_version = getFileVersion(file); @@ -7336,8 +7618,9 @@ static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape) static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape) { int i, j; - int chunk_size_expected = - (tape->num_participating_players + 1) * tape->length; + int tape_pos_size = + (tape->use_mouse ? 3 : tape->num_participating_players) + 1; + int chunk_size_expected = tape_pos_size * tape->length; if (chunk_size_expected != chunk_size) { @@ -7348,14 +7631,34 @@ 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++) + if (tape->use_mouse) { - tape->pos[i].action[j] = MV_NONE; + tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file); + tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file); + tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file); - if (tape->player_participates[j]) - tape->pos[i].action[j] = getFile8Bit(file); + tape->pos[i].action[TAPE_ACTION_UNUSED] = 0; + } + else + { + for (j = 0; j < MAX_PLAYERS; j++) + { + tape->pos[i].action[j] = MV_NONE; + + if (tape->player_participates[j]) + tape->pos[i].action[j] = getFile8Bit(file); + } } tape->pos[i].delay = getFile8Bit(file); @@ -7412,7 +7715,7 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape) } if (i != tape->length) - chunk_size = (tape->num_participating_players + 1) * i; + chunk_size = tape_pos_size * i; return chunk_size; } @@ -7681,6 +7984,8 @@ static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape) putFile8Bit(file, store_participating_players); + putFile8Bit(file, (tape->use_mouse ? 1 : 0)); + /* unused bytes not at the end here for 4-byte alignment of engine_version */ WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED); @@ -7706,9 +8011,18 @@ static void SaveTape_BODY(FILE *file, struct TapeInfo *tape) for (i = 0; i < tape->length; i++) { - for (j = 0; j < MAX_PLAYERS; j++) - if (tape->player_participates[j]) - putFile8Bit(file, tape->pos[i].action[j]); + if (tape->use_mouse) + { + putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]); + putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]); + putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]); + } + else + { + for (j = 0; j < MAX_PLAYERS; j++) + if (tape->player_participates[j]) + putFile8Bit(file, tape->pos[i].action[j]); + } putFile8Bit(file, tape->pos[i].delay); } @@ -7719,6 +8033,7 @@ void SaveTape(int nr) char *filename = getTapeFilename(nr); FILE *file; int num_participating_players = 0; + int tape_pos_size; int info_chunk_size; int body_chunk_size; int i; @@ -7739,8 +8054,10 @@ void SaveTape(int nr) if (tape.player_participates[i]) num_participating_players++; + tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1; + info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2; - body_chunk_size = (num_participating_players + 1) * tape.length; + body_chunk_size = tape_pos_size * tape.length; putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED); putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE); @@ -7764,18 +8081,18 @@ void SaveTape(int nr) tape.changed = FALSE; } -boolean SaveTapeChecked(int nr) +static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved) { char *filename = getTapeFilename(nr); boolean new_tape = !fileExists(filename); boolean tape_saved = FALSE; - if (new_tape || Request("Replace old tape?", REQ_ASK)) + if (new_tape || Request(msg_replace, REQ_ASK)) { SaveTape(nr); if (new_tape) - Request("Tape saved!", REQ_CONFIRM); + Request(msg_saved, REQ_CONFIRM); tape_saved = TRUE; } @@ -7783,6 +8100,17 @@ boolean SaveTapeChecked(int nr) return tape_saved; } +boolean SaveTapeChecked(int nr) +{ + return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!"); +} + +boolean SaveTapeChecked_LevelSolved(int nr) +{ + return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?", + "Level solved! Tape saved!"); +} + void DumpTape(struct TapeInfo *tape) { int tape_frame_counter; @@ -7979,6 +8307,11 @@ void SaveScore(int nr) #define NUM_GLOBAL_SETUP_TOKENS 44 +/* auto setup */ +#define SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE 0 + +#define NUM_AUTO_SETUP_TOKENS 1 + /* editor setup */ #define SETUP_TOKEN_EDITOR_EL_CLASSIC 0 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1 @@ -8117,6 +8450,7 @@ void SaveScore(int nr) static struct SetupInfo si; +static struct SetupAutoSetupInfo sasi; static struct SetupEditorInfo sei; static struct SetupEditorCascadeInfo seci; static struct SetupShortcutInfo ssi; @@ -8174,6 +8508,11 @@ static struct TokenInfo global_setup_tokens[] = { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" }, }; +static struct TokenInfo auto_setup_tokens[] = +{ + { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" }, +}; + static struct TokenInfo editor_setup_tokens[] = { { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" }, @@ -8194,6 +8533,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" }, @@ -8387,6 +8728,10 @@ 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; @@ -8511,6 +8856,11 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) #endif } +static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si) +{ + si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE; +} + static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si) { si->editor_cascade.el_bd = TRUE; @@ -8522,6 +8872,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; @@ -8657,6 +9010,22 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash) setup.options = soi; } +static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash) +{ + int i; + + if (!setup_file_hash) + return; + + /* auto setup */ + sasi = setup.auto_setup; + for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++) + setSetupInfo(auto_setup_tokens, i, + getHashEntry(setup_file_hash, + auto_setup_tokens[i].text)); + setup.auto_setup = sasi; +} + static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash) { int i; @@ -8731,6 +9100,26 @@ void LoadSetup() LoadSetup_SpecialPostProcessing(); } +void LoadSetup_AutoSetup() +{ + char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME); + SetupFileHash *setup_file_hash = NULL; + + /* always start with reliable default values */ + setSetupInfoToDefaults_AutoSetup(&setup); + + setup_file_hash = loadSetupFileHash(filename); + + if (setup_file_hash) + { + decodeSetupFileHash_AutoSetup(setup_file_hash); + + freeSetupFileHash(setup_file_hash); + } + + free(filename); +} + void LoadSetup_EditorCascade() { char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME); @@ -8881,6 +9270,34 @@ void SaveSetup() SetFilePermissions(filename, PERMS_PRIVATE); } +void SaveSetup_AutoSetup() +{ + char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME); + FILE *file; + int i; + + InitUserDataDirectory(); + + if (!(file = fopen(filename, MODE_WRITE))) + { + Error(ERR_WARN, "cannot write auto setup file '%s'", filename); + free(filename); + return; + } + + fprintFileHeader(file, AUTOSETUP_FILENAME); + + sasi = setup.auto_setup; + for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++) + fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i)); + + fclose(file); + + SetFilePermissions(filename, PERMS_PRIVATE); + + free(filename); +} + void SaveSetup_EditorCascade() { char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME); @@ -9342,6 +9759,28 @@ static void LoadMenuDesignSettingsFromFilename(char *filename) menu.draw_yoffset_setup[i] = get_integer_from_string(value_2); } + /* special case: initialize with default values that may be overwritten */ + /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */ + for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++) + { + char *value_1 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO"); + char *value_2 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO"); + char *value_3 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO"); + char *value_4 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO"); + char *value_5 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO"); + + if (value_1 != NULL) + menu.paragraph_spacing_info[i] = get_integer_from_string(value_1); + if (value_2 != NULL) + menu.headline1_spacing_info[i] = get_integer_from_string(value_2); + if (value_3 != NULL) + menu.headline2_spacing_info[i] = get_integer_from_string(value_3); + if (value_4 != NULL) + menu.line_spacing_info[i] = get_integer_from_string(value_4); + if (value_5 != NULL) + menu.extra_spacing_info[i] = get_integer_from_string(value_5); + } + /* special case: initialize with default values that may be overwritten */ /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)