# Run-length compression used in Dracu-riot UI images

import struct
import sys

bpp = 4

def make_bmp_hdr(width, height):
	magic = b'BM'
	bmp_size = 0x36 + bpp*width*height
	offset = 0x36
	hdr_size = 0x28
	cplanes = 1
	bitpp = 8*bpp
	cmp = 0
	img_size = bpp*width*height
	hres = 0
	vres = 0
	ncolors = 0
	icolors = 0
	hdr = struct.pack('<2sIHHIIIIHHIIIIII', magic, bmp_size, 0, 0, offset, hdr_size, width, height, cplanes, bitpp, cmp, img_size, hres, vres, ncolors, icolors)
	return hdr
	
def read_bmp_hdr(hdr):
	offset, hdr_size, width, height = struct.unpack('<IIII', hdr[10:26])
	return offset, width, height

def rle2bin(rle_data):
	pos = 0
	bin_data = []
	size = len(rle_data)
	while pos < size:
		flag = rle_data[pos]
		pos += 1
		if flag < 0x80:
			count = flag + 1
			block = rle_data[pos:pos+bpp*count]
			bin_data.append(block)
			pos += bpp*count
		else:
			count = (flag & 0x7F) + 3
			block = rle_data[pos:pos+bpp] * count
			bin_data.append(block)
			pos += bpp
	return b''.join(bin_data)
	
def get_repeat(data, pos):
	base = data[pos:pos+bpp]
	pos += bpp
	repeat = 1
	size = len(data)
	while pos < size:
		block = data[pos:pos+bpp]
		if block == base and repeat < 0x82:
			repeat += 1
		else:
			break
		pos += bpp
	return repeat

def bin2rle(bin_data):
	pos = 0
	rle_data = []
	count = 0
	blocks = []
	size = len(bin_data)
	while pos < size:
		repeat = get_repeat(bin_data, pos)
		if repeat >= 3:
			if count > 0:
				flag = count - 1
				rle_data.append(bytes([flag]))
				rle_data.append(b''.join(blocks))
				blocks = []
				count = 0
			flag = (repeat - 3) | 0x80
			block = bin_data[pos:pos+bpp]
			rle_data.append(bytes([flag]))
			rle_data.append(block)
			pos += bpp*repeat
		else:
			block = bin_data[pos:pos+bpp]
			blocks.append(block)
			count += 1
			pos += bpp
		if count == 0x80:
			flag = count - 1
			rle_data.append(bytes([flag]))
			rle_data.append(b''.join(blocks))
			blocks = []
			count = 0
	if count > 0:
		flag = count - 1
		rle_data.append(bytes([flag]))
		rle_data.append(b''.join(blocks))
	return b''.join(rle_data)
	
def bin2bmp(bin_data, width, height):
	bmp_data = []
	header = make_bmp_hdr(width, height)
	bmp_data.append(header)
	rows = list(range(height))
	rows.reverse()
	for row in rows:
		pos = row*bpp*width
		block = bin_data[pos:pos+bpp*width]
		bmp_data.append(block)
	return b''.join(bmp_data)
	
def bmp2bin(bmp_data):
	bin_data = []
	offset, width, height = read_bmp_hdr(bmp_data)
	image = bmp_data[offset:]
	rows = list(range(height))
	rows.reverse()
	for row in rows:
		pos = row*bpp*width
		block = image[pos:pos+bpp*width]
		bin_data.append(block)
	return b''.join(bin_data)

