options.ro_base_directory = RO_BASE_PATH;
options.rw_base_directory = RW_BASE_PATH;
options.level_directory = RO_BASE_PATH "/" LEVELS_DIRECTORY;
+ options.graphics_directory = RO_BASE_PATH "/" GRAPHICS_DIRECTORY;
options.serveronly = FALSE;
options.network = FALSE;
options.verbose = FALSE;
" -d, --display machine:0 X server display\n"
" -b, --basepath directory alternative base directory\n"
" -l, --level directory alternative level directory\n"
- " -s, --serveronly only start network server\n"
+ " -g, --graphics directory alternative graphics directory\n"
" -n, --network network multiplayer game\n"
+ " -s, --serveronly only start network server\n"
" -v, --verbose verbose mode\n"
" --debug display debugging information\n",
program.command_basename);
if (option_arg == next_option)
options_left++;
}
+ else if (strncmp(option, "-graphics", option_len) == 0)
+ {
+ if (option_arg == NULL)
+ Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
+
+ options.graphics_directory = option_arg;
+ if (option_arg == next_option)
+ options_left++;
+ }
else if (strncmp(option, "-network", option_len) == 0)
{
options.network = TRUE;
/* read chunk name */
fgets(chunk_name, chunk_name_length + 1, file);
- /* read chunk size */
- *chunk_size = getFile32BitInteger(file, byte_order);
+ if (chunk_size != NULL)
+ {
+ /* read chunk size */
+ *chunk_size = getFile32BitInteger(file, byte_order);
+ }
return (feof(file) || ferror(file) ? FALSE : TRUE);
}
/* write chunk name */
fputs(chunk_name, file);
- /* write chunk size */
- putFile32BitInteger(file, chunk_size, byte_order);
+ if (chunk_size >= 0)
+ {
+ /* write chunk size */
+ putFile32BitInteger(file, chunk_size, byte_order);
+ }
+}
+
+void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
+{
+ while (bytes--)
+ fgetc(file);
+}
+
+void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
+{
+ while (bytes--)
+ fputc(0, file);
}
#define TRANSLATE_KEYSYM_TO_KEYNAME 0
/* some stuff from "files.c" */
/* ========================================================================= */
+#if defined(PLATFORM_WIN32)
+#ifndef S_IRGRP
+#define S_IRGRP S_IRUSR
+#endif
+#ifndef S_IROTH
+#define S_IROTH S_IRUSR
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP S_IWUSR
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH S_IWUSR
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP S_IXUSR
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH S_IXUSR
+#endif
+#ifndef S_IRWXG
+#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
+#endif
+#ifndef S_ISGID
+#define S_ISGID 0
+#endif
+#endif /* PLATFORM_WIN32 */
+
+/* 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 MODE_W_PRIVATE (S_IWUSR)
+#define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
+#define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
+
+#define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
+#define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
+
+#define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
+#define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
char *getUserDataDir(void)
{
return userdata_dir;
}
-void createDirectory(char *dir, char *text)
+char *getSetupDir()
+{
+ return getUserDataDir();
+}
+
+static mode_t posix_umask(mode_t mask)
+{
+#if defined(PLATFORM_UNIX)
+ return umask(mask);
+#else
+ return 0;
+#endif
+}
+
+static int posix_mkdir(const char *pathname, mode_t mode)
{
- if (access(dir, F_OK) != 0)
#if defined(PLATFORM_WIN32)
- if (mkdir(dir) != 0)
+ return mkdir(pathname);
#else
- if (mkdir(dir, USERDATA_DIR_MODE) != 0)
+ return mkdir(pathname, mode);
#endif
+}
+
+void createDirectory(char *dir, char *text, int permission_class)
+{
+ /* leave "other" permissions in umask untouched, but ensure group parts
+ of USERDATA_DIR_MODE are not masked */
+ mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
+ DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
+ mode_t normal_umask = posix_umask(0);
+ mode_t group_umask = ~(dir_mode & S_IRWXG);
+ posix_umask(normal_umask & group_umask);
+
+ if (access(dir, F_OK) != 0)
+ if (posix_mkdir(dir, dir_mode) != 0)
Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
+
+ posix_umask(normal_umask); /* reset normal umask */
}
void InitUserDataDirectory()
{
- createDirectory(getUserDataDir(), "user data");
+ createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
+}
+
+void SetFilePermissions(char *filename, int permission_class)
+{
+ chmod(filename, (permission_class == PERMS_PRIVATE ?
+ FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
+}
+
+int getFileVersionFromCookieString(const char *cookie)
+{
+ const char *ptr_cookie1, *ptr_cookie2;
+ const char *pattern1 = "_FILE_VERSION_";
+ const char *pattern2 = "?.?";
+ const int len_cookie = strlen(cookie);
+ const int len_pattern1 = strlen(pattern1);
+ const int len_pattern2 = strlen(pattern2);
+ const int len_pattern = len_pattern1 + len_pattern2;
+ int version_major, version_minor;
+
+ if (len_cookie <= len_pattern)
+ return -1;
+
+ ptr_cookie1 = &cookie[len_cookie - len_pattern];
+ ptr_cookie2 = &cookie[len_cookie - len_pattern2];
+
+ if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
+ return -1;
+
+ if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
+ ptr_cookie2[1] != '.' ||
+ ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
+ return -1;
+
+ version_major = ptr_cookie2[0] - '0';
+ version_minor = ptr_cookie2[2] - '0';
+
+ return VERSION_IDENT(version_major, version_minor, 0);
+}
+
+boolean checkCookieString(const char *cookie, const char *template)
+{
+ const char *pattern = "_FILE_VERSION_?.?";
+ const int len_cookie = strlen(cookie);
+ const int len_template = strlen(template);
+ const int len_pattern = strlen(pattern);
+
+ if (len_cookie != len_template)
+ return FALSE;
+
+ if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* ------------------------------------------------------------------------- */
+/* setup file stuff */
+/* ------------------------------------------------------------------------- */
+
+static char *string_tolower(char *s)
+{
+ static char s_lower[100];
+ int i;
+
+ if (strlen(s) >= 100)
+ return s;
+
+ strcpy(s_lower, s);
+
+ for (i=0; i<strlen(s_lower); i++)
+ s_lower[i] = tolower(s_lower[i]);
+
+ return s_lower;
+}
+
+int get_string_integer_value(char *s)
+{
+ static char *number_text[][3] =
+ {
+ { "0", "zero", "null", },
+ { "1", "one", "first" },
+ { "2", "two", "second" },
+ { "3", "three", "third" },
+ { "4", "four", "fourth" },
+ { "5", "five", "fifth" },
+ { "6", "six", "sixth" },
+ { "7", "seven", "seventh" },
+ { "8", "eight", "eighth" },
+ { "9", "nine", "ninth" },
+ { "10", "ten", "tenth" },
+ { "11", "eleven", "eleventh" },
+ { "12", "twelve", "twelfth" },
+ };
+
+ int i, j;
+
+ for (i=0; i<13; i++)
+ for (j=0; j<3; j++)
+ if (strcmp(string_tolower(s), number_text[i][j]) == 0)
+ return i;
+
+ return atoi(s);
+}
+
+boolean get_string_boolean_value(char *s)
+{
+ if (strcmp(string_tolower(s), "true") == 0 ||
+ strcmp(string_tolower(s), "yes") == 0 ||
+ strcmp(string_tolower(s), "on") == 0 ||
+ get_string_integer_value(s) == 1)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+char *getFormattedSetupEntry(char *token, char *value)
+{
+ int i;
+ static char entry[MAX_LINE_LEN];
+
+ sprintf(entry, "%s:", token);
+ for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
+ entry[i] = ' ';
+ entry[i] = '\0';
+
+ strcat(entry, value);
+
+ return entry;
+}
+
+void freeSetupFileList(struct SetupFileList *setup_file_list)
+{
+ if (!setup_file_list)
+ return;
+
+ if (setup_file_list->token)
+ free(setup_file_list->token);
+ if (setup_file_list->value)
+ free(setup_file_list->value);
+ if (setup_file_list->next)
+ freeSetupFileList(setup_file_list->next);
+ free(setup_file_list);
+}
+
+static struct SetupFileList *newSetupFileList(char *token, char *value)
+{
+ struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
+
+ new->token = checked_malloc(strlen(token) + 1);
+ strcpy(new->token, token);
+
+ new->value = checked_malloc(strlen(value) + 1);
+ strcpy(new->value, value);
+
+ new->next = NULL;
+
+ return new;
+}
+
+char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
+{
+ if (!setup_file_list)
+ return NULL;
+
+ if (strcmp(setup_file_list->token, token) == 0)
+ return setup_file_list->value;
+ else
+ return getTokenValue(setup_file_list->next, token);
+}
+
+static void setTokenValue(struct SetupFileList *setup_file_list,
+ char *token, char *value)
+{
+ if (!setup_file_list)
+ return;
+
+ if (strcmp(setup_file_list->token, token) == 0)
+ {
+ free(setup_file_list->value);
+ setup_file_list->value = checked_malloc(strlen(value) + 1);
+ strcpy(setup_file_list->value, value);
+ }
+ else if (setup_file_list->next == NULL)
+ setup_file_list->next = newSetupFileList(token, value);
+ else
+ setTokenValue(setup_file_list->next, token, value);
+}
+
+#ifdef DEBUG
+static void printSetupFileList(struct SetupFileList *setup_file_list)
+{
+ if (!setup_file_list)
+ return;
+
+ printf("token: '%s'\n", setup_file_list->token);
+ printf("value: '%s'\n", setup_file_list->value);
+
+ printSetupFileList(setup_file_list->next);
+}
+#endif
+
+struct SetupFileList *loadSetupFileList(char *filename)
+{
+ int line_len;
+ char line[MAX_LINE_LEN];
+ char *token, *value, *line_ptr;
+ struct SetupFileList *setup_file_list = newSetupFileList("", "");
+ struct SetupFileList *first_valid_list_entry;
+
+ FILE *file;
+
+ if (!(file = fopen(filename, MODE_READ)))
+ {
+ Error(ERR_WARN, "cannot open configuration file '%s'", filename);
+ return NULL;
+ }
+
+ while(!feof(file))
+ {
+ /* read next line of input file */
+ if (!fgets(line, MAX_LINE_LEN, file))
+ break;
+
+ /* cut trailing comment or whitespace from input line */
+ for (line_ptr = line; *line_ptr; line_ptr++)
+ {
+ if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
+ {
+ *line_ptr = '\0';
+ break;
+ }
+ }
+
+ /* cut trailing whitespaces from input line */
+ for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
+ if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
+ *line_ptr = '\0';
+
+ /* ignore empty lines */
+ if (*line == '\0')
+ continue;
+
+ line_len = strlen(line);
+
+ /* cut leading whitespaces from token */
+ for (token = line; *token; token++)
+ if (*token != ' ' && *token != '\t')
+ break;
+
+ /* find end of token */
+ for (line_ptr = token; *line_ptr; line_ptr++)
+ {
+ if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
+ {
+ *line_ptr = '\0';
+ break;
+ }
+ }
+
+ if (line_ptr < line + line_len)
+ value = line_ptr + 1;
+ else
+ value = "\0";
+
+ /* cut leading whitespaces from value */
+ for (; *value; value++)
+ if (*value != ' ' && *value != '\t')
+ break;
+
+ if (*token && *value)
+ setTokenValue(setup_file_list, token, value);
+ }
+
+ fclose(file);
+
+ first_valid_list_entry = setup_file_list->next;
+
+ /* free empty list header */
+ setup_file_list->next = NULL;
+ freeSetupFileList(setup_file_list);
+
+ if (first_valid_list_entry == NULL)
+ Error(ERR_WARN, "configuration file '%s' is empty", filename);
+
+ return first_valid_list_entry;
+}
+
+void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
+ char *identifier)
+{
+ if (!setup_file_list)
+ return;
+
+ if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
+ {
+ if (strcmp(setup_file_list->value, identifier) != 0)
+ {
+ Error(ERR_WARN, "configuration file has wrong version");
+ return;
+ }
+ else
+ return;
+ }
+
+ if (setup_file_list->next)
+ checkSetupFileListIdentifier(setup_file_list->next, identifier);
+ else
+ {
+ Error(ERR_WARN, "configuration file has no version information");
+ return;
+ }
}