New configuration system working. Game database import started.

This commit is contained in:
Scott Duensing 2022-04-09 19:35:23 -05:00
parent 61ab16512f
commit 794bd96d3f
17 changed files with 461 additions and 256 deletions

View file

@ -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 \

View file

@ -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);
}

View file

@ -25,7 +25,7 @@
#include "os.h"
typedef void (*fileCallback)(void);
typedef void (*fileCallback)(char *updatedFile);
void fileCacheCheck(fileCallback callback, char *vpaths[]);

View file

@ -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);

View file

@ -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,74 +112,78 @@ 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.
// ***TODO*** We used to have a FORGOT PASSWORD link here, too.
TagItemT uiLogin[] = {
T_START,
T_WINDOW, O(_winLogin),
T_TITLE, P("Login"),
T_WIDTH, 300, T_HEIGHT, 155,
T_TEXTBOX, O(_txtUser),
T_TITLE, P("User Name:"),
T_X, 42, T_Y, 10,
T_WIDTH, 200,
T_LENGTH, shget(__runtimeData.integers, "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_MASK, '*',
T_TEXTBOX, T_DONE,
T_BUTTON, O(_btnCancel),
T_TITLE, P("Cancel"),
T_X, 25, T_Y, 85,
T_CLICK, P(btnCancelClick),
T_BUTTON, T_DONE,
T_BUTTON, O(_btnSignUp),
T_TITLE, P("Sign Up"),
T_X, 110, T_Y, 85,
T_CLICK, P(btnSignUpClick),
T_BUTTON, T_DONE,
T_BUTTON, O(_btnLogin),
TagItemT uiLogin[] = {
T_START,
T_WINDOW, O(_winLogin),
T_TITLE, P("Login"),
T_X, 199, T_Y, 85,
T_CLICK, P(btnLoginClick),
T_BUTTON, T_DONE,
T_TIMER, O(_timProgress),
T_EVENT, P(timLoginProgress),
T_VALUE, 0,
T_ENABLED, 0,
T_TIMER, T_DONE,
T_WIDTH, 300, T_HEIGHT, 155,
T_WINDOW, T_DONE,
T_END
};
T_TEXTBOX, O(_txtUser),
T_TITLE, P("User Name:"),
T_X, 42, T_Y, 10,
T_WIDTH, 200,
T_LENGTH, RUNTIME_INT("maxUser"),
T_TEXTBOX, T_DONE,
tagListRun(uiLogin);
_channel = netChannelGet(packetHandler);
T_TEXTBOX, O(_txtPass),
T_TITLE, P(" Password:"),
T_X, 42, T_Y, 40,
T_WIDTH, 200,
T_LENGTH, RUNTIME_INT("maxPass"),
T_MASK, '*',
T_TEXTBOX, T_DONE,
textboxValueSet(_txtUser, "test");
textboxValueSet(_txtPass, "test");
T_BUTTON, O(_btnCancel),
T_TITLE, P("Cancel"),
T_X, 25, T_Y, 85,
T_CLICK, P(btnCancelClick),
T_BUTTON, T_DONE,
T_BUTTON, O(_btnSignUp),
T_TITLE, P("Sign Up"),
T_X, 110, T_Y, 85,
T_CLICK, P(btnSignUpClick),
T_BUTTON, T_DONE,
T_BUTTON, O(_btnLogin),
T_TITLE, P("Login"),
T_X, 199, T_Y, 85,
T_CLICK, P(btnLoginClick),
T_BUTTON, T_DONE,
T_TIMER, O(_timProgress),
T_EVENT, P(timLoginProgress),
T_VALUE, 0,
T_ENABLED, 0,
T_TIMER, T_DONE,
T_WINDOW, T_DONE,
T_END
};
tagListRun(uiLogin);
_channel = netChannelGet(packetHandler);
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

View file

@ -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[]) {

View file

@ -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,51 +91,59 @@ 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);
guiAttach(guiRootGet(), W(_picLogoff));
x -= 49;
_picLogoff = pictureNew(x, y, "menu:48logoff.png");
pictureClickHandlerSet(_picLogoff, picLogoffClick);
guiAttach(guiRootGet(), W(_picLogoff));
x -= 49;
_picForums = pictureNew(x, y, "menu:48forums.png");
pictureClickHandlerSet(_picForums, picForumsClick);
guiAttach(guiRootGet(), W(_picForums));
x -= 49;
_picForums = pictureNew(x, y, "menu:48forums.png");
pictureClickHandlerSet(_picForums, picForumsClick);
guiAttach(guiRootGet(), W(_picForums));
x -= 49;
_picEmail = pictureNew(x, y, "menu:48email.png");
pictureClickHandlerSet(_picEmail, picEmailClick);
guiAttach(guiRootGet(), W(_picEmail));
x -= 49;
_picEmail = pictureNew(x, y, "menu:48email.png");
pictureClickHandlerSet(_picEmail, picEmailClick);
guiAttach(guiRootGet(), W(_picEmail));
x -= 49;
_picGames = pictureNew(x, y, "menu:48games.png");
pictureClickHandlerSet(_picGames, picGamesClick);
guiAttach(guiRootGet(), W(_picGames));
x -= 49;
_picGames = pictureNew(x, y, "menu:48games.png");
pictureClickHandlerSet(_picGames, picGamesClick);
guiAttach(guiRootGet(), W(_picGames));
x -= 49;
_picChat = pictureNew(x, y, "menu:48chat.png");
pictureClickHandlerSet(_picChat, picChatClick);
guiAttach(guiRootGet(), W(_picChat));
x -= 49;
_picChat = pictureNew(x, y, "menu:48chat.png");
pictureClickHandlerSet(_picChat, picChatClick);
guiAttach(guiRootGet(), W(_picChat));
x -= 49;
_picProfile = pictureNew(x, y, "menu:48profile.png");
pictureClickHandlerSet(_picProfile, picProfileClick);
guiAttach(guiRootGet(), W(_picProfile));
_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
View 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);
}

View file

@ -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

View file

@ -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];

View file

@ -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 dbQueryMultiple(char ****result, char *sql, char *format, ...) {
va_list args;
sqlite3_stmt *stmt = NULL;
int32_t r;
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;
static double d;
static int32_t i;
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;
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -25,12 +25,13 @@
#include "os.h"
char *utilAppNameWithNewExtensionGet(char *appName, char *extension);
void utilBitsPrint(uint8_t byte);
char *utilCreateString(char *format, ...);
char *utilCreateStringVArgs(char *format, va_list args);
void utilDie(const char *why, ...);
char **utilWrapText(char *text, uint16_t width);
char *utilAppNameWithNewExtensionGet(char *appName, char *extension);
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);
#endif // UTIL_H