diff --git a/client/client.pro b/client/client.pro
index 2b5def8..af2791f 100644
--- a/client/client.pro
+++ b/client/client.pro
@@ -63,6 +63,7 @@ HEADERS = \
$$SHARED/thirdparty/blowfish-api/blowfish.h \
$$SHARED/thirdparty/ini/src/ini.h \
$$SHARED/primes.h \
+ $$SHARED/packet.h \
src/config.h \
$$SHARED/util.h \
src/gui/msgbox.h \
@@ -105,6 +106,7 @@ SOURCES = \
$$SHARED/thirdparty/memwatch/memwatch.c \
$$SHARED/thirdparty/blowfish-api/blowfish.c \
$$SHARED/thirdparty/ini/src/ini.c \
+ $$SHARED/packet.c \
src/config.c \
$$SHARED/memory.c \
src/gui/msgbox.c \
diff --git a/shared/packet.c b/shared/packet.c
new file mode 100644
index 0000000..bbce34a
--- /dev/null
+++ b/shared/packet.c
@@ -0,0 +1,195 @@
+/*
+ * 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 "packet.h"
+
+
+static uint8_t packetCRC(char *data, uint16_t length, uint8_t startAt);
+
+
+static uint8_t packetCRC(char *data, uint16_t length, uint8_t startAt) {
+ uint16_t x = 0;
+
+ for (x=0; x 0) {
+ for (x=0; xdecodeQueue[threadData->decodeQueueHead++] = input[x];
+ if (threadData->decodeQueueHead >= PACKET_INPUT_QUEUE_SIZE) {
+ threadData->decodeQueueHead = 0;
+ }
+ }
+ }
+
+ while (1) {
+ // Do we have data to process?
+ if (threadData->decodeQueueHead == threadData->decodeQueueTail) return 0;
+
+ // Get next byte.
+ c = threadData->decodeQueue[threadData->decodeQueueTail++];
+ if (threadData->decodeQueueTail >= PACKET_INPUT_QUEUE_SIZE) {
+ threadData->decodeQueueTail = 0;
+ }
+
+ // New packet?
+ if (threadData->newPacket) {
+ threadData->newPacket = 0;
+ threadData->inEscape = 0;
+ data->length = 0;
+ }
+
+ // Are we escaped?
+ if (threadData->inEscape) {
+ threadData->inEscape = 0;
+ // Is this the end of the packet?
+ if (c != PACKET_FRAME) {
+ threadData->newPacket = 1;
+
+ // Check CRC.
+ if (threadData->decodeBuffer[data->length - 1] != packetCRC(threadData->decodeBuffer, data->length - 1, 0)) continue;
+
+ // Fill decoded data fields.
+ data->packetType = threadData->decodeBuffer[1];
+ data->channel = threadData->decodeBuffer[2];
+
+ // ***TODO*** Blowfish Decryption.
+ if (threadData->decodeBuffer[0] & 32) {
+
+ }
+
+ // Copy packet data to new buffer.
+ data->data = (char *)malloc(data->length - 4); // 4 for 3 byte header and 1 byte CRC.
+ if (!data) continue;
+ memcpy(data->data, &threadData->decodeBuffer[3], data->length - 4); // Skip header and CRC.
+ break;
+ }
+ } else {
+ // Are we escaping?
+ if (c == PACKET_FRAME) {
+ // Yes. Don't add this byte.
+ threadData->inEscape = 1;
+ continue;
+ }
+ // Did we overflow?
+ if (data->length >= PACKET_MAX) {
+ // Yup. Dump it.
+ threadData->newPacket = 1;
+ continue;
+ }
+ // Add byte to packet.
+ threadData->decodeBuffer[data->length++] = c;
+ }
+ }
+
+ return 1;
+}
+
+
+uint8_t packetEncode(PacketThreadDataT *threadData, PacketEncodeDataT *data, char *input, uint16_t length) {
+ uint8_t crc = 0;
+ uint8_t control = 0;
+
+ // Returns 1 on success, 0 on failure.
+
+ // Packet too large?
+ if (length > PACKET_MAX) return 0;
+
+ data->dataPointer = NULL;
+ data->length = 0;
+ data->sequence = threadData->sequence++;
+
+ // Make needed header bytes.
+ control = (data->control << 6) + (data->encrypt << 5) + (data->sequence & 0x1f);
+
+ // Calculate CRC over header bytes and payload.
+ crc = packetCRC((char *)&control, 1, crc);
+ crc = packetCRC((char *)&data->packetType, 1, crc);
+ crc = packetCRC((char *)&data->channel, 1, crc);
+ crc = packetCRC(input, length, crc);
+
+ // Add header bytes.
+ threadData->encodeBuffer[data->length++] = control;
+ if (control == PACKET_FRAME) threadData->encodeBuffer[data->length++] = PACKET_FRAME;
+ threadData->encodeBuffer[data->length++] = data->packetType;
+ if (data->packetType == PACKET_FRAME) threadData->encodeBuffer[data->length++] = PACKET_FRAME;
+ threadData->encodeBuffer[data->length++] = data->channel;
+ if (data->channel == PACKET_FRAME) threadData->encodeBuffer[data->length++] = PACKET_FRAME;
+
+ // ***TODO*** Blowfish Encryption.
+ if (data->encrypt) {
+
+ }
+
+ // Add payload.
+ while (length--) {
+ // Is this a frame character? If so, escape it.
+ if (*input == PACKET_FRAME) threadData->encodeBuffer[data->length++] = PACKET_FRAME;
+ // Add data.
+ threadData->encodeBuffer[data->length++] = *input++;
+ }
+
+ // Add CRC.
+ threadData->encodeBuffer[data->length++] = crc;
+ if (crc == PACKET_FRAME) threadData->encodeBuffer[data->length++] = PACKET_FRAME;
+
+ // Mark end of packet.
+ threadData->encodeBuffer[data->length++] = PACKET_FRAME;
+ threadData->encodeBuffer[data->length++] = 0;
+
+ return 1;
+}
+
+
+PacketThreadDataT *packetThreadDataCreate(void) {
+ PacketThreadDataT *data = NULL;
+
+ data = (PacketThreadDataT *)malloc(sizeof(PacketThreadDataT));
+ if (data) {
+ data->sequence = 0;
+ data->decodeQueueHead = 0;
+ data->decodeQueueTail = 0;
+ data->newPacket = 1;
+ }
+
+ return data;
+}
+
+
+void packetThreadDataDestroy(PacketThreadDataT **data) {
+ PacketThreadDataT *d = *data;
+
+ free(d);
+ d = NULL;
+ *data = d;
+}
diff --git a/shared/packet.h b/shared/packet.h
new file mode 100644
index 0000000..8d69a72
--- /dev/null
+++ b/shared/packet.h
@@ -0,0 +1,97 @@
+/*
+ * 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 PACKET_H
+#define PACKET_H
+
+
+#include "os.h"
+
+
+/*
+ * Packet format (without framing):
+ *
+ * Byte 01: cces ssss - Two control bits, one encryption bit, five sequence bits.
+ * Byte 02: pppp pppp - Eight bits, packet type.
+ * Byte 03: cccc cccc - Eight bits, channel.
+ * Byte 04: dddd dddd \
+ * Byte ..: dddd dddd > Packet payload.
+ * Byte X0: dddd dddd /
+ * Byte X1: cccc cccc - Checksum
+ *
+ */
+
+
+#define PACKET_MAX 1024 // Maximum number of bytes per packet
+#define PACKET_SEQUENCE_MAX 32 // Five bits of data
+
+#define PACKET_INPUT_QUEUE_SIZE 4096
+#define PACKET_BUFFER_SIZE (PACKET_MAX * 2 + 2 + 8) // Worst case, every byte is a PACKET_FRAME. Add two for ending frame. Add 8 for worst case header and CRC.
+
+#define PACKET_FRAME 0x7e
+
+#define PACKET_CONTROL_DAT 0 // Regular data packet.
+#define PACKET_CONTROL_RTX 1 // Retransmission packet.
+#define PACKET_CONTROL_NAK 2 // Negative acknowledge.
+
+#define PACKET_TYPE_NONE 0 // No packet.
+
+
+typedef struct PacketDecodeDataS {
+ // Output
+ uint8_t packetType; // One of PACKET_TYPE_*
+ char *data; // Buffer received. MUST FREE.
+ uint16_t length; // Length of buffer.
+ uint8_t channel; // Which data channel it arrived on.
+} PacketDecodeDataT;
+
+typedef struct PacketEncodeDataS {
+ // Input
+ uint8_t control; // One of PACKET_CONTROL_*
+ uint8_t packetType; // One of PACKET_TYPE_*
+ uint8_t channel; // Which data channel to use.
+ uint8_t encrypt; // Do we want the packet encrypted?
+ // Input & Output
+ uint16_t sequence; // For NAK packets, input the sequence number we missed. Other packets returns the sequence of this packet.
+ // Output
+ char *dataPointer; // Buffer ready to send. DO NOT FREE.
+ uint16_t length; // Length of buffer.
+} PacketEncodeDataT;
+
+typedef struct PacketThreadDataS {
+ // Internal state per thread for packet processing.
+ uint8_t sequence;
+ char decodeBuffer[PACKET_MAX];
+ char encodeBuffer[PACKET_BUFFER_SIZE];
+ char decodeQueue[PACKET_INPUT_QUEUE_SIZE];
+ uint16_t decodeQueueHead;
+ uint16_t decodeQueueTail;
+ uint8_t inEscape;
+ uint8_t newPacket;
+} PacketThreadDataT;
+
+
+uint8_t packetDecode(PacketThreadDataT *threadData, PacketDecodeDataT *data, char *input, uint16_t length);
+uint8_t packetEncode(PacketThreadDataT *threadData, PacketEncodeDataT *data, char *input, uint16_t length);
+PacketThreadDataT *packetThreadDataCreate(void);
+void packetThreadDataDestroy(PacketThreadDataT **data);
+
+
+#endif // PACKET_H