elishacloud / dxwrapper

Fixes compatibility issues with older games running on Windows 10/11 by wrapping DirectX dlls. Also allows loading custom libraries with the file extension .asi into game processes.
zlib License
1.24k stars 90 forks source link

[The Godfather] VP6 video player crash #11

Closed ThirteenAG closed 6 years ago

ThirteenAG commented 6 years ago

Crash happens slightly after video starts playing on vista+ systems. Works on windows xp.

From my observations, something happens with video frames. Debugging shows that this is the last function executed (0x8C7520):

int __thiscall VP6_CODEC_INTERNAL::GetFrameFromList(int this)
{
  int v1; // edx
  bool v2; // zf
  int result; // eax
  int v4; // edx
  int v5; // esi
  int *v6; // edx
  int **v7; // esi
  int v8; // ecx

  v1 = *(_DWORD *)(this + 44);
  if ( v1 == this + 44 )
  {
    sub_957C80("VP6_CODEC_INTERNAL::GetFrameFromList() out of frames did you call CODEC::ReleaseFrame( FRAME *Frame )\n");
    result = 0;
  }
  else
  {
    v2 = v1 == 8;
    result = v1 - 8;
    v4 = *(_DWORD *)v1;
    v5 = *(_DWORD *)(result + 0xC);
    *(_DWORD *)v5 = v4;
    *(_DWORD *)(v4 + 4) = v5;
    *(_DWORD *)(result + 8) = 0xB;
    *(_DWORD *)(result + 12) = 0xB;
    if ( v2 )
      v6 = 0;
    else
      v6 = (int *)(result + 8);
    v7 = *(int ***)(this + 40);
    v8 = this + 36;
    *v7 = v6;
    *(_DWORD *)(v8 + 4) = v6;
    v6[1] = (int)v7;
    *v6 = v8;
    *(_DWORD *)(result + 16) = 1;
  }
  return result;
}

Basically, on windows xp, ( v1 == this + 44 ) never happens and everything goes fine, on win10 for example, first 1 or 2 frames gets processed like usual, then on 3rd frame v1 equals (this + 44) for some reason and it crashes.

Since it works on winxp, I'm not sure what kind of "bug" to look for. To avoid crash you can rename movies folder or write 0x75 at 0x8C7B13. That will skip movies.

I also tried to modify that VP6_CODEC_INTERNAL::GetFrameFromList function, so it always uses first frame pointer, that resulted in black screen appearing instead of video and audio worked fine.

Another way to get black screen with audio is to use shim:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Direct3D\Shims\EnableOverlays]
"Z:\\WFP\\Games\\The Godfather\\godfather.exe"=dword:00000001

That obviously won't work with any kind of windowed mode.

Blackbird88 commented 6 years ago

Another interesting thing to add. If you use DxWnd and set Emulation in DirectX tab to Primary Buffer and then alt+tab quickly between desktop and the game you can see new frames I recorded it (yes had to use phone) https://www.youtube.com/watch?v=EGv6gIoRYNI

CookiePLMonster commented 6 years ago

This shim seems to re-enable DDCAPS_OVERLAY which got disabled in Windows 8 and newer. Looks like the game cannot handle the case where it's not present correctly (it assumes failure, and then it stops releasing frames, leading to this crash).

Removing this check or applying this shim restores to Vista/7 behaviour of no movies displayed (looks like despite what some people say, movies probably never crashed on Vista and 7). Looks like the only hope for this is to replace directdraw hardware overlays used in there with d3d9 hardware overlays, introduced in Windows 7. No clue how complex this task is.

Blackbird88 commented 6 years ago

This was fixed in SilentPatchGF!

elishacloud commented 6 years ago

Wow, great work! I am going to have to see if any of the things done there would be useful for dxwrapper.

CookiePLMonster commented 6 years ago

Feel free! Do note I initially wanted this to be a generic wrapper, but in the end I settled for a game specific solution. I'll also be publishing a rather extensive port mortem in a few days so I'll explain what exactly what wrong and how I approached the fix.

EDIT: Also note I'm aware of at least one incosistency with my "overlay emulator" - while I believe DirectDraw keeps the overlay posted after the first "Show" call and the subsequent ones are only acting as "update" calls, my implementation deletes and re-posts overlay from the render queue every time. While it is probably not a correct behaviour, it doesn't matter in the case of Godfather.

CookiePLMonster commented 6 years ago

@elishacloud Finally published an article on the fix: https://cookieplmonster.github.io/2018/05/18/fixing-the-godfather.html

Perhaps this could be useful for dxwrapper, but for that you'd need to interoperate ddraw and d3d9 somehow...

elishacloud commented 6 years ago

Thanks @CookiePLMonster! Awesome write-up! I already have ddraw and d3d9 wrappers in dxwrapper.

BTW: I also have create full generic wrappers for DirectX1-9 here. This includes, ddraw, d3d8, d3d9, dsound, dinput and dinput8.

CookiePLMonster commented 6 years ago

Handy! Would have been useful for a Far Cry fix since writing those wrappers is fairly boring and repeatable.