From aa093faabc533367537db44c3f1c4e84b4386492 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Fri, 12 Oct 2018 20:22:40 +0100 Subject: [PATCH] Move RLE decoding into a separate package --- internal/conv/object.go | 55 ++++++------------------------------ internal/data/object.go | 25 +++++++---------- internal/data/rle/rle.go | 60 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 61 deletions(-) create mode 100644 internal/data/rle/rle.go diff --git a/internal/conv/object.go b/internal/conv/object.go index 9e7f0e9..40f67f0 100644 --- a/internal/conv/object.go +++ b/internal/conv/object.go @@ -10,6 +10,8 @@ import ( "ur.gs/ordoor/internal/data" ) +var transparent = color.RGBA{0, 0, 0, 0} + // Important conversions: // // * Width & height now stored using int @@ -46,57 +48,18 @@ func ConvertObject(rawObj *data.Object, name string) *Object { return out } -var transparent = color.RGBA{0, 0, 0, 0} - -// WIP. Try to convert the pixeldata into a picture. 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))) + width := int(sprite.Width) + height := int(sprite.Height) - log.Printf("%v %v: width=%v height=%v", name, idx, sprite.Width, sprite.Height) + log.Printf("%v %v: width=%v height=%v", name, idx, width, height) - for y := 0; y < int(sprite.Height); y++ { - encoded := sprite.Rows[y] - decoded := make([]byte, 0, int(sprite.Width)) + for y := 0; y < height; y++ { + for x := 0; x < width; x++ { + b := sprite.Data[y*width+x] - // Start with all bytes transparent - for x := 0; x < int(sprite.Width); x++ { - pic.Pix[pic.Index(pixel.V(float64(x), float64(y)))] = transparent - } - - for i := 0; i < len(encoded); i++ { - b := encoded[i] - - // This appears to be a kind of RLE - if b == 0 { - continue // finished - } else if b < 0x80 { - // repeat the next byte this many times - for j := 0; j < int(b); j++ { - decoded = append(decoded, encoded[i+1]) - } - - i++ // skip the repeat byte - } else if b == 0x80 { - // transparent value, skip forward *x+1 rows - skip := int(encoded[i+1]) - for i := 0; i < skip; i++ { - decoded = append(decoded, byte(0x00)) - } - - i++ // skip the count byte - } else { - // take the next b-0x80 bytes literally - literals := int(b) - 0x80 - for j := i + 1; j <= i+literals; j++ { - decoded = append(decoded, encoded[j]) - } - - i = i + literals - } - } - - // Update the picture - for x, b := range decoded { + // Update the picture if err := setPaletteColor(pic, x, y, b); err != nil { log.Printf("%s %d: %d,%d: %v", name, idx, x, y, err) } diff --git a/internal/data/object.go b/internal/data/object.go index 9d865b7..4c7ba40 100644 --- a/internal/data/object.go +++ b/internal/data/object.go @@ -1,7 +1,6 @@ package data import ( - "bufio" "encoding/binary" "fmt" "io" @@ -9,12 +8,14 @@ import ( "os" "path/filepath" "strings" + + "ur.gs/ordoor/internal/data/rle" ) type SpriteHeader struct { Unknown0 uint32 - Width uint16 // FIXME: I'm not certain this is what these are. - Height uint16 // FIXME: If they are, they may be the wrong way around + Width uint16 + Height uint16 Padding1 uint32 // I don't think this is used. Could be wrong. PixelSize uint32 // Size of PixelData, excluding this sprite header Padding2 uint64 // I don't think this is used either. Could be wrong. @@ -36,7 +37,7 @@ func (s SpriteHeader) Check(expectedSize uint32) error { type Sprite struct { SpriteHeader - Rows [][]byte + Data []byte } type dirEntry struct { @@ -132,18 +133,12 @@ func LoadObject(filename string) (*Object, error) { 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) + buf := io.LimitReader(f, int64(sprite.PixelSize)) + sprite.Data = make([]byte, sprite.Height*sprite.Width) - for y := 0; y < int(sprite.Height); y++ { - if row, err := buf.ReadBytes(0x00); err != nil { - return nil, fmt.Errorf("Reading row %v for sprite %v: %v", y, i, err) - } else { - sprite.Rows[y] = row - } + // The pixel data is RLE-compressed. Uncompress it here. + if err := rle.Expand(buf, sprite.Data); err != nil { + return nil, err } out.Sprites = append(out.Sprites, sprite) diff --git a/internal/data/rle/rle.go b/internal/data/rle/rle.go new file mode 100644 index 0000000..c50e842 --- /dev/null +++ b/internal/data/rle/rle.go @@ -0,0 +1,60 @@ +// Package rle implements the run-length encoding scheme discovered in Chaos +// Gate .obj files. It may be used for other data too - to be determined. +package rle + +import ( + "bufio" + "io" + "io/ioutil" +) + +// Expand converts the compressed input data into uncompressed output data. The +// output buffer should be of at least the expected size. We will read until +// io.EOF is encountered. +func Expand(in io.Reader, out []byte) error { + buf := bufio.NewReader(in) + idx := 0 + + for { + b, err := buf.ReadByte() + if err != nil { + if err == io.EOF { + return nil + } + + return err + } + + if b == 0x00 { // NOP + continue + } else if b < 0x80 { // repeat the next byte this many times + repeat := b + value, err := buf.ReadByte() + if err != nil { + return err + } + + for x := 0; x < int(repeat); x++ { + out[idx+x] = value + } + idx = idx + int(repeat) + } else if b == 0x80 { // skip forward in the output by the next byte's value + skip, err := buf.ReadByte() + if err != nil { + return err + } + + idx = idx + int(skip) + } else { // Take the next b-0x80 bytes literally + lr := io.LimitReader(buf, int64(b-0x80)) + literals, err := ioutil.ReadAll(lr) + if err != nil { + return err + } + copy(out[idx:], literals) + idx = idx + len(literals) + } + } + + return nil +}