diff --git a/Userland/Applications/HexEditor/CMakeLists.txt b/Userland/Applications/HexEditor/CMakeLists.txt index 19414e8bdb..e51ef619ec 100644 --- a/Userland/Applications/HexEditor/CMakeLists.txt +++ b/Userland/Applications/HexEditor/CMakeLists.txt @@ -11,6 +11,7 @@ compile_gml(FindDialog.gml FindDialogGML.h find_dialog_gml) set(SOURCES HexEditor.cpp HexEditorWidget.cpp + HexDocument.cpp FindDialog.cpp GoToOffsetDialog.cpp main.cpp diff --git a/Userland/Applications/HexEditor/HexDocument.cpp b/Userland/Applications/HexEditor/HexDocument.cpp new file mode 100644 index 0000000000..0b6acc1c3e --- /dev/null +++ b/Userland/Applications/HexEditor/HexDocument.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2021, Arne Elster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "HexDocument.h" + +void HexDocument::set(size_t position, u8 value) +{ + m_changes.set(position, value); +} + +bool HexDocument::is_dirty() const +{ + return m_changes.size() > 0; +} + +HexDocumentMemory::HexDocumentMemory(ByteBuffer&& buffer) + : m_buffer(move(buffer)) +{ +} + +HexDocument::Cell HexDocumentMemory::get(size_t position) +{ + auto tracked_change = m_changes.get(position); + if (tracked_change.has_value()) { + return Cell { tracked_change.value(), true }; + } else { + return Cell { m_buffer[position], false }; + } +} + +size_t HexDocumentMemory::size() const +{ + return m_buffer.size(); +} + +HexDocument::Type HexDocumentMemory::type() const +{ + return Type::Memory; +} + +void HexDocumentMemory::clear_changes() +{ + m_changes.clear(); +} + +bool HexDocumentMemory::write_to_file(NonnullRefPtr file) +{ + if (!file->seek(0)) + return false; + if (!file->write(m_buffer.data(), m_buffer.size())) + return false; + for (auto& change : m_changes) { + file->seek(change.key, Core::SeekMode::SetPosition); + file->write(&change.value, 1); + } + return true; +} + +HexDocumentFile::HexDocumentFile(NonnullRefPtr file) + : m_file(file) +{ + set_file(file); +} + +void HexDocumentFile::write_to_file() +{ + for (auto& change : m_changes) { + m_file->seek(change.key, Core::SeekMode::SetPosition); + m_file->write(&change.value, 1); + } + clear_changes(); + // make sure the next get operation triggers a read + m_buffer_file_pos = m_file_size + 1; +} + +bool HexDocumentFile::write_to_file(NonnullRefPtr file) +{ + if (!file->truncate(size())) { + return false; + } + + if (!file->seek(0) || !m_file->seek(0)) { + return false; + } + + while (true) { + auto copy_buffer = m_file->read(64 * KiB); + if (copy_buffer.size() == 0) + break; + file->write(copy_buffer.data(), copy_buffer.size()); + } + + for (auto& change : m_changes) { + file->seek(change.key, Core::SeekMode::SetPosition); + file->write(&change.value, 1); + } + + return true; +} + +HexDocument::Cell HexDocumentFile::get(size_t position) +{ + auto tracked_change = m_changes.get(position); + if (tracked_change.has_value()) { + return Cell { tracked_change.value(), true }; + } + + if (position < m_buffer_file_pos || position >= m_buffer_file_pos + m_buffer.size()) { + m_file->seek(position, Core::SeekMode::SetPosition); + m_file->read(m_buffer.data(), m_buffer.size()); + m_buffer_file_pos = position; + } + return { m_buffer[position - m_buffer_file_pos], false }; +} + +size_t HexDocumentFile::size() const +{ + return m_file_size; +} + +HexDocument::Type HexDocumentFile::type() const +{ + return Type::File; +} + +void HexDocumentFile::clear_changes() +{ + m_changes.clear(); +} + +void HexDocumentFile::set_file(NonnullRefPtr file) +{ + m_file = file; + + off_t size = 0; + if (!file->seek(0, Core::SeekMode::FromEndPosition, &size)) { + m_file_size = 0; + } else { + m_file_size = size; + } + file->seek(0, Core::SeekMode::SetPosition); + + clear_changes(); + // make sure the next get operation triggers a read + m_buffer_file_pos = m_file_size + 1; +} + +NonnullRefPtr HexDocumentFile::file() const +{ + return m_file; +} diff --git a/Userland/Applications/HexEditor/HexDocument.h b/Userland/Applications/HexEditor/HexDocument.h new file mode 100644 index 0000000000..e5948f8e80 --- /dev/null +++ b/Userland/Applications/HexEditor/HexDocument.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021, Arne Elster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +class HexDocument { +public: + enum class Type { + Memory, + File + }; + + struct Cell { + u8 value; + bool modified; + }; + + virtual ~HexDocument() = default; + virtual Cell get(size_t position) = 0; + virtual void set(size_t position, u8 value); + virtual size_t size() const = 0; + virtual Type type() const = 0; + virtual bool is_dirty() const; + virtual void clear_changes() = 0; + +protected: + HashMap m_changes; +}; + +class HexDocumentMemory : public HexDocument { +public: + explicit HexDocumentMemory(ByteBuffer&& buffer); + virtual ~HexDocumentMemory() = default; + + Cell get(size_t position) override; + size_t size() const override; + Type type() const override; + void clear_changes() override; + bool write_to_file(NonnullRefPtr file); + +private: + ByteBuffer m_buffer; +}; + +class HexDocumentFile : public HexDocument { +public: + explicit HexDocumentFile(NonnullRefPtr file); + virtual ~HexDocumentFile() = default; + + HexDocumentFile(const HexDocumentFile&) = delete; + + void set_file(NonnullRefPtr file); + NonnullRefPtr file() const; + void write_to_file(); + bool write_to_file(NonnullRefPtr file); + Cell get(size_t position) override; + size_t size() const override; + Type type() const override; + void clear_changes() override; + +private: + NonnullRefPtr m_file; + size_t m_file_size; + + Array m_buffer; + size_t m_buffer_file_pos; +}; diff --git a/Userland/Applications/HexEditor/HexEditor.cpp b/Userland/Applications/HexEditor/HexEditor.cpp index 35dc37f6ec..eb47bc0614 100644 --- a/Userland/Applications/HexEditor/HexEditor.cpp +++ b/Userland/Applications/HexEditor/HexEditor.cpp @@ -28,6 +28,7 @@ HexEditor::HexEditor() : m_blink_timer(Core::Timer::construct()) + , m_document(make(ByteBuffer::create_zeroed(0).release_value())) { set_should_hide_unnecessary_scrollbars(true); set_focus_policy(GUI::FocusPolicy::StrongFocus); @@ -56,11 +57,27 @@ void HexEditor::set_readonly(bool readonly) m_readonly = readonly; } -void HexEditor::set_buffer(const ByteBuffer& buffer) +bool HexEditor::open_new_file(size_t size) { - m_buffer = buffer; - set_content_length(buffer.size()); - m_tracked_changes.clear(); + auto maybe_buffer = ByteBuffer::create_zeroed(size); + if (!maybe_buffer.has_value()) { + return false; + } + + m_document = make(maybe_buffer.release_value()); + set_content_length(m_document->size()); + m_position = 0; + m_cursor_at_low_nibble = false; + update(); + update_status(); + + return true; +} + +void HexEditor::open_file(NonnullRefPtr file) +{ + m_document = make(file); + set_content_length(m_document->size()); m_position = 0; m_cursor_at_low_nibble = false; update(); @@ -72,9 +89,8 @@ void HexEditor::fill_selection(u8 fill_byte) if (!has_selection()) return; - for (size_t i = m_selection_start; i < m_selection_end; i++) { - m_tracked_changes.set(i, m_buffer.data()[i]); - m_buffer.data()[i] = fill_byte; + for (size_t i = m_selection_start; i <= m_selection_end; i++) { + m_document->set(i, fill_byte); } update(); @@ -83,7 +99,7 @@ void HexEditor::fill_selection(u8 fill_byte) void HexEditor::set_position(size_t position) { - if (position > m_buffer.size()) + if (position > m_document->size()) return; m_position = position; @@ -93,42 +109,37 @@ void HexEditor::set_position(size_t position) update_status(); } -bool HexEditor::write_to_file(const String& path) +bool HexEditor::save_as(int fd) { - if (m_buffer.is_empty()) - return true; - - int fd = open(path.characters(), O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) { - perror("open"); + auto new_file = Core::File::construct(); + if (!new_file->open(fd, Core::OpenMode::ReadWrite, Core::File::ShouldCloseFileDescriptor::Yes)) { return false; } - return write_to_file(fd); + if (m_document->type() == HexDocument::Type::File) { + HexDocumentFile* fileDoc = static_cast(m_document.ptr()); + if (!fileDoc->write_to_file(new_file)) + return false; + fileDoc->set_file(new_file); + } else { + HexDocumentMemory* memDoc = static_cast(m_document.ptr()); + if (!memDoc->write_to_file(new_file)) + return false; + m_document = make(new_file); + } + + update(); + + return true; } -bool HexEditor::write_to_file(int fd) +bool HexEditor::save() { - ScopeGuard fd_guard = [fd] { close(fd); }; - - int rc = ftruncate(fd, m_buffer.size()); - if (rc < 0) { - perror("ftruncate"); + if (m_document->type() != HexDocument::Type::File) { return false; } - ssize_t nwritten = write(fd, m_buffer.data(), m_buffer.size()); - if (nwritten < 0) { - perror("write"); - close(fd); - return false; - } - - if (static_cast(nwritten) == m_buffer.size()) { - m_tracked_changes.clear(); - update(); - } - + static_cast(m_document.ptr())->write_to_file(); return true; } @@ -145,8 +156,8 @@ bool HexEditor::copy_selected_hex_to_clipboard() return false; StringBuilder output_string_builder; - for (size_t i = m_selection_start; i < m_selection_end; i++) - output_string_builder.appendff("{:02X} ", m_buffer.data()[i]); + for (size_t i = m_selection_start; i <= m_selection_end; i++) + output_string_builder.appendff("{:02X} ", m_document->get(i).value); GUI::Clipboard::the().set_plain_text(output_string_builder.to_string()); return true; @@ -158,8 +169,8 @@ bool HexEditor::copy_selected_text_to_clipboard() return false; StringBuilder output_string_builder; - for (size_t i = m_selection_start; i < m_selection_end; i++) - output_string_builder.append(isprint(m_buffer.data()[i]) ? m_buffer[i] : '.'); + for (size_t i = m_selection_start; i <= m_selection_end; i++) + output_string_builder.append(isprint(m_document->get(i).value) ? m_document->get(i).value : '.'); GUI::Clipboard::the().set_plain_text(output_string_builder.to_string()); return true; @@ -173,8 +184,8 @@ bool HexEditor::copy_selected_hex_to_clipboard_as_c_code() StringBuilder output_string_builder; output_string_builder.appendff("unsigned char raw_data[{}] = {{\n", (m_selection_end - m_selection_start) + 1); output_string_builder.append(" "); - for (size_t i = m_selection_start, j = 1; i < m_selection_end; i++, j++) { - output_string_builder.appendff("{:#02X}", m_buffer.data()[i]); + for (size_t i = m_selection_start, j = 1; i <= m_selection_end; i++, j++) { + output_string_builder.appendff("{:#02X}", m_document->get(i).value); if (i != m_selection_end) output_string_builder.append(", "); if ((j % 12) == 0) { @@ -234,7 +245,7 @@ void HexEditor::mousedown_event(GUI::MouseEvent& event) auto byte_y = (absolute_y - hex_start_y) / line_height(); auto offset = (byte_y * m_bytes_per_row) + byte_x; - if (offset >= m_buffer.size()) + if (offset >= m_document->size()) return; dbgln_if(HEX_DEBUG, "HexEditor::mousedown_event(hex): offset={}", offset); @@ -257,7 +268,7 @@ void HexEditor::mousedown_event(GUI::MouseEvent& event) auto byte_y = (absolute_y - text_start_y) / line_height(); auto offset = (byte_y * m_bytes_per_row) + byte_x; - if (offset >= m_buffer.size()) + if (offset >= m_document->size()) return; dbgln_if(HEX_DEBUG, "HexEditor::mousedown_event(text): offset={}", offset); @@ -306,7 +317,7 @@ void HexEditor::mousemove_event(GUI::MouseEvent& event) auto byte_y = (absolute_y - hex_start_y) / line_height(); auto offset = (byte_y * m_bytes_per_row) + byte_x; - if (offset > m_buffer.size()) + if (offset > m_document->size()) return; m_selection_end = offset; @@ -321,8 +332,7 @@ void HexEditor::mousemove_event(GUI::MouseEvent& event) auto byte_x = (absolute_x - text_start_x) / character_width(); auto byte_y = (absolute_y - text_start_y) / line_height(); auto offset = (byte_y * m_bytes_per_row) + byte_x; - - if (offset > m_buffer.size()) + if (offset > m_document->size()) return; m_selection_end = offset; @@ -383,7 +393,7 @@ void HexEditor::keydown_event(GUI::KeyEvent& event) } if (event.key() == KeyCode::Key_Down) { - if (m_position + bytes_per_row() < m_buffer.size()) { + if (m_position + bytes_per_row() < m_document->size()) { m_position += bytes_per_row(); m_selection_start = m_selection_end = m_position; m_cursor_at_low_nibble = false; @@ -409,7 +419,7 @@ void HexEditor::keydown_event(GUI::KeyEvent& event) } if (event.key() == KeyCode::Key_Right) { - if (m_position + 1 < m_buffer.size()) { + if (m_position + 1 < m_document->size()) { m_position++; m_selection_start = m_selection_end = m_position; m_cursor_at_low_nibble = false; @@ -446,12 +456,10 @@ void HexEditor::keydown_event(GUI::KeyEvent& event) void HexEditor::hex_mode_keydown_event(GUI::KeyEvent& event) { if ((event.key() >= KeyCode::Key_0 && event.key() <= KeyCode::Key_9) || (event.key() >= KeyCode::Key_A && event.key() <= KeyCode::Key_F)) { - if (m_buffer.is_empty()) - return; - if (m_position == m_buffer.size()) + if (m_document->size() == 0) return; - VERIFY(m_position <= m_buffer.size()); + VERIFY(m_position <= m_document->size()); // yes, this is terrible... but it works. auto value = (event.key() >= KeyCode::Key_0 && event.key() <= KeyCode::Key_9) @@ -459,12 +467,13 @@ void HexEditor::hex_mode_keydown_event(GUI::KeyEvent& event) : (event.key() - KeyCode::Key_A) + 0xA; if (!m_cursor_at_low_nibble) { - m_tracked_changes.set(m_position, m_buffer.data()[m_position]); - m_buffer.data()[m_position] = value << 4 | (m_buffer.data()[m_position] & 0xF); // shift new value left 4 bits, OR with existing last 4 bits + u8 existing_change = m_document->get(m_position).value; + existing_change = value << 4 | (existing_change & 0xF); // shift new value left 4 bits, OR with existing last 4 bits + m_document->set(m_position, existing_change); m_cursor_at_low_nibble = true; } else { - m_buffer.data()[m_position] = (m_buffer.data()[m_position] & 0xF0) | value; // save the first 4 bits, OR the new value in the last 4 - if (m_position + 1 < m_buffer.size()) + m_document->set(m_position, (m_document->get(m_position).value & 0xF0) | value); // save the first 4 bits, OR the new value in the last 4 + if (m_position + 1 < m_document->size()) m_position++; m_cursor_at_low_nibble = false; } @@ -478,16 +487,15 @@ void HexEditor::hex_mode_keydown_event(GUI::KeyEvent& event) void HexEditor::text_mode_keydown_event(GUI::KeyEvent& event) { - if (m_buffer.is_empty()) + if (m_document->size() == 0) return; - VERIFY(m_position < m_buffer.size()); + VERIFY(m_position < m_document->size()); if (event.code_point() == 0) // This is a control key return; - m_tracked_changes.set(m_position, m_buffer.data()[m_position]); - m_buffer.data()[m_position] = event.code_point(); - if (m_position + 1 < m_buffer.size()) + m_document->set(m_position, event.code_point()); + if (m_position + 1 < m_document->size()) m_position++; m_cursor_at_low_nibble = false; @@ -518,7 +526,7 @@ void HexEditor::paint_event(GUI::PaintEvent& event) painter.add_clip_rect(event.rect()); painter.fill_rect(event.rect(), palette().color(background_role())); - if (m_buffer.is_empty()) + if (m_document->size() == 0) return; painter.translate(frame_thickness(), frame_thickness()); @@ -564,10 +572,10 @@ void HexEditor::paint_event(GUI::PaintEvent& event) for (size_t i = min_row; i < max_row; i++) { for (size_t j = 0; j < bytes_per_row(); j++) { auto byte_position = (i * bytes_per_row()) + j; - if (byte_position >= m_buffer.size()) + if (byte_position >= m_document->size()) return; - const bool edited_flag = m_tracked_changes.contains(byte_position); + const bool edited_flag = m_document->get(byte_position).modified; const bool selection_inbetween_start_end = byte_position >= m_selection_start && byte_position < m_selection_end; const bool selection_inbetween_end_start = byte_position >= m_selection_end && byte_position < m_selection_start; @@ -592,7 +600,8 @@ void HexEditor::paint_event(GUI::PaintEvent& event) } painter.fill_rect(hex_display_rect, background_color); - auto line = String::formatted("{:02X}", m_buffer[byte_position]); + const u8 cell_value = m_document->get(byte_position).value; + auto line = String::formatted("{:02X}", cell_value); painter.draw_text(hex_display_rect, line, Gfx::TextAlignment::TopLeft, text_color); if (m_edit_mode == EditMode::Hex) { @@ -626,7 +635,7 @@ void HexEditor::paint_event(GUI::PaintEvent& event) } painter.fill_rect(text_display_rect, background_color); - painter.draw_text(text_display_rect, String::formatted("{:c}", isprint(m_buffer[byte_position]) ? m_buffer[byte_position] : '.'), Gfx::TextAlignment::TopLeft, text_color); + painter.draw_text(text_display_rect, String::formatted("{:c}", isprint(cell_value) ? cell_value : '.'), Gfx::TextAlignment::TopLeft, text_color); if (m_edit_mode == EditMode::Text) { if (byte_position == m_position && m_cursor_blink_active) { @@ -642,7 +651,7 @@ void HexEditor::paint_event(GUI::PaintEvent& event) void HexEditor::select_all() { - highlight(0, m_buffer.size() - 1); + highlight(0, m_document->size() - 1); set_position(0); } @@ -664,38 +673,50 @@ Optional HexEditor::find_and_highlight(ByteBuffer& needle, size_t start) Optional HexEditor::find(ByteBuffer& needle, size_t start) { - if (m_buffer.is_empty()) + if (m_document->size() == 0) return {}; - auto raw_offset = memmem(m_buffer.data() + start, m_buffer.size() - start, needle.data(), needle.size()); - if (raw_offset == NULL) - return {}; + for (size_t i = start; i < m_document->size(); i++) { + if (m_document->get(i).value == *needle.data()) { + bool found = true; + for (size_t j = 1; j < needle.size(); j++) { + if (m_document->get(i + j).value != needle.data()[j]) { + found = false; + break; + } + } + if (found) { + auto end_of_match = i + needle.size(); + return end_of_match; + } + } + } - int relative_offset = static_cast(raw_offset) - m_buffer.data(); - dbgln("find: start={} raw_offset={} relative_offset={}", start, raw_offset, relative_offset); - - auto end_of_match = relative_offset + needle.size(); - - return end_of_match; + return {}; } Vector HexEditor::find_all(ByteBuffer& needle, size_t start) { - if (m_buffer.is_empty()) + if (m_document->size() == 0 || needle.size() == 0) return {}; Vector matches; size_t i = start; - while (i < m_buffer.size()) { - auto raw_offset = memmem(m_buffer.data() + i, m_buffer.size() - i, needle.data(), needle.size()); - if (raw_offset == NULL) - break; - - int relative_offset = static_cast(raw_offset) - m_buffer.data(); - dbgln("find_all: needle={} start={} raw_offset={} relative_offset={}", needle.data(), i, raw_offset, relative_offset); - matches.append({ relative_offset, String::formatted("{}", StringView { needle }.to_string().characters()) }); - i = relative_offset + needle.size(); + for (; i < m_document->size(); i++) { + if (m_document->get(i).value == *needle.data()) { + bool found = true; + for (size_t j = 1; j < needle.size(); j++) { + if (m_document->get(i + j).value != needle.data()[j]) { + found = false; + break; + } + } + if (found) { + matches.append({ i, String::formatted("{}", StringView { needle }.to_string().characters()) }); + i += needle.size() - 1; + } + } } if (matches.is_empty()) @@ -709,18 +730,21 @@ Vector HexEditor::find_all(ByteBuffer& needle, size_t start) Vector HexEditor::find_all_strings(size_t min_length) { - if (m_buffer.is_empty()) + if (m_document->size() == 0) return {}; Vector matches; - int offset = -1; + bool found_string = false; + size_t offset = 0; StringBuilder builder; - for (size_t i = 0; i < m_buffer.size(); i++) { - char c = m_buffer.bytes().at(i); + for (size_t i = 0; i < m_document->size(); i++) { + char c = m_document->get(i).value; if (isprint(c)) { - if (offset == -1) + if (!found_string) { offset = i; + found_string = true; + } builder.append(c); } else { if (builder.length() >= min_length) { @@ -728,7 +752,7 @@ Vector HexEditor::find_all_strings(size_t min_length) matches.append({ offset, builder.to_string() }); } builder.clear(); - offset = -1; + found_string = false; } } diff --git a/Userland/Applications/HexEditor/HexEditor.h b/Userland/Applications/HexEditor/HexEditor.h index 5aeab329c8..4b17233274 100644 --- a/Userland/Applications/HexEditor/HexEditor.h +++ b/Userland/Applications/HexEditor/HexEditor.h @@ -7,6 +7,7 @@ #pragma once +#include "HexDocument.h" #include "SearchResultsModel.h" #include #include @@ -32,14 +33,15 @@ public: bool is_readonly() const { return m_readonly; } void set_readonly(bool); - size_t buffer_size() const { return m_buffer.size(); } - void set_buffer(const ByteBuffer&); + size_t buffer_size() const { return m_document->size(); } + bool open_new_file(size_t size); + void open_file(NonnullRefPtr file); void fill_selection(u8 fill_byte); - bool write_to_file(const String& path); - bool write_to_file(int fd); + bool save_as(int fd); + bool save(); void select_all(); - bool has_selection() const { return !((m_selection_end < m_selection_start) || m_buffer.is_empty()); } + bool has_selection() const { return !((m_selection_end < m_selection_start) || m_document->size()); } size_t selection_size(); size_t selection_start_offset() const { return m_selection_start; } bool copy_selected_text_to_clipboard(); @@ -72,16 +74,15 @@ private: size_t m_line_spacing { 4 }; size_t m_content_length { 0 }; size_t m_bytes_per_row { 16 }; - ByteBuffer m_buffer; bool m_in_drag_select { false }; size_t m_selection_start { 0 }; size_t m_selection_end { 0 }; - HashMap m_tracked_changes; size_t m_position { 0 }; bool m_cursor_at_low_nibble { false }; EditMode m_edit_mode { Hex }; NonnullRefPtr m_blink_timer; bool m_cursor_blink_active { false }; + NonnullOwnPtr m_document; static constexpr int m_address_bar_width = 90; static constexpr int m_padding = 5; diff --git a/Userland/Applications/HexEditor/HexEditorWidget.cpp b/Userland/Applications/HexEditor/HexEditorWidget.cpp index 5a37d385b3..b40cc47700 100644 --- a/Userland/Applications/HexEditor/HexEditorWidget.cpp +++ b/Userland/Applications/HexEditor/HexEditorWidget.cpp @@ -75,12 +75,10 @@ HexEditorWidget::HexEditorWidget() auto file_size = value.to_int(); if (file_size.has_value() && file_size.value() > 0) { m_document_dirty = false; - auto buffer_result = ByteBuffer::create_zeroed(file_size.value()); - if (!buffer_result.has_value()) { + if (!m_editor->open_new_file(file_size.value())) { GUI::MessageBox::show(window(), "Entered file size is too large.", "Error", GUI::MessageBox::Type::Error); return; } - m_editor->set_buffer(buffer_result.release_value()); set_path({}); update_title(); } else { @@ -90,7 +88,7 @@ HexEditorWidget::HexEditorWidget() }); m_open_action = GUI::CommonActions::make_open_action([this](auto&) { - auto response = FileSystemAccessClient::Client::the().open_file(window()->window_id()); + auto response = FileSystemAccessClient::Client::the().open_file(window()->window_id(), {}, Core::StandardPaths::home_directory(), Core::OpenMode::ReadWrite); if (response.error != 0) { if (response.error != -1) @@ -113,25 +111,18 @@ HexEditorWidget::HexEditorWidget() if (m_path.is_empty()) return m_save_as_action->activate(); - auto response = FileSystemAccessClient::Client::the().request_file(window()->window_id(), m_path, Core::OpenMode::Truncate | Core::OpenMode::WriteOnly); - - if (response.error != 0) { - if (response.error != -1) - GUI::MessageBox::show_error(window(), String::formatted("Unable to save file: {}", strerror(response.error))); - return; - } - - if (!m_editor->write_to_file(*response.fd)) { + if (!m_editor->save()) { GUI::MessageBox::show(window(), "Unable to save file.\n", "Error", GUI::MessageBox::Type::Error); } else { m_document_dirty = false; + m_editor->update(); update_title(); } return; }); m_save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { - auto response = FileSystemAccessClient::Client::the().save_file(window()->window_id(), m_name, m_extension); + auto response = FileSystemAccessClient::Client::the().save_file(window()->window_id(), m_name, m_extension, Core::OpenMode::ReadWrite | Core::OpenMode::Truncate); if (response.error != 0) { if (response.error != -1) @@ -139,7 +130,7 @@ HexEditorWidget::HexEditorWidget() return; } - if (!m_editor->write_to_file(*response.fd)) { + if (!m_editor->save_as(*response.fd)) { GUI::MessageBox::show(window(), "Unable to save file.\n", "Error", GUI::MessageBox::Type::Error); return; } @@ -357,7 +348,7 @@ void HexEditorWidget::open_file(int fd, String const& path) VERIFY(path.starts_with("/"sv)); auto file = Core::File::construct(); - if (!file->open(fd, Core::OpenMode::ReadOnly, Core::File::ShouldCloseFileDescriptor::Yes) && file->error() != ENOENT) { + if (!file->open(fd, Core::OpenMode::ReadWrite, Core::File::ShouldCloseFileDescriptor::Yes) && file->error() != ENOENT) { GUI::MessageBox::show(window(), String::formatted("Opening \"{}\" failed: {}", path, strerror(errno)), "Error", GUI::MessageBox::Type::Error); return; } @@ -373,7 +364,7 @@ void HexEditorWidget::open_file(int fd, String const& path) } m_document_dirty = false; - m_editor->set_buffer(file->read_all()); // FIXME: On really huge files, this is never going to work. Should really create a framework to fetch data from the file on-demand. + m_editor->open_file(file); set_path(path); } diff --git a/Userland/Applications/HexEditor/SearchResultsModel.h b/Userland/Applications/HexEditor/SearchResultsModel.h index edf9ec695a..dff620df99 100644 --- a/Userland/Applications/HexEditor/SearchResultsModel.h +++ b/Userland/Applications/HexEditor/SearchResultsModel.h @@ -14,7 +14,7 @@ #include struct Match { - int offset; + u64 offset; String value; };