From 8e5c077b9fd8ea6e91fc8842839dbb4725fac237 Mon Sep 17 00:00:00 2001 From: cheetah Date: Mon, 20 Jan 2025 14:36:21 +0100 Subject: [PATCH] s19-from-mpar --- commands/commands.go | 3 +- commands/s19-from-mpar.go | 74 +++++++++++++++++++ common/address.go | 11 +++ s19/checksum.go | 14 +++- s19/writer.go | 151 ++++++++++++++++++++++---------------- 5 files changed, 186 insertions(+), 67 deletions(-) create mode 100644 commands/s19-from-mpar.go create mode 100644 common/address.go diff --git a/commands/commands.go b/commands/commands.go index ea4934f..de0c5ca 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -3,7 +3,7 @@ package commands type BaseCommand struct { Help HelpCommand `command:"help" description:"Print this help message"` - PrepareBin PrepareBinCommand `command:"prepare" alias:"p" description:"Prepare a Binary file and attach the propietary header to it"` + //PrepareBin PrepareBinCommand `command:"prepare" alias:"p" description:"Prepare a Binary file and attach the propietary header to it"` DecryptDBF DecryptDBFCommand `command:"decrypt-dbf" description:"decrypt dbf-data"` DecryptDES DecryptDESCommand `command:"decrypt-des" description:"decrypt des-data"` @@ -12,6 +12,7 @@ type BaseCommand struct { S19Tree S19TreeCommand `command:"s19-tree" description:"dump s19 tree"` S19IsolatePart S19IsolatePartitionCommand `command:"s19-isolate-part" description:"isolate a segment from an s19"` + S19FromMpar S19FromMparCommand `command:"s19-from-mpar" description:"creates an s19 from an mpar"` } var MotoCLI BaseCommand diff --git a/commands/s19-from-mpar.go b/commands/s19-from-mpar.go new file mode 100644 index 0000000..1e9915e --- /dev/null +++ b/commands/s19-from-mpar.go @@ -0,0 +1,74 @@ +package commands + +import ( + "os" + + "git.cheetah.cat/cheetah/moto-flash-data/common" + "git.cheetah.cat/cheetah/moto-flash-data/s19" + "github.com/rs/zerolog/log" +) + +type S19FromMparCommand struct { + FileName string `long:"file" short:"f" required:"true" description:"input-file, format: mpar"` + StartAddress string `long:"start-address" required:"true" short:"a" description:"start address"` + + OutputFileName string `long:"out" short:"o" required:"true" description:"output-file, format: s19"` +} + +func (command *S19FromMparCommand) Execute(args []string) error { + baseAddr, err := common.ParseStringHexAddress(command.StartAddress) + if err != nil { + return err + } + log.Info().Str("input", command.FileName).Msg("S19-FromMpar") + + inputFile, err := os.Open(command.FileName) + if err != nil { + return err + } + defer inputFile.Close() + + s19File, err := s19.NewS19Writer(command.OutputFileName) + if err != nil { + return err + } + defer s19File.Close() + log.Info().Str("output", command.OutputFileName).Msg("Writing file...") + + records, err := s19File.CreateRecordsFromBinary(inputFile, uint32(baseAddr), 32) + if err != nil { + return err + } + // S0 14 5E73 3D466C617368205265706F72745E6F3D30 19 + // S0 14 5E73 3D466C617368205265706F72745E6F3D30 + header := []s19.SRecord{ + { + Type: "S0", + Count: 0x14, + Address: 0x5E73, + Data: []byte("=Flash Report^o=0"), // 3D466C617368205265706F72745E6F3D30 + }, + } + footer := []s19.SRecord{ // S701FE + { + Type: "S7", + Count: 0, + Address: 0x01FE, + Data: []byte{}, + }, + } + err = s19File.WriteRecords(header) + if err != nil { + return err + } + err = s19File.WriteRecords(records) + if err != nil { + return err + } + err = s19File.WriteRecords(footer) + if err != nil { + return err + } + + return nil +} diff --git a/common/address.go b/common/address.go new file mode 100644 index 0000000..7ececd7 --- /dev/null +++ b/common/address.go @@ -0,0 +1,11 @@ +package common + +import "strconv" + +func ParseStringHexAddress(hexAddress string) (res uint64, err error) { + parsedValue, err := strconv.ParseUint(hexAddress[2:], 16, 32) + if err != nil { + return 0, err + } + return parsedValue, nil +} diff --git a/s19/checksum.go b/s19/checksum.go index 1c63158..7f0cbb9 100644 --- a/s19/checksum.go +++ b/s19/checksum.go @@ -1,6 +1,9 @@ package s19 -import "strconv" +import ( + "encoding/hex" + "strconv" +) func validateChecksum(line string, checksum byte) bool { sum := byte(0) @@ -10,3 +13,12 @@ func validateChecksum(line string, checksum byte) bool { } return ^sum == checksum } + +func computeChecksum(body string) byte { + sum := byte(0) + for i := 0; i < len(body); i += 2 { + value, _ := hex.DecodeString(body[i : i+2]) + sum += value[0] + } + return ^sum +} diff --git a/s19/writer.go b/s19/writer.go index 0afe09f..c897bb4 100644 --- a/s19/writer.go +++ b/s19/writer.go @@ -3,14 +3,87 @@ package s19 import ( "encoding/hex" "fmt" + "io" "os" ) +type S19Writer struct { + FilePath string + File *os.File +} + +func NewS19Writer(filePath string) (*S19Writer, error) { + file, err := os.Create(filePath) + if err != nil { + return nil, err + } + + return &S19Writer{ + FilePath: filePath, + File: file, + }, nil +} + +func (fw *S19Writer) WriteCPSHeader() error { + // S0 14 5E73 3D466C617368205265706F72745E6F3D30 + // + return nil +} + +// 32byte per record +func (fw *S19Writer) CreateRecordsFromBinary(originalFile *os.File, baseAddress uint32, maxDataSize int) ([]SRecord, error) { + var records []SRecord + buffer := make([]byte, maxDataSize) + currentAddress := baseAddress + + for { + // Read up to maxDataSize bytes from the file + n, err := originalFile.Read(buffer) + if err != nil && err != io.EOF { + return nil, err + } + + // If we read nothing, we're done + if n == 0 { + break + } + + // Create an S3 record + record := SRecord{ + Type: "S3", + Address: currentAddress, + Data: append([]byte{}, buffer[:n]...), // Copy the data + } + records = append(records, record) + + // Update the current address + currentAddress += uint32(n) + } + + return records, nil +} +func (fw *S19Writer) WriteRecords(records []SRecord) error { + for _, record := range records { + line, err := writeSRecord(record) + if err != nil { + return err + } + _, err = fw.File.WriteString(line + "\n") + if err != nil { + return err + } + } + return nil +} +func (fw *S19Writer) Close() error { + return fw.File.Close() +} + // writeSRecord creates a formatted S-record line func writeSRecord(record SRecord) (string, error) { var addressLength int switch record.Type { - case "S1": // 2-byte address + case "S0", "S1": // 2-byte address addressLength = 2 case "S2": // 3-byte address addressLength = 3 @@ -18,6 +91,18 @@ func writeSRecord(record SRecord) (string, error) { addressLength = 4 case "S9": // Termination record with a 2-byte start address addressLength = 2 + case "S7": // The address field contains the starting execution address and is interpreted as a 4-byte big-endian address. There is no data field. + addressLength := 1 + if record.Address > 0xFF { + addressLength = 2 + } + if record.Address > 0xFFFF { + addressLength = 4 + } + addressFormat := fmt.Sprintf("%%0%dX", addressLength*2) + address := fmt.Sprintf(addressFormat, record.Address) + + return fmt.Sprintf("S%s%s", record.Type[1:], address), nil default: return "", fmt.Errorf("unsupported record type: %s", record.Type) } @@ -41,67 +126,3 @@ func writeSRecord(record SRecord) (string, error) { // Construct the final S-record return fmt.Sprintf("S%s%s%02X", record.Type[1:], body, checksum), nil } - -// computeChecksum calculates the checksum for an S-record -func computeChecksum(body string) byte { - sum := byte(0) - for i := 0; i < len(body); i += 2 { - value, _ := hex.DecodeString(body[i : i+2]) - sum += value[0] - } - return ^sum -} - -// writeS19File creates an S19 file with the provided records -func writeS19File(filename string, records []SRecord) error { - file, err := os.Create(filename) - if err != nil { - return err - } - defer file.Close() - - for _, record := range records { - line, err := writeSRecord(record) - if err != nil { - return err - } - _, err = file.WriteString(line + "\n") - if err != nil { - return err - } - } - return nil -} - -func main() { - // Example data to write - data := []byte("Hello, S19 world!") - - // Create a list of records - records := []SRecord{ - { - Type: "S1", - Address: 0x1000, - Data: data[:8], // First chunk - }, - { - Type: "S1", - Address: 0x1008, - Data: data[8:], // Second chunk - }, - { - Type: "S9", - Address: 0x1000, // Start address for the program - Data: nil, - }, - } - - // Write the records to a file - err := writeS19File("output.s19", records) - if err != nil { - fmt.Printf("Error writing S19 file: %v\n", err) - return - } - - fmt.Println("S19 file written successfully.") -}