Mercurial > repos > blastem
diff libblastem.c @ 1687:6c54bb5fe3b3
Hacky WIP libertro implementation
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 20 Jan 2019 01:03:21 -0800 |
parents | |
children | 395f684c5379 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libblastem.c Sun Jan 20 01:03:21 2019 -0800 @@ -0,0 +1,358 @@ +#include "libretro.h" +#include "system.h" +#include "util.h" +#include "vdp.h" +#include "render.h" +#include "io.h" + +static retro_environment_t retro_environment; +RETRO_API void retro_set_environment(retro_environment_t re) +{ + retro_environment = re; +} + +static retro_video_refresh_t retro_video_refresh; +RETRO_API void retro_set_video_refresh(retro_video_refresh_t rvf) +{ + retro_video_refresh = rvf; +} + +static retro_audio_sample_t retro_audio_sample; +RETRO_API void retro_set_audio_sample(retro_audio_sample_t ras) +{ + retro_audio_sample = ras; +} + +RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t rasb) +{ +} + +static retro_input_poll_t retro_input_poll; +RETRO_API void retro_set_input_poll(retro_input_poll_t rip) +{ + retro_input_poll = rip; +} + +static retro_input_state_t retro_input_state; +RETRO_API void retro_set_input_state(retro_input_state_t ris) +{ + retro_input_state = ris; +} + +int headless = 0; +int exit_after = 0; +int z80_enabled = 1; +char *save_filename; +tern_node *config; +uint8_t use_native_states = 1; +system_header *current_system; +system_media media; + +RETRO_API void retro_init(void) +{ +} + +RETRO_API void retro_deinit(void) +{ +} + +RETRO_API unsigned retro_api_version(void) +{ + return RETRO_API_VERSION; +} + +RETRO_API void retro_get_system_info(struct retro_system_info *info) +{ + info->library_name = "BlastEm"; + info->library_version = "0.6.2-pre"; //TODO: share this with blastem.c + info->valid_extensions = "md|gen|sms|bin|rom"; + info->need_fullpath = 0; + info->block_extract = 0; +} + +static vid_std video_standard; +RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info) +{ + info->geometry.base_width = info->geometry.max_width = LINEBUF_SIZE; + info->geometry.base_height = info->geometry.max_height = video_standard == VID_NTSC ? 243 : 294; + info->geometry.aspect_ratio = 0; + info->timing.fps = video_standard == VID_NTSC ? 60 : 50; + info->timing.sample_rate = 53267; //approximate sample rate of YM2612 +} + +RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device) +{ +} + +/* Resets the current game. */ +RETRO_API void retro_reset(void) +{ + current_system->soft_reset(current_system); +} + +/* Runs the game for one video frame. + * During retro_run(), input_poll callback must be called at least once. + * + * If a frame is not rendered for reasons where a game "dropped" a frame, + * this still counts as a frame, and retro_run() should explicitly dupe + * a frame if GET_CAN_DUPE returns true. + * In this case, the video callback can take a NULL argument for data. + */ +static uint8_t started; +RETRO_API void retro_run(void) +{ + if (started) { + current_system->resume_context(current_system); + } else { + current_system->start_context(current_system, NULL); + started = 1; + } +} + +/* Returns the amount of data the implementation requires to serialize + * internal state (save states). + * Between calls to retro_load_game() and retro_unload_game(), the + * returned size is never allowed to be larger than a previous returned + * value, to ensure that the frontend can allocate a save state buffer once. + */ +RETRO_API size_t retro_serialize_size(void) +{ + return 0; +} + +/* Serializes internal state. If failed, or size is lower than + * retro_serialize_size(), it should return false, true otherwise. */ +RETRO_API bool retro_serialize(void *data, size_t size) +{ + return 0; +} + +RETRO_API bool retro_unserialize(const void *data, size_t size) +{ + return 0; +} + +RETRO_API void retro_cheat_reset(void) +{ +} + +RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code) +{ +} + +/* Loads a game. */ +RETRO_API bool retro_load_game(const struct retro_game_info *game) +{ + if (game->path) { + media.dir = path_dirname(game->path); + media.name = basename_no_extension(game->path); + media.extension = path_extension(game->path); + } + media.buffer = malloc(nearest_pow2(game->size)); + memcpy(media.buffer, game->data, game->size); + media.size = game->size; + current_system = alloc_config_system(detect_system_type(&media), &media, 0, 0); + + unsigned format = RETRO_PIXEL_FORMAT_XRGB8888; + retro_environment(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &format); + return current_system != NULL; +} + +/* Loads a "special" kind of game. Should not be used, + * except in extreme cases. */ +RETRO_API bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info) +{ + return retro_load_game(info); +} + +/* Unloads a currently loaded game. */ +RETRO_API void retro_unload_game(void) +{ + + free(media.dir); + free(media.name); + free(media.extension); + +} + +/* Gets region of game. */ +RETRO_API unsigned retro_get_region(void) +{ + return video_standard == VID_NTSC ? RETRO_REGION_NTSC : RETRO_REGION_PAL; +} + +/* Gets region of memory. */ +RETRO_API void *retro_get_memory_data(unsigned id) +{ + return NULL; +} + +RETRO_API size_t retro_get_memory_size(unsigned id) +{ + return 0; +} + +//blastem render backend API implementation +uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b) +{ + return r << 16 | g << 8 | b; +} + +uint8_t render_create_window(char *caption, uint32_t width, uint32_t height, window_close_handler close_handler) +{ + //not supported in lib build + return 0; +} + +void render_destroy_window(uint8_t which) +{ + //not supported in lib build +} + +static uint32_t fb[LINEBUF_SIZE * 294 * 2]; +uint32_t *render_get_framebuffer(uint8_t which, int *pitch) +{ + *pitch = LINEBUF_SIZE * sizeof(uint32_t); + //TODO: deal with interlace + return fb; +} + +void render_framebuffer_updated(uint8_t which, int width) +{ + //TODO: Deal with 256 px wide modes + //TODO: deal with interlace + retro_video_refresh(fb, LINEBUF_SIZE, video_standard == VID_NTSC ? 243 : 294, LINEBUF_SIZE * sizeof(uint32_t)); + current_system->request_exit(current_system); +} + +uint8_t render_get_active_framebuffer(void) +{ + return 0; +} + +void render_set_video_standard(vid_std std) +{ + video_standard = std; +} + +void process_events() +{ + static int16_t prev_state[2][RETRO_DEVICE_ID_JOYPAD_L2]; + static const uint8_t map[] = { + BUTTON_A, BUTTON_X, BUTTON_MODE, BUTTON_START, DPAD_UP, DPAD_DOWN, + DPAD_LEFT, DPAD_RIGHT, BUTTON_B, BUTTON_Y, BUTTON_Z, BUTTON_C + }; + //TODO: handle other input device types + //TODO: handle more than 2 ports when appropriate + retro_input_poll(); + for (int port = 0; port < 2; port++) + { + for (int id = RETRO_DEVICE_ID_JOYPAD_B; id < RETRO_DEVICE_ID_JOYPAD_L2; id++) + { + int16_t new_state = retro_input_state(port, RETRO_DEVICE_JOYPAD, 0, id); + if (new_state != prev_state[port][id]) { + if (new_state) { + current_system->gamepad_down(current_system, port, map[id]); + } else { + current_system->gamepad_up(current_system, port, map[id]); + } + prev_state[port][id] = new_state; + } + } + } +} + +void render_errorbox(char *title, char *message) +{ +} +void render_warnbox(char *title, char *message) +{ +} +void render_infobox(char *title, char *message) +{ +} + +struct audio_source { + int32_t freq; + int32_t left_accum; + int32_t right_accum; + int32_t num_samples; +}; + +static audio_source *audio_sources[8]; +static uint8_t num_audio_sources; +audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) +{ + audio_sources[num_audio_sources] = calloc(1, sizeof(audio_source)); + return audio_sources[num_audio_sources++]; +} + +void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider) +{ +} + +static void check_put_sample(void) +{ + for (int i = 0; i < num_audio_sources; i++) + { + if (!audio_sources[i]->num_samples) { + return; + } + int32_t effective_freq = audio_sources[i]->freq / audio_sources[i]->num_samples; + if (abs(effective_freq - 53267) > 53267) { + return; + } + } + int16_t left = 0, right = 0; + for (int i = 0; i < num_audio_sources; i++) + { + left += audio_sources[i]->left_accum / audio_sources[i]->num_samples; + right += audio_sources[i]->right_accum / audio_sources[i]->num_samples; + audio_sources[i]->left_accum = audio_sources[i]->right_accum = audio_sources[i]->num_samples = 0; + } + retro_audio_sample(left, right); +} + +void render_put_mono_sample(audio_source *src, int16_t value) +{ + src->left_accum += value; + src->right_accum += value; + src->num_samples++; + check_put_sample(); +} +void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right) +{ + src->left_accum += left; + src->right_accum += right; + src->num_samples++; + check_put_sample(); +} +void render_pause_source(audio_source *src) +{ +} +void render_resume_source(audio_source *src) +{ +} +void render_free_source(audio_source *src) +{ + int index; + for (index = 0; index < num_audio_sources; index++) + { + if (audio_sources[index] == src) { + break; + } + } + num_audio_sources--; + audio_sources[index] = audio_sources[num_audio_sources]; + free(src); +} + +void bindings_set_mouse_mode(uint8_t mode) +{ +} +void bindings_release_capture(void) +{ +} +void bindings_reacquire_capture(void) +{ +}