package dmd
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"image/color"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
)
|
|
|
|
// Pin2DMDColoring contains a coloring description.
|
|
type Pin2DMDColoring struct {
|
|
Version uint8
|
|
Palette *Pin2DMDPalette
|
|
Palettes []*Pin2DMDPalette
|
|
Default int
|
|
Mappings map[uint32]*Pin2DMDMapping
|
|
Masks [][]byte
|
|
}
|
|
|
|
func (c *Pin2DMDColoring) GetPalette(i int) *Pin2DMDPalette {
|
|
if i < len(c.Palettes) {
|
|
return c.Palettes[i]
|
|
}
|
|
if c.Default < len(c.Palettes) {
|
|
return c.Palettes[c.Default]
|
|
}
|
|
return c.Palette
|
|
}
|
|
|
|
func (c *Pin2DMDColoring) readPalettes(r io.Reader) (err error) {
|
|
var palettes uint16
|
|
if err = binary.Read(r, binary.BigEndian, &palettes); err != nil {
|
|
return
|
|
}
|
|
// log.Printf("pal: reading %d palettes", palettes)
|
|
c.Palettes = make([]*Pin2DMDPalette, palettes)
|
|
for i := 0; i < int(palettes); i++ {
|
|
if c.Palettes[i], err = readPin2DMDPalette(r); err != nil {
|
|
return
|
|
}
|
|
// log.Printf("palette %d: %d colors, default:%t persistent:%t", i, len(c.Palettes[i].Colors), c.Palettes[i].IsDefault, c.Palettes[i].IsPersistent)
|
|
if c.Palette == nil && c.Palettes[i].IsDefault {
|
|
c.Palette = c.Palettes[i]
|
|
c.Default = i
|
|
}
|
|
}
|
|
if c.Palette == nil && len(c.Palettes) > 0 {
|
|
c.Palette = c.Palettes[0]
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *Pin2DMDColoring) readMappings(r io.Reader) (err error) {
|
|
var mappings uint16
|
|
if err = binary.Read(r, binary.BigEndian, &mappings); err != nil {
|
|
return
|
|
}
|
|
// log.Printf("pal: reading %d mappings", mappings)
|
|
c.Mappings = make(map[uint32]*Pin2DMDMapping)
|
|
for i := 0; i < int(mappings); i++ {
|
|
var m *Pin2DMDMapping
|
|
if m, err = readPin2DMDMapping(r); err != nil {
|
|
return
|
|
}
|
|
c.Mappings[m.Checksum] = m
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *Pin2DMDColoring) readMasks(r io.Reader) (err error) {
|
|
var masks byte
|
|
if err = binary.Read(r, binary.BigEndian, &masks); err != nil {
|
|
return
|
|
}
|
|
// log.Printf("pal: reading %d masks", masks)
|
|
var data []byte
|
|
if data, err = ioutil.ReadAll(r); err != nil {
|
|
return
|
|
}
|
|
maskBytes := len(data) / int(masks)
|
|
if maskBytes != 256 && maskBytes != 512 && maskBytes != 1536 {
|
|
return fmt.Errorf("dmd: %d bytes remaining for %d masks", maskBytes, masks)
|
|
}
|
|
c.Masks = make([][]byte, masks)
|
|
for i := 0; i < int(masks); i++ {
|
|
c.Masks[i], data = data[:maskBytes], data[maskBytes:]
|
|
}
|
|
return
|
|
}
|
|
|
|
/*
|
|
func (c *Pin2DMDColoring) FindMapping(p *Plane, reverse bool) *Pin2DMDMapping {
|
|
s := checkSum(p.Data, reverse)
|
|
if m, ok := c.Mappings[s]; ok {
|
|
// Unmasked CRC matched.
|
|
log.Printf("mapping: based on unmasked checksum %#08x", s)
|
|
return m
|
|
}
|
|
log.Printf("mapping: none for unmasked checksum %#08x", s)
|
|
for _, mask := range c.Masks {
|
|
s = checkSumWithMask(p.Data, mask, reverse)
|
|
if m, ok := c.Mappings[s]; ok {
|
|
log.Printf("mapping: based on masked checksum %#08x", s)
|
|
return m
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
*/
|
|
|
|
// LoadPin2DMDColoring loads *.pal Pin2DMD colorization files.
|
|
func LoadPin2DMDColoring(name string) (*Pin2DMDColoring, error) {
|
|
f, err := os.Open(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() { _ = f.Close() }()
|
|
return DecodePin2DMDColoring(f)
|
|
}
|
|
|
|
// DecodePin2DMDColoring decodes palette mappings from the supplied Reader.
|
|
func DecodePin2DMDColoring(r io.Reader) (c *Pin2DMDColoring, err error) {
|
|
c = new(Pin2DMDColoring)
|
|
if err = binary.Read(r, binary.BigEndian, &c.Version); err != nil {
|
|
return
|
|
}
|
|
// log.Printf("pal: read version %d", c.Version)
|
|
if err = c.readPalettes(r); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = c.readMappings(r); err != nil && err != io.EOF {
|
|
return nil, err
|
|
}
|
|
if err = c.readMasks(r); err != nil {
|
|
return nil, err
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
type Pin2DMDPalette struct {
|
|
Index uint16
|
|
Type uint8
|
|
IsDefault bool
|
|
IsPersistent bool
|
|
Colors color.Palette
|
|
}
|
|
|
|
func readPin2DMDPalette(r io.Reader) (p *Pin2DMDPalette, err error) {
|
|
p = new(Pin2DMDPalette)
|
|
if err = binary.Read(r, binary.BigEndian, &p.Index); err != nil {
|
|
return
|
|
}
|
|
var colors uint16
|
|
if err = binary.Read(r, binary.BigEndian, &colors); err != nil {
|
|
return
|
|
}
|
|
if err = binary.Read(r, binary.BigEndian, &p.Type); err != nil {
|
|
return
|
|
}
|
|
p.IsDefault = p.Type == 1 || p.Type == 2
|
|
p.IsPersistent = p.Type == 1
|
|
|
|
p.Colors = make(color.Palette, colors)
|
|
for i := 0; i < int(colors); i++ {
|
|
var rgb [3]byte
|
|
if _, err = io.ReadFull(r, rgb[:]); err != nil {
|
|
return
|
|
}
|
|
p.Colors[i] = color.RGBA{
|
|
R: rgb[0],
|
|
G: rgb[1],
|
|
B: rgb[2],
|
|
A: 0xff,
|
|
}
|
|
/*
|
|
if p.Colors[i], err = DecodeRGB24(r); err != nil {
|
|
return nil, err
|
|
}
|
|
*/
|
|
}
|
|
|
|
return p, nil
|
|
}
|
|
|
|
type SwitchMode byte
|
|
|
|
const (
|
|
SwitchModePalette SwitchMode = iota
|
|
SwitchModeReplace
|
|
SwitchModeColorMask
|
|
SwitchModeEvent
|
|
SwitchModeFollow
|
|
)
|
|
|
|
type Pin2DMDMapping struct {
|
|
Checksum uint32
|
|
Mode SwitchMode
|
|
PaletteIndex uint16
|
|
Duration uint32
|
|
Offset uint32
|
|
}
|
|
|
|
func readPin2DMDMapping(r io.Reader) (m *Pin2DMDMapping, err error) {
|
|
m = new(Pin2DMDMapping)
|
|
if err = binary.Read(r, binary.BigEndian, &m.Checksum); err != nil {
|
|
return
|
|
}
|
|
if err = binary.Read(r, binary.BigEndian, &m.Mode); err != nil {
|
|
return
|
|
}
|
|
if err = binary.Read(r, binary.BigEndian, &m.PaletteIndex); err != nil {
|
|
return
|
|
}
|
|
if m.Mode == SwitchModePalette {
|
|
err = binary.Read(r, binary.BigEndian, &m.Duration)
|
|
} else {
|
|
err = binary.Read(r, binary.BigEndian, &m.Offset)
|
|
}
|
|
return m, err
|
|
}
|
|
|
|
func (m Pin2DMDMapping) IsAnimation() bool {
|
|
switch m.Mode {
|
|
case SwitchModeReplace, SwitchModeColorMask, SwitchModeFollow:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|