summaryrefslogtreecommitdiff
path: root/apps/plugins/xworld/sys.c
diff options
context:
space:
mode:
authorFranklin Wei <frankhwei536@gmail.com>2014-10-13 21:00:47 -0400
committerMichael Giacomelli <giac2000@hotmail.com>2014-12-23 23:48:12 +0100
commit33cb13dee5a527ac445ea1b13d42723e4eb3e3b0 (patch)
tree3ce36ea21b53377b900049143e77e74b77ca1b0d /apps/plugins/xworld/sys.c
parentb681e932a9da797249ddc0e4ccab7ed7cf50fd41 (diff)
downloadrockbox-33cb13dee5a527ac445ea1b13d42723e4eb3e3b0.zip
rockbox-33cb13dee5a527ac445ea1b13d42723e4eb3e3b0.tar.gz
rockbox-33cb13dee5a527ac445ea1b13d42723e4eb3e3b0.tar.bz2
rockbox-33cb13dee5a527ac445ea1b13d42723e4eb3e3b0.tar.xz
Xworld - Another World interpreter for Rockbox
Co-conspirators: Franklin Wei, Benjamin Brown -------------------------------------------------------------------- This work is based on: - Fabien Sanglard's "Fabother World" based on - Piotr Padkowski's newRaw interpreter which was based on - Gregory Montoir's reverse engineering of - Eric Chahi's assembly code -------------------------------------------------------------------- Progress: * The plugin runs pretty nicely (with sound!) on most color targets * Keymaps for color LCD targets are complete * The manual entry is finished * Grayscale/monochrome support is NOT PLANNED - the game looks horrible in grayscale! :p -------------------------------------------------------------------- Notes: * The original game strings were built-in to the executable, and were copyrighted and could not be used. * This port ships with an alternate set of strings by default, but can load the "official" strings from a file at runtime. -------------------------------------------------------------------- To be done (in descending order of importance): * vertical stride compatibility <30% done> * optimization <10% done> Change-Id: I3155b0d97c2ac470cb8a2040f40d4139ddcebfa5 Reviewed-on: http://gerrit.rockbox.org/1077 Reviewed-by: Michael Giacomelli <giac2000@hotmail.com>
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 0000000..4bcdfaf
--- /dev/null
+++ b/apps/plugins/xworld/sys.c
@@ -0,0 +1,942 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2014 Franklin Wei, Benjamin Brown
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* TODO: */
+/* vertical stride support (as of Dec. 2014, only the M:Robe 500 has a color,
+ vertical stride LCD) */
+
+/* monochrome/grayscale support (many of these targets have vertical strides,
+ so get that working first!) */
+
+#include "plugin.h"
+#include "lib/display_text.h"
+#include "lib/helper.h"
+#include "lib/playback_control.h"
+#include "lib/pluginlib_actions.h"
+#include "lib/pluginlib_bmp.h"
+#include "lib/pluginlib_exit.h"
+#include "sys.h"
+#include "parts.h"
+#include "engine.h"
+#include "keymaps.h"
+
+static struct System* save_sys;
+
+static bool sys_save_settings(struct System* sys)
+{
+ File f;
+ file_create(&f, false);
+ if(!file_open(&f, SETTINGS_FILE, sys->e->_saveDir, "wb"))
+ {
+ return false;
+ }
+ file_write(&f, &sys->settings, sizeof(sys->settings));
+ file_close(&f);
+ return true;
+}
+
+static void sys_rotate_keymap(struct System* sys)
+{
+ switch(sys->settings.rotation_option)
+ {
+ case 0:
+ sys->keymap.up = BTN_UP;
+ sys->keymap.down = BTN_DOWN;
+ sys->keymap.left = BTN_LEFT;
+ sys->keymap.right = BTN_RIGHT;
+#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
+ sys->keymap.upleft = BTN_UP_LEFT;
+ sys->keymap.upright = BTN_UP_RIGHT;
+ sys->keymap.downleft = BTN_DOWN_RIGHT;
+ sys->keymap.downright = BTN_DOWN_LEFT;
+#endif
+ break;
+ case 1:
+ sys->keymap.up = BTN_RIGHT;
+ sys->keymap.down = BTN_LEFT;
+ sys->keymap.left = BTN_UP;
+ sys->keymap.right = BTN_DOWN;
+#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
+ sys->keymap.upleft = BTN_UP_RIGHT;
+ sys->keymap.upright = BTN_DOWN_RIGHT;
+ sys->keymap.downleft = BTN_UP_LEFT;
+ sys->keymap.downright = BTN_DOWN_LEFT;
+#endif
+ break;
+ case 2:
+ sys->keymap.up = BTN_LEFT;
+ sys->keymap.down = BTN_RIGHT;
+ sys->keymap.left = BTN_DOWN;
+ sys->keymap.right = BTN_UP;
+#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
+ sys->keymap.upleft = BTN_DOWN_LEFT;
+ sys->keymap.upright = BTN_UP_LEFT;
+ sys->keymap.downleft = BTN_DOWN_RIGHT;
+ sys->keymap.downright = BTN_DOWN_LEFT;
+#endif
+ break;
+ default:
+ error("sys_rotate_keymap: fall-through!");
+ }
+}
+
+static bool sys_load_settings(struct System* sys)
+{
+ File f;
+ file_create(&f, false);
+ if(!file_open(&f, SETTINGS_FILE, sys->e->_saveDir, "rb"))
+ {
+ return false;
+ }
+ file_read(&f, &sys->settings, sizeof(sys->settings));
+ file_close(&f);
+ sys_rotate_keymap(sys);
+ return true;
+}
+
+void exit_handler(void)
+{
+ sys_save_settings(save_sys);
+ sys_stopAudio(save_sys);
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ rb->cpu_boost(false);
+#endif
+ backlight_use_settings();
+}
+
+static bool sys_do_help(void)
+{
+#ifdef HAVE_LCD_COLOR
+ rb->lcd_set_foreground(LCD_WHITE);
+ rb->lcd_set_background(LCD_BLACK);
+#endif
+ rb->lcd_setfont(FONT_UI);
+ char* help_text[] = {"XWorld", "",
+ "XWorld", "is", "an", "interpreter", "for", "Another", "World,", "a", "fantastic", "game", "by", "Eric", "Chahi."
+ };
+ struct style_text style[] = {
+ {0, TEXT_CENTER | TEXT_UNDERLINE},
+ LAST_STYLE_ITEM
+ };
+ return display_text(ARRAYLEN(help_text), help_text, style, NULL, true);
+}
+
+static const struct opt_items scaling_settings[3] = {
+ { "Disabled", -1 },
+ { "Fast" , -1 },
+#ifdef HAVE_LCD_COLOR
+ { "Good" , -1 }
+#endif
+};
+
+static const struct opt_items rotation_settings[3] = {
+ { "Disabled" , -1 },
+ { "Clockwise" , -1 },
+ { "Anticlockwise", -1 }
+};
+
+static void do_video_settings(struct System* sys)
+{
+ MENUITEM_STRINGLIST(menu, "Video Settings", NULL,
+ "Negative",
+ "Scaling",
+ "Rotation",
+ "Show FPS",
+ "Zoom on code",
+ "Back");
+ int sel = 0;
+ while(1)
+ {
+ switch(rb->do_menu(&menu, &sel, NULL, false))
+ {
+ case 0:
+ rb->set_bool("Negative", &sys->settings.negative_enabled);
+ break;
+ case 1:
+ rb->set_option("Scaling", &sys->settings.scaling_quality, INT, scaling_settings,
+#ifdef HAVE_LCD_COLOR
+ 3
+#else
+ 2
+#endif
+ , NULL);
+ if(sys->settings.scaling_quality &&
+ sys->settings.zoom)
+ {
+ rb->splash(HZ*2, "Zoom automatically disabled.");
+ sys->settings.zoom = false;
+ }
+ break;
+ case 2:
+ rb->set_option("Rotation", &sys->settings.rotation_option, INT, rotation_settings, 3, NULL);
+ if(sys->settings.rotation_option &&
+ sys->settings.zoom)
+ {
+ rb->splash(HZ*2, "Zoom automatically disabled.");
+ sys->settings.zoom = false;
+ }
+ sys_rotate_keymap(sys);
+ break;
+ case 3:
+ rb->set_bool("Show FPS", &sys->settings.showfps);
+ break;
+ case 4:
+ rb->set_bool("Zoom on code", &sys->settings.zoom);
+ /* zoom only works with scaling and rotation disabled */
+ if(sys->settings.zoom &&
+ ( sys->settings.scaling_quality |
+ sys->settings.rotation_option))
+ {
+ rb->splash(HZ*2, "Scaling and rotation automatically disabled.");
+ sys->settings.scaling_quality = 0;
+ sys->settings.rotation_option = 0;
+ }
+ break;
+ case 5:
+ rb->lcd_clear_display();
+ sys_save_settings(sys);
+ return;
+ }
+ }
+}
+
+#define MAX_SOUNDBUF_SIZE 512
+const struct opt_items sound_bufsize_options[] = {
+ {"8 samples" , 8},
+ {"16 samples" , 16},
+ {"32 samples" , 32},
+ {"64 samples" , 64},
+ {"128 samples", 128},
+ {"256 samples", 256},
+ {"512 samples", 512},
+};
+
+static void do_sound_settings(struct System* sys)
+{
+ MENUITEM_STRINGLIST(menu, "Sound Settings", NULL,
+ "Enabled",
+ "Buffer Level",
+ "Back",
+ );
+ int sel = 0;
+ while(1)
+ {
+ switch(rb->do_menu(&menu, &sel, NULL, false))
+ {
+ case 0:
+ rb->set_bool("Enabled", &sys->settings.sound_enabled);
+ break;
+ case 1:
+ rb->set_option("Buffer Level", &sys->settings.sound_bufsize, INT,
+ sound_bufsize_options, ARRAYLEN(sound_bufsize_options), NULL);
+ break;
+ case 2:
+ sys_save_settings(sys);
+ return;
+ }
+ }
+}
+
+static void sys_reset_settings(struct System* sys)
+{
+ sys->settings.negative_enabled = false;
+ sys->settings.rotation_option = 0;
+ sys->settings.scaling_quality = 1;
+ sys->settings.sound_enabled = true;
+ sys->settings.sound_bufsize = 64;
+ sys->settings.showfps = true;
+ sys->settings.zoom = false;
+ sys_rotate_keymap(sys);
+}
+
+static struct System* mainmenu_sysptr;
+
+static int mainmenu_cb(int action, const struct menu_item_ex *this_item)
+{
+ int idx = ((intptr_t)this_item);
+ if(action == ACTION_REQUEST_MENUITEM && !mainmenu_sysptr->loaded && (idx == 0 || idx == 8 || idx == 10))
+ return ACTION_EXIT_MENUITEM;
+ return action;
+}
+
+static AudioCallback audio_callback;
+static void* audio_param;
+static struct System* audio_sys;
+
+/************************************** MAIN MENU ***************************************/
+
+void sys_menu(struct System* sys)
+{
+ sys_stopAudio(sys);
+ rb->splash(0, "Loading...");
+ sys->loaded = engine_loadGameState(sys->e, 0);
+ rb->lcd_update();
+ mainmenu_sysptr = sys;
+ int sel = 0;
+ MENUITEM_STRINGLIST(menu, "XWorld Menu", mainmenu_cb,
+ "Resume Game", /* 0 */
+ "Start New Game", /* 1 */
+ "Playback Control", /* 2 */
+ "Video Settings", /* 3 */
+ "Sound Settings", /* 4 */
+ "Fast Mode", /* 5 */
+ "Help", /* 6 */
+ "Reset Settings", /* 7 */
+ "Load", /* 8 */
+ "Save", /* 9 */
+ "Quit without Saving", /* 10 */
+ "Save and Quit"); /* 11 */
+ bool quit = false;
+ while(!quit)
+ {
+ int item;
+ switch(item = rb->do_menu(&menu, &sel, NULL, false))
+ {
+ case 0:
+ quit = true;
+ break;
+ case 1:
+ vm_initForPart(&sys->e->vm, GAME_PART_FIRST); // This game part is the protection screen
+ quit = true;
+ break;
+ case 2:
+ playback_control(NULL);
+ break;
+ case 3:
+ do_video_settings(sys);
+ break;
+ case 4:
+ do_sound_settings(sys);
+ break;
+ case 5:
+ rb->set_bool("Fast Mode", &sys->e->vm._fastMode);
+ sys_save_settings(sys);
+ break;
+ case 6:
+ sys_do_help();
+ break;
+ case 7:
+ sys_reset_settings(sys);
+ sys_save_settings(sys);
+ break;
+ case 8:
+ rb->splash(0, "Loading...");
+ sys->loaded = engine_loadGameState(sys->e, 0);
+ rb->lcd_update();
+ break;
+ case 9:
+ sys->e->_stateSlot = 0;
+ rb->splash(0, "Saving...");
+ engine_saveGameState(sys->e, sys->e->_stateSlot, "quicksave");
+ rb->lcd_update();
+ break;
+ case 10:
+ engine_deleteGameState(sys->e, 0);
+ exit(PLUGIN_OK);
+ break;
+ case 11:
+ /* saves are NOT deleted on loading */
+ exit(PLUGIN_OK);
+ break;
+ default:
+ error("sys_menu: fall-through!");
+ }
+ }
+ rb->lcd_clear_display();
+ sys_startAudio(sys, audio_callback, audio_param);
+}
+
+void sys_init(struct System* sys, const char* title)
+{
+ (void) title;
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ rb->cpu_boost(true);
+#endif
+ backlight_ignore_timeout();
+ rb_atexit(exit_handler);
+ save_sys = sys;
+ rb->memset(&sys->input, 0, sizeof(sys->input));
+ sys->mutex_bitfield = ~0;
+ if(!sys_load_settings(sys))
+ {
+ sys_reset_settings(sys);
+ }
+}
+
+void sys_destroy(struct System* sys)
+{
+ (void) sys;
+ rb->splash(HZ, "Exiting...");
+}
+
+void sys_setPalette(struct System* sys, uint8_t start, uint8_t n, const uint8_t *buf)
+{
+ for(int i = start; i < start + n; ++i)
+ {
+ uint8_t c[3];
+ for (int j = 0; j < 3; j++) {
+ uint8_t col = buf[i * 3 + j];
+ c[j] = (col << 2) | (col & 3);
+ }
+#if (LCD_DEPTH > 16) && (LCD_DEPTH <= 24)
+ sys->palette[i] = (fb_data) {
+ c[2], c[1], c[0]
+ };
+#elif defined(HAVE_LCD_COLOR)
+ sys->palette[i] = FB_RGBPACK(c[0], c[1], c[2]);
+#elif LCD_DEPTH > 1
+ sys->palette[i] = LCD_BRIGHTNESS((c[0] + c[1] + c[2]) / 3);
+#endif
+ }
+}
+
+/****************************** THE MAIN DRAWING METHOD ********************************/
+
+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)
+{
+ static int last_timestamp = 1;
+
+ /* get the address of the temporary framebuffer that has been allocated in the audiobuf */
+ fb_data* framebuffer = (fb_data*) sys->e->res._memPtrStart + MEM_BLOCK_SIZE + (4 * VID_PAGE_SIZE);
+ buf += y * pitch + x;
+
+ /************************ BLIT THE TEMPORARY FRAMEBUFFER ***********************/
+
+ if(sys->settings.rotation_option)
+ {
+ /* clockwise */
+ if(sys->settings.rotation_option == 1)
+ {
+ while (h--) {
+ /* one byte gives two pixels */
+ for (int i = 0; i < w / 2; ++i) {
+ uint8_t pix1 = *(buf + i) >> 4;
+ uint8_t pix2 = *(buf + i) & 0xF;
+#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+ framebuffer[( (h * 2) ) * 320 + i] = sys->palette[pix1];
+ framebuffer[( (h * 2) + 1) * 320 + i] = sys->palette[pix2];
+#else
+ framebuffer[( (i * 2) ) * 200 + h] = sys->palette[pix1];
+ framebuffer[( (i * 2) + 1) * 200 + h] = sys->palette[pix2];
+#endif
+ }
+ buf += pitch;
+ }
+ }
+ /* counterclockwise */
+ else
+ {
+ while (h--) {
+ /* one byte gives two pixels */
+ for (int i = 0; i < w / 2; ++i) {
+ uint8_t pix1 = *(buf + i) >> 4;
+ uint8_t pix2 = *(buf + i) & 0xF;
+#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+ framebuffer[(200 - h * 2 ) * 320 + ( 320 - i )] = sys->palette[pix1];
+ framebuffer[(200 - h * 2 - 1) * 320 + ( 320 - i )] = sys->palette[pix2];
+#else
+ framebuffer[(320 - i * 2 ) * 200 + ( 200 - h )] = sys->palette[pix1];
+ framebuffer[(320 - i * 2 - 1) * 200 + ( 200 - h )] = sys->palette[pix2];
+#endif
+ }
+ buf += pitch;
+ }
+ }
+ }
+ /* no rotation */
+ else
+ {
+ int next = 0;
+#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+ for(int x = 0; x < w / 2; ++x)
+ {
+ for(int y = 0; y < h; ++y)
+ {
+ uint8_t pix1 = buf[ y * w + x ] >> 4;
+ uint8_t pix2 = buf[ y * w + x ] & 0xF;
+ framebuffer[(x + 0)*h + y] = sys->palette[pix1];
+ framebuffer[(x + 1)*h + y] = sys->palette[pix2];
+ }
+ }
+#else
+ while (h--) {
+ /* one byte gives two pixels */
+ for (int i = 0; i < w / 2; ++i) {
+ uint8_t pix1 = *(buf + i) >> 4;
+ uint8_t pix2 = *(buf + i) & 0xF;
+ framebuffer[next] = sys->palette[pix1];
+ ++next;
+ framebuffer[next] = sys->palette[pix2];
+ ++next;
+ }
+ buf += pitch;
+ }
+#endif
+ }
+
+ /*************************** NOW SCALE IT! ***************************/
+
+ if(sys->settings.scaling_quality)
+ {
+ struct bitmap in_bmp;
+ if(sys->settings.rotation_option)
+ {
+#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+ in_bmp.width = 320;
+ in_bmp.height = 200;
+#else
+ in_bmp.width = 200;
+ in_bmp.height = 320;
+#endif
+ }
+ else
+ {
+#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+ in_bmp.width = 200;
+ in_bmp.height = 320;
+#else
+ in_bmp.width = 320;
+ in_bmp.height = 200;
+#endif
+ }
+ in_bmp.data = (unsigned char*) framebuffer;
+ struct bitmap out_bmp;
+ out_bmp.width = LCD_WIDTH;
+ out_bmp.height = LCD_HEIGHT;
+ out_bmp.data = (unsigned char*) rb->lcd_framebuffer;
+
+#ifdef HAVE_LCD_COLOR
+ if(sys->settings.scaling_quality == 1)
+#endif
+ simple_resize_bitmap(&in_bmp, &out_bmp);
+#ifdef HAVE_LCD_COLOR
+ else
+ smooth_resize_bitmap(&in_bmp, &out_bmp);
+#endif
+ }
+ else
+ {
+#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+ for(int x = 0; x < 320; ++x)
+ {
+ for(int y = 0; y < 200; ++y)
+ {
+ rb->lcd_set_foreground(framebuffer[x * 200 + y]);
+ rb->lcd_drawpixel(x, y);
+ }
+ }
+#else
+ if(sys->settings.zoom)
+ {
+ rb->lcd_bitmap_part(framebuffer, CODE_X, CODE_Y, STRIDE(SCREEN_MAIN, 320, 200), 0, 0, 320 - CODE_X, 200 - CODE_Y);
+ }
+ else
+ {
+ if(sys->settings.rotation_option)
+ rb->lcd_bitmap(framebuffer, 0, 0, 200, 320);
+ else
+ rb->lcd_bitmap(framebuffer, 0, 0, 320, 200);
+ }
+#endif
+ }
+
+ /************************************* APPLY FILTERS ******************************************/
+
+ if(sys->settings.negative_enabled)
+ {
+ for(int y = 0; y < LCD_HEIGHT; ++y)
+ {
+ for(int x = 0; x < LCD_WIDTH; ++x)
+ {
+#ifdef HAVE_LCD_COLOR
+ int r, g, b;
+ fb_data pix = rb->lcd_framebuffer[y * LCD_WIDTH + x];
+ r = RGB_UNPACK_RED (pix);
+ g = RGB_UNPACK_GREEN(pix);
+ b = RGB_UNPACK_BLUE (pix);
+ rb->lcd_framebuffer[y * LCD_WIDTH + x] = LCD_RGBPACK(0xff - r, 0xff - g, 0xff - b);
+#else
+ rb->lcd_framebuffer[y * LCD_WIDTH + x] = LCD_BRIGHTNESS(0xff - rb->lcd_framebuffer[y * LCD_WIDTH + x]);
+#endif
+ }
+ }
+ }
+
+ /*********************** SHOW FPS *************************/
+
+ int current_time = sys_getTimeStamp(sys);
+ if(sys->settings.showfps)
+ {
+ rb->lcd_set_foreground(LCD_BLACK);
+ rb->lcd_set_background(LCD_WHITE);
+ /* use 1000 and not HZ here because getTimeStamp is in milliseconds */
+ rb->lcd_putsf(0, 0, "FPS: %d", 1000 / ((current_time - last_timestamp == 0) ? 1 : current_time - last_timestamp));
+ }
+ rb->lcd_update();
+ last_timestamp = sys_getTimeStamp(sys);
+}
+
+static void do_pause_menu(struct System* sys)
+{
+ sys_stopAudio(sys);
+ int sel = 0;
+ MENUITEM_STRINGLIST(menu, "XWorld Menu", NULL,
+ "Resume Game", /* 0 */
+ "Start New Game", /* 1 */
+ "Playback Control", /* 2 */
+ "Video Settings", /* 3 */
+ "Sound Settings", /* 4 */
+ "Fast Mode", /* 5 */
+ "Enter Code", /* 6 */
+ "Help", /* 7 */
+ "Reset Settings", /* 8 */
+ "Load", /* 9 */
+ "Save", /* 10 */
+ "Quit"); /* 11 */
+
+ bool quit = false;
+ while(!quit)
+ {
+ switch(rb->do_menu(&menu, &sel, NULL, false))
+ {
+ case 0:
+ quit = true;
+ break;
+ case 1:
+ vm_initForPart(&sys->e->vm, GAME_PART_FIRST);
+ quit = true;
+ break;
+ case 2:
+ playback_control(NULL);
+ break;
+ case 3:
+ do_video_settings(sys);
+ break;
+ case 4:
+ do_sound_settings(sys);
+ break;
+ case 5:
+ rb->set_bool("Fast Mode", &sys->e->vm._fastMode);
+ sys_save_settings(sys);
+ break;
+ case 6:
+ sys->input.code = true;
+ quit = true;
+ break;
+ case 7:
+ sys_do_help();
+ break;
+ case 8:
+ sys_reset_settings(sys);
+ sys_save_settings(sys);
+ break;
+ case 9:
+ rb->splash(0, "Loading...");
+ sys->loaded = engine_loadGameState(sys->e, 0);
+ rb->lcd_update();
+ quit = true;
+ break;
+ case 10:
+ sys->e->_stateSlot = 0;
+ rb->splash(0, "Saving...");
+ engine_saveGameState(sys->e, sys->e->_stateSlot, "quicksave");
+ rb->lcd_update();
+ break;
+ case 11:
+ exit(PLUGIN_OK);
+ break;
+ }
+ }
+ rb->lcd_clear_display();
+ sys_startAudio(sys, audio_callback, audio_param);
+}
+
+void sys_processEvents(struct System* sys)
+{
+ int btn = rb->button_get(false);
+ btn &= ~BUTTON_REDRAW;
+ debug(DBG_SYS, "button is 0x%08x", btn);
+
+ /* exit early if we can */
+ if(btn == BUTTON_NONE)
+ {
+ return;
+ }
+
+ /* Ignore some buttons that cause errant input */
+#if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_3G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_1G2G_PAD)
+ if(btn & 0x80000000)
+ return;
+#endif
+#if (CONFIG_KEYPAD == SANSA_E200_PAD)
+ if(btn == (BUTTON_SCROLL_FWD || BUTTON_SCROLL_BACK))
+ return;
+#endif
+#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
+ if(btn == (BUTTON_SELECT))
+ return;
+#endif
+
+ /* handle special keys first */
+ switch(btn)
+ {
+ case BTN_PAUSE:
+ do_pause_menu(sys);
+ sys->input.dirMask = 0;
+ sys->input.button = false;
+ /* exit early to avoid unwanted button presses being detected */
+ return;
+ default:
+ exit_on_usb(btn);
+ break;
+ }
+
+ /* handle releases */
+ if(btn & BUTTON_REL)
+ {
+ debug(DBG_SYS, "button_rel");
+ btn &= ~BUTTON_REL;
+
+ if(btn & BTN_FIRE)
+ sys->input.button = false;
+ if(btn & sys->keymap.up)
+ sys->input.dirMask &= ~DIR_UP;
+ if(btn & sys->keymap.down)
+ sys->input.dirMask &= ~DIR_DOWN;
+ if(btn & sys->keymap.left)
+ sys->input.dirMask &= ~DIR_LEFT;
+ if(btn & sys->keymap.right)
+ sys->input.dirMask &= ~DIR_RIGHT;
+#ifdef BTN_DOWN_LEFT
+ if(btn & sys->keymap.downleft)
+ sys->input.dirMask &= ~(DIR_DOWN | DIR_LEFT);
+#endif
+#ifdef BTN_DOWN_RIGHT
+ if(btn & sys->keymap.downright)
+ sys->input.dirMask &= ~(DIR_DOWN | DIR_RIGHT);
+#endif
+#ifdef BTN_UP_LEFT
+ if(btn & sys->keymap.upleft)
+ sys->input.dirMask &= ~(DIR_UP | DIR_LEFT);
+#endif
+#ifdef BTN_UP_RIGHT
+ if(btn & sys->keymap.upright)
+ sys->input.dirMask &= ~(DIR_UP | DIR_RIGHT);
+#endif
+ }
+ /* then handle presses */
+ else
+ {
+ if(btn & BTN_FIRE)
+ sys->input.button = true;
+ if(btn & sys->keymap.up)
+ sys->input.dirMask |= DIR_UP;
+ if(btn & sys->keymap.down)
+ sys->input.dirMask |= DIR_DOWN;
+ if(btn & sys->keymap.left)
+ sys->input.dirMask |= DIR_LEFT;
+ if(btn & sys->keymap.right)
+ sys->input.dirMask |= DIR_RIGHT;
+#ifdef BTN_DOWN_LEFT
+ if(btn & sys->keymap.downleft)
+ sys->input.dirMask |= (DIR_DOWN | DIR_LEFT);
+#endif
+#ifdef BTN_DOWN_RIGHT
+ if(btn & sys->keymap.downright)
+ sys->input.dirMask |= (DIR_DOWN | DIR_RIGHT);
+#endif
+#ifdef BTN_UP_LEFT
+ if(btn & sys->keymap.upleft)
+ sys->input.dirMask |= (DIR_UP | DIR_LEFT);
+#endif
+#ifdef BTN_UP_RIGHT
+ if(btn & sys->keymap.upright)
+ sys->input.dirMask |= (DIR_UP | DIR_RIGHT);
+#endif
+ }
+ debug(DBG_SYS, "dirMask is 0x%02x", sys->input.dirMask);
+ debug(DBG_SYS, "button is %s", sys->input.button == true ? "true" : "false");
+}
+
+void sys_sleep(struct System* sys, uint32_t duration)
+{
+ (void) sys;
+ /* duration is in ms */
+ rb->sleep(duration / 10);
+}
+
+uint32_t sys_getTimeStamp(struct System* sys)
+{
+ (void) sys;
+ return (uint32_t) (*rb->current_tick) * 10;
+}
+
+static int16_t rb_soundbuf [MAX_SOUNDBUF_SIZE] IBSS_ATTR;
+static int8_t temp_soundbuf[MAX_SOUNDBUF_SIZE] IBSS_ATTR;
+static void ICODE_ATTR get_more(const void** start, size_t* size)
+{
+ if(audio_sys->settings.sound_enabled)
+ {
+ audio_callback(audio_param, temp_soundbuf, audio_sys->settings.sound_bufsize);
+ /* convert xworld format (signed 8-bit) to rockbox format (signed 16-bit) */
+ for(int i = 0; i < audio_sys->settings.sound_bufsize; ++i)
+ {
+ rb_soundbuf[i] = temp_soundbuf[i] * 0x100;
+ }
+ *start = rb_soundbuf;
+ *size = audio_sys->settings.sound_bufsize;
+ }
+ else
+ {
+ *start = NULL;
+ *size = 0;
+ }
+}
+
+void sys_startAudio(struct System* sys, AudioCallback callback, void *param)
+{
+ (void) sys;
+ audio_callback = callback;
+ audio_param = param;
+ audio_sys = sys;
+ rb->pcm_play_data(get_more, NULL, NULL, 0);
+}
+
+void sys_stopAudio(struct System* sys)
+{
+ (void) sys;
+ rb->pcm_play_stop();
+}
+
+uint32_t sys_getOutputSampleRate(struct System* sys)
+{
+ (void) sys;
+ return rb->mixer_get_frequency();
+}
+
+/* pretty non-reentrant here, but who cares? it's not like someone
+ would want to run two instances of the game on Rockbox! :D */
+
+static uint32_t cur_delay;
+static void* cur_param;
+static TimerCallback user_callback;
+static void timer_callback(void)
+{
+ debug(DBG_SYS, "timer callback with delay of %d ms callback 0x%08x param 0x%08x", cur_delay, timer_callback, cur_param);
+ uint32_t ret = user_callback(cur_delay, cur_param);
+ if(ret != cur_delay)
+ {
+ cur_delay = ret;
+ rb->timer_register(1, NULL, TIMER_FREQ / 1000 * ret, timer_callback IF_COP(, CPU));
+ }
+}
+
+void *sys_addTimer(struct System* sys, uint32_t delay, TimerCallback callback, void *param)
+{
+ (void) sys;
+ debug(DBG_SYS, "registering timer with delay of %d ms callback 0x%08x param 0x%08x", delay, callback, param);
+ user_callback = callback;
+ cur_delay = delay;
+ cur_param = param;
+ rb->timer_register(1, NULL, TIMER_FREQ / 1000 * delay, timer_callback IF_COP(, CPU));
+ return NULL;
+}
+
+void sys_removeTimer(struct System* sys, void *timerId)
+{
+ (void) sys;
+ (void) timerId;
+ /* there's only one timer per game instance, so ignore both parameters */
+ rb->timer_unregister();
+}
+
+void *sys_createMutex(struct System* sys)
+{
+ if(!sys)
+ error("sys is NULL!");
+ for(int i = 0; i < MAX_MUTEXES; ++i)
+ {
+ if(sys->mutex_bitfield & (1 << i))
+ {
+ rb->mutex_init(&sys->mutex_memory[i]);
+ sys->mutex_bitfield |= (1 << i);
+ return &sys->mutex_memory[i];
+ }
+ }
+ warning("Out of mutexes!");
+ return NULL;
+}
+
+void sys_destroyMutex(struct System* sys, void *mutex)
+{
+ int mutex_number = ((char*)mutex - (char*)sys->mutex_memory) / sizeof(struct mutex); /* pointer arithmetic! check for bugs! */
+ sys->mutex_bitfield &= ~(1 << mutex_number);
+}
+
+void sys_lockMutex(struct System* sys, void *mutex)
+{
+ (void) sys;
+ debug(DBG_SYS, "calling mutex_lock");
+ rb->mutex_lock((struct mutex*) mutex);
+}
+
+void sys_unlockMutex(struct System* sys, void *mutex)
+{
+ (void) sys;
+ debug(DBG_SYS, "calling mutex_unlock");
+ rb->mutex_unlock((struct mutex*) mutex);
+}
+
+uint8_t* getOffScreenFramebuffer(struct System* sys)
+{
+ (void) sys;
+ return NULL;
+}
+
+void *sys_get_buffer(struct System* sys, size_t sz)
+{
+ if((signed int)sys->bytes_left - (signed int)sz >= 0)
+ {
+ void* ret = sys->membuf;
+ rb->memset(ret, 0, sz);
+ sys->membuf += sz;
+ return ret;
+ }
+ else
+ {
+ error("sys_get_buffer: out of memory!");
+ return NULL;
+ }
+}
+
+void MutexStack(struct MutexStack_t* s, struct System *stub, void *mutex)
+{
+ s->sys = stub;
+ s->_mutex = mutex;
+ sys_lockMutex(s->sys, s->_mutex);
+}
+
+void MutexStack_destroy(struct MutexStack_t* s)
+{
+ sys_unlockMutex(s->sys, s->_mutex);
+}