// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. // Copyright (C) 1999-2003 Forgotten // Copyright (C) 2004 Forgotten and the VBA development team // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2, or(at your option) // any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software Foundation, // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // GBMapView.cpp : implementation file // #include "stdafx.h" #include "vba.h" #include "FileDlg.h" #include "GBMapView.h" #include "Reg.h" #include "WinResUtil.h" #include "../System.h" #include "../NLS.h" #include "../Util.h" #include "../gb/gbGlobals.h" extern "C" { #include } extern u8 gbInvertTab[256]; #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // GBMapView dialog GBMapView::GBMapView(CWnd* pParent /*=NULL*/) : ResizeDlg(GBMapView::IDD, pParent) { //{{AFX_DATA_INIT(GBMapView) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT autoUpdate = false; memset(&bmpInfo.bmiHeader, 0, sizeof(bmpInfo.bmiHeader)); bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader); bmpInfo.bmiHeader.biWidth = 1024; bmpInfo.bmiHeader.biHeight = -1024; bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 24; bmpInfo.bmiHeader.biCompression = BI_RGB; data = (u8 *)calloc(1, 3 * 1024 * 1024); mapView.setData(data); mapView.setBmpInfo(&bmpInfo); bg = 0; bank = 0; } void GBMapView::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(GBMapView) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP DDX_Control(pDX, IDC_MAP_VIEW, mapView); DDX_Control(pDX, IDC_MAP_VIEW_ZOOM, mapViewZoom); DDX_Control(pDX, IDC_COLOR, color); } BEGIN_MESSAGE_MAP(GBMapView, CDialog) //{{AFX_MSG_MAP(GBMapView) ON_BN_CLICKED(IDC_SAVE, OnSave) ON_BN_CLICKED(IDC_REFRESH, OnRefresh) ON_BN_CLICKED(IDC_BG0, OnBg0) ON_BN_CLICKED(IDC_BG1, OnBg1) ON_BN_CLICKED(IDC_BANK_0, OnBank0) ON_BN_CLICKED(IDC_BANK_1, OnBank1) ON_BN_CLICKED(IDC_STRETCH, OnStretch) ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate) ON_BN_CLICKED(IDC_CLOSE, OnClose) //}}AFX_MSG_MAP ON_MESSAGE(WM_MAPINFO, OnMapInfo) ON_MESSAGE(WM_COLINFO, OnColInfo) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // GBMapView message handlers GBMapView::~GBMapView() { free(data); data = NULL; } void GBMapView::saveBMP(const char *name) { u8 writeBuffer[1024 * 3]; FILE *fp = fopen(name,"wb"); if(!fp) { systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); return; } struct { u8 ident[2]; u8 filesize[4]; u8 reserved[4]; u8 dataoffset[4]; u8 headersize[4]; u8 width[4]; u8 height[4]; u8 planes[2]; u8 bitsperpixel[2]; u8 compression[4]; u8 datasize[4]; u8 hres[4]; u8 vres[4]; u8 colors[4]; u8 importantcolors[4]; u8 pad[2]; } bmpheader; memset(&bmpheader, 0, sizeof(bmpheader)); bmpheader.ident[0] = 'B'; bmpheader.ident[1] = 'M'; u32 fsz = sizeof(bmpheader) + w*h*3; utilPutDword(bmpheader.filesize, fsz); utilPutDword(bmpheader.dataoffset, 0x38); utilPutDword(bmpheader.headersize, 0x28); utilPutDword(bmpheader.width, w); utilPutDword(bmpheader.height, h); utilPutDword(bmpheader.planes, 1); utilPutDword(bmpheader.bitsperpixel, 24); utilPutDword(bmpheader.datasize, 3*w*h); fwrite(&bmpheader, 1, sizeof(bmpheader), fp); u8 *b = writeBuffer; int sizeX = w; int sizeY = h; u8 *pixU8 = (u8 *)data+3*w*(h-1); for(int y = 0; y < sizeY; y++) { for(int x = 0; x < sizeX; x++) { *b++ = *pixU8++; // B *b++ = *pixU8++; // G *b++ = *pixU8++; // R } pixU8 -= 2*3*w; fwrite(writeBuffer, 1, 3*w, fp); b = writeBuffer; } fclose(fp); } void GBMapView::savePNG(const char *name) { u8 writeBuffer[1024 * 3]; FILE *fp = fopen(name,"wb"); if(!fp) { systemMessage(MSG_ERROR_CREATING_FILE, "Error creating file %s", name); return; } png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!png_ptr) { fclose(fp); return; } png_infop info_ptr = png_create_info_struct(png_ptr); if(!info_ptr) { png_destroy_write_struct(&png_ptr,NULL); fclose(fp); return; } if(setjmp(png_ptr->jmpbuf)) { png_destroy_write_struct(&png_ptr,NULL); fclose(fp); return; } png_init_io(png_ptr,fp); png_set_IHDR(png_ptr, info_ptr, w, h, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png_ptr,info_ptr); u8 *b = writeBuffer; int sizeX = w; int sizeY = h; u8 *pixU8 = (u8 *)data; for(int y = 0; y < sizeY; y++) { for(int x = 0; x < sizeX; x++) { int blue = *pixU8++; int green = *pixU8++; int red = *pixU8++; *b++ = red; *b++ = green; *b++ = blue; } png_write_row(png_ptr,writeBuffer); b = writeBuffer; } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); } void GBMapView::OnSave() { CString filename; if(theApp.captureFormat == 0) filename = "map.png"; else filename = "map.bmp"; LPCTSTR exts[] = {".png", ".bmp" }; CString title = winResLoadString(IDS_SELECT_CAPTURE_NAME); CString filter = theApp.winLoadFilter(IDS_FILTER_PNG); FileDlg dlg(this, filename, filter, theApp.captureFormat ? 2 : 1, theApp.captureFormat ? "BMP" : "PNG", exts, "", title, true); if(dlg.DoModal() == IDCANCEL) { return; } if(dlg.getFilterIndex() == 2) saveBMP(dlg.GetPathName()); else savePNG(dlg.GetPathName()); } void GBMapView::render() { u8 * bank0; u8 * bank1; if(gbCgbMode) { bank0 = &gbVram[0x0000]; bank1 = &gbVram[0x2000]; } else { bank0 = &gbMemory[0x8000]; bank1 = NULL; } int tile_map_address = 0x1800; if(bg == 1) tile_map_address = 0x1c00; int tile_pattern = 0x0000; if(bank == 1) tile_pattern = 0x0800; w = 256; h = 256; int tile = 0; for(int y = 0; y < 32; y++) { for(int x = 0; x < 32; x++) { u8 *bmp = &data[y * 8 * 32 * 24 + x*24]; u8 attrs = 0; if(bank1 != NULL) attrs = bank1[tile_map_address]; u8 tile = bank0[tile_map_address]; tile_map_address++; if(bank == 1) { if(tile < 128) tile += 128; else tile -= 128; } for(int j = 0; j < 8; j++) { int tile_pattern_address = attrs & 0x40 ? tile_pattern + tile*16 + (7-j)*2: tile_pattern + tile*16+j*2; u8 tile_a = 0; u8 tile_b = 0; if(attrs & 0x08) { tile_a = bank1[tile_pattern_address++]; tile_b = bank1[tile_pattern_address]; } else { tile_a = bank0[tile_pattern_address++]; tile_b = bank0[tile_pattern_address]; } if(attrs & 0x20) { tile_a = gbInvertTab[tile_a]; tile_b = gbInvertTab[tile_b]; } u8 mask = 0x80; while(mask > 0) { u8 c = (tile_a & mask) ? 1 : 0; c += (tile_b & mask) ? 2 : 0; if(gbCgbMode) c = c + (attrs & 7)*4; u16 color = gbPalette[c]; *bmp++ = ((color >> 10) & 0x1f) << 3; *bmp++ = ((color >> 5) & 0x1f) << 3; *bmp++ = (color & 0x1f) << 3; mask >>= 1; } bmp += 31*24; } } } } void GBMapView::paint() { if(gbRom == NULL) return; render(); SIZE s; if(mapView.getStretch()) { mapView.setSize(w, h); s.cx = s.cy = 1; mapView.SetScrollSizes(MM_TEXT, s); } else { mapView.setSize(w, h); s.cx = w; s.cy = h; mapView.SetScrollSizes(MM_TEXT, s); } mapView.refresh(); } void GBMapView::OnRefresh() { paint(); } void GBMapView::update() { paint(); } BOOL GBMapView::OnInitDialog() { CDialog::OnInitDialog(); DIALOG_SIZER_START( sz ) DIALOG_SIZER_ENTRY( IDC_MAP_VIEW, DS_SizeX | DS_SizeY ) DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY) DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY) DIALOG_SIZER_ENTRY( IDC_SAVE, DS_MoveY) DIALOG_SIZER_ENTRY( IDC_COLOR, DS_MoveY) DIALOG_SIZER_ENTRY( IDC_R, DS_MoveY) DIALOG_SIZER_ENTRY( IDC_G, DS_MoveY) DIALOG_SIZER_ENTRY( IDC_B, DS_MoveY) DIALOG_SIZER_END() SetData(sz, TRUE, HKEY_CURRENT_USER, "Software\\Emulators\\VisualBoyAdvance\\Viewer\\GBMapView", NULL); int s = regQueryDwordValue("mapViewStretch", 0); if(s) mapView.setStretch(true); ((CButton *)GetDlgItem(IDC_STRETCH))->SetCheck(s); UINT id = IDC_BANK_0; if(bank == 1) id = IDC_BANK_1; CheckRadioButton(IDC_BANK_0, IDC_BANK_1, id); id = IDC_BG0; if(bg == 1) id = IDC_BG1; CheckRadioButton(IDC_BG0, IDC_BG1, id); paint(); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } void GBMapView::OnBg0() { bg = 0; paint(); } void GBMapView::OnBg1() { bg = 1; paint(); } void GBMapView::OnBank0() { bank = 0; paint(); } void GBMapView::OnBank1() { bank = 1; paint(); } void GBMapView::OnStretch() { mapView.setStretch(!mapView.getStretch()); paint(); regSetDwordValue("mapViewStretch", mapView.getStretch()); } void GBMapView::OnAutoUpdate() { autoUpdate = !autoUpdate; if(autoUpdate) { theApp.winAddUpdateListener(this); } else { theApp.winRemoveUpdateListener(this); } } void GBMapView::OnClose() { theApp.winRemoveUpdateListener(this); DestroyWindow(); } u32 GBMapView::GetClickAddress(int x, int y) { u32 base = 0x9800; if(bg == 1) base = 0x9c00; return base + (y >> 3)*32 + (x >> 3); } LRESULT GBMapView::OnMapInfo(WPARAM wParam, LPARAM lParam) { u8 *colors = (u8 *)lParam; mapViewZoom.setColors(colors); int x = wParam & 0xffff; int y = (wParam >> 16); CString buffer; buffer.Format("(%d,%d)", x, y); GetDlgItem(IDC_XY)->SetWindowText(buffer); u32 address = GetClickAddress(x,y); buffer.Format("0x%08X", address); GetDlgItem(IDC_ADDRESS)->SetWindowText(buffer); u8 attrs = 0; u8 tile = gbMemoryMap[9][address & 0xfff]; if(gbCgbMode) { attrs = gbVram[0x2000 + address - 0x8000]; tile = gbVram[address & 0x1fff]; } if(bank == 1) { if(tile > 128) tile -= 128; else tile += 128; } buffer.Format("%d", tile); GetDlgItem(IDC_TILE_NUM)->SetWindowText(buffer); buffer.Empty(); buffer += attrs & 0x20 ? 'H' : '-'; buffer += attrs & 0x40 ? 'V' : '-'; GetDlgItem(IDC_FLIP)->SetWindowText(buffer); if(gbCgbMode) { buffer.Format("%d", (attrs & 7)); } else buffer = "---"; GetDlgItem(IDC_PALETTE_NUM)->SetWindowText(buffer); buffer.Empty(); if(gbCgbMode) buffer += attrs & 0x80 ? 'P' : '-'; else buffer += '-'; GetDlgItem(IDC_PRIORITY)->SetWindowText(buffer); return TRUE; } LRESULT GBMapView::OnColInfo(WPARAM wParam, LPARAM) { u16 c = (u16)wParam; color.setColor(c); int r = (c & 0x1f); int g = (c & 0x3e0) >> 5; int b = (c & 0x7c00) >> 10; CString buffer; buffer.Format("R: %d", r); GetDlgItem(IDC_R)->SetWindowText(buffer); buffer.Format("G: %d", g); GetDlgItem(IDC_G)->SetWindowText(buffer); buffer.Format("B: %d", b); GetDlgItem(IDC_B)->SetWindowText(buffer); return TRUE; } void GBMapView::PostNcDestroy() { delete this; CDialog::PostNcDestroy(); }