mirror of
https://github.com/libretro/opera-libretro.git
synced 2026-11-07 12:37:52 -10:00
Every state load first serialized the running state into a fresh allocation the size of a state -- 25.5 MB -- so a failed load could be rolled back. That doubled the cost of every load, and run-ahead and rewind load every frame. A current-format state is now checked first and then loaded. Each current-format loader already parses its chunk into locals and checks it before touching emulator state; with opera_state_set_validating() set they stop there, so a state that validates is one the real load accepts in full and there is nothing to roll back. In that pass: - the MEM chunk's 25 MB payload is stepped over (sizes and config are still checked), not copied; - the xbus devices are not reset, and the CD-ROM device fields are checked against a scratch copy; - the memory-map sync, pbus reset and other side effects after loading are skipped. An older format, or a state carrying an older CD-ROM chunk, is not validated and takes the old backup path, as does anything that fails validation, so a refused state still leaves the running state as it was. Measured with a libretro driver over 200 loads: retro_unserialize went from 9.08 ms to 4.20 ms on average (best 7.80 ms to 3.58 ms). Checked: save, load and save again is byte-identical; a state loaded and run 60 frames matches the original run byte for byte; and a sweep flipping each of 9721 bytes (every 64th byte of the first 360 KB, every byte of the last 4 KB) is accepted or refused exactly as before, with no refused load ever changing the running state.
822 lines
17 KiB
C
822 lines
17 KiB
C
#include "file/file_path.h"
|
|
#include "libretro.h"
|
|
#include "libretro_core_options.h"
|
|
#include "retro_dirent.h"
|
|
#include "retro_miscellaneous.h"
|
|
#include "streams/file_stream.h"
|
|
|
|
#include "lr_input.h"
|
|
#include "lr_input_crosshair.h"
|
|
#include "lr_input_descs.h"
|
|
#include "opera_lr_callbacks.h"
|
|
#include "opera_lr_dsp.h"
|
|
#include "opera_lr_nvram.h"
|
|
#include "opera_lr_opts.h"
|
|
#include "retro_cdimage.h"
|
|
|
|
#include "libopera/opera_3do.h"
|
|
#include "libopera/opera_arm.h"
|
|
#include "libopera/opera_bios.h"
|
|
#include "libopera/opera_cdrom.h"
|
|
#include "libopera/opera_clock.h"
|
|
#include "libopera/opera_core.h"
|
|
#include "libopera/opera_log.h"
|
|
#include "libopera/opera_madam.h"
|
|
#include "libopera/opera_mem.h"
|
|
#include "libopera/opera_nvram.h"
|
|
#include "libopera/opera_pbus.h"
|
|
#include "libopera/opera_region.h"
|
|
#include "libopera/opera_vdlp.h"
|
|
#include "libopera/prng16.h"
|
|
#include "libopera/prng32.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#define CDIMAGE_SECTOR_SIZE 2048
|
|
|
|
typedef enum retro_reset_flags_t
|
|
{
|
|
RETRO_RESET_FLAG_NONE = 0,
|
|
RETRO_RESET_FLAG_SAVE_NVRAM = (1 << 0)
|
|
} retro_reset_flags_t;
|
|
|
|
static cdimage_t CDIMAGE;
|
|
static uint32_t CDIMAGE_SECTOR;
|
|
static char *g_GAME_INFO_PATH = NULL;
|
|
|
|
static
|
|
void
|
|
retro_environment_set_support_no_game(void)
|
|
{
|
|
bool support_no_game = true;
|
|
|
|
retro_environment_cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME,&support_no_game);
|
|
}
|
|
|
|
static
|
|
void
|
|
retro_environment_set_controller_info(void)
|
|
{
|
|
static const struct retro_controller_description port[] =
|
|
{
|
|
{ "3DO Joypad", RETRO_DEVICE_JOYPAD },
|
|
{ "3DO Flightstick", RETRO_DEVICE_FLIGHTSTICK },
|
|
{ "3DO Mouse", RETRO_DEVICE_MOUSE },
|
|
{ "3DO Lightgun", RETRO_DEVICE_LIGHTGUN },
|
|
{ "Arcade Lightgun", RETRO_DEVICE_ARCADE_LIGHTGUN },
|
|
{ "Orbatak Trackball", RETRO_DEVICE_ORBATAK_TRACKBALL },
|
|
};
|
|
|
|
static const struct retro_controller_info ports[LR_INPUT_MAX_DEVICES+1] =
|
|
{
|
|
{port, 6},
|
|
{port, 6},
|
|
{port, 6},
|
|
{port, 6},
|
|
{port, 6},
|
|
{port, 6},
|
|
{port, 6},
|
|
{port, 6},
|
|
{NULL, 0}
|
|
};
|
|
|
|
retro_environment_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO,(void*)ports);
|
|
}
|
|
|
|
static
|
|
void
|
|
retro_vfs_initialize(void)
|
|
{
|
|
struct retro_vfs_interface_info vfs_info;
|
|
|
|
vfs_info.required_interface_version = 3;
|
|
vfs_info.iface = NULL;
|
|
|
|
if(retro_environment_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE,&vfs_info) &&
|
|
(vfs_info.iface != NULL))
|
|
{
|
|
filestream_vfs_init(&vfs_info);
|
|
path_vfs_init(&vfs_info);
|
|
dirent_vfs_init(&vfs_info);
|
|
return;
|
|
}
|
|
|
|
vfs_info.required_interface_version = FILESTREAM_REQUIRED_VFS_VERSION;
|
|
vfs_info.iface = NULL;
|
|
|
|
if(retro_environment_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE,&vfs_info) &&
|
|
(vfs_info.iface != NULL))
|
|
filestream_vfs_init(&vfs_info);
|
|
}
|
|
|
|
void
|
|
retro_set_environment(retro_environment_t cb_)
|
|
{
|
|
opera_lr_callbacks_set_environment(cb_);
|
|
|
|
retro_vfs_initialize();
|
|
retro_environment_set_controller_info();
|
|
libretro_init_core_options();
|
|
libretro_set_core_options();
|
|
retro_environment_set_support_no_game();
|
|
}
|
|
|
|
void
|
|
retro_set_video_refresh(retro_video_refresh_t cb_)
|
|
{
|
|
opera_lr_callbacks_set_video_refresh(cb_);
|
|
}
|
|
|
|
void
|
|
retro_set_audio_sample(retro_audio_sample_t cb_)
|
|
{
|
|
opera_lr_callbacks_set_audio_sample(cb_);
|
|
}
|
|
|
|
void
|
|
retro_set_audio_sample_batch(retro_audio_sample_batch_t cb_)
|
|
{
|
|
opera_lr_callbacks_set_audio_sample_batch(cb_);
|
|
}
|
|
|
|
void
|
|
retro_set_input_poll(retro_input_poll_t cb_)
|
|
{
|
|
opera_lr_callbacks_set_input_poll(cb_);
|
|
}
|
|
|
|
void
|
|
retro_set_input_state(retro_input_state_t cb_)
|
|
{
|
|
opera_lr_callbacks_set_input_state(cb_);
|
|
}
|
|
|
|
static
|
|
uint32_t
|
|
cdimage_get_size(void)
|
|
{
|
|
return retro_cdimage_get_number_of_logical_blocks(&CDIMAGE);
|
|
}
|
|
|
|
static
|
|
void
|
|
cdimage_set_sector(const uint32_t sector_)
|
|
{
|
|
CDIMAGE_SECTOR = sector_;
|
|
}
|
|
|
|
static
|
|
void
|
|
cdimage_read_sector(void *buf_,
|
|
size_t len_)
|
|
{
|
|
retro_cdimage_read_sector(&CDIMAGE,CDIMAGE_SECTOR,buf_,len_);
|
|
}
|
|
|
|
static
|
|
void
|
|
cdimage_get_toc(uint8_t *track_first_,
|
|
uint8_t *track_last_,
|
|
uint8_t *disc_id_,
|
|
void *disc_toc_,
|
|
uint32_t disc_toc_size_)
|
|
{
|
|
retro_cdimage_get_toc(&CDIMAGE,
|
|
track_first_,
|
|
track_last_,
|
|
disc_id_,
|
|
disc_toc_,
|
|
disc_toc_size_);
|
|
}
|
|
|
|
static
|
|
void
|
|
content_runtime_reset(void)
|
|
{
|
|
cdimage_set_sector(0);
|
|
opera_cdrom_ode_set_root(NULL);
|
|
}
|
|
|
|
static
|
|
void*
|
|
libopera_callback(int cmd_,
|
|
void *data_)
|
|
{
|
|
switch(cmd_)
|
|
{
|
|
case EXT_DSP_TRIGGER:
|
|
opera_lr_dsp_process();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifndef GIT_VERSION
|
|
#define GIT_VERSION ""
|
|
#endif
|
|
void
|
|
retro_get_system_info(struct retro_system_info *info_)
|
|
{
|
|
memset(info_,0,sizeof(*info_));
|
|
|
|
info_->library_name = "Opera";
|
|
info_->library_version = "1.0.0" GIT_VERSION;
|
|
info_->need_fullpath = true;
|
|
info_->valid_extensions = "iso|bin|chd|cue";
|
|
}
|
|
|
|
size_t
|
|
retro_serialize_size(void)
|
|
{
|
|
return opera_3do_state_size();
|
|
}
|
|
|
|
bool
|
|
retro_serialize(void *data_,
|
|
size_t size_)
|
|
{
|
|
uint32_t size;
|
|
|
|
if(size_ == 0)
|
|
return false;
|
|
|
|
size = opera_3do_state_save(data_,size_);
|
|
|
|
return (size == size_);
|
|
}
|
|
|
|
bool
|
|
retro_unserialize(void const *data_,
|
|
size_t size_)
|
|
{
|
|
uint32_t size;
|
|
uint32_t backup_size;
|
|
uint32_t restore_size;
|
|
void *backup_state;
|
|
|
|
/*
|
|
* A current-format state is checked first and then loaded: a state
|
|
* that validates is one the load accepts in full, so there is
|
|
* nothing to roll back and no need to save the running state first.
|
|
* That backup was a full serialize into a fresh allocation the size
|
|
* of a state on every load -- twice the work for run-ahead and
|
|
* rewind, which load every frame. Anything else (an older format,
|
|
* a state that does not validate) takes the backup path below, which
|
|
* leaves the running state as it was if the load fails.
|
|
*/
|
|
if(opera_3do_state_validate(data_,size_))
|
|
{
|
|
size = opera_3do_state_load(data_,size_);
|
|
if(size != size_)
|
|
opera_log_printf(OPERA_LOG_ERROR,
|
|
"[Opera]: state validated but failed to load\n");
|
|
return (size == size_);
|
|
}
|
|
|
|
backup_state = malloc(retro_serialize_size());
|
|
if(backup_state == NULL)
|
|
return false;
|
|
backup_size = retro_serialize_size();
|
|
size = retro_serialize(backup_state,backup_size);
|
|
if(size)
|
|
{
|
|
size = opera_3do_state_load(data_,size_);
|
|
if(size != size_)
|
|
{
|
|
restore_size = opera_3do_state_load(backup_state,backup_size);
|
|
if(restore_size != backup_size)
|
|
opera_log_printf(OPERA_LOG_ERROR,
|
|
"[Opera]: failed to restore previous state after unsuccessful state load\n");
|
|
size = 0;
|
|
}
|
|
}
|
|
|
|
free(backup_state);
|
|
|
|
return (size == size_);
|
|
}
|
|
|
|
void
|
|
retro_cheat_reset(void)
|
|
{
|
|
}
|
|
|
|
void
|
|
retro_cheat_set(unsigned index_,
|
|
bool enabled_,
|
|
const char *code_)
|
|
{
|
|
}
|
|
|
|
void
|
|
retro_set_controller_port_device(unsigned port_,
|
|
unsigned device_)
|
|
{
|
|
lr_input_device_set_with_descs(port_,device_);
|
|
}
|
|
|
|
static
|
|
enum retro_pixel_format
|
|
vdlp_pixel_format_to_libretro(vdlp_pixel_format_e pf_)
|
|
{
|
|
switch (pf_)
|
|
{
|
|
case VDLP_PIXEL_FORMAT_0RGB1555:
|
|
return RETRO_PIXEL_FORMAT_0RGB1555;
|
|
case VDLP_PIXEL_FORMAT_RGB565:
|
|
return RETRO_PIXEL_FORMAT_RGB565;
|
|
case VDLP_PIXEL_FORMAT_XRGB8888:
|
|
return RETRO_PIXEL_FORMAT_XRGB8888;
|
|
}
|
|
|
|
return RETRO_PIXEL_FORMAT_XRGB8888;
|
|
}
|
|
|
|
static
|
|
int
|
|
set_pixel_format(void)
|
|
{
|
|
int rv;
|
|
enum retro_pixel_format fmt;
|
|
|
|
fmt = vdlp_pixel_format_to_libretro(g_OPTS.vdlp_pixel_format);
|
|
rv = retro_environment_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT,&fmt);
|
|
if(rv == 0)
|
|
{
|
|
retro_log_printf_cb(RETRO_LOG_ERROR,
|
|
"[Opera]: pixel format is not supported.\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
int
|
|
print_cdimage_open_fail(const char *path_)
|
|
{
|
|
retro_log_printf_cb(RETRO_LOG_ERROR,
|
|
"[Opera]: failure opening image - %s\n",
|
|
path_);
|
|
return -1;
|
|
}
|
|
|
|
static
|
|
int
|
|
open_cdimage_if_needed(const struct retro_game_info *info_)
|
|
{
|
|
int rv;
|
|
|
|
if(!info_)
|
|
return 0;
|
|
|
|
rv = retro_cdimage_open(info_->path,&CDIMAGE);
|
|
if(rv == -1)
|
|
return print_cdimage_open_fail(info_->path);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
void
|
|
game_info_path_free(void)
|
|
{
|
|
if(g_GAME_INFO_PATH == NULL)
|
|
return;
|
|
|
|
free(g_GAME_INFO_PATH);
|
|
g_GAME_INFO_PATH = NULL;
|
|
}
|
|
|
|
static
|
|
void
|
|
game_info_path_save(const struct retro_game_info *info_)
|
|
{
|
|
size_t len;
|
|
|
|
game_info_path_free();
|
|
|
|
if((info_ == NULL) || (info_->path == NULL))
|
|
return;
|
|
|
|
g_GAME_INFO_PATH = strdup(info_->path);
|
|
}
|
|
|
|
static
|
|
void
|
|
game_info_path_save_path(const char *path_)
|
|
{
|
|
game_info_path_free();
|
|
|
|
if(path_ == NULL)
|
|
return;
|
|
|
|
g_GAME_INFO_PATH = strdup(path_);
|
|
}
|
|
|
|
static
|
|
const
|
|
char*
|
|
game_info_path_get(void)
|
|
{
|
|
return g_GAME_INFO_PATH;
|
|
}
|
|
|
|
static
|
|
int
|
|
cdimage_ode_launch(const char *path_)
|
|
{
|
|
cdimage_t next;
|
|
int rv;
|
|
|
|
memset(&next,0,sizeof(next));
|
|
rv = retro_cdimage_open(path_,&next);
|
|
if(rv == -1)
|
|
{
|
|
retro_log_printf_cb(RETRO_LOG_ERROR,
|
|
"[Opera]: ODE launch failed opening image - %s\n",
|
|
path_);
|
|
return -1;
|
|
}
|
|
|
|
opera_lr_nvram_save(game_info_path_get(),
|
|
g_OPTS.nvram_shared,
|
|
g_OPTS.nvram_version);
|
|
|
|
retro_cdimage_close(&CDIMAGE);
|
|
CDIMAGE = next;
|
|
cdimage_set_sector(0);
|
|
game_info_path_save_path(path_);
|
|
|
|
retro_log_printf_cb(RETRO_LOG_INFO,
|
|
"[Opera]: ODE launched image - %s\n",
|
|
path_);
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
void
|
|
ode_root_set_for_content(const struct retro_game_info *info_)
|
|
{
|
|
char root[PATH_MAX_LENGTH];
|
|
|
|
if((info_ == NULL) || (info_->path == NULL))
|
|
{
|
|
opera_cdrom_ode_set_root(NULL);
|
|
return;
|
|
}
|
|
|
|
strncpy(root,info_->path,sizeof(root) - 1);
|
|
root[sizeof(root) - 1] = 0;
|
|
path_basedir(root);
|
|
|
|
opera_cdrom_ode_set_root(root);
|
|
}
|
|
|
|
static
|
|
bool
|
|
_init_core(void)
|
|
{
|
|
if((opera_lr_opts_process() & OPERA_LR_OPTS_CHANGE_ERROR) == 0)
|
|
{
|
|
if(opera_3do_init(libopera_callback) == 0)
|
|
return true;
|
|
|
|
retro_log_printf_cb(RETRO_LOG_ERROR,"[Opera]: unable to initialize core\n");
|
|
}
|
|
|
|
opera_lr_opts_reset();
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
retro_load_game(const struct retro_game_info *info_)
|
|
{
|
|
int rv;
|
|
|
|
content_runtime_reset();
|
|
game_info_path_save(info_);
|
|
|
|
rv = open_cdimage_if_needed(info_);
|
|
if(rv == -1)
|
|
return false;
|
|
|
|
ode_root_set_for_content(info_);
|
|
if(!_init_core())
|
|
{
|
|
content_runtime_reset();
|
|
retro_cdimage_close(&CDIMAGE);
|
|
game_info_path_free();
|
|
return false;
|
|
}
|
|
|
|
cdimage_set_sector(0);
|
|
|
|
rv = set_pixel_format();
|
|
if(rv < 0)
|
|
return false;
|
|
|
|
opera_lr_nvram_load(game_info_path_get(),
|
|
g_OPTS.nvram_shared,
|
|
g_OPTS.nvram_version);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
retro_load_game_special(unsigned game_type_,
|
|
const struct retro_game_info *info_,
|
|
size_t num_info_)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void
|
|
retro_unload_game(void)
|
|
{
|
|
opera_lr_nvram_save(game_info_path_get(),
|
|
g_OPTS.nvram_shared,
|
|
g_OPTS.nvram_version);
|
|
game_info_path_free();
|
|
|
|
opera_3do_destroy();
|
|
opera_mem_destroy();
|
|
|
|
content_runtime_reset();
|
|
retro_cdimage_close(&CDIMAGE);
|
|
|
|
opera_lr_opts_reset();
|
|
}
|
|
|
|
static
|
|
void
|
|
get_system_geometry(struct retro_game_geometry *geometry_)
|
|
{
|
|
geometry_->base_width = (opera_region_width() << g_OPTS.high_resolution);
|
|
geometry_->base_height = (opera_region_height() << g_OPTS.high_resolution);
|
|
geometry_->max_width = (opera_region_max_width() * 2);
|
|
geometry_->max_height = (opera_region_max_height() * 2);
|
|
geometry_->aspect_ratio = 4.0 / 3.0;
|
|
}
|
|
|
|
static
|
|
void
|
|
get_system_av_info(struct retro_system_av_info *info_)
|
|
{
|
|
memset(info_,0,sizeof(*info_));
|
|
|
|
info_->timing.fps = opera_region_refresh_rate();
|
|
info_->timing.sample_rate = 44100;
|
|
get_system_geometry(&info_->geometry);
|
|
}
|
|
|
|
void
|
|
retro_get_system_av_info(struct retro_system_av_info *info_)
|
|
{
|
|
get_system_av_info(info_);
|
|
}
|
|
|
|
static
|
|
void
|
|
set_system_av_info(void)
|
|
{
|
|
struct retro_system_av_info info;
|
|
|
|
get_system_av_info(&info);
|
|
retro_environment_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO,&info);
|
|
}
|
|
|
|
static
|
|
void
|
|
set_system_geometry(void)
|
|
{
|
|
struct retro_game_geometry geometry;
|
|
|
|
get_system_geometry(&geometry);
|
|
retro_environment_cb(RETRO_ENVIRONMENT_SET_GEOMETRY,&geometry);
|
|
}
|
|
|
|
unsigned
|
|
retro_get_region(void)
|
|
{
|
|
switch(opera_region_get())
|
|
{
|
|
case OPERA_REGION_PAL1:
|
|
case OPERA_REGION_PAL2:
|
|
return RETRO_REGION_PAL;
|
|
case OPERA_REGION_NTSC:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return RETRO_REGION_NTSC;
|
|
}
|
|
|
|
unsigned
|
|
retro_api_version(void)
|
|
{
|
|
return RETRO_API_VERSION;
|
|
}
|
|
|
|
void*
|
|
retro_get_memory_data(unsigned id_)
|
|
{
|
|
switch(id_)
|
|
{
|
|
case RETRO_MEMORY_SAVE_RAM:
|
|
return NULL;
|
|
case RETRO_MEMORY_SYSTEM_RAM:
|
|
return DRAM;
|
|
case RETRO_MEMORY_VIDEO_RAM:
|
|
return VRAM;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
size_t
|
|
retro_get_memory_size(unsigned id_)
|
|
{
|
|
switch(id_)
|
|
{
|
|
case RETRO_MEMORY_SAVE_RAM:
|
|
return 0;
|
|
case RETRO_MEMORY_SYSTEM_RAM:
|
|
return DRAM_SIZE;
|
|
case RETRO_MEMORY_VIDEO_RAM:
|
|
return VRAM_SIZE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
retro_init(void)
|
|
{
|
|
bool fixed_random_seed;
|
|
struct retro_log_callback log;
|
|
uint32_t random_seed;
|
|
unsigned level;
|
|
uint64_t serialization_quirks;
|
|
|
|
level = 5;
|
|
serialization_quirks = (RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT |
|
|
RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT);
|
|
|
|
if(retro_environment_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE,&log))
|
|
{
|
|
opera_lr_callbacks_set_log_printf(log.log);
|
|
opera_log_set_func(log.log);
|
|
}
|
|
|
|
retro_environment_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL,&level);
|
|
retro_environment_cb(RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS,&serialization_quirks);
|
|
|
|
opera_cdrom_set_callbacks(cdimage_get_size,
|
|
cdimage_set_sector,
|
|
cdimage_read_sector,
|
|
cdimage_get_toc);
|
|
opera_cdrom_ode_set_launch_callback(cdimage_ode_launch);
|
|
|
|
fixed_random_seed = opera_lr_opts_get_random_seed(&random_seed);
|
|
if(!fixed_random_seed)
|
|
random_seed = (uint32_t)time(NULL);
|
|
|
|
prng16_seed(random_seed);
|
|
prng32_seed(random_seed);
|
|
|
|
retro_log_printf_cb(RETRO_LOG_INFO,
|
|
"[Opera]: random seed 0x%08X (%s)\n",
|
|
(unsigned)random_seed,
|
|
fixed_random_seed ? "fixed" : "time-based");
|
|
}
|
|
|
|
void
|
|
retro_deinit(void)
|
|
{
|
|
|
|
}
|
|
|
|
static
|
|
void
|
|
retro_reset_core(retro_reset_flags_t flags_)
|
|
{
|
|
if(flags_ & RETRO_RESET_FLAG_SAVE_NVRAM)
|
|
opera_lr_nvram_save(game_info_path_get(),
|
|
g_OPTS.nvram_shared,
|
|
g_OPTS.nvram_version);
|
|
|
|
opera_3do_destroy();
|
|
opera_lr_opts_reset();
|
|
|
|
if(!_init_core())
|
|
{
|
|
retro_environment_cb(RETRO_ENVIRONMENT_SHUTDOWN,NULL);
|
|
return;
|
|
}
|
|
|
|
cdimage_set_sector(0);
|
|
|
|
opera_lr_nvram_load(game_info_path_get(),
|
|
g_OPTS.nvram_shared,
|
|
g_OPTS.nvram_version);
|
|
}
|
|
|
|
void
|
|
retro_reset(void)
|
|
{
|
|
retro_reset_core(RETRO_RESET_FLAG_SAVE_NVRAM);
|
|
}
|
|
|
|
static
|
|
bool
|
|
ode_reset_if_requested(void)
|
|
{
|
|
if(!opera_cdrom_ode_consume_restart_request())
|
|
return false;
|
|
|
|
retro_log_printf_cb(RETRO_LOG_INFO,
|
|
"[Opera]: ODE media launch requested core reset\n");
|
|
/* cdimage_ode_launch already saved NVRAM before switching game paths. */
|
|
retro_reset_core(RETRO_RESET_FLAG_NONE);
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool
|
|
variable_updated()
|
|
{
|
|
bool updated;
|
|
|
|
updated = false;
|
|
if(!retro_environment_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE,&updated))
|
|
return false;
|
|
return updated;
|
|
}
|
|
|
|
static
|
|
void
|
|
process_opts_if_updated()
|
|
{
|
|
uint32_t changes;
|
|
|
|
if(!variable_updated())
|
|
return;
|
|
|
|
changes = opera_lr_opts_process();
|
|
if(changes & OPERA_LR_OPTS_CHANGE_ERROR)
|
|
return;
|
|
|
|
if(changes & OPERA_LR_OPTS_CHANGE_TIMING)
|
|
set_system_av_info();
|
|
else if(changes & OPERA_LR_OPTS_CHANGE_GEOMETRY)
|
|
set_system_geometry();
|
|
}
|
|
|
|
static
|
|
void
|
|
draw_crosshairs_if_enabled()
|
|
{
|
|
if(g_OPTS.hide_lightgun_crosshairs)
|
|
return;
|
|
|
|
lr_input_crosshairs_draw(g_OPTS.video_buffer,
|
|
g_OPTS.video_width,
|
|
g_OPTS.video_height);
|
|
}
|
|
|
|
void
|
|
retro_run(void)
|
|
{
|
|
if(!g_OPTS.initialized_opera)
|
|
return;
|
|
|
|
if(ode_reset_if_requested())
|
|
return;
|
|
|
|
process_opts_if_updated();
|
|
|
|
lr_input_update(g_OPTS.active_devices);
|
|
|
|
opera_3do_process_frame();
|
|
if(ode_reset_if_requested())
|
|
return;
|
|
|
|
draw_crosshairs_if_enabled();
|
|
|
|
opera_lr_dsp_upload();
|
|
|
|
retro_video_refresh_cb(g_OPTS.video_buffer,
|
|
g_OPTS.video_width,
|
|
g_OPTS.video_height,
|
|
g_OPTS.video_width << g_OPTS.video_pitch_shift);
|
|
}
|