https://github.com/libsdl-org/Maelstrom/commit/4430b3635bc6a3378aef64db6aae7dce8a790c0f
From 4430b3635bc6a3378aef64db6aae7dce8a790c0f Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 4 Nov 2011 19:32:38 -0400
Subject: [PATCH] Added initial preferences implementation
---
Maelstrom_Globals.h | 4 +
Makefile.am | 2 +
Makefile.in | 7 +-
init.cpp | 24 ++++--
prefs.cpp | 176 ++++++++++++++++++++++++++++++++++++++++++++
prefs.h | 50 +++++++++++++
utils/hashtable.c | 37 ++++++++++
utils/hashtable.h | 1 +
8 files changed, 292 insertions(+), 9 deletions(-)
create mode 100644 prefs.cpp
create mode 100644 prefs.h
diff --git a/Maelstrom_Globals.h b/Maelstrom_Globals.h
index a6ccb3cc..79e7a547 100644
--- a/Maelstrom_Globals.h
+++ b/Maelstrom_Globals.h
@@ -38,6 +38,7 @@
#include "myerror.h"
#include "fastrand.h"
#include "logic.h"
+#include "prefs.h"
#include "scores.h"
#include "controls.h"
@@ -53,6 +54,9 @@ enum {
};
extern MFont *fonts[NUM_FONTS];
+// The Preferences
+extern Prefs *prefs;
+
// The Sound Server *grin*
extern Sound *sound;
diff --git a/Makefile.am b/Makefile.am
index 7eb05b8c..6b4d6853 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,6 +23,8 @@ Maelstrom_SOURCES = \
main.h \
myerror.cpp \
myerror.h \
+ prefs.cpp \
+ prefs.h \
rect.cpp \
rect.h \
scores.cpp \
diff --git a/Makefile.in b/Makefile.in
index 6efbba37..92f3d4ab 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -53,8 +53,8 @@ binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
PROGRAMS = $(bin_PROGRAMS)
am_Maelstrom_OBJECTS = MaelstromUI.$(OBJEXT) MacDialog.$(OBJEXT) \
controls.$(OBJEXT) fastrand.$(OBJEXT) init.$(OBJEXT) \
- load.$(OBJEXT) main.$(OBJEXT) myerror.$(OBJEXT) rect.$(OBJEXT) \
- scores.$(OBJEXT)
+ load.$(OBJEXT) main.$(OBJEXT) myerror.$(OBJEXT) \
+ prefs.$(OBJEXT) rect.$(OBJEXT) scores.$(OBJEXT)
Maelstrom_OBJECTS = $(am_Maelstrom_OBJECTS)
Maelstrom_DEPENDENCIES = $(LOGIC)/liblogic.a screenlib/libSDLscreen.a \
maclib/libSDLmac.a utils/libutils.a
@@ -220,6 +220,8 @@ Maelstrom_SOURCES = \
main.h \
myerror.cpp \
myerror.h \
+ prefs.cpp \
+ prefs.h \
rect.cpp \
rect.h \
scores.cpp \
@@ -334,6 +336,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/myerror.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rect.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scores.Po@am__quote@
diff --git a/init.cpp b/init.cpp
index 030c0586..436b29fc 100644
--- a/init.cpp
+++ b/init.cpp
@@ -35,7 +35,10 @@
#include "screenlib/UIElement.h"
+#define GAME_PREFS_FILE "Maelstrom_Prefs.txt"
+
// Global variables set in this file...
+Prefs *prefs = NULL;
Sound *sound = NULL;
FontServ *fontserv = NULL;
MFont *fonts[NUM_FONTS];
@@ -658,7 +661,10 @@ void CleanUp(void)
delete sound;
sound = NULL;
}
- SaveControls();
+ if ( prefs ) {
+ delete prefs;
+ prefs = NULL;
+ }
PHYSFS_deinit();
SDL_Quit();
}
@@ -671,6 +677,16 @@ int DoInitializations(Uint32 window_flags, Uint32 render_flags)
int i;
SDL_Surface *icon;
+ // -- Initialize some variables
+ gLastHigh = -1;
+
+ // -- Create our scores file
+ LoadScores();
+
+ // -- Load our preferences files
+ prefs = new Prefs(GAME_PREFS_FILE);
+ prefs->Load();
+
Uint32 init_flags = (SDL_INIT_VIDEO|SDL_INIT_AUDIO);
#ifdef SDL_INIT_JOYSTICK
init_flags |= SDL_INIT_JOYSTICK;
@@ -683,12 +699,6 @@ int DoInitializations(Uint32 window_flags, Uint32 render_flags)
}
}
- // -- Initialize some variables
- gLastHigh = -1;
-
- // -- Create our scores file
- LoadScores();
-
#ifdef SDL_INIT_JOYSTICK
/* Initialize the first joystick */
if ( SDL_NumJoysticks() > 0 ) {
diff --git a/prefs.cpp b/prefs.cpp
new file mode 100644
index 00000000..aeb32a94
--- /dev/null
+++ b/prefs.cpp
@@ -0,0 +1,176 @@
+/*
+ Maelstrom: Open Source version of the classic game by Ambrosia Software
+ Copyright (C) 1997-2011 Sam Lantinga
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+
+#include "physfs.h"
+#include "utils/hashtable.h"
+
+#include "prefs.h"
+
+
+static void
+hash_nuke_strings(const void *key, const void *value, void *data)
+{
+ SDL_free((char*)key);
+ SDL_free((char*)value);
+}
+
+Prefs::Prefs(const char *file)
+{
+ m_file = SDL_strdup(file);
+ m_values = hash_create(NULL, hash_hash_string, hash_keymatch_string, hash_nuke_strings);
+}
+
+Prefs::~Prefs()
+{
+ SDL_free(m_file);
+ hash_destroy(m_values);
+}
+
+bool
+Prefs::Load()
+{
+ PHYSFS_File *fp;
+ PHYSFS_sint64 size;
+ char *data;
+ char *key, *value, *next;
+
+ fp = PHYSFS_openRead(m_file);
+ if (!fp) {
+ /* This is fine, we just haven't written them yet */
+ return false;
+ }
+
+ size = PHYSFS_fileLength(fp);
+ data = new char[size+1];
+ if (PHYSFS_readBytes(fp, data, size) != size) {
+ fprintf(stderr, "Warning: Couldn't read from %s: %s\n",
+ m_file, PHYSFS_getLastError());
+ PHYSFS_close(fp);
+ delete[] data;
+ return false;
+ }
+ data[size] = '\0';
+ PHYSFS_close(fp);
+
+ key = data;
+ while (*key) {
+ value = SDL_strchr(key, '=');
+ if (!value) {
+ break;
+ }
+ *value++ = '\0';
+
+ next = value;
+ while (*next && *next != '\r' && *next != '\n') {
+ ++next;
+ }
+ if (*next) {
+ *next++ = '\0';
+ }
+
+ SetString(key, value);
+
+ key = next;
+ while (*key && *key == '\r' && *key == '\n') {
+ ++key;
+ }
+ }
+ delete[] data;
+
+ return true;
+}
+
+static __inline__ bool
+writeString(PHYSFS_File *fp, const char *string)
+{
+ size_t len = SDL_strlen(string);
+ return ((size_t)PHYSFS_writeBytes(fp, string, len) == len);
+}
+
+bool
+Prefs::Save()
+{
+ PHYSFS_File *fp;
+ const char *key, *value;
+ void *iter;
+
+ fp = PHYSFS_openWrite(m_file);
+ if (!fp) {
+ fprintf(stderr, "Warning: Couldn't open %s: %s\n",
+ m_file, PHYSFS_getLastError());
+ return false;
+ }
+
+ iter = NULL;
+ while (hash_iter(m_values, (const void **)&key, (const void **)&value, &iter)) {
+ if (!writeString(fp, key) ||
+ !writeString(fp, "=") ||
+ !writeString(fp, value) ||
+ !writeString(fp, "\r\n")) {
+ fprintf(stderr, "Warning: Couldn't write to %s: %s\n",
+ m_file, PHYSFS_getLastError());
+ PHYSFS_close(fp);
+ return false;
+ }
+ }
+ PHYSFS_close(fp);
+
+ return true;
+}
+
+void
+Prefs::SetString(const char *key, const char *value)
+{
+ hash_remove(m_values, key);
+ hash_insert(m_values, SDL_strdup(key), SDL_strdup(value));
+}
+
+void
+Prefs::SetNumber(const char *key, Uint32 value)
+{
+ char buf[32];
+
+ SDL_snprintf(buf, sizeof(buf), "%u", value);
+ SetString(key, buf);
+}
+
+const char *
+Prefs::GetString(const char *key, const char *defaultValue)
+{
+ const char *value;
+
+ if (hash_find(m_values, key, (const void **)&value)) {
+ return value;
+ }
+ return defaultValue;
+}
+
+Uint32
+Prefs::GetNumber(const char *key, Uint32 defaultValue)
+{
+ const char *value;
+
+ if (hash_find(m_values, key, (const void **)&value)) {
+ return SDL_atoi(value);
+ }
+ return defaultValue;
+}
diff --git a/prefs.h b/prefs.h
new file mode 100644
index 00000000..0fb7ffb7
--- /dev/null
+++ b/prefs.h
@@ -0,0 +1,50 @@
+/*
+ Maelstrom: Open Source version of the classic game by Ambrosia Software
+ Copyright (C) 1997-2011 Sam Lantinga
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+
+#ifndef _prefs_h
+#define _prefs_h
+
+#include "SDL.h"
+
+class HashTable;
+
+class Prefs
+{
+public:
+ Prefs(const char *file);
+ virtual ~Prefs();
+
+ bool Load();
+ bool Save();
+
+ void SetString(const char *key, const char *value);
+ void SetNumber(const char *key, Uint32 value);
+
+ const char *GetString(const char *key, const char *defaultValue = NULL);
+ Uint32 GetNumber(const char *key, Uint32 defaultValue = 0);
+
+protected:
+ char *m_file;
+ HashTable *m_values;
+};
+
+#endif /* _prefs_h */
diff --git a/utils/hashtable.c b/utils/hashtable.c
index d991e57d..048505c5 100644
--- a/utils/hashtable.c
+++ b/utils/hashtable.c
@@ -25,12 +25,15 @@ typedef struct HashItem
const void *key;
const void *value;
struct HashItem *next;
+ struct HashItem *global_next;
+ struct HashItem *global_prev;
} HashItem;
struct HashTable
{
HashItem **table;
unsigned table_len;
+ HashItem *list;
void *data;
HashTable_HashFn hash;
HashTable_KeyMatchFn keymatch;
@@ -72,6 +75,29 @@ int hash_find(const HashTable *table, const void *key, const void **_value)
return 0;
} // hash_find
+int hash_iter(const HashTable *table, const void **key,
+ const void **value, void **iter)
+{
+ HashItem *i = *iter;
+ if (i == NULL)
+ i = table->list;
+ else
+ i = i->global_next;
+
+ if (i != NULL)
+ {
+ *key = i->key;
+ *value = i->value;
+ *iter = i;
+ return 1;
+ }
+
+ *key = NULL;
+ *value = NULL;
+ *iter = NULL;
+ return 0;
+} // hash_iter
+
int hash_insert(HashTable *table, const void *key, const void *value)
{
HashItem *item = NULL;
@@ -87,6 +113,12 @@ int hash_insert(HashTable *table, const void *key, const void *value)
item->key = key;
item->value = value;
item->next = table->table[hash];
+ item->global_next = table->list;
+ if (table->list) {
+ table->list->global_prev = item;
+ }
+ item->global_prev = NULL;
+ table->list = item;
table->table[hash] = item;
return 1;
@@ -107,6 +139,11 @@ int hash_remove(HashTable *table, const void *key)
else
table->table[hash] = item->next;
+ if (item->global_prev)
+ item->global_prev->global_next = item->global_next;
+ if (item->global_next)
+ item->global_next->global_prev = item->global_prev;
+
table->nuke(item->key, item->value, data);
free(item);
return 1;
diff --git a/utils/hashtable.h b/utils/hashtable.h
index 6acae01f..dad098c1 100644
--- a/utils/hashtable.h
+++ b/utils/hashtable.h
@@ -37,6 +37,7 @@ void hash_destroy(HashTable *table);
int hash_insert(HashTable *table, const void *key, const void *value);
int hash_remove(HashTable *table, const void *key);
int hash_find(const HashTable *table, const void *key, const void **_value);
+int hash_iter(const HashTable *table, const void **key, const void **value, void **iter);
unsigned hash_hash_string(const void *sym, void *unused);
int hash_keymatch_string(const void *a, const void *b, void *unused);