########################################################################################### dr1p4ns1  version 2.701 ##### SOF
####################################################################################################  #######################

import os
import random
import shutil
import sys
import termios
import threading
import tty
from collections import namedtuple
from glob import glob
from time import sleep

####################################################################################################  #######################
####################################################################################################  #######################
                                                                                                                        #####
####################################################################################################  #######################
"""MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMh+MMMMMMMMMMMMMMhsMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMm/ oMMMMMMMMMMMMMMm +NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMy`  yMMMMMMMMMMMMMMM- -mMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMs+dMMMMMMMMMM+    sMMMMMMMMMMMMMMM-  `dMMMMMMMMMMms/NMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMM+ .omMMMMMM:     -MMMMMMMMMMMMMMo    `yMMMMMMMy: `dMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMM-   /dMMM+       sMMMMMMMMMMMMh      `hMMMNo`   sMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMd     :dm        `mMMMMMMMMMMN.       .NNo`    .MMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMM:      -         :MMMMMMMMMMs         :`      sMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMs                 ymNMMMMMNm.                 NMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMy                    `-/-`                   .MMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMo                                            .NMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMNh+.                                               :sdMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMhso+:.                                                      `-/+syMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMM-                                                                 dMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMM`   `.:+/.                                              `/s+:.    sMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMNo   -oms.             .//-`               `:/:`            `+md+`  .hMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMNs`                        .odNdo.       .ohmd+`                        :dMMMMMMMMMMMMM  #######################
MMMMMMMMMMMNo`                             ..           .-                             :hMMMMMMMMMMM  #######################
MMMMMMMMMd+`                                                                             -sNMMMMMMMM  #######################
###MMMNs-                         `..                          `/-.                        `+dMMMMMM  #######################
###Ny:                       ./sdNMMMh:                      `sNMMMNds/.                      .odMMM  #######################
MM+                       :ymMMMMMMMMMMh.                   +NMMMMMMMMMMmo-                      /NM  #######################
MMMh:                  .sNMMMMMMMMMMMMMMN-                `hMMMMMMMMMMMMMMMm+`                 :hMMM  #######################
MMMMMd:`       ``-:+shmMMMMMMMMMMMMMMMMMMN.               hMMMMMMMMMMMMMMMMMMMmhs+/-..``````./dMMMMM  #######################
MMMMMMMMMNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMo              .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMy              .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN.             /MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN+`         `+NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNs.    -hMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM  #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMdyymMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"""  #######################
####################################################################################################  #######################
                                                                                                                        #####
####################################################################################################  #######################
####################################################################################################  #######################
FIFO_PATH = f"{os.path.expanduser('~')}/.weechat/weechat_fifo"                                                          #####
FIFO_ENABLED = True                                                                                                     #####
COPY_PATH = f"{os.path.expanduser('~')}/Pictures"                                                                       #####
DELETE_PATH = "/tmp"                                                                                                    #####
FLOODRATE_INTERVAL_SECONDS = 0.3                                                                                      #####
global SHOW_FILENAME                                                                                                    #####
SHOW_FILENAME=False                                                                                                      #####
DR1P_VERSION="2.701 - last updated: 03/26/23 - https://git.tcp.direct/decoded/dr1p4ns1"                                 #####
####################################################################################################  #######################
####################################################################################################  #######################
                                                                                                                        #####
### "00",     "01",     "02",     "03",     "04",     "05",     "06",     "07",   ###########################################
### "08",     "09",     "10",     "11",     "12",     "13",     "14",     "15",   ###########################################
                                                                                                                        #####
0xFFFFFF, 0x000000, 0x00007F, 0x009300, 0xFF0000, 0x7F0000, 0x9C009C, 0xFC7F00,   ###########################################
0xFFFF00, 0x00FC00, 0x009393, 0x00FFFF, 0x0000FC, 0xFF00FF, 0x7F7F7F, 0xD2D2D2,   ###########################################
                                                                                                                        #####
#############################################################################################################################
#############################################################################################################################
###                                                                                                                     #####
### "16",     "17",     "18",     "19",     "20",     "21",     "22",     "23",     "24",     "25",     "26",     "27", #####
### "28",     "29",     "30",     "31",     "32",     "33",     "34",     "35",     "36",     "37",     "38",     "39", #####
### "40",     "41",     "42",     "43",     "44",     "45",     "46",     "47",     "48",     "49",     "50",     "51", #####
### "52",     "53",     "54",     "55",     "56",     "57",     "58",     "59",     "60",     "61",     "62",     "63", #####
### "64",     "65",     "66",     "67",     "68",     "69",     "70",     "71",     "72",     "73",     "74",     "75", #####
### "76",     "77",     "78",     "79",     "80",     "81",     "82",     "83",     "84",     "85",     "86",     "87", #####
### "88",     "89",     "90",     "91",     "92",     "93",     "94",     "95",     "96",     "97",     "98"        ''' #####

0x470000, 0x472100, 0x474700, 0x324700, 0x004700, 0x00472C, 0x004747, 0x002747, 0x000047, 0x2E0047, 0x470047, 0x47002A, #####
0x740000, 0x743a00, 0x747400, 0x517400, 0x007400, 0x007449, 0x007474, 0x004074, 0x000074, 0x4b0074, 0x740074, 0x740045, #####
0xb50000, 0xb56300, 0xb5b500, 0x7db500, 0x00b500, 0x00b571, 0x00b5b5, 0x0063b5, 0x0000b5, 0x7500b5, 0xb500b5, 0xb5006b, #####
0xff0000, 0xff8c00, 0xffff00, 0xb2ff00, 0x00ff00, 0x00ffa0, 0x00ffff, 0x008cff, 0x0000ff, 0xa500ff, 0xff00ff, 0xff0098, #####
0xff5959, 0xffb459, 0xffff71, 0xcfff60, 0x6fff6f, 0x65ffc9, 0x6dffff, 0x59b4ff, 0x5959ff, 0xc459ff, 0xff66ff, 0xff59bc, #####
0xff9c9c, 0xffd39c, 0xffff9c, 0xe2ff9c, 0x9cff9c, 0x9cffdb, 0x9cffff, 0x9cd3ff, 0x9c9cff, 0xdc9cff, 0xff9cff, 0xff94d3, #####
0x000000, 0x131313, 0x282828, 0x363636, 0x4d4d4d, 0x656565, 0x818181, 0x9f9f9f, 0xbcbcbc, 0xe2e2e2, 0xffffff            #####
                                                                                                                        #####
#############################################################################################################################
#############################################################################################################################
def getfiles(argv=""):
    PATH=""
    if len(argv) > 1:
        if argv[1] == ".":
            PATH=""
        else:
            PATH=f'{argv[1]}/'
    if len(argv) == 1:
        PATH=""
    files=glob(PATH+"*")
    FILES=[]
    for _ in files:
        if _.lower()[-4:]==".ans":
            FILES.append(_)
    if len(FILES)==0:
        print('ERROR: NO ANSI FILES IN THE PATH SPECIFIED',end='')
        sys.exit(1)
    return FILES
#############################################################################################################################
class D:
    def __init__(self):
        self.delay=FLOODRATE_INTERVAL_SECONDS
        self.correct=0
########################################################################################################## CLASS DR1P4NS1 - 2
class dr1p4ns1:
    ############################################################################################################ LOOKUP TABLE
    commands = {
        'A': 'up',
        'B': 'down',
        'C': 'forward',
        'D': 'back',
        'H': 'position',
        'f': 'position',
        'J': 'erase',
        'K': 'erase_line',
        's': 'save_cursor',
        'u': 'restore_cursor',
        'm': 'color',
        'R': 'report_cursor_position',
        't': 'nfi'}
    ############################################################################################################ LOOKUP TABLE
    asc_table="""\
    ╟ⁿΘΓΣαστΩδΦ∩ε∞─┼╔µ╞⌠÷≥√∙ ╓▄óúÑ₧ƒ\
    ßφ≤·±╤¬║┐⌐¼╜╝í½╗░▒▓│┤╡╢╖╕╣║╗╝╜╛┐\
    └┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀\
    α▀ΓπΣσ╡τΦΘΩδ∞φε∩≡▒≥≤⌠⌡≈≈░∙╖√ⁿ▓■á"""
    ##################################################################################################### ASCII TABLE CLEANUP
    for r in (('\n',''),(' ','')):
        asc_table=asc_table.replace(*r)
    ############################################################################################################ LOOKUP TABLE
    asc_table_full="""\
    \x00☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼\
    \_!"#$%&\'()*+,-./0123456789:;<=>\
    ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^\
    _`abcdefghijklmnopqrstuvwxyz{|}~\
    ⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧\
    ƒáíóúñÑªº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛\
    ┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐\
    ▀ɑϐᴦᴨ∑ơµᴛɸϴΩẟ∞∅∈∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\
    ÿ"""
    ##################################################################################################### ASCII TABLE CLEANUP
    for r in ((' ',''),('\_',' ')):
        asc_table_full=asc_table_full.replace(*r)
    ########################################################################################## RGB TERMINAL ESCAPE COLORCODES
    def _rgb(r=0,g=0,b=0):return f"\033[38;2;{r};{g};{b}m"
    ############################################################################################################ LOOKUP TABLE
    asc2uni=dict(zip([i for i in range(128,256)],[ord(c) for c in asc_table]))
    ############################################################################################################ LOOKUP TABLE
    asc2chr=dict(zip([i for i in range(128,256)],[c for c in asc_table]))
    ############################################################################################################ LOOKUP TABLE
    rgb2irc = { 52:  '16',   94: '17',  100: '18',   58: '19',
                22:  '20',   29: '21',   23: '22',   24: '23',
                17:  '24',   54: '25',   53: '26',   89: '27',
                88:  '28',  130: '29',  142: '30',   64: '31',
                28:  '32',   35: '33',   30: '34',   25: '35',
                18:  '36',   91: '37',   90: '38',  125: '39',
                124: '40',  166: '41',  184: '42',  106: '43',
                34:  '44',   49: '45',   37: '46',   33: '47',
                19:  '48',  129: '49',  127: '50',  161: '51',
                196: '52',  208: '53',  226: '54',  154: '55',
                46:  '56',   86: '57',   51: '58',   75: '59',
                21:  '60',  171: '61',  201: '62',  198: '63',
                203: '64',  215: '65',  227: '66',  191: '67',
                83:  '68',  122: '69',   87: '70',  111: '71',
                63:  '72',  177: '73',  207: '74',  205: '75',
                217: '76',  223: '77',  229: '78',  193: '79',
                157: '80',  158: '81',  159: '82',  153: '83',
                147: '84',  183: '85',  219: '86',  212: '87',
                16:  '88',  233: '89',  235: '90',  237: '91',
                239: '92',  241: '93',  244: '94',  247: '95',
                250: '96',  254: '97',  231: '98',  }
    ########################################################################################################## CLASS INIT - 3
    def __init__(self,ansifile="",width=80,height=25,debug=False,pump=False):
        self.DEBUG=debug
        self.width=width
        self.height=height
        self.filename=ansifile
        self.openansi(ansifile)
        self.code='\x1b[0m'
        self.cmps=[]
        self.op=[]
        self.optype=[]
        self.sauce_found=False
        print('\x1bc')
        self.pump=pump
        self.boot()
    ######################################################################################################### FILE OPERATIONS
    def fifo(self,s):
        s=[s]
        f=open(FIFO_PATH,"w")
        [f.write("*{}\n".format(x)) for x in s]
        f.close()
    ######################################################################################################### FILE OPERATIONS
    def openansi(self,s):
        f=open('log','w')
        f.write(f'{s}\n')
        f.close()
        try:
            f=open(s,'rb')
        except:
            f=open(s[0],'rb')
        self.ansifile=f.read().decode('cp437')
        f.close()
    ############################################################################################################### DEBUGGING
    def openascii(self):
        f=open('ansiscii/work.asc','r')
        self.reference=f.read()
        f.close()
    #################################################################################################################### TOOL
    def printascii(self):
        for i,_ in enumerate(self.asc_table):
            _int=str(i+128).zfill(3)
            _hex=hex(i+128)[2:].upper().zfill(2)
            _ord=str(ord(_)).zfill(4)
            print(f'{_} - {_int} - {_hex} - {_ord}')
    ############################################################################################################### DEBUGGING
    def printrange(self,n,nn):
        for index,_ in enumerate(self.ansifile):
            h=hex(_)[2:].upper()
            i=str(_).zfill(3)
            s=''
            if _>47 and _<127:
                s=chr(_)
            else:
                s='.'
            l=str(index).zfill(len(str(len(self.ansifile))))
            if index >=n and index <=nn:
                print(f"{l} {i} {h} {s}")
            if index==nn:
                break
    ############################################################################################################### DEBUGGING
    def printhex(self):
        theasc=[]
        thehex=[]
        buffer=''
        for _ in self.ansifile:
            buffer+=hex(ord(_)).upper()[2:].zfill(2)
        for _ in self.chunk(buffer,32):
            subchunks=''
            for i,__ in enumerate(self.chunk(_,2)):
                if i>0 and i%4==0: subchunks+=" "
                subchunks+=f'{__} '
            thehex.append(subchunks.strip())
        for __ in thehex:
            buffer=''
            for _ in __.split():
                i=int(_,16)
                if i>47 and i<127:
                    buffer+=chr(i)
                else:
                    buffer+='.'
            theasc.append(buffer)
        for _ in range(len(thehex)):
            old=buffer
            buffer={thehex[_]}
            if not buffer == old:
                print(f"{hex(16 * _)[2:].zfill(8)}  {thehex[_]}  {theasc[_]}")
    ############################################################################################################### DEBUGGING
    def hexdump(self):
        self.codes=[]
        code_start=0
        code_end=0
        code_state=False
        for i,_ in enumerate(self.ansifile):
            if code_state==False:
                if _ == '\x1b':
                    code_state=True
                    code_start=i
                elif _ == '\x1a':
                    code=self.ansifile[i:]
                    self.codes.append([code,i,len(self.ansifile)])
            else:
                if 64 <= ord(_) <= 126:
                    if _ in self.commands:
                        code_state=False
                        code_end=i+1
                        code=self.ansifile[code_start:code_end]
                        self.codes.append([code,code_start,code_end])
    ############################################################################################################### DEBUGGING
    def codedump(self):
        xpos_old=0
        xpos_new=0
        self.codes=[]
        code_start=0
        code_end=0
        code_state=False
        for i,_ in enumerate(self.ansifile):
            if code_state==False:
                if _ == '\x1b':
                    code_state=True
                    code_start=i
                elif _ == '\x1a':
                    self.op.append(self.ansifile[code_end:i])
                    code=self.ansifile[i:]
                    self.codes.append([code,i,len(self.ansifile)])
                    if code[1:6].upper()=="SAUCE":
                        self.getsauce()
            else:
                if 64 <= ord(_) <= 126:
                    try:
                        if _ in self.commands:
                            code_state=False
                            code_end=i+1
                            code=self.ansifile[code_start:code_end]
                            self.codes.append([code,code_start,code_end])
                            xpos_new=code_start
                            distance=xpos_new-xpos_old
                            if not distance == 0:
                                nonop=self.ansifile[xpos_old:xpos_old+distance]
                                self.op.append(nonop)
                                self.optype.append(1)
                            self.op.append(code)
                            xpos_old=code_end
                            if self.commands[_] == 'color':
                                self.optype.append(2)
                            elif self.commands[_] == 'forward':
                                self.optype.append(3)
                            else:
                                self.optype.append(0)
                    except Exception as e:
                        print(f'error: {e}')
        if not self.sauce_found:
            code=self.ansifile[i:]
    #################################################################################################################### TOOL
    def processdump(self):
        offset=0
        offsets=[]
        processed=[]
        uniqued=[]
        uniques=[]
        codes=[]
        processing=''
        try:
            for i,_optype in enumerate(self.optype):
                if _optype==2:
                    processing+=self.op[i]
                    offset+=len(self.op[i])
                elif _optype==3:
                    count=int(self.op[i].split('[')[1].split('C')[0])
                    processing+=' '*count
                elif _optype==1:
                    processing+=self.op[i]
                    _bseq='\r\n'
                    _xpos=processing.find(_bseq)
                    distance=0
                    while not processing.find(_bseq) == -1:
                        _xpos=processing.find(_bseq)
                        if not _xpos == -1:
                            uniques.append(_bseq)
                            uniques.append(_xpos)
                            if len(self.stripcodes(processing[:_xpos])) >= 80: distance=80
                            gaps=self.width-len(self.stripcodes(processing)[distance:].split('\r\n')[0])
                            processing=processing.replace(_bseq,chr(32)*gaps,1)
                if len(processing)-offset > self.width:
                    trailing_size=0
                    while len(processing)-offset > self.width:
                        deconcatenate=processing[0:self.width+offset]
                        if deconcatenate.endswith('m'):
                            if not deconcatenate.rfind('\x1b')==-1:
                                trailing_size=len(deconcatenate[deconcatenate.rfind('\x1b'):])
                                deconcatenate=deconcatenate[:deconcatenate.rfind('\x1b')]
                        processed.append(deconcatenate)
                        if len(uniques) < 2:
                            uniqued.append('')
                        else:
                            uniqued.append(uniques)
                        uniqued.append(uniques)
                        uniques=[]
                        offsets.append(offset)
                        processing=processing.replace(deconcatenate,'',1)
                        offset=0+trailing_size
            if len(processing) > 0:
                gaps=int(80-len(self.stripcodes(processing)))
                processing+=chr(32)*gaps
                processed.append(processing)
            processed_colors=[]; last_colors=''; color_fg=''; color_bg=''; color_set='';
            for y,__ in enumerate(processed):
                code_start=0; code_end=0; code_state=False; codes=[];
                for x,_ in enumerate(__):
                    if not code_state:
                        if _ =='\x1b':
                            code_start = x
                            code_state = True
                    else:
                        if _ in self.commands:
                            if self.commands[_] == 'color':
                                code_end = x+1
                                code_state = False
                                codes.append(__[code_start:code_end])
                for code in codes:
                    for _ in code.replace('m','').split('[')[1].split(';'):
                        try:
                            if 0 <= int(_) <= 9:
                                color_set=_
                            if 30 <= int(_) <= 39:
                                color_fg=_
                            if 40 <= int(_) <= 49:
                                color_bg=_
                        except:
                            print('error')
                            pass
                colors=f"{color_set}"
                processed_colors.append(colors)
            self.processed = processed
            irc_processed=[]
            terminal_processed=[]
            color_table=[1,5,3,7,2,6,10,15,14,4,9,8,12,13,11,0];
            default_fg_color=7
            default_bg_color=0
            codes=[]
            for _ in processed:
                line=_
                for r in (('\x16',chr(9644)),('\x15','\u00A7')):
                    line=line.replace(*r)
                _line=line
                bold_offset=0
                bold_before=[]
                bold_init=False
                codes_root_popped=False
                fg_color=color_table[default_fg_color]
                bg_color=color_table[default_bg_color]
                color_set='0'
                for _ in [line[x[0]:x[1]+1] for x in zip([x for x in self.findall(line,'\x1b')],[line.find('m',x) for x in self.findall(line,'\x1b')])]:
                    codes.append(_)
                    code_values=_.split('[')[1].split(';')
                    code_replace=_
                    newcode=''
                    for __ in code_values:
                        __=__.replace('m','')
                        if 0 <= int(__) <= 9:
                            color_set=int(__)
                            if color_set==1:
                                bold_offset=8
                                bold_before=[]
                            else:
                                bold_offset=0
                        elif 40 <= int(__) <= 49:
                            if bold_init and bold_offset==0:
                                bg_color=color_table[default_bg_color]
                            else:
                                bg_color=__
                            if not bold_offset==0 and not bold_init:
                                bold_before.append(int(bg_color))
                        elif 30 <= int(__) <= 39:
                            if bold_init and bold_offset==0:
                                fg_color=color_table[default_fg_color]
                            else:
                                fg_color=__
                            if not bold_offset==0 and not bold_init:
                                bold_before.append(int(fg_color))
                    if bold_init and bold_offset==0:
                        bold_init=False
                        newcode='\x03'
                        bold_before.sort()
                        for _ in bold_before:
                            if 30 <= int(_) <= 39:
                                newcode += str(color_table[default_fg_color])
                            if 40 <= int(_) <= 49:
                                newcode += "," + str(color_table[default_bg_color])
                    elif not bold_offset == 0 and not bold_init:
                        newcode='\x03'
                        bold_before.sort()
                        for _ in bold_before:
                            if 30 <= int(_) <= 39:
                                newcode+=str(color_table[int(fg_color)-30+bold_offset])
                            if 40 <= int(_) <= 49:
                                newcode+=","+str(color_table[int(bg_color)-40])
                        bold_init=True
                    elif not bold_offset == 0 and bold_init:
                        for _ in bold_before:
                            if len(bold_before)==1:
                                if 30 <= int(_) <= 39:
                                    bold_before.append(int(bg_color))
                                else:
                                    bold_before.append(int(fg_color))
                        newcode='\x03'
                        bold_before.sort()
                        for _ in bold_before:
                            if 30 <= int(_) <= 39:
                                newcode+=str(color_table[int(fg_color)-30+bold_offset])
                            if 40 <= int(_) <= 49:
                                newcode+=","+str(color_table[int(bg_color)-40])
                    else:
                        pass #non-bold-operations
                    if len(codes) == 1 and not codes_root_popped:
                        if color_set == 0:
                            _line=_line.replace(codes[-1],"",1)
                            codes_root_popped=True
                    else:
                        _line=_line.replace(code_replace,newcode,1)
                irc_processed.append(_line)
                terminal_processed.append(line)
        except Exception as e:
            print(f'bp error: {e}')
    #################################################################################################################### TOOL
    def getsauce(self):
        try:
            f=open(self.filename,'rb')
        except:
            f=open(self.filename[0],'rb')
        l=f.read()
        f.close()
        self.sauce=l[-128:]
        self.sauce_found=True
        self.width=self.sauce[96]
        self.height=self.sauce[98]
        if self.width==0:
            print("ERROR: sauce.width has 0 value, defaulting to 80 width")
            self.width=80
        offset=len("SAUCE")
        try:
            self.sauce_version=str(int(self.sauce[offset:offset+2].strip()))
            offset+=2
            self.sauce_title=self.sauce[offset:offset+35].strip()
            offset+=35
            self.sauce_title=self.sauce[offset:offset+20].strip()
            offset+=20
            self.sauce_group=self.sauce[offset:offset+20].strip()
            offset+=20
            self.sauce_date=self.sauce[offset:offset+8].strip()
            offset+=8
        except:
            pass
    #################################################################################################################### TOOL
    def findall(self,s,w):
        return [i for i in range(len(s)) if s.startswith(w, i)]
    ############################################################################################################### DEBUGGING
    def cmpans(self,s1,s2,n1=0,n2=0):
        _s1=''; _s2=''; _sn1=''; _sn2=''
        _s1=self.stripcodes(s1)
        _s2=self.stripcodes(s2)
        if not n1 == n2:
            _sn1=_s1[n1:n2]
            _sn2=_s2[n1:n2]
            _sn3=''
            for i in range(self.width):
                try:
                    if ord(_sn1[i]) == ord(_sn2[i]):
                        _sn3+='\x1b[32m0\x1b[0m'
                    else:
                        _sn3+='\x1b[31m1\x1b[0m'
                except Exception as e:
                    print(e)
            self.cmps.append(f'{_sn1}\n{_sn2}\n{_sn3}')
            n1+=self.width
            n2+=self.width
    #################################################################################################################### TOOL
    def chunk(self,s,n):
        return [s[i:i+n] for i in range(0,len(s),n)]
    #################################################################################################################### TOOL
    def stripcodes(self,s):
        buffer=s
        code_start=0
        code_end=0
        code_state=False
        for i,_ in enumerate(s):
            if code_state==False:
                if _ == '\x1b':
                    code_state=True
                    code_start=i
            else:
                if 64 <= ord(_) <= 126:
                    if _ in self.commands:
                        code_state=False
                        code_end=i+1
                        code=s[code_start:code_end]
                        buffer=buffer.replace(code,'')
        return buffer
    #################################################################################################################### TOOL
    def rgb(self,t='',r=0,g=0,b=0):
        """colorize text with rgb values"""
        return f"\033[38;2;{r};{g};{b}m{t}\033[38;2;255;255;255m"
    #################################################################################################################### TOOL
    def vga(self,t='',i=0):
        """colorize text using vga color set"""
        vga = [ '#000000','#aa0000','#00aa00','#aa5500',
                '#0000aa','#aa00aa','#00aaaa','#aaaaaa',
                '#555555','#ff5555','#55ff55','#ffff55',
                '#5555ff','#ff55ff','#55ffff','#ffffff', ]
        try:
            r=int(vga[i][1:][0:2],16)
            g=int(vga[i][1:][2:4],16)
            b=int(vga[i][1:][4:6],16)
        except:
            r,g,b=0,0,0
        return f"\033[38;2;{r};{g};{b}m{t}\033[38;2;255;255;255m"
    #################################################################################################################### TOOL
    def invert_dict(self,d):
        return {v: k for k, v in d.items()}
    #################################################################################################################### BOOT
    def boot(self):
        if self.DEBUG:
            print("\n\x1b[1;31m[ HEXDUMP ]\x1b[0m\n")
            self.printhex()
        self.codedump()
        self.processdump()
####################################################################################### HUMAN INTERFACE * * * * * * * * * * *
class UI:
    def __init__(self,files=""):
        self.REMOVED=False
        self.QUEUE=[]
        self.files=files
        self.uis_menu()
    ################################################################################### HUMAN INTERFACE * * * * * * * * * * *
    def getkey(self):
        old_settings = termios.tcgetattr(sys.stdin)
        tty.setcbreak(sys.stdin.fileno())
        try:
            while True:
                b = os.read(sys.stdin.fileno(), 3).decode()
                if len(b) == 3:
                    k = ord(b[2])
                else:
                    k = ord(b)
                key_mapping = {
                    112: 'p',
                    27: 'esc',
                    67: 'right',
                    68: 'left',
                    113: 'q',
                    120: 'x',
                    99: 'c',
                    100: 'd',
                    82: 'R'
                }
                return key_mapping.get(k, chr(k))
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
    ################################################################################### HUMAN INTERFACE * * * * * * * * * * *
    def uis_menu(self):
        global SHOW_FILENAME
        index=0
        try:
            print(f'loading: {self.files[index]}')
            d=dr1p4ns1(ansifile=self.files[index],width=80,debug=False)
            decoder=ANSIDecoder(self.files[index],d.width)
            decoder.as_terminal()
        except:
            pass
        #######################################
        try:
            while True:
                B='\x1b[1;94m'; C='\x1b[1;95m'; S='\x1b[1;36m'; M='\x1b[1;92m'; E='\x1b[0m'; R='\x1b[31m'
                msg=f"{B}[ {C}DR1P {B}] {B}- {B}[ {M}x{B}: {M}exit{B}, {M}c{B}: {M}copy{B}, {M}R{B}: {M}remove{B}, {M}f{B}: {M}show_filename{B}:{C}{SHOW_FILENAME}{B}, {M}q{B}: {M}pumpqueue{B}, {M}z{B}/{M}p{B}: {M}auto{B}/{M}pump{B}, "
                msg+=f"{M}left{B}/{M}right{B}: {M}browse {B}]\n[ {M}d{B}: {M}delay{B} - {M}{dr1p.delay}{B} ] {B}- "
                msg+=f"{B}[ {S}pumpqueue{B}: {C}{len(self.QUEUE)} {B}- {S}index{B}: {C}{index+1}{B}/{C}{len(self.files)} {B}- {S}filename{B}: {C}{d.filename} {B}]{E}"
                if self.REMOVED==True: msg+=f'{R} - FILE REMOVED'
                print(msg)
                k = self.getkey()
                if k == 'left':
                    index-=1
                    if index < 0: index=len(self.files)-1
                    try:
                        self.REMOVED=False
                        if len(self.files) == 0:
                            try:
                                del(dr1p4ns1.AUTOQUEUE)
                            except:
                                pass
                            print(f'{B}NO FILES LEFT IN QUEUE - {R} EXITING')
                            sleep(1)
                            break
                        print(f'{C}loading{B}: {S}{self.files[index]}')
                        try:
                            d=dr1p4ns1(ansifile=self.files[index],width=80,debug=False)
                        except:
                            pass
                        decoder=ANSIDecoder(self.files[index],d.width)
                        print('\x1bc')
                        decoder.as_terminal()
                    except:
                        pass
                elif k == 'right':
                    index+=1
                    if index >= len(self.files): index=0
                    try:
                        self.REMOVED=False
                        if len(self.files) == 0:
                            print(f'{B}NO FILES LEFT IN QUEUE - {R} EXITING')
                            sleep(1)
                            break
                        print(f'{C}loading{B}: {S}{self.files[index]}')
                        d=dr1p4ns1(ansifile=self.files[index],width=80,debug=False)
                        decoder=ANSIDecoder(self.files[index],d.width)
                        print('\x1bc')
                        decoder.as_terminal()
                    except:
                        pass
                elif k == 'esc' or k == 'x':
                    quit()
                elif k == 'c':
                    try:
                        if not self.files[index].find('/') == -1:
                            FILEPATH=f'{COPY_PATH}/{str(self.files[index]).split("/")[1]}'
                            shutil.copyfile(self.files[index],FILEPATH)
                            print(f'{C}{self.files[index]} copied to {FILEPATH}')
                        else:
                            FILEPATH=f'{COPY_PATH}/{self.files[index]}'
                            shutil.copyfile(self.files[index],FILEPATH)
                            print(f'{C}{self.files[index]} {M}copied to {C}{FILEPATH}')
                    except:
                        print(f'{C}{self.files[index]} {M}was not copied')
                    sleep(1)
                    print('\x1bc')
                    decoder.as_terminal()
                elif k == 'q':
                    self.QUEUE.append((self.files[index],d.width))
                    print(f'{C}{self.files[index]} {B}added to pumpqueue')
                    sleep(1)
                    print('\x1bc')
                    decoder.as_terminal()
                elif k == 'f':
                    if SHOW_FILENAME:
                        SHOW_FILENAME=False
                    else:
                        SHOW_FILENAME=True
                elif k == 'd':
                    while True:
                        print(f'{C}input floodrate delay{B}: {C}',end='')
                        USER_INPUT=input()
                        TRAPFLAG=-1
                        try:
                            TRAPFLAG=int(USER_INPUT)
                        except:
                            try:
                                TRAPFLAG=float(USER_INPUT)
                            except:
                                print(f'{S}accepted floodrate delay range is between 0 and 10.0 seconds')
                        try:
                            if TRAPFLAG>=0 and TRAPFLAG<=10:
                                dr1p.delay=TRAPFLAG
                                break
                        except:
                            print(f'{S}accepted floodrate delay range is between 0 and 10.0 seconds')
                    print(f'{M}floodrate delay set to{B}: {C}{dr1p.delay}')
                    sleep(1)
                    print('\x1bc')
                    decoder.as_terminal()

                elif k == 'z':
                    flag_done=False
                    dr1p4ns1.AUTOQUEUE=[0,0,0,0] #0=index,1=len,2=list,3=filename
                    dr1p4ns1.AUTOQUEUE[0]=0
                    if len(self.QUEUE)==0:
                        self.QUEUE=self.files
                        print(f'\n{dr1p4ns1._rgb(55,55,55)}pumpqueue empty{dr1p4ns1._rgb(155,155,155)}:{dr1p4ns1._rgb(55,55,55)} autopumping from directory index{dr1p4ns1._rgb(155,155,155)}: {dr1p4ns1._rgb(55,55,55)}{sys.argv[1]}')
                        sleep(2)
                    else:
                        print(f'{R} autopumping from pump queue')
                        sleep(2)
                    dr1p4ns1.AUTOQUEUE[1]=len(self.QUEUE)
                    dr1p4ns1.AUTOQUEUE[2]=self.QUEUE
                    while not flag_done:
                        if len(self.QUEUE) > 0:
                            print('\x1bc')
                            dr1p4ns1.filename=self.QUEUE[-1];
                            dr1p4ns1.AUTOQUEUE[3]=dr1p4ns1.filename
                            dr1p4ns1.getsauce(dr1p4ns1)
                            d=dr1p4ns1(ansifile=self.QUEUE[0],width=dr1p4ns1.width,debug=False)
                            decoder=ANSIDecoder(self.QUEUE[0],d.width)
                            decoder.as_terminal()
                            self.QUEUE.reverse()
                            buffer_filename=self.QUEUE[-1]
                            buffer_width=dr1p4ns1.width
                            self.QUEUE.pop()
                            self.QUEUE.reverse()
                            decoder=ANSIDecoder(buffer_filename,buffer_width)
                            decoder.as_irc()
                            N=random.randint(5,10)
                            try:
                                print(f'{dr1p4ns1._rgb(155,155,155)}<<<{dr1p4ns1._rgb(55,55,55)} sleeping {dr1p4ns1._rgb(155,155,155)}{N}{dr1p4ns1._rgb(55,55,55)} seconds before next autopump {dr1p4ns1._rgb(155,155,155)}>>> {dr1p4ns1._rgb(200,200,200)} {self.QUEUE[-1]}')
                            except:
                                pass
                            sleep(N)
                            try:
                                dr1p4ns1.AUTOQUEUE[0]+=1
                            except:
                                pass
                        else:
                            flag_done=True
                            print(f'{B}NO FILES LEFT IN QUEUE - {R} EXITING')
                            sleep(1)
                            break
                elif k == 'p':
                    print(f'{C}p.u.m.p.i.n.g ', end = "")
                    PMSG=""
                    try:
                        if FIFO_ENABLED:
                            if len(self.QUEUE) > 0:
                                self.QUEUE.reverse()
                                buffer_filename, buffer_width=self.QUEUE[-1]
                                self.QUEUE.pop()
                                self.QUEUE.reverse()
                                decoder=ANSIDecoder(buffer_filename,buffer_width)
                                PMSG=f'{M}pump: pumped from pumpqueue {C}{self.files[index]} {M}to fifo - {C}{FIFO_PATH}'
                            else:
                                try:
                                    del(dr1p4ns1.AUTOQUEUE)
                                except:
                                    pass
                                decoder=ANSIDecoder(self.files[index],d.width)
                                PMSG=f'{M}pump: nothing in pumpqueue, pumping from screen {C}{self.files[index]} {M}to fifo - {C}{FIFO_PATH}'
                            decoder.as_irc()
                        else:
                            PMSG=f'{M}pumping disabled - fifo not detected in path: {C}{FIFO_PATH}'
                    except Exception as e:
                            PMSG=f'{M}pumping failed - {e}'
                    decoder.as_terminal()
                    print(PMSG)

                elif k == 'R':
                    try:
                        if not self.files[index].find('/') == -1:
                            FILEPATH=f'{DELETE_PATH}/{str(self.files[index]).split("/")[1]}'
                            shutil.copyfile(self.files[index],FILEPATH)
                        else:
                            FILEPATH=f'{DELETE_PATH}/{self.files[index]}'
                            shutil.copyfile(self.files[index],FILEPATH)
                        os.remove(self.files[index])
                        self.REMOVED=True
                        buffer=self.files[index]
                        self.files.remove(self.files[index])
                        print(f'{R}{buffer} removed: placed in {DELETE_PATH} directory')
                    except:
                        print(f'{C}{self.files[index]} {M}was not removed')
                    sleep(3)
                    print('\x1bc')
                    decoder.as_terminal()

                else:
                    index+=1
                    if index >= len(self.files): index=0
                    try:
                        self.REMOVED=False
                        if len(self.files) == 0:
                            print(f'{B}NO FILES LEFT IN QUEUE - {R} EXITING')
                            sleep(1)
                            break
                        d=dr1p4ns1(ansifile=self.files[index],width=80,debug=False)
                        decoder=ANSIDecoder(self.files[index],d.width)
                        print('\x1bc')
                        decoder.as_terminal()
                    except:
                        pass
        #######################################
        except (KeyboardInterrupt, SystemExit):
            os.system('stty sane')
#############################################################################################################################
class ANSIDecodeError(ValueError):
    pass
#############################################################################################################################
class ANSIDecoder:
#############################################################################################################################
    #################
    ESCAPE_PUMP=False
    #################
    PALETTES = {
        'vga': {
            False: ['000', 'a00', '0a0', 'a50', '00a', 'a0a', '0aa', 'aaa'],
            True: ['555', 'f55', '5f5', 'ff5', '55f', 'f5f', '5ff', 'fff'],
        },
        'workbench': {
            False: ['aaa', '000', 'fff', '68b', '00f', 'f0f', '0ff', 'fff'],
            True: ['aaa', '000', 'fff', '68b', '00f', 'f0f', '0ff', 'fff'],
        }
    }
    #########################################################################################################################
    PALETTE_NAMES = PALETTES.keys()
    REMAPPED_CHARS = {
        '\x00': ' ',
        '\x01': '\u263a',
        '\x02': '\u263b',
        '\x03': '\u2665',
        '\x04': '\u2666',
        '\x05': '\u2663',
        '\x06': '\u2660',
        '\x10': '\u25ba',
        '\x11': '\u25c4',
        '\x16': '\u25ac',
        '\x1d': '\u2194',
        '\x1e': '\u25b2',
        '\x1f': '\u25bc',
    }
    #########################################################################################################################
    DEFAULT_FG = 7
    DEFAULT_BG = 0
    #########################################################################################################################
    Attribute = namedtuple('Attribute', ['fg', 'bg', 'bright', 'underline'])
    #########################################################################################################################
    DEFAULT_ATTR = Attribute(fg=DEFAULT_FG, bg=DEFAULT_BG, bright=False, underline=False)
    def __init__(self,files="",width=80):
        #stream=None, palette='vga', width=80, strict=False
        self.current_line = []
        self.buffer = [self.current_line]
        self.palette = self.PALETTES['vga']
        self.x = 0
        self.y = 0
        self.saved_x = 0
        self.saved_y = 0
        self.current_attr = self.DEFAULT_ATTR
        self.width = width
        self.strict = False
        self.filename=files
        if not type(self.filename)==str:
            self.filename=self.filename[0]
        print(self.filename)
        try:
            stream=open(self.filename,'rt',encoding='cp437')
            if stream:
                self.play(stream)
        except:
            pass
    #########################################################################################################################
    def write_char(self, char):
        # Handle an ordinary character
        try:
            self.current_line[self.x] = (self.current_attr, char)
        except IndexError:
            # X position is out of range; append to current line
            while len(self.current_line) < self.x:
                self.current_line.append((self.DEFAULT_ATTR, ' '))
            self.current_line.append((self.current_attr, char))
        self.x += 1
        if self.x >= self.width:
            self.write_newline()
    #########################################################################################################################
    def set_cursor(self, x=None, y=None):
        if x is not None:
            self.x = max(x, 0)
        if y is not None:
            self.y = max(y, 0)
        try:
            self.current_line = self.buffer[self.y]
        except IndexError:
            while len(self.buffer) <= self.y:
                self.current_line = []
                self.buffer.append(self.current_line)
    #########################################################################################################################
    def write_newline(self):
        # Handle a line break
        self.set_cursor(x=0, y=(self.y + 1))
    #########################################################################################################################
    def set_attr(self, fg=None, bg=None, bright=None, underline=None):
        if fg is None:
            fg = self.current_attr.fg
        if bg is None:
            bg = self.current_attr.bg
        if bright is None:
            bright = self.current_attr.bright
        if underline is None:
            underline = self.current_attr.underline
        self.current_attr = self.Attribute(fg=fg, bg=bg, bright=bright, underline=underline)
    #########################################################################################################################
    def write_escape(self, code, params):
        if code == 'm':
            for param in params:
                if param == 0:
                    self.current_attr = self.DEFAULT_ATTR
                elif param == 1:
                    self.set_attr(bright=True)
                elif param == 2 or param == 22:
                    self.set_attr(bright=False)
                elif param == 4:
                    self.set_attr(underline=True)
                elif param == 24:
                    self.set_attr(underline=False)
                elif 30 <= param <= 37:
                    self.set_attr(fg=(param - 30))
                elif 40 <= param <= 47:
                    self.set_attr(bg=(param - 40))
                else:
                    if self.strict:
                        raise ANSIDecodeError("Unsupported parameter to 'm' escape sequence: %r" % param)
        elif code == 'J':
            if params == [] or params == [0]:
                # erase from cursor to end of screen
                del self.current_line[self.x:]
                del self.buffer[(self.y + 1):]
            elif params == [1]:
                # erase up to cursor
                for i in range(0, self.y):
                    del self.buffer[i][:]
                try:
                    for i in range(0, self.x + 1):
                        self.current_line[i] = (self.DEFAULT_ATTR, ' ')
                except IndexError:
                    del self.current_line[:]
            elif params == [2]:
                # erase entire screen
                for i in range(0, self.y):
                    del self.buffer[i][:]
                del self.buffer[(self.y + 1):]
            else:
                if self.strict:
                    raise ANSIDecodeError("Unrecognised parameters to 'J' escape sequence: %r" % params)
        elif code == 'K':
            if params == [] or params == [0]:
                # erase from cursor to end of line
                del self.current_line[self.x:]
            elif params == [1]:
                # erase up to cursor
                try:
                    for i in range(0, self.x + 1):
                        self.current_line[i] = (self.DEFAULT_ATTR, ' ')
                except IndexError:
                    del self.current_line[:]
            elif params == [2]:
                # erase entire line
                del self.current_line[:]
            else:
                if self.strict:
                    raise ANSIDecodeError("Unrecognised parameters to 'K' escape sequence: %r" % params)
        elif code == 'A':
            # move cursor up N lines
            if not params:
                self.set_cursor(y=(self.y - 1))
            elif len(params) == 1:
                self.set_cursor(y=(self.y - params[0]))
            elif self.strict:
                raise ANSIDecodeError("Expected 0 or 1 param to 'A' escape sequence, got %d" % len(params))
        elif code == 'B':
            # move cursor down N lines
            if not params:
                self.set_cursor(y=(self.y + 1))
            elif len(params) == 1:
                self.set_cursor(y=(self.y + params[0]))
            elif self.strict:
                raise ANSIDecodeError("Expected 0 or 1 param to 'B' escape sequence, got %d" % len(params))
        elif code == 'C':
            # move cursor right N cols
            if not params:
                self.set_cursor(x=(self.x + 1))
            elif len(params) == 1:
                self.set_cursor(x=(self.x + params[0]))
            elif self.strict:
                raise ANSIDecodeError("Expected 0 or 1 param to 'C' escape sequence, got %d" % len(params))
        elif code == 'D':
            # move cursor left N cols
            if not params:
                self.set_cursor(x=(self.x - 1))
            elif len(params) == 1:
                self.set_cursor(x=(self.x - params[0]))
            elif self.strict:
                raise ANSIDecodeError("Expected 0 or 1 param to 'D' escape sequence, got %d" % len(params))
        elif code == 'E':
            # move cursor to beginning of next line, N lines down
            if not params:
                self.set_cursor(x=0, y=(self.y + 1))
            elif len(params) == 1:
                self.set_cursor(x=0, y=(self.y + params[0]))
            elif self.strict:
                raise ANSIDecodeError("Expected 0 or 1 param to 'E' escape sequence, got %d" % len(params))
        elif code == 'F':
            # move cursor to beginning of previous line, N lines up
            if not params:
                self.set_cursor(x=0, y=(self.y - 1))
            elif len(params) == 1:
                self.set_cursor(x=0, y=(self.y - params[0]))
            elif self.strict:
                raise ANSIDecodeError("Expected 0 or 1 param to 'F' escape sequence, got %d" % len(params))
        elif code == 'G':
            # move cursor to column N
            if len(params) == 1:
                self.set_cursor(x=params[0])
            elif self.strict:
                raise ANSIDecodeError("Expected 1 param to 'G' escape sequence, got %d" % len(params))
        elif code == 'H' or code == 'f':
            # move cursor to (line, col)
            if not params:
                self.set_cursor(x=0, y=0)
            elif len(params) == 2:
                self.set_cursor(x=params[1] - 1, y=params[0] - 1)
            elif self.strict:
                raise ANSIDecodeError("Expected 0 or 2 param to '%s' escape sequence, got %d" % (code, len(params)))
        elif code == 'h':
            if params == [7]:
                # enable line wrapping
                pass
            elif self.strict:
                raise ANSIDecodeError("Unrecognised params to 'h' - got %r" % params)
        elif code == 's':
            # save cursor position
            if params and self.strict:
                raise ANSIDecodeError("Unrecognised params to 's' - got %r" % params)
            self.saved_x = self.x
            self.saved_y = self.y
        elif code == 'u':
            # restore cursor position
            if params and self.strict:
                raise ANSIDecodeError("Unrecognised params to 'u' - got %r" % params)
            self.set_cursor(x=self.saved_x, y=self.saved_y)
        else:
            if self.strict:
                raise ANSIDecodeError("Unrecognised escape code: %r" % code)
    #########################################################################################################################
    def play(self, f):
        while True:
            # Read file a character at a time
            char = f.read(1)
            if not char:
                # End of file
                break
            elif char >= ' ':
                self.write_char(char)
            elif char == '\x0a':  # LF
                self.write_newline()
            elif char == '\x0d':  # CR
                continue
            elif char in self.REMAPPED_CHARS:
                self.write_char(self.REMAPPED_CHARS[char])
            elif char == '\x1a':
                # EOF when using SAUCE records
                break
            elif char == '\x1b':
                # Handle an escape sequence
                char = f.read(1)
                # Next character is expected to be '['
                if char != '[':
                    if self.strict:
                        raise ANSIDecodeError("Unrecognised character after ESC: %r" % char)
                    continue
                params = []
                param = None
                private = False
                while True:
                    char = f.read(1)
                    if '0' <= char <= '9':
                        if param is None:
                            param = int(char)
                        else:
                            param = param * 10 + int(char)
                    elif char == ';':
                        if param is None:
                            if self.strict:
                                raise ANSIDecodeError("Encountered ';' character without parameter")
                        else:
                            params.append(param)
                            param = None
                    elif char == ' ':
                        pass
                    elif char == '?':
                        private = True
                    else:
                        if param is not None:
                            params.append(param)

                        if not private:
                            self.write_escape(char, params)
                        break
            elif self.strict:
                raise ANSIDecodeError("Unrecognised character: %r" % char)
    #########################################################################################################################
    def fifo(self,s):
        s=[s]
        f=open(FIFO_PATH,"w")
        [f.write("*{}\n".format(x)) for x in s]
        f.close()
    ################################################################################################### RE-ENCODING ENCODINGS
    def as_irc_lines(self):
        default_fg=7
        default_bg=0
        color_table=[1,5,3,7,2,6,10,15,14,4,9,8,12,13,11,0]
        color_fg=color_table[default_fg]
        color_bg=color_table[default_bg]
        offset_bold=0
        reset_bold=False
        last_attr = None
        processing=''
        for i,line in enumerate(self.buffer):
            for n,(attr, char) in enumerate(line):
            ####################################################################################### FOR LINE
                if attr[2]:
                    offset_bold=8
                else:
                    offset_bold=0
                color_fg=color_table[attr[0]+offset_bold]
                color_bg=color_table[attr[1]]
                if attr != last_attr:
                    if last_attr==None:
                        processing+=f'\x03{color_table[attr[0]]},{color_table[attr[1]]}'
                        color_fg=color_table[attr[0]]
                        color_bg=color_table[attr[1]]
                    else:
                        if n==0 and not attr[0]==default_fg:
                            if attr[2]:
                                processing+=f'\x03{color_table[attr[0]+offset_bold]},{color_table[attr[1]]}'
                                color_fg=color_table[attr[0]+offset_bold]
                                color_bg=color_table[attr[1]]
                            else:
                                processing+=f'\x03{color_table[attr[0]]},{color_table[attr[1]]}'
                                color_fg=color_table[attr[0]]
                                color_bg=color_table[attr[1]]
                        elif n==0 and attr[2]:
                                processing+=f'\x03{color_table[attr[0]+offset_bold]},{color_table[attr[1]]}'
                                color_fg=attr[0]+offset_bold
                                color_bg=attr[1]
                        if not attr[2]==last_attr[2] and not n==0:
                            if attr[0] == last_attr[0] and not attr[1] == last_attr[1]:
                                processing+=f'\x03{color_table[attr[0]+offset_bold]},{color_table[attr[1]]}'
                                color_fg=attr[0]+offset_bold
                                color_bg=attr[1]
                            elif attr[0] == last_attr[0] and attr[1] == last_attr[1]:
                                processing+=f'\x03{color_table[attr[0]+offset_bold]}'
                                color_fg=attr[0]+offset_bold
                            elif not attr[0] == last_attr[0] and attr[1] == last_attr[1]:
                                processing+=f'\x03{color_table[attr[0]+offset_bold]}'
                                color_fg=attr[0]+offset_bold
                            elif not attr[0] == last_attr[0] and not attr[1] == last_attr[1]:
                                processing+=f'\x03{color_table[attr[0]+offset_bold]},{color_table[attr[1]]}'
                                color_fg=attr[0]+offset_bold
                                color_bg=attr[1]
                        elif not attr[0]==last_attr[0] and not attr[1]==last_attr[1] and not n==0:
                            processing+=f'\x03{color_table[attr[0]+offset_bold]},{color_table[attr[1]]}'
                            color_fg=attr[0]+offset_bold
                            color_bg=attr[1]
                        elif not attr[0]==last_attr[0] and not n==0:
                            if attr[2]:
                                processing+=f'\x03{color_table[attr[0]+offset_bold]}'
                                color_fg=attr[0]+offset_bold
                            else:
                                if not '\x30' <= char <= '\x39':
                                    processing+=f'\x03{color_table[attr[0]]},{color_table[attr[1]]}'
                                else:
                                    processing+=f'\x03{color_table[attr[0]]},{str(color_table[attr[1]]).zfill(2)}'
                                color_fg=attr[0]
                        elif not attr[1]==last_attr[1] and not n==0:
                            if attr[2]:
                                processing+=f'\x03{color_table[attr[0]+offset_bold]},{color_table[attr[1]]}'
                                color_fg=attr[0]+offset_bold
                                color_bg=attr[1]
                            else:
                                processing+=f'\x03{color_table[attr[0]]},{color_table[attr[1]]}'
                                color_fg=attr[0]
                                color_bg=attr[1]
                else:
                    if n==0 and not attr[0]==default_fg:
                        if attr[2]:
                            processing+=f'\x03{color_table[attr[0]+offset_bold]},{color_table[attr[1]]}'
                            color_fg=color_table[attr[0]+offset_bold]
                            color_bg=color_table[attr[1]]
                        else:
                            processing+=f'\x03{color_table[attr[0]]},{color_table[attr[1]]}'
                            color_fg=color_table[attr[0]]
                            color_bg=color_table[attr[1]]
                if len(processing) == 0:
                    processing+=f"\x0315,1"
                processing+=char
                last_attr=attr
            if len(self.buffer[i]) == 0:
                self.output_lines.append(f"\x031,1{'█'*self.width}")
            else:
                distance=int(self.width)-len(line)
                processing+=f"\x031,1{'█'*distance}"
                self.output_lines.append(processing)
            processing=''
    ######################################################################################################################### dr1p4ns1.AUTOQUEUE=[4] #0=index,1=len,2=list,3=filename
    def getkeythreaded(self):
        tty.setcbreak(sys.stdin.fileno())
        try:
            print(f'{dr1p4ns1._rgb(55,55,55)}<<<{dr1p4ns1._rgb(155,155,155)} press any key to abort pump {dr1p4ns1._rgb(55,55,55)}>>> {dr1p4ns1._rgb(155,155,155)}-{dr1p4ns1._rgb(200,200,200)} {dr1p4ns1.AUTOQUEUE[0]+1}{dr1p4ns1._rgb(155,155,155)}/{dr1p4ns1._rgb(200,200,200)}{dr1p4ns1.AUTOQUEUE[1]}{dr1p4ns1._rgb(155,155,155)}')
        except:
            print(f'{dr1p4ns1._rgb(55,55,55)}<<<{dr1p4ns1._rgb(155,155,155)} press any key to abort pump {dr1p4ns1._rgb(55,55,55)}>>> {dr1p4ns1._rgb(155,155,155)}')
        b = os.read(sys.stdin.fileno(), 3).decode()
        if len(b) == 3:
            k = ord(b[2])
        else:
            k = ord(b)
        self.ESCAPE_PUMP=True
    #########################################################################################################################
    def as_irc(self):
        DID_I_ABORT=False
        self.output_lines=[]
        self.as_irc_lines()
        x=threading.Thread(target=self.getkeythreaded)
        x.start()
        for _ in self.output_lines:
            self.fifo(_)
            sleep(dr1p.delay)
            if self.ESCAPE_PUMP:
                DID_I_ABORT=True
                break
        if SHOW_FILENAME:
            try:
                self.fifo(f"\x0315FILENAME\x0314: \x0315{self.filename.split('/')[-1]} \x0314- \x0315DR1P4NS1\x0314:\x0315 {DR1P_VERSION}\x0314 - \x0315{dr1p4ns1.AUTOQUEUE[0]+1}\x0314/\x0315{dr1p4ns1.AUTOQUEUE[1]}")
            except:
                try:
                    self.fifo(f"\x0315FILENAME\x0314: \x0315{self.filename.split('/')[-1]} \x0314- \x0315DR1P4NS1\x0314:\x0315 {DR1P_VERSION}\x0314")
                except:
                    self.fifo(f"\x0315FILENAME\x0314: \x0315{self.filename[0].split('/')[-1]} \x0314- \x0315DR1P4NS1\x0314:\x0315 {DR1P_VERSION}\x0314")
        try:
            x._stop()
        except:
            pass
        self.ESCAPE_PUMP=False
        if DID_I_ABORT:
            print(f'\x1b[31m<<< ABORTED PUMP >>>')
            self.fifo("\x035<<< ABORTED PUMP >>>")
            sleep(3)
    ################################################################################################### RE-ENCODING ENCODINGS
    def as_terminal_lines(self):
        default_fg=7
        default_bg=0
        last_attr = None
        processing=''
        for i,line in enumerate(self.buffer):
            for n,(attr, char) in enumerate(line):
                if attr != last_attr:
                    if last_attr == None:
                        processing+=f'\x1b[{attr[0]+30};{attr[1]+40}m'
                    else:
                        if n==0 and not attr[0]==default_fg:
                            if attr[2]:
                                processing+=f'\x1b[1;{attr[0]+30};{attr[1]+40}m'
                            else:
                                processing+=f'\x1b[0;{attr[0]+30};{attr[1]+40}m'
                        elif n==0 and attr[2]:
                                processing+=f'\x1b[1;{attr[0]+30};{attr[1]+40}m'
                        elif n==0 and not attr[2]:
                            processing+=f'\x1b[0;{attr[0]+30};{attr[1]+40}m'
                        if not attr[2]==last_attr[2] and not n==0:
                            if attr[0] == last_attr[0] and not attr[1] == last_attr[1]:
                                if attr[2]:
                                    processing+=f'\x1b[1;{attr[0]+30};{attr[1]+40}m'
                                else:
                                    processing+=f'\x1b[0;{attr[0]+30};{attr[1]+40}m'
                            elif attr[0] == last_attr[0] and attr[1] == last_attr[1]:
                                if attr[2]:
                                    processing+=f'\x1b[1;{attr[0]+30}m'
                                else:
                                    processing+=f'\x1b[0;{attr[0]+30}m'
                            elif not attr[0] == last_attr[0] and attr[1] == last_attr[1]:
                                if attr[2]:
                                    processing+=f'\x1b[1;{attr[0]+30}m'
                                else:
                                    processing+=f'\x1b[0;{attr[0]+30}m'
                            elif not attr[0] == last_attr[0] and not attr[1] == last_attr[1]:
                                if attr[2]:
                                    processing+=f'\x1b[1;{attr[0]+30};{attr[1]+40}m'
                                else:
                                    processing+=f'\x1b[0;{attr[0]+30};{attr[1]+40}m'
                        elif not attr[0]==last_attr[0] and not attr[1]==last_attr[1] and not n==0:
                            if attr[2]:
                                processing+=f'\x1b[1;{attr[0]+30};{attr[1]+40}m'
                            else:
                                processing+=f'\x1b[0;{attr[0]+30};{attr[1]+40}m'
                        elif not attr[0]==last_attr[0] and not n==0:
                            if attr[2]:
                                processing+=f'\x1b[1;{attr[0]+30}m'
                            else:
                                processing+=f'\x1b[{attr[0]+30};{attr[1]+40}m'
                        elif not attr[1]==last_attr[1] and not n==0:
                            if attr[2]:
                                processing+=f'\x1b[1;{attr[0]+30};{attr[1]+40}m'
                            else:
                                processing+=f'\x1b[0;{attr[0]+30};{attr[1]+40}m'
                else:
                    if n==0 and not attr[0]==default_fg:
                        if attr[2]:
                            processing+=f'\x1b[1;{attr[0]+30};{attr[1]+40}m'
                        else:
                            processing+=f'\x1b[0;{attr[0]+30};{attr[1]+40}m'
                processing+=char
                last_attr=attr
            if len(self.buffer[i]) == 0:
                self.output_lines.append(f"{'█'*self.width}")
            else:
                distance=int(self.width)-len(line)
                processing+=f"{'█'*distance}"
                self.output_lines.append(processing)
            # self.output_lines.append(processing + '\x1b[40;37m')
            processing=''
    #########################################################################################################################
    def as_terminal(self):
        self.output_lines=[]
        self.as_terminal_lines()
        for _ in self.output_lines:
            print(_)

################################################################################################################### ENTRY - 1
if __name__=="__main__":
    dr1p=D()
    FIFO_ENABLED = os.path.exists(FIFO_PATH)
    dr1p.files = getfiles(sys.argv)
    ui = UI(dr1p.files)
####################################################################################################  #######################
########################################################################################### dr1p4ns1  version 2.667 ##### EOF
