433 lines
11 KiB
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
|
|
}
|