1
0
Fork 0
forked from len0rd/rockbox
foxbox/apps/plugins/xworld/sys.c
William Wilgus 7100090f99 Remove NVRAM infavor of a separate cfg file
remove nvram and use the existing settings framework for it

add a crc check to the user_settings data to see if we need to save
the user setting file or if we can just save the status file (resume.cfg)

move volume to the system_status struct so we don't write the whole settings file
over volume changes

allow user to still export volume with save sound settings

allow the user to also export pitch and speed

name the file .resume.cfg

Rename all the SYSTEM_STATUS save file variables to TLAs to save space and
discourage tinkering

Cleanup DEBUG_AVAIL_SETTINGS output

when saving user_settings it calls status_save as well this cause the resume
file to be written twice. instead remove the callback for status_save
when setting_save is called

remove header text when saving .resume.cfg

convert status_save() to status_save(bool force)
add SYSTEM_STATUS_UPDATE_TICKS

for ATA device set this to 5 minutes
since we arlready wait for the disk to be up before saving
we don't want to miss our window

for all other every 15 minutes

that way if the battery is too low by the time shutdown comes around you
don't lose much progress

Change-Id: I27214ffd6e5d5494ee5ca83b14f04a41ba426ad7
2025-01-21 00:04:32 -05:00

1158 lines
34 KiB
C

/***************************************************************************
* __________ __ ___.
* 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 grayscale targets have vertical strides,
so get that working first!) */
#include "plugin.h"
#include "lib/display_text.h"
#include "lib/helper.h"
#include "lib/pluginlib_actions.h"
#include "lib/pluginlib_bmp.h"
#include "lib/pluginlib_exit.h"
#include "lib/keymaps.h"
#include "sys.h"
#include "parts.h"
#include "engine.h"
static struct System* save_sys;
static fb_data *lcd_fb = NULL;
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;
#ifdef BTN_HAVE_DIAGONAL
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;
#ifdef BTN_HAVE_DIAGONAL
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;
#ifdef BTN_HAVE_DIAGONAL
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);
rb->timer_unregister();
#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", "a", "port", "of", "a", "bytecode", "interpreter", "for", "`Another", "World',", "a", "cinematic", "adventure", "game", "by", "Eric", "Chahi.",
"",
"",
"Level", "Codes:", "",
"Level", "1:", "LDKD", "",
"Level", "2:", "HTDC", "",
"Level", "3:", "CLLD", "",
"Level", "4:", "LBKG", "",
"Level", "5:", "XDDJ", "",
"Level", "6:", "FXLC", "",
"Level", "7:", "KRFK", "",
"Level", "8:", "KFLB", "",
"Level", "9:", "DDRX", "",
"Level", "10:", "BFLX", "",
"Level", "11:", "BRTD", "",
"Level", "12:", "TFBB", "",
"Level", "13:", "TXHF", "",
"Level", "14:", "CKJL", "",
"Level", "15:", "LFCK", "",
};
struct style_text style[] = {
{ 0, TEXT_CENTER | TEXT_UNDERLINE },
};
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 },
{ "Counterclockwise", -1 }
};
static void do_video_settings(struct System* sys)
{
MENUITEM_STRINGLIST(menu, "Video Settings", NULL,
"Negative",
#ifdef SYS_MOTION_BLUR
"Motion Blur",
#endif
"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;
#ifdef SYS_MOTION_BLUR
case 1:
rb->set_bool("Motion Blur", &sys->settings.blur);
break;
#endif
#ifndef SYS_MOTION_BLUR
case 1:
#else
case 2:
#endif
rb->set_option("Scaling", &sys->settings.scaling_quality, RB_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;
#ifndef SYS_MOTION_BLUR
case 2:
#else
case 3:
#endif
rb->set_option("Rotation", &sys->settings.rotation_option, RB_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;
#ifndef SYS_MOTION_BLUR
case 3:
#else
case 4:
#endif
rb->set_bool("Show FPS", &sys->settings.showfps);
break;
#ifndef SYS_MOTION_BLUR
case 4:
#else
case 5:
#endif
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;
default:
sys_save_settings(sys);
return;
}
}
}
#define MAX_SOUNDBUF_SIZE 256
static void do_sound_settings(struct System* sys)
{
MENUITEM_STRINGLIST(menu, "Sound Settings", NULL,
"Enabled",
"Buffer Level",
"Volume",
"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_int("Buffer Level", "samples", UNIT_INT, &sys->settings.sound_bufsize, NULL, 16, 16, MAX_SOUNDBUF_SIZE, NULL);
break;
case 2:
{
const struct settings_list* vol =
rb->find_setting(&rb->global_status->volume);
rb->option_screen((struct settings_list*)vol, NULL, false, "Volume");
break;
}
case 3:
default:
sys_save_settings(sys);
return;
}
}
}
static void sys_reset_settings(struct System* sys)
{
sys->settings.negative_enabled = false;
sys->settings.rotation_option = 0; // off
sys->settings.scaling_quality = 1; // fast
sys->settings.sound_enabled = true;
sys->settings.sound_bufsize = 32; /* keep this low */
sys->settings.showfps = false;
sys->settings.zoom = false;
sys->settings.blur = false;
sys_rotate_keymap(sys);
}
static struct System* mainmenu_sysptr;
static int mainmenu_cb(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list)
{
(void)this_list;
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 = NULL;
static void get_more(const void** start, size_t* size);
static void* audio_param;
static struct System* audio_sys;
/************************************** MAIN MENU ***************************************/
/* called after game init */
void sys_menu(struct System* sys)
{
sys_stopAudio(sys);
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
/* boost for load */
rb->cpu_boost(true);
#endif
rb->splash(0, "Loading...");
sys->loaded = engine_loadGameState(sys->e, 0);
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
rb->lcd_update();
mainmenu_sysptr = sys;
MENUITEM_STRINGLIST(menu, "XWorld Menu", mainmenu_cb,
"Resume Game", /* 0 */
"Start New Game", /* 1 */
"Video Settings", /* 2 */
"Sound Settings", /* 3 */
"Fast Mode", /* 4 */
"Help", /* 5 */
"Reset Settings", /* 6 */
"Load", /* 7 */
"Save", /* 8 */
"Quit without Saving", /* 9 */
"Save and Quit"); /* 10 */
bool quit = false;
while(!quit)
{
switch(rb->do_menu(&menu, NULL, 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:
do_video_settings(sys);
break;
case 3:
do_sound_settings(sys);
break;
case 4:
rb->set_bool("Fast Mode", &sys->e->vm._fastMode);
sys_save_settings(sys);
break;
case 5:
sys_do_help();
break;
case 6:
sys_reset_settings(sys);
sys_save_settings(sys);
break;
case 7:
rb->splash(0, "Loading...");
sys->loaded = engine_loadGameState(sys->e, 0);
rb->lcd_update();
break;
case 8:
sys->e->_stateSlot = 0;
rb->splash(0, "Saving...");
engine_saveGameState(sys->e, sys->e->_stateSlot, "quicksave");
rb->lcd_update();
break;
case 9:
engine_deleteGameState(sys->e, 0);
exit(PLUGIN_OK);
break;
case 10:
/* saves are NOT deleted on loading */
exit(PLUGIN_OK);
break;
default:
break;
}
}
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
/* boost for game */
rb->cpu_boost(true);
#endif
sys_startAudio(sys, audio_callback, audio_param);
}
void sys_init(struct System* sys, const char* title)
{
(void) title;
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);
}
struct viewport *vp_main = rb->lcd_set_viewport(NULL);
lcd_fb = vp_main->buffer->fb_ptr;
}
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 > 24)
sys->palette[i] = (fb_data) {
c[2], c[1], c[0], 255
};
#elif (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 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 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 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 LCD_STRIDEFORMAT == VERTICAL_STRIDE
in_bmp.width = 320;
in_bmp.height = 200;
#else
in_bmp.width = 200;
in_bmp.height = 320;
#endif
}
else
{
#if 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*) lcd_fb;
#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 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 = lcd_fb[y * LCD_WIDTH + x];
#if (LCD_DEPTH > 24)
r = 0xff - pix.r;
g = 0xff - pix.g;
b = 0xff - pix.b;
lcd_fb[y * LCD_WIDTH + x] = (fb_data) { b, g, r, 255 };
#elif (LCD_DEPTH == 24)
r = 0xff - pix.r;
g = 0xff - pix.g;
b = 0xff - pix.b;
lcd_fb[y * LCD_WIDTH + x] = (fb_data) { b, g, r };
#else
r = RGB_UNPACK_RED (pix);
g = RGB_UNPACK_GREEN(pix);
b = RGB_UNPACK_BLUE (pix);
lcd_fb[y * LCD_WIDTH + x] = LCD_RGBPACK(0xff - r, 0xff - g, 0xff - b);
#endif
#else
lcd_fb[y * LCD_WIDTH + x] = LCD_BRIGHTNESS(0xff - lcd_fb[y * LCD_WIDTH + x]);
#endif
}
}
}
#ifdef SYS_MOTION_BLUR
if(sys->settings.blur)
{
static fb_data *prev_frames = NULL;
static fb_data *orig_fb = NULL;
static int prev_baseidx = 0; /* circular buffer */
if(!prev_frames)
{
prev_frames = sys_get_buffer(sys, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT * BLUR_FRAMES);
orig_fb = sys_get_buffer(sys, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT);
rb->memset(prev_frames, 0, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT * BLUR_FRAMES);
}
if(prev_frames && orig_fb)
{
rb->memcpy(orig_fb, lcd_fb, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT);
/* fancy useless slow motion blur */
for(int y = 0; y < LCD_HEIGHT; ++y)
{
for(int x = 0; x < LCD_WIDTH; ++x)
{
int r, g, b;
fb_data pix = lcd_fb[y * LCD_WIDTH + x];
r = RGB_UNPACK_RED (pix);
g = RGB_UNPACK_GREEN(pix);
b = RGB_UNPACK_BLUE (pix);
r *= BLUR_FRAMES + 1;
g *= BLUR_FRAMES + 1;
b *= BLUR_FRAMES + 1;
for(int i = 0; i < BLUR_FRAMES; ++i)
{
fb_data prev_pix = prev_frames[ (prev_baseidx + i * (LCD_WIDTH * LCD_HEIGHT)) % (LCD_WIDTH * LCD_HEIGHT * BLUR_FRAMES) + y * LCD_WIDTH + x];
r += (BLUR_FRAMES-i) * RGB_UNPACK_RED(prev_pix);
g += (BLUR_FRAMES-i) * RGB_UNPACK_GREEN(prev_pix);
b += (BLUR_FRAMES-i) * RGB_UNPACK_BLUE(prev_pix);
}
r /= (BLUR_FRAMES + 1) / 2 * (1 + BLUR_FRAMES + 1);
g /= (BLUR_FRAMES + 1) / 2 * (1 + BLUR_FRAMES + 1);
b /= (BLUR_FRAMES + 1) / 2 * (1 + BLUR_FRAMES + 1);
lcd_fb[y * LCD_WIDTH + x] = LCD_RGBPACK(r, g, b);
}
}
prev_baseidx -= LCD_WIDTH * LCD_HEIGHT;
if(prev_baseidx < 0)
prev_baseidx += BLUR_FRAMES * LCD_WIDTH * LCD_HEIGHT;
rb->memcpy(prev_frames + prev_baseidx, orig_fb, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT);
}
}
#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);
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
int sel = 0;
MENUITEM_STRINGLIST(menu, "XWorld Menu", NULL,
"Resume Game", /* 0 */
"Start New Game", /* 1 */
"Video Settings", /* 2 */
"Sound Settings", /* 3 */
"Fast Mode", /* 4 */
"Enter Code", /* 5 */
"Help", /* 6 */
"Reset Settings", /* 7 */
"Load", /* 8 */
"Save", /* 9 */
"Quit"); /* 10 */
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:
do_video_settings(sys);
break;
case 3:
do_sound_settings(sys);
break;
case 4:
rb->set_bool("Fast Mode", &sys->e->vm._fastMode);
sys_save_settings(sys);
break;
case 5:
sys->input.code = true;
quit = true;
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();
quit = true;
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:
exit(PLUGIN_OK);
break;
}
}
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(true);
#endif
sys_startAudio(sys, audio_callback, audio_param);
}
void sys_processEvents(struct System* sys)
{
int btn = rb->button_status();
rb->button_clear_queue();
static int oldbuttonstate = 0;
debug(DBG_SYS, "button is 0x%08x", btn);
/* exit early if we can */
if(btn == oldbuttonstate)
{
return;
}
/* 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;
}
/* Ignore some buttons that cause errant input */
btn &= ~BUTTON_REDRAW;
#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
/* copied from doom which was copied from rockboy... */
unsigned released = ~btn & oldbuttonstate;
unsigned pressed = btn & ~oldbuttonstate;
oldbuttonstate = btn;
if(released)
{
if(released & BTN_FIRE)
sys->input.button = false;
if(released & sys->keymap.up)
sys->input.dirMask &= ~DIR_UP;
if(released & sys->keymap.down)
sys->input.dirMask &= ~DIR_DOWN;
if(released & sys->keymap.left)
sys->input.dirMask &= ~DIR_LEFT;
if(released & sys->keymap.right)
sys->input.dirMask &= ~DIR_RIGHT;
#ifdef BTN_DOWN_LEFT
if(released & sys->keymap.downleft)
sys->input.dirMask &= ~(DIR_DOWN | DIR_LEFT);
#endif
#ifdef BTN_DOWN_RIGHT
if(released & sys->keymap.downright)
sys->input.dirMask &= ~(DIR_DOWN | DIR_RIGHT);
#endif
#ifdef BTN_UP_LEFT
if(released & sys->keymap.upleft)
sys->input.dirMask &= ~(DIR_UP | DIR_LEFT);
#endif
#ifdef BTN_UP_RIGHT
if(released & sys->keymap.upright)
sys->input.dirMask &= ~(DIR_UP | DIR_RIGHT);
#endif
}
if(pressed)
{
if(pressed & BTN_FIRE)
sys->input.button = true;
if(pressed & sys->keymap.up)
sys->input.dirMask |= DIR_UP;
if(pressed & sys->keymap.down)
sys->input.dirMask |= DIR_DOWN;
if(pressed & sys->keymap.left)
sys->input.dirMask |= DIR_LEFT;
if(pressed & sys->keymap.right)
sys->input.dirMask |= DIR_RIGHT;
#ifdef BTN_DOWN_LEFT
if(pressed & sys->keymap.downleft)
sys->input.dirMask |= (DIR_DOWN | DIR_LEFT);
#endif
#ifdef BTN_DOWN_RIGHT
if(pressed & sys->keymap.downright)
sys->input.dirMask |= (DIR_DOWN | DIR_RIGHT);
#endif
#ifdef BTN_UP_LEFT
if(pressed & sys->keymap.upleft)
sys->input.dirMask |= (DIR_UP | DIR_LEFT);
#endif
#ifdef BTN_UP_RIGHT
if(pressed & sys->keymap.upright)
sys->input.dirMask |= (DIR_UP | DIR_RIGHT);
#endif
}
#if 0
/* 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");
#endif
}
void sys_sleep(struct System* sys, uint32_t duration)
{
(void) sys;
/* duration is in ms */
rb->sleep(duration / (1000/HZ));
}
uint32_t sys_getTimeStamp(struct System* sys)
{
(void) sys;
return (uint32_t) (*rb->current_tick * (1000/HZ));
}
/* game provides us mono samples, we need stereo */
static int16_t rb_soundbuf[MAX_SOUNDBUF_SIZE * 2];
static int8_t temp_soundbuf[MAX_SOUNDBUF_SIZE];
static void get_more(const void** start, size_t* size)
{
if(audio_sys->settings.sound_enabled && audio_callback)
{
audio_callback(audio_param, temp_soundbuf, audio_sys->settings.sound_bufsize);
/* convert xworld format (signed 8-bit) to rockbox format (stereo signed 16-bit) */
for(int i = 0; i < audio_sys->settings.sound_bufsize; ++i)
{
rb_soundbuf[2*i] = rb_soundbuf[2*i+1] = temp_soundbuf[i] * 256;
}
}
else
{
rb->memset(rb_soundbuf, 0, audio_sys->settings.sound_bufsize * 2 * sizeof(int16_t));
}
*start = rb_soundbuf;
*size = audio_sys->settings.sound_bufsize * 2 * sizeof(int16_t);
}
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!");
debug(DBG_SYS, "allocating mutex");
/* this bitfield works as follows: bit set = free, unset = in use */
for(int i = 0; i < MAX_MUTEXES; ++i)
{
/* check that the corresponding bit is 1 (free) */
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;
rb->mutex_lock((struct mutex*) mutex);
}
void sys_unlockMutex(struct System* sys, void *mutex)
{
(void) sys;
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 = (char*)(sys->membuf) + sz;
sys->bytes_left -= 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;
/* FW 2017-2-12: disabled; no blocking ops in IRQ context! */
/*sys_lockMutex(s->sys, s->_mutex);*/
}
void MutexStack_destroy(struct MutexStack_t* s)
{
(void) s;
/*sys_unlockMutex(s->sys, s->_mutex);*/
}