From d41f9468b7622a4e535f2ce7cf87ea788366aead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucio=20Andr=C3=A9s=20Illanes=20Albornoz?= Date: Wed, 4 Sep 2019 11:03:53 +0200 Subject: [PATCH] assets/tools/SAUCEToMiRCART.py: added (for spoke.) assets/text/TODO: updated. --- assets/text/TODO | 23 ++--- assets/tools/SAUCEToMiRCART.py | 150 +++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 11 deletions(-) create mode 100755 assets/tools/SAUCEToMiRCART.py 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