diff --git a/CHANGELOG.md b/CHANGELOG.md index bf2ce67..483d357 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## dz6 v0.7.0 + +- Hex view: + - Replace mode: + - `t` now truncates the file (it was `T` before). + - `T` reverse truncates the file (deletes from offset 0 to current offset). + - Both commands above need confirmation from the user as they can't be undone. + ## dz6 v0.6.0 - Hex view: diff --git a/src/editor.rs b/src/editor.rs index 95bdfb7..a960e7e 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -23,9 +23,11 @@ pub enum UIState { DialogLog, DialogNames, DialogNamesRegex, + DialogReverseTruncate, DialogSearch, DialogStrings, DialogStringsRegex, + DialogTruncate, Error, HexEditing, HexSelection, diff --git a/src/events.rs b/src/events.rs index 778697f..57376c7 100644 --- a/src/events.rs +++ b/src/events.rs @@ -48,6 +48,10 @@ pub fn handle_events(app: &mut App) -> Result { UIState::DialogCalculator => { global::calculator::dialog_calculator_events(app, &event)? } + UIState::DialogTruncate => hex::truncate::dialog_truncate_events(app, &event)?, + UIState::DialogReverseTruncate => { + hex::truncate::dialog_reverse_truncate_events(app, &event)? + } }; } Event::Resize(width, _height) => { diff --git a/src/global/status_bar.rs b/src/global/status_bar.rs index cf06e4b..9897735 100644 --- a/src/global/status_bar.rs +++ b/src/global/status_bar.rs @@ -57,7 +57,13 @@ pub fn status_bar_draw(app: &mut App, frame: &mut Frame, area: Rect) { frame.render_widget(status_bar_info_left, area); let selected = if app.state == UIState::HexSelection { - format!("{:X}", app.hex_view.selection.end.saturating_sub(app.hex_view.selection.start)) + format!( + "{:X}", + app.hex_view + .selection + .end + .saturating_sub(app.hex_view.selection.start) + ) } else { "".to_string() }; diff --git a/src/hex/edit.rs b/src/hex/edit.rs index 5b65303..00dcbb6 100644 --- a/src/hex/edit.rs +++ b/src/hex/edit.rs @@ -111,12 +111,16 @@ pub fn edit_events(app: &mut App, key: KeyEvent) -> Result { fill_with(app, b.wrapping_sub(1), false); } } else if c == 'T' { - // truncate the file - if let Some(f) = &app.file_info.file { - f.set_len((app.hex_view.offset + 1) as u64)?; - app.reload_file(); - app.state = UIState::Normal; - app.hex_view.editing_hex = true; + // set a new start (reverse truncate) + if app.hex_view.offset > 0 { + app.dialog_renderer = Some(super::truncate::dialog_reverse_truncate); + app.state = UIState::DialogReverseTruncate; + } + } else if c == 't' { + // set a new end (truncate) + if app.hex_view.offset < app.file_info.size.saturating_sub(1) { + app.dialog_renderer = Some(super::truncate::dialog_truncate); + app.state = UIState::DialogTruncate; } } else if c == '~' { if let Some(b) = app.read_u8(app.hex_view.offset) { diff --git a/src/hex/mod.rs b/src/hex/mod.rs index 0e05096..5750988 100644 --- a/src/hex/mod.rs +++ b/src/hex/mod.rs @@ -8,3 +8,4 @@ pub mod names; pub mod search; pub mod selection; pub mod strings; +pub mod truncate; diff --git a/src/hex/truncate.rs b/src/hex/truncate.rs new file mode 100644 index 0000000..d2956b6 --- /dev/null +++ b/src/hex/truncate.rs @@ -0,0 +1,68 @@ +use crossterm::event::{Event, KeyCode}; +use ratatui::Frame; +use std::io::{Result, Write}; + +use crate::{ + app::App, + editor::UIState, + widgets::{Message, MessageType}, +}; + +pub fn dialog_truncate(app: &mut App, frame: &mut Frame) { + let mut dialog = Message::from(&format!( + "Permanently delete from offset {:X} to the end of file? (y/N)", + app.hex_view.offset.saturating_add(1) + )); + dialog.kind = MessageType::Error; + dialog.render(app, frame); +} + +pub fn dialog_truncate_events(app: &mut App, event: &Event) -> Result { + if let Event::Key(key) = event { + match key.code { + KeyCode::Char('y') => { + if let Some(f) = &app.file_info.file { + f.set_len((app.hex_view.offset + 1) as u64)?; + app.reload_file(); + } + } + _ => {} + } + app.dialog_renderer = None; + app.state = UIState::Normal; + app.hex_view.editing_hex = true; + } + Ok(false) +} + +pub fn dialog_reverse_truncate(app: &mut App, frame: &mut Frame) { + let mut dialog = Message::from(&format!( + "Permanently delete from offset 0 to {:X}? (y/N)", + app.hex_view.offset.saturating_sub(1) + )); + dialog.kind = MessageType::Error; + dialog.render(app, frame); +} + +pub fn dialog_reverse_truncate_events(app: &mut App, event: &Event) -> Result { + if let Event::Key(key) = event { + match key.code { + KeyCode::Char('y') => { + let buff = &mut app.file_info.get_buffer().to_vec(); + let new_buff = buff.drain(app.hex_view.offset..); + + if let Some(f) = &mut app.file_info.file { + f.write_all(new_buff.as_slice())?; + f.set_len(new_buff.len() as u64)?; + app.reload_file(); + app.goto(0); + } + } + _ => {} + } + app.dialog_renderer = None; + app.state = UIState::Normal; + app.hex_view.editing_hex = true; + } + Ok(false) +}