Kistaro icon

Unsuccessful attempt to guess Analogue Pocket GB Palette file format

Kistaro | PRO | 12/10/23 04:24:13 AM UTC | 0 ⭐ | 15375 👁️ | Never ⏰ | []
Go |

4.55 KB

|

Gaming

|

0 👍

/

0 👎

package main
 
// Binary apgbpal creates a 56-byte file named "test.pal" in the current working
// directory. I hope it might be an Analogue Pocket GB Palette file, but I don't
// know that for sure yet.
 
// apgbpal is licensed under 0BSD.
//
// Zero-Clause BSD
// =============
//
// Permission to use, copy, modify, and/or distribute this software for
// any purpose with or without fee is hereby granted.
//
// THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
// FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
// DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
// OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 
import (
    "fmt"
    "log"
    "os"
    "strconv"
    "strings"
)
 
type RGBA struct {
    R byte
    G byte
    B byte
    A byte
}
 
func (c RGBA) Bytes() [4]byte {
    return [4]byte{c.R, c.G, c.B, c.A}
}
 
func HexByteAt(str string, index int) (byte, error) {
    if len(str) < index+2 {
        return 0, fmt.Errorf("no hex byte at %d in too short string %q", index, str)
    }
    big, err := strconv.ParseUint(str[index:index+1], 16, 8)
    if err != nil {
        return 0, fmt.Errorf("can't parse hex byte at %d in string %q: %w", index, str, err)
    }
    return byte(big), nil
}
 
func ParseRGBA(str string) (RGBA, error) {
    s := strings.TrimSpace(str)
    s = strings.ToLower(s)
    s, _ = strings.CutPrefix(s, "#")
    var ret RGBA
    switch len(s) {
    case 6:
        ret.A = 0xFF
    case 8:
        var err error
        ret.A, err = HexByteAt(s, 6)
        if err != nil {
            return RGBA{}, fmt.Errorf("can't parse alpha channel: %v", err)
        }
    default:
        return RGBA{}, fmt.Errorf("%q is not an RGBA value: wrong length after trimming", str)
    }
    var err error
    if ret.R, err = HexByteAt(s, 0); err != nil {
        return RGBA{}, fmt.Errorf("can't parse red channel: %v", err)
    }
    if ret.G, err = HexByteAt(s, 2); err != nil {
        return RGBA{}, fmt.Errorf("can't parse green channel: %v", err)
    }
    if ret.B, err = HexByteAt(s, 4); err != nil {
        return RGBA{}, fmt.Errorf("can't parse blue channel: %v", err)
    }
    return ret, nil
}
 
type BGPal [4]RGBA
 
func (b BGPal) Bytes() [20]byte {
    var ret [20]byte
    ret[0] = 'b'
    ret[1] = 'g'
    ret[2] = ':'
    for c := 0; c < 4; c++ {
        bb := b[c].Bytes()
        for i := 0; i < 4; i++ {
            ret[3+i+4*c] = bb[i]
        }
    }
    ret[19] = '\n'
    return ret
}
 
func MustParseBGPal(strs []string) BGPal {
    if len(strs) != 4 {
        log.Fatalf("MustParseBGPal requires length 4, got %d", len(strs))
    }
    var ret BGPal
    for i := 0; i < 4; i++ {
        var err error
        ret[i], err = ParseRGBA(strs[i])
        if err != nil {
            log.Fatalf("MustParseBGPal can't parse at %d: %v", i, err)
        }
    }
    return ret
}
 
type OBJPal [3]RGBA
 
func (o OBJPal) Bytes(indexchar byte) [18]byte {
    var ret [18]byte
    ret[0] = 'o'
    ret[1] = 'b'
    ret[2] = 'j'
    ret[3] = indexchar
    ret[4] = ':'
    for c := 0; c < 3; c++ {
        oo := o[c].Bytes()
        for i := 0; i < 4; i++ {
            ret[5+i+4*c] = oo[i]
        }
    }
    ret[17] = '\n'
    return ret
}
 
func MustParseOBJPal(strs []string) OBJPal {
    if len(strs) == 4 {
        log.Println("Discarding index 0 of OBJ pal")
        strs = strs[1:]
    }
    if len(strs) != 3 {
        log.Fatalf("MustParseObjPal requires length 3, got %d", len(strs))
    }
    var ret OBJPal
    for i := 0; i < 3; i++ {
        var err error
        ret[i], err = ParseRGBA(strs[i])
        if err != nil {
            log.Fatalf("MustParseOBJPal can't parse at %d: %v", i, err)
        }
    }
    return ret
}
 
type APGBPal struct {
    BG   BGPal
    OBJ0 OBJPal
    OBJ1 OBJPal
}
 
func (a APGBPal) Bytes() [56]byte {
    var ret [56]byte
    for i, b := range a.BG.Bytes() {
        ret[i] = b
    }
    for i, b := range a.OBJ0.Bytes('0') {
        ret[i+20] = b
    }
    for i, b := range a.OBJ1.Bytes('1') {
        ret[i+38] = b
    }
    return ret
}
 
func main() {
    f, err := os.OpenFile(
        "test.pal",
        os.O_WRONLY|os.O_CREATE|os.O_EXCL,
        0644)
    if err != nil {
        log.Fatalf("can't create test.pal: %v", err)
    }
    aUpPal := APGBPal{
        BG:   MustParseBGPal([]string{"ffffff", "ff8484", "943a3a", "000000"}),
        OBJ0: MustParseOBJPal([]string{"7bff31", "008400", "000000"}),
        OBJ1: MustParseOBJPal([]string{"63a5ff", "0000ff", "000000"}),
    }
    palb := aUpPal.Bytes()
    n, err := f.Write(palb[:])
    if err != nil {
        log.Fatalf("can't save test.pal: %v", err)
    }
    if n != 56 {
        log.Fatalf("unexpected write length: %d", n)
    }
    f.Close()
    os.Exit(0)
}
 

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎