From 5f1a3d809239171d5620f2f208e6e635100b22eb Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Tue, 21 Aug 2018 13:09:58 -0400
Subject: Clean up Makefile

Having .o files depend on .h files works correctly everywhere, it seems.
---
 .gitignore |  1 +
 Makefile   | 29 ++++++++++++++---------------
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/.gitignore b/.gitignore
index 2f400d8..7f76e0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+*.o
 chroot.tar
 client
 help
diff --git a/Makefile b/Makefile
index 8df5425..4091276 100644
--- a/Makefile
+++ b/Makefile
@@ -1,15 +1,17 @@
-USER = torus
-BINS = server client help meta merge
+CHROOT_USER = torus
+CHROOT_GROUP = $(CHROOT_USER)
+
 CFLAGS += -Wall -Wextra -Wpedantic
 LDLIBS = -lm -lcurses
+BINS = server client help meta merge
+OBJS = $(BINS:%=%.o)
 
 all: tags $(BINS)
 
-$(BINS): torus.h
+$(OBJS): torus.h
 
-# Only necessary so GNU make doesn't try to use torus.h as a source.
-.c:
-	$(CC) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@
+tags: *.h *.c
+	ctags -w *.h *.c
 
 chroot.tar: server client help
 	mkdir -p root
@@ -21,22 +23,19 @@ chroot.tar: server client help
 	    root/usr \
 	    root/usr/share \
 	    root/usr/share/misc
-	install -d -o $(USER) -g $(USER) root/home/$(USER)
-	install -o root -g wheel -m 555 /libexec/ld-elf.so.1 root/libexec
-	install -o root -g wheel -m 444 \
+	install -d -o $(CHROOT_USER) -g $(CHROOT_GROUP) root/home/$(CHROOT_USER)
+	cp -p -f /libexec/ld-elf.so.1 root/libexec
+	cp -p -f \
 	    /lib/libc.so.7 \
 		/lib/libm.so.5 \
 	    /lib/libedit.so.7 \
 	    /lib/libncurses.so.8 \
 	    /lib/libncursesw.so.8 \
 	    root/lib
-	install -o root -g wheel -m 444 /usr/share/misc/termcap.db root/usr/share/misc
-	install -o root -g wheel -m 555 /bin/sh root/bin
+	cp -p -f /usr/share/misc/termcap.db root/usr/share/misc
+	cp -p -f /bin/sh root/bin
 	install -o root -g wheel -m 555 server client help root/bin
 	tar -c -f chroot.tar -C root bin home lib libexec usr
 
-tags: *.h *.c
-	ctags -w *.h *.c
-
 clean:
-	rm -f tags $(BINS) chroot.tar
+	rm -f tags $(OBJS) $(BINS) chroot.tar
-- 
cgit 1.4.1


From 642e1fe5360044ebe4207b1eb2817472fc6ddb36 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Tue, 21 Aug 2018 13:29:51 -0400
Subject: Remove spawns

This reverts ce13621292bdfeafd7c6561c432a1d96deccbd3a and commits after
it.
---
 client.c |  15 ---------
 server.c | 113 +++++++++++++++++++++++++++++----------------------------------
 torus.h  |  17 ++--------
 3 files changed, 54 insertions(+), 91 deletions(-)

diff --git a/client.c b/client.c
index 7af6950..1e3421b 100644
--- a/client.c
+++ b/client.c
@@ -58,14 +58,6 @@ static void clientPut(uint8_t color, char cell) {
 	clientMessage(msg);
 }
 
-static void clientSpawn(uint8_t spawn) {
-	struct ClientMessage msg = {
-		.type = CLIENT_SPAWN,
-		.spawn = spawn,
-	};
-	clientMessage(msg);
-}
-
 static void clientMap(void) {
 	struct ClientMessage msg = { .type = CLIENT_MAP };
 	clientMessage(msg);
@@ -162,13 +154,6 @@ static void inputNormal(int c) {
 		break; case ESC: input.mode = MODE_NORMAL;
 
 		break; case 'q': endwin(); exit(EX_OK);
-		break; case 'Q': {
-			if ((input.color & 0x07) < ARRAY_LEN(SPAWNS)) {
-				clientSpawn(input.color & 0x07);
-			} else {
-				clientSpawn(0);
-			}
-		}
 		break; case 'm': clientMap();
 
 		break; case 'i': insertMode(1, 0);
diff --git a/server.c b/server.c
index 9cae5dd..f59e9c4 100644
--- a/server.c
+++ b/server.c
@@ -97,8 +97,8 @@ static struct Client *clientAdd(int fd) {
 	if (!client) err(EX_OSERR, "malloc");
 
 	client->fd = fd;
-	client->tileX = TILE_VOID_X;
-	client->tileY = TILE_VOID_Y;
+	client->tileX = TILE_INIT_X;
+	client->tileY = TILE_INIT_Y;
 	client->cellX = CELL_INIT_X;
 	client->cellY = CELL_INIT_Y;
 
@@ -172,61 +172,6 @@ static bool clientCursors(const struct Client *client) {
 	return true;
 }
 
-static bool clientUpdate(const struct Client *client, const struct Client *old) {
-	struct ServerMessage msg = {
-		.type = SERVER_MOVE,
-		.move = { .cellX = client->cellX, .cellY = client->cellY },
-	};
-	if (!clientSend(client, msg)) return false;
-
-	if (client->tileX != old->tileX || client->tileY != old->tileY) {
-		msg.type = SERVER_TILE;
-		if (!clientSend(client, msg)) return false;
-
-		if (!clientCursors(client)) return false;
-
-		msg = (struct ServerMessage) {
-			.type = SERVER_CURSOR,
-			.cursor = {
-				.oldCellX = old->cellX,  .oldCellY = old->cellY,
-				.newCellX = CURSOR_NONE, .newCellY = CURSOR_NONE,
-			},
-		};
-		clientCast(old, msg);
-
-		msg = (struct ServerMessage) {
-			.type = SERVER_CURSOR,
-			.cursor = {
-				.oldCellX = CURSOR_NONE,   .oldCellY = CURSOR_NONE,
-				.newCellX = client->cellX, .newCellY = client->cellY,
-			},
-		};
-		clientCast(client, msg);
-
-	} else {
-		msg = (struct ServerMessage) {
-			.type = SERVER_CURSOR,
-			.cursor = {
-				.oldCellX = old->cellX,    .oldCellY = old->cellY,
-				.newCellX = client->cellX, .newCellY = client->cellY,
-			},
-		};
-		clientCast(client, msg);
-	}
-
-	return true;
-}
-
-static bool clientSpawn(struct Client *client, uint8_t spawn) {
-	if (spawn >= ARRAY_LEN(SPAWNS)) return false;
-	struct Client old = *client;
-	client->tileX = SPAWNS[spawn].tileX;
-	client->tileY = SPAWNS[spawn].tileY;
-	client->cellX = CELL_INIT_X;
-	client->cellY = CELL_INIT_Y;
-	return clientUpdate(client, &old);
-}
-
 static bool clientMove(struct Client *client, int8_t dx, int8_t dy) {
 	struct Client old = *client;
 
@@ -265,7 +210,48 @@ static bool clientMove(struct Client *client, int8_t dx, int8_t dy) {
 	assert(client->tileX < TILE_COLS);
 	assert(client->tileY < TILE_ROWS);
 
-	return clientUpdate(client, &old);
+	struct ServerMessage msg = {
+		.type = SERVER_MOVE,
+		.move = { .cellX = client->cellX, .cellY = client->cellY },
+	};
+	if (!clientSend(client, msg)) return false;
+
+	if (client->tileX != old.tileX || client->tileY != old.tileY) {
+		msg.type = SERVER_TILE;
+		if (!clientSend(client, msg)) return false;
+
+		if (!clientCursors(client)) return false;
+
+		msg = (struct ServerMessage) {
+			.type = SERVER_CURSOR,
+			.cursor = {
+				.oldCellX = old.cellX,  .oldCellY = old.cellY,
+				.newCellX = CURSOR_NONE, .newCellY = CURSOR_NONE,
+			},
+		};
+		clientCast(&old, msg);
+
+		msg = (struct ServerMessage) {
+			.type = SERVER_CURSOR,
+			.cursor = {
+				.oldCellX = CURSOR_NONE,   .oldCellY = CURSOR_NONE,
+				.newCellX = client->cellX, .newCellY = client->cellY,
+			},
+		};
+		clientCast(client, msg);
+
+	} else {
+		msg = (struct ServerMessage) {
+			.type = SERVER_CURSOR,
+			.cursor = {
+				.oldCellX = old.cellX,    .oldCellY = old.cellY,
+				.newCellX = client->cellX, .newCellY = client->cellY,
+			},
+		};
+		clientCast(client, msg);
+	}
+
+	return true;
 }
 
 static bool clientPut(const struct Client *client, uint8_t color, char cell) {
@@ -366,7 +352,11 @@ int main() {
 			nevents = kevent(kq, &event, 1, NULL, 0, NULL);
 			if (nevents < 0) err(EX_IOERR, "kevent");
 
-			if (!clientSpawn(client, 0)) clientRemove(client);
+			struct ServerMessage msg = { .type = SERVER_TILE };
+			bool success = clientSend(client, msg)
+				&& clientMove(client, 0, 0)
+				&& clientCursors(client);
+			if (!success) clientRemove(client);
 
 			continue;
 		}
@@ -392,8 +382,9 @@ int main() {
 			break; case CLIENT_PUT: {
 				success = clientPut(client, msg.put.color, msg.put.cell);
 			}
-			break; case CLIENT_SPAWN: success = clientSpawn(client, msg.spawn);
-			break; case CLIENT_MAP: success = clientMap(client);
+			break; case CLIENT_MAP: {
+				success = clientMap(client);
+			}
 		}
 		if (!success) clientRemove(client);
 	}
diff --git a/torus.h b/torus.h
index b5a0235..93622ad 100644
--- a/torus.h
+++ b/torus.h
@@ -75,19 +75,8 @@ enum {
 };
 static const size_t TILES_SIZE = sizeof(struct Tile[TILE_ROWS][TILE_COLS]);
 
-static const uint32_t TILE_VOID_X = UINT32_MAX;
-static const uint32_t TILE_VOID_Y = UINT32_MAX;
-
-static const struct {
-	uint32_t tileX;
-	uint32_t tileY;
-} SPAWNS[] = {
-	{ 0, 0 },
-	{ TILE_COLS * 3 / 4, TILE_ROWS * 3 / 4 }, // NW
-	{ TILE_COLS * 1 / 4, TILE_ROWS * 3 / 4 }, // NE
-	{ TILE_COLS * 1 / 4, TILE_ROWS * 1 / 4 }, // SE
-	{ TILE_COLS * 3 / 4, TILE_ROWS * 1 / 4 }, // SW
-};
+static const uint32_t TILE_INIT_X = TILE_COLS / 2;
+static const uint32_t TILE_INIT_Y = TILE_ROWS / 2;
 
 enum {
 	MAP_ROWS = 11,
@@ -138,7 +127,6 @@ struct ClientMessage {
 	enum PACKED {
 		CLIENT_MOVE,
 		CLIENT_PUT,
-		CLIENT_SPAWN,
 		CLIENT_MAP,
 	} type;
 	union {
@@ -150,6 +138,5 @@ struct ClientMessage {
 			uint8_t color;
 			char cell;
 		} put;
-		uint8_t spawn;
 	};
 };
-- 
cgit 1.4.1


From d8c973966760095026218ab09be8d30eb9983e3b Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Tue, 21 Aug 2018 14:00:15 -0400
Subject: Shrink the torus and rearrange struct Tile

---
 client.c | 18 +++++++++---------
 merge.c  |  4 ++--
 meta.c   | 10 +++++-----
 server.c | 22 +++++++---------------
 torus.h  | 28 ++++++++++++----------------
 5 files changed, 35 insertions(+), 47 deletions(-)

diff --git a/client.c b/client.c
index 1e3421b..cb0107e 100644
--- a/client.c
+++ b/client.c
@@ -346,23 +346,23 @@ static void serverMap(void) {
 	time_t timeMin = timeNow;
 	for (int y = 0; y < MAP_ROWS; ++y) {
 		for (int x = 0; x < MAP_COLS; ++x) {
-			struct MapTile tile = map.tiles[y][x];
-			if (countMax < tile.modifyCount) countMax = tile.modifyCount;
-			if (tile.modifyTime && timeMin > tile.modifyTime) {
-				timeMin = tile.modifyTime;
+			struct Meta meta = map.meta[y][x];
+			if (countMax < meta.modifyCount) countMax = meta.modifyCount;
+			if (meta.modifyTime && timeMin > meta.modifyTime) {
+				timeMin = meta.modifyTime;
 			}
 		}
 	}
 
 	for (int y = 0; y < MAP_ROWS; ++y) {
 		for (int x = 0; x < MAP_COLS; ++x) {
-			struct MapTile tile = map.tiles[y][x];
+			struct Meta meta = map.meta[y][x];
 
-			double count = (tile.modifyCount && countMax > 1)
-				? log(tile.modifyCount) / log(countMax)
+			double count = (meta.modifyCount && countMax > 1)
+				? log(meta.modifyCount) / log(countMax)
 				: 0.0;
-			double time = (tile.modifyTime && timeNow - timeMin)
-				? (double)(tile.modifyTime - timeMin) / (double)(timeNow - timeMin)
+			double time = (meta.modifyTime && timeNow - timeMin)
+				? (double)(meta.modifyTime - timeMin) / (double)(timeNow - timeMin)
 				: 0.0;
 			count *= ARRAY_LEN(MAP_CELLS) - 2;
 			time *= ARRAY_LEN(MAP_COLORS) - 1;
diff --git a/merge.c b/merge.c
index 70c5d4e..e6d2fde 100644
--- a/merge.c
+++ b/merge.c
@@ -93,11 +93,11 @@ int main(int argc, char *argv[]) {
 		if (!countA && !countB) break;
 		if (!countA || !countB) errx(EX_DATAERR, "different size inputs");
 
-		const struct Tile *tileC = (tileA.accessTime > tileB.accessTime)
+		const struct Tile *tileC = (tileA.meta.accessTime > tileB.meta.accessTime)
 			? &tileA
 			: &tileB;
 
-		if (tileA.modifyTime != tileB.modifyTime) {
+		if (tileA.meta.modifyTime != tileB.meta.modifyTime) {
 			drawTile(0, &tileA);
 			drawTile(CELL_ROWS + 1, &tileB);
 			move(CELL_ROWS * 2 + 2, 0);
diff --git a/meta.c b/meta.c
index c913cf7..990928a 100644
--- a/meta.c
+++ b/meta.c
@@ -32,11 +32,11 @@ int main() {
 			"%d,%d,%jd,%u,%jd,%u,%jd\n",
 			i % TILE_COLS,
 			i / TILE_COLS,
-			tile.createTime,
-			tile.modifyCount,
-			tile.modifyTime,
-			tile.accessCount,
-			tile.accessTime
+			tile.meta.createTime,
+			tile.meta.modifyCount,
+			tile.meta.modifyTime,
+			tile.meta.accessCount,
+			tile.meta.accessTime
 		);
 	}
 }
diff --git a/server.c b/server.c
index f59e9c4..ff13af3 100644
--- a/server.c
+++ b/server.c
@@ -58,25 +58,25 @@ static void tilesMap(void) {
 
 static struct Tile *tileGet(uint32_t tileX, uint32_t tileY) {
 	struct Tile *tile = &tiles[tileY * TILE_ROWS + tileX];
-	if (!tile->createTime) {
+	if (!tile->meta.createTime) {
 		memset(tile->cells, ' ', CELLS_SIZE);
 		memset(tile->colors, COLOR_WHITE, CELLS_SIZE);
-		tile->createTime = time(NULL);
+		tile->meta.createTime = time(NULL);
 	}
 	return tile;
 }
 
 static struct Tile *tileAccess(uint32_t tileX, uint32_t tileY) {
 	struct Tile *tile = tileGet(tileX, tileY);
-	tile->accessTime = time(NULL);
-	tile->accessCount++;
+	tile->meta.accessTime = time(NULL);
+	tile->meta.accessCount++;
 	return tile;
 }
 
 static struct Tile *tileModify(uint32_t tileX, uint32_t tileY) {
 	struct Tile *tile = tileGet(tileX, tileY);
-	tile->modifyTime = time(NULL);
-	tile->modifyCount++;
+	tile->meta.modifyTime = time(NULL);
+	tile->meta.modifyCount++;
 	return tile;
 }
 
@@ -282,15 +282,7 @@ static bool clientMap(const struct Client *client) {
 		for (int32_t x = 0; x < MAP_COLS; ++x) {
 			uint32_t tileY = ((mapY + y) % TILE_ROWS + TILE_ROWS) % TILE_ROWS;
 			uint32_t tileX = ((mapX + x) % TILE_COLS + TILE_COLS) % TILE_COLS;
-
-			const struct Tile *tile = &tiles[tileY * TILE_ROWS + tileX];
-			map.tiles[y][x] = (struct MapTile) {
-				.createTime = tile->createTime,
-				.modifyTime = tile->modifyTime,
-				.accessTime = tile->accessTime,
-				.modifyCount = tile->modifyCount,
-				.accessCount = tile->accessCount,
-			};
+			map.meta[y][x] = tiles[tileY * TILE_ROWS + tileX].meta;
 		}
 	}
 
diff --git a/torus.h b/torus.h
index 93622ad..4109b89 100644
--- a/torus.h
+++ b/torus.h
@@ -48,7 +48,7 @@ enum {
 };
 
 enum {
-	CELL_ROWS = 25,
+	CELL_ROWS = 24,
 	CELL_COLS = 80,
 };
 static const size_t CELLS_SIZE = sizeof(char[CELL_ROWS][CELL_COLS]);
@@ -56,22 +56,24 @@ static const size_t CELLS_SIZE = sizeof(char[CELL_ROWS][CELL_COLS]);
 static const uint8_t CELL_INIT_X = CELL_COLS / 2;
 static const uint8_t CELL_INIT_Y = CELL_ROWS / 2;
 
-struct ALIGNED(4096) Tile {
+struct Meta {
 	time_t createTime;
 	time_t modifyTime;
-	char ALIGNED(16) cells[CELL_ROWS][CELL_COLS];
-	uint8_t ALIGNED(16) colors[CELL_ROWS][CELL_COLS];
+	time_t accessTime;
 	uint32_t modifyCount;
 	uint32_t accessCount;
-	time_t accessTime;
+};
+
+struct ALIGNED(4096) Tile {
+	char cells[CELL_ROWS][CELL_COLS];
+	uint8_t colors[CELL_ROWS][CELL_COLS];
+	struct Meta meta;
 };
 static_assert(4096 == sizeof(struct Tile), "struct Tile is page-sized");
-static_assert(16 == offsetof(struct Tile, cells), "stable cells offset");
-static_assert(2016 == offsetof(struct Tile, colors), "stable colors offset");
 
 enum {
-	TILE_ROWS = 512,
-	TILE_COLS = 512,
+	TILE_ROWS = 64,
+	TILE_COLS = 64,
 };
 static const size_t TILES_SIZE = sizeof(struct Tile[TILE_ROWS][TILE_COLS]);
 
@@ -84,13 +86,7 @@ enum {
 };
 
 struct Map {
-	struct MapTile {
-		time_t createTime;
-		time_t modifyTime;
-		time_t accessTime;
-		uint32_t modifyCount;
-		uint32_t accessCount;
-	} tiles[MAP_ROWS][MAP_COLS];
+	struct Meta meta[MAP_ROWS][MAP_COLS];
 };
 
 struct ServerMessage {
-- 
cgit 1.4.1


From a8f4b6f1804e2bc446ef35960a7b0c5e3235e6f9 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Tue, 21 Aug 2018 15:38:38 -0400
Subject: Use alignas rather than attributes

---
 torus.h | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/torus.h b/torus.h
index 4109b89..9e8697c 100644
--- a/torus.h
+++ b/torus.h
@@ -15,15 +15,13 @@
  */
 
 #include <assert.h>
+#include <stdalign.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <time.h>
 
-#define PACKED __attribute__((packed))
-#define ALIGNED(x) __attribute__((aligned(x)))
-
 #define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
 
 #undef COLOR_BLACK
@@ -64,8 +62,8 @@ struct Meta {
 	uint32_t accessCount;
 };
 
-struct ALIGNED(4096) Tile {
-	char cells[CELL_ROWS][CELL_COLS];
+struct Tile {
+	alignas(4096) char cells[CELL_ROWS][CELL_COLS];
 	uint8_t colors[CELL_ROWS][CELL_COLS];
 	struct Meta meta;
 };
@@ -90,7 +88,7 @@ struct Map {
 };
 
 struct ServerMessage {
-	enum PACKED {
+	enum {
 		SERVER_TILE,
 		SERVER_MOVE,
 		SERVER_PUT,
@@ -120,7 +118,7 @@ struct ServerMessage {
 static const uint8_t CURSOR_NONE = UINT8_MAX;
 
 struct ClientMessage {
-	enum PACKED {
+	enum {
 		CLIENT_MOVE,
 		CLIENT_PUT,
 		CLIENT_MAP,
-- 
cgit 1.4.1


From 7c5e77d57127d9b84ce92e1ff39ea6c5dd6ec2d6 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Tue, 21 Aug 2018 22:46:18 -0400
Subject: Replace client with rudimentary CP437 support

---
 Makefile |   3 +-
 client.c | 614 +++++++++++++++++++++++----------------------------------------
 server.c |   2 +-
 torus.h  |  28 ++-
 4 files changed, 246 insertions(+), 401 deletions(-)

diff --git a/Makefile b/Makefile
index 4091276..f2b2ca0 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ CHROOT_USER = torus
 CHROOT_GROUP = $(CHROOT_USER)
 
 CFLAGS += -Wall -Wextra -Wpedantic
-LDLIBS = -lm -lcurses
+LDLIBS = -lm -lcursesw
 BINS = server client help meta merge
 OBJS = $(BINS:%=%.o)
 
@@ -32,6 +32,7 @@ chroot.tar: server client help
 	    /lib/libncurses.so.8 \
 	    /lib/libncursesw.so.8 \
 	    root/lib
+	cp -a -f /usr/share/locale root/usr/share
 	cp -p -f /usr/share/misc/termcap.db root/usr/share/misc
 	cp -p -f /bin/sh root/bin
 	install -o root -g wheel -m 555 server client help root/bin
diff --git a/client.c b/client.c
index cb0107e..8086d3f 100644
--- a/client.c
+++ b/client.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2017  June McEnroe <june@causal.agency>
+/* Copyright (C) 2018  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -14,12 +14,14 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <ctype.h>
+#define _XOPEN_SOURCE_EXTENDED
+
 #include <curses.h>
 #include <err.h>
 #include <errno.h>
-#include <math.h>
+#include <locale.h>
 #include <poll.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -27,43 +29,42 @@
 #include <sys/un.h>
 #include <sysexits.h>
 #include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
 
 #include "torus.h"
 
+#define err(...) do { endwin(); err(__VA_ARGS__); } while(0)
+#define errx(...) do { endwin(); errx(__VA_ARGS__); } while (0)
+
+#define CTRL(ch) ((ch) ^ 0x40)
 enum {
 	ESC = 0x1B,
 	DEL = 0x7F,
 };
 
-static int client;
-
-static void clientMessage(struct ClientMessage msg) {
-	ssize_t size = send(client, &msg, sizeof(msg), 0);
-	if (size < 0) err(EX_IOERR, "send");
-}
-
-static void clientMove(int8_t dx, int8_t dy) {
-	struct ClientMessage msg = {
-		.type = CLIENT_MOVE,
-		.move = { .dx = dx, .dy = dy },
-	};
-	clientMessage(msg);
-}
-
-static void clientPut(uint8_t color, char cell) {
-	struct ClientMessage msg = {
-		.type = CLIENT_PUT,
-		.put = { .color = color, .cell = cell },
-	};
-	clientMessage(msg);
-}
+static void curse(void) {
+	setlocale(LC_CTYPE, "");
 
-static void clientMap(void) {
-	struct ClientMessage msg = { .type = CLIENT_MAP };
-	clientMessage(msg);
-}
+	initscr();
+	start_color();
+	if (!has_colors() || COLOR_PAIRS < 64) {
+		endwin();
+		fprintf(stderr, "Sorry, your terminal doesn't support colors!\n");
+		fprintf(stderr, "If you think it should, check TERM.\n");
+		exit(EX_CONFIG);
+	}
+	if (LINES < CELL_ROWS || COLS < CELL_COLS) {
+		endwin();
+		fprintf(
+			stderr,
+			"Sorry, your terminal is too small!\n"
+			"It must be at least %ux%u characters.\n",
+			CELL_COLS, CELL_ROWS
+		);
+		exit(EX_CONFIG);
+	}
 
-static void colorPairs(void) {
 	assume_default_colors(0, 0);
 	if (COLORS >= 16) {
 		for (short pair = 1; pair < 0x80; ++pair) {
@@ -74,421 +75,243 @@ static void colorPairs(void) {
 			init_pair(pair, pair & 007, (pair & 070) >> 3);
 		}
 	}
-}
 
-static chtype colorAttr(uint8_t color) {
-	if (COLORS >= 16) return COLOR_PAIR(color);
-	chtype bold = (color & COLOR_BRIGHT) ? A_BOLD : A_NORMAL;
-	short pair = (color & 0x70) >> 1 | (color & 0x07);
-	return bold | COLOR_PAIR(pair);
-}
+	color_set(COLOR_WHITE, NULL);
+	bool hline = (LINES > CELL_ROWS);
+	bool vline = (COLS > CELL_COLS);
+	if (hline) mvhline(CELL_ROWS, 0, 0, CELL_COLS);
+	if (vline) mvvline(0, CELL_COLS, 0, CELL_ROWS);
+	if (hline && vline) mvaddch(CELL_ROWS, CELL_COLS, ACS_LRCORNER);
 
-static uint8_t attrColor(chtype attr) {
-	if (COLORS >= 16) return PAIR_NUMBER(attr);
-	uint8_t bright = (attr & A_BOLD) ? COLOR_BRIGHT : 0;
-	short pair = PAIR_NUMBER(attr);
-	return (pair & 070) << 1 | bright | (pair & 007);
+	cbreak();
+	noecho();
+	keypad(stdscr, true);
+	set_escdelay(100);
 }
 
-static struct {
-	int8_t speed;
-	uint8_t color;
-	enum {
-		MODE_NORMAL,
-		MODE_MAP,
-		MODE_INSERT,
-		MODE_REPLACE,
-		MODE_PUT,
-		MODE_DRAW,
-	} mode;
-	int8_t dx;
-	int8_t dy;
-	uint8_t len;
-	char draw;
-} input = {
-	.speed = 1,
-	.color = COLOR_WHITE,
-	.dx = 1,
-};
-
-static void colorFg(uint8_t fg) {
-	input.color = (input.color & 0xF8) | fg;
+static attr_t colorAttr(uint8_t color) {
+	if (COLORS >= 16) return A_NORMAL;
+	return (color & COLOR_BRIGHT) ? A_BOLD : A_NORMAL;
 }
-
-static void colorBg(uint8_t bg) {
-	input.color = (input.color & 0x0F) | (bg << 4);
+static short colorPair(uint8_t color) {
+	if (COLORS >= 16) return color;
+	return (color & 0x70) >> 1 | (color & 0x07);
 }
 
-static void colorInvert(void) {
-	input.color =
-		(input.color & 0x08) |
-		((input.color & 0x07) << 4) |
-		((input.color & 0x70) >> 4);
+static void put(uint8_t cellX, uint8_t cellY, uint8_t color, uint8_t cell) {
+	cchar_t cch;
+	wchar_t wch[] = { CP437[cell], L'\0' };
+	setcchar(&cch, wch, colorAttr(color), colorPair(color), NULL);
+	mvadd_wch(cellY, cellX, &cch);
 }
 
-static void insertMode(int8_t dx, int8_t dy) {
-	input.mode = MODE_INSERT;
-	input.dx = dx;
-	input.dy = dy;
-	input.len = 0;
-}
+static int client;
 
-static void swapCell(int8_t dx, int8_t dy) {
-	uint8_t aColor = attrColor(inch());
-	char aCell = inch() & A_CHARTEXT;
+static void serverTile(void) {
+	struct Tile tile;
+	ssize_t size = recv(client, &tile, sizeof(tile), 0);
+	if (size < 0) err(EX_IOERR, "recv");
+	if ((size_t)size < sizeof(tile)) errx(EX_PROTOCOL, "truncated tile");
 
-	int sy, sx;
-	getyx(stdscr, sy, sx);
-	move(sy + dy, sx + dx);
-	uint8_t bColor = attrColor(inch());
-	char bCell = inch() & A_CHARTEXT;
-	move(sy, sx);
+	for (uint8_t cellY = 0; cellY < CELL_ROWS; ++cellY) {
+		for (uint8_t cellX = 0; cellX < CELL_COLS; ++cellX) {
+			put(cellX, cellY, tile.colors[cellY][cellX], tile.cells[cellY][cellX]);
+		}
+	}
+}
 
-	clientPut(bColor, bCell);
-	clientMove(dx, dy);
-	clientPut(aColor, aCell);
+static void serverPut(struct ServerMessage msg) {
+	put(msg.put.cellX, msg.put.cellY, msg.put.color, msg.put.cell);
 }
 
-static void inputNormal(int c) {
-	switch (c) {
-		break; case ESC: input.mode = MODE_NORMAL;
+static void serverCursor(struct ServerMessage msg) {
+}
 
-		break; case 'q': endwin(); exit(EX_OK);
-		break; case 'm': clientMap();
+static void serverMap(void) {
+}
 
-		break; case 'i': insertMode(1, 0);
-		break; case 'a': clientMove(1, 0); insertMode(1, 0);
-		break; case 'I': insertMode(0, 0);
-		break; case 'r': input.mode = MODE_REPLACE;
-		break; case 'p': input.mode = MODE_PUT;
-		break; case 'R': input.mode = MODE_DRAW; input.draw = 0;
-		break; case 'x': clientPut(attrColor(inch()), ' ');
-
-		break; case '~': {
-			clientPut(input.color, inch() & A_CHARTEXT);
-			clientMove(input.dx, input.dy);
-		}
+static void readMessage(void) {
+	struct ServerMessage msg;
+	ssize_t size = recv(client, &msg, sizeof(msg), 0);
+	if (size < 0) err(EX_IOERR, "recv");
+	if ((size_t)size < sizeof(msg)) errx(EX_PROTOCOL, "truncated message");
 
-		break; case '[': if (input.speed > 1) input.speed--;
-		break; case ']': if (input.speed < 4) input.speed++;
-
-		break; case 'h': clientMove(-input.speed,            0);
-		break; case 'j': clientMove(           0,  input.speed);
-		break; case 'k': clientMove(           0, -input.speed);
-		break; case 'l': clientMove( input.speed,            0);
-		break; case 'y': clientMove(-input.speed, -input.speed);
-		break; case 'u': clientMove( input.speed, -input.speed);
-		break; case 'b': clientMove(-input.speed,  input.speed);
-		break; case 'n': clientMove( input.speed,  input.speed);
-
-		break; case 'H': swapCell(-1,  0);
-		break; case 'J': swapCell( 0,  1);
-		break; case 'K': swapCell( 0, -1);
-		break; case 'L': swapCell( 1,  0);
-		break; case 'Y': swapCell(-1, -1);
-		break; case 'U': swapCell( 1, -1);
-		break; case 'B': swapCell(-1,  1);
-		break; case 'N': swapCell( 1,  1);
-
-		break; case '`': input.color = attrColor(inch());
-
-		break; case '0': colorFg(COLOR_BLACK);
-		break; case '1': colorFg(COLOR_RED);
-		break; case '2': colorFg(COLOR_GREEN);
-		break; case '3': colorFg(COLOR_YELLOW);
-		break; case '4': colorFg(COLOR_BLUE);
-		break; case '5': colorFg(COLOR_MAGENTA);
-		break; case '6': colorFg(COLOR_CYAN);
-		break; case '7': colorFg(COLOR_WHITE);
-
-		break; case ')': colorBg(COLOR_BLACK);
-		break; case '!': colorBg(COLOR_RED);
-		break; case '@': colorBg(COLOR_GREEN);
-		break; case '#': colorBg(COLOR_YELLOW);
-		break; case '$': colorBg(COLOR_BLUE);
-		break; case '%': colorBg(COLOR_MAGENTA);
-		break; case '^': colorBg(COLOR_CYAN);
-		break; case '&': colorBg(COLOR_WHITE);
-
-		break; case '*': case '8': input.color ^= COLOR_BRIGHT;
-
-		break; case '(': case '9': colorInvert();
-
-		break; case KEY_LEFT:  clientMove(-1,  0);
-		break; case KEY_DOWN:  clientMove( 0,  1);
-		break; case KEY_UP:    clientMove( 0, -1);
-		break; case KEY_RIGHT: clientMove( 1,  0);
+	int y, x;
+	getyx(stdscr, y, x);
+	switch (msg.type) {
+		break; case SERVER_TILE:   serverTile();
+		break; case SERVER_MOVE:   y = msg.move.cellY; x = msg.move.cellX;
+		break; case SERVER_PUT:    serverPut(msg);
+		break; case SERVER_CURSOR: serverCursor(msg);
+		break; case SERVER_MAP:    serverMap();
+		break; default: errx(EX_PROTOCOL, "unknown message type %d", msg.type);
 	}
+	move(y, x);
 }
 
-static void inputMap(void) {
-	input.mode = MODE_NORMAL;
-	curs_set(1);
-	touchwin(stdscr);
+static void clientMessage(struct ClientMessage msg) {
+	ssize_t size = send(client, &msg, sizeof(msg), 0);
+	if (size < 0) err(EX_IOERR, "send");
 }
 
-static void inputInsert(int c) {
-	if (c == ESC) {
-		input.mode = MODE_NORMAL;
-		clientMove(-input.dx, -input.dy);
-	} else if (!input.dx && !input.dy) {
-		switch (c) {
-			break; case 'h': insertMode(-1,  0);
-			break; case 'j': insertMode( 0,  1);
-			break; case 'k': insertMode( 0, -1);
-			break; case 'l': insertMode( 1,  0);
-			break; case 'y': insertMode(-1, -1);
-			break; case 'u': insertMode( 1, -1);
-			break; case 'b': insertMode(-1,  1);
-			break; case 'n': insertMode( 1,  1);
-		}
-	} else if (c == '\b' || c == DEL) {
-		clientMove(-input.dx, -input.dy);
-		clientPut(input.color, ' ');
-		input.len--;
-	} else if (c == '\n') {
-		clientMove(input.dy, input.dx);
-		clientMove(-input.dx * input.len, -input.dy * input.len);
-		input.len = 0;
-	} else if (isprint(c)) {
-		clientPut(input.color, c);
-		clientMove(input.dx, input.dy);
-		input.len++;
-	}
+static void clientMove(int8_t dx, int8_t dy) {
+	struct ClientMessage msg = {
+		.type = CLIENT_MOVE,
+		.move = { .dx = dx, .dy = dy },
+	};
+	clientMessage(msg);
 }
 
-static void inputReplace(int c) {
-	if (isprint(c)) clientPut(attrColor(inch()), c);
-	input.mode = MODE_NORMAL;
+static void clientPut(uint8_t color, uint8_t cell) {
+	struct ClientMessage msg = {
+		.type = CLIENT_PUT,
+		.put = { .color = color, .cell = cell },
+	};
+	clientMessage(msg);
 }
 
-static void inputPut(int c) {
-	if (isprint(c)) clientPut(input.color, c);
-	input.mode = MODE_NORMAL;
+static void clientMap(void) {
+	struct ClientMessage msg = { .type = CLIENT_MAP };
+	clientMessage(msg);
 }
 
-static void inputDraw(int c) {
-	if (input.draw) {
-		inputNormal(c);
-		clientPut(input.color, input.draw);
-	} else if (isprint(c)) {
-		input.draw = c;
-		clientPut(input.color, c);
-	} else if (c == ESC) {
-		input.mode = MODE_NORMAL;
-	}
-}
+static struct {
+	enum {
+		MODE_NORMAL,
+		MODE_INSERT,
+	} mode;
+	uint8_t color;
+	uint8_t shift;
+} input;
 
-static void readInput(void) {
-	int c = getch();
-	switch (input.mode) {
-		break; case MODE_NORMAL:  inputNormal(c);
-		break; case MODE_MAP:     inputMap();
-		break; case MODE_INSERT:  inputInsert(c);
-		break; case MODE_REPLACE: inputReplace(c);
-		break; case MODE_PUT:     inputPut(c);
-		break; case MODE_DRAW:    inputDraw(c);
-	}
-}
+static struct {
+	int8_t dx;
+	int8_t dy;
+	uint8_t len;
+} insert;
 
-static void serverPut(uint8_t x, uint8_t y, uint8_t color, char cell) {
-	mvaddch(y, x, colorAttr(color) | cell);
+static void insertMode(int8_t dx, int8_t dy) {
+	input.mode = MODE_INSERT;
+	insert.dx = dx;
+	insert.dy = dy;
+	insert.len = 0;
 }
 
-static void serverTile(void) {
-	struct Tile tile;
-	ssize_t size = recv(client, &tile, sizeof(tile), 0);
-	if (size < 0) err(EX_IOERR, "recv");
-	if ((size_t)size < sizeof(tile)) {
-		errx(EX_PROTOCOL, "This tile isn't big enough...");
-	}
-
-	for (int y = 0; y < CELL_ROWS; ++y) {
-		for (int x = 0; x < CELL_COLS; ++x) {
-			serverPut(x, y, tile.colors[y][x], tile.cells[y][x]);
-		}
-	}
+static void inputFg(uint8_t fg) {
+	input.color = (input.color & 0x78) | (fg & 0x07);
+}
+static void inputBg(uint8_t bg) {
+	input.color = (input.color & 0x0F) | (bg & 0x07) << 4;
+}
+static void inputInvert(void) {
+	input.color = (input.color & 0x08)
+		| (input.color & 0x70) >> 4
+		| (input.color & 0x07) << 4;
 }
 
-static void serverCursor(uint8_t oldX, uint8_t oldY, uint8_t newX, uint8_t newY) {
-	if (oldX != CURSOR_NONE) {
-		move(oldY, oldX);
-		addch(inch() & ~A_REVERSE);
-	}
-	if (newX != CURSOR_NONE) {
-		move(newY, newX);
-		addch(inch() | A_REVERSE);
+static uint8_t inputCell(wchar_t ch) {
+	if (ch < 0x7F) return (uint8_t)ch + input.shift;
+	for (size_t i = 0; i < ARRAY_LEN(CP437); ++i) {
+		if (ch == CP437[i]) return i;
 	}
+	return 0;
 }
 
-static WINDOW *mapFrame;
-static WINDOW *mapWindow;
-
-static const char MAP_CELLS[] = " -~=+:$%#";
-static const uint8_t MAP_COLORS[] = {
-	COLOR_BLUE,    COLOR_BRIGHT | COLOR_BLUE,
-	COLOR_CYAN,    COLOR_BRIGHT | COLOR_CYAN,
-	COLOR_GREEN,   COLOR_BRIGHT | COLOR_GREEN,
-	COLOR_YELLOW,  COLOR_BRIGHT | COLOR_YELLOW,
-	COLOR_RED,     COLOR_BRIGHT | COLOR_RED,
-	COLOR_MAGENTA, COLOR_BRIGHT | COLOR_MAGENTA,
-	COLOR_WHITE,   COLOR_BRIGHT | COLOR_WHITE,
-};
-
-static void serverMap(void) {
-	struct Map map;
-	ssize_t size = recv(client, &map, sizeof(map), 0);
-	if (size < 0) err(EX_IOERR, "recv");
-	if ((size_t)size < sizeof(map)) errx(EX_PROTOCOL, "This map is incomplete...");
-
-	uint32_t countMax = 0;
-	time_t timeNow = time(NULL);
-	time_t timeMin = timeNow;
-	for (int y = 0; y < MAP_ROWS; ++y) {
-		for (int x = 0; x < MAP_COLS; ++x) {
-			struct Meta meta = map.meta[y][x];
-			if (countMax < meta.modifyCount) countMax = meta.modifyCount;
-			if (meta.modifyTime && timeMin > meta.modifyTime) {
-				timeMin = meta.modifyTime;
-			}
+static void inputNormal(bool keyCode, wchar_t ch) {
+	if (keyCode) {
+		switch (ch) {
+			break; case KEY_LEFT:  clientMove(-1,  0);
+			break; case KEY_RIGHT: clientMove( 1,  0);
+			break; case KEY_UP:    clientMove( 0, -1);
+			break; case KEY_DOWN:  clientMove( 0,  1);
 		}
+		return;
 	}
 
-	for (int y = 0; y < MAP_ROWS; ++y) {
-		for (int x = 0; x < MAP_COLS; ++x) {
-			struct Meta meta = map.meta[y][x];
-
-			double count = (meta.modifyCount && countMax > 1)
-				? log(meta.modifyCount) / log(countMax)
-				: 0.0;
-			double time = (meta.modifyTime && timeNow - timeMin)
-				? (double)(meta.modifyTime - timeMin) / (double)(timeNow - timeMin)
-				: 0.0;
-			count *= ARRAY_LEN(MAP_CELLS) - 2;
-			time *= ARRAY_LEN(MAP_COLORS) - 1;
-
-			char cell = MAP_CELLS[(int)round(count)];
-			chtype attr = colorAttr(MAP_COLORS[(int)round(time)]);
-			if (y == MAP_ROWS / 2 && x == MAP_COLS / 2) {
-				attr |= A_REVERSE;
-			}
+	switch (ch) {
+		break; case ESC: input.mode = MODE_NORMAL;
+		break; case 'q': endwin(); exit(EX_OK);
 
-			wmove(mapWindow, y, 3 * x);
-			waddch(mapWindow, attr | cell);
-			waddch(mapWindow, attr | cell);
-			waddch(mapWindow, attr | cell);
-		}
-	}
+		break; case 'h': clientMove(-1,  0);
+		break; case 'l': clientMove( 1,  0);
+		break; case 'k': clientMove( 0, -1);
+		break; case 'j': clientMove( 0,  1);
+		break; case 'y': clientMove(-1, -1);
+		break; case 'u': clientMove( 1, -1);
+		break; case 'b': clientMove(-1,  1);
+		break; case 'n': clientMove( 1,  1);
+
+		break; case '0': inputFg(COLOR_BLACK);
+		break; case '1': inputFg(COLOR_RED);
+		break; case '2': inputFg(COLOR_GREEN);
+		break; case '3': inputFg(COLOR_YELLOW);
+		break; case '4': inputFg(COLOR_BLUE);
+		break; case '5': inputFg(COLOR_MAGENTA);
+		break; case '6': inputFg(COLOR_CYAN);
+		break; case '7': inputFg(COLOR_WHITE);
+
+		break; case ')': inputBg(COLOR_BLACK);
+		break; case '!': inputBg(COLOR_RED);
+		break; case '@': inputBg(COLOR_GREEN);
+		break; case '#': inputBg(COLOR_YELLOW);
+		break; case '$': inputBg(COLOR_BLUE);
+		break; case '%': inputBg(COLOR_MAGENTA);
+		break; case '^': inputBg(COLOR_CYAN);
+		break; case '&': inputBg(COLOR_WHITE);
+
+		break; case '8': case '*': input.color ^= COLOR_BRIGHT;
+		break; case '9': case '(': inputInvert();
+
+		break; case CTRL('P'): input.shift -= 0x20;
+		break; case CTRL('N'): input.shift += 0x20;
 
-	input.mode = MODE_MAP;
-	curs_set(0);
+		break; case 'i': insertMode(1, 0);
+		break; case 'a': clientMove(1, 0); insertMode(1, 0);
+	}
 }
 
-static void readMessage(void) {
-	struct ServerMessage msg;
-	ssize_t size = recv(client, &msg, sizeof(msg), 0);
-	if (size < 0) err(EX_IOERR, "recv");
-	if ((size_t)size < sizeof(msg)) errx(EX_PROTOCOL, "A message was cut short.");
-
-	int sy, sx;
-	getyx(stdscr, sy, sx);
-	switch (msg.type) {
-		break; case SERVER_TILE: serverTile();
-		break; case SERVER_MOVE: move(msg.move.cellY, msg.move.cellX); return;
-		break; case SERVER_PUT: {
-			serverPut(
-				msg.put.cellX,
-				msg.put.cellY,
-				msg.put.color,
-				msg.put.cell
-			);
+static void inputInsert(bool keyCode, wchar_t ch) {
+	if (keyCode) {
+		inputNormal(keyCode, ch);
+		return;
+	}
+	switch (ch) {
+		break; case ESC: {
+			input.mode = MODE_NORMAL;
+			clientMove(-insert.dx, -insert.dy);
 		}
-		break; case SERVER_CURSOR: {
-			serverCursor(
-				msg.cursor.oldCellX,
-				msg.cursor.oldCellY,
-				msg.cursor.newCellX,
-				msg.cursor.newCellY
-			);
+		break; case '\b': case DEL: {
+			clientMove(-insert.dx, -insert.dy);
+			clientPut(input.color, ' ');
+			insert.len--;
 		}
-		break; case SERVER_MAP: serverMap();
-		break; default: errx(EX_PROTOCOL, "I don't know what %d means!", msg.type);
-	}
-	move(sy, sx);
-}
+		break; case '\n': {
+			clientMove(insert.dy, insert.dx);
+			clientMove(insert.len * -insert.dx, insert.len * -insert.dy);
+			insert.len = 0;
+		}
+		break; default: {
+			if (iswcntrl(ch)) {
+				inputNormal(false, ch);
+				break;
+			}
 
-static void draw(void) {
-	wnoutrefresh(stdscr);
-	if (input.mode == MODE_MAP) {
-		touchwin(mapFrame);
-		touchwin(mapWindow);
-		wnoutrefresh(mapFrame);
-		wnoutrefresh(mapWindow);
+			uint8_t cell = inputCell(ch);
+			if (!cell) break;
+			clientPut(input.color, cell);
+			clientMove(insert.dx, insert.dy);
+			insert.len++;
+		}
 	}
-	doupdate();
 }
 
-static void curse(void) {
-	initscr();
-	cbreak();
-	noecho();
-	keypad(stdscr, true);
-	set_escdelay(100);
-
-	if (!has_colors()) {
-		endwin();
-		fprintf(
-			stderr,
-			"Sorry, your terminal doesn't support colors!\n"
-			"If you think it does, check TERM.\n"
-		);
-		exit(EX_CONFIG);
-	}
-	start_color();
-	if (COLOR_PAIRS < 64) {
-		endwin();
-		fprintf(
-			stderr,
-			"Sorry, your terminal doesn't support enough color pairs!\n"
-			"If you think it does, check TERM.\n"
-		);
-		exit(EX_CONFIG);
-	}
-	colorPairs();
-
-	if (LINES < CELL_ROWS || COLS < CELL_COLS) {
-		endwin();
-		fprintf(stderr, "Sorry, your terminal is too small!\n");
-		fprintf(stderr, "It needs to be at least 80x25 characters.\n");
-		exit(EX_CONFIG);
-	}
-
-	attrset(colorAttr(COLOR_WHITE));
-	if (LINES > CELL_ROWS) {
-		mvhline(CELL_ROWS, 0, 0, CELL_COLS);
-	}
-	if (COLS > CELL_COLS) {
-		mvvline(0, CELL_COLS, 0, CELL_ROWS);
-	}
-	if (LINES > CELL_ROWS && COLS > CELL_COLS) {
-		mvaddch(CELL_ROWS, CELL_COLS, ACS_LRCORNER);
+static void readInput(void) {
+	wint_t ch;
+	bool keyCode = (KEY_CODE_YES == get_wch(&ch));
+	switch (input.mode) {
+		break; case MODE_NORMAL: inputNormal(keyCode, ch);
+		break; case MODE_INSERT: inputInsert(keyCode, ch);
 	}
-	attrset(A_NORMAL);
-
-	mapFrame = newwin(
-		MAP_ROWS + 2,
-		3 * MAP_COLS + 2,
-		CELL_INIT_Y - MAP_ROWS / 2 - 1,
-		CELL_INIT_X - 3 * MAP_COLS / 2 - 1
-	);
-	mapWindow = newwin(
-		MAP_ROWS,
-		3 * MAP_COLS,
-		CELL_INIT_Y - MAP_ROWS / 2,
-		CELL_INIT_X - 3 * MAP_COLS / 2
-	);
-	wattrset(mapFrame, colorAttr(COLOR_WHITE));
-	box(mapFrame, 0, 0);
 }
 
 int main() {
@@ -515,6 +338,7 @@ int main() {
 
 		if (fds[0].revents) readInput();
 		if (fds[1].revents) readMessage();
-		draw();
+
+		refresh();
 	}
 }
diff --git a/server.c b/server.c
index ff13af3..3dedad8 100644
--- a/server.c
+++ b/server.c
@@ -254,7 +254,7 @@ static bool clientMove(struct Client *client, int8_t dx, int8_t dy) {
 	return true;
 }
 
-static bool clientPut(const struct Client *client, uint8_t color, char cell) {
+static bool clientPut(const struct Client *client, uint8_t color, uint8_t cell) {
 	struct Tile *tile = tileModify(client->tileX, client->tileY);
 	tile->colors[client->cellY][client->cellX] = color;
 	tile->cells[client->cellY][client->cellX] = cell;
diff --git a/torus.h b/torus.h
index 9e8697c..1bbb43a 100644
--- a/torus.h
+++ b/torus.h
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <time.h>
+#include <wchar.h>
 
 #define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
 
@@ -45,11 +46,30 @@ enum {
 	COLOR_BRIGHT,
 };
 
+static const wchar_t CP437[256] = (
+	L" ☺☻♥♦♣♠•◘○◙♂♀♪♫☼"
+	L"►◄↕‼¶§▬↨↑↓→←∟↔▲▼"
+	L" !\"#$%&'()*+,-./"
+	L"0123456789:;<=>?"
+	L"@ABCDEFGHIJKLMNO"
+	L"PQRSTUVWXYZ[\\]^_"
+	L"`abcdefghijklmno"
+	L"pqrstuvwxyz{|}~⌂"
+	L"ÇüéâäàåçêëèïîìÄÅ"
+	L"ÉæÆôöòûùÿÖÜ¢£¥₧ƒ"
+	L"áíóúñѪº¿⌐¬½¼¡«»"
+	L"░▒▓│┤╡╢╖╕╣║╗╝╜╛┐"
+	L"└┴┬├─┼╞╟╚╔╩╦╠═╬╧"
+	L"╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀"
+	L"αßΓπΣσµτΦΘΩδ∞φε∩"
+	L"≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ "
+);
+
 enum {
 	CELL_ROWS = 24,
 	CELL_COLS = 80,
 };
-static const size_t CELLS_SIZE = sizeof(char[CELL_ROWS][CELL_COLS]);
+static const size_t CELLS_SIZE = sizeof(uint8_t[CELL_ROWS][CELL_COLS]);
 
 static const uint8_t CELL_INIT_X = CELL_COLS / 2;
 static const uint8_t CELL_INIT_Y = CELL_ROWS / 2;
@@ -63,7 +83,7 @@ struct Meta {
 };
 
 struct Tile {
-	alignas(4096) char cells[CELL_ROWS][CELL_COLS];
+	alignas(4096) uint8_t cells[CELL_ROWS][CELL_COLS];
 	uint8_t colors[CELL_ROWS][CELL_COLS];
 	struct Meta meta;
 };
@@ -104,7 +124,7 @@ struct ServerMessage {
 			uint8_t cellX;
 			uint8_t cellY;
 			uint8_t color;
-			char cell;
+			uint8_t cell;
 		} put;
 		struct {
 			uint8_t oldCellX;
@@ -130,7 +150,7 @@ struct ClientMessage {
 		} move;
 		struct {
 			uint8_t color;
-			char cell;
+			uint8_t cell;
 		} put;
 	};
 };
-- 
cgit 1.4.1


From 184e580484e466d2b86815f033fd305f303f0c44 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Tue, 21 Aug 2018 23:09:38 -0400
Subject: Maintain a tile client-side and support cursors

---
 client.c | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/client.c b/client.c
index 8086d3f..3c34525 100644
--- a/client.c
+++ b/client.c
@@ -98,33 +98,45 @@ static short colorPair(uint8_t color) {
 	return (color & 0x70) >> 1 | (color & 0x07);
 }
 
-static void put(uint8_t cellX, uint8_t cellY, uint8_t color, uint8_t cell) {
+static struct Tile tile;
+
+static void tileDraw(uint8_t cellX, uint8_t cellY, attr_t attr) {
+	uint8_t color = tile.colors[cellY][cellX];
+	uint8_t cell = tile.cells[cellY][cellX];
+
 	cchar_t cch;
 	wchar_t wch[] = { CP437[cell], L'\0' };
-	setcchar(&cch, wch, colorAttr(color), colorPair(color), NULL);
+	setcchar(&cch, wch, attr | colorAttr(color), colorPair(color), NULL);
 	mvadd_wch(cellY, cellX, &cch);
 }
 
 static int client;
 
 static void serverTile(void) {
-	struct Tile tile;
 	ssize_t size = recv(client, &tile, sizeof(tile), 0);
 	if (size < 0) err(EX_IOERR, "recv");
 	if ((size_t)size < sizeof(tile)) errx(EX_PROTOCOL, "truncated tile");
 
 	for (uint8_t cellY = 0; cellY < CELL_ROWS; ++cellY) {
 		for (uint8_t cellX = 0; cellX < CELL_COLS; ++cellX) {
-			put(cellX, cellY, tile.colors[cellY][cellX], tile.cells[cellY][cellX]);
+			tileDraw(cellX, cellY, A_NORMAL);
 		}
 	}
 }
 
 static void serverPut(struct ServerMessage msg) {
-	put(msg.put.cellX, msg.put.cellY, msg.put.color, msg.put.cell);
+	tile.colors[msg.put.cellY][msg.put.cellX] = msg.put.color;
+	tile.cells[msg.put.cellY][msg.put.cellX] = msg.put.cell;
+	tileDraw(msg.put.cellY, msg.put.cellX, A_NORMAL);
 }
 
 static void serverCursor(struct ServerMessage msg) {
+	if (msg.cursor.oldCellX != CURSOR_NONE) {
+		tileDraw(msg.cursor.oldCellX, msg.cursor.oldCellY, A_NORMAL);
+	}
+	if (msg.cursor.newCellX != CURSOR_NONE) {
+		tileDraw(msg.cursor.newCellX, msg.cursor.newCellY, A_REVERSE);
+	}
 }
 
 static void serverMap(void) {
-- 
cgit 1.4.1


From 2445704f2db269a605b6059cb3e54d0de33e7494 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Tue, 21 Aug 2018 23:46:26 -0400
Subject: Track cell{X,Y} client-side and implement swaps

---
 client.c | 43 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 37 insertions(+), 6 deletions(-)

diff --git a/client.c b/client.c
index 3c34525..093169d 100644
--- a/client.c
+++ b/client.c
@@ -98,6 +98,8 @@ static short colorPair(uint8_t color) {
 	return (color & 0x70) >> 1 | (color & 0x07);
 }
 
+static uint8_t cellX;
+static uint8_t cellY;
 static struct Tile tile;
 
 static void tileDraw(uint8_t cellX, uint8_t cellY, attr_t attr) {
@@ -124,10 +126,15 @@ static void serverTile(void) {
 	}
 }
 
+static void serverMove(struct ServerMessage msg) {
+	cellX = msg.move.cellX;
+	cellY = msg.move.cellY;
+}
+
 static void serverPut(struct ServerMessage msg) {
 	tile.colors[msg.put.cellY][msg.put.cellX] = msg.put.color;
 	tile.cells[msg.put.cellY][msg.put.cellX] = msg.put.cell;
-	tileDraw(msg.put.cellY, msg.put.cellX, A_NORMAL);
+	tileDraw(msg.put.cellX, msg.put.cellY, A_NORMAL);
 }
 
 static void serverCursor(struct ServerMessage msg) {
@@ -148,17 +155,15 @@ static void readMessage(void) {
 	if (size < 0) err(EX_IOERR, "recv");
 	if ((size_t)size < sizeof(msg)) errx(EX_PROTOCOL, "truncated message");
 
-	int y, x;
-	getyx(stdscr, y, x);
 	switch (msg.type) {
 		break; case SERVER_TILE:   serverTile();
-		break; case SERVER_MOVE:   y = msg.move.cellY; x = msg.move.cellX;
+		break; case SERVER_MOVE:   serverMove(msg);
 		break; case SERVER_PUT:    serverPut(msg);
 		break; case SERVER_CURSOR: serverCursor(msg);
 		break; case SERVER_MAP:    serverMap();
 		break; default: errx(EX_PROTOCOL, "unknown message type %d", msg.type);
 	}
-	move(y, x);
+	move(cellY, cellX);
 }
 
 static void clientMessage(struct ClientMessage msg) {
@@ -194,7 +199,9 @@ static struct {
 	} mode;
 	uint8_t color;
 	uint8_t shift;
-} input;
+} input = {
+	.color = COLOR_WHITE,
+};
 
 static struct {
 	int8_t dx;
@@ -221,6 +228,21 @@ static void inputInvert(void) {
 		| (input.color & 0x07) << 4;
 }
 
+static void inputSwap(int8_t dx, int8_t dy) {
+	if ((uint8_t)(cellX + dx) >= CELL_COLS) return;
+	if ((uint8_t)(cellY + dy) >= CELL_ROWS) return;
+
+	uint8_t aColor = tile.colors[cellY][cellX];
+	uint8_t aCell = tile.cells[cellY][cellX];
+
+	uint8_t bColor = tile.colors[cellY + dy][cellX + dx];
+	uint8_t bCell = tile.cells[cellY + dy][cellX + dx];
+
+	clientPut(bColor, bCell);
+	clientMove(dx, dy);
+	clientPut(aColor, aCell);
+}
+
 static uint8_t inputCell(wchar_t ch) {
 	if (ch < 0x7F) return (uint8_t)ch + input.shift;
 	for (size_t i = 0; i < ARRAY_LEN(CP437); ++i) {
@@ -274,6 +296,15 @@ static void inputNormal(bool keyCode, wchar_t ch) {
 		break; case '8': case '*': input.color ^= COLOR_BRIGHT;
 		break; case '9': case '(': inputInvert();
 
+		break; case 'H': inputSwap(-1,  0);
+		break; case 'L': inputSwap( 1,  0);
+		break; case 'K': inputSwap( 0, -1);
+		break; case 'J': inputSwap( 0,  1);
+		break; case 'Y': inputSwap(-1, -1);
+		break; case 'U': inputSwap( 1, -1);
+		break; case 'B': inputSwap(-1,  1);
+		break; case 'N': inputSwap( 1,  1);
+
 		break; case CTRL('P'): input.shift -= 0x20;
 		break; case CTRL('N'): input.shift += 0x20;
 
-- 
cgit 1.4.1


From ffb25298550cd49978eaedea3916f82736ff82ab Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Wed, 22 Aug 2018 00:33:31 -0400
Subject: Fix black-on-black once again

---
 client.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/client.c b/client.c
index 093169d..3dba3cc 100644
--- a/client.c
+++ b/client.c
@@ -82,6 +82,7 @@ static void curse(void) {
 	if (hline) mvhline(CELL_ROWS, 0, 0, CELL_COLS);
 	if (vline) mvvline(0, CELL_COLS, 0, CELL_ROWS);
 	if (hline && vline) mvaddch(CELL_ROWS, CELL_COLS, ACS_LRCORNER);
+	color_set(0, NULL);
 
 	cbreak();
 	noecho();
-- 
cgit 1.4.1


From a37c41807b0bfa84420b2eb98ea3e1dedda05f8b Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Wed, 22 Aug 2018 00:33:58 -0400
Subject: Implement ` ~ and x

---
 client.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/client.c b/client.c
index 3dba3cc..1972be4 100644
--- a/client.c
+++ b/client.c
@@ -296,6 +296,7 @@ static void inputNormal(bool keyCode, wchar_t ch) {
 
 		break; case '8': case '*': input.color ^= COLOR_BRIGHT;
 		break; case '9': case '(': inputInvert();
+		break; case '`': input.color = tile.colors[cellY][cellX];
 
 		break; case 'H': inputSwap(-1,  0);
 		break; case 'L': inputSwap( 1,  0);
@@ -306,6 +307,12 @@ static void inputNormal(bool keyCode, wchar_t ch) {
 		break; case 'B': inputSwap(-1,  1);
 		break; case 'N': inputSwap( 1,  1);
 
+		break; case 'x': clientPut(tile.colors[cellY][cellX], ' ');
+		break; case '~': {
+			clientPut(input.color, tile.cells[cellY][cellX]);
+			clientMove(1, 0);
+		}
+
 		break; case CTRL('P'): input.shift -= 0x20;
 		break; case CTRL('N'): input.shift += 0x20;
 
-- 
cgit 1.4.1


From 2d3f28fb6a01c30f5179c4cf86e9cef156e8121a Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Wed, 22 Aug 2018 00:49:24 -0400
Subject: Add C-a and C-x

---
 client.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/client.c b/client.c
index 1972be4..6b60cb8 100644
--- a/client.c
+++ b/client.c
@@ -313,6 +313,13 @@ static void inputNormal(bool keyCode, wchar_t ch) {
 			clientMove(1, 0);
 		}
 
+		break; case CTRL('A'): {
+			clientPut(tile.colors[cellY][cellX], tile.cells[cellY][cellX] + 1);
+		}
+		break; case CTRL('X'): {
+			clientPut(tile.colors[cellY][cellX], tile.cells[cellY][cellX] - 1);
+		}
+
 		break; case CTRL('P'): input.shift -= 0x20;
 		break; case CTRL('N'): input.shift += 0x20;
 
-- 
cgit 1.4.1


From 0dbd71e08191204a95116ca5e399e430ea1af7d7 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Wed, 22 Aug 2018 01:29:28 -0400
Subject: Add F-keys to set shift

---
 client.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/client.c b/client.c
index 6b60cb8..ff1a12c 100644
--- a/client.c
+++ b/client.c
@@ -259,12 +259,18 @@ static void inputNormal(bool keyCode, wchar_t ch) {
 			break; case KEY_RIGHT: clientMove( 1,  0);
 			break; case KEY_UP:    clientMove( 0, -1);
 			break; case KEY_DOWN:  clientMove( 0,  1);
+
+			break; case KEY_F(1): input.shift = 0x00;
+			break; case KEY_F(2): input.shift = 0xC0;
+			break; case KEY_F(3): input.shift = 0xA0;
+			break; case KEY_F(4): input.shift = 0x70;
+			break; case KEY_F(5): input.shift = 0x40;
 		}
 		return;
 	}
 
 	switch (ch) {
-		break; case ESC: input.mode = MODE_NORMAL;
+		break; case ESC: input.mode = MODE_NORMAL; input.shift = 0;
 		break; case 'q': endwin(); exit(EX_OK);
 
 		break; case 'h': clientMove(-1,  0);
@@ -320,9 +326,6 @@ static void inputNormal(bool keyCode, wchar_t ch) {
 			clientPut(tile.colors[cellY][cellX], tile.cells[cellY][cellX] - 1);
 		}
 
-		break; case CTRL('P'): input.shift -= 0x20;
-		break; case CTRL('N'): input.shift += 0x20;
-
 		break; case 'i': insertMode(1, 0);
 		break; case 'a': clientMove(1, 0); insertMode(1, 0);
 	}
-- 
cgit 1.4.1


From 0fa74113df3004c7c70ad4c270aa10f8efaab5d0 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Wed, 22 Aug 2018 15:23:46 -0400
Subject: Implement r

---
 client.c | 59 ++++++++++++++++++++++++++++++-----------------------------
 1 file changed, 30 insertions(+), 29 deletions(-)

diff --git a/client.c b/client.c
index ff1a12c..9fd4cc7 100644
--- a/client.c
+++ b/client.c
@@ -30,7 +30,6 @@
 #include <sysexits.h>
 #include <unistd.h>
 #include <wchar.h>
-#include <wctype.h>
 
 #include "torus.h"
 
@@ -197,6 +196,7 @@ static struct {
 	enum {
 		MODE_NORMAL,
 		MODE_INSERT,
+		MODE_REPLACE,
 	} mode;
 	uint8_t color;
 	uint8_t shift;
@@ -252,23 +252,22 @@ static uint8_t inputCell(wchar_t ch) {
 	return 0;
 }
 
-static void inputNormal(bool keyCode, wchar_t ch) {
-	if (keyCode) {
-		switch (ch) {
-			break; case KEY_LEFT:  clientMove(-1,  0);
-			break; case KEY_RIGHT: clientMove( 1,  0);
-			break; case KEY_UP:    clientMove( 0, -1);
-			break; case KEY_DOWN:  clientMove( 0,  1);
-
-			break; case KEY_F(1): input.shift = 0x00;
-			break; case KEY_F(2): input.shift = 0xC0;
-			break; case KEY_F(3): input.shift = 0xA0;
-			break; case KEY_F(4): input.shift = 0x70;
-			break; case KEY_F(5): input.shift = 0x40;
-		}
-		return;
+static void inputKeyCode(wchar_t ch) {
+	switch (ch) {
+		break; case KEY_LEFT:  clientMove(-1,  0);
+		break; case KEY_RIGHT: clientMove( 1,  0);
+		break; case KEY_UP:    clientMove( 0, -1);
+		break; case KEY_DOWN:  clientMove( 0,  1);
+
+		break; case KEY_F(1): input.shift = 0x00;
+		break; case KEY_F(2): input.shift = 0xC0;
+		break; case KEY_F(3): input.shift = 0xA0;
+		break; case KEY_F(4): input.shift = 0x70;
+		break; case KEY_F(5): input.shift = 0x40;
 	}
+}
 
+static void inputNormal(wchar_t ch) {
 	switch (ch) {
 		break; case ESC: input.mode = MODE_NORMAL; input.shift = 0;
 		break; case 'q': endwin(); exit(EX_OK);
@@ -328,14 +327,11 @@ static void inputNormal(bool keyCode, wchar_t ch) {
 
 		break; case 'i': insertMode(1, 0);
 		break; case 'a': clientMove(1, 0); insertMode(1, 0);
+		break; case 'r': input.mode = MODE_REPLACE;
 	}
 }
 
-static void inputInsert(bool keyCode, wchar_t ch) {
-	if (keyCode) {
-		inputNormal(keyCode, ch);
-		return;
-	}
+static void inputInsert(wchar_t ch) {
 	switch (ch) {
 		break; case ESC: {
 			input.mode = MODE_NORMAL;
@@ -352,11 +348,6 @@ static void inputInsert(bool keyCode, wchar_t ch) {
 			insert.len = 0;
 		}
 		break; default: {
-			if (iswcntrl(ch)) {
-				inputNormal(false, ch);
-				break;
-			}
-
 			uint8_t cell = inputCell(ch);
 			if (!cell) break;
 			clientPut(input.color, cell);
@@ -366,12 +357,22 @@ static void inputInsert(bool keyCode, wchar_t ch) {
 	}
 }
 
+static void inputReplace(wchar_t ch) {
+	uint8_t cell = inputCell(ch);
+	if (ch != ESC && cell) clientPut(tile.colors[cellY][cellX], cell);
+	input.mode = MODE_NORMAL;
+}
+
 static void readInput(void) {
 	wint_t ch;
-	bool keyCode = (KEY_CODE_YES == get_wch(&ch));
+	if (KEY_CODE_YES == get_wch(&ch)) {
+		inputKeyCode(ch);
+		return;
+	}
 	switch (input.mode) {
-		break; case MODE_NORMAL: inputNormal(keyCode, ch);
-		break; case MODE_INSERT: inputInsert(keyCode, ch);
+		break; case MODE_NORMAL:  inputNormal(ch);
+		break; case MODE_INSERT:  inputInsert(ch);
+		break; case MODE_REPLACE: inputReplace(ch);
 	}
 }
 
-- 
cgit 1.4.1


From 3ff8639f865083abe28b9ef178101b42aef234a9 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Wed, 22 Aug 2018 16:05:56 -0400
Subject: Implement R

---
 client.c | 26 +++++++++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/client.c b/client.c
index 9fd4cc7..dec86a9 100644
--- a/client.c
+++ b/client.c
@@ -197,9 +197,11 @@ static struct {
 		MODE_NORMAL,
 		MODE_INSERT,
 		MODE_REPLACE,
+		MODE_DRAW,
 	} mode;
 	uint8_t color;
 	uint8_t shift;
+	uint8_t draw;
 } input = {
 	.color = COLOR_WHITE,
 };
@@ -245,7 +247,7 @@ static void inputSwap(int8_t dx, int8_t dy) {
 }
 
 static uint8_t inputCell(wchar_t ch) {
-	if (ch < 0x7F) return (uint8_t)ch + input.shift;
+	if (ch < 0x80) return (uint8_t)ch + input.shift;
 	for (size_t i = 0; i < ARRAY_LEN(CP437); ++i) {
 		if (ch == CP437[i]) return i;
 	}
@@ -328,6 +330,7 @@ static void inputNormal(wchar_t ch) {
 		break; case 'i': insertMode(1, 0);
 		break; case 'a': clientMove(1, 0); insertMode(1, 0);
 		break; case 'r': input.mode = MODE_REPLACE;
+		break; case 'R': input.mode = MODE_DRAW; input.draw = 0;
 	}
 }
 
@@ -358,11 +361,27 @@ static void inputInsert(wchar_t ch) {
 }
 
 static void inputReplace(wchar_t ch) {
-	uint8_t cell = inputCell(ch);
-	if (ch != ESC && cell) clientPut(tile.colors[cellY][cellX], cell);
+	if (ch != ESC) {
+		uint8_t cell = inputCell(ch);
+		if (!cell) return;
+		clientPut(tile.colors[cellY][cellX], cell);
+	}
 	input.mode = MODE_NORMAL;
 }
 
+static void inputDraw(wchar_t ch) {
+	if (ch == ESC) {
+		input.mode = MODE_NORMAL;
+		return;
+	}
+	if (input.draw) {
+		inputNormal(ch);
+	} else {
+		input.draw = inputCell(ch);
+	}
+	clientPut(input.color, input.draw);
+}
+
 static void readInput(void) {
 	wint_t ch;
 	if (KEY_CODE_YES == get_wch(&ch)) {
@@ -373,6 +392,7 @@ static void readInput(void) {
 		break; case MODE_NORMAL:  inputNormal(ch);
 		break; case MODE_INSERT:  inputInsert(ch);
 		break; case MODE_REPLACE: inputReplace(ch);
+		break; case MODE_DRAW:    inputDraw(ch);
 	}
 }
 
-- 
cgit 1.4.1


From d3db23bc59a6ea3cdf17e1f9ee239dde33154eaf Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Wed, 22 Aug 2018 22:08:22 -0400
Subject: Add ssh:// links to index.html

---
 index.html | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/index.html b/index.html
index a934f1c..b3d71a6 100644
--- a/index.html
+++ b/index.html
@@ -3,18 +3,12 @@
 <p>
 Hey there, friend. This should get you there.
 <p>
-<code>
-	ssh torus@ascii.town
-</code>
+<a href="ssh://torus@ascii.town"><code>ssh torus@ascii.town</code></a>
 <p>
 Or you can just come and chat.
 <p>
-<code>
-	ssh chat@ascii.town
-</code>
+<a href="ssh://chat@ascii.town"><code>ssh chat@ascii.town</code></a>
 <p>
 You can also take a detour and play NetHack.
 <p>
-<code>
-	ssh nethack@ascii.town
-</code>
+<a href="ssh://nethack@ascii.town"><code>ssh nethack@ascii.town</code></a>
-- 
cgit 1.4.1


From 48117dd2266757e4a79f09d5ca2bae9138a94ff2 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Thu, 23 Aug 2018 00:06:11 -0400
Subject: Support CP437 in merge

---
 merge.c | 68 ++++++++++++++++++++++++++++++++++++++---------------------------
 1 file changed, 40 insertions(+), 28 deletions(-)

diff --git a/merge.c b/merge.c
index e6d2fde..22b0eac 100644
--- a/merge.c
+++ b/merge.c
@@ -14,15 +14,24 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define _XOPEN_SOURCE_EXTENDED
+
 #include <curses.h>
 #include <err.h>
 #include <fcntl.h>
+#include <locale.h>
 #include <stdio.h>
 #include <sysexits.h>
+#include <wchar.h>
 
 #include "torus.h"
 
-static void colorPairs(void) {
+static void curse(void) {
+	setlocale(LC_CTYPE, "");
+
+	initscr();
+	start_color();
+
 	assume_default_colors(0, 0);
 	if (COLORS >= 16) {
 		for (short pair = 1; pair < 0x80; ++pair) {
@@ -33,22 +42,40 @@ static void colorPairs(void) {
 			init_pair(pair, pair & 007, (pair & 070) >> 3);
 		}
 	}
+
+	color_set(COLOR_WHITE, NULL);
+	mvhline(CELL_ROWS, 0, 0, CELL_COLS);
+	mvhline(CELL_ROWS * 2 + 1, 0, 0, CELL_COLS);
+	mvvline(0, CELL_COLS, 0, CELL_ROWS * 2 + 1);
+	mvaddch(CELL_ROWS, CELL_COLS, ACS_RTEE);
+	mvaddch(CELL_ROWS * 2 + 1, CELL_COLS, ACS_LRCORNER);
+	color_set(0, NULL);
+
+	cbreak();
+	noecho();
+	keypad(stdscr, true);
+	set_escdelay(100);
 }
 
-static chtype colorAttr(uint8_t color) {
-	if (COLORS >= 16) return COLOR_PAIR(color);
-	chtype bold = (color & COLOR_BRIGHT) ? A_BOLD : A_NORMAL;
-	short pair = (color & 0x70) >> 1 | (color & 0x07);
-	return bold | COLOR_PAIR(pair);
+static attr_t colorAttr(uint8_t color) {
+	if (COLORS >= 16) return A_NORMAL;
+	return (color & COLOR_BRIGHT) ? A_BOLD : A_NORMAL;
+}
+static short colorPair(uint8_t color) {
+	if (COLORS >= 16) return color;
+	return (color & 0x70) >> 1 | (color & 0x07);
 }
 
 static void drawTile(int offsetY, const struct Tile *tile) {
-	for (uint8_t y = 0; y < CELL_ROWS; ++y) {
-		for (uint8_t x = 0; x < CELL_COLS; ++x) {
-			uint8_t color = tile->colors[y][x];
-			char cell = tile->cells[y][x];
-
-			mvaddch(offsetY + y, x, colorAttr(color) | cell);
+	for (uint8_t cellY = 0; cellY < CELL_ROWS; ++cellY) {
+		for (uint8_t cellX = 0; cellX < CELL_COLS; ++cellX) {
+			uint8_t color = tile->colors[cellY][cellX];
+			uint8_t cell = tile->cells[cellY][cellX];
+
+			cchar_t cch;
+			wchar_t wch[] = { CP437[cell], L'\0' };
+			setcchar(&cch, wch, colorAttr(color), colorPair(color), NULL);
+			mvadd_wch(offsetY + cellY, cellX, &cch);
 		}
 	}
 }
@@ -65,22 +92,7 @@ int main(int argc, char *argv[]) {
 	FILE *fileC = fopen(argv[3], "w");
 	if (!fileC) err(EX_CANTCREAT, "%s", argv[3]);
 
-	initscr();
-	cbreak();
-	noecho();
-	keypad(stdscr, true);
-	set_escdelay(100);
-
-	start_color();
-	colorPairs();
-
-	attrset(colorAttr(COLOR_WHITE));
-	mvhline(CELL_ROWS, 0, 0, CELL_COLS);
-	mvhline(CELL_ROWS * 2 + 1, 0, 0, CELL_COLS);
-	mvvline(0, CELL_COLS, 0, CELL_ROWS * 2 + 1);
-	mvaddch(CELL_ROWS, CELL_COLS, ACS_RTEE);
-	mvaddch(CELL_ROWS * 2 + 1, CELL_COLS, ACS_LRCORNER);
-	attrset(A_NORMAL);
+	curse();
 
 	struct Tile tileA, tileB;
 	for (;;) {
-- 
cgit 1.4.1


From d47cceb793c5932ab752bf5db46be43729f99e8c Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Thu, 23 Aug 2018 12:43:26 -0400
Subject: Add C-l to redraw

---
 client.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/client.c b/client.c
index dec86a9..44eb904 100644
--- a/client.c
+++ b/client.c
@@ -271,6 +271,8 @@ static void inputKeyCode(wchar_t ch) {
 
 static void inputNormal(wchar_t ch) {
 	switch (ch) {
+		break; case CTRL('L'): clearok(curscr, true);
+
 		break; case ESC: input.mode = MODE_NORMAL; input.shift = 0;
 		break; case 'q': endwin(); exit(EX_OK);
 
-- 
cgit 1.4.1


From a2a08a114e6128c2adc10224ee7b95cc4bc1e3d1 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Thu, 23 Aug 2018 14:53:11 -0400
Subject: Implement I, directional insert

---
 client.c | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/client.c b/client.c
index 44eb904..e110686 100644
--- a/client.c
+++ b/client.c
@@ -195,6 +195,7 @@ static void clientMap(void) {
 static struct {
 	enum {
 		MODE_NORMAL,
+		MODE_DIRECTION,
 		MODE_INSERT,
 		MODE_REPLACE,
 		MODE_DRAW,
@@ -331,11 +332,26 @@ static void inputNormal(wchar_t ch) {
 
 		break; case 'i': insertMode(1, 0);
 		break; case 'a': clientMove(1, 0); insertMode(1, 0);
+		break; case 'I': input.mode = MODE_DIRECTION;
 		break; case 'r': input.mode = MODE_REPLACE;
 		break; case 'R': input.mode = MODE_DRAW; input.draw = 0;
 	}
 }
 
+static void inputDirection(wchar_t ch) {
+	switch (ch) {
+		break; case ESC: input.mode = MODE_NORMAL;
+		break; case 'h': insertMode(-1,  0);
+		break; case 'l': insertMode( 1,  0);
+		break; case 'k': insertMode( 0, -1);
+		break; case 'j': insertMode( 0,  1);
+		break; case 'y': insertMode(-1, -1);
+		break; case 'u': insertMode( 1, -1);
+		break; case 'b': insertMode(-1,  1);
+		break; case 'n': insertMode( 1,  1);
+	}
+}
+
 static void inputInsert(wchar_t ch) {
 	switch (ch) {
 		break; case ESC: {
@@ -391,10 +407,11 @@ static void readInput(void) {
 		return;
 	}
 	switch (input.mode) {
-		break; case MODE_NORMAL:  inputNormal(ch);
-		break; case MODE_INSERT:  inputInsert(ch);
-		break; case MODE_REPLACE: inputReplace(ch);
-		break; case MODE_DRAW:    inputDraw(ch);
+		break; case MODE_NORMAL:    inputNormal(ch);
+		break; case MODE_DIRECTION: inputDirection(ch);
+		break; case MODE_INSERT:    inputInsert(ch);
+		break; case MODE_REPLACE:   inputReplace(ch);
+		break; case MODE_DRAW:      inputDraw(ch);
 	}
 }
 
-- 
cgit 1.4.1


From f919c5170bfb43aff48050331fd3054c8de52fa9 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Thu, 23 Aug 2018 15:33:56 -0400
Subject: Add g for flip

---
 client.c |  6 ++++
 server.c | 96 +++++++++++++++++++++++++++++++++++++---------------------------
 torus.h  |  1 +
 3 files changed, 62 insertions(+), 41 deletions(-)

diff --git a/client.c b/client.c
index e110686..239ae8c 100644
--- a/client.c
+++ b/client.c
@@ -179,6 +179,11 @@ static void clientMove(int8_t dx, int8_t dy) {
 	clientMessage(msg);
 }
 
+static void clientFlip(void) {
+	struct ClientMessage msg = { .type = CLIENT_FLIP };
+	clientMessage(msg);
+}
+
 static void clientPut(uint8_t color, uint8_t cell) {
 	struct ClientMessage msg = {
 		.type = CLIENT_PUT,
@@ -277,6 +282,7 @@ static void inputNormal(wchar_t ch) {
 		break; case ESC: input.mode = MODE_NORMAL; input.shift = 0;
 		break; case 'q': endwin(); exit(EX_OK);
 
+		break; case 'g': clientFlip();
 		break; case 'h': clientMove(-1,  0);
 		break; case 'l': clientMove( 1,  0);
 		break; case 'k': clientMove( 0, -1);
diff --git a/server.c b/server.c
index 3dedad8..80c9e75 100644
--- a/server.c
+++ b/server.c
@@ -172,6 +172,51 @@ static bool clientCursors(const struct Client *client) {
 	return true;
 }
 
+static bool clientUpdate(struct Client *client, const struct Client *old) {
+	struct ServerMessage msg = {
+		.type = SERVER_MOVE,
+		.move = { .cellX = client->cellX, .cellY = client->cellY },
+	};
+	if (!clientSend(client, msg)) return false;
+
+	if (client->tileX != old->tileX || client->tileY != old->tileY) {
+		msg.type = SERVER_TILE;
+		if (!clientSend(client, msg)) return false;
+
+		if (!clientCursors(client)) return false;
+
+		msg = (struct ServerMessage) {
+			.type = SERVER_CURSOR,
+			.cursor = {
+				.oldCellX = old->cellX,  .oldCellY = old->cellY,
+				.newCellX = CURSOR_NONE, .newCellY = CURSOR_NONE,
+			},
+		};
+		clientCast(old, msg);
+
+		msg = (struct ServerMessage) {
+			.type = SERVER_CURSOR,
+			.cursor = {
+				.oldCellX = CURSOR_NONE,   .oldCellY = CURSOR_NONE,
+				.newCellX = client->cellX, .newCellY = client->cellY,
+			},
+		};
+		clientCast(client, msg);
+
+	} else {
+		msg = (struct ServerMessage) {
+			.type = SERVER_CURSOR,
+			.cursor = {
+				.oldCellX = old->cellX,    .oldCellY = old->cellY,
+				.newCellX = client->cellX, .newCellY = client->cellY,
+			},
+		};
+		clientCast(client, msg);
+	}
+
+	return true;
+}
+
 static bool clientMove(struct Client *client, int8_t dx, int8_t dy) {
 	struct Client old = *client;
 
@@ -210,48 +255,14 @@ static bool clientMove(struct Client *client, int8_t dx, int8_t dy) {
 	assert(client->tileX < TILE_COLS);
 	assert(client->tileY < TILE_ROWS);
 
-	struct ServerMessage msg = {
-		.type = SERVER_MOVE,
-		.move = { .cellX = client->cellX, .cellY = client->cellY },
-	};
-	if (!clientSend(client, msg)) return false;
-
-	if (client->tileX != old.tileX || client->tileY != old.tileY) {
-		msg.type = SERVER_TILE;
-		if (!clientSend(client, msg)) return false;
-
-		if (!clientCursors(client)) return false;
-
-		msg = (struct ServerMessage) {
-			.type = SERVER_CURSOR,
-			.cursor = {
-				.oldCellX = old.cellX,  .oldCellY = old.cellY,
-				.newCellX = CURSOR_NONE, .newCellY = CURSOR_NONE,
-			},
-		};
-		clientCast(&old, msg);
-
-		msg = (struct ServerMessage) {
-			.type = SERVER_CURSOR,
-			.cursor = {
-				.oldCellX = CURSOR_NONE,   .oldCellY = CURSOR_NONE,
-				.newCellX = client->cellX, .newCellY = client->cellY,
-			},
-		};
-		clientCast(client, msg);
-
-	} else {
-		msg = (struct ServerMessage) {
-			.type = SERVER_CURSOR,
-			.cursor = {
-				.oldCellX = old.cellX,    .oldCellY = old.cellY,
-				.newCellX = client->cellX, .newCellY = client->cellY,
-			},
-		};
-		clientCast(client, msg);
-	}
+	return clientUpdate(client, &old);
+}
 
-	return true;
+static bool clientFlip(struct Client *client) {
+	struct Client old = *client;
+	client->tileX = (client->tileX + TILE_COLS / 2) % TILE_COLS;
+	client->tileY = (client->tileY + TILE_ROWS / 2) % TILE_ROWS;
+	return clientUpdate(client, &old);
 }
 
 static bool clientPut(const struct Client *client, uint8_t color, uint8_t cell) {
@@ -371,6 +382,9 @@ int main() {
 			break; case CLIENT_MOVE: {
 				success = clientMove(client, msg.move.dx, msg.move.dy);
 			}
+			break; case CLIENT_FLIP: {
+				success = clientFlip(client);
+			}
 			break; case CLIENT_PUT: {
 				success = clientPut(client, msg.put.color, msg.put.cell);
 			}
diff --git a/torus.h b/torus.h
index 1bbb43a..194f227 100644
--- a/torus.h
+++ b/torus.h
@@ -140,6 +140,7 @@ static const uint8_t CURSOR_NONE = UINT8_MAX;
 struct ClientMessage {
 	enum {
 		CLIENT_MOVE,
+		CLIENT_FLIP,
 		CLIENT_PUT,
 		CLIENT_MAP,
 	} type;
-- 
cgit 1.4.1


From 7f2e7db45c4b2f7db9c84338fda83875a7855317 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Thu, 23 Aug 2018 15:52:58 -0400
Subject: Remove help

---
 .gitignore |   1 -
 Makefile   |   6 +-
 help.c     | 187 -------------------------------------------------------------
 3 files changed, 3 insertions(+), 191 deletions(-)
 delete mode 100644 help.c

diff --git a/.gitignore b/.gitignore
index 7f76e0f..230472d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
 *.o
 chroot.tar
 client
-help
 merge
 meta
 root
diff --git a/Makefile b/Makefile
index f2b2ca0..2b0ca87 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ CHROOT_GROUP = $(CHROOT_USER)
 
 CFLAGS += -Wall -Wextra -Wpedantic
 LDLIBS = -lm -lcursesw
-BINS = server client help meta merge
+BINS = server client meta merge
 OBJS = $(BINS:%=%.o)
 
 all: tags $(BINS)
@@ -13,7 +13,7 @@ $(OBJS): torus.h
 tags: *.h *.c
 	ctags -w *.h *.c
 
-chroot.tar: server client help
+chroot.tar: server client
 	mkdir -p root
 	install -d -o root -g wheel \
 	    root/bin \
@@ -35,7 +35,7 @@ chroot.tar: server client help
 	cp -a -f /usr/share/locale root/usr/share
 	cp -p -f /usr/share/misc/termcap.db root/usr/share/misc
 	cp -p -f /bin/sh root/bin
-	install -o root -g wheel -m 555 server client help root/bin
+	install -o root -g wheel -m 555 server client root/bin
 	tar -c -f chroot.tar -C root bin home lib libexec usr
 
 clean:
diff --git a/help.c b/help.c
deleted file mode 100644
index dd13c4f..0000000
--- a/help.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/* Copyright (C) 2017  June McEnroe <june@causal.agency>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <err.h>
-#include <stdint.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sysexits.h>
-#include <unistd.h>
-
-#include "torus.h"
-
-static int client;
-
-static void clientMessage(struct ClientMessage msg) {
-	ssize_t size = send(client, &msg, sizeof(msg), 0);
-	if (size < 0) err(EX_IOERR, "send");
-}
-
-static void clientMove(int8_t dx, int8_t dy) {
-	struct ClientMessage msg = {
-		.type = CLIENT_MOVE,
-		.move = { .dx = dx, .dy = dy },
-	};
-	clientMessage(msg);
-}
-
-static void clientPut(uint8_t color, char cell) {
-	struct ClientMessage msg = {
-		.type = CLIENT_PUT,
-		.put = { .color = color, .cell = cell },
-	};
-	clientMessage(msg);
-}
-
-static const useconds_t DELAY = 50000;
-
-enum {
-	K = COLOR_BLACK,
-	R = COLOR_RED,
-	G = COLOR_GREEN,
-	Y = COLOR_YELLOW,
-	B = COLOR_BLUE,
-	M = COLOR_MAGENTA,
-	C = COLOR_CYAN,
-	W = COLOR_WHITE,
-	I = COLOR_BRIGHT | COLOR_WHITE,
-};
-
-static void h(void) { clientMove(-1,  0); usleep(DELAY); }
-static void j(void) { clientMove( 0,  1); usleep(DELAY); }
-static void k(void) { clientMove( 0, -1); usleep(DELAY); }
-static void l(void) { clientMove( 1,  0); usleep(DELAY); }
-static void y(void) { clientMove(-1, -1); usleep(DELAY); }
-static void u(void) { clientMove( 1, -1); usleep(DELAY); }
-static void b(void) { clientMove(-1,  1); usleep(DELAY); }
-static void n(void) { clientMove( 1,  1); usleep(DELAY); }
-
-static void p(uint8_t color, char cell) {
-	clientPut(color, cell);
-	usleep(DELAY);
-}
-
-static uint8_t len;
-
-static void s(uint8_t color, const char *str) {
-	for (; *str; ++len, ++str) {
-		clientPut(color, *str);
-		clientMove(1, 0);
-		usleep(DELAY);
-	}
-}
-
-static void r(void) {
-	clientMove(-len, 1);
-	usleep(DELAY);
-	len = 0;
-}
-
-int main() {
-	client = socket(PF_LOCAL, SOCK_STREAM, 0);
-	if (client < 0) err(EX_OSERR, "socket");
-
-	struct sockaddr_un addr = {
-		.sun_family = AF_LOCAL,
-		.sun_path = "torus.sock",
-	};
-	int error = connect(client, (struct sockaddr *)&addr, sizeof(addr));
-	if (error) err(EX_NOINPUT, "torus.sock");
-
-	pid_t pid = fork();
-	if (pid < 0) err(EX_OSERR, "fork");
-
-	if (!pid) {
-		for (;;) {
-			char buf[4096];
-			ssize_t size = recv(client, buf, sizeof(buf), 0);
-			if (size < 0) err(EX_IOERR, "recv");
-			if (!size) return EX_OK;
-		}
-	}
-
-	clientMove(-CELL_INIT_X, -CELL_INIT_Y);
-	clientMove(28, 0);
-
-	for (;;) {
-		for (int i = 0; i < 10; ++i) {
-			clientMove(0, 1);
-			usleep(DELAY / 5);
-		}
-		for (int i = 0; i < 11; ++i) {
-			for (int j = 0; j < 28; ++j) {
-				clientPut(W, ' ');
-				if (i % 2) clientMove(1, 0);
-				else clientMove(-1, 0);
-				usleep(DELAY / 5);
-			}
-			clientPut(W, ' ');
-			if (i != 10) clientMove(0, -1);
-			usleep(DELAY / 5);
-		}
-
-		j(); l(); l();
-		s(W, "Welcome to "); s(I, "ascii.town"); s(W, "!"); r();
-		r(); r();
-
-		n(); n(); s(W, "o-"); s(I, "l");
-		h(); b(); p(W, '\\'); n(); p(I, 'n');
-		y(); h(); p(W, '|'); j(); p(I, 'j');
-		y(); p(W, '/'); b(); p(I, 'b');
-		k(); u(); p(W, '-'); h(); p(I, 'h');
-		u(); p(W, '\\'); y(); p(I, 'y');
-		n(); l(); p(W, '|'); k(); p(I, 'k');
-		n(); p(W, '/');  u(); p(I, 'u');
-
-		u(); s(W, "    "); len = 0;
-
-		s(I, "q "); s(W, "quit");    r();
-		s(I, "i "); s(W, "insert");  r();
-		s(I, "r "); s(W, "replace"); r();
-		s(I, "R "); s(W, "draw");    r();
-		s(I, "~ "); s(W, "color");   r();
-		s(I, "` "); s(W, "pipette"); r();
-		s(I, "* "); s(W, "bright");
-
-		s(W, "     "); len = 0;
-
-		clientPut(W, '7'); k();
-		clientPut(C, '6'); k();
-		clientPut(M, '5'); k();
-		clientPut(B, '4'); k();
-		clientPut(Y, '3'); k();
-		clientPut(G, '2'); k();
-		clientPut(R, '1'); k();
-		clientPut(K, '0');
-
-		l(); l();
-
-		clientPut(K << 4, ')'); j();
-		clientPut(R << 4, '!'); j();
-		clientPut(G << 4, '@'); j();
-		clientPut(Y << 4, '#'); j();
-		clientPut(B << 4, '$'); j();
-		clientPut(M << 4, '%'); j();
-		clientPut(C << 4, '^'); j();
-		clientPut(W << 4, '&'); j();
-
-		h(); k(); k(); k(); k(); k(); k(); k(); k(); k(); h();
-
-		sleep(30);
-
-		u(); l(); l(); l();
-	}
-}
-- 
cgit 1.4.1


From 6a53db288a4698cb51b540b062d63a297fb806e3 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Fri, 24 Aug 2018 18:14:34 -0400
Subject: Add client-side help page

---
 Makefile |   9 ++
 client.c | 112 ++++++++++++-------
 help.h   | 376 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 460 insertions(+), 37 deletions(-)
 create mode 100644 help.h

diff --git a/Makefile b/Makefile
index 2b0ca87..9d00e9e 100644
--- a/Makefile
+++ b/Makefile
@@ -10,6 +10,15 @@ all: tags $(BINS)
 
 $(OBJS): torus.h
 
+client.o: help.h
+
+help.h:
+	head -c 4096 torus.dat \
+		| file2c -s -x 'static const uint8_t HELP_DATA[] = {' '};' \
+		> help.h
+	echo 'static const struct Tile *HELP = (const struct Tile *)HELP_DATA;' \
+		>> help.h
+
 tags: *.h *.c
 	ctags -w *.h *.c
 
diff --git a/client.c b/client.c
index 239ae8c..ba94410 100644
--- a/client.c
+++ b/client.c
@@ -32,6 +32,7 @@
 #include <wchar.h>
 
 #include "torus.h"
+#include "help.h"
 
 #define err(...) do { endwin(); err(__VA_ARGS__); } while(0)
 #define errx(...) do { endwin(); errx(__VA_ARGS__); } while (0)
@@ -98,13 +99,11 @@ static short colorPair(uint8_t color) {
 	return (color & 0x70) >> 1 | (color & 0x07);
 }
 
-static uint8_t cellX;
-static uint8_t cellY;
-static struct Tile tile;
-
-static void tileDraw(uint8_t cellX, uint8_t cellY, attr_t attr) {
-	uint8_t color = tile.colors[cellY][cellX];
-	uint8_t cell = tile.cells[cellY][cellX];
+static void drawCell(
+	const struct Tile *tile, uint8_t cellX, uint8_t cellY, attr_t attr
+) {
+	uint8_t color = tile->colors[cellY][cellX];
+	uint8_t cell = tile->cells[cellY][cellX];
 
 	cchar_t cch;
 	wchar_t wch[] = { CP437[cell], L'\0' };
@@ -112,18 +111,25 @@ static void tileDraw(uint8_t cellX, uint8_t cellY, attr_t attr) {
 	mvadd_wch(cellY, cellX, &cch);
 }
 
+static void drawTile(const struct Tile *tile) {
+	for (uint8_t cellY = 0; cellY < CELL_ROWS; ++cellY) {
+		for (uint8_t cellX = 0; cellX < CELL_COLS; ++cellX) {
+			drawCell(tile, cellX, cellY, A_NORMAL);
+		}
+	}
+}
+
 static int client;
 
+static uint8_t cellX;
+static uint8_t cellY;
+static struct Tile tile;
+
 static void serverTile(void) {
 	ssize_t size = recv(client, &tile, sizeof(tile), 0);
 	if (size < 0) err(EX_IOERR, "recv");
 	if ((size_t)size < sizeof(tile)) errx(EX_PROTOCOL, "truncated tile");
-
-	for (uint8_t cellY = 0; cellY < CELL_ROWS; ++cellY) {
-		for (uint8_t cellX = 0; cellX < CELL_COLS; ++cellX) {
-			tileDraw(cellX, cellY, A_NORMAL);
-		}
-	}
+	drawTile(&tile);
 }
 
 static void serverMove(struct ServerMessage msg) {
@@ -134,15 +140,15 @@ static void serverMove(struct ServerMessage msg) {
 static void serverPut(struct ServerMessage msg) {
 	tile.colors[msg.put.cellY][msg.put.cellX] = msg.put.color;
 	tile.cells[msg.put.cellY][msg.put.cellX] = msg.put.cell;
-	tileDraw(msg.put.cellX, msg.put.cellY, A_NORMAL);
+	drawCell(&tile, msg.put.cellX, msg.put.cellY, A_NORMAL);
 }
 
 static void serverCursor(struct ServerMessage msg) {
 	if (msg.cursor.oldCellX != CURSOR_NONE) {
-		tileDraw(msg.cursor.oldCellX, msg.cursor.oldCellY, A_NORMAL);
+		drawCell(&tile, msg.cursor.oldCellX, msg.cursor.oldCellY, A_NORMAL);
 	}
 	if (msg.cursor.newCellX != CURSOR_NONE) {
-		tileDraw(msg.cursor.newCellX, msg.cursor.newCellY, A_REVERSE);
+		drawCell(&tile, msg.cursor.newCellX, msg.cursor.newCellY, A_REVERSE);
 	}
 }
 
@@ -200,6 +206,7 @@ static void clientMap(void) {
 static struct {
 	enum {
 		MODE_NORMAL,
+		MODE_HELP,
 		MODE_DIRECTION,
 		MODE_INSERT,
 		MODE_REPLACE,
@@ -218,7 +225,27 @@ static struct {
 	uint8_t len;
 } insert;
 
-static void insertMode(int8_t dx, int8_t dy) {
+static void modeHelp(void) {
+	input.mode = MODE_HELP;
+	drawTile(HELP);
+	curs_set(0);
+}
+static void modeNormal(void) {
+	input.mode = MODE_NORMAL;
+	move(cellY, cellX);
+	curs_set(1);
+}
+static void modeDraw(void) {
+	input.mode = MODE_DRAW;
+	input.draw = 0;
+}
+static void modeReplace(void) {
+	input.mode = MODE_REPLACE;
+}
+static void modeDirection(void) {
+	input.mode = MODE_DIRECTION;
+}
+static void modeInsert(int8_t dx, int8_t dy) {
 	input.mode = MODE_INSERT;
 	insert.dx = dx;
 	insert.dy = dy;
@@ -279,7 +306,7 @@ static void inputNormal(wchar_t ch) {
 	switch (ch) {
 		break; case CTRL('L'): clearok(curscr, true);
 
-		break; case ESC: input.mode = MODE_NORMAL; input.shift = 0;
+		break; case ESC: modeNormal(); input.shift = 0;
 		break; case 'q': endwin(); exit(EX_OK);
 
 		break; case 'g': clientFlip();
@@ -336,33 +363,40 @@ static void inputNormal(wchar_t ch) {
 			clientPut(tile.colors[cellY][cellX], tile.cells[cellY][cellX] - 1);
 		}
 
-		break; case 'i': insertMode(1, 0);
-		break; case 'a': clientMove(1, 0); insertMode(1, 0);
-		break; case 'I': input.mode = MODE_DIRECTION;
-		break; case 'r': input.mode = MODE_REPLACE;
-		break; case 'R': input.mode = MODE_DRAW; input.draw = 0;
+		break; case '?': modeHelp();
+		break; case 'R': modeDraw();
+		break; case 'r': modeReplace();
+		break; case 'I': modeDirection();
+		break; case 'i': modeInsert(1, 0);
+		break; case 'a': modeInsert(1, 0); clientMove(1, 0);
 	}
 }
 
+static void inputHelp(wchar_t ch) {
+	(void)ch;
+	if (tile.meta.createTime) drawTile(&tile);
+	modeNormal();
+}
+
 static void inputDirection(wchar_t ch) {
 	switch (ch) {
-		break; case ESC: input.mode = MODE_NORMAL;
-		break; case 'h': insertMode(-1,  0);
-		break; case 'l': insertMode( 1,  0);
-		break; case 'k': insertMode( 0, -1);
-		break; case 'j': insertMode( 0,  1);
-		break; case 'y': insertMode(-1, -1);
-		break; case 'u': insertMode( 1, -1);
-		break; case 'b': insertMode(-1,  1);
-		break; case 'n': insertMode( 1,  1);
+		break; case ESC: modeNormal();
+		break; case 'h': modeInsert(-1,  0);
+		break; case 'l': modeInsert( 1,  0);
+		break; case 'k': modeInsert( 0, -1);
+		break; case 'j': modeInsert( 0,  1);
+		break; case 'y': modeInsert(-1, -1);
+		break; case 'u': modeInsert( 1, -1);
+		break; case 'b': modeInsert(-1,  1);
+		break; case 'n': modeInsert( 1,  1);
 	}
 }
 
 static void inputInsert(wchar_t ch) {
 	switch (ch) {
 		break; case ESC: {
-			input.mode = MODE_NORMAL;
 			clientMove(-insert.dx, -insert.dy);
+			modeNormal();
 		}
 		break; case '\b': case DEL: {
 			clientMove(-insert.dx, -insert.dy);
@@ -390,12 +424,12 @@ static void inputReplace(wchar_t ch) {
 		if (!cell) return;
 		clientPut(tile.colors[cellY][cellX], cell);
 	}
-	input.mode = MODE_NORMAL;
+	modeNormal();
 }
 
 static void inputDraw(wchar_t ch) {
 	if (ch == ESC) {
-		input.mode = MODE_NORMAL;
+		modeNormal();
 		return;
 	}
 	if (input.draw) {
@@ -413,11 +447,12 @@ static void readInput(void) {
 		return;
 	}
 	switch (input.mode) {
+		break; case MODE_HELP:      inputHelp(ch);
 		break; case MODE_NORMAL:    inputNormal(ch);
+		break; case MODE_DRAW:      inputDraw(ch);
+		break; case MODE_REPLACE:   inputReplace(ch);
 		break; case MODE_DIRECTION: inputDirection(ch);
 		break; case MODE_INSERT:    inputInsert(ch);
-		break; case MODE_REPLACE:   inputReplace(ch);
-		break; case MODE_DRAW:      inputDraw(ch);
 	}
 }
 
@@ -434,6 +469,9 @@ int main() {
 
 	curse();
 
+	modeHelp();
+	readInput();
+
 	struct pollfd fds[2] = {
 		{ .fd = STDIN_FILENO, .events = POLLIN },
 		{ .fd = client, .events = POLLIN },
diff --git a/help.h b/help.h
new file mode 100644
index 0000000..39afce3
--- /dev/null
+++ b/help.h
@@ -0,0 +1,376 @@
+static const uint8_t HELP_DATA[] = {
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e,
+	0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f,
+	0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x2e, 0x2e, 0x2e, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0xa9, 0x60, 0x80, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xac, 0x75,
+	0x80, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x75, 0x80, 0x5b, 0x00,
+	0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00
+};
+static const struct Tile *HELP = (const struct Tile *)HELP_DATA;
-- 
cgit 1.4.1


From 925c039fe2e871681de3379d46233a185bc3822d Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Fri, 24 Aug 2018 20:43:09 -0400
Subject: Add copy and paste

---
 client.c | 89 +++++++++++++++++++++++++++++++++++++---------------------------
 1 file changed, 51 insertions(+), 38 deletions(-)

diff --git a/client.c b/client.c
index ba94410..989c231 100644
--- a/client.c
+++ b/client.c
@@ -219,6 +219,11 @@ static struct {
 	.color = COLOR_WHITE,
 };
 
+static struct {
+	uint8_t color;
+	uint8_t cell;
+} copy;
+
 static struct {
 	int8_t dx;
 	int8_t dy;
@@ -226,18 +231,18 @@ static struct {
 } insert;
 
 static void modeHelp(void) {
-	input.mode = MODE_HELP;
-	drawTile(HELP);
 	curs_set(0);
+	drawTile(HELP);
+	input.mode = MODE_HELP;
 }
 static void modeNormal(void) {
-	input.mode = MODE_NORMAL;
-	move(cellY, cellX);
 	curs_set(1);
+	move(cellY, cellX);
+	input.mode = MODE_NORMAL;
 }
 static void modeDraw(void) {
-	input.mode = MODE_DRAW;
 	input.draw = 0;
+	input.mode = MODE_DRAW;
 }
 static void modeReplace(void) {
 	input.mode = MODE_REPLACE;
@@ -246,25 +251,30 @@ static void modeDirection(void) {
 	input.mode = MODE_DIRECTION;
 }
 static void modeInsert(int8_t dx, int8_t dy) {
-	input.mode = MODE_INSERT;
 	insert.dx = dx;
 	insert.dy = dy;
 	insert.len = 0;
+	input.mode = MODE_INSERT;
 }
 
-static void inputFg(uint8_t fg) {
+static void colorFg(uint8_t fg) {
 	input.color = (input.color & 0x78) | (fg & 0x07);
 }
-static void inputBg(uint8_t bg) {
+static void colorBg(uint8_t bg) {
 	input.color = (input.color & 0x0F) | (bg & 0x07) << 4;
 }
-static void inputInvert(void) {
+static void colorInvert(void) {
 	input.color = (input.color & 0x08)
 		| (input.color & 0x70) >> 4
 		| (input.color & 0x07) << 4;
 }
 
-static void inputSwap(int8_t dx, int8_t dy) {
+static void cellCopy(void) {
+	copy.color = tile.colors[cellY][cellX];
+	copy.cell = tile.cells[cellY][cellX];
+}
+
+static void cellSwap(int8_t dx, int8_t dy) {
 	if ((uint8_t)(cellX + dx) >= CELL_COLS) return;
 	if ((uint8_t)(cellY + dy) >= CELL_ROWS) return;
 
@@ -319,38 +329,41 @@ static void inputNormal(wchar_t ch) {
 		break; case 'b': clientMove(-1,  1);
 		break; case 'n': clientMove( 1,  1);
 
-		break; case '0': inputFg(COLOR_BLACK);
-		break; case '1': inputFg(COLOR_RED);
-		break; case '2': inputFg(COLOR_GREEN);
-		break; case '3': inputFg(COLOR_YELLOW);
-		break; case '4': inputFg(COLOR_BLUE);
-		break; case '5': inputFg(COLOR_MAGENTA);
-		break; case '6': inputFg(COLOR_CYAN);
-		break; case '7': inputFg(COLOR_WHITE);
-
-		break; case ')': inputBg(COLOR_BLACK);
-		break; case '!': inputBg(COLOR_RED);
-		break; case '@': inputBg(COLOR_GREEN);
-		break; case '#': inputBg(COLOR_YELLOW);
-		break; case '$': inputBg(COLOR_BLUE);
-		break; case '%': inputBg(COLOR_MAGENTA);
-		break; case '^': inputBg(COLOR_CYAN);
-		break; case '&': inputBg(COLOR_WHITE);
+		break; case '0': colorFg(COLOR_BLACK);
+		break; case '1': colorFg(COLOR_RED);
+		break; case '2': colorFg(COLOR_GREEN);
+		break; case '3': colorFg(COLOR_YELLOW);
+		break; case '4': colorFg(COLOR_BLUE);
+		break; case '5': colorFg(COLOR_MAGENTA);
+		break; case '6': colorFg(COLOR_CYAN);
+		break; case '7': colorFg(COLOR_WHITE);
+
+		break; case ')': colorBg(COLOR_BLACK);
+		break; case '!': colorBg(COLOR_RED);
+		break; case '@': colorBg(COLOR_GREEN);
+		break; case '#': colorBg(COLOR_YELLOW);
+		break; case '$': colorBg(COLOR_BLUE);
+		break; case '%': colorBg(COLOR_MAGENTA);
+		break; case '^': colorBg(COLOR_CYAN);
+		break; case '&': colorBg(COLOR_WHITE);
 
 		break; case '8': case '*': input.color ^= COLOR_BRIGHT;
-		break; case '9': case '(': inputInvert();
+		break; case '9': case '(': colorInvert();
 		break; case '`': input.color = tile.colors[cellY][cellX];
 
-		break; case 'H': inputSwap(-1,  0);
-		break; case 'L': inputSwap( 1,  0);
-		break; case 'K': inputSwap( 0, -1);
-		break; case 'J': inputSwap( 0,  1);
-		break; case 'Y': inputSwap(-1, -1);
-		break; case 'U': inputSwap( 1, -1);
-		break; case 'B': inputSwap(-1,  1);
-		break; case 'N': inputSwap( 1,  1);
+		break; case 'H': cellSwap(-1,  0);
+		break; case 'L': cellSwap( 1,  0);
+		break; case 'K': cellSwap( 0, -1);
+		break; case 'J': cellSwap( 0,  1);
+		break; case 'Y': cellSwap(-1, -1);
+		break; case 'U': cellSwap( 1, -1);
+		break; case 'B': cellSwap(-1,  1);
+		break; case 'N': cellSwap( 1,  1);
+
+		break; case 's': cellCopy();
+		break; case 'p': clientPut(copy.color, copy.cell);
 
-		break; case 'x': clientPut(tile.colors[cellY][cellX], ' ');
+		break; case 'x': cellCopy(); clientPut(copy.color, ' ');
 		break; case '~': {
 			clientPut(input.color, tile.cells[cellY][cellX]);
 			clientMove(1, 0);
@@ -365,7 +378,7 @@ static void inputNormal(wchar_t ch) {
 
 		break; case '?': modeHelp();
 		break; case 'R': modeDraw();
-		break; case 'r': modeReplace();
+		break; case 'r': modeReplace(); cellCopy();
 		break; case 'I': modeDirection();
 		break; case 'i': modeInsert(1, 0);
 		break; case 'a': modeInsert(1, 0); clientMove(1, 0);
-- 
cgit 1.4.1


From 2b5634094a6754b69f8bd90e21addbcd874d599e Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Fri, 24 Aug 2018 22:08:18 -0400
Subject: Separate 8, 9 and *, (

---
 client.c | 30 +++++++++++++++++++++++-------
 1 file changed, 23 insertions(+), 7 deletions(-)

diff --git a/client.c b/client.c
index 989c231..a4742b2 100644
--- a/client.c
+++ b/client.c
@@ -263,10 +263,11 @@ static void colorFg(uint8_t fg) {
 static void colorBg(uint8_t bg) {
 	input.color = (input.color & 0x0F) | (bg & 0x07) << 4;
 }
-static void colorInvert(void) {
-	input.color = (input.color & 0x08)
-		| (input.color & 0x70) >> 4
-		| (input.color & 0x07) << 4;
+
+static uint8_t colorInvert(uint8_t color) {
+	return (color & 0x08)
+		| (color & 0x70) >> 4
+		| (color & 0x07) << 4;
 }
 
 static void cellCopy(void) {
@@ -347,8 +348,8 @@ static void inputNormal(wchar_t ch) {
 		break; case '^': colorBg(COLOR_CYAN);
 		break; case '&': colorBg(COLOR_WHITE);
 
-		break; case '8': case '*': input.color ^= COLOR_BRIGHT;
-		break; case '9': case '(': colorInvert();
+		break; case '8': input.color ^= COLOR_BRIGHT;
+		break; case '9': input.color = colorInvert(input.color);
 		break; case '`': input.color = tile.colors[cellY][cellX];
 
 		break; case 'H': cellSwap(-1,  0);
@@ -361,13 +362,28 @@ static void inputNormal(wchar_t ch) {
 		break; case 'N': cellSwap( 1,  1);
 
 		break; case 's': cellCopy();
+		break; case 'x': cellCopy(); clientPut(copy.color, ' ');
 		break; case 'p': clientPut(copy.color, copy.cell);
 
-		break; case 'x': cellCopy(); clientPut(copy.color, ' ');
 		break; case '~': {
+			cellCopy();
 			clientPut(input.color, tile.cells[cellY][cellX]);
 			clientMove(1, 0);
 		}
+		break; case '*': {
+			clientPut(
+				tile.colors[cellY][cellX] ^ COLOR_BRIGHT,
+				tile.cells[cellY][cellX]
+			);
+			clientMove(1, 0);
+		}
+		break; case '(': {
+			clientPut(
+				colorInvert(tile.colors[cellY][cellX]),
+				tile.cells[cellY][cellX]
+			);
+			clientMove(1, 0);
+		}
 
 		break; case CTRL('A'): {
 			clientPut(tile.colors[cellY][cellX], tile.cells[cellY][cellX] + 1);
-- 
cgit 1.4.1


From 601b8aab6399a060e220827b68fa05a12766a9c8 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Fri, 24 Aug 2018 22:10:59 -0400
Subject: Always interpret space as space

---
 client.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/client.c b/client.c
index a4742b2..6ba1cf6 100644
--- a/client.c
+++ b/client.c
@@ -291,6 +291,7 @@ static void cellSwap(int8_t dx, int8_t dy) {
 }
 
 static uint8_t inputCell(wchar_t ch) {
+	if (ch == ' ') return ' ';
 	if (ch < 0x80) return (uint8_t)ch + input.shift;
 	for (size_t i = 0; i < ARRAY_LEN(CP437); ++i) {
 		if (ch == CP437[i]) return i;
-- 
cgit 1.4.1


From 9b9201f5f2e9e57672112ad868233a9bc72c3cef Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Fri, 24 Aug 2018 22:31:14 -0400
Subject: Fill in the help page

Also display it before connecting to the socket.
---
 client.c |   9 +-
 help.h   | 290 +++++++++++++++++++++++++++++++--------------------------------
 2 files changed, 149 insertions(+), 150 deletions(-)

diff --git a/client.c b/client.c
index 6ba1cf6..bdc77b0 100644
--- a/client.c
+++ b/client.c
@@ -487,6 +487,10 @@ static void readInput(void) {
 }
 
 int main() {
+	curse();
+	modeHelp();
+	readInput();
+
 	client = socket(PF_LOCAL, SOCK_STREAM, 0);
 	if (client < 0) err(EX_OSERR, "socket");
 
@@ -497,11 +501,6 @@ int main() {
 	int error = connect(client, (struct sockaddr *)&addr, sizeof(addr));
 	if (error) err(EX_NOINPUT, "torus.sock");
 
-	curse();
-
-	modeHelp();
-	readInput();
-
 	struct pollfd fds[2] = {
 		{ .fd = STDIN_FILENO, .events = POLLIN },
 		{ .fd = client, .events = POLLIN },
diff --git a/help.h b/help.h
index 39afce3..fa9b7fb 100644
--- a/help.h
+++ b/help.h
@@ -6,6 +6,13 @@ static const uint8_t HELP_DATA[] = {
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d,
+	0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x73, 0x63, 0x69, 0x69, 0x2e,
+	0x74, 0x6f, 0x77, 0x6e, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x71, 0x20, 0x71, 0x75, 0x69, 0x74, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x20, 0x6d, 0x69, 0x6e,
+	0x69, 0x2d, 0x6d, 0x61, 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x3f, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
@@ -14,19 +21,63 @@ static const uint8_t HELP_DATA[] = {
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x6b, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, 0x34, 0x20, 0x35,
+	0x20, 0x36, 0x20, 0x37, 0x20, 0x20, 0x20, 0x20, 0x65, 0x73, 0x63,
+	0x20, 0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x20, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x20, 0x20, 0x69, 0x20, 0x69,
+	0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x79, 0x20, 0x18, 0x20, 0x75, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x61, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20,
+	0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x49, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x64, 0x69,
+	0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x20, 0x1b, 0xf9, 0x1a,
+	0x20, 0x6c, 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x21, 0x20, 0x40,
+	0x20, 0x23, 0x20, 0x24, 0x20, 0x25, 0x20, 0x5e, 0x20, 0x26, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x20, 0x72, 0x65, 0x70, 0x6c,
+	0x61, 0x63, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x52, 0x20, 0x64, 0x72, 0x61, 0x77, 0x20, 0x63,
+	0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62,
+	0x20, 0x19, 0x20, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x20, 0x63,
+	0x6f, 0x70, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x20, 0x70, 0x61, 0x73,
+	0x74, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x6a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x38, 0x20, 0x62, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x20, 0x39,
+	0x20, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x20, 0x20, 0x20,
+	0x78, 0x20, 0x65, 0x72, 0x61, 0x73, 0x65, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7e, 0x20,
+	0x70, 0x61, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x20, 0x70, 0x69,
+	0x70, 0x65, 0x74, 0x74, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x2a, 0x20, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x20,
+	0x62, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x28, 0x20, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e,
+	0x76, 0x65, 0x72, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x20, 0x74, 0x75,
+	0x6e, 0x6e, 0x65, 0x6c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x43, 0x2d, 0x61, 0x20, 0x69, 0x6e, 0x63,
+	0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x43, 0x2d, 0x78, 0x20, 0x64, 0x65, 0x63, 0x72, 0x65,
+	0x6d, 0x65, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x48, 0x4a, 0x4b, 0x4c, 0x59, 0x55, 0x42, 0x4e, 0x20, 0x6d, 0x6f,
+	0x76, 0x65, 0x20, 0x63, 0x65, 0x6c, 0x6c, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
@@ -35,16 +86,41 @@ static const uint8_t HELP_DATA[] = {
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x31, 0x20, 0x40, 0x41, 0x42,
+	0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
+	0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+	0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
+	0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+	0x6f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x32, 0x20,
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x46, 0x33, 0x20, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2,
+	0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd,
+	0xfe, 0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x46, 0x34, 0x20, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4,
+	0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
+	0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5,
+	0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x35, 0x20, 0x80, 0x81,
+	0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c,
+	0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2,
+	0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad,
+	0xae, 0xaf, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
@@ -61,10 +137,20 @@ static const uint8_t HELP_DATA[] = {
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73,
+	0x20, 0x41, 0x47, 0x50, 0x4c, 0x76, 0x33, 0x20, 0x46, 0x72, 0x65,
+	0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x21,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61,
+	0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x74,
+	0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63,
+	0x6f, 0x64, 0x65, 0x2e, 0x63, 0x61, 0x75, 0x73, 0x61, 0x6c, 0x2e,
+	0x61, 0x67, 0x65, 0x6e, 0x63, 0x79, 0x2f, 0x6a, 0x75, 0x6e, 0x65,
+	0x2f, 0x74, 0x6f, 0x72, 0x75, 0x73, 0x3e, 0x2e, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
@@ -73,6 +159,12 @@ static const uint8_t HELP_DATA[] = {
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x3f, 0x20, 0x74,
+	0x6f, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73,
+	0x20, 0x68, 0x65, 0x6c, 0x70, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e,
+	0x2e, 0x20, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79,
+	0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6e,
+	0x74, 0x69, 0x6e, 0x75, 0x65, 0x2e, 0x2e, 0x2e, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
@@ -81,98 +173,6 @@ static const uint8_t HELP_DATA[] = {
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e,
-	0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f,
-	0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x2e, 0x2e, 0x2e, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
@@ -182,7 +182,11 @@ static const uint8_t HELP_DATA[] = {
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x0c, 0x0e, 0x0a, 0x0b, 0x09, 0x0d, 0x0c, 0x0e, 0x0a, 0x0b, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
@@ -191,30 +195,63 @@ static const uint8_t HELP_DATA[] = {
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x0f, 0x01, 0x0f, 0x02,
+	0x0f, 0x03, 0x0f, 0x04, 0x0f, 0x05, 0x0f, 0x06, 0x0f, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f,
+	0x0f, 0x07, 0x07, 0x0f, 0x0f, 0x07, 0x0f, 0x07, 0x0f, 0x07, 0x0f,
+	0x07, 0x0f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x0f, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07,
+	0x0f, 0x08, 0x07, 0x10, 0x07, 0x20, 0x07, 0x30, 0x07, 0x40, 0x07,
+	0x50, 0x07, 0x60, 0x07, 0x70, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x0f, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f,
+	0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+	0x0f, 0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
@@ -224,24 +261,28 @@ static const uint8_t HELP_DATA[] = {
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
@@ -249,6 +290,7 @@ static const uint8_t HELP_DATA[] = {
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
@@ -270,6 +312,8 @@ static const uint8_t HELP_DATA[] = {
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
@@ -277,6 +321,11 @@ static const uint8_t HELP_DATA[] = {
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06,
+	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+	0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
@@ -285,6 +334,7 @@ static const uint8_t HELP_DATA[] = {
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
@@ -298,59 +348,9 @@ static const uint8_t HELP_DATA[] = {
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0xa9, 0x60, 0x80, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xac, 0x75,
-	0x80, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x75, 0x80, 0x5b, 0x00,
-	0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+	0x07, 0xa9, 0x60, 0x80, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x97, 0xbe,
+	0x80, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xbe, 0x80, 0x5b, 0x00,
+	0x00, 0x00, 0x00, 0x7d, 0x1d, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-- 
cgit 1.4.1


From 078357c101d044e8b06e94646663faa248fc4105 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sat, 25 Aug 2018 00:39:43 -0400
Subject: Calculate min and max meta for map server-side

---
 server.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 torus.h  |  2 ++
 2 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/server.c b/server.c
index 80c9e75..4c62b70 100644
--- a/server.c
+++ b/server.c
@@ -288,12 +288,61 @@ static bool clientMap(const struct Client *client) {
 	int32_t mapY = (int32_t)client->tileY - MAP_ROWS / 2;
 	int32_t mapX = (int32_t)client->tileX - MAP_COLS / 2;
 
-	struct Map map;
+	time_t now = time(NULL);
+	struct Map map = {
+		.min = {
+			.createTime = now,
+			.modifyTime = now,
+			.accessTime = now,
+			.modifyCount = UINT32_MAX,
+			.accessCount = UINT32_MAX,
+		},
+	};
+
 	for (int32_t y = 0; y < MAP_ROWS; ++y) {
 		for (int32_t x = 0; x < MAP_COLS; ++x) {
 			uint32_t tileY = ((mapY + y) % TILE_ROWS + TILE_ROWS) % TILE_ROWS;
 			uint32_t tileX = ((mapX + x) % TILE_COLS + TILE_COLS) % TILE_COLS;
-			map.meta[y][x] = tiles[tileY * TILE_ROWS + tileX].meta;
+			struct Meta meta = tiles[tileY * TILE_ROWS + tileX].meta;
+
+			if (meta.createTime) {
+				if (meta.createTime < map.min.createTime) {
+					map.min.createTime = meta.createTime;
+				}
+				if (meta.createTime > map.max.createTime) {
+					map.max.createTime = meta.createTime;
+				}
+			}
+			if (meta.modifyTime) {
+				if (meta.modifyTime < map.min.modifyTime) {
+					map.min.modifyTime = meta.modifyTime;
+				}
+				if (meta.modifyTime > map.max.modifyTime) {
+					map.max.modifyTime = meta.modifyTime;
+				}
+			}
+			if (meta.accessTime) {
+				if (meta.accessTime < map.min.accessTime) {
+					map.min.accessTime = meta.accessTime;
+				}
+				if (meta.accessTime > map.max.accessTime) {
+					map.max.accessTime = meta.accessTime;
+				}
+			}
+			if (meta.modifyCount < map.min.modifyCount) {
+				map.min.modifyCount = meta.modifyCount;
+			}
+			if (meta.modifyCount > map.max.modifyCount) {
+				map.max.modifyCount = meta.modifyCount;
+			}
+			if (meta.accessCount < map.min.accessCount) {
+				map.min.accessCount = meta.accessCount;
+			}
+			if (meta.accessCount > map.max.accessCount) {
+				map.max.accessCount = meta.accessCount;
+			}
+
+			map.meta[y][x] = meta;
 		}
 	}
 
diff --git a/torus.h b/torus.h
index 194f227..8194839 100644
--- a/torus.h
+++ b/torus.h
@@ -104,6 +104,8 @@ enum {
 };
 
 struct Map {
+	struct Meta min;
+	struct Meta max;
 	struct Meta meta[MAP_ROWS][MAP_COLS];
 };
 
-- 
cgit 1.4.1


From b9fc50f0259c22d9e4ef3f3e96b46d276b0c5d26 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sat, 25 Aug 2018 00:44:51 -0400
Subject: Send now in map

---
 server.c | 1 +
 torus.h  | 1 +
 2 files changed, 2 insertions(+)

diff --git a/server.c b/server.c
index 4c62b70..b8cf76a 100644
--- a/server.c
+++ b/server.c
@@ -290,6 +290,7 @@ static bool clientMap(const struct Client *client) {
 
 	time_t now = time(NULL);
 	struct Map map = {
+		.now = now,
 		.min = {
 			.createTime = now,
 			.modifyTime = now,
diff --git a/torus.h b/torus.h
index 8194839..d446488 100644
--- a/torus.h
+++ b/torus.h
@@ -104,6 +104,7 @@ enum {
 };
 
 struct Map {
+	time_t now;
 	struct Meta min;
 	struct Meta max;
 	struct Meta meta[MAP_ROWS][MAP_COLS];
-- 
cgit 1.4.1


From 205c1b9e71f806f46044048ee6f6fb84e45f8db2 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sat, 25 Aug 2018 01:03:20 -0400
Subject: Shrink map

---
 torus.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/torus.h b/torus.h
index d446488..3b4d133 100644
--- a/torus.h
+++ b/torus.h
@@ -99,8 +99,8 @@ static const uint32_t TILE_INIT_X = TILE_COLS / 2;
 static const uint32_t TILE_INIT_Y = TILE_ROWS / 2;
 
 enum {
-	MAP_ROWS = 11,
-	MAP_COLS = 11,
+	MAP_ROWS = 7,
+	MAP_COLS = 7,
 };
 
 struct Map {
-- 
cgit 1.4.1


From a97e140a8042a67e6651d8e09f181e1645626e83 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sat, 25 Aug 2018 01:11:34 -0400
Subject: Implement map rendering

---
 client.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 70 insertions(+), 1 deletion(-)

diff --git a/client.c b/client.c
index bdc77b0..bf545b8 100644
--- a/client.c
+++ b/client.c
@@ -37,6 +37,8 @@
 #define err(...) do { endwin(); err(__VA_ARGS__); } while(0)
 #define errx(...) do { endwin(); errx(__VA_ARGS__); } while (0)
 
+#define DIV_ROUND(a, b) (((a) + (b) / 2) / (b))
+
 #define CTRL(ch) ((ch) ^ 0x40)
 enum {
 	ESC = 0x1B,
@@ -152,7 +154,60 @@ static void serverCursor(struct ServerMessage msg) {
 	}
 }
 
+static const uint8_t MAP_X = (CELL_COLS / 2) - (3 * MAP_COLS / 2);
+static const uint8_t MAP_Y = (CELL_ROWS / 2) - (MAP_ROWS / 2);
+
+static const wchar_t MAP_CELLS[5] = L" ░▒▓█";
+static const uint8_t MAP_COLORS[] = {
+	COLOR_BLUE, COLOR_CYAN, COLOR_GREEN, COLOR_YELLOW, COLOR_RED,
+};
+
 static void serverMap(void) {
+	int t = MAP_Y - 1;
+	int l = MAP_X - 1;
+	int b = MAP_Y + MAP_ROWS;
+	int r = MAP_X + 3 * MAP_COLS;
+	color_set(colorPair(COLOR_WHITE), NULL);
+	mvhline(t, MAP_X, ACS_HLINE, 3 * MAP_COLS);
+	mvhline(b, MAP_X, ACS_HLINE, 3 * MAP_COLS);
+	mvvline(MAP_Y, l, ACS_VLINE, MAP_ROWS);
+	mvvline(MAP_Y, r, ACS_VLINE, MAP_ROWS);
+	mvaddch(t, l, ACS_ULCORNER);
+	mvaddch(t, r, ACS_URCORNER);
+	mvaddch(b, l, ACS_LLCORNER);
+	mvaddch(b, r, ACS_LRCORNER);
+	color_set(0, NULL);
+
+	struct Map map;
+	ssize_t size = recv(client, &map, sizeof(map), 0);
+	if (size < 0) err(EX_IOERR, "recv");
+	if ((size_t)size < sizeof(map)) errx(EX_PROTOCOL, "truncated map");
+
+	if (0 == map.max.modifyCount) return;
+	if (0 == map.now - map.min.createTime) return;
+
+	for (uint8_t y = 0; y < MAP_ROWS; ++y) {
+		for (uint8_t x = 0; x < MAP_COLS; ++x) {
+			struct Meta meta = map.meta[y][x];
+
+			uint32_t count = DIV_ROUND(
+				(ARRAY_LEN(MAP_CELLS) - 1) * meta.modifyCount,
+				map.max.modifyCount
+			);
+			uint32_t time = DIV_ROUND(
+				(ARRAY_LEN(MAP_COLORS) - 1) * (meta.modifyTime - map.min.createTime),
+				map.now - map.min.createTime
+			);
+			if (!meta.modifyTime) time = 0;
+
+			wchar_t cell = MAP_CELLS[count];
+			uint8_t color = MAP_COLORS[time];
+			wchar_t tile[] = { cell, cell, cell, L'\0' };
+			attr_set(colorAttr(color), colorPair(color), NULL);
+			mvaddwstr(MAP_Y + y, MAP_X + 3 * x, tile);
+		}
+	}
+	attr_set(A_NORMAL, 0, NULL);
 }
 
 static void readMessage(void) {
@@ -207,6 +262,7 @@ static struct {
 	enum {
 		MODE_NORMAL,
 		MODE_HELP,
+		MODE_MAP,
 		MODE_DIRECTION,
 		MODE_INSERT,
 		MODE_REPLACE,
@@ -235,6 +291,11 @@ static void modeHelp(void) {
 	drawTile(HELP);
 	input.mode = MODE_HELP;
 }
+static void modeMap(void) {
+	curs_set(0);
+	clientMap();
+	input.mode = MODE_MAP;
+}
 static void modeNormal(void) {
 	curs_set(1);
 	move(cellY, cellX);
@@ -394,6 +455,7 @@ static void inputNormal(wchar_t ch) {
 		}
 
 		break; case '?': modeHelp();
+		break; case 'm': modeMap();
 		break; case 'R': modeDraw();
 		break; case 'r': modeReplace(); cellCopy();
 		break; case 'I': modeDirection();
@@ -408,6 +470,12 @@ static void inputHelp(wchar_t ch) {
 	modeNormal();
 }
 
+static void inputMap(wchar_t ch) {
+	(void)ch;
+	drawTile(&tile);
+	modeNormal();
+}
+
 static void inputDirection(wchar_t ch) {
 	switch (ch) {
 		break; case ESC: modeNormal();
@@ -477,8 +545,9 @@ static void readInput(void) {
 		return;
 	}
 	switch (input.mode) {
-		break; case MODE_HELP:      inputHelp(ch);
 		break; case MODE_NORMAL:    inputNormal(ch);
+		break; case MODE_HELP:      inputHelp(ch);
+		break; case MODE_MAP:       inputMap(ch);
 		break; case MODE_DRAW:      inputDraw(ch);
 		break; case MODE_REPLACE:   inputReplace(ch);
 		break; case MODE_DIRECTION: inputDirection(ch);
-- 
cgit 1.4.1


From 8f73696d3577a2c65b040838597a1710097c5488 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sat, 25 Aug 2018 01:17:16 -0400
Subject: Explicitly build from .o objects

Otherwise BSD make just builds the binaries from the .c files directly,
ignoring the .h dependencies.
---
 Makefile | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Makefile b/Makefile
index 9d00e9e..50535fb 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,14 @@ OBJS = $(BINS:%=%.o)
 
 all: tags $(BINS)
 
+server: server.o
+
+client: client.o
+
+meta: meta.o
+
+merge: merge.o
+
 $(OBJS): torus.h
 
 client.o: help.h
-- 
cgit 1.4.1


From 4b579ff9d662462db6f70d375815009f48c47556 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sat, 25 Aug 2018 01:18:59 -0400
Subject: Remove -lm

Map rendering uses only integer arithmetic this time.
---
 Makefile | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Makefile b/Makefile
index 50535fb..f95420c 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ CHROOT_USER = torus
 CHROOT_GROUP = $(CHROOT_USER)
 
 CFLAGS += -Wall -Wextra -Wpedantic
-LDLIBS = -lm -lcursesw
+LDLIBS = -lcursesw
 BINS = server client meta merge
 OBJS = $(BINS:%=%.o)
 
@@ -44,7 +44,6 @@ chroot.tar: server client
 	cp -p -f /libexec/ld-elf.so.1 root/libexec
 	cp -p -f \
 	    /lib/libc.so.7 \
-		/lib/libm.so.5 \
 	    /lib/libedit.so.7 \
 	    /lib/libncurses.so.8 \
 	    /lib/libncursesw.so.8 \
-- 
cgit 1.4.1


From c8cc08015b721a622477e491e5b62df091ec5a63 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sat, 25 Aug 2018 01:24:18 -0400
Subject: Revert "Explicitly build from .o objects"

This reverts commit 8f73696d3577a2c65b040838597a1710097c5488.
---
 Makefile | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/Makefile b/Makefile
index f95420c..04fa105 100644
--- a/Makefile
+++ b/Makefile
@@ -8,14 +8,6 @@ OBJS = $(BINS:%=%.o)
 
 all: tags $(BINS)
 
-server: server.o
-
-client: client.o
-
-meta: meta.o
-
-merge: merge.o
-
 $(OBJS): torus.h
 
 client.o: help.h
-- 
cgit 1.4.1


From 6241cd6c503f6166f4ab365c000de194749a7101 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sat, 25 Aug 2018 01:24:50 -0400
Subject: Add .o rule for BSD make

---
 Makefile | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Makefile b/Makefile
index 04fa105..9199a7b 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,9 @@ OBJS = $(BINS:%=%.o)
 
 all: tags $(BINS)
 
+.o:
+	$(CC) $(LDFLAGS) $< $(LDLIBS) -o $@
+
 $(OBJS): torus.h
 
 client.o: help.h
-- 
cgit 1.4.1


From 96b969b937cf405275d834826951c7055e91bdf2 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sat, 25 Aug 2018 01:58:15 -0400
Subject: Fix key code handling

---
 client.c | 79 +++++++++++++++++++++++++++++++++++++---------------------------
 1 file changed, 46 insertions(+), 33 deletions(-)

diff --git a/client.c b/client.c
index bf545b8..1f75c5c 100644
--- a/client.c
+++ b/client.c
@@ -360,22 +360,23 @@ static uint8_t inputCell(wchar_t ch) {
 	return 0;
 }
 
-static void inputKeyCode(wchar_t ch) {
-	switch (ch) {
-		break; case KEY_LEFT:  clientMove(-1,  0);
-		break; case KEY_RIGHT: clientMove( 1,  0);
-		break; case KEY_UP:    clientMove( 0, -1);
-		break; case KEY_DOWN:  clientMove( 0,  1);
-
-		break; case KEY_F(1): input.shift = 0x00;
-		break; case KEY_F(2): input.shift = 0xC0;
-		break; case KEY_F(3): input.shift = 0xA0;
-		break; case KEY_F(4): input.shift = 0x70;
-		break; case KEY_F(5): input.shift = 0x40;
+static void inputNormal(bool keyCode, wchar_t ch) {
+	if (keyCode) {
+		switch (ch) {
+			break; case KEY_LEFT:  clientMove(-1,  0);
+			break; case KEY_RIGHT: clientMove( 1,  0);
+			break; case KEY_UP:    clientMove( 0, -1);
+			break; case KEY_DOWN:  clientMove( 0,  1);
+
+			break; case KEY_F(1): input.shift = 0x00;
+			break; case KEY_F(2): input.shift = 0xC0;
+			break; case KEY_F(3): input.shift = 0xA0;
+			break; case KEY_F(4): input.shift = 0x70;
+			break; case KEY_F(5): input.shift = 0x40;
+		}
+		return;
 	}
-}
 
-static void inputNormal(wchar_t ch) {
 	switch (ch) {
 		break; case CTRL('L'): clearok(curscr, true);
 
@@ -464,19 +465,22 @@ static void inputNormal(wchar_t ch) {
 	}
 }
 
-static void inputHelp(wchar_t ch) {
+static void inputHelp(bool keyCode, wchar_t ch) {
+	(void)keyCode;
 	(void)ch;
 	if (tile.meta.createTime) drawTile(&tile);
 	modeNormal();
 }
 
-static void inputMap(wchar_t ch) {
+static void inputMap(bool keyCode, wchar_t ch) {
+	(void)keyCode;
 	(void)ch;
 	drawTile(&tile);
 	modeNormal();
 }
 
-static void inputDirection(wchar_t ch) {
+static void inputDirection(bool keyCode, wchar_t ch) {
+	if (keyCode) return;
 	switch (ch) {
 		break; case ESC: modeNormal();
 		break; case 'h': modeInsert(-1,  0);
@@ -490,7 +494,11 @@ static void inputDirection(wchar_t ch) {
 	}
 }
 
-static void inputInsert(wchar_t ch) {
+static void inputInsert(bool keyCode, wchar_t ch) {
+	if (keyCode) {
+		inputNormal(keyCode, ch);
+		return;
+	}
 	switch (ch) {
 		break; case ESC: {
 			clientMove(-insert.dx, -insert.dy);
@@ -516,7 +524,11 @@ static void inputInsert(wchar_t ch) {
 	}
 }
 
-static void inputReplace(wchar_t ch) {
+static void inputReplace(bool keyCode, wchar_t ch) {
+	if (keyCode) {
+		inputNormal(keyCode, ch);
+		return;
+	}
 	if (ch != ESC) {
 		uint8_t cell = inputCell(ch);
 		if (!cell) return;
@@ -525,14 +537,18 @@ static void inputReplace(wchar_t ch) {
 	modeNormal();
 }
 
-static void inputDraw(wchar_t ch) {
-	if (ch == ESC) {
+static void inputDraw(bool keyCode, wchar_t ch) {
+	if (!keyCode && ch == ESC) {
 		modeNormal();
 		return;
 	}
 	if (input.draw) {
-		inputNormal(ch);
+		inputNormal(keyCode, ch);
 	} else {
+		if (keyCode) {
+			inputNormal(keyCode, ch);
+			return;
+		}
 		input.draw = inputCell(ch);
 	}
 	clientPut(input.color, input.draw);
@@ -540,18 +556,15 @@ static void inputDraw(wchar_t ch) {
 
 static void readInput(void) {
 	wint_t ch;
-	if (KEY_CODE_YES == get_wch(&ch)) {
-		inputKeyCode(ch);
-		return;
-	}
+	bool keyCode = (KEY_CODE_YES == get_wch(&ch));
 	switch (input.mode) {
-		break; case MODE_NORMAL:    inputNormal(ch);
-		break; case MODE_HELP:      inputHelp(ch);
-		break; case MODE_MAP:       inputMap(ch);
-		break; case MODE_DRAW:      inputDraw(ch);
-		break; case MODE_REPLACE:   inputReplace(ch);
-		break; case MODE_DIRECTION: inputDirection(ch);
-		break; case MODE_INSERT:    inputInsert(ch);
+		break; case MODE_NORMAL:    inputNormal(keyCode, ch);
+		break; case MODE_HELP:      inputHelp(keyCode, ch);
+		break; case MODE_MAP:       inputMap(keyCode, ch);
+		break; case MODE_DRAW:      inputDraw(keyCode, ch);
+		break; case MODE_REPLACE:   inputReplace(keyCode, ch);
+		break; case MODE_DIRECTION: inputDirection(keyCode, ch);
+		break; case MODE_INSERT:    inputInsert(keyCode, ch);
 	}
 }
 
-- 
cgit 1.4.1


From 6638f33f5548bca6adb6d0e72da7e767765b2d25 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sat, 25 Aug 2018 14:17:30 -0400
Subject: Add line mode

---
 client.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 103 insertions(+), 16 deletions(-)

diff --git a/client.c b/client.c
index 1f75c5c..411ef81 100644
--- a/client.c
+++ b/client.c
@@ -267,6 +267,7 @@ static struct {
 		MODE_INSERT,
 		MODE_REPLACE,
 		MODE_DRAW,
+		MODE_LINE,
 	} mode;
 	uint8_t color;
 	uint8_t shift;
@@ -286,6 +287,11 @@ static struct {
 	uint8_t len;
 } insert;
 
+static void modeNormal(void) {
+	curs_set(1);
+	move(cellY, cellX);
+	input.mode = MODE_NORMAL;
+}
 static void modeHelp(void) {
 	curs_set(0);
 	drawTile(HELP);
@@ -296,18 +302,6 @@ static void modeMap(void) {
 	clientMap();
 	input.mode = MODE_MAP;
 }
-static void modeNormal(void) {
-	curs_set(1);
-	move(cellY, cellX);
-	input.mode = MODE_NORMAL;
-}
-static void modeDraw(void) {
-	input.draw = 0;
-	input.mode = MODE_DRAW;
-}
-static void modeReplace(void) {
-	input.mode = MODE_REPLACE;
-}
 static void modeDirection(void) {
 	input.mode = MODE_DIRECTION;
 }
@@ -317,6 +311,16 @@ static void modeInsert(int8_t dx, int8_t dy) {
 	insert.len = 0;
 	input.mode = MODE_INSERT;
 }
+static void modeReplace(void) {
+	input.mode = MODE_REPLACE;
+}
+static void modeDraw(void) {
+	input.draw = 0;
+	input.mode = MODE_DRAW;
+}
+static void modeLine(void) {
+	input.mode = MODE_LINE;
+}
 
 static void colorFg(uint8_t fg) {
 	input.color = (input.color & 0x78) | (fg & 0x07);
@@ -457,11 +461,12 @@ static void inputNormal(bool keyCode, wchar_t ch) {
 
 		break; case '?': modeHelp();
 		break; case 'm': modeMap();
-		break; case 'R': modeDraw();
-		break; case 'r': modeReplace(); cellCopy();
 		break; case 'I': modeDirection();
 		break; case 'i': modeInsert(1, 0);
 		break; case 'a': modeInsert(1, 0); clientMove(1, 0);
+		break; case 'r': modeReplace(); cellCopy();
+		break; case 'R': modeDraw();
+		break; case '.': modeLine();
 	}
 }
 
@@ -554,6 +559,87 @@ static void inputDraw(bool keyCode, wchar_t ch) {
 	clientPut(input.color, input.draw);
 }
 
+static uint8_t lineCell(uint8_t cell, int8_t dx, int8_t dy) {
+	if (dx < 0) {
+		switch (CP437[cell]) {
+			default:   return inputCell(L'→');
+			case L'←': return inputCell(L'─'); case L'─': return 0;
+			case L'↑': return inputCell(L'┐'); case L'┐': return 0;
+			case L'↓': return inputCell(L'┘'); case L'┘': return 0;
+			case L'│': return inputCell(L'┤'); case L'┤': return 0;
+			case L'└': return inputCell(L'┴'); case L'┴': return 0;
+			case L'┌': return inputCell(L'┬'); case L'┬': return 0;
+			case L'├': return inputCell(L'┼'); case L'┼': return 0;
+		}
+	} else if (dx > 0) {
+		switch (CP437[cell]) {
+			default:   return inputCell(L'←');
+			case L'→': return inputCell(L'─'); case L'─': return 0;
+			case L'↑': return inputCell(L'┌'); case L'┌': return 0;
+			case L'↓': return inputCell(L'└'); case L'└': return 0;
+			case L'│': return inputCell(L'├'); case L'├': return 0;
+			case L'┘': return inputCell(L'┴'); case L'┴': return 0;
+			case L'┐': return inputCell(L'┬'); case L'┬': return 0;
+			case L'┤': return inputCell(L'┼'); case L'┼': return 0;
+		}
+	} else if (dy < 0) {
+		switch (CP437[cell]) {
+			default:   return inputCell(L'↓');
+			case L'↑': return inputCell(L'│'); case L'│': return 0;
+			case L'←': return inputCell(L'└'); case L'└': return 0;
+			case L'→': return inputCell(L'┘'); case L'┘': return 0;
+			case L'─': return inputCell(L'┴'); case L'┴': return 0;
+			case L'┌': return inputCell(L'├'); case L'├': return 0;
+			case L'┐': return inputCell(L'┤'); case L'┤': return 0;
+			case L'┬': return inputCell(L'┼'); case L'┼': return 0;
+		}
+	} else if (dy > 0) {
+		switch (CP437[cell]) {
+			default:   return inputCell(L'↑');
+			case L'↓': return inputCell(L'│'); case L'│': return 0;
+			case L'←': return inputCell(L'┌'); case L'┌': return 0;
+			case L'→': return inputCell(L'┐'); case L'┐': return 0;
+			case L'─': return inputCell(L'┬'); case L'┬': return 0;
+			case L'└': return inputCell(L'├'); case L'├': return 0;
+			case L'┘': return inputCell(L'┤'); case L'┤': return 0;
+			case L'┴': return inputCell(L'┼'); case L'┼': return 0;
+		}
+	}
+	return 0;
+}
+
+static void inputLine(bool keyCode, wchar_t ch) {
+	int8_t dx = 0;
+	int8_t dy = 0;
+	if (keyCode) {
+		switch (ch) {
+			break; case KEY_LEFT:  dx = -1;
+			break; case KEY_RIGHT: dx =  1;
+			break; case KEY_UP:    dy = -1;
+			break; case KEY_DOWN:  dy =  1;
+			break; default: return;
+		}
+	} else {
+		switch (ch) {
+			break; case ESC: modeNormal(); return;
+			break; case 'h': dx = -1;
+			break; case 'l': dx =  1;
+			break; case 'k': dy = -1;
+			break; case 'j': dy =  1;
+			break; default: return;
+		}
+	}
+	if ((uint8_t)(cellX + dx) >= CELL_COLS) return;
+	if ((uint8_t)(cellY + dy) >= CELL_ROWS) return;
+
+	uint8_t leave = lineCell(tile.cells[cellY][cellX], dx, dy);
+	uint8_t enter = lineCell(tile.cells[cellY + dy][cellX + dx], -dx, -dy);
+
+	if (leave) clientPut(input.color, leave);
+	clientMove(dx, dy);
+	if (enter) clientPut(input.color, enter);
+}
+
 static void readInput(void) {
 	wint_t ch;
 	bool keyCode = (KEY_CODE_YES == get_wch(&ch));
@@ -561,10 +647,11 @@ static void readInput(void) {
 		break; case MODE_NORMAL:    inputNormal(keyCode, ch);
 		break; case MODE_HELP:      inputHelp(keyCode, ch);
 		break; case MODE_MAP:       inputMap(keyCode, ch);
-		break; case MODE_DRAW:      inputDraw(keyCode, ch);
-		break; case MODE_REPLACE:   inputReplace(keyCode, ch);
 		break; case MODE_DIRECTION: inputDirection(keyCode, ch);
 		break; case MODE_INSERT:    inputInsert(keyCode, ch);
+		break; case MODE_REPLACE:   inputReplace(keyCode, ch);
+		break; case MODE_DRAW:      inputDraw(keyCode, ch);
+		break; case MODE_LINE:      inputLine(keyCode, ch);
 	}
 }
 
-- 
cgit 1.4.1


From 785b737910362f4362a35dbc3963c188cf295fbc Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sun, 26 Aug 2018 17:59:39 -0400
Subject: Leave line mode with .

---
 client.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/client.c b/client.c
index 411ef81..29e1cce 100644
--- a/client.c
+++ b/client.c
@@ -621,7 +621,7 @@ static void inputLine(bool keyCode, wchar_t ch) {
 		}
 	} else {
 		switch (ch) {
-			break; case ESC: modeNormal(); return;
+			break; case ESC: case '.': modeNormal(); return;
 			break; case 'h': dx = -1;
 			break; case 'l': dx =  1;
 			break; case 'k': dy = -1;
-- 
cgit 1.4.1


From dfb62452beecb4cf11a977e18f22c1c20a320b7f Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sun, 26 Aug 2018 22:26:04 -0400
Subject: Update help page

---
 help.h | 238 ++++++++++++++++++++++++++++++++---------------------------------
 1 file changed, 119 insertions(+), 119 deletions(-)

diff --git a/help.h b/help.h
index fa9b7fb..8c092ed 100644
--- a/help.h
+++ b/help.h
@@ -1,106 +1,106 @@
 static const uint8_t HELP_DATA[] = {
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d,
+	0xda, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xbf, 0xb3, 0x20, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d,
 	0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x73, 0x63, 0x69, 0x69, 0x2e,
 	0x74, 0x6f, 0x77, 0x6e, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x71, 0x20, 0x71, 0x75, 0x69, 0x74, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x20, 0x6d, 0x69, 0x6e,
 	0x69, 0x2d, 0x6d, 0x61, 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x3f, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20,
 	0x20, 0x20, 0x20, 0x6b, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, 0x34, 0x20, 0x35,
 	0x20, 0x36, 0x20, 0x37, 0x20, 0x20, 0x20, 0x20, 0x65, 0x73, 0x63,
 	0x20, 0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-	0x20, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x20, 0x20, 0x69, 0x20, 0x69,
-	0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x20,
+	0x20, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
+	0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x79, 0x20, 0x18, 0x20, 0x75, 0x20, 0x20,
+	0xb3, 0xb3, 0x20, 0x20, 0x79, 0x20, 0x18, 0x20, 0x75, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x61, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20,
-	0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x49, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x64, 0x69,
-	0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x20, 0x1b, 0xf9, 0x1a,
+	0x20, 0x20, 0x69, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20,
+	0x6d, 0x6f, 0x64, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x70, 0x20, 0x70, 0x61, 0x73, 0x74, 0x65, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20, 0x68, 0x20, 0x1b, 0xf9, 0x1a,
 	0x20, 0x6c, 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x21, 0x20, 0x40,
 	0x20, 0x23, 0x20, 0x24, 0x20, 0x25, 0x20, 0x5e, 0x20, 0x26, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x20, 0x72, 0x65, 0x70, 0x6c,
-	0x61, 0x63, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x52, 0x20, 0x64, 0x72, 0x61, 0x77, 0x20, 0x63,
-	0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x20, 0x69, 0x6e, 0x73, 0x65,
+	0x72, 0x74, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x7e, 0x20, 0x70, 0x61, 0x69, 0x6e,
+	0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20, 0x20, 0x62,
 	0x20, 0x19, 0x20, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x20, 0x63,
-	0x6f, 0x70, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x20, 0x70, 0x61, 0x73,
-	0x74, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x49, 0x20, 0x69,
+	0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63,
+	0x74, 0x69, 0x6f, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x70,
+	0x61, 0x69, 0x6e, 0x74, 0x20, 0x62, 0x72, 0x69, 0x67, 0x68, 0x74,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3,
 	0x20, 0x20, 0x20, 0x20, 0x6a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x38, 0x20, 0x62, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x20, 0x39,
 	0x20, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x20, 0x20, 0x20,
-	0x78, 0x20, 0x65, 0x72, 0x61, 0x73, 0x65, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7e, 0x20,
-	0x70, 0x61, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x52, 0x20, 0x64, 0x72, 0x61, 0x77, 0x20, 0x6d, 0x6f, 0x64, 0x65,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x28, 0x20, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x76,
+	0x65, 0x72, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0xb3, 0xb3, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x20, 0x70, 0x69,
 	0x70, 0x65, 0x74, 0x74, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x2a, 0x20, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x20,
-	0x62, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x28, 0x20, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e,
-	0x76, 0x65, 0x72, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x20, 0x74, 0x75,
+	0x20, 0x20, 0x20, 0x2e, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x6d,
+	0x6f, 0x64, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x43, 0x2d, 0x61, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d,
+	0x65, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20, 0x67, 0x20, 0x74, 0x75,
 	0x6e, 0x6e, 0x65, 0x6c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x43, 0x2d, 0x61, 0x20, 0x69, 0x6e, 0x63,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x20, 0x72, 0x65, 0x70,
+	0x6c, 0x61, 0x63, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x43, 0x2d, 0x78, 0x20, 0x64, 0x65, 0x63,
 	0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x43, 0x2d, 0x78, 0x20, 0x64, 0x65, 0x63, 0x72, 0x65,
-	0x6d, 0x65, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x48, 0x4a, 0x4b, 0x4c, 0x59, 0x55, 0x42, 0x4e, 0x20, 0x6d, 0x6f,
-	0x76, 0x65, 0x20, 0x63, 0x65, 0x6c, 0x6c, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x20,
+	0x65, 0x72, 0x61, 0x73, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x48, 0x4a, 0x4b, 0x4c, 0x59, 0x55, 0x42, 0x4e, 0x20,
+	0x73, 0x77, 0x61, 0x70, 0x20, 0x63, 0x65, 0x6c, 0x6c, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb3,
+	0xb3, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x31, 0x20, 0x40, 0x41, 0x42,
+	0x20, 0x20, 0xb3, 0xb3, 0x20, 0x46, 0x31, 0x20, 0x40, 0x41, 0x42,
 	0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
 	0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
 	0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
 	0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
 	0x6f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x32, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20, 0x46, 0x32, 0x20,
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20,
 	0x46, 0x33, 0x20, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
 	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2,
 	0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd,
@@ -108,26 +108,28 @@ static const uint8_t HELP_DATA[] = {
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x46, 0x34, 0x20, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4,
+	0xb3, 0xb3, 0x20, 0x46, 0x34, 0x20, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4,
 	0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
 	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
 	0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5,
 	0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x35, 0x20, 0x80, 0x81,
+	0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20, 0x46, 0x35, 0x20, 0x80, 0x81,
 	0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c,
 	0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
 	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2,
 	0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad,
 	0xae, 0xaf, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
@@ -135,52 +137,50 @@ static const uint8_t HELP_DATA[] = {
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73,
+	0x20, 0xb3, 0xb3, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73,
 	0x20, 0x41, 0x47, 0x50, 0x4c, 0x76, 0x33, 0x20, 0x46, 0x72, 0x65,
 	0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x21,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20, 0x54, 0x68, 0x65, 0x20,
 	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61,
 	0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x74,
 	0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63,
 	0x6f, 0x64, 0x65, 0x2e, 0x63, 0x61, 0x75, 0x73, 0x61, 0x6c, 0x2e,
 	0x61, 0x67, 0x65, 0x6e, 0x63, 0x79, 0x2f, 0x6a, 0x75, 0x6e, 0x65,
 	0x2f, 0x74, 0x6f, 0x72, 0x75, 0x73, 0x3e, 0x2e, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb3, 0xb3, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x3f, 0x20, 0x74,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb3,
+	0xb3, 0x20, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x3f, 0x20, 0x74,
 	0x6f, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73,
 	0x20, 0x68, 0x65, 0x6c, 0x70, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e,
 	0x2e, 0x20, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79,
 	0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6e,
 	0x74, 0x69, 0x6e, 0x75, 0x65, 0x2e, 0x2e, 0x2e, 0x20, 0x20, 0x20,
 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x20, 0x20, 0xb3, 0xc0, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+	0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xd9, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x0c, 0x0e, 0x0a, 0x0b, 0x09, 0x0d, 0x0c, 0x0e, 0x0a, 0x0b, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f,
@@ -188,79 +188,79 @@ static const uint8_t HELP_DATA[] = {
 	0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07,
+	0x07, 0x07, 0x07, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x0f, 0x01, 0x0f, 0x02,
 	0x0f, 0x03, 0x0f, 0x04, 0x0f, 0x05, 0x0f, 0x06, 0x0f, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x07, 0x07, 0x0f,
 	0x0f, 0x07, 0x07, 0x0f, 0x0f, 0x07, 0x0f, 0x07, 0x0f, 0x07, 0x0f,
 	0x07, 0x0f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03,
 	0x07, 0x0f, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07,
 	0x0f, 0x08, 0x07, 0x10, 0x07, 0x20, 0x07, 0x30, 0x07, 0x40, 0x07,
 	0x50, 0x07, 0x60, 0x07, 0x70, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x0f, 0x07,
+	0x07, 0x03, 0x03, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x0f, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f,
+	0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x0f,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03,
+	0x03, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f,
 	0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
-	0x0f, 0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f,
+	0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x07,
 	0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
@@ -268,28 +268,28 @@ static const uint8_t HELP_DATA[] = {
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x03, 0x03, 0x07, 0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x03, 0x03, 0x07, 0x0f, 0x0f, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x07, 0x0f, 0x0f,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03,
 	0x07, 0x0f, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
@@ -297,60 +297,60 @@ static const uint8_t HELP_DATA[] = {
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03, 0x03,
 	0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03,
+	0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06,
 	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
 	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
 	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
 	0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
 	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
-	0x07, 0xa9, 0x60, 0x80, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x97, 0xbe,
-	0x80, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xbe, 0x80, 0x5b, 0x00,
-	0x00, 0x00, 0x00, 0x7d, 0x1d, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0xa9, 0x60, 0x80, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x60,
+	0x83, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x99, 0x60, 0x83, 0x5b, 0x00,
+	0x00, 0x00, 0x00, 0xb4, 0x2e, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-- 
cgit 1.4.1


From c5ee7405e650fd184deaf55794b4843f733c3986 Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Sun, 26 Aug 2018 23:14:48 -0400
Subject: Map modifyCount on log scale

---
 client.c | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/client.c b/client.c
index 29e1cce..1478ebe 100644
--- a/client.c
+++ b/client.c
@@ -16,6 +16,7 @@
 
 #define _XOPEN_SOURCE_EXTENDED
 
+#include <assert.h>
 #include <curses.h>
 #include <err.h>
 #include <errno.h>
@@ -45,6 +46,11 @@ enum {
 	DEL = 0x7F,
 };
 
+static uint32_t log2(uint32_t n) {
+	assert(n > 0);
+	return 32 - __builtin_clz(n) - 1;
+}
+
 static void curse(void) {
 	setlocale(LC_CTYPE, "");
 
@@ -190,15 +196,21 @@ static void serverMap(void) {
 		for (uint8_t x = 0; x < MAP_COLS; ++x) {
 			struct Meta meta = map.meta[y][x];
 
-			uint32_t count = DIV_ROUND(
-				(ARRAY_LEN(MAP_CELLS) - 1) * meta.modifyCount,
-				map.max.modifyCount
-			);
-			uint32_t time = DIV_ROUND(
-				(ARRAY_LEN(MAP_COLORS) - 1) * (meta.modifyTime - map.min.createTime),
-				map.now - map.min.createTime
-			);
-			if (!meta.modifyTime) time = 0;
+			uint32_t count = 0;
+			if (meta.modifyCount && log2(map.max.modifyCount)) {
+				count = DIV_ROUND(
+					(ARRAY_LEN(MAP_CELLS) - 1) * log2(meta.modifyCount),
+					log2(map.max.modifyCount)
+				);
+			}
+			uint32_t time = 0;
+			if (meta.modifyTime) {
+				uint32_t modify = meta.modifyTime - map.min.createTime;
+				time = DIV_ROUND(
+					(ARRAY_LEN(MAP_COLORS) - 1) * modify,
+					map.now - map.min.createTime
+				);
+			}
 
 			wchar_t cell = MAP_CELLS[count];
 			uint8_t color = MAP_COLORS[time];
-- 
cgit 1.4.1


From 9d60fb33e9163f052ce942d3991bdbb9475de61b Mon Sep 17 00:00:00 2001
From: June McEnroe <june@causal.agency>
Date: Mon, 27 Aug 2018 13:01:28 -0400
Subject: Dump HELP_DATA with client -h

---
 client.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/client.c b/client.c
index 1478ebe..66e6d08 100644
--- a/client.c
+++ b/client.c
@@ -667,7 +667,17 @@ static void readInput(void) {
 	}
 }
 
-int main() {
+int main(int argc, char *argv[]) {
+	int opt;
+	while (0 < (opt = getopt(argc, argv, "h"))) {
+		if (opt == 'h') {
+			fwrite(HELP_DATA, sizeof(HELP_DATA), 1, stdout);
+			return EX_OK;
+		} else {
+			return EX_USAGE;
+		}
+	}
+
 	curse();
 	modeHelp();
 	readInput();
-- 
cgit 1.4.1