|
|
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);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.