diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 49 | ||||
-rw-r--r-- | client.c | 888 | ||||
-rw-r--r-- | help.c | 187 | ||||
-rw-r--r-- | help.h | 376 | ||||
-rw-r--r-- | index.html | 12 | ||||
-rw-r--r-- | merge.c | 72 | ||||
-rw-r--r-- | meta.c | 10 | ||||
-rw-r--r-- | server.c | 111 | ||||
-rw-r--r-- | torus.h | 87 |
10 files changed, 1113 insertions, 681 deletions
diff --git a/.gitignore b/.gitignore index 2f400d8..230472d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ +*.o chroot.tar client -help merge meta root diff --git a/Makefile b/Makefile index 8df5425..9199a7b 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,31 @@ -USER = torus -BINS = server client help meta merge +CHROOT_USER = torus +CHROOT_GROUP = $(CHROOT_USER) + CFLAGS += -Wall -Wextra -Wpedantic -LDLIBS = -lm -lcurses +LDLIBS = -lcursesw +BINS = server client meta merge +OBJS = $(BINS:%=%.o) all: tags $(BINS) -$(BINS): torus.h +.o: + $(CC) $(LDFLAGS) $< $(LDLIBS) -o $@ + +$(OBJS): torus.h + +client.o: help.h -# Only necessary so GNU make doesn't try to use torus.h as a source. -.c: - $(CC) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@ +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 -chroot.tar: server client help +tags: *.h *.c + ctags -w *.h *.c + +chroot.tar: server client mkdir -p root install -d -o root -g wheel \ root/bin \ @@ -21,22 +35,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 - install -o root -g wheel -m 555 server client help root/bin + 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 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 diff --git a/client.c b/client.c index b09e5b0..eafea91 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,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <ctype.h> +#define _XOPEN_SOURCE_EXTENDED + +#include <assert.h> #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,16 +30,215 @@ #include <sys/un.h> #include <sysexits.h> #include <unistd.h> +#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) +#define DIV_ROUND(a, b) (((a) + (b) / 2) / (b)) + +#define CTRL(ch) ((ch) ^ 0x40) enum { ESC = 0x1B, 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, ""); + + 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); + } + + assume_default_colors(0, 0); + if (COLORS >= 16) { + for (short pair = 1; pair < 0x80; ++pair) { + init_pair(pair, pair & 0x0F, (pair & 0xF0) >> 4); + } + } else { + for (short pair = 1; pair < 0100; ++pair) { + init_pair(pair, pair & 007, (pair & 070) >> 3); + } + } + + 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); + color_set(0, NULL); + + cbreak(); + noecho(); + keypad(stdscr, true); + set_escdelay(100); +} + +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 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' }; + setcchar(&cch, wch, attr | colorAttr(color), colorPair(color), NULL); + 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"); + drawTile(&tile); +} + +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; + drawCell(&tile, msg.put.cellX, msg.put.cellY, A_NORMAL); +} + +static void serverCursor(struct ServerMessage msg) { + if (msg.cursor.oldCellX != CURSOR_NONE) { + drawCell(&tile, msg.cursor.oldCellX, msg.cursor.oldCellY, A_NORMAL); + } + if (msg.cursor.newCellX != CURSOR_NONE) { + drawCell(&tile, msg.cursor.newCellX, msg.cursor.newCellY, A_REVERSE); + } +} + +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 = 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]; + 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) { + 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"); + + switch (msg.type) { + break; case SERVER_TILE: serverTile(); + 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(cellY, cellX); +} + static void clientMessage(struct ClientMessage msg) { ssize_t size = send(client, &msg, sizeof(msg), 0); if (size < 0) err(EX_IOERR, "send"); @@ -50,18 +252,15 @@ static void clientMove(int8_t dx, int8_t dy) { clientMessage(msg); } -static void clientPut(uint8_t color, char cell) { - struct ClientMessage msg = { - .type = CLIENT_PUT, - .put = { .color = color, .cell = cell }, - }; +static void clientFlip(void) { + struct ClientMessage msg = { .type = CLIENT_FLIP }; clientMessage(msg); } -static void clientSpawn(uint8_t spawn) { +static void clientPut(uint8_t color, uint8_t cell) { struct ClientMessage msg = { - .type = CLIENT_SPAWN, - .spawn = spawn, + .type = CLIENT_PUT, + .put = { .color = color, .cell = cell }, }; clientMessage(msg); } @@ -71,141 +270,144 @@ static void clientMap(void) { clientMessage(msg); } -static void colorPairs(void) { - assume_default_colors(0, 0); - if (COLORS >= 16) { - for (short pair = 1; pair < 0x80; ++pair) { - init_pair(pair, pair & 0x0F, (pair & 0xF0) >> 4); - } - } else { - for (short pair = 1; pair < 0100; ++pair) { - 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); -} - -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); -} - static struct { - int8_t speed; - uint8_t color; enum { MODE_NORMAL, + MODE_HELP, MODE_MAP, + MODE_DIRECTION, MODE_INSERT, MODE_REPLACE, - MODE_PUT, MODE_DRAW, + MODE_LINE, } mode; - int8_t dx; - int8_t dy; - uint8_t len; - char draw; + uint8_t color; + uint8_t shift; + uint8_t draw; } input = { - .speed = 1, .color = COLOR_WHITE, - .dx = 1, }; -static void colorFg(uint8_t fg) { - input.color = (input.color & 0xF8) | fg; +static struct { + uint8_t color; + uint8_t cell; +} copy; + +static struct { + int8_t dx; + int8_t dy; + 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); + input.mode = MODE_HELP; +} +static void modeMap(void) { + curs_set(0); + clientMap(); + input.mode = MODE_MAP; +} +static void modeDirection(void) { + input.mode = MODE_DIRECTION; +} +static void modeInsert(int8_t dx, int8_t dy) { + insert.dx = dx; + insert.dy = 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); +} static void colorBg(uint8_t bg) { - input.color = (input.color & 0x0F) | (bg << 4); + input.color = (input.color & 0x0F) | (bg & 0x07) << 4; } -static void colorInvert(void) { - input.color = - (input.color & 0x08) | - ((input.color & 0x07) << 4) | - ((input.color & 0x70) >> 4); +static uint8_t colorInvert(uint8_t color) { + return (color & 0x08) + | (color & 0x70) >> 4 + | (color & 0x07) << 4; } -static void insertMode(int8_t dx, int8_t dy) { - input.mode = MODE_INSERT; - input.dx = dx; - input.dy = dy; - input.len = 0; +static void cellCopy(void) { + copy.color = tile.colors[cellY][cellX]; + copy.cell = tile.cells[cellY][cellX]; } -static void swapCell(int8_t dx, int8_t dy) { - uint8_t aColor = attrColor(inch()); - char aCell = inch() & A_CHARTEXT; +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; - 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); + 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 void inputNormal(int c) { - switch (c) { - break; case ESC: input.mode = MODE_NORMAL; +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; + } + return 0; +} - break; case 'q': endwin(); exit(EX_OK); - break; case 'Q': { - if ((input.color & 0x07) < ARRAY_LEN(SPAWNS)) { - clientSpawn(input.color & 0x07); - } else { - clientSpawn(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; } - break; case 'm': clientMap(); + return; + } - 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()), ' '); + switch (ch) { + break; case CTRL('L'): clearok(curscr, true); - break; case '~': { - clientPut(input.color, inch() & A_CHARTEXT); - clientMove(input.dx, input.dy); - } + break; case ESC: modeNormal(); input.shift = 0; + break; case 'q': endwin(); exit(EX_OK); - 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 'g': clientFlip(); + 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': colorFg(COLOR_BLACK); break; case '1': colorFg(COLOR_RED); @@ -225,288 +427,261 @@ static void inputNormal(int c) { break; case '^': colorBg(COLOR_CYAN); break; case '&': colorBg(COLOR_WHITE); - break; case '*': case '8': input.color ^= COLOR_BRIGHT; + 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 '(': case '9': colorInvert(); + 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 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); - } -} + break; case 's': cellCopy(); + break; case 'x': cellCopy(); clientPut(copy.color, ' '); + break; case 'p': clientPut(copy.color, copy.cell); -static void inputMap(void) { - input.mode = MODE_NORMAL; - curs_set(1); - touchwin(stdscr); -} - -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); + 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); } - } 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 inputReplace(int c) { - if (isprint(c)) clientPut(attrColor(inch()), c); - input.mode = MODE_NORMAL; -} -static void inputPut(int c) { - if (isprint(c)) clientPut(input.color, c); - input.mode = MODE_NORMAL; -} + 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); + } -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; + break; case '?': modeHelp(); + break; case 'm': modeMap(); + 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(); } } -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 void inputHelp(bool keyCode, wchar_t ch) { + (void)keyCode; + (void)ch; + if (tile.meta.createTime) drawTile(&tile); + modeNormal(); } -static void serverPut(uint8_t x, uint8_t y, uint8_t color, char cell) { - mvaddch(y, x, colorAttr(color) | cell); +static void inputMap(bool keyCode, wchar_t ch) { + (void)keyCode; + (void)ch; + drawTile(&tile); + modeNormal(); } -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..."); +static void inputDirection(bool keyCode, wchar_t ch) { + if (keyCode) return; + switch (ch) { + 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); } +} - 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 inputInsert(bool keyCode, wchar_t ch) { + if (keyCode) { + inputNormal(keyCode, ch); + return; + } + switch (ch) { + break; case ESC: { + clientMove(-insert.dx, -insert.dy); + modeNormal(); + } + break; case '\b': case DEL: { + clientMove(-insert.dx, -insert.dy); + clientPut(input.color, ' '); + insert.len--; + } + break; case '\n': { + clientMove(insert.dy, insert.dx); + clientMove(insert.len * -insert.dx, insert.len * -insert.dy); + insert.len = 0; + } + break; default: { + uint8_t cell = inputCell(ch); + if (!cell) break; + clientPut(input.color, cell); + clientMove(insert.dx, insert.dy); + insert.len++; } } } -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); +static void inputReplace(bool keyCode, wchar_t ch) { + if (keyCode) { + inputNormal(keyCode, ch); + return; } - if (newX != CURSOR_NONE) { - move(newY, newX); - addch(inch() | A_REVERSE); + if (ch != ESC) { + uint8_t cell = inputCell(ch); + if (!cell) return; + clientPut(tile.colors[cellY][cellX], cell); } + modeNormal(); } -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 MapTile tile = map.tiles[y][x]; - if (countMax < tile.modifyCount) countMax = tile.modifyCount; - if (tile.modifyTime && timeMin > tile.modifyTime) { - timeMin = tile.modifyTime; - } +static void inputDraw(bool keyCode, wchar_t ch) { + if (!keyCode && ch == ESC) { + modeNormal(); + return; + } + if (input.draw) { + inputNormal(keyCode, ch); + } else { + if (keyCode) { + inputNormal(keyCode, ch); + return; } + input.draw = inputCell(ch); } + clientPut(input.color, input.draw); +} - for (int y = 0; y < MAP_ROWS; ++y) { - for (int x = 0; x < MAP_COLS; ++x) { - struct MapTile tile = map.tiles[y][x]; - - double count = (tile.modifyCount && countMax > 1) - ? log(tile.modifyCount) / log(countMax) - : 0.0; - double time = (tile.modifyTime && timeNow - timeMin) - ? (double)(tile.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; - } - - wmove(mapWindow, y, 3 * x); - waddch(mapWindow, attr | cell); - waddch(mapWindow, attr | cell); - waddch(mapWindow, attr | cell); +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; } } - - input.mode = MODE_MAP; - curs_set(0); + return 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 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; } - break; case SERVER_CURSOR: { - serverCursor( - msg.cursor.oldCellX, - msg.cursor.oldCellY, - msg.cursor.newCellX, - msg.cursor.newCellY - ); + } else { + switch (ch) { + break; case ESC: case '.': 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; } - break; case SERVER_MAP: serverMap(); - break; default: errx(EX_PROTOCOL, "I don't know what %d means!", msg.type); } - move(sy, sx); + 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 draw(void) { - wnoutrefresh(stdscr); - if (input.mode == MODE_MAP) { - touchwin(mapFrame); - touchwin(mapWindow); - wnoutrefresh(mapFrame); - wnoutrefresh(mapWindow); +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_HELP: inputHelp(keyCode, ch); + break; case MODE_MAP: inputMap(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); } - 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); +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; + } } - 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); - } + curse(); + modeHelp(); + readInput(); - 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); - } - 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() { client = socket(PF_LOCAL, SOCK_STREAM, 0); if (client < 0) err(EX_OSERR, "socket"); @@ -517,8 +692,6 @@ int main() { int error = connect(client, (struct sockaddr *)&addr, sizeof(addr)); if (error) err(EX_NOINPUT, "torus.sock"); - curse(); - struct pollfd fds[2] = { { .fd = STDIN_FILENO, .events = POLLIN }, { .fd = client, .events = POLLIN }, @@ -530,6 +703,7 @@ int main() { if (fds[0].revents) readInput(); if (fds[1].revents) readMessage(); - draw(); + + refresh(); } } 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(); - } -} diff --git a/help.h b/help.h new file mode 100644 index 0000000..8c092ed --- /dev/null +++ b/help.h @@ -0,0 +1,376 @@ +static const uint8_t HELP_DATA[] = { + 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, 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, 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, + 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, 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, 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, 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, + 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, 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, 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, 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, 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, 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, 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, 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, + 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, + 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, 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, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 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, 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, 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, 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, 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, + 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, + 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 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, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 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, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 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, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 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, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 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, 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, 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, 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, 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 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; 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> diff --git a/merge.c b/merge.c index 276d071..ba9d4f3 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 (;;) { @@ -93,11 +105,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 14a0930..ed55a8d 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 a6807e8..08b8dd1 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; } @@ -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,7 +172,7 @@ static bool clientCursors(const struct Client *client) { return true; } -static bool clientUpdate(const struct Client *client, const struct Client *old) { +static bool clientUpdate(struct Client *client, const struct Client *old) { struct ServerMessage msg = { .type = SERVER_MOVE, .move = { .cellX = client->cellX, .cellY = client->cellY }, @@ -217,16 +217,6 @@ static bool clientUpdate(const struct Client *client, const struct Client *old) 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; @@ -268,7 +258,14 @@ static bool clientMove(struct Client *client, int8_t dx, int8_t dy) { return clientUpdate(client, &old); } -static bool clientPut(const struct Client *client, uint8_t color, char cell) { +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) { struct Tile *tile = tileModify(client->tileX, client->tileY); tile->colors[client->cellY][client->cellX] = color; tile->cells[client->cellY][client->cellX] = cell; @@ -291,20 +288,62 @@ 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 = { + .now = now, + .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; + 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; + } - 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] = meta; } } @@ -366,7 +405,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; } @@ -389,11 +432,15 @@ 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); } - 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 775df3c..62994b9 100644 --- a/torus.h +++ b/torus.h @@ -15,14 +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))) +#include <wchar.h> #define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0])) @@ -47,65 +46,72 @@ 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 = 25, + 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; -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 Tile { + alignas(4096) uint8_t 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]); -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, - MAP_COLS = 11, + MAP_ROWS = 7, + MAP_COLS = 7, }; struct Map { - struct MapTile { - time_t createTime; - time_t modifyTime; - time_t accessTime; - uint32_t modifyCount; - uint32_t accessCount; - } tiles[MAP_ROWS][MAP_COLS]; + time_t now; + struct Meta min; + struct Meta max; + struct Meta meta[MAP_ROWS][MAP_COLS]; }; struct ServerMessage { - enum PACKED { + enum { SERVER_TILE, SERVER_MOVE, SERVER_PUT, @@ -121,7 +127,7 @@ struct ServerMessage { uint8_t cellX; uint8_t cellY; uint8_t color; - char cell; + uint8_t cell; } put; struct { uint8_t oldCellX; @@ -135,10 +141,10 @@ struct ServerMessage { static const uint8_t CURSOR_NONE = UINT8_MAX; struct ClientMessage { - enum PACKED { + enum { CLIENT_MOVE, + CLIENT_FLIP, CLIENT_PUT, - CLIENT_SPAWN, CLIENT_MAP, } type; union { @@ -148,8 +154,7 @@ struct ClientMessage { } move; struct { uint8_t color; - char cell; + uint8_t cell; } put; - uint8_t spawn; }; }; |