package main | |
import ( | |
"bytes" | |
"fmt" | |
"io/ioutil" | |
"os" | |
"path/filepath" | |
"strings" | |
) | |
func nextControlBit(data []byte, i *int, controlByte *byte) bool { | |
new := false | |
if *controlByte == 0x80 { | |
*controlByte = data[*i] | |
*i++ | |
new = true | |
} | |
ret := false | |
if *controlByte&0x80 != 0 { | |
ret = true | |
} | |
*controlByte <<= 1 | |
if new { | |
*controlByte |= 1 | |
} | |
return ret | |
} | |
func decompress(data []byte) ([]byte, int) { | |
controlByte := data[0] | |
controlByte = (controlByte << 1) | 1 | |
i := 1 | |
nextControlBit(data, &i, &controlByte) | |
out := []byte{} | |
for { | |
if nextControlBit(data, &i, &controlByte) { | |
numBytes := 2 | |
if nextControlBit(data, &i, &controlByte) { | |
if nextControlBit(data, &i, &controlByte) { | |
numBytes++ | |
if nextControlBit(data, &i, &controlByte) { | |
numBytes = int(data[i]) | |
i++ | |
if numBytes == 0 { | |
if nextControlBit(data, &i, &controlByte) { | |
continue | |
} | |
return out, i | |
} | |
numBytes += 8 | |
out = copyBytesCalcOffset(data, out, &i, &controlByte, numBytes) | |
} else { | |
out = copyBytesCalcOffset(data, out, &i, &controlByte, numBytes) | |
} | |
} else { | |
out = copyBytes(data, out, int(data[i]), 0, numBytes) | |
i++ | |
} | |
} else { | |
numBytes *= 2 | |
if nextControlBit(data, &i, &controlByte) { | |
numBytes++ | |
} | |
if nextControlBit(data, &i, &controlByte) { | |
numBytes-- | |
numBytes *= 2 | |
if nextControlBit(data, &i, &controlByte) { | |
numBytes++ | |
} | |
if numBytes == 9 { | |
numRows := 0 | |
for n := 0; n < 4; n++ { | |
numRows <<= 1 | |
if nextControlBit(data, &i, &controlByte) { | |
numRows |= 1 | |
} | |
} | |
numRows = (numRows + 3) * 2 | |
for n := 0; n < numRows; n++ { | |
out = append(out, data[i]) | |
out = append(out, data[i+1]) | |
i += 2 | |
} | |
} else { | |
out = copyBytesCalcOffset(data, out, &i, &controlByte, numBytes) | |
} | |
} else { | |
out = copyBytesCalcOffset(data, out, &i, &controlByte, numBytes) | |
} | |
} | |
} else { | |
out = append(out, data[i]) | |
i++ | |
} | |
} | |
} | |
func copyBytesCalcOffset(data, out []byte, i *int, controlByte *byte, numBytes int) []byte { | |
offset := 0 | |
if nextControlBit(data, i, controlByte) { | |
if nextControlBit(data, i, controlByte) { | |
offset++ | |
} | |
if nextControlBit(data, i, controlByte) { | |
offset *= 2 | |
if nextControlBit(data, i, controlByte) { | |
offset++ | |
} | |
offset |= 0x4 | |
if !nextControlBit(data, i, controlByte) { | |
offset *= 2 | |
if nextControlBit(data, i, controlByte) { | |
offset++ | |
} | |
} | |
} else if offset == 0 { | |
offset = 2 | |
if nextControlBit(data, i, controlByte) { | |
offset++ | |
} | |
} | |
} | |
out = copyBytes(data, out, int(data[*i]), offset, numBytes) | |
*i++ | |
return out | |
} | |
func copyBytes(data, out []byte, sub, offset, numBytes int) []byte { | |
src := len(out) - ((offset << 8) | sub) - 1 | |
for n := 0; n < numBytes; n++ { | |
out = append(out, out[src+n]) | |
} | |
return out | |
} | |
// convert4bppToImage produces a paletted image from the given 4bpp data. | |
// Width parameter is specifried in tiles, not pixels. | |
func convert4bppToImage(data []byte, tilesWide int, interleave bool) (image.PalettedImage, error) { | |
if tilesWide < 0 { | |
tilesWide = len(data) / 16 | |
} | |
if len(data)%16 != 0 { | |
return nil, fmt.Errorf("invalid 4bpp data: length must be a multiple of 16, but got %d instead", len(data)) | |
} | |
numTiles := len(data) / 16 | |
tilesHigh := numTiles / tilesWide | |
if numTiles%tilesWide != 0 { | |
tilesHigh++ | |
} | |
if tilesHigh <= 0 { | |
tilesHigh = 1 | |
} | |
if interleave && tilesHigh%2 == 1 { | |
tilesHigh++ | |
} | |
width := tilesWide * 8 | |
height := tilesHigh * 8 | |
palette := []color.Color{color.RGBA{224, 248, 208, 255}, color.RGBA{136, 192, 112, 255}, color.RGBA{52, 104, 86, 255}, color.RGBA{8, 24, 32, 255}} | |
rect := image.Rect(0, 0, width, height) | |
img := image.NewPaletted(rect, palette) | |
var tileX, tileY, tileIndex, yOffset int | |
for x := 0; x < width; x++ { | |
for y := 0; y < height; y++ { | |
tileX = x / 8 | |
tileY = y / 8 | |
if interleave { | |
tmp := tileX | |
tileX = (tileX * 2) + tileY%2 | |
if tileY%2 == 1 { | |
tileY-- | |
} | |
tileY += tmp / tilesWide | |
} | |
tileIndex = tileY*tilesWide + tileX | |
yOffset = y % 8 | |
dataOffset := tileIndex*16 + yOffset*2 | |
if dataOffset < len(data) { | |
bit := 7 - (x % 8) | |
pixelVal := ((data[dataOffset] >> bit) & 1) | (((data[dataOffset+1] >> bit) & 1) << 1) | |
img.SetColorIndex(x, y, pixelVal) | |
} | |
} | |
} | |
return img, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment