diff --git a/.gitignore b/.gitignore index 8a94e30..0230785 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ retired/ test/ doors/ client/src/thirdparty/sqlite-3.4.2/build +client/src/thirdparty/sqlite-3.4.2/build-linux */out/ diff --git a/Makefile.djgpp b/Makefile.djgpp index 11b0d21..d3e4d37 100644 --- a/Makefile.djgpp +++ b/Makefile.djgpp @@ -33,13 +33,11 @@ BINDIR = bin # CFLAGS, LDFLAGS, CPPFLAGS, PREFIX can be overriden on CLI CFLAGS := $(DEBUG) -#CFLAGS += -DALLEGRONOTAVAIL -DALLEGRONOTPROGS -DDZCOMM_SRC CFLAGS += -I$(SRCDIR)/client/src -I$(SRCDIR)/client/src/system -I$(SRCDIR)/client/src/dos -I$(SRCDIR)/client/src/gui -I$(SRCDIR)/client/src/thirdparty CFLAGS += -I$(SRCDIR)/client/src/thirdparty/sqlite-3.4.2/build -I$(SRCDIR)/shared -I$(SRCDIR)/shared/thirdparty -#CFLAGS += -I$(SRCDIR)/client/src/thirdparty/dzcomm -I$(SRCDIR)/client/src/thirdparty/dzcomm/include CPPFLAGS := -LDFLAGS := -L$(SRCDIR)/client/src/thirdparty/sqlite-3.4.2/build/.libs -lsqlite3 -#LDFLAGS += -L$(SRCDIR)/client/src/thirdparty/dzcomm -ldzcom +LDFLAGS := -L$(SRCDIR)/client/src/thirdparty/sqlite-3.4.2/build/.libs +LDLIBS := -lsqlite3 PREFIX := /usr/local TARGET_ARCH := @@ -54,7 +52,7 @@ ALL_CPPFLAGS := $(CPPFLAGS) # Linker Flags ALL_LDFLAGS := $(LDFLAGS) -ALL_LDLIBS := -lc +ALL_LDLIBS := $(LDLIBS) -lc # Source, Binaries, Dependencies @@ -64,8 +62,6 @@ DEP := $(OBJ:.o=.d) BIN := $(BINDIR)/$(TARGET) -include $(DEP) -#obj/client/src/thirdparty/dzcomm/src/dos/djirqs.o - #$(info [${SRC}]) #$(info [${OBJ}]) diff --git a/buildPrerequisites.sh b/buildPrerequisites.sh index 65f6208..f587269 100755 --- a/buildPrerequisites.sh +++ b/buildPrerequisites.sh @@ -85,7 +85,7 @@ PATH=/opt/cross/djgpp/bin:$PATH #createEmbeddedBinary vga8x14 dat ../../client/src/embedded #popd -# Build SQLite +# Build SQLite for DOS pushd client/src/thirdparty/sqlite-3.4.2 [[ -d build ]] && rm -rf build mkdir -p build @@ -97,3 +97,13 @@ sh ../configure \ --host=i586-pc-msdosdjgpp make popd + +# Build SQLite for Linux +pushd client/src/thirdparty/sqlite-3.4.2 +[[ -d build-linux ]] && rm -rf build +mkdir -p build-linux +cd build-linux +sh ../configure \ + --disable-tcl +make +popd diff --git a/client/client.pro b/client/client.pro index 2283235..b8f7e90 100644 --- a/client/client.pro +++ b/client/client.pro @@ -30,11 +30,9 @@ QMAKE_CFLAGS += -O0 DEFINES *= CLIENT -UNUSED_CRAP = \ - src/thirdparty/dzcomm/include/dzcomm.h - DOS_HEADERS = \ - src/thirdparty/serial/serial.h + src/thirdparty/serial/serial.h \ + src/thirdparty/sqlite-3.4.2/build/sqlite.h DOS_SOURCES = \ src/thirdparty/serial/serial.c \ @@ -43,10 +41,12 @@ DOS_SOURCES = \ src/dos/vesa.c LINUX_INCLUDES = \ - $$PWD/src/linux + src/linux \ + src/thirdparty/sqlite-3.4.2/build-linux LINUX_HEADERS = \ - $$SHARED/thirdparty/enet/include/enet.h + $$SHARED/thirdparty/enet/include/enet.h \ + src/thirdparty/sqlite-3.4.2/build-linux/sqlite.h LINUX_SOURCES = \ src/linux/linux.c @@ -54,9 +54,9 @@ LINUX_SOURCES = \ INCLUDEPATH += \ $$LINUX_INCLUDES \ $$SHARED \ - $$PWD/src/system \ - $$PWD/src/gui \ - $$PWD/src + src/system \ + src/gui \ + src HEADERS = \ $$LINUX_HEADERS \ @@ -82,6 +82,7 @@ HEADERS = \ src/runtime.h \ src/signup.h \ src/system/cache.h \ + src/system/db.h \ src/thirdparty/SHA256/sha256.h \ src/thirdparty/minicoro/minicoro.h \ src/system/comport.h \ @@ -133,6 +134,7 @@ SOURCES = \ src/hangup.c \ src/system/cache.c \ src/system/comport.c \ + src/system/db.c \ src/system/os.c \ src/system/surface.c \ src/system/network.c \ @@ -166,6 +168,8 @@ SOURCES = \ src/settings.c LIBS = \ + -L$$PWD/src/thirdparty/sqlite-3.4.2/build-linux/.libs \ + -lsqlite3 \ -lSDL2 \ -lSDL2_image diff --git a/client/src/file.c b/client/src/file.c index 354789d..b9a8f5d 100644 --- a/client/src/file.c +++ b/client/src/file.c @@ -27,6 +27,7 @@ #include "label.h" #include "timer.h" #include "msgbox.h" +#include "image.h" #include "file.h" #include "hangup.h" @@ -232,6 +233,7 @@ static void packetHandler(PacketDecodeDataT *packet) { _handle = NULL; // Update cache entry to include SHA. cacheEntryAdd(_currentSha256, cacheEntryNameGet(_file), _file); + imageCacheIfNeeded(_file, _currentSha256); // If this was an image, decompress it now. DEL(_currentSha256); // Next file! fileCheckNext(); diff --git a/client/src/gui/image.c b/client/src/gui/image.c index a53668c..c1c3068 100644 --- a/client/src/gui/image.c +++ b/client/src/gui/image.c @@ -18,6 +18,8 @@ */ +#include "ctype.h" + #define STB_IMAGE_IMPLEMENTATION #define STBI_ONLY_PNG #include "image.h" @@ -26,6 +28,9 @@ #include "vesa.h" +#define PIXEL_COMPONENTS 3 + + static ImageT *imageNativeImageGet(unsigned char *raw, uint32_t x, uint32_t y); @@ -64,6 +69,59 @@ ImageT *imageAllocate(uint16_t w, uint16_t h) { } +void imageCacheIfNeeded(char *cacheFilename, char *sha256) { + int16_t len; + char ext[4]; + uint32_t x = 0; + uint32_t y = 0; + uint32_t n = 0; + unsigned char *raw = NULL; + char *temp = NULL; + char *rawSha = NULL; + FILE *f = NULL; + + // This function expects a cache filename, not a real filesystem filename! + + len = strlen(cacheFilename); + + // Is this something we care about? + if (len > 4) { + if (cacheFilename[len - 4] == '.') { + ext[0] = toupper(cacheFilename[len - 3]); + ext[1] = toupper(cacheFilename[len - 2]); + ext[2] = toupper(cacheFilename[len - 1]); + ext[3] = 0; + if ((strcmp(ext, "PNG") == 0) || (strcmp(ext, "GIF") == 0) || (strcmp(ext, "JPG") == 0)) { + // It's an image we care about. Is it decompressed already? + temp = (char *)malloc(strlen(cacheFilename) + 5); + if (!temp) return; + sprintf(temp, "%s.raw", cacheFilename); + rawSha = cacheSha256Get(temp); + if (!rawSha || strcmp(rawSha, sha256) != 0) { + // The decompressed file either does not exist or the SHA does not match the file we're checking. + logWrite("Unpacking %s - %s\n", cacheFilenameGet(cacheFilename), cacheFilename); + raw = stbi_load(cacheFilenameGet(cacheFilename), (int *)&x, (int *)&y, (int *)&n, PIXEL_COMPONENTS); + if (!raw) return; + // Write unpacked version to disk. + f = cacheFOpen(temp, "wb"); + if (f) { + fwrite(&x, sizeof(uint32_t), 1, f); + fwrite(&y, sizeof(uint32_t), 1, f); + fwrite(raw, x * y * PIXEL_COMPONENTS, 1, f); + cacheFClose(f); + } + free(raw); + // Update SHA on unpacked image. + cacheEntryAdd(sha256, cacheEntryNameGet(temp), temp); + logWrite("Created %s - %s\n", cacheFilenameGet(temp), temp); + } + free(temp); + } + } + } +} + + ImageT *imageCreate(uint16_t w, uint16_t h, ColorT color) { uint16_t x; uint16_t y; @@ -88,7 +146,7 @@ ImageT *imageFromRAMLoad(uint8_t *buffer, size_t len) { unsigned char *raw; // Load image from RAM. - raw = stbi_load_from_memory((const unsigned char *)buffer, len, (int *)&x, (int *)&y, (int *)&n, 3); + raw = stbi_load_from_memory((const unsigned char *)buffer, len, (int *)&x, (int *)&y, (int *)&n, PIXEL_COMPONENTS); if (!raw) return NULL; return imageNativeImageGet(raw, x, y); @@ -117,20 +175,83 @@ uint8_t imageInfoGet(char *filename, uint16_t *width, uint16_t *height) { } -ImageT *imageLoad(char *filename) { - uint32_t x; - uint32_t y; - uint32_t n; - unsigned char *raw; +uint8_t imageInfoGetCache(char *virtualPath, uint16_t *width, uint16_t *height) { + int w; // Using boring old compiler 'int' on purpose. + int h; + int n; + int r; + char *file; - // Load image from disk - raw = stbi_load(filename, (int *)&x, (int *)&y, (int *)&n, 3); + file = cacheFilenameGet(virtualPath); + if (file) { + r = stbi_info(file, &w, &h, &n); + if (r) { + *width = w; + *height = h; + return SUCCESS; + } + } + + return FAIL; +} + + +ImageT *imageLoad(char *filename) { + uint32_t x = 0;; + uint32_t y = 0; + uint32_t n = 0; + unsigned char *raw = NULL; + + // Load compressed image from disk + raw = stbi_load(filename, (int *)&x, (int *)&y, (int *)&n, PIXEL_COMPONENTS); if (!raw) return NULL; return imageNativeImageGet(raw, x, y); } +ImageT *imageLoadCache(char *virtualPath) { + uint32_t x = 0;; + uint32_t y = 0; + uint32_t n = 0; + unsigned char *raw = NULL; + char *temp = NULL; + char *file = NULL; + FILE *f = NULL; + + // Get name of uncompressed version. + temp = (char *)malloc(strlen(virtualPath) + 5); + if (!temp) return NULL; + sprintf(temp, "%s.raw", virtualPath); + + file = cacheFilenameGet(temp); + if (file && osFileExists(file)) { + // Read unpacked version from disk. + logWrite("Loaded %s\n", file); + f = fopen(file, "rb"); + if (f) { + fread(&x, sizeof(uint32_t), 1, f); + fread(&y, sizeof(uint32_t), 1, f); + raw = (unsigned char *)malloc(x * y * PIXEL_COMPONENTS); + if (raw) { + fread(raw, x * y * PIXEL_COMPONENTS, 1, f); + } + fclose(f); + } + } else { + // Load compressed image from disk + file = cacheFilenameGet(virtualPath); + logWrite("Loaded %s\n", file); + raw = stbi_load(file, (int *)&x, (int *)&y, (int *)&n, PIXEL_COMPONENTS); + if (!raw) return NULL; + } + + free(temp); + + return imageNativeImageGet(raw, x, y); +} + + static ImageT *imageNativeImageGet(unsigned char *raw, uint32_t x, uint32_t y) { uint32_t b; ImageT *image; @@ -147,7 +268,7 @@ static ImageT *imageNativeImageGet(unsigned char *raw, uint32_t x, uint32_t y) { for (y=0; yheight; y++) { for (x=0; xwidth; x++) { image->pixels[x][y] = vbeColorMake(raw[b], raw[b + 1], raw[b + 2]); - b += 3; + b += PIXEL_COMPONENTS; } } diff --git a/client/src/gui/image.h b/client/src/gui/image.h index 4c943c3..74e6a7f 100644 --- a/client/src/gui/image.h +++ b/client/src/gui/image.h @@ -36,11 +36,14 @@ typedef struct ImageS { ImageT *imageAllocate(uint16_t w, uint16_t h); +void imageCacheIfNeeded(char *cacheFilename, char *sha256); ImageT *imageCreate(uint16_t w, uint16_t h, ColorT color); ImageT *imageFromRAMLoad(uint8_t *buffer, size_t len); uint16_t imageHeightGet(ImageT *image); uint8_t imageInfoGet(char *filename, uint16_t *width, uint16_t *height); +uint8_t imageInfoGetCache(char *virtualPath, uint16_t *width, uint16_t *height); ImageT *imageLoad(char *filename); +ImageT *imageLoadCache(char *virtualPath); ColorT imagePixelGet(ImageT *image, uint16_t x, uint16_t y); void imageRender(ImageT *image, uint16_t x, uint16_t y); void imageRenderHalf(ImageT *image, uint16_t x, uint16_t y); diff --git a/client/src/gui/listbox.c b/client/src/gui/listbox.c index ae50147..ebba214 100644 --- a/client/src/gui/listbox.c +++ b/client/src/gui/listbox.c @@ -377,7 +377,7 @@ void listboxStepSet(ListboxT *listbox, int32_t step) { void listboxTitleSet(ListboxT *listbox, char *title) { if (listbox->title) free(listbox->title); - listbox->title = strdup(title); + if (title) listbox->title = strdup(title); listboxSizesRecalculate(listbox); GUI_SET_FLAG((WidgetT *)listbox, WIDGET_FLAG_DIRTY); } diff --git a/client/src/gui/msgbox.c b/client/src/gui/msgbox.c index be49329..024f519 100644 --- a/client/src/gui/msgbox.c +++ b/client/src/gui/msgbox.c @@ -184,7 +184,7 @@ void msgBoxThree(char *title, MsgBoxIconT icon, char *message, char *buttonOne, // Load proper icon, if desired. if (icon > MSGBOX_ICON_NONE && icon < MSGBOX_ICON_COUNT) { - context->picIcon = pictureNew(x, y, cacheFilenameGet(_iconFiles[icon - 1])); + context->picIcon = pictureNew(x, y, _iconFiles[icon - 1]); guiAttach(W(context->winDialog), W(context->picIcon)); } diff --git a/client/src/gui/picture.c b/client/src/gui/picture.c index 99a0bde..f11db64 100644 --- a/client/src/gui/picture.c +++ b/client/src/gui/picture.c @@ -49,7 +49,7 @@ WidgetT *pictureInit(WidgetT *widget, char *filename) { l->clicked = NULL; l->zoom = PICTURE_FULL; - l->image = imageLoad(l->filename); + l->image = imageLoadCache(l->filename); if (!l->image) { free(l->filename); return NULL; @@ -81,7 +81,7 @@ PictureT *pictureNew(uint16_t x, uint16_t y, char *filename) { if (!picture) return NULL; - if (!imageInfoGet(filename, &w, &h)) { + if (!imageInfoGetCache(filename, &w, &h)) { free(picture); return NULL; } diff --git a/client/src/gui/taglist.c b/client/src/gui/taglist.c index 0186f9b..9613628 100644 --- a/client/src/gui/taglist.c +++ b/client/src/gui/taglist.c @@ -22,6 +22,7 @@ #include "gui.h" #include "widget.h" +#include "cache.h" #include "button.h" #include "checkbox.h" @@ -84,6 +85,7 @@ static void tagListWidgetAttributeHandle(void) { WidgetT *parent; char *title; char *filename; + char *cachename; RectT pos; uint8_t hasWidth; uint8_t hasHeight; @@ -119,6 +121,7 @@ static void tagListWidgetAttributeHandle(void) { // Reset all attributes for this widget. title = NULL; filename = NULL; + cachename = NULL; pos.x = 0; pos.y = 0; pos.w = 0; @@ -157,6 +160,10 @@ static void tagListWidgetAttributeHandle(void) { switch (t) { + case T_CACHENAME: + cachename = (char *)v; + break; + case T_COLOR_ACTIVE: colorActive = (ColorT)v; hasColorActive = 1; @@ -320,7 +327,7 @@ static void tagListWidgetAttributeHandle(void) { break; case T_PICTURE: - widget = W(pictureNew(pos.x, pos.y, filename)); + widget = W(pictureNew(pos.x, pos.y, cachename)); pictureZoomSet((PictureT *)widget, zoom); if (click != NULL) pictureClickHandlerSet((PictureT *)widget, click); break; diff --git a/client/src/gui/taglist.h b/client/src/gui/taglist.h index 883bf86..12b0cde 100644 --- a/client/src/gui/taglist.h +++ b/client/src/gui/taglist.h @@ -61,7 +61,8 @@ enum TagItemsE { T_META_END_OF_WIDGETS, T_META_START_OF_ATTRIBUTES, - T_CLICK, // 17 + T_CACHENAME, // 17 + T_CLICK, T_COLOR_ACTIVE, T_COLOR_BACKGROUND, T_COLOR_FOREGROUND, diff --git a/client/src/main.c b/client/src/main.c index 0e607d8..480790f 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -48,6 +48,7 @@ #include "comport.h" #include "network.h" #include "timer.h" +#include "db.h" #include "welcome.h" #include "settings.h" @@ -131,6 +132,7 @@ static void shutdown(void) { surfaceShutdown(); vbeShutdown(); cacheShutdown(); + dbShutdown(); configShutdown(); osShutdown(); logClose(); @@ -146,7 +148,6 @@ static void shutdown(void) { putc(fgetc(in), stdout); } fclose(in); - free(in); } free(_logName); _Exit(0); @@ -187,13 +188,14 @@ static uint8_t startup(int argc, char *argv[]) { return 1; } + dbStartup(); cacheStartup(argv[0]); surfaceStartup(); mouseStartup(); guiStartup(); netStartup(); - _pointer = imageLoad(cacheFilenameGet("gui:mouse.png")); + _pointer = imageLoadCache("gui:mouse.png"); _alpha = imagePixelGet(_pointer, 5, 0); tableLoad(); @@ -301,8 +303,8 @@ 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(); + //browserShow(); eventLoop(); } else { // Run the setup. diff --git a/client/src/menu.c b/client/src/menu.c index 02ed159..365e08d 100644 --- a/client/src/menu.c +++ b/client/src/menu.c @@ -93,32 +93,32 @@ static void menuFilesReady(void) { uint16_t x = vbeDisplayWidthGet() - 49; uint16_t y = vbeDisplayHeightGet() - 49; - _picLogoff = pictureNew(x, y, cacheFilenameGet("menu:48logoff.png")); + _picLogoff = pictureNew(x, y, "menu:48logoff.png"); pictureClickHandlerSet(_picLogoff, picLogoffClick); guiAttach(guiRootGet(), W(_picLogoff)); x -= 49; - _picForums = pictureNew(x, y, cacheFilenameGet("menu:48forums.png")); + _picForums = pictureNew(x, y, "menu:48forums.png"); pictureClickHandlerSet(_picForums, picForumsClick); guiAttach(guiRootGet(), W(_picForums)); x -= 49; - _picEmail = pictureNew(x, y, cacheFilenameGet("menu:48email.png")); + _picEmail = pictureNew(x, y, "menu:48email.png"); pictureClickHandlerSet(_picEmail, picEmailClick); guiAttach(guiRootGet(), W(_picEmail)); x -= 49; - _picGames = pictureNew(x, y, cacheFilenameGet("menu:48games.png")); + _picGames = pictureNew(x, y, "menu:48games.png"); pictureClickHandlerSet(_picGames, picGamesClick); guiAttach(guiRootGet(), W(_picGames)); x -= 49; - _picChat = pictureNew(x, y, cacheFilenameGet("menu:48chat.png")); + _picChat = pictureNew(x, y, "menu:48chat.png"); pictureClickHandlerSet(_picChat, picChatClick); guiAttach(guiRootGet(), W(_picChat)); x -= 49; - _picProfile = pictureNew(x, y, cacheFilenameGet("menu:48profile.png")); + _picProfile = pictureNew(x, y, "menu:48profile.png"); pictureClickHandlerSet(_picProfile, picProfileClick); guiAttach(guiRootGet(), W(_picProfile)); } diff --git a/client/src/system/cache.c b/client/src/system/cache.c index 066b350..3ff46d3 100644 --- a/client/src/system/cache.c +++ b/client/src/system/cache.c @@ -20,153 +20,54 @@ #include "thirdparty/SHA256/sha256.h" +#include "db.h" +#include "image.h" + #include "cache.h" -#define CACHE_FIELD_SHA256 0 -#define CACHE_FIELD_ENTRYNAME 1 -#define CACHE_FIELD_FILENAME 2 - - static char *cacheEntryNameDirGet(char *entryName, uint8_t includeCache); static char *cacheEntryNameGenerate(void); -static char *cacheFieldGet(char *virtualPath, uint8_t field); uint8_t cacheDelete(char *virtualPath) { - FILE *in = NULL; - FILE *out = NULL; - char index[16] = { 0 }; - char indexNew[16] = { 0 }; - char buffer[1024] = { 0 }; - char *name = NULL; - char *path = NULL; char *dir2 = NULL; char *dir1 = NULL; char *entryDir = NULL; // Deletes entry from cache index AND the data from the cache. - sprintf(index, "CACHE%cINDEX.DAT", OS_PATH_SLASH); - sprintf(indexNew, "CACHE%cINDEX.NEW", OS_PATH_SLASH); - - // Do we have an index yet? - if (osFileExists(index)) { - in = fopen(index, "rt"); - if (in) { - out = fopen(indexNew, "wt"); - if (out) { - while (fgets(buffer, 1024, in) != 0) { - name = strstr(buffer, " "); - *name = 0; - name++; - path = strstr(name, " "); - *path = 0; - path++; - path[strlen(path) - 1] = 0; - if (strcmp(virtualPath, path) == 0) { - // Found! Delete data from disk. - entryDir = cacheEntryNameDirGet(name, 1); - unlink(entryDir); - // Attempt to remove the second-level directory. - dir2 = osLastPathComponentGetUpTo(entryDir); - if (rmdir(dir2) == 0) { - // Attempt to move the top level directory. - dir1 = osLastPathComponentGetUpTo(dir2); - rmdir(dir1); - DEL(dir1); - } - DEL(dir2); - } else { - // Not what we're after, copy to new index. - fprintf(out, "%s %s %s\n", buffer, name, path); - } - } - } else { - fclose(in); - return FAIL; - } - fclose(in); - unlink(index); - rename(indexNew, index); - return SUCCESS; + entryDir = cacheFilenameGet(virtualPath); + if (entryDir) { + // Found! Delete data from disk. + unlink(entryDir); + // Attempt to remove the second-level directory. + dir2 = osLastPathComponentGetUpTo(entryDir); + if (rmdir(dir2) == 0) { + // Attempt to move the top level directory. + dir1 = osLastPathComponentGetUpTo(dir2); + rmdir(dir1); + DEL(dir1); } + DEL(dir2); } - return FAIL; + return SUCCESS; } uint8_t cacheEntryAdd(char *sha256, char *entryName, char *virtualPath) { - FILE *in = NULL; - FILE *out = NULL; - char index[16] = { 0 }; - char indexNew[16] = { 0 }; - char *name = NULL; - char *path = NULL; - char buffer[1024] = { 0 }; - uint8_t found = 0; // This adds or updates an entry to the index. // It does not add data to the actual cache. - // Index format is: SHA256 ENTRYNAME VIRTUALPATH + dbExecute( + "REPLACE INTO cache (sha, entry, path) VALUES (?, ?, ?)", + "vvv", + sha256, entryName, virtualPath + ); - sprintf(index, "CACHE%cINDEX.DAT", OS_PATH_SLASH); - sprintf(indexNew, "CACHE%cINDEX.NEW", OS_PATH_SLASH); - - // Do we have an index yet? - if (!osFileExists(index)) { - // Nope. Just add this entry and be done. - out = fopen(index, "wt"); - if (out) { - fprintf(out, "%s %s %s\n", sha256, entryName, virtualPath); - fclose(out); - return SUCCESS; - } - return FAIL; - } - - // Read existing index, update the entry if it exists. - // If not, we'll add it at the end. - in = fopen(index, "rt"); - if (in) { - out = fopen(indexNew, "wt"); - if (out) { - while (fgets(buffer, 1024, in) != 0) { - name = strstr(buffer, " "); - *name = 0; - name++; - path = strstr(name, " "); - *path = 0; - path++; - path[strlen(path) - 1] = 0; - if (strcmp(virtualPath, path) == 0) { - // Update this entry. - fprintf(out, "%s %s %s\n", sha256, entryName, virtualPath); - found = 1; - } else { - // Not what we're after, copy to new index. - fprintf(out, "%s %s %s\n", buffer, name, path); - } - } - // Did we replace an entry? - if (!found) { - // Add new entry to end. - fprintf(out, "%s %s %s\n", sha256, entryName, virtualPath); - } - fclose(out); - } else { - fclose(in); - return FAIL; - } - fclose(in); - unlink(index); - rename(indexNew, index); - return SUCCESS; - } - - return FAIL; + return SUCCESS; } @@ -218,16 +119,10 @@ static char *cacheEntryNameGenerate(void) { char *cacheEntryNameGet(char *virtualPath) { - return cacheFieldGet(virtualPath, CACHE_FIELD_ENTRYNAME); -} - - -uint8_t cacheExists(void) { - char index[16] = { 0 }; - - sprintf(index, "CACHE%cINDEX.DAT", OS_PATH_SLASH); - - return osFileExists(index); + char *temp = NULL; + dbQuerySingle('v', (void **)&temp, "SELECT entry FROM cache WHERE path = ?", "v", virtualPath); + if (DB_RESULT_EMPTY(temp)) temp = NULL; + return temp; } @@ -237,60 +132,11 @@ void cacheFClose(FILE *handle) { } -static char *cacheFieldGet(char *virtualPath, uint8_t field) { - FILE *in = NULL; - char index[16] = { 0 }; - static char buffer[2048] = { 0 }; - static char *name = NULL; - static char *path = NULL; - static char *result = NULL; - - // Return SHA256 given virtual path or NULL if not found. - - sprintf(index, "CACHE%cINDEX.DAT", OS_PATH_SLASH); - - result = NULL; - - // Do we have an index yet? - if (osFileExists(index)) { - in = fopen(index, "rt"); - if (in) { - // Be sure the fread is the last conditional so it short-circuts properly. - while (result == NULL && (fgets(buffer, 2048, in) != 0)) { - name = strstr(buffer, " "); - *name = 0; - name++; - path = strstr(name, " "); - *path = 0; - path++; - path[strlen(path) - 1] = 0; - if (strcmp(virtualPath, path) == 0) { - // Found! Return requested data. - switch (field) { - case CACHE_FIELD_SHA256: - result = buffer; - break; - - case CACHE_FIELD_ENTRYNAME: - result = name; - break; - - case CACHE_FIELD_FILENAME: - result = cacheEntryNameDirGet(name, 1); - break; - } - } - } - fclose(in); - } - } - - return result; -} - - char *cacheFilenameGet(char *virtualPath) { - return cacheFieldGet(virtualPath, CACHE_FIELD_FILENAME); + char *temp = NULL; + dbQuerySingle('v', (void **)&temp, "SELECT entry FROM cache WHERE path = ?", "v", virtualPath); + if (DB_RESULT_EMPTY(temp)) return NULL; + return cacheEntryNameDirGet(temp, 1); } @@ -448,6 +294,7 @@ uint8_t cachePreUnpack(char *name) { fwrite(buffer, 1, i, out); } while (length > 0); fclose(out); + imageCacheIfNeeded(virtualPath, (char *)buffer); } else { result = FAIL; } @@ -462,7 +309,10 @@ uint8_t cachePreUnpack(char *name) { char *cacheSha256Get(char *virtualPath) { - return cacheFieldGet(virtualPath, CACHE_FIELD_SHA256); + char *temp = NULL; + dbQuerySingle('v', (void **)&temp, "SELECT sha FROM cache WHERE path = ?", "v", virtualPath); + if (DB_RESULT_EMPTY(temp)) temp = NULL; + return temp; } @@ -474,8 +324,18 @@ void cacheShutdown(void) { void cacheStartup(char *appName) { char *temp = NULL; + // Do we need to create the cache database? + dbExecute( + "CREATE TABLE IF NOT EXISTS cache (" + "sha VARCHAR(65), " + "entry VARCHAR(13), " + "path VARCHAR(1024) PRIMARY KEY" + ")", + NULL); + // Do we need to unpack the initial cache? - if (!cacheExists()) { + dbQuerySingle('v', (void **)&temp, "SELECT sha FROM cache LIMIT 1", NULL); + if (DB_RESULT_EMPTY(temp)) { temp = utilAppNameWithNewExtensionGet(appName, "pre"); cachePreUnpack(temp); DEL(temp); diff --git a/client/src/system/db.c b/client/src/system/db.c new file mode 100644 index 0000000..d28b0c9 --- /dev/null +++ b/client/src/system/db.c @@ -0,0 +1,153 @@ +/* + * 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 . + * + */ + + +#include "db.h" + +#include "sqlite3.h" + + +static sqlite3 *_db = NULL; + + +static sqlite3_stmt *dbSqlBind(char *sql, char *format, va_list args); + + +static sqlite3_stmt *dbSqlBind(char *sql, char *format, va_list args) { + int32_t r; + int32_t index; + sqlite3_stmt *stmt; + char *buffer; + + // Bind parameters. + r = sqlite3_prepare_v2(_db, sql, -1, &stmt, NULL); + if (r == SQLITE_OK) { + index = 1; + while (*format != 0) { + switch (*format) { + case 'd': + r = sqlite3_bind_double(stmt, index, va_arg(args, double)); + break; + + case 'i': + r = sqlite3_bind_int(stmt, index, va_arg(args, int)); + break; + + case 'v': + buffer = va_arg(args, char *); + r = sqlite3_bind_text(stmt, index, buffer, strlen(buffer), SQLITE_STATIC); + break; + + default: + utilDie("db: Unknown format option '%c'.\n", *format); + break; + } + format++; + index++; + } + } + + return stmt; +} + + +uint8_t dbExecute(char *sql, char *format, ...) { + char *error; + int32_t r; + va_list args; + sqlite3_stmt *stmt; + + if (format == NULL) { + // No parameters, just run it. + r = sqlite3_exec(_db, sql, NULL, NULL, &error); + } else { + va_start(args, format); + stmt = dbSqlBind(sql, format, args); + va_end(args); + r = (sqlite3_step(stmt) == SQLITE_DONE ? SQLITE_OK : SQLITE_ERROR); + sqlite3_finalize(stmt); + } + + return r == SQLITE_OK ? SUCCESS : FAIL; +} + + +uint8_t dbQuerySingle(char rformat, void **result, char *sql, char *format, ...) { + va_list args; + sqlite3_stmt *stmt; + int32_t r; + static double d; + static int32_t i; + static char *v; + + 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; + } + + if (r == SQLITE_OK) { + r = sqlite3_step(stmt); + if (r == SQLITE_ROW) { + switch (rformat) { + case 'd': + d = sqlite3_column_double(stmt, 0); + *result = &d; + break; + + case 'i': + i = sqlite3_column_double(stmt, 0); + *result = &i; + break; + + case 'v': + v = (char *)sqlite3_column_text(stmt, 0); + *result = v; + break; + + default: + utilDie("db: Unknown format option '%c'.\n", rformat); + break; + } + } + } + + return r == SQLITE_OK ? SUCCESS : FAIL; +} + + +void dbShutdown(void) { + sqlite3_close(_db); + _db = NULL; +} + + +void dbStartup(void) { + char temp[OS_PATH_MAX]; + int32_t r; + + osMkDirP("CACHE"); + sprintf(temp, "CACHE%cCACHE.DB", OS_PATH_SLASH); + r = sqlite3_open(temp, &_db); + if (r) utilDie("Unable to open database: %s\n", sqlite3_errmsg(_db)); +} diff --git a/client/src/system/db.h b/client/src/system/db.h new file mode 100644 index 0000000..8008220 --- /dev/null +++ b/client/src/system/db.h @@ -0,0 +1,37 @@ +/* + * 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 . + * + */ + + +#ifndef DB_H +#define DB_H + + +#include "os.h" + + +#define DB_RESULT_EMPTY(r) (r == NULL || r[0] == 0) + + +uint8_t dbExecute(char *sql, char *format, ...); +uint8_t dbQuerySingle(char rformat, void **result, char *sql, char *format, ...); +void dbShutdown(void); +void dbStartup(void); + + +#endif // DB_H diff --git a/client/src/welcome.c b/client/src/welcome.c index 86694b9..91e73d7 100644 --- a/client/src/welcome.c +++ b/client/src/welcome.c @@ -195,11 +195,6 @@ static void settingsFinished(WidgetT *widget) { void welcomeShow(void) { - char *logo = strdup(cacheFilenameGet("welcome:logo.png")); - char *init = strdup(cacheFilenameGet("welcome:init.png")); - char *dial = strdup(cacheFilenameGet("welcome:dialing.png")); - char *conn = strdup(cacheFilenameGet("welcome:connect.png")); - // 450x128 logo TagItemT uiWelcome[] = { @@ -209,22 +204,22 @@ void welcomeShow(void) { T_WIDTH, 500, T_HEIGHT, 225, T_PICTURE, O(_picLogo), - T_FILENAME, P(logo), + T_CACHENAME, P("welcome:logo.png"), T_X, 18, T_Y, 18, T_PICTURE, T_DONE, T_PICTURE, O(_picInit), - T_FILENAME, P(init), + T_CACHENAME, P("welcome:init.png"), T_X, 18, T_Y, 18, T_VISIBLE, T_FALSE, T_PICTURE, T_DONE, T_PICTURE, O(_picDialing), - T_FILENAME, P(dial), + T_CACHENAME, P("welcome:dialing.png"), T_X, 179, T_Y, 18, T_VISIBLE, T_FALSE, T_PICTURE, T_DONE, T_PICTURE, O(_picConnect), - T_FILENAME, P(conn), + T_CACHENAME, P("welcome:connect.png"), T_X, 339, T_Y, 18, T_VISIBLE, T_FALSE, T_PICTURE, T_DONE, @@ -256,11 +251,6 @@ void welcomeShow(void) { }; tagListRun(uiWelcome); - - DEL(conn); - DEL(dial); - DEL(init); - DEL(logo); } diff --git a/shared/packet.c b/shared/packet.c index a6d1e71..0959e12 100644 --- a/shared/packet.c +++ b/shared/packet.c @@ -102,7 +102,7 @@ uint8_t *packetContentPack(uint16_t *length, char *format, ...) { break; default: - utilDie("restRequest: Unknown format option '%c'.\n", *format); + utilDie("packet: Unknown format option '%c'.\n", *format); break; } format++;