mirror of
https://github.com/MikronMIK32/mik32-uploader.git
synced 2026-01-01 13:37:03 +03:00
107 lines
3.4 KiB
Python
107 lines
3.4 KiB
Python
from enum import Enum
|
|
import os
|
|
from typing import List, NamedTuple, Union
|
|
|
|
from parsers import Record, RecordType, parse_line
|
|
|
|
|
|
class MemoryType(Enum):
|
|
BOOT = 0
|
|
EEPROM = 1
|
|
RAM = 2
|
|
SPIFI = 3
|
|
UNKNOWN = -1
|
|
|
|
|
|
class MemorySection(NamedTuple):
|
|
type: MemoryType
|
|
offset: int
|
|
length: int # Memory section length in bytes
|
|
|
|
|
|
class Segment:
|
|
offset: int
|
|
memory: Union[MemorySection, None] = None
|
|
data: List[int]
|
|
|
|
def __init__(self, offset: int, data: List[int], sections: List[MemorySection]):
|
|
self.offset = offset
|
|
self.data = data
|
|
|
|
self._locate_memory_section(sections)
|
|
|
|
def _locate_memory_section(self, sections: List[MemorySection]):
|
|
for section in sections:
|
|
if self._belongs_memory_section(section, self.offset):
|
|
self.memory = section
|
|
|
|
if self.memory is None:
|
|
raise Exception(
|
|
f"ERROR: segment with offset {self.offset:#0x} doesn't belong to any section")
|
|
|
|
if (self.offset + self.data.__len__()) > (self.memory.offset + self.memory.length):
|
|
raise Exception(
|
|
f"ERROR: segment with offset {self.offset:#0x} "
|
|
f"and length {self.data.__len__()} "
|
|
f"overflows section {self.memory.type.name}"
|
|
)
|
|
|
|
def _belongs_memory_section(self, memory_section: MemorySection, offset: int) -> bool:
|
|
if offset < memory_section.offset:
|
|
return False
|
|
if offset >= (memory_section.offset + memory_section.length):
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
supported_text_formats = [".hex"]
|
|
|
|
|
|
class FirmwareFile:
|
|
file_name: str
|
|
file_extension: str
|
|
segments: List[Segment] = []
|
|
|
|
def __init__(self, path: str, sections: List[MemorySection]):
|
|
self.file_name, self.file_extension = os.path.splitext(path)
|
|
|
|
if self.file_extension in supported_text_formats:
|
|
with open(path) as f:
|
|
lines = f.readlines()
|
|
self._parse_hex(lines, sections)
|
|
elif self.file_extension == ".bin":
|
|
with open(path, "rb") as f:
|
|
bin_content = list(f.read())
|
|
self.segments.append(Segment(offset=0, data=bin_content, sections=sections))
|
|
else:
|
|
raise Exception(f"Unsupported file format: {self.file_extension}")
|
|
|
|
def _parse_hex(self, lines: List[str], sections: List[MemorySection]):
|
|
segments: List[Segment] = []
|
|
|
|
lba: int = 0 # Linear Base Address
|
|
expect_address = 0 # Address of the next byte
|
|
|
|
for i, line in enumerate(lines):
|
|
record: Record = parse_line(line, i, self.file_extension)
|
|
if record.type == RecordType.DATA:
|
|
drlo: int = record.address # Data Record Load Offset
|
|
if (expect_address != lba+drlo) or (segments.__len__() == 0):
|
|
expect_address = lba+drlo
|
|
segments.append(Segment(
|
|
offset=expect_address, data=[], sections=sections))
|
|
|
|
for byte in record.data:
|
|
segments[-1].data.append(byte)
|
|
expect_address += 1
|
|
elif record.type == RecordType.EXTADDR:
|
|
lba = record.address
|
|
elif record.type == RecordType.EOF:
|
|
break
|
|
|
|
self.segments.extend(segments)
|
|
|
|
def get_segments(self) -> List[Segment]:
|
|
return self.segments
|