Skip to content

Instantly share code, notes, and snippets.

@huderlem
Created January 14, 2020 15:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save huderlem/6bb2f26f2c7ba749d16b590b9832fff3 to your computer and use it in GitHub Desktop.
Save huderlem/6bb2f26f2c7ba749d16b590b9832fff3 to your computer and use it in GitHub Desktop.
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