This commit is contained in:
decoded
2022-02-21 16:49:47 -06:00
parent 64457efc5c
commit 7509104ca3
2 changed files with 251 additions and 129 deletions

View File

@@ -33,13 +33,14 @@ MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNs. -hMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMdyymMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMdyymMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
``` ```
``` ```
cp437 ansi parsing - this will be integrated into bots like krylon/maple/g1mp when finished. dr1p4ns1 v1.2 - cp437 ansi parsing - this logic will be rolled into bots like krylon/maple/g1mp
``` ```
## prerequisites ## prerequisites
- `apt install python3` - `apt install python3`
## usage - main file ## usage - main file
``` ```
running 'python3 dr1p4ns1.py' will batch process all the {ans,asc} files in the ansiscii directory running 'python3 dr1p4ns1.py' will batch process all the {ans,asc} files in the ansiscii directory
running 'python3 dr1p4ns1.py /path/to/ansi/files' will load all the .ans files in that directory
``` ```
## test data and debugging ## test data and debugging
- running `python3 test.py` will present table data and create an ansi file for testing - running `python3 test.py` will present table data and create an ansi file for testing
@@ -47,18 +48,11 @@ running 'python3 dr1p4ns1.py' will batch process all the {ans,asc} files in the
```d=dr1p4ns1(ansifile=_FILE_,width=80,debug=False)``` ```d=dr1p4ns1(ansifile=_FILE_,width=80,debug=False)```
## files overview ## files overview
``` ```
/access.log - after usage the files laded or with error are logged into this file
/dr1p4ns1.py - main file, reads 'work.ans' and attempts to parse and display /dr1p4ns1.py - main file, reads 'work.ans' and attempts to parse and display
/ansiscii/ - storing the ansi and ascii graphics in this subdirectory while testing
/test.py - used to generate test ansi file and presents table data /test.py - used to generate test ansi file and presents table data
/test.ans - thie file is created after running test.py to contain controlled test data /test.ans - thie file is created after running test.py to contain controlled test data
/ansiscii/ - storing the ansi and ascii graphics in this subdirectory while testing
/ansiscii/work.ans - this ansi file is loaded by 'dr1p4ns1.py', this is the logic testing
/ansiscii/work.asc - escape codes/colors stripped in text for test referencing
``` ```
## main files of interest ## main files of interest
- dr1p4ns1.py - dr1p4ns1.py
- work.ans
- work.asc
- test.py
- test.ans
``` ```

View File

@@ -2,7 +2,7 @@
#################################################################################################### ####################### #################################################################################################### #######################
from glob import glob from glob import glob
from time import sleep from time import sleep
import os import sys,tty,os,termios
"""MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM ####################### """MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMh+MMMMMMMMMMMMMMhsMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM ####################### MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMh+MMMMMMMMMMMMMMhsMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM #######################
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMm/ oMMMMMMMMMMMMMMm +NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM ####################### MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMm/ oMMMMMMMMMMMMMMm +NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM #######################
@@ -81,7 +81,7 @@ class dr1p4ns1:
'm': 'color', 'm': 'color',
'R': 'report_cursor_position'} 'R': 'report_cursor_position'}
############################################################################################################ LOOKUP TABLE ############################################################################################################ LOOKUP TABLE
asc_table=""" asc_table="""\
╟ⁿΘΓΣαστΩδΦ∩ε∞─┼╔µ╞⌠÷≥√∙ ╓▄óúÑ₧ƒ\ ╟ⁿΘΓΣαστΩδΦ∩ε∞─┼╔µ╞⌠÷≥√∙ ╓▄óúÑ₧ƒ\
ßφ≤·±╤¬║┐⌐¼╜╝í½╗░▒▓│┤╡╢╖╕╣║╗╝╜╛┐\ ßφ≤·±╤¬║┐⌐¼╜╝í½╗░▒▓│┤╡╢╖╕╣║╗╝╜╛┐\
└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀\ └┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀\
@@ -90,10 +90,24 @@ class dr1p4ns1:
for r in (('\n',''),(' ','')): for r in (('\n',''),(' ','')):
asc_table=asc_table.replace(*r) asc_table=asc_table.replace(*r)
############################################################################################################ LOOKUP TABLE ############################################################################################################ LOOKUP TABLE
asc_table_full="""\
\x00☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼\
\_!"#$%&\'()*+,-./0123456789:;<=>\
?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^\
_`abcdefghijklmnopqrstuvwxyz{|}~\
⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧\
ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛\
┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐\
▀ɑϐᴦᴨ∑ơµᴛɸϴΩẟ∞∅∈∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\
ÿ"""
##################################################################################################### ASCII TABLE CLEANUP
for r in ((' ',''),('\_',' ')):
asc_table_full=asc_table_full.replace(*r)
############################################################################################################ LOOKUP TABLE
asc2uni=dict(zip([i for i in range(128,256)],[ord(c) for c in asc_table])) asc2uni=dict(zip([i for i in range(128,256)],[ord(c) for c in asc_table]))
############################################################################################################ LOOKUP TABLE ############################################################################################################ LOOKUP TABLE
asc2chr=dict(zip([i for i in range(128,256)],[c for c in asc_table])) asc2chr=dict(zip([i for i in range(128,256)],[c for c in asc_table]))
############################################################################################################ LOOKUP TABLE ############################################################################################################ LOOKUP TABLE
rgb2irc = { 52: '16', 94: '17', 100: '18', 58: '19', rgb2irc = { 52: '16', 94: '17', 100: '18', 58: '19',
22: '20', 29: '21', 23: '22', 24: '23', 22: '20', 29: '21', 23: '22', 24: '23',
17: '24', 54: '25', 53: '26', 89: '27', 17: '24', 54: '25', 53: '26', 89: '27',
@@ -122,10 +136,12 @@ class dr1p4ns1:
self.height=height self.height=height
self.filename=ansifile self.filename=ansifile
self.openansi(ansifile) self.openansi(ansifile)
self.openascii() #self.openascii()
self.code='\x1b[0m' self.code='\x1b[0m'
self.cmps=[] self.cmps=[]
print("\x1bc\x1b[1;31m[ DR1P ]\x1b[0m\n") self.op=[]
self.optype=[]
self.sauce_found=False
self.boot() self.boot()
######################################################################################################### FILE OPERATIONS ######################################################################################################### FILE OPERATIONS
def fifo(self,s): def fifo(self,s):
@@ -194,9 +210,6 @@ class dr1p4ns1:
print(f"{hex(16 * _)[2:].zfill(8)} {thehex[_]} {theasc[_]}") print(f"{hex(16 * _)[2:].zfill(8)} {thehex[_]} {theasc[_]}")
############################################################################################################### DEBUGGING ############################################################################################################### DEBUGGING
def hexdump(self): def hexdump(self):
if self.DEBUG:
print("\n\x1b[1;31m[ HEXDUMP ]\x1b[0m\n")
self.printhex()
self.codes=[] self.codes=[]
code_start=0 code_start=0
code_end=0 code_end=0
@@ -216,25 +229,173 @@ class dr1p4ns1:
code_end=i+1 code_end=i+1
code=self.ansifile[code_start:code_end] code=self.ansifile[code_start:code_end]
self.codes.append([code,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(code[1:])
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=[]
processing=''
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(';'):
if 0 <= int(_) <= 9:
color_set=_
if 30 <= int(_) <= 39:
color_fg=_
if 40 <= int(_) <= 49:
color_bg=_
colors=f"{color_set}"
processed_colors.append(colors)
# last_processed_color_set=0
# for i,_ in enumerate(processed):
# line=''
# if last_processed_color_set:
# line+='\x1b[1m'
# if int(processed_colors[i]):
# line+=f'{_}\x1b[0m'
# else:
# line+=f'{_}'
# last_processed_color_set=int(processed_colors[i])
# for r in (('\x16',chr(9644)),('\x15','\u00A7')):
# line=line.replace(*r)
# print(line+f" - {str(i).zfill(3)}")
for _ in processed:
line=_
for r in (('\x16',chr(9644)),('\x15','\u00A7')):
line=line.replace(*r)
print(line)
#################################################################################################################### TOOL
def getsauce(self,sauce):
self.sauce=sauce
self.sauce_found=True
self.width=ord(self.sauce[96])
self.height=ord(self.sauce[98])
offset=len("SAUCE")
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
# SAUCE_PASS='\x1b[1;31m[ SAUCE INFO FOUND ] - X: {} Y: {}\x1b[0m\n'
# SAUCE_FAIL='\x1b[1;31m[ NO SAUCE INFO FOUND ]\x1b[0m\n'
# if self.sauce_found==True:
# print(SAUCE_PASS.format(self.width,self.height))
# else:
# print(SAUCE_FAIL.format(self))
#################################################################################################################### TOOL #################################################################################################################### TOOL
def findall(self,s,w): def findall(self,s,w):
return [i for i in range(len(s)) if s.startswith(w, i)] return [i for i in range(len(s)) if s.startswith(w, i)]
#################################################################################################################### TOOL
def getsauce(self):
SPASS='\x1b[1;31m[ SAUCE INFO FOUND ] - X: {} Y: {} - FILE: {}\x1b[0m\n'
SFAIL='\x1b[1;31m[ NO SAUCE INFO FOUND ] - FILE: {}\x1b[0m\n'
try:
if self.ansifile[-129:][0] == '\x1a' \
and self.ansifile[-128:][:5] == 'SAUCE':
self.ansifile_xwidth=ord(self.ansifile[-128:][96])
self.ansifile_yheight=ord(self.ansifile[-128:][98])
self.width=self.ansifile_xwidth
self.height=self.ansifile_yheight
print(SPASS.format(self.width,self.height,self.filename))
else:
print(SFAIL.format(self.filename))
except:
print(SFAIL.format(self.filename))
############################################################################################################### DEBUGGING ############################################################################################################### DEBUGGING
def cmpans(self,s1,s2,n1=0,n2=0): def cmpans(self,s1,s2,n1=0,n2=0):
_s1=''; _s2=''; _sn1=''; _sn2='' _s1=''; _s2=''; _sn1=''; _sn2=''
@@ -300,104 +461,71 @@ class dr1p4ns1:
return {v: k for k, v in d.items()} return {v: k for k, v in d.items()}
########################################################################################################## CLASS MAIN - 4 ########################################################################################################## CLASS MAIN - 4
def boot(self): def boot(self):
self.getsauce() if self.DEBUG:
self.hexdump() print("\n\x1b[1;31m[ HEXDUMP ]\x1b[0m\n")
op=[] self.printhex()
fb=self.ansifile self.codedump()
xnew=0 self.processdump()
xold=0 #############################################################################################################################
for _ in self.codes: def getkey():
xnew=_[1] old_settings = termios.tcgetattr(sys.stdin)
distance=xnew-xold tty.setcbreak(sys.stdin.fileno())
if not distance == 0: try:
gap=fb[xold:xold+distance] while True:
op.append(gap) b = os.read(sys.stdin.fileno(), 3).decode()
op.append(_[0]) if len(b) == 3:
xold=_[2] k = ord(b[2])
optype=[]
for i,_op in enumerate(op):
if _op.startswith('\x1b'):
if _op.endswith('m'):
optype.append(2)
elif _op.endswith('C'):
optype.append(3)
else:
optype.append(0)
elif _op.startswith('\x1a'):
optype.append(4)
else: else:
optype.append(1) k = ord(b)
offset=0 key_mapping = {
offsets=[] 27: 'esc',
processed=[] 67: 'right',
uniqued=[] 68: 'left',
uniques=[] 113: 'q',
processing='' 120: 'x'
for i,_optype in enumerate(optype): }
if _optype==2: return key_mapping.get(k, chr(k))
processing+=op[i] finally:
offset+=len(op[i]) termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
elif _optype==3:
count=int(op[i].split('[')[1].split('C')[0])
processing+=' '*count
elif _optype==1:
processing+=op[i]
_bseq='\r\n'
_xpos=processing.find(_bseq)
if not _xpos == -1:
uniques.append(_bseq)
uniques.append(_xpos)
processing=processing.replace(_bseq,'',1)
gaps=self.width-len(self.stripcodes(processing))
processing+=' '*int(gaps)
if len(processing)-offset > self.width:
while len(processing)-offset > self.width:
deconcatenate=processing[0:self.width+offset]
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
# if self.DEBUG:
# print("\n\x1b[1;31m[ COMPARE LINE AGAINST REFERENCE ]\x1b[0m\n")
# _r=self.reference.strip().splitlines()
# for i,_ in enumerate(processed):
# s1=_r[i]
# s2=_
# self.cmpans(s1=s1,s2=s2,n1=0,n2=self.width)
# if i >= 128:
# break
# for i,_ in enumerate(self.cmps):
# print(_+f' - {str(i).zfill(4)}')
# if self.DEBUG:
# print("\n \x1b[1;31m[ PROCESSED ANSI ]\x1b[0m\n")
for i,_ in enumerate(processed):
print(_)
################################################################################################################### ENTRY - 1 ################################################################################################################### ENTRY - 1
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) > 1:
PATH=''
if sys.argv[1]=='.':
PATH=os.getcwd()
files=glob(PATH+"/*.ans")
files+=glob(PATH+"/*.ANS")
else:
files=glob(sys.argv[1]+"/*.ans")
files+=glob(sys.argv[1]+"/*.ANS")
else:
files=glob('ansiscii/*.ans')
files+=glob('ansiscii/*.ANS')
if len(files)==0:
print('ERROR: NO ANSI FILES IN THE PATH SPECIFIED',end='')
sys.exit(1)
index=0
d=dr1p4ns1(ansifile=files[index],width=80,debug=False)
try: try:
os.remove('access.log') while True:
except: B='\x1b[1;94m'; C='\x1b[1;94m'; S='\x1b[1;36m'; M='\x1b[1;92m'; E='\x1b[0m'
pass msg=f"{B}[ {C}DR1P {B}] {S}- {B}[ {C}q{S}: {M}quit{S}, {C}left{S}: {M}previous{S}, {C}right{S}: {M}next {B}] {S}- {C}filename{S}: {M}"
files=glob('ansiscii/*.ans') msg+=f"{d.filename}{E}"
files+=glob('ansiscii/*.ANS') print(msg)
for _FILE_ in files: k = getkey()
print(f'\n[ LOADING FILE ] - {_FILE_}\n') if k == 'left':
try: index-=1
d=dr1p4ns1(ansifile=_FILE_,width=80,debug=False) if index < 0: index=len(files)-1
f=open('access.log','a') d=dr1p4ns1(ansifile=files[index],width=80,debug=False)
f.write(f'LOADED: {_FILE_}\n') if k == 'right':
f.close() index+=1
except Exception as e: if index >= len(files): index=0
print(f'[ ERROR WITH FILE: {_FILE_} ] - {e}') d=dr1p4ns1(ansifile=files[index],width=80,debug=False)
f=open('access.log','a') if k == 'esc' or k == 'q' or k == 'x':
f.write(f'FAILED: {_FILE_} - {e}\n') quit()
f.close() except (KeyboardInterrupt, SystemExit):
del(d) os.system('stty sane')
sleep(5)
######################################################################################################################### EOF ######################################################################################################################### EOF