X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Ffiles.c;h=a4f7f10a786f31803e77ae160a858778a96e86c9;hb=881bd7909d3a28c07c0bed2185e60a437b407e7c;hp=5cca0e3055b97595d87e418087e9dac35b3be83a;hpb=f5b65dacd63ac8767720f317ed227291a286e306;p=rocksndiamonds.git diff --git a/src/files.c b/src/files.c index 5cca0e30..a4f7f10a 100644 --- a/src/files.c +++ b/src/files.c @@ -61,6 +61,8 @@ #define TAPE_CHUNK_HEAD_UNUSED 1 // unused tape header bytes #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk +#define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk + #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x)) #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE) #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48) @@ -68,7 +70,7 @@ // file identifier strings #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x" #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x" -#define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2" +#define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x" // values for deciding when (not) to save configuration data #define SAVE_CONF_NEVER 0 @@ -264,6 +266,12 @@ static struct LevelFileConfigInfo chunk_config_INFO[] = &li.time_score_base, 1 }, + { + -1, -1, + TYPE_BOOLEAN, CONF_VALUE_8_BIT(13), + &li.rate_time_over_score, FALSE + }, + { -1, -1, -1, -1, @@ -3930,12 +3938,11 @@ static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level) level->time_wheel = 0; level->amoeba_content = EL_EMPTY; -#if 1 - // original Supaplex does not use score values -- use default values -#else + // original Supaplex does not use score values -- rate by playing time for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++) level->score[i] = 0; -#endif + + level->rate_time_over_score = TRUE; // there are no yamyams in supaplex levels for (i = 0; i < level->num_yamyam_contents; i++) @@ -6603,6 +6610,27 @@ static void LoadLevel_InitCustomElements(struct LevelInfo *level) element_info[element].ignition_delay = 8; } } + + // set mouse click change events to work for left/middle/right mouse button + if (level->game_version < VERSION_IDENT(4,2,3,0)) + { + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + struct ElementInfo *ei = &element_info[element]; + + for (j = 0; j < ei->num_change_pages; j++) + { + struct ElementChangeInfo *change = &ei->change_page[j]; + + if (change->has_event[CE_CLICKED_BY_MOUSE] || + change->has_event[CE_PRESSED_BY_MOUSE] || + change->has_event[CE_MOUSE_CLICKED_ON_X] || + change->has_event[CE_MOUSE_PRESSED_ON_X]) + change->trigger_side = CH_SIDE_ANY; + } + } + } } static void LoadLevel_InitElements(struct LevelInfo *level) @@ -8267,13 +8295,10 @@ void SaveTapeToFilename(char *filename) SetFilePermissions(filename, PERMS_PRIVATE); } -void SaveTape(int nr) +static void SaveTapeExt(char *filename) { - char *filename = getTapeFilename(nr); int i; - InitTapeDirectory(leveldir_current->subdir); - tape.file_version = FILE_VERSION_ACTUAL; tape.game_version = GAME_VERSION_ACTUAL; @@ -8289,6 +8314,25 @@ void SaveTape(int nr) tape.changed = FALSE; } +void SaveTape(int nr) +{ + char *filename = getTapeFilename(nr); + + InitTapeDirectory(leveldir_current->subdir); + + SaveTapeExt(filename); +} + +void SaveScoreTape(int nr) +{ + char *filename = getScoreTapeFilename(tape.score_tape_basename, nr); + + // used instead of "leveldir_current->subdir" (for network games) + InitScoreTapeDirectory(levelset.identifier, nr); + + SaveTapeExt(filename); +} + static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved, unsigned int req_state_added) { @@ -8380,7 +8424,20 @@ void DumpTape(struct TapeInfo *tape) // score file functions // ============================================================================ -void LoadScore(int nr) +static void setScoreInfoToDefaults(void) +{ + int i; + + for (i = 0; i < MAX_SCORE_ENTRIES; i++) + { + strcpy(scores.entry[i].tape_basename, UNDEFINED_FILENAME); + strcpy(scores.entry[i].name, EMPTY_PLAYER_NAME); + scores.entry[i].score = 0; + scores.entry[i].time = 0; + } +} + +static void LoadScore_OLD(int nr) { int i; char *filename = getScoreFilename(nr); @@ -8389,13 +8446,6 @@ void LoadScore(int nr) char *line_ptr; FILE *file; - // always start with reliable default values - for (i = 0; i < MAX_SCORE_ENTRIES; i++) - { - strcpy(highscore[i].Name, EMPTY_PLAYER_NAME); - highscore[i].Score = 0; - } - if (!(file = fopen(filename, MODE_READ))) return; @@ -8405,7 +8455,7 @@ void LoadScore(int nr) if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n') cookie[strlen(cookie) - 1] = '\0'; - if (!checkCookieString(cookie, SCORE_COOKIE)) + if (!checkCookieString(cookie, SCORE_COOKIE_TMPL)) { Warn("unknown format of score file '%s'", filename); @@ -8416,7 +8466,7 @@ void LoadScore(int nr) for (i = 0; i < MAX_SCORE_ENTRIES; i++) { - if (fscanf(file, "%d", &highscore[i].Score) == EOF) + if (fscanf(file, "%d", &scores.entry[i].score) == EOF) Warn("fscanf() failed; %s", strerror(errno)); if (fgets(line, MAX_LINE_LEN, file) == NULL) @@ -8429,8 +8479,8 @@ void LoadScore(int nr) { if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0') { - strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN); - highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0'; + strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN); + scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0'; break; } } @@ -8439,7 +8489,241 @@ void LoadScore(int nr) fclose(file); } -void SaveScore(int nr) +static void ConvertScore_OLD(void) +{ + // only convert score to time for levels that rate playing time over score + if (!level.rate_time_over_score) + return; + + // convert old score to playing time for score-less levels (like Supaplex) + int time_final_max = 999; + int i; + + for (i = 0; i < MAX_SCORE_ENTRIES; i++) + { + int score = scores.entry[i].score; + + if (score > 0 && score < time_final_max) + scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND; + } +} + +static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores) +{ + scores->file_version = getFileVersion(file); + scores->game_version = getFileVersion(file); + + return chunk_size; +} + +static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores) +{ + char *level_identifier = NULL; + int level_identifier_size; + int i; + + level_identifier_size = getFile16BitBE(file); + + level_identifier = checked_malloc(level_identifier_size); + + for (i = 0; i < level_identifier_size; i++) + level_identifier[i] = getFile8Bit(file); + + strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN); + scores->level_identifier[MAX_FILENAME_LEN] = '\0'; + + checked_free(level_identifier); + + scores->level_nr = getFile16BitBE(file); + scores->num_entries = getFile16BitBE(file); + + chunk_size = 2 + level_identifier_size + 2 + 2; + + return chunk_size; +} + +static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores) +{ + int i, j; + + for (i = 0; i < scores->num_entries; i++) + { + for (j = 0; j < MAX_PLAYER_NAME_LEN; j++) + scores->entry[i].name[j] = getFile8Bit(file); + + scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0'; + } + + chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN; + + return chunk_size; +} + +static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores) +{ + int i; + + for (i = 0; i < scores->num_entries; i++) + scores->entry[i].score = getFile16BitBE(file); + + chunk_size = scores->num_entries * 2; + + return chunk_size; +} + +static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores) +{ + int i; + + for (i = 0; i < scores->num_entries; i++) + scores->entry[i].time = getFile32BitBE(file); + + chunk_size = scores->num_entries * 4; + + return chunk_size; +} + +static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores) +{ + int i, j; + + for (i = 0; i < scores->num_entries; i++) + { + for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++) + scores->entry[i].tape_basename[j] = getFile8Bit(file); + + scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0'; + } + + chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN; + + return chunk_size; +} + +void LoadScore(int nr) +{ + char *filename = getScoreFilename(nr); + char cookie[MAX_LINE_LEN]; + char chunk_name[CHUNK_ID_LEN + 1]; + int chunk_size; + boolean old_score_file_format = FALSE; + File *file; + + // always start with reliable default values + setScoreInfoToDefaults(); + + if (!(file = openFile(filename, MODE_READ))) + return; + + getFileChunkBE(file, chunk_name, NULL); + if (strEqual(chunk_name, "RND1")) + { + getFile32BitBE(file); // not used + + getFileChunkBE(file, chunk_name, NULL); + if (!strEqual(chunk_name, "SCOR")) + { + Warn("unknown format of score file '%s'", filename); + + closeFile(file); + + return; + } + } + else // check for old file format with cookie string + { + strcpy(cookie, chunk_name); + if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL) + cookie[4] = '\0'; + if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n') + cookie[strlen(cookie) - 1] = '\0'; + + if (!checkCookieString(cookie, SCORE_COOKIE_TMPL)) + { + Warn("unknown format of score file '%s'", filename); + + closeFile(file); + + return; + } + + old_score_file_format = TRUE; + } + + if (old_score_file_format) + { + // score files from versions before 4.2.4.0 without chunk structure + LoadScore_OLD(nr); + + // convert score to time, if possible (mainly for Supaplex levels) + ConvertScore_OLD(); + } + else + { + static struct + { + char *name; + int size; + int (*loader)(File *, int, struct ScoreInfo *); + } + chunk_info[] = + { + { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS }, + { "INFO", -1, LoadScore_INFO }, + { "NAME", -1, LoadScore_NAME }, + { "SCOR", -1, LoadScore_SCOR }, + { "TIME", -1, LoadScore_TIME }, + { "TAPE", -1, LoadScore_TAPE }, + + { NULL, 0, NULL } + }; + + while (getFileChunkBE(file, chunk_name, &chunk_size)) + { + int i = 0; + + while (chunk_info[i].name != NULL && + !strEqual(chunk_name, chunk_info[i].name)) + i++; + + if (chunk_info[i].name == NULL) + { + Warn("unknown chunk '%s' in score file '%s'", + chunk_name, filename); + + ReadUnusedBytesFromFile(file, chunk_size); + } + else if (chunk_info[i].size != -1 && + chunk_info[i].size != chunk_size) + { + Warn("wrong size (%d) of chunk '%s' in score file '%s'", + chunk_size, chunk_name, filename); + + ReadUnusedBytesFromFile(file, chunk_size); + } + else + { + // call function to load this score chunk + int chunk_size_expected = + (chunk_info[i].loader)(file, chunk_size, &scores); + + // the size of some chunks cannot be checked before reading other + // chunks first (like "HEAD" and "BODY") that contain some header + // information, so check them here + if (chunk_size_expected != chunk_size) + { + Warn("wrong size (%d) of chunk '%s' in score file '%s'", + chunk_size, chunk_name, filename); + } + } + } + } + + closeFile(file); +} + +#if ENABLE_HISTORIC_CHUNKS +void SaveScore_OLD(int nr) { int i; int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE); @@ -8459,13 +8743,154 @@ void SaveScore(int nr) fprintf(file, "%s\n\n", SCORE_COOKIE); for (i = 0; i < MAX_SCORE_ENTRIES; i++) - fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name); + fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name); + + fclose(file); + + SetFilePermissions(filename, permissions); +} +#endif + +static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores) +{ + putFileVersion(file, scores->file_version); + putFileVersion(file, scores->game_version); +} + +static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores) +{ + int level_identifier_size = strlen(scores->level_identifier) + 1; + int i; + + putFile16BitBE(file, level_identifier_size); + + for (i = 0; i < level_identifier_size; i++) + putFile8Bit(file, scores->level_identifier[i]); + + putFile16BitBE(file, scores->level_nr); + putFile16BitBE(file, scores->num_entries); +} + +static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores) +{ + int i, j; + + for (i = 0; i < scores->num_entries; i++) + { + int name_size = strlen(scores->entry[i].name); + + for (j = 0; j < MAX_PLAYER_NAME_LEN; j++) + putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0)); + } +} + +static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores) +{ + int i; + + for (i = 0; i < scores->num_entries; i++) + putFile16BitBE(file, scores->entry[i].score); +} + +static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores) +{ + int i; + + for (i = 0; i < scores->num_entries; i++) + putFile32BitBE(file, scores->entry[i].time); +} + +static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores) +{ + int i, j; + + for (i = 0; i < scores->num_entries; i++) + { + int size = strlen(scores->entry[i].tape_basename); + + for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++) + putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0)); + } +} + +static void SaveScoreToFilename(char *filename) +{ + FILE *file; + int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE); + int info_chunk_size; + int name_chunk_size; + int scor_chunk_size; + int time_chunk_size; + int tape_chunk_size; + + if (!(file = fopen(filename, MODE_WRITE))) + { + Warn("cannot save score file '%s'", filename); + + return; + } + + info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2; + name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN; + scor_chunk_size = scores.num_entries * 2; + time_chunk_size = scores.num_entries * 4; + tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN; + + putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED); + putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE); + + putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE); + SaveScore_VERS(file, &scores); + + putFileChunkBE(file, "INFO", info_chunk_size); + SaveScore_INFO(file, &scores); + + putFileChunkBE(file, "NAME", name_chunk_size); + SaveScore_NAME(file, &scores); + + putFileChunkBE(file, "SCOR", scor_chunk_size); + SaveScore_SCOR(file, &scores); + + putFileChunkBE(file, "TIME", time_chunk_size); + SaveScore_TIME(file, &scores); + + putFileChunkBE(file, "TAPE", tape_chunk_size); + SaveScore_TAPE(file, &scores); fclose(file); SetFilePermissions(filename, permissions); } +void SaveScore(int nr) +{ + char *filename = getScoreFilename(nr); + int i; + + // used instead of "leveldir_current->subdir" (for network games) + InitScoreDirectory(levelset.identifier); + + scores.file_version = FILE_VERSION_ACTUAL; + scores.game_version = GAME_VERSION_ACTUAL; + + strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN); + scores.level_identifier[MAX_FILENAME_LEN] = '\0'; + scores.level_nr = level_nr; + + for (i = 0; i < MAX_SCORE_ENTRIES; i++) + if (scores.entry[i].score == 0 && + scores.entry[i].time == 0 && + strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME)) + break; + + scores.num_entries = i; + + if (scores.num_entries == 0) + return; + + SaveScoreToFilename(filename); +} + // ============================================================================ // setup file functions @@ -8604,6 +9029,14 @@ static struct TokenInfo global_setup_tokens[] = TYPE_SWITCH, &setup.ask_on_game_over, "ask_on_game_over" }, + { + TYPE_SWITCH, + &setup.ask_on_quit_game, "ask_on_quit_game" + }, + { + TYPE_SWITCH, + &setup.ask_on_quit_program, "ask_on_quit_program" + }, { TYPE_SWITCH, &setup.quick_switch, "quick_player_switch" @@ -9296,6 +9729,8 @@ static void setSetupInfoToDefaults(struct SetupInfo *si) si->ask_on_escape = TRUE; si->ask_on_escape_editor = TRUE; si->ask_on_game_over = TRUE; + si->ask_on_quit_game = TRUE; + si->ask_on_quit_program = TRUE; si->quick_switch = FALSE; si->input_on_focus = FALSE; si->prefer_aga_graphics = TRUE;