moto-flash-data/commands/mpar-spritemap-dump.go

433 lines
11 KiB
Go

package commands
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
"os"
"git.cheetah.cat/cheetah/moto-flash-data/flashpart"
"github.com/rs/zerolog/log"
)
type MparSpritemapDumpCommand struct {
SourceFileName string `long:"file" short:"f" required:"true" description:"input-filename"`
}
func writeBitmap(filename string, width, height int, pixelData []byte) error {
// BMP Header constants
fileHeaderSize := 14
infoHeaderSize := 40
bitsPerPixel := 24 // 3 bytes per pixel (RGB)
rowPadding := (4 - (width*3)%4) % 4 // Rows are padded to multiples of 4 bytes
imageSize := (width*3 + rowPadding) * height
fileSize := fileHeaderSize + infoHeaderSize + imageSize
// Create BMP file
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
// BMP File Header
fileHeader := []byte{
'B', 'M', // Signature
0, 0, 0, 0, // File size (placeholder)
0, 0, // Reserved
0, 0, // Reserved
54, 0, 0, 0, // Offset to pixel data
}
binary.LittleEndian.PutUint32(fileHeader[2:], uint32(fileSize))
// BMP Info Header
infoHeader := make([]byte, infoHeaderSize)
binary.LittleEndian.PutUint32(infoHeader[0:], uint32(infoHeaderSize)) // Header size
binary.LittleEndian.PutUint32(infoHeader[4:], uint32(width)) // Image width
binary.LittleEndian.PutUint32(infoHeader[8:], uint32(height)) // Image height
binary.LittleEndian.PutUint16(infoHeader[12:], 1) // Planes
binary.LittleEndian.PutUint16(infoHeader[14:], uint16(bitsPerPixel)) // Bits per pixel
binary.LittleEndian.PutUint32(infoHeader[20:], uint32(imageSize)) // Image size
// Write headers to file
if _, err := file.Write(fileHeader); err != nil {
return err
}
if _, err := file.Write(infoHeader); err != nil {
return err
}
// Write pixel data with row padding
for y := height - 1; y >= 0; y-- { // BMP rows are bottom to top
rowStart := y * width * 3
rowEnd := rowStart + width*3
row := pixelData[rowStart:rowEnd]
// Write row data
if _, err := file.Write(row); err != nil {
return err
}
// Write padding
padding := make([]byte, rowPadding)
if _, err := file.Write(padding); err != nil {
return err
}
}
return nil
}
func writeBitmapRGB565(filename string, width, height int, pixelData []uint16) error {
// BMP Header constants
fileHeaderSize := 14
infoHeaderSize := 40
bitsPerPixel := 16 // 16 bits per pixel for RGB565
rowPadding := (4 - (width*2)%4) % 4 // Rows are padded to multiples of 4 bytes
imageSize := (width*2 + rowPadding) * height
fileSize := fileHeaderSize + infoHeaderSize + imageSize
// Create BMP file
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
// BMP File Header
fileHeader := []byte{
'B', 'M', // Signature
0, 0, 0, 0, // File size (placeholder)
0, 0, // Reserved
0, 0, // Reserved
54, 0, 0, 0, // Offset to pixel data
}
binary.LittleEndian.PutUint32(fileHeader[2:], uint32(fileSize))
// BMP Info Header
infoHeader := make([]byte, infoHeaderSize)
binary.LittleEndian.PutUint32(infoHeader[0:], uint32(infoHeaderSize)) // Header size
binary.LittleEndian.PutUint32(infoHeader[4:], uint32(width)) // Image width
binary.LittleEndian.PutUint32(infoHeader[8:], uint32(height)) // Image height
binary.LittleEndian.PutUint16(infoHeader[12:], 1) // Planes
binary.LittleEndian.PutUint16(infoHeader[14:], uint16(bitsPerPixel)) // Bits per pixel
binary.LittleEndian.PutUint32(infoHeader[20:], uint32(imageSize)) // Image size
// Write headers to file
if _, err := file.Write(fileHeader); err != nil {
return err
}
if _, err := file.Write(infoHeader); err != nil {
return err
}
// Write pixel data with row padding
for y := height - 1; y >= 0; y-- { // BMP rows are bottom to top
rowStart := y * width
rowEnd := rowStart + width
row := pixelData[rowStart:rowEnd]
// Write row data
for _, pixel := range row {
if err := binary.Write(file, binary.LittleEndian, pixel); err != nil {
return err
}
}
// Write padding
padding := make([]byte, rowPadding)
if _, err := file.Write(padding); err != nil {
return err
}
}
return nil
}
func writeBitmapRGB565Bytes(filename string, width, height int, pixelData []byte) error {
// BMP Header constants
fileHeaderSize := 14
infoHeaderSize := 40
bitsPerPixel := 16 // 16 bits per pixel for RGB565
rowPadding := (4 - (width*2)%4) % 4 // Rows are padded to multiples of 4 bytes
imageSize := len(pixelData) // (width*2 + rowPadding) * height
fileSize := fileHeaderSize + infoHeaderSize + imageSize
// Create BMP file
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
// BMP File Header
fileHeader := []byte{
'B', 'M', // Signature
0, 0, 0, 0, // File size (placeholder)
0, 0, // Reserved
0, 0, // Reserved
54, 0, 0, 0, // Offset to pixel data
}
binary.LittleEndian.PutUint32(fileHeader[2:], uint32(fileSize))
// BMP Info Header
infoHeader := make([]byte, infoHeaderSize)
binary.LittleEndian.PutUint32(infoHeader[0:], uint32(infoHeaderSize)) // Header size
binary.LittleEndian.PutUint32(infoHeader[4:], uint32(width)) // Image width
binary.LittleEndian.PutUint32(infoHeader[8:], uint32(height)) // Image height
binary.LittleEndian.PutUint16(infoHeader[12:], 1) // Planes
binary.LittleEndian.PutUint16(infoHeader[14:], uint16(bitsPerPixel)) // Bits per pixel
binary.LittleEndian.PutUint32(infoHeader[20:], uint32(imageSize)) // Image size
// Write headers to file
if _, err := file.Write(fileHeader); err != nil {
return err
}
if _, err := file.Write(infoHeader); err != nil {
return err
}
// Write pixel data with row padding
for y := height - 1; y >= 0; y-- { // BMP rows are bottom to top
rowStart := y * width * 2
rowEnd := rowStart + width*2
row := pixelData[rowStart:rowEnd]
// Write row data
if _, err := file.Write(row); err != nil {
return err
}
// Write padding
padding := make([]byte, rowPadding)
if _, err := file.Write(padding); err != nil {
return err
}
}
return nil
}
func (command *MparSpritemapDumpCommand) Execute(args []string) error {
inputFile, err := os.Open(command.SourceFileName)
if err != nil {
return err
}
defer inputFile.Close()
inputFileStat, err := inputFile.Stat()
if err != nil {
return err
}
log.Info().Int64("fileSize", inputFileStat.Size()).Msg("Reading raw file...")
pdata := make([]byte, inputFileStat.Size())
_, err = io.ReadFull(inputFile, pdata)
if err != nil {
return err
}
fpHeader := flashpart.ParseFlashPartHeader(pdata[:32])
log.Info().Msg(
fmt.Sprintf("Media-ID: 0x%02X, Partition Size: 0x%08X / %d bytes, Partition Tag: %s",
fpHeader.MediaID,
fpHeader.TotalSize,
fpHeader.TotalSize,
fpHeader.VersionText,
),
)
log.Info().Msg(
fmt.Sprintf("Original-Full-Header-Hex: %s", hex.EncodeToString(pdata[:32])),
)
if len(pdata[32:]) != len(pdata)-32 {
return errors.New("something doesnt add up")
}
if len(pdata[32:]) != int(fpHeader.TotalSize)-32 {
log.Error().Uint32("header ts", fpHeader.TotalSize).Uint32("len pdata", uint32(len(pdata))).Msg("size mismatch")
return nil
}
breader := bytes.NewReader(pdata[32:])
// RGB565
// 16 byte initial offset
// each image 13 x 13
{
header := make([]byte, 380)
breader.Read(header)
fmt.Println(header)
}
{
// 42
// 338 + 42 = 380
// + 42 = 718 = wrong
// + 12 = 730
// + 338 = 1068 = wrong
// + 6 = 1074
// + 338 = 1412 = wrong
// + 8 = 1420
// + 338 = 1758 = wrong
// + 6 = 1762
// + 338 = 2100 = wrong
// + 6 = 2106
// + 338 = 2444 = wrong
// + 22 =
// + 338 = 2804 = wrong
// + 6 = 2810
// + 338 = 3148 = wrong
// + 6 = 3154
// + 338 = 3492 = wrong
// + 6 = 3498
// + 338 = 3836 = wrong
// + 6 = 3842
// + 338 = 4180 = wrong
// + 6 = 4186
// + 338 = 4524 = wrong
// + 6 = 4530
// + 338 = 4868 = wrong
// + 6 = 4874
// + 338 = 5212 = wrong
// + 6 = 5218
// + 338 = 5556 = wrong
// + 6 = 5562
// ..
// + 344 = 5906
// + 344 = 6250 = ???
// + 344 = 6594 = wrong
// - 6 = 6568
// + 344 = 6912
// + 344 = 7256
// + 344 = 7600 = wrong
// = 7892 = ?
// = 7944 = right?
// + 292 = 8236 = right!
// + 338 = 8574 = wrong
// + 6 = 8580
// + 344 = 8924
// + 344 = 9268
// + 344 = 9612
// + 344 = 9956
// + 344 = 10300 = wrong
// + 266 = 10566
// + 344 = 10910 = wrong
// + 16 = 10926
// + 344+16 = 11270
// + 344 = 11614
// + 344 = 11958
// + 344 = 12302
// + 344 = 12646
// + 344 = 12990
// + 344 = 13334
// + 344 = 13678
// + 344 = 14022
// + 344 = 14366
// + 344 = 14710
// + 344 = 15054 gps
// + 344 = 15398 alarm
// + 344 = 15742 ??? = WRONG?
// 16086 = wrong
// 16774 = wrong
// 16858 check-green
// 17202 7x13px Tower
// 17390 13x13px lock
// 17734 14x13 dmo-icon
// 18130 13x12 abc-input-mode
// 18448 13x13 save-icon
// 18792 13x13 dial-icon
// + 344
// 19136 13x13 msgicon1
// + 344
// 19480 13x14 alarm-clock-icon
// + 344
// 19824 13x13 clock-icon
// + 344
// 20168 13x13 msg-forward-read icon
// + 344
// 20512 13x13 msg-forward-unread icon
// + 344
// 20856 13x13 green-left-arrow
// + 344
// 21200 13x13 blue-triangle
// + 344
// 21544 13x13 hourglass
// + 344
// 21888 13x13 idk-icon
// + 344
// 22232 13x13 idk-icon
// + 344
// 22576 13x13 idk-icon
// + 344
// 22920
// + 344
// 23264
// + 344
// 23608
// + 344
// 23952
// 24296
// 24640
// 24984
// 25328
// 25672
// 26016 14x12 abc-input-icon
// + 370
// 26386 14x12 normal-case
// + 370
// 26756 14x12 normal-case
// + 370
// 27126 14x12 numbers-input
// + 370
// 27496 14x12 at-input
// +370
// 27866 14x12 'I'
// +370
// 28236 14x12 'I' UP
// +370
// 28606 14x12 'I' UPUP
// +370
// 28976 14x12 '2'
PICTURE := 16
pictoHead := make([]byte, 6)
for i := 0; i < PICTURE; i++ {
breader.Read(pictoHead)
width := binary.LittleEndian.Uint16(pictoHead[0:2])
height := binary.LittleEndian.Uint16(pictoHead[2:4])
magic := binary.LittleEndian.Uint16(pictoHead[4:6])
log.Info().Uint16("width", width).Uint16("height", height).Msg("found pic")
firstPic := make([]byte, width*height*2)
breader.Read(firstPic)
fmt.Println(magic)
writeBitmapRGB565Bytes(fmt.Sprintf("debug/%02d.bmp", i), int(width), int(height), firstPic)
}
}
// tarReader := tar.NewReader(breader)
// for {
// header, err := tarReader.Next()
// if err == io.EOF {
// break
// }
// if err != nil {
// log.Error().Err(err)
// break
// }
// fmt.Println(header)
// // if header.Typeflag == tar.TypeReg && !strings.Contains(header.Name, "._") {
// // // content, err := io.ReadAll(tarReader)
// // // if err != nil {
// // // log.Error().Err(err)
// // // }
// // fmt.Println("FILE_NAME: ", header.Name)
// // ///fmt.Println(string(content))
// // }
// }
return nil
}