Personal projects, programming experiments, hobbies, and random discoveries

DirectX8/9 Proxy-DLL: Concept

Introduction

Ever wanted to paint your own graphics (or text) on top of a DirectX application (e.g., to show TeamSpeak informations, or a self-created map, within a game )?

Then, the solution discussed later on might be of help for you. By using a “proxy-dll”, calls to DirectX can be intercepted, data altered and even new objects can be created and be shown within another application.

The different topics of this section on mikoweb.eu cover the creation of a basic proxy-dll for DirectX8/9 3D-calls (d3d8.dll / d3d9.dll), including the full source. Based on your imagination and knowledge, you might enhance this for your very own applications and needs.

In case you like to use a “ready made solution”, check out the GPP-Package at the download page. Please note that the source for this one is also available.

Don’t hesitate to send in feedback and examples of how you did achieve certain goals by using the discussed concept. I’m quite sure some things can be done better, and I’d be happy to present your input here.

As a teaser, here is a picture of a fast’n dirty implementation of a 3D model into the proxy-dll, directly taken from the screen (testing application: Krakout-Arkanoid – DX8). The yellow rectangle in the upper left corner and the cube in the middle aren’t part of the original game, they were created within the proxy-dll and “added”.

Btw, Andrey A. Ugolnik, the author of Krakout RE (the game I used a lot for testing the DX8 proxy) contacted me and offered a free license key. This is a nice move, really. Check out other games and stuff by him here:
http://www.ugolnik.info
I’m proud that Codeguru (https://www.codeguru.com) accepted my article about the proxy-dll concept (it seems to be offline by now, tho). It was mainly a compilation of information available on these very pages.

The Concept

To obtain DirectX functionality, an application needs to load certain dynamic link libraries (dlls), e.g. d3d8.dll (d3d9.dll respectively). A “proxy-dll” names exactly like a dll in question and manages to get loaded by the app instead of the “real, original” one. Once loaded, the proxy loads the “real” dll by itself and passes all calls from the application on to it.

So, a “filter” is installed between the application and its calls to regular dll functions. Obviously, new own functionality can be added now when intercepting (even altering/faking) certain function calls.

By the way, this concept is very similar to one used by a Microsoft DirectX programming utility, named D3DSpy. Quote from the D3DSpy help file (DX9 here):
D3DSpy works by providing a proxy DLL, which the application connects to and treats like the real D3D9.DLL. It intercepts all the calls and gathers various information before making the real D3D call.
The proxy-concept doesn’t work with all applications due to several reasons. It has to be tested thoroughly with an application (game) before making it available to a wider range of users.

There are other approaches on how to draw own stuff in a DirectX application (like “run-time code-injection”, or even altering the code of an executable file). In case the proxy-dll concept doesn’t work for you, you may want to look for one of those.

Realisation

We focus on DirectX8/9 3D calls here. This said, we need to create a d3d8.dll (d3d9.dll) that will be called by the application instead of the “real” (system-provided) library.

Note: For testing the proxy-dll, I used Krakout-Arkanoid (DX8) and Flatspace Demo (DX9), amongst others. Both are nice games, and they were at hand that time. For debugging purposes, DebugView is a nice tool. Ah, and check out dependency walker for the examination of dlls.

First, let’s take a look at an application that uses DirectX 3D calls. It enables D3D support by loading the d3d8.dll, then obtains the IDirect3D8 interface via Direct3DCreate8 and creates a IDirect3DDevice8 with CreateDevice).

If we can manage to get our d3d8.dll/d3d9.dll loaded by the application, thus getting the call to Direct3DCreate8/Direct3DCreate9 redirected to our code, we could create a IDirect3D8/9 Interface object by ourselves. Same goes for the IDirect3DDevice8/9 Interface. We would then offer all funcionality like the original objects to the caller’s code (in fact just redirecting every call to a IDirect3D/IDirect3DDevice we secretetly created of our own).
This gives us the possibility to change data and/or add new things.
Any drawbacks on that ? Yes.

Normally, an application looks for a dll within its working directory first, then browsing the system’s path. Applications may prevent that by directly targetting the system directory. And we can’t just replace the system’s d3d8/9.dll, as we need its functions as described above (btw., I strongly advise to
not touch the original system dll. You may screw your DirectX installation).

Furthermore, an application might implement its own e.g. texture manager (or even use undocumented calls). As we will not be able to handle such calls (obviously), the app would possibly not work with a proxy-dll.

Finally, we know what we need…

  • a file named d3d8.dll/d3d9.dll that will be placed within the application’s working directory (and hopefully get loaded by the app).
  • a function named “Direct3DCreate8” being exposed by that dll (“Direct3DCreate9” respectively).
  • an interface provided by the dll that exactly looks the same as the “real” IDirect3D8/9 interface
  • an interface provided by the dll that exactly looks the same as the “real” IDirect3DDevice interface

Luckily enough, some header files that come with the DX SDK point out what calls should be handled by our “faked” interfaces.

So, my “basic proxy-dll” implementation consists of

  • global routines for the dll handling (entry point, load the system d3d8.dll/d3d9.dll, and so on)
  • an object called myIDirect3D8/9, derived from Direct3D8/9, exposing over a dozen functions
  • an object called myIDirect3DDevice8/9, derived from IDirect3DDevice8/9, exposing about 100 functions

Basically, all functions within the “my…” objects pass parameters on to the original system dll, providing the possibility to change or add data. A few routines need special attention though.

Release (both Objects)
In case the application releases the interface (and no more references are present), we destroy our own object as well.

QueryInterface (both Objects)
If the application queries for certain interfaces, we do of course pass the address of our very own routine, not passing the system-dll response this time.

myIDirect3D8/9::CreateDevice
Once this is called, we create our own D3DDevice (internally), as we need it to process the application calls. Then, we respond in sending our own object’s address.

For testing purposes, the function myIDirect3DDevice8/9::ShowWeAreHere was added. It is called within myIDirect3DDevice8/9::Present and does nothing but create a yellow rectangle in the upper left screen. This might give you an impression of how to add own content. Drawing own real 3D Objects is possible as well, believe me (if you can handle the application-internal world coordinates, hehe).
As it turned out on later tests, some games (like Tomb Raider – Legend) use IDirect3DSwapChain9::Present for showing their screens, never calling IDirect3DDevice9::Present (as e.g. World of Warcraft does).
Obviously, a proxy-dll has to cover IDirect3DSwapChain9 then, too. An updated code example has beed submitted to the downloads page. Check for the Basic d3d8/9.dll Implementation section.

DirectX8/9 Proxy-DLL: Usage Examples

HowTo 1: Show an ingame info screen (a simple bitmap)

Okay, you got familiar with the proxy-dll concept so far ? Fine. In this HowTo, we’ll try to add an “ingame info screen” to a DirectX 9 application (I use the Flatspace Demo for testing). Please note that you need the DX9 SDK with the Summer2004 update.
Based on the “basic proxy-dll implementation” (see downloads section), we’ll add a partly transparent texture and show it ingame. For the sake of simplicity, I do not cover switching this feature on or off using a keyboard hook. It is always on.

  • To keep things a bit straight, we add a new class CmyOwnStuff to the project, and delete the myIDirect3DDevice9::ShowWeAreHere routine (and the call within ::Present, of course). Ensure the CmyOwnStuff receives a pointer to the dll-internal IDirect3DDevice9 and stores it.
  • Create a fancy 256×128 bitmap (*.bmp), and paint all soon-to-be transparent areas as black (0/0/0). Add this bitmap as a resource to your project.
  • To show this bitmap ingame, we’ll create a ID3DXSprite Object. Using textured triangles would work aswell. I just like the Sprite thingy, though.
  • Add a ID3DXSprite* to your CmyOwnStuff. Create the Sprite in the Constructor, and release it within the Destructor.
  • Add a IDirect3DTexture9* to CmyOwnStuff. Load your fancy bitmap from the resource, while in the Constructor, and release it within the Destructor.
  • Add a public CmyOwnStuff::Render routine. Here we’ll draw the Sprite using the ID3DXSprite::Draw method.
  • Now we need to ensure that our sprite object is drawn every time the routine myIDirect3DDevice9::Present is called. So, add an CmyOwnStuff pointer to myIDirect3DDevice9. Create the CmyOwnStuff Object once (I do this within ::Present), and delete it withinmyIDirect3DDevice9::Release. Don’t forget to set the OwnStuff pointer to NULL in the myIDirect3DDevice9 Constructor.
  • Hang on, work is nearly finished. As we created the texture for the background as D3DPOOL_MANAGED (believe me, we did), there is no need to take care of it if the DirectX device is in lost state (e.g. when someone tabs out). We need to cover the sprite object, though. So, add two new public routines to CmyOwnStuff: OnLostDevice and OnResetDevice. Call the corresponding routines from ID3DXSprite there. Ensure that CmyOwnStuff::OnLostDevice and CmyOwnStuff::OnResetDevice get called frommyIDirect3DDevice9 (I use the ::Reset for this). Another way (and probably less prone to errors) is to create the ID3DXSprite Object on every render pass (and delete it afterwards). This increases impact on the fps, though.

Well, basically, this is the way I chose to add own content to a DirectX application. You may want to add several security checks. Remember if something fails within your added code, the application will most probably break, leaving a lot of memory garbage on the system.

Code fragments as discussed above (the project source is available at the downloads page):
// myOwnStuff.h
#pragma once
#include <d3dx9core.h>

class CmyOwnStuff
{
public:
CmyOwnStuff(IDirect3DDevice9 *pD3DDevice);
~CmyOwnStuff(void);
void Render(void);
void OnLostDevice(void);
void OnResetDevice(void);
private:
IDirect3DDevice9* m_pD3DDevice;
ID3DXSprite* m_pBackgroundSprite;
IDirect3DTexture9* m_pBackgroundTexture;
};
// myOwnStuff.cpp
#include "StdAfx.h"
#include ".\myownstuff.h"

CmyOwnStuff::CmyOwnStuff(IDirect3DDevice9 *pD3DDevice)
{
extern HINSTANCE gl_hThisInstance;
m_pD3DDevice = pD3DDevice;
m_pBackgroundSprite = NULL;
m_pBackgroundTexture = NULL;

D3DXCreateSprite(m_pD3DDevice,&m_pBackgroundSprite);
D3DXCreateTextureFromResourceEx(m_pD3DDevice,
gl_hThisInstance,
MAKEINTRESOURCE(IDB_BITMAP1),
256,
128,
0,
NULL,
D3DFMT_UNKNOWN,
D3DPOOL_MANAGED,
D3DX_FILTER_NONE,
D3DX_FILTER_NONE,
0xFF000000,
NULL,
NULL,
&m_pBackgroundTexture);
}

CmyOwnStuff::~CmyOwnStuff(void)
{
if (m_pBackgroundSprite) m_pBackgroundSprite->Release();
if (m_pBackgroundTexture) m_pBackgroundTexture->Release();
}

void CmyOwnStuff::Render(void)
{
m_pD3DDevice->BeginScene();
m_pBackgroundSprite->Begin(NULL);
m_pBackgroundSprite->Draw(m_pBackgroundTexture,
NULL,
NULL,
NULL,
D3DCOLOR_ARGB(0xaa,0xff,0xff,0xff));
m_pBackgroundSprite->End();
m_pD3DDevice->EndScene();
}

void CmyOwnStuff::OnLostDevice(void)
{
m_pBackgroundSprite->OnLostDevice();
}

void CmyOwnStuff::OnResetDevice(void)
{
m_pBackgroundSprite->OnResetDevice();
}
// myIDirect3DDevice9.cpp
HRESULT myIDirect3DDevice9::Present(...)
{
// we may want to draw own things here before flipping surfaces
// ... draw own stuff ...
if (!m_pMyOwnStuff) m_pMyOwnStuff = new CmyOwnStuff(m_pIDirect3DDevice9);
if (m_pMyOwnStuff) m_pMyOwnStuff->Render();

// call original routine
HRESULT hres = m_pIDirect3DDevice9->Present( pSourceRect,
pDestRect,
hDestWindowOverride,
pDirtyRegion);

return (hres);
}

HRESULT myIDirect3DDevice9::Reset(...)
{
if (m_pMyOwnStuff) m_pMyOwnStuff->OnLostDevice();
if (m_pMyOwnStuff) m_pMyOwnStuff->OnResetDevice();

return(m_pIDirect3DDevice9->Reset(pPresentationParameters));
}

HowTo 2: Record an ingame movie (*.avi file)

Now that we can intercept every IDirect3DDevice9::Present event, how about saving every frame to a file, thus creating an ingame video ? Well, let see how this could work.

The video file creation routines of this HowTo are based on a fine article by James Dougherty, written for gamedev.net (
http://www.gamedev.net/reference/articles/article2063.asp). I do use some parts of his code, too.

Again, I utilize the
Flatspace Demo for testing, focusing on DX9.

How would video creation work ? First, we’d need a keyboard hook to switch recording ingame on and off. Then, we’d intercept every call to IDirect3DDevice9::Present , take a screen snapshot and process it and save it to an avi-stream. Basically, we should have an ingame video then.

  • Add a global keyboard hook to your project (using SetWindowsHookEx and a callback routine).
  • Add a new class (like “CAviSystem”) to your project (where all the new processing takes place).
  • Call bitmap/avi processing routines from within myIDirect3DDevice9::Present, if needed.

For the demo, I chose the “INSERT” key to toggle ingame recording on and off. The newly created *.avi file will always be saved to C:\myVideoX.avi (where “X” is an increasing number, in case several recordings are done after a game was started). There is a beep when recording toggles.

To see what I got from Flatspace with this setup, see the downloads page. Of course, you might need some more tweaking to enhance this up to a “fully qualifying ingame video tool”. The source to this HowTo is available at the downloads page, too.

Additional info, brought up by Peter Kuhn:

Taken screenshots can sometimes miss some elements of the scene (preferably, HUD or Sprites). This is a result of delayed caching within the video driver. The elements not visible are simply not yet drawn while intercepting Present – although one would expect that at first glance.
Peter proposes to add a “wait loop” inside the proxy’d ::Present call. This will have some impact on fps (minor, though). It could look like so:
HRESULT myIDirect3DDevice9::Present(CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion)
{
// call original routine
HRESULT hres = m_pIDirect3DDevice9->Present(
pSourceRect,
pDestRect,
hDestWindowOverride,
pDirtyRegion);

// Flush the engine content
IDirect3DQuery9* mQuery = NULL;

this->CreateQuery(D3DQUERYTYPE_EVENT, &mQuery);
mQuery->Issue(D3DISSUE_END);
while(mQuery->GetData(NULL, 0, D3DGETDATA_FLUSH) == S_FALSE); // wait loop here
mQuery->Release();

// do anything you want here (e.g. capturing
// using the GDI), the frame is now rendered
// completely

return (hres);
}

DirectX8/9 Proxy-DLL: References

For reference, these are some applications that use the Proxy-Dll concept. Some are not available anymore.

ets2-local-radio

Developer: Koen van Hove
Website: https://github.com/Koenvh1/ets2-local-radio
Game: Euro Truck Simulator 2 (ETS2)
Description: (quote) “ETS2 Local Radio plays radio from where you are, in ETS2. Feel the immersion when the Swedish radio plays in Sweden, and the Polish radio in Poland. It will switch automatically, and allow you to switch stations while driving.” (/quote)
A (slightly adapted) GPP version (DX resource release changed) is used to optionally show the current radio station.

BearForce’s GPP-App

Developer: CaveBear
Website: http://www.bearforce.de
Game: Novalogic’s Joint Operations (modded)
Description: They use a modified GPP version (more text) to show statistics, points, kills, targets etc. Data is handled by a central server that connects to clients.

Delphi-GPP-Unit

Developer: DOHRMANN-SOFTWARE
Website: (See Download section below)
Game: Several (utility)
Description: GPP wrapper and additional functionality for Delphi. Includes demo (shown below). Works with GPP 1.8 DX9.

Analog Gauges

Developer: riskredruM, screama
Website: https://www.lfs.net/forum/thread/28186-Analog-Gauges-v-1-0
Game: Life for Speed (lfs) (https://www.lfs.net/)
Description: Add analog gauges to lfs, including your own designs.

TV Style Display

Developer: Carlo Maker, Dinix
Website: https://www.rfactorcentral.com
Game: rFactor (https://www.rfactor.net)
Description: A TV-Style Overlay for rFactor, with lots of features. It already got high ratings on rfactorcentral.com

Gear Indicator

Developer: Vladimír Kadlec
Website: https://nlp.fi.muni.cz/~xkadlec/lfs/gidll/
Game: Richard Burns Rally (http://www.richardburnsrally.com)
Description: (quote) “The Gear Indicator Dll (GIDll) is a “proxy” dll, that allows to send OutGauge packets from any DirectX 9 application. Currently only Richard Burns Rally (RBR) is tested and supported. The client application can be any application, that receives OutGauge packets. I tested only my Gear Indicator (GI), tough.” (/quote)

RBR Analog Gauges

Developer: Racer_S
Website: https://tocaedit.com/
Game: Richard Burns Rally (https://www.richardburnsrally.com)

Flatout ChatMod (aka PGXM – Pavcules Game Extension Mod)

Developer: Pavcules
Website: https://www.pavcules.com
Game: Flatout (http://www.flatoutgame.com/)

TsShow4DX8

Developer: Peter Barrette
Website: https://www.peterbarrette.com
Game: Black Hawk Down (https://www.novalogic.com/games/DFBHD)

JGRotPro

Developer: Mogar & Miquant (aka miko)
Website: http://www.myrotacol.net
Game: Jumpgate (http://www.jossh.com)

DirectX8/9 Proxy-DLL: Downloads/Links

Users’ Contributions

GPP Delphi Wrapper with additional functionality, by Dohrmann Software. Works with GPP 1.8 DX9. German docs.
Delphi-GPP-Unit_v2015.04.19.zip (4/19/2015, 13 kB)
Delphi-GPP-Unit-Beispiel_v2015.04.19_19.56.zip (4/19/2015, 249 kB)
Delphi-Data-Unit_v2015.04.12.zip (4/19/2015, 17 kB)
GPP-Bild-Beispiel_v2015.04.19.zip (4/19/2015, 252 kB)

VB6 Demo Source by NestorX, to show the usage of d3dx.dll/gpcomms.dll (GPP1.5) with VB6.
VB6_GPP15_Demo.zip (8/19/2009, 136 kB)

VB6 Demo Source by Jean-Philippe Caissy, to show the usage of d3dx.dll/gpcomms.dll (GPP1.3) with VB6.
VB6_Demo_GPP.zip (8/1/2005, 131 kB)

VB6 Demo Source by Pavcules, to show the usage of d3dx.dll/gpcomms.dll (GPP1.3) with VB6.
pav_demo.zip (8/1/2005, 492 kB)

VB6 Demo Source by Pavcules, to show the usage of d3dx.dll/gpcomms.dll (GPP1.4) with VB6.
Pav_VB_GPP14_Demo.zip (9/11/2005, 1.5 MB)

VB .NET 2003 Demo Source by Pavcules, to show the usage of d3dx.dll/gpcomms.dll (GPP1.4) with VB .NET 2003.
Pav_VB_NET2003_GPP14_Demo.zip (9/11/2005, 1.6 MB)

License Disclaimer

The downloadable files listed below (some come with source, some without) are provided under the zlib license. Please consider offering me a job as your gardener after you made billions with it. Ah, and I’d like to link to your application’s website from the “references” section, too.

Basic d3d8/9.dll Implementation

My basic implementations of a proxy dll for DirectX8/9 d3d8/9.dll. If you want to see how the concept works and/or need a clean base to start from, check here.
DX8: The “basic proxy-dll” implementation source (Visual C++ .net 2003)
proxydll.zip (1/1/2005, 115 KB, d3d8.dll, proxy-dll, source DX8)

DX9: The “basic proxy-dll” implementation source (Visual C++ .net 2003)
proxydll_9.zip (1/1/2005, 115KB, d3d9.dll, proxy-dll, source DX9)

DX9: The “basic proxy-dll” implementation source (Visual C++ .net 2003),
covering calls to IDirect3DSwapChain9::Present
proxydll_9_060422.zip (4/22/2006, 270 KB, d3d9.dll, proxy-dll, source DX9)

‘HowTo’ Source

This is the source code for HowTo 1 and HowTo 2 (see ‘Usage Examples’ section).
The source of the HowTo #1 (Visual C++ .net 2003)
howto1.zip (1/1/2005, 214 KB)

The source of the HowTo #2 (Visual C++ .net 2003)
howto2.zip (3/20/2005, 186 KB)

Basic dinput8.dll Implementation

This is the source code for a proxy dinput8.dll (experimental)
The “basic proxy-dll” implementation source (Visual C++ .net 2003)
DInputDll.zip (6/25/2005, 291 KB, dinput8.dll, proxy-dll, source)

Basic ddraw.dll Implementation

This is the source code for a proxy ddraw.dll (*experimental*, ddraw7 only)
It works, but seems to be a bit unstable (Visual C++ .net 2003)
work_ddrawproxy_1.0.zip (2/18/2006, 2.1 MB, ddraw.dll, proxy-dll, source 1.0)

Added few new exported functions. still several missing, though (Visual C++ .net 2003)
work_ddrawproxy_1.1.zip (2/4/2008, 2.1 MB, ddraw.dll, proxy-dll, source 1.1)

Incorporating IDDrawSurface7 proxy code. not fully working though (Visual C++ .net 2003)
work_ddrawproxy_1.2.zip (6/13/2009, 270 KB, ddraw.dll, proxy-dll, source 1.2)

Ingame Video Recording

Based on the HowTo2 (ingame video recording), d3d9_vid is a small package for ingame video recording. It creates *.avi and uses some thread techniques to reduce lag while recording (lag is still very noticeable, tho).
I made it for my personal use, to capture videos within World of Warcraft. It might (or might not) work on your machine. It might (or might not) work with other games. DX9 only. Please read the readme.txt that comes with this package!
d3d9_vid package. An ingame video recording tool (DX9 only), based on the proxy dll concept
d3d9_vid.zip (8/30/2005, 1.1 MB)

Example WoW Ingame Recording: Gadgetzan
Gadgetzan.zip (8/29/2005, 10.7 MB)

GPP

And here we have the packages from the “general proxy-dll project” (GPP). The d3d8/9.dll provided here got features for ingame text (and others), including a wrapper dll for easy access (gpcomms.dll), and a demo application. For details, please refer to the readme.pdf that comes with the package. As of 6/28/2009, the source of this package is now available, too.
GPP 1.5 DX8 complete source Visual C++ 2003 project
gp_source_1.5_DX8.zip (6/28/2009, 27.5 MB)

GPP 1.5 DX9 complete source Visual C++ 2003 project
gp_source_1.5_DX9.zip (6/28/2009, 29 MB)

GPP 1.6 DX9 complete source Visual C++ 2008 project
150105_gp_demo_1.6_DX9.zip (1/7/2015, 11.2 MB)

GPP 1.7 DX9 x32+x64 complete source Visual C++ 2013 project, 32 and 64 bit compiles
150117_gpp_1.7_DX9_32_64.zip (1/18/2015, 30.1 MB)

GPP 1.8 DX9 x32+x64 complete source Visual C++ 2013 project, 32 and 64 bit compiles
150405_gpp_1.8_DX9_32_64.zip (4/5/2015, 25.5 MB)

Older Versions

GPP 1.2 test package (DX8 only)
Added basic functions to show pictures ingame. Changed multiline parameters.
Including d3d8.dll, gpcomms.dll, gp_demo.exe, gphook.dll, general info.
GPP12.zip (4/10/2005, 311 KB)

GPP 1.3 test package (rev.5/5)
Added a function to retrieve screen dimensions.
Including d3d8.dll, d3d9.dll, gpcomms.dll, gp_demo.exe, gphook.dll, general info.
GPP13.zip (5/5/2005, 353 KB)

GPP 1.4 test package (DX8+9)
Added a basic FPS counter.
Including d3d8.dll, d3d9.dll, gpcomms.dll, gp_demo.exe, gphook.dll, general info.
GPP14.zip (9/10/2005, 355 KB)

GPP 1.5 DX8 test package (DX8 only)
Up to 5 single-/multiline objects to use. Reduced impact of dll on fps (if no actions active) somewhat. Added screenshot feature. This pack is focused on DX8. Including d3d8.dll, gpcomms.dll, gp_demo.exe, gphook.dll, general info.
GPP_1.5_DX8.zip (1/17/2007, 355 KB)

GPP 1.5 DX9 test package (DX9 only)
Up to 5 single-/multiline objects to use. Reduced impact of dll on fps (if no actions active) somewhat. Added screenshot feature. This pack is focused on DX9. Including d3d9.dll, d3dx9_27.dll, gpcomms.dll, gp_demo.exe, gphook.dll, general info.
GPP_1.5_DX9.zip (10/2/2005, 1/17/2007, 1.2 MB)