From 05f8af310c2ae4b647e771802e65d3c5fafa5ec1 Mon Sep 17 00:00:00 2001 From: David Ali Date: Mon, 19 Jan 2026 16:30:00 +0100 Subject: [PATCH] first commit --- LICENSE | 21 ++++++ README.md | 49 ++++++++++++ chexed.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 chexed.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2e2989f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Dávid Ali + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..585efd4 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# chexed + +A simple terminal-based hex editor. It is written in C and uses the ncurses library. + +## Prerequisites + +You need a Linux system and the ncurses library. + +Install it on Debian/Ubuntu with this command: +```bash +sudo apt install libncurses-dev +``` + +## Compilation + +Use `gcc` to compile the source code. Run this command: + +```bash +gcc chexed.c -o chexed -lncurses +``` + +## Usage + +Run the program with a file path: + +```bash +./chexed your_file.bin +``` + +### Controls + +* **Arrow Keys**: navigate through the file. +* **Page Up / Page Down**: scroll quickly. +* **0-9, A-F**: input hex values. +* **+ / -**: increment or decrement the byte value by 1. +* **Ctrl+S**: save changes to the file. +* **Ctrl+Q**: quit the program. + +## Known Limitations + +The program loads the entire file into RAM. Do not open files larger than your available memory. + +The file size is fixed: you cannot insert or delete bytes. + +Editing works in a specific way: typing a new digit shifts the current value 4 bits to the left. + +## License + +MIT diff --git a/chexed.c b/chexed.c new file mode 100644 index 0000000..5a54bf8 --- /dev/null +++ b/chexed.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include + +#define ROW_SIZE 16 +#define CTRL_KEY(k) ((k) & 0x1f) + +unsigned char* buffer; +long file_size; +char* filename; +int cursor_pos = 0; +int scroll_offset = 0; + +void load_file(const char* fname) { + FILE* f = fopen(fname, "rb"); + if (!f) { + perror("Nie można otworzyć pliku"); + exit(1); + } + + fseek(f, 0, SEEK_END); + file_size = ftell(f); + rewind(f); + + buffer = malloc(file_size); + fread(buffer, 1, file_size, f); + fclose(f); +} + +void save_file() { + FILE* f = fopen(filename, "wb"); + if (f) { + fwrite(buffer, 1, file_size, f); + fclose(f); + } +} +void draw_interface() { + clear(); + int max_y, max_x; + getmaxyx(stdscr, max_y, max_x); + + attron(COLOR_PAIR(1)); + mvprintw(0, 0, "HEXED: %s (ROZMIAR: %ld bajtow)", filename, file_size); + mvprintw( + 1, 0, + "^S: Zapisz | ^Q: Wyjscie | Strzalki: Nawigacja | +/-: Zmien wartosc"); + mvhline(2, 0, ACS_HLINE, max_x); + attroff(COLOR_PAIR(1)); + + int rows_to_draw = max_y - 4; + + for (int i = 0; i < rows_to_draw; i++) { + int offset = scroll_offset + (i * ROW_SIZE); + if (offset >= file_size) break; + + attron(COLOR_PAIR(1)); + mvprintw(3 + i, 0, "%08X: ", offset); + attroff(COLOR_PAIR(1)); + + for (int j = 0; j < ROW_SIZE; j++) { + if (offset + j < file_size) { + unsigned char val = buffer[offset + j]; + int color_pair = (val == 0x00) ? 3 : 2; + + if (offset + j == cursor_pos) { + attron(COLOR_PAIR(4)); + printw("%02X ", val); + attroff(COLOR_PAIR(4)); + } else { + attron(COLOR_PAIR(color_pair)); + printw("%02X ", val); + attroff(COLOR_PAIR(color_pair)); + } + } else { + printw(" "); + } + } + + printw("| "); + + for (int j = 0; j < ROW_SIZE; j++) { + if (offset + j < file_size) { + unsigned char c = buffer[offset + j]; + char ch = (isprint(c)) ? c : '.'; + int color_pair = (c == 0x00) ? 3 : 2; + + if (offset + j == cursor_pos) { + attron(COLOR_PAIR(4)); + printw("%c", ch); + attroff(COLOR_PAIR(4)); + } else { + attron(COLOR_PAIR(color_pair)); + printw("%c", ch); + attroff(COLOR_PAIR(color_pair)); + } + } + } + } + refresh(); +} +void update_byte(int ch) { + int val = -1; + if (ch >= '0' && ch <= '9') val = ch - '0'; + if (ch >= 'a' && ch <= 'f') val = ch - 'a' + 10; + if (ch >= 'A' && ch <= 'F') val = ch - 'A' + 10; + + if (val != -1) { + buffer[cursor_pos] = (buffer[cursor_pos] << 4) | val; + } +} + +int main(int argc, char* argv[]) { + if (argc < 2) { + printf("Uzycie: %s \n", argv[0]); + return 1; + } + + filename = argv[1]; + load_file(filename); + + initscr(); + start_color(); + use_default_colors(); + + init_pair(1, COLOR_CYAN, -1); + init_pair(2, COLOR_WHITE, -1); + init_pair(3, COLOR_BLUE, -1); + init_pair(4, COLOR_BLACK, COLOR_WHITE); + + raw(); + noecho(); + keypad(stdscr, TRUE); + curs_set(0); + + int ch; + + int max_y, max_x; + draw_interface(); + int running = 1; + while (running) { + ch = getch(); + + getmaxyx(stdscr, max_y, max_x); + + switch (ch) { + case CTRL_KEY('q'): + running = 0; + break; + case KEY_UP: + if (cursor_pos >= ROW_SIZE) cursor_pos -= ROW_SIZE; + break; + case KEY_DOWN: + if (cursor_pos + ROW_SIZE < file_size) cursor_pos += ROW_SIZE; + break; + case KEY_LEFT: + if (cursor_pos > 0) cursor_pos--; + break; + case KEY_RIGHT: + if (cursor_pos < file_size - 1) cursor_pos++; + break; + case '+': + buffer[cursor_pos]++; + break; + case '-': + buffer[cursor_pos]--; + break; + case CTRL_KEY('s'): + save_file(); + mvprintw(max_y - 1, 0, "ZAPISANO!"); + break; + case KEY_NPAGE: { + int rows = max_y - 4; + int jump = rows * ROW_SIZE; + + if (cursor_pos + jump < file_size) { + cursor_pos += jump; + } else { + cursor_pos = file_size - 1; + } + } break; + + case KEY_PPAGE: { + int rows = max_y - 4; + int jump = rows * ROW_SIZE; + + if (cursor_pos - jump >= 0) { + cursor_pos -= jump; + } else { + cursor_pos = 0; + } + } break; + default: + update_byte(ch); + break; + } + + int rows_on_screen = max_y - 4; + int cursor_row_start = (cursor_pos / ROW_SIZE) * ROW_SIZE; + + if (cursor_pos < scroll_offset) { + scroll_offset = cursor_row_start; + } + + else if (cursor_pos >= scroll_offset + (rows_on_screen * ROW_SIZE)) { + scroll_offset = cursor_row_start - ((rows_on_screen - 1) * ROW_SIZE); + + if (scroll_offset < 0) scroll_offset = 0; + } + + draw_interface(); + } + + endwin(); + free(buffer); + return 0; +} \ No newline at end of file