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 }