2017-04-20 19:03:52 +00:00
# include <stdio.h>
2018-03-08 17:01:39 +00:00
# include <string.h>
2017-04-20 19:03:52 +00:00
# include <stdbool.h>
# include <unistd.h>
# include <time.h>
# include <assert.h>
# include <signal.h>
# include <stdarg.h>
2018-02-05 23:31:31 +00:00
# ifndef WIIU
2018-01-15 20:23:20 +00:00
# define AUDIO_FREQUENCY 384000
2018-02-05 23:31:31 +00:00
# else
2018-02-10 13:02:22 +00:00
/* Use the internal sample rate for the Wii U */
# define AUDIO_FREQUENCY 48000
2018-02-05 23:31:31 +00:00
# endif
2017-04-20 19:03:52 +00:00
# ifdef _WIN32
# include <direct.h>
# include <windows.h>
# define snprintf _snprintf
# endif
2017-10-12 21:02:02 +00:00
# include <Core/gb.h>
2017-04-20 19:03:52 +00:00
# include "libretro.h"
# ifdef _WIN32
2018-01-19 22:06:33 +00:00
static const char slash = ' \\ ' ;
2017-04-20 19:03:52 +00:00
# else
2018-01-19 22:06:33 +00:00
static const char slash = ' / ' ;
2017-04-20 19:03:52 +00:00
# endif
2020-05-01 20:42:08 +00:00
# define MAX_VIDEO_WIDTH 256
# define MAX_VIDEO_HEIGHT 224
# define MAX_VIDEO_PIXELS (MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT)
2017-04-20 19:03:52 +00:00
2019-01-06 02:25:20 +00:00
2018-02-03 21:48:40 +00:00
# define RETRO_MEMORY_GAMEBOY_1_SRAM ((1 << 8) | RETRO_MEMORY_SAVE_RAM)
2018-02-03 17:38:11 +00:00
# define RETRO_MEMORY_GAMEBOY_1_RTC ((2 << 8) | RETRO_MEMORY_RTC)
2018-02-03 21:48:40 +00:00
# define RETRO_MEMORY_GAMEBOY_2_SRAM ((3 << 8) | RETRO_MEMORY_SAVE_RAM)
2018-02-03 17:38:11 +00:00
# define RETRO_MEMORY_GAMEBOY_2_RTC ((3 << 8) | RETRO_MEMORY_RTC)
# define RETRO_GAME_TYPE_GAMEBOY_LINK_2P 0x101
2018-02-03 18:59:06 +00:00
char battery_save_path [ 512 ] ;
2017-04-20 19:03:52 +00:00
char symbols_path [ 512 ] ;
2018-01-19 22:06:33 +00:00
enum model {
MODEL_DMG ,
MODEL_CGB ,
2018-01-27 19:46:13 +00:00
MODEL_AGB ,
2019-01-06 01:58:23 +00:00
MODEL_SGB ,
MODEL_SGB2 ,
2018-01-27 19:46:13 +00:00
MODEL_AUTO
2018-01-19 22:06:33 +00:00
} ;
2018-06-16 10:59:33 +00:00
static const GB_model_t libretro_to_internal_model [ ] =
{
[ MODEL_DMG ] = GB_MODEL_DMG_B ,
[ MODEL_CGB ] = GB_MODEL_CGB_E ,
2019-01-06 01:58:23 +00:00
[ MODEL_AGB ] = GB_MODEL_AGB ,
[ MODEL_SGB ] = GB_MODEL_SGB ,
[ MODEL_SGB2 ] = GB_MODEL_SGB2
2018-06-16 10:59:33 +00:00
} ;
2018-02-01 15:03:40 +00:00
enum screen_layout {
2018-02-01 02:13:44 +00:00
LAYOUT_TOP_DOWN ,
LAYOUT_LEFT_RIGHT
} ;
2018-01-27 19:59:18 +00:00
2018-02-01 15:03:40 +00:00
enum audio_out {
GB_1 ,
GB_2
} ;
2018-02-01 03:06:41 +00:00
static enum model model [ 2 ] ;
2018-02-03 03:01:27 +00:00
static enum model auto_model = MODEL_CGB ;
2018-01-27 19:59:18 +00:00
static uint32_t * frame_buf = NULL ;
2018-02-01 18:26:58 +00:00
static uint32_t * frame_buf_copy = NULL ;
2017-04-20 19:03:52 +00:00
static struct retro_log_callback logging ;
static retro_log_printf_t log_cb ;
static retro_video_refresh_t video_cb ;
2019-07-17 21:13:41 +00:00
static retro_audio_sample_t audio_sample_cb ;
2017-04-20 19:03:52 +00:00
static retro_input_poll_t input_poll_cb ;
static retro_input_state_t input_state_cb ;
2020-10-10 17:14:10 +00:00
static bool libretro_supports_bitmasks = false ;
2018-02-01 03:42:26 +00:00
static unsigned emulated_devices = 1 ;
2018-03-22 05:00:03 +00:00
static bool initialized = false ;
2018-02-01 15:03:40 +00:00
static unsigned screen_layout = 0 ;
static unsigned audio_out = 0 ;
2018-02-01 02:09:47 +00:00
2018-02-01 18:26:58 +00:00
static bool geometry_updated = false ;
2018-02-03 20:59:31 +00:00
static bool link_cable_emulation = false ;
2018-02-03 21:06:50 +00:00
/*static bool infrared_emulation = false;*/
2018-02-01 18:26:58 +00:00
2018-01-15 20:23:20 +00:00
signed short soundbuf [ 1024 * 2 ] ;
2017-04-20 19:03:52 +00:00
2017-04-25 02:25:08 +00:00
char retro_system_directory [ 4096 ] ;
char retro_save_directory [ 4096 ] ;
2017-04-20 19:03:52 +00:00
char retro_game_path [ 4096 ] ;
2018-02-01 14:15:22 +00:00
GB_gameboy_t gameboy [ 2 ] ;
2018-02-01 02:09:47 +00:00
2019-01-06 01:58:23 +00:00
extern const unsigned char dmg_boot [ ] , cgb_boot [ ] , agb_boot [ ] , sgb_boot [ ] , sgb2_boot [ ] ;
extern const unsigned dmg_boot_length , cgb_boot_length , agb_boot_length , sgb_boot_length , sgb2_boot_length ;
2018-01-31 20:33:46 +00:00
bool vblank1_occurred = false , vblank2_occurred = false ;
2017-04-20 19:03:52 +00:00
static void fallback_log ( enum retro_log_level level , const char * fmt , . . . )
{
2018-01-15 20:23:20 +00:00
( void ) level ;
va_list va ;
va_start ( va , fmt ) ;
vfprintf ( stderr , fmt , va ) ;
va_end ( va ) ;
2017-04-20 19:03:52 +00:00
}
2017-10-15 23:46:37 +00:00
static struct retro_rumble_interface rumble ;
2018-01-27 19:59:18 +00:00
static void GB_update_keys_status ( GB_gameboy_t * gb , unsigned port )
2017-04-20 19:03:52 +00:00
{
2020-10-10 17:14:10 +00:00
uint16_t joypad_bits = 0 ;
2018-01-15 20:23:20 +00:00
input_poll_cb ( ) ;
2018-02-03 18:59:06 +00:00
2020-10-10 17:14:10 +00:00
if ( libretro_supports_bitmasks ) {
joypad_bits = input_state_cb ( port , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_MASK ) ;
}
else {
unsigned j ;
for ( j = 0 ; j < ( RETRO_DEVICE_ID_JOYPAD_R3 + 1 ) ; j + + ) {
if ( input_state_cb ( port , RETRO_DEVICE_JOYPAD , 0 , j ) ) {
joypad_bits | = ( 1 < < j ) ;
}
}
}
2019-01-18 00:44:43 +00:00
GB_set_key_state_for_player ( gb , GB_KEY_RIGHT , emulated_devices = = 1 ? port : 0 ,
2020-10-10 17:14:10 +00:00
joypad_bits & ( 1 < < RETRO_DEVICE_ID_JOYPAD_RIGHT ) ) ;
2019-01-18 00:44:43 +00:00
GB_set_key_state_for_player ( gb , GB_KEY_LEFT , emulated_devices = = 1 ? port : 0 ,
2020-10-10 17:14:10 +00:00
joypad_bits & ( 1 < < RETRO_DEVICE_ID_JOYPAD_LEFT ) ) ;
2019-01-18 00:44:43 +00:00
GB_set_key_state_for_player ( gb , GB_KEY_UP , emulated_devices = = 1 ? port : 0 ,
2020-10-10 17:14:10 +00:00
joypad_bits & ( 1 < < RETRO_DEVICE_ID_JOYPAD_UP ) ) ;
2019-01-18 00:44:43 +00:00
GB_set_key_state_for_player ( gb , GB_KEY_DOWN , emulated_devices = = 1 ? port : 0 ,
2020-10-10 17:14:10 +00:00
joypad_bits & ( 1 < < RETRO_DEVICE_ID_JOYPAD_DOWN ) ) ;
2019-01-18 00:44:43 +00:00
GB_set_key_state_for_player ( gb , GB_KEY_A , emulated_devices = = 1 ? port : 0 ,
2020-10-10 17:14:10 +00:00
joypad_bits & ( 1 < < RETRO_DEVICE_ID_JOYPAD_A ) ) ;
2019-01-18 00:44:43 +00:00
GB_set_key_state_for_player ( gb , GB_KEY_B , emulated_devices = = 1 ? port : 0 ,
2020-10-10 17:14:10 +00:00
joypad_bits & ( 1 < < RETRO_DEVICE_ID_JOYPAD_B ) ) ;
2019-01-18 00:44:43 +00:00
GB_set_key_state_for_player ( gb , GB_KEY_SELECT , emulated_devices = = 1 ? port : 0 ,
2020-10-10 17:14:10 +00:00
joypad_bits & ( 1 < < RETRO_DEVICE_ID_JOYPAD_SELECT ) ) ;
2019-01-18 00:44:43 +00:00
GB_set_key_state_for_player ( gb , GB_KEY_START , emulated_devices = = 1 ? port : 0 ,
2020-10-10 17:14:10 +00:00
joypad_bits & ( 1 < < RETRO_DEVICE_ID_JOYPAD_START ) ) ;
2018-02-03 17:38:11 +00:00
2017-04-20 19:03:52 +00:00
}
2019-10-19 16:26:04 +00:00
static void rumble_callback ( GB_gameboy_t * gb , double amplitude )
{
2020-05-07 20:43:49 +00:00
if ( ! rumble . set_rumble_state ) return ;
2019-10-19 16:26:04 +00:00
if ( gb = = & gameboy [ 0 ] ) {
rumble . set_rumble_state ( 0 , RETRO_RUMBLE_STRONG , 65535 * amplitude ) ;
}
else if ( gb = = & gameboy [ 1 ] ) {
rumble . set_rumble_state ( 1 , RETRO_RUMBLE_STRONG , 65535 * amplitude ) ;
}
}
2017-04-20 19:03:52 +00:00
2019-06-15 20:22:27 +00:00
static void audio_callback ( GB_gameboy_t * gb , GB_sample_t * sample )
2017-04-20 19:03:52 +00:00
{
2019-06-15 20:22:27 +00:00
if ( ( audio_out = = GB_1 & & gb = = & gameboy [ 0 ] ) | |
( audio_out = = GB_2 & & gb = = & gameboy [ 1 ] ) ) {
2019-07-17 21:13:41 +00:00
audio_sample_cb ( sample - > left , sample - > right ) ;
2018-01-15 20:23:20 +00:00
}
2017-04-20 19:03:52 +00:00
}
2018-01-27 19:59:18 +00:00
static void vblank1 ( GB_gameboy_t * gb )
2017-04-20 19:03:52 +00:00
{
2018-01-31 20:33:46 +00:00
vblank1_occurred = true ;
2017-04-20 19:03:52 +00:00
}
2018-01-27 19:59:18 +00:00
static void vblank2 ( GB_gameboy_t * gb )
{
2018-01-31 20:33:46 +00:00
vblank2_occurred = true ;
2018-01-27 19:59:18 +00:00
}
2019-03-15 12:36:10 +00:00
static bool bit_to_send1 = true , bit_to_send2 = true ;
2018-01-31 20:33:46 +00:00
2019-03-15 12:36:10 +00:00
static void serial_start1 ( GB_gameboy_t * gb , bool bit_received )
2018-01-27 19:59:18 +00:00
{
2019-03-15 12:36:10 +00:00
bit_to_send1 = bit_received ;
2018-01-27 19:59:18 +00:00
}
2019-03-15 12:36:10 +00:00
static bool serial_end1 ( GB_gameboy_t * gb )
2018-01-27 19:59:18 +00:00
{
2019-03-15 12:36:10 +00:00
bool ret = GB_serial_get_data_bit ( & gameboy [ 1 ] ) ;
GB_serial_set_data_bit ( & gameboy [ 1 ] , bit_to_send1 ) ;
2018-01-31 20:33:46 +00:00
return ret ;
2018-01-27 19:59:18 +00:00
}
2019-03-15 12:36:10 +00:00
static void serial_start2 ( GB_gameboy_t * gb , bool bit_received )
2018-01-27 19:59:18 +00:00
{
2019-03-15 12:36:10 +00:00
bit_to_send2 = bit_received ;
2018-01-27 19:59:18 +00:00
}
2019-03-15 12:36:10 +00:00
static bool serial_end2 ( GB_gameboy_t * gb )
2018-01-27 19:59:18 +00:00
{
2019-03-15 12:36:10 +00:00
bool ret = GB_serial_get_data_bit ( & gameboy [ 0 ] ) ;
GB_serial_set_data_bit ( & gameboy [ 0 ] , bit_to_send2 ) ;
2018-01-31 20:33:46 +00:00
return ret ;
2017-04-20 19:03:52 +00:00
}
2020-11-21 13:36:21 +00:00
static void infrared_callback1 ( GB_gameboy_t * gb , bool output )
{
GB_set_infrared_input ( & gameboy [ 1 ] , output ) ;
}
static void infrared_callback2 ( GB_gameboy_t * gb , bool output )
{
GB_set_infrared_input ( & gameboy [ 0 ] , output ) ;
}
2018-02-01 03:42:26 +00:00
static uint32_t rgb_encode ( GB_gameboy_t * gb , uint8_t r , uint8_t g , uint8_t b )
{
2020-04-24 17:37:57 +00:00
return r < < 16 | g < < 8 | b ;
2018-02-01 03:42:26 +00:00
}
static retro_environment_t environ_cb ;
2018-03-08 19:45:58 +00:00
/* variables for single cart mode */
static const struct retro_variable vars_single [ ] = {
2021-06-25 16:57:56 +00:00
{ " sameboy_color_correction_mode " , " Color correction; emulate hardware|preserve brightness|reduce contrast|harsh reality|off|correct curves " } ,
2020-05-01 20:42:08 +00:00
{ " sameboy_high_pass_filter_mode " , " High-pass filter; accurate|remove dc offset|off " } ,
2020-10-08 02:59:29 +00:00
{ " sameboy_model " , " Emulated model (Restart game); Auto|Game Boy|Game Boy Color|Game Boy Advance|Super Game Boy|Super Game Boy 2 " } ,
2020-05-01 20:42:08 +00:00
{ " sameboy_border " , " Display border; Super Game Boy only|always|never " } ,
{ " sameboy_rumble " , " Enable rumble; rumble-enabled games|all games|never " } ,
2021-02-25 23:07:46 +00:00
{ " sameboy_rtc " , " Real Time Clock emulation; sync to system clock|accurate " } ,
2018-02-03 19:22:47 +00:00
{ NULL }
} ;
2018-03-08 19:45:58 +00:00
/* variables for dual cart dual gameboy mode */
static const struct retro_variable vars_dual [ ] = {
2018-03-08 15:57:57 +00:00
{ " sameboy_link " , " Link cable emulation; enabled|disabled " } ,
2018-02-03 21:06:50 +00:00
/*{ "sameboy_ir", "Infrared Sensor Emulation; disabled|enabled" },*/
2018-03-08 15:57:57 +00:00
{ " sameboy_screen_layout " , " Screen layout; top-down|left-right " } ,
2018-02-03 19:22:47 +00:00
{ " sameboy_audio_output " , " Audio output; Game Boy #1|Game Boy #2 " } ,
2020-10-08 02:59:29 +00:00
{ " sameboy_model_1 " , " Emulated model for Game Boy #1 (Restart game); Auto|Game Boy|Game Boy Color|Game Boy Advance " } ,
{ " sameboy_model_2 " , " Emulated model for Game Boy #2 (Restart game); Auto|Game Boy|Game Boy Color|Game Boy Advance " } ,
2021-06-25 16:57:56 +00:00
{ " sameboy_color_correction_mode_1 " , " Color correction for Game Boy #1; emulate hardware|preserve brightness|reduce contrast|harsh reality|off|correct curves " } ,
{ " sameboy_color_correction_mode_2 " , " Color correction for Game Boy #2; emulate hardware|preserve brightness|harsh reality|off|correct curves " } ,
2020-05-01 20:42:08 +00:00
{ " sameboy_high_pass_filter_mode_1 " , " High-pass filter for Game Boy #1; accurate|remove dc offset|off " } ,
{ " sameboy_high_pass_filter_mode_2 " , " High-pass filter for Game Boy #2; accurate|remove dc offset|off " } ,
{ " sameboy_rumble_1 " , " Enable rumble for Game Boy #1; rumble-enabled games|all games|never " } ,
{ " sameboy_rumble_2 " , " Enable rumble for Game Boy #2; rumble-enabled games|all games|never " } ,
2018-02-01 03:42:26 +00:00
{ NULL }
} ;
2018-02-03 17:38:11 +00:00
static const struct retro_subsystem_memory_info gb1_memory [ ] = {
2018-02-04 01:23:08 +00:00
{ " srm " , RETRO_MEMORY_GAMEBOY_1_SRAM } ,
2018-02-03 17:38:11 +00:00
{ " rtc " , RETRO_MEMORY_GAMEBOY_1_RTC } ,
} ;
static const struct retro_subsystem_memory_info gb2_memory [ ] = {
2018-02-04 01:23:08 +00:00
{ " srm " , RETRO_MEMORY_GAMEBOY_2_SRAM } ,
2018-02-03 17:38:11 +00:00
{ " rtc " , RETRO_MEMORY_GAMEBOY_2_RTC } ,
} ;
static const struct retro_subsystem_rom_info gb_roms [ ] = {
{ " GameBoy #1 " , " gb|gbc " , true , false , true , gb1_memory , 1 } ,
{ " GameBoy #2 " , " gb|gbc " , true , false , true , gb2_memory , 1 } ,
} ;
2019-01-06 22:09:37 +00:00
static const struct retro_subsystem_info subsystems [ ] = {
{ " 2 Player Game Boy Link " , " gb_link_2p " , gb_roms , 2 , RETRO_GAME_TYPE_GAMEBOY_LINK_2P } ,
{ NULL } ,
2018-02-03 17:38:11 +00:00
} ;
2018-02-03 18:59:06 +00:00
static const struct retro_controller_description controllers [ ] = {
2018-02-03 20:59:31 +00:00
{ " Nintendo Game Boy " , RETRO_DEVICE_SUBCLASS ( RETRO_DEVICE_JOYPAD , 0 ) } ,
2018-02-03 18:59:06 +00:00
} ;
2019-01-18 00:44:43 +00:00
static const struct retro_controller_description controllers_sgb [ ] = {
{ " SNES/SFC Gamepad " , RETRO_DEVICE_SUBCLASS ( RETRO_DEVICE_JOYPAD , 0 ) } ,
} ;
static struct retro_input_descriptor descriptors_1p [ ] = {
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_LEFT , " Left " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_UP , " Up " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_DOWN , " Down " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_RIGHT , " Right " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_B , " B " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_A , " A " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_SELECT , " Select " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_START , " Start " } ,
{ 0 } ,
} ;
static struct retro_input_descriptor descriptors_2p [ ] = {
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_LEFT , " Left " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_UP , " Up " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_DOWN , " Down " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_RIGHT , " Right " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_B , " B " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_A , " A " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_SELECT , " Select " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_START , " Start " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_LEFT , " Left " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_UP , " Up " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_DOWN , " Down " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_RIGHT , " Right " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_B , " B " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_A , " A " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_SELECT , " Select " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_START , " Start " } ,
{ 0 } ,
} ;
2019-01-18 01:33:20 +00:00
static struct retro_input_descriptor descriptors_4p [ ] = {
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_LEFT , " Left " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_UP , " Up " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_DOWN , " Down " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_RIGHT , " Right " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_B , " B " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_A , " A " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_SELECT , " Select " } ,
{ 0 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_START , " Start " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_LEFT , " Left " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_UP , " Up " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_DOWN , " Down " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_RIGHT , " Right " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_B , " B " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_A , " A " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_SELECT , " Select " } ,
{ 1 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_START , " Start " } ,
{ 2 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_LEFT , " Left " } ,
{ 2 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_UP , " Up " } ,
{ 2 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_DOWN , " Down " } ,
{ 2 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_RIGHT , " Right " } ,
{ 2 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_B , " B " } ,
{ 2 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_A , " A " } ,
{ 2 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_SELECT , " Select " } ,
{ 3 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_START , " Start " } ,
{ 3 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_LEFT , " Left " } ,
{ 3 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_UP , " Up " } ,
{ 3 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_DOWN , " Down " } ,
{ 3 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_RIGHT , " Right " } ,
{ 3 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_B , " B " } ,
{ 3 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_A , " A " } ,
{ 3 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_SELECT , " Select " } ,
{ 3 , RETRO_DEVICE_JOYPAD , 0 , RETRO_DEVICE_ID_JOYPAD_START , " Start " } ,
{ 0 } ,
} ;
2018-02-03 20:59:31 +00:00
static void set_link_cable_state ( bool state )
{
2020-04-24 17:37:57 +00:00
if ( state & & emulated_devices = = 2 ) {
2019-03-15 12:36:10 +00:00
GB_set_serial_transfer_bit_start_callback ( & gameboy [ 0 ] , serial_start1 ) ;
GB_set_serial_transfer_bit_end_callback ( & gameboy [ 0 ] , serial_end1 ) ;
GB_set_serial_transfer_bit_start_callback ( & gameboy [ 1 ] , serial_start2 ) ;
GB_set_serial_transfer_bit_end_callback ( & gameboy [ 1 ] , serial_end2 ) ;
2020-11-21 13:36:21 +00:00
GB_set_infrared_callback ( & gameboy [ 0 ] , infrared_callback1 ) ;
GB_set_infrared_callback ( & gameboy [ 1 ] , infrared_callback2 ) ;
2018-02-03 20:59:31 +00:00
}
2020-04-24 17:37:57 +00:00
else if ( ! state ) {
2019-03-15 12:36:10 +00:00
GB_set_serial_transfer_bit_start_callback ( & gameboy [ 0 ] , NULL ) ;
GB_set_serial_transfer_bit_end_callback ( & gameboy [ 0 ] , NULL ) ;
GB_set_serial_transfer_bit_start_callback ( & gameboy [ 1 ] , NULL ) ;
GB_set_serial_transfer_bit_end_callback ( & gameboy [ 1 ] , NULL ) ;
2020-11-21 13:36:21 +00:00
GB_set_infrared_callback ( & gameboy [ 0 ] , NULL ) ;
GB_set_infrared_callback ( & gameboy [ 1 ] , NULL ) ;
2018-02-03 20:59:31 +00:00
}
}
2020-05-01 20:42:08 +00:00
static void boot_rom_load ( GB_gameboy_t * gb , GB_boot_rom_t type )
{
const char * model_name = ( char * [ ] ) {
2020-05-10 19:05:47 +00:00
[ GB_BOOT_ROM_DMG0 ] = " dmg0 " ,
[ GB_BOOT_ROM_DMG ] = " dmg " ,
[ GB_BOOT_ROM_MGB ] = " mgb " ,
[ GB_BOOT_ROM_SGB ] = " sgb " ,
[ GB_BOOT_ROM_SGB2 ] = " sgb2 " ,
[ GB_BOOT_ROM_CGB0 ] = " cgb0 " ,
[ GB_BOOT_ROM_CGB ] = " cgb " ,
[ GB_BOOT_ROM_AGB ] = " agb " ,
2020-05-01 20:42:08 +00:00
} [ type ] ;
const uint8_t * boot_code = ( const unsigned char * [ ] )
{
[ GB_BOOT_ROM_DMG0 ] = dmg_boot , // dmg0 not implemented yet
[ GB_BOOT_ROM_DMG ] = dmg_boot ,
[ GB_BOOT_ROM_MGB ] = dmg_boot , // mgb not implemented yet
[ GB_BOOT_ROM_SGB ] = sgb_boot ,
[ GB_BOOT_ROM_SGB2 ] = sgb2_boot ,
[ GB_BOOT_ROM_CGB0 ] = cgb_boot , // cgb0 not implemented yet
[ GB_BOOT_ROM_CGB ] = cgb_boot ,
[ GB_BOOT_ROM_AGB ] = agb_boot ,
} [ type ] ;
unsigned boot_length = ( unsigned [ ] ) {
[ GB_BOOT_ROM_DMG0 ] = dmg_boot_length , // dmg0 not implemented yet
[ GB_BOOT_ROM_DMG ] = dmg_boot_length ,
[ GB_BOOT_ROM_MGB ] = dmg_boot_length , // mgb not implemented yet
[ GB_BOOT_ROM_SGB ] = sgb_boot_length ,
[ GB_BOOT_ROM_SGB2 ] = sgb2_boot_length ,
[ GB_BOOT_ROM_CGB0 ] = cgb_boot_length , // cgb0 not implemented yet
[ GB_BOOT_ROM_CGB ] = cgb_boot_length ,
[ GB_BOOT_ROM_AGB ] = agb_boot_length ,
} [ type ] ;
char buf [ 256 ] ;
snprintf ( buf , sizeof ( buf ) , " %s%c%s_boot.bin " , retro_system_directory , slash , model_name ) ;
log_cb ( RETRO_LOG_INFO , " Initializing as model: %s \n " , model_name ) ;
log_cb ( RETRO_LOG_INFO , " Loading boot image: %s \n " , buf ) ;
if ( GB_load_boot_rom ( gb , buf ) ) {
GB_load_boot_rom_from_buffer ( gb , boot_code , boot_length ) ;
}
}
2020-06-09 17:09:50 +00:00
static void retro_set_memory_maps ( void )
2018-01-19 22:06:33 +00:00
{
2019-07-19 20:55:59 +00:00
struct retro_memory_descriptor descs [ 11 ] ;
2018-01-19 22:06:33 +00:00
size_t size ;
uint16_t bank ;
2020-06-09 17:09:50 +00:00
unsigned i ;
2018-02-05 23:17:55 +00:00
2018-02-01 02:09:47 +00:00
/* todo: add netplay awareness for this so achievements can be granted on the respective client */
i = 0 ;
2018-01-19 22:06:33 +00:00
memset ( descs , 0 , sizeof ( descs ) ) ;
2018-02-01 02:09:47 +00:00
2019-07-19 20:55:59 +00:00
descs [ 0 ] . ptr = GB_get_direct_access ( & gameboy [ i ] , GB_DIRECT_ACCESS_IE , & size , & bank ) ;
descs [ 0 ] . start = 0xFFFF ;
descs [ 0 ] . len = 1 ;
descs [ 1 ] . ptr = GB_get_direct_access ( & gameboy [ i ] , GB_DIRECT_ACCESS_HRAM , & size , & bank ) ;
descs [ 1 ] . start = 0xFF80 ;
descs [ 1 ] . len = 0x0080 ;
descs [ 2 ] . ptr = GB_get_direct_access ( & gameboy [ i ] , GB_DIRECT_ACCESS_RAM , & size , & bank ) ;
descs [ 2 ] . start = 0xC000 ;
descs [ 2 ] . len = 0x1000 ;
descs [ 3 ] . ptr = descs [ 2 ] . ptr + 0x1000 ; /* GB RAM/GBC RAM bank 1 */
descs [ 3 ] . start = 0xD000 ;
descs [ 3 ] . len = 0x1000 ;
descs [ 4 ] . ptr = GB_get_direct_access ( & gameboy [ i ] , GB_DIRECT_ACCESS_CART_RAM , & size , & bank ) ;
descs [ 4 ] . start = 0xA000 ;
descs [ 4 ] . len = 0x2000 ;
descs [ 5 ] . ptr = GB_get_direct_access ( & gameboy [ i ] , GB_DIRECT_ACCESS_VRAM , & size , & bank ) ;
descs [ 5 ] . start = 0x8000 ;
descs [ 5 ] . len = 0x2000 ;
descs [ 6 ] . ptr = GB_get_direct_access ( & gameboy [ i ] , GB_DIRECT_ACCESS_ROM , & size , & bank ) ;
descs [ 6 ] . start = 0x0000 ;
descs [ 6 ] . len = 0x4000 ;
descs [ 6 ] . flags = RETRO_MEMDESC_CONST ;
descs [ 7 ] . ptr = descs [ 6 ] . ptr + ( bank * 0x4000 ) ;
descs [ 7 ] . start = 0x4000 ;
descs [ 7 ] . len = 0x4000 ;
descs [ 7 ] . flags = RETRO_MEMDESC_CONST ;
2019-07-13 14:11:29 +00:00
descs [ 8 ] . ptr = GB_get_direct_access ( & gameboy [ i ] , GB_DIRECT_ACCESS_OAM , & size , & bank ) ;
descs [ 8 ] . start = 0xFE00 ;
descs [ 8 ] . len = 0x00A0 ;
2020-04-24 17:37:57 +00:00
descs [ 8 ] . select = 0xFFFFFF00 ;
2019-07-19 20:55:59 +00:00
2019-07-13 14:11:29 +00:00
descs [ 9 ] . ptr = descs [ 2 ] . ptr + 0x2000 ; /* GBC RAM bank 2 */
descs [ 9 ] . start = 0x10000 ;
descs [ 9 ] . len = GB_is_cgb ( & gameboy [ i ] ) ? 0x6000 : 0 ; /* 0x1000 per bank (2-7), unmapped on GB */
2020-04-24 17:37:57 +00:00
descs [ 9 ] . select = 0xFFFF0000 ;
2019-07-19 20:55:59 +00:00
2019-07-13 14:11:29 +00:00
descs [ 10 ] . ptr = GB_get_direct_access ( & gameboy [ i ] , GB_DIRECT_ACCESS_IO , & size , & bank ) ;
descs [ 10 ] . start = 0xFF00 ;
descs [ 10 ] . len = 0x0080 ;
2020-04-24 17:37:57 +00:00
descs [ 10 ] . select = 0xFFFFFF00 ;
2019-04-06 08:10:41 +00:00
2018-01-19 22:06:33 +00:00
struct retro_memory_map mmaps ;
mmaps . descriptors = descs ;
mmaps . num_descriptors = sizeof ( descs ) / sizeof ( descs [ 0 ] ) ;
environ_cb ( RETRO_ENVIRONMENT_SET_MEMORY_MAPS , & mmaps ) ;
2020-06-09 17:09:50 +00:00
}
static void init_for_current_model ( unsigned id )
{
unsigned i = id ;
enum model effective_model ;
effective_model = model [ i ] ;
if ( effective_model = = MODEL_AUTO ) {
effective_model = auto_model ;
}
if ( GB_is_inited ( & gameboy [ i ] ) ) {
GB_switch_model_and_reset ( & gameboy [ i ] , libretro_to_internal_model [ effective_model ] ) ;
}
else {
GB_init ( & gameboy [ i ] , libretro_to_internal_model [ effective_model ] ) ;
}
GB_set_boot_rom_load_callback ( & gameboy [ i ] , boot_rom_load ) ;
/* When running multiple devices they are assumed to use the same resolution */
GB_set_pixels_output ( & gameboy [ i ] ,
( uint32_t * ) ( frame_buf + GB_get_screen_width ( & gameboy [ 0 ] ) * GB_get_screen_height ( & gameboy [ 0 ] ) * i ) ) ;
GB_set_rgb_encode_callback ( & gameboy [ i ] , rgb_encode ) ;
GB_set_sample_rate ( & gameboy [ i ] , AUDIO_FREQUENCY ) ;
GB_apu_set_sample_callback ( & gameboy [ i ] , audio_callback ) ;
GB_set_rumble_callback ( & gameboy [ i ] , rumble_callback ) ;
/* todo: attempt to make these more generic */
GB_set_vblank_callback ( & gameboy [ 0 ] , ( GB_vblank_callback_t ) vblank1 ) ;
if ( emulated_devices = = 2 ) {
GB_set_vblank_callback ( & gameboy [ 1 ] , ( GB_vblank_callback_t ) vblank2 ) ;
if ( link_cable_emulation ) {
set_link_cable_state ( true ) ;
}
}
2019-01-18 00:44:43 +00:00
/* Let's be extremely nitpicky about how devices and descriptors are set */
2020-04-24 17:37:57 +00:00
if ( emulated_devices = = 1 & & ( model [ 0 ] = = MODEL_SGB | | model [ 0 ] = = MODEL_SGB2 ) ) {
2019-01-18 00:44:43 +00:00
static const struct retro_controller_info ports [ ] = {
2019-01-18 01:33:20 +00:00
{ controllers_sgb , 1 } ,
{ controllers_sgb , 1 } ,
2019-01-18 00:44:43 +00:00
{ controllers_sgb , 1 } ,
{ controllers_sgb , 1 } ,
{ NULL , 0 } ,
} ;
environ_cb ( RETRO_ENVIRONMENT_SET_CONTROLLER_INFO , ( void * ) ports ) ;
2019-01-18 01:33:20 +00:00
environ_cb ( RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS , descriptors_4p ) ;
2019-01-18 00:44:43 +00:00
}
2020-04-24 17:37:57 +00:00
else if ( emulated_devices = = 1 ) {
2019-01-18 00:44:43 +00:00
static const struct retro_controller_info ports [ ] = {
{ controllers , 1 } ,
{ NULL , 0 } ,
} ;
environ_cb ( RETRO_ENVIRONMENT_SET_CONTROLLER_INFO , ( void * ) ports ) ;
2019-01-18 01:33:20 +00:00
environ_cb ( RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS , descriptors_1p ) ;
2019-01-18 00:44:43 +00:00
}
2020-04-24 17:37:57 +00:00
else {
2019-01-18 00:44:43 +00:00
static const struct retro_controller_info ports [ ] = {
{ controllers , 1 } ,
{ controllers , 1 } ,
{ NULL , 0 } ,
} ;
environ_cb ( RETRO_ENVIRONMENT_SET_CONTROLLER_INFO , ( void * ) ports ) ;
2019-01-18 01:33:20 +00:00
environ_cb ( RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS , descriptors_2p ) ;
2019-01-18 00:44:43 +00:00
}
2018-01-19 22:06:33 +00:00
}
2019-01-06 22:09:37 +00:00
static void check_variables ( )
2017-04-20 19:03:52 +00:00
{
2018-01-15 20:23:20 +00:00
struct retro_variable var = { 0 } ;
2020-04-24 17:37:57 +00:00
if ( emulated_devices = = 1 ) {
2018-02-01 03:06:41 +00:00
var . key = " sameboy_color_correction_mode " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " off " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_DISABLED ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " correct curves " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_CORRECT_CURVES ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " emulate hardware " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_EMULATE_HARDWARE ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " preserve brightness " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " reduce contrast " ) = = 0 ) {
2020-03-25 18:33:13 +00:00
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_REDUCE_CONTRAST ) ;
2020-05-01 13:03:26 +00:00
}
2021-06-25 16:57:56 +00:00
else if ( strcmp ( var . value , " harsh reality " ) = = 0 ) {
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_LOW_CONTRAST ) ;
}
2018-01-27 19:59:18 +00:00
}
2020-05-01 12:50:22 +00:00
var . key = " sameboy_rumble " ;
var . value = NULL ;
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " never " ) = = 0 ) {
2020-05-01 12:50:22 +00:00
GB_set_rumble_mode ( & gameboy [ 0 ] , GB_RUMBLE_DISABLED ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " rumble-enabled games " ) = = 0 ) {
2020-05-01 12:50:22 +00:00
GB_set_rumble_mode ( & gameboy [ 0 ] , GB_RUMBLE_CARTRIDGE_ONLY ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " all games " ) = = 0 ) {
2020-05-01 12:50:22 +00:00
GB_set_rumble_mode ( & gameboy [ 0 ] , GB_RUMBLE_ALL_GAMES ) ;
2020-05-01 13:03:26 +00:00
}
2020-05-01 12:50:22 +00:00
}
2021-02-25 23:07:46 +00:00
var . key = " sameboy_rtc " ;
var . value = NULL ;
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
if ( strcmp ( var . value , " sync to system clock " ) = = 0 ) {
GB_set_rtc_mode ( & gameboy [ 0 ] , GB_RTC_MODE_SYNC_TO_HOST ) ;
}
else if ( strcmp ( var . value , " accurate " ) = = 0 ) {
GB_set_rtc_mode ( & gameboy [ 0 ] , GB_RTC_MODE_ACCURATE ) ;
}
}
2018-02-03 18:59:06 +00:00
2018-02-01 03:06:41 +00:00
var . key = " sameboy_high_pass_filter_mode " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " off " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_highpass_filter_mode ( & gameboy [ 0 ] , GB_HIGHPASS_OFF ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " accurate " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_highpass_filter_mode ( & gameboy [ 0 ] , GB_HIGHPASS_ACCURATE ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " remove dc offset " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_highpass_filter_mode ( & gameboy [ 0 ] , GB_HIGHPASS_REMOVE_DC_OFFSET ) ;
2020-05-01 13:03:26 +00:00
}
2018-01-27 19:59:18 +00:00
}
2018-02-03 18:59:06 +00:00
2018-02-01 03:06:41 +00:00
var . key = " sameboy_model " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2018-02-01 03:06:41 +00:00
enum model new_model = model [ 0 ] ;
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " Game Boy " ) = = 0 ) {
2018-02-01 03:06:41 +00:00
new_model = MODEL_DMG ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Game Boy Color " ) = = 0 ) {
2018-02-01 03:06:41 +00:00
new_model = MODEL_CGB ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Game Boy Advance " ) = = 0 ) {
2018-02-01 03:06:41 +00:00
new_model = MODEL_AGB ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Super Game Boy " ) = = 0 ) {
2019-01-06 01:58:23 +00:00
new_model = MODEL_SGB ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Super Game Boy 2 " ) = = 0 ) {
2019-01-06 02:25:20 +00:00
new_model = MODEL_SGB2 ;
2020-05-01 13:03:26 +00:00
}
else {
2018-02-01 03:06:41 +00:00
new_model = MODEL_AUTO ;
2020-05-01 13:03:26 +00:00
}
2018-02-03 18:59:06 +00:00
2020-10-08 02:59:29 +00:00
model [ 0 ] = new_model ;
2018-01-27 19:59:18 +00:00
}
2019-10-06 01:51:59 +00:00
var . key = " sameboy_border " ;
var . value = NULL ;
2020-05-01 20:42:08 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
if ( strcmp ( var . value , " never " ) = = 0 ) {
GB_set_border_mode ( & gameboy [ 0 ] , GB_BORDER_NEVER ) ;
2020-05-01 13:03:26 +00:00
}
2020-05-01 20:42:08 +00:00
else if ( strcmp ( var . value , " Super Game Boy only " ) = = 0 ) {
GB_set_border_mode ( & gameboy [ 0 ] , GB_BORDER_SGB ) ;
2020-05-01 13:03:26 +00:00
}
2020-05-01 20:42:08 +00:00
else if ( strcmp ( var . value , " always " ) = = 0 ) {
GB_set_border_mode ( & gameboy [ 0 ] , GB_BORDER_ALWAYS ) ;
}
geometry_updated = true ;
2019-10-06 01:51:59 +00:00
}
2018-01-15 20:23:20 +00:00
}
2020-05-01 20:42:08 +00:00
else {
GB_set_border_mode ( & gameboy [ 0 ] , GB_BORDER_NEVER ) ;
GB_set_border_mode ( & gameboy [ 1 ] , GB_BORDER_NEVER ) ;
2021-02-25 23:07:46 +00:00
GB_set_rtc_mode ( & gameboy [ 0 ] , GB_RTC_MODE_ACCURATE ) ;
GB_set_rtc_mode ( & gameboy [ 1 ] , GB_RTC_MODE_ACCURATE ) ;
2018-02-01 03:06:41 +00:00
var . key = " sameboy_color_correction_mode_1 " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " off " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_DISABLED ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " correct curves " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_CORRECT_CURVES ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " emulate hardware " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_EMULATE_HARDWARE ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " preserve brightness " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " reduce contrast " ) = = 0 ) {
2020-03-25 18:33:13 +00:00
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_REDUCE_CONTRAST ) ;
2020-05-01 13:03:26 +00:00
}
2021-06-25 16:57:56 +00:00
else if ( strcmp ( var . value , " harsh reality " ) = = 0 ) {
GB_set_color_correction_mode ( & gameboy [ 0 ] , GB_COLOR_CORRECTION_LOW_CONTRAST ) ;
}
2018-01-27 19:59:18 +00:00
}
2018-02-01 03:06:41 +00:00
var . key = " sameboy_color_correction_mode_2 " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " off " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 1 ] , GB_COLOR_CORRECTION_DISABLED ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " correct curves " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 1 ] , GB_COLOR_CORRECTION_CORRECT_CURVES ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " emulate hardware " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 1 ] , GB_COLOR_CORRECTION_EMULATE_HARDWARE ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " preserve brightness " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_color_correction_mode ( & gameboy [ 1 ] , GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " reduce contrast " ) = = 0 ) {
2020-03-25 18:33:13 +00:00
GB_set_color_correction_mode ( & gameboy [ 1 ] , GB_COLOR_CORRECTION_REDUCE_CONTRAST ) ;
2020-05-01 13:03:26 +00:00
}
2021-06-25 16:57:56 +00:00
else if ( strcmp ( var . value , " harsh reality " ) = = 0 ) {
GB_set_color_correction_mode ( & gameboy [ 1 ] , GB_COLOR_CORRECTION_LOW_CONTRAST ) ;
}
2020-03-25 18:33:13 +00:00
2018-01-27 19:59:18 +00:00
}
2020-05-01 12:50:22 +00:00
var . key = " sameboy_rumble_1 " ;
var . value = NULL ;
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " never " ) = = 0 ) {
2020-05-01 12:50:22 +00:00
GB_set_rumble_mode ( & gameboy [ 0 ] , GB_RUMBLE_DISABLED ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " rumble-enabled games " ) = = 0 ) {
2020-05-01 12:50:22 +00:00
GB_set_rumble_mode ( & gameboy [ 0 ] , GB_RUMBLE_CARTRIDGE_ONLY ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " all games " ) = = 0 ) {
2020-05-01 12:50:22 +00:00
GB_set_rumble_mode ( & gameboy [ 0 ] , GB_RUMBLE_ALL_GAMES ) ;
2020-05-01 13:03:26 +00:00
}
2020-05-01 12:50:22 +00:00
}
var . key = " sameboy_rumble_2 " ;
var . value = NULL ;
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " never " ) = = 0 ) {
2020-05-01 12:50:22 +00:00
GB_set_rumble_mode ( & gameboy [ 1 ] , GB_RUMBLE_DISABLED ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " rumble-enabled games " ) = = 0 ) {
2020-05-01 12:50:22 +00:00
GB_set_rumble_mode ( & gameboy [ 1 ] , GB_RUMBLE_CARTRIDGE_ONLY ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " all games " ) = = 0 ) {
2020-05-01 12:50:22 +00:00
GB_set_rumble_mode ( & gameboy [ 1 ] , GB_RUMBLE_ALL_GAMES ) ;
2020-05-01 13:03:26 +00:00
}
2020-05-01 12:50:22 +00:00
}
2018-02-01 03:06:41 +00:00
var . key = " sameboy_high_pass_filter_mode_1 " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " off " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_highpass_filter_mode ( & gameboy [ 0 ] , GB_HIGHPASS_OFF ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " accurate " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_highpass_filter_mode ( & gameboy [ 0 ] , GB_HIGHPASS_ACCURATE ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " remove dc offset " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_highpass_filter_mode ( & gameboy [ 0 ] , GB_HIGHPASS_REMOVE_DC_OFFSET ) ;
2020-05-01 13:03:26 +00:00
}
2018-01-27 19:59:18 +00:00
}
2018-02-05 23:17:55 +00:00
2018-02-01 03:06:41 +00:00
var . key = " sameboy_high_pass_filter_mode_2 " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " off " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_highpass_filter_mode ( & gameboy [ 1 ] , GB_HIGHPASS_OFF ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " accurate " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_highpass_filter_mode ( & gameboy [ 1 ] , GB_HIGHPASS_ACCURATE ) ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " remove dc offset " ) = = 0 ) {
2018-02-01 14:15:22 +00:00
GB_set_highpass_filter_mode ( & gameboy [ 1 ] , GB_HIGHPASS_REMOVE_DC_OFFSET ) ;
2020-05-01 13:03:26 +00:00
}
2018-01-27 19:59:18 +00:00
}
2018-02-01 03:06:41 +00:00
var . key = " sameboy_model_1 " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2018-02-01 03:06:41 +00:00
enum model new_model = model [ 0 ] ;
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " Game Boy " ) = = 0 ) {
2018-02-01 03:06:41 +00:00
new_model = MODEL_DMG ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Game Boy Color " ) = = 0 ) {
2018-02-01 03:06:41 +00:00
new_model = MODEL_CGB ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Game Boy Advance " ) = = 0 ) {
2018-02-01 03:06:41 +00:00
new_model = MODEL_AGB ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Super Game Boy " ) = = 0 ) {
2019-01-06 01:58:23 +00:00
new_model = MODEL_SGB ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Super Game Boy 2 " ) = = 0 ) {
2019-01-06 02:25:20 +00:00
new_model = MODEL_SGB2 ;
2020-05-01 13:03:26 +00:00
}
else {
2018-02-03 18:59:06 +00:00
new_model = MODEL_AUTO ;
2020-05-01 13:03:26 +00:00
}
2018-03-08 15:57:57 +00:00
2020-10-08 02:59:29 +00:00
model [ 0 ] = new_model ;
2018-01-19 22:06:33 +00:00
}
2018-02-01 03:06:41 +00:00
var . key = " sameboy_model_2 " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2018-02-01 04:21:29 +00:00
enum model new_model = model [ 1 ] ;
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " Game Boy " ) = = 0 ) {
2018-02-01 03:06:41 +00:00
new_model = MODEL_DMG ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Game Boy Color " ) = = 0 ) {
2018-02-01 03:06:41 +00:00
new_model = MODEL_CGB ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Game Boy Advance " ) = = 0 ) {
2018-02-01 03:06:41 +00:00
new_model = MODEL_AGB ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Super Game Boy " ) = = 0 ) {
2019-01-06 01:58:23 +00:00
new_model = MODEL_SGB ;
2020-05-01 13:03:26 +00:00
}
else if ( strcmp ( var . value , " Super Game Boy 2 " ) = = 0 ) {
2019-01-06 02:25:20 +00:00
new_model = MODEL_SGB ;
2020-05-01 13:03:26 +00:00
}
else {
2018-02-03 18:59:06 +00:00
new_model = MODEL_AUTO ;
2020-05-01 13:03:26 +00:00
}
2018-03-08 15:57:57 +00:00
2020-10-08 02:59:29 +00:00
model [ 1 ] = new_model ;
2018-01-27 19:46:13 +00:00
}
2018-02-01 02:09:47 +00:00
2019-01-06 22:09:37 +00:00
var . key = " sameboy_screen_layout " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " top-down " ) = = 0 ) {
2019-01-06 22:09:37 +00:00
screen_layout = LAYOUT_TOP_DOWN ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
screen_layout = LAYOUT_LEFT_RIGHT ;
2020-05-01 13:03:26 +00:00
}
2018-02-01 15:03:40 +00:00
2019-01-06 22:09:37 +00:00
geometry_updated = true ;
}
2018-02-03 20:59:31 +00:00
2019-01-06 22:09:37 +00:00
var . key = " sameboy_link " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2019-01-06 22:09:37 +00:00
bool tmp = link_cable_emulation ;
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " enabled " ) = = 0 ) {
2019-01-06 22:09:37 +00:00
link_cable_emulation = true ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
link_cable_emulation = false ;
2020-05-01 13:03:26 +00:00
}
if ( link_cable_emulation & & link_cable_emulation ! = tmp ) {
2019-01-06 22:09:37 +00:00
set_link_cable_state ( true ) ;
2020-05-01 13:03:26 +00:00
}
else if ( ! link_cable_emulation & & link_cable_emulation ! = tmp ) {
2019-01-06 22:09:37 +00:00
set_link_cable_state ( false ) ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
}
2018-02-03 20:16:36 +00:00
2019-01-06 22:09:37 +00:00
var . key = " sameboy_audio_output " ;
var . value = NULL ;
2020-04-24 17:37:57 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE , & var ) & & var . value ) {
2020-05-01 13:03:26 +00:00
if ( strcmp ( var . value , " Game Boy #1 " ) = = 0 ) {
2019-01-06 22:09:37 +00:00
audio_out = GB_1 ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
audio_out = GB_2 ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
}
2018-02-01 02:09:47 +00:00
}
2017-04-20 19:03:52 +00:00
}
2018-02-01 03:42:26 +00:00
void retro_init ( void )
{
const char * dir = NULL ;
2018-02-03 18:59:06 +00:00
2020-05-01 13:03:26 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY , & dir ) & & dir ) {
2018-02-01 03:42:26 +00:00
snprintf ( retro_system_directory , sizeof ( retro_system_directory ) , " %s " , dir ) ;
2020-05-01 13:03:26 +00:00
}
else {
2018-02-01 03:42:26 +00:00
snprintf ( retro_system_directory , sizeof ( retro_system_directory ) , " %s " , " . " ) ;
2020-05-01 13:03:26 +00:00
}
2018-02-03 18:59:06 +00:00
2020-05-01 13:03:26 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY , & dir ) & & dir ) {
2018-02-01 03:42:26 +00:00
snprintf ( retro_save_directory , sizeof ( retro_save_directory ) , " %s " , dir ) ;
2020-05-01 13:03:26 +00:00
}
else {
2018-02-01 03:42:26 +00:00
snprintf ( retro_save_directory , sizeof ( retro_save_directory ) , " %s " , " . " ) ;
2020-05-01 13:03:26 +00:00
}
2019-05-10 20:50:16 +00:00
2020-05-01 13:03:26 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_LOG_INTERFACE , & logging ) ) {
2019-05-10 20:50:16 +00:00
log_cb = logging . log ;
2020-05-01 13:03:26 +00:00
}
else {
2019-05-10 20:50:16 +00:00
log_cb = fallback_log ;
2020-05-01 13:03:26 +00:00
}
2020-10-10 17:14:10 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_INPUT_BITMASKS , NULL ) ) {
libretro_supports_bitmasks = true ;
}
2018-02-01 03:42:26 +00:00
}
void retro_deinit ( void )
{
free ( frame_buf ) ;
2018-02-01 18:26:58 +00:00
free ( frame_buf_copy ) ;
2018-02-01 03:42:26 +00:00
frame_buf = NULL ;
2018-02-01 18:26:58 +00:00
frame_buf_copy = NULL ;
2020-10-10 17:14:10 +00:00
libretro_supports_bitmasks = false ;
2018-02-01 03:42:26 +00:00
}
unsigned retro_api_version ( void )
{
return RETRO_API_VERSION ;
}
void retro_set_controller_port_device ( unsigned port , unsigned device )
{
2018-02-03 21:17:25 +00:00
log_cb ( RETRO_LOG_INFO , " Connecting device %u into port %u \n " , device , port ) ;
2018-02-01 03:42:26 +00:00
}
void retro_get_system_info ( struct retro_system_info * info )
{
memset ( info , 0 , sizeof ( * info ) ) ;
info - > library_name = " SameBoy " ;
# ifdef GIT_VERSION
2021-05-30 17:55:04 +00:00
info - > library_version = GB_VERSION GIT_VERSION ;
2018-02-01 03:42:26 +00:00
# else
2021-05-30 17:55:04 +00:00
info - > library_version = GB_VERSION ;
2018-02-01 03:42:26 +00:00
# endif
info - > need_fullpath = true ;
info - > valid_extensions = " gb|gbc " ;
}
void retro_get_system_av_info ( struct retro_system_av_info * info )
{
2018-02-01 18:26:58 +00:00
struct retro_game_geometry geom ;
2019-06-19 20:49:43 +00:00
struct retro_system_timing timing = { GB_get_usual_frame_rate ( & gameboy [ 0 ] ) , AUDIO_FREQUENCY } ;
2018-02-01 18:26:58 +00:00
2020-04-24 17:37:57 +00:00
if ( emulated_devices = = 2 ) {
2019-01-06 02:25:20 +00:00
if ( screen_layout = = LAYOUT_TOP_DOWN ) {
2020-05-01 20:42:08 +00:00
geom . base_width = GB_get_screen_width ( & gameboy [ 0 ] ) ;
geom . base_height = GB_get_screen_height ( & gameboy [ 0 ] ) * emulated_devices ;
geom . aspect_ratio = ( double ) GB_get_screen_width ( & gameboy [ 0 ] ) / ( emulated_devices * GB_get_screen_height ( & gameboy [ 0 ] ) ) ;
2020-04-24 17:37:57 +00:00
}
else if ( screen_layout = = LAYOUT_LEFT_RIGHT ) {
2020-05-01 20:42:08 +00:00
geom . base_width = GB_get_screen_width ( & gameboy [ 0 ] ) * emulated_devices ;
geom . base_height = GB_get_screen_height ( & gameboy [ 0 ] ) ;
geom . aspect_ratio = ( ( double ) GB_get_screen_width ( & gameboy [ 0 ] ) * emulated_devices ) / GB_get_screen_height ( & gameboy [ 0 ] ) ;
2019-01-06 02:25:20 +00:00
}
}
2020-04-24 17:37:57 +00:00
else {
2020-05-01 20:42:08 +00:00
geom . base_width = GB_get_screen_width ( & gameboy [ 0 ] ) ;
geom . base_height = GB_get_screen_height ( & gameboy [ 0 ] ) ;
geom . aspect_ratio = ( double ) GB_get_screen_width ( & gameboy [ 0 ] ) / GB_get_screen_height ( & gameboy [ 0 ] ) ;
2018-02-01 18:26:58 +00:00
}
2020-05-01 20:42:08 +00:00
geom . max_width = MAX_VIDEO_WIDTH * emulated_devices ;
geom . max_height = MAX_VIDEO_HEIGHT * emulated_devices ;
2018-02-01 18:26:58 +00:00
2018-02-01 03:42:26 +00:00
info - > geometry = geom ;
info - > timing = timing ;
}
2019-01-06 02:25:20 +00:00
2018-02-01 03:42:26 +00:00
void retro_set_environment ( retro_environment_t cb )
{
environ_cb = cb ;
2018-02-03 18:59:06 +00:00
2018-02-03 17:38:11 +00:00
cb ( RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO , ( void * ) subsystems ) ;
2018-02-01 03:42:26 +00:00
}
void retro_set_audio_sample ( retro_audio_sample_t cb )
{
2019-07-17 21:13:41 +00:00
audio_sample_cb = cb ;
2018-02-01 03:42:26 +00:00
}
void retro_set_audio_sample_batch ( retro_audio_sample_batch_t cb )
{
}
void retro_set_input_poll ( retro_input_poll_t cb )
{
input_poll_cb = cb ;
}
void retro_set_input_state ( retro_input_state_t cb )
{
input_state_cb = cb ;
}
void retro_set_video_refresh ( retro_video_refresh_t cb )
{
video_cb = cb ;
}
void retro_reset ( void )
{
2020-10-08 02:59:29 +00:00
check_variables ( ) ;
2020-05-01 13:03:26 +00:00
for ( int i = 0 ; i < emulated_devices ; i + + ) {
2020-10-08 02:59:29 +00:00
init_for_current_model ( i ) ;
2019-01-06 02:25:20 +00:00
GB_reset ( & gameboy [ i ] ) ;
2020-05-01 13:03:26 +00:00
}
2018-02-01 03:42:26 +00:00
2020-10-08 02:59:29 +00:00
geometry_updated = true ;
2018-02-01 03:42:26 +00:00
}
2017-04-20 19:03:52 +00:00
void retro_run ( void )
{
2018-03-22 05:00:03 +00:00
2018-01-31 20:33:46 +00:00
bool updated = false ;
2018-02-01 18:26:58 +00:00
2020-05-01 13:03:26 +00:00
if ( ! initialized ) {
2018-02-01 18:26:58 +00:00
geometry_updated = false ;
2020-05-01 13:03:26 +00:00
}
2018-02-01 18:26:58 +00:00
if ( geometry_updated ) {
struct retro_system_av_info info ;
retro_get_system_av_info ( & info ) ;
2018-02-03 03:07:30 +00:00
environ_cb ( RETRO_ENVIRONMENT_SET_GEOMETRY , & info . geometry ) ;
2019-01-06 02:25:20 +00:00
geometry_updated = false ;
2018-02-01 18:26:58 +00:00
}
2020-05-01 13:03:26 +00:00
if ( ! frame_buf ) {
2018-01-31 20:33:46 +00:00
return ;
2020-05-01 13:03:26 +00:00
}
2018-02-01 02:09:47 +00:00
2020-05-01 13:03:26 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE , & updated ) & & updated ) {
2019-01-06 22:09:37 +00:00
check_variables ( ) ;
2020-05-01 13:03:26 +00:00
}
2018-02-01 02:09:47 +00:00
2020-04-24 17:37:57 +00:00
if ( emulated_devices = = 2 ) {
2019-03-13 00:54:26 +00:00
GB_update_keys_status ( & gameboy [ 0 ] , 0 ) ;
2019-01-06 02:25:20 +00:00
GB_update_keys_status ( & gameboy [ 1 ] , 1 ) ;
2019-03-13 00:54:26 +00:00
}
2020-04-24 17:37:57 +00:00
else if ( emulated_devices = = 1 & & ( model [ 0 ] = = MODEL_SGB | | model [ 0 ] = = MODEL_SGB2 ) ) {
2020-05-01 13:03:26 +00:00
for ( unsigned i = 0 ; i < 4 ; i + + ) {
2019-01-18 01:33:20 +00:00
GB_update_keys_status ( & gameboy [ 0 ] , i ) ;
2020-05-01 13:03:26 +00:00
}
2019-01-18 01:33:20 +00:00
}
2020-05-01 13:03:26 +00:00
else {
2019-01-18 01:33:20 +00:00
GB_update_keys_status ( & gameboy [ 0 ] , 0 ) ;
2020-05-01 13:03:26 +00:00
}
2018-02-03 11:32:31 +00:00
2018-01-31 20:33:46 +00:00
vblank1_occurred = vblank2_occurred = false ;
signed delta = 0 ;
2020-04-24 17:37:57 +00:00
if ( emulated_devices = = 2 ) {
2018-01-31 20:33:46 +00:00
while ( ! vblank1_occurred | | ! vblank2_occurred ) {
2018-02-01 03:42:26 +00:00
if ( delta > = 0 ) {
2018-02-01 14:15:22 +00:00
delta - = GB_run ( & gameboy [ 0 ] ) ;
2018-02-01 03:42:26 +00:00
}
else {
2018-02-01 14:15:22 +00:00
delta + = GB_run ( & gameboy [ 1 ] ) ;
2018-02-01 03:42:26 +00:00
}
2018-01-31 20:33:46 +00:00
}
}
2020-04-24 17:37:57 +00:00
else {
2019-07-17 21:13:41 +00:00
GB_run_frame ( & gameboy [ 0 ] ) ;
2019-01-06 02:25:20 +00:00
}
2020-04-24 17:37:57 +00:00
if ( emulated_devices = = 2 ) {
2019-01-06 02:25:20 +00:00
if ( screen_layout = = LAYOUT_TOP_DOWN ) {
2020-05-01 20:42:08 +00:00
video_cb ( frame_buf ,
GB_get_screen_width ( & gameboy [ 0 ] ) ,
GB_get_screen_height ( & gameboy [ 0 ] ) * emulated_devices ,
GB_get_screen_width ( & gameboy [ 0 ] ) * sizeof ( uint32_t ) ) ;
2020-04-24 17:37:57 +00:00
}
else if ( screen_layout = = LAYOUT_LEFT_RIGHT ) {
2020-05-01 20:42:08 +00:00
unsigned pitch = GB_get_screen_width ( & gameboy [ 0 ] ) * emulated_devices ;
unsigned pixels_per_device = GB_get_screen_width ( & gameboy [ 0 ] ) * GB_get_screen_height ( & gameboy [ 0 ] ) ;
for ( int y = 0 ; y < GB_get_screen_height ( & gameboy [ 0 ] ) ; y + + ) {
for ( unsigned i = 0 ; i < emulated_devices ; i + + ) {
memcpy ( frame_buf_copy + y * pitch + GB_get_screen_width ( & gameboy [ 0 ] ) * i ,
frame_buf + pixels_per_device * i + y * GB_get_screen_width ( & gameboy [ 0 ] ) ,
GB_get_screen_width ( & gameboy [ 0 ] ) * sizeof ( uint32_t ) ) ;
2018-02-01 18:26:58 +00:00
}
}
2020-05-01 20:42:08 +00:00
video_cb ( frame_buf_copy , GB_get_screen_width ( & gameboy [ 0 ] ) * emulated_devices , GB_get_screen_height ( & gameboy [ 0 ] ) , GB_get_screen_width ( & gameboy [ 0 ] ) * emulated_devices * sizeof ( uint32_t ) ) ;
2019-01-06 02:25:20 +00:00
}
2018-02-01 18:26:58 +00:00
}
2020-05-01 20:42:08 +00:00
else {
video_cb ( frame_buf ,
GB_get_screen_width ( & gameboy [ 0 ] ) ,
GB_get_screen_height ( & gameboy [ 0 ] ) ,
GB_get_screen_width ( & gameboy [ 0 ] ) * sizeof ( uint32_t ) ) ;
2019-01-06 02:25:20 +00:00
}
2018-03-22 05:00:03 +00:00
initialized = true ;
2017-04-20 19:03:52 +00:00
}
bool retro_load_game ( const struct retro_game_info * info )
{
2018-03-08 19:45:58 +00:00
environ_cb ( RETRO_ENVIRONMENT_SET_VARIABLES , ( void * ) vars_single ) ;
2019-01-06 22:09:37 +00:00
check_variables ( ) ;
2018-02-03 18:05:05 +00:00
2020-05-01 20:42:08 +00:00
frame_buf = ( uint32_t * ) malloc ( MAX_VIDEO_PIXELS * emulated_devices * sizeof ( uint32_t ) ) ;
memset ( frame_buf , 0 , MAX_VIDEO_PIXELS * emulated_devices * sizeof ( uint32_t ) ) ;
2018-02-03 18:05:05 +00:00
2018-01-15 20:23:20 +00:00
enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888 ;
2020-04-24 17:37:57 +00:00
if ( ! environ_cb ( RETRO_ENVIRONMENT_SET_PIXEL_FORMAT , & fmt ) ) {
2018-02-03 21:17:25 +00:00
log_cb ( RETRO_LOG_INFO , " XRGB8888 is not supported \n " ) ;
2018-01-15 20:23:20 +00:00
return false ;
}
2018-02-05 23:17:55 +00:00
2018-02-03 18:59:06 +00:00
auto_model = ( info - > path [ strlen ( info - > path ) - 1 ] & ~ 0x20 ) = = ' C ' ? MODEL_CGB : MODEL_DMG ;
2018-01-15 20:23:20 +00:00
snprintf ( retro_game_path , sizeof ( retro_game_path ) , " %s " , info - > path ) ;
2018-02-05 23:17:55 +00:00
2020-04-24 17:37:57 +00:00
for ( int i = 0 ; i < emulated_devices ; i + + ) {
2018-06-16 16:05:07 +00:00
init_for_current_model ( i ) ;
2020-04-24 17:37:57 +00:00
if ( GB_load_rom ( & gameboy [ i ] , info - > path ) ) {
2018-02-03 21:17:25 +00:00
log_cb ( RETRO_LOG_INFO , " Failed to load ROM at %s \n " , info - > path ) ;
2018-02-01 02:09:47 +00:00
return false ;
}
2018-01-15 20:23:20 +00:00
}
2018-02-01 02:09:47 +00:00
2018-02-03 18:59:06 +00:00
bool achievements = true ;
environ_cb ( RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS , & achievements ) ;
2018-02-05 23:17:55 +00:00
2020-05-01 13:03:26 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE , & rumble ) ) {
2018-02-03 21:17:25 +00:00
log_cb ( RETRO_LOG_INFO , " Rumble environment supported \n " ) ;
2020-05-01 13:03:26 +00:00
}
else {
2018-02-03 21:17:25 +00:00
log_cb ( RETRO_LOG_INFO , " Rumble environment not supported \n " ) ;
2020-05-01 13:03:26 +00:00
}
2018-02-05 23:17:55 +00:00
2019-01-06 22:09:37 +00:00
check_variables ( ) ;
2020-06-09 17:09:50 +00:00
retro_set_memory_maps ( ) ;
2018-01-15 20:23:20 +00:00
return true ;
2017-04-20 19:03:52 +00:00
}
void retro_unload_game ( void )
{
2020-05-01 13:03:26 +00:00
for ( int i = 0 ; i < emulated_devices ; i + + ) {
2021-02-28 14:45:18 +00:00
log_cb ( RETRO_LOG_INFO , " Unloading GB: %d \n " , emulated_devices ) ;
2018-02-01 14:15:22 +00:00
GB_free ( & gameboy [ i ] ) ;
2020-05-01 13:03:26 +00:00
}
2017-04-20 19:03:52 +00:00
}
unsigned retro_get_region ( void )
{
2018-01-15 20:23:20 +00:00
return RETRO_REGION_NTSC ;
2017-04-20 19:03:52 +00:00
}
2018-02-03 17:38:11 +00:00
bool retro_load_game_special ( unsigned type , const struct retro_game_info * info , size_t num_info )
2017-04-20 19:03:52 +00:00
{
2018-02-03 18:05:05 +00:00
2020-05-01 13:03:26 +00:00
if ( type = = RETRO_GAME_TYPE_GAMEBOY_LINK_2P ) {
2018-02-03 18:59:06 +00:00
emulated_devices = 2 ;
2020-05-01 13:03:26 +00:00
}
else {
2018-02-03 18:59:06 +00:00
return false ; /* all other types are unhandled for now */
2020-05-01 13:03:26 +00:00
}
2018-02-03 18:59:06 +00:00
2018-03-08 19:45:58 +00:00
environ_cb ( RETRO_ENVIRONMENT_SET_VARIABLES , ( void * ) vars_dual ) ;
2019-01-06 22:09:37 +00:00
check_variables ( ) ;
2018-02-03 18:05:05 +00:00
2020-05-01 20:42:08 +00:00
frame_buf = ( uint32_t * ) malloc ( emulated_devices * MAX_VIDEO_PIXELS * sizeof ( uint32_t ) ) ;
frame_buf_copy = ( uint32_t * ) malloc ( emulated_devices * MAX_VIDEO_PIXELS * sizeof ( uint32_t ) ) ;
2018-02-03 18:05:05 +00:00
2020-05-01 20:42:08 +00:00
memset ( frame_buf , 0 , emulated_devices * MAX_VIDEO_PIXELS * sizeof ( uint32_t ) ) ;
memset ( frame_buf_copy , 0 , emulated_devices * MAX_VIDEO_PIXELS * sizeof ( uint32_t ) ) ;
2018-02-03 18:05:05 +00:00
2018-02-03 17:38:11 +00:00
enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888 ;
2020-04-24 17:37:57 +00:00
if ( ! environ_cb ( RETRO_ENVIRONMENT_SET_PIXEL_FORMAT , & fmt ) ) {
2018-02-03 21:17:25 +00:00
log_cb ( RETRO_LOG_INFO , " XRGB8888 is not supported \n " ) ;
2018-02-03 17:38:11 +00:00
return false ;
}
2018-02-03 18:59:06 +00:00
auto_model = ( info - > path [ strlen ( info - > path ) - 1 ] & ~ 0x20 ) = = ' C ' ? MODEL_CGB : MODEL_DMG ;
2018-02-03 17:38:11 +00:00
snprintf ( retro_game_path , sizeof ( retro_game_path ) , " %s " , info - > path ) ;
2018-02-03 18:59:06 +00:00
2020-04-24 17:37:57 +00:00
for ( int i = 0 ; i < emulated_devices ; i + + ) {
2018-06-16 16:05:07 +00:00
init_for_current_model ( i ) ;
2020-04-24 17:37:57 +00:00
if ( GB_load_rom ( & gameboy [ i ] , info [ i ] . path ) ) {
2018-02-03 17:38:11 +00:00
log_cb ( RETRO_LOG_INFO , " Failed to load ROM \n " ) ;
return false ;
}
}
2018-02-03 18:59:06 +00:00
bool achievements = true ;
environ_cb ( RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS , & achievements ) ;
2020-05-01 13:03:26 +00:00
if ( environ_cb ( RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE , & rumble ) ) {
2018-02-03 21:17:25 +00:00
log_cb ( RETRO_LOG_INFO , " Rumble environment supported \n " ) ;
2020-05-01 13:03:26 +00:00
}
else {
2018-02-03 21:17:25 +00:00
log_cb ( RETRO_LOG_INFO , " Rumble environment not supported \n " ) ;
2020-05-01 13:03:26 +00:00
}
2018-02-03 17:38:11 +00:00
2019-01-06 22:09:37 +00:00
check_variables ( ) ;
2018-02-03 17:38:11 +00:00
return true ;
2017-04-20 19:03:52 +00:00
}
size_t retro_serialize_size ( void )
{
2019-06-19 20:49:43 +00:00
static size_t maximum_save_size = 0 ;
if ( maximum_save_size ) {
return maximum_save_size * 2 ;
}
GB_gameboy_t temp ;
GB_init ( & temp , GB_MODEL_DMG_B ) ;
maximum_save_size = GB_get_save_state_size ( & temp ) ;
GB_free ( & temp ) ;
GB_init ( & temp , GB_MODEL_CGB_E ) ;
maximum_save_size = MAX ( maximum_save_size , GB_get_save_state_size ( & temp ) ) ;
GB_free ( & temp ) ;
GB_init ( & temp , GB_MODEL_SGB2 ) ;
maximum_save_size = MAX ( maximum_save_size , GB_get_save_state_size ( & temp ) ) ;
GB_free ( & temp ) ;
return maximum_save_size * 2 ;
2017-04-20 19:03:52 +00:00
}
2017-10-09 17:03:51 +00:00
bool retro_serialize ( void * data , size_t size )
2017-04-20 19:03:52 +00:00
{
2018-03-22 05:00:03 +00:00
2020-05-01 13:03:26 +00:00
if ( ! initialized | | ! data ) {
2018-02-01 04:06:14 +00:00
return false ;
2020-05-01 13:03:26 +00:00
}
2018-02-01 04:06:14 +00:00
2018-03-22 05:20:51 +00:00
size_t offset = 0 ;
2018-02-03 16:28:35 +00:00
2019-06-19 20:49:43 +00:00
for ( int i = 0 ; i < emulated_devices ; i + + ) {
size_t state_size = GB_get_save_state_size ( & gameboy [ i ] ) ;
if ( state_size > size ) {
return false ;
}
GB_save_state_to_buffer ( & gameboy [ i ] , ( ( uint8_t * ) data ) + offset ) ;
offset + = state_size ;
size - = state_size ;
2018-02-01 03:42:26 +00:00
}
2018-01-31 21:27:56 +00:00
2019-06-19 20:49:43 +00:00
return true ;
2017-04-20 19:03:52 +00:00
}
2017-10-09 17:03:51 +00:00
bool retro_unserialize ( const void * data , size_t size )
2017-04-20 19:03:52 +00:00
{
2020-04-24 17:37:57 +00:00
for ( int i = 0 ; i < emulated_devices ; i + + ) {
2019-06-19 20:49:43 +00:00
size_t state_size = GB_get_save_state_size ( & gameboy [ i ] ) ;
if ( state_size > size ) {
return false ;
}
2018-02-01 03:42:26 +00:00
2019-06-19 20:49:43 +00:00
if ( GB_load_state_from_buffer ( & gameboy [ i ] , data , state_size ) ) {
2018-02-01 03:42:26 +00:00
return false ;
2019-06-19 20:49:43 +00:00
}
size - = state_size ;
data = ( ( uint8_t * ) data ) + state_size ;
2018-02-01 03:42:26 +00:00
}
2018-01-31 21:27:56 +00:00
2018-02-01 03:42:26 +00:00
return true ;
2018-01-31 21:27:56 +00:00
2017-04-20 19:03:52 +00:00
}
2017-10-09 17:03:51 +00:00
void * retro_get_memory_data ( unsigned type )
2017-04-20 19:03:52 +00:00
{
2018-11-03 11:18:08 +00:00
void * data = NULL ;
2020-04-24 17:37:57 +00:00
if ( emulated_devices = = 1 ) {
switch ( type ) {
2019-01-06 22:09:37 +00:00
case RETRO_MEMORY_SYSTEM_RAM :
data = gameboy [ 0 ] . ram ;
break ;
case RETRO_MEMORY_SAVE_RAM :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 0 ] . cartridge_type - > has_battery & & gameboy [ 0 ] . mbc_ram_size ! = 0 ) {
2019-01-06 22:09:37 +00:00
data = gameboy [ 0 ] . mbc_ram ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
data = NULL ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
case RETRO_MEMORY_VIDEO_RAM :
data = gameboy [ 0 ] . vram ;
break ;
case RETRO_MEMORY_RTC :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 0 ] . cartridge_type - > has_battery ) {
2019-01-06 22:09:37 +00:00
data = GB_GET_SECTION ( & gameboy [ 0 ] , rtc ) ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
data = NULL ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
default :
break ;
}
}
2020-04-24 17:37:57 +00:00
else {
switch ( type ) {
2019-01-06 22:09:37 +00:00
case RETRO_MEMORY_GAMEBOY_1_SRAM :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 0 ] . cartridge_type - > has_battery & & gameboy [ 0 ] . mbc_ram_size ! = 0 ) {
2019-01-06 22:09:37 +00:00
data = gameboy [ 0 ] . mbc_ram ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
data = NULL ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
case RETRO_MEMORY_GAMEBOY_2_SRAM :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 1 ] . cartridge_type - > has_battery & & gameboy [ 1 ] . mbc_ram_size ! = 0 ) {
2019-01-06 22:09:37 +00:00
data = gameboy [ 1 ] . mbc_ram ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
data = NULL ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
case RETRO_MEMORY_GAMEBOY_1_RTC :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 0 ] . cartridge_type - > has_battery ) {
2019-01-06 22:09:37 +00:00
data = GB_GET_SECTION ( & gameboy [ 0 ] , rtc ) ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
data = NULL ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
case RETRO_MEMORY_GAMEBOY_2_RTC :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 1 ] . cartridge_type - > has_battery ) {
2019-01-06 22:09:37 +00:00
data = GB_GET_SECTION ( & gameboy [ 1 ] , rtc ) ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
data = NULL ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
default :
break ;
}
2018-01-15 20:23:20 +00:00
}
2019-01-06 22:09:37 +00:00
2018-01-15 20:23:20 +00:00
return data ;
2017-04-20 19:03:52 +00:00
}
2017-10-09 17:03:51 +00:00
size_t retro_get_memory_size ( unsigned type )
2017-04-20 19:03:52 +00:00
{
2018-02-03 21:48:40 +00:00
size_t size = 0 ;
2020-04-24 17:37:57 +00:00
if ( emulated_devices = = 1 ) {
switch ( type ) {
2019-01-06 22:09:37 +00:00
case RETRO_MEMORY_SYSTEM_RAM :
size = gameboy [ 0 ] . ram_size ;
break ;
case RETRO_MEMORY_SAVE_RAM :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 0 ] . cartridge_type - > has_battery & & gameboy [ 0 ] . mbc_ram_size ! = 0 ) {
2019-01-06 22:09:37 +00:00
size = gameboy [ 0 ] . mbc_ram_size ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
size = 0 ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
case RETRO_MEMORY_VIDEO_RAM :
size = gameboy [ 0 ] . vram_size ;
break ;
case RETRO_MEMORY_RTC :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 0 ] . cartridge_type - > has_battery ) {
2019-01-06 22:09:37 +00:00
size = GB_SECTION_SIZE ( rtc ) ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
size = 0 ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
default :
break ;
}
}
2020-04-24 17:37:57 +00:00
else {
switch ( type ) {
2019-01-06 22:09:37 +00:00
case RETRO_MEMORY_GAMEBOY_1_SRAM :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 0 ] . cartridge_type - > has_battery & & gameboy [ 0 ] . mbc_ram_size ! = 0 ) {
2019-01-06 22:09:37 +00:00
size = gameboy [ 0 ] . mbc_ram_size ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
size = 0 ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
case RETRO_MEMORY_GAMEBOY_2_SRAM :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 1 ] . cartridge_type - > has_battery & & gameboy [ 1 ] . mbc_ram_size ! = 0 ) {
2019-01-06 22:09:37 +00:00
size = gameboy [ 1 ] . mbc_ram_size ;
2020-05-01 13:03:26 +00:00
}
else {
2019-01-06 22:09:37 +00:00
size = 0 ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
case RETRO_MEMORY_GAMEBOY_1_RTC :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 0 ] . cartridge_type - > has_battery ) {
2019-01-06 22:09:37 +00:00
size = GB_SECTION_SIZE ( rtc ) ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
case RETRO_MEMORY_GAMEBOY_2_RTC :
2020-05-01 13:03:26 +00:00
if ( gameboy [ 1 ] . cartridge_type - > has_battery ) {
2019-01-06 22:09:37 +00:00
size = GB_SECTION_SIZE ( rtc ) ;
2020-05-01 13:03:26 +00:00
}
2019-01-06 22:09:37 +00:00
break ;
default :
break ;
}
2018-01-31 20:33:46 +00:00
}
2018-02-03 18:59:06 +00:00
2018-01-31 20:33:46 +00:00
return size ;
2017-04-20 19:03:52 +00:00
}
void retro_cheat_reset ( void )
{ }
void retro_cheat_set ( unsigned index , bool enabled , const char * code )
{
2018-01-15 20:23:20 +00:00
( void ) index ;
( void ) enabled ;
( void ) code ;
2017-04-20 19:03:52 +00:00
}