From 114dd3651397b744657ce77bc7840d6ebb02a30a Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Mon, 31 Jan 2022 19:11:58 -0600 Subject: [PATCH] Completed conversion from task-based code to event-based. Needs testing but now runs on DOS! --- Makefile.djgpp | 4 + build.sh | 1 + buildPrerequisites.sh | 6 +- client/client.pro | 31 +- client/src/embedded/embedded.c | 2 +- .../{system/task.h => embedded/embedded.h} | 21 +- client/src/embedded/mouse.h | 457 ++++++++++++++++++ client/src/firstrun.c | 138 ------ client/src/gui/button.c | 2 +- client/src/gui/checkbox.c | 2 +- client/src/gui/gui.c | 136 +++++- client/src/gui/gui.h | 10 + client/src/gui/image.c | 27 +- client/src/gui/image.h | 1 + client/src/gui/label.c | 2 +- client/src/gui/picture.c | 2 +- client/src/gui/radio.c | 2 +- client/src/gui/textbox.c | 2 +- client/src/gui/timer.c | 109 +++++ client/src/{system => gui}/timer.h | 28 +- client/src/gui/updown.c | 2 +- client/src/hangup.c | 107 ++++ client/src/{firstrun.h => hangup.h} | 8 +- client/src/linux/linux.c | 10 +- client/src/login.c | 190 ++++---- client/src/login.h | 2 +- client/src/main.c | 159 +++--- client/src/menu.c | 3 +- client/src/menu.h | 2 +- client/src/network.c | 457 +++++++----------- client/src/network.h | 25 +- client/src/settings.c | 192 +++++--- client/src/settings.h | 3 +- client/src/signup.c | 228 +++++---- client/src/signup.h | 2 +- client/src/system/comport.c | 87 ++-- client/src/system/comport.h | 12 +- client/src/system/os.c | 10 + client/src/system/os.h | 17 +- client/src/system/taglist.c | 9 + client/src/system/taglist.h | 2 + client/src/system/task.c | 121 ----- client/src/system/timer.c | 110 ----- client/src/welcome.c | 294 ++++++++--- client/src/welcome.h | 2 +- .../4_chat/{ => 1_discord}/redirect.txt | 2 +- .../content/4_chat/2_matrix/redirect.txt | 5 + kanga.world/content/4_chat/go-home.txt | 1 + .../kangaworld-integration/api/user.php | 1 + .../kangaworld-integration/features/api.php | 5 + kanga.world/site/snippets/menu.php | 4 +- kanga.world/site/templates/go-home.php | 5 + server/src/client.c | 33 +- server/src/client.h | 2 + server/src/client/login.c | 2 +- server/src/client/signup.c | 2 +- server/src/client/version.c | 10 +- server/src/main.c | 2 + server/src/rest.c | 8 +- server/src/server.c | 3 +- shared/log.c | 4 +- shared/packet.c | 2 - shared/packets.h | 12 +- test.sh | 4 +- 64 files changed, 1928 insertions(+), 1216 deletions(-) rename client/src/{system/task.h => embedded/embedded.h} (69%) create mode 100644 client/src/embedded/mouse.h delete mode 100644 client/src/firstrun.c create mode 100644 client/src/gui/timer.c rename client/src/{system => gui}/timer.h (55%) create mode 100644 client/src/hangup.c rename client/src/{firstrun.h => hangup.h} (89%) delete mode 100644 client/src/system/task.c delete mode 100644 client/src/system/timer.c rename kanga.world/content/4_chat/{ => 1_discord}/redirect.txt (75%) create mode 100644 kanga.world/content/4_chat/2_matrix/redirect.txt create mode 100644 kanga.world/content/4_chat/go-home.txt create mode 100644 kanga.world/site/templates/go-home.php diff --git a/Makefile.djgpp b/Makefile.djgpp index 2305d58..b82d070 100644 --- a/Makefile.djgpp +++ b/Makefile.djgpp @@ -33,10 +33,13 @@ 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)/shared -I$(SRCDIR)/shared/thirdparty +#CFLAGS += -I$(SRCDIR)/client/src/thirdparty/dzcomm -I$(SRCDIR)/client/src/thirdparty/dzcomm/include CPPFLAGS := LDFLAGS := +#LDFLAGS += -L$(SRCDIR)/client/src/thirdparty/dzcomm -ldzcom PREFIX := /usr/local TARGET_ARCH := @@ -61,6 +64,7 @@ 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/build.sh b/build.sh index 108e3fc..53c0aea 100755 --- a/build.sh +++ b/build.sh @@ -27,6 +27,7 @@ mkdir -p \ obj/client/src/system \ obj/client/src/dos \ obj/client/src/gui \ + obj/client/src/embedded \ obj/client/src/thirdparty/serial \ obj/shared/thirdparty/memwatch \ obj/shared/thirdparty/blowfish-api \ diff --git a/buildPrerequisites.sh b/buildPrerequisites.sh index 76f7338..894d595 100755 --- a/buildPrerequisites.sh +++ b/buildPrerequisites.sh @@ -79,8 +79,10 @@ function outputLicense() { pushd font/data - createEmbeddedBinary vga8x14 dat ../../client/src/embedded - +popd + +pushd client/data +createEmbeddedBinary mouse png ../../client/src/embedded popd diff --git a/client/client.pro b/client/client.pro index 4bcb464..800790b 100644 --- a/client/client.pro +++ b/client/client.pro @@ -29,6 +29,9 @@ QMAKE_CFLAGS += -O0 DEFINES *= CLIENT +UNUSED_CRAP = \ + src/thirdparty/dzcomm/include/dzcomm.h + DOS_HEADERS = \ src/thirdparty/serial/serial.h @@ -68,9 +71,10 @@ HEADERS = \ $$SHARED/packets.h \ src/config.h \ $$SHARED/util.h \ - src/embedded/vga8x14.h \ - src/firstrun.h \ + src/embedded/embedded.h \ src/gui/msgbox.h \ + src/gui/timer.h \ + src/hangup.h \ src/login.h \ src/menu.h \ src/network.h \ @@ -84,13 +88,13 @@ HEADERS = \ src/system/surface.h \ src/system/taglist.h \ src/system/keyboard.h \ - src/system/task.h \ - src/system/timer.h \ $$SHARED/array.h \ $$SHARED/log.h \ src/system/mouse.h \ src/system/vesa.h \ src/system/os.h \ + src/embedded/vga8x14.h \ + src/embedded/mouse.h \ src/gui/listbox.h \ src/gui/terminal.h \ src/gui/updown.h \ @@ -117,16 +121,11 @@ SOURCES = \ $$SHARED/packet.c \ $$SHARED/thirdparty/tiny-AES-c/aes.c \ $$SHARED/thirdparty/tiny-AES128-C/pkcs7_padding.c \ - src/config.c \ $$SHARED/memory.c \ src/embedded/embedded.c \ - src/firstrun.c \ src/gui/msgbox.c \ - src/login.c \ - src/menu.c \ - src/network.c \ - src/settings.c \ - src/signup.c \ + src/gui/timer.c \ + src/hangup.c \ src/system/comport.c \ src/system/os.c \ src/system/surface.c \ @@ -134,8 +133,6 @@ SOURCES = \ $$SHARED/util.c \ $$SHARED/array.c \ $$SHARED/log.c \ - src/system/timer.c \ - src/system/task.c \ src/gui/listbox.c \ src/gui/terminal.c \ src/gui/updown.c \ @@ -152,8 +149,14 @@ SOURCES = \ src/gui/button.c \ src/gui/checkbox.c \ src/gui/label.c \ + src/config.c \ src/main.c \ - src/welcome.c + src/welcome.c \ + src/login.c \ + src/menu.c \ + src/signup.c \ + src/network.c \ + src/settings.c LIBS = \ -lSDL2 \ diff --git a/client/src/embedded/embedded.c b/client/src/embedded/embedded.c index d96da36..8885719 100644 --- a/client/src/embedded/embedded.c +++ b/client/src/embedded/embedded.c @@ -19,4 +19,4 @@ #define EMBED_HERE -#include "vga8x14.h" +#include "embedded/embedded.h" diff --git a/client/src/system/task.h b/client/src/embedded/embedded.h similarity index 69% rename from client/src/system/task.h rename to client/src/embedded/embedded.h index fbe64fc..0d511e5 100644 --- a/client/src/system/task.h +++ b/client/src/embedded/embedded.h @@ -18,23 +18,12 @@ */ -#ifndef TASK_H -#define TASK_H +#ifndef EMBEDDED_H +#define EMBEDDED_H -#include "os.h" -#include "widget.h" +#include "mouse.h" +#include "vga8x14.h" -typedef void (*taskFunction)(void *data); - - -uint8_t taskCreate(void (*function)(void *), void *data); -void taskProxy(WidgetT *widget); -void taskRun(void); -void taskShutdown(void); -void taskStartup(void); -void taskYield(void); - - -#endif // TASK_H +#endif // EMBEDDED_H diff --git a/client/src/embedded/mouse.h b/client/src/embedded/mouse.h new file mode 100644 index 0000000..de48447 --- /dev/null +++ b/client/src/embedded/mouse.h @@ -0,0 +1,457 @@ +/* + * 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 MOUSE_PNG_H +#define MOUSE_PNG_H + + +// ===== THIS FILE IS AUTOMATICALLY GENERATED - DO NOT EDIT ===== + + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" + +#ifdef EMBED_HERE + +unsigned char mouse_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x18, + 0x08, 0x06, 0x00, 0x00, 0x00, 0xf3, 0xa0, 0x7d, 0x0c, 0x00, 0x00, 0x10, + 0x66, 0x7a, 0x54, 0x58, 0x74, 0x52, 0x61, 0x77, 0x20, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65, 0x78, + 0x69, 0x66, 0x00, 0x00, 0x78, 0xda, 0xc5, 0x9a, 0x5b, 0x76, 0x1b, 0x39, + 0x12, 0x44, 0xff, 0xb1, 0x8a, 0x59, 0x02, 0xde, 0x8f, 0xe5, 0xe0, 0x91, + 0x38, 0xa7, 0x77, 0x30, 0xcb, 0x9f, 0x9b, 0xa8, 0x22, 0x2d, 0xca, 0x94, + 0x65, 0xbb, 0x3f, 0xc6, 0x6c, 0x89, 0xa5, 0x62, 0x15, 0x0a, 0xc8, 0x8c, + 0x8c, 0xc8, 0x00, 0xdb, 0xc8, 0x7f, 0xff, 0xd9, 0xe6, 0x3f, 0xfc, 0x4b, + 0xd6, 0x66, 0x13, 0x53, 0xa9, 0xb9, 0xe5, 0x6c, 0xf9, 0x17, 0x5b, 0x6c, + 0xbe, 0x73, 0x50, 0xed, 0xf5, 0xaf, 0x9f, 0xdf, 0xce, 0xc6, 0xf3, 0xfb, + 0xfe, 0xc3, 0x3e, 0x0e, 0x5e, 0xce, 0x1b, 0xf7, 0xb8, 0xc9, 0x73, 0x2a, + 0xf0, 0x1e, 0xae, 0x3f, 0x6b, 0xbe, 0xcf, 0x0b, 0xe7, 0x3d, 0xd7, 0xfb, + 0xfb, 0xfc, 0xbc, 0xc7, 0xe9, 0x9c, 0x4f, 0x1f, 0x06, 0x6a, 0x72, 0x7f, + 0x30, 0x5e, 0x3f, 0xe8, 0xf7, 0x40, 0xbe, 0xde, 0x0f, 0x78, 0x4c, 0xe5, + 0x7e, 0x50, 0x70, 0xd7, 0x03, 0xec, 0xba, 0x07, 0xea, 0xf7, 0x40, 0xc1, + 0xdf, 0x4f, 0x8e, 0xd7, 0xdf, 0xe3, 0x7e, 0x72, 0x6e, 0xb5, 0x7c, 0x5c, + 0xc2, 0x7a, 0x2c, 0xad, 0xde, 0x67, 0xea, 0xf5, 0x63, 0xf4, 0x57, 0x0c, + 0xc5, 0xe7, 0x94, 0x5d, 0x89, 0xfc, 0x8e, 0xde, 0x96, 0x92, 0x1b, 0xc7, + 0xd5, 0xdb, 0x58, 0x88, 0xdb, 0xd2, 0x89, 0xee, 0xe9, 0x9b, 0xde, 0x97, + 0xc6, 0x35, 0xce, 0xe7, 0xbf, 0xcd, 0xe3, 0x52, 0xcf, 0x9c, 0xbc, 0x04, + 0x17, 0xec, 0xf9, 0x5d, 0xaf, 0x59, 0x06, 0xfd, 0x71, 0xa1, 0xeb, 0xf9, + 0xf3, 0x9b, 0x50, 0xe9, 0xa7, 0x1c, 0x87, 0x73, 0x26, 0x84, 0x72, 0xe2, + 0x6b, 0x0d, 0x29, 0x63, 0x0a, 0xcc, 0xbc, 0xdd, 0xb1, 0x15, 0xfb, 0x8c, + 0xe6, 0x4b, 0x6c, 0x9e, 0xe9, 0x7a, 0xff, 0xcf, 0xfc, 0xce, 0xb2, 0x6e, + 0x38, 0xbc, 0xa4, 0xbb, 0xf7, 0x57, 0x18, 0x98, 0x0f, 0x4f, 0x72, 0xef, + 0x60, 0x90, 0xe5, 0x3e, 0x1f, 0x3e, 0x65, 0x2f, 0x3f, 0xdf, 0xcf, 0x79, + 0xf3, 0xf9, 0x03, 0x97, 0xde, 0xa7, 0xfb, 0xe4, 0xf4, 0xc3, 0x8c, 0x4a, + 0x7c, 0x3e, 0xd8, 0xbf, 0xcc, 0xc8, 0xc5, 0x07, 0xe4, 0x9e, 0xe9, 0x7d, + 0xfe, 0xec, 0xbd, 0xea, 0xde, 0x72, 0xad, 0xae, 0xc7, 0xcc, 0x92, 0xf3, + 0xbd, 0xa8, 0xc7, 0x4a, 0x9c, 0x39, 0xa9, 0xdc, 0x6b, 0x28, 0x08, 0xce, + 0x6d, 0x99, 0x57, 0xe1, 0x27, 0x71, 0x5c, 0xce, 0xab, 0xf1, 0xaa, 0x94, + 0xcb, 0x04, 0x63, 0xcb, 0x4e, 0x3b, 0x78, 0x4d, 0xd7, 0x9c, 0x27, 0x95, + 0x9b, 0x09, 0x2c, 0xd7, 0xdd, 0x36, 0x4e, 0xce, 0xc1, 0x74, 0x93, 0x39, + 0x46, 0x2f, 0xbe, 0xf0, 0xee, 0xfd, 0x24, 0xeb, 0x7a, 0xae, 0x92, 0x8b, + 0xe6, 0x67, 0xd0, 0xa4, 0x47, 0x7d, 0xb9, 0xed, 0x4b, 0x68, 0x61, 0x01, + 0x01, 0x1f, 0x26, 0x50, 0x09, 0x9c, 0xf5, 0x7b, 0x9b, 0x7b, 0x2e, 0xee, + 0x3c, 0xb7, 0x9d, 0xe7, 0x4d, 0xea, 0x6f, 0xd9, 0xe5, 0xb8, 0xd4, 0x3b, + 0x06, 0x73, 0xdc, 0xf2, 0xed, 0xcb, 0xfc, 0xce, 0x45, 0xbf, 0x7a, 0xed, + 0xad, 0xb5, 0xe4, 0x9c, 0x79, 0xd4, 0x0c, 0xb8, 0x60, 0x5e, 0x5e, 0x03, + 0xce, 0x34, 0x34, 0x73, 0xfa, 0x9b, 0xcb, 0xc8, 0x88, 0xdb, 0x77, 0x50, + 0xd3, 0x09, 0xf0, 0xe3, 0xf5, 0x0a, 0xc8, 0x3b, 0xb1, 0x81, 0x14, 0xa6, + 0x13, 0xe6, 0xca, 0x02, 0xbb, 0x1d, 0xd7, 0x10, 0x23, 0xb9, 0x1f, 0xd8, + 0x0a, 0x27, 0xd1, 0x81, 0xeb, 0x12, 0xef, 0x57, 0x71, 0xbb, 0xb2, 0xae, + 0xfb, 0x75, 0xa0, 0xc8, 0xb3, 0x13, 0x93, 0xa1, 0x92, 0xa2, 0xb3, 0xd9, + 0x85, 0xe4, 0xb2, 0xb3, 0xc5, 0xfb, 0xe2, 0x1c, 0x81, 0xac, 0x24, 0xa8, + 0x3b, 0x5b, 0x7d, 0x88, 0x7e, 0x90, 0x01, 0x97, 0x92, 0x5f, 0x4c, 0xd2, + 0xc7, 0x10, 0x32, 0xb9, 0xa1, 0x08, 0x78, 0xb4, 0xe1, 0x9e, 0xe2, 0xce, + 0xb5, 0x3e, 0xf9, 0xeb, 0x3c, 0x2c, 0x49, 0x26, 0x52, 0xc8, 0xa1, 0x90, + 0x9b, 0x16, 0x3a, 0xc9, 0x8a, 0x31, 0x81, 0x9f, 0x12, 0x2b, 0x18, 0xea, + 0x29, 0xa4, 0x98, 0x52, 0xca, 0xa9, 0xa4, 0x9a, 0x5a, 0xea, 0x39, 0xe4, + 0x68, 0xa8, 0xb0, 0x9c, 0x4b, 0x56, 0xba, 0xed, 0x25, 0x94, 0x58, 0x52, + 0xc9, 0xa5, 0x94, 0x5a, 0x5a, 0xe9, 0x35, 0xd4, 0x58, 0x53, 0xcd, 0xb5, + 0xd4, 0x5a, 0x5b, 0xed, 0xcd, 0xb7, 0x00, 0x1b, 0xa7, 0x46, 0x39, 0xb6, + 0xda, 0x5a, 0xeb, 0x9d, 0x67, 0x76, 0x46, 0xee, 0xd9, 0x74, 0xae, 0xef, + 0x9c, 0x19, 0x7e, 0x84, 0x11, 0x47, 0x1a, 0x79, 0x94, 0x51, 0x47, 0x1b, + 0x7d, 0x02, 0x9f, 0x19, 0x67, 0x9a, 0x79, 0x96, 0x59, 0x67, 0x9b, 0x7d, + 0xf9, 0x15, 0x16, 0x75, 0xbc, 0xf2, 0x2a, 0xab, 0xae, 0xb6, 0xba, 0x38, + 0x01, 0x4a, 0x12, 0x25, 0x19, 0xc9, 0x52, 0xa4, 0x4a, 0x93, 0xbe, 0xc1, + 0xda, 0x0e, 0x3b, 0xee, 0xb4, 0xf3, 0x2e, 0xbb, 0xee, 0xb6, 0xfb, 0x33, + 0x6b, 0xee, 0x2e, 0xdb, 0xcf, 0xaf, 0x3f, 0xc8, 0x9a, 0xbb, 0xb3, 0xe6, + 0x4f, 0xa6, 0xf4, 0xba, 0xf2, 0xcc, 0x1a, 0x67, 0x4b, 0xb9, 0xde, 0xcd, + 0xc1, 0x0b, 0x83, 0x68, 0xce, 0xc8, 0x98, 0x8f, 0x8e, 0x8c, 0x17, 0xcd, + 0x00, 0x80, 0xf6, 0x9a, 0x33, 0x5b, 0x5d, 0x8c, 0x5e, 0x33, 0xa7, 0x39, + 0xb3, 0xcd, 0x53, 0x15, 0xc9, 0x33, 0xc9, 0xa4, 0xb9, 0x59, 0xce, 0x76, + 0x97, 0x7d, 0x34, 0x21, 0x8a, 0xf3, 0x69, 0xbb, 0x67, 0xee, 0x7e, 0x64, + 0xee, 0x8f, 0xf2, 0x66, 0x72, 0xfc, 0x65, 0xde, 0xfc, 0xef, 0x66, 0xce, + 0x68, 0xea, 0xfe, 0x65, 0xe6, 0x4e, 0xde, 0x8c, 0xa4, 0x0f, 0x79, 0x7b, + 0x93, 0xb5, 0xa5, 0xdc, 0x3c, 0x4f, 0xc6, 0xae, 0x2a, 0xd4, 0x98, 0xda, + 0x40, 0xf5, 0xf1, 0xb9, 0xf0, 0xd8, 0xb8, 0xf5, 0xb8, 0x9a, 0x5d, 0xf2, + 0xde, 0x65, 0xec, 0x6e, 0xfb, 0x5c, 0xbd, 0x89, 0x1b, 0xbd, 0x10, 0xe0, + 0xde, 0x6b, 0x02, 0xfa, 0x73, 0x87, 0x24, 0x33, 0xc7, 0x54, 0x57, 0x4f, + 0xd5, 0xcf, 0xd4, 0xfb, 0xd2, 0x09, 0x13, 0xef, 0x3e, 0x1c, 0xc1, 0x5c, + 0x84, 0x75, 0x11, 0xae, 0x68, 0x58, 0x48, 0x2f, 0x4d, 0x92, 0xef, 0x4a, + 0x80, 0x76, 0x34, 0xf1, 0x7d, 0xad, 0x22, 0x6d, 0x71, 0x5f, 0x5e, 0xe7, + 0x67, 0xe7, 0x15, 0x4b, 0x0e, 0xb5, 0x27, 0x52, 0x52, 0x72, 0x76, 0x31, + 0xf1, 0x5f, 0x49, 0xcd, 0x5f, 0x47, 0x4a, 0xd4, 0xe6, 0x71, 0xf0, 0x67, + 0xef, 0xd9, 0xe9, 0xbb, 0x4b, 0xbb, 0x16, 0x27, 0x0d, 0xc2, 0xec, 0x66, + 0xce, 0x01, 0xa1, 0xa7, 0x56, 0xe1, 0xfe, 0x2e, 0x3c, 0x75, 0x14, 0xe2, + 0x02, 0x1a, 0x76, 0x49, 0xba, 0x7a, 0xef, 0xb6, 0x84, 0xd4, 0x06, 0x33, + 0x18, 0x8e, 0x49, 0x6e, 0xe6, 0x54, 0x7d, 0xd7, 0x05, 0xd7, 0x91, 0xf2, + 0x86, 0x02, 0x5a, 0xf2, 0x75, 0x9a, 0xb6, 0x56, 0x1b, 0xe3, 0x9a, 0xab, + 0x0c, 0x0f, 0x4e, 0xcf, 0xb3, 0xe2, 0x9f, 0xbe, 0x9b, 0xe7, 0x09, 0x57, + 0x53, 0x22, 0x65, 0x5b, 0xa6, 0x84, 0xbd, 0x7d, 0x24, 0xcc, 0xc8, 0xcb, + 0x20, 0x07, 0x4d, 0x67, 0x06, 0x5a, 0x7c, 0xdf, 0xad, 0xfa, 0x21, 0xe7, + 0xef, 0xd5, 0x66, 0xd9, 0x12, 0x9b, 0x64, 0x24, 0x84, 0xbf, 0x0d, 0xab, + 0x6d, 0xdc, 0x18, 0x15, 0xa9, 0xd7, 0x8a, 0x6b, 0x91, 0xe5, 0xd6, 0x59, + 0x16, 0xa3, 0x31, 0x34, 0x47, 0x3a, 0x5e, 0xd9, 0x9b, 0xb1, 0xd2, 0x3d, + 0x9a, 0x94, 0xed, 0x7c, 0xa9, 0x69, 0xc4, 0xbc, 0x49, 0x40, 0x33, 0xdc, + 0x53, 0xf3, 0x68, 0xb9, 0xec, 0x3c, 0x46, 0xaa, 0x92, 0x28, 0x31, 0xd2, + 0x08, 0x17, 0x88, 0x9b, 0x5a, 0xad, 0x13, 0x76, 0x2c, 0x34, 0x22, 0x7b, + 0xca, 0x68, 0x0c, 0x06, 0x2c, 0xd2, 0xb6, 0x00, 0x4e, 0x6a, 0x12, 0x7d, + 0xc8, 0x6c, 0x6b, 0xd4, 0x6d, 0x08, 0x65, 0x63, 0x5d, 0xdb, 0xdb, 0x94, + 0x47, 0x62, 0xd6, 0xb3, 0x74, 0x5f, 0x58, 0x1d, 0xf7, 0xf9, 0xdc, 0x53, + 0x1c, 0x54, 0x41, 0x88, 0x00, 0x8b, 0x21, 0xdc, 0x52, 0xe5, 0x2c, 0x5c, + 0x67, 0x25, 0xf3, 0x91, 0x87, 0x22, 0xa6, 0x42, 0x2b, 0x2e, 0x88, 0xcd, + 0x65, 0xf0, 0x59, 0x4a, 0xdc, 0x7d, 0xa4, 0x55, 0xa7, 0x00, 0xa8, 0xb9, + 0x52, 0xa3, 0xd6, 0x59, 0xad, 0xa3, 0x68, 0x6a, 0xcb, 0x64, 0xb5, 0x8f, + 0x41, 0x5d, 0xae, 0xb9, 0xe1, 0x87, 0xd2, 0xc7, 0x8e, 0x7d, 0x0e, 0x28, + 0x2c, 0x6e, 0x07, 0xc8, 0xd6, 0x30, 0xc9, 0x47, 0xca, 0x87, 0x21, 0x98, + 0x65, 0x9e, 0xe2, 0x1b, 0x85, 0x95, 0x56, 0xab, 0x17, 0x4e, 0x56, 0x8f, + 0xf5, 0xff, 0x9f, 0xb5, 0x26, 0xf3, 0x01, 0xe0, 0x32, 0xa7, 0x5e, 0xc5, + 0xd0, 0x42, 0xbf, 0xa0, 0xc3, 0xd9, 0x06, 0xa1, 0xe8, 0x11, 0xa3, 0x64, + 0x7d, 0x07, 0xad, 0x94, 0xfd, 0x16, 0xe3, 0xdb, 0xb6, 0x6b, 0x49, 0xa9, + 0xb1, 0xcc, 0x08, 0x0d, 0x75, 0x32, 0x3f, 0x1b, 0xbd, 0x7b, 0x21, 0xf8, + 0x23, 0x14, 0xba, 0xdb, 0x19, 0x39, 0x39, 0x2b, 0xb8, 0xb7, 0xb1, 0x6a, + 0x06, 0xad, 0xac, 0xb4, 0x4b, 0x58, 0x30, 0x8b, 0xdb, 0x6e, 0xe8, 0xcc, + 0xba, 0xd1, 0xac, 0xeb, 0xc8, 0xf9, 0xa4, 0x72, 0x77, 0x0d, 0x1e, 0xcf, + 0xb3, 0x8b, 0x12, 0xf7, 0xd3, 0x79, 0x35, 0x05, 0x91, 0x05, 0x55, 0x2d, + 0xe6, 0xbd, 0x73, 0x8d, 0x69, 0x53, 0xd4, 0x7b, 0x32, 0xb1, 0xba, 0x72, + 0x5e, 0x4e, 0xab, 0xc5, 0x0e, 0x13, 0xd6, 0x4c, 0x62, 0x7b, 0xa3, 0x92, + 0xba, 0x87, 0x22, 0x68, 0x80, 0x24, 0xc5, 0x1e, 0x60, 0xf5, 0x9c, 0x41, + 0xe6, 0x29, 0x4d, 0xe2, 0x34, 0xbf, 0x29, 0x64, 0xf3, 0xb6, 0xb2, 0xcb, + 0x0a, 0x07, 0xcf, 0x81, 0x46, 0x6d, 0xb6, 0x30, 0x57, 0x04, 0x7f, 0xd3, + 0x53, 0x8a, 0xc0, 0x10, 0x36, 0x1d, 0x92, 0x6d, 0xdf, 0x9d, 0x60, 0xe5, + 0x72, 0x42, 0xdd, 0xbc, 0x01, 0xc4, 0x4c, 0x72, 0x55, 0x5a, 0x5a, 0xe8, + 0x10, 0x76, 0xa0, 0x76, 0x1d, 0xa0, 0xcc, 0x65, 0x7c, 0x4e, 0xb4, 0x93, + 0x6b, 0x78, 0x42, 0x79, 0x0d, 0x1f, 0xf6, 0x19, 0xfe, 0xca, 0x1a, 0x4f, + 0x68, 0xf9, 0x7a, 0xc2, 0x68, 0x00, 0x15, 0x8e, 0x64, 0xdd, 0x03, 0x2c, + 0x75, 0x74, 0x20, 0xbb, 0x2e, 0xc0, 0x1d, 0x62, 0x5e, 0x99, 0x0a, 0x00, + 0xc6, 0xe4, 0x7b, 0x4c, 0x94, 0x41, 0xbb, 0x44, 0x18, 0x32, 0x3a, 0xa9, + 0xe0, 0x20, 0x74, 0x38, 0xfb, 0x24, 0xd1, 0x91, 0x26, 0x78, 0xfe, 0xa4, + 0xd8, 0x95, 0x13, 0x7e, 0x5f, 0xb2, 0x4e, 0x81, 0xf2, 0x53, 0x1c, 0x54, + 0x97, 0xfa, 0xa2, 0x22, 0xa9, 0xc1, 0xda, 0x20, 0x74, 0xee, 0x9d, 0xad, + 0x43, 0x45, 0x92, 0x73, 0x5d, 0x08, 0x24, 0xeb, 0x75, 0xa8, 0x15, 0xa2, + 0x96, 0x78, 0x6a, 0x0c, 0x8d, 0xcf, 0x14, 0xfb, 0x94, 0xee, 0x48, 0x28, + 0x1b, 0x48, 0x57, 0xe5, 0x58, 0x2b, 0x48, 0x5f, 0xf0, 0x2d, 0x5c, 0x6d, + 0x4b, 0x20, 0x10, 0x79, 0x12, 0x06, 0x7e, 0x8b, 0x78, 0xb4, 0x29, 0x1b, + 0xcf, 0xac, 0xa9, 0x2b, 0x50, 0x61, 0x41, 0x56, 0xdf, 0xcb, 0x6b, 0x29, + 0x12, 0xed, 0x59, 0x92, 0x53, 0xb2, 0x6e, 0x21, 0x0e, 0xa4, 0x87, 0xb0, + 0x50, 0x77, 0x95, 0xd2, 0xfc, 0x48, 0xdb, 0xf9, 0x49, 0xdb, 0xe6, 0x57, + 0xb4, 0x7c, 0x92, 0x77, 0x52, 0xa7, 0x64, 0xf1, 0xab, 0xe4, 0x69, 0x89, + 0x68, 0xfe, 0x9a, 0x7f, 0x49, 0x1e, 0x95, 0xf4, 0xcb, 0xf4, 0x3d, 0xd3, + 0xb8, 0x03, 0xa5, 0x32, 0x03, 0x8f, 0xaa, 0x66, 0x2a, 0x43, 0xf9, 0xa4, + 0x95, 0x77, 0x58, 0xb1, 0x9d, 0x40, 0xd7, 0x95, 0x0a, 0x47, 0xac, 0x64, + 0x86, 0x82, 0x5a, 0x39, 0x4a, 0xc1, 0x51, 0x49, 0x95, 0x25, 0xd2, 0x67, + 0xf8, 0xb4, 0x52, 0x97, 0xb9, 0x64, 0xa3, 0x97, 0x9e, 0x93, 0xde, 0x9b, + 0x51, 0xa9, 0x03, 0xab, 0x49, 0x0c, 0x83, 0xb5, 0x4c, 0x74, 0x16, 0x8b, + 0x84, 0x9a, 0xe9, 0x3c, 0x07, 0x79, 0x57, 0x5b, 0x70, 0x06, 0x17, 0x16, + 0xa7, 0x0e, 0x64, 0x86, 0xd4, 0x7f, 0xd6, 0x1e, 0xf3, 0x77, 0xe2, 0xf3, + 0xb3, 0xf6, 0x98, 0x6f, 0x68, 0xeb, 0xb7, 0x59, 0xca, 0xbc, 0xa7, 0xa9, + 0x5f, 0xb2, 0xd4, 0xdb, 0x77, 0xa3, 0xb0, 0x55, 0x0f, 0x75, 0x14, 0xe8, + 0xd6, 0x1f, 0xe8, 0xe4, 0xd6, 0x9f, 0xf3, 0xcc, 0xaf, 0x14, 0xe8, 0x45, + 0x80, 0xcc, 0xef, 0x28, 0x10, 0x59, 0x7c, 0xd5, 0xa0, 0x4b, 0x81, 0x0e, + 0x3f, 0x3e, 0x24, 0xc8, 0x5c, 0x1a, 0x04, 0xe0, 0xfe, 0x4e, 0x85, 0x9e, + 0x22, 0x64, 0xfe, 0xa5, 0x0a, 0x3d, 0x45, 0xc8, 0xe8, 0x6c, 0x7e, 0x4f, + 0x87, 0x1e, 0x09, 0x64, 0x41, 0xf3, 0x62, 0xa6, 0x47, 0x12, 0x55, 0xd2, + 0xcd, 0xc9, 0xe3, 0xab, 0xd6, 0xbc, 0xa6, 0xf0, 0x4e, 0x20, 0xde, 0xe7, + 0x97, 0x29, 0x34, 0x5f, 0xe5, 0xf4, 0x56, 0x22, 0xfb, 0xd4, 0xa2, 0x87, + 0x12, 0x5d, 0x3a, 0xf4, 0x50, 0xa1, 0xa7, 0x08, 0x99, 0xbf, 0x52, 0x21, + 0xd8, 0x95, 0x62, 0x98, 0x64, 0x87, 0x10, 0x45, 0x1d, 0x2e, 0x1b, 0x6e, + 0x6a, 0x99, 0xbc, 0xef, 0x95, 0xb2, 0x28, 0xb5, 0x6d, 0x8f, 0x69, 0x82, + 0x9c, 0x74, 0x53, 0x8a, 0x43, 0x6f, 0x29, 0x46, 0xcc, 0x31, 0x0e, 0x17, + 0x63, 0x56, 0xb9, 0xbb, 0xda, 0x30, 0x07, 0x6c, 0x99, 0xe9, 0x4e, 0x21, + 0xc1, 0x89, 0xe5, 0x48, 0xc5, 0x99, 0x99, 0xd2, 0x8c, 0x03, 0xfa, 0x5c, + 0x64, 0x93, 0x18, 0x45, 0x02, 0x14, 0x5d, 0x1c, 0x08, 0x14, 0x68, 0x83, + 0x35, 0xe9, 0x5a, 0x17, 0x9c, 0x4b, 0xaa, 0x71, 0x84, 0x30, 0x3d, 0xfe, + 0x60, 0xe2, 0xc2, 0x75, 0xea, 0x20, 0x8f, 0xb9, 0x4c, 0xdd, 0x92, 0x49, + 0x06, 0x4b, 0x8b, 0x66, 0x61, 0x16, 0xa9, 0xcb, 0x11, 0x3a, 0xd5, 0x4c, + 0x66, 0x9b, 0x96, 0x21, 0x4b, 0x01, 0xca, 0x7d, 0x43, 0x12, 0x73, 0xca, + 0x76, 0x98, 0x85, 0xc4, 0xdc, 0x56, 0x50, 0x34, 0x2b, 0x25, 0x8c, 0x05, + 0x9a, 0x4a, 0x94, 0x80, 0x1e, 0x36, 0xda, 0x63, 0xb2, 0x51, 0xa4, 0xcc, + 0x53, 0xfd, 0x61, 0x25, 0xd0, 0xbb, 0xbb, 0xa3, 0x6d, 0xcf, 0x76, 0x82, + 0x78, 0x7c, 0xaa, 0x74, 0x0f, 0x7e, 0x00, 0xe6, 0xb1, 0x18, 0x68, 0x2d, + 0x59, 0x8c, 0x10, 0x62, 0x6a, 0xd8, 0x99, 0xda, 0x7d, 0xa5, 0xd3, 0x8e, + 0xe6, 0xdf, 0xc9, 0xe2, 0x0f, 0x62, 0x35, 0xdf, 0xca, 0xe2, 0x77, 0xef, + 0xb7, 0x6c, 0x9a, 0xf7, 0xba, 0xf9, 0xe7, 0xb2, 0x69, 0xde, 0xeb, 0xe6, + 0x3e, 0x4f, 0x71, 0xb9, 0x4c, 0x6f, 0x31, 0xe6, 0x19, 0x2c, 0x83, 0xbb, + 0xe2, 0x79, 0xd4, 0x8e, 0x7e, 0x7b, 0x51, 0x91, 0x8b, 0x9b, 0x7a, 0x44, + 0x4e, 0xf7, 0x48, 0x53, 0x4c, 0xc9, 0x98, 0x29, 0xc4, 0x2e, 0x8a, 0x1d, + 0x62, 0x05, 0xbb, 0xb8, 0x32, 0x48, 0x91, 0x44, 0x08, 0x49, 0x23, 0x4a, + 0xbb, 0x63, 0x44, 0xe8, 0x76, 0x03, 0x63, 0x2b, 0x82, 0x8a, 0x10, 0x62, + 0x46, 0x4e, 0xa1, 0x15, 0x7c, 0x1a, 0xc6, 0xa9, 0x43, 0x47, 0x1e, 0x1c, + 0x31, 0xaf, 0x4c, 0x09, 0xa1, 0xb0, 0x58, 0x1f, 0xd8, 0x5a, 0xfc, 0xc2, + 0x02, 0x0a, 0x45, 0xa7, 0x18, 0x77, 0x6d, 0x25, 0x3c, 0x24, 0x56, 0x0e, + 0x6c, 0xd6, 0xde, 0x03, 0x9e, 0xd0, 0x21, 0xc5, 0x81, 0xa8, 0xe6, 0x59, + 0x17, 0x26, 0x08, 0x4e, 0xdf, 0xa9, 0x40, 0xb5, 0xa9, 0xc1, 0x43, 0xd8, + 0xb5, 0xd0, 0xeb, 0x1e, 0xb9, 0xc7, 0xd6, 0xd5, 0x48, 0x8d, 0x2d, 0x4a, + 0xc0, 0x29, 0x54, 0x2c, 0xe0, 0x08, 0x56, 0xb3, 0xec, 0xe1, 0xfb, 0xc6, + 0xe1, 0xbb, 0xd2, 0x36, 0xaf, 0xb5, 0x1d, 0xec, 0xf7, 0x20, 0xd8, 0xf0, + 0xe2, 0x0a, 0x01, 0x1b, 0x76, 0x30, 0xa4, 0x9b, 0xa7, 0xbc, 0x9b, 0xc7, + 0xc1, 0xfd, 0x3e, 0x9c, 0x6c, 0x92, 0xa3, 0x06, 0x70, 0x05, 0xa2, 0x1a, + 0x61, 0x38, 0x3a, 0x10, 0x9b, 0x5b, 0xc2, 0xc4, 0x26, 0x72, 0x8d, 0x64, + 0x86, 0x25, 0xb3, 0xe0, 0x10, 0x25, 0x5a, 0xaf, 0x98, 0x15, 0x5c, 0xb3, + 0x81, 0x9d, 0xb7, 0x6e, 0x39, 0x2e, 0x1d, 0xa5, 0x5d, 0xb6, 0x32, 0xca, + 0x56, 0x0e, 0xd1, 0x79, 0x62, 0x20, 0xaf, 0x2e, 0xb6, 0x06, 0xa7, 0x07, + 0x1a, 0xa6, 0x2d, 0x8e, 0xd1, 0xb9, 0x6f, 0x2f, 0xbb, 0x8a, 0x2e, 0x4c, + 0xec, 0xa4, 0x3d, 0x3e, 0xb8, 0xf1, 0x9d, 0x54, 0x61, 0x47, 0xff, 0x7a, + 0x30, 0xa3, 0xa3, 0x7d, 0x1a, 0x4c, 0x49, 0xd9, 0x4b, 0x1b, 0x54, 0x67, + 0x10, 0x58, 0x17, 0x82, 0x1d, 0x83, 0x1a, 0xa3, 0xcf, 0x1a, 0x6b, 0xf8, + 0xd6, 0x97, 0xd0, 0x37, 0xe9, 0x86, 0x05, 0x39, 0xb5, 0x58, 0x7a, 0x60, + 0xd8, 0x0c, 0xce, 0x97, 0x1a, 0xed, 0xa7, 0x37, 0x69, 0x6b, 0x57, 0xc5, + 0x37, 0xfc, 0xb1, 0xec, 0xbe, 0xd8, 0xfc, 0x6a, 0xd4, 0xa9, 0xfd, 0x78, + 0x98, 0xac, 0xc8, 0xf5, 0x37, 0xb7, 0xa0, 0xdf, 0xa4, 0xc3, 0xd1, 0x34, + 0x9c, 0x12, 0x81, 0x8f, 0x54, 0x22, 0x9f, 0xed, 0xc9, 0xdf, 0x76, 0x27, + 0xe6, 0x73, 0x7b, 0xf2, 0xa9, 0x3b, 0x39, 0x32, 0xf8, 0xcd, 0x5c, 0xe9, + 0x5f, 0x66, 0x61, 0x46, 0x7b, 0xd0, 0x95, 0xdd, 0xcc, 0x7b, 0x11, 0x6f, + 0x29, 0x36, 0xad, 0x85, 0xbe, 0x90, 0x57, 0x3a, 0x4e, 0xa2, 0x11, 0x6b, + 0x5d, 0xb5, 0x7a, 0xfa, 0x2a, 0x48, 0xea, 0x23, 0xe9, 0x56, 0x41, 0x36, + 0x5b, 0x26, 0x46, 0x00, 0xc5, 0xea, 0x16, 0x0d, 0xc0, 0x80, 0x79, 0x3d, + 0xca, 0x54, 0x0a, 0xab, 0xed, 0xda, 0xc5, 0xa1, 0xb0, 0x09, 0x6a, 0x26, + 0xe2, 0xdf, 0xf3, 0xa6, 0x79, 0x4f, 0x9c, 0x5f, 0xf1, 0x66, 0x69, 0x47, + 0x3e, 0x76, 0x3a, 0x42, 0x09, 0xd5, 0xc3, 0x63, 0xc4, 0x8e, 0x2a, 0x35, + 0xa7, 0x53, 0x90, 0x1d, 0xe9, 0xa8, 0xa0, 0xfe, 0x99, 0xc3, 0x1f, 0xb5, + 0xa0, 0x3f, 0x3a, 0x50, 0xf3, 0xa6, 0x05, 0xa5, 0x35, 0xe9, 0xf1, 0x2d, + 0x55, 0xa6, 0xcb, 0xa3, 0xb1, 0x32, 0xaa, 0xb9, 0xcf, 0xad, 0xde, 0xcc, + 0x0d, 0x08, 0x61, 0x33, 0x23, 0x64, 0x72, 0x35, 0x2f, 0x1d, 0xb2, 0x71, + 0x1e, 0x2a, 0xdb, 0xbe, 0x79, 0x16, 0xe9, 0xaa, 0x44, 0x1f, 0xb3, 0xee, + 0x3b, 0x2e, 0x5d, 0x25, 0xc2, 0xea, 0x14, 0xb7, 0x40, 0x53, 0xfc, 0x01, + 0x30, 0x3e, 0x2a, 0x5c, 0xcc, 0x4d, 0x4f, 0xb8, 0x8d, 0x7e, 0x9f, 0x90, + 0x45, 0xa9, 0x6c, 0x6c, 0x22, 0xcc, 0x7b, 0xc8, 0xd7, 0xa2, 0xd0, 0x34, + 0x1e, 0x25, 0x6b, 0x5d, 0x85, 0xa2, 0x3c, 0xc3, 0x15, 0x42, 0x63, 0xb4, + 0xd0, 0x6a, 0x97, 0x01, 0x84, 0xaa, 0x74, 0x9f, 0xda, 0xd0, 0x64, 0xd3, + 0x57, 0x0f, 0x65, 0x75, 0xd2, 0x45, 0x93, 0x22, 0x39, 0x8e, 0x9d, 0x7b, + 0x6b, 0xa8, 0xce, 0x22, 0x75, 0x81, 0x60, 0xaa, 0x0b, 0xd4, 0xef, 0x6b, + 0x4e, 0x3f, 0xa0, 0x1d, 0x3f, 0x72, 0xce, 0x01, 0xe8, 0x04, 0xe6, 0x78, + 0x39, 0x80, 0x50, 0x43, 0x9e, 0x86, 0xfe, 0x2b, 0x86, 0xa5, 0x71, 0x86, + 0x25, 0x68, 0xb1, 0x00, 0x0d, 0x04, 0xcb, 0xc8, 0xf0, 0x94, 0xaf, 0x70, + 0xbb, 0x6d, 0x54, 0x6a, 0xf6, 0xa1, 0xc3, 0x7b, 0xb3, 0x8f, 0x09, 0x56, + 0xb5, 0x21, 0x1c, 0x89, 0x5b, 0x74, 0xef, 0x49, 0xb3, 0x3a, 0xcf, 0xd2, + 0x52, 0xa0, 0x63, 0xaa, 0x72, 0x0a, 0x42, 0xc8, 0xaf, 0x74, 0xf4, 0x03, + 0xfe, 0x62, 0x4a, 0x93, 0xb5, 0x85, 0x19, 0x84, 0x16, 0x04, 0xef, 0xd9, + 0x6a, 0x59, 0xd9, 0x7b, 0xa8, 0x5b, 0x9d, 0xd2, 0xce, 0x61, 0x9c, 0xcc, + 0x35, 0x80, 0xd5, 0x9b, 0xe9, 0xe1, 0x1a, 0x98, 0x58, 0xe9, 0xc0, 0x3a, + 0xea, 0xee, 0x1a, 0xea, 0x4d, 0xaf, 0x52, 0xd6, 0xe5, 0x74, 0x9b, 0xa3, + 0x4c, 0x27, 0xba, 0xd3, 0x3e, 0x4c, 0xe5, 0xc3, 0x1d, 0xcc, 0xc3, 0x30, + 0x91, 0x7b, 0x1a, 0x00, 0x27, 0xc5, 0xa0, 0xed, 0x0d, 0x6d, 0xe7, 0xb8, + 0x42, 0xec, 0x49, 0x50, 0xaa, 0x28, 0x43, 0x88, 0x48, 0xc1, 0x3d, 0xf1, + 0xe7, 0xf5, 0xc3, 0xad, 0x53, 0xd2, 0x13, 0xf5, 0x32, 0x8b, 0x89, 0x72, + 0x9a, 0x68, 0x8c, 0x4d, 0x55, 0xef, 0xcb, 0xf4, 0x49, 0x8c, 0xa4, 0x51, + 0xf7, 0x34, 0xb3, 0xca, 0xd2, 0xcb, 0x2d, 0xe7, 0x8e, 0x6b, 0x5d, 0xd7, + 0xaa, 0xa0, 0xb9, 0x85, 0x39, 0x7e, 0x8d, 0xc3, 0x33, 0x0c, 0x7f, 0x8a, + 0x6f, 0xf3, 0x95, 0xc5, 0x42, 0xee, 0xc6, 0xc7, 0x56, 0xf1, 0xd1, 0xe8, + 0xd5, 0x45, 0xd2, 0xc7, 0x49, 0x7a, 0x15, 0x4a, 0x8b, 0x85, 0x0c, 0xaa, + 0x93, 0x26, 0x62, 0x68, 0x7b, 0xdc, 0xc6, 0x42, 0x64, 0x71, 0xfb, 0x4f, + 0x4e, 0x0a, 0x9e, 0x5a, 0xd5, 0xc4, 0x77, 0xf9, 0x82, 0xee, 0x9f, 0x04, + 0x3d, 0x04, 0x6f, 0x60, 0xea, 0x58, 0xf4, 0x5e, 0x24, 0x8a, 0xf2, 0x41, + 0xad, 0xf3, 0x86, 0x00, 0x9a, 0xf8, 0x31, 0x55, 0x0b, 0x91, 0xd2, 0x16, + 0x60, 0x86, 0xda, 0x94, 0x12, 0xb9, 0x17, 0x17, 0xdd, 0x8a, 0x57, 0x65, + 0xf7, 0x78, 0x5c, 0x6c, 0xac, 0x8b, 0x72, 0x7a, 0x26, 0xfc, 0x1a, 0xed, + 0x2a, 0x2e, 0x0a, 0x0e, 0xda, 0x14, 0x9a, 0xf8, 0x29, 0x95, 0x2a, 0x21, + 0x71, 0x40, 0x5e, 0x5a, 0xe9, 0xf4, 0x1f, 0x18, 0x01, 0xa2, 0xcc, 0xe2, + 0x7a, 0x82, 0x8e, 0xd3, 0x42, 0xfa, 0x3b, 0xde, 0x00, 0x8c, 0x8a, 0xcd, + 0x6a, 0x98, 0xdb, 0x1c, 0xd5, 0xc8, 0x82, 0xe3, 0x90, 0xbd, 0x28, 0xc8, + 0x04, 0x56, 0x81, 0xde, 0xbf, 0x51, 0x40, 0xc0, 0xb9, 0xe7, 0x45, 0x17, + 0xef, 0x93, 0x55, 0x0b, 0x0c, 0xfd, 0x21, 0x54, 0x44, 0xf2, 0xb4, 0xf3, + 0xfe, 0x67, 0x23, 0x64, 0xbe, 0x70, 0x42, 0x20, 0x1b, 0x9c, 0x20, 0xa2, + 0x9c, 0x81, 0x2e, 0x5c, 0x56, 0x4f, 0x19, 0x51, 0x5f, 0x21, 0xc2, 0xbd, + 0x30, 0x2d, 0xd1, 0x15, 0xd1, 0x20, 0x39, 0x44, 0xb8, 0xfa, 0x6d, 0x2e, + 0x43, 0xa8, 0x1b, 0x43, 0x08, 0x13, 0x1d, 0x2b, 0x9e, 0x91, 0x16, 0xa0, + 0xab, 0xf6, 0x00, 0xfe, 0x7b, 0xd7, 0xc8, 0xa5, 0x90, 0xce, 0xe6, 0xc2, + 0x74, 0x25, 0x2e, 0xdd, 0x7c, 0x5f, 0xfa, 0xc5, 0x62, 0xd5, 0xf2, 0xdb, + 0x95, 0x7e, 0xa3, 0x74, 0xc3, 0x2d, 0x3c, 0xca, 0x37, 0x35, 0x35, 0x15, + 0xeb, 0x43, 0x14, 0x97, 0xfa, 0xcb, 0xc5, 0xe0, 0x3c, 0x36, 0xcb, 0xd2, + 0x56, 0x11, 0x51, 0xd9, 0x94, 0xf8, 0xb2, 0x97, 0xfd, 0xcc, 0xdd, 0x5f, + 0xed, 0xc9, 0xa9, 0xdf, 0xf3, 0x6e, 0x1e, 0x07, 0x8f, 0x6e, 0xe5, 0x1b, + 0x37, 0xf9, 0x68, 0x57, 0x7e, 0x82, 0xaa, 0xf9, 0xa2, 0x5f, 0xd9, 0xa1, + 0x1c, 0xec, 0x79, 0x1a, 0x35, 0xfa, 0xbf, 0x40, 0xb3, 0xa4, 0x7d, 0xc9, + 0xb2, 0xb2, 0xc3, 0x2b, 0xe3, 0xa8, 0x43, 0xc8, 0x09, 0xa5, 0x2d, 0xde, + 0xa1, 0x57, 0x20, 0x43, 0xb4, 0x37, 0xa0, 0xfb, 0xb2, 0xf5, 0xf2, 0x8c, + 0x54, 0x17, 0x45, 0xaf, 0xa2, 0xa8, 0xdf, 0x45, 0x91, 0xb8, 0x72, 0x79, + 0x46, 0xb5, 0xec, 0xd4, 0xd8, 0xbd, 0xe7, 0x62, 0xed, 0xb5, 0xeb, 0x72, + 0xfc, 0x5a, 0x78, 0x70, 0xc9, 0x4d, 0x52, 0xca, 0x51, 0x09, 0xe7, 0xe5, + 0xa7, 0x36, 0x8b, 0x10, 0xb7, 0xee, 0xc2, 0x24, 0x24, 0x4f, 0x22, 0x44, + 0xa7, 0xdf, 0xe5, 0x3d, 0x2c, 0x64, 0x8b, 0xe9, 0xf2, 0x90, 0xd9, 0x90, + 0xe3, 0xb2, 0x92, 0x67, 0x20, 0xaf, 0x4d, 0x24, 0xe8, 0xc3, 0x10, 0x2d, + 0x2d, 0xbe, 0x59, 0x46, 0x3f, 0xea, 0xb5, 0xb4, 0x30, 0x72, 0x5e, 0xba, + 0xe3, 0xe2, 0x7c, 0xc4, 0x53, 0x26, 0x32, 0x9c, 0xf1, 0x49, 0x99, 0x35, + 0x5c, 0xc4, 0x30, 0xcd, 0xc5, 0x0b, 0xe0, 0x70, 0x15, 0xbb, 0x33, 0x1d, + 0x1b, 0x1d, 0x57, 0x49, 0xbd, 0xc5, 0xee, 0xe9, 0x2e, 0x75, 0xd7, 0x4d, + 0xbb, 0x49, 0x8c, 0xa5, 0xee, 0xcd, 0x57, 0xfa, 0xdf, 0xc9, 0xa3, 0x81, + 0xb6, 0xea, 0x04, 0x25, 0xc8, 0x3a, 0xa6, 0x76, 0x25, 0xc8, 0x91, 0xb6, + 0x4e, 0x3c, 0xe6, 0x22, 0x32, 0xc0, 0x5d, 0xd6, 0x65, 0xf8, 0x46, 0x49, + 0x88, 0xaf, 0xea, 0x84, 0x26, 0x21, 0xd2, 0xdd, 0x0e, 0xb8, 0x0d, 0x94, + 0x46, 0x35, 0xa4, 0x42, 0x4f, 0x82, 0x88, 0xd1, 0x56, 0xaf, 0x35, 0x72, + 0x2e, 0xc9, 0xe8, 0x17, 0x96, 0x4f, 0xa1, 0x40, 0xa4, 0xa5, 0x30, 0xb9, + 0xfa, 0xdc, 0x8e, 0xb1, 0x8f, 0xbd, 0xe0, 0x4f, 0x3e, 0x7c, 0x28, 0xe0, + 0xf1, 0x91, 0x78, 0x2f, 0xac, 0x32, 0x37, 0x99, 0x8d, 0x5f, 0xd3, 0x1d, + 0x91, 0x7d, 0x9a, 0x59, 0xbd, 0xcc, 0x67, 0x57, 0x61, 0x1b, 0x80, 0x74, + 0x9a, 0x54, 0xca, 0x0b, 0x37, 0xfa, 0x19, 0x7f, 0x1f, 0xe1, 0x07, 0x90, + 0x84, 0x86, 0x1d, 0x8d, 0xb2, 0xba, 0x4b, 0x0d, 0x9a, 0x1c, 0xea, 0xb7, + 0x80, 0xd5, 0xa0, 0x3d, 0x58, 0xfa, 0x18, 0x7c, 0xf8, 0xdc, 0x34, 0x09, + 0xa8, 0x18, 0xd1, 0xc3, 0x75, 0x70, 0x86, 0xfe, 0x86, 0xe8, 0xd3, 0x89, + 0x10, 0x6c, 0x81, 0x6d, 0x84, 0xda, 0x77, 0x63, 0x98, 0x4a, 0x1a, 0x03, + 0x25, 0x95, 0x2c, 0x0c, 0x01, 0xeb, 0x45, 0x60, 0x75, 0x36, 0x62, 0x60, + 0x3b, 0x69, 0xba, 0x0d, 0xa4, 0x73, 0x87, 0xac, 0xfa, 0x85, 0xae, 0xe6, + 0xf4, 0x9b, 0xdf, 0x7e, 0xc4, 0x3a, 0xf3, 0x50, 0x84, 0x1f, 0x39, 0xf5, + 0xc3, 0x14, 0xe5, 0xd0, 0x05, 0xad, 0x61, 0xe6, 0xa9, 0x05, 0xdd, 0xe9, + 0xcb, 0x5e, 0xbd, 0x34, 0x2c, 0x83, 0xe8, 0x6e, 0x4a, 0x81, 0x96, 0xee, + 0x94, 0x2e, 0xf3, 0xda, 0xc8, 0xd4, 0xc1, 0x99, 0x34, 0x40, 0x06, 0x36, + 0x89, 0xe7, 0xd0, 0xef, 0xbc, 0x96, 0x61, 0x85, 0x18, 0x22, 0x4a, 0x9e, + 0x12, 0xdf, 0x04, 0xda, 0xe5, 0x58, 0x6a, 0x9c, 0xa0, 0x10, 0x28, 0x52, + 0xe5, 0x61, 0x97, 0x2e, 0xba, 0xc7, 0x5a, 0x90, 0x1a, 0x38, 0x95, 0xd4, + 0xc5, 0xce, 0xcf, 0xbc, 0xda, 0x7c, 0x9c, 0x51, 0x68, 0x5a, 0x53, 0xe6, + 0xf4, 0xfc, 0xd3, 0x6b, 0x7f, 0xa0, 0x05, 0x60, 0x9b, 0xba, 0x37, 0x4f, + 0xaf, 0x06, 0x7c, 0x96, 0x7a, 0x3a, 0x00, 0x15, 0x15, 0x35, 0x4a, 0x52, + 0xd4, 0x1f, 0x4e, 0xcc, 0xc3, 0x9f, 0x4b, 0x1a, 0x8e, 0x7f, 0x94, 0x98, + 0x8f, 0x35, 0xd0, 0x0d, 0x84, 0xa7, 0x3b, 0xd8, 0x6d, 0x0f, 0x28, 0x59, + 0xd0, 0x2c, 0x85, 0x53, 0x80, 0xb1, 0x23, 0xbc, 0xd2, 0x66, 0x9d, 0xf4, + 0x8c, 0xba, 0x65, 0xf0, 0xd8, 0x86, 0xd0, 0xed, 0x93, 0xcc, 0xea, 0x86, + 0xd0, 0xce, 0x5a, 0x96, 0x31, 0x2a, 0xbe, 0xbf, 0x2c, 0xbf, 0x81, 0x1e, + 0xcd, 0x0c, 0x88, 0x8e, 0x5e, 0xf9, 0x13, 0x83, 0xce, 0x2c, 0x20, 0x31, + 0x6f, 0x21, 0xbb, 0xe4, 0xb5, 0x1f, 0x8c, 0xd0, 0xf6, 0x50, 0x1b, 0xdc, + 0xcf, 0xf7, 0x58, 0x2a, 0xd8, 0x3c, 0x95, 0xf9, 0xa3, 0xb9, 0xba, 0x11, + 0x64, 0xfa, 0xa5, 0xe2, 0x99, 0x78, 0xe3, 0xa5, 0xe1, 0xb6, 0xaa, 0x91, + 0xa5, 0x15, 0x83, 0xe0, 0x37, 0x5d, 0x35, 0xb9, 0x57, 0xf7, 0xa5, 0xdf, + 0xf8, 0x53, 0xec, 0x9d, 0x36, 0x77, 0xfb, 0x5d, 0x10, 0x34, 0xf0, 0x32, + 0x46, 0x0b, 0x54, 0x8d, 0x4a, 0x07, 0xda, 0x9f, 0xc8, 0x4d, 0xfa, 0xb8, + 0x57, 0xfe, 0x6e, 0xab, 0x9c, 0x2b, 0xe9, 0xa8, 0xb9, 0xd6, 0x22, 0x3e, + 0x09, 0xb4, 0xa0, 0x06, 0xb9, 0xe3, 0x27, 0x0a, 0x2d, 0x3c, 0x60, 0xae, + 0xcd, 0x40, 0x04, 0xfa, 0x45, 0x60, 0xd3, 0x4d, 0x2a, 0x9a, 0xb3, 0xb6, + 0xbd, 0x53, 0xbd, 0x2c, 0xc0, 0xaa, 0x4d, 0x48, 0x72, 0x52, 0xf7, 0x17, + 0x73, 0x02, 0x27, 0x0b, 0x4c, 0xcf, 0xf0, 0x28, 0x49, 0x87, 0x2f, 0x7b, + 0x92, 0xe8, 0xea, 0xe8, 0x60, 0xc0, 0xd0, 0xe7, 0xab, 0xd4, 0x2e, 0x49, + 0x85, 0x5e, 0xb2, 0x08, 0xed, 0x2d, 0xe6, 0xb3, 0x83, 0xd3, 0x79, 0x3b, + 0xbb, 0x4d, 0xc5, 0xbf, 0x9a, 0xbc, 0x2b, 0xcf, 0xfa, 0x25, 0xe2, 0x45, + 0x75, 0xd4, 0x2d, 0xdd, 0x88, 0xa8, 0xf7, 0x60, 0xfe, 0x37, 0xaf, 0x5c, + 0xb4, 0x02, 0xf1, 0x4c, 0x25, 0x1f, 0x59, 0xf1, 0x6c, 0x86, 0xc0, 0x87, + 0x60, 0x11, 0x30, 0xc1, 0x15, 0xf4, 0xe8, 0x6e, 0xc2, 0x7b, 0x08, 0x7e, + 0x72, 0x67, 0xca, 0x24, 0x99, 0x26, 0xc2, 0xc5, 0xaa, 0x2e, 0x3f, 0x65, + 0xc5, 0x67, 0x23, 0x26, 0xc7, 0x48, 0xca, 0x44, 0x8b, 0xdf, 0x54, 0xe8, + 0xbb, 0x26, 0x07, 0x12, 0x99, 0x00, 0x12, 0x58, 0x5b, 0xad, 0x37, 0x5a, + 0x4f, 0x1e, 0xae, 0xd5, 0x2b, 0x89, 0xbc, 0xe6, 0x18, 0x6a, 0xd3, 0x92, + 0xd4, 0x95, 0xeb, 0xa3, 0x29, 0x40, 0xcd, 0x68, 0xd6, 0x3e, 0x21, 0xd1, + 0x71, 0x71, 0x65, 0x4a, 0x88, 0x24, 0xb2, 0xbc, 0x54, 0x45, 0xee, 0xdd, + 0xb2, 0xa4, 0x65, 0x7b, 0xed, 0x96, 0x11, 0x7c, 0x18, 0x8f, 0x8a, 0xd3, + 0xf6, 0x07, 0x57, 0xe0, 0xaa, 0x06, 0x59, 0x79, 0x13, 0x2c, 0x23, 0x11, + 0xc1, 0xa7, 0xad, 0xfe, 0xaa, 0xed, 0xac, 0xbb, 0x43, 0x1e, 0xe9, 0x40, + 0x4c, 0xcc, 0xad, 0x26, 0x6a, 0x80, 0xf0, 0x3d, 0xcf, 0xe6, 0x95, 0xdf, + 0xfb, 0x29, 0x24, 0xba, 0xc5, 0x71, 0x35, 0x31, 0xb7, 0x98, 0x20, 0x25, + 0x09, 0xa5, 0x42, 0xf7, 0xd2, 0xbe, 0xbf, 0x66, 0x31, 0x5f, 0x63, 0xe7, + 0x57, 0x7d, 0xfe, 0xd5, 0xe6, 0x5f, 0xbd, 0xfb, 0xc5, 0xc8, 0xe6, 0xd9, + 0xba, 0xf7, 0x45, 0x03, 0x97, 0xab, 0xca, 0x6d, 0xde, 0x3f, 0xf8, 0x98, + 0xaa, 0xa9, 0xa2, 0x4e, 0x24, 0x1c, 0x46, 0x26, 0xe4, 0xfe, 0x99, 0x76, + 0x6d, 0xdd, 0x48, 0x98, 0xee, 0x2f, 0x45, 0xd3, 0xf1, 0x7a, 0xd7, 0xd7, + 0x83, 0xd0, 0xeb, 0x79, 0x96, 0x5a, 0x14, 0x92, 0x43, 0x88, 0x56, 0x18, + 0x87, 0x39, 0x63, 0x9a, 0x03, 0x0d, 0xc3, 0xb5, 0xee, 0x07, 0xe9, 0xe2, + 0xb0, 0xe6, 0x8c, 0x1f, 0xd0, 0x65, 0x5e, 0x60, 0xf6, 0xe5, 0x7b, 0xef, + 0x2a, 0x59, 0x52, 0x28, 0xe5, 0x2d, 0x39, 0xa3, 0xeb, 0x03, 0x8f, 0x31, + 0xd4, 0xdd, 0x07, 0xd7, 0xaa, 0xeb, 0xce, 0x28, 0x47, 0x74, 0xfd, 0xe6, + 0x9c, 0x8a, 0x07, 0xdb, 0x54, 0x3e, 0xe9, 0xcf, 0x22, 0xfa, 0x9d, 0xbc, + 0x78, 0x01, 0x62, 0x6e, 0x2f, 0x75, 0xc8, 0x34, 0xba, 0x47, 0xa0, 0x2f, + 0x87, 0x30, 0xd6, 0x7d, 0x1a, 0xbb, 0xa3, 0x1f, 0xb8, 0xd3, 0xb0, 0x33, + 0x6f, 0x70, 0x38, 0xa8, 0xbe, 0x4d, 0x24, 0xfd, 0xf9, 0xb8, 0x91, 0x90, + 0xad, 0x5f, 0x65, 0xec, 0x39, 0x61, 0x00, 0xed, 0xb0, 0xc0, 0xc7, 0x61, + 0xdd, 0xc3, 0xb9, 0x84, 0xe2, 0x4a, 0x4c, 0x92, 0x43, 0xc6, 0xe6, 0x67, + 0x36, 0x56, 0x01, 0x52, 0x1f, 0x31, 0x4a, 0x4b, 0x17, 0x7e, 0xf5, 0x7f, + 0x8d, 0x68, 0x9d, 0x00, 0x65, 0x27, 0x0b, 0x85, 0x9f, 0x0a, 0xef, 0x8c, + 0x75, 0x86, 0xac, 0x48, 0xab, 0x40, 0x7d, 0xd9, 0x00, 0x67, 0xe2, 0xad, + 0x8d, 0x21, 0xcd, 0xac, 0x2b, 0x96, 0x06, 0x92, 0x1f, 0xa6, 0x9d, 0x90, + 0x46, 0x20, 0x45, 0x52, 0x72, 0x8c, 0xba, 0x91, 0xc2, 0x0d, 0x57, 0xcc, + 0x40, 0x7b, 0xfc, 0x1c, 0x45, 0x6a, 0x8d, 0x7b, 0xcc, 0xff, 0x00, 0x59, + 0x81, 0x84, 0x20, 0x42, 0x67, 0xd2, 0x1b, 0x00, 0x00, 0x01, 0x86, 0x69, + 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x00, 0x00, 0x78, 0x9c, 0x7d, 0x91, 0x3d, 0x48, 0xc3, 0x40, + 0x1c, 0xc5, 0x5f, 0x53, 0xa5, 0x52, 0x2b, 0x0e, 0x16, 0x11, 0x11, 0xc9, + 0x50, 0x9d, 0x2c, 0x88, 0x8a, 0x08, 0x2e, 0x5a, 0x85, 0x22, 0x54, 0x08, + 0xb5, 0x42, 0xab, 0x0e, 0x26, 0x97, 0x7e, 0x08, 0x4d, 0x1a, 0x92, 0x14, + 0x17, 0x47, 0xc1, 0xb5, 0xe0, 0xe0, 0xc7, 0x62, 0xd5, 0xc1, 0xc5, 0x59, + 0x57, 0x07, 0x57, 0x41, 0x10, 0xfc, 0x00, 0x71, 0x73, 0x73, 0x52, 0x74, + 0x91, 0x12, 0xff, 0x97, 0x14, 0x5a, 0xc4, 0x7a, 0x70, 0xdc, 0x8f, 0x77, + 0xf7, 0x1e, 0x77, 0xef, 0x00, 0xa1, 0x5a, 0x64, 0x9a, 0xd5, 0x36, 0x0a, + 0x68, 0xba, 0x6d, 0x26, 0xe3, 0x31, 0x31, 0x9d, 0x59, 0x11, 0x03, 0xaf, + 0x08, 0xa2, 0x13, 0xbd, 0x98, 0xc6, 0xa0, 0xcc, 0x2c, 0x63, 0x56, 0x92, + 0x12, 0x68, 0x39, 0xbe, 0xee, 0xe1, 0xe3, 0xeb, 0x5d, 0x94, 0x67, 0xb5, + 0x3e, 0xf7, 0xe7, 0xe8, 0x52, 0xb3, 0x16, 0x03, 0x7c, 0x22, 0xf1, 0x0c, + 0x33, 0x4c, 0x9b, 0x78, 0x9d, 0x78, 0x72, 0xd3, 0x36, 0x38, 0xef, 0x13, + 0x87, 0x59, 0x41, 0x56, 0x89, 0xcf, 0x89, 0x47, 0x4c, 0xba, 0x20, 0xf1, + 0x23, 0xd7, 0x15, 0x8f, 0xdf, 0x38, 0xe7, 0x5d, 0x16, 0x78, 0x66, 0xd8, + 0x4c, 0x25, 0xe7, 0x88, 0xc3, 0xc4, 0x62, 0xbe, 0x89, 0x95, 0x26, 0x66, + 0x05, 0x53, 0x23, 0x9e, 0x20, 0x8e, 0xa8, 0x9a, 0x4e, 0xf9, 0x42, 0xda, + 0x63, 0x95, 0xf3, 0x16, 0x67, 0xad, 0x58, 0x66, 0xf5, 0x7b, 0xf2, 0x17, + 0x86, 0xb2, 0xfa, 0xf2, 0x12, 0xd7, 0x69, 0x0e, 0x20, 0x8e, 0x05, 0x2c, + 0x42, 0x82, 0x08, 0x05, 0x65, 0x6c, 0xa0, 0x08, 0x1b, 0x51, 0x5a, 0x75, + 0x52, 0x2c, 0x24, 0x69, 0x3f, 0xd6, 0xc2, 0xdf, 0xef, 0xfa, 0x25, 0x72, + 0x29, 0xe4, 0xda, 0x00, 0x23, 0xc7, 0x3c, 0x4a, 0xd0, 0x20, 0xbb, 0x7e, + 0xf0, 0x3f, 0xf8, 0xdd, 0xad, 0x95, 0x1b, 0x1f, 0xf3, 0x92, 0x42, 0x31, + 0xa0, 0xfd, 0xc5, 0x71, 0x3e, 0x86, 0x80, 0xc0, 0x2e, 0x50, 0xab, 0x38, + 0xce, 0xf7, 0xb1, 0xe3, 0xd4, 0x4e, 0x00, 0xff, 0x33, 0x70, 0xa5, 0x37, + 0xfc, 0xa5, 0x2a, 0x30, 0xf5, 0x49, 0x7a, 0xa5, 0xa1, 0x45, 0x8e, 0x80, + 0xee, 0x6d, 0xe0, 0xe2, 0xba, 0xa1, 0x29, 0x7b, 0xc0, 0xe5, 0x0e, 0xd0, + 0xf7, 0x64, 0xc8, 0xa6, 0xec, 0x4a, 0x7e, 0x9a, 0x42, 0x2e, 0x07, 0xbc, + 0x9f, 0xd1, 0x37, 0x65, 0x80, 0x9e, 0x5b, 0x20, 0xb8, 0xea, 0xf5, 0x56, + 0xdf, 0xc7, 0xe9, 0x03, 0x90, 0xa2, 0xae, 0x12, 0x37, 0xc0, 0xc1, 0x21, + 0x30, 0x9c, 0xa7, 0xec, 0xb5, 0x16, 0xef, 0xee, 0x68, 0xee, 0xed, 0xdf, + 0x33, 0xf5, 0xfe, 0x7e, 0x00, 0xb6, 0x7a, 0x72, 0xc2, 0xb7, 0x19, 0xd7, + 0x5a, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4b, 0x47, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf9, 0x43, 0xbb, 0x7f, 0x00, 0x00, 0x00, 0x09, 0x70, + 0x48, 0x59, 0x73, 0x00, 0x00, 0x2e, 0x23, 0x00, 0x00, 0x2e, 0x23, 0x01, + 0x78, 0xa5, 0x3f, 0x76, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, + 0x07, 0xe5, 0x0a, 0x0b, 0x17, 0x1e, 0x25, 0x91, 0xc0, 0xe1, 0x5c, 0x00, + 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, + 0x6e, 0x74, 0x00, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, + 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0x0e, 0x17, + 0x00, 0x00, 0x00, 0x8f, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, 0xa5, 0x92, + 0x49, 0x0e, 0xc0, 0x20, 0x0c, 0x03, 0x1d, 0x8b, 0xff, 0x7f, 0xd9, 0x3d, + 0x54, 0xdd, 0x21, 0x5b, 0x91, 0x10, 0x82, 0x90, 0x89, 0x21, 0x36, 0x00, + 0x02, 0x84, 0xee, 0xe0, 0xbe, 0xd8, 0x5f, 0x40, 0x1f, 0x42, 0x00, 0x90, + 0xd4, 0x86, 0x9c, 0x0a, 0xba, 0x10, 0xde, 0x37, 0x1d, 0x08, 0xdf, 0x07, + 0x55, 0x08, 0x67, 0x87, 0x15, 0x08, 0x57, 0x81, 0x2c, 0x84, 0x5e, 0x30, + 0x03, 0x61, 0x24, 0x31, 0x82, 0x30, 0xf3, 0x51, 0x1e, 0x84, 0xd9, 0x76, + 0xad, 0x20, 0xac, 0x98, 0x66, 0x06, 0x61, 0xd5, 0xba, 0x6f, 0xc8, 0xf0, + 0x2e, 0x9b, 0x45, 0x3e, 0xb0, 0xb9, 0x82, 0x23, 0xf1, 0xaa, 0xa6, 0xe5, + 0x64, 0xbd, 0xaa, 0xd3, 0x85, 0x3d, 0x59, 0x93, 0x37, 0x27, 0x8c, 0xf4, + 0x4c, 0x56, 0x5a, 0x09, 0x57, 0x95, 0xb3, 0x2a, 0x78, 0x7d, 0x52, 0xa7, + 0x0b, 0x80, 0x01, 0x92, 0x1b, 0x7e, 0x6a, 0xf9, 0xdc, 0x18, 0x81, 0x6d, + 0x42, 0x05, 0x1b, 0x88, 0xd3, 0x3f, 0x1e, 0x42, 0x28, 0x07, 0xad, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int mouse_png_len = 4907; + +#else // EMBED_HERE + +extern unsigned char mouse_png[]; +extern unsigned int mouse_png_len; + +#endif // EMBED_HERE + + +#pragma GCC diagnostic pop + + +#endif // MOUSE_PNG_H diff --git a/client/src/firstrun.c b/client/src/firstrun.c deleted file mode 100644 index d931b06..0000000 --- a/client/src/firstrun.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * 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 "config.h" -#include "log.h" -#include "memory.h" -#include "timer.h" -#include "task.h" -#include "network.h" -#include "settings.h" - -#include "firstrun.h" - - -static uint8_t _firstRunRunning = 1; - - -static void firstRunFail(void); -static void taskFirstRun(void *data); -static void taskFirstRunHelper(void *data); - - -static void firstRunFail(void) { - netShutdown(); - taskShutdown(); - timerShutdown(); - configShutdown(); - osShutdown(); - logClose(); - memoryShutdown(); - exit(1); -} - - -void firstRunCheckSetup(void) { - // ***TODO*** Check for needed files here. - mkdir("cache", 0777); - - taskCreate(taskFirstRunHelper, NULL); - taskCreate(taskFirstRun, NULL); - taskRun(); -} - - -static void taskFirstRun(void *data) { - int8_t comPort = -1; - int8_t r = 0; - - (void)data; - - logWrite("First Run Setup\n"); - logWrite("===============\n\n"); - - // 0 1 2 3 4 5 6 7 8 - // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 - logWrite("Welcome to the KangaWorld Client! Before you can connect to KangaWorld, Setup\n"); - logWrite("needs to detect a compatible softmodem and download several files.\n\n"); - logWrite("One moment while we check for a compatible softmodem...\n\n"); - - comPort = settingsScanComPorts(); - - if (comPort < 0) { - logWrite("Unable to detect a compatible softmodem! For assistance, visit:\n"); - logWrite("https://Kanga.World\n"); - firstRunFail(); - } - - logWrite("Compatible softmodem found on COM%d:! Connecting to KangaWorld...\n\n", comPort + 1); - - logWrite("Initializing modem...\n"); - r = netConnectStep1(); - if (r == NETWORK_CONNECT_OPEN_FAILED) { - logWrite("Unable to open COM port! Please check settings.\n"); - _firstRunRunning = 0; - return; - } - if (r == NETWORK_CONNECT_BAD_MODEM) { - logWrite("Modem does not support ENET! Please check settings.\n"); - _firstRunRunning = 0; - return; - } - - logWrite("Dialing modem...\n"); - r = netConnectStep2(); - if (r == NETWORK_CONNECT_NO_CARRIER) { - logWrite("Unable to connect to server! Please check settings or try later.\n"); - _firstRunRunning = 0; - return; - } - if (r == NETWORK_CONNECT_BAD_ENCRYPTION) { - logWrite("Unable to negotiate encryption settings!\n"); - _firstRunRunning = 0; - return; - } - if (r == NETWORK_CONNECT_BAD_SETTINGS) { - logWrite("Unable to fetch client settings!\n"); - _firstRunRunning = 0; - return; - } - - logWrite("Connected! Pre-caching needed files...\n"); - - - logWrite("Disconnecting...\n"); - netDisconnect(); - - _firstRunRunning = 0; - - return; -} - - -static void taskFirstRunHelper(void *data) { - - (void)data; - - while (_firstRunRunning) { - timerUpdate(); - taskYield(); - } -} diff --git a/client/src/gui/button.c b/client/src/gui/button.c index 45d19b0..9c87cb7 100644 --- a/client/src/gui/button.c +++ b/client/src/gui/button.c @@ -77,7 +77,7 @@ static void buttonMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_ // Fire callback on mouse up. if (event == MOUSE_EVENT_LEFT_UP) { - if (b->clicked) b->clicked(widget); + if (b->clicked) guiPendingEventAdd(widget, b->clicked); } } diff --git a/client/src/gui/checkbox.c b/client/src/gui/checkbox.c index f2e151a..dd399b0 100644 --- a/client/src/gui/checkbox.c +++ b/client/src/gui/checkbox.c @@ -64,7 +64,7 @@ static void checkboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint1 // Toggle value. checkboxValueSet(c, !checkboxValueGet(c)); // Fire callback on mouse up. - if (c->clicked) c->clicked(widget); + if (c->clicked) guiPendingEventAdd(widget, c->clicked); } } diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c index 369df14..51110e5 100644 --- a/client/src/gui/gui.c +++ b/client/src/gui/gui.c @@ -22,10 +22,16 @@ #include "gui.h" -#include "embedded/vga8x14.h" #include "widget.h" #include "desktop.h" #include "window.h" +#include "embedded/embedded.h" + + +typedef struct PendingEventsS { + WidgetT *widget; + widgetCallback callback; +} PendingEventsT; int16_t _guiMetric[METRIC_COUNT]; @@ -37,13 +43,23 @@ uint16_t _guiDragOffsetY = 0; WindowT *_guiActiveWindow = NULL; -static DesktopT *_guiDesktop = NULL; -static WidgetT *_guiLastWidgetLeft = NULL; -static uint8_t _guiLastWidgetLeftEvent = MOUSE_EVENT_NONE; -static WidgetT *_guiFocused = NULL; -static uint8_t _guiHasStopped = 0; // This starts as 0 even if the GUI hasn't been started. -static WidgetT ***_guiDeleteList = NULL; - +static DesktopT *_guiDesktop = NULL; +static WidgetT *_guiLastWidgetLeft = NULL; +static uint8_t _guiLastWidgetLeftEvent = MOUSE_EVENT_NONE; +static WidgetT *_guiFocused = NULL; +static uint8_t _guiHasStopped = 0; // This starts as 0 even if the GUI hasn't been started. +static WidgetT ***_guiDeleteList = NULL; +static uint32_t _guiTimerValue = 0; +static uint32_t _guiTimerLast = 0; +static uint32_t _guiTimerHalfSecond = 0; +static uint32_t _guiTimerSecond = 0; +static uint32_t _guiTimerQuarterSecondTick = 0; +static uint32_t _guiTimerHalfSecondTick = 0; +static uint32_t _guiTimerSecondTick = 0; +static uint32_t _guiTimerQuarterSecondOn = 0; +static uint32_t _guiTimerHalfSecondOn = 0; +static uint32_t _guiTimerSecondOn = 0; +static PendingEventsT **_guiPendingEvents = NULL; // Widget Magic Debug Info. Don't forget to change MagicE! static char *_magicDebugNames[MAGIC_COUNT] = { @@ -191,6 +207,10 @@ void guiDelete(WidgetT **widget) { // Since deleting happens in widget events, it's not safe to do it // immediately. Instead, we make a list of what to delete and // then process it before painting. + + // This may be redundant now since there is a pending event list + // that is also handled in the paint event. + arrput(_guiDeleteList, widget); } @@ -249,7 +269,6 @@ static void guiDeleteListItem(WidgetT **widget) { if (plen == 1) { // Erase the list. arrfree(w->parent->children); - w->parent->children = NULL; } } } @@ -259,7 +278,6 @@ static void guiDeleteListItem(WidgetT **widget) { if (w == _guiFocused) _guiFocused = NULL; // Delete us. - //logWrite("Deleting %s %p\n", _magicDebugNames[w->magic], w); if (w->delMethod != NULL) w->delMethod(&w); arrfree(w->children); if (w->surface != NULL) { @@ -455,6 +473,14 @@ void guiMouseProcess(MouseT *mouse) { void guiPaint(WidgetT *widget) { + // Process any pending events. + while (arrlen(_guiPendingEvents)) { + _guiPendingEvents[0]->callback(_guiPendingEvents[0]->widget); + DEL(_guiPendingEvents[0]); + arrdel(_guiPendingEvents, 0); + } + arrfree(_guiPendingEvents); + // Process any pending widget deletions. guiDeleteList(); @@ -512,6 +538,16 @@ void guiParentAndChildrenDirtySet(WidgetT *widget) { } +void guiPendingEventAdd(WidgetT *widget, void *callback) { + PendingEventsT *p = NULL; + + NEW(PendingEventsT, p); + p->callback = callback; + p->widget = widget; + arrput(_guiPendingEvents, p); +} + + WidgetT *guiRootGet(void) { return (WidgetT *)_guiDesktop; } @@ -648,6 +684,86 @@ void guiStop(void) { } +uint32_t guiTimerGet(void) { + return _guiTimerValue; +} + + +uint8_t guiTimerHalfSecondOn(void) { + return _guiTimerHalfSecondOn; +} + + +uint8_t guiTimerHalfSecondTick(void) { + return _guiTimerHalfSecondTick; +} + + +void guiTimerProcess(uint32_t rawClock) { + uint32_t delta; + + _guiTimerValue = rawClock; + + // Reset ticks. + _guiTimerQuarterSecondTick = 0; + _guiTimerHalfSecondTick = 0; + _guiTimerSecondTick = 0; + + // Ensure we haven't rolled past midnight between calls. + if (_guiTimerValue >= _guiTimerLast) { + delta = _guiTimerValue - _guiTimerLast; + } else { + // Compensate for midnight rollover. + delta = (_guiTimerValue + TICKS_PER_DAY) - _guiTimerLast; + } + + // Everything ticks off the quarter second. + if (delta > TICKS_PER_SECOND * 0.25) { + _guiTimerLast = _guiTimerValue; + + // Quarter Second timer. + _guiTimerQuarterSecondOn = !_guiTimerQuarterSecondOn; + _guiTimerQuarterSecondTick = 1; + + // Half Second timer. + if (--_guiTimerHalfSecond == 0) { + _guiTimerHalfSecond = 2; + _guiTimerHalfSecondOn = !_guiTimerHalfSecondOn; + _guiTimerHalfSecondTick = 1; + + // Second timer + if (--_guiTimerSecond == 0) { + _guiTimerSecond = 2; + _guiTimerSecondOn = !_guiTimerSecondOn; + _guiTimerSecondTick = 1; + } // Second. + + } // Half Second. + + } // Quarter Second. +} + + +uint8_t guiTimerQuarterSecondOn(void) { + return _guiTimerQuarterSecondOn; +} + + +uint8_t guiTimerQuarterSecondTick(void) { + return _guiTimerQuarterSecondTick; +} + + +uint8_t guiTimerSecondOn(void) { + return _guiTimerSecondOn; +} + + +uint8_t guiTimerSecondTick(void) { + return _guiTimerSecondTick; +} + + void *guiUserDataGet(WidgetT *widget) { return widget->userData; } diff --git a/client/src/gui/gui.h b/client/src/gui/gui.h index 948d0bd..1d2b246 100644 --- a/client/src/gui/gui.h +++ b/client/src/gui/gui.h @@ -54,6 +54,7 @@ enum MagicE { MAGIC_UPDOWN, MAGIC_LISTBOX, MAGIC_TERMINAL, + MAGIC_TIMER, //MAGIC_DROPDOWN, MAGIC_COUNT }; @@ -201,10 +202,19 @@ void guiMousePositionOnWidgetGet(WidgetT *widget, MouseT *mouse, uint16_t * void guiMouseProcess(MouseT *mouse); void guiPaint(WidgetT *widget); void guiParentAndChildrenDirtySet(WidgetT *widget); +void guiPendingEventAdd(WidgetT *widget, void *callback); WidgetT *guiRootGet(void); void guiShutdown(void); DesktopT *guiStartup(void); void guiStop(void); +uint32_t guiTimerGet(void); +uint8_t guiTimerHalfSecondOn(void); +uint8_t guiTimerHalfSecondTick(void); +void guiTimerProcess(uint32_t rawClock); +uint8_t guiTimerQuarterSecondOn(void); +uint8_t guiTimerQuarterSecondTick(void); +uint8_t guiTimerSecondOn(void); +uint8_t guiTimerSecondTick(void); void *guiUserDataGet(WidgetT *widget); void guiUserDataSet(WidgetT *widget, void *userData); void guiWidgetAndChildrenDirtySet(WidgetT *widget); diff --git a/client/src/gui/image.c b/client/src/gui/image.c index e93e3af..811dc58 100644 --- a/client/src/gui/image.c +++ b/client/src/gui/image.c @@ -26,6 +26,9 @@ #include "vesa.h" +static ImageT *imageNativeImageGet(unsigned char *raw, uint16_t x, uint16_t y); + + ImageT *imageAllocate(uint16_t w, uint16_t h) { uint16_t x; uint16_t y; @@ -78,6 +81,20 @@ ImageT *imageCreate(uint16_t w, uint16_t h, ColorT color) { } +ImageT *imageFromRAMLoad(uint8_t *buffer, size_t len) { + uint16_t x; + uint16_t y; + uint16_t n; + unsigned char *raw; + + // Load image from RAM. + raw = stbi_load_from_memory((const unsigned char *)buffer, len, (int *)&x, (int *)&y, (int *)&n, 3); + if (!raw) return NULL; + + return imageNativeImageGet(raw, x, y); +} + + uint16_t imageHeightGet(ImageT *image) { return image->height; } @@ -104,14 +121,20 @@ ImageT *imageLoad(char *filename) { uint16_t x; uint16_t y; uint16_t n; - uint32_t b; unsigned char *raw; - ImageT *image; // Load image from disk raw = stbi_load(filename, (int *)&x, (int *)&y, (int *)&n, 3); if (!raw) return NULL; + return imageNativeImageGet(raw, x, y); +} + + +static ImageT *imageNativeImageGet(unsigned char *raw, uint16_t x, uint16_t y) { + uint32_t b; + ImageT *image; + // Create native image. image = imageAllocate(x, y); if (!image) { diff --git a/client/src/gui/image.h b/client/src/gui/image.h index 57ee183..5ebac64 100644 --- a/client/src/gui/image.h +++ b/client/src/gui/image.h @@ -37,6 +37,7 @@ typedef struct ImageS { ImageT *imageAllocate(uint16_t w, uint16_t h); 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); ImageT *imageLoad(char *filename); diff --git a/client/src/gui/label.c b/client/src/gui/label.c index 0bd5213..de75bc1 100644 --- a/client/src/gui/label.c +++ b/client/src/gui/label.c @@ -95,7 +95,7 @@ static void labelMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t // Fire callback on mouse up. if (event == MOUSE_EVENT_LEFT_UP) { - if (l->clicked) l->clicked(widget); + if (l->clicked) guiPendingEventAdd(widget, l->clicked); } } diff --git a/client/src/gui/picture.c b/client/src/gui/picture.c index 1986c27..6cc5077 100644 --- a/client/src/gui/picture.c +++ b/client/src/gui/picture.c @@ -67,7 +67,7 @@ static void pictureMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16 // Fire callback on mouse up. if (event == MOUSE_EVENT_LEFT_UP) { - if (p->clicked) p->clicked(widget); + if (p->clicked) guiPendingEventAdd(widget, p->clicked); } } diff --git a/client/src/gui/radio.c b/client/src/gui/radio.c index cf01acf..3c42180 100644 --- a/client/src/gui/radio.c +++ b/client/src/gui/radio.c @@ -70,7 +70,7 @@ static void radioMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t // Select us. radioSelectedSet(r); // Fire callback on mouse up. - if (r->clicked) r->clicked(widget); + if (r->clicked) guiPendingEventAdd(widget, r->clicked); } } diff --git a/client/src/gui/textbox.c b/client/src/gui/textbox.c index c1806e1..6fc8dba 100644 --- a/client/src/gui/textbox.c +++ b/client/src/gui/textbox.c @@ -301,7 +301,7 @@ static void textboxPaint(WidgetT *widget, uint8_t enabled, RectT pos) { free(draw); // Draw cursor. - if (guiFocusGet() == widget && __timerQuarterSecondOn) { + if (guiFocusGet() == widget && guiTimerQuarterSecondOn()) { caretPos = textX + fontWidthGet(_guiFont) * t->caret; fontRender(_guiFont, cursor, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], caretPos, textY); } diff --git a/client/src/gui/timer.c b/client/src/gui/timer.c new file mode 100644 index 0000000..8f8ebcb --- /dev/null +++ b/client/src/gui/timer.c @@ -0,0 +1,109 @@ +/* + * 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 "timer.h" + + +static void timerPaint(WidgetT *widget, uint8_t enabled, RectT pos); + + +WidgetT *timerInit(WidgetT *widget, widgetCallback timeout, uint16_t quarterSeconds) { + TimerT *t = (TimerT *)widget; + + t->running = 1; + t->counter = 0; + t->quarterSeconds = quarterSeconds; + t->timeout = timeout; + t->base.paintMethod = timerPaint; + + // We use the paint event to handle timer processing. + GUI_SET_FLAG(widget, WIDGET_FLAG_ALWAYS_PAINT); + + return widget; +} + + +TimerT *timerNew(widgetCallback timeout, uint16_t quarterSeconds) { + TimerT *timer = (TimerT *)malloc(sizeof(TimerT)); + WidgetT *widget = NULL; + + if (!timer) return NULL; + + widget = widgetInit(W(timer), MAGIC_TIMER, 0, 0, 0, 0, 0, 0, 0, 0); + if (!widget) { + free(timer); + return NULL; + } + + timer = (TimerT *)timerInit((WidgetT *)timer, timeout, quarterSeconds); + if (!timer) { + free(timer); + return NULL; + } + + return timer; +} + + +static void timerPaint(WidgetT *widget, uint8_t enabled, RectT pos) { + TimerT *t = (TimerT *)widget; + + (void)enabled; + (void)pos; + + // Do we care? + if (t->running) { + // Everything ticks off the quarter second. + if (guiTimerQuarterSecondTick()) t->counter++; + // We don't check for timeout inside the tick condition so that timers of 0 fire immediately. + if (t->counter >= t->quarterSeconds) { + // Timeout! Call user. + guiPendingEventAdd(widget, t->timeout); + t->counter = 0; + } + } +} + + +void timerQuarterSecondsSet(TimerT *timer, uint16_t quarterSeconds) { + timer->quarterSeconds = quarterSeconds; + timer->counter = 0; +} + + +void timerReset(TimerT *timer) { + timer->counter = 0; +} + + +void timerStart(TimerT *timer) { + timer->counter = 0; + timer->running = 1; +} + + +void timerStop(TimerT *timer) { + timer->running = 0; +} + + +void timerTimeoutSet(TimerT *timer, widgetCallback timeout) { + timer->timeout = timeout; +} diff --git a/client/src/system/timer.h b/client/src/gui/timer.h similarity index 55% rename from client/src/system/timer.h rename to client/src/gui/timer.h index f9b8fd0..5c74278 100644 --- a/client/src/system/timer.h +++ b/client/src/gui/timer.h @@ -22,22 +22,26 @@ #define TIMER_H -#include "os.h" +#include "gui.h" +#include "widget.h" -extern uint8_t __timerQuarterSecondTick; -extern uint8_t __timerHalfSecondTick; -extern uint8_t __timerSecondTick; - -extern uint8_t __timerQuarterSecondOn; -extern uint8_t __timerHalfSecondOn; -extern uint8_t __timerSecondOn; +typedef struct TimerS { + WidgetT base; // Must be first in every widget + widgetCallback timeout; + uint16_t quarterSeconds; + uint16_t counter; + uint8_t running; +} TimerT; -void timerShutdown(void); -void timerStartup(void); -void timerUpdate(void); -void timerQuarterSecondsWait(uint8_t quarterSeconds); +WidgetT *timerInit(WidgetT *widget, widgetCallback timeout, uint16_t quarterSeconds); +TimerT *timerNew(widgetCallback timeout, uint16_t quarterSeconds); +void timerQuarterSecondsSet(TimerT *timer, uint16_t quarterSeconds); +void timerReset(TimerT *timer); +void timerStart(TimerT *timer); +void timerStop(TimerT *timer); +void timerTimeoutSet(TimerT *timer, widgetCallback timeout); #endif // TIMER_H diff --git a/client/src/gui/updown.c b/client/src/gui/updown.c index 7ef5b93..d33b6ca 100644 --- a/client/src/gui/updown.c +++ b/client/src/gui/updown.c @@ -310,7 +310,7 @@ static void updownPaint(WidgetT *widget, uint8_t enabled, RectT pos) { fontRender(_guiFont, draw, _guiColor[COLOR_UPDOWN_TEXT], _guiColor[COLOR_UPDOWN_BACKGROUND], textX, textY); // Draw cursor. - if (guiFocusGet() == widget && __timerQuarterSecondOn) { + if (guiFocusGet() == widget && guiTimerQuarterSecondOn()) { textX += (strlen(draw) - 1) * fontWidthGet(_guiFont); fontRender(_guiFont, cursor, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], textX, textY); } diff --git a/client/src/hangup.c b/client/src/hangup.c new file mode 100644 index 0000000..7438dbc --- /dev/null +++ b/client/src/hangup.c @@ -0,0 +1,107 @@ +/* + * 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 "window.h" +#include "label.h" +#include "timer.h" + +#include "taglist.h" +#include "config.h" +#include "network.h" +#include "comport.h" + +#include "hangup.h" + + +typedef enum HangupStateE { + S_START_HANGUP = 0, + S_WAITING +} HangupStateT; + + +static widgetCallback _done = NULL; +static WindowT *_winHangup = NULL; +static LabelT *_lblHangup = NULL; +static TimerT *_timProgress = NULL; +static HangupStateT _state = S_START_HANGUP; + + +static void timHangupProgress(WidgetT *widget); + + +void hangupShow(void *nextFunction) { + + _done = (widgetCallback)nextFunction; + + TagItemT uiDetecting[] = { + T_START, + T_WINDOW, O(_winHangup), + T_TITLE, P("Goodbye"), + T_WIDTH, 200, T_HEIGHT, 100, + T_LABEL, O(_lblHangup), + T_X, 41, T_Y, 25, + T_TITLE, P("Disconnecting!"), + T_LABEL, T_DONE, + T_TIMER, O(_timProgress), + T_EVENT, P(timHangupProgress), + T_VALUE, 0, + T_TIMER, T_DONE, + T_WINDOW, T_DONE, + T_END + }; + + // We can use a module-global variable instead of widget user data + // because there will never be more than one disconnect dialog on + // the screen at a time. + _state = S_START_HANGUP; + + tagListRun(uiDetecting); +} + + +static void timHangupProgress(WidgetT *widget) { + PacketEncodeDataT encoded = { 0 }; + TimerT *t = (TimerT *)widget; + + switch (_state) { + case S_START_HANGUP: + // Tell server we're disconnecting. + encoded.packetType = PACKET_TYPE_CLIENT_SHUTDOWN; + encoded.control = PACKET_CONTROL_DAT; + encoded.channel = 0; + encoded.encrypt = 0; + packetEncode(__packetThreadData, &encoded, NULL, 0); + packetSend(__packetThreadData, &encoded); + // Snooze a bit for the packet to send. + _state = S_WAITING; + timerQuarterSecondsSet(t, 3 * 4); + break; + + case S_WAITING: + // Shut down packet processing & COM port. + netShutdown(); + comClose(__configData.serialCom - 1); + timerStop(t); + // On to the next dialog. + guiDelete(D(_winHangup)); + _done(NULL); + break; + } +} diff --git a/client/src/firstrun.h b/client/src/hangup.h similarity index 89% rename from client/src/firstrun.h rename to client/src/hangup.h index dd7f45b..e64d166 100644 --- a/client/src/firstrun.h +++ b/client/src/hangup.h @@ -18,14 +18,14 @@ */ -#ifndef FIRSTRUN_H -#define FIRSTRUN_H +#ifndef HANGUP_H +#define HANGUP_H #include "os.h" -void firstRunCheckSetup(void); +void hangupShow(void *nextFunction); -#endif // FIRSTRUN_H +#endif // HANGUP_H diff --git a/client/src/linux/linux.c b/client/src/linux/linux.c index 359a92f..57d5519 100644 --- a/client/src/linux/linux.c +++ b/client/src/linux/linux.c @@ -32,9 +32,6 @@ #include "comport.h" -#define SECONDS_IN_DAY 86400 -#define TICKS_PER_SECOND 18.2 -#define TICKS_PER_DAY (SECONDS_IN_DAY * TICKS_PER_SECOND) #define TIMER_INTERVAL (1000 / TICKS_PER_SECOND) #define COM_BUFFER_SIZE 2048 @@ -61,7 +58,7 @@ static uint8_t _ASCIIKeep = 0; static uint8_t _scanCodeKeep = 0; static uint8_t _keyPressed = 0; static uint8_t _debounce = 0; -static long _timerTicks = 0; +static uint32_t _timerTicks = 0; static SDL_TimerID _timerID = { 0 }; static ENetHost *_host = NULL; static ENetPeer *_peer = NULL; @@ -86,10 +83,7 @@ static uint32_t timerCallback(uint32_t interval, void *param); static void vbeScreenshot(void); -long biostime(int cmd, long newtime) { - (void)cmd; - (void)newtime; - +uint32_t rawclock(void) { return _timerTicks; } diff --git a/client/src/login.c b/client/src/login.c index 8328a95..b5e2612 100644 --- a/client/src/login.c +++ b/client/src/login.c @@ -21,61 +21,82 @@ #include "textbox.h" #include "button.h" #include "msgbox.h" +#include "timer.h" #include "runtime.h" #include "config.h" #include "comport.h" #include "network.h" #include "taglist.h" -#include "timer.h" -#include "task.h" #include "login.h" #include "signup.h" #include "welcome.h" #include "menu.h" +#include "hangup.h" -static WindowT *_winLogin = NULL; -static TextboxT *_txtUser = NULL; -static TextboxT *_txtPass = NULL; -static ButtonT *_btnCancel = NULL; -static ButtonT *_btnSignUp = NULL; -static ButtonT *_btnLogin = NULL; +typedef enum LoginStateE { + S_START_LOGIN = 0, + S_WAIT_LOGIN +} LoginStateT; + + +static WindowT *_winLogin = NULL; +static TextboxT *_txtUser = NULL; +static TextboxT *_txtPass = NULL; +static ButtonT *_btnCancel = NULL; +static ButtonT *_btnSignUp = NULL; +static ButtonT *_btnLogin = NULL; +static TimerT *_timProgress = NULL; +static LoginStateT _state = S_START_LOGIN; +static uint8_t _channel = 0; static void btnCancelClick(WidgetT *widget); +static void btnLoginClick(WidgetT *widget); static void btnSignUpClick(WidgetT *widget); static void btnMsgBoxCancel(MsgBoxButtonT button); static void btnMsgBoxContinue(MsgBoxButtonT button); +static void packetHandler(PacketDecodeDataT *packet); static void setButtons(uint8_t enabled); -static void taskDisconnect(void *data); -static void taskLoginClick(void *data); +static void timLoginProgress(WidgetT *widget); static void btnCancelClick(WidgetT *widget) { (void)widget; - // ***TODO*** This should disable the entire dialog instead of just the buttons. Need to finish the Enable GUI code first. - setButtons(0); msgBoxTwo("Cancel?", MSGBOX_ICON_QUESTION, "Cancel login?\n \nThis will disconnect you from the server.", "Okay", btnMsgBoxCancel, "Cancel", btnMsgBoxCancel); } +static void btnLoginClick(WidgetT *widget) { + (void)widget; + + setButtons(0); + + _state = S_START_LOGIN; + timerQuarterSecondsSet(_timProgress, 0); + timerStart(_timProgress); +} + + static void btnSignUpClick(WidgetT *widget) { (void)widget; + netChannelRelease(_channel); guiDelete(D(_winLogin)); - taskCreate(taskSignUp, NULL); + signupShow(); } static void btnMsgBoxCancel(MsgBoxButtonT button) { if (button == MSGBOX_BUTTON_ONE) { + netChannelRelease(_channel); guiDelete(D(_winLogin)); - taskCreate(taskDisconnect, taskWelcome); + hangupShow(welcomeShow); } else { setButtons(1); } @@ -89,24 +110,9 @@ static void btnMsgBoxContinue(MsgBoxButtonT button) { } -static void setButtons(uint8_t enabled) { - widgetEnableSet(W(_btnCancel), enabled); - widgetEnableSet(W(_btnSignUp), enabled); - widgetEnableSet(W(_btnLogin), enabled); -} +void loginShow() { - -static void taskDisconnect(void *nextTask) { - netDisconnect(); - if (nextTask) taskCreate(taskWelcome, nextTask); -} - - -void taskLogin(void *data) { - - (void)data; - - // ***TODO*** We used to have a FORGOT PASSWORD button here, too. + // ***TODO*** We used to have a FORGOT PASSWORD link here, too. TagItemT uiLogin[] = { T_START, @@ -142,70 +148,88 @@ void taskLogin(void *data) { T_BUTTON, O(_btnLogin), T_TITLE, P("Login"), T_X, 199, T_Y, 85, - T_CLICK, P(taskProxy), - T_USER_DATA, P(taskLoginClick), + 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); } -static void taskLoginClick(void *data) { - char *packetData = NULL; - int8_t success = 0; - uint16_t length = 0; - int16_t timeout = 10; - PacketEncodeDataT encoded = { 0 }; - PacketDecodeDataT *decoded = NULL; +static void packetHandler(PacketDecodeDataT *packet) { + char *packetData; + int8_t success; - (void)data; + switch (packet->packetType) { + case PACKET_TYPE_LOGIN_RESULT: + packetContentUnpack(packet->data, "is", &success, &packetData); + logWrite("Login: %d %s\n", success, packetData); + if (success) { + netChannelRelease(_channel); + guiDelete(D(_winLogin)); + menuShow(); + } else { + msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, packetData, "Okay", btnMsgBoxContinue); + } + DEL(packetData); + timerStop(_timProgress); + break; + default: + timerStop(_timProgress); + msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, "Unexpected packet received.", "Okay", btnMsgBoxContinue); + break; + } + + packetDecodeDataDestroy(&packet); +} + + +static void setButtons(uint8_t enabled) { // ***TODO*** This should disable the entire dialog instead of just the buttons. Need to finish the Enable GUI code first. - setButtons(0); - - packetData = packetContentPack(&length, "ss", textboxValueGet(_txtUser), textboxValueGet(_txtPass)); - - // Send login request. - encoded.packetType = PACKET_TYPE_LOGIN; - encoded.control = PACKET_CONTROL_DAT; - encoded.channel = 0; - encoded.encrypt = 1; - packetEncode(__packetThreadData, &encoded, packetData, length); - packetSend(__packetThreadData, &encoded); - - DEL(packetData); - - // Wait for response. - do { - decoded = netGetPacket(0); - if (decoded) { - switch (decoded->packetType) { - case PACKET_TYPE_LOGIN_RESULT: - packetContentUnpack(decoded->data, "is", &success, &packetData); - logWrite("Login: %d %s\n", success, packetData); - if (success) { - guiDelete(D(_winLogin)); - taskCreate(taskMenu, NULL); - } else { - msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, packetData, "Okay", btnMsgBoxContinue); - } - DEL(packetData); - timeout = 0; - break; - - default: - msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, "Unexpected packet received.", "Okay", btnMsgBoxContinue); - timeout = 0; - break; - } - packetDecodeDataDestroy(&decoded); - } - taskYield(); - if (__timerQuarterSecondTick) timeout--; - } while (!guiHasStopped() && timeout > 0); + widgetEnableSet(W(_btnCancel), enabled); + widgetEnableSet(W(_btnSignUp), enabled); + widgetEnableSet(W(_btnLogin), enabled); } + + +static void timLoginProgress(WidgetT *widget) { + char *packetData; + uint16_t length; + PacketEncodeDataT encoded; + TimerT *t = (TimerT *)widget; + + switch (_state) { + case S_START_LOGIN: + setButtons(0); + packetData = packetContentPack(&length, "ss", textboxValueGet(_txtUser), textboxValueGet(_txtPass)); + // Send login request. + encoded.packetType = PACKET_TYPE_LOGIN; + encoded.control = PACKET_CONTROL_DAT; + encoded.channel = _channel; + encoded.encrypt = 1; + packetEncode(__packetThreadData, &encoded, packetData, length); + packetSend(__packetThreadData, &encoded); + DEL(packetData); + // Wait for response. + timerQuarterSecondsSet(t, 10 * 4); + _state = S_WAIT_LOGIN; + break; + + case S_WAIT_LOGIN: + timerStop(t); + msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, "No response received.", "Okay", btnMsgBoxContinue); + break; + } +} + diff --git a/client/src/login.h b/client/src/login.h index c1dfa23..e1461fb 100644 --- a/client/src/login.h +++ b/client/src/login.h @@ -25,7 +25,7 @@ #include "os.h" -void taskLogin(void *data); +void loginShow(void); #endif // LOGIN_H diff --git a/client/src/main.c b/client/src/main.c index e34b682..5792504 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -40,71 +40,125 @@ #include "surface.h" #include "mouse.h" #include "keyboard.h" -#include "task.h" #include "image.h" #include "font.h" -#include "timer.h" #include "gui.h" #include "config.h" #include "runtime.h" #include "comport.h" #include "network.h" -#include "firstrun.h" +#include "timer.h" +#include "embedded/embedded.h" #include "welcome.h" -#include "signup.h" +#include "settings.h" PacketThreadDataT *__packetThreadData = NULL; // Exported in os.h RuntimeDataT __runtimeData; // Exported in runtime.h +static MouseT *_mouse = NULL; +static ImageT *_pointer = NULL; +static ColorT _alpha = 0; -static void taskGuiEventLoop(void *data); +#ifndef __linux__ +char *_logName = NULL; +#endif -static void taskGuiEventLoop(void *data) { - MouseT *mouse = NULL; - ImageT *pointer = NULL; - ColorT alpha = { 0 }; +static void checkSettings(void); +static void eventLoop(void); +static uint8_t hasValidSettings(void); +static void shutdown(void); +static uint8_t startup(int argc, char *argv[]); - (void)data; - pointer = imageLoad("data/mouse.png"); - alpha = imagePixelGet(pointer, 5, 0); +static void checkSettings(void) { + // Do we have a valid COM port? + if (!hasValidSettings()) { + logWrite("No compatible modem found. Cannot continue.\n"); + guiStop(); + return; + } + welcomeShow(); + eventLoop(); +} + + +static void eventLoop(void) { + // Main Event Loop. do { - timerUpdate(); - mouse = mouseRead(); + netProcess(); + guiTimerProcess(rawclock()); + _mouse = mouseRead(); if (keyHit()) { guiKeyboardProcess(keyASCIIGet(), keyExtendedGet(), keyScanCodeGet(), keyShiftGet(), keyControlGet(), keyAltGet()); //logWrite("Key '%d' Extended '%d' Scancode '%d' Shift '%d' Control '%d' Alt '%d'\n", keyASCIIGet(), keyExtended(), keyScanCode(), keyShift(), keyControl(), keyAlt()); if (keyASCIIGet() == 27) guiStop(); } - guiMouseProcess(mouse); + guiMouseProcess(_mouse); guiComposite(); - imageRenderWithAlpha(pointer, mouse->x, mouse->y, alpha); + imageRenderWithAlpha(_pointer, _mouse->x, _mouse->y, _alpha); vbeVBlankWait(); vbePresent(); - taskYield(); } while (!guiHasStopped()); - imageUnload(&pointer); + shutdown(); } +static uint8_t hasValidSettings(void) { + return (__configData.serialCom > 0 && __configData.serialCom < 5); +} -int main(int argc, char *argv[]) { + +static void shutdown(void) { #ifndef __linux__ // On DOS, display the contets of the log now that we're back in text mode. - char *logName = NULL; FILE *in = NULL; #endif + imageUnload(&_pointer); + + netShutdown(); + guiShutdown(); + mouseShutdown(); + surfaceShutdown(); + vbeShutdown(); + configShutdown(); + osShutdown(); + logClose(); + memoryShutdown(); + +#ifndef __linux__ + //***TODO*** Why the frack does this not work?! + textmode(C80); + // On DOS, display the contets of the log now that we're back in text mode. + in = fopen(_logName, "r"); + if (in) { + while (!feof(in)) { + putc(fgetc(in), stdout); + } + fclose(in); + free(in); + } + free(_logName); + _Exit(0); +#endif +} + + +static uint8_t startup(int argc, char *argv[]) { memoryStartup(argv[0]); logOpenByHandle(memoryLogHandleGet()); osStartup(); configStartup(argv[0]); +#ifndef __linux__ + _logName = utilAppNameWithNewExtensionGet(argv[0], "log"); +#endif + // 0 1 2 3 4 5 6 7 8 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 logWrite("%s", "Kangaroo Punch MultiPlayer DOS Game Client Mark II\n"); @@ -116,16 +170,10 @@ int main(int argc, char *argv[]) { configShutdown(); logClose(); memoryShutdown(); - return 0; + return 1; } } - timerStartup(); - taskStartup(); - - // Perform "first run" setup tasks. - firstRunCheckSetup(); - // Do we have the video mode they asked for? if (vbeStartup(__configData.videoWidth, __configData.videoHeight, __configData.videoDepth)) { configShutdown(); @@ -137,42 +185,29 @@ int main(int argc, char *argv[]) { surfaceStartup(); mouseStartup(); guiStartup(); - // Do not call netStartup() here. It will be started when needed. + netStartup(); -// taskCreate(taskSignUp, NULL); - taskCreate(taskWelcome, NULL); - taskCreate(taskGuiEventLoop, NULL); - - taskRun(); - - netShutdown(); - guiShutdown(); - mouseShutdown(); - surfaceShutdown(); - vbeShutdown(); - taskShutdown(); - timerShutdown(); - configShutdown(); - osShutdown(); - logClose(); - memoryShutdown(); - -#ifndef __linux__ - //***TODO*** Why the frack does this not work?! - textmode(C80); - // On DOS, display the contets of the log now that we're back in text mode. - logName = utilAppNameWithNewExtensionGet(argv[0], "log"); - in = fopen(logName, "r"); - if (in) { - while (!feof(in)) { - putc(fgetc(in), stdout); - } - fclose(in); - free(in); - } - free(logName); - _Exit(0); -#endif + _pointer = imageFromRAMLoad(mouse_png, mouse_png_len); + _alpha = imagePixelGet(_pointer, 5, 0); + + return 0; +} + + +int main(int argc, char *argv[]) { + + if (startup(argc, argv)) return 1; + + // Perform "first run" setup tasks or start the client? + if (hasValidSettings()) { + // We have what we need, start the client. + welcomeShow(); + } else { + // Run the setup. + settingsShow(checkSettings); + } + + eventLoop(); return 0; } diff --git a/client/src/menu.c b/client/src/menu.c index f504656..597ea66 100644 --- a/client/src/menu.c +++ b/client/src/menu.c @@ -21,7 +21,6 @@ #include "menu.h" -void taskMenu(void *data) { - (void)data; +void menuShow(void) { } diff --git a/client/src/menu.h b/client/src/menu.h index 738a4ce..eaeb502 100644 --- a/client/src/menu.h +++ b/client/src/menu.h @@ -25,7 +25,7 @@ #include "os.h" -void taskMenu(void *data); +void menuShow(void); #endif // MENU_H diff --git a/client/src/network.c b/client/src/network.c index db6d4fd..0c72974 100644 --- a/client/src/network.c +++ b/client/src/network.c @@ -19,7 +19,6 @@ #include "gui.h" -#include "task.h" #include "comport.h" #include "config.h" #include "timer.h" @@ -28,174 +27,30 @@ #include "network.h" -typedef struct NetworkPacketS { - uint8_t key; - PacketDecodeDataT **value; -} NetworkPacketT; +typedef struct NetworkChannelsS { + uint8_t key; + netPacketHandler value; +} NetworkChannelsT; -static NetworkPacketT *_packets = NULL; -static uint8_t _netRunning = 0; -static uint8_t _netStarted = 0; +static netPacketHandler *_systemHandlers = NULL; +static NetworkChannelsT *_channels = NULL; +static uint8_t _netRunning = 0; +static uint8_t _netStarted = 0; -int8_t netConnectStep1(void) { - int32_t r = 0; - char buffer[1024] = { 0 }; - - // Open COM port. - r = comOpen(__configData.serialCom - 1, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS); - if (r != SER_SUCCESS) return NETWORK_CONNECT_OPEN_FAILED; - // Send a CR to clear anything in the modem. - snprintf(buffer, 1023, "%c", 13); - comWrite(__configData.serialCom - 1, buffer, strlen(buffer)); - // Wait roughly a second for anything. - comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4, "weExpectNothing"); - // Send actual init - snprintf(buffer, 1023, "%s%c", "AT+SOCK1", 13); - comWrite(__configData.serialCom - 1, buffer, strlen(buffer)); - // Wait roughly a second for "OK". - r = comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4, "OK"); - if (r <= 0) { - comClose(__configData.serialCom - 1); - return NETWORK_CONNECT_BAD_MODEM; - } - // Flush COM port. - timerQuarterSecondsWait(4); - comReceiveBufferFlush(__configData.serialCom - 1); - - return NETWORK_CONNECT_SUCCESS; -} - - -int8_t netConnectStep2(void) { - int32_t r = 0; - char buffer[1024] = { 0 }; - PacketDecodeDataT *decoded = NULL; - PacketEncodeDataT encoded = { 0 }; - uint8_t waiting = 1; - int16_t timeout = 0; - - snprintf(buffer, 1023, "ATDT%s:%d%c", __configData.serverHost, __configData.serverPort, 13); - comWrite(__configData.serialCom - 1, buffer, strlen(buffer)); - // Wait 7 seconds for welcome banner. - r = comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4 * 7, "KPMPGSMKII\r"); - if (r <= 0) { - comClose(__configData.serialCom - 1); - return NETWORK_CONNECT_NO_CARRIER; - } - - // Start packet handler and negotiate encryption. - netStartup(); - packetEncryptionSetup(__packetThreadData); - timeout = 5; - do { - if (packetEncryptionReady()) break; - taskYield(); - if (__timerSecondTick) timeout--; - } while (!guiHasStopped() && timeout >= 0); - if (timeout < 0) { - comClose(__configData.serialCom - 1); - return NETWORK_CONNECT_BAD_ENCRYPTION; - } - - // ***TODO*** Should probably cleanly handle a full server here with some kind of SERVER_FULL packet. - - // Wait for version and table packets to arrive. - timeout = 10; - do { - decoded = netGetPacket(0); - if (decoded) { - switch (decoded->packetType) { - case PACKET_TYPE_NUMBER: - // Store in number table. - shput(__runtimeData.integers, &decoded->data[4], (int32_t)decoded->data[0]); - // Reset timeout. - timeout = 10; - break; - - case PACKET_TYPE_PROCEED: - waiting = 0; - break; - - case PACKET_TYPE_STRING: - // Store in string table. - //logWrite("Storing [%s]=[%s]\n", decoded->data, &decoded->data[strlen(decoded->data) + 1]); - shput(__runtimeData.strings, decoded->data, strdup(&decoded->data[strlen(decoded->data) + 1])); - // Reset timeout. - timeout = 10; - break; - - case PACKET_TYPE_VERSION: - packetContentUnpack(decoded->data, "i", &__runtimeData.protocolVersion); - // Do we need to update? - if (PACKET_PROTOCOL_VERSION == __runtimeData.protocolVersion) { - // Nope, we're good. - encoded.packetType = PACKET_TYPE_VERSION_OKAY; - } else { - // Version mismatch - upgrade time! - encoded.packetType = PACKET_TYPE_VERSION_BAD; - //***TODO*** - } - encoded.control = PACKET_CONTROL_DAT; - encoded.channel = 0; - encoded.encrypt = 0; - packetEncode(__packetThreadData, &encoded, NULL, 0); - // Send GOOD or BAD. - packetSend(__packetThreadData, &encoded); - // Reset timeout. - timeout = 10; - break; - - default: - logWrite("Unexpected packet received: %d\n", decoded->packetType); - break; - } - packetDecodeDataDestroy(&decoded); - } - taskYield(); - if (__timerSecondTick) timeout--; - } while (!guiHasStopped() && timeout >= 0 && waiting); - if (timeout < 0) { - comClose(__configData.serialCom - 1); - return NETWORK_CONNECT_BAD_SETTINGS; - } - - return NETWORK_CONNECT_SUCCESS; -} - - -void netDisconnect(void) { - PacketEncodeDataT encoded = { 0 }; - int16_t timeout = 2; - - // Tell server we're disconnecting. - encoded.packetType = PACKET_TYPE_CLIENT_SHUTDOWN; - encoded.control = PACKET_CONTROL_DAT; - encoded.channel = 0; - encoded.encrypt = 0; - packetEncode(__packetThreadData, &encoded, NULL, 0); - packetSend(__packetThreadData, &encoded); - - // Snooze a bit for the packet to send. - while (timeout > 0) { - taskYield(); - if (__timerQuarterSecondTick) timeout--; - } - - // Shut down packet processing & COM port. - netShutdown(); - comClose(__configData.serialCom - 1); -} - - -uint8_t netGetChannelFree(void) { - uint8_t channel = 0; // We reserve 0 for system stuff. Returning 0 means no channel was found. +uint8_t netChannelGet(netPacketHandler handler) { + uint8_t channel = 0; uint16_t x = 0; - for (x=1; x<255; x++) { - if (hmgeti(_packets, x) < 0) { + // We reserve 0 for system stuff. + // Returning 0 means no channel was found. + + // Find first unused channel. + for (x=1; x<255; x++) { + if (hmgeti(_channels, x) < 0) { channel = x; + hmput(_channels, x, handler); break; } } @@ -204,23 +59,121 @@ uint8_t netGetChannelFree(void) { } -PacketDecodeDataT *netGetPacket(uint8_t channel) { - int32_t r = 0; - PacketDecodeDataT *packet = NULL; +void netChannelRelease(uint8_t channel) { + hmdel(_channels, channel); +} - r = hmgeti(_packets, channel); - if (r < 0) return NULL; - if (arrlen(_packets[r].value) == 0) return NULL; - packet = _packets[r].value[0]; - arrdel(_packets[r].value, 0); - return packet; +void netChannelSystemGet(netPacketHandler handler) { + arrput(_systemHandlers, handler); +} + + +void netChannelSystemRelease(netPacketHandler handler) { + uint16_t x; + + for (x=0; xchannel = decoded.channel; + packet->length = decoded.length; + packet->packetType = decoded.packetType; + packet->data = (char *)malloc(decoded.length); + memcpy(packet->data, decoded.data, decoded.length); + // Send it to the subscriber. + _systemHandlers[r](packet); + } + } else { + // Does someone want this channel? + r = hmgeti(_channels, decoded.channel); + if (r >= 0) { + // Copy the packet out. + NEW(PacketDecodeDataT, packet); + packet->channel = decoded.channel; + packet->length = decoded.length; + packet->packetType = decoded.packetType; + packet->data = (char *)malloc(decoded.length); + memcpy(packet->data, decoded.data, decoded.length); + // Send it to the subscriber. + _channels[r].value(packet); + } + } + // Destroy our copy. + packetDecodeDataStaticDestroy(&decoded); + break; + } + } + + } // _netRunning } void netShutdown(void) { FILE *cache = NULL; + netPacketHandlerStop(); + if (_netStarted) { _netStarted = 0; @@ -230,7 +183,7 @@ void netShutdown(void) { if (__runtimeData.integers) { while (shlen(__runtimeData.integers) > 0) { //logWrite("[%s]=[%d]\n", __runtimeData.integers[0].key, __runtimeData.integers[0].value); - fprintf(cache, "%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); @@ -255,8 +208,6 @@ void netShutdown(void) { packetThreadDataDestroy(&__packetThreadData); } - - _netRunning = 0; } @@ -264,131 +215,59 @@ void netStartup(void) { FILE *cache = NULL; char *line = NULL; char *p = NULL; + char *temp = NULL; - _netStarted = 1; + if (!_netStarted) { + _netStarted = 1; - __packetThreadData = packetThreadDataCreate(NULL); - packetSenderRegister(comPacketSender); + __packetThreadData = packetThreadDataCreate(NULL); + packetSenderRegister(comPacketSender); - __runtimeData.integers = NULL; - __runtimeData.strings = NULL; - __runtimeData.protocolVersion = 0; + __runtimeData.integers = NULL; + __runtimeData.strings = NULL; + __runtimeData.protocolVersion = 0; - sh_new_strdup(__runtimeData.integers); - sh_new_strdup(__runtimeData.strings); + sh_new_strdup(__runtimeData.integers); + sh_new_strdup(__runtimeData.strings); - // ***TODO*** Default initial tables + // ***TODO*** Default initial tables - line = (char *)malloc(4096); - if (line) { - // Load string cache. - cache = fopen("cache/string.dat", "rt"); - if (cache) { - while (fscanf(cache, "%s\n", line) != EOF) { - p = strstr(line, "="); - if (p) { - *p = 0; - p++; - shput(__runtimeData.strings, line, strdup(p)); + line = (char *)malloc(4096); + if (line) { + // Load string cache. + cache = fopen("cache/string.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)); + } } + fclose(cache); } - fclose(cache); - } - // Load integer cache. - cache = fopen("cache/integer.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)); + // Load integer cache. + cache = fopen("cache/integer.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)); + } } + fclose(cache); } - fclose(cache); + free(line); + line = NULL; } - free(line); - line = NULL; } - - taskCreate(taskNetwork, NULL); -} - - -void taskNetwork(void *data) { - int32_t r = 0; - char buffer[1024] = { 0 }; - PacketDecodeDataT *packet = NULL; - PacketDecodeDataT decoded = { 0 }; - PacketEncodeDataT encoded = { 0 }; - - (void)data; - - _netRunning = 1; - - do { - - // ***TODO*** Detect disconnection. Maybe have callbacks registered that can notify tasks? - - // Read pending bytes. - r = comRead(__configData.serialCom - 1, buffer, 1024); - // New data or not, process anything in the queue. - if (packetDecode(__packetThreadData, &decoded, buffer, r)) { - - // Is this something we care about? - switch (decoded.packetType) { - case PACKET_TYPE_PING: - // Reply with PONG - encoded.control = PACKET_CONTROL_DAT; - encoded.packetType = PACKET_TYPE_PONG; - encoded.channel = 0; - encoded.encrypt = 0; - packetEncode(__packetThreadData, &encoded, NULL, 0); - packetSend(__packetThreadData, &encoded); - packetDecodeDataStaticDestroy(&decoded); - continue; - break; - - default: - // Silences a warning. - break; - } - - // Copy the packet out. - NEW(PacketDecodeDataT, packet); - packet->channel = decoded.channel; - packet->length = decoded.length; - packet->packetType = decoded.packetType; - packet->data = (char *)malloc(decoded.length); - memcpy(packet->data, decoded.data, decoded.length); - - // Is there a list of packets for this channel already? - r = hmgeti(_packets, packet->channel); - if (r < 0) { - // No. Create dynamic array for this channel. - hmput(_packets, packet->channel, NULL); - r = hmgeti(_packets, packet->channel); - } - // Add new packet to existing list. - arrput(_packets[r].value, packet); - - packetDecodeDataStaticDestroy(&decoded); - } - - // Yield to UI. - taskYield(); - - } while (!guiHasStopped() && _netRunning); - - // Free any unclaimed packets. - while (hmlenu(_packets) > 0) { - // Free any packets for this channel. - while (arrlenu(_packets[0].value) > 0) { - packetDecodeDataDestroy(&_packets[0].value[0]); - arrdel(_packets[0].value, 0); - } - arrfree(_packets[0].value); - hmdel(_packets, _packets[0].key); - } - hmfree(_packets); } diff --git a/client/src/network.h b/client/src/network.h index fccc4c5..e12e0fd 100644 --- a/client/src/network.h +++ b/client/src/network.h @@ -25,23 +25,18 @@ #include "os.h" -#define NETWORK_CONNECT_SUCCESS 0 -#define NETWORK_CONNECT_OPEN_FAILED 1 -#define NETWORK_CONNECT_BAD_MODEM 2 -#define NETWORK_CONNECT_NO_CARRIER 3 -#define NETWORK_CONNECT_BAD_ENCRYPTION 4 -#define NETWORK_CONNECT_BAD_SETTINGS 5 +typedef void (*netPacketHandler)(PacketDecodeDataT *packet); -int8_t netConnectStep1(void); -int8_t netConnectStep2(void); -void netDisconnect(void); -uint8_t netGetChannelFree(void); -PacketDecodeDataT *netGetPacket(uint8_t channel); -void netShutdown(void); -void netStartup(void); - -void taskNetwork(void *data); +uint8_t netChannelGet(netPacketHandler handler); +void netChannelRelease(uint8_t channel); +void netChannelSystemGet(netPacketHandler handler); +void netChannelSystemRelease(netPacketHandler handler); +void netPacketHandlerStart(void); +void netPacketHandlerStop(void); +void netProcess(void); +void netShutdown(void); +void netStartup(void); #endif // NETWORK_H diff --git a/client/src/settings.c b/client/src/settings.c index d9ced81..739bea5 100644 --- a/client/src/settings.c +++ b/client/src/settings.c @@ -22,9 +22,7 @@ #include "welcome.h" #include "taglist.h" -#include "task.h" #include "comport.h" -#include "timer.h" #include "config.h" #include "window.h" @@ -34,6 +32,7 @@ #include "textbox.h" #include "updown.h" #include "label.h" +#include "timer.h" #define GROUP_COM 1 @@ -46,6 +45,14 @@ #define TITLE_LEN 80 +typedef enum SettingsStateE { + S_START_SCAN = 0, + S_OPEN_COM, + S_WAIT_FOR_INIT, + S_SCAN_COMPLETE +} SettingsStateT; + + typedef struct PortS { uint8_t status; char title[TITLE_LEN]; @@ -55,19 +62,23 @@ typedef struct PortS { } PortT; -static WindowT *_winDetecting; -static LabelT *_lblOneMoment; -static WindowT *_winSettings; -static FrameT *_frmComPorts; -static FrameT *_frmServer; -static ButtonT *_btnOkay; -static TextboxT *_txtServer; -static UpdownT *_updPort; -static PortT _port[4]; -static widgetCallback _done; +static WindowT *_winDetecting = NULL; +static LabelT *_lblOneMoment = NULL; +static WindowT *_winSettings = NULL; +static FrameT *_frmComPorts = NULL; +static FrameT *_frmServer = NULL; +static ButtonT *_btnOkay = NULL; +static TextboxT *_txtServer = NULL; +static TimerT *_timProgress = NULL; +static UpdownT *_updPort = NULL; +static PortT _port[4] = { 0 }; +static widgetCallback _done = NULL; +static SettingsStateT _state = S_START_SCAN; static void btnOkayClick(WidgetT *widget); +static void settingsComShow(void); +static void timSettingsProgress(WidgetT *widget); static void btnOkayClick(WidgetT *widget) { @@ -95,61 +106,9 @@ static void btnOkayClick(WidgetT *widget) { } -int8_t settingsScanComPorts(void) { - char buffer[1024] = { 0 }; - uint8_t selected = 1; - int32_t rc = 0; - uint32_t len = 0; - int8_t lastFound = -1; +void settingsShow(void *callback) { - // Scan the COM ports for a compatable modem. - for (int x=0; x<4; x++) { - rc = comOpen(x, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS); - if (rc == SER_SUCCESS) { - snprintf(buffer, 1023, "%s%c", "AT+SOCK1", 13); - comWrite(x, buffer, strlen(buffer)); - timerQuarterSecondsWait(4); - len = comRead(x, buffer, 1023); - buffer[len] = 0; - if (strstr(buffer, "OK")) { - snprintf(_port[x].title, TITLE_LEN - 1, "COM%d - SoftModem Found!", x + 1); - _port[x].status = PORT_GOOD_MODEM; - _port[x].selected = selected; - _port[x].enabled = 1; - selected = 0; - lastFound = x; - } else { - if (strstr(buffer, "ERROR")) { - snprintf(_port[x].title, TITLE_LEN - 1, "COM%d - Incompatable Modem", x + 1); - _port[x].status = PORT_BAD_MODEM; - _port[x].selected = 0; - _port[x].enabled = 0; - } else { - snprintf(_port[x].title, TITLE_LEN - 1, "COM%d - No Modem", x + 1); - _port[x].status = PORT_NO_MODEM; - _port[x].selected = 0; - _port[x].enabled = 0; - } - } - comClose(x); - } else { - snprintf(_port[x].title, TITLE_LEN - 1, "COM%d - Not Present", x + 1); - _port[x].status = PORT_NONE; - _port[x].selected = 0; - _port[x].enabled = 0; - } - } - - return lastFound; -} - - -void taskSettings(void *data) { - - int32_t rc; - uint32_t len; - - _done = (widgetCallback)data; + _done = (widgetCallback)callback; TagItemT uiDetecting[] = { T_START, @@ -160,14 +119,27 @@ void taskSettings(void *data) { T_X, 25, T_Y, 25, T_TITLE, P("One Moment Please!"), T_LABEL, T_DONE, + T_TIMER, O(_timProgress), + T_EVENT, P(timSettingsProgress), + T_VALUE, 0, + T_TIMER, T_DONE, T_WINDOW, T_DONE, T_END }; - tagListRun(uiDetecting); - taskYield(); // Cause dialog to paint. + // We can use a module-global variable instead of widget user data + // because there will never be more than one settings dialog on + // the screen at a time. + _state = S_START_SCAN; - settingsScanComPorts(); + tagListRun(uiDetecting); +} + + +static void settingsComShow(void) { + + int32_t y; + uint32_t len; guiDelete(D(_winDetecting)); @@ -222,12 +194,88 @@ void taskSettings(void *data) { } // Add COM discovery to GUI. - rc = 0; + y = 0; for (len=0; len<4; len++) { - _port[len].rdoCOM = radioNew(5, rc, _port[len].title, GROUP_COM); + _port[len].rdoCOM = radioNew(5, y, _port[len].title, GROUP_COM); if (_port[len].selected) radioSelectedSet(_port[len].rdoCOM); widgetEnableSet(W(_port[len].rdoCOM), _port[len].enabled); guiAttach(W(_frmComPorts), W(_port[len].rdoCOM)); - rc += 20; + y += 20; + } +} + + +static void timSettingsProgress(WidgetT *widget) { + char buffer[1024]; + int32_t rc; + uint32_t len; + + static uint8_t selected; + static int32_t com; + + switch (_state) { + case S_START_SCAN: + com = 0; + selected = 1; + _state = S_OPEN_COM; + break; + + case S_OPEN_COM: + rc = comOpen(com, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS); + if (rc == SER_SUCCESS) { + snprintf(buffer, 1023, "%s%c", "AT+SOCK1", 13); + comWrite(com, buffer, strlen(buffer)); + timerQuarterSecondsSet((TimerT *)widget, 4); + _state = S_WAIT_FOR_INIT; + } else { + snprintf(_port[com].title, TITLE_LEN - 1, "COM%d - Not Present", com + 1); + _port[com].status = PORT_NONE; + _port[com].selected = 0; + _port[com].enabled = 0; + com++; + if (com < 4) { + _state = S_OPEN_COM; + } else { + _state = S_SCAN_COMPLETE; + } + } + break; + + case S_WAIT_FOR_INIT: + len = comRead(com, buffer, 1023); + buffer[len] = 0; + if (strstr(buffer, "OK")) { + snprintf(_port[com].title, TITLE_LEN - 1, "COM%d - Modem Found!", com + 1); + _port[com].status = PORT_GOOD_MODEM; + _port[com].selected = selected; + _port[com].enabled = 1; + selected = 0; + } else { + if (strstr(buffer, "ERROR")) { + snprintf(_port[com].title, TITLE_LEN - 1, "COM%d - Incompatable Modem", com + 1); + _port[com].status = PORT_BAD_MODEM; + _port[com].selected = 0; + _port[com].enabled = 0; + } else { + snprintf(_port[com].title, TITLE_LEN - 1, "COM%d - No Modem", com + 1); + _port[com].status = PORT_NO_MODEM; + _port[com].selected = 0; + _port[com].enabled = 0; + } + } + comClose(com); + com++; + if (com < 4) { + _state = S_OPEN_COM; + } else { + _state = S_SCAN_COMPLETE; + } + timerQuarterSecondsSet((TimerT *)widget, 0); + break; + + case S_SCAN_COMPLETE: + timerStop(_timProgress); + settingsComShow(); + break; } } diff --git a/client/src/settings.h b/client/src/settings.h index a63c3fe..a89b2d5 100644 --- a/client/src/settings.h +++ b/client/src/settings.h @@ -25,8 +25,7 @@ #include "os.h" -int8_t settingsScanComPorts(void); -void taskSettings(void *data); +void settingsShow(void *callback); #endif // SETTINGS_H diff --git a/client/src/signup.c b/client/src/signup.c index 4addc96..7e7e522 100644 --- a/client/src/signup.c +++ b/client/src/signup.c @@ -22,34 +22,44 @@ #include "button.h" #include "label.h" #include "msgbox.h" +#include "timer.h" #include "network.h" #include "runtime.h" #include "taglist.h" -#include "timer.h" -#include "task.h" #include "signup.h" #include "login.h" -static WindowT *_winSignUp = NULL; -static LabelT *_lblRequired = NULL; -static TextboxT *_txtEmail = NULL; -static TextboxT *_txtFirst = NULL; -static TextboxT *_txtLast = NULL; -static TextboxT *_txtUser = NULL; -static TextboxT *_txtPass1 = NULL; -static TextboxT *_txtPass2 = NULL; -static ButtonT *_btnCancel = NULL; -static ButtonT *_btnSignUp = NULL; +typedef enum SignUpStateE { + S_START_SIGNUP = 0, + S_SIGNUP_WAIT +} SignUpStateT; + + +static WindowT *_winSignUp = NULL; +static LabelT *_lblRequired = NULL; +static TextboxT *_txtEmail = NULL; +static TextboxT *_txtFirst = NULL; +static TextboxT *_txtLast = NULL; +static TextboxT *_txtUser = NULL; +static TextboxT *_txtPass1 = NULL; +static TextboxT *_txtPass2 = NULL; +static ButtonT *_btnCancel = NULL; +static ButtonT *_btnSignUp = NULL; +static TimerT *_timProgress = NULL; +static SignUpStateT _state = S_START_SIGNUP; +static uint8_t _channel = 0; static void btnCancelClick(WidgetT *widget); static void btnMsgBoxContinue(MsgBoxButtonT button); static void btnMsgBoxFinish(MsgBoxButtonT button); +static void btnSignUpClick(WidgetT *widget); +static void packetHandler(PacketDecodeDataT *packet); static void setButtons(uint8_t enabled); -static void taskSignUpClick(void *data); +static void timSignUpProgress(WidgetT *widget); static uint8_t validateEmail(char *email); static uint8_t validateEmailLetter(char c); static uint8_t validateName(char *username); @@ -61,7 +71,8 @@ static void btnCancelClick(WidgetT *widget) { (void)widget; guiDelete(D(_winSignUp)); - taskCreate(taskLogin, NULL); + netChannelRelease(_channel); + loginShow(); } @@ -76,7 +87,73 @@ static void btnMsgBoxFinish(MsgBoxButtonT button) { (void)button; guiDelete(D(_winSignUp)); - taskCreate(taskLogin, NULL); + netChannelRelease(_channel); + loginShow(); +} + + +static void btnSignUpClick(WidgetT *widget) { + (void)widget; + + setButtons(0); + + // Validate it. ***TODO*** These messages could be a lot better. + if (!validateEmail(textboxValueGet(_txtEmail))) { + msgBoxOne("Invalid E-Mail", MSGBOX_ICON_ERROR, "Please enter a valid E-mail address.", "Okay", btnMsgBoxContinue); + return; + } + if (!validateName(textboxValueGet(_txtFirst))) { + msgBoxOne("Invalid First Name", MSGBOX_ICON_ERROR, "Please enter a valid first name.", "Okay", btnMsgBoxContinue); + return; + } + if (!validateName(textboxValueGet(_txtLast))) { + msgBoxOne("Invalid Last Name", MSGBOX_ICON_ERROR, "Please enter a valid last name.", "Okay", btnMsgBoxContinue); + return; + } + if (strcmp(textboxValueGet(_txtPass1), textboxValueGet(_txtPass2)) != 0) { + msgBoxOne("Invalid Password", MSGBOX_ICON_ERROR, "Passwords must match.", "Okay", btnMsgBoxContinue); + return; + } + if (!validatePassword(textboxValueGet(_txtPass1))) { + msgBoxOne("Invalid Password", MSGBOX_ICON_ERROR, "Please enter a valid password.", "Okay", btnMsgBoxContinue); + return; + } + if (!validateUser(textboxValueGet(_txtUser))) { + msgBoxOne("Invalid User Name", MSGBOX_ICON_ERROR, "Please enter a valid user name.", "Okay", btnMsgBoxContinue); + return; + } + + _state = S_START_SIGNUP; + timerQuarterSecondsSet(_timProgress, 0); + timerStart(_timProgress); +} + + +static void packetHandler(PacketDecodeDataT *packet) { + uint16_t length; + char *packetData; + + // Reset timeout. + timerReset(_timProgress); + + switch (packet->packetType) { + case PACKET_TYPE_SIGNUP_RESULT: + packetContentUnpack(packet->data, "is", &length, &packetData); + if (length) { + msgBoxOne("Success!", MSGBOX_ICON_INFORMATION, packetData, "Okay", btnMsgBoxFinish); + } else { + msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, packetData, "Okay", btnMsgBoxContinue); + } + DEL(packetData); + timerStop(_timProgress); + break; + + default: + logWrite("Unexpected packet received: %d\n", packet->packetType); + break; + } + + packetDecodeDataDestroy(&packet); } @@ -86,9 +163,7 @@ static void setButtons(uint8_t enabled) { } -void taskSignUp(void *data) { - - (void)data; +void signupShow(void) { TagItemT uiSignUp[] = { T_START, @@ -160,101 +235,56 @@ void taskSignUp(void *data) { T_BUTTON, O(_btnSignUp), T_TITLE, P("Sign Up"), T_X, 291, T_Y, 270, - T_CLICK, P(taskProxy), - T_USER_DATA, P(taskSignUpClick), + T_CLICK, P(btnSignUpClick), T_BUTTON, T_DONE, + T_TIMER, O(_timProgress), + T_EVENT, P(timSignUpProgress), + T_VALUE, 0, + T_ENABLED, 0, + T_TIMER, T_DONE, T_WINDOW, T_DONE, T_END }; tagListRun(uiSignUp); + _channel = netChannelGet(packetHandler); } -static void taskSignUpClick(void *data) { - PacketEncodeDataT encoded = { 0 }; - PacketDecodeDataT *decoded = NULL; - int16_t timeout = 10; - uint16_t length = 0; - char *packetData = NULL; +static void timSignUpProgress(WidgetT *widget) { + PacketEncodeDataT encoded; + uint16_t length; + char *packetData; + TimerT *t = (TimerT *)widget; - (void)data; + switch (_state) { + case S_START_SIGNUP: + packetData = packetContentPack(&length, "sssss", + textboxValueGet(_txtEmail), + textboxValueGet(_txtFirst), + textboxValueGet(_txtLast), + textboxValueGet(_txtPass1), + textboxValueGet(_txtUser) + ); - // ***TODO*** This should disable the entire dialog instead of just the buttons. Need to finish the Enable GUI code first. + // Send signup request. + encoded.packetType = PACKET_TYPE_SIGNUP; + encoded.control = PACKET_CONTROL_DAT; + encoded.channel = _channel; + encoded.encrypt = 1; + packetEncode(__packetThreadData, &encoded, packetData, length); + packetSend(__packetThreadData, &encoded); + DEL(packetData); + // Wait for response. + timerQuarterSecondsSet(_timProgress, 10 * 4); + break; - setButtons(0); - - // Validate it. ***TODO*** These messages could be a lot better. - if (!validateEmail(textboxValueGet(_txtEmail))) { - msgBoxOne("Invalid E-Mail", MSGBOX_ICON_ERROR, "Please enter a valid E-mail address.", "Okay", btnMsgBoxContinue); - return; + case S_SIGNUP_WAIT: + timerStop(t); + msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, "No response received.", "Okay", btnMsgBoxContinue); + break; } - if (!validateName(textboxValueGet(_txtFirst))) { - msgBoxOne("Invalid First Name", MSGBOX_ICON_ERROR, "Please enter a valid first name.", "Okay", btnMsgBoxContinue); - return; - } - if (!validateName(textboxValueGet(_txtLast))) { - msgBoxOne("Invalid Last Name", MSGBOX_ICON_ERROR, "Please enter a valid last name.", "Okay", btnMsgBoxContinue); - return; - } - if (strcmp(textboxValueGet(_txtPass1), textboxValueGet(_txtPass2)) != 0) { - msgBoxOne("Invalid Password", MSGBOX_ICON_ERROR, "Passwords must match.", "Okay", btnMsgBoxContinue); - return; - } - if (!validatePassword(textboxValueGet(_txtPass1))) { - msgBoxOne("Invalid Password", MSGBOX_ICON_ERROR, "Please enter a valid password.", "Okay", btnMsgBoxContinue); - return; - } - if (!validateUser(textboxValueGet(_txtUser))) { - msgBoxOne("Invalid User Name", MSGBOX_ICON_ERROR, "Please enter a valid user name.", "Okay", btnMsgBoxContinue); - return; - } - - packetData = packetContentPack(&length, "sssss", - textboxValueGet(_txtEmail), - textboxValueGet(_txtFirst), - textboxValueGet(_txtLast), - textboxValueGet(_txtPass1), - textboxValueGet(_txtUser) - ); - - // Send signup request. - encoded.packetType = PACKET_TYPE_SIGNUP; - encoded.control = PACKET_CONTROL_DAT; - encoded.channel = 0; - encoded.encrypt = 1; - packetEncode(__packetThreadData, &encoded, packetData, length); - packetSend(__packetThreadData, &encoded); - - DEL(packetData); - - // Wait for response. - do { - decoded = netGetPacket(0); - if (decoded) { - switch (decoded->packetType) { - case PACKET_TYPE_SIGNUP_RESULT: - packetContentUnpack(decoded->data, "is", &length, &packetData); - if (length) { - msgBoxOne("Success!", MSGBOX_ICON_INFORMATION, packetData, "Okay", btnMsgBoxFinish); - } else { - msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, packetData, "Okay", btnMsgBoxContinue); - } - DEL(packetData); - timeout = 0; - break; - - default: - msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, "Unexpected packet received.", "Okay", btnMsgBoxContinue); - timeout = 0; - break; - } - packetDecodeDataDestroy(&decoded); - } - taskYield(); - if (__timerQuarterSecondTick) timeout--; - } while (!guiHasStopped() && timeout > 0); } diff --git a/client/src/signup.h b/client/src/signup.h index aa29d5d..b7118f9 100644 --- a/client/src/signup.h +++ b/client/src/signup.h @@ -25,7 +25,7 @@ #include "os.h" -void taskSignUp(void *data); +void signupShow(void); #endif // SIGNUP_H diff --git a/client/src/system/comport.c b/client/src/system/comport.c index 6d76190..1482330 100644 --- a/client/src/system/comport.c +++ b/client/src/system/comport.c @@ -17,10 +17,8 @@ * */ - #include "comport.h" #include "timer.h" -#include "task.h" #include "config.h" @@ -43,44 +41,65 @@ int comReceiveBufferFlush(int com) { } -int comWaitWithTimeout(int com, char *buffer, int len, int quarterSeconds, char *expecting) { - int r; - int quarterTicks = 0; - int count = 0; - int bufferIndex = 0; - char data[2]; +/* +TASKBEGIN(comWaitWithTimeout) + int32_t r; + uint32_t quarterTicks; + int32_t count; + int32_t bufferIndex; + char incoming[2]; + ComWaitWithTimeoutT *args; - // Returns number of bytes read into buffer. - // Value is positive if "expect" was found. - // Value is negative if not found. + taskCode({ + // Returns number of bytes read into buffer. + // Value is positive if "expect" was found. + // Value is negative if not found. - while (quarterTicks <= quarterSeconds) { - r = comRead(com, data, 1); - if (r == 1) { - buffer[bufferIndex++] = data[0]; - buffer[bufferIndex] = 0; - if (data[0] == expecting[count]) { - count++; - if (count == (int)strlen(expecting)) { - // Found our expect. + logWrite("Entering comWaitWithTimeout\n"); + + self->args = (ComWaitWithTimeoutT *)data; + + while (self->quarterTicks <= self->args->quarterSeconds) { + + // logWrite("Calling comRead\n"); + self->r = comRead(self->args->com, self->incoming, 1); + // logWrite("Back from comRead with %d\n", self->r); + + if (self->r == 1) { + self->args->buffer[self->bufferIndex++] = self->incoming[0]; + self->args->buffer[self->bufferIndex] = 0; + if (self->incoming[0] == self->args->expecting[self->count]) { + self->count++; + if (self->count == (int)strlen(self->args->expecting)) { + // Found our expect. + break; + } + } else { + self->count = 0; + } + if (self->bufferIndex == self->args->len - 1) { + // Out of buffer. break; } - } else { - count = 0; } - if (bufferIndex == len - 1) { - // Out of buffer. - break; - } - } else { + + // logWrite("Yielding from comWaitWithTimeout\n"); taskYield(); - if (__timerQuarterSecondTick) quarterTicks++; + // logWrite("Back in comWaitWithTimeout\n"); + + if (__timerQuarterSecondTick) { + self->quarterTicks++; + logWrite("Tick\n"); + } } - } - if (count == (int)strlen(expecting)) { - return bufferIndex; - } + logWrite("Exiting comWaitWithTimeout\n"); - return -bufferIndex; -} + if (self->count == (int)strlen(self->args->expecting)) { + self->args->result = self->bufferIndex; + } + + self->args->result = -self->bufferIndex; + }); +TASKEND +*/ diff --git a/client/src/system/comport.h b/client/src/system/comport.h index d2b3d00..3441be6 100644 --- a/client/src/system/comport.h +++ b/client/src/system/comport.h @@ -26,6 +26,16 @@ #include "packet.h" +typedef struct ComWaitWithTimeoutS { + int32_t result; + uint8_t com; + char *buffer; + int32_t len; + uint32_t quarterSeconds; + char *expecting; +} ComWaitWithTimeoutT; + + #ifdef __linux__ #define SER_SUCCESS 0 @@ -35,7 +45,6 @@ #define SER_HANDSHAKING_RTSCTS 2 - int comClose(int com); int comOpen(int com, long bps, int dataBits, char parity, int stopBits, int handshaking); int comRead(int com, char *data, int len); @@ -55,7 +64,6 @@ int comWrite(int com, const char *data, int len); void comPacketSender(char *data, uint32_t length, void *userData); int comReceiveBufferFlush(int com); -int comWaitWithTimeout(int com, char *buffer, int len, int quarterSeconds, char *expecting); #endif // COMPORT_H diff --git a/client/src/system/os.c b/client/src/system/os.c index a653605..9496230 100644 --- a/client/src/system/os.c +++ b/client/src/system/os.c @@ -21,6 +21,16 @@ #include "os.h" +uint8_t osFileExists(char *filename) { + FILE *f = fopen(filename, "rb"); + if (f) { + fclose(f); + return 1; + } + return 0; +} + + void osShutdown(void) { #ifdef __linux__ linuxOsShutdown(); diff --git a/client/src/system/os.h b/client/src/system/os.h index 81cb419..c4908c5 100644 --- a/client/src/system/os.h +++ b/client/src/system/os.h @@ -27,6 +27,7 @@ // Common platform includes. #include +#include #include #include #include @@ -37,12 +38,17 @@ #include +#define SECONDS_IN_DAY 86400 +#define TICKS_PER_SECOND 18.2 +#define TICKS_PER_DAY (SECONDS_IN_DAY * TICKS_PER_SECOND) + + #ifdef __linux__ #define BITS64 // Linux DOS replacements. -long biostime(int cmd, long newtime); +uint32_t rawclock(void); // Linux support functions. void linuxOsShutdown(void); @@ -75,8 +81,8 @@ void linuxOsStartup(void); // Allocation helpers. -#define NEW(t,v) (v)=(t*)malloc(sizeof(t)) -#define DEL(v) {free(v); v=NULL;} +#define NEW(t,v) (v)=(t*)calloc(1, sizeof(t)) +#define DEL(v) { if (v) { free(v); v = NULL; } } #define SUCCESS 1 #define FAIL 0 @@ -93,8 +99,9 @@ typedef struct PacketThreadDataS PacketThreadDataT; extern PacketThreadDataT *__packetThreadData; // Declared in main.c -void osShutdown(void); -void osStartup(void); +uint8_t osFileExists(char *filename); +void osShutdown(void); +void osStartup(void); #endif // OS_H diff --git a/client/src/system/taglist.c b/client/src/system/taglist.c index f310628..3b026d3 100644 --- a/client/src/system/taglist.c +++ b/client/src/system/taglist.c @@ -32,6 +32,7 @@ #include "radio.h" #include "terminal.h" #include "textbox.h" +#include "timer.h" #include "updown.h" #include "window.h" @@ -166,6 +167,7 @@ static void tagListWidgetAttributeHandle(void) { break; case T_CLICK: + case T_EVENT: click = (widgetCallback)v; break; @@ -331,6 +333,13 @@ static void tagListWidgetAttributeHandle(void) { textboxPasswordCharacterSet((TextboxT *)widget, mask); break; + case T_TIMER: + if (hasValue && click) { + widget = W(timerNew(click, (uint32_t)valueInt)); + if (!enabled) timerStop((TimerT *)widget); + } + break; + case T_UPDOWN: widget = W(updownNew(pos.x, pos.y, minimum, maximum, step, title)); if (hasValue) updownValueSet((UpdownT *)widget, valueInt); diff --git a/client/src/system/taglist.h b/client/src/system/taglist.h index b216ff6..9f2db1f 100644 --- a/client/src/system/taglist.h +++ b/client/src/system/taglist.h @@ -54,6 +54,7 @@ enum TagItemsE { T_RADIOBUTTON, T_TERMINAL, T_TEXTBOX, + T_TIMER, T_UPDOWN, T_WINDOW, @@ -65,6 +66,7 @@ enum TagItemsE { T_COLOR_BACKGROUND, T_COLOR_FOREGROUND, T_ENABLED, + T_EVENT, T_FILENAME, T_GROUP, T_HEIGHT, diff --git a/client/src/system/task.c b/client/src/system/task.c deleted file mode 100644 index 5011d2f..0000000 --- a/client/src/system/task.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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 . - * - */ - - -#define MINICORO_IMPL -#define MCO_USE_ASM -//#define MCO_DEFAULT_STACK_SIZE 114688 // Default is 57344 -#include "thirdparty/minicoro/minicoro.h" - -#include "task.h" -#include "array.h" - - -typedef struct TaskS { - mco_coro *coroutine; - void (*function)(void *); - void *data; -} TaskT; - - -static TaskT **_taskList = NULL; - - -static void taskHandle(mco_coro* coroutine); - - -uint8_t taskCreate(void (*function)(void *), void *data) { - mco_desc desc = mco_desc_init(taskHandle, 0); - mco_result res = 0; - TaskT *task = (TaskT *)malloc(sizeof(TaskT)); - - if (task) { - task->function = function; - task->data = data; - desc.user_data = task; - res = mco_create(&task->coroutine, &desc); - if (res != MCO_SUCCESS) { - mco_destroy(task->coroutine); - free(task); - return 1; // Failed - } - arrput(_taskList, task); - } - - return 1; // Failed -} - - -static void taskHandle(mco_coro* coroutine) { - TaskT *task = (TaskT *)mco_get_user_data(coroutine); - task->function(task->data); -} - - -void taskProxy(WidgetT *widget) { - taskFunction task = (taskFunction)guiUserDataGet(widget); - taskCreate(task, NULL); -} - - -void taskRun(void) { - uint16_t taskIndex = 0; - - // Run until there are no more tasks. - while (arrlen(_taskList) > 0) { - - // Run each task in order. - taskIndex = 0; - while (taskIndex < arrlen(_taskList)) { - - // Run task. - mco_resume(_taskList[taskIndex]->coroutine); - // Did it finish? - if (mco_status(_taskList[taskIndex]->coroutine) == MCO_DEAD) { - // Task ended. Remove it. - mco_destroy(_taskList[taskIndex]->coroutine); - free(_taskList[taskIndex]); - arrdel(_taskList, taskIndex); - } else { - // Next task. - taskIndex++; - } - - } // while each task - - } // while tasks exist - - arrfree(_taskList); - _taskList = NULL; -} - - -void taskShutdown(void) { - // Nada -} - - -void taskStartup(void) { - // Nada -} - - -void __attribute__ ((noinline)) taskYield(void) { - mco_yield(mco_running()); -} diff --git a/client/src/system/timer.c b/client/src/system/timer.c deleted file mode 100644 index 8711dae..0000000 --- a/client/src/system/timer.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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 "timer.h" -#include "task.h" -#include "gui.h" - - -#define SECONDS_IN_DAY 86400 -#define TICKS_PER_SECOND 18.2 -#define TICKS_PER_DAY (SECONDS_IN_DAY * TICKS_PER_SECOND) - - -uint8_t __timerQuarterSecondTick = 0; -uint8_t __timerHalfSecondTick = 0; -uint8_t __timerSecondTick = 0; - -uint8_t __timerQuarterSecondOn = 0; -uint8_t __timerHalfSecondOn = 0; -uint8_t __timerSecondOn = 0; - - -static long _timerLast = 0; -static uint8_t _timerHalfSecond = 2; -static uint8_t _timerSecond = 2; - - -void timerShutdown(void) { - // Nothing yet. -} - - -void timerStartup(void) { - _timerLast = biostime(0, 0); -} - - -void timerUpdate(void) { - long now; - long delta; - - now = biostime(0, 0); - - // Reset ticks. - __timerQuarterSecondTick = 0; - __timerHalfSecondTick = 0; - __timerSecondTick = 0; - - // Ensure we haven't rolled past midnight between calls. - if (now >= _timerLast) { - delta = now - _timerLast; - } else { - // Compensate for midnight rollover. - delta = (now + TICKS_PER_DAY) - _timerLast; - } - - // Everything ticks off the quarter second. - if (delta > TICKS_PER_SECOND * 0.25) { - _timerLast = now; - - // Quarter Second timer. - __timerQuarterSecondOn = !__timerQuarterSecondOn; - __timerQuarterSecondTick = 1; - - // Half Second timer. - if (--_timerHalfSecond == 0) { - _timerHalfSecond = 2; - __timerHalfSecondOn = !__timerHalfSecondOn; - __timerHalfSecondTick = 1; - - // Second timer - if (--_timerSecond == 0) { - _timerSecond = 2; - __timerSecondOn = !__timerSecondOn; - __timerSecondTick = 1; - } // Second. - - } // Half Second. - - } // Quarter Second. -} - - -void timerQuarterSecondsWait(uint8_t quarterSeconds) { - uint8_t counter = 0; - - while (counter <= quarterSeconds && !guiHasStopped()) { - if (__timerQuarterSecondTick) { - counter++; - } - taskYield(); - } -} diff --git a/client/src/welcome.c b/client/src/welcome.c index 6aa0a98..c2faaf4 100644 --- a/client/src/welcome.c +++ b/client/src/welcome.c @@ -23,35 +23,62 @@ #include "login.h" #include "taglist.h" -#include "task.h" #include "config.h" #include "comport.h" -#include "timer.h" #include "network.h" +#include "runtime.h" #include "window.h" #include "picture.h" #include "button.h" #include "msgbox.h" +#include "timer.h" -static WindowT *_winWelcome = NULL; -static PictureT *_picLogo = NULL; -static PictureT *_picInit = NULL; -static PictureT *_picDialing = NULL; -static PictureT *_picConnect = NULL; -static ButtonT *_btnQuit = NULL; -static ButtonT *_btnSettings = NULL; -static ButtonT *_btnConnect = NULL; +typedef enum WelcomeStateE { + S_START_CONNECT = 0, + S_START_INIT_MODEM, + S_INIT_MODEM, + S_INIT_RESULT, + S_DIAL, + S_WAIT_FOR_BANNER, + S_WAIT_FOR_ENCRYPTION, + S_WAIT_FOR_DATA_TIMEOUT, + S_WAIT_FOR_ICON +} WelcomeStateT; -static void taskConnectClick(void *data); +static WindowT *_winWelcome = NULL; +static PictureT *_picLogo = NULL; +static PictureT *_picInit = NULL; +static PictureT *_picDialing = NULL; +static PictureT *_picConnect = NULL; +static ButtonT *_btnQuit = NULL; +static ButtonT *_btnSettings = NULL; +static ButtonT *_btnConnect = NULL; +static TimerT *_timProgress = NULL; +static WelcomeStateT _state = S_START_CONNECT; +static int16_t _timeoutCounter = 0; + + +static void btnConnectClick(WidgetT *widget); static void btnMsgBox(MsgBoxButtonT button); static void btnMsgBoxQuit(MsgBoxButtonT button); static void btnQuitClick(WidgetT *widget); static void btnSettingsClick(WidgetT *widget); +static void packetHandler(PacketDecodeDataT *packet); static void setButtons(uint8_t enabled); static void settingsFinished(WidgetT *widget); +static void timWelcomeProgress(WidgetT *widget); + + +static void btnConnectClick(WidgetT *widget) { + (void)widget; + + _state = S_START_CONNECT; + timerQuarterSecondsSet(_timProgress, 0); + timerStart(_timProgress); +} static void btnMsgBox(MsgBoxButtonT button) { @@ -79,6 +106,7 @@ static void btnMsgBoxQuit(MsgBoxButtonT button) { static void btnQuitClick(WidgetT *widget) { (void)widget; + setButtons(0); msgBoxTwo("Quit?", MSGBOX_ICON_QUESTION, "Exit to DOS?", "Okay", btnMsgBoxQuit, "Cancel", btnMsgBoxQuit); } @@ -86,8 +114,69 @@ static void btnQuitClick(WidgetT *widget) { static void btnSettingsClick(WidgetT *widget) { (void)widget; + setButtons(0); - taskCreate(taskSettings, settingsFinished); + settingsShow(settingsFinished); +} + + +static void packetHandler(PacketDecodeDataT *packet) { + PacketEncodeDataT encoded; + char *temp; + + // Reset timeout. + timerReset(_timProgress); + + switch (packet->packetType) { + case PACKET_TYPE_NUMBER: + // Store in number table. + shput(__runtimeData.integers, &packet->data[4], (int32_t)packet->data[0]); + break; + + case PACKET_TYPE_PROCEED: + // Connected! Show icon. + widgetVisibleSet(W(_picConnect), 1); + // Leave it up about 1.5 seconds. + timerQuarterSecondsSet(_timProgress, 6); + _state = S_WAIT_FOR_ICON; + break; + + case PACKET_TYPE_STRING: + // Do we have this string already? + temp = shget(__runtimeData.strings, packet->data); + if (temp) { + DEL(temp); + shdel(__runtimeData.strings, packet->data); + } + // Store in string table. + shput(__runtimeData.strings, packet->data, strdup(&packet->data[strlen(packet->data) + 1])); + break; + + case PACKET_TYPE_VERSION: + packetContentUnpack(packet->data, "i", &__runtimeData.protocolVersion); + // Do we need to update? + if (PACKET_PROTOCOL_VERSION == __runtimeData.protocolVersion) { + // Nope, we're good. + encoded.packetType = PACKET_TYPE_VERSION_OKAY; + } else { + // Version mismatch - upgrade time! + encoded.packetType = PACKET_TYPE_VERSION_BAD; + // ***TODO*** + } + encoded.control = PACKET_CONTROL_DAT; + encoded.channel = 0; + encoded.encrypt = 0; + packetEncode(__packetThreadData, &encoded, NULL, 0); + // Send GOOD or BAD. + packetSend(__packetThreadData, &encoded); + break; + + default: + logWrite("Unexpected packet received: %d\n", packet->packetType); + break; + } + + packetDecodeDataDestroy(&packet); } @@ -104,9 +193,7 @@ static void settingsFinished(WidgetT *widget) { } -void taskWelcome(void *data) { - - (void)data; +void welcomeShow(void) { // 450x128 logo @@ -150,10 +237,14 @@ void taskWelcome(void *data) { T_BUTTON, O(_btnConnect), T_TITLE, P("Connect"), T_X, 379, T_Y, 157, - T_CLICK, P(taskProxy), - T_USER_DATA, P(taskConnectClick), + T_CLICK, P(btnConnectClick), T_ENABLED, (__configData.serialCom > 0 && strlen(__configData.serverHost) > 2) ? T_TRUE : T_FALSE, T_BUTTON, T_DONE, + T_TIMER, O(_timProgress), + T_EVENT, P(timWelcomeProgress), + T_VALUE, 0, + T_ENABLED, 0, + T_TIMER, T_DONE, T_WINDOW, T_DONE, T_END @@ -163,59 +254,128 @@ void taskWelcome(void *data) { } -static void taskConnectClick(void *data) { - int32_t r = 0; - int16_t timeout = 0; +static void timWelcomeProgress(WidgetT *widget) { + TimerT *t = (TimerT *)widget; + int32_t r = 0; + uint32_t len = 0; + static char buffer[1024] = { 0 }; + static uint16_t offset = 0; - (void)data; + switch (_state) { + case S_START_CONNECT: + // Ghost all buttons. + widgetEnableSet(W(_btnConnect), 0); + widgetEnableSet(W(_btnSettings), 0); + widgetEnableSet(W(_btnQuit), 0); + // Hide welcome banner, show init. + widgetVisibleSet(W(_picLogo), 0); + widgetVisibleSet(W(_picInit), 1); + _state = S_START_INIT_MODEM; + break; - // Ghost all buttons. - widgetEnableSet(W(_btnConnect), 0); - widgetEnableSet(W(_btnSettings), 0); - widgetEnableSet(W(_btnQuit), 0); + case S_START_INIT_MODEM: + // Open COM port. + r = comOpen(__configData.serialCom - 1, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS); + if (r != SER_SUCCESS) { + timerStop(t); + msgBoxOne("COM Problem", MSGBOX_ICON_ERROR, "Unable to open COM port!\nPlease check settings.", "Okay", btnMsgBox); + break; + } + // Send a CR to clear anything in the modem. + snprintf(buffer, 1023, "%c", 13); + comWrite(__configData.serialCom - 1, buffer, strlen(buffer)); + timerQuarterSecondsSet(t, 4); + _state = S_INIT_MODEM; + break; - // Hide welcome banner, show init. - widgetVisibleSet(W(_picLogo), 0); - widgetVisibleSet(W(_picInit), 1); - taskYield(); + case S_INIT_MODEM: + // Just read anything to clear the buffer. + len = comRead(__configData.serialCom - 1, buffer, 1023); + // Send actual init + snprintf(buffer, 1023, "%s%c", "AT+SOCK1", 13); + comWrite(__configData.serialCom - 1, buffer, strlen(buffer)); + _state = S_INIT_RESULT; + break; - r = netConnectStep1(); - if (r == NETWORK_CONNECT_OPEN_FAILED) { - msgBoxOne("COM Problem", MSGBOX_ICON_ERROR, "Unable to open COM port!\nPlease check settings.", "Okay", btnMsgBox); - return; + case S_INIT_RESULT: + len = comRead(__configData.serialCom - 1, buffer, 1023); + buffer[len] = 0; + if (strstr(buffer, "OK") == NULL) { + comClose(__configData.serialCom - 1); + timerStop(t); + msgBoxOne("Modem Problem", MSGBOX_ICON_ERROR, "Modem does not support ENET!\nPlease check settings.", "Okay", btnMsgBox); + break; + } + // Show dialing, dial service. + widgetVisibleSet(W(_picDialing), 1); + timerQuarterSecondsSet(t, 0); + _state = S_DIAL; + break; + + case S_DIAL: + snprintf(buffer, 1023, "ATDT%s:%d%c", __configData.serverHost, __configData.serverPort, 13); + comWrite(__configData.serialCom - 1, buffer, strlen(buffer)); + timerQuarterSecondsSet(t, 0); // Run as fast as we can so we don't miss any data. + offset = 0; + _timeoutCounter = 7 * 4; // Seven seconds. + _state = S_WAIT_FOR_BANNER; + break; + + case S_WAIT_FOR_BANNER: + // Process incoming bytes one at a time so we don't accidentally eat the first packet after the banner. + len = comRead(__configData.serialCom - 1, &buffer[offset], 1); + offset += len; + buffer[offset] = 0; + // ***TODO*** Should probably cleanly handle a full server here with some kind of SERVER_FULL packet. + if (strstr(buffer, "KPMPGSMKII\rOKAY\r") != NULL) { + // Connect! Start packet handler and negotiate encryption. + netPacketHandlerStart(); + packetEncryptionSetup(__packetThreadData); + // Watch system channel for packets. + netChannelSystemGet(packetHandler); + timerQuarterSecondsSet(t, 1); + _timeoutCounter = 5 * 4; // Five seconds. + _state = S_WAIT_FOR_ENCRYPTION; + break; + } + // Did we time out? + if (guiTimerQuarterSecondTick()) _timeoutCounter--; + if (_timeoutCounter == 0) { + comClose(__configData.serialCom - 1); + timerStop(t); + msgBoxOne("No Connection", MSGBOX_ICON_INFORMATION, "Unable to connect to server!\nPlease check settings or try later.", "Okay", btnMsgBox); + } + break; + + case S_WAIT_FOR_ENCRYPTION: + if (packetEncryptionReady()) { + timerQuarterSecondsSet(t, 5 * 4); + _state = S_WAIT_FOR_DATA_TIMEOUT; + break; + } + // Did we time out? + _timeoutCounter--; + if (_timeoutCounter == 0) { + netPacketHandlerStop(); + comClose(__configData.serialCom - 1); + timerStop(t); + msgBoxOne("Negotiation Error", MSGBOX_ICON_INFORMATION, "Unable to negotiate encryption settings!", "Okay", btnMsgBox); + } + break; + + case S_WAIT_FOR_DATA_TIMEOUT: + netPacketHandlerStop(); + comClose(__configData.serialCom - 1); + timerStop(t); + msgBoxOne("Negotiation Error", MSGBOX_ICON_INFORMATION, "Unable to fetch client settings!", "Okay", btnMsgBox); + break; + + case S_WAIT_FOR_ICON: + // Switch to Login window. + timerStop(t); + netChannelSystemRelease(packetHandler); + guiDelete(D(_winWelcome)); + loginShow(); + break; } - if (r == NETWORK_CONNECT_BAD_MODEM) { - msgBoxOne("Modem Problem", MSGBOX_ICON_ERROR, "Modem does not support ENET!\nPlease check settings.", "Okay", btnMsgBox); - return; - } - - // Show dialing, dial service. - widgetVisibleSet(W(_picDialing), 1); - taskYield(); - - r = netConnectStep2(); - if (r == NETWORK_CONNECT_NO_CARRIER) { - msgBoxOne("No Connection", MSGBOX_ICON_INFORMATION, "Unable to connect to server!\nPlease check settings or try later.", "Okay", btnMsgBox); - return; - } - if (r == NETWORK_CONNECT_BAD_ENCRYPTION) { - msgBoxOne("Negotiation Error", MSGBOX_ICON_INFORMATION, "Unable to negotiate encryption settings!", "Okay", btnMsgBox); - return; - } - if (r == NETWORK_CONNECT_BAD_SETTINGS) { - msgBoxOne("Negotiation Error", MSGBOX_ICON_INFORMATION, "Unable to fetch client settings!", "Okay", btnMsgBox); - return; - } - - // Connected! Show icon. - widgetVisibleSet(W(_picConnect), 1); - timeout = 6; // Roughly 1.5 seconds. - while (timeout > 0 && !guiHasStopped()) { - taskYield(); - if (__timerQuarterSecondTick) timeout--; - } - - // Switch to Login window. - guiDelete(D(_winWelcome)); - taskCreate(taskLogin, NULL); } diff --git a/client/src/welcome.h b/client/src/welcome.h index bc64762..3abb33d 100644 --- a/client/src/welcome.h +++ b/client/src/welcome.h @@ -25,7 +25,7 @@ #include "os.h" -void taskWelcome(void *data); +void welcomeShow(void); #endif // WELCOME_H diff --git a/kanga.world/content/4_chat/redirect.txt b/kanga.world/content/4_chat/1_discord/redirect.txt similarity index 75% rename from kanga.world/content/4_chat/redirect.txt rename to kanga.world/content/4_chat/1_discord/redirect.txt index 051a81d..8bedea0 100644 --- a/kanga.world/content/4_chat/redirect.txt +++ b/kanga.world/content/4_chat/1_discord/redirect.txt @@ -1,4 +1,4 @@ -Title: Chat +Title: Discord ---- diff --git a/kanga.world/content/4_chat/2_matrix/redirect.txt b/kanga.world/content/4_chat/2_matrix/redirect.txt new file mode 100644 index 0000000..92a14e9 --- /dev/null +++ b/kanga.world/content/4_chat/2_matrix/redirect.txt @@ -0,0 +1,5 @@ +Title: Matrix + +---- + +Redirect: https://matrix.to/#/#DosGame:matrix.kangaroopunch.com \ No newline at end of file diff --git a/kanga.world/content/4_chat/go-home.txt b/kanga.world/content/4_chat/go-home.txt new file mode 100644 index 0000000..8afa866 --- /dev/null +++ b/kanga.world/content/4_chat/go-home.txt @@ -0,0 +1 @@ +Title: Chat \ No newline at end of file diff --git a/kanga.world/site/plugins/kangaworld-integration/api/user.php b/kanga.world/site/plugins/kangaworld-integration/api/user.php index 0b457cf..8046d7e 100644 --- a/kanga.world/site/plugins/kangaworld-integration/api/user.php +++ b/kanga.world/site/plugins/kangaworld-integration/api/user.php @@ -57,6 +57,7 @@ function kpApiUserLogin($username, $pass, &$response) { // Find user by name instead of email. $user = kirby()->users()->filterBy('name', $username)->first(); if ($user) { + // ***TODO*** Must be activated! // Attempt to sign them in. kirby()->auth()->login($user->email(), $pass); // They don't need an actual Kirby session, so log them off. diff --git a/kanga.world/site/plugins/kangaworld-integration/features/api.php b/kanga.world/site/plugins/kangaworld-integration/features/api.php index e52e24f..df2949a 100644 --- a/kanga.world/site/plugins/kangaworld-integration/features/api.php +++ b/kanga.world/site/plugins/kangaworld-integration/features/api.php @@ -23,6 +23,11 @@ return [ switch (get('command')) { + case 'API_AVAILABLE': + $response['result'] = 'true'; + $response['reason'] = 'API Available.'; + break; + case 'API_TEST': kpApiTest($response); break; diff --git a/kanga.world/site/snippets/menu.php b/kanga.world/site/snippets/menu.php index 526baa1..3578f44 100644 --- a/kanga.world/site/snippets/menu.php +++ b/kanga.world/site/snippets/menu.php @@ -12,8 +12,8 @@ diff --git a/kanga.world/site/templates/go-home.php b/kanga.world/site/templates/go-home.php new file mode 100644 index 0000000..4b82373 --- /dev/null +++ b/kanga.world/site/templates/go-home.php @@ -0,0 +1,5 @@ + + + + + diff --git a/server/src/client.c b/server/src/client.c index da5b584..66cf7c1 100644 --- a/server/src/client.c +++ b/server/src/client.c @@ -37,17 +37,10 @@ typedef void (*clientApi)(ClientThreadT *client, PacketDecodeDataT *data); // Also update enum in packets.h! -clientApi clientApiMethod[PACKET_END_OF_SERVER_PACKETS - PACKET_END_OF_DUAL_PACKETS] = { - clientApiClientShutdown, - clientApiLogin, - clientApiPong, - clientApiSignup, - clientApiVersionBad, - clientApiVersionOkay -}; +static clientApi _clientApiMethod[PACKET_TYPE_COUNT]; -static uint8_t clientDequeuePacket(ClientThreadT *client); +static uint8_t clientDequeuePacket(ClientThreadT *client); static uint8_t clientDequeuePacket(ClientThreadT *client) { @@ -68,8 +61,8 @@ static uint8_t clientDequeuePacket(ClientThreadT *client) { // New data or not, process anything in the queue. if (packetDecode(client->packetThreadData, &decode, data, length)) { - if ((decode.packetType > PACKET_END_OF_DUAL_PACKETS) && (decode.packetType < PACKET_END_OF_SERVER_PACKETS)) { - clientApiMethod[decode.packetType - (PACKET_END_OF_DUAL_PACKETS + 1)](client, &decode); + if (_clientApiMethod[decode.packetType]) { + _clientApiMethod[decode.packetType](client, &decode); } else { consoleMessageQueue("%ld: Channel %d Unknown Packet %d\n", client->threadIndex, decode.channel, decode.packetType); } @@ -103,6 +96,23 @@ void clientQueuePacket(ClientThreadT *client, uint8_t *data, uint32_t length) { } +void clientShutdown(void) { + // Nothing yet. +} + + +void clientStartup(void) { + memset(_clientApiMethod, 0, sizeof(_clientApiMethod)); + + _clientApiMethod[PACKET_TYPE_CLIENT_SHUTDOWN] = clientApiClientShutdown; + _clientApiMethod[PACKET_TYPE_LOGIN] = clientApiLogin; + _clientApiMethod[PACKET_TYPE_PONG] = clientApiPong; + _clientApiMethod[PACKET_TYPE_SIGNUP] = clientApiSignup; + _clientApiMethod[PACKET_TYPE_VERSION_BAD] = clientApiVersionBad; + _clientApiMethod[PACKET_TYPE_VERSION_OKAY] = clientApiVersionOkay; +} + + void *clientThread(void *data) { ENetPeer *peer = (ENetPeer *)data; @@ -134,6 +144,7 @@ void *clientThread(void *data) { packetSend(client->packetThreadData, &encoded); DEL(packetData); versionSent = 1; + consoleMessageQueue("PACKET_TYPE_VERSION sent.\n"); } } diff --git a/server/src/client.h b/server/src/client.h index dc34e4f..d0434c0 100644 --- a/server/src/client.h +++ b/server/src/client.h @@ -53,6 +53,8 @@ typedef struct ClientThreadS { void clientQueuePacket(ClientThreadT *client, uint8_t *data, uint32_t length); +void clientShutdown(void); +void clientStartup(void); void *clientThread(void *data); diff --git a/server/src/client/login.c b/server/src/client/login.c index 6dc221d..119683c 100644 --- a/server/src/client/login.c +++ b/server/src/client/login.c @@ -56,7 +56,7 @@ void clientApiLogin(ClientThreadT *client, PacketDecodeDataT *data) { // Build packet. encoded.control = PACKET_CONTROL_DAT; encoded.packetType = PACKET_TYPE_LOGIN_RESULT; - encoded.channel = 0; + encoded.channel = data->channel; encoded.encrypt = 0; packetEncode(client->packetThreadData, &encoded, packetData, length); // Send it. diff --git a/server/src/client/signup.c b/server/src/client/signup.c index 8d41f3e..957a080 100644 --- a/server/src/client/signup.c +++ b/server/src/client/signup.c @@ -59,7 +59,7 @@ void clientApiSignup(ClientThreadT *client, PacketDecodeDataT *data) { // Build packet. encoded.control = PACKET_CONTROL_DAT; encoded.packetType = PACKET_TYPE_SIGNUP_RESULT; - encoded.channel = 0; + encoded.channel = data->channel; encoded.encrypt = 0; packetEncode(client->packetThreadData, &encoded, packetData, length); // Send it. diff --git a/server/src/client/version.c b/server/src/client/version.c index 3558f32..a9d3c04 100644 --- a/server/src/client/version.c +++ b/server/src/client/version.c @@ -72,12 +72,13 @@ void clientApiVersionOkay(ClientThreadT *client, PacketDecodeDataT *data) { // Build packet. encoded.control = PACKET_CONTROL_DAT; encoded.packetType = PACKET_TYPE_STRING; - encoded.channel = 0; + encoded.channel = data->channel; encoded.encrypt = 0; packetEncode(client->packetThreadData, &encoded, buffer, length); // Send it. packetSend(client->packetThreadData, &encoded); DEL(buffer); + consoleMessageQueue("PACKET_TYPE_STRING [%s] sent.\n", strings[i].key); } restHelperConfigStringMapRelease(strings); @@ -111,22 +112,23 @@ void clientApiVersionOkay(ClientThreadT *client, PacketDecodeDataT *data) { // Build packet. encoded.control = PACKET_CONTROL_DAT; encoded.packetType = PACKET_TYPE_NUMBER; - encoded.channel = 0; + encoded.channel = data->channel; encoded.encrypt = 0; packetEncode(client->packetThreadData, &encoded, buffer, length); // Send it. packetSend(client->packetThreadData, &encoded); DEL(buffer); + consoleMessageQueue("PACKET_TYPE_NUMBER [%s] sent.\n", integers[i].key); } restHelperConfigIntegerMapRelease(integers); // Build PROCEED packet. encoded.control = PACKET_CONTROL_DAT; encoded.packetType = PACKET_TYPE_PROCEED; - encoded.channel = 0; + encoded.channel = data->channel; encoded.encrypt = 0; packetEncode(client->packetThreadData, &encoded, NULL, 0); // Send it. packetSend(client->packetThreadData, &encoded); - + consoleMessageQueue("PACKET_TYPE_PROCEED sent.\n"); } diff --git a/server/src/main.c b/server/src/main.c index aa6e579..aa92fec 100644 --- a/server/src/main.c +++ b/server/src/main.c @@ -109,6 +109,7 @@ int main(int argc, char *argv[]) { settingsClientVersion = restHelperConfigIntegerGet(response, "clientVersion", 1); restRelease(response); + clientStartup(); serverStartup(settingsPortNumber, settingsMaxClients); logWrite("Server online.\n"); @@ -121,6 +122,7 @@ int main(int argc, char *argv[]) { // Wait for all running threads to shut down. logWrite("Shutting down.\n"); serverShutdown(); + clientShutdown(); // Shut down. restShutdown(); diff --git a/server/src/rest.c b/server/src/rest.c index 0f6aac9..d2f5653 100644 --- a/server/src/rest.c +++ b/server/src/rest.c @@ -275,7 +275,8 @@ void restShutdown(void) { uint8_t restStartup(char *url, char *user, char *password) { - uint64_t i; + uint64_t i; + json_object *response; curl_global_init(CURL_GLOBAL_ALL); @@ -299,6 +300,11 @@ uint8_t restStartup(char *url, char *user, char *password) { _restUser = strdup(user); _restPass = strdup(password); + // Is the remote server there? + response = restRequest("API_AVAILABLE", NULL); + if (!response) return FAIL; + restRelease(response); + return SUCCESS; } diff --git a/server/src/server.c b/server/src/server.c index f9fca7a..8d6e582 100644 --- a/server/src/server.c +++ b/server/src/server.c @@ -134,8 +134,9 @@ void *serverThread(void *data) { // Tell the console. enet_address_get_host_ip(&event.peer->address, buffer, 2047); consoleMessageQueue("%ld: [%s] connected.\n", client->threadIndex, buffer); + // ***TODO*** Should probably cleanly handle a full server here with some kind of SERVER_FULL packet. // Send banner to client. - packet = enet_packet_create("KPMPGSMKII\r", 11, ENET_PACKET_FLAG_RELIABLE); + packet = enet_packet_create("KPMPGSMKII\rOKAY\r", 16, ENET_PACKET_FLAG_RELIABLE); enet_peer_send(event.peer, 0, packet); break; diff --git a/shared/log.c b/shared/log.c index b50ff30..2b32e56 100644 --- a/shared/log.c +++ b/shared/log.c @@ -78,11 +78,11 @@ void logWrite(char *format, ...) { vsnprintf(_logBuffer, _logBufferSize, format, args2); } -#ifdef __linux__ +//#ifdef __linux__ // Also output to stdout on Linux. fprintf(stdout, "%s", _logBuffer); fflush(stdout); -#endif +//#endif if (_logCallback) _logCallback(_logBuffer); diff --git a/shared/packet.c b/shared/packet.c index 36dbbc8..8c2e332 100644 --- a/shared/packet.c +++ b/shared/packet.c @@ -78,7 +78,6 @@ char *packetContentPack(uint16_t *length, char *format, ...) { static char work[PACKET_MAX] = { 0 }; char *result = NULL; char *buffer = NULL; - int8_t value8 = 0; int32_t value32 = 0; *length = 0; @@ -123,7 +122,6 @@ char *packetContentPack(uint16_t *length, char *format, ...) { void packetContentUnpack(char *buffer, char *format, ...) { va_list args = { 0 }; char **string = NULL; - int8_t *value8 = NULL; int32_t *value32 = NULL; uint16_t length = 0; diff --git a/shared/packets.h b/shared/packets.h index 9bf55db..f83d5bd 100644 --- a/shared/packets.h +++ b/shared/packets.h @@ -31,22 +31,19 @@ // This enum is treated as BYTES in the code. Do not go over 255 entries. // Also update array in client.c! typedef enum PacketTypeE { - // Packets that can received by both server and client: + // These packets have to be first and in this order! Do not change them! PACKET_TYPE_NONE = 0, // No packet. PACKET_TYPE_DH_REQUEST, PACKET_TYPE_DH_RESPONSE, - - PACKET_END_OF_DUAL_PACKETS, + PACKET_TYPE_VERSION, + PACKET_TYPE_VERSION_BAD, + PACKET_TYPE_VERSION_OKAY, // Packets received by only the server: PACKET_TYPE_CLIENT_SHUTDOWN, PACKET_TYPE_LOGIN, PACKET_TYPE_PONG, PACKET_TYPE_SIGNUP, - PACKET_TYPE_VERSION_BAD, - PACKET_TYPE_VERSION_OKAY, - - PACKET_END_OF_SERVER_PACKETS, // Packets received by only the client: PACKET_TYPE_LOGIN_RESULT, @@ -56,7 +53,6 @@ typedef enum PacketTypeE { PACKET_TYPE_SERVER_SHUTDOWN, PACKET_TYPE_SIGNUP_RESULT, PACKET_TYPE_STRING, - PACKET_TYPE_VERSION, // How many packet types do we recognize? PACKET_TYPE_COUNT diff --git a/test.sh b/test.sh index bf84970..1fb1ed5 100755 --- a/test.sh +++ b/test.sh @@ -1,2 +1,4 @@ #!/bin/bash -${HOME}/code/dosbox-custom/dosbox-staging/build/dosbox -noprimaryconf -nolocalconf -conf test.conf & +#${HOME}/code/dosbox-custom/dosbox-staging/build/dosbox -noprimaryconf -nolocalconf -conf test.conf & +#dosbox -noprimaryconf -nolocalconf -conf test.conf & +${HOME}/bin/dosbox-staging-linux-v0.78.1/dosbox -noprimaryconf -nolocalconf -conf test.conf &