From 6c78b0f24c5eabb0a3eaf451f74f657958bba0cf Mon Sep 17 00:00:00 2001 From: Christian Gleissner Date: Tue, 4 Jul 2023 14:34:01 +0100 Subject: [PATCH 1/3] Added hex viewer --- .../userinterface/user_file_interaction.cc | 28 +++++++++++-- .../userinterface/user_file_interaction.h | 1 + software/userinterface/userinterface.cc | 42 +++++++++++++++++++ software/userinterface/userinterface.h | 4 ++ 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/software/userinterface/user_file_interaction.cc b/software/userinterface/user_file_interaction.cc index bdb22b2a8..bdf20e229 100644 --- a/software/userinterface/user_file_interaction.cc +++ b/software/userinterface/user_file_interaction.cc @@ -14,6 +14,9 @@ #include "home_directory.h" #include "subsys.h" +#define MAX_FILE_SIZE_TO_VIEW 262144 +#define MAX_FILE_SIZE_TO_HEX_VIEW 26 * 1024 // TODO Investigate how this limit can be increased + // member int UserFileInteraction::fetch_context_items(BrowsableDirEntry *br, IndexedList &list) { @@ -30,8 +33,13 @@ int UserFileInteraction::fetch_context_items(BrowsableDirEntry *br, IndexedList< list.append(new Action("Enter", UserFileInteraction::S_enter, 0)); count++; } - if ((info->size <= 262144) && (!(info->attrib & AM_DIR))) { + if ((info->size <= MAX_FILE_SIZE_TO_VIEW) && (!(info->attrib & AM_DIR))) { list.append(new Action("View", UserFileInteraction::S_view, 0)); + if (info->size <= MAX_FILE_SIZE_TO_HEX_VIEW) { + printf("Hex view: file size %d <= %d\n", info->size, MAX_FILE_SIZE_TO_HEX_VIEW); + list.append(new Action("Hex View", UserFileInteraction::S_hex_view, 0)); + count++; + } count++; } if (info->is_writable()) { @@ -131,7 +139,7 @@ int UserFileInteraction::S_delete(SubsysCommand *cmd) return 0; } -int UserFileInteraction::S_view(SubsysCommand *cmd) +int _view(SubsysCommand *cmd, bool hex) { FileManager *fm = FileManager::getFileManager(); File *f = 0; @@ -143,12 +151,26 @@ int UserFileInteraction::S_view(SubsysCommand *cmd) FRESULT fres = f->read(text_buf, size, &transferred); printf("Res = %d. Read text buffer: %d bytes\n", fres, transferred); text_buf[transferred] = 0; - cmd->user_interface->run_editor(text_buf, transferred); + if (hex) { + cmd->user_interface->run_hex_editor(text_buf, transferred); + } else { + cmd->user_interface->run_editor(text_buf, transferred); + } delete text_buf; } return 0; } +int UserFileInteraction::S_view(SubsysCommand *cmd) +{ + return _view(cmd, false); +} + +int UserFileInteraction::S_hex_view(SubsysCommand *cmd) +{ + return _view(cmd, true); +} + int UserFileInteraction::S_createDir(SubsysCommand *cmd) { char buffer[64]; diff --git a/software/userinterface/user_file_interaction.h b/software/userinterface/user_file_interaction.h index bf7e6bf81..2ef9dc304 100644 --- a/software/userinterface/user_file_interaction.h +++ b/software/userinterface/user_file_interaction.h @@ -26,6 +26,7 @@ class UserFileInteraction : public SubSystem, ObjectWithMenu { static int S_rename(SubsysCommand *cmd); static int S_delete(SubsysCommand *cmd); static int S_view(SubsysCommand *cmd); + static int S_hex_view(SubsysCommand *cmd); static int S_createDir(SubsysCommand *cmd); static int S_runApp(SubsysCommand *cmd); diff --git a/software/userinterface/userinterface.cc b/software/userinterface/userinterface.cc index faa02717b..cd5698109 100644 --- a/software/userinterface/userinterface.cc +++ b/software/userinterface/userinterface.cc @@ -394,6 +394,48 @@ void UserInterface :: run_editor(const char *text_buf, int max_len) edit->deinit(); } +void add_hex_byte(char *buf, int offset, uint8_t byte) +{ + char hex_chars[] = "0123456789ABCDEF"; + buf[offset] = hex_chars[(byte >> 4) & 0x0F]; + buf[offset + 1] = hex_chars[byte & 0x0F]; +} + +void add_hex_word(char *buf, int offset, uint16_t word) +{ + add_hex_byte(buf, offset, (word >> 8) & 0xFF); + add_hex_byte(buf, offset + 2, word & 0xFF); +} + +void UserInterface :: run_hex_editor(const char *text_buf, int max_len) +{ + #define HEX_COL_START 5 + #define TXT_COL_START (HEX_COL_START + (3 * BYTES_PER_HEX_ROW)) + int hex_len = CHARS_PER_HEX_ROW * (max_len / BYTES_PER_HEX_ROW + 1); + char hex_buf[hex_len + 1]; + for (int i = 0; i < hex_len; i++) { + hex_buf[i] = ' '; + } + int row_offset = 0; + for (int i = 0; i < max_len; i++) { + int col = i % BYTES_PER_HEX_ROW; + // offset and line break + if (col == 0) { + if (i > 0) { + hex_buf[row_offset + CHARS_PER_HEX_ROW - 1] = '\n'; + row_offset += CHARS_PER_HEX_ROW; + } + add_hex_word(hex_buf, row_offset, i); + } + // data + unsigned char c = text_buf[i]; + add_hex_byte(hex_buf, row_offset + HEX_COL_START + (3 * col), c); + // represent all non-printable characters as '.' based on the character set used by firmware version 3.10j + hex_buf[row_offset + TXT_COL_START + col] = (char) ((c == 0 || c == 8 || c == 10 || c == 13 || (c >=20 && c <= 31) || (c >= 144 && c <= 159)) ? '.' : c); + } + run_editor(hex_buf, hex_len); +} + int UserInterface :: enterSelection() { #ifndef NO_FILE_ACCESS diff --git a/software/userinterface/userinterface.h b/software/userinterface/userinterface.h index e7c14bc4d..c148edd48 100644 --- a/software/userinterface/userinterface.h +++ b/software/userinterface/userinterface.h @@ -28,6 +28,9 @@ #define CFG_USERIF_ULTICOPY_NAME 0x0B #define CFG_USERIF_FILENAME_OVERFLOW_SQUEEZE 0x0C +#define BYTES_PER_HEX_ROW 8 +#define CHARS_PER_HEX_ROW 38 + class UserInterface : public ConfigurableObject, public HostClient { private: @@ -83,6 +86,7 @@ class UserInterface : public ConfigurableObject, public HostClient int getPreferredType(void); void run_editor(const char *, int); + void run_hex_editor(const char *, int); void swapDisk(void); UIObject *get_root_object(void) { return ui_objects[0]; } From 1e1e7782700153549d93145a7733f2b0a9533df9 Mon Sep 17 00:00:00 2001 From: Christian Gleissner Date: Tue, 4 Jul 2023 17:43:13 +0100 Subject: [PATCH 2/3] Reduced memory use of hex viewer and using Editor inheritance hierarchy --- software/userinterface/editor.cc | 14 +++-- software/userinterface/editor.h | 14 ++++- software/userinterface/hex_editor.cc | 57 +++++++++++++++++++ .../userinterface/user_file_interaction.cc | 29 +++++----- .../userinterface/user_file_interaction.h | 2 + software/userinterface/userinterface.cc | 47 +++------------ software/userinterface/userinterface.h | 6 +- target/u64/nios2/ultimate/Makefile | 1 + 8 files changed, 108 insertions(+), 62 deletions(-) create mode 100644 software/userinterface/hex_editor.cc diff --git a/software/userinterface/editor.cc b/software/userinterface/editor.cc index 5e0350d22..12342ca35 100644 --- a/software/userinterface/editor.cc +++ b/software/userinterface/editor.cc @@ -124,20 +124,26 @@ void Editor :: init(Screen *scr, Keyboard *key) void Editor :: draw(void) { - struct Line line; + struct Line line, *line_ptr; int width = window->get_size_x(); for(int i=0;imove_cursor(0, i); - line = (*text)[i + first_line]; + int line_idx = i + first_line; + line = (*text)[line_idx]; if (line.buffer) { - window->output_length(line.buffer, line.length); - window->repeat(' ', width - line.length); + draw(line_idx, &line); } else { window->repeat(' ', width); } } } +void Editor :: draw(int line_idx, Line *line) +{ + window->output_length(line->buffer, line->length); + window->repeat(' ', window->get_size_x() - line->length); +} + void Editor :: deinit() { if(window) diff --git a/software/userinterface/editor.h b/software/userinterface/editor.h index 1a43ef3ce..3817867f1 100644 --- a/software/userinterface/editor.h +++ b/software/userinterface/editor.h @@ -13,8 +13,6 @@ class UserInterface; class Editor : public UIObject { UserInterface *user_interface; - void line_breakdown(const char *text_buffer, int buffer_size); - void draw(); public: int line_length; int height; @@ -34,8 +32,20 @@ class Editor : public UIObject virtual void init(Screen *win, Keyboard *k); virtual void deinit(void); + virtual void line_breakdown(const char *text_buffer, int buffer_size); + virtual void draw(); + virtual void draw(int line_idx, Line *line); virtual int poll(int); virtual int handle_key(uint8_t); }; +class HexEditor : public Editor +{ + UserInterface *user_interface; + void line_breakdown(const char *text_buffer, int buffer_size); + void draw(int line_idx, Line *line); +public: + HexEditor(UserInterface *ui, const char *text_buffer, int max_len); +}; + #endif diff --git a/software/userinterface/hex_editor.cc b/software/userinterface/hex_editor.cc new file mode 100644 index 000000000..df1bbce11 --- /dev/null +++ b/software/userinterface/hex_editor.cc @@ -0,0 +1,57 @@ +#include "editor.h" +#include +#include + +static const char hex_chars[] = "0123456789ABCDEF"; + +HexEditor :: HexEditor(UserInterface *ui, const char *text_buffer, int max_len) : Editor(ui, text_buffer, max_len) +{ +} + +void HexEditor :: line_breakdown(const char *text_buffer, int buffer_size) +{ + Line current; + int pos = 0; + linecount = 0; + + text->clear_list(); + while (pos < buffer_size) { + current.buffer = text_buffer + pos; + current.length = (buffer_size - pos > BYTES_PER_HEX_ROW) ? BYTES_PER_HEX_ROW : buffer_size - pos;; + text->append(current); + pos += current.length; + linecount++; + } +} + +inline void add_hex_byte(char *buf, int offset, uint8_t byte) +{ + buf[offset] = hex_chars[(byte >> 4) & 0x0F]; + buf[offset + 1] = hex_chars[byte & 0x0F]; +} + +inline void add_hex_word(char *buf, int offset, uint16_t word) +{ + add_hex_byte(buf, offset, (word >> 8) & 0xFF); + add_hex_byte(buf, offset + 2, word & 0xFF); +} + +void HexEditor :: draw(int line_idx, Line *line) +{ + #define HEX_COL_START 5 + #define TXT_COL_START (HEX_COL_START + (3 * BYTES_PER_HEX_ROW)) + + char hex_buf[CHARS_PER_HEX_ROW]; + memset(hex_buf, ' ', CHARS_PER_HEX_ROW); + add_hex_word(hex_buf, 0, line_idx * BYTES_PER_HEX_ROW); + + for (int i = 0; i < line->length; i++) { + uint8_t c = (uint8_t) line->buffer[i]; + add_hex_byte(hex_buf, HEX_COL_START + (3 * i), c); + // represent all non-printable characters as '.' based on the character set used by firmware version 3.10j + hex_buf[TXT_COL_START + i] = (char) ((c == 0 || c == 8 || c == 10 || c == 13 || (c >=20 && c <= 31) || (c >= 144 && c <= 159)) ? '.' : c); + } + + window->output_length(hex_buf, CHARS_PER_HEX_ROW); + window->repeat(' ', window->get_size_x() - CHARS_PER_HEX_ROW); +} diff --git a/software/userinterface/user_file_interaction.cc b/software/userinterface/user_file_interaction.cc index bdf20e229..9f25d9862 100644 --- a/software/userinterface/user_file_interaction.cc +++ b/software/userinterface/user_file_interaction.cc @@ -13,9 +13,9 @@ #include "home_directory.h" #include "subsys.h" +#include "editor.h" #define MAX_FILE_SIZE_TO_VIEW 262144 -#define MAX_FILE_SIZE_TO_HEX_VIEW 26 * 1024 // TODO Investigate how this limit can be increased // member int UserFileInteraction::fetch_context_items(BrowsableDirEntry *br, IndexedList &list) @@ -35,12 +35,8 @@ int UserFileInteraction::fetch_context_items(BrowsableDirEntry *br, IndexedList< } if ((info->size <= MAX_FILE_SIZE_TO_VIEW) && (!(info->attrib & AM_DIR))) { list.append(new Action("View", UserFileInteraction::S_view, 0)); - if (info->size <= MAX_FILE_SIZE_TO_HEX_VIEW) { - printf("Hex view: file size %d <= %d\n", info->size, MAX_FILE_SIZE_TO_HEX_VIEW); - list.append(new Action("Hex View", UserFileInteraction::S_hex_view, 0)); - count++; - } - count++; + list.append(new Action("Hex View", UserFileInteraction::S_hex_view, 0)); + count += 2; } if (info->is_writable()) { list.append(new Action("Rename", UserFileInteraction::S_rename, 0)); @@ -139,7 +135,7 @@ int UserFileInteraction::S_delete(SubsysCommand *cmd) return 0; } -int _view(SubsysCommand *cmd, bool hex) +int _view(SubsysCommand *cmd, EditorType editor_type) { FileManager *fm = FileManager::getFileManager(); File *f = 0; @@ -149,12 +145,15 @@ int _view(SubsysCommand *cmd, bool hex) uint32_t size = f->get_size(); char *text_buf = new char[size + 1]; FRESULT fres = f->read(text_buf, size, &transferred); - printf("Res = %d. Read text buffer: %d bytes\n", fres, transferred); + printf("Res = %d. Read text buffer: %d bytes. File size: %d bytes\n", fres, transferred, size); text_buf[transferred] = 0; - if (hex) { - cmd->user_interface->run_hex_editor(text_buf, transferred); - } else { - cmd->user_interface->run_editor(text_buf, transferred); + switch (editor_type) { + case HEX_EDITOR: + cmd->user_interface->run_hex_editor(text_buf, transferred); + break; + default: + cmd->user_interface->run_editor(text_buf, transferred); + break; } delete text_buf; } @@ -163,12 +162,12 @@ int _view(SubsysCommand *cmd, bool hex) int UserFileInteraction::S_view(SubsysCommand *cmd) { - return _view(cmd, false); + return _view(cmd, TEXT_EDITOR); } int UserFileInteraction::S_hex_view(SubsysCommand *cmd) { - return _view(cmd, true); + return _view(cmd, HEX_EDITOR); } int UserFileInteraction::S_createDir(SubsysCommand *cmd) diff --git a/software/userinterface/user_file_interaction.h b/software/userinterface/user_file_interaction.h index 2ef9dc304..3119deec7 100644 --- a/software/userinterface/user_file_interaction.h +++ b/software/userinterface/user_file_interaction.h @@ -13,6 +13,8 @@ #include "subsys.h" #include "filemanager.h" +enum EditorType { TEXT_EDITOR, HEX_EDITOR}; + class Path; class Action; class BrowsableDirEntry; diff --git a/software/userinterface/userinterface.cc b/software/userinterface/userinterface.cc index cd5698109..0ea64a36e 100644 --- a/software/userinterface/userinterface.cc +++ b/software/userinterface/userinterface.cc @@ -383,57 +383,24 @@ void UserInterface :: hide_progress(void) delete status_box; } -void UserInterface :: run_editor(const char *text_buf, int max_len) +void UserInterface :: run_editor(Editor *editor) { - Editor *edit = new Editor(this, text_buf, max_len); - edit->init(screen, keyboard); + editor->init(screen, keyboard); int ret; do { - ret = edit->poll(0); + ret = editor->poll(0); } while(!ret); - edit->deinit(); + editor->deinit(); } -void add_hex_byte(char *buf, int offset, uint8_t byte) -{ - char hex_chars[] = "0123456789ABCDEF"; - buf[offset] = hex_chars[(byte >> 4) & 0x0F]; - buf[offset + 1] = hex_chars[byte & 0x0F]; -} - -void add_hex_word(char *buf, int offset, uint16_t word) +void UserInterface :: run_editor(const char *text_buf, int max_len) { - add_hex_byte(buf, offset, (word >> 8) & 0xFF); - add_hex_byte(buf, offset + 2, word & 0xFF); + run_editor(new Editor(this, text_buf, max_len)); } void UserInterface :: run_hex_editor(const char *text_buf, int max_len) { - #define HEX_COL_START 5 - #define TXT_COL_START (HEX_COL_START + (3 * BYTES_PER_HEX_ROW)) - int hex_len = CHARS_PER_HEX_ROW * (max_len / BYTES_PER_HEX_ROW + 1); - char hex_buf[hex_len + 1]; - for (int i = 0; i < hex_len; i++) { - hex_buf[i] = ' '; - } - int row_offset = 0; - for (int i = 0; i < max_len; i++) { - int col = i % BYTES_PER_HEX_ROW; - // offset and line break - if (col == 0) { - if (i > 0) { - hex_buf[row_offset + CHARS_PER_HEX_ROW - 1] = '\n'; - row_offset += CHARS_PER_HEX_ROW; - } - add_hex_word(hex_buf, row_offset, i); - } - // data - unsigned char c = text_buf[i]; - add_hex_byte(hex_buf, row_offset + HEX_COL_START + (3 * col), c); - // represent all non-printable characters as '.' based on the character set used by firmware version 3.10j - hex_buf[row_offset + TXT_COL_START + col] = (char) ((c == 0 || c == 8 || c == 10 || c == 13 || (c >=20 && c <= 31) || (c >= 144 && c <= 159)) ? '.' : c); - } - run_editor(hex_buf, hex_len); + run_editor(new HexEditor(this, text_buf, max_len)); } int UserInterface :: enterSelection() diff --git a/software/userinterface/userinterface.h b/software/userinterface/userinterface.h index c148edd48..0eeb1b212 100644 --- a/software/userinterface/userinterface.h +++ b/software/userinterface/userinterface.h @@ -29,8 +29,10 @@ #define CFG_USERIF_FILENAME_OVERFLOW_SQUEEZE 0x0C #define BYTES_PER_HEX_ROW 8 -#define CHARS_PER_HEX_ROW 38 +#define CHARS_PER_HEX_ROW 37 +class Editor; +class HexEditor; class UserInterface : public ConfigurableObject, public HostClient { private: @@ -48,6 +50,8 @@ class UserInterface : public ConfigurableObject, public HostClient void set_screen_title(void); bool pollFocussed(void); bool buttonDownFor(uint32_t ms); + void run_editor(Editor *); + UIStatusBox *status_box; public: int color_border, color_bg, color_fg, color_sel, color_sel_bg, config_save, filename_overflow_squeeze; diff --git a/target/u64/nios2/ultimate/Makefile b/target/u64/nios2/ultimate/Makefile index a3769e025..ba6344ad9 100755 --- a/target/u64/nios2/ultimate/Makefile +++ b/target/u64/nios2/ultimate/Makefile @@ -89,6 +89,7 @@ SRCS_CC = u2p_init.cc \ ui_elements.cc \ user_file_interaction.cc \ editor.cc \ + hex_editor.cc \ tree_browser.cc \ tree_browser_state.cc \ config_menu.cc \ From 541af318890c3e6db6a939b9bfb10e48d794f99a Mon Sep 17 00:00:00 2001 From: Christian Gleissner Date: Tue, 4 Jul 2023 18:06:48 +0100 Subject: [PATCH 3/3] Support new editor keyboard shortcuts to better navigate across large files: home/F2 (start of file) and end/F8 (end of file) --- software/userinterface/editor.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/software/userinterface/editor.cc b/software/userinterface/editor.cc index 12342ca35..ed0ca625c 100644 --- a/software/userinterface/editor.cc +++ b/software/userinterface/editor.cc @@ -188,7 +188,7 @@ int Editor :: handle_key(uint8_t c) draw(); } break; - case KEY_F1: // F1 -> page up + case KEY_F1: // page up case KEY_PAGEUP: first_line -= height + 1; if (first_line < 0) { @@ -196,7 +196,12 @@ int Editor :: handle_key(uint8_t c) } draw(); break; - case KEY_F7: // F7 -> page down + case KEY_F2: // start + case KEY_HOME: + first_line = 0; + draw(); + break; + case KEY_F7: // page down case KEY_PAGEDOWN: first_line += height - 1; if (first_line >= linecount - height) { @@ -206,6 +211,11 @@ int Editor :: handle_key(uint8_t c) } draw(); break; + case KEY_F8: // end + case KEY_END: + first_line = linecount - height; + draw(); + break; case KEY_BACK: // backspace break; case KEY_SPACE: // space