From 071ac3ece5be4c37d9c4d12a9cb07041adfa5a60 Mon Sep 17 00:00:00 2001 From: "code@koerner-de.net" Date: Sat, 4 Aug 2012 19:30:29 +0000 Subject: [PATCH] uMod2 Alpha Version: known bugs: I disabled the remove device function inside the gui, because something in the event handling goes terrible wrong. Changes: - hashing algorithm is now a CRC64 value - changed CRC32 algorithm to a faster one - in save single texture mode, the selected texture can now be displayed - the way how uMod get the raw data of the texture was changed --- .gitattributes | 3 +- uMod_DX9/uMod_ComputeHash.cpp | 26 ++ uMod_DX9/uMod_ComputeHash.h | 25 ++ uMod_DX9/uMod_IDirect3DCubeTexture9.cpp | 29 +- uMod_DX9/uMod_IDirect3DCubeTexture9.h | 8 +- uMod_DX9/uMod_IDirect3DDevice9.cpp | 388 ++++++++++++++---- uMod_DX9/uMod_IDirect3DDevice9.h | 4 +- uMod_DX9/uMod_IDirect3DDevice9Ex.h | 5 +- uMod_DX9/uMod_IDirect3DTexture9.cpp | 144 +------ uMod_DX9/uMod_IDirect3DTexture9.h | 8 +- uMod_DX9/uMod_IDirect3DVolumeTexture9.cpp | 9 +- uMod_DX9/uMod_IDirect3DVolumeTexture9.h | 8 +- uMod_DX9/uMod_TextureClient_DX9.cpp | 95 ++--- uMod_DXMain/uMod_Defines.h | 6 +- uMod_DXMain/uMod_Main.h | 3 + uMod_DXMain/uMod_TextureClient.h | 9 +- uMod_DXMain/uMod_TextureFunction.cpp | 149 ++++++- uMod_DXMain/uMod_TextureFunction.h | 29 +- uMod_DXMain/uMod_TextureServer.cpp | 185 ++++----- uMod_DXMain/uMod_TextureServer.h | 8 + uMod_GUI/bin/README_Deutsch.txt | 60 +-- uMod_GUI/bin/README_English.txt | 42 +- .../Example_uMod_LanguagePack_English.txt | 189 ++++----- .../languages/uMod_LanguagePackU_Deutsch.txt | Bin 6508 -> 8486 bytes uMod_GUI/config.gcc | 149 ------- uMod_GUI/makefile.vc | 5 +- uMod_GUI/uMod.ico | Bin 20462 -> 99713 bytes uMod_GUI/uMod_File.cpp | 123 ++---- uMod_GUI/uMod_File.h | 1 + uMod_GUI/uMod_GUI.cpp | 156 +++---- uMod_GUI/uMod_GUI.h | 5 +- uMod_GUI/uMod_GameInfo.cpp | 14 +- uMod_GUI/uMod_GameInfo.h | 5 + uMod_GUI/uMod_GamePage.cpp | 256 ++++++------ uMod_GUI/uMod_GamePage.h | 9 + uMod_GUI/uMod_Language.cpp | 23 +- uMod_GUI/uMod_Language.h | 9 +- uMod_GUI/uMod_MiniPanels.h | 6 +- uMod_GUI/uMod_Sender.cpp | 19 +- uMod_GUI/uMod_TreeView.cpp | 19 - uMod_GlobalDefines.h | 3 +- 41 files changed, 1126 insertions(+), 1108 deletions(-) create mode 100644 uMod_DX9/uMod_ComputeHash.cpp create mode 100644 uMod_DX9/uMod_ComputeHash.h delete mode 100644 uMod_GUI/config.gcc diff --git a/.gitattributes b/.gitattributes index a0944a5..d5b3d82 100644 --- a/.gitattributes +++ b/.gitattributes @@ -18,6 +18,8 @@ uMod_DX10/uMod_ID3D10Texture3D.h -text uMod_DX10/uMod_IDXGISwapChain.cpp -text uMod_DX10/uMod_IDXGISwapChain.h -text uMod_DX10/uMod_TextureClient_DX10.h -text +uMod_DX9/uMod_ComputeHash.cpp -text +uMod_DX9/uMod_ComputeHash.h -text uMod_DX9/uMod_DX9_dll.cpp -text uMod_DX9/uMod_DX9_dll.h -text uMod_DX9/uMod_IDirect3D9.cpp -text @@ -64,7 +66,6 @@ uMod_GUI/bin/languages/uMod_LanguagePackU_Deutsch.txt -text uMod_GUI/bin/languages/uMod_LanguagePackU_French.txt -text uMod_GUI/bin/languages/uMod_LanguagePackU_Russian.txt -text uMod_GUI/bin/languages/uMod_LanguagePack_Italian.txt -text -uMod_GUI/config.gcc -text uMod_GUI/config.vc -text uMod_GUI/makefile.vc -text uMod_GUI/uMod.ico -text diff --git a/uMod_DX9/uMod_ComputeHash.cpp b/uMod_DX9/uMod_ComputeHash.cpp new file mode 100644 index 0000000..35911c0 --- /dev/null +++ b/uMod_DX9/uMod_ComputeHash.cpp @@ -0,0 +1,26 @@ +/* +This file is part of Universal Modding Engine. + + +Universal Modding Engine is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Universal Modding Engine is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Universal Modding Engine. If not, see . +*/ + + +#include "../uMod_DXMain/uMod_Main.h" +#include "../uMod_DXMain/uMod_TextureFunction.h" + + +#include +#include + diff --git a/uMod_DX9/uMod_ComputeHash.h b/uMod_DX9/uMod_ComputeHash.h new file mode 100644 index 0000000..e8a8043 --- /dev/null +++ b/uMod_DX9/uMod_ComputeHash.h @@ -0,0 +1,25 @@ +/* +This file is part of Universal Modding Engine. + + +Universal Modding Engine is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Universal Modding Engine is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Universal Modding Engine. If not, see . +*/ + +#ifndef UMOD_COMPUTEHASH_H_ +#define UMOD_COMPUTEHASH_H_ + + + + +#endif /* UMOD_COMPUTEHASH_H_ */ diff --git a/uMod_DX9/uMod_IDirect3DCubeTexture9.cpp b/uMod_DX9/uMod_IDirect3DCubeTexture9.cpp index 94c40be..332c04e 100644 --- a/uMod_DX9/uMod_IDirect3DCubeTexture9.cpp +++ b/uMod_DX9/uMod_IDirect3DCubeTexture9.cpp @@ -238,7 +238,32 @@ HRESULT APIENTRY uMod_IDirect3DCubeTexture9::UnlockRect( D3DCUBEMAP_FACES FaceTy } +int uMod_IDirect3DCubeTexture9::ComputetHash( bool compute_crc) +{ + if (FAKE) return (RETURN_BAD_ARGUMENT); + IDirect3DCubeTexture9 *pTexture = m_D3Dtex; + if (CrossRef_D3Dtex!=NULL) pTexture = CrossRef_D3Dtex->m_D3Dtex; + + IDirect3DSurface9 *surface = NULL; + if (compute_crc) InitCRC32(CRC32); + InitCRC64(CRC64); + + for (int site = 0; site<6; site++) + { + if (pTexture->GetCubeMapSurface( (D3DCUBEMAP_FACES) site, 0, &surface)!=D3D_OK) + { + Message("uMod_IDirect3DCubeTexture9::GetHash() Failed: GetSurfaceLevel\n"); + return (RETURN_LockRect_FAILED); + } + ((uMod_IDirect3DDevice9*)(m_D3Ddev))->ComputetHash( CRC64, CRC32, surface, compute_crc); + + surface->Release(); + } + return (RETURN_OK); +} + +/* int uMod_IDirect3DCubeTexture9::ComputetHash( bool compute_crc) { if (FAKE) return (RETURN_BAD_ARGUMENT); @@ -304,7 +329,7 @@ int uMod_IDirect3DCubeTexture9::ComputetHash( bool compute_crc) if (compute_crc && site==0) { int size = (bits_per_pixel * desc.Width*desc.Height)/8; - CRC = GetCRC32( (char*) d3dlr.pBits, size); //calculate the crc32 of the texture + CRC = GetCRC32( (unsigned char*) d3dlr.pBits, size); //calculate the crc32 of the texture } if (pResolvedSurface!=NULL) @@ -322,5 +347,5 @@ int uMod_IDirect3DCubeTexture9::ComputetHash( bool compute_crc) Message("uMod_IDirect3DCubeTexture9::GetHash() %#llX %#LX (%d %d) %d\n", Hash, CRC, desc.Width, desc.Height, desc.Format); return (RETURN_OK); } - +*/ diff --git a/uMod_DX9/uMod_IDirect3DCubeTexture9.h b/uMod_DX9/uMod_IDirect3DCubeTexture9.h index 3fda915..edd9b4b 100644 --- a/uMod_DX9/uMod_IDirect3DCubeTexture9.h +++ b/uMod_DX9/uMod_IDirect3DCubeTexture9.h @@ -46,8 +46,8 @@ public: // original texture: stores the pointer to the fake texture object, is needed if original texture is deleted, // thus the fake texture can also be deleted Reference = -1; //need for fast deleting - Hash = 0u; - CRC = 0u; + CRC64 = 0u; + CRC32 = 0u; FAKE = false; } @@ -55,8 +55,8 @@ public: uMod_IDirect3DCubeTexture9 *CrossRef_D3Dtex; //!< cross reference from the fake texture to the game texture and vice versa IDirect3DDevice9 *m_D3Ddev; //!< pointer to the device int Reference; //!< Index in the vector (needed for a fast delete). - DWORD64 Hash; //!< computed hash value for this game texture. - DWORD32 CRC; //!< computed crc32 value for this game texture. + DWORD64 CRC64; //!< computed hash value for this game texture. + DWORD32 CRC32; //!< computed crc32 value for this game texture. bool FAKE; //!< True if this texture is was loaded by uMod (fake texture) // original interface diff --git a/uMod_DX9/uMod_IDirect3DDevice9.cpp b/uMod_DX9/uMod_IDirect3DDevice9.cpp index 8541506..7b82e3d 100644 --- a/uMod_DX9/uMod_IDirect3DDevice9.cpp +++ b/uMod_DX9/uMod_IDirect3DDevice9.cpp @@ -36,6 +36,7 @@ along with Universal Modding Engine. If not, see int uMod_IDirect3DDevice9::CreateSingleTexture(void) { + if (SingleTexture!=NULL && SingleVolumeTexture!=NULL && SingleCubeTexture!=NULL && TextureColour==uMod_Client->TextureColour) return (RETURN_OK); TextureColour = uMod_Client->TextureColour; if (SingleTexture==NULL) //create texture @@ -150,6 +151,172 @@ int uMod_IDirect3DDevice9::CreateSingleTexture(void) return (RETURN_OK); } + + +int uMod_IDirect3DDevice9::ComputetHash( DWORD64 &CRC64, DWORD32 &CRC32, IDirect3DSurface9 *surface, bool compute_crc) +{ + + IDirect3DSurface9 *offscreen_surface = NULL; + D3DLOCKED_RECT d3dlr; + D3DSURFACE_DESC desc; + unsigned char *buffer = NULL; + + bool locked = false; + int pitch = 0; + + if (surface->GetDesc(&desc)!=D3D_OK) //get the format and the size of the texture + { + Message("ComputetHash() Failed: GetLevelDesc \n"); + return (RETURN_GetLevelDesc_FAILED); + } + + Message("ComputetHash() (%d %d) F%d T%d U%d P%d M(%d %d)\n", desc.Width, desc.Height, + desc.Format, desc.Type, desc.Usage, desc.Pool, desc.MultiSampleType, desc.MultiSampleQuality); + + + if (surface->LockRect( &d3dlr, NULL, D3DLOCK_READONLY)!=D3D_OK) + { + Message("ComputetHash() Failed: surface->LockRect()\n"); + } + else + { + buffer = (unsigned char*) d3dlr.pBits; + pitch = d3dlr.Pitch; + locked = true; + } + if (!locked) + { + if (surface->GetDC( (HDC*) &buffer)!=D3D_OK) + { + Message("ComputetHash() Failed: surface->GetDC()\n"); + buffer = NULL; + } + } + + + if (buffer==NULL) + { + + IDirect3DSurface9 *resolved_surface = NULL; + if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) + { + if (D3D_OK!=m_pIDirect3DDevice9->CreateRenderTarget( desc.Width, desc.Height, desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &resolved_surface, NULL )) + { + Message("ComputetHash() Failed: CreateRenderTarget (D3DPOOL_DEFAULT)\n"); + return (RETURN_LockRect_FAILED); + } + if (D3D_OK!=m_pIDirect3DDevice9->StretchRect( surface, NULL, resolved_surface, NULL, D3DTEXF_NONE )) + { + Message("ComputetHash() Failed: StretchRect (D3DPOOL_DEFAULT)\n"); + return (RETURN_LockRect_FAILED); + } + } + + if (D3D_OK!=m_pIDirect3DDevice9->CreateOffscreenPlainSurface( desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &offscreen_surface, NULL)) + { + if (resolved_surface!=NULL) resolved_surface->Release(); + Message("ComputetHash() Failed: CreateOffscreenPlainSurface (D3DPOOL_DEFAULT)\n"); + return (RETURN_TEXTURE_NOT_LOADED); + } + + HRESULT ret; + if (resolved_surface!=NULL) ret = m_pIDirect3DDevice9->GetRenderTargetData( resolved_surface, offscreen_surface); + else ret = m_pIDirect3DDevice9->GetRenderTargetData( surface, offscreen_surface); + if (D3D_OK!=ret) + { + if (resolved_surface!=NULL) resolved_surface->Release(); + offscreen_surface->Release(); + Message("ComputetHash() Failed: GetRenderTargetData (D3DPOOL_DEFAULT)\n"); + return (RETURN_LockRect_FAILED); + } + + if (resolved_surface!=NULL) resolved_surface->Release(); + if (offscreen_surface->LockRect( &d3dlr, NULL, D3DLOCK_READONLY)!=D3D_OK) + { + offscreen_surface->Release(); + Message("ComputetHash() Failed: offscreen_surface->LockRect (D3DPOOL_DEFAULT)\n"); + return (RETURN_LockRect_FAILED); + } + + buffer = (unsigned char*) d3dlr.pBits; + pitch = d3dlr.Pitch; + } + + int bits_per_pixel = 0; + + { + unsigned char *data = buffer; + unsigned int size; + unsigned int h_max = desc.Height; + if (desc.Format == D3DFMT_DXT1) // 8 bytes per block + { + h_max /= 4; // divided by block size + size = desc.Width*2; // desc.Width/4 * 8 + if (pitch==0) pitch = ((desc.Width+3)/4)*8; + } + else if ( desc.Format==D3DFMT_DXT2 || desc.Format==D3DFMT_DXT3 || desc.Format==D3DFMT_DXT4 || desc.Format==D3DFMT_DXT5 ) // 16 bytes per block + { + h_max /= 4; // divided by block size + size = desc.Width*4; // desc.Width/4 * 16 + if (pitch==0) pitch = ((desc.Width+3)/4)*16; + } + else + { + bits_per_pixel = GetBitsFromFormat( desc.Format); + size = (bits_per_pixel * desc.Width)/8; + if (pitch==0) + { + if (desc.Format==D3DFMT_R8G8_B8G8 || desc.Format==D3DFMT_G8R8_G8B8 || desc.Format==D3DFMT_UYVY || desc.Format==D3DFMT_YUY2) + { + pitch = ((desc.Width+1) >> 1) * 4; + } + else + pitch = (desc.Width * bits_per_pixel + 7 ) / 8 ; + } + } + + for (unsigned int h=0; hUnlockRect(); + offscreen_surface->Release(); + } + else if (locked) + { + surface->UnlockRect(); + } + else + { + surface->ReleaseDC( (HDC) buffer); + } + + + Message("ComputetHash() %#llX %#LX (%d %d) %d\n", CRC64, CRC32, desc.Width, desc.Height, desc.Format); + return (RETURN_OK); +} + + + + + + + + + + uMod_IDirect3DDevice9::uMod_IDirect3DDevice9( IDirect3DDevice9* pOriginal, uMod_TextureServer* server, int back_buffer_count) { Message( PRE_MESSAGE "::" PRE_MESSAGE "( %lu, %lu): %lu\n", pOriginal, server, this); @@ -160,6 +327,7 @@ uMod_IDirect3DDevice9::uMod_IDirect3DDevice9( IDirect3DDevice9* pOriginal, uMod_ uMod_Server = server; uMod_Client = new uMod_TextureClient_DX9( this, DEF_DX_VERSION); //get a new texture client for this device uMod_Client->ConnectToServer( uMod_Server); + TextureColour = uMod_Client->TextureColour; LastCreatedTexture = NULL; LastCreatedVolumeTexture = NULL; @@ -173,6 +341,8 @@ uMod_IDirect3DDevice9::uMod_IDirect3DDevice9( IDirect3DDevice9* pOriginal, uMod_ SingleVolumeTexture = NULL; SingleCubeTexture = NULL; OSD_Font = NULL; + + pSprite = NULL; uMod_Reference = 1; } @@ -222,12 +392,16 @@ ULONG uMod_IDirect3DDevice9::Release(void) if (SingleVolumeTexture!=NULL) SingleVolumeTexture->Release(); //this is the only texture we must release by ourself if (SingleCubeTexture!=NULL) SingleCubeTexture->Release(); //this is the only texture we must release by ourself if (OSD_Font!=NULL) OSD_Font->Release(); + if (pSprite!=NULL) pSprite->Release(); if (uMod_Client!=NULL) delete uMod_Client; //must be deleted at the end, because other releases might call a function of this object uMod_Client = NULL; SingleTexture = NULL; + SingleVolumeTexture = NULL; + SingleCubeTexture = NULL; OSD_Font = NULL; + pSprite = NULL; } ULONG count = m_pIDirect3DDevice9->Release(); @@ -309,6 +483,7 @@ UINT uMod_IDirect3DDevice9::GetNumberOfSwapChains(void) HRESULT uMod_IDirect3DDevice9::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) { if(OSD_Font!=NULL) {OSD_Font->Release(); OSD_Font=NULL;} //the game will crashes if the font is not released before the game is minimized! + if (pSprite!=NULL) {pSprite->Release(); pSprite=NULL;} return(m_pIDirect3DDevice9->Reset(pPresentationParameters)); } @@ -442,14 +617,16 @@ HRESULT uMod_IDirect3DDevice9::UpdateTexture(IDirect3DBaseTexture9* pSourceTextu case 0x01000000L: { pSource = (uMod_IDirect3DTexture9*)(pSourceTexture); - DWORD64 hash = pSource->Hash; - DWORD32 crc = pSource->CRC; - if (pSource->ComputetHash( crc>0 ) == RETURN_OK) + DWORD64 crc64 = pSource->CRC64; + DWORD32 crc32 = pSource->CRC32; + if (pSource->ComputetHash( crc32>0 ) == RETURN_OK) { - if (hash != pSource->Hash || (crc>0 && crc != pSource->CRC) ) // this hash has changed !! + if (crc64 != pSource->CRC64 || (crc32>0 && crc32 != pSource->CRC32) ) // this hash has changed !! { + pSource->CRC64 = crc64; + pSource->CRC32 = crc32; if (pSource->CrossRef_D3Dtex!=NULL) UnswitchTextures(pSource); - uMod_Client->LookUpToMod( pSource); + if ( uMod_Client!=NULL) uMod_Client->LookUpToMod( pSource); } } else if (pSource->CrossRef_D3Dtex!=NULL) UnswitchTextures(pSource); // we better unswitch @@ -462,15 +639,16 @@ HRESULT uMod_IDirect3DDevice9::UpdateTexture(IDirect3DBaseTexture9* pSourceTextu case 0x01000001L: { pSourceVolume = (uMod_IDirect3DVolumeTexture9*)(pSourceTexture); - DWORD64 hash = pSourceVolume->Hash; - DWORD32 crc = pSourceVolume->CRC; - if (pSourceVolume->ComputetHash( crc>0 ) == RETURN_OK) + DWORD64 crc64 = pSourceVolume->CRC64; + DWORD32 crc32 = pSourceVolume->CRC32; + if (pSourceVolume->ComputetHash( crc32>0 ) == RETURN_OK) { - if (hash != pSourceVolume->Hash || (crc>0 && crc != pSource->CRC) ) // this hash has changed !! + if (crc64 != pSourceVolume->CRC64 || (crc32>0 && crc32 != pSource->CRC32) ) // this hash has changed !! { - pSourceVolume->Hash = hash; + pSourceVolume->CRC64 = crc64; + pSourceVolume->CRC32 = crc32; if (pSourceVolume->CrossRef_D3Dtex!=NULL) UnswitchTextures(pSourceVolume); - uMod_Client->LookUpToMod( pSourceVolume); + if ( uMod_Client!=NULL) uMod_Client->LookUpToMod( pSourceVolume); } } else if (pSourceVolume->CrossRef_D3Dtex!=NULL) UnswitchTextures(pSourceVolume); // we better unswitch @@ -483,15 +661,16 @@ HRESULT uMod_IDirect3DDevice9::UpdateTexture(IDirect3DBaseTexture9* pSourceTextu case 0x01000002L: { pSourceCube = (uMod_IDirect3DCubeTexture9*)(pSourceTexture); - DWORD64 hash = pSourceCube->Hash; - DWORD32 crc = pSourceCube->CRC; - if (pSourceCube->ComputetHash( crc>0 ) == RETURN_OK) + DWORD64 crc64 = pSourceCube->CRC64; + DWORD32 crc32 = pSourceCube->CRC32; + if (pSourceCube->ComputetHash( crc32>0 ) == RETURN_OK) { - if (hash != pSourceCube->Hash || (crc>0 && crc != pSource->CRC) ) // this hash has changed !! + if (crc64 != pSourceCube->CRC64 || (crc32>0 && crc32 != pSource->CRC32) ) // this hash has changed !! { - pSourceCube->Hash = hash; + pSourceCube->CRC64 = crc64; + pSourceCube->CRC32 = crc32; if (pSourceCube->CrossRef_D3Dtex!=NULL) UnswitchTextures(pSourceCube); - uMod_Client->LookUpToMod( pSourceCube); + if ( uMod_Client!=NULL) uMod_Client->LookUpToMod( pSourceCube); } } else if (pSourceCube->CrossRef_D3Dtex!=NULL) UnswitchTextures(pSourceCube); // we better unswitch @@ -516,9 +695,9 @@ HRESULT uMod_IDirect3DDevice9::UpdateTexture(IDirect3DBaseTexture9* pSourceTextu { uMod_IDirect3DTexture9* pDest = (uMod_IDirect3DTexture9*)(pDestinationTexture); - if (pSource!=NULL && pDest->Hash!=pSource->Hash) + if (pSource!=NULL && pDest->CRC64!=pSource->CRC64) { - pDest->Hash = pSource->Hash; // take over the hash + pDest->CRC64 = pSource->CRC64; // take over the hash UnswitchTextures(pDest); if (pSource->CrossRef_D3Dtex!=NULL) { @@ -535,9 +714,9 @@ HRESULT uMod_IDirect3DDevice9::UpdateTexture(IDirect3DBaseTexture9* pSourceTextu { uMod_IDirect3DVolumeTexture9* pDest = (uMod_IDirect3DVolumeTexture9*)(pDestinationTexture); - if (pSourceVolume!=NULL && pDest->Hash!=pSourceVolume->Hash) + if (pSourceVolume!=NULL && pDest->CRC64!=pSourceVolume->CRC64) { - pDest->Hash = pSourceVolume->Hash; // take over the hash + pDest->CRC64 = pSourceVolume->CRC64; // take over the hash UnswitchTextures(pDest); if (pSourceVolume->CrossRef_D3Dtex!=NULL) { @@ -554,9 +733,9 @@ HRESULT uMod_IDirect3DDevice9::UpdateTexture(IDirect3DBaseTexture9* pSourceTextu { uMod_IDirect3DCubeTexture9* pDest = (uMod_IDirect3DCubeTexture9*)(pDestinationTexture); - if (pSourceCube!=NULL && pDest->Hash!=pSourceCube->Hash) + if (pSourceCube!=NULL && pDest->CRC64!=pSourceCube->CRC64) { - pDest->Hash = pSourceCube->Hash; // take over the hash + pDest->CRC64 = pSourceCube->CRC64; // take over the hash UnswitchTextures(pDest); if (pSourceCube->CrossRef_D3Dtex!=NULL) { @@ -569,6 +748,8 @@ HRESULT uMod_IDirect3DDevice9::UpdateTexture(IDirect3DBaseTexture9* pSourceTextu else pDestinationTexture = pDest->m_D3Dtex; break; } + default: + break; // this is no fake texture and QueryInterface failed, because IDirect3DBaseTexture9 object cannot be a IDirect3D9 object ;) } } return(m_pIDirect3DDevice9->UpdateTexture(pSourceTexture,pDestinationTexture)); @@ -631,7 +812,7 @@ HRESULT uMod_IDirect3DDevice9::GetDepthStencilSurface(IDirect3DSurface9** ppZSte HRESULT uMod_IDirect3DDevice9::BeginScene(void) { - //if ( NormalRendering ) + if ( uMod_Client!=NULL) { if (LastCreatedTexture!=NULL) // add the last created texture { @@ -683,15 +864,15 @@ HRESULT uMod_IDirect3DDevice9::BeginScene(void) { case 0: SwitchTextures( SingleTexture, uMod_Client->OriginalTextures[CounterSaveSingleTexture]); - SingleTexture->Hash = uMod_Client->OriginalTextures[CounterSaveSingleTexture]->Hash; //set the hash for the display + SingleTexture->CRC64 = uMod_Client->OriginalTextures[CounterSaveSingleTexture]->CRC64; //set the CRC64 for the display break; case 1: SwitchTextures( SingleVolumeTexture, uMod_Client->OriginalVolumeTextures[CounterSaveSingleTexture]); - SingleVolumeTexture->Hash = uMod_Client->OriginalVolumeTextures[CounterSaveSingleTexture]->Hash; //set the hash for the display + SingleVolumeTexture->CRC64 = uMod_Client->OriginalVolumeTextures[CounterSaveSingleTexture]->CRC64; //set the CRC64 for the display break; case 2: SwitchTextures( SingleCubeTexture, uMod_Client->OriginalCubeTextures[CounterSaveSingleTexture]); - SingleCubeTexture->Hash = uMod_Client->OriginalCubeTextures[CounterSaveSingleTexture]->Hash; //set the hash for the display + SingleCubeTexture->CRC64 = uMod_Client->OriginalCubeTextures[CounterSaveSingleTexture]->CRC64; //set the CRC64 for the display break; } } @@ -759,15 +940,15 @@ HRESULT uMod_IDirect3DDevice9::BeginScene(void) { case 0: SwitchTextures( SingleTexture, uMod_Client->OriginalTextures[CounterSaveSingleTexture]); - SingleTexture->Hash = uMod_Client->OriginalTextures[CounterSaveSingleTexture]->Hash; //set the hash for the display + SingleTexture->CRC64 = uMod_Client->OriginalTextures[CounterSaveSingleTexture]->CRC64; //set the CRC64 for the display break; case 1: SwitchTextures( SingleVolumeTexture, uMod_Client->OriginalVolumeTextures[CounterSaveSingleTexture]); - SingleVolumeTexture->Hash = uMod_Client->OriginalVolumeTextures[CounterSaveSingleTexture]->Hash; //set the hash for the display + SingleVolumeTexture->CRC64 = uMod_Client->OriginalVolumeTextures[CounterSaveSingleTexture]->CRC64; //set the CRC64 for the display break; case 2: SwitchTextures( SingleCubeTexture, uMod_Client->OriginalCubeTextures[CounterSaveSingleTexture]); - SingleCubeTexture->Hash = uMod_Client->OriginalCubeTextures[CounterSaveSingleTexture]->Hash; //set the hash for the display + SingleCubeTexture->CRC64 = uMod_Client->OriginalCubeTextures[CounterSaveSingleTexture]->CRC64; //set the CRC64 for the display break; } } @@ -779,62 +960,98 @@ HRESULT uMod_IDirect3DDevice9::BeginScene(void) return (m_pIDirect3DDevice9->BeginScene()); } + HRESULT uMod_IDirect3DDevice9::EndScene(void) { - if ( NormalRendering && uMod_Client->BoolShowTextureString && uMod_Client->BoolSaveSingleTexture && SingleTexture!=NULL && SingleVolumeTexture!=NULL && SingleCubeTexture!=NULL) + if ( uMod_Client!=NULL && NormalRendering && uMod_Client->BoolSaveSingleTexture && SingleTexture!=NULL && SingleVolumeTexture!=NULL && SingleCubeTexture!=NULL) { - if (OSD_Font==NULL) // create the font - { - if (D3D_OK!=D3DXCreateFontA( m_pIDirect3DDevice9, 20, 0, FW_BOLD, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, TEXT("Arial"), &OSD_Font)) - { - OSD_Font=NULL; - return(m_pIDirect3DDevice9->EndScene()); - } - } - - char buffer[100]; - buffer[0]=0; - switch (SingleTextureMod) - { - case 0: - { - if (SingleTexture->CrossRef_D3Dtex!=NULL) sprintf_s( buffer, 100, "normal texture: %4d (1..%d): %#llX", CounterSaveSingleTexture+1, uMod_Client->OriginalTextures.GetNumber(), SingleTexture->Hash); - else - { - if (uMod_Client->OriginalTextures.GetNumber()>0) sprintf_s( buffer, 100, "normal texture: nothing selected (1..%d)", uMod_Client->OriginalTextures.GetNumber()); - else sprintf_s( buffer, 100, "normal texture: nothing loaded"); - } - break; - } - case 1: - { - if (SingleVolumeTexture->CrossRef_D3Dtex!=NULL) sprintf_s( buffer, 100, "volume texture: %4d (1..%d): %#llX", CounterSaveSingleTexture+1, uMod_Client->OriginalVolumeTextures.GetNumber(), SingleVolumeTexture->Hash); - else - { - if (uMod_Client->OriginalVolumeTextures.GetNumber()>0) sprintf_s( buffer, 100, "volume texture: nothing selected (1..%d)", uMod_Client->OriginalVolumeTextures.GetNumber()); - else sprintf_s( buffer, 100, "volume texture: nothing loaded"); - } - break; - } - case 2: - { - if (SingleCubeTexture->CrossRef_D3Dtex!=NULL) sprintf_s( buffer, 100, "cube texture: %4d (1..%d): %#llX", CounterSaveSingleTexture+1, uMod_Client->OriginalCubeTextures.GetNumber(), SingleCubeTexture->Hash); - else - { - if (uMod_Client->OriginalCubeTextures.GetNumber()>0) sprintf_s( buffer, 100, "cube texture: nothing selected (1..%d)", uMod_Client->OriginalCubeTextures.GetNumber()); - else sprintf_s( buffer, 100, "cube texture: nothing loaded"); - } - break; - } - } D3DVIEWPORT9 viewport; GetViewport( &viewport); - RECT rct; - rct.left=viewport.X + 10; - rct.right=0; //size of box is calculated automatically (DT_NOCLIP) - rct.top=viewport.Y + 10; - rct.bottom=0; //size of box is calculated automatically (DT_NOCLIP) - OSD_Font->DrawTextA(NULL, buffer, -1, &rct, DT_NOCLIP, uMod_Client->FontColour); + + if (SingleTexture->CrossRef_D3Dtex!=NULL && uMod_Client->BoolShowSingleTexture) + { + if (pSprite==NULL) + { + if (D3D_OK!=D3DXCreateSprite( m_pIDirect3DDevice9 , &pSprite)) pSprite = NULL; + } + if (pSprite!=NULL) + { + D3DSURFACE_DESC desc; + SingleTexture->m_D3Dtex->GetLevelDesc(0, &desc); + D3DXMATRIX scale; + float factorx, factory; + + pSprite->Begin(D3DXSPRITE_ALPHABLEND); + + factorx = 0.5f * ((float) viewport.Width) / ((float) desc.Width) ; + factory = 0.5f * ((float) viewport.Height) / ((float) desc.Height) ; + + if (factorySetTransform(&scale); + } + pSprite->Draw(SingleTexture->m_D3Dtex,NULL,NULL,NULL,0xFFFFFFFF); + pSprite->End(); + } + + } + if (uMod_Client->BoolShowTextureString) + { + if (OSD_Font==NULL) // create the font + { + if (D3D_OK!=D3DXCreateFontA( m_pIDirect3DDevice9, 18, 5, FW_BOLD, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, TEXT("Courier"), &OSD_Font)) + OSD_Font=NULL; + } + + if (OSD_Font!=NULL) + { + char buffer[256]; + buffer[0]=0; + switch (SingleTextureMod) + { + case 0: + { + if (SingleTexture->CrossRef_D3Dtex!=NULL) sprintf_s( buffer, 256, "normal texture: %4d (1..%d): %#llX", CounterSaveSingleTexture+1, uMod_Client->OriginalTextures.GetNumber(), SingleTexture->CRC64); + else + { + if (uMod_Client->OriginalTextures.GetNumber()>0) sprintf_s( buffer, 256, "normal texture: %4d (1..%d): nothing selected ", CounterSaveSingleTexture+1, uMod_Client->OriginalTextures.GetNumber()); + else sprintf_s( buffer, 100, "normal texture: nothing loaded"); + } + break; + } + case 1: + { + if (SingleVolumeTexture->CrossRef_D3Dtex!=NULL) sprintf_s( buffer, 256, "volume texture: %4d (1..%d): %#llX", CounterSaveSingleTexture+1, uMod_Client->OriginalVolumeTextures.GetNumber(), SingleVolumeTexture->CRC64); + else + { + if (uMod_Client->OriginalVolumeTextures.GetNumber()>0) sprintf_s( buffer, 256, "volume texture: %4d (1..%d): nothing selected", CounterSaveSingleTexture+1, uMod_Client->OriginalVolumeTextures.GetNumber()); + else sprintf_s( buffer, 100, "volume texture: nothing loaded"); + } + break; + } + case 2: + { + if (SingleCubeTexture->CrossRef_D3Dtex!=NULL) sprintf_s( buffer, 256, "cube texture: %4d (1..%d): %#llX", CounterSaveSingleTexture+1, uMod_Client->OriginalCubeTextures.GetNumber(), SingleCubeTexture->CRC64); + else + { + if (uMod_Client->OriginalCubeTextures.GetNumber()>0) sprintf_s( buffer, 256, "cube texture: %4d (1..%d): nothing selected", CounterSaveSingleTexture+1, uMod_Client->OriginalCubeTextures.GetNumber()); + else sprintf_s( buffer, 100, "cube texture: nothing loaded"); + } + break; + } + } + + RECT rct; + rct.left=viewport.X + 10; + rct.right=0; //size of box is calculated automatically (DT_NOCLIP) + rct.top=viewport.Y + 10; + rct.bottom=0; //size of box is calculated automatically (DT_NOCLIP) + OSD_Font->DrawTextA(NULL, buffer, -1, &rct, DT_NOCLIP, uMod_Client->FontColour); + } + } } return(m_pIDirect3DDevice9->EndScene()); } @@ -952,9 +1169,6 @@ HRESULT uMod_IDirect3DDevice9::GetTexture(DWORD Stage,IDirect3DBaseTexture9** pp HRESULT uMod_IDirect3DDevice9::SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture) { // we must pass the real texture objects - // if (dev != this) this texture was not initialized through our device and is thus no fake texture object - - //IDirect3DDevice9 *dev = NULL; IDirect3DBaseTexture9* cpy; if( pTexture != NULL ) { @@ -971,12 +1185,6 @@ HRESULT uMod_IDirect3DDevice9::SetTexture(DWORD Stage, IDirect3DBaseTexture9* pT break; // this is no fake texture and QueryInterface failed, because IDirect3DBaseTexture9 object cannot be a IDirect3D9 object ;) } } - /* - if (pTexture != NULL && ((uMod_IDirect3DTexture9*)(pTexture))->GetDevice(&dev) == D3D_OK) - { - if(dev == this) pTexture = ((uMod_IDirect3DTexture9*)(pTexture))->m_D3Dtex; - } - */ return (m_pIDirect3DDevice9->SetTexture(Stage, pTexture)); } diff --git a/uMod_DX9/uMod_IDirect3DDevice9.h b/uMod_DX9/uMod_IDirect3DDevice9.h index e247c7c..e667f1e 100644 --- a/uMod_DX9/uMod_IDirect3DDevice9.h +++ b/uMod_DX9/uMod_IDirect3DDevice9.h @@ -182,6 +182,7 @@ public: uMod_IDirect3DVolumeTexture9* GetSingleVolumeTexture(void) {return (SingleVolumeTexture);} uMod_IDirect3DCubeTexture9* GetSingleCubeTexture(void) {return (SingleCubeTexture);} + int ComputetHash( DWORD64 &CRC64, DWORD32 &CRC32, IDirect3DSurface9 *surface, bool compute_crc); private: int CreateSingleTexture(void); @@ -195,7 +196,8 @@ public: D3DCOLOR TextureColour; ID3DXFont *OSD_Font; - //D3DCOLOR FontColour; + ID3DXSprite *pSprite; + int BackBufferCount; bool NormalRendering; diff --git a/uMod_DX9/uMod_IDirect3DDevice9Ex.h b/uMod_DX9/uMod_IDirect3DDevice9Ex.h index a1d2137..8a65a25 100644 --- a/uMod_DX9/uMod_IDirect3DDevice9Ex.h +++ b/uMod_DX9/uMod_IDirect3DDevice9Ex.h @@ -194,6 +194,8 @@ public: uMod_IDirect3DVolumeTexture9* GetSingleVolumeTexture(void) {return (SingleVolumeTexture);} uMod_IDirect3DCubeTexture9* GetSingleCubeTexture(void) {return (SingleCubeTexture);} + int ComputetHash( DWORD64 &CRC64, DWORD32 &CRC32, IDirect3DSurface9 *surface, bool compute_crc); + private: @@ -208,7 +210,8 @@ public: D3DCOLOR TextureColour; ID3DXFont *OSD_Font; - //D3DCOLOR FontColour; + ID3DXSprite *pSprite; + int BackBufferCount; bool NormalRendering; diff --git a/uMod_DX9/uMod_IDirect3DTexture9.cpp b/uMod_DX9/uMod_IDirect3DTexture9.cpp index 5fb2260..52d22c6 100644 --- a/uMod_DX9/uMod_IDirect3DTexture9.cpp +++ b/uMod_DX9/uMod_IDirect3DTexture9.cpp @@ -246,146 +246,18 @@ int uMod_IDirect3DTexture9::ComputetHash( bool compute_crc) IDirect3DTexture9 *pTexture = m_D3Dtex; if (CrossRef_D3Dtex!=NULL) pTexture = CrossRef_D3Dtex->m_D3Dtex; - IDirect3DSurface9 *pOffscreenSurface = NULL; - //IDirect3DTexture9 *pOffscreenTexture = NULL; - IDirect3DSurface9 *pResolvedSurface = NULL; - D3DLOCKED_RECT d3dlr; - D3DSURFACE_DESC desc; - bool normal_lock=true; - if (pTexture->GetLevelDesc(0, &desc)!=D3D_OK) //get the format and the size of the texture + IDirect3DSurface9 *surface = NULL; + if (pTexture->GetSurfaceLevel(0, &surface)!=D3D_OK) { - Message("uMod_IDirect3DTexture9::GetHash() Failed: GetLevelDesc \n"); - return (RETURN_GetLevelDesc_FAILED); + Message("uMod_IDirect3DTexture9::GetHash() Failed: GetSurfaceLevel\n"); + return (RETURN_LockRect_FAILED); } - Message("uMod_IDirect3DTexture9::GetHash() (%d %d) F%d T%d U%d P%d M(%d %d)\n", desc.Width, desc.Height, - desc.Format, desc.Type, desc.Usage, desc.Pool, desc.MultiSampleType, desc.MultiSampleQuality); + if (compute_crc) InitCRC32(CRC32); + InitCRC64(CRC64); + ((uMod_IDirect3DDevice9*)(m_D3Ddev))->ComputetHash( CRC64, CRC32, surface, compute_crc); - - if (desc.Pool!=D3DPOOL_DEFAULT) - { - if (pTexture->LockRect( 0, &d3dlr, NULL, D3DLOCK_READONLY)!=D3D_OK) - { - normal_lock = false; - Message("uMod_IDirect3DTexture9::GetHash() Failed: LockRect 1\n"); - if (pTexture->GetSurfaceLevel(0, &pResolvedSurface)!=D3D_OK) - { - Message("uMod_IDirect3DTexture9::GetHash() Failed: GetSurfaceLevel\n"); - //return (RETURN_LockRect_FAILED); - pResolvedSurface = NULL; - } - else if (pResolvedSurface->LockRect( &d3dlr, NULL, D3DLOCK_READONLY)!=D3D_OK) - { - pResolvedSurface->Release(); - Message("uMod_IDirect3DTexture9::GetHash() Failed: LockRect 2\n"); - pResolvedSurface = NULL; - //return (RETURN_LockRect_FAILED); - } - } - } - if (desc.Pool==D3DPOOL_DEFAULT || (!normal_lock && pResolvedSurface==NULL))//desc.Pool==D3DPOOL_DEFAULT) //get the raw data of the texture - { - //Message("uMod_IDirect3DTexture9::GetHash() (D3DPOOL_DEFAULT)\n"); - - IDirect3DSurface9 *pSurfaceLevel_orig = NULL; - if (pTexture->GetSurfaceLevel( 0, &pSurfaceLevel_orig)!=D3D_OK) - { - Message("uMod_IDirect3DTexture9::GetHash() Failed: GetSurfaceLevel 1 (D3DPOOL_DEFAULT)\n"); - return (RETURN_LockRect_FAILED); - } - - if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) - { - //Message("uMod_IDirect3DTexture9::GetHash() MultiSampleType\n"); - if (D3D_OK!=m_D3Ddev->CreateRenderTarget( desc.Width, desc.Height, desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &pResolvedSurface, NULL )) - { - pSurfaceLevel_orig->Release(); - Message("uMod_IDirect3DTexture9::GetHash() Failed: CreateRenderTarget (D3DPOOL_DEFAULT)\n"); - return (RETURN_LockRect_FAILED); - } - if (D3D_OK!=m_D3Ddev->StretchRect( pSurfaceLevel_orig, NULL, pResolvedSurface, NULL, D3DTEXF_NONE )) - { - pSurfaceLevel_orig->Release(); - Message("uMod_IDirect3DTexture9::GetHash() Failed: StretchRect (D3DPOOL_DEFAULT)\n"); - return (RETURN_LockRect_FAILED); - } - - pSurfaceLevel_orig = pResolvedSurface; - } - - if (D3D_OK!=m_D3Ddev->CreateOffscreenPlainSurface( desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &pOffscreenSurface, NULL)) - { - pSurfaceLevel_orig->Release(); - if (pResolvedSurface!=NULL) pResolvedSurface->Release(); - Message("uMod_IDirect3DTexture9::GetHash() Failed: CreateOffscreenPlainSurface (D3DPOOL_DEFAULT)\n"); - return (RETURN_TEXTURE_NOT_LOADED); - } - - if (D3D_OK!=m_D3Ddev->GetRenderTargetData( pSurfaceLevel_orig, pOffscreenSurface)) - { - pSurfaceLevel_orig->Release(); - if (pResolvedSurface!=NULL) pResolvedSurface->Release(); - pOffscreenSurface->Release(); - Message("uMod_IDirect3DTexture9::GetHash() Failed: GetRenderTargetData (D3DPOOL_DEFAULT)\n"); - return (RETURN_LockRect_FAILED); - } - pSurfaceLevel_orig->Release(); - - if (pOffscreenSurface->LockRect( &d3dlr, NULL, D3DLOCK_READONLY)!=D3D_OK) - { - if (pResolvedSurface!=NULL) pResolvedSurface->Release(); - pOffscreenSurface->Release(); - Message("uMod_IDirect3DTexture9::GetHash() Failed: LockRect (D3DPOOL_DEFAULT)\n"); - return (RETURN_LockRect_FAILED); - } - } - - int bits_per_pixel = GetBitsFromFormat( desc.Format); - - { - Hash = HASH_INIT_VALUE; - unsigned char *data = (unsigned char*) d3dlr.pBits; - unsigned int size; - unsigned int h_max = desc.Height; - if (desc.Format == D3DFMT_DXT1) // 8 bytes per block - { - h_max /= 4; // divided by block size - size = desc.Width*2; // desc.Width/4 * 8 - } - else if ( desc.Format==D3DFMT_DXT2 || desc.Format==D3DFMT_DXT3 || desc.Format==D3DFMT_DXT4 || desc.Format==D3DFMT_DXT5 ) // 16 bytes per block - { - h_max /= 4; // divided by block size - size = desc.Width*4; // desc.Width/4 * 16 - } - else size = (bits_per_pixel * desc.Width)/8; - - for (unsigned int h=0; hUnlockRect(); - pOffscreenSurface->Release(); - if (pResolvedSurface!=NULL) pResolvedSurface->Release(); - } - else if (pResolvedSurface!=NULL) - { - pResolvedSurface->UnlockRect(); - pResolvedSurface->Release(); - } - else pTexture->UnlockRect(0); - - Message("uMod_IDirect3DTexture9::GetHash() %#llX %#LX (%d %d) %d\n", Hash, CRC, desc.Width, desc.Height, desc.Format); + surface->Release(); return (RETURN_OK); } diff --git a/uMod_DX9/uMod_IDirect3DTexture9.h b/uMod_DX9/uMod_IDirect3DTexture9.h index 1431a03..8f0b7b0 100644 --- a/uMod_DX9/uMod_IDirect3DTexture9.h +++ b/uMod_DX9/uMod_IDirect3DTexture9.h @@ -47,8 +47,8 @@ public: // original texture: stores the pointer to the fake texture object, is needed if original texture is deleted, // thus the fake texture can also be deleted Reference = -1; //need for fast deleting - Hash = 0u; - CRC = 0u; + CRC64 = 0u; + CRC32 = 0u; FAKE = false; } @@ -56,8 +56,8 @@ public: uMod_IDirect3DTexture9 *CrossRef_D3Dtex; //!< cross reference from the fake texture to the game texture and vice versa IDirect3DDevice9 *m_D3Ddev; //!< pointer to the device int Reference; //!< Index in the vector (needed for a fast delete). - DWORD64 Hash; //!< computed hash value for this game texture. - DWORD32 CRC; //!< computed crc32 value for this game texture. + DWORD64 CRC64; //!< computed hash value for this game texture. + DWORD32 CRC32; //!< computed crc32 value for this game texture. bool FAKE; //!< True if this texture is was loaded by uMod (fake texture) // original interface diff --git a/uMod_DX9/uMod_IDirect3DVolumeTexture9.cpp b/uMod_DX9/uMod_IDirect3DVolumeTexture9.cpp index 317908b..98745c3 100644 --- a/uMod_DX9/uMod_IDirect3DVolumeTexture9.cpp +++ b/uMod_DX9/uMod_IDirect3DVolumeTexture9.cpp @@ -280,7 +280,7 @@ int uMod_IDirect3DVolumeTexture9::ComputetHash( bool compute_crc) int bits_per_pixel = GetBitsFromFormat( desc.Format); { - Hash = HASH_INIT_VALUE; + InitCRC64(CRC64); unsigned char *data = (unsigned char*) d3dlr.pBits; unsigned int size; @@ -303,7 +303,7 @@ int uMod_IDirect3DVolumeTexture9::ComputetHash( bool compute_crc) unsigned char* data_inner_loop = data; for (unsigned int h=0; hUnlockBox(0); - Message("uMod_IDirect3DVolumeTexture9::GetHash() %#llX %#LX (%d %d %d) %d\n", Hash, CRC, desc.Width, desc.Height, desc.Depth, desc.Format); + Message("uMod_IDirect3DVolumeTexture9::GetHash() %#llX %#LX (%d %d %d) %d\n", CRC64, CRC32, desc.Width, desc.Height, desc.Depth, desc.Format); return (RETURN_OK); } diff --git a/uMod_DX9/uMod_IDirect3DVolumeTexture9.h b/uMod_DX9/uMod_IDirect3DVolumeTexture9.h index 39828d6..7a73e9b 100644 --- a/uMod_DX9/uMod_IDirect3DVolumeTexture9.h +++ b/uMod_DX9/uMod_IDirect3DVolumeTexture9.h @@ -47,8 +47,8 @@ public: // original texture: stores the pointer to the fake texture object, is needed if original texture is deleted, // thus the fake texture can also be deleted Reference = -1; //need for fast deleting - Hash = 0u; - CRC = 0u; + CRC64 = 0u; + CRC32 = 0u; FAKE = false; } @@ -56,8 +56,8 @@ public: uMod_IDirect3DVolumeTexture9 *CrossRef_D3Dtex; //!< cross reference from the fake texture to the game texture and vice versa IDirect3DDevice9 *m_D3Ddev; //!< pointer to the device int Reference; //!< Index in the vector (needed for a fast delete). - DWORD64 Hash; //!< computed hash value for this game texture. - DWORD32 CRC; //!< computed crc32 value for this game texture. + DWORD64 CRC64; //!< computed hash value for this game texture. + DWORD32 CRC32; //!< computed crc32 value for this game texture. bool FAKE; //!< True if this texture is was loaded by uMod (fake texture) // original interface diff --git a/uMod_DX9/uMod_TextureClient_DX9.cpp b/uMod_DX9/uMod_TextureClient_DX9.cpp index acc93a4..f27853c 100644 --- a/uMod_DX9/uMod_TextureClient_DX9.cpp +++ b/uMod_DX9/uMod_TextureClient_DX9.cpp @@ -25,14 +25,14 @@ along with Universal Modding Engine. If not, see uMod_TextureClient_DX9::uMod_TextureClient_DX9( IDirect3DDevice9* device, const int version) : uMod_TextureClient( version) { - Message("uMod_TextureClient_DX9::uMod_TextureClient_DX9(void): %lu\n", this); + Message("uMod_TextureClient_DX9::uMod_TextureClient_DX9(void): %p\n", this); D3D9Device = device; Bool_CheckAgainNonAdded = false; } uMod_TextureClient_DX9::~uMod_TextureClient_DX9(void) { - Message("uMod_TextureClient_DX9::~uMod_TextureClient(void): %lu\n", this); + Message("uMod_TextureClient_DX9::~uMod_TextureClient(void): %p\n", this); } @@ -45,7 +45,7 @@ int uMod_TextureClient_DX9::AddTexture( uMod_IDirect3DTexture9* pTexture) if (pTexture->FAKE) return (RETURN_OK); // this is a fake texture - Message("uMod_TextureClient_DX9::AddTexture( %lu): %lu (thread: %lu)\n", pTexture, this, GetCurrentThreadId()); + Message("uMod_TextureClient_DX9::AddTexture( %p): %p (thread: %lu)\n", pTexture, this, GetCurrentThreadId()); if (int ret = pTexture->ComputetHash( BoolComputeCRC)) { @@ -72,7 +72,7 @@ int uMod_TextureClient_DX9::AddTexture( uMod_IDirect3DVolumeTexture9* pTexture) if (pTexture->FAKE) return (RETURN_OK); // this is a fake texture - Message("uMod_TextureClient_DX9::AddTexture( Volume: %lu): %lu (thread: %lu)\n", pTexture, this, GetCurrentThreadId()); + Message("uMod_TextureClient_DX9::AddTexture( Volume: %p): %p (thread: %lu)\n", pTexture, this, GetCurrentThreadId()); if (int ret = pTexture->ComputetHash( BoolComputeCRC)) { @@ -99,7 +99,7 @@ int uMod_TextureClient_DX9::AddTexture( uMod_IDirect3DCubeTexture9* pTexture) if (pTexture->FAKE) return (RETURN_OK); // this is a fake texture - Message("uMod_TextureClient_DX9::AddTexture( Cube: %lu): %lu (thread: %lu)\n", pTexture, this, GetCurrentThreadId()); + Message("uMod_TextureClient_DX9::AddTexture( Cube: %p): %p (thread: %lu)\n", pTexture, this, GetCurrentThreadId()); if (int ret = pTexture->ComputetHash( BoolComputeCRC)) { @@ -120,7 +120,7 @@ int uMod_TextureClient_DX9::AddTexture( uMod_IDirect3DCubeTexture9* pTexture) int uMod_TextureClient_DX9::CheckAgainNonAdded(void) { - Message("uMod_TextureClient_DX9::CheckAgainNonAdded( %u, %u, %u): %lu\n", NonAdded_OriginalTextures.GetNumber(), + Message("uMod_TextureClient_DX9::CheckAgainNonAdded( %u, %u, %u): %p\n", NonAdded_OriginalTextures.GetNumber(), NonAdded_OriginalVolumeTextures.GetNumber(), NonAdded_OriginalCubeTextures.GetNumber(), this); Bool_CheckAgainNonAdded = false; @@ -133,6 +133,7 @@ int uMod_TextureClient_DX9::CheckAgainNonAdded(void) if (pTexture->ComputetHash( BoolComputeCRC) == RETURN_OK) { NonAdded_OriginalTextures.Remove(pTexture); + if (BoolSaveAllTextures) SaveTexture(pTexture, true); OriginalTextures.Add( pTexture); // add the texture to the list of original texture LookUpToMod(pTexture); // check if this texture should be modded } @@ -146,6 +147,7 @@ int uMod_TextureClient_DX9::CheckAgainNonAdded(void) if (pTexture->ComputetHash( BoolComputeCRC) == RETURN_OK) { NonAdded_OriginalVolumeTextures.Remove(pTexture); + if (BoolSaveAllTextures) SaveTexture(pTexture, true); OriginalVolumeTextures.Add( pTexture); // add the texture to the list of original texture LookUpToMod(pTexture); // check if this texture should be modded } @@ -159,17 +161,18 @@ int uMod_TextureClient_DX9::CheckAgainNonAdded(void) if (pTexture->ComputetHash( BoolComputeCRC) == RETURN_OK) { NonAdded_OriginalCubeTextures.Remove(pTexture); + if (BoolSaveAllTextures) SaveTexture(pTexture, true); OriginalCubeTextures.Add( pTexture); // add the texture to the list of original texture LookUpToMod(pTexture); // check if this texture should be modded } } - Message("uMod_TextureClient_DX9::CheckAgainNonAdded() END: %lu\n", this); + Message("uMod_TextureClient_DX9::CheckAgainNonAdded() END: %p\n", this); return (RETURN_OK); } int uMod_TextureClient_DX9::RemoveTexture( uMod_IDirect3DTexture9* pTexture) // is called from a texture, if it is finally released { - Message("uMod_TextureClient_DX9::RemoveTexture( %lu, %#lX): %lu\n", pTexture, pTexture->Hash, this); + Message("uMod_TextureClient_DX9::RemoveTexture( %p, %#llX): %p\n", pTexture, pTexture->CRC64, this); if (gl_ErrorState & uMod_ERROR_FATAL) return (RETURN_FATAL_ERROR); if (pTexture->FAKE) @@ -197,7 +200,7 @@ int uMod_TextureClient_DX9::RemoveTexture( uMod_IDirect3DTexture9* pTexture) // int uMod_TextureClient_DX9::RemoveTexture( uMod_IDirect3DVolumeTexture9* pTexture) // is called from a texture, if it is finally released { - Message("uMod_TextureClient_DX9::RemoveTexture( Volume %lu, %#lX): %lu\n", pTexture, pTexture->Hash, this); + Message("uMod_TextureClient_DX9::RemoveTexture( Volume %p, %#llX): %p\n", pTexture, pTexture->CRC64, this); if (gl_ErrorState & uMod_ERROR_FATAL) return (RETURN_FATAL_ERROR); if (pTexture->FAKE) @@ -225,7 +228,7 @@ int uMod_TextureClient_DX9::RemoveTexture( uMod_IDirect3DVolumeTexture9* pTextur int uMod_TextureClient_DX9::RemoveTexture( uMod_IDirect3DCubeTexture9* pTexture) // is called from a texture, if it is finally released { - Message("uMod_TextureClient_DX9::RemoveTexture( Cube %lu, %#lX): %lu\n", pTexture, pTexture->Hash, this); + Message("uMod_TextureClient_DX9::RemoveTexture( Cube %p, %#llX): %p\n", pTexture, pTexture->CRC64, this); if (gl_ErrorState & uMod_ERROR_FATAL) return (RETURN_FATAL_ERROR); if (pTexture->FAKE) @@ -254,14 +257,14 @@ int uMod_TextureClient_DX9::RemoveTexture( uMod_IDirect3DCubeTexture9* pTexture) int uMod_TextureClient_DX9::SaveAllTextures(bool val) { - Message("uMod_TextureClient_DX9::SaveAllTextures( %d): %lu\n", val, this); + Message("uMod_TextureClient_DX9::SaveAllTextures( %d): %p\n", val, this); BoolSaveAllTextures=val; return (RETURN_OK); } int uMod_TextureClient_DX9::SaveSingleTexture(bool val) { - Message("uMod_TextureClient_DX9::SaveSingleTexture( %d): %lu\n", val, this); + Message("uMod_TextureClient_DX9::SaveSingleTexture( %d): %p\n", val, this); if (BoolSaveSingleTexture && !val) //if BoolSaveSingleTexture is set to false and was previously true we switch the SingleTexture back { uMod_IDirect3DTexture9* pTexture; @@ -282,7 +285,7 @@ int uMod_TextureClient_DX9::SaveTexture(uMod_IDirect3DTexture9* pTexture, bool s if (pTexture==NULL) return (RETURN_BAD_ARGUMENT); if (SavePath[0]==0) { - Message("uMod_TextureClient_DX9::SaveTexture( %#lX, %lu): %lu, SavePath not set\n", pTexture->Hash, pTexture->m_D3Dtex, this); + Message("uMod_TextureClient_DX9::SaveTexture( %#llX, %p): %p, SavePath not set\n", pTexture->CRC64, pTexture->m_D3Dtex, this); return (RETURN_TEXTURE_NOT_SAVED); } D3DSURFACE_DESC desc; @@ -311,9 +314,9 @@ int uMod_TextureClient_DX9::SaveTexture(uMod_IDirect3DTexture9* pTexture, bool s wchar_t file[MAX_PATH]; - if (GameName[0]) swprintf_s( file, MAX_PATH, L"%ls\\%ls_W%u_H%u_F%u_T_%#llX", SavePath, GameName, desc.Width, desc.Height,desc.Format, pTexture->Hash); - else swprintf_s( file, MAX_PATH, L"%ls\\W%u_H%u_F%u_T_%#llX", SavePath, desc.Width, desc.Height, desc.Format, pTexture->Hash); - Message("uMod_TextureClient_DX9::SaveTexture( %ls): %lu\n", file, this); + if (GameName[0]) swprintf_s( file, MAX_PATH, L"%ls\\%ls_W%u_H%u_F%u_T_%#llX", SavePath, GameName, desc.Width, desc.Height,desc.Format, pTexture->CRC64); + else swprintf_s( file, MAX_PATH, L"%ls\\W%u_H%u_F%u_T_%#llX", SavePath, desc.Width, desc.Height, desc.Format, pTexture->CRC64); + Message("uMod_TextureClient_DX9::SaveTexture( %ls): %p\n", file, this); return (SaveTexture( pTexture->m_D3Dtex, file)); } @@ -321,7 +324,7 @@ int uMod_TextureClient_DX9::SaveTexture(uMod_IDirect3DTexture9* pTexture, bool s int uMod_TextureClient_DX9::SaveTexture(uMod_IDirect3DVolumeTexture9* pTexture, bool save_all) { if (pTexture==NULL) return (RETURN_BAD_ARGUMENT); - if (SavePath[0]==0) {Message("uMod_TextureClient_DX9::SaveTexture( %#lX, %lu): %lu, SavePath not set\n", pTexture->Hash, pTexture->m_D3Dtex, this); return (RETURN_TEXTURE_NOT_SAVED);} + if (SavePath[0]==0) {Message("uMod_TextureClient_DX9::SaveTexture( %#llX, %p): %p, SavePath not set\n", pTexture->CRC64, pTexture->m_D3Dtex, this); return (RETURN_TEXTURE_NOT_SAVED);} D3DVOLUME_DESC desc; if (pTexture->m_D3Dtex->GetLevelDesc(0, &desc)!=D3D_OK) //get the format and the size of the texture @@ -354,9 +357,9 @@ int uMod_TextureClient_DX9::SaveTexture(uMod_IDirect3DVolumeTexture9* pTexture, } wchar_t file[MAX_PATH]; - if (GameName[0]) swprintf_s( file, MAX_PATH, L"%ls\\%ls_W%u_H%u_D%u_F%u_V_%#llX", SavePath, GameName, desc.Width, desc.Height, desc.Depth, desc.Format, pTexture->Hash); - else swprintf_s( file, MAX_PATH, L"%ls\\W%u_H%u_D%u_F%u_V_%#llX", SavePath, desc.Width, desc.Height, desc.Depth, desc.Format, pTexture->Hash); - Message("uMod_TextureClient_DX9::SaveTexture( %ls): %lu\n", file, this); + if (GameName[0]) swprintf_s( file, MAX_PATH, L"%ls\\%ls_W%u_H%u_D%u_F%u_V_%#llX", SavePath, GameName, desc.Width, desc.Height, desc.Depth, desc.Format, pTexture->CRC64); + else swprintf_s( file, MAX_PATH, L"%ls\\W%u_H%u_D%u_F%u_V_%#llX", SavePath, desc.Width, desc.Height, desc.Depth, desc.Format, pTexture->CRC64); + Message("uMod_TextureClient_DX9::SaveTexture( %ls): %p\n", file, this); return (SaveTexture( pTexture->m_D3Dtex, file)); } @@ -364,7 +367,7 @@ int uMod_TextureClient_DX9::SaveTexture(uMod_IDirect3DVolumeTexture9* pTexture, int uMod_TextureClient_DX9::SaveTexture(uMod_IDirect3DCubeTexture9* pTexture, bool save_all) { if (pTexture==NULL) return (RETURN_BAD_ARGUMENT); - if (SavePath[0]==0) {Message("uMod_TextureClient_DX9::SaveTexture( %#lX, %lu): %lu, SavePath not set\n", pTexture->Hash, pTexture->m_D3Dtex, this); return (RETURN_TEXTURE_NOT_SAVED);} + if (SavePath[0]==0) {Message("uMod_TextureClient_DX9::SaveTexture( %#llX, %p): %p, SavePath not set\n", pTexture->CRC64, pTexture->m_D3Dtex, this); return (RETURN_TEXTURE_NOT_SAVED);} D3DSURFACE_DESC desc; if (pTexture->m_D3Dtex->GetLevelDesc(0, &desc)!=D3D_OK) //get the format and the size of the texture @@ -385,9 +388,9 @@ int uMod_TextureClient_DX9::SaveTexture(uMod_IDirect3DCubeTexture9* pTexture, bo } wchar_t file[MAX_PATH]; - if (GameName[0]) swprintf_s( file, MAX_PATH, L"%ls\\%ls_W%u_F%u_C_%#llX.dds", SavePath, GameName, desc.Width, desc.Format, pTexture->Hash); - else swprintf_s( file, MAX_PATH, L"%ls\\W%u_F%u_C_%#llX.dds", SavePath, desc.Width, desc.Format, pTexture->Hash); - Message("uMod_TextureClient_DX9::SaveTexture( %ls): %lu\n", file, this); + if (GameName[0]) swprintf_s( file, MAX_PATH, L"%ls\\%ls_W%u_F%u_C_%#llX.dds", SavePath, GameName, desc.Width, desc.Format, pTexture->CRC64); + else swprintf_s( file, MAX_PATH, L"%ls\\W%u_F%u_C_%#llX.dds", SavePath, desc.Width, desc.Format, pTexture->CRC64); + Message("uMod_TextureClient_DX9::SaveTexture( %ls): %p\n", file, this); return (SaveTexture( pTexture->m_D3Dtex, file)); @@ -524,7 +527,7 @@ int uMod_TextureClient_DX9::MergeUpdate(void) if (NumberOfUpdate<0) {return (RETURN_OK);} if (int ret = LockMutex()) {gl_ErrorState |= uMod_ERROR_TEXTURE ; return (ret);} - Message("uMod_TextureClient_DX9::MergeUpdate(): %lu\n", this); + Message("uMod_TextureClient_DX9::MergeUpdate(): %p\n", this); for (int i=0; iHash); + Message("MergeUpdate(): textures not switched %#llX\n", pRefTexture->CRC64); fake_Texture->Release(); } else @@ -609,7 +612,7 @@ int uMod_TextureClient_DX9::MergeUpdate(void) if (int ret = LoadTexture( & (Update[pos_new]), &fake_Texture)) return (ret); if (SwitchTextures( fake_Texture, pRefTexture)) { - Message("MergeUpdate(): textures not switched %#lX\n", pRefTexture->Hash); + Message("MergeUpdate(): textures not switched %#llX\n", pRefTexture->CRC64); fake_Texture->Release(); } else @@ -630,7 +633,7 @@ int uMod_TextureClient_DX9::MergeUpdate(void) if (int ret = LoadTexture( & (Update[pos_new]), &fake_Texture)) return (ret); if (SwitchTextures( fake_Texture, pRefTexture)) { - Message("MergeUpdate(): textures not switched %#lX\n", pRefTexture->Hash); + Message("MergeUpdate(): textures not switched %#llX\n", pRefTexture->CRC64); fake_Texture->Release(); } else @@ -735,16 +738,16 @@ int uMod_TextureClient_DX9::MergeUpdate(void) int uMod_TextureClient_DX9::LookUpToMod( uMod_IDirect3DTexture9* pTexture, int num_index_list, int *index_list) // should only be called for original textures { - Message("uMod_TextureClient_DX9::LookUpToMod( %p): hash: %#llX, %p\n", pTexture, pTexture->Hash, this); + Message("uMod_TextureClient_DX9::LookUpToMod( %p): hash: %#llX, %p\n", pTexture, pTexture->CRC64, this); if (pTexture->CrossRef_D3Dtex!=NULL) return (RETURN_OK); // bug, this texture is already switched - int index = GetIndex( pTexture->Hash, num_index_list, index_list); + int index = GetIndex( pTexture->CRC64, num_index_list, index_list); if (index>=0) { uMod_IDirect3DTexture9 *fake_Texture; if (int ret = LoadTexture( & (FileToMod[index]), &fake_Texture)) return (ret); if (SwitchTextures( fake_Texture, pTexture)) { - Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#lX\n", FileToMod[index].Hash); + Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#llX\n", FileToMod[index].Hash); fake_Texture->Release(); } else @@ -759,16 +762,16 @@ int uMod_TextureClient_DX9::LookUpToMod( uMod_IDirect3DTexture9* pTexture, int n fake_Texture->Reference = index; } } - else if (BoolComputeCRC && pTexture->CRC>0) + else if (BoolComputeCRC && pTexture->CRC32>0) { - index = GetIndex( pTexture->CRC, num_index_list, index_list); + index = GetIndex( pTexture->CRC32, num_index_list, index_list); if (index>=0) { uMod_IDirect3DTexture9 *fake_Texture; if (int ret = LoadTexture( & (FileToMod[index]), &fake_Texture)) return (ret); if (SwitchTextures( fake_Texture, pTexture)) { - Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#lX\n", FileToMod[index].Hash); + Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#llX\n", FileToMod[index].Hash); fake_Texture->Release(); } else @@ -789,16 +792,16 @@ int uMod_TextureClient_DX9::LookUpToMod( uMod_IDirect3DTexture9* pTexture, int n int uMod_TextureClient_DX9::LookUpToMod( uMod_IDirect3DVolumeTexture9* pTexture, int num_index_list, int *index_list) // should only be called for original textures { - Message("uMod_TextureClient_DX9::LookUpToMod( Volume %p): hash: %#llX, %p\n", pTexture, pTexture->Hash, this); + Message("uMod_TextureClient_DX9::LookUpToMod( Volume %p): hash: %#llX, %p\n", pTexture, pTexture->CRC64, this); if (pTexture->CrossRef_D3Dtex!=NULL) return (RETURN_OK); // bug, this texture is already switched - int index = GetIndex( pTexture->Hash, num_index_list, index_list); + int index = GetIndex( pTexture->CRC64, num_index_list, index_list); if (index>=0) { uMod_IDirect3DVolumeTexture9 *fake_Texture; if (int ret = LoadTexture( & (FileToMod[index]), &fake_Texture)) return (ret); if (SwitchTextures( fake_Texture, pTexture)) { - Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#lX\n", FileToMod[index].Hash); + Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#llX\n", FileToMod[index].Hash); fake_Texture->Release(); } else @@ -813,16 +816,16 @@ int uMod_TextureClient_DX9::LookUpToMod( uMod_IDirect3DVolumeTexture9* pTexture, fake_Texture->Reference = index; } } - else if (BoolComputeCRC && pTexture->CRC>0) + else if (BoolComputeCRC && pTexture->CRC32>0) { - index = GetIndex( pTexture->CRC, num_index_list, index_list); + index = GetIndex( pTexture->CRC32, num_index_list, index_list); if (index>=0) { uMod_IDirect3DVolumeTexture9 *fake_Texture; if (int ret = LoadTexture( & (FileToMod[index]), &fake_Texture)) return (ret); if (SwitchTextures( fake_Texture, pTexture)) { - Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#lX\n", FileToMod[index].Hash); + Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#llX\n", FileToMod[index].Hash); fake_Texture->Release(); } else @@ -843,16 +846,16 @@ int uMod_TextureClient_DX9::LookUpToMod( uMod_IDirect3DVolumeTexture9* pTexture, int uMod_TextureClient_DX9::LookUpToMod( uMod_IDirect3DCubeTexture9* pTexture, int num_index_list, int *index_list) // should only be called for original textures { - Message("uMod_TextureClient_DX9::LookUpToMod( Cube %p): hash: %#llX, %p\n", pTexture, pTexture->Hash, this); + Message("uMod_TextureClient_DX9::LookUpToMod( Cube %p): hash: %#llX, %p\n", pTexture, pTexture->CRC64, this); if (pTexture->CrossRef_D3Dtex!=NULL) return (RETURN_OK); // bug, this texture is already switched - int index = GetIndex( pTexture->Hash, num_index_list, index_list); + int index = GetIndex( pTexture->CRC64, num_index_list, index_list); if (index>=0) { uMod_IDirect3DCubeTexture9 *fake_Texture; if (int ret = LoadTexture( & (FileToMod[index]), &fake_Texture)) return (ret); if (SwitchTextures( fake_Texture, pTexture)) { - Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#lX\n", FileToMod[index].Hash); + Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#llX\n", FileToMod[index].Hash); fake_Texture->Release(); } else @@ -867,16 +870,16 @@ int uMod_TextureClient_DX9::LookUpToMod( uMod_IDirect3DCubeTexture9* pTexture, i fake_Texture->Reference = index; } } - else if (BoolComputeCRC && pTexture->CRC>0) + else if (BoolComputeCRC && pTexture->CRC32>0) { - index = GetIndex( pTexture->CRC, num_index_list, index_list); + index = GetIndex( pTexture->CRC32, num_index_list, index_list); if (index>=0) { uMod_IDirect3DCubeTexture9 *fake_Texture; if (int ret = LoadTexture( & (FileToMod[index]), &fake_Texture)) return (ret); if (SwitchTextures( fake_Texture, pTexture)) { - Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#lX\n", FileToMod[index].Hash); + Message("uMod_TextureClient_DX9::LookUpToMod(): textures not switched %#llX\n", FileToMod[index].Hash); fake_Texture->Release(); } else diff --git a/uMod_DXMain/uMod_Defines.h b/uMod_DXMain/uMod_Defines.h index d6c0229..68b236e 100644 --- a/uMod_DXMain/uMod_Defines.h +++ b/uMod_DXMain/uMod_Defines.h @@ -27,15 +27,15 @@ extern FILE *gl_File; #define Message(...) {if (gl_File!=NULL) {fprintf( gl_File, __VA_ARGS__); fflush(gl_File);}} #if INJECTION_METHOD == HOOK_INJECTION -#define OpenMessage(...) {if (fopen_s( &gl_File, "uMod_log.txt", "wt")) gl_File=NULL; else fprintf( gl_File, "HI R40: 0000002\n");} +#define OpenMessage(...) {if (fopen_s( &gl_File, "uMod_log.txt", "wt")) gl_File=NULL; else fprintf( gl_File, "HI R49: 0000001\n");} #endif #if INJECTION_METHOD == DIRECT_INJECTION -#define OpenMessage(...) {if (fopen_s( &gl_File, "uMod_log.txt", "wt")) gl_File=NULL; else fprintf( gl_File, "DI R40: 0000002\n");} +#define OpenMessage(...) {if (fopen_s( &gl_File, "uMod_log.txt", "wt")) gl_File=NULL; else fprintf( gl_File, "DI R49: 0000001\n");} #endif #if INJECTION_METHOD == NO_INJECTION -#define OpenMessage(...) {if (fopen_s( &gl_File, "uMod_log.txt", "wt")) gl_File=NULL; else fprintf( gl_File, "NI R40: 0000002\n");} +#define OpenMessage(...) {if (fopen_s( &gl_File, "uMod_log.txt", "wt")) gl_File=NULL; else fprintf( gl_File, "NI R49: 0000001\n");} #endif #define CloseMessage(...) {if (gl_File!=NULL) fclose(gl_File);} diff --git a/uMod_DXMain/uMod_Main.h b/uMod_DXMain/uMod_Main.h index 1c44994..7a136f0 100644 --- a/uMod_DXMain/uMod_Main.h +++ b/uMod_DXMain/uMod_Main.h @@ -49,6 +49,9 @@ along with Universal Modding Engine. If not, see #include "../uMod_Utils.h" #include "uMod_Defines.h" + +#include "uMod_TextureFunction.h" + #include "uMod_DXMain_dll.h" diff --git a/uMod_DXMain/uMod_TextureClient.h b/uMod_DXMain/uMod_TextureClient.h index 8a86acc..d398d15 100644 --- a/uMod_DXMain/uMod_TextureClient.h +++ b/uMod_DXMain/uMod_TextureClient.h @@ -61,7 +61,6 @@ public: */ virtual int SaveSingleTexture(bool val) = 0; - /** * Enable/Disable the string in the left upper corner during save single texture mode (called from the mainloop). * @param[in] val @@ -69,6 +68,13 @@ public: */ int ShowTextureString(bool val) {BoolShowTextureString = val; return (RETURN_OK);} + /** + * Enable/Disable the string in the left upper corner during save single texture mode (called from the mainloop). + * @param[in] val + * @return + */ + int ShowSingleTexture(bool val) {BoolShowSingleTexture = val; return (RETURN_OK);} + /** * Enable/Disable the hashing with CRC32, which is needed to support also tpf mods (called from the mainloop). * @param[in] val @@ -172,6 +178,7 @@ public: bool BoolSaveAllTextures; //!< true if all textures should be saved bool BoolSaveSingleTexture; //!< true if "save single texture" mode is enabled bool BoolShowTextureString; //!< true if a string should be displayed during "save single texture" mode is enabled + bool BoolShowSingleTexture; //!< true if the texture should be displayed during "save single texture" mode is enabled bool BoolComputeCRC; //!< if true also the crc32 is calculated, which is need to support tpf int KeyBack; //!< key value for going to the previous texture diff --git a/uMod_DXMain/uMod_TextureFunction.cpp b/uMod_DXMain/uMod_TextureFunction.cpp index 8398f14..c914fce 100644 --- a/uMod_DXMain/uMod_TextureFunction.cpp +++ b/uMod_DXMain/uMod_TextureFunction.cpp @@ -20,25 +20,6 @@ along with Universal Modding Engine. If not, see #include "uMod_Main.h" -void GetHash( unsigned char *str, unsigned int len, DWORD64 &hash) // estimate the hash -{ - DWORD32 hash1 = hash & 0xFFFFFFFF; - DWORD32 hash2 = hash>>32 & 0xFFFFFFFF; - - for (int i=0; i= 0; j--) + { // Do eight times. + unsigned int mask = -(crc & 1); + crc = (crc >> 1) ^ (CRC32POLY & mask); + } + table[byte] = crc; + } + } + + // Through with table setup, now calculate the CRC. + + for (unsigned int i=0; i> 8) ^ table[(crc ^ byte) & 0xFF]; + } +} + +/* + * poly 0x95AC9329AC4BC9B5ULL and init 0xFFFFFFFFFFFFFFFFULL + */ +const DWORD64 crctab64[256] = { + 0x0000000000000000ULL, 0x7ad870c830358979ULL, 0xf5b0e190606b12f2ULL, + 0x8f689158505e9b8bULL, 0xc038e5739841b68fULL, 0xbae095bba8743ff6ULL, + 0x358804e3f82aa47dULL, 0x4f50742bc81f2d04ULL, 0xab28ecb46814fe75ULL, + 0xd1f09c7c5821770cULL, 0x5e980d24087fec87ULL, 0x24407dec384a65feULL, + 0x6b1009c7f05548faULL, 0x11c8790fc060c183ULL, 0x9ea0e857903e5a08ULL, + 0xe478989fa00bd371ULL, 0x7d08ff3b88be6f81ULL, 0x07d08ff3b88be6f8ULL, + 0x88b81eabe8d57d73ULL, 0xf2606e63d8e0f40aULL, 0xbd301a4810ffd90eULL, + 0xc7e86a8020ca5077ULL, 0x4880fbd87094cbfcULL, 0x32588b1040a14285ULL, + 0xd620138fe0aa91f4ULL, 0xacf86347d09f188dULL, 0x2390f21f80c18306ULL, + 0x594882d7b0f40a7fULL, 0x1618f6fc78eb277bULL, 0x6cc0863448deae02ULL, + 0xe3a8176c18803589ULL, 0x997067a428b5bcf0ULL, 0xfa11fe77117cdf02ULL, + 0x80c98ebf2149567bULL, 0x0fa11fe77117cdf0ULL, 0x75796f2f41224489ULL, + 0x3a291b04893d698dULL, 0x40f16bccb908e0f4ULL, 0xcf99fa94e9567b7fULL, + 0xb5418a5cd963f206ULL, 0x513912c379682177ULL, 0x2be1620b495da80eULL, + 0xa489f35319033385ULL, 0xde51839b2936bafcULL, 0x9101f7b0e12997f8ULL, + 0xebd98778d11c1e81ULL, 0x64b116208142850aULL, 0x1e6966e8b1770c73ULL, + 0x8719014c99c2b083ULL, 0xfdc17184a9f739faULL, 0x72a9e0dcf9a9a271ULL, + 0x08719014c99c2b08ULL, 0x4721e43f0183060cULL, 0x3df994f731b68f75ULL, + 0xb29105af61e814feULL, 0xc849756751dd9d87ULL, 0x2c31edf8f1d64ef6ULL, + 0x56e99d30c1e3c78fULL, 0xd9810c6891bd5c04ULL, 0xa3597ca0a188d57dULL, + 0xec09088b6997f879ULL, 0x96d1784359a27100ULL, 0x19b9e91b09fcea8bULL, + 0x636199d339c963f2ULL, 0xdf7adabd7a6e2d6fULL, 0xa5a2aa754a5ba416ULL, + 0x2aca3b2d1a053f9dULL, 0x50124be52a30b6e4ULL, 0x1f423fcee22f9be0ULL, + 0x659a4f06d21a1299ULL, 0xeaf2de5e82448912ULL, 0x902aae96b271006bULL, + 0x74523609127ad31aULL, 0x0e8a46c1224f5a63ULL, 0x81e2d7997211c1e8ULL, + 0xfb3aa75142244891ULL, 0xb46ad37a8a3b6595ULL, 0xceb2a3b2ba0eececULL, + 0x41da32eaea507767ULL, 0x3b024222da65fe1eULL, 0xa2722586f2d042eeULL, + 0xd8aa554ec2e5cb97ULL, 0x57c2c41692bb501cULL, 0x2d1ab4dea28ed965ULL, + 0x624ac0f56a91f461ULL, 0x1892b03d5aa47d18ULL, 0x97fa21650afae693ULL, + 0xed2251ad3acf6feaULL, 0x095ac9329ac4bc9bULL, 0x7382b9faaaf135e2ULL, + 0xfcea28a2faafae69ULL, 0x8632586aca9a2710ULL, 0xc9622c4102850a14ULL, + 0xb3ba5c8932b0836dULL, 0x3cd2cdd162ee18e6ULL, 0x460abd1952db919fULL, + 0x256b24ca6b12f26dULL, 0x5fb354025b277b14ULL, 0xd0dbc55a0b79e09fULL, + 0xaa03b5923b4c69e6ULL, 0xe553c1b9f35344e2ULL, 0x9f8bb171c366cd9bULL, + 0x10e3202993385610ULL, 0x6a3b50e1a30ddf69ULL, 0x8e43c87e03060c18ULL, + 0xf49bb8b633338561ULL, 0x7bf329ee636d1eeaULL, 0x012b592653589793ULL, + 0x4e7b2d0d9b47ba97ULL, 0x34a35dc5ab7233eeULL, 0xbbcbcc9dfb2ca865ULL, + 0xc113bc55cb19211cULL, 0x5863dbf1e3ac9decULL, 0x22bbab39d3991495ULL, + 0xadd33a6183c78f1eULL, 0xd70b4aa9b3f20667ULL, 0x985b3e827bed2b63ULL, + 0xe2834e4a4bd8a21aULL, 0x6debdf121b863991ULL, 0x1733afda2bb3b0e8ULL, + 0xf34b37458bb86399ULL, 0x8993478dbb8deae0ULL, 0x06fbd6d5ebd3716bULL, + 0x7c23a61ddbe6f812ULL, 0x3373d23613f9d516ULL, 0x49aba2fe23cc5c6fULL, + 0xc6c333a67392c7e4ULL, 0xbc1b436e43a74e9dULL, 0x95ac9329ac4bc9b5ULL, + 0xef74e3e19c7e40ccULL, 0x601c72b9cc20db47ULL, 0x1ac40271fc15523eULL, + 0x5594765a340a7f3aULL, 0x2f4c0692043ff643ULL, 0xa02497ca54616dc8ULL, + 0xdafce7026454e4b1ULL, 0x3e847f9dc45f37c0ULL, 0x445c0f55f46abeb9ULL, + 0xcb349e0da4342532ULL, 0xb1eceec59401ac4bULL, 0xfebc9aee5c1e814fULL, + 0x8464ea266c2b0836ULL, 0x0b0c7b7e3c7593bdULL, 0x71d40bb60c401ac4ULL, + 0xe8a46c1224f5a634ULL, 0x927c1cda14c02f4dULL, 0x1d148d82449eb4c6ULL, + 0x67ccfd4a74ab3dbfULL, 0x289c8961bcb410bbULL, 0x5244f9a98c8199c2ULL, + 0xdd2c68f1dcdf0249ULL, 0xa7f41839ecea8b30ULL, 0x438c80a64ce15841ULL, + 0x3954f06e7cd4d138ULL, 0xb63c61362c8a4ab3ULL, 0xcce411fe1cbfc3caULL, + 0x83b465d5d4a0eeceULL, 0xf96c151de49567b7ULL, 0x76048445b4cbfc3cULL, + 0x0cdcf48d84fe7545ULL, 0x6fbd6d5ebd3716b7ULL, 0x15651d968d029fceULL, + 0x9a0d8ccedd5c0445ULL, 0xe0d5fc06ed698d3cULL, 0xaf85882d2576a038ULL, + 0xd55df8e515432941ULL, 0x5a3569bd451db2caULL, 0x20ed197575283bb3ULL, + 0xc49581ead523e8c2ULL, 0xbe4df122e51661bbULL, 0x3125607ab548fa30ULL, + 0x4bfd10b2857d7349ULL, 0x04ad64994d625e4dULL, 0x7e7514517d57d734ULL, + 0xf11d85092d094cbfULL, 0x8bc5f5c11d3cc5c6ULL, 0x12b5926535897936ULL, + 0x686de2ad05bcf04fULL, 0xe70573f555e26bc4ULL, 0x9ddd033d65d7e2bdULL, + 0xd28d7716adc8cfb9ULL, 0xa85507de9dfd46c0ULL, 0x273d9686cda3dd4bULL, + 0x5de5e64efd965432ULL, 0xb99d7ed15d9d8743ULL, 0xc3450e196da80e3aULL, + 0x4c2d9f413df695b1ULL, 0x36f5ef890dc31cc8ULL, 0x79a59ba2c5dc31ccULL, + 0x037deb6af5e9b8b5ULL, 0x8c157a32a5b7233eULL, 0xf6cd0afa9582aa47ULL, + 0x4ad64994d625e4daULL, 0x300e395ce6106da3ULL, 0xbf66a804b64ef628ULL, + 0xc5bed8cc867b7f51ULL, 0x8aeeace74e645255ULL, 0xf036dc2f7e51db2cULL, + 0x7f5e4d772e0f40a7ULL, 0x05863dbf1e3ac9deULL, 0xe1fea520be311aafULL, + 0x9b26d5e88e0493d6ULL, 0x144e44b0de5a085dULL, 0x6e963478ee6f8124ULL, + 0x21c640532670ac20ULL, 0x5b1e309b16452559ULL, 0xd476a1c3461bbed2ULL, + 0xaeaed10b762e37abULL, 0x37deb6af5e9b8b5bULL, 0x4d06c6676eae0222ULL, + 0xc26e573f3ef099a9ULL, 0xb8b627f70ec510d0ULL, 0xf7e653dcc6da3dd4ULL, + 0x8d3e2314f6efb4adULL, 0x0256b24ca6b12f26ULL, 0x788ec2849684a65fULL, + 0x9cf65a1b368f752eULL, 0xe62e2ad306bafc57ULL, 0x6946bb8b56e467dcULL, + 0x139ecb4366d1eea5ULL, 0x5ccebf68aecec3a1ULL, 0x2616cfa09efb4ad8ULL, + 0xa97e5ef8cea5d153ULL, 0xd3a62e30fe90582aULL, 0xb0c7b7e3c7593bd8ULL, + 0xca1fc72bf76cb2a1ULL, 0x45775673a732292aULL, 0x3faf26bb9707a053ULL, + 0x70ff52905f188d57ULL, 0x0a2722586f2d042eULL, 0x854fb3003f739fa5ULL, + 0xff97c3c80f4616dcULL, 0x1bef5b57af4dc5adULL, 0x61372b9f9f784cd4ULL, + 0xee5fbac7cf26d75fULL, 0x9487ca0fff135e26ULL, 0xdbd7be24370c7322ULL, + 0xa10fceec0739fa5bULL, 0x2e675fb4576761d0ULL, 0x54bf2f7c6752e8a9ULL, + 0xcdcf48d84fe75459ULL, 0xb71738107fd2dd20ULL, 0x387fa9482f8c46abULL, + 0x42a7d9801fb9cfd2ULL, 0x0df7adabd7a6e2d6ULL, 0x772fdd63e7936bafULL, + 0xf8474c3bb7cdf024ULL, 0x829f3cf387f8795dULL, 0x66e7a46c27f3aa2cULL, + 0x1c3fd4a417c62355ULL, 0x935745fc4798b8deULL, 0xe98f353477ad31a7ULL, + 0xa6df411fbfb21ca3ULL, 0xdc0731d78f8795daULL, 0x536fa08fdfd90e51ULL, + 0x29b7d047efec8728ULL +}; + diff --git a/uMod_DXMain/uMod_TextureFunction.h b/uMod_DXMain/uMod_TextureFunction.h index 1478840..a25253e 100644 --- a/uMod_DXMain/uMod_TextureFunction.h +++ b/uMod_DXMain/uMod_TextureFunction.h @@ -20,32 +20,39 @@ along with Universal Modding Engine. If not, see -// this value is simply the init value for the D.J. Bernsteins algorithm (djb2) -#define HASH_INIT_VALUE 5381 +inline void InitCRC64(DWORD64 &crc) +{ + crc = 0xFFFFFFFFFFFFFFFFULL; +} +extern const DWORD64 crctab64[256]; /** - * This hash function combines the djb2 and sdbm algorithm. Both are designed for hashing strings, - * but compared to many others they don't include a Hash*data[i] operation, which would reset the hash - * if data[i]==0. This is never the case while hashing text, but it is very often the case when hashing - * textures. Therefore many good algorithm cannot be used at this point. - * - * Here are two algorithm used to reduce the number of collision (collision == two different textures have - * the same hash value) + * CRC64 function * * @param[in] data pointer to the buffer * @param[in] len length of buffer in bytes * @param[in,out] hash in = initial value of the hash; out = returned hash */ -void GetHash( unsigned char *str, unsigned int len, DWORD64 &hash); // estimate the hash +inline void GetCRC64(DWORD64 &crc, const unsigned char *cp, unsigned long length) +{ + while (length--) + crc = crctab64[(crc ^ *(cp++)) & 0xff] ^ (crc >> 8); +} + +inline void InitCRC32(DWORD32 &crc) +{ + crc = 0xffffffff; +} + /** * Caluclate the CRC32 value over a buffer. This function is used in texmod. Thanks to RS for given me this information! * @param[in] data pointer to the buffer * @param[in] len length of buffer in bytes * @return CRC32 value */ -DWORD32 GetCRC32( char *data, unsigned int len); +void GetCRC32( DWORD32 &crc, unsigned char *data, unsigned int len); /* case D3DFMT_MULTI2_ARGB8: case D3DFMT_VERTEXDATA: diff --git a/uMod_DXMain/uMod_TextureServer.cpp b/uMod_DXMain/uMod_TextureServer.cpp index bd0abbc..8d0e234 100644 --- a/uMod_DXMain/uMod_TextureServer.cpp +++ b/uMod_DXMain/uMod_TextureServer.cpp @@ -22,7 +22,7 @@ along with Universal Modding Engine. If not, see uMod_TextureServer::uMod_TextureServer(wchar_t *game) { - Message("uMod_TextureServer(void): %lu\n", this); + Message("uMod_TextureServer(void): %p\n", this); Mutex = CreateMutex(NULL, false, NULL); @@ -32,6 +32,7 @@ uMod_TextureServer::uMod_TextureServer(wchar_t *game) BoolSaveAllTextures = false; BoolSaveSingleTexture = false; BoolShowTextureString = false; + BoolShowSingleTexture = false; BoolSupportTPF = false; SavePath[0] = 0; @@ -74,15 +75,25 @@ uMod_TextureServer::uMod_TextureServer(wchar_t *game) uMod_TextureServer::~uMod_TextureServer(void) { - Message("~uMod_TextureServer(void): %lu\n", this); + Message("~uMod_TextureServer(void): %p\n", this); if (Mutex != NULL) CloseHandle(Mutex); //delete the files in memory int num = CurrentMod.GetNumber(); - for (int i = 0; i < num; i++) delete[] CurrentMod[i]->pData; //delete the file content of the texture + for (int i = 0; i < num; i++) + { + delete [] CurrentMod[i]->pData; //delete the file content of the texture + delete CurrentMod[i]; //delete the structure + } num = OldMod.GetNumber(); - for (int i = 0; i < num; i++) delete[] OldMod[i]->pData; //delete the file content of the texture + for (int i = 0; i < num; i++) + { + delete [] OldMod[i]->pData; //delete the file content of the texture + delete OldMod[i]; //delete the structure + } + + if (Clients != NULL) delete [] Clients; if (Pipe.In != INVALID_HANDLE_VALUE ) CloseHandle(Pipe.In); Pipe.In = INVALID_HANDLE_VALUE; @@ -92,7 +103,7 @@ uMod_TextureServer::~uMod_TextureServer(void) int uMod_TextureServer::AddClient(uMod_TextureClient *client, TextureFileStruct* &update, int &number, const int version) // called from a client { - Message("AddClient(%lu): %lu\n", client, this); + Message("AddClient(%p): %p\n", client, this); if (int ret = LockMutex()) { gl_ErrorState |= uMod_ERROR_SERVER; @@ -106,6 +117,7 @@ int uMod_TextureServer::AddClient(uMod_TextureClient *client, TextureFileStruct* client->SaveAllTextures(BoolSaveAllTextures); client->SaveSingleTexture(BoolSaveSingleTexture); client->ShowTextureString(BoolShowTextureString); + client->ShowSingleTexture(BoolShowSingleTexture); client->SupportTPF(BoolSupportTPF); client->SetSaveDirectory(SavePath); @@ -162,7 +174,7 @@ int uMod_TextureServer::AddClient(uMod_TextureClient *client, TextureFileStruct* int uMod_TextureServer::RemoveClient(uMod_TextureClient *client, const int version) // called from a client { - Message("RemoveClient(%lu): %lu\n", client); + Message("RemoveClient(%p): %p\n", client); if (int ret = LockMutex()) { gl_ErrorState |= uMod_ERROR_SERVER; @@ -200,8 +212,13 @@ int uMod_TextureServer::RemoveClient(uMod_TextureClient *client, const int versi int uMod_TextureServer::AddFile( char* buffer, DWORD64 size, DWORD64 hash, bool force) // called from Mainloop() { - Message("uMod_TextureServer::AddFile( %p %llu, %#llX, %d): %lu\n", buffer, size, hash, force, this); + Message("uMod_TextureServer::AddFile( %p %llu, %#llX, %d): %p\n", buffer, size, hash, force, this); + if (int ret = LockMutex()) + { + gl_ErrorState |= uMod_ERROR_SERVER; + return (ret); + } TextureFileStruct* temp = NULL; int num = CurrentMod.GetNumber(); @@ -258,101 +275,22 @@ int uMod_TextureServer::AddFile( char* buffer, DWORD64 size, DWORD64 hash, bool //else temp->ForceReload = force; - Message("uMod_TextureServer::End AddFile(%#lX)\n", hash); - if (new_file) return (CurrentMod.Add(temp)); // new files must be added to the list of the CurrentMod - else return (RETURN_OK); + Message("uMod_TextureServer::End AddFile(%#llX)\n", hash); + if (new_file) CurrentMod.Add(temp); // new files must be added to the list of the CurrentMod+ + + return (UnlockMutex()); } -/* -int uMod_TextureServer::AddFile(wchar_t* file_name, MyTypeHash hash, bool force) // called from Mainloop -// this functions does the same, but loads the file content from disk -{ - Message("uMod_TextureServer::AddFile( %ls, %#lX, %d): %lu\n", file_name, hash, force, this); - TextureFileStruct* temp = NULL; - - int num = CurrentMod.GetNumber(); - for (int i = 0; i < num; i++) if (CurrentMod[i]->Hash == hash) - { - if (force) {temp = CurrentMod[i]; break;} - else return (RETURN_OK); - } - if (temp==NULL) - { - num = OldMod.GetNumber(); - for (int i = 0; i < num; i++) if (OldMod[i]->Hash == hash) - { - temp = OldMod[i]; - OldMod.Remove(temp); - CurrentMod.Add(temp); - if (force) break; - else return (RETURN_OK); - } - } - - FILE* file; - if (_wfopen_s(&file, file_name, L"rb") != 0) - { - Message("AddFile( ): file not found\n"); - return (RETURN_FILE_NOT_LOADED); - } - - fseek (file, 0, SEEK_END); - unsigned int size = ftell(file); - fseek (file, 0, SEEK_SET); - - bool new_file = true; - if (temp!=NULL) - { - new_file = false; - if (temp->pData!=NULL) delete [] temp->pData; - temp->pData = NULL; - } - else - { - new_file = true; - temp = new TextureFileStruct; - temp->Reference = -1; - } - - try - { - temp->pData = new char[size]; - } - catch (...) - { - if (!new_file) CurrentMod.Remove( temp); - delete temp; - gl_ErrorState |= uMod_ERROR_MEMORY | uMod_ERROR_SERVER; - return (RETURN_NO_MEMORY); - } - int result = fread(temp->pData, 1, size, file); - fclose(file); - if (result != size) - { - delete[] temp->pData; - if (!new_file) CurrentMod.Remove( temp); - delete temp; - return (RETURN_FILE_NOT_LOADED); - } - - temp->Size = size; - temp->NumberOfTextures = 0; - temp->Textures = NULL; - temp->Hash = hash; - - if (new_file) temp->ForceReload = false; - else temp->ForceReload = force; - - Message("End AddFile(%#lX)\n", hash); - if (new_file) return (CurrentMod.Add(temp)); - else return (RETURN_OK); -} -*/ int uMod_TextureServer::RemoveFile(DWORD64 hash) // called from Mainloop() { - Message("RemoveFile( %lu): %lu\n", hash, this); + Message("RemoveFile( %#llx): %p\n", hash, this); + if (int ret = LockMutex()) + { + gl_ErrorState |= uMod_ERROR_SERVER; + return (ret); + } int num = CurrentMod.GetNumber(); for (int i = 0; i < num; i++) if (CurrentMod[i]->Hash == hash) { @@ -360,7 +298,7 @@ int uMod_TextureServer::RemoveFile(DWORD64 hash) // called from Mainloop() CurrentMod.Remove(temp); return (OldMod.Add(temp)); } - return (RETURN_OK); + return (UnlockMutex()); } int uMod_TextureServer::SaveAllTextures(bool val) // called from Mainloop() @@ -414,6 +352,23 @@ int uMod_TextureServer::ShowTextureString(bool val) // called from Mainloop() return (UnlockMutex()); } +int uMod_TextureServer::ShowSingleTexture(bool val) // called from Mainloop() +{ + if (BoolShowSingleTexture == val) return (RETURN_OK); + BoolShowSingleTexture = val; + + if (int ret = LockMutex()) + { + gl_ErrorState |= uMod_ERROR_SERVER; + return (ret); + } + for (int i = 0; i < NumberOfClients; i++) + { + Clients[i]->ShowSingleTexture(BoolShowSingleTexture); + } + return (UnlockMutex()); +} + int uMod_TextureServer::SupportTPF(bool val) // called from Mainloop() { if (BoolSupportTPF == val) return (RETURN_OK); @@ -433,7 +388,7 @@ int uMod_TextureServer::SupportTPF(bool val) // called from Mainloop() int uMod_TextureServer::SetSaveDirectory(wchar_t *dir) // called from Mainloop() { - Message("uMod_TextureServer::SetSaveDirectory( %ls): %lu\n", dir, this); + Message("uMod_TextureServer::SetSaveDirectory( %ls): %p\n", dir, this); int i = 0; for (i = 0; i < MAX_PATH && (dir[i]); i++) SavePath[i] = dir[i]; if (i == MAX_PATH) @@ -512,7 +467,7 @@ int uMod_TextureServer::SetFontColour(DWORD64 colour) // called from Mainloop() return (ret); } FontColour = colour; - Message("uMod_TextureServer::SetFontColour( %#lX): %lu\n",colour, this); + Message("uMod_TextureServer::SetFontColour( %#llX): %p\n",colour, this); for (int i = 0; i < NumberOfClients; i++) { Clients[i]->SetFontColour( colour); @@ -529,7 +484,7 @@ int uMod_TextureServer::SetTextureColour(DWORD64 colour) // called from Mainloop return (ret); } TextureColour = colour; - Message("uMod_TextureServer::SetTextureColour( %#lX): %lu\n", colour, this); + Message("uMod_TextureServer::SetTextureColour( %#llX): %p\n", colour, this); for (int i = 0; i < NumberOfClients; i++) { Clients[i]->SetTextureColour( colour); @@ -545,7 +500,7 @@ int uMod_TextureServer::SetFileFormat(DWORD64 format) // called from Mainloop() return (ret); } FileFormat = format; - Message("uMod_TextureServer::SetFileFormat( %lu): %lu\n", format, this); + Message("uMod_TextureServer::SetFileFormat( %#llX): %p\n", format, this); for (int i = 0; i < NumberOfClients; i++) { Clients[i]->SetFileFormat( format); @@ -561,7 +516,7 @@ int uMod_TextureServer::SetFormatFilter(DWORD64 format) // called from Mainloop( return (ret); } FormatFilter = format; - Message("uMod_TextureServer::SetFormatFilter( %lu): %lu\n", format, this); + Message("uMod_TextureServer::SetFormatFilter( %#llX): %p\n", format, this); for (int i = 0; i < NumberOfClients; i++) { Clients[i]->SetFormatFilter( format); @@ -577,7 +532,7 @@ int uMod_TextureServer::SetWidthFilter(DWORD64 size) // called from Mainloop() return (ret); } WidthFilter = size; - Message("uMod_TextureServer::SetWidthFilter( %lu): %lu\n", size, this); + Message("uMod_TextureServer::SetWidthFilter( %#llX): %p\n", size, this); for (int i = 0; i < NumberOfClients; i++) { Clients[i]->SetWidthFilter( size); @@ -593,7 +548,7 @@ int uMod_TextureServer::SetHeightFilter(DWORD64 size) // called from Mainloop() return (ret); } HeightFilter = size; - Message("uMod_TextureServer::SetHeightFilter( %lu): %lu\n", size, this); + Message("uMod_TextureServer::SetHeightFilter( %#llX): %p\n", size, this); for (int i = 0; i < NumberOfClients; i++) { Clients[i]->SetHeightFilter( size); @@ -609,7 +564,7 @@ int uMod_TextureServer::SetDepthFilter(DWORD64 size) // called from Mainloop() return (ret); } DepthFilter = size; - Message("uMod_TextureServer::SetDepthFilter( %lu): %lu\n", size, this); + Message("uMod_TextureServer::SetDepthFilter( %#llX): %p\n", size, this); for (int i = 0; i < NumberOfClients; i++) { Clients[i]->SetDepthFilter( size); @@ -619,7 +574,7 @@ int uMod_TextureServer::SetDepthFilter(DWORD64 size) // called from Mainloop() int uMod_TextureServer::PropagateUpdate(uMod_TextureClient* client) // called from Mainloop(), send the update to all clients { - Message("PropagateUpdate(%lu): %lu\n", client, this); + Message("PropagateUpdate(%p): %p\n", client, this); if (int ret = LockMutex()) { gl_ErrorState |= uMod_ERROR_TEXTURE; @@ -667,7 +622,8 @@ int TextureFileStruct_Compare( const void * elem1, const void * elem2 ) int uMod_TextureServer::PrepareUpdate(TextureFileStruct* &update, int &number) // called from the PropagateUpdate() and AddClient. // Prepare an update for one client. The allocated memory must deleted by the client. { - Message("PrepareUpdate(%lu, %d): %lu\n", update, number, this); + update = NULL; + number = 0; TextureFileStruct* temp = NULL; int num = CurrentMod.GetNumber(); @@ -684,9 +640,10 @@ int uMod_TextureServer::PrepareUpdate(TextureFileStruct* &update, int &number) / qsort( temp, num, sizeof(TextureFileStruct), TextureFileStruct_Compare); } - update = temp; number = num; + + Message("PrepareUpdate(%p, %d): %p\n", update, number, this); return (RETURN_OK); } #undef cpy_file_struct @@ -841,6 +798,13 @@ int uMod_TextureServer::MainLoop(void) // run as a separated thread else ShowTextureString(true); break; } + case CONTROL_SHOW_TEXTURE: + { + Message("MainLoop: CONTROL_SHOW_TEXTURE (%#llX): %p\n", commands->Value, this); + if (commands->Value == 0) ShowSingleTexture(false); + else ShowSingleTexture(true); + break; + } case CONTROL_SUPPORT_TPF: { Message("MainLoop: CONTROL_SUPPORT_TPF (%#llX): %p\n", commands->Value, this); @@ -850,8 +814,9 @@ int uMod_TextureServer::MainLoop(void) // run as a separated thread } case CONTROL_SET_DIR: { + Message("MainLoop: CONTROL_SET_DIR (%ls): %p\n", (wchar_t*) &buffer[pos + sizeof(MsgStruct)], this); size = commands->Value; - if (pos + sizeof(MsgStruct) +size <= num) SetSaveDirectory( (wchar_t*) &buffer[pos + sizeof(MsgStruct)]); + if (pos + sizeof(MsgStruct) + size <= num) SetSaveDirectory( (wchar_t*) &buffer[pos + sizeof(MsgStruct)]); break; } @@ -918,7 +883,7 @@ int uMod_TextureServer::MainLoop(void) // run as a separated thread default: { - Message("MainLoop: DEFAULT: %lu %#llX %#llX\n", commands->Control, commands->Value, commands->Hash, this); + Message("MainLoop: DEFAULT: %lu %#llX %#llX: %p\n", commands->Control, commands->Value, commands->Hash, this); break; } } diff --git a/uMod_DXMain/uMod_TextureServer.h b/uMod_DXMain/uMod_TextureServer.h index eb30059..ea5077f 100644 --- a/uMod_DXMain/uMod_TextureServer.h +++ b/uMod_DXMain/uMod_TextureServer.h @@ -134,6 +134,13 @@ private: */ int ShowTextureString(bool val); // called from Mainloop() + /** + * Enable/Disable the rendering of the current selected texture in the upper corner during save single texture mode (called from the mainloop). + * @param[in] val + * @return + */ + int ShowSingleTexture(bool val); // called from Mainloop() + /** * Enable/Disable the hashing with CRC32, which is needed to support tpf mods (called from the mainloop). * @param[in] val @@ -249,6 +256,7 @@ private: bool BoolSaveAllTextures; bool BoolSaveSingleTexture; bool BoolShowTextureString; + bool BoolShowSingleTexture; bool BoolSupportTPF; wchar_t SavePath[MAX_PATH]; diff --git a/uMod_GUI/bin/README_Deutsch.txt b/uMod_GUI/bin/README_Deutsch.txt index 7bc70c8..3bb61df 100644 --- a/uMod_GUI/bin/README_Deutsch.txt +++ b/uMod_GUI/bin/README_Deutsch.txt @@ -16,14 +16,14 @@ diese dll nicht mit Universal Modding Engine mit geliefert werden. Wenn diese dl deinem System nicht installiert ist, wird dich Universal Modding Engine darauf hinweisen. -Was kann Universal Modding Engine (uMod) V1.0? +Was kann Universal Modding Engine (uMod) V2.0? -einzelne Texturen aus einem Spiel extrahieren und speichern (Die Zieltextur kann im Spiel geändert werden.) --alle Texturen aus einem Spiel extrahieren und speichern +-alle Texturen aus einem Spiel extrahieren und speichern (mit Größen- und Texturformat-Filter) -Texturen in ein Spiel laden und Zieltexturen ersetzen --Support einzelner dds Texturen --Support von zip-Dateien --Support der originalen TexMod *.tpf Datein +-Support einzelner Texturen +-Support von zip-Dateien (mit Darstellung von Textur-Gruppen als Baumstruktur) +-Support der originalen TexMod *.tpf Dateien (der Support muss über das Kontext-Menü aktiviert werden) Alle diese Optionen können während des Spieles an und aus geschaltet werden! Du kannst also nach einer Textur suchen, diese speichern, sie anschließend @@ -37,12 +37,8 @@ geladen werden. Stellst du diese Option an, w wahrscheinlich nichts geschehen, da alle Texturen für diese Map bereits geladen sind. Wechsle also die Map oder lade erneut. -Zip Dateien können eine "texmod.def" Datei enthalten, welche die Hash-Werte und -Dateinamen enthält. Jede Zeile sollte dem Format "hash|filename.dds" entsprechen. -Wenn es keine "texmod.def" Datei enthält, wird jede Datei entpackt und jene, deren -Namen auf das Wildcard "*_hash.dds" zutrifft, werden benutzt. -Die Zip Datei kann eine "Comment.txt" Datei enthalten. Der Inhalte wird als Kommentar -in der GUI angezeigt. +Zip Dateien können eine "content.txt" Datei enthalten, welche Informationen Gruppen und Texturen enthält. +Siehe dazu die zwei mitgelieferten Beispiele. Wenn du einzelne Dateien lädst, sollten diese dem Wildcard "*_hash.dds" entsprechen. @@ -69,9 +65,9 @@ Es gibt drei Wege wie uMod sich in die DirectX Verbindung einklinken kann: bekannte Probleme: Guild Wars (Win XP) -2) Starte das Spiel direkt durch OTM über das Menü - "Einstellungen->Sarte Spiel durch OTM" oder - "Einstellungen->Sarte Spiel durch OTM (mit Kommandozeile)". +2) Starte das Spiel direkt durch uMod über das Menü + "Einstellungen->Sarte Spiel durch uMod" oder + "Einstellungen->Sarte Spiel durch uMod (mit Kommandozeile)". Das Spiel startet sofort. 3) Kopiere die d3d9.dll (vom uMod Verzeichnis) in das Spiele Verzeichnis. @@ -86,25 +82,31 @@ Universal Modding Engine und danach das Spiel. Starte das Spiel in beiden F Universal Modding Engine . Wenn das Spiel startet und alles glatt läuft, öffnet sich sofort ein neuer Tab in uMod. -In diesem Tab kannst du nun das Spiel modden. Drücke den "Update" Button um -die Einstellungen an das Spiel zu senden. Du kannst deine aktuellen Einstellungen auch -als ein Template speichern. Ein Template kann auch als Standard eingestellt werden. -Es wird dann automatisch geladen und an das Spiel gesendet, wenn -dieses Spiel das nächste Mal gestartet wird. +Im obersten Feld steht die Methode, welche verwendet wurde um sich in das Spiel +ein zu klinken. Danach sollten Informationen über erzeugte DirectX Devices stehen. +z.B. (DX9) oder (DX9EX: 3). Dies gibt an, welches Device und wie viele durch das Spiel +gestartet (und von uMod umgeleitet) wurden. Fehlt diese Information, so ist das Modden +nicht möglich. Mögliche Ursachen können sein, dass das Spiel kein DirectX 9 verwendet +oder uMod zu langsam war, um sich in das Spiel ein zu klinken. -Um einen Mod zu laden, musst du das Häkchen vor den Namen setzen. Wenn du -den Mod entladen willst, entferne das Häkchen und klicke auf "Update". -Der Update Button lädt nur Veränderungen (wenn 1) Pakete aus der Liste entfernt -wurden, 2) du die Häkchen verändert hast oder 3) sich die Reihenfolge geändert hat) -Der "neu laden" Button erzwingt das Neuladen von Festplatte (z.B. wenn du die -Texturen verändert hast). +Wenn Du Veränderungen vornimmst, so musst Du diese über das Kontext-Menü +(rechte Maustaste im unteren Fenster) -> Update bestätigen. +Update (neu laden) bewirkt, dass die Texturen neu von der Festplatte geladen werden. +Diese Option ist nur für diejenigen sinnvoll, die Texturen editieren. -Weil verschiedene Mods die gleiche Ziel-Textur verändern könnten, wird nur die -Mod-Textur des ersten Mods berücksichtigt. Die Aktion dieses Mods (also Laden oder -Entladen) wird durchgeführt, ungeachtet dessen, was die restlichen Mods mit der -Ziel-Textur machen sollen. +Neue Mods oder Texturen kannst Du über das Kontext-Menü oder per Drag&Drop aus +dem Explorer heraus öffnen. Die Reihenfolge der Mods veränderst Du ebenfalls über +Drag&Drop. Die Reihenfolge kann dann wichtig sein, wenn zwei Mods, die gleiche +Spiele-Textur verändern wollen. Dann wird die Änderung des Mods aktiv, welcher weiter +oben gelistet ist. +Zusätzlich kannst Du jeden Mod aufklappen und einzelne Texturen aktivieren oder deaktivieren. +Mods mit dem neuen Mod-Format können auch mehrere Ebenen von Gruppen enthalten. +Hier kannst du einzelne Gruppen aktivieren und deaktivieren. +Während bei Texturen immer eines der beiden Häkchen gesetzt sein muss, können beim +Mod-Paket oder bei Gruppen beide Häkchen entfernt werden, um die Kontrolle an die nächst +tiefer liegende Ebene zu übergeben. Wie bekommt man Universal Modding Engine mit Steam zum Laufen? diff --git a/uMod_GUI/bin/README_English.txt b/uMod_GUI/bin/README_English.txt index 44b7f0d..9479b4e 100644 --- a/uMod_GUI/bin/README_English.txt +++ b/uMod_GUI/bin/README_English.txt @@ -13,14 +13,14 @@ cannot be delivered together with Universal Modding Engine . If D3DX9_43.dll is installed on your system, Universal Modding Engine will give you a hint at program start. -What can Universal Modding Engine (uMod) V1.0? +What can Universal Modding Engine (uMod) V2.0? -extract and save single textures from a DX9 game (the target texture can be toggled in the game) --extract and save all textures from a DX9 game +-extract and save all textures from a DX9 game (with size and texture format filter) -load textures into a game to replace target textures -support single dds texture --support zip-files as whole mod package --support the original TexMod *.tpf files +-support zip-files as whole mod package (groups of textures can be displayed as tree view) +-support the original TexMod *.tpf files (the tpf support must be enabled through the context menu) All these options can be switched on or off, while the game is already running! So you can search for a texture in the game, save it to disk, edit it, @@ -32,12 +32,8 @@ the textures are loaded by the game and only in the moment they are loaded. If you switch this option on, while a map is loaded, probably nothing will hapen, because all textures are loaded for this map. Change the Map or reload it again. -Zip files can include a "texmod.def" file which contains the hash and the file name. -Each line should be in the format "hash|filename.dds" -If it does not contain a "texmod.def" each file will be unpacked and those which match -the wildcard "*_hash.dds" will be used. -The zip file can include a "Comment.txt" file. The content will be shown as comment -in the GUI. +Zip files can include a content.txt" file which contains information about +groups and textures. Two examples are included in the package. If you load single files, they should match the wildcard "*_hash.dds" @@ -80,23 +76,29 @@ If you have chosen the first or third method, you simply start Universal Modding and afterwards the game. Do NOT start the game through Universal Modding Engine. If the game starts and all works fine, a new tab opens immediately in uMod. -In this tab you can now mod the game. Press the "update" button to commit -the changes to the game. You can also save your current settings as a template. -One template can be set as default for a game, which will be loaded and -and committed automatically when you start this game the next time. +In the upper text control you can read the method of injection. An additional information +about created DirectX devices follows e.g. (DX9) or (DX9EX: 3). It informs you, what kind of +device and how many devices were created by the game (and detoured by uMod). If this +information is not displayed, you are not able to modify the game. Possible reason might +be, that the game does not use DirectX9 or uMod was not fast enough to inject the game. -To load a mod, you must set the check mark of the file. If you wish to unload a mod, -just remove the check mark and click on update again. +After you have changed Settings you have to update via the context menu (right click +in the bottom window). Update (reload) reloads all texture from disk, this is only for +users, which edit textures. Clicking on update will only update the differences (if packages have been removed from the list, you toggled check marks or changed the order). The reload button forces to reload from disk (if you have edited the texture itself). -Due to the fact that different mods can modify the same target texture, only the -mod-texture of the first file in the list is taken into account. The action of this file -(load or unload) is proceeded regardless of what the following mods are opposed -to do with their mod-textures. +New mods or textures can be opened through the context menu or through drag&drop +from the explorer. The order of mods can be rearranged also using drag&drop. The order +of mods is important, if two mods try to modify the same game texture. In this case, +the texture from the mod listed first is taken. +Additionally you can expand mods and activate or deactivate single textures. Mod +with the new mod-format can contain many groups, which can be activated or deactivated +separately. For textures at least one tick mark must be set, for mods and groups bot tick +marks can be unset to give the control to the underlying layer. How to get Universal Modding Engine work together with Steam? diff --git a/uMod_GUI/bin/languages/Example_uMod_LanguagePack_English.txt b/uMod_GUI/bin/languages/Example_uMod_LanguagePack_English.txt index 9aba258..a665301 100644 --- a/uMod_GUI/bin/languages/Example_uMod_LanguagePack_English.txt +++ b/uMod_GUI/bin/languages/Example_uMod_LanguagePack_English.txt @@ -1,5 +1,5 @@ If you have created a language package and would like to add to the officially download, please send it to me. -Email address and support can be found at http://code.google.com/p/texmod/ +Email address and support can be found at http://code.google.com/p/texmod/ The file name must match the wildcard uMod_LanguagePack_NAMEOFLANGUAGE.txt @@ -36,117 +36,122 @@ English itself is compiled into the uMod_GUI.exe, thus there exists no uMod_Lang The following list is an example of how an English package would look like (maybe not all keywords are present). - -MenuHelp: -Help| -MenuAbout: -About| +MenuLanguage:Change language| +MenuHelp:Help| +MenuAbout:About| MenuAcknowledgement:Acknowledgement| + MenuStartGame:Start game through uMod| -MenuStartGameCMD :Start game through uMod (with command line)| +MenuStartGameCMD:Start game through uMod (with command line)| + MenuUseHook:Use global hook| -MenuAddGame: -Add game| -MenuDeleteGame: -Delete Game| +MenuAddGame:Add game| +MenuDeleteGame:Delete game| MenuLoadTemplate:Load template| MenuSaveTemplate:Save template| MenuSaveTemplateAs:Save template as ...| MenuSetDefaultTemplate:Set template as default| -MenuLanguage:Change language| MenuExit:Exit| + MainMenuMain:Main| -MainMenuHelp: -Help| +MainMenuHelp:Help| +MenuOpenPackage:Open texture/package| +MenuRemovePackage:Remove package| +MenuRemoveSelectedPackages:Remove selected packages| -ButtonOpen: -Open texture| -ButtonDirectory: -save directory| -ButtonUpdate: -Update| -ButtonReload:Update (reload)| - +MenuUpdate:Update| +MenuReload:Update (reload)| +MenuSupportTPF:Support TPF-Mods| +ButtonUpdate:Update| +ButtonDirectory:Set save directory| ChooseFile:Choose a file| -ChooseDir: -Choose a directory| -CheckBoxSaveSingleTexture: -Save single texture| +ChooseDir:Choose a directory| + +MultipleSingleFiles:Multiple single files| + +HookInjection:Hook Injection: | +DirectInjection:Direct Injection: | +NoInjection:No Injection: | +InvalidGamePage:default page| TextCtrlTemplate:Template: | -CheckBoxSaveAllTextures: -Save all textures| -TextCtrlSavePath: -Save path: | -SelectLanguage: -Select a language| + +CollapseTextureCapture:Capture textures| +CheckBoxSaveSingleTexture:Save single texture| +CheckBoxShowStringSaveSingleTexture:Show message in the upper left corner| +CheckBoxShowSingleTexture:Show texture in the upper left corner| +CheckBoxSaveAllTextures:Save all textures| + +SelectLanguage:Select a language.| + StartGame:Select the game to start.| CommandLine:Set command line arguments.| -ChooseGame: -Select a game binary. -DeleteGame: -Select the games to be deleted.| -GameAlreadyAdded: -Game has been already added.| -ExitGameAnyway: -Closing Universal Modding Engine while a game is running might lead to a crash of the game. -Exit anyway?| -NoComment: -No comment.| -Author: -Author: | +ChooseTemplate:Chose a template| +OpenTemplate:Open template| +SaveTemplate:Save template| +DefaultTemplate:Set default template| +AutoSaveTemplate:auto save| + +ChooseGame:Select a game binary.| +DeleteGame:Select the games to be deleted.| +GameAlreadyAdded:Game has been already added.| +ExitGameAnyway:Closing OpenTexMod while a game is running might lead to a crash of the game. +Exit anyway?| +Title:Title| +Author:Author| +Comment:Comment| +SingleTextureNode:Single Textures| Error_GameIsHooked:The global hook is active and this game will be injected! Please delete the game from the list or disable the hook.| Error_ProcessNotStarted:The game could not be started.| Error_RemoveHook:Removing the Hook while a game is running might lead to crash.| -Error_FileNotSupported: -This file type is not supported:| -Error_DLLNotFound: -Could not load the dll. -The dll injection won't work. -This might happen if D3DX9_43.dll is not installed on your system. + +Error_FileNotSupported:This file type is not supported: +| +Error_FileIsEmpty:Could not retrieve textures from file: +| +Error_D3DX9NotFound:The D3DX9_43.dll (32bit) is not available on your system. +Please install the newest DirectX End-User Runtime Installer.| +Error_DLLNotFound:Could not load the dll. +The dll injection won't work. +This might happen if D3DX9_43.dll (32bit) is not installed on your system. Please install the newest DirectX End-User Runtime Web Installer.| -Error_FktNotFound: -Could not load function out of dll. +Error_FktNotFound:Could not load function out of dll. The dll injection won't work.| -Error_AlreadyRunning:An other instance of Universal Modding Engine is already running.| -Error_Send: -Could not send to game.| -Error_KeyTwice: -You assigned a key twice.| -Error_NoSavePath: -You did not set a save path.| -Error_KeyNotSet: -At least one key is not set.| -Error_SaveFile: -Could not save to file.| -Error_NoPipe: -Pipe is not opened.| -Error_WritePipe: -Could not write in pipe.| -Error_FlushPipe: -Could not flush pipe buffer.| -Error_Hash: -Could not find hash, maybe file is not named as *_HASH.dds| -Error_FileOpen: -Could not open file.| -Error_FileRead: -Could not read file.| -Error_Memory: -Could not allocate enough memory| -Error_Unzip: -Could not unzip.| -Error_ZipEntry: -Could not find zip entry.| -KeyBack: -Back| -KeySave: -Save| -KeyNext: -Next| -FontColour: -Font colour (RGB):| -TextureColour: -Texture colour (RGB):| \ No newline at end of file +Error_AlreadyRunning:An other instance of OpenTexMod is already running.| + +Error_Send:Could not send to game.| +Error_KeyTwice:You have assigned the same key twice.| +Error_NoSavePath:You did not set a save path.| +Error_KeyNotSet:At least one key is not set.| +Error_SaveFile:Could not save to file.| +Error_NoPipe:Pipe is not opened.| +Error_WritePipe:Could not write in pipe.| +Error_FlushPipe:Could not flush pipe buffer.| +Error_Hash:Could not find hash, maybe file is not named as *_HASH.dds| +Error_FileOpen:Could not open file:| +Error_FileRead:Could not read file:| +Error_NoTemplates:No templates available.| +Error_Memory:Could not allocate enough memory.| +Error_Unzip:Could not unzip.| +Error_ZipEntry:Could not find zip entry.| + +CheckBoxUseSizeFilter:Use size filter| +CheckBoxUseFormatFilter:Use texture format filter| +SetFormatFilter:Set the filter| +WidthSpin:Width :| +HeightSpin:Height :| +DepthSpin:Depth :| + +KeyBack:Back| +KeySave:Save| +KeyNext:Next| + +AskForKey:Pleas press a Key.| +KeyNotSet:Not set| +UnknownKey:Unknown| + +FontColour:Font colour| +TextureColour:Texture colour| \ No newline at end of file diff --git a/uMod_GUI/bin/languages/uMod_LanguagePackU_Deutsch.txt b/uMod_GUI/bin/languages/uMod_LanguagePackU_Deutsch.txt index e8e2700cfe45d81d128a9f64dc21a11375a116aa..04a5c2b77a71ae1602d686bd3fc332186e114993 100644 GIT binary patch delta 2009 zcmbVN&rcIk5T5?n8ftm~B+w|y5(5n}Kw_fCa)Ls^D8FopiEycGf!eZ5+69q7R{sDK z@g0mFyy?vg{0Ck2VR9Q$-T~SZUi#oF++JWX^ZA)Q_kioM=Mms zdkyO(eW6u6rr3{kJ0rx>BKB>Jy}I-Fv9m!IZR6W$V-g;0SjkfvvpnfOJFTr`pjpCL zk=_iNU#^2z0l2>n=is2?9xUQu0rRpn;CJeQ_Yok@!@U>%>x6b=!MCOiNZQ0>Nqbp@ zkOqQ985;Yjz{?rNEA_FoNW=x!fZ;MaiVVQ;{ZQo%JJf@*%f!aH;_t*`( zfE^P)Jel59s=Kx)5Np{z+02SxUbW;%c0EU)saZH~X5uc)7XXO^Y$o^4vuqp5DnZ+z zDaILI~eP3w0a zLaQKQtB_ZrRYo{D=qcLx`snh6j!3u?bQ!oQFAB>ndL;Li=A`uMRbXoz3d3)$YsQbE zKFa^_D^iiXtiE57^FFWo7zNj#$KwV#WoB(*Y28a)&3w7tJKGhJ z3mf7%7{8VT>)d<_V1~288Ed6~E7Uj2wc_2gS}A#z)QCo59YZD9WU;^YB)KcZo8yLc zbb28cIBdHYh%+Hs0I?B1cRw{p9}-TqSCgj1LbS8X0v%k_+}GH#lALOnHNHEBXp1C7 zxxM4)F#0Z5z2l9!UX*-i;EsDn8s8Gkar{Y|m3UPznZnLb`YPS6`m|JJ&8?;Z!>v>2 zAEucszGmv0_=|g}?v!$+i1(SU)8RDM5>pGuap|E4z;3|Vy399MMH07Y9L%?68{vb_A+xOzeS1?PI+^n1lV2HJ~2&IEBsdn#ct1duO2 zcElY6k~8<3nl$W!Qo5X92E~zbnpDx+0L8tnBKLP8Q-SdBp@_k)#jJYU!kk+I|7A@) To_18CGQ~;HpcshX4DQh%`z(Ys delta 273 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 blKQNoComment; - return -1; - } - return 0; -} - -int uMod_File::GetContent( AddTextureClass &tex, bool add) -{ - wxString file_type = FileName.AfterLast( '.'); - if (file_type == L"zip") - { - AddZip( tex, add, false); - } - else if (file_type == L"tpf") - { - AddZip( tex, add, true); - } - else if (file_type == L"dds") - { - AddFile( tex, add); - } - else - { - LastError << Language->Error_FileNotSupported; - LastError << "\n" << FileName; - } - if (LastError.Len()>0) return -1; - else - { - if (add) tex.Loaded = true; - return 0; - } -} -*/ - int uMod_File::GetContent( uMod_TreeViewNode* node) { wxString file_type = FileName.AfterLast( '.'); - if (file_type == L"zip") + if (file_type == "zip") { AddZip( node); } - else if (file_type == L"tpf") + else if (file_type == "tpf") { AddTpf( node); } - else if (file_type == L"dds") + else if (SingleFile()) { AddFile( node); } @@ -219,53 +188,17 @@ int uMod_File::UnXOR(void) } - -/* int uMod_File::AddFile( uMod_TreeViewNode* node) { DWORD64 temp_hash; wxString name = FileName.AfterLast( '_'); name = name.BeforeLast( '.'); - if (!name.ToULongLong( &temp_hash, 16)) {LastError << Language->Error_Hash <<"\n" << FileName << "\n"; return -1;} // return if hash could not be extracted - - if (int ret = ReadFile()) return ret; - - uMod_TextureElement *texture = new uMod_TextureElement(); - - if (texture->Content().SetSize(FileLen)) + if (!name.ToULongLong( &temp_hash, 16)) // return if hash could not be extracted { - LastError << Language->Error_Memory; - texture->Release(); + LastError << Language->Error_Hash <<"\n" << FileName << "\n"; return -1; } - char *data = texture->Content().Data(); - - for (unsigned int i=0; iHash() = temp_hash; - texture->Title() = FileName; - texture->Status() = uMod_TextureElement::Activate; - - uMod_ModElement *element = new uMod_ModElement(uMod_ModElement::Group, uMod_ModElement::NONE); - element->Title() = FileName; - node = new uMod_TreeViewNode( (uMod_TreeViewNode*)0, element); - uMod_TreeViewNode *texture_node = new uMod_TreeViewNode( node, texture); - node->Append(texture_node); - - texture->Release(); - element->Release(); - return 0; -} -*/ - -int uMod_File::AddFile( uMod_TreeViewNode* node) -{ - DWORD64 temp_hash; - - wxString name = FileName.AfterLast( '_'); - name = name.BeforeLast( '.'); - if (!name.ToULongLong( &temp_hash, 16)) {LastError << Language->Error_Hash <<"\n" << FileName << "\n"; return -1;} // return if hash could not be extracted if (int ret = ReadFile()) return ret; @@ -398,7 +331,6 @@ int uMod_File::GetContentTemplate_ZIP( const uMod_TreeViewNode_ArrayPtr &list_no int uMod_File::GetContentTemplate_SF( const uMod_TreeViewNode_ArrayPtr &list_node, uMod_TreeViewNode* node) { - DWORD64 temp_hash; wxString name; for (unsigned int i=0; iHash() = temp_hash; texture->Status() = uMod_TextureElement::Activate; } diff --git a/uMod_GUI/uMod_File.h b/uMod_GUI/uMod_File.h index 7fed246..2a73f2b 100644 --- a/uMod_GUI/uMod_File.h +++ b/uMod_GUI/uMod_File.h @@ -29,6 +29,7 @@ public: ~uMod_File(void); bool FileSupported(void); + bool PackageFile(void); bool SingleFile(void); //int AddSingleFileToNode( uMod_TreeViewNode* node); int GetContentTemplate(const wxString &content, uMod_TreeViewNode* node); diff --git a/uMod_GUI/uMod_GUI.cpp b/uMod_GUI/uMod_GUI.cpp index 67d6012..e0e6b36 100644 --- a/uMod_GUI/uMod_GUI.cpp +++ b/uMod_GUI/uMod_GUI.cpp @@ -33,10 +33,6 @@ DEFINE_EVENT_TYPE(uMod_EVENT_TYPE) BEGIN_EVENT_TABLE(uMod_Frame, wxFrame) EVT_CLOSE(uMod_Frame::OnClose) - //EVT_BUTTON(ID_Button_Open, uMod_Frame::OnButtonOpen) - //EVT_BUTTON(ID_Button_Path, uMod_Frame::OnButtonPath) - //EVT_BUTTON(ID_Button_Update, uMod_Frame::OnButtonUpdate) - //EVT_BUTTON(ID_Button_Reload, uMod_Frame::OnButtonReload) EVT_MENU(ID_Menu_Help, uMod_Frame::OnMenuHelp) EVT_MENU(ID_Menu_About, uMod_Frame::OnMenuAbout) @@ -52,7 +48,7 @@ BEGIN_EVENT_TABLE(uMod_Frame, wxFrame) EVT_MENU(ID_Menu_LoadTemplate, uMod_Frame::OnMenuOpenTemplate) EVT_MENU(ID_Menu_SaveTemplate, uMod_Frame::OnMenuSaveTemplate) - EVT_MENU(ID_Menu_SaveTemplateAs, uMod_Frame::OnMenuSaveTemplateAs) + EVT_MENU(ID_Menu_SaveTemplateAs, uMod_Frame::OnMenuSaveTemplate) EVT_MENU(ID_Menu_SetDefaultTemplate, uMod_Frame::OnMenuSetDefaultTemplate) EVT_MENU(ID_Menu_Lang, uMod_Frame::OnMenuLanguage) @@ -85,7 +81,7 @@ bool MyApp::OnInit(void) wxMessageBox( Language->Error_AlreadyRunning, "ERROR", wxOK|wxICON_ERROR); return false; } - uMod_Frame *frame = new uMod_Frame( uMod_VERSION, set); + uMod_Frame *frame = new uMod_Frame( uMod_VERSION L" by ROTA", set); SetTopWindow( frame ); return true; @@ -142,7 +138,7 @@ uMod_Frame::uMod_Frame(const wxString& title, uMod_Settings &set) MainSizer->Add( (wxWindow*) Notebook, 1, wxEXPAND , 0 ); PipeStruct pipe = {INVALID_HANDLE_VALUE,INVALID_HANDLE_VALUE}; - uMod_GamePage *page = new uMod_GamePage( Notebook, "uMod", INVALID_GAME_PAGE, "templates/uMod.txt", pipe); + uMod_GamePage *page = new uMod_GamePage( Notebook, "uMod", INVALID_GAME_PAGE, "uMod", pipe); if (page->LastError.Len()>0) { wxMessageBox(page->LastError, "ERROR", wxOK|wxICON_ERROR); @@ -253,7 +249,7 @@ void uMod_Frame::OnAddGame( wxCommandEvent &event) client->Create(); client->Run(); - wxString save_file; + wxString save_file = "auto_save"; int num = SaveFile_Exe.GetCount(); for (int i=0; iGetPageCount()==0) return; - uMod_GamePage *page = (uMod_GamePage*) Notebook->GetCurrentPage(); - if (page==NULL) return; + file.Empty(); - wxString temp = "D:\\Code\\uMod\\temp\\bf.tpf"; - if (page->AddTexture( temp)) wxMessageBox(page->LastError); - - temp = "D:\\Code\\uMod\\temp\\tex.zip"; - if (page->AddTexture( temp)) wxMessageBox(page->LastError); - - temp = "D:\\Code\\uMod\\temp\\test.zip"; - if (page->AddTexture( temp)) wxMessageBox(page->LastError); - - return; - //wxString file_name = wxFileSelector( Language->ChooseFile, page->GetOpenPath(), "", "*.*", "textures (*.dds)|*.dds|zip (*.zip)|*.zip|tpf (*.tpf)|*.tpf", wxFD_OPEN | wxFD_FILE_MUST_EXIST, this); - wxString file_name = wxFileSelector( Language->ChooseFile, page->GetOpenPath(), "", "", "", wxFD_OPEN | wxFD_FILE_MUST_EXIST, this); - if ( !file_name.empty() ) + wxArrayString files; + if (wxDir::GetAllFiles( "templates/", &files, "*.txt", wxDIR_FILES)<=0) { - page->SetOpenPath(file_name.BeforeLast( '/')); - if (page->AddTexture( file_name)) - { - wxMessageBox(page->LastError, "ERROR", wxOK|wxICON_ERROR); - page->LastError.Empty(); - } + wxMessageBox( Language->Error_NoTemplates, "ERROR", wxOK|wxICON_ERROR); + return -1; } -} -void uMod_Frame::OnButtonPath(wxCommandEvent& WXUNUSED(event)) -{ - if (Notebook->GetPageCount()==0) return; - uMod_GamePage *page = (uMod_GamePage*) Notebook->GetCurrentPage(); - if (page==NULL) return; + for (unsigned int i=0; iChooseDir, page->GetSavePath()); - if ( !dir.empty() ) + + files.Insert("auto_save", 0); + + wxSingleChoiceDialog *dialog = new wxSingleChoiceDialog(this, Language->ChooseTemplate, message, files); + + if (dialog->ShowModal ()==wxID_OK) { - page->SetSavePath( dir); + file = dialog->GetStringSelection(); + return 0; } + return 1; } -void uMod_Frame::OnButtonUpdate(wxCommandEvent& WXUNUSED(event)) -{ - if (Notebook->GetPageCount()==0) return; - uMod_GamePage *page = (uMod_GamePage*) Notebook->GetCurrentPage(); - if (page==NULL) return; - if (page->UpdateGame()) - { - wxMessageBox(page->LastError, "ERROR", wxOK|wxICON_ERROR); - page->LastError.Empty(); - } -} - -void uMod_Frame::OnButtonReload(wxCommandEvent& WXUNUSED(event)) -{ - if (Notebook->GetPageCount()==0) return; - uMod_GamePage *page = (uMod_GamePage*) Notebook->GetCurrentPage(); - if (page==NULL) return; - if (page->ReloadGame()) - { - wxMessageBox(page->LastError, "ERROR", wxOK|wxICON_ERROR); - page->LastError.Empty(); - } -} -*/ - - - void uMod_Frame::OnMenuOpenTemplate(wxCommandEvent& WXUNUSED(event)) { if (Notebook->GetPageCount()==0) return; uMod_GamePage *page = (uMod_GamePage*) Notebook->GetCurrentPage(); if (page==NULL) return; - - //wxString file_name = wxFileSelector( Language->ChooseFile, page->GetOpenPath(), "", "*.*", "textures (*.dds)|*.dds|zip (*.zip)|*.zip|tpf (*.tpf)|*.tpf", wxFD_OPEN | wxFD_FILE_MUST_EXIST, this); - - wxString dir = wxGetCwd(); - dir << "/templates"; - wxString file_name = wxFileSelector( Language->ChooseFile, dir, "", "*.txt", "text (*.txt)|*.txt", wxFD_OPEN | wxFD_FILE_MUST_EXIST, this); - if ( !file_name.empty() ) + wxString file_name; + if (ChooseTemplate( Language->OpenTemplate, file_name)!=0) + return; + if (page->LoadTemplate( file_name)) { - if (page->LoadTemplate( file_name)) - { - wxMessageBox(page->LastError, "ERROR", wxOK|wxICON_ERROR); - page->LastError.Empty(); - } + wxMessageBox(page->LastError, "ERROR", wxOK|wxICON_ERROR); + page->LastError.Empty(); } } -void uMod_Frame::OnMenuSaveTemplate(wxCommandEvent& WXUNUSED(event)) +void uMod_Frame::OnMenuSaveTemplate(wxCommandEvent& event) { + int id = event.GetId(); + if (Notebook->GetPageCount()==0) return; uMod_GamePage *page = (uMod_GamePage*) Notebook->GetCurrentPage(); if (page==NULL) return; wxString file_name = page->GetTemplateName(); - if ( file_name.empty() ) + if (id==ID_Menu_SaveTemplateAs || file_name.IsEmpty()) { - wxString dir = wxGetCwd(); - dir << "/templates"; - file_name = wxFileSelector( Language->ChooseFile, dir, "", "*.txt", "text (*.txt)|*.txt", wxFD_SAVE | wxFD_OVERWRITE_PROMPT, this); - } - if ( !file_name.empty() ) - { - if (page->SaveTemplate(file_name)) + file_name = file_name.AfterFirst('\\').BeforeLast('.'); + wxTextEntryDialog *dialog = new wxTextEntryDialog( this, Language->ChooseTemplate , Language->SaveTemplate, file_name); + if (dialog->ShowModal ()==wxID_OK) { - wxMessageBox(page->LastError, "ERROR", wxOK|wxICON_ERROR); - page->LastError.Empty(); + file_name = dialog->GetValue(); } + + delete dialog; } -} -void uMod_Frame::OnMenuSaveTemplateAs(wxCommandEvent& WXUNUSED(event)) -{ - if (Notebook->GetPageCount()==0) return; - uMod_GamePage *page = (uMod_GamePage*) Notebook->GetCurrentPage(); - if (page==NULL) return; - - wxString dir = wxGetCwd(); - dir << "/templates"; - wxString file_name = wxFileSelector( Language->ChooseFile, dir, "", "*.txt", "text (*.txt)|*.txt", wxFD_SAVE | wxFD_OVERWRITE_PROMPT, this); if ( !file_name.empty() ) { if (page->SaveTemplate(file_name)) @@ -472,21 +411,24 @@ void uMod_Frame::OnMenuSetDefaultTemplate(wxCommandEvent& WXUNUSED(event)) if (page==NULL) return; if (page == (uMod_GamePage*) Notebook->GetPage(0)) return; + wxString file_name; + if (ChooseTemplate( Language->DefaultTemplate, file_name)!=0) + return; + wxString exe = page->GetExeName(); - wxString file = page->GetTemplateName(); int num = SaveFile_Exe.GetCount(); bool hit = false; for (int i=0; iMenuAcknowledgement, wxOK); } diff --git a/uMod_GUI/uMod_GUI.h b/uMod_GUI/uMod_GUI.h index 389fca9..1586e36 100644 --- a/uMod_GUI/uMod_GUI.h +++ b/uMod_GUI/uMod_GUI.h @@ -74,7 +74,6 @@ public: void OnMenuOpenTemplate(wxCommandEvent& WXUNUSED(event)); void OnMenuSaveTemplate(wxCommandEvent& WXUNUSED(event)); - void OnMenuSaveTemplateAs(wxCommandEvent& WXUNUSED(event)); void OnMenuSetDefaultTemplate(wxCommandEvent& WXUNUSED(event)); void OnMenuLanguage(wxCommandEvent& WXUNUSED(event)); @@ -86,6 +85,8 @@ public: private: + int ChooseTemplate( const wxString &message, wxString &file); + int ActivateGamesControl(void); int DeactivateGamesControl(void); @@ -97,6 +98,8 @@ private: int GetInjectedGames( wxArrayString &games, wxArrayString &cmd); int SetInjectedGames( wxArrayString &games, wxArrayString &cmd); + + uMod_Server *Server; wxNotebook *Notebook; diff --git a/uMod_GUI/uMod_GameInfo.cpp b/uMod_GUI/uMod_GameInfo.cpp index 32a62eb..a0ac60b 100644 --- a/uMod_GUI/uMod_GameInfo.cpp +++ b/uMod_GUI/uMod_GameInfo.cpp @@ -39,6 +39,7 @@ void uMod_GameInfo::Init(void) SaveSingleTexture = false; SaveAllTextures = false; myShowSingleTextureString = false; + myShowSingleTexture = false; mySupportTPF = false; KeyBack = -1; @@ -59,7 +60,9 @@ void uMod_GameInfo::Init(void) myFormatFilter = 0u; myFileFormat = uMod_D3DXIFF_DDS; - SavePath.Empty(); + SavePath = wxGetCwd(); + SavePath << "\\textures"; + OpenPath.Empty(); } @@ -90,6 +93,9 @@ int uMod_GameInfo::SaveToString( wxString &content ) temp.Printf( "ShowSingleTextureString:%d\n", myShowSingleTextureString); content << temp; + temp.Printf( "ShowSingleTexture:%d\n", myShowSingleTexture); + content << temp; + temp.Printf( "SupportTPF:%d\n", mySupportTPF); content << temp; @@ -183,6 +189,12 @@ int uMod_GameInfo::LoadFromString( const wxString &content) if (temp.Len()>0 && temp[0]=='0') myShowSingleTextureString = false; else myShowSingleTextureString = true; } + else if (command == "ShowSingleTexture") + { + temp = line.AfterFirst(':'); + if (temp.Len()>0 && temp[0]=='0') myShowSingleTexture = false; + else myShowSingleTexture = true; + } else if (command == "SupportTPF") { temp = line.AfterFirst(':'); diff --git a/uMod_GUI/uMod_GameInfo.h b/uMod_GUI/uMod_GameInfo.h index 0ca6b60..a63c664 100644 --- a/uMod_GUI/uMod_GameInfo.h +++ b/uMod_GUI/uMod_GameInfo.h @@ -42,6 +42,7 @@ public: bool &ShowCollPane(void) {return myShowCollPane;} + const bool &ShowCollPane(void) const {return myShowCollPane;} bool &SupportTPF(void) {return mySupportTPF;} @@ -67,6 +68,9 @@ public: bool &ShowSingleTextureString() {return myShowSingleTextureString;} const bool &ShowSingleTextureString() const {return myShowSingleTextureString;} + bool &ShowSingleTexture() {return myShowSingleTexture;} + const bool &ShowSingleTexture() const {return myShowSingleTexture;} + int SetFontColour(const unsigned char *colour) {FontColour[0]=colour[0];FontColour[1]=colour[1];FontColour[2]=colour[2];FontColour[3]=colour[3];return 0;} int GetFontColour(unsigned char *colour) const {colour[0]=FontColour[0];colour[1]=FontColour[1];colour[2]=FontColour[2];colour[3]=FontColour[3];return 0;} @@ -135,6 +139,7 @@ private: unsigned long myFileFormat; bool myShowSingleTextureString; + bool myShowSingleTexture; unsigned char FontColour[4]; unsigned char TextureColour[4]; diff --git a/uMod_GUI/uMod_GamePage.cpp b/uMod_GUI/uMod_GamePage.cpp index 2214a40..a26e8b8 100644 --- a/uMod_GUI/uMod_GamePage.cpp +++ b/uMod_GUI/uMod_GamePage.cpp @@ -122,9 +122,13 @@ uMod_GamePage::uMod_GamePage( wxNotebook *parent, const wxString &exe, int injec SaveSingleTexture = new wxCheckBox( SingleTexturePanel, ID_SaveSingleTexture, Language->CheckBoxSaveSingleTexture); SingleTextureSizer->Add( (wxWindow*) SaveSingleTexture, 0, wxEXPAND, 0); + // check box for showing the string + ShowSingleTexture = new wxCheckBox( SingleTexturePanel, wxID_ANY, Language->CheckBoxShowSingleTexture); + SingleTextureSizer->Add( (wxWindow*) ShowSingleTexture, 0, wxEXPAND| wxUP | wxLEFT | wxRIGHT, 5); + // check box for showing the string ShowSingleTextureString = new wxCheckBox( SingleTexturePanel, ID_ShowSingleTextureString, Language->CheckBoxShowStringSaveSingleTexture); - SingleTextureSizer->Add( (wxWindow*) ShowSingleTextureString, 0, wxEXPAND|wxALL, 5); + SingleTextureSizer->Add( (wxWindow*) ShowSingleTextureString, 0, wxEXPAND| wxBOTTOM | wxLEFT | wxRIGHT, 5); //set the two colour buttons into one horizontal sizer @@ -235,7 +239,7 @@ uMod_GamePage::uMod_GamePage( wxNotebook *parent, const wxString &exe, int injec temp_sizer->Add( (wxWindow*) SavePathButton, 0, wxEXPAND|wxALL, 0); temp_sizer->AddSpacer(10); - SavePath = new wxTextCtrl(win, wxID_ANY, Language->TextCtrlSavePath, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + SavePath = new wxTextCtrl(win, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY); temp_sizer->Add( (wxWindow*) SavePath, 1, wxEXPAND, 0); CollSizer->Add( temp_sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); @@ -305,20 +309,25 @@ uMod_GamePage::uMod_GamePage( wxNotebook *parent, const wxString &exe, int injec if (LastError.IsEmpty()) { if (TemplateName.Len()>0) LoadTemplate(TemplateName); - LastError.Empty(); + else LoadGameData(Game); + if (!LastError.IsEmpty()) + { + LoadGameData(Game); + LastError.Empty(); + } } + else LoadGameData(Game); } uMod_GamePage::~uMod_GamePage(void) { - + GetSettings(); + SaveTemplate("auto_save"); } int uMod_GamePage::SetSavePath( const wxString &path) { - wxString save_path = Language->TextCtrlSavePath; - save_path << path; - SavePath->SetValue(save_path); + SavePath->SetValue(path); Game.SetSavePath( path); return 0; } @@ -413,6 +422,9 @@ int uMod_GamePage::SetInfo(void) case NO_INJECTION: info = Language->NoInjection; break; + case INVALID_GAME_PAGE: + info = Language->InvalidGamePage; + break; default: info = "BUG^^ injection:"; break; @@ -483,6 +495,7 @@ int uMod_GamePage::GetSettings(void) Game.SetSaveAllTextures( save_all); Game.ShowSingleTextureString() = ShowSingleTextureString->GetValue(); + Game.ShowSingleTexture() = ShowSingleTexture->GetValue(); Game.UseSizeFilter() = UseSizeFilter->GetValue(); SpinHeight->GetValue( Game.HeightMin(), Game.HeightMax()); @@ -525,9 +538,35 @@ int uMod_GamePage::ReloadGame(void) return LoadTemplateFromString(content); } -int uMod_GamePage::SaveTemplate( const wxString &file_name) + +int uMod_GamePage::GetTemplateFileName( const wxString &template_name, wxString &file_name) +{ + if (template_name=="auto_save") + { + unsigned int hash = 0u; + int len = ExeName.Len(); + unsigned char *str = (unsigned char*) ExeName.fn_str(); + for (int i=0; iError_FileOpen << "\n" << file_name; return -1; } file.Open(file_name, wxFile::read); if (!file.IsOpened()) { + if (template_name == "auto_save") return 0; LastError = Language->Error_FileOpen << "\n" << file_name; return -1; } @@ -614,7 +660,7 @@ int uMod_GamePage::LoadTemplate( const wxString &file_name) if (int ret = LoadTemplateFromString(content)) return ret; - TemplateName = file_name; + TemplateName = template_name; wxString path; path = Language->TextCtrlTemplate; path << TemplateName; @@ -657,13 +703,22 @@ int uMod_GamePage::LoadTemplateFromString( const wxString &content_orig) LastError << ViewModel->LastError; } + LoadGameData(Game); - //if (Sender.Send( Game, GameOld, true)==0) GameOld = Game; - - CollPane->Collapse( !Game.ShowCollPane()); + if (LastError.IsEmpty()) + { + return UpdateGame(); + } + else return -1; +} - if (Game.ShowSingleTextureString()) +int uMod_GamePage::LoadGameData(const uMod_GameInfo &game) +{ + CollPane->Collapse( !game.ShowCollPane()); + + + if (game.ShowSingleTextureString()) { ShowSingleTextureString->SetValue(true); FontColour->Enable(); @@ -673,16 +728,20 @@ int uMod_GamePage::LoadTemplateFromString( const wxString &content_orig) ShowSingleTextureString->SetValue(false); FontColour->Enable(false); } + if (game.ShowSingleTexture()) + ShowSingleTexture->SetValue(true); + else + ShowSingleTexture->SetValue(false); - KeyBack->SetKey(Game.GetKeyBack()); - KeySave->SetKey(Game.GetKeySave()); - KeyNext->SetKey(Game.GetKeyNext()); + KeyBack->SetKey(game.GetKeyBack()); + KeySave->SetKey(game.GetKeySave()); + KeyNext->SetKey(game.GetKeyNext()); - SpinWidth->SetValue( Game.WidthMin(), Game.WidthMax()); - SpinHeight->SetValue( Game.HeightMin(), Game.HeightMax()); - SpinDepth->SetValue( Game.DepthMin(), Game.DepthMax()); + SpinWidth->SetValue( game.WidthMin(), game.WidthMax()); + SpinHeight->SetValue( game.HeightMin(), game.HeightMax()); + SpinDepth->SetValue( game.DepthMin(), game.DepthMax()); - if (Game.UseSizeFilter()) + if (game.UseSizeFilter()) { UseSizeFilter->SetValue(true); SpinWidth->Enable(); @@ -697,7 +756,7 @@ int uMod_GamePage::LoadTemplateFromString( const wxString &content_orig) SpinDepth->Enable(false); } - if (Game.UseFormatFilter()) + if (game.UseFormatFilter()) { UseFormatFilter->SetValue(true); FormatFilter->Enable(); @@ -709,14 +768,14 @@ int uMod_GamePage::LoadTemplateFromString( const wxString &content_orig) } - if (Game.GetSaveSingleTexture()) + if (game.GetSaveSingleTexture()) { SaveSingleTexture->SetValue( true); SaveAllTextures->SetValue(false); SingleTexturePanel->Enable(); AllTexturePanel->Disable(); } - else if (Game.GetSaveAllTextures()) + else if (game.GetSaveAllTextures()) { SaveSingleTexture->SetValue( false); SaveAllTextures->SetValue( true); @@ -731,11 +790,9 @@ int uMod_GamePage::LoadTemplateFromString( const wxString &content_orig) AllTexturePanel->Enable(); } - FileFormats->SetValue(Game.FileFormat()); + FileFormats->SetValue(game.FileFormat()); - wxString path = Language->TextCtrlSavePath; - path << Game.GetSavePath(); - SavePath->SetValue( path); + SavePath->SetValue( game.GetSavePath()); // The wxCollapsiblePane could be hide/shown thus we must manually layout and refresh the sizer and the window @@ -745,11 +802,7 @@ int uMod_GamePage::LoadTemplateFromString( const wxString &content_orig) // VieCtrl must repaint otherwise some graphical glitches ocure ViewCtrl->Refresh(); - if (LastError.IsEmpty()) - { - return UpdateGame(); - } - else return -1; + return 0; } void uMod_GamePage::OnCollPane(wxCollapsiblePaneEvent& WXUNUSED(event)) @@ -965,99 +1018,6 @@ void uMod_GamePage::OnButtonFormatFilter(wxCommandEvent& WXUNUSED(event)) temp << "D3DFMT_YUY2 (" << MAKEFOURCC('Y', 'U', 'Y', '2') << ")"; choices.Add(temp); temp.Empty(); - /* - choices.Add("D3DFMT_DXT1 (827611204)"); 31545844 - choices.Add("D3DFMT_DXT2 (844388420)"); 32545844 - choices.Add("D3DFMT_DXT3 (861165636"); 33545844 - choices.Add("D3DFMT_DXT4 (877942852)"); 34545844 - choices.Add("D3DFMT_DXT5 (894720068)"); 35545844 - choices.Add("D3DFMT_G8R8_G8B8 (MAKEFOURCC('G', 'R', 'G', 'B')"); - choices.Add("D3DFMT_MULTI2_ARGB8 (MAKEFOURCC('M','E','T','1')"); 31 - choices.Add("D3DFMT_R8G8_B8G8 (MAKEFOURCC('R', 'G', 'B', 'G')"); - choices.Add("D3DFMT_UYVY (MAKEFOURCC('U', 'Y', 'V', 'Y')"); - choices.Add("D3DFMT_YUY2 (MAKEFOURCC('Y', 'U', 'Y', '2')"); -*/ -/* - choices.Add("R8G8B8"); - choices.Add("A8R8G8B8"); - choices.Add("X8R8G8B8"); - choices.Add("R5G6B"); - choices.Add("X1R5G5B5"); - choices.Add("A1R5G5B5"); - choices.Add("A4R4G4B4"); - choices.Add("R3G3B2"); - choices.Add("A8"); - choices.Add("A8R3G3B2"); - choices.Add("X4R4G4B4"); - choices.Add("A2B10G10R10"); - choices.Add("A8B8G8R8"); - choices.Add("X8B8G8R8"); - choices.Add("G16R16"); - choices.Add("A2R10G10B10"); - choices.Add("A16B16G16R16"); - - choices.Add("A8P8"); - choices.Add("P8"); - - choices.Add("L8"); - choices.Add("A8L8"); - choices.Add("A4L4"); - - choices.Add("V8U8"); - choices.Add("L6V5U5"); - choices.Add("X8L8V8U8"); - choices.Add("Q8W8V8U8"); - choices.Add("V16U16"); - choices.Add("A2W10V10U10"); - - choices.Add("UYVY"); - choices.Add("R8G8_B8G8"); - choices.Add("YUY2"); - choices.Add("G8R8_G8B8"); - choices.Add("DXT1"); - choices.Add("DXT2"); - choices.Add("DXT3"); - choices.Add("DXT4"); - choices.Add("DXT5"); - - choices.Add("D16_LOCKABLE"); - choices.Add("D32"); - choices.Add("D15S1"); - choices.Add("D24S8"); - choices.Add("D24X8"); - choices.Add("D24X4S4"); - choices.Add("D16"); - - choices.Add("D32F_LOCKABLE"); - choices.Add("D24FS8"); - - choices.Add("D32_LOCKABLE"); - choices.Add("S8_LOCKABLE"); - - choices.Add("L16"); - - choices.Add("VERTEXDATA"); - choices.Add("INDEX16"); - choices.Add("INDEX32"); - - choices.Add("Q16W16V16U16"); - - choices.Add("MULTI2_ARGB8"); - - choices.Add("R16F"); - choices.Add("G16R16F"); - choices.Add("A16B16G16R16F"); - - choices.Add("R32F"); - choices.Add("G32R32F"); - choices.Add("A32B32G32R32F"); - - choices.Add("CxV8U8"); - - choices.Add("A1"); - choices.Add("A2B10G10R10_XR_BIAS"); - choices.Add("BINARYBUFFER"); -*/ wxArrayInt selections; unsigned long selected = Game.FormatFilter(); @@ -1083,11 +1043,7 @@ void uMod_GamePage::OnButtonSavePath(wxCommandEvent& WXUNUSED(event)) if ( !dir.empty() ) { Game.SetSavePath(dir); - - wxString path; - path = Language->TextCtrlSavePath; - path << dir; - SavePath->SetValue( path); + SavePath->SetValue( dir); } } @@ -1231,6 +1187,13 @@ void uMod_GamePage::OnContextMenu( wxDataViewEvent &event ) else if (node->GetParent() != (uMod_TreeViewNode*) 0 ) menu.Enable(ID_RemovePackage, false); + // the first page has no update or reload function (it is not connected to a game) + if (InjectionMethod == INVALID_GAME_PAGE) + { + menu.Enable(ID_Update, false); + menu.Enable(ID_Reload, false); + } + // if nothing is selected, we cannot deleted selected packages ;) if (ViewCtrl->GetSelectedItemsCount()<=0) menu.Enable(ID_RemoveSelectedPackages, false); @@ -1301,25 +1264,46 @@ int uMod_GamePage::OpenPackage(void) int uMod_GamePage::UpdateLanguage(void) { + SetInfo(); + wxString path; + path = Language->TextCtrlTemplate; + path << TemplateName; + TemplateFile->SetValue( path); + + CollPane->SetLabel(Language->CollapseTextureCapture); SaveSingleTexture->SetLabel( Language->CheckBoxSaveSingleTexture); ShowSingleTextureString->SetLabel( Language->CheckBoxShowStringSaveSingleTexture); + ShowSingleTexture->SetLabel( Language->CheckBoxShowSingleTexture); KeyBack->SetLabel( Language->KeyBack); + KeyBack->SetKeyLabel(); KeySave->SetLabel( Language->KeySave); + KeySave->SetKeyLabel(); KeyNext->SetLabel( Language->KeyNext); + KeyNext->SetKeyLabel(); FontColour->SetLabel( Language->FontColour); TextureColour->SetLabel( Language->TextureColour); + SaveAllTextures->SetLabel( Language->CheckBoxSaveAllTextures); + + UseSizeFilter->SetLabel( Language->CheckBoxUseSizeFilter); SpinWidth->SetLabel( Language->WidthSpin); SpinHeight->SetLabel( Language->HeightSpin); SpinDepth->SetLabel( Language->DepthSpin); - SaveAllTextures->SetLabel( Language->CheckBoxSaveAllTextures); - wxString temp = Language->TextCtrlSavePath; - temp << Game.GetSavePath(); - SavePath->SetValue( temp); + UseFormatFilter->SetLabel( Language->CheckBoxUseFormatFilter); + FormatFilter->SetLabel( Language->SetFormatFilter); + + SavePathButton->SetLabel( Language->ButtonDirectory); + SavePath->SetValue( Game.GetSavePath()); + + + ViewCtrl->GetColumn(0)->SetTitle(Language->Title); + ViewCtrl->GetColumn(3)->SetTitle(Language->Author); + ViewCtrl->GetColumn(4)->SetTitle(Language->Comment); + return 0; } diff --git a/uMod_GUI/uMod_GamePage.h b/uMod_GUI/uMod_GamePage.h index e34c803..b761d44 100644 --- a/uMod_GUI/uMod_GamePage.h +++ b/uMod_GUI/uMod_GamePage.h @@ -37,6 +37,7 @@ public: int LoadTemplate( const wxString &file_name); int LoadTemplateFromString( const wxString &content); + int LoadGameData(const uMod_GameInfo &game); wxString GetExeName(void) {return ExeName;} wxString GetTemplateName(void) {return TemplateName;} @@ -172,6 +173,13 @@ private: */ int GetSettings(void); + /** + * returns the file name to a given template name + * @param template_name + * @param file_name + * @return 0 on success + */ + int GetTemplateFileName( const wxString &template_name, wxString &file_name); wxString ExeName; wxString TemplateName; @@ -191,6 +199,7 @@ private: wxBoxSizer *SingleTextureSizer; wxCheckBox *SaveSingleTexture; wxCheckBox *ShowSingleTextureString; + wxCheckBox *ShowSingleTexture; uMod_KeyPanel *KeyBack; diff --git a/uMod_GUI/uMod_Language.cpp b/uMod_GUI/uMod_Language.cpp index e526e39..a75603b 100644 --- a/uMod_GUI/uMod_Language.cpp +++ b/uMod_GUI/uMod_Language.cpp @@ -20,7 +20,7 @@ along with Universal Modding Engine. If not, see #include "uMod_Main.h" -uMod_Language *Language = NULL; +uMod_Language *Language = (uMod_Language*)0; uMod_Language::uMod_Language(void) { @@ -217,11 +217,16 @@ int uMod_Language::LoadLanguage(const wxString &name) CheckEntry( command, msg, CollapseTextureCapture) CheckEntry( command, msg, CheckBoxSaveSingleTexture) CheckEntry( command, msg, CheckBoxShowStringSaveSingleTexture) + CheckEntry( command, msg, CheckBoxShowSingleTexture) CheckEntry( command, msg, CheckBoxSaveAllTextures) - CheckEntry( command, msg, TextCtrlSavePath) CheckEntry( command, msg, SelectLanguage) CheckEntry( command, msg, StartGame) CheckEntry( command, msg, CommandLine) + CheckEntry( command, msg, ChooseTemplate) + CheckEntry( command, msg, OpenTemplate) + CheckEntry( command, msg, SaveTemplate) + CheckEntry( command, msg, DefaultTemplate) + CheckEntry( command, msg, AutoSaveTemplate) CheckEntry( command, msg, ChooseGame) CheckEntry( command, msg, DeleteGame) CheckEntry( command, msg, GameAlreadyAdded) @@ -250,6 +255,7 @@ int uMod_Language::LoadLanguage(const wxString &name) CheckEntry( command, msg, Error_Hash) CheckEntry( command, msg, Error_FileOpen) CheckEntry( command, msg, Error_FileRead) + CheckEntry( command, msg, Error_NoTemplates) CheckEntry( command, msg, Error_Memory) CheckEntry( command, msg, Error_Unzip) CheckEntry( command, msg, Error_ZipEntry) @@ -278,8 +284,8 @@ int uMod_Language::LoadDefault(void) CurrentLanguage="English"; MenuLanguage = "Change language"; - MenuHelp = "Help"; - MenuAbout = "About"; + MenuHelp = "Help"; + MenuAbout = "About"; MenuAcknowledgement = "Acknowledgement"; MenuStartGame = "Start game through uMod"; @@ -321,14 +327,20 @@ int uMod_Language::LoadDefault(void) CollapseTextureCapture = "Capture textures"; CheckBoxSaveSingleTexture = "Save single texture"; CheckBoxShowStringSaveSingleTexture = "Show message in the upper left corner"; + CheckBoxShowSingleTexture = "Show texture in the upper left corner"; CheckBoxSaveAllTextures = "Save all textures"; - TextCtrlSavePath = "Save path:"; SelectLanguage = "Select a language."; StartGame = "Select the game to start."; CommandLine = "Set command line arguments."; + ChooseTemplate = "Chose a template"; + OpenTemplate = "Open template"; + SaveTemplate = "Save template"; + DefaultTemplate = "Set default template"; + AutoSaveTemplate = "auto save"; + ChooseGame = "Select a game binary."; DeleteGame = "Select the games to be deleted."; GameAlreadyAdded = "Game has been already added."; @@ -360,6 +372,7 @@ int uMod_Language::LoadDefault(void) Error_Hash = "Could not find hash, maybe file is not named as *_HASH.dds"; Error_FileOpen = "Could not open file:"; Error_FileRead = "Could not read file:"; + Error_NoTemplates = "No templates available."; Error_Memory = "Could not allocate enough memory."; Error_Unzip = "Could not unzip."; Error_ZipEntry = "Could not find zip entry."; diff --git a/uMod_GUI/uMod_Language.h b/uMod_GUI/uMod_Language.h index 74d67fb..4467f2b 100644 --- a/uMod_GUI/uMod_Language.h +++ b/uMod_GUI/uMod_Language.h @@ -74,14 +74,20 @@ public: wxString CollapseTextureCapture; wxString CheckBoxSaveSingleTexture; wxString CheckBoxShowStringSaveSingleTexture; + wxString CheckBoxShowSingleTexture; wxString CheckBoxSaveAllTextures; - wxString TextCtrlSavePath; wxString SelectLanguage; wxString StartGame; wxString CommandLine; + wxString ChooseTemplate; + wxString OpenTemplate; + wxString SaveTemplate; + wxString DefaultTemplate; + wxString AutoSaveTemplate; + wxString ChooseGame; wxString DeleteGame; wxString GameAlreadyAdded; @@ -113,6 +119,7 @@ public: wxString Error_Hash; wxString Error_FileOpen; wxString Error_FileRead; + wxString Error_NoTemplates; wxString Error_Memory; wxString Error_Unzip; wxString Error_ZipEntry; diff --git a/uMod_GUI/uMod_MiniPanels.h b/uMod_GUI/uMod_MiniPanels.h index 6477f46..5c84240 100644 --- a/uMod_GUI/uMod_MiniPanels.h +++ b/uMod_GUI/uMod_MiniPanels.h @@ -116,6 +116,11 @@ public: * @param label */ void SetLabel(const wxString &label); + + /** + * Set the label for the key. + */ + void SetKeyLabel(); private: /** @@ -124,7 +129,6 @@ private: */ void OnButton(wxCommandEvent& event); - void SetKeyLabel(); wxButton *Button; wxTextCtrl *KeyText; diff --git a/uMod_GUI/uMod_Sender.cpp b/uMod_GUI/uMod_Sender.cpp index cbe0b41..f5786fc 100644 --- a/uMod_GUI/uMod_Sender.cpp +++ b/uMod_GUI/uMod_Sender.cpp @@ -96,15 +96,18 @@ int uMod_Sender::Send( const uMod_GameInfo &game, const uMod_GameInfo &game_old, } SendBool( game.ShowSingleTextureString(), CONTROL_SHOW_STRING); + SendBool( game.ShowSingleTexture(), CONTROL_SHOW_TEXTURE); SendBool( game.GetSaveSingleTexture(), CONTROL_SAVE_SINGLE); - SendBool(game.GetSaveAllTextures(), CONTROL_SAVE_ALL); + SendBool( game.GetSaveAllTextures(), CONTROL_SAVE_ALL); SendBool(game.SupportTPF(), CONTROL_SUPPORT_TPF); + SendPath(game.GetSavePath()); + /* wxString path; path = game.GetSavePath(); if (path!=game_old.GetSavePath()) SendPath(path); - +*/ @@ -304,16 +307,16 @@ int uMod_Sender::SendPath( const wxString &path) msg->Hash = 0u; msg->Control = CONTROL_SET_DIR; + int len = path.Len(); + if ((len+1)*sizeof(wchar_t) + sizeof(MsgStruct) >= BIG_BUFSIZE) return -1; const wchar_t *file = path.wc_str(); wchar_t *buff_file = (wchar_t*) &Buffer[sizeof(MsgStruct)]; - int len = 0; - while (file[len] && (sizeof(MsgStruct)+len*sizeof(wchar_t))Value = len*sizeof(wchar_t); - return SendToGame( Buffer, sizeof(MsgStruct)+len*sizeof(wchar_t)); + msg->Value = (len+1)*sizeof(wchar_t); + return SendToGame( Buffer, sizeof(MsgStruct)+(len+1)*sizeof(wchar_t)); } diff --git a/uMod_GUI/uMod_TreeView.cpp b/uMod_GUI/uMod_TreeView.cpp index 938393d..7abb19c 100644 --- a/uMod_GUI/uMod_TreeView.cpp +++ b/uMod_GUI/uMod_TreeView.cpp @@ -237,26 +237,7 @@ int uMod_TreeViewModel::AddPackages(const wxArrayString &files) uMod_File file; wxDataViewItemArray added_items; wxDataViewItemArray added_single_items; - /* - bool single_file = false; - // detect if there are more than one single file - for (unsigned int i=0; i1) - { - uMod_ModElement *element = new uMod_ModElement(uMod_ModElement::Group, uMod_ModElement::NONE); - element->Title() = Language->MultipleSingleFiles; - single_file_node = new uMod_TreeViewNode( (uMod_TreeViewNode*)0, element); - element->Release(); - } -*/ for (unsigned int i=0; i