2020-02-09 11:36:49 +00:00
/*
2021-05-18 11:51:36 +00:00
N - Rage ` s Dinput8 Plugin
( C ) 2002 , 2006 Norbert Wladyka
2016-01-27 09:11:59 +00:00
2021-05-18 11:51:36 +00:00
Author ` s Email : norbert . wladyka @ chello . at
Website : http : //go.to/nrage
2016-01-27 09:11:59 +00:00
2021-05-18 11:51:36 +00:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
2016-01-27 09:11:59 +00:00
2021-05-18 11:51:36 +00:00
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
2016-01-27 09:11:59 +00:00
2021-05-18 11:51:36 +00:00
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2016-01-27 09:11:59 +00:00
*/
# include <windows.h>
2020-02-10 09:57:07 +00:00
# include <commctrl.h>
2016-01-27 09:11:59 +00:00
# include <dinput.h>
2020-02-10 09:57:07 +00:00
# include <xinput.h>
2020-02-09 11:25:05 +00:00
# include "commonIncludes.h"
2016-01-27 09:11:59 +00:00
# include "NRagePluginV2.h"
# include "Interface.h"
# include "FileAccess.h"
# include "PakIO.h"
# include "DirectInput.h"
# include "International.h"
# include "Version.h"
2021-05-18 11:51:36 +00:00
// Prototypes
2016-01-27 09:11:59 +00:00
bool prepareHeap ( ) ;
void FillControls ( CONTROL * Controls ) ;
void InitiatePaks ( bool bInitialize ) ;
void DoShortcut ( int iPlayer , int iShortcut ) ;
DWORD WINAPI MsgThreadFunction ( LPVOID lpParam ) ;
DWORD WINAPI DelayedShortcut ( LPVOID lpParam ) ;
2021-05-18 11:51:36 +00:00
// Global Variables
2020-02-09 11:36:49 +00:00
HMODULE g_hDirectInputDLL = NULL ; // Handle to DirectInput8 library
2021-05-18 11:51:36 +00:00
HMODULE g_hXInputDLL = NULL ; // Handle to XInput library
2020-02-09 11:36:49 +00:00
HMODULE g_hResourceDLL = NULL ; // Handle to resource library; used by LoadString for internationalization
HANDLE g_hHeap = NULL ; // Handle to our heap
2021-05-18 11:51:36 +00:00
int g_nDevices = 0 ; // Number of devices in g_devList
DEVICE g_devList [ MAX_DEVICES ] ; // List of attached input devices, except SysMouse
// Note: we never purge the list of devices during normal operation
DEVICE g_sysMouse ; // We need to treat the sysmouse differently, as we may use "locking"; changed from g_apInputDevice[1] (comment by rabid)
2020-02-09 11:36:49 +00:00
2021-05-18 11:51:36 +00:00
EMULATOR_INFO g_strEmuInfo ; // Emulator info? Stores stuff like our hWnd handle and whether the plugin is initialized yet
TCHAR g_aszDefFolders [ 3 ] [ MAX_PATH ] ; // Default folders: DIRECTORY_MEMPAK, DIRECTORY_GBROMS, DIRECTORY_GBSAVES
TCHAR g_aszLastBrowse [ 6 ] [ MAX_PATH ] ; // Last browsed folders: BF_MEMPAK, BF_GBROM, BF_GBSAVE, BF_PROFILE, BF_NOTE, BF_SHORTCUTS
2020-02-09 11:36:49 +00:00
2021-05-18 11:51:36 +00:00
CRITICAL_SECTION g_critical ; // Our critical section semaphore
2020-02-09 11:36:49 +00:00
int g_iFirstController = - 1 ; // The first controller which is plugged in
// Normally controllers are scanned all at once in sequence, 1-4. We only want to scan devices once per pass;
2021-05-18 11:51:36 +00:00
// This is so we get consistent sample rates on our mouse
2020-02-09 11:36:49 +00:00
bool g_bRunning = false ; // Is the emulator running (i.e. have we opened a ROM)?
bool g_bConfiguring = false ; // Are we currently in a config menu?
2021-05-18 11:51:36 +00:00
bool g_bExclusiveMouse = true ; // Do we have an exclusive mouse lock? This defaults to true unless we have no bound mouse buttons/axes.
2020-02-09 11:36:49 +00:00
CONTROLLER g_pcControllers [ 4 ] ; // Our four N64 controllers, connected or otherwise
2016-01-27 09:11:59 +00:00
SHORTCUTS g_scShortcuts ;
2021-05-18 11:51:36 +00:00
LPDIRECTINPUTDEVICE8 g_apFFDevice [ 4 ] = { NULL , NULL , NULL , NULL } ; // Added by rabid
LPDIRECTINPUTEFFECT g_apdiEffect [ 4 ] = { NULL , NULL , NULL , NULL } ; // Array of handles for FF-Effects, one for each controller
2016-01-27 09:11:59 +00:00
TCHAR g_pszThreadMessage [ DEFAULT_BUFFER ] = _T ( " " ) ;
BOOL APIENTRY DllMain ( HINSTANCE hModule , DWORD ul_reason_for_call , LPVOID lpReserved )
{
2020-02-09 11:36:49 +00:00
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH :
DisableThreadLibraryCalls ( hModule ) ;
if ( ! prepareHeap ( ) )
return FALSE ;
2021-05-18 11:51:36 +00:00
DebugWriteA ( " DLL attach ( " VER_FILE_VERSION_STR " -Debugbuild | built on " __DATE__ " at " __TIME__ " ) \n " ) ;
2020-02-09 11:36:49 +00:00
ZeroMemory ( & g_strEmuInfo , sizeof ( g_strEmuInfo ) ) ;
ZeroMemory ( g_devList , sizeof ( g_devList ) ) ;
ZeroMemory ( & g_sysMouse , sizeof ( g_sysMouse ) ) ;
ZeroMemory ( g_aszDefFolders , sizeof ( g_aszDefFolders ) ) ;
ZeroMemory ( g_aszLastBrowse , sizeof ( g_aszLastBrowse ) ) ;
g_strEmuInfo . hinst = hModule ;
2021-05-18 11:51:36 +00:00
g_strEmuInfo . fDisplayShortPop = true ; // Display pak switching message windows by default
2016-01-27 09:11:59 +00:00
# ifdef _UNICODE
2020-02-09 11:36:49 +00:00
{
g_strEmuInfo . Language = GetLanguageFromINI ( ) ;
if ( g_strEmuInfo . Language = = 0 )
{
g_strEmuInfo . Language = DetectLanguage ( ) ;
2021-05-18 11:51:36 +00:00
DebugWriteA ( " Auto select language: %d \n " , g_strEmuInfo . Language ) ;
2020-02-09 11:36:49 +00:00
}
2021-05-18 11:51:36 +00:00
g_hResourceDLL = LoadLanguageDLL ( g_strEmuInfo . Language ) ; // HACK: it's theoretically not safe to call LoadLibraryEx from DllMain... (comment by rabid)
2020-02-09 11:36:49 +00:00
if ( g_hResourceDLL = = NULL )
{
g_strEmuInfo . Language = 0 ;
g_hResourceDLL = hModule ;
2021-05-18 11:51:36 +00:00
DebugWriteA ( " Couldn't load language DLL, falling back to defaults \n " ) ;
2020-02-09 11:36:49 +00:00
}
}
2016-01-27 09:11:59 +00:00
# else
2020-02-09 11:36:49 +00:00
DebugWriteA ( " (compiled in ANSI mode, language detection DISABLED.) \n " ) ;
g_strEmuInfo . Language = 0 ;
g_hResourceDLL = hModule ;
2016-01-27 09:11:59 +00:00
# endif // #ifndef _UNICODE
2020-02-09 11:36:49 +00:00
InitializeCriticalSection ( & g_critical ) ;
break ;
case DLL_THREAD_ATTACH :
break ;
case DLL_THREAD_DETACH :
break ;
case DLL_PROCESS_DETACH :
//CloseDLL();
if ( g_hResourceDLL ! = g_strEmuInfo . hinst )
FreeLibrary ( g_hResourceDLL ) ; // HACK: it's not safe to call FreeLibrary from DllMain... but screw it
2021-05-18 11:51:36 +00:00
DebugWriteA ( " DLL detach \n " ) ;
2020-02-09 11:36:49 +00:00
CloseDebugFile ( ) ; // Moved here from CloseDll
DeleteCriticalSection ( & g_critical ) ;
2021-05-18 11:51:36 +00:00
// Moved here from CloseDll, then heap is created from DllMain,
// and now it's destroyed by DllMain...just safer code (comment by rabid)
2020-02-09 11:36:49 +00:00
if ( g_hHeap ! = NULL )
{
HeapDestroy ( g_hHeap ) ;
g_hHeap = NULL ;
}
break ;
}
2016-01-27 09:11:59 +00:00
return TRUE ;
}
2021-05-18 11:51:36 +00:00
/*
Function : GetDllInfo
Purpose : This function allows the emulator to gather information
about the DLL by filling in the PluginInfo structure .
Input : a pointer to a PLUGIN_INFO structure that needs to be
filled by the function . ( see def above )
Output : None
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL GetDllInfo ( PLUGIN_INFO * PluginInfo )
{
2020-02-09 11:36:49 +00:00
DebugWriteA ( " CALLED: GetDllInfo \n " ) ;
2016-01-27 09:11:59 +00:00
# ifdef _DEBUG
2021-05-18 11:51:36 +00:00
sprintf ( PluginInfo - > Name , " N-Rage For Project64 (debug): %s " , VER_FILE_VERSION_STR ) ;
2016-01-27 09:11:59 +00:00
# else
2021-05-18 11:51:36 +00:00
sprintf ( PluginInfo - > Name , " N-Rage For Project64: %s " , VER_FILE_VERSION_STR ) ;
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
PluginInfo - > Type = PLUGIN_TYPE_CONTROLLER ;
PluginInfo - > Version = SPECS_VERSION ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
/*
Function : DllAbout
Purpose : This function is optional function that is provided
to give further information about the DLL .
Input : A handle to the window that calls this function
Output : None
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL DllAbout ( HWND hParent )
{
2020-02-09 11:36:49 +00:00
DebugWriteA ( " CALLED: DllAbout \n " ) ;
TCHAR tszTitle [ DEFAULT_BUFFER ] , tszTranslator [ DEFAULT_BUFFER ] ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
LoadString ( g_hResourceDLL , IDS_DLG_ABOUT_TITLE , tszTitle , DEFAULT_BUFFER ) ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
TCHAR szText [ DEFAULT_BUFFER * 4 ] = _T ( VER_FILE_DESCRIPTION_STR ) _T ( " \n \n " ) \
_T ( " Visit my site for support: >>http://go.to/nrage<< \n \n " ) \
_T ( " Version " ) VER_FILE_VERSION_STR _T ( " ( " ) _T ( __DATE__ ) _T ( " ) \n " ) \
_T ( " Done by N-Rage \n " ) \
_T ( " \n " ) \
_T ( " - - - - - \n " ) \
2021-05-18 11:51:36 +00:00
_T ( " Transfer pak emulation done by MadManMarkAu \n " ) \
2020-02-09 11:36:49 +00:00
_T ( " Cleanup, tweaks, and language support by RabidDeity \n " ) ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
LoadString ( g_hResourceDLL , IDS_DLG_ABOUT , tszTranslator , DEFAULT_BUFFER ) ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
_tcscat ( szText , tszTranslator ) ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
MessageBox ( hParent , szText , tszTitle , MB_OK | MB_ICONINFORMATION ) ;
return ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
/*
Function : DllConfig
Purpose : This function is optional function that is provided
to allow the user to configure the DLL
Input : A handle to the window that calls this function
Output : None
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL DllConfig ( HWND hParent )
{
2020-02-09 11:36:49 +00:00
DebugWriteA ( " CALLED: DllConfig \n " ) ;
static bool bInitCC = false ;
if ( ! prepareHeap ( ) )
return ;
if ( ! g_pDIHandle )
{
if ( InitDirectInput ( hParent ) )
{
EnterCriticalSection ( & g_critical ) ;
InitMouse ( ) ;
g_pDIHandle - > EnumDevices ( DI8DEVCLASS_ALL , EnumMakeDeviceList , NULL , DIEDFL_ATTACHEDONLY ) ;
LeaveCriticalSection ( & g_critical ) ;
DebugWriteA ( " InitDirectInput run in DllConfig, g_nDevices=%d \n " , g_nDevices ) ;
}
}
if ( g_hXInputDLL = = NULL )
{
if ( ! InitXinput ( ) )
{
2021-05-18 11:51:36 +00:00
// TODO: Disable ability to set XInput
// TODO: Make XInput and DirectInput settings same page
2020-02-09 11:36:49 +00:00
}
}
if ( g_pDIHandle & & ! g_bConfiguring )
{
g_bConfiguring = true ;
if ( ! bInitCC )
{
INITCOMMONCONTROLSEX ccCtrls = { sizeof ( INITCOMMONCONTROLSEX ) ,
ICC_BAR_CLASSES | ICC_TAB_CLASSES | ICC_LISTVIEW_CLASSES } ;
2021-05-18 11:51:36 +00:00
InitCommonControlsEx ( & ccCtrls ) ; // Needed for TrackBars and Tabs
2020-02-09 11:36:49 +00:00
}
EnterCriticalSection ( & g_critical ) ;
if ( g_sysMouse . didHandle )
2021-05-18 11:51:36 +00:00
{ // Unlock mouse while configuring
2020-02-09 11:36:49 +00:00
g_sysMouse . didHandle - > SetCooperativeLevel ( g_strEmuInfo . hMainWindow , DIB_DEVICE ) ;
g_sysMouse . didHandle - > Acquire ( ) ;
}
LeaveCriticalSection ( & g_critical ) ;
int iOK = DialogBox ( g_hResourceDLL , MAKEINTRESOURCE ( IDD_MAINCFGDIALOG ) , hParent , ( DLGPROC ) MainDlgProc ) ;
2021-05-18 11:51:36 +00:00
// If we go into the dialog box, and the user navigates to the rumble window, our FF device can get unacquired.
// So let's reinitialize them now if we're running, just to be safe (comment by rabid)
2020-02-09 11:36:49 +00:00
if ( g_bRunning )
{
EnterCriticalSection ( & g_critical ) ;
// PrepareInputDevices resets g_bExclusiveMouse to false if no mouse keys are bound, and the only way to
// re-enable exclusive mouse is with a shortcut.
2021-05-18 11:51:36 +00:00
// This is undesirable behavior, but it beats the alternative (and we REALLY need to re-initialize FF devices here)
2020-02-09 11:36:49 +00:00
PrepareInputDevices ( ) ;
if ( iOK )
{
2021-05-18 11:51:36 +00:00
InitiatePaks ( false ) ; // only re-initialize the memory paks and such if the user clicked save or use
2020-02-09 11:36:49 +00:00
}
if ( g_sysMouse . didHandle )
{
if ( g_bExclusiveMouse )
2021-05-18 11:51:36 +00:00
{ // If we have exclusive mouse, we need to relock mouse after closing the config
2020-02-09 11:36:49 +00:00
g_sysMouse . didHandle - > SetCooperativeLevel ( g_strEmuInfo . hMainWindow , DIB_MOUSE ) ;
g_sysMouse . didHandle - > Acquire ( ) ;
if ( g_strEmuInfo . fDisplayShortPop )
{
LoadString ( g_hResourceDLL , IDS_POP_MOUSELOCKED , g_pszThreadMessage , ARRAYSIZE ( g_pszThreadMessage ) ) ;
// HWND hMessage = CreateWindowEx( WS_EX_NOPARENTNOTIFY | WS_EX_STATICEDGE | WS_EX_TOPMOST, _T("STATIC"), pszMessage, WS_CHILD | WS_VISIBLE, 10, 10, 200, 30, g_strEmuInfo.hMainWindow, NULL, g_strEmuInfo.hinst, NULL );
// SetTimer( hMessage, TIMER_MESSAGEWINDOW, 2000, MessageTimer );
CreateThread ( NULL , 0 , MsgThreadFunction , g_pszThreadMessage , 0 , NULL ) ;
}
}
else
{
g_sysMouse . didHandle - > SetCooperativeLevel ( g_strEmuInfo . hMainWindow , DIB_KEYBOARD ) ;
g_sysMouse . didHandle - > Acquire ( ) ;
}
}
LeaveCriticalSection ( & g_critical ) ;
}
g_bConfiguring = false ;
}
return ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
/*
Function : DllTest
Purpose : This function is optional function that is provided
to allow the user to test the DLL
Input : A handle to the window that calls this function
Output : None
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL DllTest ( HWND hParent )
{
2020-02-09 11:36:49 +00:00
DebugWriteA ( " CALLED: DllTest \n " ) ;
return ;
2016-01-27 09:11:59 +00:00
}
// It's easier to maintain one version of this, as not much really changes
2021-05-18 11:51:36 +00:00
// between versions (comment by rabid)
/*
Function : InitiateControllers
Purpose : This function initializes how each of the controllers
should be handled .
Input : A controller structure that needs to be filled for
the emulator to know how to handle each controller .
Output : None
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL InitiateControllers (
# if (SPECS_VERSION < 0x0101)
void * hMainWindow , CONTROL Controls [ 4 ]
# elif (SPECS_VERSION == 0x0101)
CONTROL_INFO ControlInfo
# else
CONTROL_INFO * ControlInfo
# endif
)
{
2020-02-09 11:36:49 +00:00
DebugWriteA ( " CALLED: InitiateControllers \n " ) ;
if ( ! prepareHeap ( ) )
return ;
2016-01-27 09:11:59 +00:00
# if (SPECS_VERSION < 0x0101)
g_strEmuInfo . controllers = & Controls [ 0 ] ;
g_strEmuInfo . hMainWindow = hMainWindow ;
// g_strEmuInfo.MemoryBswaped = TRUE; // Or FALSE. Really does not matter.
// g_strEmuInfo.HEADER = NULL;
# elif (SPECS_VERSION == 0x0101)
g_strEmuInfo . controllers = ControlInfo . Controls ;
g_strEmuInfo . hMainWindow = ControlInfo . hMainWindow ;
// g_strEmuInfo.MemoryBswaped = ControlInfo.MemoryBswaped;
// g_strEmuInfo.HEADER = ControlInfo.HEADER;
# else
g_strEmuInfo . controllers = ControlInfo - > Controls ;
g_strEmuInfo . hMainWindow = ControlInfo - > hMainWindow ;
// g_strEmuInfo.MemoryBswaped = ControlInfo->MemoryBswaped;
// g_strEmuInfo.HEADER = ControlInfo->HEADER;
# endif
2020-02-09 11:36:49 +00:00
// UNDONE: Instead of just storing the header, figure out what ROM we're running and save that information somewhere
2021-05-18 11:51:36 +00:00
// The emulator expects us to tell what controllers are plugged in and what their paks are at this point
2020-02-09 11:36:49 +00:00
2021-05-18 11:51:36 +00:00
if ( ! g_pDIHandle ) // If we don't have a DirectInput handle, we need to make one, attach it to the main window (so it will die if our emulator dies), and enumerate devices
2020-02-09 11:36:49 +00:00
{
if ( InitDirectInput ( g_strEmuInfo . hMainWindow ) )
{
EnterCriticalSection ( & g_critical ) ;
InitMouse ( ) ;
g_pDIHandle - > EnumDevices ( DI8DEVCLASS_ALL , EnumMakeDeviceList , NULL , DIEDFL_ATTACHEDONLY ) ;
LeaveCriticalSection ( & g_critical ) ;
DebugWriteA ( " InitDirectInput run in InitiateControllers, g_nDevices=%d \n " , g_nDevices ) ;
}
else
return ;
}
if ( g_hXInputDLL = = NULL )
{
if ( ! InitXinput ( ) )
{
2021-05-18 11:51:36 +00:00
// TODO: Disable ability to set XInput
// TODO: Make XInput and DirectInput settings same page
2020-02-09 11:36:49 +00:00
}
}
2021-05-18 11:51:36 +00:00
// To handle XInput controllers better, we need to set ID to 0
2020-02-09 11:36:49 +00:00
iXinputControlId = 0 ;
int iDevice ;
EnterCriticalSection ( & g_critical ) ;
2021-05-18 11:51:36 +00:00
// ZeroMemory( g_apFFDevice, sizeof(g_apFFDevice) ); // No, we'll reinitialize the existing reference if it's already loaded
// ZeroMemory( g_apdiEffect, sizeof(g_apdiEffect) ); // No, we'll release it with CloseControllerPak
2020-02-09 11:36:49 +00:00
for ( int i = 3 ; i > = 0 ; i - - )
{
SaveControllerPak ( i ) ;
CloseControllerPak ( i ) ;
2021-05-18 11:51:36 +00:00
// freePakData( &g_pcControllers[i] ); // Already called by CloseControllerPak
2020-02-09 11:36:49 +00:00
freeModifiers ( & g_pcControllers [ i ] ) ;
SetControllerDefaults ( & g_pcControllers [ i ] ) ;
}
g_pcControllers [ 0 ] . fPlugged = true ;
if ( ! LoadConfigFromINI ( ) )
{
DebugWriteA ( " \t INI load failed, loading defaults from resource \n " ) ;
for ( int i = 0 ; i < 4 ; i + + )
LoadProfileFromResource ( i , false ) ;
LoadShortcutsFromResource ( false ) ;
}
2021-05-18 11:51:36 +00:00
// Init: Find force feedback devices and initialize
2020-02-09 11:36:49 +00:00
for ( int i = 0 ; i < 4 ; i + + )
{
DebugWriteA ( " Controller %d: " , i + 1 ) ;
if ( g_pcControllers [ i ] . fPlugged )
{
if ( g_pcControllers [ i ] . fXInput )
{
InitiateXInputController ( & g_pcControllers [ i ] . xiController , i ) ;
2020-06-14 09:19:49 +00:00
//continue;
2020-02-09 11:36:49 +00:00
}
2021-05-18 11:51:36 +00:00
// Search for right controller
2020-02-09 11:36:49 +00:00
iDevice = FindDeviceinList ( g_pcControllers [ i ] . guidFFDevice ) ;
if ( iDevice ! = - 1 & & g_devList [ iDevice ] . bEffType )
{
2021-05-18 11:51:36 +00:00
DebugWriteA ( " Rumble device set, " ) ;
2020-02-09 11:36:49 +00:00
}
2021-05-18 11:51:36 +00:00
else // We couldn't find the device specified in the INI file, or it was already null
2020-02-09 11:36:49 +00:00
{
g_pcControllers [ i ] . guidFFDevice = GUID_NULL ;
2021-05-18 11:51:36 +00:00
DebugWriteA ( " No rumble device/effect type set, " ) ;
2020-02-09 11:36:49 +00:00
}
if ( g_pcControllers [ i ] . nModifiers > 0 )
SetModifier ( & g_pcControllers [ i ] ) ;
g_iFirstController = i ;
2021-05-18 11:51:36 +00:00
DebugWriteA ( " Plugged in, with pak type %d, " , g_pcControllers [ i ] . PakType ) ;
2020-02-09 11:36:49 +00:00
DebugWriteA ( " RawMode is %d \n " , g_pcControllers [ i ] . fRawData ) ;
}
else
{
2021-05-18 11:51:36 +00:00
DebugWriteA ( " Unplugged \n " ) ;
freePakData ( & g_pcControllers [ i ] ) ; // We don't need to do this again, but there's not much overhead so I'll leave it (comment by rabid)
2020-02-09 11:36:49 +00:00
freeModifiers ( & g_pcControllers [ i ] ) ;
}
}
PrepareInputDevices ( ) ;
if ( g_bExclusiveMouse )
{
// g_sysMouse.didHandle->Unacquire();
// g_sysMouse.didHandle->SetCooperativeLevel( g_strEmuInfo.hMainWindow, DIB_MOUSE ); // PrepareInputDevices does this.
g_sysMouse . didHandle - > Acquire ( ) ;
}
InitiatePaks ( true ) ;
g_strEmuInfo . fInitialisedPlugin = true ;
LeaveCriticalSection ( & g_critical ) ;
FillControls ( g_strEmuInfo . controllers ) ;
return ;
2016-01-27 09:11:59 +00:00
} // end InitiateControllers
2021-05-18 11:51:36 +00:00
/*
Function : RomOpen
Purpose : This function is called when a ROM is open ( from the
emulation thread )
Input : None
Output : None
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL RomOpen ( void )
{
2020-02-09 11:36:49 +00:00
DebugWriteA ( " CALLED: RomOpen \n " ) ;
2021-05-18 11:51:36 +00:00
//XInputEnable( TRUE ); // Enables XInput (comment by tecnicors)
2020-02-09 11:36:49 +00:00
if ( ! g_strEmuInfo . fInitialisedPlugin )
{
ErrorMessage ( IDS_ERR_NOINIT , 0 , false ) ;
return ;
}
EnterCriticalSection ( & g_critical ) ;
2021-05-18 11:51:36 +00:00
// Re-initialize our paks and shortcuts
2020-02-09 11:36:49 +00:00
InitiatePaks ( true ) ;
2021-05-18 11:51:36 +00:00
// LoadShortcuts( &g_scShortcuts ); Why are we loading shortcuts again? They should already be loaded!
2020-02-09 11:36:49 +00:00
LeaveCriticalSection ( & g_critical ) ;
g_bRunning = true ;
return ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
/*
Function : RomClosed
Purpose : This function is called when a ROM is closed
Input : None
Output : None
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL RomClosed ( void )
{
2020-02-09 11:36:49 +00:00
int i ;
2021-05-18 11:51:36 +00:00
//XInputEnable( FALSE ); // Disables XInput (comment by tecnicors)
2020-02-09 11:36:49 +00:00
DebugWriteA ( " CALLED: RomClosed \n " ) ;
EnterCriticalSection ( & g_critical ) ;
if ( g_sysMouse . didHandle )
{
g_sysMouse . didHandle - > Unacquire ( ) ;
2021-05-18 11:51:36 +00:00
g_sysMouse . didHandle - > SetCooperativeLevel ( g_strEmuInfo . hMainWindow , DIB_KEYBOARD ) ; // Unlock the mouse, just in case
2020-02-09 11:36:49 +00:00
}
for ( i = 0 ; i < ARRAYSIZE ( g_pcControllers ) ; + + i )
{
if ( g_pcControllers [ i ] . pPakData )
{
SaveControllerPak ( i ) ;
CloseControllerPak ( i ) ;
}
2021-05-18 11:51:36 +00:00
// freePakData( &g_pcControllers[i] ); // Already done by CloseControllerPak (comment by rabid)
// Don't free the modifiers!
2020-02-09 11:36:49 +00:00
// ZeroMemory( &g_pcControllers[i], sizeof(CONTROLLER) );
}
for ( i = 0 ; i < ARRAYSIZE ( g_apdiEffect ) ; + + i )
ReleaseEffect ( g_apdiEffect [ i ] ) ;
ZeroMemory ( g_apdiEffect , sizeof ( g_apdiEffect ) ) ;
g_bRunning = false ;
LeaveCriticalSection ( & g_critical ) ;
return ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
/*
Function : GetKeys
Purpose : To get the current state of the controllers buttons .
Input : Controller Number ( 0 to 3 )
A pointer to a BUTTONS structure to be filled with
the controller state .
Output : None
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL GetKeys ( int Control , BUTTONS * Keys )
{
# ifdef ENABLE_RAWPAK_DEBUG
2020-02-09 11:36:49 +00:00
DebugWriteA ( " CALLED: GetKeys \n " ) ;
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
if ( g_bConfiguring | | ( ! g_pcControllers [ Control ] . bBackgroundInput & & GetForegroundWindow ( ) ! = g_strEmuInfo . hMainWindow ) )
Keys - > Value = 0 ;
else
{
EnterCriticalSection ( & g_critical ) ;
if ( g_pcControllers [ Control ] . fPlugged )
{
if ( Control = = g_iFirstController )
{
GetDeviceDatas ( ) ;
CheckShortcuts ( ) ;
}
2021-05-18 11:51:36 +00:00
if ( g_pcControllers [ Control ] . fXInput ) // Reads the XInput controller keys, if connected (comment by tecnicors)
2020-02-09 11:36:49 +00:00
GetXInputControllerKeys ( Control , & Keys - > Value ) ;
else
GetNControllerInput ( Control , & Keys - > Value ) ;
}
LeaveCriticalSection ( & g_critical ) ;
}
return ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
/*
Function : ControllerCommand
Purpose : To process the raw data that has just been sent to a
specific controller .
Input : Controller Number ( 0 to 3 ) and - 1 signaling end of
processing the PIF RAM .
- Pointer of data to be processed .
Output : None
Note : This function is only needed if the DLL is allowing raw
data .
The data that is being processed looks like this :
Initialize controller : 01 03 00 FF FF FF
Read controller : 01 04 01 FF FF FF FF
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL ControllerCommand ( int Control , BYTE * Command )
{
2020-02-09 11:36:49 +00:00
// We don't need to use this because it will be echoed immediately afterwards
// by a call to ReadController
return ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
/*
Function : ReadController
Purpose : To process the raw data in the PIF RAM that is about to
be read .
Input : - Controller Number ( 0 to 3 ) and - 1 signaling end of
processing the PIF RAM .
- Pointer of data to be processed .
Output : None
Note : This function is only needed if the DLL is allowing raw
data .
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL ReadController ( int Control , BYTE * Command )
{
# ifdef ENABLE_RAWPAK_DEBUG
2020-02-09 11:36:49 +00:00
DebugWriteA ( " CALLED: ReadController \n " ) ;
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
if ( Control = = - 1 )
return ;
EnterCriticalSection ( & g_critical ) ;
if ( ! g_pcControllers [ Control ] . fPlugged )
{
Command [ 1 ] | = RD_ERROR ;
LeaveCriticalSection ( & g_critical ) ;
return ;
}
switch ( Command [ 2 ] )
{
case RD_RESETCONTROLLER :
WriteDatasA ( " ResetController-PreProcessing " , Control , Command , 0 ) ;
case RD_GETSTATUS :
2021-05-18 11:51:36 +00:00
// Expected: controller gets 1 byte (command), controller sends back 3 bytes
// Should be: Command[0] == 0x01
2020-02-09 11:36:49 +00:00
// Command[1] == 0x03
2016-01-27 09:11:59 +00:00
# ifdef ENABLE_RAWPAK_DEBUG
2020-02-09 11:36:49 +00:00
WriteDatasA ( " GetStatus-PreProcessing " , Control , Command , 0 ) ;
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
Command [ 3 ] = RD_GAMEPAD | RD_ABSOLUTE ;
Command [ 4 ] = RD_NOEEPROM ;
2021-05-18 11:51:36 +00:00
if ( g_pcControllers [ Control ] . fN64Mouse ) // Is controller a mouse?
2020-02-09 11:36:49 +00:00
Command [ 3 ] = RD_RELATIVE ;
if ( g_pcControllers [ Control ] . fPakInitialized & & g_pcControllers [ Control ] . pPakData )
{
if ( * ( BYTE * ) g_pcControllers [ Control ] . pPakData = = PAK_ADAPTOID )
{
Command [ 5 ] = GetAdaptoidStatus ( Control ) ;
if ( Command [ 5 ] & RD_NOTINITIALIZED )
( ( ADAPTOIDPAK * ) g_pcControllers [ Control ] . pPakData ) - > fRumblePak = true ;
}
else
{
Command [ 5 ] = ( * ( BYTE * ) g_pcControllers [ Control ] . pPakData ! = PAK_NONE ) ? RD_PLUGIN : RD_NOPLUGIN ;
if ( g_pcControllers [ Control ] . fPakCRCError )
{
Command [ 5 ] = Command [ 5 ] | RD_ADDRCRCERR ;
g_pcControllers [ Control ] . fPakCRCError = false ;
}
}
}
else
{
if ( ! g_bConfiguring & & InitControllerPak ( Control ) & & g_pcControllers [ Control ] . pPakData )
{
g_pcControllers [ Control ] . fPakInitialized = true ;
if ( * ( BYTE * ) g_pcControllers [ Control ] . pPakData = = PAK_ADAPTOID )
Command [ 5 ] = GetAdaptoidStatus ( Control ) ;
else
{
Command [ 5 ] = ( * ( BYTE * ) g_pcControllers [ Control ] . pPakData ) ? RD_PLUGIN : RD_NOPLUGIN ;
Command [ 5 ] = Command [ 5 ] | ( g_pcControllers [ Control ] . fPakCRCError ? RD_ADDRCRCERR : 0 ) ;
}
}
else
Command [ 5 ] = RD_NOPLUGIN | RD_NOTINITIALIZED ;
}
if ( g_pcControllers [ Control ] . fPakCRCError )
{
Command [ 5 ] = Command [ 5 ] | RD_ADDRCRCERR ;
g_pcControllers [ Control ] . fPakCRCError = false ;
}
2016-01-27 09:11:59 +00:00
# ifdef ENABLE_RAWPAK_DEBUG
2020-02-09 11:36:49 +00:00
WriteDatasA ( " GetStatus-PostProcessing " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
break ;
case RD_READKEYS :
2021-05-18 11:51:36 +00:00
// Expected: controller gets 1 byte (command), controller sends back 4 bytes
// Should be: Command[0] == 0x01
2020-02-09 11:36:49 +00:00
// Command[1] == 0x04
if ( g_bConfiguring | | ( ! g_pcControllers [ Control ] . bBackgroundInput & & GetForegroundWindow ( ) ! = g_strEmuInfo . hMainWindow ) )
Command [ 3 ] = Command [ 4 ] = Command [ 5 ] = Command [ 6 ] = 0 ;
else
{
if ( Control = = g_iFirstController )
{
GetDeviceDatas ( ) ;
CheckShortcuts ( ) ;
}
2021-05-18 11:51:36 +00:00
if ( g_pcControllers [ Control ] . fXInput ) // Reads XInput controller keys, if connected (comment by tecnicors)
2020-02-09 11:36:49 +00:00
GetXInputControllerKeys ( Control , ( LPDWORD ) & Command [ 3 ] ) ;
else
GetNControllerInput ( Control , ( DWORD * ) & Command [ 3 ] ) ;
}
break ;
case RD_READPAK :
2016-01-27 09:11:59 +00:00
# ifdef ENABLE_RAWPAK_DEBUG
2020-02-09 11:36:49 +00:00
WriteDatasA ( " ReadPak-PreProcessing " , Control , Command , 0 ) ;
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
if ( g_pcControllers [ Control ] . fPakInitialized )
//Command[1] = Command[1] | ReadControllerPak( Control, &Command[3] );
ReadControllerPak ( Control , & Command [ 3 ] ) ;
else
{
2021-05-18 11:51:36 +00:00
DebugWriteA ( " Tried to read, but pak wasn't initialized! \n " ) ;
2020-02-09 11:36:49 +00:00
// InitControllerPak( Control );
Command [ 1 ] | = RD_ERROR ;
//ZeroMemory( &Command[5], 32 );
}
2016-01-27 09:11:59 +00:00
# ifdef ENABLE_RAWPAK_DEBUG
2020-02-09 11:36:49 +00:00
WriteDatasA ( " ReadPak-PostProcessing " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
break ;
case RD_WRITEPAK :
2016-01-27 09:11:59 +00:00
# ifdef ENABLE_RAWPAK_DEBUG
2020-02-09 11:36:49 +00:00
WriteDatasA ( " WritePak-PreProcessing " , Control , Command , 0 ) ;
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
if ( g_pcControllers [ Control ] . fPakInitialized )
//Command[1] = Command[1] | WriteControllerPak( Control, &Command[3] );
WriteControllerPak ( Control , & Command [ 3 ] ) ;
else
{
2021-05-18 11:51:36 +00:00
DebugWriteA ( " Tried to write, but pak wasn't initialized! (pak type was %u) \n " , g_pcControllers [ Control ] . PakType ) ;
2020-02-09 11:36:49 +00:00
// InitControllerPak( Control );
Command [ 1 ] | = RD_ERROR ;
}
2016-01-27 09:11:59 +00:00
# ifdef ENABLE_PAK_WRITES_DEBUG
2020-02-09 11:36:49 +00:00
WriteDatasA ( " WritePak-PostProcessing " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
break ;
case RD_READEEPROM :
2021-05-18 11:51:36 +00:00
// Should be handled by the emulator
2020-02-09 11:36:49 +00:00
WriteDatasA ( " ReadEeprom-PreProcessing " , Control , Command , 0 ) ;
WriteDatasA ( " ReadEeprom-PostProcessing " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
break ;
case RD_WRITEEPROM :
2021-05-18 11:51:36 +00:00
// Should be handled by the emulator
2020-02-09 11:36:49 +00:00
WriteDatasA ( " WriteEeprom-PreProcessing " , Control , Command , 0 ) ;
WriteDatasA ( " WriteEeprom-PostProcessing " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
break ;
default :
2021-05-18 11:51:36 +00:00
// Only accessible if the emulator has bugs, or maybe the ROM is flawed in some way
2020-02-09 11:36:49 +00:00
WriteDatasA ( " ReadController: Bad read " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
Command [ 1 ] = Command [ 1 ] | RD_ERROR ;
}
LeaveCriticalSection ( & g_critical ) ;
return ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
/*
Function : WM_KeyDown
Purpose : To pass the WM_KeyDown message from the emulator to the
plugin .
Input : wParam and lParam of the WM_KEYDOWN message .
Output : None
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL WM_KeyDown ( WPARAM wParam , LPARAM lParam )
{
2020-02-09 11:36:49 +00:00
return ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
/*
Function : WM_KeyUp
Purpose : To pass the WM_KEYUP message from the emulator to the
plugin .
Input : wParam and lParam of the WM_KEYDOWN message .
Output : None
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL WM_KeyUp ( WPARAM wParam , LPARAM lParam )
{
2020-02-09 11:36:49 +00:00
return ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
/*
Function : CloseDLL
Purpose : This function is called when the emulator is closing
down allowing the DLL to de - initialize .
Input : None
Output : None
*/
2016-01-27 09:11:59 +00:00
EXPORT void CALL CloseDLL ( void )
2021-05-18 11:51:36 +00:00
{ // Hack: This is broken in Project64 1.6 (it calls CloseDLL too often)
2020-02-09 11:36:49 +00:00
DebugWriteA ( " CALLED: CloseDLL \n " ) ;
if ( g_bRunning )
RomClosed ( ) ;
for ( int i = 0 ; i < 4 ; i + + )
{
freePakData ( & g_pcControllers [ i ] ) ;
freeModifiers ( & g_pcControllers [ i ] ) ;
}
2021-05-18 11:51:36 +00:00
// ZeroMemory( g_pcControllers, sizeof(g_pcControllers) ); // Why zero the memory if we're just going to close down?
2020-02-09 11:36:49 +00:00
FreeDirectInput ( ) ;
FreeXinput ( ) ;
return ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
// Prepare a global heap. Use P_malloc and P_free as wrappers to grab/release memory.
2016-01-27 09:11:59 +00:00
bool prepareHeap ( )
{
2020-02-09 11:36:49 +00:00
if ( g_hHeap = = NULL )
g_hHeap = HeapCreate ( 0 , 4 * 1024 , 0 ) ;
return ( g_hHeap ! = NULL ) ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
// Frees pak data memory (called more often than you'd think)
2016-01-27 09:11:59 +00:00
void freePakData ( CONTROLLER * pcController )
{
2020-02-09 11:36:49 +00:00
if ( pcController & & pcController - > pPakData )
{
P_free ( pcController - > pPakData ) ;
pcController - > pPakData = NULL ;
}
2016-01-27 09:11:59 +00:00
}
// Frees modifier memory
void freeModifiers ( CONTROLLER * pcController )
{
2020-02-09 11:36:49 +00:00
if ( pcController & & pcController - > pModifiers )
{
pcController - > nModifiers = 0 ;
P_free ( pcController - > pModifiers ) ;
pcController - > pModifiers = NULL ;
}
2016-01-27 09:11:59 +00:00
}
// After enumerating DirectInput devices into g_devList, find a device by product name and counter
int FindDeviceinList ( const TCHAR * pszProductName , const BYTE bProductCounter , bool fFindSimilar )
{
2020-02-09 11:36:49 +00:00
if ( ! ( * pszProductName ) )
return - 1 ;
int i = 0 , iSimilar = - 1 , iExact = - 1 ;
while ( ( i < ARRAYSIZE ( g_devList ) ) & & ( iExact = = - 1 ) )
{
if ( ! lstrcmp ( g_devList [ i ] . szProductName , pszProductName ) )
{
if ( ( bProductCounter > 0 ) | | ( iSimilar = = - 1 ) )
iSimilar = i ;
if ( g_devList [ i ] . bProductCounter = = bProductCounter )
iExact = i ;
}
i + + ;
}
if ( fFindSimilar & & ( iExact = = - 1 ) )
iExact = iSimilar ;
return iExact ;
2016-01-27 09:11:59 +00:00
}
// After enumerating DirectInput devices into g_devList, find a device by GUID. Finding similar devices is impossible.
int FindDeviceinList ( REFGUID rGUID )
{
2020-02-09 11:36:49 +00:00
if ( rGUID = = GUID_NULL )
return - 1 ;
int i = 0 ;
while ( i < ARRAYSIZE ( g_devList ) )
{
if ( IsEqualGUID ( g_devList [ i ] . guidInstance , rGUID ) )
return i ;
i + + ;
}
return - 1 ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
// Let's initialize all connected controller paks.
// Input: false means run InitControlPak on each plugged controller. Input true means just clear each paks CRC error status?
2016-01-27 09:11:59 +00:00
// When we call this from RomOpen, it's true, otherwise (in DllConfig) it's false.
// Rather counterintuitive.
2021-05-18 11:51:36 +00:00
2016-01-27 09:11:59 +00:00
void InitiatePaks ( bool bInitialize )
{
2020-02-09 11:36:49 +00:00
for ( int i = 0 ; i < 4 ; i + + )
{
if ( g_pcControllers [ i ] . fPlugged )
{
g_pcControllers [ i ] . fPakCRCError = false ;
if ( g_pcControllers [ i ] . fRawData )
{
if ( ! bInitialize )
g_pcControllers [ i ] . fPakInitialized = InitControllerPak ( i ) ;
}
else
2021-05-18 11:51:36 +00:00
{ // We only support "raw mode" paks so this won't do much
2020-02-09 11:36:49 +00:00
; //if( g_pcControllers[i].PakType == PAK_RUMBLE )
// CreateEffectHandle( i, g_pcControllers[i].bRumbleTyp, g_pcControllers[i].bRumbleStrength );
}
}
}
2016-01-27 09:11:59 +00:00
}
// This used to be "NotifyEmulator" which was supposed to tell the emulator if we changed the way it sees controllers.
2021-05-18 11:51:36 +00:00
// Unfortunately the spec doesn't work that way. Fixed the function and changed the function name to something that makes more sense.
2016-01-27 09:11:59 +00:00
// FillControls takes a Controls array from InitiateControllers and fills it with what we know about whether
2021-05-18 11:51:36 +00:00
// a controller is plugged in, accepting raw data, and what type of pak is plugged in.
2016-01-27 09:11:59 +00:00
void FillControls ( CONTROL * Controls )
{
2020-02-09 11:36:49 +00:00
for ( int i = 4 - 1 ; i > = 0 ; i - - )
{
if ( g_pcControllers [ i ] . fPlugged )
{
Controls [ i ] . Present = g_pcControllers [ i ] . fPlugged ;
Controls [ i ] . RawData = g_pcControllers [ i ] . fRawData ;
switch ( g_pcControllers [ i ] . PakType )
{
case PAK_MEM :
Controls [ i ] . Plugin = PLUGIN_MEMPAK ;
2020-06-14 09:19:49 +00:00
//Controls[i].RawData = false;
2020-02-09 11:36:49 +00:00
break ;
case PAK_RUMBLE :
Controls [ i ] . Plugin = PLUGIN_RUMBLE_PAK ;
break ;
case PAK_TRANSFER :
Controls [ i ] . Plugin = PLUGIN_TRANSFER_PAK ;
break ;
case PAK_VOICE :
Controls [ i ] . Plugin = g_pcControllers [ i ] . fRawData ? PLUGIN_RAW : PLUGIN_NONE ;
break ;
case PAK_ADAPTOID :
Controls [ i ] . Plugin = g_pcControllers [ i ] . fRawData ? PLUGIN_RAW : PLUGIN_NONE ;
break ;
case PAK_NONE :
default :
Controls [ i ] . Plugin = PLUGIN_NONE ;
}
}
else
{
Controls [ i ] . Plugin = PLUGIN_NONE ;
Controls [ i ] . Present = false ;
Controls [ i ] . RawData = true ;
}
}
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
// Called after a poll to execute any shortcuts
2016-01-27 09:11:59 +00:00
void CheckShortcuts ( )
{
2020-02-09 11:36:49 +00:00
static bool bWasPressed [ sizeof ( SHORTCUTSPL ) / sizeof ( BUTTON ) ] [ 4 ] ;
2021-05-18 11:51:36 +00:00
static bool bMLWasPressed ; // Mouselock
2020-02-09 11:36:49 +00:00
bool bMatching = false ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
if ( g_bConfiguring | | ! g_bRunning )
2021-05-18 11:51:36 +00:00
return ; // We don't process shortcuts if we're in a config menu or are not running emulation
2016-01-27 09:11:59 +00:00
2021-05-18 11:51:36 +00:00
// just process if key wasn't pressed before
for ( int i = 0 ; i < 4 ; i + + ) // Controllers
2020-02-09 11:36:49 +00:00
{
for ( int j = 0 ; j < SC_TOTAL ; j + + )
{
bMatching = IsBtnPressed ( g_scShortcuts . Player [ i ] . aButtons [ j ] ) ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
if ( bMatching & & ! bWasPressed [ j ] [ i ] )
DoShortcut ( i , j ) ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
bWasPressed [ j ] [ i ] = bMatching ;
}
}
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
bMatching = IsBtnPressed ( g_scShortcuts . bMouseLock ) ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
if ( bMatching & & ! bMLWasPressed )
2021-05-18 11:51:36 +00:00
DoShortcut ( - 1 , - 1 ) ; // Controller -1 means do mouselock shortcut
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
bMLWasPressed = bMatching ;
2016-01-27 09:11:59 +00:00
}
// Executes the shortcut iShortcut on controller iController
// Special case: if iPlayer is -1, run the mouselock shortcut
void DoShortcut ( int iControl , int iShortcut )
{
2020-02-09 11:36:49 +00:00
DebugWriteA ( " Shortcut: %d %d \n " , iControl , iShortcut ) ;
TCHAR pszMessage [ DEFAULT_BUFFER / 2 ] = TEXT ( " " ) ;
bool bEjectFirst = false ;
if ( iControl = = - 1 )
{
EnterCriticalSection ( & g_critical ) ;
if ( g_sysMouse . didHandle )
{
g_sysMouse . didHandle - > Unacquire ( ) ;
if ( g_bExclusiveMouse )
{
g_sysMouse . didHandle - > Unacquire ( ) ;
g_sysMouse . didHandle - > SetCooperativeLevel ( g_strEmuInfo . hMainWindow , DIB_KEYBOARD ) ;
g_sysMouse . didHandle - > Acquire ( ) ;
LoadString ( g_hResourceDLL , IDS_POP_MOUSEUNLOCKED , pszMessage , ARRAYSIZE ( pszMessage ) ) ;
}
else
{
g_sysMouse . didHandle - > Unacquire ( ) ;
g_sysMouse . didHandle - > SetCooperativeLevel ( g_strEmuInfo . hMainWindow , DIB_MOUSE ) ;
g_sysMouse . didHandle - > Acquire ( ) ;
LoadString ( g_hResourceDLL , IDS_POP_MOUSELOCKED , pszMessage , ARRAYSIZE ( pszMessage ) ) ;
}
g_sysMouse . didHandle - > Acquire ( ) ;
g_bExclusiveMouse = ! g_bExclusiveMouse ;
}
LeaveCriticalSection ( & g_critical ) ;
}
else if ( g_pcControllers [ iControl ] . fPlugged )
{
if ( g_pcControllers [ iControl ] . pPakData )
{
SaveControllerPak ( iControl ) ;
CloseControllerPak ( iControl ) ;
}
switch ( iShortcut )
{
case SC_NOPAK :
EnterCriticalSection ( & g_critical ) ;
g_pcControllers [ iControl ] . PakType = PAK_NONE ;
g_pcControllers [ iControl ] . fPakInitialized = false ;
LoadString ( g_hResourceDLL , IDS_P_NONE , pszMessage , ARRAYSIZE ( pszMessage ) ) ;
LeaveCriticalSection ( & g_critical ) ;
break ;
case SC_MEMPAK :
if ( PAK_NONE = = g_pcControllers [ iControl ] . PakType )
{
EnterCriticalSection ( & g_critical ) ;
g_pcControllers [ iControl ] . PakType = PAK_MEM ;
g_pcControllers [ iControl ] . fPakInitialized = false ;
LoadString ( g_hResourceDLL , IDS_P_MEMPAK , pszMessage , ARRAYSIZE ( pszMessage ) ) ;
LeaveCriticalSection ( & g_critical ) ;
}
else
{
bEjectFirst = true ;
}
break ;
case SC_RUMBPAK :
if ( PAK_NONE = = g_pcControllers [ iControl ] . PakType )
{
EnterCriticalSection ( & g_critical ) ;
g_pcControllers [ iControl ] . PakType = PAK_RUMBLE ;
2020-06-14 09:19:49 +00:00
g_pcControllers [ iControl ] . fPakInitialized = true ;
2020-02-09 11:36:49 +00:00
if ( g_pcControllers [ iControl ] . fRawData )
if ( CreateEffectHandle ( iControl , g_pcControllers [ iControl ] . bRumbleTyp , g_pcControllers [ iControl ] . bRumbleStrength ) )
{
DebugWriteA ( " CreateEffectHandle for shortcut switch: OK \n " ) ;
}
else
{
DebugWriteA ( " Couldn't CreateEffectHandle for shortcut switch. \n " ) ;
}
LoadString ( g_hResourceDLL , IDS_P_RUMBLEPAK , pszMessage , ARRAYSIZE ( pszMessage ) ) ;
LeaveCriticalSection ( & g_critical ) ;
}
else
{
bEjectFirst = true ;
}
break ;
case SC_TRANSPAK :
if ( PAK_NONE = = g_pcControllers [ iControl ] . PakType )
{
EnterCriticalSection ( & g_critical ) ;
g_pcControllers [ iControl ] . PakType = PAK_TRANSFER ;
g_pcControllers [ iControl ] . fPakInitialized = false ;
LoadString ( g_hResourceDLL , IDS_P_TRANSFERPAK , pszMessage , ARRAYSIZE ( pszMessage ) ) ;
LeaveCriticalSection ( & g_critical ) ;
}
else
{
bEjectFirst = true ;
}
break ;
case SC_VOICEPAK :
if ( PAK_NONE = = g_pcControllers [ iControl ] . PakType )
{
EnterCriticalSection ( & g_critical ) ;
g_pcControllers [ iControl ] . PakType = PAK_VOICE ;
g_pcControllers [ iControl ] . fPakInitialized = false ;
LoadString ( g_hResourceDLL , IDS_P_VOICEPAK , pszMessage , ARRAYSIZE ( pszMessage ) ) ;
LeaveCriticalSection ( & g_critical ) ;
}
else
{
bEjectFirst = true ;
}
break ;
case SC_ADAPTPAK :
if ( PAK_NONE = = g_pcControllers [ iControl ] . PakType )
{
EnterCriticalSection ( & g_critical ) ;
g_pcControllers [ iControl ] . PakType = PAK_ADAPTOID ;
g_pcControllers [ iControl ] . fPakInitialized = false ;
LoadString ( g_hResourceDLL , IDS_P_ADAPTOIDPAK , pszMessage , ARRAYSIZE ( pszMessage ) ) ;
LeaveCriticalSection ( & g_critical ) ;
}
else
{
bEjectFirst = true ;
}
break ;
case SC_SWMEMRUMB :
bEjectFirst = true ;
if ( g_pcControllers [ iControl ] . PakType = = PAK_MEM )
{
iShortcut = PAK_RUMBLE ;
}
else
{
iShortcut = PAK_MEM ;
}
break ;
case SC_SWMEMADAPT :
bEjectFirst = true ;
if ( g_pcControllers [ iControl ] . PakType = = PAK_MEM )
{
iShortcut = PAK_ADAPTOID ;
}
else
{
iShortcut = PAK_MEM ;
}
break ;
default :
DebugWriteA ( " Invalid iShortcut passed to DoShortcut \n " ) ;
EnterCriticalSection ( & g_critical ) ;
g_pcControllers [ iControl ] . fPakInitialized = false ;
LeaveCriticalSection ( & g_critical ) ;
return ;
} // switch (iShortcut)
} // else if
2021-05-18 11:51:36 +00:00
// Let the game code re-initialize the pak
2020-02-09 11:36:49 +00:00
2021-05-18 11:51:36 +00:00
if ( bEjectFirst ) // We need to eject the current pack first; then set a DoShortcut to try again in 1 second
2020-02-09 11:36:49 +00:00
{
EnterCriticalSection ( & g_critical ) ;
g_pcControllers [ iControl ] . PakType = PAK_NONE ;
g_pcControllers [ iControl ] . fPakInitialized = false ;
LoadString ( g_hResourceDLL , IDS_P_SWITCHING , pszMessage , ARRAYSIZE ( pszMessage ) ) ;
LeaveCriticalSection ( & g_critical ) ;
LPMSHORTCUT lpmNextShortcut = ( LPMSHORTCUT ) P_malloc ( sizeof ( MSHORTCUT ) ) ;
if ( ! lpmNextShortcut )
return ;
lpmNextShortcut - > iControl = iControl ;
lpmNextShortcut - > iShortcut = iShortcut ;
CreateThread ( NULL , 0 , DelayedShortcut , lpmNextShortcut , 0 , NULL ) ;
2021-05-18 11:51:36 +00:00
iControl = - 2 ; // This is just a hack to get around the check that appends "Changing pak X to..."
2020-02-09 11:36:49 +00:00
}
if ( g_strEmuInfo . fDisplayShortPop & & _tcslen ( pszMessage ) > 0 )
{
if ( iControl > = 0 )
{
TCHAR tszNotify [ DEFAULT_BUFFER / 2 ] ;
LoadString ( g_hResourceDLL , IDS_POP_CHANGEPAK , tszNotify , ARRAYSIZE ( tszNotify ) ) ;
wsprintf ( g_pszThreadMessage , tszNotify , iControl + 1 , pszMessage ) ;
}
else
lstrcpyn ( g_pszThreadMessage , pszMessage , ARRAYSIZE ( g_pszThreadMessage ) ) ;
CreateThread ( NULL , 0 , MsgThreadFunction , g_pszThreadMessage , 0 , NULL ) ;
}
2016-01-27 09:11:59 +00:00
}
// Use string table refs to generate and throw an error message with title IDS_ERR_TITLE (see string table in resources)
// Also if compiled with DEBUG, will log the message with DebugWrite
2021-05-18 11:51:36 +00:00
// uID - The string table ref to display
// dwError - If nonzero, will display a Windows error message using FormatMessage (for use when an API function fails)
// fUserChoose - If true, display buttons Retry and Cancel. If false, display single button OK.
// for fUserChoose==true; ErrorMessage returns true if user selects Retry, and false if user selects Cancel.
// for fUserChoose==false; ErrorMessage always returns false.
2016-01-27 09:11:59 +00:00
bool ErrorMessage ( UINT uID , DWORD dwError , bool fUserChoose )
{
2020-02-09 11:36:49 +00:00
TCHAR pszFirstLine [ DEFAULT_BUFFER ] ;
bool fReturn = false ;
int iBytes ;
TCHAR szError [ 512 ] ;
TCHAR tszErrorTitle [ DEFAULT_BUFFER ] ;
LoadString ( g_hResourceDLL , uID , pszFirstLine , DEFAULT_BUFFER ) ;
LoadString ( g_hResourceDLL , IDS_ERR_TITLE , tszErrorTitle , DEFAULT_BUFFER ) ;
if ( dwError )
{
iBytes = wsprintf ( szError , _T ( " %s \n \n Error description: " ) , pszFirstLine ) ;
FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM , NULL , dwError , 0 , & szError [ iBytes ] , sizeof ( szError ) - iBytes , NULL ) ;
}
else
lstrcpyn ( szError , pszFirstLine , 512 ) ;
DebugWrite ( _T ( " ErrorMessage! ID:%d " ) , uID ) ;
DebugFlush ( ) ;
if ( fUserChoose )
fReturn = MessageBox ( g_strEmuInfo . hMainWindow , szError , tszErrorTitle , MB_RETRYCANCEL | MB_ICONERROR ) = = IDRETRY ;
else
MessageBox ( g_strEmuInfo . hMainWindow , szError , tszErrorTitle , MB_OK | MB_ICONERROR ) ;
DebugWriteA ( fReturn ? " (user: retry) \n " : " (user: acknowledge) \n " ) ;
return fReturn ;
2016-01-27 09:11:59 +00:00
}
// Post a message box, using string resource uTextID and MessageBox style uType
int WarningMessage ( UINT uTextID , UINT uType )
{
2020-02-09 11:36:49 +00:00
DebugWriteA ( " WarningMessage: ID:%d Type:%d \n " , uTextID , uType ) ;
DebugFlush ( ) ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
TCHAR tszTitle [ DEFAULT_BUFFER ] , tszText [ DEFAULT_BUFFER ] ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
LoadString ( g_hResourceDLL , uTextID , tszText , DEFAULT_BUFFER ) ;
LoadString ( g_hResourceDLL , IDS_DLG_WARN_TITLE , tszTitle , DEFAULT_BUFFER ) ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
return MessageBox ( g_strEmuInfo . hMainWindow , tszText , tszTitle , uType ) ;
2016-01-27 09:11:59 +00:00
}
2021-05-18 11:51:36 +00:00
/*
H . Morii
MsgThreadFunction is used because the SetTimer function relies
on the WM_TIMER message which is low priority and will not be
executed if other windows messages are frequently dispatched .
*/
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
DWORD WINAPI MsgThreadFunction ( LPVOID lpParam )
2016-01-27 09:11:59 +00:00
{
2020-02-09 11:36:49 +00:00
HWND hMessage = CreateWindowEx ( WS_EX_NOPARENTNOTIFY | WS_EX_STATICEDGE | WS_EX_TOPMOST , _T ( " STATIC " ) , NULL , WS_CHILD | WS_VISIBLE , 10 , 10 , 200 , 40 , g_strEmuInfo . hMainWindow , NULL , g_strEmuInfo . hinst , NULL ) ;
2021-05-18 11:51:36 +00:00
// Prepare the screen to bitblt
2020-02-09 11:36:49 +00:00
RECT rt ;
GetClientRect ( hMessage , & rt ) ;
HDC hdc = GetDC ( hMessage ) ;
HDC memdc = CreateCompatibleDC ( hdc ) ;
HBITMAP hbitmap = CreateCompatibleBitmap ( hdc , rt . right - rt . left , rt . bottom - rt . top ) ;
SelectObject ( memdc , hbitmap ) ;
2021-05-18 11:51:36 +00:00
// Draw some things here, like choosing fonts, painting the background, etc. here
2020-02-09 11:36:49 +00:00
FillRect ( memdc , & rt , ( HBRUSH ) ( COLOR_WINDOW + 1 ) ) ;
DrawText ( memdc , ( LPCTSTR ) lpParam , - 1 , & rt , DT_WORDBREAK ) ;
2021-05-18 11:51:36 +00:00
// bitblt to kingdom come
2020-02-09 11:36:49 +00:00
for ( int i = 0 ; i < 60 ; i + + )
{
Sleep ( 16 ) ; // 1/60 second
BitBlt ( hdc , rt . left , rt . top , rt . right - rt . left , rt . bottom - rt . top , memdc , 0 , 0 , SRCCOPY ) ;
}
2021-05-18 11:51:36 +00:00
// Cleanup
2020-02-09 11:36:49 +00:00
DeleteObject ( hbitmap ) ;
DeleteDC ( memdc ) ;
ReleaseDC ( hMessage , hdc ) ;
2021-05-18 11:51:36 +00:00
// Can only destroy windows created from the owner thread
2020-02-09 11:36:49 +00:00
DestroyWindow ( hMessage ) ;
return 0 ;
2016-01-27 09:11:59 +00:00
}
// This function is called as a thread by DoShortcut in order to tell the plugin to insert a pak shortly
// (usually after we've just removed whatever pak was there)
DWORD WINAPI DelayedShortcut ( LPVOID lpParam )
{
2020-02-09 11:36:49 +00:00
LPMSHORTCUT sc = ( LPMSHORTCUT ) lpParam ;
2021-05-18 11:51:36 +00:00
if ( sc & & sc - > iShortcut ! = SC_SWMEMRUMB & & sc - > iShortcut ! = SC_SWMEMADAPT ) // Don't allow recursion into self, it would cause a deadlock
2020-02-09 11:36:49 +00:00
{
2021-05-18 11:51:36 +00:00
Sleep ( 1000 ) ; // Sleep a little bit before calling DoShortcut again
2020-02-09 11:36:49 +00:00
DoShortcut ( sc - > iControl , sc - > iShortcut ) ;
}
P_free ( lpParam ) ;
return 0 ;
2016-01-27 09:11:59 +00:00
}