1 #include "stdafx.h"
2 #include "vba.h"
3 #include "FileDlg.h"
4 #include "GBMapView.h"
5 #include "Reg.h"
6 #include "WinResUtil.h"
7 
8 #include "../System.h"
9 #include "../NLS.h"
10 #include "../Util.h"
11 #include "../gb/gbGlobals.h"
12 
13 extern "C" {
14 #include <png.h>
15 }
16 
17 extern u8 gbInvertTab[256];
18 
19 #ifdef _DEBUG
20 #define new DEBUG_NEW
21 #undef THIS_FILE
22 static char THIS_FILE[] = __FILE__;
23 #endif
24 
25 /////////////////////////////////////////////////////////////////////////////
26 // GBMapView dialog
27 
28 
GBMapView(CWnd * pParent)29 GBMapView::GBMapView(CWnd* pParent /*=NULL*/)
30   : ResizeDlg(GBMapView::IDD, pParent)
31 {
32   //{{AFX_DATA_INIT(GBMapView)
33   // NOTE: the ClassWizard will add member initialization here
34   //}}AFX_DATA_INIT
35   autoUpdate = false;
36 
37   memset(&bmpInfo.bmiHeader, 0, sizeof(bmpInfo.bmiHeader));
38 
39   bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
40   bmpInfo.bmiHeader.biWidth = 1024;
41   bmpInfo.bmiHeader.biHeight = -1024;
42   bmpInfo.bmiHeader.biPlanes = 1;
43   bmpInfo.bmiHeader.biBitCount = 24;
44   bmpInfo.bmiHeader.biCompression = BI_RGB;
45   data = (u8 *)calloc(1, 3 * 1024 * 1024);
46 
47   mapView.setData(data);
48   mapView.setBmpInfo(&bmpInfo);
49 
50   bg = 0;
51   bank = 0;
52 }
53 
54 
DoDataExchange(CDataExchange * pDX)55 void GBMapView::DoDataExchange(CDataExchange* pDX)
56 {
57   CDialog::DoDataExchange(pDX);
58   //{{AFX_DATA_MAP(GBMapView)
59   // NOTE: the ClassWizard will add DDX and DDV calls here
60   //}}AFX_DATA_MAP
61   DDX_Control(pDX, IDC_MAP_VIEW, mapView);
62   DDX_Control(pDX, IDC_MAP_VIEW_ZOOM, mapViewZoom);
63   DDX_Control(pDX, IDC_COLOR, color);
64 }
65 
66 
BEGIN_MESSAGE_MAP(GBMapView,CDialog)67 BEGIN_MESSAGE_MAP(GBMapView, CDialog)
68   //{{AFX_MSG_MAP(GBMapView)
69   ON_BN_CLICKED(IDC_SAVE, OnSave)
70   ON_BN_CLICKED(IDC_REFRESH, OnRefresh)
71   ON_BN_CLICKED(IDC_BG0, OnBg0)
72   ON_BN_CLICKED(IDC_BG1, OnBg1)
73   ON_BN_CLICKED(IDC_BANK_0, OnBank0)
74   ON_BN_CLICKED(IDC_BANK_1, OnBank1)
75   ON_BN_CLICKED(IDC_STRETCH, OnStretch)
76   ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate)
77   ON_BN_CLICKED(IDC_CLOSE, OnClose)
78   //}}AFX_MSG_MAP
79   ON_MESSAGE(WM_MAPINFO, OnMapInfo)
80   ON_MESSAGE(WM_COLINFO, OnColInfo)
81   END_MESSAGE_MAP()
82 
83   /////////////////////////////////////////////////////////////////////////////
84 // GBMapView message handlers
85 
86 GBMapView::~GBMapView()
87 {
88   free(data);
89   data = NULL;
90 }
91 
saveBMP(const char * name)92 void GBMapView::saveBMP(const char *name)
93 {
94   u8 writeBuffer[1024 * 3];
95 
96   FILE *fp = fopen(name,"wb");
97 
98   if(!fp) {
99     systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name);
100     return;
101   }
102 
103   struct {
104     u8 ident[2];
105     u8 filesize[4];
106     u8 reserved[4];
107     u8 dataoffset[4];
108     u8 headersize[4];
109     u8 width[4];
110     u8 height[4];
111     u8 planes[2];
112     u8 bitsperpixel[2];
113     u8 compression[4];
114     u8 datasize[4];
115     u8 hres[4];
116     u8 vres[4];
117     u8 colors[4];
118     u8 importantcolors[4];
119     u8 pad[2];
120   } bmpheader;
121   memset(&bmpheader, 0, sizeof(bmpheader));
122 
123   bmpheader.ident[0] = 'B';
124   bmpheader.ident[1] = 'M';
125 
126   u32 fsz = sizeof(bmpheader) + w*h*3;
127   utilPutDword(bmpheader.filesize, fsz);
128   utilPutDword(bmpheader.dataoffset, 0x38);
129   utilPutDword(bmpheader.headersize, 0x28);
130   utilPutDword(bmpheader.width, w);
131   utilPutDword(bmpheader.height, h);
132   utilPutDword(bmpheader.planes, 1);
133   utilPutDword(bmpheader.bitsperpixel, 24);
134   utilPutDword(bmpheader.datasize, 3*w*h);
135 
136   fwrite(&bmpheader, 1, sizeof(bmpheader), fp);
137 
138   u8 *b = writeBuffer;
139 
140   int sizeX = w;
141   int sizeY = h;
142 
143   u8 *pixU8 = (u8 *)data+3*w*(h-1);
144   for(int y = 0; y < sizeY; y++) {
145     for(int x = 0; x < sizeX; x++) {
146       *b++ = *pixU8++; // B
147       *b++ = *pixU8++; // G
148       *b++ = *pixU8++; // R
149     }
150     pixU8 -= 2*3*w;
151     fwrite(writeBuffer, 1, 3*w, fp);
152 
153     b = writeBuffer;
154   }
155 
156   fclose(fp);
157 }
158 
savePNG(const char * name)159 void GBMapView::savePNG(const char *name)
160 {
161   u8 writeBuffer[1024 * 3];
162 
163   FILE *fp = fopen(name,"wb");
164 
165   if(!fp) {
166     systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name);
167     return;
168   }
169 
170   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
171                                                 NULL,
172                                                 NULL,
173                                                 NULL);
174   if(!png_ptr) {
175     fclose(fp);
176     return;
177   }
178 
179   png_infop info_ptr = png_create_info_struct(png_ptr);
180 
181   if(!info_ptr) {
182     png_destroy_write_struct(&png_ptr,NULL);
183     fclose(fp);
184     return;
185   }
186 
187   if(setjmp(png_ptr->jmpbuf)) {
188     png_destroy_write_struct(&png_ptr,NULL);
189     fclose(fp);
190     return;
191   }
192 
193   png_init_io(png_ptr,fp);
194 
195   png_set_IHDR(png_ptr,
196                info_ptr,
197                w,
198                h,
199                8,
200                PNG_COLOR_TYPE_RGB,
201                PNG_INTERLACE_NONE,
202                PNG_COMPRESSION_TYPE_DEFAULT,
203                PNG_FILTER_TYPE_DEFAULT);
204 
205   png_write_info(png_ptr,info_ptr);
206 
207   u8 *b = writeBuffer;
208 
209   int sizeX = w;
210   int sizeY = h;
211 
212   u8 *pixU8 = (u8 *)data;
213   for(int y = 0; y < sizeY; y++) {
214     for(int x = 0; x < sizeX; x++) {
215       int blue = *pixU8++;
216       int green = *pixU8++;
217       int red = *pixU8++;
218 
219       *b++ = red;
220       *b++ = green;
221       *b++ = blue;
222     }
223     png_write_row(png_ptr,writeBuffer);
224 
225     b = writeBuffer;
226   }
227 
228   png_write_end(png_ptr, info_ptr);
229 
230   png_destroy_write_struct(&png_ptr, &info_ptr);
231 
232   fclose(fp);
233 }
234 
OnSave()235 void GBMapView::OnSave()
236 {
237   CString filename;
238 
239   if(captureFormat == 0)
240     filename = "map.png";
241   else
242     filename = "map.bmp";
243 
244   LPCTSTR exts[] = {".png", ".bmp" };
245   CString title = winResLoadString(IDS_SELECT_CAPTURE_NAME);
246   CString filter = theApp.winLoadFilter(IDS_FILTER_PNG);
247 
248   FileDlg dlg(this,
249               filename,
250               filter,
251               captureFormat ? 2 : 1,
252               captureFormat ? "BMP" : "PNG",
253               exts,
254               "",
255               title,
256               true);
257 
258   if(dlg.DoModal() == IDCANCEL) {
259     return;
260   }
261 
262   if(dlg.getFilterIndex() == 2)
263     saveBMP(dlg.GetPathName());
264   else
265     savePNG(dlg.GetPathName());
266 }
267 
render()268 void GBMapView::render()
269 {
270   u8 * bank0;
271   u8 * bank1;
272   if(gbCgbMode) {
273     bank0 = &gbVram[0x0000];
274     bank1 = &gbVram[0x2000];
275   } else {
276     bank0 = &gbMemory[0x8000];
277     bank1 = NULL;
278   }
279 
280   int tile_map_address = 0x1800;
281   if(bg == 1)
282     tile_map_address = 0x1c00;
283 
284   int tile_pattern = 0x0000;
285   if(bank == 1)
286     tile_pattern = 0x0800;
287 
288   w = 256;
289   h = 256;
290 
291   int tile = 0;
292   for(int y = 0; y < 32; y++) {
293     for(int x = 0; x < 32; x++) {
294       u8 *bmp = &data[y * 8 * 32 * 24 + x*24];
295       u8 attrs = 0;
296       if(bank1 != NULL)
297         attrs = bank1[tile_map_address];
298       u8 tile = bank0[tile_map_address];
299       tile_map_address++;
300 
301       if(bank == 1) {
302         if(tile < 128) tile += 128;
303         else tile -= 128;
304       }
305       for(int j = 0; j < 8; j++) {
306         int tile_pattern_address = attrs & 0x40 ?
307           tile_pattern + tile*16 + (7-j)*2:
308           tile_pattern + tile*16+j*2;
309 
310         u8 tile_a = 0;
311         u8 tile_b = 0;
312 
313         if(attrs & 0x08) {
314           tile_a = bank1[tile_pattern_address++];
315           tile_b = bank1[tile_pattern_address];
316         } else {
317           tile_a = bank0[tile_pattern_address++];
318           tile_b = bank0[tile_pattern_address];
319         }
320 
321         if(attrs & 0x20) {
322           tile_a = gbInvertTab[tile_a];
323           tile_b = gbInvertTab[tile_b];
324         }
325 
326         u8 mask = 0x80;
327 
328         while(mask > 0) {
329           u8 c = (tile_a & mask) ? 1 : 0;
330           c += (tile_b & mask) ? 2 : 0;
331 
332           if(gbCgbMode)
333             c = c + (attrs & 7)*4;
334 
335           u16 color = gbPalette[c];
336 
337           *bmp++ = ((color >> 10) & 0x1f) << 3;
338           *bmp++ = ((color >> 5) & 0x1f) << 3;
339           *bmp++ = (color & 0x1f) << 3;
340 
341           mask >>= 1;
342         }
343         bmp += 31*24;
344       }
345     }
346   }
347 }
348 
paint()349 void GBMapView::paint()
350 {
351   if(gbRom == NULL)
352     return;
353   render();
354 
355   SIZE s;
356   if(mapView.getStretch()) {
357     mapView.setSize(w, h);
358     s.cx = s.cy = 1;
359     mapView.SetScrollSizes(MM_TEXT, s);
360   } else {
361     mapView.setSize(w, h);
362     s.cx = w;
363     s.cy = h;
364     mapView.SetScrollSizes(MM_TEXT, s);
365   }
366 
367   mapView.refresh();
368 }
369 
OnRefresh()370 void GBMapView::OnRefresh()
371 {
372   paint();
373 }
374 
update()375 void GBMapView::update()
376 {
377   paint();
378 }
379 
OnInitDialog()380 BOOL GBMapView::OnInitDialog()
381 {
382   CDialog::OnInitDialog();
383 
384   DIALOG_SIZER_START( sz )
385     DIALOG_SIZER_ENTRY( IDC_MAP_VIEW, DS_SizeX | DS_SizeY )
386     DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY)
387     DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY)
388     DIALOG_SIZER_ENTRY( IDC_SAVE,  DS_MoveY)
389     DIALOG_SIZER_ENTRY( IDC_COLOR, DS_MoveY)
390     DIALOG_SIZER_ENTRY( IDC_R, DS_MoveY)
391     DIALOG_SIZER_ENTRY( IDC_G, DS_MoveY)
392     DIALOG_SIZER_ENTRY( IDC_B, DS_MoveY)
393     DIALOG_SIZER_END()
394     SetData(sz,
395             TRUE,
396             HKEY_CURRENT_USER,
397             "Software\\Emulators\\VisualBoyAdvance\\Viewer\\GBMapView",
398             NULL);
399 
400   int s = regQueryDwordValue("mapViewStretch", 0);
401   if(s)
402     mapView.setStretch(true);
403   ((CButton *)GetDlgItem(IDC_STRETCH))->SetCheck(s);
404 
405   UINT id = IDC_BANK_0;
406   if(bank == 1)
407     id = IDC_BANK_1;
408   CheckRadioButton(IDC_BANK_0, IDC_BANK_1, id);
409   id = IDC_BG0;
410   if(bg == 1)
411     id = IDC_BG1;
412   CheckRadioButton(IDC_BG0, IDC_BG1, id);
413   paint();
414 
415   return TRUE;  // return TRUE unless you set the focus to a control
416                 // EXCEPTION: OCX Property Pages should return FALSE
417 }
418 
OnBg0()419 void GBMapView::OnBg0()
420 {
421   bg = 0;
422   paint();
423 }
424 
OnBg1()425 void GBMapView::OnBg1()
426 {
427   bg = 1;
428   paint();
429 }
430 
OnBank0()431 void GBMapView::OnBank0()
432 {
433   bank = 0;
434   paint();
435 }
436 
OnBank1()437 void GBMapView::OnBank1()
438 {
439   bank = 1;
440   paint();
441 }
442 
OnStretch()443 void GBMapView::OnStretch()
444 {
445   mapView.setStretch(!mapView.getStretch());
446   paint();
447   regSetDwordValue("mapViewStretch", mapView.getStretch());
448 }
449 
OnAutoUpdate()450 void GBMapView::OnAutoUpdate()
451 {
452   autoUpdate = !autoUpdate;
453   if(autoUpdate) {
454     theApp.winAddUpdateListener(this);
455   } else {
456     theApp.winRemoveUpdateListener(this);
457   }
458 }
459 
OnClose()460 void GBMapView::OnClose()
461 {
462   theApp.winRemoveUpdateListener(this);
463 
464   DestroyWindow();
465 }
466 
GetClickAddress(int x,int y)467 u32 GBMapView::GetClickAddress(int x, int y)
468 {
469   u32 base = 0x9800;
470   if(bg == 1)
471     base = 0x9c00;
472 
473   return base + (y >> 3)*32 + (x >> 3);
474 }
475 
OnMapInfo(WPARAM wParam,LPARAM lParam)476 LRESULT GBMapView::OnMapInfo(WPARAM wParam, LPARAM lParam)
477 {
478   u8 *colors = (u8 *)lParam;
479   mapViewZoom.setColors(colors);
480 
481   int x = (int)(wParam & 0xffff);
482   int y = (int)(wParam >> 16);
483 
484   CString buffer;
485   buffer.Format("(%d,%d)", x, y);
486   GetDlgItem(IDC_XY)->SetWindowText(buffer);
487 
488   u32 address = GetClickAddress(x,y);
489   buffer.Format("0x%08X", address);
490   GetDlgItem(IDC_ADDRESS)->SetWindowText(buffer);
491 
492   u8 attrs = 0;
493 
494   u8 tile = gbMemoryMap[9][address & 0xfff];
495   if(gbCgbMode) {
496     attrs = gbVram[0x2000 + address - 0x8000];
497     tile = gbVram[address & 0x1fff];
498   }
499 
500   if(bank == 1) {
501     if(tile > 128) tile -= 128;
502     else tile += 128;
503   }
504 
505   buffer.Format("%d", tile);
506   GetDlgItem(IDC_TILE_NUM)->SetWindowText(buffer);
507 
508   buffer.Empty();
509   buffer += attrs & 0x20 ? 'H' : '-';
510   buffer += attrs & 0x40 ? 'V' : '-';
511   GetDlgItem(IDC_FLIP)->SetWindowText(buffer);
512 
513   if(gbCgbMode) {
514     buffer.Format("%d", (attrs & 7));
515   } else
516     buffer = "---";
517   GetDlgItem(IDC_PALETTE_NUM)->SetWindowText(buffer);
518 
519   buffer.Empty();
520   if(gbCgbMode)
521     buffer += attrs & 0x80 ? 'P' : '-';
522   else
523     buffer += '-';
524   GetDlgItem(IDC_PRIORITY)->SetWindowText(buffer);
525 
526   return TRUE;
527 }
528 
OnColInfo(WPARAM wParam,LPARAM)529 LRESULT GBMapView::OnColInfo(WPARAM wParam, LPARAM)
530 {
531   u16 c = (u16)wParam;
532 
533   color.setColor(c);
534 
535   int r = (c & 0x1f);
536   int g = (c & 0x3e0) >> 5;
537   int b = (c & 0x7c00) >> 10;
538 
539   CString buffer;
540   buffer.Format("R: %d", r);
541   GetDlgItem(IDC_R)->SetWindowText(buffer);
542 
543   buffer.Format("G: %d", g);
544   GetDlgItem(IDC_G)->SetWindowText(buffer);
545 
546   buffer.Format("B: %d", b);
547   GetDlgItem(IDC_B)->SetWindowText(buffer);
548 
549   return TRUE;
550 }
551 
PostNcDestroy()552 void GBMapView::PostNcDestroy()
553 {
554   delete this;
555   CDialog::PostNcDestroy();
556 }
557