#include "bizhawk.hpp" #include #include #include #include #include #include #include #include std::string _biosFilePath; std::string _gameFilePath; std::string _fontFilePath; int _port1Type; int _port2Type; controllerData_t _port1Value; controllerData_t _port2Value; uint32_t* _videoBuffer; size_t _videoHeight; size_t _videoWidth; size_t _videoPitch; int _region; int _nvramChanged; int _inputPortsRead; #define _MAX_SAMPLES 4096 #define _CHANNEL_COUNT 2 int16_t _audioBuffer[_MAX_SAMPLES * _CHANNEL_COUNT]; size_t _audioSamples; extern "C" { void* xbus_cdrom_plugin(int proc_, void* data_); void opera_cdrom_set_callbacks(opera_cdrom_get_size_cb_t get_size_, opera_cdrom_set_sector_cb_t set_sector_, opera_cdrom_read_sector_cb_t read_sector_); void opera_nvram_init(void *buf, const int bufsize); void opera_lr_callbacks_set_audio_sample(retro_audio_sample_t cb); void opera_lr_callbacks_set_audio_sample_batch(retro_audio_sample_batch_t cb); void opera_lr_callbacks_set_environment(retro_environment_t cb); void opera_lr_callbacks_set_input_poll(retro_input_poll_t cb); void opera_lr_callbacks_set_input_state(retro_input_state_t cb); void opera_lr_callbacks_set_log_printf(retro_log_printf_t cb); void opera_lr_callbacks_set_video_refresh(retro_video_refresh_t cb); RETRO_API void *retro_get_memory_data(unsigned id); RETRO_API size_t retro_get_memory_size(unsigned id); void retro_set_controller_port_device(unsigned port_, unsigned device_); void retro_get_system_av_info(struct retro_system_av_info *info_); } void RETRO_CALLCONV retro_video_refresh_callback(const void *data, unsigned width, unsigned height, size_t pitch) { // printf("Video %p, w: %u, h: %u, p: %lu\n", data, width, height, pitch); _videoBuffer = (uint32_t*)data; _videoWidth = width; _videoHeight = height; _videoPitch = pitch; } void RETRO_CALLCONV retro_log_printf_callback(enum retro_log_level level, const char *format, ...) { va_list ap; va_start(ap, format); printf(format, ap); va_end(ap); } size_t RETRO_CALLCONV retro_audio_sample_batch_callback(const int16_t *data, size_t frames) { memcpy(_audioBuffer, data, sizeof(int16_t) * frames * _CHANNEL_COUNT); _audioSamples = frames; return frames; } void RETRO_CALLCONV retro_input_poll_callback() { // printf("Libretro Input Poll Callback Called:\n"); } int16_t processController(const int portType, controllerData_t& portValue, const unsigned device, const unsigned index, const unsigned id) { switch (portType) { case RETRO_DEVICE_JOYPAD: switch (id) { case RETRO_DEVICE_ID_JOYPAD_UP: return portValue.gamePad.up; case RETRO_DEVICE_ID_JOYPAD_DOWN: return portValue.gamePad.down; case RETRO_DEVICE_ID_JOYPAD_LEFT: return portValue.gamePad.left; case RETRO_DEVICE_ID_JOYPAD_RIGHT: return portValue.gamePad.right; case RETRO_DEVICE_ID_JOYPAD_L: return portValue.gamePad.buttonL; case RETRO_DEVICE_ID_JOYPAD_R: return portValue.gamePad.buttonR; case RETRO_DEVICE_ID_JOYPAD_SELECT: return portValue.gamePad.buttonX; case RETRO_DEVICE_ID_JOYPAD_START: return portValue.gamePad.buttonP; case RETRO_DEVICE_ID_JOYPAD_Y: return portValue.gamePad.buttonA; case RETRO_DEVICE_ID_JOYPAD_B: return portValue.gamePad.buttonB; case RETRO_DEVICE_ID_JOYPAD_A: return portValue.gamePad.buttonC; default: return 0; } case RETRO_DEVICE_MOUSE: switch (id) { case RETRO_DEVICE_ID_MOUSE_X: return portValue.mouse.dX; case RETRO_DEVICE_ID_MOUSE_Y: return portValue.mouse.dY; case RETRO_DEVICE_ID_MOUSE_LEFT: return portValue.mouse.leftButton; case RETRO_DEVICE_ID_MOUSE_MIDDLE: return portValue.mouse.middleButton; case RETRO_DEVICE_ID_MOUSE_RIGHT: return portValue.mouse.rightButton; case RETRO_DEVICE_ID_MOUSE_BUTTON_4: return portValue.mouse.fourthButton; default: return 0; } case RETRO_DEVICE_FLIGHTSTICK: if (index == RETRO_DEVICE_INDEX_ANALOG_BUTTON) { switch (id) { case RETRO_DEVICE_ID_JOYPAD_R2: return portValue.flightStick.fire; case RETRO_DEVICE_ID_JOYPAD_Y: return portValue.flightStick.buttonA; case RETRO_DEVICE_ID_JOYPAD_B: return portValue.flightStick.buttonB; case RETRO_DEVICE_ID_JOYPAD_A: return portValue.flightStick.buttonC; case RETRO_DEVICE_ID_JOYPAD_UP: return portValue.flightStick.up; case RETRO_DEVICE_ID_JOYPAD_DOWN: return portValue.flightStick.down; case RETRO_DEVICE_ID_JOYPAD_LEFT: return portValue.flightStick.left; case RETRO_DEVICE_ID_JOYPAD_RIGHT: return portValue.flightStick.right; case RETRO_DEVICE_ID_JOYPAD_START: return portValue.flightStick.buttonP; case RETRO_DEVICE_ID_JOYPAD_SELECT: return portValue.flightStick.buttonX; case RETRO_DEVICE_ID_JOYPAD_L: return portValue.flightStick.leftTrigger; case RETRO_DEVICE_ID_JOYPAD_R: return portValue.flightStick.rightTrigger; default: return 0; } } else { switch (id) { case RETRO_DEVICE_ID_ANALOG_X: if (index == RETRO_DEVICE_INDEX_ANALOG_LEFT) return portValue.flightStick.horizontalAxis; if (index == RETRO_DEVICE_INDEX_ANALOG_RIGHT) return portValue.flightStick.altitudeAxis; return 0; case RETRO_DEVICE_ID_ANALOG_Y: if (index == RETRO_DEVICE_INDEX_ANALOG_LEFT) return portValue.flightStick.verticalAxis; if (index == RETRO_DEVICE_INDEX_ANALOG_RIGHT) return portValue.flightStick.altitudeAxis; return 0; default: return 0; } } case RETRO_DEVICE_LIGHTGUN: switch (id) { case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X: return portValue.lightGun.screenX; case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y: return portValue.lightGun.screenY; case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER: return portValue.lightGun.trigger; case RETRO_DEVICE_ID_LIGHTGUN_SELECT: return portValue.lightGun.select; case RETRO_DEVICE_ID_LIGHTGUN_RELOAD: return portValue.lightGun.reload; case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN: return portValue.lightGun.isOffScreen; default: return 0; } case RETRO_DEVICE_ARCADE_LIGHTGUN: switch (id) { case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X: return portValue.arcadeLightGun.screenX; case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y: return portValue.arcadeLightGun.screenY; case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER: return portValue.arcadeLightGun.trigger; case RETRO_DEVICE_ID_LIGHTGUN_SELECT: return portValue.arcadeLightGun.select; case RETRO_DEVICE_ID_LIGHTGUN_START: return portValue.arcadeLightGun.start; case RETRO_DEVICE_ID_LIGHTGUN_RELOAD: return portValue.arcadeLightGun.reload; case RETRO_DEVICE_ID_LIGHTGUN_AUX_A: return portValue.arcadeLightGun.auxA; case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN: return portValue.arcadeLightGun.isOffScreen; default: return 0; } case RETRO_DEVICE_ORBATAK_TRACKBALL: switch (id) { case RETRO_DEVICE_ID_ANALOG_X: if (index == RETRO_DEVICE_INDEX_ANALOG_LEFT) return portValue.orbatakTrackball.dX; if (index == RETRO_DEVICE_INDEX_ANALOG_RIGHT) return portValue.orbatakTrackball.dX; return 0; case RETRO_DEVICE_ID_ANALOG_Y: if (index == RETRO_DEVICE_INDEX_ANALOG_LEFT) return portValue.orbatakTrackball.dY; if (index == RETRO_DEVICE_INDEX_ANALOG_RIGHT) return portValue.orbatakTrackball.dY; return 0; case RETRO_DEVICE_ID_JOYPAD_SELECT: return portValue.orbatakTrackball.startP1; case RETRO_DEVICE_ID_JOYPAD_START: return portValue.orbatakTrackball.startP2; case RETRO_DEVICE_ID_JOYPAD_L: return portValue.orbatakTrackball.coinP1; case RETRO_DEVICE_ID_JOYPAD_R: return portValue.orbatakTrackball.coinP2; case RETRO_DEVICE_ID_JOYPAD_R2: return portValue.orbatakTrackball.service; default: return 0; } default: return 0; } return 0; } int16_t RETRO_CALLCONV retro_input_state_callback(unsigned port, unsigned device, unsigned index, unsigned id) { // printf("Libretro Input State Callback Called. Port: %u, Device: %u, Index: %u, Id: %u\n", port, device, index, id); if (port == 0) return processController(_port1Type, _port1Value, device, index, id); if (port == 1) return processController(_port2Type, _port2Value, device, index, id); return 0; } char _deviceCountOption[256]; void configHandler(struct retro_variable *var) { printf("Variable Name: %s / Value: %s\n", var->key, var->value); std::string key(var->key); if (key == "opera_bios" && _biosFilePath != "None") var->value = _biosFilePath.c_str(); if (key == "opera_font" && _fontFilePath != "None") var->value = _fontFilePath.c_str(); if (key == "opera_region") { if (_region == 0) var->value = "ntsc"; if (_region == 1) var->value = "pal1"; if (_region == 2) var->value = "pal2"; } if (key == "opera_active_devices") { int deviceCount = 0; if (_port1Type != RETRO_DEVICE_NONE) deviceCount++; if (_port2Type != RETRO_DEVICE_NONE) deviceCount++; sprintf(_deviceCountOption, "%d", deviceCount); var->value = _deviceCountOption; } } const char* systemPath = "."; bool RETRO_CALLCONV retro_environment_callback(unsigned cmd, void *data) { // printf("Libretro Environment Callback Called: %u\n", cmd); if (cmd == RETRO_ENVIRONMENT_GET_LOG_INTERFACE) { *((retro_log_printf_t*)data) = retro_log_printf_callback; return true; } if (cmd == RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL) { return true; } if (cmd == RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS) { return true; } if (cmd == RETRO_ENVIRONMENT_GET_VARIABLE) { configHandler((struct retro_variable *)data); return true; } if (cmd == RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE) { return true; } if (cmd == RETRO_ENVIRONMENT_SET_PIXEL_FORMAT) { *((vdlp_pixel_format_e*) data) = VDLP_PIXEL_FORMAT_XRGB8888; return true; } if (cmd == RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY) { *((const char**)data) = systemPath; return true; } if (cmd == RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY) { *((const char**)data) = systemPath; return true; } fprintf(stderr, "Unrecognized environment callback command: %u\n", cmd); return false; } /// CD Management Logic Start #define CDIMAGE_SECTOR_SIZE 2048 uint32_t _currentSector = 0; void (*cd_read_callback)(int32_t lba, void * dest); int (*cd_sector_count_callback)(); ECL_EXPORT void SetCdCallbacks(void (*cdrc)(int32_t lba, void * dest), int (*cdscc)()) { cd_read_callback = cdrc; cd_sector_count_callback = cdscc; } uint32_t cd_get_size(void) { return cd_sector_count_callback(); } void cd_set_sector(const uint32_t sector_) { _currentSector = sector_; } void cd_read_sector(void *buf_) { cd_read_callback(_currentSector, buf_); } /// CD Management Logic End // SRAM management start bool _sram_changed = false; ECL_EXPORT bool sram_changed() { return _nvramChanged; } ECL_EXPORT int get_sram_size() { return NVRAM_SIZE; } ECL_EXPORT uint8_t* get_sram_buffer() { return (uint8_t*) NVRAM; } ECL_EXPORT void get_sram(uint8_t* sramBuffer) { if (NVRAM == NULL) return; memcpy(sramBuffer, get_sram_buffer(), get_sram_size()); } ECL_EXPORT void set_sram(uint8_t* sramBuffer) { if (NVRAM == NULL) opera_nvram_init(NVRAM,NVRAM_SIZE); memcpy(get_sram_buffer(), sramBuffer, get_sram_size()); } // SRAM Management end ECL_EXPORT bool Init(const char* gameFilePath, const char* biosFilePath, const char* fontFilePath, int port1Type, int port2Type, int region) { _gameFilePath = gameFilePath; _biosFilePath = biosFilePath; _fontFilePath = fontFilePath; _port1Type = port1Type; _port2Type = port2Type; _region = region; opera_lr_callbacks_set_environment(retro_environment_callback); opera_lr_callbacks_set_input_state(retro_input_state_callback); opera_lr_callbacks_set_input_poll(retro_input_poll_callback); opera_lr_callbacks_set_audio_sample_batch(retro_audio_sample_batch_callback); opera_lr_callbacks_set_video_refresh(retro_video_refresh_callback); retro_set_controller_port_device(0, port1Type); retro_set_controller_port_device(1, port2Type); retro_init(); // Setting cd callbacks opera_cdrom_set_callbacks(cd_get_size, cd_set_sector, cd_read_sector); // Loading game file struct retro_game_info game; game.path = _gameFilePath.c_str(); auto loadResult = retro_load_game(&game); if (loadResult == false) { fprintf(stderr, "Could not load game: '%s'\n", _gameFilePath.c_str()); return false; } // Getting av info struct retro_system_av_info info; retro_get_system_av_info(&info); printf("3DO Framerate: %f\n", info.timing.fps); return true; } ECL_EXPORT void opera_get_video(int& w, int& h, int& pitch, uint8_t*& buffer) { buffer = (uint8_t*)_videoBuffer; w = _videoWidth; h = _videoHeight; pitch = _videoPitch; } ECL_EXPORT void FrameAdvance(MyFrameInfo* f) { // Setting inputs _port1Value = f->port1; _port2Value = f->port2; //printf("Mouse X%d(%d), Y%d(%d), L%d, M%d, B%d\n", _port1Value.mouse.posX, _port1Value.mouse.dX, _port1Value.mouse.posY, _port1Value.mouse.dY, _port1Value.mouse.leftButton, _port1Value.mouse.middleButton, _port1Value.mouse.rightButton); //fflush(stdout); // Checking for changes in NVRAM _nvramChanged = false; // Checking if ports have been read _inputPortsRead = 0; // If resetting, do it now. Otherwise, running a single frame if (f->isReset == 1) retro_reset(); else retro_run(); // The frame is lagged if no inputs were read f->base.Lagged = !_inputPortsRead; // Setting video buffer f->base.Width = _videoWidth; f->base.Height = _videoHeight; memcpy(f->base.VideoBuffer, _videoBuffer, sizeof(uint32_t) * _videoWidth * _videoHeight); // Setting audio buffer f->base.Samples = _audioSamples; memcpy(f->base.SoundBuffer, _audioBuffer, _audioSamples * sizeof(int16_t) * _CHANNEL_COUNT); } ECL_EXPORT void GetMemoryAreas(MemoryArea *m) { int memAreaIdx = 0; m[memAreaIdx].Data = retro_get_memory_data(RETRO_MEMORY_SYSTEM_RAM); m[memAreaIdx].Name = "System RAM"; m[memAreaIdx].Size = retro_get_memory_size(RETRO_MEMORY_SYSTEM_RAM); m[memAreaIdx].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY; memAreaIdx++; m[memAreaIdx].Data = retro_get_memory_data(RETRO_MEMORY_VIDEO_RAM); m[memAreaIdx].Name = "Video RAM"; m[memAreaIdx].Size = retro_get_memory_size(RETRO_MEMORY_VIDEO_RAM); m[memAreaIdx].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE; memAreaIdx++; m[memAreaIdx].Data = get_sram_buffer(); m[memAreaIdx].Name = "Non-volatile RAM"; m[memAreaIdx].Size = get_sram_size(); m[memAreaIdx].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_SAVERAMMABLE; memAreaIdx++; } void (*InputCallback)(); ECL_EXPORT void SetInputCallback(void (*callback)()) { InputCallback = callback; }