2016-01-27 09:11:59 +00:00
/*
N - Rage ` s Dinput8 Plugin
( C ) 2002 , 2006 Norbert Wladyka
Author ` s Email : norbert . wladyka @ chello . at
Website : http : //go.to/nrage
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 .
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 .
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
*/
# include "commonIncludes.h"
# include <windows.h>
# include <Commctrl.h>
# include <dinput.h>
# include <Xinput.h>
# include "NRagePluginV2.h"
# include "Interface.h"
# include "FileAccess.h"
# include "PakIO.h"
# include "DirectInput.h"
# include "International.h"
# include "Version.h"
// ProtoTypes //
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 ) ;
// Global Variables //
HMODULE g_hDirectInputDLL = NULL ; // Handle to DirectInput8 library
HMODULE g_hXInputDLL = NULL ; // Handle to XInput Library
HMODULE g_hResourceDLL = NULL ; // Handle to resource library; used by LoadString for internationalization
HANDLE g_hHeap = NULL ; // Handle to our heap
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] --rabid
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
CRITICAL_SECTION g_critical ; // our critical section semaphore
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;
// this is so we get consistent sample rates on our mouse.
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?
bool g_bExclusiveMouse = true ; // Do we have an exclusive mouse lock? defaults to true unless we have no bound mouse buttons/axes
CONTROLLER g_pcControllers [ 4 ] ; // Our four N64 controllers, connected or otherwise
SHORTCUTS g_scShortcuts ;
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
TCHAR g_pszThreadMessage [ DEFAULT_BUFFER ] = _T ( " " ) ;
BOOL APIENTRY DllMain ( HINSTANCE hModule , DWORD ul_reason_for_call , LPVOID lpReserved )
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH :
DisableThreadLibraryCalls ( hModule ) ;
if ( ! prepareHeap ( ) )
return FALSE ;
DebugWriteA ( " *** DLL Attach ( " VER_FILE_VERSION_STR " -Debugbuild | built on " __DATE__ " at " __TIME__ " ) \n " ) ;
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 ;
g_strEmuInfo . fDisplayShortPop = true ; // display pak switching message windows by default
# ifdef _UNICODE
{
g_strEmuInfo . Language = GetLanguageFromINI ( ) ;
if ( g_strEmuInfo . Language = = 0 )
{
g_strEmuInfo . Language = DetectLanguage ( ) ;
DebugWriteA ( " Autoselect language: %d \n " , g_strEmuInfo . Language ) ;
}
g_hResourceDLL = LoadLanguageDLL ( g_strEmuInfo . Language ) ; // HACK: it's theoretically not safe to call LoadLibraryEx from DllMain... --rabid
if ( g_hResourceDLL = = NULL )
{
g_strEmuInfo . Language = 0 ;
g_hResourceDLL = hModule ;
DebugWriteA ( " couldn't load language DLL, falling back to defaults \n " ) ;
}
}
# else
DebugWriteA ( " (compiled in ANSI mode, language detection DISABLED.) \n " ) ;
g_strEmuInfo . Language = 0 ;
g_hResourceDLL = hModule ;
# endif // #ifndef _UNICODE
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
DebugWriteA ( " *** DLL Detach \n " ) ;
CloseDebugFile ( ) ; // Moved here from CloseDll
DeleteCriticalSection ( & g_critical ) ;
// Moved here from CloseDll... Heap is created from DllMain,
// and now it's destroyed by DllMain... just safer code --rabid
if ( g_hHeap ! = NULL )
{
HeapDestroy ( g_hHeap ) ;
g_hHeap = NULL ;
}
break ;
}
return TRUE ;
}
/******************************************************************
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 stucture that needs to be
filled by the function . ( see def above )
output : none
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL GetDllInfo ( PLUGIN_INFO * PluginInfo )
{
DebugWriteA ( " CALLED: GetDllInfo \n " ) ;
# ifdef _DEBUG
sprintf ( PluginInfo - > Name , " N-Rage For PJ64 (Debug): %s " , VER_FILE_VERSION_STR ) ;
# else
sprintf ( PluginInfo - > Name , " N-Rage For PJ64: %s " , VER_FILE_VERSION_STR ) ;
# endif
PluginInfo - > Type = PLUGIN_TYPE_CONTROLLER ;
PluginInfo - > Version = SPECS_VERSION ;
}
/******************************************************************
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL DllAbout ( HWND hParent )
{
DebugWriteA ( " CALLED: DllAbout \n " ) ;
TCHAR tszTitle [ DEFAULT_BUFFER ] , tszTranslator [ DEFAULT_BUFFER ] ;
LoadString ( g_hResourceDLL , IDS_DLG_ABOUT_TITLE , tszTitle , DEFAULT_BUFFER ) ;
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 " ) \
_T ( " Transferpak emulation done by MadManMarkAu \n " ) \
_T ( " Cleanup, tweaks, and language support by RabidDeity \n " ) ;
LoadString ( g_hResourceDLL , IDS_DLG_ABOUT , tszTranslator , DEFAULT_BUFFER ) ;
_tcscat ( szText , tszTranslator ) ;
MessageBox ( hParent , szText , tszTitle , MB_OK | MB_ICONINFORMATION ) ;
return ;
}
/******************************************************************
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL DllConfig ( HWND hParent )
{
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 ( ) )
{
//TODO Disable ability to set XInput
//TODO Make XInput and DirectInput settings same page
}
}
if ( g_pDIHandle & & ! g_bConfiguring )
{
g_bConfiguring = true ;
if ( ! bInitCC )
{
INITCOMMONCONTROLSEX ccCtrls = { sizeof ( INITCOMMONCONTROLSEX ) ,
ICC_BAR_CLASSES | ICC_TAB_CLASSES | ICC_LISTVIEW_CLASSES } ;
InitCommonControlsEx ( & ccCtrls ) ; // needed for TrackBars & Tabs
}
EnterCriticalSection ( & g_critical ) ;
if ( g_sysMouse . didHandle )
{ // unlock mouse while configuring
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 ) ;
// If we go into the dialog box, and the user navigates to the Rumble window, our FF device can get unacquired.
// So let's reinit them now if we're running, just to be safe --rabid
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.
// This is undesirable behavior, but it beats the alternative (and we REALLY need to re-init FF devices here)
PrepareInputDevices ( ) ;
if ( iOK )
{
InitiatePaks ( false ) ; // only re-init the mempaks and such if the user clicked Save or Use
}
if ( g_sysMouse . didHandle )
{
if ( g_bExclusiveMouse )
{ // if we have exclusive mouse, we need to relock mouse after closing the config
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 ;
}
/******************************************************************
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL DllTest ( HWND hParent )
{
DebugWriteA ( " CALLED: DllTest \n " ) ;
return ;
}
// It's easier to maintain one version of this, as not much really changes
// between versions. --rabid
/******************************************************************
Function : InitiateControllers
Purpose : This function initialises 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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
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
)
{
DebugWriteA ( " CALLED: InitiateControllers \n " ) ;
if ( ! prepareHeap ( ) )
return ;
# 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
// UNDONE: Instead of just storing the header, figure out what ROM we're running and save that information somewhere
// The emulator expects us to tell what controllers are plugged in and what their paks are at this point.
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
{
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 ( ) )
{
//TODO Disable ability to set XInput
//TODO Make XInput and DirectInput settings same page
}
}
//To handle XInput controllers better, we need to set id to 0
iXinputControlId = 0 ;
int iDevice ;
EnterCriticalSection ( & g_critical ) ;
// ZeroMemory( g_apFFDevice, sizeof(g_apFFDevice) ); // NO, we'll reinit the existing reference if it's already loaded
// ZeroMemory( g_apdiEffect, sizeof(g_apdiEffect) ); // NO, we'll release it with CloseControllerPak
for ( int i = 3 ; i > = 0 ; i - - )
{
SaveControllerPak ( i ) ;
CloseControllerPak ( i ) ;
// freePakData( &g_pcControllers[i] ); // already called by CloseControllerPak
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 ) ;
}
// Init: Find force-feedback devices and init
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 ) ;
continue ;
}
// Search for right Controller
iDevice = FindDeviceinList ( g_pcControllers [ i ] . guidFFDevice ) ;
if ( iDevice ! = - 1 & & g_devList [ iDevice ] . bEffType )
{
DebugWriteA ( " rumble device set, " ) ;
}
else // we couldn't find the device specified in the INI file, or it was already null
{
g_pcControllers [ i ] . guidFFDevice = GUID_NULL ;
DebugWriteA ( " no rumble device/effect type set, " ) ;
}
if ( g_pcControllers [ i ] . nModifiers > 0 )
SetModifier ( & g_pcControllers [ i ] ) ;
g_iFirstController = i ;
DebugWriteA ( " plugged in, with paktype %d, " , g_pcControllers [ i ] . PakType ) ;
DebugWriteA ( " RawMode is %d \n " , g_pcControllers [ i ] . fRawData ) ;
}
else
{
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 --rabid
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 ;
} // end InitiateControllers
/******************************************************************
Function : RomOpen
Purpose : This function is called when a rom is open . ( from the
emulation thread )
input : none
output : none
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL RomOpen ( void )
{
DebugWriteA ( " CALLED: RomOpen \n " ) ;
//XInputEnable( TRUE ); // enables xinput --tecnicors
if ( ! g_strEmuInfo . fInitialisedPlugin )
{
ErrorMessage ( IDS_ERR_NOINIT , 0 , false ) ;
return ;
}
EnterCriticalSection ( & g_critical ) ;
// re-init our paks and shortcuts
InitiatePaks ( true ) ;
// LoadShortcuts( &g_scShortcuts ); WHY are we loading shortcuts again?? Should already be loaded!
LeaveCriticalSection ( & g_critical ) ;
g_bRunning = true ;
return ;
}
/******************************************************************
Function : RomClosed
Purpose : This function is called when a rom is closed .
input : none
output : none
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL RomClosed ( void )
{
int i ;
//XInputEnable( FALSE ); // disables xinput --tecnicors
DebugWriteA ( " CALLED: RomClosed \n " ) ;
EnterCriticalSection ( & g_critical ) ;
if ( g_sysMouse . didHandle )
g_sysMouse . didHandle - > SetCooperativeLevel ( g_strEmuInfo . hMainWindow , DIB_KEYBOARD ) ; // unlock the mouse, just in case
for ( i = 0 ; i < ARRAYSIZE ( g_pcControllers ) ; + + i )
{
if ( g_pcControllers [ i ] . pPakData )
{
SaveControllerPak ( i ) ;
CloseControllerPak ( i ) ;
}
// freePakData( &g_pcControllers[i] ); already done by CloseControllerPak --rabid
// DON'T free the modifiers!
// 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 ;
}
/******************************************************************
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL GetKeys ( int Control , BUTTONS * Keys )
{
# ifdef ENABLE_RAWPAK_DEBUG
DebugWriteA ( " CALLED: GetKeys \n " ) ;
# endif
if ( g_bConfiguring )
Keys - > Value = 0 ;
else
{
EnterCriticalSection ( & g_critical ) ;
if ( g_pcControllers [ Control ] . fPlugged )
{
if ( Control = = g_iFirstController )
{
GetDeviceDatas ( ) ;
CheckShortcuts ( ) ;
}
if ( g_pcControllers [ Control ] . fXInput ) // reads the xinput controller keys, if connected --tecnicors
GetXInputControllerKeys ( Control , & Keys - > Value ) ;
else
GetNControllerInput ( Control , & Keys - > Value ) ;
}
LeaveCriticalSection ( & g_critical ) ;
}
return ;
}
/******************************************************************
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 signalling 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 :
initilize controller : 01 03 00 FF FF FF
read controller : 01 04 01 FF FF FF FF
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL ControllerCommand ( int Control , BYTE * Command )
{
// We don't need to use this because it will be echoed immediately afterwards
// by a call to ReadController
return ;
}
/******************************************************************
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 signalling 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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL ReadController ( int Control , BYTE * Command )
{
# ifdef ENABLE_RAWPAK_DEBUG
DebugWriteA ( " CALLED: ReadController \n " ) ;
# endif
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 :
// expected: controller gets 1 byte (command), controller sends back 3 bytes
// should be: Command[0] == 0x01
// Command[1] == 0x03
# ifdef ENABLE_RAWPAK_DEBUG
WriteDatasA ( " GetStatus-PreProcessing " , Control , Command , 0 ) ;
# endif
Command [ 3 ] = RD_GAMEPAD | RD_ABSOLUTE ;
Command [ 4 ] = RD_NOEEPROM ;
if ( g_pcControllers [ Control ] . fN64Mouse ) // Is Controller a mouse?
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 ;
}
# ifdef ENABLE_RAWPAK_DEBUG
WriteDatasA ( " GetStatus-PostProcessing " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
# endif
break ;
case RD_READKEYS :
// expected: controller gets 1 byte (command), controller sends back 4 bytes
// should be: Command[0] == 0x01
// Command[1] == 0x04
if ( g_bConfiguring )
Command [ 3 ] = Command [ 4 ] = Command [ 5 ] = Command [ 6 ] = 0 ;
else
{
if ( Control = = g_iFirstController )
{
GetDeviceDatas ( ) ;
CheckShortcuts ( ) ;
}
if ( g_pcControllers [ Control ] . fXInput ) // reads xinput controller kesy, if connected --tecnicors
GetXInputControllerKeys ( Control , ( LPDWORD ) & Command [ 3 ] ) ;
else
GetNControllerInput ( Control , ( DWORD * ) & Command [ 3 ] ) ;
}
break ;
case RD_READPAK :
# ifdef ENABLE_RAWPAK_DEBUG
WriteDatasA ( " ReadPak-PreProcessing " , Control , Command , 0 ) ;
# endif
if ( g_pcControllers [ Control ] . fPakInitialized )
//Command[1] = Command[1] | ReadControllerPak( Control, &Command[3] );
ReadControllerPak ( Control , & Command [ 3 ] ) ;
else
{
DebugWriteA ( " Tried to read, but pak wasn't initialized!! \n " ) ;
// InitControllerPak( Control );
Command [ 1 ] | = RD_ERROR ;
//ZeroMemory( &Command[5], 32 );
}
# ifdef ENABLE_RAWPAK_DEBUG
WriteDatasA ( " ReadPak-PostProcessing " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
# endif
break ;
case RD_WRITEPAK :
# ifdef ENABLE_RAWPAK_DEBUG
WriteDatasA ( " WritePak-PreProcessing " , Control , Command , 0 ) ;
# endif
if ( g_pcControllers [ Control ] . fPakInitialized )
//Command[1] = Command[1] | WriteControllerPak( Control, &Command[3] );
WriteControllerPak ( Control , & Command [ 3 ] ) ;
else
{
DebugWriteA ( " Tried to write, but pak wasn't initialized! (paktype was %u) \n " , g_pcControllers [ Control ] . PakType ) ;
// InitControllerPak( Control );
Command [ 1 ] | = RD_ERROR ;
}
# ifdef ENABLE_PAK_WRITES_DEBUG
WriteDatasA ( " WritePak-PostProcessing " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
# endif
break ;
case RD_READEEPROM :
// Should be handled by the Emulator
WriteDatasA ( " ReadEeprom-PreProcessing " , Control , Command , 0 ) ;
WriteDatasA ( " ReadEeprom-PostProcessing " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
break ;
case RD_WRITEEPROM :
// Should be handled by the Emulator
WriteDatasA ( " WriteEeprom-PreProcessing " , Control , Command , 0 ) ;
WriteDatasA ( " WriteEeprom-PostProcessing " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
break ;
default :
// only accessible if the Emulator has bugs.. or maybe the Rom is flawed
WriteDatasA ( " ReadController: Bad read " , Control , Command , 0 ) ;
DebugWriteA ( NULL ) ;
Command [ 1 ] = Command [ 1 ] | RD_ERROR ;
}
LeaveCriticalSection ( & g_critical ) ;
return ;
}
/******************************************************************
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL WM_KeyDown ( WPARAM wParam , LPARAM lParam )
{
return ;
}
/******************************************************************
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL WM_KeyUp ( WPARAM wParam , LPARAM lParam )
{
return ;
}
/******************************************************************
Function : CloseDLL
Purpose : This function is called when the emulator is closing
down allowing the dll to de - initialise .
input : none
output : none
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXPORT void CALL CloseDLL ( void )
{ // HACK: THIS IS BROKEN IN PJ64 1.6 (it calls CloseDLL too often)
DebugWriteA ( " CALLED: CloseDLL \n " ) ;
if ( g_bRunning )
RomClosed ( ) ;
for ( int i = 0 ; i < 4 ; i + + )
{
freePakData ( & g_pcControllers [ i ] ) ;
freeModifiers ( & g_pcControllers [ i ] ) ;
}
// ZeroMemory( g_pcControllers, sizeof(g_pcControllers) ); // why zero the memory if we're just going to close down?
FreeDirectInput ( ) ;
FreeXinput ( ) ;
return ;
}
// Prepare a global heap. Use P_malloc and P_free as wrappers to grab/release memory.
bool prepareHeap ( )
{
if ( g_hHeap = = NULL )
g_hHeap = HeapCreate ( 0 , 4 * 1024 , 0 ) ;
return ( g_hHeap ! = NULL ) ;
}
// Frees pakdata memory (called more often than you'd think).
void freePakData ( CONTROLLER * pcController )
{
if ( pcController & & pcController - > pPakData )
{
P_free ( pcController - > pPakData ) ;
pcController - > pPakData = NULL ;
}
}
// Frees modifier memory
void freeModifiers ( CONTROLLER * pcController )
{
if ( pcController & & pcController - > pModifiers )
{
pcController - > nModifiers = 0 ;
P_free ( pcController - > pModifiers ) ;
pcController - > pModifiers = NULL ;
}
}
// 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 )
{
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 ;
}
// After enumerating DirectInput devices into g_devList, find a device by GUID. Finding similar devices is impossible.
int FindDeviceinList ( REFGUID rGUID )
{
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 ;
}
// Let's initialize all plugged in controllers' paks.
// Input: false means run InitControlPak on each plugged controller. Input true means just clear each pak's CRC error status?
// When we call this from RomOpen, it's true, otherwise (in DllConfig) it's false.
// Rather counterintuitive.
void InitiatePaks ( bool bInitialize )
{
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
{ // we only support "RAW mode" paks so this won't do much
; //if( g_pcControllers[i].PakType == PAK_RUMBLE )
// CreateEffectHandle( i, g_pcControllers[i].bRumbleTyp, g_pcControllers[i].bRumbleStrength );
}
}
}
}
// This used to be "NotifyEmulator" which was supposed to tell the emulator if we changed the way it sees controllers.
// Unfortunately the spec doesn't work that way. Fixed the func and changed the func name to something that makes more sense.
// FillControls takes a Controls array from InitiateControllers and fills it with what we know about whether
// a controller is plugged in, accepting raw data, and what type of pak is plugged in.
void FillControls ( CONTROL * Controls )
{
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 ;
Controls [ i ] . RawData = false ;
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 ;
}
}
}
// called after a poll to execute any shortcuts
void CheckShortcuts ( )
{
static bool bWasPressed [ sizeof ( SHORTCUTSPL ) / sizeof ( BUTTON ) ] [ 4 ] ;
static bool bMLWasPressed ; // mouselock
bool bMatching = false ;
if ( g_bConfiguring | | ! g_bRunning )
return ; // we don't process shortcuts if we're in a config menu or are not running emulation
// just process if key wasnt pressed before
for ( int i = 0 ; i < 4 ; i + + ) // controllers
{
for ( int j = 0 ; j < SC_TOTAL ; j + + )
{
bMatching = IsBtnPressed ( g_scShortcuts . Player [ i ] . aButtons [ j ] ) ;
if ( bMatching & & ! bWasPressed [ j ] [ i ] )
DoShortcut ( i , j ) ;
bWasPressed [ j ] [ i ] = bMatching ;
}
}
bMatching = IsBtnPressed ( g_scShortcuts . bMouseLock ) ;
if ( bMatching & & ! bMLWasPressed )
DoShortcut ( - 1 , - 1 ) ; // controller -1 means do mouselock shortcut
bMLWasPressed = bMatching ;
}
// Executes the shortcut iShortcut on controller iController
// Special case: if iPlayer is -1, run the mouselock shortcut
void DoShortcut ( int iControl , int iShortcut )
{
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 ;
g_pcControllers [ iControl ] . fPakInitialized = false ;
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
// let the game code re-init the pak.
if ( bEjectFirst ) // we need to eject the current pack first; then set a DoShortcut to try again in 1 second
{
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 ) ;
iControl = - 2 ; // this is just a hack to get around the check that appends "Changing Pak X to ..."
}
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 ) ;
}
}
// 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
// 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.
bool ErrorMessage ( UINT uID , DWORD dwError , bool fUserChoose )
{
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 ;
}
// Post a message box, using string resource uTextID and MessageBox style uType
int WarningMessage ( UINT uTextID , UINT uType )
{
DebugWriteA ( " WarningMessage: ID:%d Type:%d \n " , uTextID , uType ) ;
DebugFlush ( ) ;
TCHAR tszTitle [ DEFAULT_BUFFER ] , tszText [ DEFAULT_BUFFER ] ;
LoadString ( g_hResourceDLL , uTextID , tszText , DEFAULT_BUFFER ) ;
LoadString ( g_hResourceDLL , IDS_DLG_WARN_TITLE , tszTitle , DEFAULT_BUFFER ) ;
return MessageBox ( g_strEmuInfo . hMainWindow , tszText , tszTitle , uType ) ;
}
/* H.Morii <koolsmoky(at)users.sourceforge.net>
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 . */
DWORD WINAPI MsgThreadFunction ( LPVOID lpParam )
{
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 ) ;
/* prepare the screen to bitblt */
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 ) ;
/* draw some nice stuff.
choose fonts , paint the back ground , etc here . */
FillRect ( memdc , & rt , ( HBRUSH ) ( COLOR_WINDOW + 1 ) ) ;
DrawText ( memdc , ( LPCTSTR ) lpParam , - 1 , & rt , DT_WORDBREAK ) ;
/* bitblt to kingdom come */
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 ) ;
}
/* cleanup */
DeleteObject ( hbitmap ) ;
DeleteDC ( memdc ) ;
ReleaseDC ( hMessage , hdc ) ;
/* Can only destroy windows created from the owner thread */
DestroyWindow ( hMessage ) ;
return 0 ;
}
// 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 )
{
LPMSHORTCUT sc = ( LPMSHORTCUT ) lpParam ;
if ( sc & & sc - > iShortcut ! = SC_SWMEMRUMB & & sc - > iShortcut ! = SC_SWMEMADAPT ) // don't allow recursion into self, it would cause a deadlock
{
Sleep ( 1000 ) ; // sleep a little bit before calling DoShortcut again
DoShortcut ( sc - > iControl , sc - > iShortcut ) ;
}
P_free ( lpParam ) ;
return 0 ;
}