17 changed files with 1599 additions and 550 deletions
@ -0,0 +1,52 @@ |
|||
package dmd |
|||
|
|||
import ( |
|||
"errors" |
|||
"fmt" |
|||
"io" |
|||
"strings" |
|||
) |
|||
|
|||
var ( |
|||
ErrHeaderMagic = errors.New("dmd: invalid header magic") |
|||
) |
|||
|
|||
type animationSet interface { |
|||
DecodeAnimationSet(r io.Reader) ([]*Animation, error) |
|||
} |
|||
|
|||
func ReadAnimations(reader io.ReadSeeker) ([]*Animation, error) { |
|||
if err := rewind(reader); err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
var magic [4]byte |
|||
if _, err := io.ReadFull(reader, magic[:]); err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
var ( |
|||
id = cString(magic[:]) |
|||
set animationSet |
|||
) |
|||
switch { |
|||
case id == "ANIM": |
|||
set = vniAnimationSet{} // pin2DMDAnimationSet{}
|
|||
|
|||
case id == "VPIN": |
|||
set = vniAnimationSet{} |
|||
|
|||
case strings.HasPrefix(id, runDMDHeaderMagic): |
|||
return readRunDMDAnimations(reader) |
|||
case id == "\x11HDD": |
|||
return nil, errors.New("dmd: please unpack the HDD Raw Copy image to raw (dd) format") |
|||
|
|||
default: |
|||
return nil, fmt.Errorf("dmd: magic %q does not denote a supported format", cString(magic[:])) |
|||
} |
|||
|
|||
if _, err := reader.Seek(0, io.SeekStart); err != nil { |
|||
return nil, err |
|||
} |
|||
return set.DecodeAnimationSet(reader) |
|||
} |
@ -0,0 +1,148 @@ |
|||
package dmd |
|||
|
|||
import ( |
|||
"fmt" |
|||
"image/color" |
|||
"io" |
|||
"log" |
|||
) |
|||
|
|||
type pin2DMDAnimationSet struct{} |
|||
|
|||
func (s pin2DMDAnimationSet) DecodeAnimationSet(r io.Reader) (set []*Animation, err error) { |
|||
var header [4]byte |
|||
if _, err = io.ReadFull(r, header[:]); err != nil { |
|||
return |
|||
} else if string(header[:]) != "ANIM" { |
|||
return nil, ErrHeaderMagic |
|||
} |
|||
|
|||
var version uint16 |
|||
if version, err = readUint16(r); err != nil { |
|||
return |
|||
} |
|||
|
|||
var animations uint16 |
|||
if animations, err = readUint16(r); err != nil { |
|||
return |
|||
} |
|||
|
|||
if version > 6 { |
|||
return nil, fmt.Errorf("dmd: pin2dmd animation version %d is not supported", version) |
|||
} |
|||
|
|||
if version >= 4 { |
|||
log.Printf("pin2dmd: skipping %d bytes of animation indexes", 4*int(animations)) |
|||
for i := 0; i < int(animations); i++ { |
|||
if _, err = readUint32(r); err != nil { |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
set = make([]*Animation, animations) |
|||
log.Printf("vni: reading %d animations from %s v%d", animations, string(header[:]), version) |
|||
for i := 0; i < int(animations); i++ { |
|||
if set[i], err = s.decodeAnimation(r, version); err != nil { |
|||
return |
|||
} |
|||
} |
|||
return set, nil |
|||
} |
|||
|
|||
func (s pin2DMDAnimationSet) decodeAnimation(r io.Reader, version uint16) (a *Animation, err error) { |
|||
a = &Animation{ |
|||
Width: 128, |
|||
Height: 32, |
|||
PaletteIndex: -1, |
|||
} |
|||
|
|||
if a.Name, err = readString(r); err != nil { |
|||
return |
|||
} |
|||
|
|||
var header struct { |
|||
Cycles uint16 |
|||
Hold uint16 |
|||
ClockFrom uint16 |
|||
ClockSmall uint8 |
|||
ClockFront uint8 |
|||
ClockXOffset uint16 |
|||
ClockYOffset uint16 |
|||
Delay uint16 |
|||
_ uint8 |
|||
FSK uint8 |
|||
Frames int16 |
|||
} |
|||
if err = readBE(r, &header); err != nil { |
|||
return |
|||
} |
|||
|
|||
frames := int(header.Frames) |
|||
if frames < 0 { |
|||
frames += 65536 |
|||
} |
|||
|
|||
if version >= 2 { |
|||
// Custom palette.
|
|||
var ( |
|||
index uint8 |
|||
palette color.Palette |
|||
) |
|||
if index, palette, err = readPin2DMDPalette(r); err != nil { |
|||
return nil, fmt.Errorf("error decoding palette: %w", err) |
|||
} |
|||
//if a.Name == "Tilt" {
|
|||
log.Printf("custom palette %d in %q: %d colors: %+v", index, a.Name, len(palette), palette) |
|||
//}
|
|||
a.Palettes[int(index)] = palette |
|||
} |
|||
|
|||
if version >= 3 { |
|||
// Edit mode.
|
|||
if _, err = readByte(r); err != nil { |
|||
return |
|||
} |
|||
} |
|||
|
|||
if version >= 4 { |
|||
// Size of the panel.
|
|||
if a.Width, err = readInt16Int(r); err != nil { |
|||
return |
|||
} |
|||
if a.Height, err = readInt16Int(r); err != nil { |
|||
return |
|||
} |
|||
} else { |
|||
a.Width = pin2DMDPanelWidth |
|||
a.Height = pin2DMDPanelHeight |
|||
} |
|||
|
|||
if version >= 5 { |
|||
// Masks.
|
|||
var n int16 |
|||
if err = readBE(r, &n); err != nil { |
|||
return |
|||
} |
|||
} |
|||
|
|||
if version >= 6 { |
|||
var isRecorded bool |
|||
if isRecorded, err = readBool(r); err != nil { |
|||
return |
|||
} |
|||
if isRecorded { |
|||
// Link to the recording (ignored).
|
|||
if _, err = readString(r); err != nil { |
|||
return |
|||
} |
|||
// Start frame (ignored)
|
|||
var n int16 |
|||
if err = readBE(r, &n); err != nil { |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
return |
|||
} |
@ -0,0 +1,281 @@ |
|||
package dmd |
|||
|
|||
import ( |
|||
"bytes" |
|||
"image" |
|||
"image/color" |
|||
"io" |
|||
"log" |
|||
"time" |
|||
|
|||
"maze.io/x/dmd/internal/heatshrink" |
|||
) |
|||
|
|||
const vniDelayStep = time.Millisecond |
|||
|
|||
type vniAnimationSet struct{} |
|||
|
|||
func (s vniAnimationSet) DecodeAnimationSet(r io.Reader) (set []*Animation, err error) { |
|||
var header [4]byte |
|||
if _, err = io.ReadFull(r, header[:]); err != nil { |
|||
return |
|||
} else if string(header[:]) != "VPIN" && string(header[:]) != "ANIM" { |
|||
return nil, ErrHeaderMagic |
|||
} |
|||
|
|||
var version int16 |
|||
if version, err = readInt16(r); err != nil { |
|||
return |
|||
} |
|||
|
|||
var animations int16 |
|||
if animations, err = readInt16(r); err != nil { |
|||
return |
|||
} |
|||
|
|||
if version >= 2 { |
|||
log.Printf("vni: skipping %d bytes of animation indexes", 4*int(animations)) |
|||
for i := 0; i < int(animations); i++ { |
|||
if _, err = readUint32(r); err != nil { |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
set = make([]*Animation, animations) |
|||
log.Printf("vni: reading %d animations from %s v%d", animations, string(header[:]), version) |
|||
for i := 0; i < int(animations); i++ { |
|||
if set[i], err = s.decodeAnimation(r, version); err != nil { |
|||
return |
|||
} |
|||
} |
|||
return set, nil |
|||
} |
|||
|
|||
func (s vniAnimationSet) decodeAnimation(r io.Reader, version int16) (a *Animation, err error) { |
|||
a = &Animation{ |
|||
Width: 128, |
|||
Height: 32, |
|||
PaletteIndex: -1, |
|||
} |
|||
|
|||
if a.Name, err = readString(r); err != nil { |
|||
return |
|||
} |
|||
|
|||
var ( |
|||
clockOffsetX int16 |
|||
clockOffsetY int16 |
|||
frameCount int16 |
|||
) |
|||
if _, err = readInt16(r); err != nil { // cycles
|
|||
return |
|||
} |
|||
if _, err = readInt16(r); err != nil { // hold
|
|||
return |
|||
} |
|||
if _, err = readInt16(r); err != nil { // clock from
|
|||
return |
|||
} |
|||
if a.ClockSmall, err = readBool(r); err != nil { |
|||
return |
|||
} |
|||
if a.ClockInFront, err = readBool(r); err != nil { |
|||
return |
|||
} |
|||
if clockOffsetX, err = readInt16(r); err != nil { |
|||
return |
|||
} |
|||
if clockOffsetY, err = readInt16(r); err != nil { |
|||
return |
|||
} |
|||
a.ClockOffset = image.Pt(int(clockOffsetX), int(clockOffsetY)) |
|||
if _, err = readInt16(r); err != nil { // refresh delay
|
|||
return |
|||
} |
|||
if _, err = readByte(r); err != nil { // type
|
|||
return |
|||
} |
|||
if _, err = readByte(r); err != nil { // fsk
|
|||
return |
|||
} |
|||
|
|||
if frameCount, err = readInt16(r); err != nil { |
|||
return |
|||
} |
|||
frames := int(frameCount) |
|||
if frames < 0 { |
|||
frames += 65536 |
|||
} |
|||
|
|||
if version >= 2 { |
|||
if err = s.readPalettesAndColors(r, a); err != nil { |
|||
return |
|||
} |
|||
} |
|||
|
|||
if version >= 3 { |
|||
if _, err = readByte(r); err != nil { // edit mode
|
|||
return |
|||
} |
|||
} |
|||
|
|||
if version >= 4 { |
|||
if a.Width, err = readInt16Int(r); err != nil { |
|||
return |
|||
} |
|||
if a.Height, err = readInt16Int(r); err != nil { |
|||
return |
|||
} |
|||
} |
|||
|
|||
if version >= 5 { |
|||
if err = s.readMasks(r, a); err != nil { |
|||
return |
|||
} |
|||
} |
|||
|
|||
log.Printf("vni: reading %d frames for animation %q", frames, a.Name) |
|||
a.Index = make([]int, frames) |
|||
a.Frame = make([]*Frame, frames) |
|||
for i := 0; i < frames; i++ { |
|||
a.Index[i] = i |
|||
if a.Frame[i], err = s.readFrame(r, a.Width, a.Height, version); err != nil { |
|||
return |
|||
} |
|||
} |
|||
return |
|||
} |
|||
|
|||
func (s vniAnimationSet) readPalettesAndColors(r io.Reader, a *Animation) (err error) { |
|||
if a.PaletteIndex, err = readInt16Int(r); err != nil { |
|||
a.PaletteIndex = -1 |
|||
return err |
|||
} |
|||
|
|||
var colors int |
|||
if colors, err = readInt16Int(r); err != nil { |
|||
return |
|||
} |
|||
if colors <= 0 { |
|||
log.Printf("vni: no colors defined, palette index %d", a.PaletteIndex) |
|||
return |
|||
} |
|||
|
|||
a.Palette = make(color.Palette, colors) |
|||
for i := 0; i < colors; i++ { |
|||
if a.Palette[i], err = readRGB24(r); err != nil { |
|||
return |
|||
} |
|||
} |
|||
log.Printf("vni: found %d colors for palette %d", colors, a.PaletteIndex) |
|||
return |
|||
} |
|||
|
|||
func (s vniAnimationSet) readMasks(r io.Reader, a *Animation) (err error) { |
|||
var masks int |
|||
if masks, err = readInt16Int(r); err != nil { |
|||
return |
|||
} |
|||
a.Masks = make([][]byte, masks) |
|||
for i := 0; i < masks; i++ { |
|||
if _, err = readByte(r); err != nil { // locked
|
|||
return |
|||
} |
|||
var size int16 |
|||
if size, err = readInt16(r); err != nil { |
|||
return |
|||
} |
|||
a.Masks[i] = make([]byte, size) |
|||
if _, err = io.ReadFull(r, a.Masks[i]); err != nil { |
|||
return |
|||
} |
|||
} |
|||
return |
|||
} |
|||
|
|||
func (s vniAnimationSet) readFrame(r io.Reader, width, height int, version int16) (f *Frame, err error) { |
|||
f = NewFrame(width, height) |
|||
|
|||
var ( |
|||
planeSize int16 |
|||
delay uint16 |
|||
bitLength byte |
|||
) |
|||
if planeSize, err = readInt16(r); err != nil { |
|||
return |
|||
} |
|||
if delay, err = readUint16(r); err != nil { |
|||
return |
|||
} |
|||
f.Delay = time.Duration(delay) * vniDelayStep |
|||
|
|||
if version >= 4 { |
|||
if f.Checksum, err = readUint32(r); err != nil { |
|||
return |
|||
} |
|||
} |
|||
if bitLength, err = readByte(r); err != nil { |
|||
return |
|||
} |
|||
|
|||
f.Plane = make([]*Plane, 0, bitLength) |
|||
|
|||
if version < 3 { |
|||
return f, s.readPlanes(r, f, bitLength, planeSize) |
|||
} |
|||
|
|||
var isCompressed bool |
|||
if isCompressed, err = readBool(r); err != nil { |
|||
return |
|||
} else if isCompressed { |
|||
var ( |
|||
size int32 |
|||
compressedPlanes []byte |
|||
decompressedPlanes []byte |
|||
) |
|||
if size, err = readInt32(r); err != nil { |
|||
return |
|||
} |
|||
compressedPlanes = make([]byte, size) |
|||
if decompressedPlanes, err = heatshrink.Decompress(10, 0, compressedPlanes); err != nil { |
|||
return |
|||
} |
|||
return f, s.readPlanes(bytes.NewReader(decompressedPlanes), f, bitLength, planeSize) |
|||
} |
|||
|
|||
return f, s.readPlanes(r, f, bitLength, planeSize) |
|||
} |
|||
|
|||
func (s vniAnimationSet) readPlanes(r io.Reader, f *Frame, bitLength byte, planeSize int16) (err error) { |
|||
for i := 0; i < int(bitLength); i++ { |
|||
var marker byte |
|||
if marker, err = readByte(r); err != nil { |
|||
return |
|||
} |
|||
if marker == 0x6d { |
|||
f.Mask = make([]byte, planeSize) |
|||
if _, err = io.ReadFull(r, f.Mask); err != nil { |
|||
return |
|||
} |
|||
} else { |
|||
var p *Plane |
|||
if p, err = s.readPlane(r, marker, planeSize); err != nil { |
|||
return |
|||
} |
|||
f.Plane = append(f.Plane, p) |
|||
} |
|||
} |
|||
return |
|||
} |
|||
|
|||
func (s vniAnimationSet) readPlane(r io.Reader, marker byte, planeSize int16) (p *Plane, err error) { |
|||
p = &Plane{ |
|||
Marker: marker, |
|||
Data: make([]byte, planeSize), |
|||
} |
|||
if _, err = io.ReadFull(r, p.Data); err != nil { |
|||
return |
|||
} |
|||
return |
|||
} |
@ -0,0 +1,67 @@ |
|||
package dmd |
|||
|
|||
import ( |
|||
"os" |
|||
"path/filepath" |
|||
"strings" |
|||
"testing" |
|||
) |
|||
|
|||
func TestReadVNIAnimations(t *testing.T) { |
|||
tests := []struct { |
|||
Name string |
|||
Palette string |
|||
}{ |
|||
{ |
|||
Name: "sprk_103.vni", |
|||
Palette: "sprk_103.pal", |
|||
}, |
|||
{ |
|||
Name: "mb_106b.vni", |
|||
Palette: "mb_106b.pal", |
|||
}, |
|||
{ |
|||
Name: "ID4_1.00.vni", |
|||
Palette: "ID4_1.00.pal", |
|||
}, |
|||
} |
|||
for _, test := range tests { |
|||
t.Run(test.Name, func(it *testing.T) { |
|||
f, err := os.Open(filepath.Join("testdata", test.Name)) |
|||
if err != nil { |
|||
it.Skip(err) |
|||
} |
|||
defer f.Close() |
|||
|
|||
all, err := ReadAnimations(f) |
|||
if err != nil { |
|||
it.Fatal(err) |
|||
} |
|||
it.Logf("%s: %d animations", test.Name, len(all)) |
|||
|
|||
var c *Coloring |
|||
if test.Palette != "" { |
|||
if c, err = LoadColoring(filepath.Join("testdata", test.Palette)); err != nil { |
|||
it.Fatal(err) |
|||
} |
|||
it.Logf("%s: coloring: %d palettes, %d mappings, %d masks", test.Palette, len(c.Palettes), len(c.Mappings), len(c.Masks)) |
|||
} |
|||
|
|||
for i, a := range all { |
|||
//if len(a.Frame) > 1 {
|
|||
if strings.Contains(a.Name, "game over") { |
|||
it.Logf("%s: animation %d: %d frames, duration %s", test.Name, i, len(a.Frame), a.Duration()) |
|||
it.Logf("%s: animation: %#+v", test.Name, a) |
|||
/* |
|||
for _, f := range a.Frame { |
|||
for _, p := range f.Plane { |
|||
dumpPlane(p.Data, f.Width/8) |
|||
} |
|||
} |
|||
*/ |
|||
testSaveGIF(t, test.Name, a, c) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
} |
@ -0,0 +1,110 @@ |
|||
package dmd |
|||
|
|||
func checkSum(b []byte, reverse bool) (cs uint32) { |
|||
cs = ^cs |
|||
if reverse { |
|||
for _, c := range b { |
|||
cs = (cs >> 8) ^ checksumTable[byte(cs)^reverseTable[c]] |
|||
} |
|||
} else { |
|||
for _, c := range b { |
|||
cs = (cs >> 8) ^ checksumTable[byte(cs)^c] |
|||
} |
|||
} |
|||
return ^cs |
|||
} |
|||
|
|||
func checkSumWithMask(b, m []byte, reverse bool) (cs uint32) { |
|||
cs = ^cs |
|||
if reverse { |
|||
for i, c := range b { |
|||
cs = (cs >> 8) ^ checksumTable[byte(cs)^reverseTable[c&m[i]]] |
|||
} |
|||
} else { |
|||
for i, c := range b { |
|||
cs = (cs >> 8) ^ checksumTable[byte(cs)^(c&m[i])] |
|||
} |
|||
} |
|||
return ^cs |
|||
} |
|||
|
|||
var checksumTable = []uint32{ |
|||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, |
|||
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, |
|||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, |
|||
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, |
|||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, |
|||
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, |
|||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, |
|||
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, |
|||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, |
|||
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, |
|||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, |
|||
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, |
|||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, |
|||
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, |
|||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, |
|||
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, |
|||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, |
|||
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, |
|||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, |
|||
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, |
|||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, |
|||
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, |
|||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, |
|||
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, |
|||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, |
|||
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, |
|||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, |
|||
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, |
|||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, |
|||
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, |
|||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, |
|||
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, |
|||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, |
|||
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, |
|||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, |
|||
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, |
|||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, |
|||
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, |
|||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, |
|||
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, |
|||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, |
|||
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, |
|||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, |
|||
} |
|||
|
|||
var reverseTable = []byte{ |
|||
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, |
|||
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, |
|||
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, |
|||
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, |
|||
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, |
|||
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, |
|||
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, |
|||
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, |
|||
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, |
|||
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, |
|||
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, |
|||
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, |
|||
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, |
|||
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, |
|||
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, |
|||
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, |
|||
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, |
|||
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, |
|||
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, |
|||
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, |
|||
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, |
|||
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, |
|||
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, |
|||
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, |
|||
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, |
|||
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, |
|||
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, |
|||
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, |
|||
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, |
|||
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, |
|||
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, |
|||
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, |
|||
} |
@ -0,0 +1,50 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"flag" |
|||
"fmt" |
|||
"os" |
|||
"sort" |
|||
|
|||
"maze.io/x/dmd" |
|||
) |
|||
|
|||
func main() { |
|||
flag.Parse() |
|||
|
|||
for _, name := range flag.Args() { |
|||
dump(name) |
|||
} |
|||
} |
|||
|
|||
func dump(name string) { |
|||
fmt.Println("reading", name) |
|||
coloring, err := dmd.LoadColoring(name) |
|||
if err != nil { |
|||
fmt.Fprintln(os.Stderr, "error:", err) |
|||
return |
|||
} |
|||
|
|||
checksums := make([]uint32, 0, len(coloring.Mappings)) |
|||
for checksum := range coloring.Mappings { |
|||
checksums = append(checksums, checksum) |
|||
} |
|||
sort.Slice(checksums, func(i, j int) bool { return checksums[i] < checksums[j] }) |
|||
|
|||
for i, palette := range coloring.Palettes { |
|||
fmt.Println("palette", i+1) |
|||
for j, color := range palette.Colors { |
|||
// ESC[ 38;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB foreground color
|
|||
r, g, b, _ := color.RGBA() |
|||
fmt.Printf("%02d:\x1b[38;2;%d;%d;%dm▂▄▆█\x1b[0m ", j+1, (r>>8)&0xff, (g>>8)&0xff, (b>>8)&0xff) |
|||
< |