1 /*
2  * PROPRIETARY INFORMATION.  This software is proprietary to POWDER
3  * Development, and is not to be reproduced, transmitted, or disclosed
4  * in any way without written permission.
5  *
6  * Produced by:	Jeff Lait
7  *
8  *      	POWDER Development
9  *
10  * NAME:        hamfake.cpp ( POWDER Library, C++ )
11  *
12  * COMMENTS:
13  *	This file implements all the fake ham functions.
14  *	It also stores the global state of the hardware.
15  */
16 
17 
18 #include "mygba.h"
19 #include "hamfake.h"
20 
21 #include "../../gfxengine.h"
22 #include "../../bmp.h"
23 #include "../../control.h"
24 
25 #include <nds.h>
26 #include <fat.h>
27 
28 #include <sys/stat.h>
29 
30 #define SRAMSIZE 65536
31 
32 #define KEY_REPEAT_INITIAL 15
33 #define KEY_REPEAT_AFTER 7
34 
35 //
36 // Global GBA state:
37 //
38 
39 bg_info			ham_bg[4];
40 
41 int			ham_vramtop = 0;
42 u8			ham_winin[2], ham_winout[2];
43 bool			ham_fatvalid = false;
44 
45 bool			ham_extratileset = false;
46 
47 short			glbStylusX, glbStylusY;
48 bool			glbStylusState;
49 
50 // It is important to use this rather than the SpriteEntry
51 // defined in sprite.h because it uses a union of enums and
52 // we don't allow short enums, so it will have the entirely
53 // wrong size.
54 struct POWDER_SpriteEntry
55 {
56     u16 attribute[4];
57 };
58 
59 static const int POWDER_SPRITES = 128;
60 POWDER_SpriteEntry		glbSpriteList[POWDER_SPRITES];
61 bool			glbSpriteDirty = false;
62 
63 // This is the path, including trailing slash, where we will
64 // save all of our data such as .SAV files and character dumps.
65 // If it doesn't exist, we create it on start up.  If creation
66 // fails, we dump a warning and use /
67 const char		*glbAbsoluteDataPath;
68 
69 // Keyboard state:
70 
71 void
hamfake_rebuildScreen()72 hamfake_rebuildScreen()
73 {
74 }
75 
76 void
updateOAM()77 updateOAM()
78 {
79     if (!glbSpriteDirty)
80 	return;
81 
82     // Compiling without short-enums can cause problems with DS
83     // data structures.
84     DC_FlushRange(glbSpriteList,POWDER_SPRITES*sizeof(POWDER_SpriteEntry));
85     dmaCopy(glbSpriteList, OAM, POWDER_SPRITES*sizeof(POWDER_SpriteEntry));
86 
87     glbSpriteDirty = false;
88 }
89 
90 // Wait for an event to occur.
91 void
hamfake_awaitEvent()92 hamfake_awaitEvent()
93 {
94     // Check for the lid going down, in which case we power down until
95     // it goes up...
96     if (ctrl_rawpressed(BUTTON_LID))
97     {
98 	u16		powerstatus;
99 
100 	powerstatus = REG_POWERCNT;
101 	REG_POWERCNT = 0;
102 	while (ctrl_rawpressed(BUTTON_LID))
103 	{
104 	    // Can't await frame here as we are already doing so :>
105 	    // Note that they await a VGL,
106 	    // perhaps that would halt the porcessor
107 	    // for more savings?
108 	    // Apparently it does, which is why we added it to
109 	    // the main loop but foolishly forgot it in the powerdown loop,
110 	    // ironically thus consuming less CPU power when the lid
111 	    // is open as opposed to closed.
112 	    swiWaitForVBlank();
113 	}
114 	REG_POWERCNT = powerstatus;
115     }
116     updateOAM();
117     swiWaitForVBlank();
118 }
119 
120 // Return our internal screen.
121 u16 *
hamfake_lockScreen()122 hamfake_lockScreen()
123 {
124     // Because we are the very epitome of laziness,
125     // we give the proper offset to center this GBA style
126     // rather than fixing everything else.
127     return VRAM_A + 8 + 16 * 256;
128 }
129 
130 void
hamfake_unlockScreen(u16 *)131 hamfake_unlockScreen(u16 *)
132 {
133 }
134 
135 char *glbSRAM = 0;
136 
137 // Deal with our SRAM buffer.
138 char *
hamfake_writeLockSRAM()139 hamfake_writeLockSRAM()
140 {
141     // TODO: determine if we are proper PASSME or not before
142     // writing to SRAM.
143     if (!glbSRAM)
144     {
145 	glbSRAM = (char *)malloc(SRAMSIZE);
146 
147 	// Read our .sav file from fat if possible.
148 	if (ham_fatvalid)
149 	{
150 	    FILE	*fp;
151 
152 	    fp = hamfake_fopen("powder.sav", "rb");
153 	    if (!fp)
154 	    {
155 		iprintf("No save file found.\n");
156 	    }
157 	    else
158 	    {
159 		fread(glbSRAM, SRAMSIZE, 1, fp);
160 		fclose(fp);
161 	    }
162 	}
163     }
164     return glbSRAM;
165 }
166 
167 void
hamfake_writeUnlockSRAM(char *)168 hamfake_writeUnlockSRAM(char *)
169 {
170 }
171 
172 char *
hamfake_readLockSRAM()173 hamfake_readLockSRAM()
174 {
175     return hamfake_writeLockSRAM();
176 }
177 
178 void
hamfake_readUnlockSRAM(char *)179 hamfake_readUnlockSRAM(char *)
180 {
181 }
182 
183 void
hamfake_endWritingSession()184 hamfake_endWritingSession()
185 {
186     // Why the hell do I have a write unlock if there is an endWritingSession?
187     FILE		*fp;
188 
189     fp = hamfake_fopen("powder.sav", "wb");
190 
191     if (!fp)
192     {
193 	iprintf("Failure to open powder.sav for writing!\n");
194 	return;
195     }
196 
197     fwrite(glbSRAM, SRAMSIZE, 1, fp);
198 
199     fclose(fp);
200 }
201 
202 bool
hamfake_fatvalid()203 hamfake_fatvalid()
204 {
205     return ham_fatvalid;
206 }
207 
208 // Called to run our event poll
209 void
hamfake_pollEvents()210 hamfake_pollEvents()
211 {
212     updateOAM();
213     scanKeys();
214     touchPosition	pos = touchReadXY();
215     glbStylusX = pos.px;
216     glbStylusY = pos.py;
217     glbStylusState = (keysHeld() & KEY_TOUCH) ? true : false;
218 }
219 
220 bool
hamfake_isPressed(BUTTONS button)221 hamfake_isPressed(BUTTONS button)
222 {
223     hamfake_pollEvents();
224     switch (button)
225     {
226 	case BUTTON_UP:
227 	    return keysHeld() & KEY_UP;
228 
229 	case BUTTON_DOWN:
230 	    return keysHeld() & KEY_DOWN;
231 
232 	case BUTTON_LEFT:
233 	    return keysHeld() & KEY_LEFT;
234 
235 	case BUTTON_RIGHT:
236 	    return keysHeld() & KEY_RIGHT;
237 
238 	case BUTTON_SELECT:
239 	    return keysHeld() & KEY_SELECT;
240 	case BUTTON_START:
241 	    return keysHeld() & KEY_START;
242 
243 	case BUTTON_A:
244 	    return keysHeld() & KEY_A;
245 	case BUTTON_B:
246 	    return keysHeld() & KEY_B;
247 	case BUTTON_R:
248 	    return keysHeld() & KEY_R;
249 	case BUTTON_L:
250 	    return keysHeld() & KEY_L;
251 
252 	case BUTTON_X:
253 	    return keysHeld() & KEY_X;
254 	case BUTTON_Y:
255 	    return keysHeld() & KEY_Y;
256 
257 	case BUTTON_TOUCH:
258 	    return keysHeld() & KEY_TOUCH;
259 
260 	case BUTTON_LID:
261 	    return keysHeld() & KEY_LID;
262 
263 	case NUM_BUTTONS:
264 	    return false;
265     }
266 
267     return false;
268 }
269 
270 int
hamfake_isAnyPressed()271 hamfake_isAnyPressed()
272 {
273     hamfake_pollEvents();
274     if (keysHeld() == 0)
275     {
276 	// For backwards compatability with R_CTRLINPUT the sense of this
277 	// return is a bit odd...
278 	return 0x3FF;
279     }
280     return 0;
281 }
282 
283 int
hamfake_peekKeyPress()284 hamfake_peekKeyPress()
285 {
286     return 0;
287 }
288 
289 int
hamfake_getKeyPress(bool onlyascii)290 hamfake_getKeyPress(bool onlyascii)
291 {
292     return 0;
293 }
294 
295 void
hamfake_insertKeyPress(int key)296 hamfake_insertKeyPress(int key)
297 {
298     return;
299 }
300 
301 void
hamfake_clearKeyboardBuffer()302 hamfake_clearKeyboardBuffer()
303 {
304 }
305 
306 void
hamfake_setScrollX(int layer,int scroll)307 hamfake_setScrollX(int layer, int scroll)
308 {
309     BG_OFFSET[layer].x = scroll;
310 }
311 
312 void
hamfake_setScrollY(int layer,int scroll)313 hamfake_setScrollY(int layer, int scroll)
314 {
315     BG_OFFSET[layer].y = scroll;
316 }
317 
318 void
ham_Init()319 ham_Init()
320 {
321     if (fatInitDefault())
322     {
323 	iprintf("FAT system initialized\n");
324 	ham_fatvalid = true;
325 
326 	// Determine our save directory
327 	struct stat		buffer;
328 	int			status;
329 
330 	status = stat("/data", &buffer);
331 	if (status < 0)
332 	{
333 	    // An error, try to make the directory.
334 	    mkdir("/data", S_IRWXU | S_IRWXG | S_IRWXO);
335 	}
336 	status = stat("/data/powder", &buffer);
337 	if (status < 0)
338 	{
339 	    // Again, an error, so try another make.  I don't
340 	    // think mkdir will make two...
341 	    mkdir("/data/powder", S_IRWXU | S_IRWXG | S_IRWXO);
342 	}
343 
344 	// And stat again..
345 	status = stat("/data/powder", &buffer);
346 
347 	if (!status && S_ISDIR(buffer.st_mode))
348 	{
349 	    // Everything is cool!
350 	    glbAbsoluteDataPath = "/data/powder/";
351 	}
352 	else
353 	{
354 	    // Failed to create directory...
355 	    iprintf("Failed to find/create /data/powder.  POWDER's data will be saved to the root of your flash card.\n");
356 	}
357 
358 	// Attempt to load foreign images.
359 	if (bmp_loadExtraTileset())
360 	{
361 	    ham_extratileset = true;
362 	    iprintf("Extra tileset loaded.\n");
363 	}
364     }
365     else
366     {
367 	iprintf("No FAT system found - save disabled\n");
368 	ham_fatvalid = false;
369     }
370 
371     int		i;
372     for (i = 0; i < POWDER_SPRITES; i++)
373     {
374 	glbSpriteList[i].attribute[0] = ATTR0_DISABLED;
375 	glbSpriteList[i].attribute[1] = ATTR1_SIZE_16;
376 	glbSpriteList[i].attribute[2] = 0;
377 	glbSpriteList[i].attribute[3] = 0;
378     }
379 }
380 
381 void
ham_StartIntHandler(u8 intno,void (* fp)())382 ham_StartIntHandler(u8 intno, void (*fp)())
383 {
384     // assert(intno == INT_TYPE_VBL);
385     irqSet(IRQ_VBLANK, fp);
386     irqEnable(IRQ_VBLANK);
387 }
388 
389 void
ham_SetBgMode(u8 mode)390 ham_SetBgMode(u8 mode)
391 {
392     switch (mode)
393     {
394 	case 3:
395 #if 1
396 	    vramSetBankA(VRAM_A_LCD);
397 	    videoSetMode(MODE_FB0 |
398 		    DISPLAY_SPR_ACTIVE |
399 		    DISPLAY_SPR_1D |
400 		    DISPLAY_SPR_1D_BMP);
401 	    // Desire a flat memory model.
402 #else
403 	    videoSetMode(MODE_3_2D |
404 			DISPLAY_BG0_ACTIVE |
405 			DISPLAY_BG1_ACTIVE |
406 			DISPLAY_BG2_ACTIVE |
407 			DISPLAY_BG3_ACTIVE);
408 #endif
409 	    break;
410 
411 	case 0:
412 	    // Desire tiled mode.
413 	    vramSetBankA(VRAM_A_MAIN_BG);
414 	    vramSetBankE(VRAM_E_MAIN_SPRITE);
415 	    videoSetMode(MODE_0_2D |
416 			DISPLAY_SPR_ACTIVE |
417 			DISPLAY_BG0_ACTIVE |
418 			DISPLAY_BG1_ACTIVE |
419 			DISPLAY_BG2_ACTIVE |
420 			DISPLAY_BG3_ACTIVE |
421 			DISPLAY_SPR_1D |	// I am too old school.
422 			DISPLAY_SPR_1D_BMP |
423 			DISPLAY_WIN0_ON);
424 
425 	    // Reset the top of our vram.
426 	    ham_vramtop = 0;
427 	    ham_DeleteWin(0);
428 	    ham_DeleteWin(1);
429 	    break;
430     }
431 }
432 
433 
434 void
ham_LoadBGPal(void * vpal,int bytes)435 ham_LoadBGPal(void *vpal, int bytes)
436 {
437     u16		*pal = (u16 *) vpal;
438     bytes /= 2;
439     int		i;
440 
441     for (i = 0; i < bytes; i++)
442     {
443 	BG_PALETTE[i] = pal[i];
444     }
445 }
446 
447 void
hamfake_LoadSpritePal(void * vpal,int bytes)448 hamfake_LoadSpritePal(void *vpal, int bytes)
449 {
450     u16		*pal = (u16 *) vpal;
451     bytes /= 2;
452     int		i;
453 
454     for (i = 0; i < bytes; i++)
455     {
456 	// The sprite palette could theoritically be entirely independent,
457 	// but since we'll likely just be grabbing tiles from the shared
458 	// graphics list...
459 	SPRITE_PALETTE[i] = pal[i];
460     }
461 }
462 
463 u8
ham_AllocateVramBlock(int bytes,int blocksize)464 ham_AllocateVramBlock(int bytes, int blocksize)
465 {
466     int		blockid;
467 
468     // Round current vram to nearest block.
469     blockid = (ham_vramtop + blocksize-1) / blocksize;
470     ham_vramtop = blockid * blocksize;
471 
472     // Round up bytes to allocate...
473     bytes = (bytes + blocksize -1) / blocksize;
474     bytes *= blocksize;
475 
476     ham_vramtop += bytes;
477 
478     return blockid;
479 }
480 
481 map_info_ptr
ham_InitMapEmptySet(u8 size,u8 rot)482 ham_InitMapEmptySet(u8 size, u8 rot)
483 {
484     map_info_ptr		mapinfo;
485     int				vramsize;
486 
487     mapinfo = new map_info;
488 
489     // Find size in bytes of map.
490     // if 0 size, is 32x32 so hence 2k.
491     // If 3 size, is 64x64 so hence 8k.
492     vramsize = 0;
493     switch (size)
494     {
495 	case 0:
496 	    vramsize = 2 * 32 * 32;
497 	    mapinfo->largemap = false;
498 	    break;
499 	case 3:
500 	    vramsize = 2 * 64 * 64;
501 	    mapinfo->largemap = true;
502 	    break;
503     }
504 
505     mapinfo->blockid = ham_AllocateVramBlock(vramsize, 0x800);
506 
507     return mapinfo;
508 }
509 
510 tile_info_ptr
ham_InitTileEmptySet(u16 entries,u8 colormode,u8 onlybaseblock)511 ham_InitTileEmptySet(u16 entries, u8 colormode, u8 onlybaseblock)
512 {
513     tile_info_ptr	tileinfo;
514     int			vramsize;
515 
516     tileinfo = new tile_info;
517 
518     vramsize = entries;
519     if (colormode == 0)
520 	vramsize *= 8 * 8 / 2;			// 16 colours
521     else
522 	vramsize *= 8 * 8;
523 
524     tileinfo->blockid = ham_AllocateVramBlock(vramsize, 0x2000);
525 
526     return tileinfo;
527 }
528 
529 void
ham_InitBg(int layer,int active,int priority,int mosaic)530 ham_InitBg(int layer, int active, int priority, int mosaic)
531 {
532     int		mapsize;
533     if (ham_bg[layer].mi->largemap)
534 	mapsize = BG_64x64;
535     else
536 	mapsize = BG_32x32;
537 
538     BGCTRL[layer] = BG_256_COLOR |
539 		    BG_PRIORITY(priority) |
540 		    BG_TILE_BASE(ham_bg[layer].ti->blockid) |
541 		    BG_MAP_BASE(ham_bg[layer].mi->blockid) |
542 		    mapsize;
543 }
544 
545 void
ham_CreateWin(int wid,int x1,int y1,int x2,int y2,int inside,int outside,int fx)546 ham_CreateWin(int wid, int x1, int y1, int x2, int y2, int inside, int outside, int fx)
547 {
548     switch (wid)
549     {
550 	case 0:
551 	    WIN0_X0 = x1;
552 	    WIN0_X1 = x2;
553 	    WIN0_Y0 = y1;
554 	    WIN0_Y1 = y2;
555 	    break;
556 	case 1:
557 	    WIN1_X0 = x1;
558 	    WIN1_X1 = x2;
559 	    WIN1_Y0 = y1;
560 	    WIN1_Y1 = y2;
561 	    break;
562     }
563     ham_winin[wid] = inside;
564     ham_winout[wid] = outside;
565     // Update control registers.
566     WIN_IN = ham_winin[0] | (ham_winin[1] << 8);
567     // This is wrong.
568     WIN_OUT = ham_winout[0] | (ham_winout[1] << 8);
569 }
570 
571 void
ham_DeleteWin(int wid)572 ham_DeleteWin(int wid)
573 {
574     // Allow everything inside & outside.
575     ham_winin[wid] = WIN_BG0 | WIN_BG1 | WIN_BG2 | WIN_BG3 | WIN_OBJ;
576     ham_winout[wid] = WIN_BG0 | WIN_BG1 | WIN_BG2 | WIN_BG3 | WIN_OBJ;
577     WIN_IN = ham_winin[0] | (ham_winin[1] << 8);
578     WIN_OUT = ham_winout[0] | (ham_winout[1] << 8);
579 }
580 
581 void
ham_SetMapTile(int layer,int mx,int my,int tileidx)582 ham_SetMapTile(int layer, int mx, int my, int tileidx)
583 {
584     int		blockid = ham_bg[layer].mi->blockid;
585 
586     u16		*map = (u16 *) SCREEN_BASE_BLOCK(blockid);
587 
588     // We are interleaved in a funny way...
589     if (mx >= 32)
590     {
591 	mx -= 32;
592 	map += 32 * 32;
593     }
594     if (my >= 32)
595     {
596 	my -= 32;
597 	map += 2 * 32 * 32;
598     }
599     map[my*32 + mx] = tileidx;
600 }
601 
602 void
ham_ReloadTileGfx(tile_info_ptr tiledata,const u16 * data,int destidx,int numtile)603 ham_ReloadTileGfx(tile_info_ptr tiledata, const u16 *data, int destidx,
604 		  int numtile)
605 {
606     int		i;
607     int		 blockid = tiledata->blockid;
608     u16		*dest = (u16 *) CHAR_BASE_BLOCK(blockid);
609     dest += 4 * 8 * destidx;
610     numtile *= 4 * 8;
611 
612     for (i = 0; i < numtile; i++)
613     {
614 	dest[i] = data[i];
615     }
616 }
617 
618 void
hamfake_ReloadSpriteGfx(const u16 * data,int destidx,int numtile)619 hamfake_ReloadSpriteGfx(const u16 *data, int destidx, int numtile)
620 {
621     u16		*dest = &SPRITE_GFX[0];
622     int		 i;
623 
624     dest += destidx * 4 * 8;
625     numtile *= 4 * 8;
626 
627     for (i = 0; i < numtile; i++)
628     {
629 	dest[i] = data[i];
630     }
631 }
632 
633 void
hamfake_softReset()634 hamfake_softReset()
635 {
636 }
637 
638 void
hamfake_setFullScreen(bool fullscreen)639 hamfake_setFullScreen(bool fullscreen)
640 {
641 }
642 
643 bool
hamfake_isFullScreen()644 hamfake_isFullScreen()
645 {
646     return true;		// Always full :>
647 }
648 
649 bool
hamfake_extratileset()650 hamfake_extratileset()
651 {
652     return ham_extratileset;
653 }
654 
655 void
hamfake_getstyluspos(int & x,int & y)656 hamfake_getstyluspos(int &x, int &y)
657 {
658     x = glbStylusX;
659     y = glbStylusY;
660 
661     // Stylus positions are returned in the original GBA window,
662     // not the extended window of the DS!
663     x -= 8;
664     y -= 16;
665 }
666 
667 bool
hamfake_getstylusstate()668 hamfake_getstylusstate()
669 {
670     return glbStylusState;
671 }
672 
673 void
hamfake_movesprite(int spriteno,int x,int y)674 hamfake_movesprite(int spriteno, int x, int y)
675 {
676     // The caller is specifying in GBA space.
677     x += 8;
678     y += 16;
679 
680     // Clear out our old coord.
681     glbSpriteList[spriteno].attribute[0] &= ~255;
682     glbSpriteList[spriteno].attribute[1] &= ~511;
683 
684     // Merge in new coord.
685     y &= 255;
686     x &= 511;
687     glbSpriteList[spriteno].attribute[0] |= y;
688     glbSpriteList[spriteno].attribute[1] |= x;
689 
690     glbSpriteDirty = true;
691 }
692 
693 void
hamfake_enablesprite(int spriteno,bool enabled)694 hamfake_enablesprite(int spriteno, bool enabled)
695 {
696     // Change the flag to reflect the new value.
697     // Track if dirty to save cycles.
698     if (enabled)
699     {
700 	int		y;
701 	y = glbSpriteList[spriteno].attribute[0] & 255;
702 	glbSpriteList[spriteno].attribute[0] = ATTR0_COLOR_256 | ATTR0_SQUARE;
703 	glbSpriteList[spriteno].attribute[0] |= y;
704 	// We want sprites to always be under the text.
705 	// Exception is the cursor.
706 	if (spriteno)
707 	    glbSpriteList[spriteno].attribute[2] = 8 * spriteno | ATTR2_PRIORITY(1);
708 	else
709 	    glbSpriteList[spriteno].attribute[2] = 8 * spriteno;
710 	glbSpriteDirty = true;
711     }
712     else
713     {
714 	glbSpriteList[spriteno].attribute[0] = ATTR0_DISABLED;
715 	glbSpriteDirty = true;
716     }
717 }
718 
719 FILE *
hamfake_fopen(const char * path,const char * mode)720 hamfake_fopen(const char *path, const char *mode)
721 {
722     FILE		*result;
723 
724     // We want to use an absolute path.
725     // Prefix /data/powder.
726     char		*buf;
727     buf = new char[strlen(path) + strlen(glbAbsoluteDataPath) + 5];
728     sprintf(buf, "%s%s", glbAbsoluteDataPath, path);
729 
730     result = fopen(buf, mode);
731 
732     delete [] buf;
733 
734     return result;
735 }
736 
737 void
hamfake_setTileSize(int width,int height)738 hamfake_setTileSize(int width, int height)
739 {
740 }
741 
742 int
hamfake_getKeyModifiers()743 hamfake_getKeyModifiers()
744 {
745     return 0;
746 }
747