diff --git a/assets/text/TODO b/assets/text/TODO index ad80458..837d06e 100644 --- a/assets/text/TODO +++ b/assets/text/TODO @@ -1,16 +1,17 @@ 1) Scrollbar 2) Allow {un,re}doing resizing -3) {Copy to,Paste from} clipboard -4) Incremental auto{load,save} & {backup,restore} -5) Open and toggle a reference image in the background -6) Client-Server or Peer-to-Peer realtime collaboration -7) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.) -8) Hotkey & graphical interfaces to {composed,parametrised} tools -9) Layer, layout (e.g. for comics, zines, etc.) & {re,un}do canvas traits -10) Im- and exporting from/to ANSI, Blender, GIF, HTML, mIRC, Pastebin/..., PNG, printer, SAUCE, WEBM, etc. -11) Asset management (e.g. kade, lion, etc.) & traits w/ simple linking & synchronised editing respecting layers -12) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...) -13) Composition and parametrisation of tools from higher-order operators (brushes, filters, outlines, patterns & shaders) and unit tools; unit tools: +3) Don't use ^C, encoding +4) {Copy to,Paste from} clipboard +5) Incremental auto{load,save} & {backup,restore} +6) Open and toggle a reference image in the background +7) Client-Server or Peer-to-Peer realtime collaboration +8) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.) +9) Hotkey & graphical interfaces to {composed,parametrised} tools +10) Layer, layout (e.g. for comics, zines, etc.) & {re,un}do canvas traits +11) Im- and exporting from/to ANSI, Blender, GIF, HTML, mIRC, Pastebin/..., PNG, printer, SAUCE, WEBM, etc. +12) Asset management (e.g. kade, lion, etc.) & traits w/ simple linking & synchronised editing respecting layers +13) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...) +14) Composition and parametrisation of tools from higher-order operators (brushes, filters, outlines, patterns & shaders) and unit tools; unit tools: a) geometric primitives (arrow, circle, cloud/speech bubble, curve, heart, hexagon, line, pentagon, polygon, rhombus, triangle, square, star) b) regions (crop, duplicate, erase, fill, invert, measure, pick, rotate, scale, select, shift, slice, tile, translate) c) text (edit, Unicode sets) diff --git a/assets/tools/SAUCEToMiRCART.py b/assets/tools/SAUCEToMiRCART.py new file mode 100755 index 0000000..272725c --- /dev/null +++ b/assets/tools/SAUCEToMiRCART.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +# +# SAUCEToMiRCART.py -- convert SAUCE-encoded ANSi to mIRC art file (for spoke) +# Copyright (c) 2019 Lucio Andrés Illanes Albornoz +# This project is licensed under the terms of the MIT licence. +# + +import os, re, struct, sys + +AnsiBgToMiRCARTColours = { + 107: 0, # Bright White + 40: 1, # Black + 104: 2, # Blue + 42: 3, # Green + 101: 4, # Red + 41: 5, # Light Red + 45: 6, # Pink + 43: 7, # Yellow + 103: 8, # Light Yellow + 102: 9, # Light Green + 46: 10, # Cyan + 106: 11, # Light Cyan + 44: 12, # Light Blue + 105: 13, # Light Pink + 100: 14, # Grey + 47: 15, # Light Grey +}; + +AnsiFgToMiRCARTColours = { + 97: 0, # Bright White + 30: 1, # Black + 94: 2, # Blue + 32: 3, # Green + 91: 4, # Red + 31: 5, # Light Red + 35: 6, # Pink + 33: 7, # Yellow + 93: 8, # Light Yellow + 92: 9, # Light Green + 36: 10, # Cyan + 96: 11, # Light Cyan + 34: 12, # Light Blue + 95: 13, # Light Pink + 90: 14, # Grey + 37: 15, # Light Grey +}; + +AnsiFgBoldToMiRCARTColours = { + 97: 0, # Bright White + 30: 14, # Grey + 94: 12, # Light Blue + 32: 9, # Light Green + 91: 4, # Light Red + 31: 4, # Light Red + 35: 13, # Light Pink + 33: 8, # Light Yellow + 93: 8, # Light Yellow + 92: 9, # Light Green + 36: 11, # Light Cyan + 96: 11, # Light Cyan + 34: 12, # Light Blue + 95: 13, # Light Pink + 90: 14, # Grey + 37: 0, # Bright White +}; + +def SAUCEToMiRCART(inPathName, outPathName): + with open(inPathName, "rb") as inFile: + inFileStat = os.stat(inPathName) + inFile.seek(inFileStat.st_size - 128, 0) + inFile.seek(5 + 2 + 35 + 20 + 20 + 8 + 4, 1) + if (inFile.read(1) != b'\x01') \ + or (inFile.read(1) != b'\x01'): + print("error: only character based ANSi SAUCE files are supported.", file=sys.stderr) + return 1 + else: + width, height = struct.unpack("H", inFile.read(2))[0], struct.unpack("H", inFile.read(2))[0] + with open(outPathName, "w+") as outFile: + inFile.seek(0, 0) + inFileData, row, rowChars = inFile.read(inFileStat.st_size - 128).decode("cp437"), "", 0 + inFileChar, inFileCharMax = 0, len(inFileData) + curBg, curFg = 1, 15; curBgAnsi, curBoldAnsi, curFgAnsi = 30, False, 37; + while True: + if inFileChar >= inFileCharMax: + break + else: + m = re.match('\x1b\[((?:\d{1,3};?)+)m', inFileData[inFileChar:]) + if m: + newBg, newFg = -1, -1 + for ansiCode in m[1].split(";"): + ansiCode = int(ansiCode) + if ansiCode == 0: + curBgAnsi, curBoldAnsi, curFgAnsi = 30, False, 37; newBg, newFg = 1, 15; + elif ansiCode == 1: + curBoldAnsi, newFg = True, AnsiFgBoldToMiRCARTColours[curFgAnsi] + elif ansiCode == 2: + curBoldAnsi, newFg = False, AnsiFgToMiRCARTColours[curFgAnsi] + elif ansiCode == 7: + newBg, newFg = curFg, curBg; curBgAnsi, curFgAnsi = curFgAnsi, curBgAnsi; + elif ansiCode in AnsiBgToMiRCARTColours: + curBgAnsi, newBg = ansiCode, AnsiBgToMiRCARTColours[ansiCode] + elif ansiCode in AnsiFgToMiRCARTColours: + if curBoldAnsi: + newFg = AnsiFgBoldToMiRCARTColours[ansiCode] + else: + newFg = AnsiFgToMiRCARTColours[ansiCode] + curFgAnsi = ansiCode + elif ansiCode in AnsiFgBoldToMiRCARTColours: + curFgAnsi, newFg = ansiCode, AnsiFgBoldToMiRCARTColours[ansiCode] + if ((newBg != -1) and (newFg != -1)) \ + and ((newBg == curFg) and (newFg == curBg)): + row += "\u0016"; curBg, curFg = newBg, newFg; + elif ((newBg != -1) and (newFg != -1)) \ + and ((newBg != curBg) and (newFg != curFg)): + row += "\u0003{},{}".format(newFg, newBg); curBg, curFg = newBg, newFg; + elif (newBg != -1) and (newBg != curBg): + row += "\u0003{},{}".format(curFg, newBg); curBg = newBg; + elif (newFg != -1) and (newFg != curFg): + row += "\u0003{}".format(newFg); curFg = newFg; + inFileChar += len(m[0]) + else: + m = re.match('\x1b\[(\d+)C', inFileData[inFileChar:]) + if m: + row += m[0]; inFileChar += len(m[0]); rowChars += int(m[1]); + elif inFileData[inFileChar:inFileChar+2] == "\r\n": + inFileChar += 2; rowChars = width; + elif inFileData[inFileChar] == "\r" \ + or inFileData[inFileChar] == "\n": + inFileChar += 1; rowChars = width; + else: + row += inFileData[inFileChar]; inFileChar += 1; rowChars += 1; + if rowChars >= width: + print(row, file=outFile); row = ""; rowChars = 0; + if (curBg != 1) and (curFg != 15): + row += "\u0003{},{}".format(curFg, curBg); + elif curBg != 1: + row += "\u0003{},{}".format(curFg, curBg); + elif curFg != 1: + row += "\u0003{}".format(curFg); +# +# Entry point +def main(*argv): + SAUCEToMiRCART(argv[1], argv[2]) +if __name__ == "__main__": + if (len(sys.argv) - 1) != 2: + print("usage: {} ".format(sys.argv[0]), file=sys.stderr) + else: + main(*sys.argv) + +# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120