diff --git a/Makefile b/Makefile index 0a3b1fece..719a766b8 100644 --- a/Makefile +++ b/Makefile @@ -224,7 +224,7 @@ CFLAGS += -DNO_FRONTEND endif # misc -OBJS += frontend/main.o frontend/plugin.o +OBJS += frontend/main.o frontend/plugin.o frontend/configfile.o frontend/menu.o frontend/main.o: frontend/revision.h diff --git a/frontend/configfile.c b/frontend/configfile.c new file mode 100644 index 000000000..70b8d53b5 --- /dev/null +++ b/frontend/configfile.c @@ -0,0 +1,272 @@ +// configfile.c - handles loading and saving the configuration options +#include +#include +#include +#include +#include +#include + +#include "configfile.h" + +#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) + +enum ConfigOptionType { + CONFIG_TYPE_BOOL, + CONFIG_TYPE_UINT, + CONFIG_TYPE_FLOAT, + CONFIG_TYPE_ASPECT_RATIO, +}; + +struct ConfigOption { + const char *name; + enum ConfigOptionType type; + union { + bool *boolValue; + unsigned int *uintValue; + float *floatValue; + }; +}; + +#undef X +#define X(a, b) b, +const char *aspect_ratio_name[] = {ASPECT_RATIOS}; +unsigned int aspect_ratio_factor_step = 10; + +/* + *Config options and default values + */ +unsigned int aspect_ratio = ASPECT_RATIOS_TYPE_STRETCHED; +unsigned int aspect_ratio_factor_percent = 50; + +static const struct ConfigOption options[] = { + {.name = "aspect_ratio", .type = CONFIG_TYPE_ASPECT_RATIO, .uintValue = &aspect_ratio}, + {.name = "aspect_ratio_factor_percent", .type = CONFIG_TYPE_UINT, .uintValue = &aspect_ratio_factor_percent}, +}; + +// Reads an entire line from a file (excluding the newline character) and returns an allocated string +// Returns NULL if no lines could be read from the file +static char *read_file_line(FILE *file) { + char *buffer; + size_t bufferSize = 64; + size_t offset = 0; // offset in buffer to write + + buffer = (char*)malloc(bufferSize); + while (1) { + // Read a line from the file + if (fgets(buffer + offset, bufferSize - offset, file) == NULL) { + free(buffer); + return NULL; // Nothing could be read. + } + offset = strlen(buffer); + assert(offset > 0); + + if (feof(file)) // EOF was reached + break; + + // If a newline was found, remove the trailing newline and exit + if (buffer[offset - 1] == '\n') { + buffer[offset - 1] = '\0'; + break; + } + + // If no newline or EOF was reached, then the whole line wasn't read. + bufferSize *= 2; // Increase buffer size + buffer = (char*)realloc(buffer, bufferSize); + assert(buffer != NULL); + } + + return buffer; +} + +// Returns the position of the first non-whitespace character +static char *skip_whitespace(char *str) { + while (isspace(*str)) + str++; + return str; +} + +// Returns the position of the first non-whitespace or '=' character +static char *skip_whitespace_or_equal(char *str) { + while (isspace(*str) || *str=='=') + str++; + return str; +} + +// NULL-terminates the current whitespace-delimited word, and returns a pointer to the next word +static char *word_split(char *str) { + // Precondition: str must not point to whitespace + assert(!isspace(*str)); + + // Find either the next whitespace, '=' or end of string + while (!isspace(*str) && *str != '\0' && *str != '=') + str++; + if (*str == '\0') // End of string + return str; + + // Terminate current word + *(str++) = '\0'; + + // Skip whitespace to next word + return skip_whitespace_or_equal(str); +} + +// Splits a string into words, and stores the words into the 'tokens' array +// 'maxTokens' is the length of the 'tokens' array +// Returns the number of tokens parsed +static unsigned int tokenize_string(char *str, int maxTokens, char **tokens) { + int count = 0; + + str = skip_whitespace(str); + while (str[0] != '\0' && count < maxTokens) { + tokens[count] = str; + str = word_split(str); + count++; + } + return count; +} + +// Loads the config file specified by 'filepath' +void configfile_load(const char *filepath) { + FILE *file; + char *line; + unsigned int cur_line = 0; + char *current_section = NULL; + + printf("Loading configuration from '%s'\n", filepath); + + // Open file or create it if it does not exist + file = fopen(filepath, "r"); + if (file == NULL) { + // Create a new config file and save defaults + printf("Config file '%s' not found. Creating it.\n", filepath); + configfile_save(filepath); + return; + } + + // Go through each line in the file + while ((line = read_file_line(file)) != NULL) { + char *p = line; + char *tokens[2]; + int numTokens; + cur_line++; + + // Get tokens + while (isspace(*p)) p++; + numTokens = tokenize_string(p, 2, tokens); + + // Get content + if (numTokens != 0) { + + // Pass comments + if(tokens[0][0]=='#') continue; + + // Check sections - useless for now + if(tokens[0][0]=='['){ + p=tokens[0]; + while(*p != '\0' && *p!=']') p++; + if(*p == '\0') continue; + *p=0; + if(current_section) free(current_section); + current_section = (char*)malloc(strlen(tokens[0])); //strlen(tokens[0])-1+1 + strcpy(current_section, &tokens[0][1]); + printf("New Section: %s\n", current_section); + continue; + } + + if (numTokens == 2) { + const struct ConfigOption *option = NULL; + + for (unsigned int i = 0; i < ARRAY_LEN(options); i++) { + if (strcmp(tokens[0], options[i].name) == 0) { + option = &options[i]; + break; + } + } + if (option == NULL){ + printf("Unknown option '%s'\n", tokens[0]); + } + else { + printf("Reading option: '%s', value: '%s'\n", tokens[0], tokens[1]); + switch (option->type) { + case CONFIG_TYPE_BOOL: + if (strcmp(tokens[1], "true") == 0) + *option->boolValue = true; + else if (strcmp(tokens[1], "false") == 0) + *option->boolValue = false; + else{ + printf("Unknown CONFIG_TYPE_BOOL value: '%s', using default: %s\n", + tokens[1], (*option->boolValue)?"true":"false"); + } + break; + case CONFIG_TYPE_UINT: + sscanf(tokens[1], "%u", option->uintValue); + break; + case CONFIG_TYPE_FLOAT: + sscanf(tokens[1], "%f", option->floatValue); + break; + case CONFIG_TYPE_ASPECT_RATIO: + ;unsigned int cur_ar; + for(cur_ar=0; cur_aruintValue = cur_ar; + break; + } + } + if(cur_ar >= NB_ASPECT_RATIOS_TYPES){ + printf("Unknown CONFIG_TYPE_ASPECT_RATIO value: '%s', using default value: %s\n", + tokens[1], aspect_ratio_name[*option->uintValue]); + } + break; + default: + printf("Unknown option type '%d'\n", option->type); + break; + } + } + } + else{ + fprintf(stderr, "Error in line %d: wrong format\n", cur_line); + } + } + free(line); + } + + fclose(file); +} + +// Writes the config file to 'filepath' +void configfile_save(const char *filepath) { + FILE *file; + + printf("Saving configuration to '%s'\n", filepath); + + file = fopen(filepath, "w"); + if (file == NULL) { + // error + printf("Could not save\n"); + return; + } + printf("Saved !\n"); + + for (unsigned int i = 0; i < ARRAY_LEN(options); i++) { + const struct ConfigOption *option = &options[i]; + + switch (option->type) { + case CONFIG_TYPE_BOOL: + fprintf(file, "%s = %s\n", option->name, *option->boolValue ? "true" : "false"); + break; + case CONFIG_TYPE_UINT: + fprintf(file, "%s = %u\n", option->name, *option->uintValue); + break; + case CONFIG_TYPE_FLOAT: + fprintf(file, "%s = %f\n", option->name, *option->floatValue); + break; + case CONFIG_TYPE_ASPECT_RATIO: + fprintf(file, "%s = %s\n", option->name, aspect_ratio_name[*option->uintValue]); + break; + default: + assert(0); // unknown type + } + } + + fclose(file); +} diff --git a/frontend/configfile.h b/frontend/configfile.h new file mode 100644 index 000000000..83db21509 --- /dev/null +++ b/frontend/configfile.h @@ -0,0 +1,28 @@ +#ifndef CONFIGFILE_H +#define CONFIGFILE_H + +#include + + +///------ Definition of the different aspect ratios +#define ASPECT_RATIOS \ + X(ASPECT_RATIOS_TYPE_MANUAL, "ZOOMED") \ + X(ASPECT_RATIOS_TYPE_STRETCHED, "STRETCHED") \ + X(ASPECT_RATIOS_TYPE_CROPPED, "CROPPED") \ + X(ASPECT_RATIOS_TYPE_SCALED, "SCALED") \ + X(NB_ASPECT_RATIOS_TYPES, "") + +////------ Enumeration of the different aspect ratios ------ +#undef X +#define X(a, b) a, +typedef enum {ASPECT_RATIOS} ENUM_ASPECT_RATIOS_TYPES; + +extern unsigned int aspect_ratio; +extern unsigned int aspect_ratio_factor_percent; +extern const char * aspect_ratio_name[]; +extern unsigned int aspect_ratio_factor_step; + +void configfile_load(const char *filename); +void configfile_save(const char *filename); + +#endif diff --git a/frontend/main.c b/frontend/main.c index ddf8f86f7..c09b193ea 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -20,6 +20,7 @@ #include "plugin_lib.h" #include "pcnt.h" #include "menu.h" +#include "configfile.h" #include "plat.h" #include "../libpcsxcore/misc.h" #include "../libpcsxcore/cheat.h" @@ -63,6 +64,10 @@ char *cdfile = NULL; char *cdPath = NULL; static char *quick_save_file_extension = "quicksave"; char *quick_save_file = NULL; +char *cfg_file_default = NULL; +char *cfg_file_rom = NULL; +static char *cfg_file_default_name = "default_config"; +static char *cfg_file_extension = "cfg"; @@ -324,6 +329,9 @@ void do_emu_action(void) if (fp == NULL) { printf("Failed to run command %s\n", shell_cmd); } + + // Save config file + configfile_save(cfg_file_rom); break; case SACTION_ASPECT_RATIO_FACTOR_DECREASE: if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){ @@ -342,6 +350,9 @@ void do_emu_action(void) if (fp == NULL) { printf("Failed to run command %s\n", shell_cmd); } + + // Save config file + configfile_save(cfg_file_rom); break; case SACTION_ASPECT_RATIO_FACTOR_INCREASE: if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){ @@ -361,6 +372,9 @@ void do_emu_action(void) if (fp == NULL) { printf("Failed to run command %s\n", shell_cmd); } + + // Save config file + configfile_save(cfg_file_rom); break; case SACTION_FAST_FORWARD: toggle_fast_forward(0); @@ -776,6 +790,24 @@ int main(int argc, char *argv[]) sprintf(quick_save_file, "%s/%s.%s", cdPath, cdfile_no_ext, quick_save_file_extension); printf("quick_save_file: %s\n", quick_save_file); + + /* Set rom cfg filepath */ + cfg_file_rom = (char *)malloc(strlen(cdPath) + strlen(slash+1) + + strlen(cfg_file_extension) + 2 + 1); + sprintf(cfg_file_rom, "%s/%s.%s", + cdPath, slash+1, cfg_file_extension); + printf("cfg_file_rom: %s\n", cfg_file_rom); + + /* Set console cfg filepath */ + cfg_file_default = (char *)malloc(strlen(cdPath) + strlen(cfg_file_default_name) + + strlen(cfg_file_extension) + 2 + 1); + sprintf(cfg_file_default, "%s/%s.%s", + cdPath, cfg_file_default_name, cfg_file_extension); + printf("cfg_file_default: %s\n", cfg_file_default); + + /** Load config files */ + configfile_load(cfg_file_default); + configfile_load(cfg_file_rom); fclose(f); } diff --git a/frontend/menu.c b/frontend/menu.c index 11a690829..e96dc6e4a 100644 --- a/frontend/menu.c +++ b/frontend/menu.c @@ -24,6 +24,7 @@ #include "main.h" #include "menu.h" +#include "configfile.h" #include "config.h" #include "plugin.h" #include "plugin_lib.h" @@ -224,13 +225,6 @@ static uint16_t y_brightness_bar = 0; int volume_percentage = 0; int brightness_percentage = 0; -#undef X -#define X(a, b) b, -const char *aspect_ratio_name[] = {ASPECT_RATIOS}; -int aspect_ratio = ASPECT_RATIOS_TYPE_STRETCHED; -int aspect_ratio_factor_percent = 50; -int aspect_ratio_factor_step = 10; - #undef X #define X(a, b) b, const char *resume_options_str[] = {RESUME_OPTIONS}; @@ -915,8 +909,12 @@ void run_menu_loop() else if(idx_menus[menuItem] == MENU_TYPE_ASPECT_RATIO){ MENU_DEBUG_PRINTF("Aspect Ratio DOWN\n"); aspect_ratio = (!aspect_ratio)?(NB_ASPECT_RATIOS_TYPES-1):(aspect_ratio-1); + /// ------ Refresh screen ------ screen_refresh = 1; + + // Save config file + configfile_save(cfg_file_rom); } break; @@ -982,8 +980,12 @@ void run_menu_loop() else if(idx_menus[menuItem] == MENU_TYPE_ASPECT_RATIO){ MENU_DEBUG_PRINTF("Aspect Ratio UP\n"); aspect_ratio = (aspect_ratio+1)%NB_ASPECT_RATIOS_TYPES; + /// ------ Refresh screen ------ screen_refresh = 1; + + // Save config file + configfile_save(cfg_file_rom); } break; @@ -1343,6 +1345,10 @@ int launch_resume_menu_loop() screen_refresh = 0; } + /// ----- Clear screen ----- + SDL_FillRect(hw_screen, NULL, 0); + SDL_Flip(hw_screen); + /* Free SDL Surfaces */ if(bg_surface) SDL_FreeSurface(bg_surface); diff --git a/frontend/menu.h b/frontend/menu.h index 824f8a400..13ad62651 100644 --- a/frontend/menu.h +++ b/frontend/menu.h @@ -47,20 +47,6 @@ typedef enum{ NB_MENU_TYPES, } ENUM_MENU_TYPE; - -///------ Definition of the different aspect ratios -#define ASPECT_RATIOS \ - X(ASPECT_RATIOS_TYPE_MANUAL, "MANUAL ZOOM") \ - X(ASPECT_RATIOS_TYPE_STRETCHED, "STRETCHED") \ - X(ASPECT_RATIOS_TYPE_CROPPED, "CROPPED") \ - X(ASPECT_RATIOS_TYPE_SCALED, "SCALED") \ - X(NB_ASPECT_RATIOS_TYPES, "") - -////------ Enumeration of the different aspect ratios ------ -#undef X -#define X(a, b) a, -typedef enum {ASPECT_RATIOS} ENUM_ASPECT_RATIOS_TYPES; - ///------ Definition of the different resume options #define RESUME_OPTIONS \ X(RESUME_YES, "RESUME GAME") \ @@ -103,9 +89,6 @@ extern int g_menuscreen_h; extern int volume_percentage; extern int brightness_percentage; -extern const char *aspect_ratio_name[]; -extern int aspect_ratio; -extern int aspect_ratio_factor_percent; -extern int aspect_ratio_factor_step; extern int stop_menu_loop; extern char *quick_save_file; +extern char *cfg_file_rom; diff --git a/frontend/plat_sdl.c b/frontend/plat_sdl.c index 4a5bf29f9..7132fde34 100644 --- a/frontend/plat_sdl.c +++ b/frontend/plat_sdl.c @@ -24,6 +24,7 @@ #include "plugin.h" #include "main.h" #include "menu.h" +#include "configfile.h" #include "plat.h" #include "revision.h"