From 7313897064dc20ed23f5803d2a8dfa8ae1e41efe Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Sat, 4 Oct 2025 01:29:03 +0100 Subject: [PATCH] Add parsing and serialization functions for Lisa family disk tags --- CMakeLists.txt | 4 +- include/aaruformat/structs/lisa_tag.h | 95 ++++++ src/lisa_tag.c | 425 ++++++++++++++++++++++++++ 3 files changed, 523 insertions(+), 1 deletion(-) create mode 100644 include/aaruformat/structs/lisa_tag.h create mode 100644 src/lisa_tag.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 754da45..493877e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,7 +131,9 @@ add_library(aaruformat SHARED include/aaruformat/consts.h include/aaruformat/enu src/checksum/sha1.c include/sha1.h src/checksum/sha256.c - include/sha256.h) + include/sha256.h + src/lisa_tag.c + include/aaruformat/structs/lisa_tag.h) include_directories(include include/aaruformat 3rdparty/uthash/include 3rdparty/uthash/src) diff --git a/include/aaruformat/structs/lisa_tag.h b/include/aaruformat/structs/lisa_tag.h new file mode 100644 index 0000000..96ebb23 --- /dev/null +++ b/include/aaruformat/structs/lisa_tag.h @@ -0,0 +1,95 @@ +/* + * This file is part of the Aaru Data Preservation Suite. + * Copyright (c) 2019-2025 Natalia Portillo. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/** + * @file lisa_tag.h + * @brief Structure definitions and conversion/serialization function declarations for Lisa family disk tags. + * + * Provides compact C representations for on-disk tag metadata used by Sony, Profile and Priam storage devices + * in the Apple Lisa ecosystem. See lisa_tag.c for detailed description of field semantics and conversion rules. + */ + +#ifndef LIBAARUFORMAT_LISA_TAG_H +#define LIBAARUFORMAT_LISA_TAG_H + +#include + +#pragma pack(push, 1) + +typedef struct sony_tag +{ + uint16_t version; ///< 0x00, Lisa OS version number + uint8_t kind : 2; ///< 0x02 bits 7 to 6, kind of info in this block + uint8_t reserved : 6; ///< 0x02 bits 5 to 0, reserved + uint8_t volume; ///< 0x03, disk volume number + int16_t file_id; ///< 0x04, file ID + uint16_t rel_page; ///< 0x06, relative page number + uint16_t next_block; ///< 0x08, next block, 0x7FF if it's last block, 0x8000 set if block is valid + uint16_t prev_block; ///< 0x0A, previous block, 0x7FF if it's first block +} sony_tag; + +typedef struct profile_tag +{ + uint16_t version; ///< 0x00, Lisa OS version number + uint8_t kind : 2; ///< 0x02 bits 7 to 6, kind of info in this block + uint8_t reserved : 6; ///< 0x02 bits 5 to 0, reserved + uint8_t volume; ///< 0x03, disk volume number + int16_t file_id; ///< 0x04, file ID + uint8_t valid_chk : 1; ///< 0x06 bit 7, checksum valid? + uint16_t used_bytes : 15; ///< 0x06 bits 6 to 0, used bytes in block + uint32_t abs_page; ///< 0x08, 3 bytes, absolute page number + uint8_t checksum; ///< 0x0B, checksum of data + uint16_t rel_page; ///< 0x0C, relative page number + uint32_t next_block; ///< 0x0E, 3 bytes, next block, 0xFFFFFF if it's last block + uint32_t prev_block; ///< 0x11, 3 bytes, previous block, 0xFFFFFF if it's first block +} profile_tag; + +typedef struct priam_tag +{ + uint16_t version; ///< 0x00, Lisa OS version number + uint8_t kind : 2; ///< 0x02 bits 7 to 6, kind of info in this block + uint8_t reserved : 6; ///< 0x02 bits 5 to 0, reserved + uint8_t volume; ///< 0x03, disk volume number + int16_t file_id; ///< 0x04, file ID + uint8_t valid_chk : 1; ///< 0x06 bit 7, checksum valid? + uint16_t used_bytes : 15; ///< 0x06 bits 6 to 0, used bytes in block + uint32_t abs_page; ///< 0x08, 3 bytes, absolute page number + uint8_t checksum; ///< 0x0B, checksum of data + uint16_t rel_page; ///< 0x0C, relative page number + uint32_t next_block; ///< 0x0E, 3 bytes, next block, 0xFFFFFF if it's last block + uint32_t prev_block; ///< 0x11, 3 bytes, previous block, 0xFFFFFF if it's first block + uint32_t disk_size; ///< 0x14, disk size +} priam_tag; + +#pragma pack(pop) + +sony_tag bytes_to_sony_tag(const uint8_t *bytes); +profile_tag bytes_to_profile_tag(const uint8_t *bytes); +priam_tag bytes_to_priam_tag(const uint8_t *bytes); +uint8_t *sony_tag_to_bytes(sony_tag tag); +uint8_t *profile_tag_to_bytes(profile_tag tag); +uint8_t *priam_tag_to_bytes(priam_tag tag); + +profile_tag sony_tag_to_profile(sony_tag tag); +profile_tag priam_tag_to_profile(priam_tag tag); +priam_tag sony_tag_to_priam(sony_tag tag); +priam_tag profile_tag_to_priam(profile_tag tag); +sony_tag profile_tag_to_sony(profile_tag tag); +sony_tag priam_tag_to_sony(priam_tag tag); + +#endif // LIBAARUFORMAT_LISA_TAG_H diff --git a/src/lisa_tag.c b/src/lisa_tag.c new file mode 100644 index 0000000..9f4fa35 --- /dev/null +++ b/src/lisa_tag.c @@ -0,0 +1,425 @@ +/* + * This file is part of the Aaru Data Preservation Suite. + * Copyright (c) 2019-2025 Natalia Portillo. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/** + * @file lisa_tag.c + * @brief Parsing, conversion and serialization helpers for Lisa (Apple Lisa / Profile / Priam / Sony) + * disk block tags. + * + * The Lisa operating system and related peripheral disk formats (Sony 3.5" diskettes, ProFile hard disks, + * Priam drives) store per-block (or per-page) metadata in compact on-disk "tag" records. These records use + * mixed-size big-endian integer fields and bit fields to describe allocation chains, relative/absolute page + * numbering, volume numbers, versions, and (for some variants) checksums and disk sizing. + * + * This module provides: + * - Parsing raw big-endian on-disk tag byte sequences into the in-memory structures: ::sony_tag, + * ::profile_tag, ::priam_tag. + * - Lossless (within each format's limits) conversion between the different tag structure variants, + * mapping sentinel values (e.g. 0x7FF <-> 0xFFFFFF for end-of-chain markers) appropriately. + * - Serialization of the in-memory structures back into their canonical on-disk packed byte layouts. + * + * Endianness: All multi-byte numeric fields in the on-disk tag formats are stored big-endian. The helper + * routines perform the necessary byte shifting and masking; the resulting structure fields are in host + * endianness. + * + * Memory management: The serialization functions ( *_tag_to_bytes ) allocate new byte buffers using + * calloc(). The caller owns the returned pointer and must free() it when no longer needed. Parsing + * functions do not allocate dynamic memory; they return structures by value. + * + * Validation: For performance and low-level usage reasons, the parsing routines do not perform NULL + * pointer checks nor length validation on the input byte arrays. Supplying a NULL pointer or insufficient + * bytes is undefined behavior. If such safety is required, perform validation before calling these + * functions. + * + * Sentinel mapping: Sony tag next/prev block fields are 11-bit values (masked with 0x7FF) with the value + * 0x7FF meaning end-of-chain (or no previous). In the wider Profile/Priam formats these become 24-bit + * fields, with 0xFFFFFF used for the same semantic meaning. Conversion helpers transparently translate + * between these sentinel values. + * + * Bit fields: The structures define C bit fields for compact representation (e.g., kind, reserved, + * valid_chk, used_bytes). The parsing and serialization routines reconstruct / pack these manually through + * masking and shifting because on-disk layout may not match compiler bit-field packing. This provides + * portability irrespective of platform-specific bit-field ordering. + */ + +#include + +#include "aaruformat/structs/lisa_tag.h" + +#include + +/** + * @brief Parse a 12-byte Sony tag record into a ::sony_tag structure. + * + * Field extraction follows the documented on-disk layout (big-endian). The next/prev block numbers are + * masked to 11 bits (0x7FF) since only those bits are defined in the Sony tag format. The reserved bits + * are preserved in the structure's reserved member for round-tripping if needed. + * + * @param bytes Pointer to at least 12 consecutive bytes containing a raw Sony tag as read from disk. + * @return A populated ::sony_tag structure with host-endian integer fields. + * @warning Behavior is undefined if bytes is NULL or does not point to at least 12 readable bytes. + * @note No validation of version, kind, or chain linkage consistency is performed. + */ +sony_tag bytes_to_sony_tag(const uint8_t *bytes) +{ + sony_tag tag = {0}; + + tag.version = (uint16_t)(bytes[0] << 8 | bytes[1]); + tag.kind = (bytes[2] & 0xC0) >> 6; + tag.reserved = bytes[2] & 0x3F; + tag.volume = bytes[3]; + tag.file_id = (int16_t)(bytes[4] << 8 | bytes[5]); + tag.rel_page = (uint16_t)(bytes[6] << 8 | bytes[7]); + tag.next_block = (uint16_t)(bytes[8] << 8 | bytes[9]) & 0x7FF; + tag.prev_block = (uint16_t)(bytes[10] << 8 | bytes[11]) & 0x7FF; + + return tag; +} + +/** + * @brief Parse a 24-byte Priam tag record into a ::priam_tag structure. + * + * Priam tags extend the Profile tag with an additional 4-byte disk_size field. This routine decodes all bit fields, + * separating the valid checksum flag (bit 7 of byte 6) from the 15-bit used_bytes value. + * + * @param bytes Pointer to at least 24 consecutive bytes containing a raw Priam tag. + * @return A populated ::priam_tag structure. + * @warning Undefined behavior if bytes is NULL or shorter than 24 bytes. + * @note The abs_page, next_block and prev_block 24-bit values are expanded into 32-bit integers in host memory. + */ +priam_tag bytes_to_priam_tag(const uint8_t *bytes) +{ + priam_tag tag = {0}; + + tag.version = (uint16_t)(bytes[0] << 8 | bytes[1]); + tag.kind = (bytes[2] & 0xC0) >> 6; + tag.reserved = bytes[2] & 0x3F; + tag.volume = bytes[3]; + tag.file_id = (int16_t)(bytes[4] << 8 | bytes[5]); + tag.used_bytes = (uint16_t)((bytes[6] & 0x7F) << 8 | bytes[7]); + tag.valid_chk = (bytes[6] & 0x80) != 0; + tag.abs_page = (uint32_t)(bytes[8] << 16 | bytes[9] << 8 | bytes[10]); + tag.checksum = bytes[11]; + tag.rel_page = (uint16_t)(bytes[12] << 8 | bytes[13]); + tag.next_block = (uint32_t)(bytes[14] << 16 | bytes[15] << 8 | bytes[16]); + tag.prev_block = (uint32_t)(bytes[17] << 16 | bytes[18] << 8 | bytes[19]); + tag.disk_size = (uint32_t)(bytes[20] << 24 | bytes[21] << 16 | bytes[22] << 8 | bytes[23]); + + return tag; +} + +/** + * @brief Parse a 20-byte Profile tag record into a ::profile_tag structure. + * + * A Profile tag is similar to Priam but without the trailing disk_size field. Multi-byte numeric values are big-endian. + * + * @param bytes Pointer to at least 20 consecutive bytes containing a raw Profile tag. + * @return A populated ::profile_tag structure. + * @warning Undefined behavior if bytes is NULL or shorter than 20 bytes. + */ +profile_tag bytes_to_profile_tag(const uint8_t *bytes) +{ + profile_tag tag = {0}; + + tag.version = (uint16_t)(bytes[0] << 8 | bytes[1]); + tag.kind = (bytes[2] & 0xC0) >> 6; + tag.reserved = bytes[2] & 0x3F; + tag.volume = bytes[3]; + tag.file_id = (int16_t)(bytes[4] << 8 | bytes[5]); + tag.used_bytes = (uint16_t)((bytes[6] & 0x7F) << 8 | bytes[7]); + tag.valid_chk = (bytes[6] & 0x80) != 0; + tag.abs_page = (uint32_t)(bytes[8] << 16 | bytes[9] << 8 | bytes[10]); + tag.checksum = bytes[11]; + tag.rel_page = (uint16_t)(bytes[12] << 8 | bytes[13]); + tag.next_block = (uint32_t)(bytes[14] << 16 | bytes[15] << 8 | bytes[16]); + tag.prev_block = (uint32_t)(bytes[17] << 16 | bytes[18] << 8 | bytes[19]); + + return tag; +} + +/** + * @brief Convert a ::sony_tag to a ::profile_tag representation. + * + * Sony tags contain a reduced-width (11-bit) next/prev block. To preserve chain termination semantics, values of + * 0x7FF are translated to 0xFFFFFF (24-bit "no next/prev" sentinel). Other values are copied verbatim into the wider + * fields. Only fields common to both formats are populated; Profile-specific fields (abs_page, checksum, used_bytes, + * valid_chk, reserved) remain zero-initialized. + * + * @param tag Source Sony tag. + * @return Profile tag with mapped values. + */ +profile_tag sony_tag_to_profile(const sony_tag tag) +{ + profile_tag profile = {0}; + + profile.file_id = tag.file_id; + profile.kind = tag.kind; + profile.next_block = tag.next_block == 0x7FF ? 0xFFFFFF : tag.next_block; + profile.prev_block = tag.prev_block == 0x7FF ? 0xFFFFFF : tag.prev_block; + profile.rel_page = tag.rel_page; + profile.version = tag.version; + profile.volume = tag.volume; + + return profile; +} + +/** + * @brief Convert a ::sony_tag to a ::priam_tag representation. + * + * Similar to sony_tag_to_profile() but returns a Priam tag. Priam-specific fields (abs_page, checksum, used_bytes, + * valid_chk, disk_size, reserved) are zero-initialized. 0x7FF next/prev sentinels expand to 0xFFFFFF. + * + * @param tag Source Sony tag. + * @return Priam tag with mapped values. + */ +priam_tag sony_tag_to_priam(const sony_tag tag) +{ + priam_tag priam = {0}; + + priam.file_id = tag.file_id; + priam.kind = tag.kind; + priam.next_block = tag.next_block == 0x7FF ? 0xFFFFFF : tag.next_block; + priam.prev_block = tag.prev_block == 0x7FF ? 0xFFFFFF : tag.prev_block; + priam.rel_page = tag.rel_page; + priam.version = tag.version; + priam.volume = tag.volume; + + return priam; +} + +/** + * @brief Convert a ::profile_tag to a ::priam_tag. + * + * Copies all overlapping fields directly. The Priam-only disk_size field remains zero unless modified later. + * + * @param tag Source Profile tag. + * @return Priam tag with copied values. + */ +priam_tag profile_tag_to_priam(const profile_tag tag) +{ + priam_tag priam = {0}; + + priam.abs_page = tag.abs_page; + priam.checksum = tag.checksum; + priam.file_id = tag.file_id; + priam.kind = tag.kind; + priam.next_block = tag.next_block; + priam.prev_block = tag.prev_block; + priam.rel_page = tag.rel_page; + priam.used_bytes = tag.used_bytes; + priam.valid_chk = tag.valid_chk; + priam.version = tag.version; + priam.volume = tag.volume; + + return priam; +} + +/** + * @brief Convert a ::profile_tag to a ::sony_tag. + * + * Narrows 24-bit next/prev block numbers to 11 bits. A value of 0xFFFFFF (end-of-chain sentinel) is mapped back to + * 0x7FF. Any non-sentinel value is truncated to its lower 11 bits. Fields without Sony equivalents are dropped. + * + * @param tag Source Profile tag. + * @return Sony tag with mapped/narrowed values. + */ +sony_tag profile_tag_to_sony(const profile_tag tag) +{ + sony_tag sony = {0}; + + sony.file_id = tag.file_id; + sony.kind = tag.kind; + sony.next_block = tag.next_block == 0xFFFFFF ? 0x7FF : (uint16_t)tag.next_block & 0x7FF; + sony.prev_block = tag.prev_block == 0xFFFFFF ? 0x7FF : (uint16_t)tag.prev_block & 0x7FF; + sony.rel_page = tag.rel_page; + sony.version = tag.version; + sony.volume = tag.volume; + + return sony; +} + +/** + * @brief Convert a ::priam_tag to a ::sony_tag. + * + * See profile_tag_to_sony(); Priam-only fields (abs_page, checksum, used_bytes, valid_chk, disk_size) are discarded. + * 0xFFFFFF chain terminators become 0x7FF; other values are truncated to 11 bits. + * + * @param tag Source Priam tag. + * @return Sony tag with mapped values. + */ +sony_tag priam_tag_to_sony(const priam_tag tag) +{ + sony_tag sony = {0}; + + sony.file_id = tag.file_id; + sony.kind = tag.kind; + sony.next_block = tag.next_block == 0xFFFFFF ? 0x7FF : (uint16_t)tag.next_block & 0x7FF; + sony.prev_block = tag.prev_block == 0xFFFFFF ? 0x7FF : (uint16_t)tag.prev_block & 0x7FF; + sony.rel_page = tag.rel_page; + sony.version = tag.version; + sony.volume = tag.volume; + + return sony; +} + +/** + * @brief Convert a ::priam_tag to a ::profile_tag. + * + * Copies overlapping fields, omitting the Priam-specific disk_size. Useful when reusing Priam metadata in a context + * that expects Profile tag semantics. + * + * @param tag Source Priam tag. + * @return Profile tag with copied values. + */ +profile_tag priam_tag_to_profile(const priam_tag tag) +{ + profile_tag profile = {0}; + + profile.abs_page = tag.abs_page; + profile.checksum = tag.checksum; + profile.file_id = tag.file_id; + profile.kind = tag.kind; + profile.next_block = tag.next_block; + profile.prev_block = tag.prev_block; + profile.rel_page = tag.rel_page; + profile.used_bytes = tag.used_bytes; + profile.valid_chk = tag.valid_chk; + profile.version = tag.version; + profile.volume = tag.volume; + + return profile; +} + +/** + * @brief Serialize a ::profile_tag into a newly allocated 20-byte big-endian on-disk representation. + * + * Bit/byte packing mirrors bytes_to_profile_tag(). The 24-bit next_block, prev_block and abs_page values are written + * most-significant byte first. The used_bytes high 7 bits and valid_chk flag are packed into the first byte of the + * 16-bit used_bytes field as per on-disk layout. + * + * @param tag Profile tag to serialize. + * @return Pointer to a 20-byte buffer containing the serialized tag, or NULL on allocation failure. + * @retval NULL Allocation failure (caller should check before use). + * @note Caller must free() the returned buffer. + */ +uint8_t *profile_tag_to_bytes(const profile_tag tag) +{ + uint8_t *bytes = calloc(1, 20); + if(!bytes) return NULL; + + bytes[0] = tag.version >> 8 & 0xFF; + bytes[1] = tag.version & 0xFF; + bytes[2] = (uint8_t)(tag.kind << 6); + bytes[3] = tag.volume; + bytes[4] = tag.file_id >> 8 & 0xFF; + bytes[5] = tag.file_id & 0xFF; + bytes[6] = tag.used_bytes >> 8 & 0x7F; + if(tag.valid_chk) bytes[6] += 0x80; + bytes[7] = tag.used_bytes & 0xFF; + bytes[8] = tag.abs_page >> 16 & 0xFF; + bytes[9] = tag.abs_page >> 8 & 0xFF; + bytes[10] = tag.abs_page & 0xFF; + bytes[11] = tag.checksum; + bytes[12] = tag.rel_page >> 8 & 0xFF; + bytes[13] = tag.rel_page & 0xFF; + bytes[14] = tag.next_block >> 16 & 0xFF; + bytes[15] = tag.next_block >> 8 & 0xFF; + bytes[16] = tag.next_block & 0xFF; + bytes[17] = tag.prev_block >> 16 & 0xFF; + bytes[18] = tag.prev_block >> 8 & 0xFF; + bytes[19] = tag.prev_block & 0xFF; + + return bytes; +} + +/** + * @brief Serialize a ::priam_tag into a newly allocated 24-byte big-endian on-disk representation. + * + * Layout matches bytes_to_priam_tag(). Includes the final 4-byte disk_size field. Packing rules for used_bytes and + * valid_chk mirror those used in profile_tag_to_bytes(). + * + * @param tag Priam tag to serialize. + * @return Pointer to a 24-byte buffer, or NULL on allocation failure. + * @retval NULL Allocation failure. + * @note Caller must free() the returned buffer. + */ +uint8_t *priam_tag_to_bytes(const priam_tag tag) +{ + uint8_t *bytes = calloc(1, 24); + if(!bytes) return NULL; + + bytes[0] = tag.version >> 8 & 0xFF; + bytes[1] = tag.version & 0xFF; + bytes[2] = (uint8_t)(tag.kind << 6); + bytes[3] = tag.volume; + bytes[4] = tag.file_id >> 8 & 0xFF; + bytes[5] = tag.file_id & 0xFF; + bytes[6] = tag.used_bytes >> 8 & 0x7F; + if(tag.valid_chk) bytes[6] += 0x80; + bytes[7] = tag.used_bytes & 0xFF; + bytes[8] = tag.abs_page >> 16 & 0xFF; + bytes[9] = tag.abs_page >> 8 & 0xFF; + bytes[10] = tag.abs_page & 0xFF; + bytes[11] = tag.checksum; + bytes[12] = tag.rel_page >> 8 & 0xFF; + bytes[13] = tag.rel_page & 0xFF; + bytes[14] = tag.next_block >> 16 & 0xFF; + bytes[15] = tag.next_block >> 8 & 0xFF; + bytes[16] = tag.next_block & 0xFF; + bytes[17] = tag.prev_block >> 16 & 0xFF; + bytes[18] = tag.prev_block >> 8 & 0xFF; + bytes[19] = tag.prev_block & 0xFF; + bytes[20] = tag.disk_size >> 24 & 0xFF; + bytes[21] = tag.disk_size >> 16 & 0xFF; + bytes[22] = tag.disk_size >> 8 & 0xFF; + bytes[23] = tag.disk_size & 0xFF; + + return bytes; +} + +/** + * @brief Serialize a ::sony_tag into a newly allocated 12-byte big-endian on-disk representation. + * + * The next_block and prev_block fields are masked to 11 bits (0x7FF) during packing. Caller should ensure sentinel + * semantics are already encoded (i.e., use 0x7FF for end-of-chain) prior to calling. + * + * @param tag Sony tag to serialize. + * @return Pointer to a 12-byte buffer, or NULL on allocation failure. + * @retval NULL Allocation failure. + * @note Caller must free() the returned buffer. + */ +uint8_t *sony_tag_to_bytes(sony_tag tag) +{ + uint8_t *bytes = calloc(1, 12); + if(!bytes) return NULL; + + bytes[0] = tag.version >> 8 & 0xFF; + bytes[1] = tag.version & 0xFF; + bytes[2] = (uint8_t)(tag.kind << 6); + bytes[3] = tag.volume; + bytes[4] = tag.file_id >> 8 & 0xFF; + bytes[5] = tag.file_id & 0xFF; + bytes[6] = tag.rel_page >> 8 & 0xFF; + bytes[7] = tag.rel_page & 0xFF; + bytes[8] = (uint8_t)((tag.next_block & 0x7FF) >> 8); + bytes[9] = (uint8_t)(tag.next_block & 0xFF); + bytes[10] = (uint8_t)((tag.prev_block & 0x7FF) >> 8); + bytes[11] = (uint8_t)(tag.prev_block & 0xFF); + + return bytes; +} \ No newline at end of file