Sign in to follow this  
atomicJo

Render() / Update() Directx7's method hooking

Recommended Posts

Hi everybody, First of all congratulations for this amazing game ressource website! And here is my question / need of advice from experienced 3D developpers : I'm currently trying to developp a patch for an old DirectX7 game which doesn't run smoothly on recent hardware. I assume the game uses DirectX7 because it is required to play it, even though there is only a DDRAW.DLL loaded at starting (and no d3dx.dll, saw this with Dependency Walker), but it doesn't mean only Direct Draw functions are used by the engine isn't it? Also, I've heard the game engine didn't use hardware acceleration (although it's 3D) that's why I'm a little bit confused... Any idea on what technology is used ? Once I figure out what library is used (D3D7 or DDRAW) I will need to hook the refresh screen function. My investigations for Directx7 case, According to directx7.chm : Use the IDirect3DRMViewport::Render method to render the current scene into the current viewport. Use the IDirect3DRMDevice::Update method to copy the rendered image to the display. What function do you think I must hook to have a control on the application's framerate ? My hooking method which works fine with Dx8/9 applications consists in creating a Dx8/9 device in memory, get Present() function adress and replace it by the adress of one of my customs functions (and then call back the real Present() function). For this I will need Directx7 SDK (or just headers and lib files), do you think it's still possible to dowload it somewhere ? Cause I didn't find after hours of "googling" :( Thanks in advance for answers, please tell me if it isn't clear.

Share this post


Link to post
Share on other sites
Quote:
Original post by atomicJo
I'm currently trying to developp a patch for an old DirectX7 game which doesn't run smoothly on recent hardware.
I assume the game uses DirectX7 because it is required to play it, even though there is only a DDRAW.DLL loaded at starting (and no d3dx.dll, saw this with Dependency Walker), but it doesn't mean only Direct Draw functions are used by the engine isn't it?
With DX7 and earlier, you query the DirectDraw object for an IDirect3D object. The DLL will be loaded then (If there is one, I can't remember if the D3D code is inside ddraw.dll in DX7).

Quote:
Original post by atomicJo
Also, I've heard the game engine didn't use hardware acceleration (although it's 3D) that's why I'm a little bit confused...
Any idea on what technology is used ?
What game is it? It's possible it uses a software rasterizer, in which case it won't use D3D at all.

Quote:
Original post by atomicJo
Once I figure out what library is used (D3D7 or DDRAW) I will need to hook the refresh screen function.

My investigations for Directx7 case,
According to directx7.chm :

Use the IDirect3DRMViewport::Render method to render the current
scene into the current viewport.
Use the IDirect3DRMDevice::Update method to copy the rendered image
to the display.

What function do you think I must hook to have a control on the application's framerate ?
Those are only for D3D retained mode, which is a very dumbed down version of D3D. Most games would use D3D immediate mode. The actual frame rate is likely controlled by the app itself, unrelated to the rendering, or it's controlled by the vertical sync, in which case you may be able to override that (Again, I don't remember how that's done in DX7, but you'll need to find out what exactly it's rendering with first).

Quote:
Original post by atomicJo
My hooking method which works fine with Dx8/9 applications consists in
creating a Dx8/9 device in memory, get Present() function adress and
replace it by the adress of one of my customs functions (and then call
back the real Present() function).

For this I will need Directx7 SDK (or just headers and lib files), do
you think it's still possible to dowload it somewhere ? Cause I didn't find after hours of "googling" :(
Just the headers should do. Your method of hooking should also work, provided you can find the correct function(s) to hook.

There's another thread that may be of interest to you going on at the moment too.

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
What game is it? It's possible it uses a software rasterizer, in which case it won't use D3D at all.

The game is Outcast


Quote:
Original post by Evil Steve
Quote:
Original post by atomicJo
Once I figure out what library is used (D3D7 or DDRAW) I will need to hook the refresh screen function.

My investigations for Directx7 case,
According to directx7.chm :

Use the IDirect3DRMViewport::Render method to render the current
scene into the current viewport.
Use the IDirect3DRMDevice::Update method to copy the rendered image
to the display.

What function do you think I must hook to have a control on the application's framerate ?
Those are only for D3D retained mode, which is a very dumbed down version of D3D. Most games would use D3D immediate mode. The actual frame rate is likely controlled by the app itself, unrelated to the rendering, or it's controlled by the vertical sync, in which case you may be able to override that (Again, I don't remember how that's done in DX7, but you'll need to find out what exactly it's rendering with first).


Then if the game uses immediate mode, what would be the refresh / update function to hook ? (I mean the equivalent of Dx8/9's Present() function )
I found this article on immediate mode (thx GameDev^^), I can't see such a function in the render loop (step 11).
There is no vertical sync in the game, so refresh rate only depends on how fast your computer render the next frame, doesn't it? though, I don't understand why you told it is controlled by the app itself ?!


Quote:
Original post by Evil Steve
Just the headers should do. Your method of hooking should also work, provided you can find the correct function(s) to hook.

There's another thread that may be of interest to you going on at the moment too.


Ok, any idea where could I find those headers ? and how are they named ? d3d7.h ? d3d7types.h ?

Thanks for the link I already read it!

Share this post


Link to post
Share on other sites
A while back I did some directx9 hooking w/o having the sdk handy. I used the wine headers as a reference.

I just did a quick google and came across what I think you need at http://source.winehq.org/source/include/ddraw.h?v=wine-1.0#L1764

According to the DirectX7 SDK help a call to DirectDrawCreateEx is used to create an instance of the IDirectDraw7 interface. I did a quick look through the wine ddraw.h header and it looks like the necessary CLSID and IID GUIDs for DX7 are in there.

I think you should be able to tell if the app uses IM or RM by whether it requires the d3drm.dll runtime. There were some useful RM functions for loading and such that may also have been used while still using IM for rendering though.

And IIRC, there was no dxutil runtime lib for dx7. All of the util code was given to you via the framework source. But it's been a long time so I can't be sure.

Share this post


Link to post
Share on other sites
Quote:
Original post by atomicJo
Then if the game uses immediate mode, what would be the refresh / update function to hook ? (I mean the equivalent of Dx8/9's Present() function )
I found this article on immediate mode (thx GameDev^^), I can't see such a function in the render loop (step 11).
There is no vertical sync in the game, so refresh rate only depends on how fast your computer render the next frame, doesn't it? though, I don't understand why you told it is controlled by the app itself ?!
From a quick look at some videos on Google, it looks like the games uses D3D certainly, and I'd assume immediate mode (As redllar said, you can see if it depends on d3drm.dll to check).
What exactly do you want to happen? There's two common ways to write a game loop:
Speed controlled by V-sync:

while(running)
{
process_logic();
render();
swap_buffers(); // Waits for vertical sync, so 60 FPS on 60Hz monitor
}


Game logic running independently of frame rate:

while(running)
{
start_time = time();
process_logic(delta_time); // Process delta_time worth of game updates
render();
swap_buffers();
delta_time = time() - start_time;
}


The first one will run at 60 FPS on a 60Hz monitor (Or a multiple thereof), and if you disable v-sync it'll run as fast as your PC can manage. In the second case, the game will execute at the same rate no matter what the monitor refresh rate is, and disabling v-sync won't make any difference.

You can't control how fast the game executes (without modifying the game EXE) unless it's controlled by v-sync. If that's the case, you can enable or disable v-sync by overriding the IDirectDrawSurface7::Flip function, and specifying DDFLIP_DONOTWAIT as the last parameter.

Share this post


Link to post
Share on other sites
Hi, thanks for the answers.
I've been working on it and here is what I found :
- there is no apparent vertical sync settings but it seems like the framerate is limited to 60fps (saw with a fps counter software)
- I launched the game in profiling mode through dependency walker to have a look at dynamics dependencies, d3dim.dll is loaded, no sign of d3drm.
I didn't find any mention of the IDirectDrawSurface7::Flip function (the one I'd like to hook) in DDRAW called functions menus of dependency walker, does it necessarily mean that the game doesn't use it ?

I found an old DirectX SDK from 2006 which contains all headers and lib files I need (ddraw.h, ddraw.lib and d3d.h) so I started creating ddraw interface. Apparently there are 2 differents methods :
- the first uses DirectDrawCreate function, I followed the D3D immediate mode article mentionned above. Here is my sample code which works creating a ddraw surface :


HRESULT hr;
LPDIRECTDRAW lpDD;
hr = DirectDrawCreate(NULL, &lpDD, NULL);
hr = lpDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);

LPDIRECTDRAWSURFACE lpDDSFront;
DDSURFACEDESC ddsd;
ZeroMemory( &ddsd, sizeof(DDSURFACEDESC) );
ddsd.dwSize = sizeof(DDSURFACEDESC);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

hr = lpDD->CreateSurface(&ddsd, &lpDDSFront, NULL);




Problem : I can't add a backbuffer if I choose DDSCL_NORMAL as parameter for SetCooperativeLevel, I must set DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN to get it working, that's weird ?!

- the second method uses DirectDrawCreateEx function (which is more recent), I followed the DirectX7.chm tutorial for immediate mode :


HRESULT hr;
IDirectDraw7 *g_pDD;
IDirect3D7 *g_pD3D;
hr = DirectDrawCreateEx( NULL, (VOID**)&g_pDD, IID_IDirectDraw7, NULL );



This sample code does compile, but I have an unresolved external symbol _IID_IDirectDraw7 at linking, I don't understand why because this GUID is defined in ddraw.h : DEFINE_GUID( IID_IDirectDraw7, 0x15e65ec0,0x3b9c,0x11d2,0xb9,0x2f,0x00,0x60,0x97,0x97,0xea,0x5b );
and ddraw.lib linked in my Visual project.

One last thing, I noticed there are calls of those 2 functions (DirectDrawCreate and DirectDrawCreateEx) in dependency walkers when I launch the profiling mode, I didn't think the same app could use both ?!

PS : where is the edit toolbar ? can't find a clean way to display code

[Edited by - atomicJo on March 2, 2010 9:02:25 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by atomicJo
I didn't find any mention of the IDirectDrawSurface7::Flip function (the one I'd like to hook) in DDRAW called functions menus of dependency walker, does it necessarily mean that the game doesn't use it ?
The function isn't exported directly, you access it through the virtual function table of IDirectDrawSurface7. It won't show up in depends; just like how a single function in a DLL that returns a struct with 3000 function pointers in it will only show up as a single function export.

Quote:
Original post by atomicJo
I found an old DirectX SDK from 2006 which contains all headers and lib files I need (ddraw.h, ddraw.lib and d3d.h) so I started creating ddraw interface. Apparently there are 2 differents methods :
- the first uses DirectDrawCreate function, I followed the D3D immediate mode article mentionned above. Here is my sample code which works creating a ddraw surface :

[snip]

Problem : I can't add a backbuffer if I choose DDSCL_NORMAL as parameter for SetCooperativeLevel, I must set DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN to get it working, that's weird ?!
It is - what is the return code from the function that fails (As a 8 digit hex number preferably)?

Quote:
Original post by atomicJo
- the second method uses DirectDrawCreateEx function (which is more recent), I followed the DirectX7.chm tutorial for immediate mode :

This sample code does compile, but I have an unresolved external symbol _IID_IDirectDraw7 at linking, I don't understand why because this GUID is defined in ddraw.h : DEFINE_GUID( IID_IDirectDraw7, 0x15e65ec0,0x3b9c,0x11d2,0xb9,0x2f,0x00,0x60,0x97,0x97,0xea,0x5b );
and ddraw.lib linked in my Visual project.
You need to link to dxguid.lib (Or it might be dxguids.lib, I forget which), or #define INITGUID (or INIT_GUID, again, I forget) in ONE source file before any other #includes.

Quote:
Original post by atomicJo
One last thing, I noticed there are calls of those 2 functions (DirectDrawCreate and DirectDrawCreateEx) in dependency walkers when I launch the profiling mode, I didn't think the same app could use both ?!
The code could use both, but it's unlikely that it uses both at the same time. For instance, it may use DirectDrawCreate to create a DirectDraw object to enumerate display modes, then use DirectDrawCreateEx to create an object for the game to use. It does seem strange though - if you hook both, are they both actually called?

Quote:
Original post by atomicJo
PS : where is the edit toolbar ? can't find a clean way to display code
There is none. The tag you want is [ source ] [ /source ] (Lower case), or for short snippets, you can use [ code ]


The difference between DirectDrawCreate and DirectDrawCreateEx is that the former creates a version 1 DirectDraw interface, and the latter creates a version 7 interface. Prior to DX7, you had to use DirectDrawCreate, and then QueryInterface on the returned interface to query for DirectDraw6 (Or whatever version) support. DirectDrawCreateEx is a helper function which just does that internally (to all intents and purposes).

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Quote:
Original post by atomicJo
- the second method uses DirectDrawCreateEx function (which is more recent), I followed the DirectX7.chm tutorial for immediate mode :

This sample code does compile, but I have an unresolved external symbol _IID_IDirectDraw7 at linking, I don't understand why because this GUID is defined in ddraw.h : DEFINE_GUID( IID_IDirectDraw7, 0x15e65ec0,0x3b9c,0x11d2,0xb9,0x2f,0x00,0x60,0x97,0x97,0xea,0x5b );
and ddraw.lib linked in my Visual project.
You need to link to dxguid.lib (Or it might be dxguids.lib, I forget which), or #define INITGUID (or INIT_GUID, again, I forget) in ONE source file before any other #includes.


Thanks a lot, no more linking problems ! (it was dxguid.lib)



Quote:
Original post by Evil Steve
Quote:
Original post by atomicJo
I found an old DirectX SDK from 2006 which contains all headers and lib files I need (ddraw.h, ddraw.lib and d3d.h) so I started creating ddraw interface. Apparently there are 2 differents methods :
- the first uses DirectDrawCreate function, I followed the D3D immediate mode article mentionned above. Here is my sample code which works creating a ddraw surface :

[snip]

Problem : I can't add a backbuffer if I choose DDSCL_NORMAL as parameter for SetCooperativeLevel, I must set DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN to get it working, that's weird ?!
It is - what is the return code from the function that fails (As a 8 digit hex number preferably)?


Here is my source code to create primary surface (with double buffering) :

LPDIRECTDRAWSURFACE7 lpDDSFront, lpDDSBack;
// obtain the primary surface
DDSURFACEDESC2 ddsd; DDSCAPS2 ddscaps;
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_3DDEVICE;
ddsd.dwBackBufferCount = 1;
hr = g_pDD->CreateSurface(&ddsd,&lpDDSFront,NULL);//create primary surface
if( FAILED( hr ) )
{
char *ch; ch = new char[50]; itoa(HRESULT_CODE(hr),ch,10);
MessageBox( hWnd, ch, "ddraw primary surface error code :", MB_OK );
return false;
}






Error code is 225, according to ddraw headers it means :
/*
* Operation requires the application to have exclusive mode but the
* application does not have exclusive mode.
*/
And if I set Excusive Mode (in SetCooperativeLevel param) I must add Fullscreen Mode too.


I'm going to hook Flip function (once I get double buffering working) from a surface created with DirectDrawCreateEx at first, and if it doesn't get called I'll try with DirectDrawCreate.
One thing I noticed, I can create my ddraw surfaces even before querying DirectDraw for access to Direct3D, so it mustn't be necessary to create that D3Ddevice to hook ddraw flip function ?!

Share this post


Link to post
Share on other sites
Quote:
Original post by atomicJo
Problem : I can't add a backbuffer if I choose DDSCL_NORMAL as parameter for SetCooperativeLevel, I must set DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN to get it working, that's weird ?!
I don't remember enough about DirectDraw to answer that I'm afraid (And I don't have the documentation handy, and it's not on the MSDN). Is this not just a limitation of DirectDraw? If you have a backbuffer, you need to be in exclusive mode?

Quote:
Original post by atomicJo
I'm going to hook Flip function (once I get double buffering working) from a surface created with DirectDrawCreateEx at first, and if it doesn't get called I'll try with DirectDrawCreate.
You'll probably find that only one of the interfaces is actually used to create any surfaces. My bet is on the one from DirectDrawCreateEx.

Quote:
Original post by atomicJo
One thing I noticed, I can create my ddraw surfaces even before querying DirectDraw for access to Direct3D, so it mustn't be necessary to create that D3Ddevice to hook ddraw flip function ?!
Direct3D just renders to a surface, and then you flip the surface. You shouldn't need to go near Direct3D for this to work - which has the nice side effect that your code will probably work on plain DirectDraw games, not just D3D games.

Share this post


Link to post
Share on other sites
Okay, I finally managed to make it works !! A big thx for all the tips and advices.

I did hook the Flip() function both using DirectDrawCreate and DirectDrawCreateEx functions, they produce the same results, the primary surfaces created (LPDIRECTDRAWSURFACE for the first, LPDIRECTDRAWSURFACE7 for the other) have exactly the same vtable so Flip() function has the same relative adress.
Plus I din't even have to create a backbuffer!
Here is the code for each case :

DirectDrawCreate :

LPDIRECTDRAW lpDD;
DirectDrawCreate(NULL, &lpDD, NULL);
lpDD->SetCooperativeLevel(hWnd,DDSCL_NORMAL);
LPDIRECTDRAWSURFACE lpDDSFront;
// create Primary surface
DDSURFACEDESC ddsd;
ZeroMemory( &ddsd, sizeof(DDSURFACEDESC) );
ddsd.dwSize = sizeof(DDSURFACEDESC);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
lpDD->CreateSurface(&ddsd, &lpDDSFront, NULL);




DirectDrawCreateEx :

IDirectDraw7 *g_pDD;
IDirect3D7 *g_pD3D;
DirectDrawCreateEx( NULL, (VOID**)&g_pDD, IID_IDirectDraw7, NULL );
g_pDD->SetCooperativeLevel( hWnd, DDSCL_NORMAL );
LPDIRECTDRAWSURFACE7 lpDDSFront;
// obtain the primary surface
DDSURFACEDESC2 ddsd;
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
g_pDD->CreateSurface(&ddsd,&lpDDSFront,NULL);




Once I get lpDDSFront pointer, Flip() is the 11th function in the vtable.

I won't finish my patch cause some people find a better way to fix bugs by modifying executable.

But if I havthe time, I'll probably contribute to Taksi project (http://taksi.sourceforge.net/) by adding Dx7 support.

Don't know if this works with plain DDraw games, I have some which do not. I think they use another refresh display function, maybe the Blt() :
g_pddsPrimary->Blt( &g_rcScreenRect, g_pddsBackBuffer,&g_rcViewportRect, DDBLT_WAIT, NULL );

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this