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