Add saving/loading configuration

LaiNES now can save and load configuration. At the moment, it's the size
of the window, and the key bindings.

Instead of cluttering the gui, I broke the config out to its own file.
Also added "home config directory" support, which is the standard for
*nix I suppose.
This commit is contained in:
Jeff Katz 2016-12-03 21:55:20 +01:00
parent db56e638d2
commit 48f85a4323
6 changed files with 203 additions and 24 deletions

View file

@ -9,7 +9,7 @@ env = Environment(ENV = environ,
CPPFLAGS = ['-Wno-unused-value'],
CXXFLAGS = flags,
LINKFLAGS = flags,
CPPPATH = ['#lib/include', '#src/include'],
CPPPATH = ['#simpleini', '#lib/include', '#src/include'],
LIBS = ['SDL2', 'SDL2_image', 'SDL2_ttf'])
env.Program('laines', Glob('build/*/*.cpp') + Glob('build/*/*/*.cpp'))

155
src/config.cpp Normal file
View file

@ -0,0 +1,155 @@
#include <cstdlib>
#include <SimpleIni.h>
#include "config.hpp"
#include "gui.hpp"
namespace GUI {
/* Settings */
CSimpleIniA ini(true, false, false);
/* Window settings */
int last_window_size = 1;
/* Controls settings */
SDL_Scancode KEY_A [] = { SDL_SCANCODE_A, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_B [] = { SDL_SCANCODE_S, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_SELECT[] = { SDL_SCANCODE_SPACE, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_START [] = { SDL_SCANCODE_RETURN, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_UP [] = { SDL_SCANCODE_UP, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_DOWN [] = { SDL_SCANCODE_DOWN, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_LEFT [] = { SDL_SCANCODE_LEFT, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_RIGHT [] = { SDL_SCANCODE_RIGHT, SDL_SCANCODE_ESCAPE };
int BTN_UP [] = { -1, -1 };
int BTN_DOWN [] = { -1, -1 };
int BTN_LEFT [] = { -1, -1 };
int BTN_RIGHT [] = { -1, -1 };
int BTN_A [] = { -1, -1 };
int BTN_B [] = { -1, -1 };
int BTN_SELECT[] = { -1, -1 };
int BTN_START [] = { -1, -1 };
bool useJoystick[] = { false, false };
/* Ensure config directory exists */
const char* get_config_path(char * buf, int buflen)
{
/* Bail on the complex stuff if we don't need it */
if (!USE_CONFIG_DIR)
{
return CONFIG_FALLBACK;
}
/* First, get the home directory */
char homepath[CONFIG_PATH_MAX];
char path[CONFIG_PATH_MAX];
char * home = getenv("HOME");
if (home == NULL)
return CONFIG_FALLBACK;
snprintf(homepath, sizeof(homepath), "%s/.config", home);
/* Then, .config as a folder */
int res = mkdir(homepath, CONFIG_DIR_DEFAULT_MODE);
int err = errno;
if (res == -1 && err != EEXIST)
return CONFIG_FALLBACK;
snprintf(path, sizeof(path), "%s/%s", homepath, CONFIG_DIR_NAME);
/* Finally, CONFIG_DIR_NAME as a sub-folder */
res = mkdir(path, CONFIG_DIR_DEFAULT_MODE);
err = errno;
if (res == -1 && err != EEXIST)
return CONFIG_FALLBACK;
snprintf(buf, buflen, "%s/settings", path);
return buf;
}
/* Load settings */
void load_settings()
{
/* Files */
char path[CONFIG_PATH_MAX];
ini.LoadFile(get_config_path(path, sizeof(path)));
/* Screen settings */
int screen_size = atoi(ini.GetValue("screen", "size", "1"));
if (screen_size < 1 || screen_size > 4)
screen_size = 1;
set_size(screen_size);
/* Control settings */
for (int p = 0; p < 1; p++)
{
const char* section = p==0?"controls p1":"controls p2";
useJoystick[p] = (ini.GetValue(section, "usejoy", "no"))[0] == 'y';
if (useJoystick[p])
{
BTN_UP[p] = atoi(ini.GetValue(section, "UP", "-1"));
BTN_DOWN[p] = atoi(ini.GetValue(section, "DOWN", "-1"));
BTN_LEFT[p] = atoi(ini.GetValue(section, "LEFT", "-1"));
BTN_RIGHT[p] = atoi(ini.GetValue(section, "RIGHT", "-1"));
BTN_A[p] = atoi(ini.GetValue(section, "A", "-1"));
BTN_B[p] = atoi(ini.GetValue(section, "B", "-1"));
BTN_SELECT[p] = atoi(ini.GetValue(section, "SELECT", "-1"));
BTN_START[p] = atoi(ini.GetValue(section, "START", "-1"));
} else {
KEY_UP[p] = (SDL_Scancode)atoi(ini.GetValue(section, "UP", "82"));
KEY_DOWN[p] = (SDL_Scancode)atoi(ini.GetValue(section, "DOWN", "81"));
KEY_LEFT[p] = (SDL_Scancode)atoi(ini.GetValue(section, "LEFT", "80"));
KEY_RIGHT[p] = (SDL_Scancode)atoi(ini.GetValue(section, "RIGHT", "79"));
KEY_A[p] = (SDL_Scancode)atoi(ini.GetValue(section, "A", "4"));
KEY_B[p] = (SDL_Scancode)atoi(ini.GetValue(section, "B", "22"));
KEY_SELECT[p] = (SDL_Scancode)atoi(ini.GetValue(section, "SELECT", "44"));
KEY_START[p] = (SDL_Scancode)atoi(ini.GetValue(section, "START", "40"));
}
}
}
/* Save settings */
void save_settings()
{
/* Screen settings */
char buf[2] = {0};
sprintf(buf, "%d", last_window_size);
ini.SetValue("screen", "size", buf);
/* Control settings */
for (int p = 0; p < 1; p++)
{
const char* section = p==0?"controls p1":"controls p2";
sprintf(buf, "%d", useJoystick[p]?BTN_UP[p]:KEY_UP[p]);
ini.SetValue("section", "UP", buf);
sprintf(buf, "%d", useJoystick[p]?BTN_DOWN[p]:KEY_DOWN[p]);
ini.SetValue("section", "DOWN", buf);
sprintf(buf, "%d", useJoystick[p]?BTN_LEFT[p]:KEY_LEFT[p]);
ini.SetValue("section", "LEFT", buf);
sprintf(buf, "%d", useJoystick[p]?BTN_RIGHT[p]:KEY_RIGHT[p]);
ini.SetValue("section", "RIGHT", buf);
sprintf(buf, "%d", useJoystick[p]?BTN_A[p]:KEY_A[p]);
ini.SetValue("section", "A", buf);
sprintf(buf, "%d", useJoystick[p]?BTN_B[p]:KEY_B[p]);
ini.SetValue("section", "B", buf);
sprintf(buf, "%d", useJoystick[p]?BTN_SELECT[p]:KEY_SELECT[p]);
ini.SetValue("section", "SELECT", buf);
sprintf(buf, "%d", useJoystick[p]?BTN_START[p]:KEY_START[p]);
ini.SetValue("section", "START", buf);
ini.SetValue("section", "usejoy", useJoystick[p]?"yes":"no");
}
char path[CONFIG_PATH_MAX];
ini.SaveFile(get_config_path(path, sizeof(path)));
}
}

View file

@ -7,10 +7,10 @@
#include "cpu.hpp"
#include "menu.hpp"
#include "gui.hpp"
#include "config.hpp"
namespace GUI {
// Screen size:
const unsigned WIDTH = 256;
const unsigned HEIGHT = 240;
@ -36,28 +36,10 @@ FileMenu* fileMenu;
bool pause = true;
// Controls settings:
SDL_Scancode KEY_A [] = { SDL_SCANCODE_A, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_B [] = { SDL_SCANCODE_S, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_SELECT[] = { SDL_SCANCODE_SPACE, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_START [] = { SDL_SCANCODE_RETURN, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_UP [] = { SDL_SCANCODE_UP, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_DOWN [] = { SDL_SCANCODE_DOWN, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_LEFT [] = { SDL_SCANCODE_LEFT, SDL_SCANCODE_ESCAPE };
SDL_Scancode KEY_RIGHT [] = { SDL_SCANCODE_RIGHT, SDL_SCANCODE_ESCAPE };
int BTN_UP [] = { -1, -1 };
int BTN_DOWN [] = { -1, -1 };
int BTN_LEFT [] = { -1, -1 };
int BTN_RIGHT [] = { -1, -1 };
int BTN_A [] = { -1, -1 };
int BTN_B [] = { -1, -1 };
int BTN_SELECT[] = { -1, -1 };
int BTN_START [] = { -1, -1 };
bool useJoystick[] = { false, false };
/* Set the window size multiplier */
void set_size(int mul)
{
last_window_size = mul;
SDL_SetWindowSize(window, WIDTH * mul, HEIGHT * mul);
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
}
@ -80,7 +62,7 @@ void init()
// Initialize graphics structures:
window = SDL_CreateWindow ("LaiNES",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
WIDTH, HEIGHT, 0);
WIDTH * last_window_size, HEIGHT * last_window_size, 0);
renderer = SDL_CreateRenderer(window, -1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
@ -110,12 +92,14 @@ void init()
settingsMenu->add(new Entry("Video", []{ menu = videoMenu; }));
settingsMenu->add(new Entry("Controller 1", []{ menu = useJoystick[0] ? joystickMenu[0] : keyboardMenu[0]; }));
settingsMenu->add(new Entry("Controller 2", []{ menu = useJoystick[1] ? joystickMenu[1] : keyboardMenu[1]; }));
settingsMenu->add(new Entry("Save Settings", []{ save_settings(); menu = mainMenu; }));
videoMenu = new Menu;
videoMenu->add(new Entry("<", []{ menu = settingsMenu; }));
videoMenu->add(new Entry("Size 1x", []{ set_size(1); }));
videoMenu->add(new Entry("Size 2x", []{ set_size(2); }));
videoMenu->add(new Entry("Size 3x", []{ set_size(3); }));
videoMenu->add(new Entry("Size 4x", []{ set_size(4); }));
for (int i = 0; i < 2; i++)
{

39
src/include/config.hpp Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include <cerrno>
#include <sys/stat.h>
#include <SDL2/SDL.h>
#define CONFIG_DIR_DEFAULT_MODE S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
#define USE_CONFIG_DIR true
#define CONFIG_DIR_NAME "LaiNES"
#define CONFIG_FALLBACK ".laines-settings"
/* PATH_MAX is a portability nightmare. */
#define CONFIG_PATH_MAX 1024
namespace GUI {
/* Loading and saving */
void load_settings();
void save_settings();
const char* get_config_path(char * buf, int buflen);
extern int last_window_size;
extern SDL_Scancode KEY_A [];
extern SDL_Scancode KEY_B [];
extern SDL_Scancode KEY_SELECT[];
extern SDL_Scancode KEY_START [];
extern SDL_Scancode KEY_UP [];
extern SDL_Scancode KEY_DOWN [];
extern SDL_Scancode KEY_LEFT [];
extern SDL_Scancode KEY_RIGHT [];
extern int BTN_UP [];
extern int BTN_DOWN [];
extern int BTN_LEFT [];
extern int BTN_RIGHT [];
extern int BTN_A [];
extern int BTN_B [];
extern int BTN_SELECT[];
extern int BTN_START [];
extern bool useJoystick[];
}

View file

@ -23,6 +23,6 @@ void render_texture(SDL_Texture* texture, int x, int y);
u8 get_joypad_state(int n);
void new_frame(u32* pixels);
void new_samples(const blip_sample_t* samples, size_t count);
void set_size(int mul);
}

View file

@ -1,8 +1,9 @@
#include "gui.hpp"
#include "config.hpp"
int main(int argc, char *argv[])
{
GUI::load_settings();
GUI::init();
GUI::run();