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