summaryrefslogtreecommitdiff
path: root/apps/plugins/xworld/sys.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/xworld/sys.c')
-rw-r--r--apps/plugins/xworld/sys.c942
1 files changed, 942 insertions, 0 deletions
diff --git a/apps/plugins/xworld/sys.c b/apps/plugins/xworld/sys.c
new file mode 100644
index 0000000000..4bcdfafebd
--- /dev/null
+++ b/apps/plugins/xworld/sys.c
@@ -0,0 +1,942 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Franklin Wei, Benjamin Brown
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ***************************************************************************/
21
22/* TODO: */
23/* vertical stride support (as of Dec. 2014, only the M:Robe 500 has a color,
24 vertical stride LCD) */
25
26/* monochrome/grayscale support (many of these targets have vertical strides,
27 so get that working first!) */
28
29#include "plugin.h"
30#include "lib/display_text.h"
31#include "lib/helper.h"
32#include "lib/playback_control.h"
33#include "lib/pluginlib_actions.h"
34#include "lib/pluginlib_bmp.h"
35#include "lib/pluginlib_exit.h"
36#include "sys.h"
37#include "parts.h"
38#include "engine.h"
39#include "keymaps.h"
40
41static struct System* save_sys;
42
43static bool sys_save_settings(struct System* sys)
44{
45 File f;
46 file_create(&f, false);
47 if(!file_open(&f, SETTINGS_FILE, sys->e->_saveDir, "wb"))
48 {
49 return false;
50 }
51 file_write(&f, &sys->settings, sizeof(sys->settings));
52 file_close(&f);
53 return true;
54}
55
56static void sys_rotate_keymap(struct System* sys)
57{
58 switch(sys->settings.rotation_option)
59 {
60 case 0:
61 sys->keymap.up = BTN_UP;
62 sys->keymap.down = BTN_DOWN;
63 sys->keymap.left = BTN_LEFT;
64 sys->keymap.right = BTN_RIGHT;
65#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
66 sys->keymap.upleft = BTN_UP_LEFT;
67 sys->keymap.upright = BTN_UP_RIGHT;
68 sys->keymap.downleft = BTN_DOWN_RIGHT;
69 sys->keymap.downright = BTN_DOWN_LEFT;
70#endif
71 break;
72 case 1:
73 sys->keymap.up = BTN_RIGHT;
74 sys->keymap.down = BTN_LEFT;
75 sys->keymap.left = BTN_UP;
76 sys->keymap.right = BTN_DOWN;
77#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
78 sys->keymap.upleft = BTN_UP_RIGHT;
79 sys->keymap.upright = BTN_DOWN_RIGHT;
80 sys->keymap.downleft = BTN_UP_LEFT;
81 sys->keymap.downright = BTN_DOWN_LEFT;
82#endif
83 break;
84 case 2:
85 sys->keymap.up = BTN_LEFT;
86 sys->keymap.down = BTN_RIGHT;
87 sys->keymap.left = BTN_DOWN;
88 sys->keymap.right = BTN_UP;
89#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
90 sys->keymap.upleft = BTN_DOWN_LEFT;
91 sys->keymap.upright = BTN_UP_LEFT;
92 sys->keymap.downleft = BTN_DOWN_RIGHT;
93 sys->keymap.downright = BTN_DOWN_LEFT;
94#endif
95 break;
96 default:
97 error("sys_rotate_keymap: fall-through!");
98 }
99}
100
101static bool sys_load_settings(struct System* sys)
102{
103 File f;
104 file_create(&f, false);
105 if(!file_open(&f, SETTINGS_FILE, sys->e->_saveDir, "rb"))
106 {
107 return false;
108 }
109 file_read(&f, &sys->settings, sizeof(sys->settings));
110 file_close(&f);
111 sys_rotate_keymap(sys);
112 return true;
113}
114
115void exit_handler(void)
116{
117 sys_save_settings(save_sys);
118 sys_stopAudio(save_sys);
119#ifdef HAVE_ADJUSTABLE_CPU_FREQ
120 rb->cpu_boost(false);
121#endif
122 backlight_use_settings();
123}
124
125static bool sys_do_help(void)
126{
127#ifdef HAVE_LCD_COLOR
128 rb->lcd_set_foreground(LCD_WHITE);
129 rb->lcd_set_background(LCD_BLACK);
130#endif
131 rb->lcd_setfont(FONT_UI);
132 char* help_text[] = {"XWorld", "",
133 "XWorld", "is", "an", "interpreter", "for", "Another", "World,", "a", "fantastic", "game", "by", "Eric", "Chahi."
134 };
135 struct style_text style[] = {
136 {0, TEXT_CENTER | TEXT_UNDERLINE},
137 LAST_STYLE_ITEM
138 };
139 return display_text(ARRAYLEN(help_text), help_text, style, NULL, true);
140}
141
142static const struct opt_items scaling_settings[3] = {
143 { "Disabled", -1 },
144 { "Fast" , -1 },
145#ifdef HAVE_LCD_COLOR
146 { "Good" , -1 }
147#endif
148};
149
150static const struct opt_items rotation_settings[3] = {
151 { "Disabled" , -1 },
152 { "Clockwise" , -1 },
153 { "Anticlockwise", -1 }
154};
155
156static void do_video_settings(struct System* sys)
157{
158 MENUITEM_STRINGLIST(menu, "Video Settings", NULL,
159 "Negative",
160 "Scaling",
161 "Rotation",
162 "Show FPS",
163 "Zoom on code",
164 "Back");
165 int sel = 0;
166 while(1)
167 {
168 switch(rb->do_menu(&menu, &sel, NULL, false))
169 {
170 case 0:
171 rb->set_bool("Negative", &sys->settings.negative_enabled);
172 break;
173 case 1:
174 rb->set_option("Scaling", &sys->settings.scaling_quality, INT, scaling_settings,
175#ifdef HAVE_LCD_COLOR
176 3
177#else
178 2
179#endif
180 , NULL);
181 if(sys->settings.scaling_quality &&
182 sys->settings.zoom)
183 {
184 rb->splash(HZ*2, "Zoom automatically disabled.");
185 sys->settings.zoom = false;
186 }
187 break;
188 case 2:
189 rb->set_option("Rotation", &sys->settings.rotation_option, INT, rotation_settings, 3, NULL);
190 if(sys->settings.rotation_option &&
191 sys->settings.zoom)
192 {
193 rb->splash(HZ*2, "Zoom automatically disabled.");
194 sys->settings.zoom = false;
195 }
196 sys_rotate_keymap(sys);
197 break;
198 case 3:
199 rb->set_bool("Show FPS", &sys->settings.showfps);
200 break;
201 case 4:
202 rb->set_bool("Zoom on code", &sys->settings.zoom);
203 /* zoom only works with scaling and rotation disabled */
204 if(sys->settings.zoom &&
205 ( sys->settings.scaling_quality |
206 sys->settings.rotation_option))
207 {
208 rb->splash(HZ*2, "Scaling and rotation automatically disabled.");
209 sys->settings.scaling_quality = 0;
210 sys->settings.rotation_option = 0;
211 }
212 break;
213 case 5:
214 rb->lcd_clear_display();
215 sys_save_settings(sys);
216 return;
217 }
218 }
219}
220
221#define MAX_SOUNDBUF_SIZE 512
222const struct opt_items sound_bufsize_options[] = {
223 {"8 samples" , 8},
224 {"16 samples" , 16},
225 {"32 samples" , 32},
226 {"64 samples" , 64},
227 {"128 samples", 128},
228 {"256 samples", 256},
229 {"512 samples", 512},
230};
231
232static void do_sound_settings(struct System* sys)
233{
234 MENUITEM_STRINGLIST(menu, "Sound Settings", NULL,
235 "Enabled",
236 "Buffer Level",
237 "Back",
238 );
239 int sel = 0;
240 while(1)
241 {
242 switch(rb->do_menu(&menu, &sel, NULL, false))
243 {
244 case 0:
245 rb->set_bool("Enabled", &sys->settings.sound_enabled);
246 break;
247 case 1:
248 rb->set_option("Buffer Level", &sys->settings.sound_bufsize, INT,
249 sound_bufsize_options, ARRAYLEN(sound_bufsize_options), NULL);
250 break;
251 case 2:
252 sys_save_settings(sys);
253 return;
254 }
255 }
256}
257
258static void sys_reset_settings(struct System* sys)
259{
260 sys->settings.negative_enabled = false;
261 sys->settings.rotation_option = 0;
262 sys->settings.scaling_quality = 1;
263 sys->settings.sound_enabled = true;
264 sys->settings.sound_bufsize = 64;
265 sys->settings.showfps = true;
266 sys->settings.zoom = false;
267 sys_rotate_keymap(sys);
268}
269
270static struct System* mainmenu_sysptr;
271
272static int mainmenu_cb(int action, const struct menu_item_ex *this_item)
273{
274 int idx = ((intptr_t)this_item);
275 if(action == ACTION_REQUEST_MENUITEM && !mainmenu_sysptr->loaded && (idx == 0 || idx == 8 || idx == 10))
276 return ACTION_EXIT_MENUITEM;
277 return action;
278}
279
280static AudioCallback audio_callback;
281static void* audio_param;
282static struct System* audio_sys;
283
284/************************************** MAIN MENU ***************************************/
285
286void sys_menu(struct System* sys)
287{
288 sys_stopAudio(sys);
289 rb->splash(0, "Loading...");
290 sys->loaded = engine_loadGameState(sys->e, 0);
291 rb->lcd_update();
292 mainmenu_sysptr = sys;
293 int sel = 0;
294 MENUITEM_STRINGLIST(menu, "XWorld Menu", mainmenu_cb,
295 "Resume Game", /* 0 */
296 "Start New Game", /* 1 */
297 "Playback Control", /* 2 */
298 "Video Settings", /* 3 */
299 "Sound Settings", /* 4 */
300 "Fast Mode", /* 5 */
301 "Help", /* 6 */
302 "Reset Settings", /* 7 */
303 "Load", /* 8 */
304 "Save", /* 9 */
305 "Quit without Saving", /* 10 */
306 "Save and Quit"); /* 11 */
307 bool quit = false;
308 while(!quit)
309 {
310 int item;
311 switch(item = rb->do_menu(&menu, &sel, NULL, false))
312 {
313 case 0:
314 quit = true;
315 break;
316 case 1:
317 vm_initForPart(&sys->e->vm, GAME_PART_FIRST); // This game part is the protection screen
318 quit = true;
319 break;
320 case 2:
321 playback_control(NULL);
322 break;
323 case 3:
324 do_video_settings(sys);
325 break;
326 case 4:
327 do_sound_settings(sys);
328 break;
329 case 5:
330 rb->set_bool("Fast Mode", &sys->e->vm._fastMode);
331 sys_save_settings(sys);
332 break;
333 case 6:
334 sys_do_help();
335 break;
336 case 7:
337 sys_reset_settings(sys);
338 sys_save_settings(sys);
339 break;
340 case 8:
341 rb->splash(0, "Loading...");
342 sys->loaded = engine_loadGameState(sys->e, 0);
343 rb->lcd_update();
344 break;
345 case 9:
346 sys->e->_stateSlot = 0;
347 rb->splash(0, "Saving...");
348 engine_saveGameState(sys->e, sys->e->_stateSlot, "quicksave");
349 rb->lcd_update();
350 break;
351 case 10:
352 engine_deleteGameState(sys->e, 0);
353 exit(PLUGIN_OK);
354 break;
355 case 11:
356 /* saves are NOT deleted on loading */
357 exit(PLUGIN_OK);
358 break;
359 default:
360 error("sys_menu: fall-through!");
361 }
362 }
363 rb->lcd_clear_display();
364 sys_startAudio(sys, audio_callback, audio_param);
365}
366
367void sys_init(struct System* sys, const char* title)
368{
369 (void) title;
370#ifdef HAVE_ADJUSTABLE_CPU_FREQ
371 rb->cpu_boost(true);
372#endif
373 backlight_ignore_timeout();
374 rb_atexit(exit_handler);
375 save_sys = sys;
376 rb->memset(&sys->input, 0, sizeof(sys->input));
377 sys->mutex_bitfield = ~0;
378 if(!sys_load_settings(sys))
379 {
380 sys_reset_settings(sys);
381 }
382}
383
384void sys_destroy(struct System* sys)
385{
386 (void) sys;
387 rb->splash(HZ, "Exiting...");
388}
389
390void sys_setPalette(struct System* sys, uint8_t start, uint8_t n, const uint8_t *buf)
391{
392 for(int i = start; i < start + n; ++i)
393 {
394 uint8_t c[3];
395 for (int j = 0; j < 3; j++) {
396 uint8_t col = buf[i * 3 + j];
397 c[j] = (col << 2) | (col & 3);
398 }
399#if (LCD_DEPTH > 16) && (LCD_DEPTH <= 24)
400 sys->palette[i] = (fb_data) {
401 c[2], c[1], c[0]
402 };
403#elif defined(HAVE_LCD_COLOR)
404 sys->palette[i] = FB_RGBPACK(c[0], c[1], c[2]);
405#elif LCD_DEPTH > 1
406 sys->palette[i] = LCD_BRIGHTNESS((c[0] + c[1] + c[2]) / 3);
407#endif
408 }
409}
410
411/****************************** THE MAIN DRAWING METHOD ********************************/
412
413void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint8_t *buf, uint32_t pitch)
414{
415 static int last_timestamp = 1;
416
417 /* get the address of the temporary framebuffer that has been allocated in the audiobuf */
418 fb_data* framebuffer = (fb_data*) sys->e->res._memPtrStart + MEM_BLOCK_SIZE + (4 * VID_PAGE_SIZE);
419 buf += y * pitch + x;
420
421 /************************ BLIT THE TEMPORARY FRAMEBUFFER ***********************/
422
423 if(sys->settings.rotation_option)
424 {
425 /* clockwise */
426 if(sys->settings.rotation_option == 1)
427 {
428 while (h--) {
429 /* one byte gives two pixels */
430 for (int i = 0; i < w / 2; ++i) {
431 uint8_t pix1 = *(buf + i) >> 4;
432 uint8_t pix2 = *(buf + i) & 0xF;
433#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
434 framebuffer[( (h * 2) ) * 320 + i] = sys->palette[pix1];
435 framebuffer[( (h * 2) + 1) * 320 + i] = sys->palette[pix2];
436#else
437 framebuffer[( (i * 2) ) * 200 + h] = sys->palette[pix1];
438 framebuffer[( (i * 2) + 1) * 200 + h] = sys->palette[pix2];
439#endif
440 }
441 buf += pitch;
442 }
443 }
444 /* counterclockwise */
445 else
446 {
447 while (h--) {
448 /* one byte gives two pixels */
449 for (int i = 0; i < w / 2; ++i) {
450 uint8_t pix1 = *(buf + i) >> 4;
451 uint8_t pix2 = *(buf + i) & 0xF;
452#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
453 framebuffer[(200 - h * 2 ) * 320 + ( 320 - i )] = sys->palette[pix1];
454 framebuffer[(200 - h * 2 - 1) * 320 + ( 320 - i )] = sys->palette[pix2];
455#else
456 framebuffer[(320 - i * 2 ) * 200 + ( 200 - h )] = sys->palette[pix1];
457 framebuffer[(320 - i * 2 - 1) * 200 + ( 200 - h )] = sys->palette[pix2];
458#endif
459 }
460 buf += pitch;
461 }
462 }
463 }
464 /* no rotation */
465 else
466 {
467 int next = 0;
468#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
469 for(int x = 0; x < w / 2; ++x)
470 {
471 for(int y = 0; y < h; ++y)
472 {
473 uint8_t pix1 = buf[ y * w + x ] >> 4;
474 uint8_t pix2 = buf[ y * w + x ] & 0xF;
475 framebuffer[(x + 0)*h + y] = sys->palette[pix1];
476 framebuffer[(x + 1)*h + y] = sys->palette[pix2];
477 }
478 }
479#else
480 while (h--) {
481 /* one byte gives two pixels */
482 for (int i = 0; i < w / 2; ++i) {
483 uint8_t pix1 = *(buf + i) >> 4;
484 uint8_t pix2 = *(buf + i) & 0xF;
485 framebuffer[next] = sys->palette[pix1];
486 ++next;
487 framebuffer[next] = sys->palette[pix2];
488 ++next;
489 }
490 buf += pitch;
491 }
492#endif
493 }
494
495 /*************************** NOW SCALE IT! ***************************/
496
497 if(sys->settings.scaling_quality)
498 {
499 struct bitmap in_bmp;
500 if(sys->settings.rotation_option)
501 {
502#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
503 in_bmp.width = 320;
504 in_bmp.height = 200;
505#else
506 in_bmp.width = 200;
507 in_bmp.height = 320;
508#endif
509 }
510 else
511 {
512#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
513 in_bmp.width = 200;
514 in_bmp.height = 320;
515#else
516 in_bmp.width = 320;
517 in_bmp.height = 200;
518#endif
519 }
520 in_bmp.data = (unsigned char*) framebuffer;
521 struct bitmap out_bmp;
522 out_bmp.width = LCD_WIDTH;
523 out_bmp.height = LCD_HEIGHT;
524 out_bmp.data = (unsigned char*) rb->lcd_framebuffer;
525
526#ifdef HAVE_LCD_COLOR
527 if(sys->settings.scaling_quality == 1)
528#endif
529 simple_resize_bitmap(&in_bmp, &out_bmp);
530#ifdef HAVE_LCD_COLOR
531 else
532 smooth_resize_bitmap(&in_bmp, &out_bmp);
533#endif
534 }
535 else
536 {
537#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
538 for(int x = 0; x < 320; ++x)
539 {
540 for(int y = 0; y < 200; ++y)
541 {
542 rb->lcd_set_foreground(framebuffer[x * 200 + y]);
543 rb->lcd_drawpixel(x, y);
544 }
545 }
546#else
547 if(sys->settings.zoom)
548 {
549 rb->lcd_bitmap_part(framebuffer, CODE_X, CODE_Y, STRIDE(SCREEN_MAIN, 320, 200), 0, 0, 320 - CODE_X, 200 - CODE_Y);
550 }
551 else
552 {
553 if(sys->settings.rotation_option)
554 rb->lcd_bitmap(framebuffer, 0, 0, 200, 320);
555 else
556 rb->lcd_bitmap(framebuffer, 0, 0, 320, 200);
557 }
558#endif
559 }
560
561 /************************************* APPLY FILTERS ******************************************/
562
563 if(sys->settings.negative_enabled)
564 {
565 for(int y = 0; y < LCD_HEIGHT; ++y)
566 {
567 for(int x = 0; x < LCD_WIDTH; ++x)
568 {
569#ifdef HAVE_LCD_COLOR
570 int r, g, b;
571 fb_data pix = rb->lcd_framebuffer[y * LCD_WIDTH + x];
572 r = RGB_UNPACK_RED (pix);
573 g = RGB_UNPACK_GREEN(pix);
574 b = RGB_UNPACK_BLUE (pix);
575 rb->lcd_framebuffer[y * LCD_WIDTH + x] = LCD_RGBPACK(0xff - r, 0xff - g, 0xff - b);
576#else
577 rb->lcd_framebuffer[y * LCD_WIDTH + x] = LCD_BRIGHTNESS(0xff - rb->lcd_framebuffer[y * LCD_WIDTH + x]);
578#endif
579 }
580 }
581 }
582
583 /*********************** SHOW FPS *************************/
584
585 int current_time = sys_getTimeStamp(sys);
586 if(sys->settings.showfps)
587 {
588 rb->lcd_set_foreground(LCD_BLACK);
589 rb->lcd_set_background(LCD_WHITE);
590 /* use 1000 and not HZ here because getTimeStamp is in milliseconds */
591 rb->lcd_putsf(0, 0, "FPS: %d", 1000 / ((current_time - last_timestamp == 0) ? 1 : current_time - last_timestamp));
592 }
593 rb->lcd_update();
594 last_timestamp = sys_getTimeStamp(sys);
595}
596
597static void do_pause_menu(struct System* sys)
598{
599 sys_stopAudio(sys);
600 int sel = 0;
601 MENUITEM_STRINGLIST(menu, "XWorld Menu", NULL,
602 "Resume Game", /* 0 */
603 "Start New Game", /* 1 */
604 "Playback Control", /* 2 */
605 "Video Settings", /* 3 */
606 "Sound Settings", /* 4 */
607 "Fast Mode", /* 5 */
608 "Enter Code", /* 6 */
609 "Help", /* 7 */
610 "Reset Settings", /* 8 */
611 "Load", /* 9 */
612 "Save", /* 10 */
613 "Quit"); /* 11 */
614
615 bool quit = false;
616 while(!quit)
617 {
618 switch(rb->do_menu(&menu, &sel, NULL, false))
619 {
620 case 0:
621 quit = true;
622 break;
623 case 1:
624 vm_initForPart(&sys->e->vm, GAME_PART_FIRST);
625 quit = true;
626 break;
627 case 2:
628 playback_control(NULL);
629 break;
630 case 3:
631 do_video_settings(sys);
632 break;
633 case 4:
634 do_sound_settings(sys);
635 break;
636 case 5:
637 rb->set_bool("Fast Mode", &sys->e->vm._fastMode);
638 sys_save_settings(sys);
639 break;
640 case 6:
641 sys->input.code = true;
642 quit = true;
643 break;
644 case 7:
645 sys_do_help();
646 break;
647 case 8:
648 sys_reset_settings(sys);
649 sys_save_settings(sys);
650 break;
651 case 9:
652 rb->splash(0, "Loading...");
653 sys->loaded = engine_loadGameState(sys->e, 0);
654 rb->lcd_update();
655 quit = true;
656 break;
657 case 10:
658 sys->e->_stateSlot = 0;
659 rb->splash(0, "Saving...");
660 engine_saveGameState(sys->e, sys->e->_stateSlot, "quicksave");
661 rb->lcd_update();
662 break;
663 case 11:
664 exit(PLUGIN_OK);
665 break;
666 }
667 }
668 rb->lcd_clear_display();
669 sys_startAudio(sys, audio_callback, audio_param);
670}
671
672void sys_processEvents(struct System* sys)
673{
674 int btn = rb->button_get(false);
675 btn &= ~BUTTON_REDRAW;
676 debug(DBG_SYS, "button is 0x%08x", btn);
677
678 /* exit early if we can */
679 if(btn == BUTTON_NONE)
680 {
681 return;
682 }
683
684 /* Ignore some buttons that cause errant input */
685#if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
686 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
687 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
688 if(btn & 0x80000000)
689 return;
690#endif
691#if (CONFIG_KEYPAD == SANSA_E200_PAD)
692 if(btn == (BUTTON_SCROLL_FWD || BUTTON_SCROLL_BACK))
693 return;
694#endif
695#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
696 if(btn == (BUTTON_SELECT))
697 return;
698#endif
699
700 /* handle special keys first */
701 switch(btn)
702 {
703 case BTN_PAUSE:
704 do_pause_menu(sys);
705 sys->input.dirMask = 0;
706 sys->input.button = false;
707 /* exit early to avoid unwanted button presses being detected */
708 return;
709 default:
710 exit_on_usb(btn);
711 break;
712 }
713
714 /* handle releases */
715 if(btn & BUTTON_REL)
716 {
717 debug(DBG_SYS, "button_rel");
718 btn &= ~BUTTON_REL;
719
720 if(btn & BTN_FIRE)
721 sys->input.button = false;
722 if(btn & sys->keymap.up)
723 sys->input.dirMask &= ~DIR_UP;
724 if(btn & sys->keymap.down)
725 sys->input.dirMask &= ~DIR_DOWN;
726 if(btn & sys->keymap.left)
727 sys->input.dirMask &= ~DIR_LEFT;
728 if(btn & sys->keymap.right)
729 sys->input.dirMask &= ~DIR_RIGHT;
730#ifdef BTN_DOWN_LEFT
731 if(btn & sys->keymap.downleft)
732 sys->input.dirMask &= ~(DIR_DOWN | DIR_LEFT);
733#endif
734#ifdef BTN_DOWN_RIGHT
735 if(btn & sys->keymap.downright)
736 sys->input.dirMask &= ~(DIR_DOWN | DIR_RIGHT);
737#endif
738#ifdef BTN_UP_LEFT
739 if(btn & sys->keymap.upleft)
740 sys->input.dirMask &= ~(DIR_UP | DIR_LEFT);
741#endif
742#ifdef BTN_UP_RIGHT
743 if(btn & sys->keymap.upright)
744 sys->input.dirMask &= ~(DIR_UP | DIR_RIGHT);
745#endif
746 }
747 /* then handle presses */
748 else
749 {
750 if(btn & BTN_FIRE)
751 sys->input.button = true;
752 if(btn & sys->keymap.up)
753 sys->input.dirMask |= DIR_UP;
754 if(btn & sys->keymap.down)
755 sys->input.dirMask |= DIR_DOWN;
756 if(btn & sys->keymap.left)
757 sys->input.dirMask |= DIR_LEFT;
758 if(btn & sys->keymap.right)
759 sys->input.dirMask |= DIR_RIGHT;
760#ifdef BTN_DOWN_LEFT
761 if(btn & sys->keymap.downleft)
762 sys->input.dirMask |= (DIR_DOWN | DIR_LEFT);
763#endif
764#ifdef BTN_DOWN_RIGHT
765 if(btn & sys->keymap.downright)
766 sys->input.dirMask |= (DIR_DOWN | DIR_RIGHT);
767#endif
768#ifdef BTN_UP_LEFT
769 if(btn & sys->keymap.upleft)
770 sys->input.dirMask |= (DIR_UP | DIR_LEFT);
771#endif
772#ifdef BTN_UP_RIGHT
773 if(btn & sys->keymap.upright)
774 sys->input.dirMask |= (DIR_UP | DIR_RIGHT);
775#endif
776 }
777 debug(DBG_SYS, "dirMask is 0x%02x", sys->input.dirMask);
778 debug(DBG_SYS, "button is %s", sys->input.button == true ? "true" : "false");
779}
780
781void sys_sleep(struct System* sys, uint32_t duration)
782{
783 (void) sys;
784 /* duration is in ms */
785 rb->sleep(duration / 10);
786}
787
788uint32_t sys_getTimeStamp(struct System* sys)
789{
790 (void) sys;
791 return (uint32_t) (*rb->current_tick) * 10;
792}
793
794static int16_t rb_soundbuf [MAX_SOUNDBUF_SIZE] IBSS_ATTR;
795static int8_t temp_soundbuf[MAX_SOUNDBUF_SIZE] IBSS_ATTR;
796static void ICODE_ATTR get_more(const void** start, size_t* size)
797{
798 if(audio_sys->settings.sound_enabled)
799 {
800 audio_callback(audio_param, temp_soundbuf, audio_sys->settings.sound_bufsize);
801 /* convert xworld format (signed 8-bit) to rockbox format (signed 16-bit) */
802 for(int i = 0; i < audio_sys->settings.sound_bufsize; ++i)
803 {
804 rb_soundbuf[i] = temp_soundbuf[i] * 0x100;
805 }
806 *start = rb_soundbuf;
807 *size = audio_sys->settings.sound_bufsize;
808 }
809 else
810 {
811 *start = NULL;
812 *size = 0;
813 }
814}
815
816void sys_startAudio(struct System* sys, AudioCallback callback, void *param)
817{
818 (void) sys;
819 audio_callback = callback;
820 audio_param = param;
821 audio_sys = sys;
822 rb->pcm_play_data(get_more, NULL, NULL, 0);
823}
824
825void sys_stopAudio(struct System* sys)
826{
827 (void) sys;
828 rb->pcm_play_stop();
829}
830
831uint32_t sys_getOutputSampleRate(struct System* sys)
832{
833 (void) sys;
834 return rb->mixer_get_frequency();
835}
836
837/* pretty non-reentrant here, but who cares? it's not like someone
838 would want to run two instances of the game on Rockbox! :D */
839
840static uint32_t cur_delay;
841static void* cur_param;
842static TimerCallback user_callback;
843static void timer_callback(void)
844{
845 debug(DBG_SYS, "timer callback with delay of %d ms callback 0x%08x param 0x%08x", cur_delay, timer_callback, cur_param);
846 uint32_t ret = user_callback(cur_delay, cur_param);
847 if(ret != cur_delay)
848 {
849 cur_delay = ret;
850 rb->timer_register(1, NULL, TIMER_FREQ / 1000 * ret, timer_callback IF_COP(, CPU));
851 }
852}
853
854void *sys_addTimer(struct System* sys, uint32_t delay, TimerCallback callback, void *param)
855{
856 (void) sys;
857 debug(DBG_SYS, "registering timer with delay of %d ms callback 0x%08x param 0x%08x", delay, callback, param);
858 user_callback = callback;
859 cur_delay = delay;
860 cur_param = param;
861 rb->timer_register(1, NULL, TIMER_FREQ / 1000 * delay, timer_callback IF_COP(, CPU));
862 return NULL;
863}
864
865void sys_removeTimer(struct System* sys, void *timerId)
866{
867 (void) sys;
868 (void) timerId;
869 /* there's only one timer per game instance, so ignore both parameters */
870 rb->timer_unregister();
871}
872
873void *sys_createMutex(struct System* sys)
874{
875 if(!sys)
876 error("sys is NULL!");
877 for(int i = 0; i < MAX_MUTEXES; ++i)
878 {
879 if(sys->mutex_bitfield & (1 << i))
880 {
881 rb->mutex_init(&sys->mutex_memory[i]);
882 sys->mutex_bitfield |= (1 << i);
883 return &sys->mutex_memory[i];
884 }
885 }
886 warning("Out of mutexes!");
887 return NULL;
888}
889
890void sys_destroyMutex(struct System* sys, void *mutex)
891{
892 int mutex_number = ((char*)mutex - (char*)sys->mutex_memory) / sizeof(struct mutex); /* pointer arithmetic! check for bugs! */
893 sys->mutex_bitfield &= ~(1 << mutex_number);
894}
895
896void sys_lockMutex(struct System* sys, void *mutex)
897{
898 (void) sys;
899 debug(DBG_SYS, "calling mutex_lock");
900 rb->mutex_lock((struct mutex*) mutex);
901}
902
903void sys_unlockMutex(struct System* sys, void *mutex)
904{
905 (void) sys;
906 debug(DBG_SYS, "calling mutex_unlock");
907 rb->mutex_unlock((struct mutex*) mutex);
908}
909
910uint8_t* getOffScreenFramebuffer(struct System* sys)
911{
912 (void) sys;
913 return NULL;
914}
915
916void *sys_get_buffer(struct System* sys, size_t sz)
917{
918 if((signed int)sys->bytes_left - (signed int)sz >= 0)
919 {
920 void* ret = sys->membuf;
921 rb->memset(ret, 0, sz);
922 sys->membuf += sz;
923 return ret;
924 }
925 else
926 {
927 error("sys_get_buffer: out of memory!");
928 return NULL;
929 }
930}
931
932void MutexStack(struct MutexStack_t* s, struct System *stub, void *mutex)
933{
934 s->sys = stub;
935 s->_mutex = mutex;
936 sys_lockMutex(s->sys, s->_mutex);
937}
938
939void MutexStack_destroy(struct MutexStack_t* s)
940{
941 sys_unlockMutex(s->sys, s->_mutex);
942}