05733649bc
Fixes sound on most platforms, original root cause was bad menu code as well as DMA callbacks taking too long. Worked around with smaller chunk sizes. Permanent fix would include moving mixing out of the callback. Rewrites input with code from rockboy/doom. Cherry-picks a change from Gregory Montoir's `rawgl' to patch the code wheel screen. Finally, adds a motion blur filter on select targets. Change-Id: I8df549c923c5075800c6625c36c8202e53de1d27
1138 lines
33 KiB
C
1138 lines
33 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 "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", "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, 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, 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_settings->volume, NULL);
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
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];
|
|
#if (LCD_DEPTH == 24)
|
|
r = 0xff - pix.r;
|
|
g = 0xff - pix.g;
|
|
b = 0xff - pix.b;
|
|
rb->lcd_framebuffer[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);
|
|
rb->lcd_framebuffer[y * LCD_WIDTH + x] = LCD_RGBPACK(0xff - r, 0xff - g, 0xff - b);
|
|
#endif
|
|
#else
|
|
rb->lcd_framebuffer[y * LCD_WIDTH + x] = LCD_BRIGHTNESS(0xff - rb->lcd_framebuffer[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, rb->lcd_framebuffer, 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 = rb->lcd_framebuffer[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);
|
|
rb->lcd_framebuffer[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 += 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);
|
|
|
|
}
|