New configuration system working. Game database import started.
This commit is contained in:
parent
61ab16512f
commit
794bd96d3f
17 changed files with 461 additions and 256 deletions
|
@ -21,9 +21,8 @@
|
|||
|
||||
TEMPLATE = app
|
||||
CONFIG -= qt
|
||||
CONFIG += \
|
||||
ASAN \
|
||||
c99
|
||||
CONFIG += c99
|
||||
CONFIG += ASAN
|
||||
|
||||
DESTDIR = $$OUT_PWD/bin
|
||||
SHARED = $$PWD/../shared
|
||||
|
@ -136,6 +135,7 @@ SOURCES = \
|
|||
src/gui/msgbox.c \
|
||||
src/gui/timer.c \
|
||||
src/hangup.c \
|
||||
src/runtime.c \
|
||||
src/system/cache.c \
|
||||
src/system/comport.c \
|
||||
src/system/db.c \
|
||||
|
|
|
@ -137,7 +137,8 @@ static void fileCheckNext(void) {
|
|||
// End of list!
|
||||
logWrite("End of file list.\n");
|
||||
arrfree(_current->files);
|
||||
_current->callback();
|
||||
// Call with NULL to signify end of transfers.
|
||||
_current->callback(NULL);
|
||||
DEL(_current);
|
||||
// See if there's more.
|
||||
doRecheck = 1;
|
||||
|
@ -253,6 +254,8 @@ static void packetHandler(PacketDecodeDataT *packet) {
|
|||
cacheEntryAdd(_currentSha256, cacheEntryNameGet(_file), _file);
|
||||
imageCacheIfNeeded(_file, _currentSha256); // If this was an image, decompress it now.
|
||||
DEL(_currentSha256);
|
||||
// Call with filename to signify this file was updated.
|
||||
_current->callback(_file);
|
||||
// Next file!
|
||||
fileCheckNext();
|
||||
} else {
|
||||
|
@ -299,7 +302,7 @@ static void timTimerTimeout(WidgetT *widget) {
|
|||
}
|
||||
}
|
||||
// If we're good so far, call the callback.
|
||||
if (!missing) _fileList[0]->callback();
|
||||
if (!missing) _fileList[0]->callback(NULL);
|
||||
arrfree(_fileList[0]->files);
|
||||
arrdel(_fileList, 0);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "os.h"
|
||||
|
||||
|
||||
typedef void (*fileCallback)(void);
|
||||
typedef void (*fileCallback)(char *updatedFile);
|
||||
|
||||
|
||||
void fileCacheCheck(fileCallback callback, char *vpaths[]);
|
||||
|
|
|
@ -626,7 +626,7 @@ uint8_t vbeStartup(uint16_t xRes, uint16_t yRes, uint8_t bpp) {
|
|||
|
||||
_windowScale = 3;
|
||||
|
||||
_window = SDL_CreateWindow("GUI Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, xRes, yRes, SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
_window = SDL_CreateWindow("KangaWorld Debug Client", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, xRes, yRes, SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
_surface = SDL_GetWindowSurface(_window);
|
||||
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
|
||||
_texture = SDL_CreateTexture(_renderer, _pixelFormat, SDL_TEXTUREACCESS_STREAMING, xRes, yRes);
|
||||
|
|
|
@ -43,8 +43,6 @@ typedef enum LoginStateE {
|
|||
} LoginStateT;
|
||||
|
||||
|
||||
static char *_shaClientDat = NULL;
|
||||
|
||||
static WindowT *_winLogin = NULL;
|
||||
static TextboxT *_txtUser = NULL;
|
||||
static TextboxT *_txtPass = NULL;
|
||||
|
@ -61,7 +59,7 @@ static void btnLoginClick(WidgetT *widget);
|
|||
static void btnSignUpClick(WidgetT *widget);
|
||||
static void btnMsgBoxCancel(MsgBoxButtonT button);
|
||||
static void btnMsgBoxContinue(MsgBoxButtonT button);
|
||||
static void loginFilesReady(void);
|
||||
static void loginFilesReady(char *updatedFile);
|
||||
static void packetHandler(PacketDecodeDataT *packet);
|
||||
static void setButtons(uint8_t enabled);
|
||||
static void timLoginProgress(WidgetT *widget);
|
||||
|
@ -114,13 +112,17 @@ static void btnMsgBoxContinue(MsgBoxButtonT button) {
|
|||
}
|
||||
|
||||
|
||||
static void loginFilesReady(void) {
|
||||
char *p;
|
||||
static void loginFilesReady(char *updatedFile) {
|
||||
|
||||
BEGIN
|
||||
|
||||
//***TODO*** Load into database if changed.
|
||||
p = cacheSha256Get("generated:client.dat");
|
||||
// Is this an updated file or the end of the transfer list?
|
||||
if (updatedFile) {
|
||||
// client.dat was updated - read it into DB.
|
||||
runtimeDataUpdate();
|
||||
} else {
|
||||
// End of cache update. Display UI.
|
||||
runtimeDataLoad();
|
||||
|
||||
// ***TODO*** We used to have a FORGOT PASSWORD link here, too.
|
||||
|
||||
|
@ -134,14 +136,14 @@ static void loginFilesReady(void) {
|
|||
T_TITLE, P("User Name:"),
|
||||
T_X, 42, T_Y, 10,
|
||||
T_WIDTH, 200,
|
||||
T_LENGTH, shget(__runtimeData.integers, "maxUser"),
|
||||
T_LENGTH, RUNTIME_INT("maxUser"),
|
||||
T_TEXTBOX, T_DONE,
|
||||
|
||||
T_TEXTBOX, O(_txtPass),
|
||||
T_TITLE, P(" Password:"),
|
||||
T_X, 42, T_Y, 40,
|
||||
T_WIDTH, 200,
|
||||
T_LENGTH, shget(__runtimeData.integers, "maxPass"),
|
||||
T_LENGTH, RUNTIME_INT("maxPass"),
|
||||
T_MASK, '*',
|
||||
T_TEXTBOX, T_DONE,
|
||||
|
||||
|
@ -175,13 +177,13 @@ static void loginFilesReady(void) {
|
|||
|
||||
textboxValueSet(_txtUser, "test");
|
||||
textboxValueSet(_txtPass, "test");
|
||||
}
|
||||
|
||||
END
|
||||
}
|
||||
|
||||
|
||||
void loginShow() {
|
||||
char *p;
|
||||
char *fileList[] = {
|
||||
"generated:client.dat",
|
||||
NULL
|
||||
|
@ -189,14 +191,6 @@ void loginShow() {
|
|||
|
||||
BEGIN
|
||||
|
||||
// Keep old SHA to know if we need to reload after updating.
|
||||
p = cacheSha256Get("generated:client.dat");
|
||||
if (p) {
|
||||
_shaClientDat = strdup(p);
|
||||
} else {
|
||||
_shaClientDat = NULL;
|
||||
}
|
||||
|
||||
fileCacheCheck(loginFilesReady, fileList);
|
||||
|
||||
END
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
/*
|
||||
* To Do:
|
||||
*
|
||||
* - In-app video settings configuration with sensible auto-detected defaults
|
||||
* - Replace any direct data manipulation from outside a class with methods to handle it
|
||||
* - More widget states: Ghosted (underway)
|
||||
* - Methods that can change the width of a widget (such as setTitle) need to repaint the parent window as well
|
||||
|
@ -31,6 +32,8 @@
|
|||
* - No thumb in listbox scrollbar
|
||||
* - Layout container widgets!
|
||||
* - Fix variable names. something = local; _something = file global; __something = project global
|
||||
* - Use https://qoiformat.org/ instead of PNG for faster decoding
|
||||
* - Store unpacked images on surfaces instead of inside an ImageT for faster blitting and less RAM usage
|
||||
*/
|
||||
|
||||
|
||||
|
@ -55,7 +58,7 @@
|
|||
|
||||
|
||||
PacketThreadDataT *__packetThreadData = NULL; // Exported in os.h
|
||||
RuntimeDataT __runtimeData; // Exported in runtime.h
|
||||
|
||||
|
||||
static MouseT *_mouse = NULL;
|
||||
static ImageT *_pointer = NULL;
|
||||
|
@ -71,8 +74,6 @@ static void eventLoop(void);
|
|||
static uint8_t hasValidSettings(void);
|
||||
static void shutdown(void);
|
||||
static uint8_t startup(int argc, char *argv[]);
|
||||
static void tableLoad(void);
|
||||
static void tableSave(void);
|
||||
|
||||
|
||||
static void checkSettings(void) {
|
||||
|
@ -124,13 +125,12 @@ static void shutdown(void) {
|
|||
|
||||
imageUnload(&_pointer);
|
||||
|
||||
tableSave();
|
||||
|
||||
netShutdown();
|
||||
guiShutdown();
|
||||
mouseShutdown();
|
||||
surfaceShutdown();
|
||||
vbeShutdown();
|
||||
runtimeShutdown();
|
||||
cacheShutdown();
|
||||
dbShutdown();
|
||||
configShutdown();
|
||||
|
@ -190,6 +190,7 @@ static uint8_t startup(int argc, char *argv[]) {
|
|||
|
||||
dbStartup();
|
||||
cacheStartup(argv[0]);
|
||||
runtimeStartup();
|
||||
surfaceStartup();
|
||||
mouseStartup();
|
||||
guiStartup();
|
||||
|
@ -198,102 +199,10 @@ static uint8_t startup(int argc, char *argv[]) {
|
|||
_pointer = imageLoadCache("gui:mouse.png");
|
||||
_alpha = imagePixelGet(_pointer, 5, 0);
|
||||
|
||||
tableLoad();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void tableLoad(void) {
|
||||
FILE *cache = NULL;
|
||||
char *line = NULL;
|
||||
char *p = NULL;
|
||||
char *temp = NULL;
|
||||
|
||||
__runtimeData.integers = NULL;
|
||||
__runtimeData.strings = NULL;
|
||||
__runtimeData.protocolVersion = 0;
|
||||
|
||||
sh_new_strdup(__runtimeData.integers);
|
||||
sh_new_strdup(__runtimeData.strings);
|
||||
|
||||
// ***TODO*** Default initial tables
|
||||
|
||||
line = (char *)malloc(4096);
|
||||
if (line) {
|
||||
// Load string cache.
|
||||
cache = cacheFOpen("data:strings.dat", "rt");
|
||||
if (cache) {
|
||||
while (fscanf(cache, "%s\n", line) != EOF) {
|
||||
p = strstr(line, "=");
|
||||
if (p) {
|
||||
*p = 0;
|
||||
p++;
|
||||
// Do we have this string already?
|
||||
temp = shget(__runtimeData.strings, line);
|
||||
if (temp) {
|
||||
DEL(temp);
|
||||
shdel(__runtimeData.strings, line);
|
||||
}
|
||||
shput(__runtimeData.strings, line, strdup(p));
|
||||
}
|
||||
}
|
||||
cacheFClose(cache);
|
||||
}
|
||||
// Load integer cache.
|
||||
cache = cacheFOpen("data:integers.dat", "rt");
|
||||
if (cache) {
|
||||
while (fscanf(cache, "%s\n", line) != EOF) {
|
||||
p = strstr(line, "=");
|
||||
if (p) {
|
||||
*p = 0;
|
||||
p++;
|
||||
shput(__runtimeData.integers, line, atol(p));
|
||||
}
|
||||
}
|
||||
cacheFClose(cache);
|
||||
}
|
||||
free(line);
|
||||
line = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void tableSave(void) {
|
||||
FILE *cache = NULL;
|
||||
|
||||
// Save & free integer table.
|
||||
cache = cacheFOpen("data:integers.dat", "wt");
|
||||
if (cache) {
|
||||
if (__runtimeData.integers) {
|
||||
while (shlen(__runtimeData.integers) > 0) {
|
||||
//logWrite("[%s]=[%d]\n", __runtimeData.integers[0].key, __runtimeData.integers[0].value);
|
||||
fprintf(cache, "%s=%ld\n", __runtimeData.integers[0].key, (long)__runtimeData.integers[0].value);
|
||||
shdel(__runtimeData.integers, __runtimeData.integers[0].key);
|
||||
}
|
||||
shfree(__runtimeData.integers);
|
||||
}
|
||||
cacheFClose(cache);
|
||||
}
|
||||
|
||||
// Save & free string table.
|
||||
cache = cacheFOpen("data:strings.dat", "wt");
|
||||
if (cache) {
|
||||
if (__runtimeData.strings) {
|
||||
while (shlen(__runtimeData.strings) > 0) {
|
||||
//logWrite("[%s]=[%s]\n", __runtimeData.strings[0].key, __runtimeData.strings[0].value);
|
||||
fprintf(cache, "%s=%s\n", __runtimeData.strings[0].key, __runtimeData.strings[0].value);
|
||||
DEL(__runtimeData.strings[0].value);
|
||||
shdel(__runtimeData.strings, __runtimeData.strings[0].key);
|
||||
}
|
||||
shfree(__runtimeData.strings);
|
||||
}
|
||||
cacheFClose(cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern void browserShow(void);
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
* Kangaroo Punch MultiPlayer Game Server Mark II
|
||||
* Copyright (C) 2020-2021 Scott Duensing
|
||||
*
|
||||
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "network.h"
|
||||
#include "vesa.h"
|
||||
#include "db.h"
|
||||
|
||||
#include "taglist.h"
|
||||
#include "msgbox.h"
|
||||
|
@ -36,17 +37,16 @@
|
|||
static void btnMsgBoxLogoff(MsgBoxButtonT button);
|
||||
static void btnMsgBoxOkay(MsgBoxButtonT button);
|
||||
static void menuEnable(uint8_t enable);
|
||||
static void menuFilesReady(void);
|
||||
static void menuFilesReady(char *updatedFile);
|
||||
static void picChatClick(WidgetT *widget);
|
||||
static void picEmailClick(WidgetT *widget);
|
||||
static void picForumsClick(WidgetT *widget);
|
||||
static void picGamesClick(WidgetT *widget);
|
||||
static void picLogoffClick(WidgetT *widget);
|
||||
static void picProfileClick(WidgetT *widget);
|
||||
static void updateGameDatabase(void);
|
||||
|
||||
|
||||
static char *_shaGamesDat = NULL;
|
||||
|
||||
static PictureT *_picChat = NULL;
|
||||
static PictureT *_picEmail = NULL;
|
||||
static PictureT *_picForums = NULL;
|
||||
|
@ -91,15 +91,23 @@ static void menuEnable(uint8_t enable) {
|
|||
}
|
||||
|
||||
|
||||
static void menuFilesReady(void) {
|
||||
uint16_t x = vbeDisplayWidthGet() - 49;
|
||||
uint16_t y = vbeDisplayHeightGet() - 49;
|
||||
char *p;
|
||||
static void menuFilesReady(char *updatedFile) {
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
|
||||
BEGIN
|
||||
|
||||
//***TODO*** Load into database if changed.
|
||||
p = cacheSha256Get("generated:games.dat");
|
||||
// Did a file get updated or are we done?
|
||||
if (updatedFile) {
|
||||
// Was this the game database?
|
||||
if (strcmp(updatedFile, "generated:games.dat") == 0) {
|
||||
// Load changes into database.
|
||||
updateGameDatabase();
|
||||
}
|
||||
} else {
|
||||
// Show UI
|
||||
x = vbeDisplayWidthGet() - 49;
|
||||
y = vbeDisplayHeightGet() - 49;
|
||||
|
||||
_picLogoff = pictureNew(x, y, "menu:48logoff.png");
|
||||
pictureClickHandlerSet(_picLogoff, picLogoffClick);
|
||||
|
@ -129,13 +137,13 @@ static void menuFilesReady(void) {
|
|||
_picProfile = pictureNew(x, y, "menu:48profile.png");
|
||||
pictureClickHandlerSet(_picProfile, picProfileClick);
|
||||
guiAttach(guiRootGet(), W(_picProfile));
|
||||
}
|
||||
|
||||
END
|
||||
}
|
||||
|
||||
|
||||
void menuShow(void) {
|
||||
char *p;
|
||||
char *fileList[] = {
|
||||
"menu:48chat.png",
|
||||
"menu:48email.png",
|
||||
|
@ -149,14 +157,6 @@ void menuShow(void) {
|
|||
|
||||
BEGIN
|
||||
|
||||
// Keep old SHA to know if we need to reload after updating.
|
||||
p = cacheSha256Get("generated:games.dat");
|
||||
if (p) {
|
||||
_shaGamesDat = strdup(p);
|
||||
} else {
|
||||
_shaGamesDat = NULL;
|
||||
}
|
||||
|
||||
fileCacheCheck(menuFilesReady, fileList);
|
||||
|
||||
END
|
||||
|
@ -208,3 +208,49 @@ static void picProfileClick(WidgetT *widget) {
|
|||
menuEnable(0);
|
||||
msgBoxOne("Options", MSGBOX_ICON_MESSAGE, "Yeah, this doesn't do anything yet.", "Okay", btnMsgBoxOkay);
|
||||
}
|
||||
|
||||
|
||||
static void updateGameDatabase(void) {
|
||||
FILE *f = NULL;
|
||||
uint8_t c;
|
||||
|
||||
// Do we need to create the game database?
|
||||
dbExecute(
|
||||
"CREATE TABLE IF NOT EXISTS games ("
|
||||
"title VARCHAR(255), "
|
||||
"publisher VARCHAR(255), "
|
||||
"developer VARCHAR(255), "
|
||||
"description TEXT, "
|
||||
"releaseDate VARCHAR(255), "
|
||||
"rating VARCHAR(255), "
|
||||
"series VARCHAR(255), "
|
||||
"origin VARCHAR(255), "
|
||||
"shortName VARCHAR(8) PRIMARY KEY, "
|
||||
"worksWith VARCHAR(255), "
|
||||
"type VARCHAR(8), "
|
||||
"maxPlayers INTEGER, "
|
||||
"joinable BOOLEAN, "
|
||||
"screens INTEGER, "
|
||||
"boxes INTEGER, "
|
||||
"touched BOOLEAN "
|
||||
")",
|
||||
NULL);
|
||||
|
||||
// Mark everything untouched.
|
||||
dbExecute("UPDATE games SET touced=0", NULL);
|
||||
|
||||
// Process latest downloaded game list.
|
||||
f = cacheFOpen("generated:games.dat", "rb");
|
||||
if (f) {
|
||||
while (1) {
|
||||
// Get next byte.
|
||||
c = fgetc(f);
|
||||
// End of file?
|
||||
if (feof(f)) break;
|
||||
|
||||
}
|
||||
cacheFClose(f);
|
||||
// Delete anything untouched.
|
||||
dbExecute("DELETE FROM games WHERE touced=0", NULL);
|
||||
}
|
||||
}
|
||||
|
|
130
client/src/runtime.c
Normal file
130
client/src/runtime.c
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Kangaroo Punch MultiPlayer Game Server Mark II
|
||||
* Copyright (C) 2020-2021 Scott Duensing
|
||||
*
|
||||
* 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "db.h"
|
||||
#include "array.h"
|
||||
|
||||
#include "runtime.h"
|
||||
|
||||
|
||||
RuntimeDataT __runtimeData;
|
||||
|
||||
|
||||
void runtimeDataLoad(void) {
|
||||
uint16_t count = 0;
|
||||
char ***records = NULL;
|
||||
char **fields = NULL;
|
||||
|
||||
dbQueryMultiple(&records, "SELECT name, data FROM data", NULL);
|
||||
if (records) {
|
||||
for (count = 0; count < arrlen(records); count++) {
|
||||
fields = records[count];
|
||||
if (fields) {
|
||||
shput(__runtimeData.strings, fields[0], strdup(fields[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbResultRelease(&records);
|
||||
|
||||
/*
|
||||
logWrite("\n\n");
|
||||
for (int r = 0; r < shlen(__runtimeData.strings); r++) {
|
||||
logWrite("%s = %s\n", __runtimeData.strings[r].key, __runtimeData.strings[r].value);
|
||||
}
|
||||
logWrite("\n\n");
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
void runtimeDataUpdate(void) {
|
||||
FILE *f;
|
||||
uint8_t c;
|
||||
char name[33];
|
||||
char data[1025];
|
||||
uint8_t which = 0; // 0 = Reading name, 1 = Reading data.
|
||||
uint16_t x = 0;
|
||||
|
||||
f = cacheFOpen("generated:client.dat", "rb");
|
||||
if (f) {
|
||||
while (1) {
|
||||
// Get next byte.
|
||||
c = fgetc(f);
|
||||
// End of file?
|
||||
if (feof(f)) break;
|
||||
// End of string?
|
||||
if (c == 0) {
|
||||
if (which == 0) {
|
||||
// Got the name, move on to data.
|
||||
which++;
|
||||
name[x] = 0;
|
||||
x = 0;
|
||||
} else {
|
||||
// Got data. Write both to database and start over.
|
||||
which = 0;
|
||||
data[x] = 0;
|
||||
x = 0;
|
||||
dbExecute(
|
||||
"REPLACE INTO data (name, data) VALUES (?, ?)",
|
||||
"vv",
|
||||
name, data
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Got byte.
|
||||
if (which == 0) {
|
||||
name[x++] = c;
|
||||
} else {
|
||||
data[x++] = c;
|
||||
}
|
||||
}
|
||||
cacheFClose(f);
|
||||
}
|
||||
|
||||
runtimeDataLoad();
|
||||
}
|
||||
|
||||
|
||||
void runtimeShutdown(void) {
|
||||
// Free string table.
|
||||
while (shlen(__runtimeData.strings) > 0) {
|
||||
DEL(__runtimeData.strings[0].value);
|
||||
shdel(__runtimeData.strings, __runtimeData.strings[0].key);
|
||||
}
|
||||
shfree(__runtimeData.strings);
|
||||
}
|
||||
|
||||
|
||||
void runtimeStartup(void) {
|
||||
|
||||
__runtimeData.strings = NULL;
|
||||
__runtimeData.protocolVersion = 0;
|
||||
|
||||
sh_new_strdup(__runtimeData.strings);
|
||||
|
||||
// Do we need to create the client runtime config database?
|
||||
dbExecute(
|
||||
"CREATE TABLE IF NOT EXISTS data ("
|
||||
"name VARCHAR(32) PRIMARY KEY, "
|
||||
"data VARCHAR(1024) "
|
||||
")",
|
||||
NULL);
|
||||
}
|
|
@ -25,25 +25,28 @@
|
|||
#include "os.h"
|
||||
|
||||
|
||||
#define RUNTIME_INT(k) (atoi(shget(__runtimeData.strings, (k))))
|
||||
#define RUNTIME_STR(k) (shget(__runtimeData.strings, (k)))
|
||||
|
||||
|
||||
typedef struct StringMapS {
|
||||
char *key;
|
||||
char *value;
|
||||
} StringMapT;
|
||||
|
||||
typedef struct IntegerMapS {
|
||||
char *key;
|
||||
int32_t value;
|
||||
} IntegerMapT;
|
||||
|
||||
|
||||
typedef struct RuntimeDataS {
|
||||
uint32_t protocolVersion;
|
||||
StringMapT *strings;
|
||||
IntegerMapT *integers;
|
||||
} RuntimeDataT;
|
||||
|
||||
|
||||
extern RuntimeDataT __runtimeData;
|
||||
|
||||
|
||||
void runtimeDataLoad(void);
|
||||
void runtimeDataUpdate(void);
|
||||
void runtimeShutdown(void);
|
||||
void runtimeStartup(void);
|
||||
|
||||
|
||||
#endif // RUNTIME_H
|
||||
|
|
|
@ -188,7 +188,7 @@ void signupShow(void) {
|
|||
T_TITLE, P(" User Name:"),
|
||||
T_X, 40, T_Y, 64,
|
||||
T_WIDTH, 300,
|
||||
T_LENGTH, shget(__runtimeData.integers, "maxUser"),
|
||||
T_LENGTH, RUNTIME_INT("maxUser"),
|
||||
T_TEXTBOX, T_DONE,
|
||||
|
||||
T_TEXTBOX, O(_txtPass1),
|
||||
|
@ -196,7 +196,7 @@ void signupShow(void) {
|
|||
T_TITLE, P(" Password:"),
|
||||
T_X, 40, T_Y, 94,
|
||||
T_WIDTH, 300,
|
||||
T_LENGTH, shget(__runtimeData.integers, "maxPass"),
|
||||
T_LENGTH, RUNTIME_INT("maxPass"),
|
||||
T_MASK, '*',
|
||||
T_TEXTBOX, T_DONE,
|
||||
|
||||
|
@ -205,7 +205,7 @@ void signupShow(void) {
|
|||
T_TITLE, P(" Password (again):"),
|
||||
T_X, 40, T_Y, 124,
|
||||
T_WIDTH, 300,
|
||||
T_LENGTH, shget(__runtimeData.integers, "maxPass"),
|
||||
T_LENGTH, RUNTIME_INT("maxPass"),
|
||||
T_MASK, '*',
|
||||
T_TEXTBOX, T_DONE,
|
||||
|
||||
|
@ -214,7 +214,7 @@ void signupShow(void) {
|
|||
T_TITLE, P(" REAL First Name:"),
|
||||
T_X, 40, T_Y, 154,
|
||||
T_WIDTH, 300,
|
||||
T_LENGTH, shget(__runtimeData.integers, "maxName"),
|
||||
T_LENGTH, RUNTIME_INT("maxName"),
|
||||
T_TEXTBOX, T_DONE,
|
||||
|
||||
T_TEXTBOX, O(_txtLast),
|
||||
|
@ -222,7 +222,7 @@ void signupShow(void) {
|
|||
T_TITLE, P(" REAL Last Name:"),
|
||||
T_X, 40, T_Y, 184,
|
||||
T_WIDTH, 300,
|
||||
T_LENGTH, shget(__runtimeData.integers, "maxName"),
|
||||
T_LENGTH, RUNTIME_INT("maxName"),
|
||||
T_TEXTBOX, T_DONE,
|
||||
|
||||
T_TEXTBOX, O(_txtEmail),
|
||||
|
@ -230,7 +230,7 @@ void signupShow(void) {
|
|||
T_TITLE, P(" VALID E-Mail:"),
|
||||
T_X, 40, T_Y, 214,
|
||||
T_WIDTH, 300,
|
||||
T_LENGTH, shget(__runtimeData.integers, "maxEmail"),
|
||||
T_LENGTH, RUNTIME_INT("maxEmail"),
|
||||
T_TEXTBOX, T_DONE,
|
||||
|
||||
T_BUTTON, O(_btnCancel),
|
||||
|
@ -363,8 +363,8 @@ static uint8_t validateName(char *username) {
|
|||
char *allowed;
|
||||
char needle[2];
|
||||
|
||||
if ((int32_t)strlen(username) < shget(__runtimeData.integers, "minName")) return 0;
|
||||
if ((int32_t)strlen(username) > shget(__runtimeData.integers, "maxName")) return 0;
|
||||
if ((int32_t)strlen(username) < RUNTIME_INT("minName")) return 0;
|
||||
if ((int32_t)strlen(username) > RUNTIME_INT("maxName")) return 0;
|
||||
|
||||
allowed = shget(__runtimeData.strings, "nameAllowed");
|
||||
needle[1] = 0;
|
||||
|
@ -389,13 +389,13 @@ static uint8_t validatePassword(char *password) {
|
|||
char *passNumeric;
|
||||
char needle[2];
|
||||
|
||||
if ((int32_t)strlen(password) < shget(__runtimeData.integers, "minPass")) return 0;
|
||||
if ((int32_t)strlen(password) > shget(__runtimeData.integers, "maxPass")) return 0;
|
||||
if ((int32_t)strlen(password) < RUNTIME_INT("minPass")) return 0;
|
||||
if ((int32_t)strlen(password) > RUNTIME_INT("maxPass")) return 0;
|
||||
|
||||
passLower = shget(__runtimeData.strings, "passLower");
|
||||
passUpper = shget(__runtimeData.strings, "passUpper");
|
||||
passSpecial = shget(__runtimeData.strings, "passSpecial");
|
||||
passNumeric = shget(__runtimeData.strings, "passNumeric");
|
||||
passLower = RUNTIME_STR("passLower");
|
||||
passUpper = RUNTIME_STR("passUpper");
|
||||
passSpecial = RUNTIME_STR("passSpecial");
|
||||
passNumeric = RUNTIME_STR("passNumeric");
|
||||
needle[1] = 0;
|
||||
for (x=0; x<strlen(password); x++) {
|
||||
needle[0] = password[x];
|
||||
|
@ -405,10 +405,10 @@ static uint8_t validatePassword(char *password) {
|
|||
if (strstr(passNumeric, needle)) numericCount++;
|
||||
}
|
||||
|
||||
if (lowerCount < shget(__runtimeData.integers, "minPassLower")) return 0;
|
||||
if (upperCount < shget(__runtimeData.integers, "minPassUpper")) return 0;
|
||||
if (specialCount < shget(__runtimeData.integers, "minPassSpecial")) return 0;
|
||||
if (numericCount < shget(__runtimeData.integers, "minPassNumeric")) return 0;
|
||||
if (lowerCount < RUNTIME_INT("minPassLower")) return 0;
|
||||
if (upperCount < RUNTIME_INT("minPassUpper")) return 0;
|
||||
if (specialCount < RUNTIME_INT("minPassSpecial")) return 0;
|
||||
if (numericCount < RUNTIME_INT("minPassNumeric")) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -419,10 +419,10 @@ static uint8_t validateUser(char *username) {
|
|||
char *allowed;
|
||||
char needle[2];
|
||||
|
||||
if ((int32_t)strlen(username) < shget(__runtimeData.integers, "minUser")) return 0;
|
||||
if ((int32_t)strlen(username) > shget(__runtimeData.integers, "maxUser")) return 0;
|
||||
if ((int32_t)strlen(username) < RUNTIME_INT("minUser")) return 0;
|
||||
if ((int32_t)strlen(username) > RUNTIME_INT("maxUser")) return 0;
|
||||
|
||||
allowed = shget(__runtimeData.strings, "userAllowed");
|
||||
allowed = RUNTIME_STR("userAllowed");
|
||||
needle[1] = 0;
|
||||
for (x=0; x<strlen(username); x++) {
|
||||
needle[0] = username[x];
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
|
||||
#include "db.h"
|
||||
#include "array.h"
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
|
@ -91,16 +92,72 @@ uint8_t dbExecute(char *sql, char *format, ...) {
|
|||
}
|
||||
|
||||
|
||||
uint8_t dbQuerySingle(char rformat, void **result, char *sql, char *format, ...) {
|
||||
uint8_t dbQueryMultiple(char ****result, char *sql, char *format, ...) {
|
||||
va_list args;
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
int32_t r;
|
||||
static double d;
|
||||
static int32_t i;
|
||||
uint8_t cols;
|
||||
uint8_t x;
|
||||
char ***records = NULL;
|
||||
char **fields = NULL;
|
||||
|
||||
if (format == NULL) {
|
||||
// No parameters, just run it.
|
||||
r = sqlite3_prepare_v2(_db, sql, -1, &stmt, NULL);
|
||||
} else {
|
||||
va_start(args, format);
|
||||
stmt = dbSqlBind(sql, format, args);
|
||||
va_end(args);
|
||||
r = SQLITE_OK;
|
||||
}
|
||||
|
||||
while (r == SQLITE_OK || r == SQLITE_ROW) {
|
||||
r = sqlite3_step(stmt);
|
||||
if (r == SQLITE_ROW) {
|
||||
cols = sqlite3_data_count(stmt);
|
||||
x = 0;
|
||||
while (cols-- > 0) {
|
||||
arrput(fields, strdup((char *)sqlite3_column_text(stmt, x++)));
|
||||
}
|
||||
arrput(records, fields);
|
||||
fields = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
logWrite("\n\n");
|
||||
for (int r = 0; r < arrlen(records); r++) {
|
||||
fields = records[r];
|
||||
for (int f = 0; f < arrlen(fields); f++) {
|
||||
logWrite("%s\t", fields[f]);
|
||||
}
|
||||
logWrite("\n");
|
||||
}
|
||||
logWrite("\n\n");
|
||||
*/
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
*result = records;
|
||||
|
||||
return (r == SQLITE_OK || r == SQLITE_DONE) ? SUCCESS : FAIL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t dbQuerySingle(char rformat, void **result, char *sql, char *format, ...) {
|
||||
va_list args;
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
int32_t r = 0;
|
||||
static double d = 0;
|
||||
static int32_t i = 0;
|
||||
static char *v = NULL;
|
||||
|
||||
if (v) DEL(v);
|
||||
|
||||
// If everything is NULL, we're cleaning up internal variables (which we do before this). Just exit.
|
||||
if (rformat == 0 && result == NULL && sql == NULL && format == NULL) return SUCCESS;
|
||||
|
||||
if (format == NULL) {
|
||||
// No parameters, just run it.
|
||||
r = sqlite3_prepare_v2(_db, sql, -1, &stmt, NULL);
|
||||
|
@ -140,11 +197,32 @@ uint8_t dbQuerySingle(char rformat, void **result, char *sql, char *format, ...)
|
|||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return r == SQLITE_OK ? SUCCESS : FAIL;
|
||||
return (r == SQLITE_OK || r == SQLITE_DONE || r == SQLITE_ROW) ? SUCCESS : FAIL;
|
||||
}
|
||||
|
||||
|
||||
void dbResultRelease(char ****result) {
|
||||
char ***records = *result;
|
||||
char **fields = NULL;
|
||||
char *value = NULL;
|
||||
|
||||
while (arrlen(records) > 0) {
|
||||
fields = records[0];
|
||||
while (arrlen(fields) > 0) {
|
||||
value = fields[0];
|
||||
DEL(value);
|
||||
arrdel(fields, 0);
|
||||
}
|
||||
arrdel(records, 0);
|
||||
}
|
||||
arrfree(records);
|
||||
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
|
||||
void dbShutdown(void) {
|
||||
dbQuerySingle(0, NULL, NULL, NULL); // Release memory held by dbQuerySingle.
|
||||
sqlite3_close(_db);
|
||||
_db = NULL;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
|
||||
|
||||
uint8_t dbExecute(char *sql, char *format, ...);
|
||||
uint8_t dbQueryMultiple(char ****result, char *sql, char *format, ...);
|
||||
uint8_t dbQuerySingle(char rformat, void **result, char *sql, char *format, ...);
|
||||
void dbResultRelease(char ****result);
|
||||
void dbShutdown(void);
|
||||
void dbStartup(void);
|
||||
|
||||
|
|
|
@ -298,6 +298,7 @@ void dbGameRelease(DbGameT **game) {
|
|||
DEL(g->series);
|
||||
DEL(g->origin);
|
||||
DEL(g->shortName);
|
||||
DEL(g->worksWith);
|
||||
DEL(g);
|
||||
}
|
||||
}
|
||||
|
@ -312,6 +313,8 @@ DbGameT **dbGamesGet(void) {
|
|||
int32_t i = 0;
|
||||
DbGameT **gameList = NULL;
|
||||
DbGameT *game = NULL;
|
||||
char filename[MAX_PATH];
|
||||
int32_t x = 0;
|
||||
|
||||
pthread_mutex_lock(&_mutex);
|
||||
|
||||
|
@ -321,8 +324,8 @@ DbGameT **dbGamesGet(void) {
|
|||
}
|
||||
|
||||
p += sprintf(p,
|
||||
"SELECT title, publisher, developer, description, releaseDate, "
|
||||
"rating, series, origin, shortName, type, maxPlayers, joinable "
|
||||
"SELECT title, publisher, developer, description, releaseDate, rating, series, "
|
||||
"origin, shortName, worksWith, type, maxPlayers, joinable "
|
||||
"FROM games WHERE active=1");
|
||||
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||
logWrite("dbGamesGet: %s\n", mysql_error(_sql));
|
||||
|
@ -357,13 +360,31 @@ DbGameT **dbGamesGet(void) {
|
|||
game->series = strdup(row[6]);
|
||||
game->origin = strdup(row[7]);
|
||||
game->shortName = strdup(row[8]);
|
||||
game->worksWith = strdup(row[9]);
|
||||
game->type = GAME_TYPE_UNKNOWN;
|
||||
game->maxPlayers = atoi(row[10]);
|
||||
game->joinable = atoi(row[11]);
|
||||
if (!strcasecmp(row[9], "DOOR")) game->type = GAME_TYPE_DOOR;
|
||||
if (!strcasecmp(row[9], "SERIAL")) game->type = GAME_TYPE_SERIAL;
|
||||
if (!strcasecmp(row[9], "IPX")) game->type = GAME_TYPE_IPX;
|
||||
if (!strcasecmp(row[9], "FICTION")) game->type = GAME_TYPE_FICTION;
|
||||
if (!strcasecmp(row[10], "DOOR")) game->type = GAME_TYPE_DOOR;
|
||||
if (!strcasecmp(row[10], "SERIAL")) game->type = GAME_TYPE_SERIAL;
|
||||
if (!strcasecmp(row[10], "IPX")) game->type = GAME_TYPE_IPX;
|
||||
if (!strcasecmp(row[10], "FICTION")) game->type = GAME_TYPE_FICTION;
|
||||
game->maxPlayers = atoi(row[11]);
|
||||
game->joinable = atoi(row[12]);
|
||||
|
||||
// Find screenshot and box image count from filesystem.
|
||||
x = 0;
|
||||
while (1) {
|
||||
snprintf(filename, MAX_PATH, "%s/games/%c/%s/screen%d.png", __settingsFile, game->shortName[0], game->shortName, x + 1);
|
||||
if (!utilFileExists(filename)) break;
|
||||
x++;
|
||||
}
|
||||
game->screens = x;
|
||||
x = 0;
|
||||
while (1) {
|
||||
snprintf(filename, MAX_PATH, "%s/games/%c/%s/box%d.png", __settingsFile, game->shortName[0], game->shortName, x + 1);
|
||||
if (!utilFileExists(filename)) break;
|
||||
x++;
|
||||
}
|
||||
game->boxes = x;
|
||||
|
||||
arrput(gameList, game);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,9 +62,12 @@ typedef struct DbGameS {
|
|||
char *series;
|
||||
char *origin;
|
||||
char *shortName;
|
||||
char *worksWith;
|
||||
DbGameTypeT type;
|
||||
uint8_t maxPlayers;
|
||||
uint8_t joinable;
|
||||
uint8_t screens;
|
||||
uint8_t boxes;
|
||||
} DbGameT;
|
||||
|
||||
|
||||
|
|
|
@ -233,9 +233,12 @@ static void updateGames(void) {
|
|||
fwrite(game->series, strlen(game->series) + 1, 1, f);
|
||||
fwrite(game->origin, strlen(game->origin) + 1, 1, f);
|
||||
fwrite(game->shortName, strlen(game->shortName) + 1, 1, f);
|
||||
fwrite(game->worksWith, strlen(game->worksWith) + 1, 1, f);
|
||||
c = game->type; fputc(c, f);
|
||||
c = game->maxPlayers; fputc(c, f);
|
||||
c = game->joinable; fputc(c, f);
|
||||
c = game->screens; fputc(c, f);
|
||||
c = game->boxes; fputc(c, f);
|
||||
|
||||
dbGameRelease(&game);
|
||||
arrdel(gameList, 0);
|
||||
|
|
|
@ -105,6 +105,18 @@ void utilDie(const char *why, ...) {
|
|||
}
|
||||
|
||||
|
||||
uint8_t utilFileExists(char *filename) {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
|
||||
if (f) {
|
||||
fclose(f);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
||||
char **utilWrapText(char *text, uint16_t width) {
|
||||
char **lines = NULL;
|
||||
char *head = text;
|
||||
|
|
|
@ -30,6 +30,7 @@ void utilBitsPrint(uint8_t byte);
|
|||
char *utilCreateString(char *format, ...);
|
||||
char *utilCreateStringVArgs(char *format, va_list args);
|
||||
void utilDie(const char *why, ...);
|
||||
uint8_t utilFileExists(char *filename);
|
||||
char **utilWrapText(char *text, uint16_t width);
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue