From 64ff36f4b12148a1f2db46f5856845f17a0b4433 Mon Sep 17 00:00:00 2001
From: Curtis 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.
---
 Makefile | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

(limited to 'Makefile')

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 0c8fb2b9fe779bdb7ba85c8458d4efc0034fb77b Mon Sep 17 00:00:00 2001
From: Curtis 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(-)

(limited to 'Makefile')

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 37cc7ce..ad0bc8e 100644
--- a/client.c
+++ b/client.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2017  Curtis McEnroe <june@causal.agency>
+/* Copyright (C) 2018  Curtis 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 e565816..9486fcd 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 775670c..53e469c 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 79386ebf961be25496a9a525b4007ae9cd370192 Mon Sep 17 00:00:00 2001
From: Curtis 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

(limited to 'Makefile')

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 f338771..0000000
--- a/help.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/* Copyright (C) 2017  Curtis 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 02c3c58225056b6f689d3375f79f3ad4233de041 Mon Sep 17 00:00:00 2001
From: Curtis 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

(limited to 'Makefile')

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 e0286eb..60d81ee 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 10df862510e610cf735ad181c2dd4cdc235e9102 Mon Sep 17 00:00:00 2001
From: Curtis 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(+)

(limited to 'Makefile')

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 ede0aa9a91490f1926572b7d43ec38cdcbe8e354 Mon Sep 17 00:00:00 2001
From: Curtis 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(-)

(limited to 'Makefile')

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 08d2be6206d257f8b74ca467d7e194d815aa5422 Mon Sep 17 00:00:00 2001
From: Curtis McEnroe <june@causal.agency>
Date: Sat, 25 Aug 2018 01:24:18 -0400
Subject: Revert "Explicitly build from .o objects"

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

(limited to 'Makefile')

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 3d8f04a5577e0556f5be50398db232a407b4ae82 Mon Sep 17 00:00:00 2001
From: Curtis 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(+)

(limited to 'Makefile')

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