Skip to content

Commit 03b84a1

Browse files
committed
feat(cmds): Allow all functions to both return bytes and write to files
1 parent 81b2eae commit 03b84a1

File tree

5 files changed

+136
-70
lines changed

5 files changed

+136
-70
lines changed

esptool/__init__.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -636,8 +636,7 @@ def image_info_cli(ctx, filename):
636636
"--output",
637637
"-o",
638638
type=str,
639-
help="Output filename prefix (for version 1 image), or filename (for version 2 "
640-
"single image)",
639+
help="Output filename or filename prefix (for ESP8266 V1 image)",
641640
)
642641
@click.option(
643642
"--version",
@@ -725,7 +724,11 @@ def image_info_cli(ctx, filename):
725724
def elf2image_cli(ctx, **kwargs):
726725
"""Create an application image from ELF file"""
727726
append_digest = not kwargs.pop("dont_append_digest", False)
728-
elf2image(chip=ctx.obj["chip"], append_digest=append_digest, **kwargs)
727+
# Default to ESP8266 for backwards compatibility
728+
chip = "esp8266" if ctx.obj["chip"] == "auto" else ctx.obj["chip"]
729+
output = kwargs.pop("output", None)
730+
output = "auto" if output is None else output
731+
elf2image(chip=chip, output=output, append_digest=append_digest, **kwargs)
729732

730733

731734
@cli.command("read_mac")

esptool/bin_image.py

+57-25
Original file line numberDiff line numberDiff line change
@@ -457,27 +457,40 @@ def default_output_name(self, input_file):
457457
"""Derive a default output name from the ELF name."""
458458
return input_file + "-"
459459

460-
def save(self, basename):
461-
"""Save a set of V1 images for flashing. Parameter is a base filename."""
462-
# IROM data goes in its own plain binary file
460+
def save(self, filename: str | None) -> tuple[bytes | None, bytes] | None:
461+
irom_data: bytes | None = None
462+
other_data: bytes | None = None
463+
464+
# Handle IROM data
463465
irom_segment = self.get_irom_segment()
464466
if irom_segment is not None:
465-
with open(
466-
"%s0x%05x.bin"
467-
% (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START),
468-
"wb",
469-
) as f:
470-
f.write(irom_segment.data)
471-
472-
# everything but IROM goes at 0x00000 in an image file
473-
normal_segments = self.get_non_irom_segments()
474-
with open("%s0x00000.bin" % basename, "wb") as f:
467+
irom_data = irom_segment.data
468+
469+
# Handle other segments (everything but IROM)
470+
with io.BytesIO() as f: # Use BytesIO to write to memory
471+
normal_segments = self.get_non_irom_segments()
475472
self.write_common_header(f, normal_segments)
476473
checksum = ESPLoader.ESP_CHECKSUM_MAGIC
477474
for segment in normal_segments:
478475
checksum = self.save_segment(f, segment, checksum)
479476
self.append_checksum(f, checksum)
480477

478+
other_data = f.getvalue() # Get the bytes from BytesIO
479+
480+
if filename is not None:
481+
# Write IROM data to a file
482+
if irom_data is not None:
483+
offset = irom_segment.addr - ESP8266ROM.IROM_MAP_START
484+
with open(f"{filename}{offset:#07x}.bin", "wb") as f:
485+
f.write(irom_data)
486+
# Write other data to a file
487+
if other_data is not None:
488+
with open(f"{filename}{0:#07x}.bin", "wb") as f:
489+
f.write(other_data)
490+
return None
491+
else:
492+
return (irom_data, other_data)
493+
481494

482495
ESP8266ROM.BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage
483496

@@ -561,8 +574,8 @@ def default_output_name(self, input_file):
561574
irom_offs & ~(ESPLoader.FLASH_SECTOR_SIZE - 1),
562575
)
563576

564-
def save(self, filename):
565-
with open(filename, "wb") as f:
577+
def save(self, filename: str | None) -> bytes | None:
578+
with io.BytesIO() as f: # Write to memory first
566579
# Save first header for irom0 segment
567580
f.write(
568581
struct.pack(
@@ -592,13 +605,20 @@ def save(self, filename):
592605
checksum = self.save_segment(f, segment, checksum)
593606
self.append_checksum(f, checksum)
594607

595-
# calculate a crc32 of entire file and append
596-
# (algorithm used by recent 8266 SDK bootloaders)
597-
with open(filename, "rb") as f:
608+
# Calculate CRC32 of the entire file and append
609+
f.seek(0) # Move to the start of the BytesIO buffer
598610
crc = esp8266_crc32(f.read())
599-
with open(filename, "ab") as f:
600611
f.write(struct.pack(b"<I", crc))
601612

613+
if filename is not None:
614+
# Write the content to a real file
615+
with open(filename, "wb") as real_file:
616+
real_file.write(f.getvalue())
617+
return None
618+
else:
619+
# Return the bytes if no filename is provided
620+
return f.getvalue()
621+
602622

603623
def esp8266_crc32(data):
604624
"""
@@ -686,7 +706,7 @@ def default_output_name(self, input_file):
686706
def warn_if_unusual_segment(self, offset, size, is_irom_segment):
687707
pass # TODO: add warnings for wrong ESP32 segment offset/size combinations
688708

689-
def save(self, filename):
709+
def save(self, filename: str | None) -> bytes | None:
690710
total_segments = 0
691711
with io.BytesIO() as f: # write file to memory first
692712
self.write_common_header(f, self.segments)
@@ -895,8 +915,14 @@ def get_alignment_data_needed(segment):
895915
pad_by = self.pad_to_size - (image_length % self.pad_to_size)
896916
f.write(b"\xff" * pad_by)
897917

898-
with open(filename, "wb") as real_file:
899-
real_file.write(f.getvalue())
918+
if filename is not None:
919+
# Write the content to a real file
920+
with open(filename, "wb") as real_file:
921+
real_file.write(f.getvalue())
922+
return None
923+
else:
924+
# Return the bytes if no filename is provided
925+
return f.getvalue()
900926

901927
def load_extended_header(self, load_file):
902928
def split_byte(n):
@@ -965,7 +991,7 @@ class ESP8266V3FirmwareImage(ESP32FirmwareImage):
965991
def is_flash_addr(self, addr):
966992
return addr > ESP8266ROM.IROM_MAP_START
967993

968-
def save(self, filename):
994+
def save(self, filename: str | None) -> bytes | None:
969995
total_segments = 0
970996
with io.BytesIO() as f: # write file to memory first
971997
self.write_common_header(f, self.segments)
@@ -1033,8 +1059,14 @@ def save(self, filename):
10331059
digest.update(f.read(image_length))
10341060
f.write(digest.digest())
10351061

1036-
with open(filename, "wb") as real_file:
1037-
real_file.write(f.getvalue())
1062+
if filename is not None:
1063+
# Write the content to a real file
1064+
with open(filename, "wb") as real_file:
1065+
real_file.write(f.getvalue())
1066+
return None
1067+
else:
1068+
# Return the bytes if no filename is provided
1069+
return f.getvalue()
10381070

10391071
def load_extended_header(self, load_file):
10401072
def split_byte(n):

0 commit comments

Comments
 (0)