Game Accessibility Library logo SourceForge.net Logo
Game Accessibility Suite: GASPilotDLL/GASPilotDLL.cpp Source File

GASPilotDLL.cpp

Go to the documentation of this file.
00001 /// \file GASPilotDLL.cpp
00002 /// \brief Entry Point file for GASPilotDLL
00003 /// \ingroup GASPilotDLL
00004 ///
00005 /// Injected library for adjusting the speed within DirectX/DirectSound games.
00006 ///
00007 /// Copyright (c) 2007-2008 by Michael Ellison.
00008 /// See COPYING.txt for the \ref gaslicense License (MIT License).
00009 /// \sa gaslicense
00010 ///
00011 // $Author: mikeellison $
00012 // $Date: 2008-01-31 09:36:59 -0600 (Thu, 31 Jan 2008) $
00013 // $Revision:   $
00014 // $NoKeywords: $
00015 
00016 #include "GASPilotDLL.h"
00017 
00018 #define CAT_WM_FASTER WM_USER + 555
00019 #define CAT_WM_SLOWER WM_USER + 556
00020 
00021 unsigned int _stdcall InjectedThreadFunc(void *param);
00022 
00023 HANDLE  gThreadHandle = 0;
00024 HANDLE  gExitEvent    = 0;
00025 HMODULE gInstance     = 0;
00026 int     gPID          = 0;
00027 wchar_t gEventName[64];
00028 
00029 BOOL WINAPI DllMain(  HINSTANCE hinstDLL,
00030                       DWORD fdwReason,
00031                       LPVOID lpvReserved)
00032 {
00033     switch (fdwReason)
00034     {
00035         case DLL_PROCESS_ATTACH:
00036             {   
00037                 gPID           = _getpid();
00038                 gInstance      = hinstDLL;
00039                 swprintf(gEventName,64,L"GASPilotDLL_%d",gPID);
00040                 gExitEvent     = ::CreateEvent(0,TRUE,0,gEventName);                
00041                 
00042                 // Start up our injected thread.
00043                 unsigned int threadId = 0;
00044                 gThreadHandle  = (HANDLE)_beginthreadex(0,0,InjectedThreadFunc,0,0,&threadId);
00045                 ::OutputDebugString(L"GASPilotDLL injected.\n");
00046             }
00047             break;
00048 
00049         case DLL_PROCESS_DETACH:                  
00050             // If the thread hasn't been exited already from other methods, it's getting
00051             // nuked as we speak and won't receive an event even if we fire it.
00052             if (gExitEvent)
00053             {
00054                 ::CloseHandle(gExitEvent);            
00055                 gExitEvent = 0;
00056             }
00057             if (gThreadHandle)
00058             {
00059                 ::CloseHandle(gThreadHandle);
00060             }
00061             ::OutputDebugString(L"GASPilotDLL exiting.\n");            
00062             break;
00063     }
00064 
00065     return TRUE;
00066 }
00067 
00068 unsigned int _stdcall InjectedThreadFunc(void *param)
00069 {   
00070     ::OutputDebugString(L"Starting InjectedThread.\n");
00071     GASPilotDLL* gp = new GASPilotDLL();
00072     gp->Run();
00073     delete gp;
00074     ::OutputDebugString(L"Exiting InjectedThread.\n");
00075     ::FreeLibraryAndExitThread(gInstance,-1);
00076     return 0;
00077 }
00078 
00079 
00080 extern "C"
00081 {
00082     void*        gPassedData    = 0;
00083     unsigned int gPassedDataLen = 0;
00084     void _declspec(dllexport) UnpatchProcess(void*        passData,
00085                                              unsigned int passDataLen,
00086                                              void*        startLoc,
00087                                              unsigned int patchSize,
00088                                              void*        hostBuffer,
00089                                              unsigned int hostProcId)
00090     {
00091         // Open host process and read the stored bytes into our 
00092         // hooked process over the start location, restoring the
00093         // original executable code.        
00094         HANDLE hostProcess = ::OpenProcess(PROCESS_ALL_ACCESS,
00095                                            FALSE,
00096                                            hostProcId);
00097         if (hostProcess != 0)
00098         {
00099             DWORD amountRead = 0;
00100             ::ReadProcessMemory(hostProcess,hostBuffer,startLoc,patchSize,&amountRead);
00101             if (passDataLen && passData)
00102             {
00103                 gPassedDataLen = passDataLen;
00104                 gPassedData = new BYTE[passDataLen];
00105                 ::ReadProcessMemory(hostProcess,passData,gPassedData,passDataLen,&amountRead);
00106             }
00107             CloseHandle(hostProcess);
00108         }
00109 
00110         // Got the data from the parent. Open the wait event and set it.
00111         wchar_t eventName[64];
00112         wsprintf(eventName,L"UnpatchProcess_%d",hostProcId);
00113         HANDLE waitEvent = ::OpenEvent(GENERIC_READ|GENERIC_WRITE,FALSE,eventName);
00114         if (waitEvent != 0)
00115         {
00116             ::SetEvent(waitEvent);
00117             CloseHandle(waitEvent);
00118         }
00119 
00120         // Now, restore the stack and go back to the starting location                
00121         __asm
00122         {         
00123             mov esp,ebp  // Get original base pointer
00124             pop ebp      
00125             add esp,0x1c // Restore stack point to start of our code
00126             popfd
00127             popad
00128             ret
00129         }
00130     }
00131 }
00132 
00133 GASPilotDLL::GASPilotDLL()
00134 {
00135     fSpeed        = 1.00;
00136     fExecCap      = 0;
00137     fTimeWarp     = 0;
00138     fOverlayDX9   = 0;
00139     fOverlayGL    = 0;
00140     fSpeedImg     = 0;
00141     fSlowImg      = 0;
00142     fFastImg      = 0;
00143     fSpeedImg     = 0;
00144 
00145     ::GetModuleFileName(0,fHostApp.GetUnicodeBuffer(1024),1023);
00146     fHostApp.ReleaseBuffer();
00147 
00148     ::GetModuleFileName(gInstance,fDLLPath.GetUnicodeBuffer(1024),1023);
00149     fDLLPath.ReleaseBuffer();
00150     fBasePath = fDLLPath.GetDriveDirectory();
00151     fSlowImgPath = fBasePath;
00152     fSlowImgPath << L"Overlays\\GameSpeed_e.png";    
00153     fFastImgPath = fBasePath;
00154     fFastImgPath << L"Overlays\\GameSpeed_f.png";
00155 
00156     fFastImg = 0;
00157     fSlowImg = 0;
00158 
00159     CATStreamFile *imgStream = new CATStreamFile();
00160     CATResult result = imgStream->Open(fSlowImgPath,CATStream::READ_ONLY);
00161     if (CATSUCCEEDED(result))
00162     {
00163         result = CATImage::Load(imgStream,fSlowImg);
00164         imgStream->Close();
00165     }
00166     if (CATFAILED(result))
00167     {
00168         ::OutputDebugString(L"Failed to load speed image");
00169         ::OutputDebugString(fSlowImgPath);        
00170     }
00171     
00172     result = imgStream->Open(fFastImgPath,CATStream::READ_ONLY);
00173     if (CATSUCCEEDED(result))
00174     {
00175         result = CATImage::Load(imgStream,fFastImg);
00176         imgStream->Close();
00177     }
00178 
00179     if (CATFAILED(result))
00180     {
00181         ::OutputDebugString(L"Failed to load speed image");
00182         ::OutputDebugString(fFastImgPath);
00183     }
00184 
00185     delete imgStream;
00186 }
00187 
00188 GASPilotDLL::~GASPilotDLL()
00189 {
00190     if (fOverlayDX9)
00191         fOverlayDX9->RestoreAll();
00192     
00193     if (fTimeWarp)
00194         fTimeWarp->RestoreAll();
00195 
00196     if (fExecCap)
00197         fExecCap->RestoreAll();
00198 
00199     if (fOverlayGL)
00200         fOverlayGL->RestoreAll();
00201 
00202     delete fOverlayDX9;
00203     fOverlayDX9 = 0;
00204 
00205     delete fTimeWarp;
00206     fTimeWarp = 0;
00207 
00208     delete fExecCap;
00209     fExecCap = 0;
00210     
00211     delete fOverlayGL;
00212     fOverlayGL = 0;
00213 
00214     if (fSpeedImg)
00215         CATImage::ReleaseImage(fSpeedImg);
00216     if (fFastImg)
00217         CATImage::ReleaseImage(fFastImg);
00218     if (fSlowImg)
00219         CATImage::ReleaseImage(fSlowImg); 
00220 }
00221 
00222 void GASPilotDLL::Run()
00223 {   
00224     CATUInt32 hookFlags = this->CheckHooks();
00225 
00226 
00227     if (hookFlags & CATHook_Exec)
00228     {
00229         fExecCap = new CATInjectionPropagate(fDLLPath);
00230         fExecCap->HookFunctions();
00231     }
00232     
00233     if (hookFlags & CATHook_OGL)
00234     {
00235         fOverlayGL    = new CATOverlayOpenGL();
00236         fOverlayGL->HookFunctions();
00237     }
00238 
00239     if (hookFlags & CATHook_DX9)
00240     {
00241         fOverlayDX9   = new CATOverlayDirect3D9();
00242         fOverlayDX9->HookFunctions();
00243     }
00244 
00245     if (hookFlags & CATHook_Time)
00246     {
00247         fTimeWarp     = new CATTimeWarp();
00248         fTimeWarp->HookFunctions();
00249     }
00250     
00251 
00252     CATFloat32 speed         = 1.0f;
00253     CATInt32   logoWidth     = 60;
00254 
00255     if (fTimeWarp)
00256         fTimeWarp->GetSpeed();
00257 
00258     CATRect rect(0,0,360,50);
00259     if (fFastImg)
00260     {
00261         fOverlayDX9->SetOverlayImage(fFastImg,rect,1024,768);
00262         fOverlayGL->SetOverlayImage(fFastImg,rect,1024,768);            
00263     }
00264 
00265 
00266     // Create a message window for others to post to
00267     WNDCLASS msgWndClass;
00268     memset(&msgWndClass,0,sizeof(msgWndClass));
00269     HINSTANCE instance = gInstance;
00270     msgWndClass.style           = 0;
00271     msgWndClass.lpfnWndProc     = (WNDPROC)EventMsgProc;
00272     msgWndClass.cbClsExtra      = 0;
00273     msgWndClass.cbWndExtra      = 0;
00274     msgWndClass.hInstance       = instance;
00275     msgWndClass.hIcon           = 0;
00276     msgWndClass.hbrBackground   = 0;
00277     msgWndClass.lpszMenuName    = 0;    
00278     msgWndClass.lpszClassName   = L"GASPilotDLLWnd";    
00279     ATOM classAtom = RegisterClass(&msgWndClass);
00280     ::RegisterClass(&msgWndClass);
00281 
00282     fMsgWnd = ::CreateWindowEx(0,L"GASPilotDLLWnd",L"GASPilotMsgs",0,0,0,0,0,0,0,gInstance,this);
00283 
00284 
00285     
00286     for (; ;)
00287     {
00288         // This needs to be put in a hook instead.... asynckeystate not reliable.
00289         DWORD waitResult = MsgWaitForMultipleObjects(1, 
00290                                                      &gExitEvent,
00291                                                      FALSE,
00292                                                      150,
00293                                                      QS_ALLINPUT);
00294 
00295         if (waitResult == WAIT_OBJECT_0)
00296         {
00297             break;
00298         } 
00299         else if (WAIT_TIMEOUT == waitResult)
00300         {
00301             CheckKeys();
00302         }
00303         else
00304         {
00305             MSG msg;
00306             while (PeekMessage(&msg, fMsgWnd, 0, 0, PM_REMOVE)) 
00307             { 
00308                 CheckKeys();
00309                 TranslateMessage(&msg);
00310                 DispatchMessage(&msg);
00311             }
00312         }
00313     }
00314 
00315     DestroyWindow(fMsgWnd);
00316     UnregisterClass(L"GASPilotDLLWnd",gInstance);
00317 
00318 }
00319 
00320 LRESULT CALLBACK GASPilotDLL::EventMsgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
00321 {
00322     GASPilotDLL* pilot = 0;
00323 
00324     if (message == WM_CREATE)
00325     {
00326         LPCREATESTRUCT lpc = (LPCREATESTRUCT)lParam;
00327         SetWindowLong(hWnd,GWL_USERDATA,PtrToUlong(lpc->lpCreateParams));
00328         SetWindowPos(hWnd,0,0,0,0,0,SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOZORDER|SWP_NOSIZE);
00329         pilot = (GASPilotDLL*)lpc->lpCreateParams;        
00330     }
00331     else
00332     {
00333         pilot = (GASPilotDLL*)(ULONG_PTR)GetWindowLong(hWnd,GWL_USERDATA);
00334     }
00335 
00336     switch (message)
00337     {
00338         case CAT_WM_SLOWER:
00339             pilot->OnSlower();
00340             break;
00341         case CAT_WM_FASTER:
00342             pilot->OnFaster();
00343         default:
00344             break;
00345     }
00346     
00347     return DefWindowProc(hWnd,message,wParam,lParam);
00348 }
00349 
00350 void GASPilotDLL::OnFaster()
00351 {
00352     fSpeed += 0.05f;
00353     UpdateSpeed();
00354 }
00355 void GASPilotDLL::OnSlower()
00356 {
00357     fSpeed -= 0.05f;
00358     UpdateSpeed();
00359 }
00360 
00361 void GASPilotDLL::OnFast()
00362 {
00363     fSpeed = 1.0f;
00364     UpdateSpeed();
00365 }
00366 void GASPilotDLL::OnSlow()
00367 {
00368     fSpeed = 0.4f;
00369     UpdateSpeed();
00370 }
00371 
00372 void GASPilotDLL::UpdateSpeed()
00373 {
00374      if (fSpeed > 1.0f)
00375         fSpeed = 1.0f;   
00376     if (fSpeed < 0.2f)
00377         fSpeed = 0.2f;
00378 
00379    if (fFastImg && fSlowImg)
00380     {
00381         CATImage* newSpeedImage = 0;
00382         CATImage::CopyImage(fSlowImg,newSpeedImage);
00383         
00384         CATRect rect(0,0,360,50);
00385         
00386         if (newSpeedImage)
00387         {
00388             newSpeedImage->Overlay(fFastImg,0,0,0,0,60 + (fFastImg->Width()-60)*fSpeed,fFastImg->Height());
00389             if (fOverlayDX9)
00390                 fOverlayDX9->SetOverlayImage(newSpeedImage,rect,1024,768);
00391             if (fOverlayGL)
00392                 fOverlayGL->SetOverlayImage(newSpeedImage,rect,1024,768);
00393         }
00394 
00395         if (fSpeedImg != 0)
00396             CATImage::ReleaseImage(fSpeedImg );
00397         fSpeedImg = newSpeedImage;
00398     }
00399 
00400    CATString speedStr;
00401    speedStr.Format(L"Setting speed to %f\n",fSpeed);
00402    ::OutputDebugString(speedStr);
00403 
00404    if (fTimeWarp)
00405         fTimeWarp->SetSpeed(fSpeed);
00406 
00407     ::InvalidateRect(::GetForegroundWindow(),0,FALSE);
00408 }
00409 
00410 CATUInt32 GASPilotDLL::CheckHooks()
00411 {
00412     if (fHostApp.IsEmpty())
00413         return CATHook_All;
00414     
00415     /// \todo
00416     /// Add XML exception list to exclude hooking of launch apps
00417     CATString appName = fHostApp.GetFilenameNoExt();
00418     if (0 == appName.CompareNoCase(L"steam"))
00419     {
00420         return CATHook_None;   
00421     }
00422 
00423     return CATHook_All;
00424 }
00425 
00426 void GASPilotDLL::CheckKeys()
00427 {
00428     bool nextDown = (0x8000 & GetAsyncKeyState(VK_NEXT));
00429     bool prevDown = (0x8000 & GetAsyncKeyState(VK_PRIOR));
00430     bool f13      = (0x8000 & GetAsyncKeyState(VK_F13));
00431     bool f14      = (0x8000 & GetAsyncKeyState(VK_F14));
00432 
00433     if (nextDown)
00434     {
00435         this->OnSlower();
00436     }
00437     
00438     if (prevDown)
00439     {
00440         this->OnFaster();
00441     }
00442 
00443     if (f13)    
00444         this->OnSlow();
00445     
00446     if (f14)
00447         this->OnFast();
00448 }

Generated on Mon Feb 11 04:09:59 2008 for Game Accessibility Suite by doxygen 1.5.4