#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 #include #include #include #include #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); }