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