binaryninja/binary_view/
writer.rs

1// Copyright 2022-2025 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A convenience class for writing binary data
16
17use binaryninjacore_sys::*;
18use std::fmt::Debug;
19
20use crate::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt};
21use crate::Endianness;
22
23use crate::rc::Ref;
24use std::io::{ErrorKind, Seek, SeekFrom, Write};
25
26pub struct BinaryWriter {
27    view: Ref<BinaryView>,
28    handle: *mut BNBinaryWriter,
29}
30
31impl BinaryWriter {
32    pub fn new(view: &BinaryView) -> Self {
33        let handle = unsafe { BNCreateBinaryWriter(view.handle) };
34        Self {
35            view: view.to_owned(),
36            handle,
37        }
38    }
39
40    pub fn new_with_opts(view: &BinaryView, options: &BinaryWriterOptions) -> Self {
41        let mut writer = Self::new(view);
42        if let Some(endianness) = options.endianness {
43            writer.set_endianness(endianness);
44        }
45        if let Some(address) = options.address {
46            writer.seek_to_offset(address);
47        }
48        writer
49    }
50
51    pub fn endianness(&self) -> Endianness {
52        unsafe { BNGetBinaryWriterEndianness(self.handle) }
53    }
54
55    pub fn set_endianness(&mut self, endianness: Endianness) {
56        unsafe { BNSetBinaryWriterEndianness(self.handle, endianness) }
57    }
58
59    pub fn seek_to_offset(&mut self, offset: u64) {
60        unsafe { BNSeekBinaryWriter(self.handle, offset) }
61    }
62
63    pub fn seek_to_relative_offset(&mut self, offset: i64) {
64        unsafe { BNSeekBinaryWriterRelative(self.handle, offset) }
65    }
66
67    pub fn offset(&self) -> u64 {
68        unsafe { BNGetWriterPosition(self.handle) }
69    }
70}
71
72impl Debug for BinaryWriter {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        f.debug_struct("BinaryWriter")
75            .field("offset", &self.offset())
76            .field("endianness", &self.endianness())
77            .finish()
78    }
79}
80
81impl Seek for BinaryWriter {
82    /// Seek to the specified position.
83    fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
84        match pos {
85            SeekFrom::Current(offset) => self.seek_to_relative_offset(offset),
86            SeekFrom::Start(offset) => self.seek_to_offset(offset),
87            SeekFrom::End(end_offset) => {
88                let view_end = self.view.original_image_base() + self.view.len();
89                let offset = view_end
90                    .checked_add_signed(end_offset)
91                    .ok_or(std::io::Error::new(
92                        ErrorKind::Other,
93                        "Seeking from end overflowed",
94                    ))?;
95                self.seek_to_offset(offset);
96            }
97        };
98
99        Ok(self.offset())
100    }
101}
102
103impl Write for BinaryWriter {
104    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
105        let len = buf.len();
106        let result = unsafe { BNWriteData(self.handle, buf.as_ptr() as *mut _, len) };
107        if !result {
108            Err(std::io::Error::new(
109                std::io::ErrorKind::Other,
110                "write out of bounds",
111            ))
112        } else {
113            Ok(len)
114        }
115    }
116
117    fn flush(&mut self) -> std::io::Result<()> {
118        Ok(())
119    }
120}
121
122impl Drop for BinaryWriter {
123    fn drop(&mut self) {
124        unsafe { BNFreeBinaryWriter(self.handle) }
125    }
126}
127
128unsafe impl Sync for BinaryWriter {}
129unsafe impl Send for BinaryWriter {}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
132pub struct BinaryWriterOptions {
133    endianness: Option<Endianness>,
134    address: Option<u64>,
135}
136
137impl BinaryWriterOptions {
138    pub fn new() -> Self {
139        Self::default()
140    }
141
142    pub fn with_endianness(mut self, endian: Endianness) -> Self {
143        self.endianness = Some(endian);
144        self
145    }
146
147    pub fn with_address(mut self, address: u64) -> Self {
148        self.address = Some(address);
149        self
150    }
151}