d4c15687c8
Some BIOSs initialize the VGA card by default into a mode where the high bit of background nybble signals that the cell should blink. The simple way to avoid this is by restricting the background colours to the range 0…7. However, since our mouse cursor is implemented by swapping the foreground and the background colours, we also need to restrict the foreground colours to the range 0…7.
141 lines
3.9 KiB
Python
141 lines
3.9 KiB
Python
import os
|
|
import sys
|
|
|
|
from collections import namedtuple
|
|
|
|
Attribute = namedtuple('Attribute', ['fg', 'bg', 'bold'])
|
|
|
|
WIDTH = 80
|
|
HEIGHT = 25
|
|
# ANSI orders the colours black, red, green, yellow, blue, magenta, cyan, white
|
|
# VGA orders them black, blue, green, cyan, red, magenta, yellow, white
|
|
color_map = [0, 4, 2, 6, 1, 5, 3, 7]
|
|
|
|
if len(sys.argv) != 7:
|
|
print("Usage: {sys.argv[0]} outfile infile default_fgcolor default_bgcolor origin_x origin_y", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
blinky_vgamode = False
|
|
if 'BUILDOPTS' in os.environ:
|
|
for opt in os.environ['BUILDOPTS'].split():
|
|
if opt == '-DBLINKY':
|
|
blinky_vgamode = True
|
|
else:
|
|
print(f"Error: Unrecognized build option {opt}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
outfile = sys.argv[1]
|
|
infile = sys.argv[2]
|
|
|
|
default_fgcolor = int(sys.argv[3])
|
|
assert 0 <= default_fgcolor <= 15
|
|
default_bgcolor = int(sys.argv[4])
|
|
assert 0 <= default_bgcolor <= 15
|
|
|
|
origin_x = int(sys.argv[5])
|
|
assert 0 <= origin_x < WIDTH
|
|
origin_y = int(sys.argv[6])
|
|
assert 0 <= origin_y < HEIGHT
|
|
|
|
chars = [bytearray([0]*HEIGHT) for _ in range(WIDTH)]
|
|
attributes = [[(Attribute(default_fgcolor, default_bgcolor, False))]*HEIGHT for _ in range(WIDTH)]
|
|
|
|
with open(infile, 'rb') as f:
|
|
ansitext = f.read()
|
|
|
|
x = origin_x
|
|
y = origin_y
|
|
fgcolor = default_fgcolor
|
|
bgcolor = default_bgcolor
|
|
bold = False
|
|
|
|
error = False
|
|
|
|
line = 1
|
|
line_start = 0
|
|
index = 0
|
|
while index < len(ansitext):
|
|
if ansitext[index:].startswith(b'\x1b['):
|
|
index += len(b'\x1b[')
|
|
escape_start = index
|
|
while index < len(ansitext) and ansitext[index] not in b'CDHJhm':
|
|
index += 1
|
|
escape_params = ansitext[escape_start:index]
|
|
escape_type = ansitext[index:index+1]
|
|
index += 1
|
|
if escape_type == b'C':
|
|
x += int(escape_params)
|
|
elif escape_type == b'D':
|
|
x -= int(escape_params)
|
|
elif escape_type == b'H':
|
|
row, column = escape_params.split(b';')
|
|
y = int(row) - 1
|
|
x = int(column) - 1
|
|
elif escape_type == b'J':
|
|
# Erase display
|
|
# We just ignore this, because why would you have
|
|
# erase command anywhere but at the start
|
|
pass
|
|
elif escape_type == b'h':
|
|
# Something nonstandard, ignore
|
|
pass
|
|
elif escape_type == b'm':
|
|
for color_parameter in escape_params.split(b';'):
|
|
color_parameter = int(color_parameter)
|
|
if color_parameter == 0:
|
|
bold = False
|
|
elif color_parameter == 1:
|
|
bold = True
|
|
elif color_parameter == 39:
|
|
fgcolor = default_fgcolor
|
|
elif color_parameter == 49:
|
|
bgcolor = default_bgcolor
|
|
elif 30 <= color_parameter <= 37:
|
|
fgcolor = color_map[color_parameter - 30]
|
|
elif 40 <= color_parameter <= 47:
|
|
bgcolor = color_map[color_parameter - 40]
|
|
elif 90 <= color_parameter <= 97:
|
|
fgcolor = color_map[color_parameter - 90] + 8
|
|
elif 100 <= color_parameter <= 107:
|
|
bgcolor = color_map[color_parameter - 100] + 8
|
|
else:
|
|
print(f'{line},{escape_start-line_start+1}: Unknown colour escape {color_parameter}')
|
|
error = True
|
|
else:
|
|
print(f'{line},{escape_start-line_start+1}: Unknown escape ^[[{escape_params.decode()}{escape_type.decode()}')
|
|
error = True
|
|
elif ansitext[index] == 13:
|
|
x = origin_x
|
|
index += 1
|
|
elif ansitext[index] == 10:
|
|
for i in range(x, WIDTH):
|
|
attributes[i][y] = Attribute(fgcolor, bgcolor, bold)
|
|
x = origin_x
|
|
y += 1
|
|
index += 1
|
|
line += 1
|
|
line_start = index
|
|
else:
|
|
chars[x][y] = ansitext[index]
|
|
attributes[x][y] = Attribute(fgcolor, bgcolor, bold)
|
|
index += 1
|
|
x += 1
|
|
|
|
if error:
|
|
sys.exit(1)
|
|
|
|
with open(outfile, 'wb') as f:
|
|
for y in range(HEIGHT):
|
|
for x in range(WIDTH):
|
|
fgcolor, bgcolor, bold = attributes[x][y]
|
|
fgcolor = fgcolor | (8 if bold else 0)
|
|
if blinky_vgamode:
|
|
# VGA mode can be set up so that the highest
|
|
# bit of the bg colour marks that the cell
|
|
# should blink instead of intensity
|
|
# Restrict the colours (including foreground
|
|
# to account for swapping of the two) to 0…7
|
|
fgcolor = fgcolor & 0x7
|
|
bgcolor = bgcolor & 0x7
|
|
char = chars[x][y]
|
|
f.write(bytes([char, (bgcolor<<4) | fgcolor]))
|