mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-10-14 02:17:36 +08:00
Game Link base code (without integration)
This commit is contained in:
12
configure.ac
12
configure.ac
@@ -420,6 +420,17 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
AH_TEMPLATE(C_GAMELINK,[Define to 1 to enable game link headless mode])
|
||||
AC_CHECK_LIB(rt, main, have_rt_lib=yes, have_rt_lib=no , )
|
||||
AC_ARG_ENABLE(gamelink,AC_HELP_STRING([--disable-gamelink],[Disable headless game link output mode]),,enable_gamelink=yes)
|
||||
AC_MSG_CHECKING(whether gamelink is enabled)
|
||||
if test x$enable_gamelink = xyes; then
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(C_GAMELINK,1)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
dnl FEATURE: Whether to use OpenGL
|
||||
AH_TEMPLATE(C_OPENGL,[Define to 1 to use opengl display output support])
|
||||
AC_ARG_ENABLE(opengl,AC_HELP_STRING([--disable-opengl],[Disable opengl support]),enable_opengl=$enableval,enable_opengl=yes)
|
||||
@@ -1226,6 +1237,7 @@ src/cpu/core_normal/Makefile
|
||||
src/debug/Makefile
|
||||
src/dos/Makefile
|
||||
src/fpu/Makefile
|
||||
src/gamelink/Makefile
|
||||
src/gui/Makefile
|
||||
src/hardware/Makefile
|
||||
src/hardware/mame/Makefile
|
||||
|
@@ -5,7 +5,7 @@ if EMSCRIPTEN
|
||||
dosbox_x_LDFLAGS = -s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['$$ERRNO_CODES']
|
||||
endif
|
||||
|
||||
SUBDIRS = cpu debug dos fpu gui hardware libs ints misc shell builtin platform aviwriter output
|
||||
SUBDIRS = cpu debug dos fpu gui hardware libs ints misc shell builtin platform aviwriter output gamelink
|
||||
|
||||
bin_PROGRAMS = dosbox-x
|
||||
|
||||
@@ -32,7 +32,7 @@ dosbox_x_LDADD = debug/libdebug.a dos/libdos.a shell/libshell.a builtin/libbuilt
|
||||
ints/libints.a hardware/serialport/libserial.a hardware/parport/libparallel.a \
|
||||
libs/porttalk/libporttalk.a gui/libgui.a libs/gui_tk/libgui_tk.a hardware/libhardware.a \
|
||||
cpu/libcpu.a hardware/reSID/libresid.a fpu/libfpu.a gui/libgui.a \
|
||||
misc/libmisc.a output/liboutput.a hardware/mame/libmame.a libs/zmbv/libzmbv.a libs/decoders/internal/libopusint.a
|
||||
misc/libmisc.a output/liboutput.a gamelink/libgamelink.a hardware/mame/libmame.a libs/zmbv/libzmbv.a libs/decoders/internal/libopusint.a
|
||||
|
||||
if !EMSCRIPTEN
|
||||
dosbox_x_LDADD += aviwriter/libaviwriter.a
|
||||
|
4
src/gamelink/Makefile.am
Normal file
4
src/gamelink/Makefile.am
Normal file
@@ -0,0 +1,4 @@
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
|
||||
|
||||
noinst_LIBRARIES = libgamelink.a
|
||||
libgamelink_a_SOURCES = gamelink.cpp gamelink_term.cpp
|
737
src/gamelink/gamelink.cpp
Normal file
737
src/gamelink/gamelink.cpp
Normal file
@@ -0,0 +1,737 @@
|
||||
|
||||
// Game Link
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dependencies
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if C_GAMELINK
|
||||
|
||||
// External Dependencies
|
||||
#ifdef WIN32
|
||||
#include <Windows.h>
|
||||
#else // WIN32
|
||||
#include <sys/mman.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <semaphore.h>
|
||||
#endif // WIN32
|
||||
|
||||
// SDL Dependencies
|
||||
#include "SDL_syswm.h"
|
||||
#include "SDL.h"
|
||||
|
||||
// Local Dependencies
|
||||
#include "dosbox.h"
|
||||
#include "gamelink.h"
|
||||
#include "logging.h"
|
||||
#include "sdlmain.h"
|
||||
#include "../resource.h"
|
||||
|
||||
extern bool is_paused;
|
||||
extern uint32_t RunningProgramLoadAddress;
|
||||
|
||||
//==============================================================================
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Local Definitions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define SYSTEM_NAME "DOSBox"
|
||||
|
||||
#define PROTOCOL_VER 4
|
||||
|
||||
// Ugly hack: grid cartographer sends absolute addresses, but different dosbox
|
||||
// versions load executables at different addresses.
|
||||
|
||||
// To fix those cases, this code calculates an offset from the original and
|
||||
// the actual load address. To configure the original load address for a
|
||||
// game, edit the corresponding grid cartographer profile XML file, search
|
||||
// for the <peek> tag, add 0x1000_0000 to the address and put it at the end. For
|
||||
// example, if the load address is 0x1a70 the XML tag could look like:
|
||||
// <peek bytes="1299c 1299e 19c42 19c2e 19c30 19c40 19c32 17a8a 10001a70" />
|
||||
|
||||
// To find out the original load address, use the "gamelinksnoop" option and
|
||||
// load the same game in dosbox-gridc and this dosbox, load the same save
|
||||
// game/move to the same position. Console output should then contain a
|
||||
// suggested original load address.
|
||||
|
||||
// This default offset works for some simple cases.
|
||||
#define PEEK_ADDR_DEFAULT_OFFSET 0x6850
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#define GAMELINK_MUTEX_NAME "DWD_GAMELINK_MUTEX_R4"
|
||||
#else // WIN32
|
||||
#define GAMELINK_MUTEX_NAME "/DWD_GAMELINK_MUTEX_R4"
|
||||
#endif // WIN32
|
||||
|
||||
#ifdef MACOSX
|
||||
#define GAMELINK_MMAP_NAME "/DWD_GAMELINK_MMAP_R4"
|
||||
#else // MACOSX
|
||||
#define GAMELINK_MMAP_NAME "DWD_GAMELINK_MMAP_R4"
|
||||
#endif // MACOSX
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Local Data
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
static HANDLE g_mutex_handle;
|
||||
static HANDLE g_mmap_handle;
|
||||
|
||||
#else // WIN32
|
||||
|
||||
static sem_t* g_mutex_handle;
|
||||
static int g_mmap_handle; // fd!
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
static bool g_trackonly_mode;
|
||||
|
||||
static uint32_t g_membase_size;
|
||||
|
||||
static GameLink::sSharedMemoryMap_R4* g_p_shared_memory;
|
||||
|
||||
#define MEMORY_MAP_CORE_SIZE sizeof( GameLink::sSharedMemoryMap_R4 )
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Local Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void shared_memory_init()
|
||||
{
|
||||
if (sdl.gamelink.snoop) return;
|
||||
|
||||
// Initialise
|
||||
|
||||
g_p_shared_memory->version = PROTOCOL_VER;
|
||||
g_p_shared_memory->flags = 0;
|
||||
|
||||
memset( g_p_shared_memory->system, 0, sizeof( g_p_shared_memory->system ) );
|
||||
strcpy( g_p_shared_memory->system, SYSTEM_NAME );
|
||||
memset( g_p_shared_memory->program, 0, sizeof( g_p_shared_memory->program ) );
|
||||
|
||||
g_p_shared_memory->program_hash[ 0 ] = 0;
|
||||
g_p_shared_memory->program_hash[ 1 ] = 0;
|
||||
g_p_shared_memory->program_hash[ 2 ] = 0;
|
||||
g_p_shared_memory->program_hash[ 3 ] = 0;
|
||||
|
||||
// reset input
|
||||
g_p_shared_memory->input.mouse_dx = 0;
|
||||
g_p_shared_memory->input.mouse_dy = 0;
|
||||
|
||||
g_p_shared_memory->input.mouse_btn = 0;
|
||||
for ( int i = 0; i < 8; ++i ) {
|
||||
g_p_shared_memory->input.keyb_state[ i ] = 0;
|
||||
}
|
||||
|
||||
// reset peek interface
|
||||
g_p_shared_memory->peek.addr_count = 0;
|
||||
memset( g_p_shared_memory->peek.addr, 0, GameLink::sSharedMMapPeek_R2::PEEK_LIMIT * sizeof( uint32_t ) );
|
||||
memset( g_p_shared_memory->peek.data, 0, GameLink::sSharedMMapPeek_R2::PEEK_LIMIT * sizeof( uint8_t ) );
|
||||
|
||||
// blank frame
|
||||
g_p_shared_memory->frame.seq = 0;
|
||||
g_p_shared_memory->frame.image_fmt = 0; // = no frame
|
||||
g_p_shared_memory->frame.width = 0;
|
||||
g_p_shared_memory->frame.height = 0;
|
||||
|
||||
g_p_shared_memory->frame.par_x = 1;
|
||||
g_p_shared_memory->frame.par_y = 1;
|
||||
memset( g_p_shared_memory->frame.buffer, 0, GameLink::sSharedMMapFrame_R1::MAX_PAYLOAD );
|
||||
|
||||
// audio: 100%
|
||||
g_p_shared_memory->audio.master_vol_l = 100;
|
||||
g_p_shared_memory->audio.master_vol_r = 100;
|
||||
|
||||
// RAM
|
||||
g_p_shared_memory->ram_size = g_membase_size;
|
||||
}
|
||||
|
||||
//
|
||||
// create_mutex
|
||||
//
|
||||
// Create a globally unique mutex.
|
||||
//
|
||||
// \returns 1 if we made one, 0 if it failed or the mutex existed already (also a failure).
|
||||
//
|
||||
static int create_mutex( const char* p_name )
|
||||
{
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
// The mutex is already open?
|
||||
g_mutex_handle = OpenMutexA( SYNCHRONIZE, FALSE, p_name );
|
||||
if ( g_mutex_handle != 0 ) {
|
||||
if (sdl.gamelink.snoop) return 1;
|
||||
// .. didn't fail - so must already exist.
|
||||
CloseHandle( g_mutex_handle );
|
||||
g_mutex_handle = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Actually create it.
|
||||
g_mutex_handle = CreateMutexA( NULL, FALSE, p_name );
|
||||
if ( g_mutex_handle ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else // WIN32
|
||||
|
||||
// The mutex is already open?
|
||||
g_mutex_handle = sem_open( p_name, 0, 0666, 0 );
|
||||
if ( g_mutex_handle != SEM_FAILED )
|
||||
{
|
||||
// .. didn't fail - so must already exist.
|
||||
LOG_MSG( "GAMELINK: ERROR: MUTEX \"%s\" already exists.", p_name );
|
||||
LOG_MSG( "GAMELINK: Already running Game Link, or maybe a crash?", p_name );
|
||||
#ifdef MACOSX
|
||||
LOG_MSG( "GAMELINK: Might need to manually reboot the system." );
|
||||
#else // MACOSX
|
||||
LOG_MSG( "GAMELINK: Might need to manually tidy up in /dev/shm (or reboot system)." );
|
||||
#endif // MACOSX
|
||||
sem_close( g_mutex_handle );
|
||||
g_mutex_handle = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Actually create it.
|
||||
g_mutex_handle = sem_open( p_name, O_CREAT, 0666, 1 );
|
||||
if ( g_mutex_handle == SEM_FAILED )
|
||||
{
|
||||
LOG_MSG( "GAMELINK: Creating MUTEX \"%s\" failed. [sem_open=%d, errno=%d]", p_name, (int)(size_t)g_mutex_handle, errno );
|
||||
#ifdef MACOSX
|
||||
LOG_MSG( "GAMELINK: Might need to manually reboot the system." );
|
||||
#else // MACOSX
|
||||
LOG_MSG( "GAMELINK: Might need to manually tidy up in /dev/shm (or reboot system)." );
|
||||
#endif // MACOSX
|
||||
g_mutex_handle = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// destroy_mutex
|
||||
//
|
||||
// Tidy up the mutex.
|
||||
//
|
||||
static void destroy_mutex( const char* p_name )
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
||||
(void)(p_name);
|
||||
|
||||
if ( g_mutex_handle )
|
||||
{
|
||||
CloseHandle( g_mutex_handle );
|
||||
g_mutex_handle = NULL;
|
||||
}
|
||||
|
||||
#else // WIN32
|
||||
|
||||
if ( g_mutex_handle )
|
||||
{
|
||||
sem_close( g_mutex_handle );
|
||||
sem_unlink( p_name );
|
||||
g_mutex_handle = NULL;
|
||||
}
|
||||
|
||||
#endif // WIN32
|
||||
}
|
||||
|
||||
//
|
||||
// create_shared_memory
|
||||
//
|
||||
// Create a shared memory area.
|
||||
//
|
||||
// \returns 1 if we made one, 0 if it failed.
|
||||
//
|
||||
static int create_shared_memory()
|
||||
{
|
||||
const int memory_map_size = MEMORY_MAP_CORE_SIZE + g_membase_size;
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
g_mmap_handle = CreateFileMappingA( INVALID_HANDLE_VALUE, NULL,
|
||||
PAGE_READWRITE, 0, memory_map_size, GAMELINK_MMAP_NAME );
|
||||
|
||||
if ( g_mmap_handle )
|
||||
{
|
||||
g_p_shared_memory = reinterpret_cast< GameLink::sSharedMemoryMap_R4* >(
|
||||
MapViewOfFile( g_mmap_handle, FILE_MAP_ALL_ACCESS, 0, 0, memory_map_size )
|
||||
);
|
||||
|
||||
if ( g_p_shared_memory )
|
||||
{
|
||||
return 1; // Success!
|
||||
}
|
||||
else
|
||||
{
|
||||
// tidy up file mapping.
|
||||
CloseHandle( g_mmap_handle );
|
||||
g_mmap_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#else // WIN32
|
||||
|
||||
g_mmap_handle = shm_open( GAMELINK_MMAP_NAME, O_CREAT
|
||||
#ifndef MACOSX
|
||||
| O_TRUNC
|
||||
#endif // !MACOSX
|
||||
| O_RDWR, 0666 );
|
||||
g_p_shared_memory = NULL; // defensive
|
||||
|
||||
if ( g_mmap_handle < 0 )
|
||||
{
|
||||
LOG_MSG( "GAMELINK: shm_open( \"" GAMELINK_MMAP_NAME "\" ) failed. errno = %d", errno );
|
||||
}
|
||||
else
|
||||
{
|
||||
// set size
|
||||
int r;
|
||||
r = ftruncate( g_mmap_handle, memory_map_size );
|
||||
if ( r < 0 ) {
|
||||
LOG_MSG( "GAMELINK: ftruncate failed with %d. errno = %d", r, errno );
|
||||
close( g_mmap_handle );
|
||||
g_mmap_handle = -1;
|
||||
shm_unlink( GAMELINK_MMAP_NAME );
|
||||
g_p_shared_memory = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// map to a pointer.
|
||||
g_p_shared_memory = reinterpret_cast< GameLink::sSharedMemoryMap_R4* >(
|
||||
mmap( 0, memory_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, g_mmap_handle, 0 )
|
||||
);
|
||||
|
||||
if ( g_p_shared_memory == MAP_FAILED )
|
||||
{
|
||||
LOG_MSG( "GAMELINK: mmap failed. errno = %d", errno );
|
||||
close( g_mmap_handle );
|
||||
g_mmap_handle = -1;
|
||||
shm_unlink( GAMELINK_MMAP_NAME );
|
||||
g_p_shared_memory = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// success!
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
// Failure
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// destroy_shared_memory
|
||||
//
|
||||
// Destroy the shared memory area.
|
||||
//
|
||||
static void destroy_shared_memory()
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
||||
if ( g_p_shared_memory )
|
||||
{
|
||||
UnmapViewOfFile( g_p_shared_memory );
|
||||
g_p_shared_memory = NULL;
|
||||
}
|
||||
|
||||
if ( g_mmap_handle )
|
||||
{
|
||||
CloseHandle( g_mmap_handle );
|
||||
g_mmap_handle = NULL;
|
||||
}
|
||||
|
||||
#else // WIN32
|
||||
|
||||
const int memory_map_size = MEMORY_MAP_CORE_SIZE + g_membase_size;
|
||||
|
||||
if ( g_p_shared_memory )
|
||||
{
|
||||
munmap( g_p_shared_memory, memory_map_size );
|
||||
g_p_shared_memory = NULL;
|
||||
}
|
||||
|
||||
if ( g_mmap_handle >= 0 )
|
||||
{
|
||||
close( g_mmap_handle );
|
||||
g_mmap_handle = -1;
|
||||
}
|
||||
|
||||
shm_unlink( GAMELINK_MMAP_NAME );
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// GameLink::Init
|
||||
//------------------------------------------------------------------------------
|
||||
int GameLink::Init( const bool trackonly_mode )
|
||||
{
|
||||
//LOG_MSG("GAMELINK: Init %i", trackonly_mode);
|
||||
int iresult;
|
||||
|
||||
// Already initialised?
|
||||
if ( g_mutex_handle )
|
||||
{
|
||||
LOG_MSG( "GAMELINK: Ignoring re-initialisation." );
|
||||
|
||||
// success
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Store the mode we're in.
|
||||
g_trackonly_mode = trackonly_mode;
|
||||
|
||||
// Create a fresh mutex.
|
||||
iresult = create_mutex( GAMELINK_MUTEX_NAME );
|
||||
if ( iresult != 1 )
|
||||
{
|
||||
// failed.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// GameLink::AllocRAM
|
||||
//------------------------------------------------------------------------------
|
||||
uint8_t* GameLink::AllocRAM( const uint32_t size )
|
||||
{
|
||||
//LOG_MSG("GAMELINK: AllocRAM %i", size);
|
||||
int iresult;
|
||||
|
||||
g_membase_size = size;
|
||||
|
||||
// Create a shared memory area.
|
||||
iresult = create_shared_memory();
|
||||
if ( iresult != 1 )
|
||||
{
|
||||
destroy_mutex( GAMELINK_MUTEX_NAME );
|
||||
// failed.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialise
|
||||
shared_memory_init();
|
||||
|
||||
GameLink::InitTerminal();
|
||||
|
||||
const int memory_map_size = MEMORY_MAP_CORE_SIZE + g_membase_size;
|
||||
LOG_MSG( "GAMELINK: Initialised. Allocated %d MB of shared memory.", (memory_map_size + (1024*1024) - 1) / (1024*1024) );
|
||||
|
||||
uint8_t *membase;
|
||||
if (sdl.gamelink.snoop) {
|
||||
membase = (uint8_t*)malloc(g_membase_size);
|
||||
} else {
|
||||
membase = ((uint8_t*)g_p_shared_memory) + MEMORY_MAP_CORE_SIZE;
|
||||
}
|
||||
|
||||
// Return RAM base pointer.
|
||||
return membase;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// GameLink::Term
|
||||
//------------------------------------------------------------------------------
|
||||
void GameLink::Term()
|
||||
{
|
||||
//LOG_MSG("GAMELINK: Term");
|
||||
|
||||
// SEND ABORT CODE TO CLIENT (don't care if it fails)
|
||||
if (!sdl.gamelink.snoop && g_p_shared_memory)
|
||||
g_p_shared_memory->version = 0;
|
||||
|
||||
destroy_shared_memory();
|
||||
|
||||
destroy_mutex( GAMELINK_MUTEX_NAME );
|
||||
|
||||
g_membase_size = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// GameLink::In
|
||||
//------------------------------------------------------------------------------
|
||||
int GameLink::In( GameLink::sSharedMMapInput_R2* p_input,
|
||||
GameLink::sSharedMMapAudio_R1* p_audio )
|
||||
{
|
||||
// LOG_MSG("GAMELINK: In");
|
||||
int ready = 0;
|
||||
|
||||
if (!sdl.gamelink.snoop && g_p_shared_memory)
|
||||
{
|
||||
if ( g_trackonly_mode )
|
||||
{
|
||||
// No input.
|
||||
memset( p_input, 0, sizeof( sSharedMMapInput_R2 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( g_p_shared_memory->input.ready )
|
||||
{
|
||||
// Copy client input out of shared memory
|
||||
memcpy( p_input, &( g_p_shared_memory->input ), sizeof( sSharedMMapInput_R2 ) );
|
||||
|
||||
// Clear remote delta, prevent counting more than once.
|
||||
g_p_shared_memory->input.mouse_dx = 0;
|
||||
g_p_shared_memory->input.mouse_dy = 0;
|
||||
|
||||
g_p_shared_memory->input.ready = 0;
|
||||
|
||||
ready = 1; // Got some input
|
||||
}
|
||||
|
||||
// Volume sync, ignore if out of range.
|
||||
if ( g_p_shared_memory->audio.master_vol_l <= 100 )
|
||||
p_audio->master_vol_l = g_p_shared_memory->audio.master_vol_l;
|
||||
if ( g_p_shared_memory->audio.master_vol_r <= 100 )
|
||||
p_audio->master_vol_r = g_p_shared_memory->audio.master_vol_r;
|
||||
}
|
||||
}
|
||||
|
||||
return ready;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// GameLink::Out
|
||||
//------------------------------------------------------------------------------
|
||||
void GameLink::Out( const uint16_t frame_width,
|
||||
const uint16_t frame_height,
|
||||
const double source_ratio,
|
||||
const bool want_mouse,
|
||||
const char* p_program,
|
||||
const uint32_t* p_program_hash,
|
||||
const uint8_t* p_frame,
|
||||
const uint8_t* p_sysmem )
|
||||
{
|
||||
// LOG_MSG("GAMELINK: Out");
|
||||
// Not initialised (or disabled) ?
|
||||
if ( g_p_shared_memory == NULL ) {
|
||||
return; // <=== EARLY OUT
|
||||
}
|
||||
|
||||
// Create integer ratio
|
||||
uint16_t par_x, par_y;
|
||||
if ( source_ratio >= 1.0 )
|
||||
{
|
||||
par_x = 4096;
|
||||
par_y = static_cast< uint16_t >( source_ratio * 4096.0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
par_x = static_cast< uint16_t >( 4096.0 / source_ratio );
|
||||
par_y = 4096;
|
||||
}
|
||||
|
||||
// Build flags
|
||||
uint8_t flags;
|
||||
|
||||
if ( g_trackonly_mode )
|
||||
{
|
||||
// Tracking Only - DOSBox handles video/input as usual.
|
||||
flags = sSharedMemoryMap_R4::FLAG_NO_FRAME;
|
||||
}
|
||||
else
|
||||
{
|
||||
// External Input Mode
|
||||
flags = sSharedMemoryMap_R4::FLAG_WANT_KEYB;
|
||||
if ( want_mouse )
|
||||
flags |= sSharedMemoryMap_R4::FLAG_WANT_MOUSE;
|
||||
}
|
||||
|
||||
// Paused?
|
||||
if ( is_paused )
|
||||
flags |= sSharedMemoryMap_R4::FLAG_PAUSED;
|
||||
|
||||
|
||||
//
|
||||
// Send data?
|
||||
|
||||
// Message buffer
|
||||
sSharedMMapBuffer_R1 proc_mech_buffer;
|
||||
proc_mech_buffer.payload = 0;
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
DWORD mutex_result;
|
||||
mutex_result = WaitForSingleObject( g_mutex_handle, INFINITE );
|
||||
if ( mutex_result == WAIT_OBJECT_0 )
|
||||
|
||||
#else // WIN32
|
||||
|
||||
int mutex_result;
|
||||
mutex_result = sem_wait( g_mutex_handle );
|
||||
if ( mutex_result < 0 )
|
||||
{
|
||||
LOG_MSG( "GAMELINK: MUTEX lock failed with %d. errno = %d", mutex_result, errno );
|
||||
}
|
||||
else
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
{
|
||||
|
||||
if (!sdl.gamelink.snoop) { // ========================
|
||||
|
||||
// Set version
|
||||
g_p_shared_memory->version = PROTOCOL_VER;
|
||||
|
||||
// Set program
|
||||
strncpy( g_p_shared_memory->program, p_program, 256 );
|
||||
for ( int i = 0; i < 4; ++i )
|
||||
{
|
||||
g_p_shared_memory->program_hash[ i ] = p_program_hash[ i ];
|
||||
}
|
||||
|
||||
// Store flags
|
||||
g_p_shared_memory->flags = flags;
|
||||
|
||||
if ( g_trackonly_mode == false )
|
||||
{
|
||||
// Update the frame sequence
|
||||
++g_p_shared_memory->frame.seq;
|
||||
|
||||
// Copy frame properties
|
||||
g_p_shared_memory->frame.image_fmt = 1; // = 32-bit RGBA
|
||||
g_p_shared_memory->frame.width = frame_width;
|
||||
g_p_shared_memory->frame.height = frame_height;
|
||||
g_p_shared_memory->frame.par_x = par_x;
|
||||
g_p_shared_memory->frame.par_y = par_y;
|
||||
|
||||
// Frame Buffer
|
||||
uint32_t payload;
|
||||
payload = frame_width * frame_height * 4;
|
||||
if ( frame_width <= sSharedMMapFrame_R1::MAX_WIDTH && frame_height <= sSharedMMapFrame_R1::MAX_HEIGHT )
|
||||
{
|
||||
memcpy( g_p_shared_memory->frame.buffer, p_frame, payload );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG_MSG("Hash: %x %x %x %x Flags: %x",
|
||||
g_p_shared_memory->program_hash[0],
|
||||
g_p_shared_memory->program_hash[1],
|
||||
g_p_shared_memory->program_hash[2],
|
||||
g_p_shared_memory->program_hash[3],
|
||||
g_p_shared_memory->flags);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
// Find peek offset
|
||||
uint32_t offset = PEEK_ADDR_DEFAULT_OFFSET;
|
||||
int cnt = g_p_shared_memory->peek.addr_count;
|
||||
if (cnt <= sSharedMMapPeek_R2::PEEK_LIMIT
|
||||
&& cnt >= 1
|
||||
&& g_p_shared_memory->peek.addr[ cnt-1 ] > 0x1000'0000)
|
||||
{
|
||||
offset = RunningProgramLoadAddress - (g_p_shared_memory->peek.addr[ cnt-1 ]-0x1000'0000);
|
||||
}
|
||||
if (sdl.gamelink.snoop) offset = 0;
|
||||
|
||||
// Peek
|
||||
for ( uint32_t pindex = 0;
|
||||
pindex < g_p_shared_memory->peek.addr_count &&
|
||||
pindex < sSharedMMapPeek_R2::PEEK_LIMIT;
|
||||
++pindex )
|
||||
{
|
||||
// read address
|
||||
uint32_t address;
|
||||
address = g_p_shared_memory->peek.addr[ pindex ] + offset;
|
||||
|
||||
uint8_t data;
|
||||
// valid?
|
||||
if ( address < g_membase_size )
|
||||
{
|
||||
data = p_sysmem[ address ]; // read data
|
||||
}
|
||||
else
|
||||
{
|
||||
data = 0; // <-- safe
|
||||
}
|
||||
|
||||
if (!sdl.gamelink.snoop) {
|
||||
g_p_shared_memory->peek.data[ pindex ] = data;
|
||||
} else {
|
||||
uint8_t seek = g_p_shared_memory->peek.data[ pindex ];
|
||||
LOG_MSG("_____ peek: %04x = %i / %i / %i", address, seek, data, address >= g_membase_size?0:*(((uint8_t*)g_p_shared_memory) + MEMORY_MAP_CORE_SIZE + address));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sdl.gamelink.snoop) {
|
||||
// Message Processing.
|
||||
ExecTerminal( &(g_p_shared_memory->buf_recv),
|
||||
&(g_p_shared_memory->buf_tohost),
|
||||
&(proc_mech_buffer) );
|
||||
|
||||
} else if (RunningProgramLoadAddress && g_p_shared_memory->peek.addr_count) {
|
||||
LOG_MSG("Load Address = %06x", RunningProgramLoadAddress);
|
||||
for (int addr = 0; addr < g_membase_size-1024; addr++) {
|
||||
bool match = true;
|
||||
int base = g_p_shared_memory->peek.addr[ 0 ];
|
||||
for (int i = 0; i < g_p_shared_memory->peek.addr_count; i++) {
|
||||
uint8_t seek = g_p_shared_memory->peek.data[ i ];
|
||||
int oaddr = g_p_shared_memory->peek.addr[ i ] - base + addr;
|
||||
if (oaddr >= g_membase_size || p_sysmem[ oaddr ] != seek) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
LOG_MSG("Match at %06x, Offset %06x, Original Load Address %06x", addr, addr-base, RunningProgramLoadAddress - (addr-base));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // ========================
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
ReleaseMutex( g_mutex_handle );
|
||||
|
||||
#else // WIN32
|
||||
|
||||
mutex_result = sem_post( g_mutex_handle );
|
||||
if ( mutex_result < 0 ) {
|
||||
LOG_MSG( "GAMELINK: MUTEX unlock failed with %d. errno = %d", mutex_result, errno );
|
||||
} else {
|
||||
// printf( "GAMELINK: MUTEX unlock ok.\n" );
|
||||
}
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
// Mechanical Message Processing, out of mutex.
|
||||
if (!sdl.gamelink.snoop && proc_mech_buffer.payload)
|
||||
ExecTerminalMech( &proc_mech_buffer );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#endif // C_GAMELINK
|
||||
|
175
src/gamelink/gamelink.h
Normal file
175
src/gamelink/gamelink.h
Normal file
@@ -0,0 +1,175 @@
|
||||
#ifndef __GAMELINK_H___
|
||||
#define __GAMELINK_H___
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <Windows.h>
|
||||
#endif // WIN32
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Namespace Declaration
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace GameLink
|
||||
{
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Global Declarations
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
#pragma pack( push, 1 )
|
||||
|
||||
//
|
||||
// sSharedMMapFrame_R1
|
||||
//
|
||||
// Server -> Client Frame. 32-bit RGBA up to MAX_WIDTH x MAX_HEIGHT
|
||||
//
|
||||
struct sSharedMMapFrame_R1
|
||||
{
|
||||
uint16_t seq;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
|
||||
uint8_t image_fmt; // 0 = no frame; 1 = 32-bit 0xAARRGGBB
|
||||
uint8_t reserved0;
|
||||
|
||||
uint16_t par_x; // pixel aspect ratio
|
||||
uint16_t par_y;
|
||||
|
||||
enum { MAX_WIDTH = 1280 };
|
||||
enum { MAX_HEIGHT = 1024 };
|
||||
|
||||
enum { MAX_PAYLOAD = MAX_WIDTH * MAX_HEIGHT * 4 };
|
||||
uint8_t buffer[ MAX_PAYLOAD ];
|
||||
};
|
||||
|
||||
//
|
||||
// sSharedMMapInput_R2
|
||||
//
|
||||
// Client -> Server Input Data
|
||||
//
|
||||
struct sSharedMMapInput_R2
|
||||
{
|
||||
float mouse_dx;
|
||||
float mouse_dy;
|
||||
uint8_t ready;
|
||||
uint8_t mouse_btn;
|
||||
uint32_t keyb_state[ 8 ];
|
||||
};
|
||||
|
||||
//
|
||||
// sSharedMMapPeek_R2
|
||||
//
|
||||
// Memory reading interface.
|
||||
//
|
||||
struct sSharedMMapPeek_R2
|
||||
{
|
||||
enum { PEEK_LIMIT = 16 * 1024 };
|
||||
|
||||
uint32_t addr_count;
|
||||
uint32_t addr[ PEEK_LIMIT ];
|
||||
uint8_t data[ PEEK_LIMIT ];
|
||||
};
|
||||
|
||||
//
|
||||
// sSharedMMapBuffer_R1
|
||||
//
|
||||
// General buffer (64Kb)
|
||||
//
|
||||
struct sSharedMMapBuffer_R1
|
||||
{
|
||||
enum { BUFFER_SIZE = ( 64 * 1024 ) };
|
||||
|
||||
uint16_t payload;
|
||||
uint8_t data[ BUFFER_SIZE ];
|
||||
};
|
||||
|
||||
//
|
||||
// sSharedMMapAudio_R1
|
||||
//
|
||||
// Audio control interface.
|
||||
//
|
||||
struct sSharedMMapAudio_R1
|
||||
{
|
||||
uint8_t master_vol_l;
|
||||
uint8_t master_vol_r;
|
||||
};
|
||||
|
||||
//
|
||||
// sSharedMemoryMap_R4
|
||||
//
|
||||
// Memory Map (top-level object)
|
||||
//
|
||||
struct sSharedMemoryMap_R4
|
||||
{
|
||||
enum {
|
||||
FLAG_WANT_KEYB = 1 << 0,
|
||||
FLAG_WANT_MOUSE = 1 << 1,
|
||||
FLAG_NO_FRAME = 1 << 2,
|
||||
FLAG_PAUSED = 1 << 3,
|
||||
};
|
||||
|
||||
enum {
|
||||
SYSTEM_MAXLEN = 64
|
||||
};
|
||||
|
||||
enum {
|
||||
PROGRAM_MAXLEN = 260
|
||||
};
|
||||
|
||||
uint8_t version; // = PROTOCOL_VER
|
||||
uint8_t flags;
|
||||
char system[ SYSTEM_MAXLEN ]; // System name.
|
||||
char program[ PROGRAM_MAXLEN ]; // Program name. Zero terminated.
|
||||
uint32_t program_hash[ 4 ]; // Program code hash (256-bits)
|
||||
|
||||
sSharedMMapFrame_R1 frame;
|
||||
sSharedMMapInput_R2 input;
|
||||
sSharedMMapPeek_R2 peek;
|
||||
sSharedMMapBuffer_R1 buf_recv; // a message to us.
|
||||
sSharedMMapBuffer_R1 buf_tohost;
|
||||
sSharedMMapAudio_R1 audio;
|
||||
|
||||
// added for protocol v4
|
||||
uint32_t ram_size;
|
||||
};
|
||||
|
||||
#pragma pack( pop )
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Global Functions
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
extern int Init( const bool trackonly_mode );
|
||||
|
||||
extern uint8_t* AllocRAM( const uint32_t size );
|
||||
|
||||
extern void Term();
|
||||
|
||||
extern int In( sSharedMMapInput_R2* p_input,
|
||||
sSharedMMapAudio_R1* p_audio );
|
||||
|
||||
extern void Out( const uint16_t frame_width,
|
||||
const uint16_t frame_height,
|
||||
const double source_ratio,
|
||||
const bool need_mouse,
|
||||
const char* p_program,
|
||||
const uint32_t* p_program_hash,
|
||||
const uint8_t* p_frame,
|
||||
const uint8_t* p_sysmem );
|
||||
|
||||
extern void ExecTerminal( sSharedMMapBuffer_R1* p_inbuf,
|
||||
sSharedMMapBuffer_R1* p_outbuf,
|
||||
sSharedMMapBuffer_R1* p_mechbuf );
|
||||
|
||||
extern void ExecTerminalMech( sSharedMMapBuffer_R1* p_mechbuf );
|
||||
|
||||
extern void InitTerminal();
|
||||
|
||||
}; // namespace GameLink
|
||||
|
||||
//==============================================================================
|
||||
|
||||
#endif // __GAMELINK_HDR__
|
260
src/gamelink/gamelink_term.cpp
Normal file
260
src/gamelink/gamelink_term.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
|
||||
// Game Link - Console
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dependencies
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if C_GAMELINK
|
||||
|
||||
// External Dependencies
|
||||
#ifdef WIN32
|
||||
#else // WIN32
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#endif // WIN32
|
||||
|
||||
// Local Dependencies
|
||||
#include "dosbox.h"
|
||||
#include "gamelink.h"
|
||||
#include "sdlmain.h"
|
||||
#include "../resource.h"
|
||||
#include <stdio.h>
|
||||
#include "mem.h"
|
||||
|
||||
typedef GameLink::sSharedMMapBuffer_R1 Buffer;
|
||||
|
||||
//==============================================================================
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Local Data
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static Buffer* g_p_outbuf;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Local Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// out_char
|
||||
//
|
||||
// Copy a string into the output buffer.
|
||||
//
|
||||
static void out_char( const char ch )
|
||||
{
|
||||
// Overflow?
|
||||
if ( g_p_outbuf->payload >= Buffer::BUFFER_SIZE - 1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy character
|
||||
g_p_outbuf->data[ g_p_outbuf->payload++ ] = ch;
|
||||
|
||||
// terminate
|
||||
g_p_outbuf->data[ g_p_outbuf->payload ] = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// out_strcpy
|
||||
//
|
||||
// Copy a string into the output buffer.
|
||||
//
|
||||
static void out_strcpy( const char* p )
|
||||
{
|
||||
while ( *p )
|
||||
{
|
||||
// Overflow?
|
||||
if ( g_p_outbuf->payload >= Buffer::BUFFER_SIZE - 1 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy character
|
||||
g_p_outbuf->data[ g_p_outbuf->payload++ ] = *p++;
|
||||
}
|
||||
|
||||
// terminate
|
||||
g_p_outbuf->data[ g_p_outbuf->payload ] = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// out_strint
|
||||
//
|
||||
// Copy an integer as plain text into the output buffer.
|
||||
//
|
||||
static void out_strint( const int v )
|
||||
{
|
||||
char buf[ 32 ];
|
||||
sprintf( buf, "%d", v );
|
||||
out_strcpy( buf );
|
||||
}
|
||||
|
||||
//
|
||||
// out_strhex
|
||||
//
|
||||
// Copy a hex value as plain text into the output buffer.
|
||||
// Zero prefixed.
|
||||
//
|
||||
static void out_strhex( const int v, const int wide )
|
||||
{
|
||||
char buf[ 32 ], fmt[ 32 ] = "%08X";
|
||||
|
||||
if ( wide >= 1 && wide <= 9 ) {
|
||||
fmt[ 2 ] = '0' + wide;
|
||||
sprintf( buf, fmt, v );
|
||||
out_strcpy( buf );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// out_strhexspc
|
||||
//
|
||||
// Copy a hex value as plain text into the output buffer.
|
||||
//
|
||||
static void out_strhexspc( const int v, const int wide )
|
||||
{
|
||||
char buf[ 32 ], fmt[ 32 ] = "%8X";
|
||||
|
||||
if ( wide >= 1 && wide <= 9 ) {
|
||||
fmt[ 1 ] = '0' + wide;
|
||||
sprintf( buf, fmt, v );
|
||||
out_strcpy( buf );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// out_memcpy
|
||||
//
|
||||
// Copy a block of memory into the output buffer.
|
||||
//
|
||||
static void out_memcpy( const void* p, const uint16_t len )
|
||||
{
|
||||
const uint8_t* src = (const uint8_t*)p;
|
||||
|
||||
for ( uint16_t i = 0; i < len; ++i )
|
||||
{
|
||||
// Overflow?
|
||||
if ( g_p_outbuf->payload >= Buffer::BUFFER_SIZE - 1 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy data
|
||||
g_p_outbuf->data[ g_p_outbuf->payload++ ] = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
//
|
||||
// proc_mech
|
||||
//
|
||||
// Process a mechanical command - encoded form for computer-computer communication. Minimal feedback.
|
||||
//
|
||||
static void proc_mech( Buffer* cmd, uint16_t payload )
|
||||
{
|
||||
// Ignore NULL commands.
|
||||
if ( payload <= 1 || payload > 128 )
|
||||
return;
|
||||
|
||||
cmd->payload = 0;
|
||||
char* com = (char*)(cmd->data);
|
||||
com[ payload ] = 0;
|
||||
|
||||
// printf( "command = %s; payload = %d\n", com, payload );
|
||||
|
||||
//
|
||||
// Decode
|
||||
|
||||
if ( strcmp( com, ":reset" ) == 0 )
|
||||
{
|
||||
// printf( "do reset\n" );
|
||||
|
||||
ResetSystem( true );
|
||||
}
|
||||
else if ( strcmp( com, ":pause" ) == 0 )
|
||||
{
|
||||
// printf( "do pause\n" );
|
||||
|
||||
PauseDOSBox( true );
|
||||
}
|
||||
else if ( strcmp( com, ":shutdown" ) == 0 )
|
||||
{
|
||||
// printf( "do shutdown\n" );
|
||||
|
||||
DoKillSwitch();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Global Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GameLink::InitTerminal()
|
||||
{
|
||||
g_p_outbuf = 0;
|
||||
}
|
||||
|
||||
void GameLink::ExecTerminalMech( Buffer* p_procbuf )
|
||||
{
|
||||
proc_mech( p_procbuf, p_procbuf->payload );
|
||||
}
|
||||
|
||||
void GameLink::ExecTerminal( Buffer* p_inbuf,
|
||||
Buffer* p_outbuf,
|
||||
Buffer* p_procbuf )
|
||||
{
|
||||
// Nothing from the host, or host hasn't acknowledged our last message.
|
||||
if ( p_inbuf->payload == 0 ) {
|
||||
return;
|
||||
}
|
||||
if ( p_outbuf->payload > 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store output pointer
|
||||
g_p_outbuf = p_outbuf;
|
||||
|
||||
// Process mode select ...
|
||||
if ( p_inbuf->data[ 0 ] == ':' )
|
||||
{
|
||||
// Acknowledge now, to avoid loops.
|
||||
uint16_t payload = p_inbuf->payload;
|
||||
p_inbuf->payload = 0;
|
||||
|
||||
// Copy out.
|
||||
memcpy( p_procbuf->data, p_inbuf->data, payload );
|
||||
p_procbuf->payload = payload;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Human command
|
||||
char buf[ Buffer::BUFFER_SIZE + 1 ], *b = buf;
|
||||
|
||||
// Convert into printable ASCII
|
||||
for ( uint32_t i = 0; i < p_inbuf->payload; ++i )
|
||||
{
|
||||
uint8_t u8 = p_inbuf->data[ i ];
|
||||
if ( u8 < 32 || u8 > 127 ) {
|
||||
*b++ = '?';
|
||||
} else {
|
||||
*b++ = (char)u8;
|
||||
}
|
||||
}
|
||||
|
||||
// ... terminate
|
||||
*b++ = 0;
|
||||
|
||||
// Acknowledge
|
||||
p_inbuf->payload = 0;
|
||||
|
||||
// proc_human( buf ); // <-- deprecated
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#endif // C_GAMELINK
|
||||
|
@@ -7,4 +7,4 @@ SUBDIRS += direct3d
|
||||
endif
|
||||
|
||||
noinst_LIBRARIES = liboutput.a
|
||||
liboutput_a_SOURCES = output_direct3d.cpp output_opengl.cpp output_surface.cpp output_tools.cpp output_tools_xbrz.cpp output_ttf.cpp
|
||||
liboutput_a_SOURCES = output_direct3d.cpp output_opengl.cpp output_surface.cpp output_tools.cpp output_tools_xbrz.cpp output_ttf.cpp output_gamelink.cpp
|
||||
|
287
src/output/output_gamelink.cpp
Normal file
287
src/output/output_gamelink.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "dosbox.h"
|
||||
#include "logging.h"
|
||||
#include "menudef.h"
|
||||
#include "sdlmain.h"
|
||||
#include "render.h"
|
||||
#include "vga.h"
|
||||
#include "mixer.h"
|
||||
#include "mapper.h"
|
||||
#include "scancodes_windows.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern uint32_t RunningProgramHash[4];
|
||||
extern const char* RunningProgram;
|
||||
extern "C" SDL_Scancode SDL_GetScancodeFromTable(int, int);
|
||||
|
||||
// output API below
|
||||
|
||||
void OUTPUT_GAMELINK_Initialize()
|
||||
{
|
||||
LOG_MSG("OUTPUT_GAMELINK: Initialize");
|
||||
}
|
||||
|
||||
void OUTPUT_GAMELINK_Select()
|
||||
{
|
||||
LOG_MSG("OUTPUT_GAMELINK: Select");
|
||||
sdl.desktop.want_type = SCREEN_GAMELINK;
|
||||
render.aspectOffload = true;
|
||||
sdl.desktop.fullscreen = false;
|
||||
sdl.mouse.autoenable = false;
|
||||
sdl.gamelink.want_mouse = false;
|
||||
}
|
||||
|
||||
void OUTPUT_GAMELINK_InputEvent()
|
||||
{
|
||||
// LOG_MSG("OUTPUT_GAMELINK: InputEvent");
|
||||
//
|
||||
// Client Mouse & Keyboard
|
||||
|
||||
if ( GameLink::In( &sdl.gamelink.input, &sdl.gamelink.audio ) )
|
||||
{
|
||||
//
|
||||
// -- Audio
|
||||
|
||||
float vol0 = sdl.gamelink.audio.master_vol_l / 100.0f;
|
||||
float vol1 = sdl.gamelink.audio.master_vol_r / 100.0f;
|
||||
MIXER_SetMaster( vol0, vol1 );
|
||||
|
||||
|
||||
//
|
||||
// -- Input
|
||||
|
||||
Mouse_CursorMoved(
|
||||
sdl.gamelink.input.mouse_dx,
|
||||
sdl.gamelink.input.mouse_dy,
|
||||
0,
|
||||
0, true /*emulate*/ );
|
||||
|
||||
// Cache old and new
|
||||
const uint8_t old = sdl.gamelink.input_prev.mouse_btn;;
|
||||
const uint8_t btn = sdl.gamelink.input.mouse_btn;
|
||||
|
||||
// Generate mouse events.
|
||||
for ( uint8_t i = 0; i < 3; ++i )
|
||||
{
|
||||
const uint8_t mask = 1 << i;
|
||||
if ( ( btn & mask ) && !( old & mask ) ) {
|
||||
Mouse_ButtonPressed( i ); // L
|
||||
}
|
||||
if ( !( btn & mask ) && ( old & mask ) ) {
|
||||
Mouse_ButtonReleased( i ); // L
|
||||
}
|
||||
}
|
||||
|
||||
// Generate key events
|
||||
for ( uint8_t blk = 0; blk < 8; ++blk )
|
||||
{
|
||||
const uint32_t old = sdl.gamelink.input_prev.keyb_state[ blk ];
|
||||
const uint32_t key = sdl.gamelink.input.keyb_state[ blk ];
|
||||
for ( uint8_t bit = 0; bit < 32; ++bit )
|
||||
{
|
||||
const SDL_Scancode scancode = windows_scancode_table[static_cast< int >( ( blk * 32 ) + bit )];
|
||||
|
||||
// Build event
|
||||
SDL_Event ev;
|
||||
ev.key.type = 0;
|
||||
ev.key.keysym.scancode = scancode;
|
||||
|
||||
ev.key.keysym.mod = KMOD_NONE; // todo
|
||||
ev.key.keysym.sym = SDLK_UNKNOWN; // todo
|
||||
|
||||
const uint32_t mask = 1 << bit;
|
||||
if ( ( key & mask ) && !( old & mask ) ) {
|
||||
ev.key.type = SDL_KEYDOWN;
|
||||
ev.key.state = SDL_PRESSED;
|
||||
}
|
||||
if ( !( key & mask ) && ( old & mask ) ) {
|
||||
ev.key.type = SDL_KEYUP;
|
||||
ev.key.state = SDL_RELEASED;
|
||||
}
|
||||
|
||||
// Dispatch?
|
||||
if ( ev.key.type != 0 )
|
||||
{
|
||||
MAPPER_CheckEvent( &ev );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update history state
|
||||
memcpy( &sdl.gamelink.input_prev, &sdl.gamelink.input, sizeof( GameLink::sSharedMMapInput_R2 ) );
|
||||
}
|
||||
}
|
||||
|
||||
void OUTPUT_GAMELINK_TrackingMode()
|
||||
{
|
||||
LOG_MSG("OUTPUT_GAMELINK: TrackingMode %i", sdl.gamelink.enable);
|
||||
if (!sdl.gamelink.enable) return;
|
||||
|
||||
bool trackonly_mode = sdl.desktop.want_type != SCREEN_GAMELINK;
|
||||
|
||||
// GAMELINK Init (after splash screen, so we have a HWND for tray icon on Win32)
|
||||
memset(&sdl.gamelink.input_prev, 0, sizeof(GameLink::sSharedMMapInput_R2));
|
||||
int iresult = GameLink::Init(trackonly_mode);
|
||||
if (iresult != 1) {
|
||||
#ifdef WIN32
|
||||
MessageBoxA( NULL, "ERROR: Couldn't initialise inter-process communication.",
|
||||
"DOSBox \"Game Link\" Error", MB_OK | MB_ICONSTOP );
|
||||
#else // WIN32
|
||||
LOG_MSG("GAMELINK: Couldn't initialise inter-process communication.");
|
||||
#endif // WIN32
|
||||
DoKillSwitch();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(C_SDL2)
|
||||
Bitu OUTPUT_GAMELINK_SetSize()
|
||||
{
|
||||
LOG_MSG("OUTPUT_GAMELINK: SetSize");
|
||||
Bitu bpp = 0;
|
||||
Bitu retFlags = 0;
|
||||
(void)bpp;
|
||||
|
||||
if (sdl.desktop.fullscreen) GFX_ForceFullscreenExit();
|
||||
|
||||
sdl.surface = 0;
|
||||
sdl.clip.w = sdl.draw.width;
|
||||
sdl.clip.h = sdl.draw.height;
|
||||
sdl.clip.x = 0;
|
||||
sdl.clip.y = 0;
|
||||
|
||||
if (sdl.gamelink.framebuf == 0 ) {
|
||||
sdl.gamelink.framebuf = malloc(SCALER_MAXWIDTH * SCALER_MAXHEIGHT * 4); // 32 bit color frame buffer
|
||||
}
|
||||
sdl.gamelink.pitch = sdl.draw.width*4;
|
||||
|
||||
sdl.desktop.type = SCREEN_GAMELINK;
|
||||
retFlags = GFX_CAN_32 | GFX_SCALING;
|
||||
|
||||
LOG_MSG("gamelink rendersize=%ux%u",
|
||||
(unsigned int)sdl.draw.width,
|
||||
(unsigned int)sdl.draw.height);
|
||||
|
||||
#if C_XBRZ
|
||||
if (sdl_xbrz.enable)
|
||||
{
|
||||
bool old_scale_on = sdl_xbrz.scale_on;
|
||||
xBRZ_SetScaleParameters(sdl.draw.width, sdl.draw.height, sdl.desktop.window.width, sdl.desktop.window.height);
|
||||
if (sdl_xbrz.scale_on != old_scale_on) {
|
||||
// when we are scaling, we ask render code not to do any aspect correction
|
||||
// when we are not scaling, render code is allowed to do aspect correction at will
|
||||
// due to this, at each scale mode change we need to schedule resize again because window size could change
|
||||
PIC_AddEvent(VGA_SetupDrawing, 50); // schedule another resize here, render has already been initialized at this point and we have just changed its option
|
||||
}
|
||||
sdl.clip.x = sdl.clip.y = 0;
|
||||
sdl.clip.w = sdl.desktop.window.width;
|
||||
sdl.clip.h = sdl.desktop.window.height;
|
||||
sdl.gamelink.pitch = sdl.clip.w*4;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sdl.clip.w > GameLink::sSharedMMapFrame_R1::MAX_WIDTH || sdl.clip.h > GameLink::sSharedMMapFrame_R1::MAX_HEIGHT) {
|
||||
#ifdef WIN32
|
||||
MessageBoxA( NULL, "ERROR: Output resolution too big (windowresolution).",
|
||||
"DOSBox \"Game Link\" Error", MB_OK | MB_ICONSTOP );
|
||||
#else // WIN32
|
||||
LOG_MSG("GAMELINK: Output resolution too big (windowresolution).");
|
||||
#endif // WIN32
|
||||
DoKillSwitch();
|
||||
}
|
||||
|
||||
sdl.deferred_resize = false;
|
||||
sdl.must_redraw_all = true;
|
||||
|
||||
sdl.window = GFX_SetSDLWindowMode(640, 480, SCREEN_GAMELINK);
|
||||
if (sdl.window == NULL)
|
||||
E_Exit("Could not set windowed video mode %ix%i: %s", (int)sdl.draw.width, (int)sdl.draw.height, SDL_GetError());
|
||||
|
||||
sdl.surface = SDL_GetWindowSurface(sdl.window);
|
||||
|
||||
return retFlags;
|
||||
}
|
||||
#else
|
||||
#error "gamelink output requires SDL2"
|
||||
#endif /*!defined(C_SDL2)*/
|
||||
|
||||
bool OUTPUT_GAMELINK_StartUpdate(uint8_t* &pixels, Bitu &pitch)
|
||||
{
|
||||
// LOG_MSG("OUTPUT_GAMELINK: StartUpdate");
|
||||
#if C_XBRZ
|
||||
if (sdl_xbrz.enable && sdl_xbrz.scale_on)
|
||||
{
|
||||
sdl_xbrz.renderbuf.resize(sdl.draw.width * sdl.draw.height);
|
||||
pixels = sdl_xbrz.renderbuf.empty() ? nullptr : reinterpret_cast<uint8_t*>(&sdl_xbrz.renderbuf[0]);
|
||||
pitch = sdl.draw.width * sizeof(uint32_t);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
pixels = (uint8_t *)sdl.gamelink.framebuf;
|
||||
pitch = sdl.gamelink.pitch;
|
||||
}
|
||||
|
||||
sdl.updating = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OUTPUT_GAMELINK_Transfer()
|
||||
{
|
||||
// LOG_MSG("OUTPUT_GAMELINK: Transfer");
|
||||
GameLink::Out( (uint16_t)sdl.desktop.window.width, (uint16_t)sdl.desktop.window.height, render.src.ratio,
|
||||
sdl.gamelink.want_mouse,
|
||||
RunningProgram,
|
||||
RunningProgramHash,
|
||||
(const uint8_t*)sdl.gamelink.framebuf,
|
||||
MemBase );
|
||||
}
|
||||
|
||||
void OUTPUT_GAMELINK_EndUpdate(const uint16_t *changedLines)
|
||||
{
|
||||
// LOG_MSG("OUTPUT_GAMELINK: EndUpdate");
|
||||
#if C_XBRZ
|
||||
if (sdl_xbrz.enable && sdl_xbrz.scale_on)
|
||||
{
|
||||
const uint32_t srcWidth = sdl.draw.width;
|
||||
const uint32_t srcHeight = sdl.draw.height;
|
||||
if (sdl_xbrz.renderbuf.size() == (unsigned int)srcWidth * (unsigned int)srcHeight && srcWidth > 0 && srcHeight > 0)
|
||||
{
|
||||
// please use sdl.clip to keep screen positioning consistent with the rest of the emulator
|
||||
int clipWidth = sdl.clip.w;
|
||||
int clipHeight = sdl.clip.h;
|
||||
int clipX = sdl.clip.x;
|
||||
int clipY = sdl.clip.y;
|
||||
|
||||
// 1. xBRZ-scale render buffer into xbrz pixel buffer
|
||||
unsigned int xbrzWidth = 0;
|
||||
unsigned int xbrzHeight = 0;
|
||||
uint32_t* xbrzBuf;
|
||||
xbrzWidth = srcWidth * (unsigned int)sdl_xbrz.scale_factor;
|
||||
xbrzHeight = srcHeight * (unsigned int)sdl_xbrz.scale_factor;
|
||||
sdl_xbrz.pixbuf.resize(xbrzWidth * xbrzHeight);
|
||||
|
||||
const uint32_t* renderBuf = &sdl_xbrz.renderbuf[0]; // help VS compiler a little + support capture by value
|
||||
xbrzBuf = &sdl_xbrz.pixbuf[0];
|
||||
xBRZ_Render(renderBuf, xbrzBuf, changedLines, (int)srcWidth, (int)srcHeight, sdl_xbrz.scale_factor);
|
||||
|
||||
// 2. nearest neighbor/bilinear scale xbrz buffer into output surface clipping area
|
||||
uint32_t* clipTrg = reinterpret_cast<uint32_t*>(static_cast<char*>(sdl.gamelink.framebuf) + clipY * sdl.gamelink.pitch + (unsigned int)clipX * sizeof(uint32_t));
|
||||
xBRZ_PostScale(&xbrzBuf[0], (int)xbrzWidth, (int)xbrzHeight, (int)(xbrzWidth * sizeof(uint32_t)),
|
||||
&clipTrg[0], clipWidth, clipHeight, sdl.gamelink.pitch,
|
||||
sdl_xbrz.postscale_bilinear, sdl_xbrz.task_granularity);
|
||||
|
||||
}
|
||||
}
|
||||
#endif /*C_XBRZ*/
|
||||
if (!menu.hidecycles) frames++;
|
||||
}
|
||||
|
||||
void OUTPUT_GAMELINK_Shutdown()
|
||||
{
|
||||
LOG_MSG("OUTPUT_GAMELINK: Shutdown");
|
||||
GameLink::Term();
|
||||
}
|
24
src/output/output_gamelink.h
Normal file
24
src/output/output_gamelink.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "dosbox.h"
|
||||
|
||||
#ifndef DOSBOX_OUTPUT_GAMELINK_H
|
||||
#define DOSBOX_OUTPUT_GAMELINK_H
|
||||
|
||||
#if C_GAMELINK
|
||||
#include "../gamelink/gamelink.h"
|
||||
#endif // C_GAMELINK
|
||||
|
||||
// output API
|
||||
void OUTPUT_GAMELINK_Initialize();
|
||||
void OUTPUT_GAMELINK_Select();
|
||||
Bitu OUTPUT_GAMELINK_GetBestMode(Bitu flags);
|
||||
Bitu OUTPUT_GAMELINK_SetSize();
|
||||
bool OUTPUT_GAMELINK_StartUpdate(uint8_t* &pixels, Bitu &pitch);
|
||||
void OUTPUT_GAMELINK_EndUpdate(const uint16_t *changedLines);
|
||||
void OUTPUT_GAMELINK_Shutdown();
|
||||
|
||||
// specific additions
|
||||
void OUTPUT_GAMELINK_InputEvent();
|
||||
void OUTPUT_GAMELINK_Transfer();
|
||||
void OUTPUT_GAMELINK_TrackingMode();
|
||||
|
||||
#endif /*DOSBOX_OUTPUT_GAMELINK_H*/
|
79
src/output/scancodes_windows.h
Normal file
79
src/output/scancodes_windows.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include <SDL_scancode.h>
|
||||
|
||||
/* Windows scancode to SDL scancode mapping table */
|
||||
/* derived from Microsoft scan code document, http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc */
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
static const SDL_Scancode windows_scancode_table[] =
|
||||
{
|
||||
/* 0 1 2 3 4 5 6 7 */
|
||||
/* 8 9 A B C D E F */
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, /* 0 */
|
||||
SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0, SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB, /* 0 */
|
||||
|
||||
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I, /* 1 */
|
||||
SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S, /* 1 */
|
||||
|
||||
SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON, /* 2 */
|
||||
SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V, /* 2 */
|
||||
|
||||
SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA, SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_PRINTSCREEN,/* 3 */
|
||||
SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1, SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5, /* 3 */
|
||||
|
||||
SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9, SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_KP_7, /* 4 */
|
||||
SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_4, SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_KP_1, /* 4 */
|
||||
|
||||
SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_NONUSBACKSLASH,SDL_SCANCODE_F11, /* 5 */
|
||||
SDL_SCANCODE_F12, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI, SDL_SCANCODE_RGUI, SDL_SCANCODE_APPLICATION, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 5 */
|
||||
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_F16, /* 6 */
|
||||
SDL_SCANCODE_F17, SDL_SCANCODE_F18, SDL_SCANCODE_F19, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 6 */
|
||||
|
||||
SDL_SCANCODE_INTERNATIONAL2, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL1, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 7 */
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL4, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL5, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL3, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 7 */
|
||||
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 8 */
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 8 */
|
||||
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 9 */
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RCTRL, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 9 */
|
||||
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* A */
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* A */
|
||||
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_PRINTSCREEN, /* B */
|
||||
SDL_SCANCODE_RALT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* B */
|
||||
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_HOME, /* C */
|
||||
SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_END, /* C */
|
||||
|
||||
SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* D */
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI, SDL_SCANCODE_RGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* D */
|
||||
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* E */
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* E */
|
||||
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* F */
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN /* F */
|
||||
};
|
||||
/* *INDENT-ON* */
|
@@ -148,6 +148,9 @@
|
||||
/* Define to 1 to use opengl display output support */
|
||||
#define C_OPENGL 1
|
||||
|
||||
/* Define to 1 to enable gamelink support */
|
||||
#define C_GAMELINK 1
|
||||
|
||||
/* Set to 1 to enable XBRZ support */
|
||||
#define C_XBRZ 1
|
||||
|
||||
|
Reference in New Issue
Block a user