1 // VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
2 // Copyright (C) 1999-2003 Forgotten
3 // Copyright (C) 2004 Forgotten and the VBA development team
4
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2, or(at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 // TileView.cpp : implementation file
20 //
21
22 #include "stdafx.h"
23 #include "vba.h"
24 #include "FileDlg.h"
25 #include "Reg.h"
26 #include "TileView.h"
27 #include "WinResUtil.h"
28
29 #include "../System.h"
30 #include "../GBA.h"
31 #include "../Globals.h"
32 #include "../NLS.h"
33 #include "../Util.h"
34
35 extern "C" {
36 #include <png.h>
37 }
38
39 #ifdef _DEBUG
40 #define new DEBUG_NEW
41 #undef THIS_FILE
42 static char THIS_FILE[] = __FILE__;
43 #endif
44
45 /////////////////////////////////////////////////////////////////////////////
46 // TileView dialog
47
48
TileView(CWnd * pParent)49 TileView::TileView(CWnd* pParent /*=NULL*/)
50 : ResizeDlg(TileView::IDD, pParent)
51 {
52 //{{AFX_DATA_INIT(TileView)
53 m_colors = -1;
54 m_charBase = -1;
55 m_stretch = FALSE;
56 //}}AFX_DATA_INIT
57 autoUpdate = false;
58
59 memset(&bmpInfo, 0, sizeof(bmpInfo));
60
61 bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
62 bmpInfo.bmiHeader.biWidth = 32*8;
63 bmpInfo.bmiHeader.biHeight = 32*8;
64 bmpInfo.bmiHeader.biPlanes = 1;
65 bmpInfo.bmiHeader.biBitCount = 24;
66 bmpInfo.bmiHeader.biCompression = BI_RGB;
67 data = (u8 *)calloc(1, 3 * 32*32 * 64);
68
69 tileView.setData(data);
70 tileView.setBmpInfo(&bmpInfo);
71
72 charBase = 0;
73 is256Colors = 0;
74 palette = 0;
75 w = h = 0;
76 }
77
~TileView()78 TileView::~TileView()
79 {
80 free(data);
81 data = NULL;
82 }
83
DoDataExchange(CDataExchange * pDX)84 void TileView::DoDataExchange(CDataExchange* pDX)
85 {
86 CDialog::DoDataExchange(pDX);
87 //{{AFX_DATA_MAP(TileView)
88 DDX_Control(pDX, IDC_PALETTE_SLIDER, m_slider);
89 DDX_Radio(pDX, IDC_16_COLORS, m_colors);
90 DDX_Radio(pDX, IDC_CHARBASE_0, m_charBase);
91 DDX_Check(pDX, IDC_STRETCH, m_stretch);
92 //}}AFX_DATA_MAP
93 DDX_Control(pDX, IDC_TILE_VIEW, tileView);
94 DDX_Control(pDX, IDC_MAP_VIEW_ZOOM, zoom);
95 DDX_Control(pDX, IDC_COLOR, color);
96 }
97
98
BEGIN_MESSAGE_MAP(TileView,CDialog)99 BEGIN_MESSAGE_MAP(TileView, CDialog)
100 //{{AFX_MSG_MAP(TileView)
101 ON_BN_CLICKED(IDC_SAVE, OnSave)
102 ON_BN_CLICKED(IDC_CLOSE, OnClose)
103 ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate)
104 ON_BN_CLICKED(IDC_16_COLORS, On16Colors)
105 ON_BN_CLICKED(IDC_256_COLORS, On256Colors)
106 ON_BN_CLICKED(IDC_CHARBASE_0, OnCharbase0)
107 ON_BN_CLICKED(IDC_CHARBASE_1, OnCharbase1)
108 ON_BN_CLICKED(IDC_CHARBASE_2, OnCharbase2)
109 ON_BN_CLICKED(IDC_CHARBASE_3, OnCharbase3)
110 ON_BN_CLICKED(IDC_CHARBASE_4, OnCharbase4)
111 ON_BN_CLICKED(IDC_STRETCH, OnStretch)
112 ON_WM_HSCROLL()
113 //}}AFX_MSG_MAP
114 ON_MESSAGE(WM_MAPINFO, OnMapInfo)
115 ON_MESSAGE(WM_COLINFO, OnColInfo)
116 END_MESSAGE_MAP()
117
118 /////////////////////////////////////////////////////////////////////////////
119 // TileView message handlers
120
121 void TileView::saveBMP(const char *name)
122 {
123 u8 writeBuffer[1024 * 3];
124
125 FILE *fp = fopen(name,"wb");
126
127 if(!fp) {
128 systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name);
129 return;
130 }
131
132 struct {
133 u8 ident[2];
134 u8 filesize[4];
135 u8 reserved[4];
136 u8 dataoffset[4];
137 u8 headersize[4];
138 u8 width[4];
139 u8 height[4];
140 u8 planes[2];
141 u8 bitsperpixel[2];
142 u8 compression[4];
143 u8 datasize[4];
144 u8 hres[4];
145 u8 vres[4];
146 u8 colors[4];
147 u8 importantcolors[4];
148 u8 pad[2];
149 } bmpheader;
150 memset(&bmpheader, 0, sizeof(bmpheader));
151
152 bmpheader.ident[0] = 'B';
153 bmpheader.ident[1] = 'M';
154
155 u32 fsz = sizeof(bmpheader) + w*h*3;
156 utilPutDword(bmpheader.filesize, fsz);
157 utilPutDword(bmpheader.dataoffset, 0x38);
158 utilPutDword(bmpheader.headersize, 0x28);
159 utilPutDword(bmpheader.width, w);
160 utilPutDword(bmpheader.height, h);
161 utilPutDword(bmpheader.planes, 1);
162 utilPutDword(bmpheader.bitsperpixel, 24);
163 utilPutDword(bmpheader.datasize, 3*w*h);
164
165 fwrite(&bmpheader, 1, sizeof(bmpheader), fp);
166
167 u8 *b = writeBuffer;
168
169 int sizeX = w;
170 int sizeY = h;
171
172 u8 *pixU8 = (u8 *)data+3*w*(h-1);
173 for(int y = 0; y < sizeY; y++) {
174 for(int x = 0; x < sizeX; x++) {
175 *b++ = *pixU8++; // B
176 *b++ = *pixU8++; // G
177 *b++ = *pixU8++; // R
178 }
179 pixU8 -= 2*3*w;
180 fwrite(writeBuffer, 1, 3*w, fp);
181
182 b = writeBuffer;
183 }
184
185 fclose(fp);
186 }
187
188
savePNG(const char * name)189 void TileView::savePNG(const char *name)
190 {
191 u8 writeBuffer[1024 * 3];
192
193 FILE *fp = fopen(name,"wb");
194
195 if(!fp) {
196 systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name);
197 return;
198 }
199
200 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
201 NULL,
202 NULL,
203 NULL);
204 if(!png_ptr) {
205 fclose(fp);
206 return;
207 }
208
209 png_infop info_ptr = png_create_info_struct(png_ptr);
210
211 if(!info_ptr) {
212 png_destroy_write_struct(&png_ptr,NULL);
213 fclose(fp);
214 return;
215 }
216
217 if(setjmp(png_ptr->jmpbuf)) {
218 png_destroy_write_struct(&png_ptr,NULL);
219 fclose(fp);
220 return;
221 }
222
223 png_init_io(png_ptr,fp);
224
225 png_set_IHDR(png_ptr,
226 info_ptr,
227 w,
228 h,
229 8,
230 PNG_COLOR_TYPE_RGB,
231 PNG_INTERLACE_NONE,
232 PNG_COMPRESSION_TYPE_DEFAULT,
233 PNG_FILTER_TYPE_DEFAULT);
234
235 png_write_info(png_ptr,info_ptr);
236
237 u8 *b = writeBuffer;
238
239 int sizeX = w;
240 int sizeY = h;
241
242 u8 *pixU8 = (u8 *)data;
243 for(int y = 0; y < sizeY; y++) {
244 for(int x = 0; x < sizeX; x++) {
245 int blue = *pixU8++;
246 int green = *pixU8++;
247 int red = *pixU8++;
248
249 *b++ = red;
250 *b++ = green;
251 *b++ = blue;
252 }
253 png_write_row(png_ptr,writeBuffer);
254
255 b = writeBuffer;
256 }
257
258 png_write_end(png_ptr, info_ptr);
259
260 png_destroy_write_struct(&png_ptr, &info_ptr);
261
262 fclose(fp);
263 }
264
265
OnSave()266 void TileView::OnSave()
267 {
268 CString captureBuffer;
269
270 if(theApp.captureFormat == 0)
271 captureBuffer = "tiles.png";
272 else
273 captureBuffer = "tiles.bmp";
274
275 LPCTSTR exts[] = {".png", ".bmp" };
276
277 CString filter = theApp.winLoadFilter(IDS_FILTER_PNG);
278 CString title = winResLoadString(IDS_SELECT_CAPTURE_NAME);
279
280 FileDlg dlg(this,
281 captureBuffer,
282 filter,
283 theApp.captureFormat ? 2 : 1,
284 theApp.captureFormat ? "BMP" : "PNG",
285 exts,
286 "",
287 title,
288 true);
289
290 if(dlg.DoModal() == IDCANCEL) {
291 return;
292 }
293
294 captureBuffer = dlg.GetPathName();
295
296 if(dlg.getFilterIndex() == 2)
297 saveBMP(captureBuffer);
298 else
299 savePNG(captureBuffer);
300 }
301
renderTile256(int tile,int x,int y,u8 * charBase,u16 * palette)302 void TileView::renderTile256(int tile, int x, int y, u8 *charBase, u16 *palette)
303 {
304 u8 *bmp = &data[24*x + 8*32*24*y];
305
306 for(int j = 0; j < 8; j++) {
307 for(int i = 0; i < 8; i++) {
308 u8 c = charBase[tile*64 + j * 8 + i];
309
310 u16 color = palette[c];
311
312 *bmp++ = ((color >> 10) & 0x1f) << 3;
313 *bmp++ = ((color >> 5) & 0x1f) << 3;
314 *bmp++ = (color & 0x1f) << 3;
315
316 }
317 bmp += 31*24; // advance line
318 }
319 }
320
renderTile16(int tile,int x,int y,u8 * charBase,u16 * palette)321 void TileView::renderTile16(int tile, int x, int y, u8 *charBase, u16 *palette)
322 {
323 u8 *bmp = &data[24*x + 8*32*24*y];
324
325 int pal = this->palette;
326
327 if(this->charBase == 4)
328 pal += 16;
329
330 for(int j = 0; j < 8; j++) {
331 for(int i = 0; i < 8; i++) {
332 u8 c = charBase[tile*32 + j * 4 + (i>>1)];
333
334 if(i & 1)
335 c = c>>4;
336 else
337 c = c & 15;
338
339 u16 color = palette[pal*16+c];
340
341 *bmp++ = ((color >> 10) & 0x1f) << 3;
342 *bmp++ = ((color >> 5) & 0x1f) << 3;
343 *bmp++ = (color & 0x1f) << 3;
344 }
345 bmp += 31*24; // advance line
346 }
347 }
348
349
render()350 void TileView::render()
351 {
352 u16 *palette = (u16 *)paletteRAM;
353 u8 *charBase = &vram[this->charBase * 0x4000];
354
355 int maxY;
356
357 if(is256Colors) {
358 int tile = 0;
359 maxY = 16;
360 for(int y = 0; y < maxY; y++) {
361 for(int x = 0; x < 32; x++) {
362 if(this->charBase == 4)
363 renderTile256(tile, x, y, charBase, &palette[256]);
364 else
365 renderTile256(tile, x, y, charBase, palette);
366 tile++;
367 }
368 }
369 tileView.setSize(32*8, maxY*8);
370 w = 32*8;
371 h = maxY*8;
372 SIZE s;
373 s.cx = 32*8;
374 s.cy = maxY*8;
375 if(tileView.getStretch()) {
376 s.cx = s.cy = 1;
377 }
378 tileView.SetScrollSizes(MM_TEXT,s);
379 } else {
380 int tile = 0;
381 maxY = 32;
382 if(this->charBase == 3)
383 maxY = 16;
384 for(int y = 0; y < maxY; y++) {
385 for(int x = 0; x < 32; x++) {
386 renderTile16(tile, x, y, charBase, palette);
387 tile++;
388 }
389 }
390 tileView.setSize(32*8, maxY*8);
391 w = 32*8;
392 h = maxY*8;
393 SIZE s;
394 s.cx = 32*8;
395 s.cy = maxY*8;
396 if(tileView.getStretch()) {
397 s.cx = s.cy = 1;
398 }
399 tileView.SetScrollSizes(MM_TEXT, s);
400 }
401 }
402
update()403 void TileView::update()
404 {
405 paint();
406 }
407
408
OnInitDialog()409 BOOL TileView::OnInitDialog()
410 {
411 CDialog::OnInitDialog();
412
413 DIALOG_SIZER_START( sz )
414 DIALOG_SIZER_ENTRY( IDC_TILE_VIEW, DS_SizeX | DS_SizeY )
415 DIALOG_SIZER_ENTRY( IDC_COLOR, DS_MoveY)
416 DIALOG_SIZER_ENTRY( IDC_R, DS_MoveY)
417 DIALOG_SIZER_ENTRY( IDC_G, DS_MoveY)
418 DIALOG_SIZER_ENTRY( IDC_B, DS_MoveY)
419 DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY)
420 DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY)
421 DIALOG_SIZER_ENTRY( IDC_SAVE, DS_MoveY)
422 DIALOG_SIZER_END()
423 SetData(sz,
424 TRUE,
425 HKEY_CURRENT_USER,
426 "Software\\Emulators\\VisualBoyAdvance\\Viewer\\TileView",
427 NULL);
428
429 m_colors = is256Colors;
430 m_charBase = charBase;
431
432 m_slider.SetRange(0, 15);
433 m_slider.SetPageSize(4);
434 m_slider.SetTicFreq(1);
435
436 paint();
437
438 m_stretch = regQueryDwordValue("tileViewStretch", 0);
439 if(m_stretch)
440 tileView.setStretch(true);
441 UpdateData(FALSE);
442
443 return TRUE; // return TRUE unless you set the focus to a control
444 // EXCEPTION: OCX Property Pages should return FALSE
445 }
446
OnClose()447 void TileView::OnClose()
448 {
449 theApp.winRemoveUpdateListener(this);
450
451 DestroyWindow();
452 }
453
454
OnAutoUpdate()455 void TileView::OnAutoUpdate()
456 {
457 autoUpdate = !autoUpdate;
458 if(autoUpdate) {
459 theApp.winAddUpdateListener(this);
460 } else {
461 theApp.winRemoveUpdateListener(this);
462 }
463 }
464
465
paint()466 void TileView::paint()
467 {
468 if(vram != NULL && paletteRAM != NULL) {
469 render();
470 tileView.refresh();
471 }
472 }
473
474
On16Colors()475 void TileView::On16Colors()
476 {
477 is256Colors = 0;
478 paint();
479 }
480
On256Colors()481 void TileView::On256Colors()
482 {
483 is256Colors = 1;
484 paint();
485 }
486
OnCharbase0()487 void TileView::OnCharbase0()
488 {
489 charBase = 0;
490 paint();
491 }
492
OnCharbase1()493 void TileView::OnCharbase1()
494 {
495 charBase = 1;
496 paint();
497 }
498
OnCharbase2()499 void TileView::OnCharbase2()
500 {
501 charBase = 2;
502 paint();
503 }
504
OnCharbase3()505 void TileView::OnCharbase3()
506 {
507 charBase = 3;
508 paint();
509 }
510
OnCharbase4()511 void TileView::OnCharbase4()
512 {
513 charBase = 4;
514 paint();
515 }
516
OnStretch()517 void TileView::OnStretch()
518 {
519 tileView.setStretch(!tileView.getStretch());
520 paint();
521 regSetDwordValue("tileViewStretch", tileView.getStretch());
522 }
523
OnMapInfo(WPARAM wParam,LPARAM lParam)524 LRESULT TileView::OnMapInfo(WPARAM wParam, LPARAM lParam)
525 {
526 u8 *colors = (u8 *)lParam;
527 zoom.setColors(colors);
528
529 int x = (wParam & 0xFFFF)/8;
530 int y = ((wParam >> 16) & 0xFFFF)/8;
531
532 u32 address = 0x6000000 + 0x4000 * charBase;
533 int tile = 32 * y + x;
534 if(is256Colors)
535 tile *= 2;
536 address += 32 * tile;
537
538 CString buffer;
539 buffer.Format("%d", tile);
540 GetDlgItem(IDC_TILE_NUMBER)->SetWindowText(buffer);
541
542 buffer.Format("%08x", address);
543 GetDlgItem(IDC_ADDRESS)->SetWindowText(buffer);
544
545 return TRUE;
546 }
547
OnColInfo(WPARAM wParam,LPARAM)548 LRESULT TileView::OnColInfo(WPARAM wParam, LPARAM)
549 {
550 u16 c = (u16)wParam;
551
552 color.setColor(c);
553
554 int r = (c & 0x1f);
555 int g = (c & 0x3e0) >> 5;
556 int b = (c & 0x7c00) >> 10;
557
558 CString buffer;
559 buffer.Format("R: %d", r);
560 GetDlgItem(IDC_R)->SetWindowText(buffer);
561
562 buffer.Format("G: %d", g);
563 GetDlgItem(IDC_G)->SetWindowText(buffer);
564
565 buffer.Format("B: %d", b);
566 GetDlgItem(IDC_B)->SetWindowText(buffer);
567
568 return TRUE;
569 }
570
OnHScroll(UINT nSBCode,UINT nPos,CScrollBar * pScrollBar)571 void TileView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
572 {
573 switch(nSBCode) {
574 case TB_THUMBPOSITION:
575 palette = nPos;
576 break;
577 default:
578 palette = m_slider.GetPos();
579 break;
580 }
581 paint();
582 }
583
PostNcDestroy()584 void TileView::PostNcDestroy()
585 {
586 delete this;
587 }
588