More .obj investigating. 0x80 seems to be a special value

This commit is contained in:
2018-03-24 21:47:34 +00:00
parent 6ba93486a1
commit 4d4c4da892
10 changed files with 269 additions and 99 deletions

View File

@@ -1,7 +1,7 @@
package conv
import (
"bytes"
"fmt"
"image/color"
"log"
@@ -34,7 +34,7 @@ func ConvertObject(rawObj *data.Object, name string) *Object {
}
for i, rawSpr := range rawObj.Sprites {
pic := spriteToPic(rawSpr)
pic := spriteToPic(name, i, rawSpr)
out.Sprites[i] = Sprite{
Width: int(rawSpr.Width),
Height: int(rawSpr.Height),
@@ -49,53 +49,81 @@ func ConvertObject(rawObj *data.Object, name string) *Object {
var transparent = color.RGBA{0, 0, 0, 0}
// WIP. Try to convert the pixeldata into a picture.
func spriteToPic(sprite *data.Sprite) *pixel.PictureData {
func spriteToPic(name string, idx int, sprite *data.Sprite) *pixel.PictureData {
pic := pixel.MakePictureData(pixel.R(float64(0), float64(0), float64(sprite.Width), float64(sprite.Height)))
buf := bytes.NewBuffer(sprite.PixelData)
log.Printf("%v %v: width=%v height=%v", name, idx, sprite.Width, sprite.Height)
// The pixeldata seems to be formed of Y null-terminated records, with
// varying numbers of bytes in each row. Probably [type, *data] but ignore
// type for now.
//
// Theory: perhaps the data in each X is centered around the origin?
for y := 0; y < int(sprite.Height); y++ {
insn := buf.Next(1)[0] // Take the instruction byte, if that's what it is
switch insn {
case 0:
log.Printf("Reached the end of the sprite at y=%v (height=%v)", y, sprite.Height)
case 1, 0x80:
// Ignore these, as we know they exist and the logic below seems to handle them
// Although I suspect 0x80 means "centered run of bytes" while 0x1
// means "left-aligned run of bytes", since all 0x1 rows seem to be
// for 1x1 images, it makes no practical difference
default:
log.Printf("Record of unknown type %v", insn)
// Start with all bytes transparent
for x := 0; x < int(sprite.Width); x++ {
pic.Pix[pic.Index(pixel.V(float64(x), float64(y)))] = transparent
}
rowData, err := buf.ReadBytes(0)
if err != nil {
log.Printf("Error at y=%d: %v", y, err)
continue
row := sprite.Rows[y]
log.Printf("%#v", row)
pixels := row[0 : len(row)-1] // Strip off the record separator (0x00)
// Not really clear on what this does yet. Aligned with sprite width in
// many cases but can also vary above and below that value.
u0 := int(pixels[0])
pixels = pixels[1:len(pixels)]
// In some cases, the column data is indented relative to the start of
// the row. Certainly true when u0 == 0x80, perhaps in other cases
// too.
//
// Definitely not the case when u0 == 0x01 - there aren't enough bytes
// in that case for it to be anything but pixeldata
xOffset := 0
// Do nothing if we're out of pixels
if u0 == 0x80 {
log.Printf("Handling 0x80: %#v", pixels)
xOffset = int(pixels[0])
pixels = pixels[1:len(pixels)]
// Sometimes, pixels is now empty. e.g. l_ivy02 sprite 6
if len(pixels) > 3 {
// For tiles, this has an inverse relationship with u0. Seems to add
// up to 0x42 in all cases, which matches byte 3 of the header?
//_ = int(pixels[0])
pixels = pixels[1:len(pixels)]
// On tiles, this removes some junk around the edge, but doesn't
// seem to be reasonable in-general?
pixels = pixels[0 : len(pixels)-2]
}
}
// Ignore the record separator
rowData = rowData[0 : len(rowData)-1]
leftPad := (int(sprite.Width) - len(rowData)) / 2
log.Printf(
"%v %d: len(row)=%v, len(pixels)=%v sprWidth=%v u0=%v xOffset=%v",
name, idx, len(row), len(pixels), sprite.Width, u0, xOffset,
)
// Set all bytes to be transparent by default
for allX := 0; allX < int(sprite.Width); allX++ {
idx := pic.Index(pixel.V(float64(allX), float64(y)))
pic.Pix[idx] = transparent
}
for x, b := range rowData {
idx := pic.Index(pixel.V(float64(leftPad+x), float64(y)))
r, g, b, a := data.ColorPalette[int(b)].RGBA()
pic.Pix[idx] = color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
for x, b := range pixels {
vec := pixel.V(float64(xOffset+x), float64(y))
if err := setPaletteColor(pic, vec, b); err != nil {
log.Printf("%s %d: %d,%d: %v", name, idx, x, y, err)
}
}
}
return pic
}
func setPaletteColor(pic *pixel.PictureData, point pixel.Vec, colorIdx byte) error {
idx := pic.Index(point)
if idx > len(pic.Pix)-1 {
return fmt.Errorf("Got index %v which exceeds bounds", idx)
}
r, g, b, a := data.ColorPalette[int(colorIdx)].RGBA()
color := color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
pic.Pix[idx] = color
return nil
}

View File

@@ -1,7 +1,7 @@
package data
import (
"bytes"
"bufio"
"encoding/binary"
"fmt"
"io"
@@ -36,7 +36,7 @@ func (s SpriteHeader) Check(expectedSize uint32) error {
type Sprite struct {
SpriteHeader
PixelData []byte
Rows [][]byte
}
type dirEntry struct {
@@ -113,27 +113,18 @@ func LoadObject(filename string) (*Object, error) {
return nil, err
}
// FIXME: this might - *might* - load interstitial data we don't really
// need, so wasting memory.
data, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
buf := bytes.NewReader(data)
for _, dirEntry := range dir {
if err := dirEntry.Check(); err != nil {
return nil, err
}
if _, err := buf.Seek(int64(dirEntry.Offset), io.SeekStart); err != nil {
if _, err := f.Seek(int64(out.DataOffset+dirEntry.Offset), io.SeekStart); err != nil {
return nil, err
}
sprite := &Sprite{}
if err := binary.Read(buf, binary.LittleEndian, &sprite.SpriteHeader); err != nil {
if err := binary.Read(f, binary.LittleEndian, &sprite.SpriteHeader); err != nil {
return nil, err
}
@@ -141,11 +132,18 @@ func LoadObject(filename string) (*Object, error) {
return nil, err
}
// It's safe to assume that a `bytes.Reader` will always satisfy the
// requested read size.
sprite.PixelData = make([]byte, sprite.PixelSize)
if _, err := buf.Read(sprite.PixelData); err != nil {
return nil, err
// The pixeldata seems to be formed of Y null-terminated records, with
// varying numbers of bytes in each row. I don't know the internal
// structure yet, but there's definitely working pixel data in there
buf := bufio.NewReader(io.LimitReader(f, int64(sprite.PixelSize)))
sprite.Rows = make([][]byte, sprite.Height)
for y := 0; y < int(sprite.Height); y++ {
if row, err := buf.ReadBytes(0x00); err != nil {
return nil, err
} else {
sprite.Rows[y] = row
}
}
out.Sprites = append(out.Sprites, sprite)