Compare commits

...

10 Commits

Author SHA1 Message Date
9b544ce55d . 2026-05-07 21:36:22 +09:00
aceec99522 . 2026-05-07 21:35:39 +09:00
fe5a09edfb Merge branch 'master' of 192.168.10.104:suwako/Hexagon 2026-05-07 21:33:09 +09:00
ffa88142e8 macOS対応 2026-05-07 21:30:59 +09:00
1fff6cf5be ごめん・・・ 2026-05-04 22:48:56 +09:00
a143b44f59 ちんこ 2026-05-03 20:50:56 +09:00
c68adc95fb . 2026-05-03 20:34:18 +09:00
a8eae150d4 Windowsでウィンドウサイズの修正 2026-05-03 20:15:47 +09:00
6415199b8c ファイルなしでも開ける様に+:oコマンドの追加 2026-05-03 20:08:03 +09:00
b510b2830f Windowsでマウスクリックが動いている 2026-05-03 19:44:22 +09:00
8 changed files with 220 additions and 43 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.svn .svn
hexagon hexagon
hexagon.exe hexagon.exe
hexagon-*

View File

@@ -1,6 +1,11 @@
## 1.3.0 (????年??月??日)
* macOS対応
## 1.2.0 (2026年05月03日) ## 1.2.0 (2026年05月03日)
* Debianでコンパイル出来る様に * Debianでコンパイル出来る様に
* マウス対応の追加 * マウス対応の追加
* ファイルなしでも開ける様に
* `:o`コマンドの追加
## 1.1.0 (2025年12月28日) ## 1.1.0 (2025年12月28日)
* PgUp・PgDownで使ってページを動ける様に * PgUp・PgDownで使ってページを動ける様に

View File

@@ -35,11 +35,17 @@ PREFIX = /usr
PREFIX = /boot/home/config/non-packaged PREFIX = /boot/home/config/non-packaged
.endif .endif
CFLAGS = -I/usr/include -I/usr/local/include -I/usr/pkg/include\ CFLAGS = -I/usr/include -I/usr/local/include -L/usr/lib -L/usr/local/lib
-I/usr/pkg/include/ncurses -I/boot/system/develop/headers\
-L/usr/lib -L/usr/local/lib -L/usr/pkg/lib -L/boot/system/develop/lib
.if ${UNAME_S} == "NetBSD" .if ${OS} == "netbsd"
CFLAGS += -I/usr/pkg/include -I/usr/pkg/include/ncurses -L/usr/pkg/lib
.elif ${OS} == "macos"
CFLAGS += -I/opt/homebrew/opt/ncurses/include -L/opt/homebrew/opt/ncurses/lib
.elif ${OS} == "haiku"
CFLAGS += -I/boot/system/develop/headers -L/boot/system/develop/lib
.endif
.if ${OS} == "netbsd"
LDFLAGS = -lncurses LDFLAGS = -lncurses
.else .else
LDFLAGS = -lncursesw LDFLAGS = -lncursesw
@@ -52,6 +58,10 @@ all:
${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -std=c++17 -static ${LDFLAGS} ${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -std=c++17 -static ${LDFLAGS}
strip ${NAME} strip ${NAME}
mac:
${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -std=c++17 ${LDFLAGS}
strip ${NAME}
debian: debian:
${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -std=c++17 -static ${LDFLAGS} -ltinfo ${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -std=c++17 -static ${LDFLAGS} -ltinfo
strip ${NAME} strip ${NAME}

View File

@@ -8,12 +8,24 @@ make
doas make install doas make install
``` ```
### Linux/macOS ### Linux
```sh ```sh
bmake bmake
sudo bmake install sudo bmake install
``` ```
#### Debianの場合
```sh
bmake debian
sudo bmake install
```
### macOS
```sh
bmake mac
sudo bmake install
```
### Haiku ### Haiku
```sh ```sh
bmake bmake

10
main.cc
View File

@@ -39,13 +39,13 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "src/hexeditor.hh" #include "src/hexeditor.hh"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
if (argc != 2) { std:: string filename;
std::cerr << "usage: hexagon <filename>\n";
return 1; if (argc > 1) filename = argv[1];
} else filename = "";
try { try {
HexEditor editor(argv[1]); HexEditor editor(filename);
editor.run(); editor.run();
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cerr << "エラー: " << e.what() << "\n"; std::cerr << "エラー: " << e.what() << "\n";

View File

@@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
g++ -O3 -o hexagon.exe -I/mingw64/include -L/mingw64/lib main.cc src/*.cc\ g++ -O3 -DNDEBUG -o hexagon.exe -I/mingw64/include -L/mingw64/lib main.cc src/*.cc\
-std=c++17 -static -lpdcurses -lgdi32 -lcomdlg32 -lwinmm -luser32 -lwinpthread\ -std=c++17 -static -lpdcurses -lgdi32 -lcomdlg32 -lwinmm -luser32 -lwinpthread\
-lstdc++ -mwindows -lstdc++ -mconsole -s

View File

@@ -80,7 +80,7 @@ const std::vector<HexEditor::FileSignature> HexEditor::signatures = {
HexEditor::HexEditor(const std::string &filename) HexEditor::HexEditor(const std::string &filename)
: curPos(0), dpOffset(0), bpr(16), : curPos(0), dpOffset(0), bpr(16),
statusMode(Status_Normal), modified(false), statusMode(Status_Normal), modified(false),
running(true), running(true), hasFile(!filename.empty()),
lastSearchDir(Direction_Forward), lastSearchDir(Direction_Forward),
headerLen(0), headerType("") { headerLen(0), headerType("") {
// SIGINT (CTRL + C)を無効に // SIGINT (CTRL + C)を無効に
@@ -94,6 +94,40 @@ HexEditor::HexEditor(const std::string &filename)
// ncurses // ncurses
initscr(); initscr();
#ifdef _WIN32
HWND console = GetConsoleWindow();
if (console) {
LONG style = GetWindowLong(console, GWL_STYLE);
style |= WS_MAXIMIZEBOX | WS_THICKFRAME;
SetWindowLong(console, GWL_STYLE, style);
const int MIN_WIDTH = 100;
const int MIN_HEIGHT = 30;
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(hOut, &csbi);
RECT rect;
GetWindowRect(console, &rect);
int cw = csbi.srWindow.Right - csbi.srWindow.Left + 1;
int ch = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
MINMAXINFO mmi = {};
mmi.ptMinTrackSize.x = MIN_WIDTH * 8;
mmi.ptMinTrackSize.y = MIN_HEIGHT * 16;
COORD minBuf = { MIN_WIDTH, MIN_HEIGHT };
SetConsoleScreenBufferSize(hOut, minBuf);
SMALL_RECT rectWin = { 0, 0, MIN_WIDTH - 1, MIN_HEIGHT - 1 };
SetConsoleWindowInfo(hOut, TRUE, &rectWin);
}
resize_term(40, 120);
#endif
getmaxyx(stdscr, rows, cols);
clearok(stdscr, TRUE); clearok(stdscr, TRUE);
cbreak(); cbreak();
noecho(); noecho();
@@ -102,7 +136,13 @@ HexEditor::HexEditor(const std::string &filename)
start_color(); start_color();
// マウス対応 // マウス対応
#ifdef _WIN32
mousemask(ALL_MOUSE_EVENTS, nullptr);
mouse_set(ALL_MOUSE_EVENTS);
request_mouse_pos();
#else
mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, nullptr); mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, nullptr);
#endif
isMouse = true; isMouse = true;
init_pair(1, COLOR_BLACK, COLOR_WHITE); // カーソル init_pair(1, COLOR_BLACK, COLOR_WHITE); // カーソル
@@ -117,39 +157,46 @@ HexEditor::HexEditor(const std::string &filename)
refresh(); refresh();
// ファイルをバッファーに読み込む // ファイルをバッファーに読み込む
std::ifstream file(filename, std::ios::binary); if (hasFile) {
if (!file) { std::ifstream file(filename, std::ios::binary);
endwin(); if (!file) {
throw std::runtime_error("ファイルを開くに失敗"); endwin();
} throw std::runtime_error("ファイルを開くに失敗");
}
buf.assign((std::istreambuf_iterator<char>(file)), {}); buf.assign((std::istreambuf_iterator<char>(file)), {});
file.close(); file.close();
if (buf.empty()) { if (buf.empty()) {
endwin(); endwin();
throw std::runtime_error("ファイルが空です"); throw std::runtime_error("ファイルが空です");
} }
fname = filename; fname = filename;
hasFile = true;
// ファイルヘッダー // ファイルヘッダー
for (const auto &sig : signatures) { for (const auto &sig : signatures) {
if (buf.size() >= sig.signature.size()) { if (buf.size() >= sig.signature.size()) {
bool match = true; bool match = true;
for (size_t i = 0; i < sig.signature.size(); ++i) { for (size_t i = 0; i < sig.signature.size(); ++i) {
if (buf[i] != sig.signature[i]) { if (buf[i] != sig.signature[i]) {
match = false; match = false;
break;
}
}
if (match) {
headerLen = sig.signature.size();
headerType = sig.type;
break; break;
} }
} }
if (match) {
headerLen = sig.signature.size();
headerType = sig.type;
break;
}
} }
} else {
fname = "[No File]";
buf.clear();
hasFile = false;
} }
// ウィンドウを作成する // ウィンドウを作成する
@@ -510,9 +557,13 @@ void HexEditor::handleCommand() {
} else if (statusText == "noh") { } else if (statusText == "noh") {
lastSearch = ""; lastSearch = "";
isCommand = true; isCommand = true;
} else if (statusText.rfind("o ", 0) == 0 || statusText.rfind("open ", 0) == 0) {
std::string path = statusText.substr(statusText.find(' ') + 1);
handleOpen(path);
isCommand = true;
} else { } else {
statusMode = Status_Error; statusMode = Status_Error;
statusText.clear(); statusText = "不正なコマンド。";
} }
if (isCommand) { if (isCommand) {
@@ -532,6 +583,67 @@ void HexEditor::handleQuit(bool force) {
} }
} }
void HexEditor::handleOpen(const std::string &path) {
if (path.empty()) {
statusMode = Status_Error;
statusText = "利用方法: :o <filename>";
render();
return;
}
std::ifstream file(path, std::ios::binary);
if (!file) {
statusMode = Status_Error;
statusText = "ファイルを開くに失敗: " + path;
render();
return;
}
std::vector<uint8_t> newbuf((std::istreambuf_iterator<char>(file)), {});
file.close();
if (newbuf.empty()) {
statusMode = Status_Error;
statusText = "ファイルは空です。";
render();
return;
}
buf = std::move(newbuf);
fname = path;
hasFile = true;
curPos = 0;
dpOffset = 0;
modified = false;
undoStack.clear();
redoStack.clear();
lastSearch.clear();
lastHexSearch.clear();
headerLen = 0;
headerType = "";
for (const auto &sig : signatures) {
if (buf.size() >= sig.signature.size()) {
bool match = true;
for (size_t i = 0; i < sig.signature.size(); ++i) {
if (buf[i] != sig.signature[i]) {
match = false;
break;
}
}
if (match) {
headerLen = sig.signature.size();
headerType = sig.type;
break;
}
}
}
statusMode = Status_Normal;
statusText = path + " を開きました。";
render();
}
void HexEditor::handleSave() { void HexEditor::handleSave() {
std::ofstream file(fname, std::ios::binary); std::ofstream file(fname, std::ios::binary);
if (file) { if (file) {
@@ -764,17 +876,24 @@ void HexEditor::handleReplace() {
void HexEditor::handleMouse() { void HexEditor::handleMouse() {
#ifdef _WIN32 #ifdef _WIN32
if (getmouse() == 0) return; request_mouse_pos();
int mx = Mouse_status.x; int mx = Mouse_status.x;
int my = Mouse_status.y; int my = Mouse_status.y;
if (!(Mouse_status.button[0] & BUTTON1_PRESSED) bool click = false;
|| (Mouse_status.changes & 0x01)) return; for (int i = 0; i < 3; ++i) {
if ((Mouse_status.button[i] & BUTTON1_PRESSED)
|| (Mouse_status.button[i] & BUTTON1_CLICKED)) {
click = true;
break;
}
}
if (!click) return;
#else #else
MEVENT event; MEVENT event;
if (getmouse(&event) != OK) return; if (getmouse(&event) != OK) return;
if (!(event.bstate & BUTTON1_PRESSED)) return; if (!(event.bstate & (BUTTON1_PRESSED | BUTTON1_CLICKED))) return;
int mx = event.x; int mx = event.x;
int my = event.y; int my = event.y;
@@ -849,6 +968,34 @@ void HexEditor::input() {
if (statusMode != Status_Normal && statusMode != Status_Error) continue; if (statusMode != Status_Normal && statusMode != Status_Error) continue;
ch = getch(); ch = getch();
if (ch == KEY_RESIZE) {
getmaxyx(stdscr, rows, cols);
delwin(hexPanel);
delwin(asciiPanel);
bpr = std::min<size_t>(16, (cols / 2 - 10) / 4);
if (bpr < 4) bpr = 4;
hexPanel = newwin(rows - 3, cols / 2, 1, 0);
asciiPanel = newwin(rows - 3, cols / 2, 1, cols / 2);
scrollok(hexPanel, TRUE);
scrollok(asciiPanel, TRUE);
wattrset(hexPanel, COLOR_PAIR(8));
wattrset(asciiPanel, COLOR_PAIR(8));
render();
continue;
}
if (!hasFile && ch != ':' && ch != 'Z') {
statusMode = Status_Error;
statusText = "ファイルを読み込んでいません。「:o <file>」を御利用下さい。";
render();
continue;
}
if ((ch == 'j' || ch == KEY_DOWN) && curPos + bpr < buf.size()) { if ((ch == 'j' || ch == KEY_DOWN) && curPos + bpr < buf.size()) {
curPos += bpr; // 下 curPos += bpr; // 下
if (statusMode == Status_Error) statusMode = Status_Normal; if (statusMode == Status_Error) statusMode = Status_Normal;

View File

@@ -79,6 +79,7 @@ class HexEditor {
bool running; bool running;
std::string lastSearch; std::string lastSearch;
bool isMouse = false; bool isMouse = false;
bool hasFile = false;
WINDOW *hexPanel; WINDOW *hexPanel;
WINDOW *asciiPanel; WINDOW *asciiPanel;
@@ -102,6 +103,7 @@ class HexEditor {
void topbar(); void topbar();
void statusbar(); void statusbar();
void handleCommand(); void handleCommand();
void handleOpen(const std::string &path);
void handleSave(); void handleSave();
void handleQuit(bool force); void handleQuit(bool force);
void handleSearch(); void handleSearch();