More .obj investigating. 0x80 seems to be a special value
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user