ROTA
unread,Sep 12, 2011, 7:57:16 AM9/12/11Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to TexMod development
3) OTM_DX9 introduction
Communication between the game and the GUI:
The dll is loaded into the game, thus it has its own memory space. To
interchange data with the GUI it uses two pipes. One for outgoing
messages (error state etc, which is not used at the moment) and an
incoming pipe to receive the path of the textures, etc.
A Server (OTM_TextureServer) is listening on the incoming pipe in the
Mainloop(). Because the ReadFile is blocking (it waits till something
is written on the opposite site of the pipe) it must run in an extra
thread. If The Server receive some messages it prepare an update and
give it to all Clients. (Note all these things are done by the server-
thread)
A Client (OTM_TextureClient) Object is hold by each
OTM_IDirect3DDevice9 object. Somehow there can exist more than one
device object, thus the OTM_TextureServer can handle with more than
one OTM_TextureClient.
At the beginning of each fake_device->BeginScene the MergUpdate() of
the Client is called. Afterwards the original device->BeginScene() is
called. If the Server has add an update the MergUpdate() will remove
unused fake texture and load/reload new fake textures. (Note: this
will be called from the game rendering thread, during the render
process of one frame)
Note: Because the server does not know when and if a client has merged
the update, it can't delete the allocated memory. This is done by the
Client.
Note: To guarantee a stable run, one has to use a mutex, if the server
adds an update or the client merge the update. But the corresponding
functions are commented out.
How to distinguish the texture:
How does one detect which texture to mod or to save? Characterization
by the pointer address is a bad idea, cause the address range always
change (also the relative addresses). One has to use hash values. The
hash is computed over the raw texture data. With LockRect() one gets
the pointer to this data. But the size of the data has to be calculate
by the width and hight of the texture as well as the format. This has
to be done with care! Although one doesn't change the data (while
computing the hash), an access violation during the read can also
crash the game.
This procedure has to be done with each texture loaded by the game.
(if save all texture is set to true, the texture is afterwards saved)
Because we don't have the code of the original TexMod we don't know
the exact hash function or over which parts of the texture the hash is
computed. Thus we will probably not be able to support mods from the
original TexMod.
But there is the next problem. On load a Texture with function
D3DXCreateTextureFromFile(), D3DXCreateTextureFromFileInMemory(), ...
but these function can be static linked by the game, thus one can't
intercept them. The only function we can intercept (and we already
have) is the IDirect3DDevice9::CreateTexture(). This function
generates an empty texture and is called by the
D3DXCreateTextureFromFile(), D3DXCreateTextureFromFileInMemory(),...
functions.
One gets the pointer to the created texture, when the texture is
empty. So one stores this texture to LastCreatedTexture. The next time
a texture is created and LastCreatedTexture is unequal NULL, we can
calculate the hash (hopefully now this texture is loaded completely).
Note: LockRect() sometimes fails. Never mind, we just return..
Note: Somehow texture can be loaded and immediately released, thus
CreateTexture() is not called in between. The problem is, that
LastCreatedTexture now points to an non existing texture. The next
estimation of the hash would crash the game. If a texture is released,
one has to compare the this pointer with LastCreatedTexture and if
they are the same, one set LastCreatedTexture to NULL.
Note: If loading a fake texture, also the CreateTexture() will be
called, thus one must set the fake-bit to true, otherwise during the
next call of CreateTexture() our fake texture would be treated as
original texture (one could also set the LastCreatedTexture to NULL,
but we need the fake-bit at least if a texture is released).
How to switch the texture on the fly:
If one likes to switch the texture, one need the save point of the
pointer to the texture, thus one can write to the save point the
pointer to the new texture. Unfortunately one only knows the pointer
to the texture, but not the memory place, where the game stores the
pointer.
The trick is again the use of a fake texture object.
OTM_IDirect3DTexture9 is inherited from the original IDirect3DTexture9
class. After the original IDirect3DDevice9::CreateTexture() is
called, we create our fake texture object and return this to the game.
But we must take care, if a texture is loaded with
IDirect3DDevice9::SetTexture, we must always pass the original texture
object to the original device!!
Note: There exist also some standard texture, which are not loaded
through our fake device. These texture must not be handled as
OTM_IDirect3DTexture9!!
The OTM_IDirect3DTexture9 holds a Pointer to the original texture, the
hash of the texture and a Cross reference to an other
OTM_IDirect3DTexture9. Switching OTM_IDirect3DTexture9 texture is
simple, one interchange the pointer of the original texture and
generate the cross reference.
The cross reference is needed for unloading or deleting a texture.
Next, one has to keep in mind all the textures which are loaded from
the game (OTM_TextureHandler OriginalTextures) and all textures which
are loaded as fake textures (OTM_TextureHandler FakeTextures). The
reference value is the index of these arrays and is only need for a
fast deleting. (dammed, while writing this line I found an error in
the code at this point)
We must store all original texture, because we want to mod them on the
fly, and we must store all fake textures, because they must be deleted
when destructing the directx device.
Maybe we should comment the (OTM_TextureHandler FakeTextures) out and
delete the fake texture, if the original texture is deleted (or it is
unmoded through an update).
Speed up:
To do not misspend texture resources, I load fake textures only if
needed. The fake textures are loaded if the matching original texture
was loaded by the gamer or if an update was send (the last point will
executed from the render thread and should be fast). For that I
decided to hold the file content of the texture in the ram. The
loading of the file from disk and the corresponding errors is done by
the server thread. The final load of a fake texture is done by the
client (a thread of the game) by calling
D3DXCreateTextureFromFileInMemory().
Threads, Threads, and Threads and a stable run...
we only know that there exist one server thread, which we start at the
beginning. But we don't know, how many threads the game use to handle
with the device (well we could ask for the TID and count....). The
problem is a stable run. E.g. if more than one thread of the game is
loading the original textures, we will get in serious trouble, not
only LastCreatedTexture is not thread safe but also the add of the of
the texture to (OTM_TextureHandler OriginalTextures).
At the moment no mutex are in use, but the functions are coded and
even called at the right position for guarantee a save flow between
the server and the client. But if the game use more than one thread,
we need to add more mutex.
How to select single texture (save single texture):
One can ask for the key state of a single key (if
(GetAsyncKeyState( Key_Number ) &1 )). This is done at the moment
before the original BeginScene() is called. To inking the selected
texture, one create a new (e.g. green) fake texture and switch it with
the selected original texture. That is all.