diff --git a/libfwupdplugin/fu-srec-firmware.c b/libfwupdplugin/fu-srec-firmware.c
index 056ade5ff..f74e1bf4c 100644
--- a/libfwupdplugin/fu-srec-firmware.c
+++ b/libfwupdplugin/fu-srec-firmware.c
@@ -391,6 +391,102 @@ fu_srec_firmware_parse (FuFirmware *firmware,
return TRUE;
}
+static void
+fu_srec_firmware_write_line (GString *str,
+ FuFirmareSrecRecordKind kind,
+ guint32 addr,
+ const guint8 *buf,
+ gsize bufsz)
+{
+ guint8 csum = 0;
+ g_autoptr(GByteArray) buf_addr = g_byte_array_new ();
+
+ if (kind == FU_FIRMWARE_SREC_RECORD_KIND_S0_HEADER ||
+ kind == FU_FIRMWARE_SREC_RECORD_KIND_S1_DATA_16 ||
+ kind == FU_FIRMWARE_SREC_RECORD_KIND_S5_COUNT_16 ||
+ kind == FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16) {
+ fu_byte_array_append_uint16 (buf_addr, addr, G_BIG_ENDIAN);
+ } else if (kind == FU_FIRMWARE_SREC_RECORD_KIND_S2_DATA_24 ||
+ kind == FU_FIRMWARE_SREC_RECORD_KIND_S6_COUNT_24 ||
+ kind == FU_FIRMWARE_SREC_RECORD_KIND_S8_TERMINATION_24) {
+ fu_byte_array_append_uint32 (buf_addr, addr, G_BIG_ENDIAN);
+ g_byte_array_remove_index (buf_addr, 0);
+ } else if (kind == FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32 ||
+ kind == FU_FIRMWARE_SREC_RECORD_KIND_S7_COUNT_32) {
+ fu_byte_array_append_uint32 (buf_addr, addr, G_BIG_ENDIAN);
+ }
+
+ /* bytecount + address + data */
+ csum = buf_addr->len + bufsz + 1;
+ for (guint i = 0; i < buf_addr->len; i++)
+ csum += buf_addr->data[i];
+ for (guint i = 0; i < bufsz; i++)
+ csum += buf[i];
+ csum ^= 0xff;
+
+ /* output record */
+ g_string_append_printf (str, "S%X", kind);
+ g_string_append_printf (str, "%02X", (guint) (buf_addr->len + bufsz + 1));
+ for (guint i = 0; i < buf_addr->len; i++)
+ g_string_append_printf (str, "%02X", buf_addr->data[i]);
+ for (guint i = 0; i < bufsz; i++)
+ g_string_append_printf (str, "%02X", buf[i]);
+ g_string_append_printf (str, "%02X\n", csum);
+}
+
+static GBytes *
+fu_srec_firmware_write (FuFirmware *firmware, GError **error)
+{
+ g_autoptr(GString) str = g_string_new (NULL);
+ g_autoptr(GPtrArray) chunks = NULL;
+ g_autoptr(GBytes) buf_blob = NULL;
+ const gchar *id = fu_firmware_get_id (firmware);
+ gsize id_strlen = id != NULL ? strlen (id) : 0;
+ FuFirmareSrecRecordKind kind_data = FU_FIRMWARE_SREC_RECORD_KIND_S1_DATA_16;
+ FuFirmareSrecRecordKind kind_coun = FU_FIRMWARE_SREC_RECORD_KIND_S5_COUNT_16;
+ FuFirmareSrecRecordKind kind_term = FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16;
+
+ /* upgrade to longer addresses? */
+ if (fu_firmware_get_addr (firmware) >= (1ull << 24)) {
+ kind_data = FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32;
+ kind_coun = FU_FIRMWARE_SREC_RECORD_KIND_S6_COUNT_24;
+ kind_term = FU_FIRMWARE_SREC_RECORD_KIND_S7_COUNT_32; /* intentional... */
+ } else if (fu_firmware_get_addr (firmware) >= (1ull << 16)) {
+ kind_data = FU_FIRMWARE_SREC_RECORD_KIND_S2_DATA_24;
+ kind_coun = FU_FIRMWARE_SREC_RECORD_KIND_S6_COUNT_24;
+ kind_term = FU_FIRMWARE_SREC_RECORD_KIND_S8_TERMINATION_24;
+ }
+
+ /* main blob */
+ buf_blob = fu_firmware_get_bytes (firmware, error);
+ if (buf_blob == NULL)
+ return NULL;
+
+ /* header */
+ fu_srec_firmware_write_line (str, FU_FIRMWARE_SREC_RECORD_KIND_S0_HEADER,
+ 0x0, (const guint8 *) id, id_strlen);
+
+ /* payload */
+ chunks = fu_chunk_array_new_from_bytes (buf_blob,
+ fu_firmware_get_addr (firmware),
+ 0x0, 64);
+ for (guint i = 0; i < chunks->len; i++) {
+ FuChunk *chk = g_ptr_array_index (chunks, i);
+ fu_srec_firmware_write_line (str, kind_data, 0x0,
+ fu_chunk_get_data (chk),
+ fu_chunk_get_data_sz (chk));
+ }
+
+ /* number of records */
+ fu_srec_firmware_write_line (str, kind_coun, chunks->len, NULL, 0);
+
+ /* EOF */
+ fu_srec_firmware_write_line (str, kind_term, 0x0, NULL, 0);
+
+ /* success */
+ return g_string_free_to_bytes (g_steal_pointer (&str));
+}
+
static void
fu_srec_firmware_finalize (GObject *object)
{
@@ -414,6 +510,7 @@ fu_srec_firmware_class_init (FuSrecFirmwareClass *klass)
object_class->finalize = fu_srec_firmware_finalize;
klass_firmware->parse = fu_srec_firmware_parse;
klass_firmware->tokenize = fu_srec_firmware_tokenize;
+ klass_firmware->write = fu_srec_firmware_write;
}
/**
diff --git a/src/fuzzing/firmware/srec-addr32.srec b/src/fuzzing/firmware/srec-addr32.srec
index 1f15d20ce..7b68dee42 100644
--- a/src/fuzzing/firmware/srec-addr32.srec
+++ b/src/fuzzing/firmware/srec-addr32.srec
@@ -1,3 +1,4 @@
-S00400006299
-S108000031202D6E0A01
-S5030001FB
+S00600004844521B
+S3100000000068656C6C6F20776F726C6493
+S604000001FA
+S70500000000FA
diff --git a/src/fuzzing/firmware/srec.srec b/src/fuzzing/firmware/srec.srec
index 6af8c37f4..306b69142 100644
--- a/src/fuzzing/firmware/srec.srec
+++ b/src/fuzzing/firmware/srec.srec
@@ -1,3 +1,4 @@
-S0040000619A
-S30A0000000031202D6E0AFF
+S00600004844521B
+S10E000068656C6C6F20776F726C6495
S5030001FB
+S9030000FC
diff --git a/src/fuzzing/srec-addr32.builder.xml b/src/fuzzing/srec-addr32.builder.xml
new file mode 100644
index 000000000..087e0407a
--- /dev/null
+++ b/src/fuzzing/srec-addr32.builder.xml
@@ -0,0 +1,5 @@
+
+ 0x1000000
+ HDR
+ aGVsbG8gd29ybGQ=
+
diff --git a/src/fuzzing/srec.builder.xml b/src/fuzzing/srec.builder.xml
new file mode 100644
index 000000000..9a1f1b3f1
--- /dev/null
+++ b/src/fuzzing/srec.builder.xml
@@ -0,0 +1,4 @@
+
+ HDR
+ aGVsbG8gd29ybGQ=
+