X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Ffiles.c;h=d28c1f48279502d5dcfa181d4f271e802c212ef0;hb=c020bbb83dd5b52a353fa867d7746a43eb5e6ffc;hp=46671d4631c18ca86423f370eb81c7a3a01dbc70;hpb=6ea6dac4068909e5adc06102dd215539c64afef8;p=rocksndiamonds.git diff --git a/src/files.c b/src/files.c index 46671d46..d28c1f48 100644 --- a/src/files.c +++ b/src/files.c @@ -22,7 +22,55 @@ #include "tape.h" #include "joystick.h" -#define MAX_LINE_LEN 1000 +#define MAX_FILENAME_LEN 256 /* maximal filename length */ +#define MAX_LINE_LEN 1000 /* maximal input line length */ +#define CHUNK_ID_LEN 4 /* IFF style chunk id length */ +#define LEVEL_HEADER_SIZE 80 /* size of level file header */ +#define LEVEL_HEADER_UNUSED 18 /* unused level header bytes */ +#define TAPE_HEADER_SIZE 20 /* size of tape file header */ +#define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */ +#define FILE_VERSION_1_0 10 /* old 1.0 file version */ +#define FILE_VERSION_1_2 12 /* actual file version */ + +/* file identifier strings */ +#define LEVEL_COOKIE "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2" +#define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2" +#define TAPE_COOKIE "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2" +#define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2" +#define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2" +#define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2" +/* old file identifiers for backward compatibility */ +#define LEVEL_COOKIE_10 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0" +#define TAPE_COOKIE_10 "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0" + +/* file names and filename extensions */ +#ifndef MSDOS +#define USERDATA_DIRECTORY ".rocksndiamonds" +#define SETUP_FILENAME "setup.conf" +#define LEVELSETUP_FILENAME "levelsetup.conf" +#define LEVELINFO_FILENAME "levelinfo.conf" +#define LEVELFILE_EXTENSION "level" +#define TAPEFILE_EXTENSION "tape" +#define SCOREFILE_EXTENSION "score" +#else +#define USERDATA_DIRECTORY "userdata" +#define SETUP_FILENAME "setup.cnf" +#define LEVELSETUP_FILENAME "lvlsetup.cnf" +#define LEVELINFO_FILENAME "lvlinfo.cnf" +#define LEVELFILE_EXTENSION "lvl" +#define TAPEFILE_EXTENSION "rec" +#define SCOREFILE_EXTENSION "sco" +#endif + +/* file permissions for newly written files */ +#define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH) +#define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH) +#define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH) +#define USERDATA_DIR_MODE (MODE_R_ALL | MODE_X_ALL | S_IWUSR) +#define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL) +#define SCORE_PERMS LEVEL_PERMS +#define TAPE_PERMS LEVEL_PERMS +#define SETUP_PERMS LEVEL_PERMS static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */ static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */ @@ -104,6 +152,52 @@ static char *getScoreDir(char *level_subdir) return score_dir; } +static char *getLevelFilename(int nr) +{ + static char *filename = NULL; + char basename[20 + strlen(LEVELFILE_EXTENSION)]; + + if (filename != NULL) + free(filename); + + sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION); + filename = getPath3((leveldir[leveldir_nr].user_defined ? + getUserLevelDir("") : + options.level_directory), + leveldir[leveldir_nr].filename, + basename); + + return filename; +} + +static char *getTapeFilename(int nr) +{ + static char *filename = NULL; + char basename[20 + strlen(LEVELFILE_EXTENSION)]; + + if (filename != NULL) + free(filename); + + sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION); + filename = getPath2(getTapeDir(leveldir[leveldir_nr].filename), basename); + + return filename; +} + +static char *getScoreFilename(int nr) +{ + static char *filename = NULL; + char basename[20 + strlen(LEVELFILE_EXTENSION)]; + + if (filename != NULL) + free(filename); + + sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION); + filename = getPath2(getScoreDir(leveldir[leveldir_nr].filename), basename); + + return filename; +} + static void createDirectory(char *dir, char *text) { if (access(dir, F_OK) != 0) @@ -118,6 +212,7 @@ static void InitUserDataDirectory() static void InitTapeDirectory(char *level_subdir) { + createDirectory(getUserDataDir(), "user data"); createDirectory(getTapeDir(""), "main tape"); createDirectory(getTapeDir(level_subdir), "level tape"); } @@ -132,6 +227,7 @@ static void InitUserLevelDirectory(char *level_subdir) { if (access(getUserLevelDir(level_subdir), F_OK) != 0) { + createDirectory(getUserDataDir(), "user data"); createDirectory(getUserLevelDir(""), "main user level"); createDirectory(getUserLevelDir(level_subdir), "user level"); @@ -139,112 +235,144 @@ static void InitUserLevelDirectory(char *level_subdir) } } +static void setLevelInfoToDefaults() +{ + int i, x, y; + + lev_fieldx = level.fieldx = STD_LEV_FIELDX; + lev_fieldy = level.fieldy = STD_LEV_FIELDY; + + for(x=0; x 0 && cookie[strlen(cookie) - 1] == '\n') + cookie[strlen(cookie) - 1] = '\0'; + + if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */ + file_version = FILE_VERSION_1_0; + else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */ { - fgets(cookie, LEVEL_COOKIE_LEN, file); - fgetc(file); + Error(ERR_WARN, "wrong file identifier of level file '%s'", filename); + fclose(file); + return; + } - if (strcmp(cookie,LEVEL_COOKIE)) + /* read chunk "HEAD" */ + if (file_version >= FILE_VERSION_1_2) + { + /* first check header chunk identifier and chunk length */ + fgets(chunk, CHUNK_ID_LEN + 1, file); + chunk_length = + (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file); + if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE) { - Error(ERR_WARN, "wrong format of level file '%s'", filename); + Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename); fclose(file); - file = NULL; + return; } } - if (file) - { - lev_fieldx = level.fieldx = fgetc(file); - lev_fieldy = level.fieldy = fgetc(file); - - level.time = (fgetc(file)<<8) | fgetc(file); - level.edelsteine = (fgetc(file)<<8) | fgetc(file); - for(i=0; i= FILE_VERSION_1_2) { - lev_fieldx = level.fieldx = STD_LEV_FIELDX; - lev_fieldy = level.fieldy = STD_LEV_FIELDY; - - level.time = 100; - level.edelsteine = 0; - strcpy(level.name, "Nameless Level"); - for(i=0; i> 24) & 0xff, file); + fputc((chunk_length >> 16) & 0xff, file); + fputc((chunk_length >> 8) & 0xff, file); + fputc((chunk_length >> 0) & 0xff, file); fputc(level.fieldx, file); fputc(level.fieldy, file); @@ -264,7 +401,7 @@ void SaveLevel(int level_nr) for(i=0; i> 24) & 0xff, file); + fputc((chunk_length >> 16) & 0xff, file); + fputc((chunk_length >> 8) & 0xff, file); + fputc((chunk_length >> 0) & 0xff, file); + for(y=0; y 0 && cookie[strlen(cookie) - 1] == '\n') + cookie[strlen(cookie) - 1] = '\0'; + + if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */ + file_version = FILE_VERSION_1_0; + else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */ { - fgets(cookie, LEVELREC_COOKIE_LEN, file); - fgetc(file); - if (!strcmp(cookie, LEVELREC_COOKIE_10)) /* old 1.0 tape format */ - levelrec_10 = TRUE; - else if (strcmp(cookie, LEVELREC_COOKIE)) /* unknown tape format */ + Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename); + fclose(file); + return; + } + + /* read chunk "HEAD" */ + if (file_version >= FILE_VERSION_1_2) + { + /* first check header chunk identifier and chunk length */ + fgets(chunk, CHUNK_ID_LEN + 1, file); + chunk_length = + (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file); + + if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE) { - Error(ERR_WARN, "wrong format of level recording file '%s'", filename); + Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename); fclose(file); - file = NULL; + return; } } - if (!file) - return; - tape.random_seed = (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file); tape.date = @@ -323,6 +483,28 @@ void LoadTape(int level_nr) tape.length = (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file); + /* read header fields that are new since version 1.2 */ + if (file_version >= FILE_VERSION_1_2) + { + byte store_participating_players = fgetc(file); + + for(i=0; i= FILE_VERSION_1_2) { - int j; + /* next check body chunk identifier and chunk length */ + fgets(chunk, CHUNK_ID_LEN + 1, file); + chunk_length = + (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file); + if (strcmp(chunk, "BODY") || + chunk_length != (num_participating_players + 1) * tape.length) + { + Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename); + fclose(file); + return; + } + } + for(i=0; i= MAX_TAPELEN) break; for(j=0; j 0) - { - tape.pos[i].action[j] = MV_NO_MOVING; + tape.pos[i].action[j] = MV_NO_MOVING; + + /* pre-1.2 tapes store data for only one player */ + if (file_version == FILE_VERSION_1_0 && j > 0) continue; - } - tape.pos[i].action[j] = fgetc(file); + + if (tape.player_participates[j]) + tape.pos[i].action[j] = fgetc(file); } tape.pos[i].delay = fgetc(file); - if (levelrec_10) + if (file_version == FILE_VERSION_1_0) { /* eliminate possible diagonal moves in old tapes */ /* this is only for backward compatibility */ @@ -393,98 +591,129 @@ void LoadTape(int level_nr) void SaveTape(int level_nr) { int i; - char filename[MAX_FILENAME_LEN]; + char *filename = getTapeFilename(level_nr); FILE *file; boolean new_tape = TRUE; + byte store_participating_players; + int num_participating_players; + int chunk_length; InitTapeDirectory(leveldir[leveldir_nr].filename); - sprintf(filename, "%s/%d.%s", - getTapeDir(leveldir[leveldir_nr].filename), - level_nr, TAPEFILE_EXTENSION); - /* if a tape still exists, ask to overwrite it */ - if ((file = fopen(filename, "r"))) + if (access(filename, F_OK) == 0) { new_tape = FALSE; - fclose(file); - if (!Request("Replace old tape ?", REQ_ASK)) return; } + /* count number of players and set corresponding bits for compact storage */ + store_participating_players = 0; + num_participating_players = 0; + for(i=0; i> 24) & 0xff,file); - fputc((tape.random_seed >> 16) & 0xff,file); - fputc((tape.random_seed >> 8) & 0xff,file); - fputc((tape.random_seed >> 0) & 0xff,file); + fputc((chunk_length >> 24) & 0xff, file); + fputc((chunk_length >> 16) & 0xff, file); + fputc((chunk_length >> 8) & 0xff, file); + fputc((chunk_length >> 0) & 0xff, file); - fputc((tape.date >> 24) & 0xff,file); - fputc((tape.date >> 16) & 0xff,file); - fputc((tape.date >> 8) & 0xff,file); - fputc((tape.date >> 0) & 0xff,file); + fputc((tape.random_seed >> 24) & 0xff, file); + fputc((tape.random_seed >> 16) & 0xff, file); + fputc((tape.random_seed >> 8) & 0xff, file); + fputc((tape.random_seed >> 0) & 0xff, file); - fputc((tape.length >> 24) & 0xff,file); - fputc((tape.length >> 16) & 0xff,file); - fputc((tape.length >> 8) & 0xff,file); - fputc((tape.length >> 0) & 0xff,file); + fputc((tape.date >> 24) & 0xff, file); + fputc((tape.date >> 16) & 0xff, file); + fputc((tape.date >> 8) & 0xff, file); + fputc((tape.date >> 0) & 0xff, file); + + fputc((tape.length >> 24) & 0xff, file); + fputc((tape.length >> 16) & 0xff, file); + fputc((tape.length >> 8) & 0xff, file); + fputc((tape.length >> 0) & 0xff, file); + + fputc(store_participating_players, file); + + for(i=0; i> 24) & 0xff, file); + fputc((chunk_length >> 16) & 0xff, file); + fputc((chunk_length >> 8) & 0xff, file); + fputc((chunk_length >> 0) & 0xff, file); for(i=0; i 0 && cookie[strlen(cookie) - 1] == '\n') + cookie[strlen(cookie) - 1] = '\0'; if (strcmp(cookie, SCORE_COOKIE) != 0) { - Error(ERR_WARN, "wrong format of score file '%s'", filename); + Error(ERR_WARN, "wrong file identifier of score file '%s'", filename); fclose(file); return; } @@ -514,15 +743,11 @@ void LoadScore(int level_nr) void SaveScore(int level_nr) { int i; - char filename[MAX_FILENAME_LEN]; + char *filename = getScoreFilename(level_nr); FILE *file; InitScoreDirectory(leveldir[leveldir_nr].filename); - sprintf(filename, "%s/%d.%s", - getScoreDir(leveldir[leveldir_nr].filename), - level_nr, SCOREFILE_EXTENSION); - if (!(file = fopen(filename, "w"))) { Error(ERR_WARN, "cannot save score for level %d", level_nr); @@ -804,7 +1029,7 @@ static struct SetupFileList *loadSetupFileList(char *filename) if (!(file = fopen(filename, "r"))) { - Error(ERR_WARN, "cannot open setup/info file '%s'", filename); + Error(ERR_WARN, "cannot open configuration file '%s'", filename); return NULL; } @@ -873,7 +1098,7 @@ static struct SetupFileList *loadSetupFileList(char *filename) freeSetupFileList(setup_file_list); if (first_valid_list_entry == NULL) - Error(ERR_WARN, "setup/info file '%s' is empty", filename); + Error(ERR_WARN, "configuration file '%s' is empty", filename); return first_valid_list_entry; } @@ -888,7 +1113,7 @@ static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list, { if (strcmp(setup_file_list->value, identifier) != 0) { - Error(ERR_WARN, "setup/info file has wrong version"); + Error(ERR_WARN, "configuration file has wrong version"); return; } else @@ -899,7 +1124,7 @@ static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list, checkSetupFileListIdentifier(setup_file_list->next, identifier); else { - Error(ERR_WARN, "setup/info file has no version information"); + Error(ERR_WARN, "configuration file has no version information"); return; } } @@ -1092,7 +1317,10 @@ static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry) int i, current_entry = start_entry; if ((dir = opendir(level_directory)) == NULL) - Error(ERR_EXIT, "cannot read level directory '%s'", level_directory); + { + Error(ERR_WARN, "cannot read level directory '%s'", level_directory); + return current_entry; + } while (current_entry < MAX_LEVDIR_ENTRIES) { @@ -1113,13 +1341,6 @@ static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry) continue; } - if (strlen(dir_entry->d_name) >= MAX_LEVDIR_FILENAME) - { - Error(ERR_WARN, "filename of level directory '%s' too long -- ignoring", - dir_entry->d_name); - continue; - } - filename = getPath2(directory, LEVELINFO_FILENAME); setup_file_list = loadSetupFileList(filename); @@ -1153,8 +1374,8 @@ static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry) closedir(dir); - if (current_entry == start_entry && start_entry != -1) - Error(ERR_EXIT, "cannot find any valid level series in directory '%s'", + if (current_entry == start_entry) + Error(ERR_WARN, "cannot find any valid level series in directory '%s'", level_directory); return current_entry; @@ -1171,6 +1392,10 @@ void LoadLevelInfo() num_leveldirs); num_leveldirs = LoadLevelInfoFromLevelDir(getUserLevelDir(""), num_leveldirs); + + if (num_leveldirs == 0) + Error(ERR_EXIT, "cannot find any valid level series in any directory"); + if (num_leveldirs > 1) qsort(leveldir, num_leveldirs, sizeof(struct LevelDirInfo), compareLevelDirInfoEntries); @@ -1212,7 +1437,7 @@ void LoadSetup() char filename[MAX_FILENAME_LEN]; struct SetupFileList *setup_file_list = NULL; - /* always start with reliable default setup values */ + /* always start with reliable default values */ setSetupInfoToDefaults(&setup); sprintf(filename, "%s/%s", getSetupDir(), SETUP_FILENAME); @@ -1360,8 +1585,7 @@ void LoadLevelSetup() { char filename[MAX_FILENAME_LEN]; - /* always start with reliable default setup values */ - + /* always start with reliable default values */ leveldir_nr = 0; level_nr = 0;