diff options
Diffstat (limited to 'apps/plugins/xworld/sys.c')
-rw-r--r-- | apps/plugins/xworld/sys.c | 942 |
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 | |||
41 | static struct System* save_sys; | ||
42 | |||
43 | static 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 | |||
56 | static 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 | |||
101 | static 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 | |||
115 | void 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 | |||
125 | static 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 | |||
142 | static 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 | |||
150 | static const struct opt_items rotation_settings[3] = { | ||
151 | { "Disabled" , -1 }, | ||
152 | { "Clockwise" , -1 }, | ||
153 | { "Anticlockwise", -1 } | ||
154 | }; | ||
155 | |||
156 | static 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 | ||
222 | const 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 | |||
232 | static 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 | |||
258 | static 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 | |||
270 | static struct System* mainmenu_sysptr; | ||
271 | |||
272 | static 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 | |||
280 | static AudioCallback audio_callback; | ||
281 | static void* audio_param; | ||
282 | static struct System* audio_sys; | ||
283 | |||
284 | /************************************** MAIN MENU ***************************************/ | ||
285 | |||
286 | void 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 | |||
367 | void 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 | |||
384 | void sys_destroy(struct System* sys) | ||
385 | { | ||
386 | (void) sys; | ||
387 | rb->splash(HZ, "Exiting..."); | ||
388 | } | ||
389 | |||
390 | void 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 | |||
413 | void 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 | |||
597 | static 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 | |||
672 | void 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 | |||
781 | void sys_sleep(struct System* sys, uint32_t duration) | ||
782 | { | ||
783 | (void) sys; | ||
784 | /* duration is in ms */ | ||
785 | rb->sleep(duration / 10); | ||
786 | } | ||
787 | |||
788 | uint32_t sys_getTimeStamp(struct System* sys) | ||
789 | { | ||
790 | (void) sys; | ||
791 | return (uint32_t) (*rb->current_tick) * 10; | ||
792 | } | ||
793 | |||
794 | static int16_t rb_soundbuf [MAX_SOUNDBUF_SIZE] IBSS_ATTR; | ||
795 | static int8_t temp_soundbuf[MAX_SOUNDBUF_SIZE] IBSS_ATTR; | ||
796 | static 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 | |||
816 | void 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 | |||
825 | void sys_stopAudio(struct System* sys) | ||
826 | { | ||
827 | (void) sys; | ||
828 | rb->pcm_play_stop(); | ||
829 | } | ||
830 | |||
831 | uint32_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 | |||
840 | static uint32_t cur_delay; | ||
841 | static void* cur_param; | ||
842 | static TimerCallback user_callback; | ||
843 | static 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 | |||
854 | void *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 | |||
865 | void 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 | |||
873 | void *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 | |||
890 | void 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 | |||
896 | void 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 | |||
903 | void 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 | |||
910 | uint8_t* getOffScreenFramebuffer(struct System* sys) | ||
911 | { | ||
912 | (void) sys; | ||
913 | return NULL; | ||
914 | } | ||
915 | |||
916 | void *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 | |||
932 | void 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 | |||
939 | void MutexStack_destroy(struct MutexStack_t* s) | ||
940 | { | ||
941 | sys_unlockMutex(s->sys, s->_mutex); | ||
942 | } | ||