File:  [Isaki's NoNo m68k/m88k emulator] / nono / util / viewcgrom / viewcgrom.cpp
Revision 1.1.1.8 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:58 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// X680x0 CGROM ビューア
//
// Copyright (C) 2012-2019 Tetsuya Isaki
//

#include "wxheader.h"
PRAGMA_PUSH_WARNINGS
#include <wx/aboutdlg.h>
#include <wx/cmdline.h>
#include <wx/file.h>
#include <wx/image.h>
#include <wx/statline.h>
PRAGMA_POP_WARNINGS
#include <string>
#include <vector>

#define TITLE	wxT("X680x0 CGROMビューア")
#define COPYRIGHT	wxT("Copyright (C) 2012-2019 Tetsuya Isaki")

/* CGROM.DAT の大きさ(バイト単位) */
#define CGROM_SIZE	(768 * 1024)

/* キャンバス全体の大きさ (ピクセル単位) */
#define VIEW_WIDTH	(25 * 16)
#define VIEW_HEIGHT	(25 * 16)

/* 文字境界線の色 (これを3バイト並べる) */
#define BORDER_COLOR	(0xaa)

#define countof(x)	(sizeof(x) / sizeof(x[0]))

#define ROUND(x, y)	(((x) + (y - 1)) / (y))
#define ROUND8(x)	ROUND(x, 8)

class MyFrame;

struct ctxmenu_t {
	int id;
	int addr;
	wxString label;
	int size;
};

const wxSize view(VIEW_WIDTH, VIEW_HEIGHT);

class ViewCtrl : public wxWindow
{
	typedef wxWindow inherited;
 public:
	ViewCtrl(wxWindow *parent);

	unsigned char *imagebuf;
 private:
	void OnPaint(wxPaintEvent& event);
	wxDECLARE_EVENT_TABLE();
};

class MyFrame : public wxFrame
{
	typedef wxFrame inherited;
 public:
	MyFrame();

 private:
	void OnWindowCreate(wxWindowCreateEvent& event);
	void OnOpen(wxCommandEvent& event);
	void DoOpenDialog();
	void DoOpen();
	void OnExit(wxCommandEvent& event);
	void OnAbout(wxCommandEvent& event);
	void OnContextMenu(wxContextMenuEvent& event);
	void OnReloadUI(wxUpdateUIEvent& event);
	void OnReload(wxCommandEvent& event);
	bool DoReload();
	void OnAddr(wxCommandEvent& event);
	void OnMouseWheel(wxMouseEvent& event);
	void OnScroll(wxScrollEvent& event);
	void OnTextEnter(wxCommandEvent& event);
	void OnSizeBox(wxCommandEvent& event);
	void DoUpdateSize();
	void DoUpdateAddr();
	bool CreateImage();

	ViewCtrl *viewctrl;
	wxScrollBar *scrlbar;
	wxTextCtrl *addrctrl;
	wxChoice *sizectrl;
	wxMenu *menu;
	wxString filename;
	wxFile file;
	int start_addr;		/* 現在のサイズのフォントの開始アドレス */
	int start_offset;	/* 現在のサイズのフォントの開始オフセット */
	int start_line;		/* 現在のサイズのフォントの開始行 */
	int total_line;		/* 現在のサイズのフォントでの行数 */
	int line;			/* 表示開始行 */
	int addr;		/* 表示開始アドレス */
	int size;		/* フォントサイズ (番号) */
	wxSize pxsz;	/* フォントサイズ (ピクセル数) */
	wxSize bdsz;	/* 境界線を含めた大きさ (ピクセル数) */
	wxSize num;		/* 1画面の文字数 */
	wxSize oct;		/* 1文字のバイト数(縦,横) */
	int bytes;		/* 1文字のバイト数 */
	int w_bytes;	/* 1行分のバイト数(スクロールバー用) */

	static ctxmenu_t ctxmenu[];

	wxDECLARE_EVENT_TABLE();
};

class MyApp : public wxApp
{
 public:
	bool OnInit() override;
	wxString filename;

 private:
	MyFrame *frame;
	static const wxCmdLineEntryDesc desc[];
};

// IMPLEMENT_APP() は闇マクロすぎて clang のちからには耐えられない
PRAGMA_PUSH_WARNINGS
IMPLEMENT_APP(MyApp)
PRAGMA_POP_WARNINGS

// ---

enum {
	ID_SCROLL,
	ID_ADDRBOX,
	ID_SIZEBOX,
	ID_RELOAD,
	ID_FONT_BEGIN,
	ID_FONT_16x16 = ID_FONT_BEGIN,
	ID_FONT_8x8,
	ID_FONT_8x16,
	ID_FONT_12x12,
	ID_FONT_12x24,
	ID_FONT_24x24,
	ID_FONT_6x12,
	ID_FONT_END,
};

enum {
	SIZE_8x8 = 0,
	SIZE_8x16,
	SIZE_12x12,
	SIZE_12x24,
	SIZE_16x16,
	SIZE_24x24,
	SIZE_6x12,
	SIZE_MAXxMAX,
};

const wxCmdLineEntryDesc MyApp::desc[] =
{
	{ wxCMD_LINE_PARAM, NULL, NULL, "<path of CGROM.DAT>",
		wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL, },
	{ wxCMD_LINE_NONE, },
};

bool MyApp::OnInit()
{
	wxCmdLineParser cmd(argc, argv);
	cmd.SetDesc(desc);
	if (cmd.Parse() != 0) {
		return false;
	}
	if (cmd.GetParamCount() > 0) {
		filename = cmd.GetParam(0);
	}

	frame = new MyFrame();
	frame->Show();
	SetTopWindow(frame);
    return true;
}

// イベントテーブル
wxBEGIN_EVENT_TABLE(MyFrame, inherited)
	EVT_CONTEXT_MENU(MyFrame::OnContextMenu)
	EVT_MENU(ID_RELOAD, MyFrame::OnReload)
	EVT_UPDATE_UI(ID_RELOAD, MyFrame::OnReloadUI)
	EVT_COMMAND_SCROLL(ID_SCROLL, MyFrame::OnScroll)
	EVT_MOUSEWHEEL(MyFrame::OnMouseWheel)
	EVT_TEXT_ENTER(ID_ADDRBOX, MyFrame::OnTextEnter)
	EVT_CHOICE(ID_SIZEBOX, MyFrame::OnSizeBox)
	EVT_BUTTON(ID_FONT_16x16, MyFrame::OnAddr)
	EVT_BUTTON(ID_FONT_8x8,   MyFrame::OnAddr)
	EVT_BUTTON(ID_FONT_8x16,  MyFrame::OnAddr)
	EVT_BUTTON(ID_FONT_12x12, MyFrame::OnAddr)
	EVT_BUTTON(ID_FONT_12x24, MyFrame::OnAddr)
	EVT_BUTTON(ID_FONT_24x24, MyFrame::OnAddr)
	EVT_BUTTON(ID_FONT_6x12,  MyFrame::OnAddr)
	EVT_MENU(wxID_OPEN, MyFrame::OnOpen)
	EVT_MENU(wxID_EXIT, MyFrame::OnExit)
	EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
	EVT_WINDOW_CREATE(MyFrame::OnWindowCreate)
wxEND_EVENT_TABLE()

// ショートカット用データ
struct ctxmenu_t MyFrame::ctxmenu[] = {
	{ ID_FONT_16x16, 0x00000, wxT("16x16フォント"), SIZE_16x16, },
	{ ID_FONT_8x8,   0x3a000, wxT("8x8フォント"),   SIZE_8x8,   },
	{ ID_FONT_8x16,  0x3a800, wxT("8x16フォント"),  SIZE_8x16,  },
	{ ID_FONT_12x12, 0x3b800, wxT("12x12フォント"), SIZE_12x12, },
	{ ID_FONT_12x24, 0x3d000, wxT("12x24フォント"), SIZE_12x24, },
	{ ID_FONT_24x24, 0x40000, wxT("24x24フォント"), SIZE_24x24, },
	{ ID_FONT_6x12,  0xbf400, wxT("6x12フォント"),  SIZE_6x12, },
};

// コンストラクタ
MyFrame::MyFrame()
	: inherited(NULL, wxID_ANY, TITLE)
{
	wxMenuBar *menubar = new wxMenuBar();
	wxMenu *menuFile = new wxMenu();
	menuFile->Append(wxID_OPEN, wxT("開く(&O)"));
	menuFile->Append(wxID_EXIT, wxT("終了(&Q)"));
#if defined(__WXMAC__)
	/*
	 * Macでは wxID_ABOUT は自動的にリンゴメニューのほうに移され、
	 * 「ヘルプ」が子要素なしのメニューとして出来てしまう。
	 * それを防ぐため Mac では「ヘルプ」メニューごとなくす。
	 */
	menuFile->Append(wxID_ABOUT, wxT("バージョン(&A)"));
#endif
	menubar->Append(menuFile, wxT("ファイル(&F)"));
#if !defined(__WXMAC__)
	/* Mac以外なら「ヘルプ > バージョン」メニューを作成 */
	wxMenu *menuHelp = new wxMenu();
	menuHelp->Append(wxID_ABOUT, wxT("バージョン(&A)"));
	menubar->Append(menuHelp, wxT("ヘルプ(&H)"));
#endif
	SetMenuBar(menubar);

	wxPanel *panel = new wxPanel(this, wxID_ANY);

	wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);

	/* ビュー */
	viewctrl = new ViewCtrl(panel);
	hbox->Add(viewctrl);

	/* スクロールバー */
	scrlbar = new wxScrollBar(panel, ID_SCROLL,
		wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL);
	hbox->Add(scrlbar, 0, wxEXPAND);

	hbox->AddSpacer(5);

	/* コントロールパネル */
	wxBoxSizer *ctrlbox = new wxBoxSizer(wxVERTICAL);
	hbox->Add(ctrlbox);

	/* 開始アドレス */
	ctrlbox->Add(new wxStaticText(panel, wxID_ANY, wxT("開始アドレス")));

	/* 開始アドレス(コントロール) */
	addrctrl = new wxTextCtrl(panel, ID_ADDRBOX, wxT(""),
		wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
	ctrlbox->Add(addrctrl);

	ctrlbox->AddSpacer(5);

	/* フォントサイズ */
	ctrlbox->Add(new wxStaticText(panel, wxID_ANY, wxT("フォントサイズ")));

	/* フォントサイズ(コントロール) */
	wxString sizelist[] = {
		wxT("8x8"),
		wxT("8x16"),
		wxT("12x12"),
		wxT("12x24"),
		wxT("16x16"),
		wxT("24x24"),
		wxT("6x12"),
	};
	sizectrl = new wxChoice(panel, ID_SIZEBOX,
		wxDefaultPosition, wxDefaultSize,
		countof(sizelist), sizelist);
	ctrlbox->Add(sizectrl);

	ctrlbox->AddSpacer(10);
	ctrlbox->Add(new wxStaticLine(panel), 0, wxEXPAND);
	ctrlbox->AddSpacer(10);

	/* ショートカットボタン */
	for (int i = 0; i < countof(ctxmenu); i++) {
		ctxmenu_t *c = &ctxmenu[i];
		wxString label;

		label.Printf(wxT("%05X: "), c->addr);
		label += c->label;
		wxButton *btn = new wxButton(panel, c->id, label,
			wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
		ctrlbox->Add(btn, 0, wxEXPAND);
	}

	ctrlbox->AddSpacer(5);
	ctrlbox->Add(new wxStaticText(panel, wxID_ANY,
		wxT("(6x12 フォントは\nX68030 のみ)")));

	panel->SetSizer(hbox);
	hbox->SetSizeHints(this);

	/* コンテキストメニュー */
	menu = new wxMenu();
	menu->Append(ID_RELOAD, wxT("リロード"));

	/* 固定地 */
	num.x = 16;

	/* 初期値を入れて更新 */
	size = SIZE_16x16;
	DoUpdateSize();

	addr = 0;
	line = -1;
	DoUpdateAddr();

	Connect(wxEVT_CREATE,
		wxWindowCreateEventHandler(MyFrame::OnWindowCreate), NULL, this);
}

// ウィンドウ作成イベント
void
MyFrame::OnWindowCreate(wxWindowCreateEvent& event)
{
	if (!::wxGetApp().filename.IsEmpty()) {
		/* 引数でファイルが指定されていればそれを使う */
		filename = ::wxGetApp().filename;

		/* ファイルオープン */
		DoOpen();
	}

	// 一度処理したら抜いておく (こうしないと複数回呼ばれる)
	Disconnect(wxEVT_CREATE,
		wxWindowCreateEventHandler(MyFrame::OnWindowCreate));
}

// オープン
void
MyFrame::OnOpen(wxCommandEvent& event)
{
	DoOpenDialog();
	DoOpen();
}

// ファイルを開くダイアログ
void
MyFrame::DoOpenDialog()
{
	wxFileDialog dialog(this, wxT("Select a file"),
		::wxGetCwd(), wxEmptyString,
		wxT("DAT files (*.DAT)|*.DAT;*.dat|すべてのファイル (*.*)|*.*"));

	if (dialog.ShowModal() != wxID_OK) {
		return;
	}

	filename = dialog.GetPath();
}

// オープン処理
void
MyFrame::DoOpen()
{
	/* すでにオープンしてあればそれは閉じる */
	if (file.IsOpened()) {
		file.Close();
	}

	/* ファイル名がなければ (タイトルを戻して) 帰る */
	if (filename.IsEmpty()) {
		SetTitle(TITLE);
		return;
	}

	if (!file.Open(filename, wxFile::read)) {
		Close();
		return;
	}

	/* オープンできたのでタイトルを変更 */
	wxString title;
	title = TITLE;
	title += wxT(" - ");
	title += filename;
	SetTitle(title);

	CreateImage();
}

// 終了
void
MyFrame::OnExit(wxCommandEvent& event)
{
	Close();
}

// ヘルプ
void
MyFrame::OnAbout(wxCommandEvent& event)
{
	wxAboutDialogInfo ab;

	ab.SetName(wxT("viewcgrom"));
	ab.SetVersion(wxT("1.1"));
	ab.SetDescription(TITLE);
	ab.SetCopyright(COPYRIGHT);

	wxAboutBox(ab);
}

// コンテキストメニュー
void
MyFrame::OnContextMenu(wxContextMenuEvent& event)
{
	PopupMenu(menu);
}

// リロード UI
void
MyFrame::OnReloadUI(wxUpdateUIEvent& event)
{
	event.Enable(file.IsOpened());
}

// リロードイベント
void
MyFrame::OnReload(wxCommandEvent& event)
{
	DoReload();
}

// リロード処理
bool
MyFrame::DoReload()
{
	if (file.IsOpened()) {
		file.Close();
	}

	if (!file.Open(filename, wxFile::read)) {
		return false;
	}

	CreateImage();
	return true;
}

// フォント帯変更
void
MyFrame::OnAddr(wxCommandEvent& event)
{
	ctxmenu_t *c;
	for (int i = 0; i < countof(ctxmenu); i++) {
		c = &ctxmenu[i];
		if (event.GetId() == c->id) {
			/* 一致したら変更 */
			size = c->size;
			DoUpdateSize();

			addr = c->addr;
			line = -1;
			DoUpdateAddr();
			return;
		}
	}
}

// マウスホイール
void
MyFrame::OnMouseWheel(wxMouseEvent& event)
{
	if (event.GetWheelRotation() < 0) {
		line += 4;
		if (line > total_line - num.y) {
			line = total_line - num.y;
		}
	} else {
		line -= 4;
		if (line < 0) {
			line = 0;
		}
	}
	DoUpdateAddr();
}

// スクロール
void
MyFrame::OnScroll(wxScrollEvent& event)
{
	line = event.GetPosition();
	DoUpdateAddr();
}

// アドレス変更
void
MyFrame::OnTextEnter(wxCommandEvent& event)
{
	long val = 0;

	event.GetString().ToLong(&val, 16);
	line = -1;
	addr = val;
	DoUpdateAddr();
}

// サイズ変更
void
MyFrame::OnSizeBox(wxCommandEvent& event)
{
	size = event.GetSelection();
	DoUpdateSize();
	DoUpdateAddr();
}

// サイズ変更 (アドレス変更も呼び出す)
void
MyFrame::DoUpdateSize()
{
	/* 範囲チェック */
	if (size < 0 || size >= SIZE_MAXxMAX) {
		size = SIZE_16x16;
	}

	/* コントロールを更新 */
	sizectrl->SetSelection(size);

	/*
	 * サイズに付随する変数を更新
	 */
	/* ピクセルサイズ、開始アドレス、開始オフセット */
	start_offset = 0;
	switch (size) {
	 case SIZE_6x12:
		pxsz = wxSize(6, 12);
		break;
	 case SIZE_8x8:
		pxsz = wxSize(8, 8);
		start_addr = 0x3a000;
		break;
	 case SIZE_8x16:
		pxsz = wxSize(8, 16);
		start_addr = 0x3a800;
		break;
	 case SIZE_12x12:
		pxsz = wxSize(12, 12);
		start_addr = 0x3b800;
		start_offset = 128;
		break;
	 case SIZE_12x24:
		pxsz = wxSize(12, 24);
		start_addr = 0x3d000;
		start_offset = 512;
		break;
	 case SIZE_16x16:
		pxsz = wxSize(16, 16);
		start_addr = 0;
		break;
	 case SIZE_24x24:
		pxsz = wxSize(24, 24);
		start_addr = 0x40000;
		start_offset = 512;
		break;
	}

	/* バイト数 */
	oct.x = (pxsz.x + 7) / 8;
	oct.y = pxsz.y;
	bytes = oct.x * oct.y;
	w_bytes = bytes * num.x;

	/* 境界線入りサイズ */
	bdsz.x = (oct.x * 8) + 1;
	bdsz.y = oct.y + 1;

	/* 縦文字数(1画面の行数)は計算 */
	num.y = view.y / bdsz.y;

	/* 負側の行数(端数行も含めるため round up) */
	/* これが基準行 */
	start_line = ROUND(start_addr, w_bytes);
	/* 正側の行数(端数行も含めるため round up) */
	int pline = ROUND(CGROM_SIZE - start_addr, w_bytes);
	/* 足したものが全行数 */
	total_line = pline + start_line;

	/* line は一旦無効に */
	line = -1;

	/* スクロールバーに反映 */
	/* 現在位置は後で計算するのではみださないように 0 にしておく */
	scrlbar->SetScrollbar(0, num.y, total_line, num.y - 1);
}

// アドレス変更
void
MyFrame::DoUpdateAddr()
{
	if (line < 0) {
		/* line が無効ならアドレスを近いところに */
		line = (addr + start_offset) / w_bytes;
	}

	/* 行からアドレスを求める */
	addr = line * w_bytes - start_offset;

	/* アドレス欄に反映 */
	wxString str;
	str.Printf(wxT("%05X"), addr);
	addrctrl->SetValue(str);

	/* スクロールバーに反映 */
	int current = scrlbar->GetThumbPosition();
	if (addr / w_bytes != current) {
		scrlbar->SetThumbPosition(line);
	}

	/* イメージ作成 */
	CreateImage();
}

// イメージ作成
bool
MyFrame::CreateImage()
{
	unsigned char *imagebuf;
	std::vector<unsigned char> buf(bytes);
	size_t n;
	int minus_addr;

	imagebuf = viewctrl->imagebuf;

	/* 全面クリア */
	memset(imagebuf, 0xff, view.x * view.y * 3);

	/* ファイルが準備できていなければ何もしない */
	if (!file.IsOpened()) {
		return true;
	}

	if (addr >= 0) {
		file.Seek(addr);
		minus_addr = 0;
	} else {
		file.Seek(0);
		minus_addr = addr;
	}

	for (int cy = 0; cy < num.y; cy++) {
		for (int cx = 0; cx < num.x; cx++) {
			unsigned char *p =
				&imagebuf[((view.x * bdsz.y) * cy + bdsz.x * cx) * 3];
			/* ファイルオフセットが正なら1文字分読み込み */
			if (minus_addr >= 0) {
				n = file.Read(buf.data(), buf.size());
			} else {
				/* 負なら0バイト読んだことにしてカウントだけする */
				/* 文字の途中でプラスに転じるケースは面倒なので無視 */
				minus_addr += bytes;
				n = 0;
			}

			/* 展開 */
			for (int y = 0; y < oct.y; y++) {
				for (int x = 0; x < oct.x; x++) {
					int offset = y * oct.x + x;
					if (offset >= n) {
						/* ファイルからはみ出る部分は塗りつぶし? */
						memset(p, BORDER_COLOR, 8 * 3);
						p += 8 * 3;
						continue;
					}
					unsigned char ch = buf[offset];
					for (int b = 0; b < 8; b++) {
						if ((ch & 0x80)) {
							p[0] = 0;
							p[1] = 0;
							p[2] = 0;
						}
						ch <<= 1;
						p += 3;
					}
				}
				/* 右境界線 */
				p[0] = BORDER_COLOR;
				p[1] = BORDER_COLOR;
				p[2] = BORDER_COLOR;
				p += 3;

				p -= bdsz.x * 3;
				p += view.x * 3;
			}
			/* 下境界線 */
			memset(p, BORDER_COLOR, bdsz.x * 3);
		}
	}

	/* 画面を再描画させる */
	viewctrl->Refresh(false);

	return true;
}

// イベントテーブル
wxBEGIN_EVENT_TABLE(ViewCtrl, inherited)
	EVT_PAINT(ViewCtrl::OnPaint)
wxEND_EVENT_TABLE()

// コンストラクタ
ViewCtrl::ViewCtrl(wxWindow *parent)
	: inherited(parent, wxID_ANY,
		wxDefaultPosition, wxSize(VIEW_WIDTH, VIEW_HEIGHT))
{
	imagebuf = new unsigned char [ VIEW_WIDTH * VIEW_HEIGHT * 3 ];
}

// ペイントイベント
void
ViewCtrl::OnPaint(wxPaintEvent& event)
{
	wxPaintDC dc(this);

	wxImage image(VIEW_WIDTH, VIEW_HEIGHT, imagebuf, true);
	wxBitmap bitmap(image);
	wxMemoryDC memDC(bitmap);

	dc.Blit(0, 0, VIEW_WIDTH, VIEW_HEIGHT, &memDC, 0, 0);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.