sharksforarms / deku

Declarative binary reading and writing: bit-level, symmetric, serialization/deserialization
Apache License 2.0
1.11k stars 54 forks source link

i get endian in the binary, how to set endian in the root? #330

Closed jeryaiwei closed 1 year ago

jeryaiwei commented 1 year ago

this is dex binary header struct; i get endian from endian_tag, how to set endian for header?

/// The constant NO_INDEX is used to indicate that an index value is absent.
pub const NO_INDEX: uint = 0xffff_ffff;
pub const ENDIAN_CONSTANT: [ubyte; 4] = [0x12, 0x34, 0x56, 0x78];
pub const REVERSE_ENDIAN_CONSTANT: [ubyte; 4] = [0x78, 0x56, 0x34, 0x12];

/// 8-bit signed int
#[allow(non_camel_case_types)]
pub type byte = i8;
/// 32-bit unsigned int
#[allow(non_camel_case_types)]
pub type uint = u32;
/// 32-bit signed int
#[allow(non_camel_case_types)]
pub type int = i32;
/// 16-bit unsigned int
#[allow(non_camel_case_types)]
pub type ushort = u16;
/// 16-bit signed int
#[allow(non_camel_case_types)]
pub type short = i16;
/// 8-bit unsigned int
#[allow(non_camel_case_types)]
pub type ubyte = u8;
/// 64-bit unsigned int
#[allow(non_camel_case_types)]
pub type ulong = u64;
/// 64-bit signed int
#[allow(non_camel_case_types)]
pub type long = i64;

#[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)]
pub struct Header {
    /// Magic value that must appear at the beginning of the header section
    /// Contains dex\n<version>\0
    pub magic: Magic,
    /// Adler32 checksum of the rest of the file (everything but magic and this field);
    /// Used to detect file corruption.
    pub checksum: uint,
    /// SHA-1 signature (hash) of the rest of the file (everything but magic, checksum, and this field);
    /// Used to uniquely identify files.
    pub signature: [ubyte; 20],
    /// Size of the entire file (including the header), in bytes.
    file_size: uint,
    /// Size of the header in bytes. Usually 0x70.
    header_size: uint,
    /// Endianness tag
    /// A value of 0x12345678 denotes little-endian, 0x78563412 denotes byte-swapped form.
    endian_tag: uint,
    /// Size of the link section, or 0 if this file isn't statically linked
    link_size: uint,
    /// Offset from the start of the file to the link section
    /// The offset, if non-zero, should be into the link_data section.
    link_off: uint,
    /// Offset from the start of the file to the map item.
    /// Must be non-zero and into the data section.
    map_off: uint,
    /// Count of strings in the string identifiers list.
    string_ids_size: uint,
    /// Offset from the start of the file to the string identifiers list
    /// The offset, if non-zero, should be to the start of the string_ids section.
    string_ids_off: uint,
    /// Count of elements in the type identifiers list, at most 65535.
    type_ids_size: uint,
    /// Offset from the start of the file to the type identifiers list
    /// The offset, if non-zero, should be to the start of the type_ids section.
    type_ids_off: uint,
    /// Count of elements in the prototype identifiers list, at most 65535.
    proto_ids_size: uint,
    /// Offset from the start of the file to the prototype identifiers list.
    /// The offset, if non-zero, should be to the start of the proto_ids section.
    proto_ids_off: uint,
    /// Count of elements in the field identifiers list
    field_ids_size: uint,
    /// Offset from the start of the file to the field identifiers list
    /// The offset, if non-zero, should be to the start of the field_ids section
    field_ids_off: uint,
    /// Count of elements in the method identifiers list
    method_ids_size: uint,
    /// Offset from the start of the file to the method identifiers list.
    /// The offset, if non-zero, should be to the start of the method_ids section.
    method_ids_off: uint,
    /// Count of elements in the class definitions list
    class_defs_size: uint,
    /// Offset from the start of the file to the class definitions list.
    /// The offset, if non-zero, should be to the start of the class_defs section.
    class_defs_off: uint,
    /// Size of data section in bytes. Must be an even multiple of sizeof(uint).
    data_size: uint,
    /// Offset from the start of the file to the start of the data section.
    data_off: uint,
}
wcampbell0x2a commented 1 year ago

You might have figured it out by now, but here is a small example of how to do that:

use deku::prelude::*;

use dbg_hex::dbg_hex;

pub const ENDIAN_CONSTANT: [u8; 4] = [0x12, 0x34, 0x56, 0x78];
pub const REVERSE_ENDIAN_CONSTANT: [u8; 4] = [0x78, 0x56, 0x34, 0x12];

/// 32-bit unsigned int
#[allow(non_camel_case_types)]
pub type uint = u32;

#[derive(Debug, DekuRead, DekuWrite)]
#[deku(type = "u32", ctx = "endian: deku::ctx::Endian", endian = "endian")]
enum Endian {
    Little = 0x12345678,
    Big = 0x78563412,
}

impl Endian {
    fn to_ctx(&self) -> deku::ctx::Endian {
        match self {
            Endian::Little => deku::ctx::Endian::Little,
            Endian::Big => deku::ctx::Endian::Big,
        }
    }
}

#[derive(Debug, DekuRead, DekuWrite)]
#[deku(endian = "big")]
pub struct Header {
    /// Endianness tag
    /// A value of 0x12345678 denotes little-endian, 0x78563412 denotes byte-swapped form.
    endian_tag: Endian,
    /// Size of the link section, or 0 if this file isn't statically linked
    #[deku(endian = "endian_tag.to_ctx()")]
    link_size: uint,
}

fn main() {
    // Little
    let bytes = hex::decode("12345678ff112233").unwrap();
    let header = Header::from_bytes((&bytes, 0));
    dbg_hex!(header);

    // Big
    let bytes = hex::decode("78563412ff112233").unwrap();
    let header = Header::from_bytes((&bytes, 0));
    dbg_hex!(header);
}

Output:

[src/[main.rs:43](http://main.rs:43/)] header = Ok(
    (
        (
            [],
            0x0,
        ),
        Header {
            endian_tag: Little,
            link_size: 0x332211ff,
        },
    ),
)
[src/[main.rs:48](http://main.rs:48/)] header = Ok(
    (
        (
            [],
            0x0,
        ),
        Header {
            endian_tag: Big,
            link_size: 0xff112233,
        },
    ),
)
wcampbell0x2a commented 1 year ago

Closing, lmk if you have any more questions

jeryaiwei commented 1 year ago

You might have figured it out by now, but here is a small example of how to do that:

use deku::prelude::*;

use dbg_hex::dbg_hex;

pub const ENDIAN_CONSTANT: [u8; 4] = [0x12, 0x34, 0x56, 0x78];
pub const REVERSE_ENDIAN_CONSTANT: [u8; 4] = [0x78, 0x56, 0x34, 0x12];

/// 32-bit unsigned int
#[allow(non_camel_case_types)]
pub type uint = u32;

#[derive(Debug, DekuRead, DekuWrite)]
#[deku(type = "u32", ctx = "endian: deku::ctx::Endian", endian = "endian")]
enum Endian {
    Little = 0x12345678,
    Big = 0x78563412,
}

impl Endian {
    fn to_ctx(&self) -> deku::ctx::Endian {
        match self {
            Endian::Little => deku::ctx::Endian::Little,
            Endian::Big => deku::ctx::Endian::Big,
        }
    }
}

#[derive(Debug, DekuRead, DekuWrite)]
#[deku(endian = "big")]
pub struct Header {
    /// Endianness tag
    /// A value of 0x12345678 denotes little-endian, 0x78563412 denotes byte-swapped form.
    endian_tag: Endian,
    /// Size of the link section, or 0 if this file isn't statically linked
    #[deku(endian = "endian_tag.to_ctx()")]
    link_size: uint,
}

fn main() {
    // Little
    let bytes = hex::decode("12345678ff112233").unwrap();
    let header = Header::from_bytes((&bytes, 0));
    dbg_hex!(header);

    // Big
    let bytes = hex::decode("78563412ff112233").unwrap();
    let header = Header::from_bytes((&bytes, 0));
    dbg_hex!(header);
}

Output:

[src/[main.rs:43](http://main.rs:43/)] header = Ok(
    (
        (
            [],
            0x0,
        ),
        Header {
            endian_tag: Little,
            link_size: 0x332211ff,
        },
    ),
)
[src/[main.rs:48](http://main.rs:48/)] header = Ok(
    (
        (
            [],
            0x0,
        ),
        Header {
            endian_tag: Big,
            link_size: 0xff112233,
        },
    ),
)

You might have figured it out by now, but here is a small example of how to do that:

use deku::prelude::*;

use dbg_hex::dbg_hex;

pub const ENDIAN_CONSTANT: [u8; 4] = [0x12, 0x34, 0x56, 0x78];
pub const REVERSE_ENDIAN_CONSTANT: [u8; 4] = [0x78, 0x56, 0x34, 0x12];

/// 32-bit unsigned int
#[allow(non_camel_case_types)]
pub type uint = u32;

#[derive(Debug, DekuRead, DekuWrite)]
#[deku(type = "u32", ctx = "endian: deku::ctx::Endian", endian = "endian")]
enum Endian {
    Little = 0x12345678,
    Big = 0x78563412,
}

impl Endian {
    fn to_ctx(&self) -> deku::ctx::Endian {
        match self {
            Endian::Little => deku::ctx::Endian::Little,
            Endian::Big => deku::ctx::Endian::Big,
        }
    }
}

#[derive(Debug, DekuRead, DekuWrite)]
#[deku(endian = "big")]
pub struct Header {
    /// Endianness tag
    /// A value of 0x12345678 denotes little-endian, 0x78563412 denotes byte-swapped form.
    endian_tag: Endian,
    /// Size of the link section, or 0 if this file isn't statically linked
    #[deku(endian = "endian_tag.to_ctx()")]
    link_size: uint,
}

fn main() {
    // Little
    let bytes = hex::decode("12345678ff112233").unwrap();
    let header = Header::from_bytes((&bytes, 0));
    dbg_hex!(header);

    // Big
    let bytes = hex::decode("78563412ff112233").unwrap();
    let header = Header::from_bytes((&bytes, 0));
    dbg_hex!(header);
}

Output:

[src/[main.rs:43](http://main.rs:43/)] header = Ok(
    (
        (
            [],
            0x0,
        ),
        Header {
            endian_tag: Little,
            link_size: 0x332211ff,
        },
    ),
)
[src/[main.rs:48](http://main.rs:48/)] header = Ok(
    (
        (
            [],
            0x0,
        ),
        Header {
            endian_tag: Big,
            link_size: 0xff112233,
        },
    ),
)

thanks,but i think this is not the best method.