This patch is really sketchy but it worked for me so I'll share -> Theory behind the NT Fix for Final Liberation by AQRIT ---------------------------------------------------------- The game crashes when it is minimized... If we pause execution of the game in some way while it is minimized we can prevent the game from doing whatever it is doing that causes a crash? so hook PeekMessage() and watch for a message thats says 'hey minimize your window' then keep the game trapped in an infinte loop until a message comes across that says 'hey restore your window' if "Run this program in compatibility mode:" ... is enabled the PeekMessage IAT is hook by AcLayers pre-runtime... so after disabling compatibility mode... Start looking at the messages returned by PeekMessage there is no WM_SIZE message coming across? (is this message being eaten/redirected to directX?) (or is it being sent directly to the wndproc and not being posted?) So instead watch for a WM_PALETTECHANGED message to signal that the window has been minimized --- and watch for a WM_SYSCOMMAND specifing SC_RESTORE to signal that the window should be restored which means if you choose the 'bring to front' option in task manager the program will still be frozen after its restored... :( // call IDirectDraw::TestCooperativeLevel if Interface v4? --- // here is my PeekMessageA Hook for testing purposes bool __stdcall PeekMsgHook( MSG * pMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg) { bool bResult; if(bResult = PeekMsg( pMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg )){ if(pMsg->message == WM_PALETTECHANGED){ // if minimizing while(true){ // trap in an infinite loop DispatchMsg(pMsg); Snooze(100); if(bResult = PeekMsg(pMsg,0,0,0,1)){ if(pMsg->message == WM_SYSCOMMAND) if(pMsg->wParam == SC_RESTORE) return bResult; // escape from loop } } } } return bResult; } The game itself no longer crashes when flipping between windows but when the DoVideo option is re-enabled the movies no longer play its just a blank screen... So I broke out my debugger :) //// revised 07/09 //// // I ended up looking at a call the game makes to ddrawSurface->Lock() // // The game checks the returned value of this function // against 887601C2 ( DDERR_SURFACELOST ) // and calls ddrawSurface->Restore() if DDERR_SURFACELOST A quick hack to get the movies working is to patch the code when SmackOpen() is called changing the test against 887601C2 to a test against some value other than zero and 887601C2 ( like 0xDEADBEEF ) and restoring the orginal test when SmackClosed() is called... of course when writting a release I don't want to be hooking the IAT and I don't want to be writting to write protected memory... I need to know when a movie is playing and when its not... so I fire up a memory scanner and find a memory location which appears to signify whether or not a movie is playing and I make an assumption based on its value :P The patch code was written byte-by-byte so the source isn't much to look at :) but here it is anyways: ------------------------------------------------------------------------- /*** hook test of PeekMessage return value: ***/ 004561AD 0F85 736C0200 JNZ Epic40k.0047CE26 (ESP is a pointer to a MSG struct) /*** Msg hook proc (in code cave): ***/ 0047CE26 817C24 04 11030000 CMP DWORD PTR SS:[ESP+4],311 ; WM_PALETTECHANGED 0047CE2E 75 3B JNZ SHORT Epic40k.0047CE6B 0047CE30 54 PUSH ESP 0047CE31 E8 44FDFFFF CALL <JMP.&USER32.TranslateMessage> ; call 0047CB7A 0047CE36 54 PUSH ESP 0047CE37 E8 38FDFFFF CALL <JMP.&USER32.DispatchMessageA> ; call 0047CB74 0047CE3C 6A 64 PUSH 64 0047CE3E E8 4FFDFFFF CALL <JMP.&KERNEL32.Sleep> ; call 0047CB92 0047CE43 8BC4 MOV EAX,ESP 0047CE45 6A 01 PUSH 1 0047CE47 6A 00 PUSH 0 0047CE49 6A 00 PUSH 0 0047CE4B 6A 00 PUSH 0 0047CE4D 50 PUSH EAX 0047CE4E E8 2DFDFFFF CALL <JMP.&USER32.PeekMessageA> ; call 0047CB80 0047CE53 84C0 TEST AL,AL 0047CE55 74 E5 JE SHORT Epic40k.0047CE3C 0047CE57 817C24 04 12010000 CMP DWORD PTR SS:[ESP+4],112 ; WM_SYSCOMMAND 0047CE5F 75 CF JNZ SHORT Epic40k.0047CE30 0047CE61 817C24 08 20F10000 CMP DWORD PTR SS:[ESP+8],0F120 ; SC_RESTORE 0047CE69 75 C5 JNZ SHORT Epic40k.0047CE30 0047CE6B E9 9094FDFF JMP Epic40k.00456300 81 7C 24 04 11 03 00 00 75 3B 54 E8 44 FD FF FF 54 E8 38 FD FF FF 6A 64 E8 4F FD FF FF 8B C4 6A 01 6A 00 6A 00 6A 00 50 E8 2D FD FF FF 84 C0 74 E5 81 7C 24 04 12 01 00 00 75 CF 81 7C 24 08 20 F1 00 00 75 C5 E9 90 94 FD FF ----------------------------------------------------------------------------------------------- /* Hook test of DDRAW->Lock() return value */ /*** Hook Code: ***/ 00453D0F E8 5C920200 CALL Epic40k.0047CF70 /*** Hook Proc (in code cave): ***/ 0047CF70 50 PUSH EAX ; preserve EAX (for now :p) 0047CF71 8BC4 MOV EAX,ESP ;;; 0047CF73 83C0 04 ADD EAX,4 ;;; this is so we have a 0047CF76 8B00 MOV EAX,DWORD PTR DS:[EAX] ;;; relative address... 0047CF78 05 F0030600 ADD EAX,603F0 ;;; 004B4104 - video playing flag? 0047CF7D 8338 01 CMP DWORD PTR DS:[EAX],1 ; test if a movie is playing 0047CF80 58 POP EAX ; restore eax 0047CF81 74 06 JE SHORT Epic40k.0047CF89 ; if a movie is playing jump over the next two lines 0047CF83 3D C2017688 CMP EAX,887601C2 ; else do the orginal code (which was overwritten) 0047CF88 C3 RETN ; return to normal program flow 0047CF89 58 POP EAX ; kill the return address 0047CF8A E9 AA6DFDFF JMP Epic40k.00453D39 ; transfer flow to play the movie code... 50 8B C4 83 C0 04 8B 00 05 F0 03 06 00 83 38 01 58 74 06 3D C2 01 76 88 C3 58 E9 AA 6D FD FF