Searching local game database works.

This commit is contained in:
Scott Duensing 2022-04-18 20:08:00 -05:00
parent 88369132e3
commit 377e40f28f
13 changed files with 352 additions and 112 deletions

View file

@ -72,6 +72,7 @@ HEADERS = \
$$SHARED/primes.h \
$$SHARED/packet.h \
$$SHARED/packets.h \
../shared/game.h \
../shared/macros.h \
src/browser.h \
src/config.h \

View file

@ -27,28 +27,29 @@
#include "button.h"
#include "frame.h"
#include "listbox.h"
#include "textbox.h"
#include "timer.h"
#include "db.h"
#include "network.h"
#include "util.h"
static WindowT *_winBrowser = NULL;
static ButtonT *_btnBoxTab = NULL;
static ButtonT *_btnDescriptionTab = NULL;
static ButtonT *_btnInfoTab = NULL;
static ButtonT *_btnScreensTab = NULL;
static TimerT *_timSearchUpdate = NULL;
static ButtonT *_btnSearchTab = NULL;
static uint8_t _channel = 0;
static FrameT *_fraBox = NULL;
static FrameT *_fraDescription = NULL;
static FrameT *_fraInfo = NULL;
static FrameT *_fraScreens = NULL;
static FrameT *_fraSearch = NULL;
static ListboxT *_lstDescription = NULL;
static ListboxT *_lstInfo = NULL;
static TextboxT *_txtSearch = NULL;
static ListboxT *_lstResults = NULL;
static ButtonT *_btnDescriptionTab = NULL;
static FrameT *_fraDescription = NULL;
static PictureT *_picBanner = NULL;
static PictureT *_picBox1 = NULL;
static PictureT *_picBox2 = NULL;
static ListboxT *_lstDescription = NULL;
static ButtonT *_btnScreensTab = NULL;
static FrameT *_fraScreens = NULL;
static PictureT *_picThumb1 = NULL;
static PictureT *_picThumb2 = NULL;
static PictureT *_picThumb3 = NULL;
@ -56,14 +57,36 @@ static PictureT *_picThumb4 = NULL;
static PictureT *_picThumb5 = NULL;
static PictureT *_picThumb6 = NULL;
static ButtonT *_btnBoxTab = NULL;
static FrameT *_fraBox = NULL;
static PictureT *_picBox1 = NULL;
static PictureT *_picBox2 = NULL;
static ButtonT *_btnInfoTab = NULL;
static FrameT *_fraInfo = NULL;
static ListboxT *_lstInfo = NULL;
static uint8_t _channel = 0;
static char *_lastSearchText = NULL;
static void browserHide(void);
static void btnTabClick(WidgetT *widget);
static void packetHandler(PacketDecodeDataT *packet);
static void timSearchUpdate(WidgetT *widget);
static void browserHide(void) {
timerStop(_timSearchUpdate);
DEL(_lastSearchText);
netChannelRelease(_channel);
guiDelete(D(_winBrowser));
}
void browserShow(void) {
char **lines = NULL;
//char **lines = NULL;
TagItemT uiBrowser[] = {
T_START,
@ -81,7 +104,18 @@ void browserShow(void) {
T_X, 5, T_Y, 32,
T_WIDTH, 500, T_HEIGHT, 250,
T_TITLE, P("Search"),
T_VISIBLE, T_FALSE,
T_TEXTBOX, O(_txtSearch),
T_TITLE, P("Game, Publisher, or Developer:"),
T_X, 5, T_Y, 3,
T_WIDTH, 490,
T_LENGTH, 255,
T_TEXTBOX, T_DONE,
T_LISTBOX, O(_lstResults),
T_X, 1, T_Y, 35,
T_WIDTH, 493, T_HEIGHT, 190, // Works out to 57 characters wide, 13 high.
T_LISTBOX, T_DONE,
T_FRAME, T_DONE,
// GAME
@ -89,6 +123,7 @@ void browserShow(void) {
T_X, 78, T_Y, 5,
T_TITLE, P("Game"),
T_CLICK, P(btnTabClick),
T_ENABLED, T_FALSE,
T_BUTTON, T_DONE,
T_FRAME, O(_fraDescription),
T_X, 5, T_Y, 32,
@ -98,7 +133,7 @@ void browserShow(void) {
T_PICTURE, O(_picBanner),
T_X, 1, // 490PX wide
T_FILENAME, P("banner.png"),
T_CACHENAME, P("games:d:descent:banner.png"), // ***TODO*** This needs a "browser:no-banner.png" image.
T_PICTURE, T_DONE,
T_LISTBOX, O(_lstDescription),
T_X, 1, T_Y, 105,
@ -112,6 +147,7 @@ void browserShow(void) {
T_X, 135, T_Y, 5,
T_TITLE, P("Screens"),
T_CLICK, P(btnTabClick),
T_ENABLED, T_FALSE,
T_BUTTON, T_DONE,
T_FRAME, O(_fraScreens),
T_X, 5, T_Y, 32,
@ -121,28 +157,33 @@ void browserShow(void) {
T_PICTURE, O(_picThumb1), // Thumbs are 160x100
T_X, 1, T_Y, 5,
T_FILENAME, P("thumb1.png"),
T_CACHENAME, P("browser:no-screen.png"),
T_PICTURE, T_DONE,
T_PICTURE, O(_picThumb2),
T_X, 167, T_Y, 5,
T_FILENAME, P("thumb2.png"),
T_CACHENAME, P("browser:no-screen.png"),
T_VISIBLE, T_FALSE,
T_PICTURE, T_DONE,
T_PICTURE, O(_picThumb3),
T_X, 333, T_Y, 5,
T_FILENAME, P("thumb3.png"),
T_CACHENAME, P("browser:no-screen.png"),
T_VISIBLE, T_FALSE,
T_PICTURE, T_DONE,
T_PICTURE, O(_picThumb4),
T_X, 1, T_Y, 116,
T_FILENAME, P("thumb4.png"),
T_CACHENAME, P("browser:no-screen.png"),
T_VISIBLE, T_FALSE,
T_PICTURE, T_DONE,
T_PICTURE, O(_picThumb5),
T_X, 167, T_Y, 116,
T_FILENAME, P("thumb5.png"),
T_CACHENAME, P("browser:no-screen.png"),
T_VISIBLE, T_FALSE,
T_PICTURE, T_DONE,
T_PICTURE, O(_picThumb6),
T_X, 333, T_Y, 116,
T_FILENAME, P("thumb6.png"),
T_CACHENAME, P("browser:no-screen.png"),
T_VISIBLE, T_FALSE,
T_PICTURE, T_DONE,
T_FRAME, T_DONE,
@ -152,6 +193,7 @@ void browserShow(void) {
T_X, 216, T_Y, 5,
T_TITLE, P("Box"),
T_CLICK, P(btnTabClick),
T_ENABLED, T_FALSE,
T_BUTTON, T_DONE,
T_FRAME, O(_fraBox),
T_X, 5, T_Y, 32,
@ -161,11 +203,12 @@ void browserShow(void) {
T_PICTURE, O(_picBox1), // Thumbs are 240x210
T_X, 1, T_Y, 5,
T_FILENAME, P("box1.png"),
T_CACHENAME, P("browser:no-box.png"),
T_PICTURE, T_DONE,
T_PICTURE, O(_picBox2),
T_X, 250, T_Y, 5,
T_FILENAME, P("box2.png"),
T_CACHENAME, P("browser:no-box.png"),
T_VISIBLE, T_FALSE,
T_PICTURE, T_DONE,
T_FRAME, T_DONE,
@ -175,12 +218,13 @@ void browserShow(void) {
T_X, 265, T_Y, 5,
T_TITLE, P("Info"),
T_CLICK, P(btnTabClick),
T_ENABLED, T_FALSE,
T_BUTTON, T_DONE,
T_FRAME, O(_fraInfo),
T_X, 5, T_Y, 32,
T_WIDTH, 500, T_HEIGHT, 250,
T_TITLE, P("Information"),
T_VISIBLE, T_TRUE,
T_VISIBLE, T_FALSE,
T_LISTBOX, O(_lstInfo),
T_X, 1, T_Y, 5,
@ -189,19 +233,24 @@ void browserShow(void) {
T_FRAME, T_DONE,
T_TIMER, O(_timSearchUpdate),
T_EVENT, P(timSearchUpdate),
T_VALUE, 3,
T_ENABLED, 1,
T_TIMER, T_DONE,
T_WINDOW, T_DONE,
T_END
};
tagListRun(uiBrowser);
guiFocusSet(W(_txtSearch));
_channel = netChannelGet(packetHandler);
guiDebugAreaShow(W(_btnSearchTab));
guiDebugAreaShow(W(_btnDescriptionTab));
guiDebugAreaShow(W(_btnScreensTab));
guiDebugAreaShow(W(_btnBoxTab));
guiDebugAreaShow(W(_btnInfoTab));
_lastSearchText = strdup("");
/*
guiDebugAreaShow(W(_fraSearch));
lines = utilWrapText(
"The Post-Terran Minerals Corporation (PTMC) digs up minerals on all nine planets of the solar system, "
@ -216,23 +265,24 @@ void browserShow(void) {
arrdel(lines, 0);
}
listboxItemAdd(_lstInfo, "123456789012345678901234567890123456789012345678901234567");
listboxItemAdd(_lstInfo, "2");
listboxItemAdd(_lstInfo, "3");
listboxItemAdd(_lstInfo, "4");
listboxItemAdd(_lstInfo, "5");
listboxItemAdd(_lstInfo, "6");
listboxItemAdd(_lstInfo, "7");
listboxItemAdd(_lstInfo, "8");
listboxItemAdd(_lstInfo, "9");
listboxItemAdd(_lstInfo, "10");
listboxItemAdd(_lstInfo, "11");
listboxItemAdd(_lstInfo, "12");
listboxItemAdd(_lstInfo, "13");
listboxItemAdd(_lstInfo, "14");
listboxItemAdd(_lstInfo, "15");
listboxItemAdd(_lstInfo, "16");
listboxItemAdd(_lstInfo, "17");
listboxItemAdd(_lstResults, "123456789012345678901234567890123456789012345678901234567");
listboxItemAdd(_lstResults, "2");
listboxItemAdd(_lstResults, "3");
listboxItemAdd(_lstResults, "4");
listboxItemAdd(_lstResults, "5");
listboxItemAdd(_lstResults, "6");
listboxItemAdd(_lstResults, "7");
listboxItemAdd(_lstResults, "8");
listboxItemAdd(_lstResults, "9");
listboxItemAdd(_lstResults, "10");
listboxItemAdd(_lstResults, "11");
listboxItemAdd(_lstResults, "12");
listboxItemAdd(_lstResults, "13");
listboxItemAdd(_lstResults, "14");
listboxItemAdd(_lstResults, "15");
listboxItemAdd(_lstResults, "16");
listboxItemAdd(_lstResults, "17");
*/
}
@ -255,3 +305,43 @@ static void btnTabClick(WidgetT *widget) {
static void packetHandler(PacketDecodeDataT *packet) {
}
static void timSearchUpdate(WidgetT *widget) {
char ***records = NULL;
char **fields = NULL;
uint16_t count = 0;
char query[258]; // 255 search characters, two %'s, and the terminating null.
(void)widget;
// Are there more than two characters in the search field?
if (textboxValueGet(_txtSearch) && strlen(textboxValueGet(_txtSearch)) > 2) {
// Did the search contents change?
if (strcmp(_lastSearchText, textboxValueGet(_txtSearch))) {
// Remember what was in the search box.
DEL(_lastSearchText);
_lastSearchText = strdup(textboxValueGet(_txtSearch));
// Clear current search results.
listboxItemsClear(_lstResults);
// Execute search.
snprintf(query, 258, "%%%s%%", _lastSearchText);
dbQueryMultiple(&records,
"SELECT title FROM games WHERE "
"title LIKE ? OR developer LIKE ? OR publisher LIKE ? "
"ORDER BY title",
"vvv",
query,
query,
query);
// Did we get anything?
if (records) {
for (count = 0; count < arrlen(records); count++) {
fields = records[count];
listboxItemAdd(_lstResults, fields[0]);
}
dbResultRelease(&records);
}
}
}
}

View file

@ -44,15 +44,8 @@ static void listboxSizesRecalculate(ListboxT *listbox);
static void listboxDel(WidgetT **widget) {
ListboxT *l = (ListboxT *)*widget;
size_t len = arrlenu(l->values);
size_t x;
if (len > 0) {
for (x=0; x<len; x++) {
free(l->values[x]);
}
}
arrfree(l->values);
listboxItemsClear(l);
}
@ -102,6 +95,7 @@ void listboxItemRemove(ListboxT *listbox, char *item) {
if (len > 0) {
for (x=0; x<len; x++) {
if (strcmp(item, listbox->values[x]) == 0) {
DEL(listbox->values[x]);
arrdel(listbox->values, x);
if (listbox->selected > len || listbox->selected > 0) listbox->selected--;
GUI_SET_FLAG((WidgetT *)listbox, WIDGET_FLAG_DIRTY);
@ -112,6 +106,17 @@ void listboxItemRemove(ListboxT *listbox, char *item) {
}
void listboxItemsClear(ListboxT *listbox) {
while (arrlen(listbox->values) > 0) {
DEL(listbox->values[0]);
arrdel(listbox->values, 0);
}
arrfree(listbox->values);
listbox->selected = 0;
}
static void listboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) {
ListboxT *l = (ListboxT *)widget;

View file

@ -41,6 +41,7 @@ void listboxIndexSet(ListboxT *listbox, uint16_t index);
WidgetT *listboxInit(WidgetT *widget, char *title);
void listboxItemAdd(ListboxT *listbox, char *item);
void listboxItemRemove(ListboxT *listbox, char *item);
void listboxItemsClear(ListboxT *listbox);
ListboxT *listboxNew(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title);
void listboxStepSet(ListboxT *listbox, int32_t step);
void listboxTitleSet(ListboxT *listbox, char *title);

View file

@ -211,6 +211,7 @@ static uint8_t startup(int argc, char *argv[]) {
extern void browserShow(void);
extern void updateGameDatabase(void);
int main(int argc, char *argv[]) {
@ -221,8 +222,9 @@ int main(int argc, char *argv[]) {
// Perform "first run" setup tasks or start the client?
if (hasValidSettings()) {
// We have what we need, start the client.
welcomeShow();
//browserShow();
//welcomeShow();
updateGameDatabase();
browserShow();
eventLoop();
} else {
// Run the setup.

View file

@ -21,6 +21,7 @@
#include "network.h"
#include "vesa.h"
#include "db.h"
#include "game.h"
#include "taglist.h"
#include "msgbox.h"
@ -44,7 +45,7 @@ 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 */ void updateGameDatabase(void);
static PictureT *_picChat = NULL;
@ -154,6 +155,7 @@ void menuShow(void) {
"menu:48profile.png",
"generated:games.dat",
// LOAD THE WORLD! This is for debugging the browser.
"games:d:descent:banner.png",
"games:d:descent:box2.png",
"games:d:descent:screen1.png",
@ -355,9 +357,51 @@ static void picProfileClick(WidgetT *widget) {
}
static void updateGameDatabase(void) {
uint8_t utilFromFileReadByte(FILE *f, uint8_t *result) {
unsigned char c;
// Get next byte.
c = fgetc(f);
// End of file?
if (feof(f)) return FAIL;
// Add to result.
*result = c;
return SUCCESS;
}
uint8_t utilFromFileReadString(FILE *f, char **result) {
unsigned char c;
char buffer[GAME_MAX_DESCRIPTION];
uint16_t x = 0;
// Something already in 'result'?
DEL(*result);
while (1) {
// Get next byte.
c = fgetc(f);
// End of file?
if (feof(f)) return FAIL;
// Add to result.
buffer[x++] = c;
buffer[x] = 0;
// End of string?
if (c == 0) {
*result = strdup(buffer);
return SUCCESS;
}
}
}
/* static */ void updateGameDatabase(void) {
FILE *f = NULL;
uint8_t c;
uint8_t result;
GameT game;
// Clear 'game'.
memset(&game, 0, sizeof(GameT));
// Do we need to create the game database?
dbExecute(
@ -372,7 +416,7 @@ static void updateGameDatabase(void) {
"origin VARCHAR(255), "
"shortName VARCHAR(8) PRIMARY KEY, "
"worksWith VARCHAR(255), "
"type VARCHAR(8), "
"type INTEGER, "
"maxPlayers INTEGER, "
"joinable BOOLEAN, "
"screens INTEGER, "
@ -382,22 +426,85 @@ static void updateGameDatabase(void) {
NULL);
// Mark everything untouched.
dbExecute("UPDATE games SET touced=0", NULL);
dbExecute("UPDATE games SET touched=0", NULL);
/*
// Process latest downloaded game list.
f = cacheFOpen("generated:games.dat", "rb");
if (f) {
result = FAIL;
while (1) {
// Get next byte.
c = fgetc(f);
// End of file?
if (feof(f)) break;
// If this first read fails, we're out of data. Any other read fails, we have problems.
if (!utilFromFileReadString(f, &game.title)) {
result = SUCCESS;
break;
}
if (!utilFromFileReadString(f, &game.publisher)) break;
if (!utilFromFileReadString(f, &game.developer)) break;
if (!utilFromFileReadString(f, &game.description)) break;
if (!utilFromFileReadString(f, &game.releaseDate)) break;
if (!utilFromFileReadString(f, &game.rating)) break;
if (!utilFromFileReadString(f, &game.series)) break;
if (!utilFromFileReadString(f, &game.origin)) break;
if (!utilFromFileReadString(f, &game.shortName)) break;
if (!utilFromFileReadString(f, &game.worksWith)) break;
if (!utilFromFileReadByte(f, (uint8_t *)&game.type)) break;
if (!utilFromFileReadByte(f, &game.maxPlayers)) break;
if (!utilFromFileReadByte(f, &game.joinable)) break;
if (!utilFromFileReadByte(f, &game.screens)) break;
if (!utilFromFileReadByte(f, &game.boxes)) break;
// Write this to the database.
dbExecute(
"REPLACE INTO games ("
"shortName, title, publisher, developer, description, releaseDate, "
"rating, series, origin, worksWith, type, maxPlayers, joinable, "
"screens, boxes, touched"
") VALUES ("
"?, ?, ?, ?, ?, ?, "
"?, ?, ?, ?, ?, ?, ?, "
"?, ?, 1 "
")",
"vvvvvv"
"vvvviii"
"iii",
game.shortName,
game.title,
game.publisher,
game.developer,
game.description,
game.releaseDate,
game.rating,
game.series,
game.origin,
game.worksWith,
game.type,
game.maxPlayers,
game.joinable,
game.screens,
game.boxes
);
}
cacheFClose(f);
// Delete anything untouched.
dbExecute("DELETE FROM games WHERE touced=0", NULL);
dbExecute("DELETE FROM games WHERE touched=0", NULL);
// Free 'game'.
DEL(game.title);
DEL(game.publisher);
DEL(game.developer);
DEL(game.description);
DEL(game.releaseDate);
DEL(game.rating);
DEL(game.series);
DEL(game.origin);
DEL(game.shortName);
DEL(game.worksWith);
//***TODO*** Did we have problems updating the game database?
if (result == FAIL) {
// Do something here.
}
}
*/
}

View file

@ -40,10 +40,9 @@ void runtimeDataLoad(void) {
shput(__runtimeData.strings, fields[0], strdup(fields[1]));
}
}
dbResultRelease(&records);
}
dbResultRelease(&records);
/*
logWrite("\n\n");
for (int r = 0; r < shlen(__runtimeData.strings); r++) {

View file

@ -109,6 +109,8 @@ static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *
if (!client->authenticated && mustAuth) {
logWrite("Unauthenticated user requested file: %s\n", path);
clientApiFileFailed(client, data);
DEL(sha256);
DEL(path);
return;
}
@ -165,13 +167,13 @@ static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *
packetSend(client->packetThreadData, &encoded);
DEL(packetData);
DEL(sha256);
DEL(path);
// Start sending actual file data.
clientApiFileRequestNext(client, data);
}
DEL(sha256);
DEL(path);
END
}

View file

@ -286,8 +286,8 @@ static void dbFileSha256Create(char *file, char *buf) {
}
void dbGameRelease(DbGameT **game) {
DbGameT *g = *game;
void dbGameRelease(GameT **game) {
GameT *g = *game;
if (g) {
DEL(g->title);
@ -305,15 +305,15 @@ void dbGameRelease(DbGameT **game) {
}
DbGameT **dbGamesGet(void) {
GameT **dbGamesGet(void) {
char statement[STATEMENT_MAX];
char *p = statement;
MYSQL_RES *result = NULL;
MYSQL_ROW row = NULL;
int32_t count = 0;
int32_t i = 0;
DbGameT **gameList = NULL;
DbGameT *game = NULL;
GameT **gameList = NULL;
GameT *game = NULL;
char filename[MAX_PATH];
int32_t x = 0;
@ -351,7 +351,7 @@ DbGameT **dbGamesGet(void) {
pthread_mutex_unlock(&_mutex);
return NULL;
}
NEW(DbGameT, game);
NEW(GameT, game);
game->title = strdup(row[0]);
game->publisher = strdup(row[1]);
game->developer = strdup(row[2]);

View file

@ -23,20 +23,12 @@
#include "os.h"
#include "game.h"
#define DB_CONFIG_ITEM_SIZE 1024
typedef enum DbGameTypeE {
GAME_TYPE_UNKNOWN = 0,
GAME_TYPE_DOOR,
GAME_TYPE_SERIAL,
GAME_TYPE_IPX,
GAME_TYPE_FICTION
} DbGameTypeT;
typedef struct DbClientConfigS {
char *name;
char *data;
@ -52,24 +44,6 @@ typedef struct DbFileInfoS {
uint8_t touched;
} DbFileInfoT;
typedef struct DbGameS {
char *title;
char *publisher;
char *developer;
char *description;
char *releaseDate;
char *rating;
char *series;
char *origin;
char *shortName;
char *worksWith;
DbGameTypeT type;
uint8_t maxPlayers;
uint8_t joinable;
uint8_t screens;
uint8_t boxes;
} DbGameT;
uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password);
void dbClientConfigRelease(DbClientConfigT **config);
@ -77,8 +51,8 @@ DbClientConfigT **dbClientConfigGet(void);
uint8_t dbDisconnect(void);
DbFileInfoT *dbFileInfoGet(char *vpath);
void dbFileInfoRelease(DbFileInfoT **info);
void dbGameRelease(DbGameT **game);
DbGameT **dbGamesGet(void);
void dbGameRelease(GameT **game);
GameT **dbGamesGet(void);
uint8_t dbSettingsStringGet(char *host, char *key, char *value, uint32_t max);
uint8_t dbSettingsValueGet(char *host, char *key, int32_t *value);
uint8_t dbUpdateFileData(char *file, uint64_t len, char *time);

View file

@ -209,8 +209,8 @@ static int32_t updateFileEntry(const char *filepath, const struct stat *info, co
static void updateGames(void) {
DbGameT **gameList = NULL;
DbGameT *game = NULL;
GameT **gameList = NULL;
GameT *game = NULL;
char file[MAX_PATH] = { 0 };
FILE *f = NULL;
uint8_t c = 0;

59
shared/game.h Normal file
View file

@ -0,0 +1,59 @@
/*
* 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/>.
*
*/
#ifndef GAME_H
#define GAME_H
#include "os.h"
#define GAME_MAX_DESCRIPTION 4096
typedef enum GameTypeE {
GAME_TYPE_UNKNOWN = 0,
GAME_TYPE_DOOR,
GAME_TYPE_SERIAL,
GAME_TYPE_IPX,
GAME_TYPE_FICTION
} GameTypeT;
typedef struct GameS { // Data type in DAT file sent to client.
char *title; // 255
char *publisher; // 255
char *developer; // 255
char *description; // TEXT
char *releaseDate; // DATETIME
char *rating; // 255
char *series; // 255
char *origin; // 255
char *shortName; // 8
char *worksWith; // 8
GameTypeT type; // 1
uint8_t maxPlayers; // 1
uint8_t joinable; // 1
uint8_t screens; // 1
uint8_t boxes; // 1
} GameT;
#endif // GAME_H

View file

@ -38,7 +38,7 @@
// Allocation helpers.
#define NEW(t,v) (v)=(t*)malloc(sizeof(t))
#define DEL(v) {free(v); v=NULL;}
#define DEL(v) {if(v) {free(v); v=NULL;}}
// Some helper defines.
#define DIVISIBLE_BY_EIGHT(x) ((((x) >> 3) << 3) == (x))