1 
2 // these are all the viewer dialogs with graphical panel areas
3 // they can be instantiated multiple times
4 
5 #include "wxvbam.h"
6 #include "viewsupt.h"
7 #include <wx/colordlg.h>
8 #include <wx/ffile.h>
9 
10 // FIXME: many of these read e.g. palette data directly without regard to
11 // byte order.  Need to determine where things are stored in emulated machine
12 // order and where in native order, and swap the latter on big-endian
13 
14 // most of these have label fields that need to be sized and later filled
15 // mv is a string to initialize with for sizing
16 #define getlab(v, n, mv) do { \
17     v = XRCCTRL(*this, n, wxControl); \
18     if(!v) \
19     baddialog(); \
20     v->SetLabel(wxT(mv)); \
21 } while(0)
22 
23 // FIXME: this should be in a header
24 extern u8 gbInvertTab[256];
25 
26 // avoid exporting classes
27 namespace Viewers
28 {
29 class MapViewer : public GfxViewer
30 {
31 public:
MapViewer()32 	MapViewer() : GfxViewer(wxT("MapViewer"), 1024, 1024)
33 	{
34 		frame = bg = 0;
35 		getradio(fr0 = , "Frame0", frame, 0);
36 		getradio(fr1 = , "Frame1", frame, 0xa000);
37 		getradio(bg0 = , "BG0", bg, 0);
38 		getradio(bg1 = , "BG1", bg, 1);
39 		getradio(bg2 = , "BG2", bg, 2);
40 		getradio(bg3 = , "BG3", bg, 3);
41 		getlab(modelab, "Mode", "8");
42 		getlab(mapbase, "MapBase", "0xWWWWWWWW");
43 		getlab(charbase, "CharBase", "0xWWWWWWWW");
44 		getlab(size, "Size", "1024x1024");
45 		getlab(colors, "Colors", "2WW");
46 		getlab(prio, "Priority", "3");
47 		getlab(mosaic, "Mosaic", "0");
48 		getlab(overflow, "Overflow", "0");
49 		getlab(coords, "Coords", "(1023,1023)");
50 		getlab(addr, "Address", "0xWWWWWWWW");
51 		getlab(tile, "Tile", "1023");
52 		getlab(flip, "Flip", "HV");
53 		getlab(palette, "Palette", "---");
54 		Fit();
55 		selx = sely = -1;
56 		Update();
57 	}
Update()58 	void Update()
59 	{
60 		mode = DISPCNT & 7;
61 
62 		switch (bg)
63 		{
64 		case 0:
65 			control = BG0CNT;
66 			break;
67 
68 		case 1:
69 			control = BG1CNT;
70 			break;
71 
72 		case 2:
73 			control = BG2CNT;
74 			break;
75 
76 		case 3:
77 			control = BG3CNT;
78 			break;
79 		}
80 
81 		bool fr0en = true, fr1en = true, bg0en = true, bg1en = true,
82 		     bg2en = true, bg3en = true;
83 
84 		switch (mode)
85 		{
86 		case 0:
87 			fr0en = fr1en = false;
88 			renderTextScreen();
89 			break;
90 
91 		case 1:
92 			fr0en = fr1en = false;
93 			bg3en = false;
94 
95 			if (bg == 3)
96 			{
97 				bg = 0;
98 				control = BG0CNT;
99 				bg0->SetValue(true);
100 			}
101 
102 			if (bg < 2)
103 				renderTextScreen();
104 			else
105 				renderRotScreen();
106 
107 			break;
108 
109 		case 2:
110 			fr0en = fr1en = false;
111 			bg0en = bg1en = false;
112 
113 			if (bg < 2)
114 			{
115 				bg = 2;
116 				control = BG2CNT;
117 				bg2->SetValue(true);
118 			}
119 
120 			renderRotScreen();
121 			break;
122 
123 		case 3:
124 			fr0en = fr1en = false;
125 			bg0en = bg1en = bg2en = bg3en = false;
126 			bg = 2;
127 			bg2->SetValue(true);
128 			renderMode3();
129 			break;
130 
131 		case 4:
132 			bg0en = bg1en = bg2en = bg3en = false;
133 			bg = 2;
134 			bg2->SetValue(true);
135 			renderMode4();
136 			break;
137 
138 		case 5:
139 		case 6:
140 		case 7:
141 			bg = 2;
142 			bg2->SetValue(true);
143 			renderMode5();
144 			break;
145 		}
146 
147 		ChangeBMP();
148 		fr0->Enable(fr0en);
149 		fr1->Enable(fr1en);
150 		bg0->Enable(bg0en);
151 		bg1->Enable(bg1en);
152 		bg2->Enable(bg2en);
153 		bg3->Enable(bg3en);
154 		wxString s;
155 		s.Printf(wxT("%d"), (int)mode);
156 		modelab->SetLabel(s);
157 
158 		if (mode >= 3)
159 		{
160 			mapbase->SetLabel(wxEmptyString);
161 			charbase->SetLabel(wxEmptyString);
162 		}
163 		else
164 		{
165 			s.Printf(wxT("0x%08X"), ((control >> 8) & 0x1f) *  0x800 + 0x6000000);
166 			mapbase->SetLabel(s);
167 			s.Printf(wxT("0x%08X"), ((control >> 2) & 0x03) * 0x4000 + 0x6000000);
168 			charbase->SetLabel(s);
169 		}
170 
171 		s.Printf(wxT("%dx%d"), gv->bmw, gv->bmh);
172 		size->SetLabel(s);
173 		colors->SetLabel(control & 0x80 ? wxT("256") : wxT("16"));
174 		s.Printf(wxT("%d"), control & 3);
175 		prio->SetLabel(s);
176 		mosaic->SetLabel(control & 0x40 ? wxT("1") : wxT("0"));
177 		overflow->SetLabel(bg <= 1 ? wxEmptyString :
178 		                   control & 0x2000 ? wxT("1") : wxT("0"));
179 		UpdateMouseInfo();
180 	}
181 
UpdateMouseInfoEv(wxMouseEvent & ev)182 	void UpdateMouseInfoEv(wxMouseEvent &ev)
183 	{
184 		selx = ev.GetX();
185 		sely = ev.GetY();
186 		UpdateMouseInfo();  // note that this will be inaccurate if game
187 		// not paused since last refresh
188 	}
189 
AddressFromSel()190 	u32 AddressFromSel()
191 	{
192 		u32 base = ((control >> 8) & 0x1f) * 0x800 + 0x6000000;
193 
194 		// all text bgs (16 bits)
195 		if (mode == 0 || (mode < 3 && bg < 2) || mode == 6 || mode == 7)
196 		{
197 			if (sely > 255)
198 			{
199 				base += 0x800;
200 
201 				if (gv->bmw > 256)
202 					base += 0x800;
203 			}
204 
205 			if (selx >= 256)
206 				base += 0x800;
207 
208 			return base + ((selx & 0xff) >> 3) * 2 + 64 * ((sely & 0xff) >> 3);
209 		}
210 
211 		// rot bgs (8 bits)
212 		if (mode < 3)
213 			return base + (selx >> 3) + (gv->bmw >> 3) * (sely >> 3);
214 
215 		// mode 3/5 (16 bits)
216 		if (mode != 4)
217 			return 0x6000000 + 0xa000 * frame + (selx + gv->bmw * sely) * 2;
218 
219 		// mode 4 (8 bits)
220 		return 0x6000000 + 0xa000 * frame + selx + gv->bmw * sely;
221 	}
222 
UpdateMouseInfo()223 	void UpdateMouseInfo()
224 	{
225 		if (selx > gv->bmw || sely > gv->bmh)
226 			selx = sely = -1;
227 
228 		if (selx < 0)
229 		{
230 			coords->SetLabel(wxEmptyString);
231 			addr->SetLabel(wxEmptyString);
232 			tile->SetLabel(wxEmptyString);
233 			flip->SetLabel(wxEmptyString);
234 			palette->SetLabel(wxEmptyString);
235 		}
236 		else
237 		{
238 			wxString s;
239 			s.Printf(wxT("(%d,%d)"), selx, sely);
240 			coords->SetLabel(s);
241 			u32 address = AddressFromSel();
242 			s.Printf(wxT("0x%08X"), address);
243 			addr->SetLabel(s);
244 
245 			if (!mode || (mode < 3 || mode > 5) && bg < 2)
246 			{
247 				u16 value = *((u16*)&vram[address - 0x6000000]);
248 				s.Printf(wxT("%d"), value & 1023);
249 				tile->SetLabel(s);
250 				s = value & 1024 ? wxT('H') : wxT('-');
251 				s += value & 2048 ? wxT('V') : wxT('-');
252 				flip->SetLabel(s);
253 
254 				if (control & 0x80)
255 					palette->SetLabel(wxT("---"));
256 				else
257 				{
258 					s.Printf(wxT("%d"), (value >> 12) & 15);
259 					palette->SetLabel(s);
260 				}
261 			}
262 			else
263 			{
264 				tile->SetLabel(wxT("---"));
265 				flip->SetLabel(wxT("--"));
266 				palette->SetLabel(wxT("---"));
267 			}
268 		}
269 	}
270 protected:
271 	u16 control, mode;
272 	int frame, bg;
273 	wxRadioButton* fr0, *fr1, *bg0, *bg1, *bg2, *bg3;
274 	wxControl* modelab, *mapbase, *charbase, *size, *colors, *prio, *mosaic,
275 	           *overflow;
276 	wxControl* coords, *addr, *tile, *flip, *palette;
277 	int selx, sely;
278 
279 	// following routines were copied from win32/MapView.cpp with little
280 	// attempt to read & validate, except:
281 	//    stride = 1024, rgb instead of bgr
282 	// FIXME: probably needs changing for big-endian
283 
renderTextScreen()284 	void renderTextScreen()
285 	{
286 		u16* palette = (u16*)paletteRAM;
287 		u8* charBase = &vram[((control >> 2) & 0x03) * 0x4000];
288 		u16* screenBase = (u16*)&vram[((control >> 8) & 0x1f) * 0x800];
289 		u8* bmp = image.GetData();
290 		int sizeX = 256;
291 		int sizeY = 256;
292 
293 		switch ((control >> 14) & 3)
294 		{
295 		case 0:
296 			break;
297 
298 		case 1:
299 			sizeX = 512;
300 			break;
301 
302 		case 2:
303 			sizeY = 512;
304 			break;
305 
306 		case 3:
307 			sizeX = 512;
308 			sizeY = 512;
309 			break;
310 		}
311 
312 		BMPSize(sizeX, sizeY);
313 
314 		if (control & 0x80)
315 		{
316 			for (int y = 0; y < sizeY; y++)
317 			{
318 				int yy = y & 255;
319 
320 				if (y == 256 && sizeY > 256)
321 				{
322 					screenBase += 0x400;
323 
324 					if (sizeX > 256)
325 						screenBase += 0x400;
326 				}
327 
328 				u16* screenSource = screenBase + ((yy >> 3) * 32);
329 
330 				for (int x = 0; x < sizeX; x++)
331 				{
332 					u16 data = *screenSource;
333 					int tile = data & 0x3FF;
334 					int tileX = (x & 7);
335 					int tileY = y & 7;
336 
337 					if (data & 0x0400)
338 						tileX = 7 - tileX;
339 
340 					if (data & 0x0800)
341 						tileY = 7 - tileY;
342 
343 					u8 c = charBase[tile * 64 + tileY * 8 + tileX];
344 					u16 color = palette[c];
345 					*bmp++ = (color & 0x1f) << 3;
346 					*bmp++ = ((color >> 5) & 0x1f) << 3;
347 					*bmp++ = ((color >> 10) & 0x1f) << 3;
348 
349 					if (data & 0x0400)
350 					{
351 						if (tileX == 0)
352 							screenSource++;
353 					}
354 					else if (tileX == 7)
355 						screenSource++;
356 
357 					if (x == 255 && sizeX > 256)
358 					{
359 						screenSource = screenBase + 0x400 + ((yy >> 3) * 32);
360 					}
361 				}
362 
363 				bmp += 3 * (1024 - sizeX);
364 			}
365 		}
366 		else
367 		{
368 			for (int y = 0; y < sizeY; y++)
369 			{
370 				int yy = y & 255;
371 
372 				if (y == 256 && sizeY > 256)
373 				{
374 					screenBase += 0x400;
375 
376 					if (sizeX > 256)
377 						screenBase += 0x400;
378 				}
379 
380 				u16* screenSource = screenBase + ((yy >> 3) * 32);
381 
382 				for (int x = 0; x < sizeX; x++)
383 				{
384 					u16 data = *screenSource;
385 					int tile = data & 0x3FF;
386 					int tileX = (x & 7);
387 					int tileY = y & 7;
388 
389 					if (data & 0x0400)
390 						tileX = 7 - tileX;
391 
392 					if (data & 0x0800)
393 						tileY = 7 - tileY;
394 
395 					u8 color = charBase[tile * 32 + tileY * 4 + (tileX >> 1)];
396 
397 					if (tileX & 1)
398 					{
399 						color = (color >> 4);
400 					}
401 					else
402 					{
403 						color &= 0x0F;
404 					}
405 
406 					int pal = (*screenSource >> 8) & 0xF0;
407 					u16 color2 = palette[pal + color];
408 					*bmp++ = (color2 & 0x1f) << 3;
409 					*bmp++ = ((color2 >> 5) & 0x1f) << 3;
410 					*bmp++ = ((color2 >> 10) & 0x1f) << 3;
411 
412 					if (data & 0x0400)
413 					{
414 						if (tileX == 0)
415 							screenSource++;
416 					}
417 					else if (tileX == 7)
418 						screenSource++;
419 
420 					if (x == 255 && sizeX > 256)
421 					{
422 						screenSource = screenBase + 0x400 + ((yy >> 3) * 32);
423 					}
424 				}
425 
426 				bmp += 3 * (1024 - sizeX);
427 			}
428 		}
429 
430 #if 0
431 
432 		switch (bg)
433 		{
434 		case 0:
435 			renderView(BG0HOFS << 8, BG0VOFS << 8,
436 			           0x100, 0x000,
437 			           0x000, 0x100,
438 			           (sizeX - 1) << 8,
439 			           (sizeY - 1) << 8,
440 			           true);
441 			break;
442 
443 		case 1:
444 			renderView(BG1HOFS << 8, BG1VOFS << 8,
445 			           0x100, 0x000,
446 			           0x000, 0x100,
447 			           (sizeX - 1) << 8,
448 			           (sizeY - 1) << 8,
449 			           true);
450 			break;
451 
452 		case 2:
453 			renderView(BG2HOFS << 8, BG2VOFS << 8,
454 			           0x100, 0x000,
455 			           0x000, 0x100,
456 			           (sizeX - 1) << 8,
457 			           (sizeY - 1) << 8,
458 			           true);
459 			break;
460 
461 		case 3:
462 			renderView(BG3HOFS << 8, BG3VOFS << 8,
463 			           0x100, 0x000,
464 			           0x000, 0x100,
465 			           (sizeX - 1) << 8,
466 			           (sizeY - 1) << 8,
467 			           true);
468 			break;
469 		}
470 
471 #endif
472 	}
473 
renderRotScreen()474 	void renderRotScreen()
475 	{
476 		u16* palette = (u16*)paletteRAM;
477 		u8* charBase = &vram[((control >> 2) & 0x03) * 0x4000];
478 		u8* screenBase = (u8*)&vram[((control >> 8) & 0x1f) * 0x800];
479 		u8* bmp = image.GetData();
480 		int sizeX = 128;
481 		int sizeY = 128;
482 
483 		switch ((control >> 14) & 3)
484 		{
485 		case 0:
486 			break;
487 
488 		case 1:
489 			sizeX = sizeY = 256;
490 			break;
491 
492 		case 2:
493 			sizeX = sizeY = 512;
494 			break;
495 
496 		case 3:
497 			sizeX = sizeY = 1024;
498 			break;
499 		}
500 
501 		BMPSize(sizeX, sizeY);
502 
503 		if (control & 0x80)
504 		{
505 			for (int y = 0; y < sizeY; y++)
506 			{
507 				for (int x = 0; x < sizeX; x++)
508 				{
509 					int tile = screenBase[(x >> 3) + (y >> 3) * (sizeX >> 3)];
510 					int tileX = (x & 7);
511 					int tileY = y & 7;
512 					u8 color = charBase[tile * 64 + tileY * 8 + tileX];
513 					u16 color2 = palette[color];
514 					*bmp++ = (color2 & 0x1f) << 3;
515 					*bmp++ = ((color2 >> 5) & 0x1f) << 3;
516 					*bmp++ = ((color2 >> 10) & 0x1f) << 3;
517 				}
518 			}
519 
520 			bmp += 3 * (1024 - sizeX);
521 		}
522 		else
523 		{
524 			for (int y = 0; y < sizeY; y++)
525 			{
526 				for (int x = 0; x < sizeX; x++)
527 				{
528 					int tile = screenBase[(x >> 3) + (y >> 3) * (sizeX >> 3)];
529 					int tileX = (x & 7);
530 					int tileY = y & 7;
531 					u8 color = charBase[tile * 64 + tileY * 8 + tileX];
532 					u16 color2 = palette[color];
533 					*bmp++ = (color2 & 0x1f) << 3;
534 					*bmp++ = ((color2 >> 5) & 0x1f) << 3;
535 					*bmp++ = ((color2 >> 10) & 0x1f) << 3;
536 				}
537 			}
538 
539 			bmp += 3 * (1024 - sizeX);
540 		}
541 
542 		u32 xx;
543 		u32 yy;
544 
545 		switch (bg)
546 		{
547 		case 2:
548 			xx = BG2X_L | BG2X_H << 16;
549 			yy = BG2Y_L | BG2Y_H << 16;
550 #if 0
551 			renderView(xx, yy,
552 			           BG2PA, BG2PC,
553 			           BG2PB, BG2PD,
554 			           (sizeX - 1) << 8,
555 			           (sizeY - 1) << 8,
556 			           (control & 0x2000) != 0);
557 #endif
558 			break;
559 
560 		case 3:
561 			xx = BG3X_L | BG3X_H << 16;
562 			yy = BG3Y_L | BG3Y_H << 16;
563 #if 0
564 			renderView(xx, yy,
565 			           BG3PA, BG3PC,
566 			           BG3PB, BG3PD,
567 			           (sizeX - 1) << 8,
568 			           (sizeY - 1) << 8,
569 			           (control & 0x2000) != 0);
570 #endif
571 			break;
572 		}
573 	}
574 
renderMode3()575 	void renderMode3()
576 	{
577 		u8* bmp = image.GetData();
578 		u16* src = (u16*)&vram[0];
579 		BMPSize(240, 160);
580 
581 		for (int y = 0; y < 160; y++)
582 		{
583 			for (int x = 0; x < 240; x++)
584 			{
585 				u16 data = *src++;
586 				*bmp++ = (data & 0x1f) << 3;
587 				*bmp++ = ((data >> 5) & 0x1f) << 3;
588 				*bmp++ = ((data >> 10) & 0x1f) << 3;
589 			}
590 
591 			bmp += 3 * (1024 - 240);
592 		}
593 	}
594 
595 
renderMode4()596 	void renderMode4()
597 	{
598 		u8* bmp = image.GetData();
599 		u8* src = frame ? &vram[0xa000] : &vram[0];
600 		u16* pal = (u16*)&paletteRAM[0];
601 		BMPSize(240, 160);
602 
603 		for (int y = 0; y < 160; y++)
604 		{
605 			for (int x = 0; x < 240; x++)
606 			{
607 				u8 c = *src++;
608 				u16 data = pal[c];
609 				*bmp++ = (data & 0x1f) << 3;
610 				*bmp++ = ((data >> 5) & 0x1f) << 3;
611 				*bmp++ = ((data >> 10) & 0x1f) << 3;
612 			}
613 
614 			bmp += 3 * (1024 - 240);
615 		}
616 	}
617 
618 
renderMode5()619 	void renderMode5()
620 	{
621 		u8* bmp = image.GetData();
622 		u16* src = (u16*)(frame ? &vram[0xa000] : &vram[0]);
623 		BMPSize(160, 128);
624 
625 		for (int y = 0; y < 128; y++)
626 		{
627 			for (int x = 0; x < 160; x++)
628 			{
629 				u16 data = *src++;
630 				*bmp++ = (data & 0x1f) << 3;
631 				*bmp++ = ((data >> 5) & 0x1f) << 3;
632 				*bmp++ = ((data >> 10) & 0x1f) << 3;
633 			}
634 
635 			bmp += 3 * (1024 - 160);
636 		}
637 	}
638 
639 	DECLARE_EVENT_TABLE()
640 };
641 
642 BEGIN_EVENT_TABLE(MapViewer, GfxViewer)
643 	EVT_GFX_CLICK(wxID_ANY, MapViewer::UpdateMouseInfoEv)
644 END_EVENT_TABLE()
645 
646 class GBMapViewer : public GfxViewer
647 {
648 public:
GBMapViewer()649 	GBMapViewer() : GfxViewer(wxT("GBMapViewer"), 256, 256)
650 	{
651 		getradio(, "CharBase0", charbase, 0x0000);
652 		getradio(, "CharBase1", charbase, 0x0800);
653 		getradio(, "MapBase0", mapbase, 0x1800);
654 		getradio(, "MapBase1", mapbase, 0x1c00);
655 		getlab(coords, "Coords", "(2WW,2WW)");
656 		getlab(addr, "Address", "0xWWWW");
657 		getlab(tile, "Tile", "2WW");
658 		getlab(flip, "Flip", "HV");
659 		getlab(palette, "Palette", "---");
660 		getlab(prio, "Priority", "P");
661 		Fit();
662 		selx = sely = -1;
663 		Update();
664 	}
Update()665 	void Update()
666 	{
667 		u8* bank0, *bank1;
668 
669 		if (gbCgbMode)
670 		{
671 			bank0 = &gbVram[0x0000];
672 			bank1 = &gbVram[0x2000];
673 		}
674 		else
675 		{
676 			bank0 = &gbMemory[0x8000];
677 			bank1 = NULL;
678 		}
679 
680 		int tile_map_address = mapbase;
681 		// following copied almost verbatim from win32/GBMapView.cpp
682 		int tile = 0;
683 
684 		for (int y = 0; y < 32; y++)
685 		{
686 			for (int x = 0; x < 32; x++)
687 			{
688 				u8* bmp = &image.GetData()[y * 8 * 32 * 24 + x * 24];
689 				u8 attrs = 0;
690 
691 				if (bank1 != NULL)
692 					attrs = bank1[tile_map_address];
693 
694 				u8 tile = bank0[tile_map_address];
695 				tile_map_address++;
696 
697 				if (charbase)
698 				{
699 					if (tile < 128) tile += 128;
700 					else tile -= 128;
701 				}
702 
703 				for (int j = 0; j < 8; j++)
704 				{
705 					int charbase_address = attrs & 0x40 ?
706 					                       charbase + tile * 16 + (7 - j) * 2 :
707 					                       charbase + tile * 16 + j * 2;
708 					u8 tile_a = 0;
709 					u8 tile_b = 0;
710 
711 					if (attrs & 0x08)
712 					{
713 						tile_a = bank1[charbase_address++];
714 						tile_b = bank1[charbase_address];
715 					}
716 					else
717 					{
718 						tile_a = bank0[charbase_address++];
719 						tile_b = bank0[charbase_address];
720 					}
721 
722 					if (attrs & 0x20)
723 					{
724 						tile_a = gbInvertTab[tile_a];
725 						tile_b = gbInvertTab[tile_b];
726 					}
727 
728 					u8 mask = 0x80;
729 
730 					while (mask > 0)
731 					{
732 						u8 c = (tile_a & mask) ? 1 : 0;
733 						c += (tile_b & mask) ? 2 : 0;
734 
735 						if (gbCgbMode)
736 							c = c + (attrs & 7) * 4;
737 
738 						u16 color = gbPalette[c];
739 						*bmp++ = (color & 0x1f) << 3;
740 						*bmp++ = ((color >> 5) & 0x1f) << 3;
741 						*bmp++ = ((color >> 10) & 0x1f) << 3;
742 						mask >>= 1;
743 					}
744 
745 					bmp += 31 * 24;
746 				}
747 			}
748 		}
749 
750 		ChangeBMP();
751 		UpdateMouseInfo();
752 	}
753 
UpdateMouseInfoEv(wxMouseEvent & ev)754 	void UpdateMouseInfoEv(wxMouseEvent &ev)
755 	{
756 		selx = ev.GetX();
757 		sely = ev.GetY();
758 		UpdateMouseInfo();  // note that this will be inaccurate if game
759 		// not paused since last refresh
760 	}
761 
UpdateMouseInfo()762 	void UpdateMouseInfo()
763 	{
764 		if (selx > gv->bmw || sely > gv->bmh)
765 			selx = sely = -1;
766 
767 		if (selx < 0)
768 		{
769 			coords->SetLabel(wxEmptyString);
770 			addr->SetLabel(wxEmptyString);
771 			tile->SetLabel(wxEmptyString);
772 			flip->SetLabel(wxEmptyString);
773 			palette->SetLabel(wxEmptyString);
774 			prio->SetLabel(wxEmptyString);
775 		}
776 		else
777 		{
778 			wxString s;
779 			s.Printf(wxT("(%d,%d)"), selx, sely);
780 			coords->SetLabel(s);
781 			u16 address = mapbase + 0x8000 + (sely >> 3) * 32 + (selx >> 3);
782 			s.Printf(wxT("0x%04X"), address);
783 			addr->SetLabel(s);
784 			u8 attrs = 0;
785 			u8 tilev = gbMemoryMap[9][address & 0xfff];
786 
787 			if (gbCgbMode)
788 			{
789 				attrs = gbVram[0x2000 + address - 0x8000];
790 				tilev = gbVram[address & 0x1fff];
791 			}
792 
793 			if (charbase)
794 			{
795 				if (tilev >= 128)
796 					tilev -= 128;
797 				else
798 					tilev += 128;
799 			}
800 
801 			s.Printf(wxT("%d"), (int)tilev);
802 			tile->SetLabel(s);
803 			s = attrs & 0x20 ? wxT('H') : wxT('-');
804 			s += attrs & 0x40 ? wxT('V') : wxT('-');
805 			flip->SetLabel(s);
806 
807 			if (gbCgbMode)
808 			{
809 				s.Printf(wxT("%d"), attrs & 7);
810 				palette->SetLabel(s);
811 			}
812 			else
813 				palette->SetLabel(wxT("---"));
814 
815 			prio->SetLabel(wxString(attrs & 0x80 ? wxT('P') : wxT('-')));
816 		}
817 	}
818 protected:
819 	int charbase, mapbase;
820 	wxControl* coords, *addr, *tile, *flip, *palette, *prio;
821 	int selx, sely;
822 
823 	DECLARE_EVENT_TABLE()
824 };
825 
826 BEGIN_EVENT_TABLE(GBMapViewer, GfxViewer)
827 	EVT_GFX_CLICK(wxID_ANY, GBMapViewer::UpdateMouseInfoEv)
828 END_EVENT_TABLE()
829 }
830 
MapViewer()831 void MainFrame::MapViewer()
832 {
833 	switch (panel->game_type())
834 	{
835 	case IMAGE_GBA:
836 		LoadXRCViewer(Map);
837 		break;
838 
839 	case IMAGE_GB:
840 		LoadXRCViewer(GBMap);
841 		break;
842 	}
843 }
844 
845 namespace Viewers
846 {
847 class OAMViewer : public GfxViewer
848 {
849 public:
OAMViewer()850 	OAMViewer() : GfxViewer(wxT("OAMViewer"), 544, 496)
851 	{
852 		sprite = 0;
853 		getspin(, "Sprite", sprite);
854 		getlab(pos, "Pos", "5WW,2WW");
855 		getlab(mode, "Mode", "3");
856 		getlab(colors, "Colors", "256");
857 		getlab(pallab, "Palette", "1W");
858 		getlab(tile, "Tile", "1WWW");
859 		getlab(prio, "Priority", "3");
860 		getlab(size, "Size", "64x64");
861 		getlab(rot, "Rotation", "3W");
862 		getlab(flg, "Flags", "RHVMD");
863 		Fit();
864 		Update();
865 	}
Update()866 	void Update()
867 	{
868 		BMPSize(544, 496);
869 		wxImage screen(240, 160);
870 		systemRedShift = 19;
871 		systemGreenShift = 11;
872 		systemBlueShift = 3;
873 		utilReadScreenPixels(screen.GetData(), 240, 160);
874 		systemRedShift = 3;
875 		systemGreenShift = 11;
876 		systemBlueShift = 19;
877 
878 		for (int sprite_no = 0; sprite_no < 128; sprite_no++)
879 		{
880 			u16* sparms = &((u16*)oam)[4 * sprite_no];
881 			u16 a0 = sparms[0], a1 = sparms[1], a2 = sparms[2];
882 			u16* pal = &((u16*)paletteRAM)[0x100];
883 			int sizeX = 8, sizeY = 8;
884 
885 			// following is almost verbatim from OamView.cpp
886 			// shape = (a0 >> 14) & 3;
887 			// size = (a1 >> 14) & 3;
888 			switch (((a0 >> 12) & 0xc) | (a1 >> 14))
889 			{
890 			case 0:
891 				break;
892 
893 			case 1:
894 				sizeX = sizeY = 16;
895 				break;
896 
897 			case 2:
898 				sizeX = sizeY = 32;
899 				break;
900 
901 			case 3:
902 				sizeX = sizeY = 64;
903 				break;
904 
905 			case 4:
906 				sizeX = 16;
907 				break;
908 
909 			case 5:
910 				sizeX = 32;
911 				break;
912 
913 			case 6:
914 				sizeX = 32;
915 				sizeY = 16;
916 				break;
917 
918 			case 7:
919 				sizeX = 64;
920 				sizeY = 32;
921 				break;
922 
923 			case 8:
924 				sizeY = 16;
925 				break;
926 
927 			case 9:
928 				sizeY = 32;
929 				break;
930 
931 			case 10:
932 				sizeX = 16;
933 				sizeY = 32;
934 				break;
935 
936 			case 11:
937 				sizeX = 32;
938 				sizeY = 64;
939 				break;
940 
941 			default:
942 				pos->SetLabel(wxEmptyString);
943 				mode->SetLabel(wxEmptyString);
944 				colors->SetLabel(wxEmptyString);
945 				pallab->SetLabel(wxEmptyString);
946 				tile->SetLabel(wxEmptyString);
947 				prio->SetLabel(wxEmptyString);
948 				size->SetLabel(wxEmptyString);
949 				rot->SetLabel(wxEmptyString);
950 				flg->SetLabel(wxEmptyString);
951 				continue;
952 			}
953 
954 			wxImage spriteData(64, 64);
955 			u8* bmp = spriteData.GetData();
956 			int sy = (a0 & 255);
957 
958 			if (a0 & 0x2000)
959 			{
960 				int c = (a2 & 0x3FF);
961 				//if((DISPCNT & 7) > 2 && (c < 512))
962 				//    return;
963 				int inc = 32;
964 
965 				if (DISPCNT & 0x40)
966 					inc = sizeX >> 2;
967 				else
968 					c &= 0x3FE;
969 
970 				for (int y = 0; y < sizeY; y++)
971 				{
972 					for (int x = 0; x < sizeX; x++)
973 					{
974 						u32 color = vram[0x10000 + (((c + (y >> 3) * inc) *
975 						                             32 + (y & 7) * 8 + (x >> 3) * 64 +
976 						                             (x & 7)) & 0x7FFF)];
977 						color = pal[color];
978 						*bmp++ = (color & 0x1f) << 3;
979 						*bmp++ = ((color >> 5) & 0x1f) << 3;
980 						*bmp++ = ((color >> 10) & 0x1f) << 3;
981 					}
982 
983 					bmp += (64 - sizeX) * 3;
984 				}
985 			}
986 			else
987 			{
988 				int c = (a2 & 0x3FF);
989 				//if((DISPCNT & 7) > 2 && (c < 512))
990 				//    return;
991 				int inc = 32;
992 
993 				if (DISPCNT & 0x40)
994 					inc = sizeX >> 3;
995 
996 				int palette = (a2 >> 8) & 0xF0;
997 
998 				for (int y = 0; y < sizeY; y++)
999 				{
1000 					for (int x = 0; x < sizeX; x++)
1001 					{
1002 						u32 color = vram[0x10000 + (((c + (y >> 3) * inc) *
1003 						                             32 + (y & 7) * 4 + (x >> 3) * 32 +
1004 						                             ((x & 7) >> 1)) & 0x7FFF)];
1005 
1006 						if (x & 1)
1007 							color >>= 4;
1008 						else
1009 							color &= 0x0F;
1010 
1011 						color = pal[palette + color];
1012 						*bmp++ = (color & 0x1f) << 3;
1013 						*bmp++ = ((color >> 5) & 0x1f) << 3;
1014 						*bmp++ = ((color >> 10) & 0x1f) << 3;
1015 					}
1016 
1017 					bmp += (64 - sizeX) * 3;
1018 				}
1019 			}
1020 
1021 			if (sprite == sprite_no)
1022 			{
1023 				wxString s;
1024 				s.Printf(wxT("%d,%d"), a1 & 511, a0 & 255);
1025 				pos->SetLabel(s);
1026 				s.Printf(wxT("%d"), (a0 >> 10) & 3);
1027 				mode->SetLabel(s);
1028 				colors->SetLabel(a0 & 8192 ? wxT("256") : wxT("16"));
1029 				s.Printf(wxT("%d"), (a2 >> 12) & 15);
1030 				pallab->SetLabel(s);
1031 				s.Printf(wxT("%d"), a2 & 1023);
1032 				tile->SetLabel(s);
1033 				s.Printf(wxT("%d"), (a2 >> 10) & 3);
1034 				prio->SetLabel(s);
1035 				s.Printf(wxT("%dx%d"), sizeX, sizeY);
1036 				s.Printf(wxT("%dx%d"), 0, 0);
1037 				size->SetLabel(s);
1038 
1039 				if (a0 & 512)
1040 				{
1041 					s.Printf(wxT("%d"), (a1 >> 9) & 31);
1042 					rot->SetLabel(s);
1043 				}
1044 				else
1045 					rot->SetLabel(wxEmptyString);
1046 
1047 				s = wxEmptyString;
1048 
1049 				if (a0 & 512)
1050 					s.append(wxT("R--"));
1051 				else
1052 				{
1053 					s.append(wxT('-'));
1054 					s.append(a1 & 4096 ? wxT('H') : wxT('-'));
1055 					s.append(a1 & 8192 ? wxT('V') : wxT('-'));
1056 				}
1057 
1058 				s.append(a0 & 4096 ? wxT('M') : wxT('-'));
1059 				s.append(a0 & 1024 ? wxT('D') : wxT('-'));
1060 				flg->SetLabel(s);
1061 				u8* box = spriteData.GetData();
1062 				int sprite_posx = a1 & 511;
1063 				int sprite_posy = a0 & 255;
1064 				u8* screen_box = screen.GetData();
1065 
1066 				if (sprite_posx >= 0 && sprite_posx <= (239 - sizeY) && sprite_posy >= 0 && sprite_posy <= (159 - sizeX))
1067 					screen_box += (sprite_posx * 3) + (sprite_posy * screen.GetWidth() * 3);
1068 
1069 				for (int y = 0; y < sizeY; y++)
1070 				{
1071 					for (int x = 0; x < sizeX; x++)
1072 					{
1073 						u32 color = 0;
1074 
1075 						if (y == 0 || y == sizeY - 1 || x == 0 || x == sizeX - 1)
1076 						{
1077 							color = 255;
1078 							*box++ = (color & 0x1f) << 3;
1079 							*box++ = ((color >> 5) & 0x1f) << 3;
1080 							*box++ = ((color >> 10) & 0x1f) << 3;
1081 
1082 							if (sprite_posx >= 0 && sprite_posx <= (239 - sizeY) && sprite_posy >= 0 && sprite_posy <= (159 - sizeX))
1083 							{
1084 								*screen_box++ = (color & 0x1f) << 3;
1085 								*screen_box++ = ((color >> 5) & 0x1f) << 3;
1086 								*screen_box++ = ((color >> 10) & 0x1f) << 3;
1087 							}
1088 						}
1089 						else
1090 						{
1091 							box += 3;
1092 
1093 							if (sprite_posx >= 0 && sprite_posx <= (239 - sizeY) && sprite_posy >= 0 && sprite_posy <= (159 - sizeX))
1094 								screen_box += 3;
1095 						}
1096 					}
1097 
1098 					box += (spriteData.GetWidth() - sizeX) * 3;
1099 
1100 					if (sprite_posx >= 0 && sprite_posx <= (239 - sizeY) && sprite_posy >= 0 && sprite_posy <= (159 - sizeX))
1101 						screen_box += (screen.GetWidth() - sizeX) * 3;
1102 				}
1103 			}
1104 
1105 			image.Paste(spriteData, (sprite_no % 16) * 34, (sprite_no / 16) * 34);
1106 		}
1107 
1108 		image.Paste(screen, 0, 304);
1109 		ChangeBMP();
1110 	}
1111 protected:
1112 	int sprite;
1113 	wxControl* pos, *mode, *colors, *pallab, *tile, *prio, *size, *rot, *flg;
1114 };
1115 
1116 class GBOAMViewer : public GfxViewer
1117 {
1118 public:
GBOAMViewer()1119 	GBOAMViewer() : GfxViewer(wxT("GBOAMViewer"), 8, 16)
1120 	{
1121 		sprite = 0;
1122 		getspin(, "Sprite", sprite);
1123 		getlab(pos, "Pos", "2WW,2WW");
1124 		getlab(tilelab, "Tile", "2WW");
1125 		getlab(prio, "Priority", "W");
1126 		getlab(oap, "OAP", "W");
1127 		getlab(pallab, "Palette", "W");
1128 		getlab(flg, "Flags", "HV");
1129 		getlab(banklab, "Bank", "W");
1130 		Fit();
1131 		Update();
1132 	}
Update()1133 	void Update()
1134 	{
1135 		u8* bmp = image.GetData();
1136 		// following is almost verbatim from GBOamView.cpp
1137 		u16 addr = sprite * 4 + 0xfe00;
1138 		int size = register_LCDC & 4;
1139 		u8 y = gbMemory[addr++];
1140 		u8 x = gbMemory[addr++];
1141 		u8 tile = gbMemory[addr++];
1142 
1143 		if (size)
1144 			tile &= 254;
1145 
1146 		u8 flags = gbMemory[addr++];
1147 		int w = 8;
1148 		int h = size ? 16 : 8;
1149 		BMPSize(w, h);
1150 		u8* bank0;
1151 		u8* bank1;
1152 
1153 		if (gbCgbMode)
1154 		{
1155 			if (register_VBK & 1)
1156 			{
1157 				bank0 = &gbVram[0x0000];
1158 				bank1 = &gbVram[0x2000];
1159 			}
1160 			else
1161 			{
1162 				bank0 = &gbVram[0x0000];
1163 				bank1 = &gbVram[0x2000];
1164 			}
1165 		}
1166 		else
1167 		{
1168 			bank0 = &gbMemory[0x8000];
1169 			bank1 = NULL;
1170 		}
1171 
1172 		int init = 0x0000;
1173 		u8* pal = gbObp0;
1174 
1175 		if ((flags & 0x10))
1176 			pal = gbObp1;
1177 
1178 		for (int yy = 0; yy < h; yy++)
1179 		{
1180 			int address = init + tile * 16 + 2 * yy;
1181 			int a = 0;
1182 			int b = 0;
1183 
1184 			if (gbCgbMode && flags & 0x08)
1185 			{
1186 				a = bank1[address++];
1187 				b = bank1[address++];
1188 			}
1189 			else
1190 			{
1191 				a = bank0[address++];
1192 				b = bank0[address++];
1193 			}
1194 
1195 			for (int xx = 0; xx < 8; xx++)
1196 			{
1197 				u8 mask = 1 << (7 - xx);
1198 				u8 c = 0;
1199 
1200 				if ((a & mask))
1201 					c++;
1202 
1203 				if ((b & mask))
1204 					c += 2;
1205 
1206 				// make sure that sprites will work even in CGB mode
1207 				if (gbCgbMode)
1208 				{
1209 					c = c + (flags & 0x07) * 4 + 32;
1210 				}
1211 				else
1212 				{
1213 					c = pal[c];
1214 				}
1215 
1216 				u16 color = gbPalette[c];
1217 				*bmp++ = (color & 0x1f) << 3;
1218 				*bmp++ = ((color >> 5) & 0x1f) << 3;
1219 				*bmp++ = ((color >> 10) & 0x1f) << 3;
1220 			}
1221 		}
1222 
1223 		ChangeBMP();
1224 		wxString s;
1225 		s.Printf(wxT("%d,%d"), x, y);
1226 		pos->SetLabel(s);
1227 		s.Printf(wxT("%d"), tile);
1228 		tilelab->SetLabel(s);
1229 		prio->SetLabel(flags & 0x80 ? wxT("1") : wxT("0"));
1230 		oap->SetLabel(flags & 0x08 ? wxT("1") : wxT("0"));
1231 		s.Printf(wxT("%d"), flags & 7);
1232 		pallab->SetLabel(s);
1233 		s = flags & 0x20 ? wxT('H') : wxT('-');
1234 		s.append(flags & 0x40 ? wxT('V') : wxT('-'));
1235 		flg->SetLabel(s);
1236 		banklab->SetLabel(flags & 0x10 ? wxT("1") : wxT("0"));
1237 	}
1238 protected:
1239 	int sprite;
1240 	wxControl* pos, *tilelab, *prio, *oap, *pallab, *flg, *banklab;
1241 };
1242 }
1243 
OAMViewer()1244 void MainFrame::OAMViewer()
1245 {
1246 	switch (panel->game_type())
1247 	{
1248 	case IMAGE_GBA:
1249 		LoadXRCViewer(OAM);
1250 		break;
1251 
1252 	case IMAGE_GB:
1253 		LoadXRCViewer(GBOAM);
1254 		break;
1255 	}
1256 }
1257 
1258 namespace Viewers
1259 {
1260 static int ptype = 0;
1261 static wxString pdir;
savepal(wxWindow * parent,const u8 * data,int ncols,const wxChar * type)1262 void savepal(wxWindow* parent, const u8* data, int ncols, const wxChar* type)
1263 {
1264 	// no attempt is made here to translate the palette type name
1265 	// it's just a suggested name, anyway
1266 	wxString def_name = wxGetApp().frame->GetPanel()->game_name() +
1267 	                    wxT('-') + type;
1268 
1269 	if (ptype == 2)
1270 		def_name += wxT(".act");
1271 	else
1272 		def_name += wxT(".pal");
1273 
1274 	wxFileDialog dlg(parent, _("Select output file and type"), pdir, def_name,
1275 	                 _("Windows Palette (*.pal)|*.pal|PaintShop Palette (*.pal)|*.pal|Adobe Color Table (*.act)|*.act"),
1276 	                 wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
1277 	dlg.SetFilterIndex(ptype);
1278 	int ret = dlg.ShowModal();
1279 	ptype = dlg.GetFilterIndex();
1280 	pdir = dlg.GetDirectory();
1281 
1282 	if (ret != wxID_OK)
1283 		return;
1284 
1285 	wxFFile f(dlg.GetPath(), wxT("wb"));
1286 
1287 	// FIXME: check for errors
1288 	switch (ptype)
1289 	{
1290 	case 0: // Windows palette
1291 	{
1292 		f.Write("RIFF", 4);
1293 		u32 d = wxUINT32_SWAP_ON_BE(256 * 4 + 16);
1294 		f.Write(&d, 4);
1295 		f.Write("PAL data", 8);
1296 		d = wxUINT32_SWAP_ON_BE(256 * 4 + 4);
1297 		f.Write(&d, 4);
1298 		u16 w = wxUINT16_SWAP_ON_BE(0x0300);
1299 		f.Write(&w, 2);
1300 		w = wxUINT16_SWAP_ON_BE(256); // cuases problems if not 16 or 256
1301 		f.Write(&w, 2);
1302 
1303 		for (int i = 0; i < ncols; i++, data += 3)
1304 		{
1305 			f.Write(data, 3);
1306 			u8 z = 0;
1307 			f.Write(&z, 1);
1308 		}
1309 
1310 		for (int i = ncols; i < 256; i++)
1311 		{
1312 			d = 0;
1313 			f.Write(&d, 4);
1314 		}
1315 	}
1316 	break;
1317 
1318 	case 1: // PaintShop palette
1319 	{
1320 #define jasc_head "JASC-PAL\r\n0100\r\n256\r\n"
1321 		f.Write(jasc_head, sizeof(jasc_head) - 1);
1322 
1323 		for (int i = 0; i < ncols; i++, data += 3)
1324 		{
1325 			char buf[14];
1326 			int l = sprintf(buf, "%d %d %d\r\n", data[0], data[1], data[2]);
1327 			f.Write(buf, l);
1328 		}
1329 
1330 		for (int i = ncols; i < 256; i++)
1331 			f.Write("0 0 0\r\n", 7);
1332 
1333 		break;
1334 	}
1335 
1336 	case 2: // Adobe color table
1337 	{
1338 		f.Write(data, ncols * 3);
1339 		u32 d = 0;
1340 
1341 		for (int i = ncols; i < 256; i++)
1342 			f.Write(&d, 3);
1343 	}
1344 	break;
1345 	}
1346 
1347 	f.Close(); // FIXME: check for errors
1348 }
1349 
1350 class PaletteViewer : public Viewer
1351 {
1352 public:
PaletteViewer()1353 	PaletteViewer() : Viewer(wxT("PaletteViewer"))
1354 	{
1355 		colorctrl(cv, "Color");
1356 		pixview(bpv, "Background", 16, 16, cv);
1357 		pixview(spv, "Sprite", 16, 16, cv);
1358 		getlab(addr, "Address", "0x5000WWW");
1359 		getlab(val, "Value", "0xWWWW");
1360 		Fit();
1361 		Update();
1362 	}
Update()1363 	void Update()
1364 	{
1365 		if (paletteRAM)
1366 		{
1367 			u16* pp = (u16*)paletteRAM;
1368 			u8* bmp = colbmp;
1369 
1370 			for (int i = 0; i < 512; i++, pp++)
1371 			{
1372 				*bmp++ = (*pp & 0x1f) << 3;
1373 				*bmp++ = (*pp & 0x3e0) >> 2;
1374 				*bmp++ = (*pp & 0x7c00) >> 7;
1375 			}
1376 		}
1377 		else
1378 			memset(colbmp, 0, sizeof(colbmp));
1379 
1380 		bpv->SetData(colbmp, 16, 0, 0);
1381 		spv->SetData(colbmp + 16 * 16 * 3, 16, 0, 0);
1382 		ShowSel();
1383 	}
SelBG(wxMouseEvent & ev)1384 	void SelBG(wxMouseEvent &ev)
1385 	{
1386 		spv->SetSel(-1, -1, false);
1387 		ShowSel();
1388 	}
SelSprite(wxMouseEvent & ev)1389 	void SelSprite(wxMouseEvent &ev)
1390 	{
1391 		bpv->SetSel(-1, -1, false);
1392 		ShowSel();
1393 	}
ShowSel()1394 	void ShowSel()
1395 	{
1396 		int x, y;
1397 		bool isbg = true;
1398 		bpv->GetSel(x, y);
1399 
1400 		if (x < 0)
1401 		{
1402 			isbg = false;
1403 			spv->GetSel(x, y);
1404 
1405 			if (x < 0)
1406 			{
1407 				addr->SetLabel(wxEmptyString);
1408 				val->SetLabel(wxEmptyString);
1409 				return;
1410 			}
1411 		}
1412 
1413 		int off = x + y * 16;
1414 
1415 		if (!isbg)
1416 			off += 16 * 16;
1417 
1418 		u8* pix = &colbmp[off * 3];
1419 		u16 v = (pix[0] >> 3) + ((pix[1] >> 3) << 5) + ((pix[2] >> 3) << 10);
1420 		wxString s;
1421 		s.Printf(wxT("0x%04X"), (int)v);
1422 		val->SetLabel(s);
1423 		s.Printf(wxT("0x%08X"), 0x5000000 + 2 * off);
1424 		addr->SetLabel(s);
1425 	}
SaveBG(wxCommandEvent & ev)1426 	void SaveBG(wxCommandEvent &ev)
1427 	{
1428 		savepal(this, colbmp, 16 * 16, wxT("bg"));
1429 	}
SaveOBJ(wxCommandEvent & ev)1430 	void SaveOBJ(wxCommandEvent &ev)
1431 	{
1432 		savepal(this, colbmp + 16 * 16 * 3, 16 * 16, wxT("obj"));
1433 	}
ChangeBackdrop(wxCommandEvent & ev)1434 	void ChangeBackdrop(wxCommandEvent &ev)
1435 	{
1436 		// FIXME: this should really be a preference
1437 		// should also have some way of indicating selection
1438 		// perhaps replace w/ checkbox + colorpickerctrl
1439 		static wxColourData* cd = NULL;
1440 		wxColourDialog dlg(this, cd);
1441 
1442 		if (dlg.ShowModal() == wxID_OK)
1443 		{
1444 			if (!cd)
1445 				cd = new wxColourData();
1446 
1447 			*cd = dlg.GetColourData();
1448 			wxColour c = cd->GetColour();
1449 			//Binary or the upper 5 bits of each color choice
1450 			customBackdropColor =
1451 			    (c.Red() >> 3) ||
1452 			    ((c.Green() >> 3) << 5) ||
1453 			    ((c.Blue() >> 3) << 10);
1454 		}
1455 		else
1456 			// kind of an unintuitive way to turn it off...
1457 			customBackdropColor = -1;
1458 	}
1459 protected:
1460 	ColorView* cv;
1461 	PixView* bpv, *spv;
1462 	u8 colbmp[16 * 16 * 3 * 2];
1463 	wxControl* addr, *val;
1464 
1465 	DECLARE_EVENT_TABLE()
1466 };
1467 
1468 BEGIN_EVENT_TABLE(PaletteViewer, Viewer)
1469 	EVT_BUTTON(XRCID("SaveBG"), PaletteViewer::SaveBG)
1470 	EVT_BUTTON(XRCID("SaveOBJ"), PaletteViewer::SaveOBJ)
1471 	EVT_BUTTON(XRCID("ChangeBackdrop"), PaletteViewer::ChangeBackdrop)
1472 	EVT_GFX_CLICK(XRCID("Background"), PaletteViewer::SelBG)
1473 	EVT_GFX_CLICK(XRCID("Sprite"), PaletteViewer::SelSprite)
1474 END_EVENT_TABLE()
1475 
1476 class GBPaletteViewer : public Viewer
1477 {
1478 public:
GBPaletteViewer()1479 	GBPaletteViewer() : Viewer(wxT("GBPaletteViewer"))
1480 	{
1481 		colorctrl(cv, "Color");
1482 		pixview(bpv, "Background", 4, 8, cv);
1483 		pixview(spv, "Sprite", 4, 8, cv);
1484 		getlab(idx, "Index", "3W");
1485 		getlab(val, "Value", "0xWWWW");
1486 		Fit();
1487 		Update();
1488 	}
Update()1489 	void Update()
1490 	{
1491 		u16* pp = gbPalette;
1492 		u8* bmp = colbmp;
1493 
1494 		for (int i = 0; i < 64; i++, pp++)
1495 		{
1496 			*bmp++ = (*pp & 0x1f) << 3;
1497 			*bmp++ = (*pp & 0x3e0) >> 2;
1498 			*bmp++ = (*pp & 0x7c00) >> 7;
1499 		}
1500 
1501 		bpv->SetData(colbmp, 4, 0, 0);
1502 		spv->SetData(colbmp + 4 * 8 * 3, 4, 0, 0);
1503 		ShowSel();
1504 	}
SelBG(wxMouseEvent & ev)1505 	void SelBG(wxMouseEvent &ev)
1506 	{
1507 		spv->SetSel(-1, -1, false);
1508 		ShowSel();
1509 	}
SelSprite(wxMouseEvent & ev)1510 	void SelSprite(wxMouseEvent &ev)
1511 	{
1512 		bpv->SetSel(-1, -1, false);
1513 		ShowSel();
1514 	}
ShowSel()1515 	void ShowSel()
1516 	{
1517 		int x, y;
1518 		bool isbg = true;
1519 		bpv->GetSel(x, y);
1520 
1521 		if (x < 0)
1522 		{
1523 			isbg = false;
1524 			spv->GetSel(x, y);
1525 
1526 			if (x < 0)
1527 			{
1528 				idx->SetLabel(wxEmptyString);
1529 				val->SetLabel(wxEmptyString);
1530 				return;
1531 			}
1532 		}
1533 
1534 		u8* pix = &colbmp[(x + y * 4) * 3];
1535 
1536 		if (isbg)
1537 			pix += 4 * 8 * 3;
1538 
1539 		u16 v = (pix[0] >> 3) + ((pix[1] >> 3) << 5) + ((pix[2] >> 3) << 10);
1540 		wxString s;
1541 		s.Printf(wxT("0x%04X"), (int)v);
1542 		val->SetLabel(s);
1543 		s.Printf(wxT("%d"), x + y * 4);
1544 		idx->SetLabel(s);
1545 	}
SaveBG(wxCommandEvent & ev)1546 	void SaveBG(wxCommandEvent &ev)
1547 	{
1548 		savepal(this, colbmp, 4 * 8, wxT("bg"));
1549 	}
SaveOBJ(wxCommandEvent & ev)1550 	void SaveOBJ(wxCommandEvent &ev)
1551 	{
1552 		savepal(this, colbmp + 4 * 8 * 3, 4 * 8, wxT("obj"));
1553 	}
1554 protected:
1555 	ColorView* cv;
1556 	PixView* bpv, *spv;
1557 	u8 colbmp[4 * 8 * 3 * 2];
1558 	wxControl* idx, *val;
1559 	DECLARE_EVENT_TABLE()
1560 };
1561 
1562 BEGIN_EVENT_TABLE(GBPaletteViewer, Viewer)
1563 	EVT_BUTTON(XRCID("SaveBG"), GBPaletteViewer::SaveBG)
1564 	EVT_BUTTON(XRCID("SaveOBJ"), GBPaletteViewer::SaveOBJ)
1565 	EVT_GFX_CLICK(XRCID("Background"), GBPaletteViewer::SelBG)
1566 	EVT_GFX_CLICK(XRCID("Sprite"), GBPaletteViewer::SelSprite)
1567 END_EVENT_TABLE()
1568 }
1569 
PaletteViewer()1570 void MainFrame::PaletteViewer()
1571 {
1572 	switch (panel->game_type())
1573 	{
1574 	case IMAGE_GBA:
1575 		LoadXRCViewer(Palette);
1576 		break;
1577 
1578 	case IMAGE_GB:
1579 		LoadXRCViewer(GBPalette);
1580 		break;
1581 	}
1582 }
1583 
1584 namespace Viewers
1585 {
1586 class TileViewer : public GfxViewer
1587 {
1588 public:
TileViewer()1589 	TileViewer() : GfxViewer(wxT("TileViewer"), 32 * 8, 32 * 8)
1590 	{
1591 		is256 = charbase = 0;
1592 		getradio(, "Color16", is256, 0);
1593 		getradio(, "Color256", is256, 1);
1594 		getradio(, "CharBase0", charbase, 0);
1595 		getradio(, "CharBase1", charbase, 0x4000);
1596 		getradio(, "CharBase2", charbase, 0x8000);
1597 		getradio(, "CharBase3", charbase, 0xc000);
1598 		getradio(, "CharBase4", charbase, 0x10000);
1599 		getslider(, "Palette", palette);
1600 		getlab(tileno, "Tile", "1WWW");
1601 		getlab(addr, "Address", "06WWWWWW");
1602 		selx = sely = -1;
1603 		Fit();
1604 		Update();
1605 	}
Update()1606 	void Update()
1607 	{
1608 		// Following copied almost verbatim from TileView.cpp
1609 		u16* palette = (u16*)paletteRAM;
1610 		u8* charBase = &vram[charbase];
1611 		int maxY;
1612 
1613 		if (is256)
1614 		{
1615 			int tile = 0;
1616 			maxY = 16;
1617 
1618 			for (int y = 0; y < maxY; y++)
1619 			{
1620 				for (int x = 0; x < 32; x++)
1621 				{
1622 					if (charbase == 4 * 0x4000)
1623 						render256(tile, x, y, charBase, &palette[256]);
1624 					else
1625 						render256(tile, x, y, charBase, palette);
1626 
1627 					tile++;
1628 				}
1629 			}
1630 
1631 			BMPSize(32 * 8, maxY * 8);
1632 		}
1633 		else
1634 		{
1635 			int tile = 0;
1636 			maxY = 32;
1637 
1638 			if (charbase == 3 * 0x4000)
1639 				maxY = 16;
1640 
1641 			for (int y = 0; y < maxY; y++)
1642 			{
1643 				for (int x = 0; x < 32; x++)
1644 				{
1645 					render16(tile, x, y, charBase, palette);
1646 					tile++;
1647 				}
1648 			}
1649 
1650 			BMPSize(32 * 8, maxY * 8);
1651 		}
1652 
1653 		ChangeBMP();
1654 		UpdateMouseInfo();
1655 	}
UpdateMouseInfoEv(wxMouseEvent & ev)1656 	void UpdateMouseInfoEv(wxMouseEvent &ev)
1657 	{
1658 		selx = ev.GetX();
1659 		sely = ev.GetY();
1660 		UpdateMouseInfo();
1661 	}
1662 
UpdateMouseInfo()1663 	void UpdateMouseInfo()
1664 	{
1665 		if (selx > gv->bmw || sely > gv->bmh)
1666 			selx = sely = -1;
1667 
1668 		if (selx < 0)
1669 		{
1670 			addr->SetLabel(wxEmptyString);
1671 			tileno->SetLabel(wxEmptyString);
1672 		}
1673 		else
1674 		{
1675 			int x = selx / 8;
1676 			int y = sely / 8;
1677 			int t = 32 * y + x;
1678 
1679 			if (is256)
1680 				t *= 2;
1681 
1682 			wxString s;
1683 			s.Printf(wxT("%d"), t);
1684 			tileno->SetLabel(s);
1685 			s.Printf(wxT("%08X"), 0x6000000 + charbase + 32 * t);
1686 			addr->SetLabel(s);
1687 		}
1688 	}
1689 	// following 2 functions copied almost verbatim from TileView.cpp
render256(int tile,int x,int y,u8 * charBase,u16 * palette)1690 	void render256(int tile, int x, int y, u8* charBase, u16* palette)
1691 	{
1692 		u8* bmp = &image.GetData()[24 * x + 8 * 32 * 24 * y];
1693 
1694 		for (int j = 0; j < 8; j++)
1695 		{
1696 			for (int i = 0; i < 8; i++)
1697 			{
1698 				u8 c = charBase[tile * 64 + j * 8 + i];
1699 				u16 color = palette[c];
1700 				*bmp++ = (color & 0x1f) << 3;
1701 				*bmp++ = ((color >> 5) & 0x1f) << 3;
1702 				*bmp++ = ((color >> 10) & 0x1f) << 3;
1703 			}
1704 
1705 			bmp += 31 * 24; // advance line
1706 		}
1707 	}
1708 
render16(int tile,int x,int y,u8 * charBase,u16 * palette)1709 	void render16(int tile, int x, int y, u8* charBase, u16* palette)
1710 	{
1711 		u8* bmp = &image.GetData()[24 * x + 8 * 32 * 24 * y];
1712 		int pal = this->palette;
1713 
1714 		if (this->charbase == 4 * 0x4000)
1715 			pal += 16;
1716 
1717 		for (int j = 0; j < 8; j++)
1718 		{
1719 			for (int i = 0; i < 8; i++)
1720 			{
1721 				u8 c = charBase[tile * 32 + j * 4 + (i >> 1)];
1722 
1723 				if (i & 1)
1724 					c = c >> 4;
1725 				else
1726 					c = c & 15;
1727 
1728 				u16 color = palette[pal * 16 + c];
1729 				*bmp++ = (color & 0x1f) << 3;
1730 				*bmp++ = ((color >> 5) & 0x1f) << 3;
1731 				*bmp++ = ((color >> 10) & 0x1f) << 3;
1732 			}
1733 
1734 			bmp += 31 * 24; // advance line
1735 		}
1736 	}
1737 
1738 protected:
1739 	int charbase, is256, palette;
1740 	wxControl* tileno, *addr;
1741 	int selx, sely;
1742 
1743 	DECLARE_EVENT_TABLE()
1744 };
1745 
1746 BEGIN_EVENT_TABLE(TileViewer, GfxViewer)
1747 	EVT_GFX_CLICK(wxID_ANY, TileViewer::UpdateMouseInfoEv)
1748 END_EVENT_TABLE()
1749 
1750 class GBTileViewer : public GfxViewer
1751 {
1752 public:
GBTileViewer()1753 	GBTileViewer() : GfxViewer(wxT("GBTileViewer"), 16 * 8, 16 * 8)
1754 	{
1755 		bank = charbase = 0;
1756 		getradio(, "Bank0", bank, 0);
1757 		getradio(, "Bank1", bank, 0x2000);
1758 		getradio(, "CharBase0", charbase, 0);
1759 		getradio(, "CharBase1", charbase, 0x800);
1760 		getslider(, "Palette", palette);
1761 		getlab(tileno, "Tile", "2WW");
1762 		getlab(addr, "Address", "WWWW");
1763 		selx = sely = -1;
1764 		Fit();
1765 		Update();
1766 	}
Update()1767 	void Update()
1768 	{
1769 		// following copied almost verbatim from GBTileView.cpp
1770 		u8* charBase = (gbVram != NULL) ?
1771 		               &gbVram[bank + charbase] :
1772 		               &gbMemory[0x8000 + charbase];
1773 		int tile = 0;
1774 
1775 		for (int y = 0; y < 16; y++)
1776 		{
1777 			for (int x = 0; x < 16; x++)
1778 			{
1779 				render(tile, x, y, charBase);
1780 				tile++;
1781 			}
1782 		}
1783 
1784 		ChangeBMP();
1785 		UpdateMouseInfo();
1786 	}
UpdateMouseInfoEv(wxMouseEvent & ev)1787 	void UpdateMouseInfoEv(wxMouseEvent &ev)
1788 	{
1789 		selx = ev.GetX();
1790 		sely = ev.GetY();
1791 		UpdateMouseInfo();
1792 	}
1793 
UpdateMouseInfo()1794 	void UpdateMouseInfo()
1795 	{
1796 		if (selx > gv->bmw || sely > gv->bmh)
1797 			selx = sely = -1;
1798 
1799 		if (selx < 0)
1800 		{
1801 			addr->SetLabel(wxEmptyString);
1802 			tileno->SetLabel(wxEmptyString);
1803 		}
1804 		else
1805 		{
1806 			int x = selx / 8;
1807 			int y = sely / 8;
1808 			int t = 16 * y + x;
1809 			wxString s;
1810 			s.Printf(wxT("%d"), t);
1811 			tileno->SetLabel(s);
1812 			s.Printf(wxT("%04X"), 0x8000 + charbase + 16 * t);
1813 			addr->SetLabel(s);
1814 		}
1815 	}
1816 
1817 	// following function copied almost verbatim from GBTileView.cpp
render(int tile,int x,int y,u8 * charBase)1818 	void render(int tile, int x, int y, u8* charBase)
1819 	{
1820 		u8* bmp = &image.GetData()[24 * x + 8 * 16 * 24 * y];
1821 
1822 		for (int j = 0; j < 8; j++)
1823 		{
1824 			u8 mask = 0x80;
1825 			u8 tile_a = charBase[tile * 16 + j * 2];
1826 			u8 tile_b = charBase[tile * 16 + j * 2 + 1];
1827 
1828 			for (int i = 0; i < 8; i++)
1829 			{
1830 				u8 c = (tile_a & mask) ? 1 : 0;
1831 				c += ((tile_b & mask) ? 2 : 0);
1832 
1833 				if (gbCgbMode)
1834 				{
1835 					c = c + palette * 4;
1836 				}
1837 				else
1838 				{
1839 					c = gbBgp[c];
1840 				}
1841 
1842 				u16 color = gbPalette[c];
1843 				*bmp++ = (color & 0x1f) << 3;
1844 				*bmp++ = ((color >> 5) & 0x1f) << 3;
1845 				*bmp++ = ((color >> 10) & 0x1f) << 3;
1846 				mask >>= 1;
1847 			}
1848 
1849 			bmp += 15 * 24; // advance line
1850 		}
1851 	}
1852 protected:
1853 	int bank, charbase, palette;
1854 	wxControl* addr, *tileno;
1855 	int selx, sely;
1856 
1857 	DECLARE_EVENT_TABLE()
1858 };
1859 
1860 BEGIN_EVENT_TABLE(GBTileViewer, GfxViewer)
1861 	EVT_GFX_CLICK(wxID_ANY, GBTileViewer::UpdateMouseInfoEv)
1862 END_EVENT_TABLE()
1863 }
1864 
TileViewer()1865 void MainFrame::TileViewer()
1866 {
1867 	switch (panel->game_type())
1868 	{
1869 	case IMAGE_GBA:
1870 		LoadXRCViewer(Tile);
1871 		break;
1872 
1873 	case IMAGE_GB:
1874 		LoadXRCViewer(GBTile);
1875 		break;
1876 	}
1877 }
1878