implemented fast forward and max ff speed
This commit is contained in:
parent
b3283adf0b
commit
f9eee026f3
3 changed files with 98 additions and 10 deletions
|
|
@ -5,6 +5,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
|
||||||
|
|
@ -148,3 +149,15 @@ void putInt(char* path, int value) {
|
||||||
sprintf(buffer, "%d", value);
|
sprintf(buffer, "%d", value);
|
||||||
putFile(path, buffer);
|
putFile(path, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t getMicroseconds(void) {
|
||||||
|
uint64_t ret;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
|
||||||
|
ret = (uint64_t)tv.tv_sec * 1000000;
|
||||||
|
ret += (uint64_t)tv.tv_usec;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef UTILS_H
|
#ifndef UTILS_H
|
||||||
#define UTILS_H
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
int prefixMatch(char* pre, char* str);
|
int prefixMatch(char* pre, char* str);
|
||||||
int suffixMatch(char* suf, char* str);
|
int suffixMatch(char* suf, char* str);
|
||||||
int exactMatch(char* str1, char* str2);
|
int exactMatch(char* str1, char* str2);
|
||||||
|
|
@ -20,4 +22,6 @@ void getFile(char* path, char* buffer, size_t buffer_size);
|
||||||
void putInt(char* path, int value);
|
void putInt(char* path, int value);
|
||||||
int getInt(char* path);
|
int getInt(char* path);
|
||||||
|
|
||||||
|
uint64_t getMicroseconds(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -674,7 +674,7 @@ static void Config_write(int override) {
|
||||||
if (config.loaded==CONFIG_GAME) unlink(path);
|
if (config.loaded==CONFIG_GAME) unlink(path);
|
||||||
Config_getPath(path, CONFIG_WRITE_ALL);
|
Config_getPath(path, CONFIG_WRITE_ALL);
|
||||||
}
|
}
|
||||||
config.loaded = CONFIG_GLOBAL;
|
config.loaded = override ? CONFIG_GAME : CONFIG_GLOBAL;
|
||||||
|
|
||||||
FILE *file = fopen(path, "wb");
|
FILE *file = fopen(path, "wb");
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
@ -996,24 +996,36 @@ static void input_poll_callback(void) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// TODO: test fast_forward once implemented
|
// TODO: test fast_forward once implemented
|
||||||
|
static int toggled_ff_on = 0; // this logic only works because TOGGLE_FF is before HOLD_FF in the menu...
|
||||||
for (int i=0; i<SHORTCUT_COUNT; i++) {
|
for (int i=0; i<SHORTCUT_COUNT; i++) {
|
||||||
ButtonMapping* mapping = &config.shortcuts[i];
|
ButtonMapping* mapping = &config.shortcuts[i];
|
||||||
int btn = 1 << mapping->local;
|
int btn = 1 << mapping->local;
|
||||||
if (btn==BTN_NONE) continue; // not bound
|
if (btn==BTN_NONE) continue; // not bound
|
||||||
if (!mapping->mod || PAD_isPressed(BTN_MENU)) {
|
if (!mapping->mod || PAD_isPressed(BTN_MENU)) {
|
||||||
if (i==SHORTCUT_HOLD_FF) {
|
if (i==SHORTCUT_TOGGLE_FF) {
|
||||||
if (PAD_justPressed(btn) || PAD_justReleased(btn)) {
|
if (PAD_justPressed(btn)) {
|
||||||
|
fast_forward = toggled_ff_on = !fast_forward;
|
||||||
|
if (mapping->mod) ignore_menu = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (PAD_justReleased(btn)) {
|
||||||
|
if (mapping->mod) ignore_menu = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (i==SHORTCUT_HOLD_FF) {
|
||||||
|
// don't allow turn off fast_forward with a release of the hold button
|
||||||
|
// if it was initially turned on with the toggle button
|
||||||
|
if (PAD_justPressed(btn) || (!toggled_ff_on && PAD_justReleased(btn))) {
|
||||||
fast_forward = PAD_isPressed(btn);
|
fast_forward = PAD_isPressed(btn);
|
||||||
if (mapping->mod) ignore_menu = 1; // very unlikely but just in case
|
if (mapping->mod) ignore_menu = 1; // very unlikely but just in case
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (PAD_justPressed(btn)) {
|
else if (PAD_justPressed(btn)) {
|
||||||
switch (i) {
|
switch (i) {
|
||||||
// TODO: do these state functions
|
|
||||||
case SHORTCUT_SAVE_STATE: State_write(); break;
|
case SHORTCUT_SAVE_STATE: State_write(); break;
|
||||||
case SHORTCUT_LOAD_STATE: State_read(); break;
|
case SHORTCUT_LOAD_STATE: State_read(); break;
|
||||||
case SHORTCUT_RESET_GAME: core.reset(); break;
|
case SHORTCUT_RESET_GAME: core.reset(); break;
|
||||||
case SHORTCUT_TOGGLE_FF: fast_forward = !fast_forward; break;
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1308,6 +1320,21 @@ static bool environment_callback(unsigned cmd, void *data) { // copied from pico
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unused
|
||||||
|
// case RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK: {
|
||||||
|
// puts("RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK"); fflush(stdout);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case RETRO_ENVIRONMENT_GET_THROTTLE_STATE: {
|
||||||
|
// puts("RETRO_ENVIRONMENT_GET_THROTTLE_STATE"); fflush(stdout);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case RETRO_ENVIRONMENT_GET_FASTFORWARDING: {
|
||||||
|
// puts("RETRO_ENVIRONMENT_GET_FASTFORWARDING"); fflush(stdout);
|
||||||
|
// break;
|
||||||
|
// };
|
||||||
|
|
||||||
// TODO: these unknowns are probably some flag OR'd to RETRO_ENVIRONMENT_EXPERIMENTAL
|
// TODO: these unknowns are probably some flag OR'd to RETRO_ENVIRONMENT_EXPERIMENTAL
|
||||||
// TODO: UNKNOWN 65572
|
// TODO: UNKNOWN 65572
|
||||||
// TODO: UNKNOWN 65578
|
// TODO: UNKNOWN 65578
|
||||||
|
|
@ -2050,7 +2077,26 @@ static void selectScaler(int width, int height, int pitch) {
|
||||||
scaler_surface = TTF_RenderUTF8_Blended(font.tiny, scaler_name, COLOR_WHITE);
|
scaler_surface = TTF_RenderUTF8_Blended(font.tiny, scaler_name, COLOR_WHITE);
|
||||||
}
|
}
|
||||||
static void video_refresh_callback(const void *data, unsigned width, unsigned height, size_t pitch) {
|
static void video_refresh_callback(const void *data, unsigned width, unsigned height, size_t pitch) {
|
||||||
|
static uint32_t last_flip_time = 0;
|
||||||
|
|
||||||
|
// 10 seems to be the sweet spot that allows 2x in NES and SNES and 8x in GB at 60fps
|
||||||
|
// 14 will let GB hit 10x but NES and SNES will drop to 1.5x at 30fps (not sure why)
|
||||||
|
// but 10 hurts PS...
|
||||||
|
if (fast_forward && SDL_GetTicks()-last_flip_time<10) return;
|
||||||
|
|
||||||
|
|
||||||
|
// FFVII menus
|
||||||
|
// 16: 30/200
|
||||||
|
// 15: 30/180
|
||||||
|
// 14: 45/180
|
||||||
|
// 12: 30/150
|
||||||
|
// 10: 30/120 (optimize text off has no effect)
|
||||||
|
// 8: 60/210 (with optimize text off)
|
||||||
|
// you can squeeze more out of every console by turning prevent tearing off
|
||||||
|
// eg. PS@10 60/240
|
||||||
|
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
fps_ticks += 1;
|
fps_ticks += 1;
|
||||||
|
|
||||||
if (width!=renderer.src_w || height!=renderer.src_h) {
|
if (width!=renderer.src_w || height!=renderer.src_h) {
|
||||||
|
|
@ -2147,16 +2193,17 @@ static void video_refresh_callback(const void *data, unsigned width, unsigned he
|
||||||
}
|
}
|
||||||
|
|
||||||
GFX_flip(screen);
|
GFX_flip(screen);
|
||||||
|
last_flip_time = SDL_GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
|
|
||||||
static void audio_sample_callback(int16_t left, int16_t right) {
|
static void audio_sample_callback(int16_t left, int16_t right) {
|
||||||
SND_batchSamples(&(const SND_Frame){left,right}, 1);
|
if (!fast_forward) SND_batchSamples(&(const SND_Frame){left,right}, 1);
|
||||||
}
|
}
|
||||||
static size_t audio_sample_batch_callback(const int16_t *data, size_t frames) {
|
static size_t audio_sample_batch_callback(const int16_t *data, size_t frames) {
|
||||||
return SND_batchSamples((const SND_Frame*)data, frames);
|
if (!fast_forward) return SND_batchSamples((const SND_Frame*)data, frames);
|
||||||
// return frames;
|
else return frames;
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
|
|
@ -2619,6 +2666,7 @@ static int options_shortcuts_open(MenuList* list, int i) {
|
||||||
return MENU_CALLBACK_NOP;
|
return MENU_CALLBACK_NOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void update_save_option_desc(void);
|
||||||
static int options_save_confirm(MenuList* list, int i) {
|
static int options_save_confirm(MenuList* list, int i) {
|
||||||
char* message;
|
char* message;
|
||||||
switch (i) {
|
switch (i) {
|
||||||
|
|
@ -2664,6 +2712,7 @@ static int options_save_confirm(MenuList* list, int i) {
|
||||||
else GFX_sync();
|
else GFX_sync();
|
||||||
}
|
}
|
||||||
GFX_setMode(MODE_MENU);
|
GFX_setMode(MODE_MENU);
|
||||||
|
update_save_option_desc();
|
||||||
return MENU_CALLBACK_EXIT;
|
return MENU_CALLBACK_EXIT;
|
||||||
}
|
}
|
||||||
static MenuList options_save_menu = {
|
static MenuList options_save_menu = {
|
||||||
|
|
@ -2676,7 +2725,6 @@ static MenuList options_save_menu = {
|
||||||
{NULL},
|
{NULL},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
static void update_save_option_desc(void);
|
|
||||||
static int options_save_open(MenuList* list, int i) {
|
static int options_save_open(MenuList* list, int i) {
|
||||||
update_save_option_desc();
|
update_save_option_desc();
|
||||||
options_save_menu.desc = getSaveDesc();
|
options_save_menu.desc = getSaveDesc();
|
||||||
|
|
@ -3076,6 +3124,7 @@ static int Menu_options(MenuList* list) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static void Menu_loop(void) {
|
static void Menu_loop(void) {
|
||||||
|
fast_forward = 0;
|
||||||
POW_enableAutosleep();
|
POW_enableAutosleep();
|
||||||
PAD_reset();
|
PAD_reset();
|
||||||
|
|
||||||
|
|
@ -3475,6 +3524,26 @@ static void trackFPS(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void limitFF(void) {
|
||||||
|
static uint64_t last_time = 0;
|
||||||
|
const uint64_t now = getMicroseconds();
|
||||||
|
|
||||||
|
if (fast_forward && max_ff_speed) {
|
||||||
|
if (last_time == 0) last_time = now;
|
||||||
|
int elapsed = now - last_time;
|
||||||
|
if (elapsed>0 && elapsed<0x80000) {
|
||||||
|
uint64_t ff_frame_time = 1000000 / (core.fps * (max_ff_speed + 1)); // TODO: define this only when max_ff_speed changes
|
||||||
|
if (elapsed<ff_frame_time) {
|
||||||
|
int delay = (ff_frame_time - elapsed) / 1000;
|
||||||
|
if (delay>0) SDL_Delay(delay);
|
||||||
|
}
|
||||||
|
last_time += ff_frame_time;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_time = now;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc , char* argv[]) {
|
int main(int argc , char* argv[]) {
|
||||||
// force a stack overflow to ensure asan is linked and actually working
|
// force a stack overflow to ensure asan is linked and actually working
|
||||||
// char tmp[2];
|
// char tmp[2];
|
||||||
|
|
@ -3496,7 +3565,8 @@ int main(int argc , char* argv[]) {
|
||||||
Core_open(core_path, tag_name);
|
Core_open(core_path, tag_name);
|
||||||
Core_init();
|
Core_init();
|
||||||
|
|
||||||
// TODO: find a better place to do this, mixing static and loaded data is messy
|
// TODO: find a better place to do this
|
||||||
|
// mixing static and loaded data is messy
|
||||||
options_menu.items[1].desc = (char*)core.version;
|
options_menu.items[1].desc = (char*)core.version;
|
||||||
|
|
||||||
Game_open(rom_path);
|
Game_open(rom_path);
|
||||||
|
|
@ -3516,6 +3586,7 @@ int main(int argc , char* argv[]) {
|
||||||
GFX_startFrame();
|
GFX_startFrame();
|
||||||
|
|
||||||
core.run();
|
core.run();
|
||||||
|
limitFF();
|
||||||
|
|
||||||
if (show_menu) Menu_loop();
|
if (show_menu) Menu_loop();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue