hello , Anyone here know who can make new suit? for twelvesky 2.8?
They are using 2.5 origin. Not 2.8 gxcw. They mistake 2.5 origin for "2.8"Quote:
make new suit = Create a new 3D model?
make new suit = Add an existing suit from version 2.8?
if( !(make new suit = Add an existing suit from version 2.8) ) return;
class INVENUI
{
public:
INVENUI(){}
static BOOL INVENUI::AskUseItem(int mX, int mY)
{
//Hook and Call Original function
//BOOL INVENUI::AskUseItem(INVENUI* This, int mX, int mY);
//INVENUI* This = 0x??????;
BOOL res = ori_INVENUI_AskUseItem(This, mX, mY);
if( !res )
return 0;
ITEM_INFO *a1 = ITEM::Search(mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][0]);
if ( !a1 )
return 1;
int k;
switch( a1->iIndex )
{
case new_costume_id:
if (a1->iIndex % 3 != (mMYINFO[0].mUseAvatar.aPreviousTribe + 1) % 3)
{
BASICUI::Insert1(1521);
return 1;
}
for (k = 0; k < 10; ++k)
{
if (a1->iIndex == mMYINFO[0].mUseAvatar.aCostume[k])
{
BASICUI::Insert1(1514);
return 1;
}
if (!mMYINFO[0].mUseAvatar.aCostume[k])
break;
}
if (k == 10)
{
BASICUI::Insert1(1516);
return 1;
}
GSOUND::Play(&mGDATA[0].mSOUND_05[2], 0, 100, 1);
mMYINFO[0].mUseInventoryPage = tPage;
mMYINFO[0].mUseInventoryIndex = tIndex;
CBOXUI::Set(7, 1522, aEmptyChar);
break;
}
return 1;
}
};
#define MAX_NETWORK_BUFFER_SIZE 200000
class NETWORK
{
public:
BOOL mCheckInitForNetwork;
char mPacketEncryptionValue[4];
BOOL mCheckConnectState;
SOCKET mSocket;
SOCKADDR_IN mAddress;
BYTE mBuffer[MAX_NETWORK_BUFFER_SIZE];
int mBufferSize;
};
NETWORK* mNETWORK = 0x?????;
#define SetInv( tPage, tIndex, v0, v1, v2, v3, v4, v5 ) \
{ \
mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][0] = v0; \
mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][1] = v1; \
mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][2] = v2; \
mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][3] = v3; \
mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][4] = v4; \
mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][5] = v5; \
}
#define SetInvSock( tPage, tIndex, v0, v1, v2 ) \
{ \
mMYINFO[0].mUseAvatar.aInvenSocket[tPage][tIndex][0] = v0; \
mMYINFO[0].mUseAvatar.aInvenSocket[tPage][tIndex][1] = v1; \
mMYINFO[0].mUseAvatar.aInvenSocket[tPage][tIndex][2] = v2; \
}
#define ClearInv( tPage, tIndex ) \
{ \
SetInv( tPage, tIndex, 0, 0, 0, 0, 0, 0 ); \
SetInvSock( tPage, tIndex, 0, 0, 0 ); \
}
void W_USE_INVENTORY_ITEM_RECV()
{
int tResult, tPage, tIndex, tValue;
CopyMemory(&tResult, &mNETWORK[0].mBuffer[1], 4);
CopyMemory(&tPage, &mNETWORK[0].mBuffer[5], 4);
CopyMemory(&tIndex, &mNETWORK[0].mBuffer[9], 4);
CopyMemory(&tValue, &mNETWORK[0].mBuffer[13], 4);
ITEM_INFO* a1 = ITEM::Search(mMYINFO[0].mUseAvatar.aInventory[tPage % 100][tIndex % 100][0]);
if ( !a1 )
return;
switch( a1->iIndex )
{
case new_costume_id:
if (tResult)
{
BASICUI::Insert1(1869);
}
else
{
mMYINFO[0].mUseAvatar.aCostume[tValue] = tITEM_INFO->iIndex;
ClearInv( tPage, tIndex );
GSOUND::Play(&mGDATA[0].mSOUND_05[296], 0, 100, 1);
BASICUI::Insert1(1517);
}
return;
}
//Hook and call Original function
//void W_USE_INVENTORY_ITEM_RECV();
ori_W_USE_INVENTORY_ITEM_RECV();
}
namespace TW2AddIn
{
class SKIN2_FOR_GXD
{
public:
BYTE data[0x378];
};
class SOBJECT2_FOR_GXD
{
public:
BOOL mCheckValidState;
int mSkinNum;
SKIN2_FOR_GXD *mSkin;
SOBJECT2_FOR_GXD()
{
Init();
}
void Init()
{
this->mCheckValidState = 0;
this->mSkinNum = 0;
this->mSkin = 0;
}
void Free()
{
//Hook and Call Original function
//void TW2AddIn::SOBJECT2_FOR_GXD::Free(TW2AddIn::SOBJECT2_FOR_GXD* This);
ori_TW2AddIn_SOBJECT2_FOR_GXD_Free(this);
}
};
class MOTION2_FOR_GXD
{
public:
BOOL mCheckValidState;
int mFrameNum;
int mBoneNum;
D3DXMATRIX *mKeyMatrix;
MOTION2_FOR_GXD()
{
Init();
}
void Init()
{
this->mCheckValidState = 0;
this->mKeyMatrix = 0;
}
void Free()
{
//Hook and Call Original function
//void TW2AddIn::MOTION2::FOR_GXD::Free(MOTION2_FOR_GXD* This);
ori_TW2AddIn_MOTION2_FOR_GXD_Free(this);
}
};
}
class GMOTION
{
public:
BOOL mCheckFrameNum;
char mFileName[100];
int mFrameNum;
float mLastUsedTime;
CRITICAL_SECTION mLock;
TW2AddIn::MOTION2_FOR_GXD mDATA;
BOOL mCheckValidState;
};
class GSOBJECT
{
public:
BOOL mCheckValidState;
char mFileName[100];
TW2AddIn::SOBJECT2_FOR_GXD mDATA;
float mLastUsedTime;
CRITICAL_SECTION mCritical;
GSOBJECT()
{
Init();
InitializeCriticalSection(&this->mCritical);
}
void Init()
{
this->mCheckValidState = FALSE;
this->mFileName[0] = 0;
this->mDATA.mCheckValidState = FALSE;
}
void Free()
{
EnterCriticalSection(&this->mCritical);
this->mCheckValidState = 0;
this->mDATA.Free();
LeaveCriticalSection(&this->mCritical);
}
void DisplaySObject(int tDrawSort, float pFrame, float *tL, float tYAngle, float tHeightForCulling, GMOTION *tMOTION, BOOL tLoadSort)
{
//Hook and Call Original function
//void GSOBJECT::DisplaySObject(GSOBJECT* This, int tDrawSort, float pFrame, float *tL, float tYAngle, float tHeightForCulling, GMOTION *tMOTION, BOOL tLoadSort);
ori_GSOBJECT_DisplaySObject(this, tDrawSort, pFrame, tL, tYAngle, tHeightForCulling, tMOTION, tLoadSort);
}
};
//initialize new object
GSOBJECT new_gsobject;
//init mFileName
sprintf( new_gsobject.mFileName, "%s", "G03_GDATA\\D04_GSOBJECT\\010\\A%03d001%03d.SOBJECT", 1, 28 );
class UTIL
{
public:
static BOOL CheckPossibleDrawMeWithCamera(float tCoord[3], float tLength)
{
//Hook and Call Original function
BOOL res = ori_UTIL_CheckPossibleDrawMeWithCamera( );
return res;
//float v3; // [esp+8h] [ebp-Ch]
//float v4; // [esp+10h] [ebp-4h]
//v4 = (mGXD[0].mCameraEye.z - tCoord[2]) * (mGXD[0].mCameraEye.z - tCoord[2]) + (mGXD[0].mCameraEye.y - (tLength * 0.5 + tCoord[1])) * (mGXD[0].mCameraEye.y - (tLength * 0.5 + tCoord[1])) + (mGXD[0].mCameraEye.x - *tCoord) * (mGXD[0].mCameraEye.x - *tCoord);
//v3 = sqrt(v4);
//return v3 >= 10.0f;
}
}
class GDATA
{
public:
static GMOTION* ReturnCharacterMotion(int aPreviousTribe, int aGender, int aType, int aSort, int aLevel1, int aLevel2, int aAnimalNumber)
{
//Hook and Call Original function
GMOTION* res = ori_GDATA_ReturnCharacterMotion(aPreviousTribe, aGender, aType, aSort, aLevel1, aLevel2, aAnimalNumber);
return res;
}
};
class AVATAR_OBJECT
{
public:
BOOL mCheckValidState;
DWORD mServerIndex;
DWORD mUniqueNumber;
float mUpdateTime;
float mUpdateTimeForRageTime;
float mUpdateTime3;
OBJECT_FOR_AVATAR mDATA;
BYTE data[1];//other unknow data
//find and hook this function
static void Draw( AVATAR_OBJECT* This, int tDrawSort, int tObjIndex, float dTime )
{
BOOL isLocalClient = tObjIndex == 0;
GMOTION *tMOTION;
if ( !This->mCheckValidState || tDrawSort < 1 || tDrawSort > 2 || !This->mDATA.aVisibleState || !UTIL::CheckPossibleDrawMeWithCamera(This->mDATA.aAction.aLocation, 20.0f) )
return;
int tAnimalNumber = 0;
if ( !This->mDATA.aAnimalAbsorbState )
tAnimalNumber = This->mDATA.aAnimalNumber;
tMOTION = GDATA::ReturnCharacterMotion(This->mDATA.aPreviousTribe, This->mDATA.aGender, This->mDATA.aAction.aType, This->mDATA.aAction.aSort, This->mDATA.aLevel1, This->mDATA.aLevel2, tAnimalNumber);
if( This->mDATA.aCostume == new_costume_id ) {
new_gsobject.DisplaySObject( tDrawSort, This->mDATA.aAction.aFrame, This->mDATA.aAction.aLocation, This->mDATA.aAction.aFront, 20.0f, tMOTION, isLocalClient );
}
}
};
on exit dll function
void dll_exit()
{
new_gsobject.Free();
}
Hello,Quote:
You must have an understanding of IDA or other debug programs and C++ skills to do this.Code:if( !(make new suit = Add an existing suit from version 2.8) ) return;
if you understand it it was easy.
1.hook right click item function
1.1.add gimage/002 for item imageCode:class INVENUI { public: INVENUI(){} static BOOL INVENUI::AskUseItem(int mX, int mY) { //Hook and Call Original function //BOOL INVENUI::AskUseItem(INVENUI* This, int mX, int mY); //INVENUI* This = 0x??????; BOOL res = ori_INVENUI_AskUseItem(This, mX, mY); if( !res ) return 0; ITEM_INFO *a1 = ITEM::Search(mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][0]); if ( !a1 ) return 1; int k; switch( a1->iIndex ) { case new_costume_id: if (a1->iIndex % 3 != (mMYINFO[0].mUseAvatar.aPreviousTribe + 1) % 3) { BASICUI::Insert1(1521); return 1; } for (k = 0; k < 10; ++k) { if (a1->iIndex == mMYINFO[0].mUseAvatar.aCostume[k]) { BASICUI::Insert1(1514); return 1; } if (!mMYINFO[0].mUseAvatar.aCostume[k]) break; } if (k == 10) { BASICUI::Insert1(1516); return 1; } GSOUND::Play(&mGDATA[0].mSOUND_05[2], 0, 100, 1); mMYINFO[0].mUseInventoryPage = tPage; mMYINFO[0].mUseInventoryIndex = tIndex; CBOXUI::Set(7, 1522, aEmptyChar); break; } return 1; } };
2.hook W_USE_INVENTORY_ITEM_RECV
3.create GSOBJECT/GMOTION classCode:#define MAX_NETWORK_BUFFER_SIZE 200000 class NETWORK { public: BOOL mCheckInitForNetwork; char mPacketEncryptionValue[4]; BOOL mCheckConnectState; SOCKET mSocket; SOCKADDR_IN mAddress; BYTE mBuffer[MAX_NETWORK_BUFFER_SIZE]; int mBufferSize; }; NETWORK* mNETWORK = 0x?????; #define SetInv( tPage, tIndex, v0, v1, v2, v3, v4, v5 ) \ { \ mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][0] = v0; \ mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][1] = v1; \ mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][2] = v2; \ mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][3] = v3; \ mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][4] = v4; \ mMYINFO[0].mUseAvatar.aInventory[tPage][tIndex][5] = v5; \ } #define SetInvSock( tPage, tIndex, v0, v1, v2 ) \ { \ mMYINFO[0].mUseAvatar.aInvenSocket[tPage][tIndex][0] = v0; \ mMYINFO[0].mUseAvatar.aInvenSocket[tPage][tIndex][1] = v1; \ mMYINFO[0].mUseAvatar.aInvenSocket[tPage][tIndex][2] = v2; \ } #define ClearInv( tPage, tIndex ) \ { \ SetInv( tPage, tIndex, 0, 0, 0, 0, 0, 0 ); \ SetInvSock( tPage, tIndex, 0, 0, 0 ); \ } void W_USE_INVENTORY_ITEM_RECV() { int tResult, tPage, tIndex, tValue; CopyMemory(&tResult, &mNETWORK[0].mBuffer[1], 4); CopyMemory(&tPage, &mNETWORK[0].mBuffer[5], 4); CopyMemory(&tIndex, &mNETWORK[0].mBuffer[9], 4); CopyMemory(&tValue, &mNETWORK[0].mBuffer[13], 4); ITEM_INFO* a1 = ITEM::Search(mMYINFO[0].mUseAvatar.aInventory[tPage % 100][tIndex % 100][0]); if ( !a1 ) return; switch( a1->iIndex ) { case new_costume_id: if (tResult) { BASICUI::Insert1(1869); } else { mMYINFO[0].mUseAvatar.aCostume[tValue] = tITEM_INFO->iIndex; ClearInv( tPage, tIndex ); GSOUND::Play(&mGDATA[0].mSOUND_05[296], 0, 100, 1); BASICUI::Insert1(1517); } return; } //Hook and call Original function //void W_USE_INVENTORY_ITEM_RECV(); ori_W_USE_INVENTORY_ITEM_RECV(); }
4.Use GSOBJECT and Hook AVATAR_OBJECT:: DrawCode:namespace TW2AddIn { class SKIN2_FOR_GXD { public: BYTE data[0x378]; }; class SOBJECT2_FOR_GXD { public: BOOL mCheckValidState; int mSkinNum; SKIN2_FOR_GXD *mSkin; SOBJECT2_FOR_GXD() { Init(); } void Init() { this->mCheckValidState = 0; this->mSkinNum = 0; this->mSkin = 0; } void Free() { //Hook and Call Original function //void TW2AddIn::SOBJECT2_FOR_GXD::Free(TW2AddIn::SOBJECT2_FOR_GXD* This); ori_TW2AddIn_SOBJECT2_FOR_GXD_Free(this); } }; class MOTION2_FOR_GXD { public: BOOL mCheckValidState; int mFrameNum; int mBoneNum; D3DXMATRIX *mKeyMatrix; MOTION2_FOR_GXD() { Init(); } void Init() { this->mCheckValidState = 0; this->mKeyMatrix = 0; } void Free() { //Hook and Call Original function //void TW2AddIn::MOTION2::FOR_GXD::Free(MOTION2_FOR_GXD* This); ori_TW2AddIn_MOTION2_FOR_GXD_Free(this); } }; } class GMOTION { public: BOOL mCheckFrameNum; char mFileName[100]; int mFrameNum; float mLastUsedTime; CRITICAL_SECTION mLock; TW2AddIn::MOTION2_FOR_GXD mDATA; BOOL mCheckValidState; }; class GSOBJECT { public: BOOL mCheckValidState; char mFileName[100]; TW2AddIn::SOBJECT2_FOR_GXD mDATA; float mLastUsedTime; CRITICAL_SECTION mCritical; GSOBJECT() { Init(); InitializeCriticalSection(&this->mCritical); } void Init() { this->mCheckValidState = FALSE; this->mFileName[0] = 0; this->mDATA.mCheckValidState = FALSE; } void Free() { EnterCriticalSection(&this->mCritical); this->mCheckValidState = 0; this->mDATA.Free(); LeaveCriticalSection(&this->mCritical); } void DisplaySObject(int tDrawSort, float pFrame, float *tL, float tYAngle, float tHeightForCulling, GMOTION *tMOTION, BOOL tLoadSort) { //Hook and Call Original function //void GSOBJECT::DisplaySObject(GSOBJECT* This, int tDrawSort, float pFrame, float *tL, float tYAngle, float tHeightForCulling, GMOTION *tMOTION, BOOL tLoadSort); ori_GSOBJECT_DisplaySObject(this, tDrawSort, pFrame, tL, tYAngle, tHeightForCulling, tMOTION, tLoadSort); } }; //initialize new object GSOBJECT new_gsobject; //init mFileName sprintf( new_gsobject.mFileName, "%s", "G03_GDATA\\D04_GSOBJECT\\010\\A%03d001%03d.SOBJECT", 1, 28 );
Code:class UTIL { public: static BOOL CheckPossibleDrawMeWithCamera(float tCoord[3], float tLength) { //Hook and Call Original function BOOL res = ori_UTIL_CheckPossibleDrawMeWithCamera( ); return res; //float v3; // [esp+8h] [ebp-Ch] //float v4; // [esp+10h] [ebp-4h] //v4 = (mGXD[0].mCameraEye.z - tCoord[2]) * (mGXD[0].mCameraEye.z - tCoord[2]) + (mGXD[0].mCameraEye.y - (tLength * 0.5 + tCoord[1])) * (mGXD[0].mCameraEye.y - (tLength * 0.5 + tCoord[1])) + (mGXD[0].mCameraEye.x - *tCoord) * (mGXD[0].mCameraEye.x - *tCoord); //v3 = sqrt(v4); //return v3 >= 10.0f; } } class GDATA { public: static GMOTION* ReturnCharacterMotion(int aPreviousTribe, int aGender, int aType, int aSort, int aLevel1, int aLevel2, int aAnimalNumber) { //Hook and Call Original function GMOTION* res = ori_GDATA_ReturnCharacterMotion(aPreviousTribe, aGender, aType, aSort, aLevel1, aLevel2, aAnimalNumber); return res; } }; class AVATAR_OBJECT { public: BOOL mCheckValidState; DWORD mServerIndex; DWORD mUniqueNumber; float mUpdateTime; float mUpdateTimeForRageTime; float mUpdateTime3; OBJECT_FOR_AVATAR mDATA; BYTE data[1];//other unknow data //find and hook this function static void Draw( AVATAR_OBJECT* This, int tDrawSort, int tObjIndex, float dTime ) { BOOL isLocalClient = tObjIndex == 0; GMOTION *tMOTION; if ( !This->mCheckValidState || tDrawSort < 1 || tDrawSort > 2 || !This->mDATA.aVisibleState || !UTIL::CheckPossibleDrawMeWithCamera(This->mDATA.aAction.aLocation, 20.0f) ) return; int tAnimalNumber = 0; if ( !This->mDATA.aAnimalAbsorbState ) tAnimalNumber = This->mDATA.aAnimalNumber; tMOTION = GDATA::ReturnCharacterMotion(This->mDATA.aPreviousTribe, This->mDATA.aGender, This->mDATA.aAction.aType, This->mDATA.aAction.aSort, This->mDATA.aLevel1, This->mDATA.aLevel2, tAnimalNumber); if( This->mDATA.aCostume == new_costume_id ) { new_gsobject.DisplaySObject( tDrawSort, This->mDATA.aAction.aFrame, This->mDATA.aAction.aLocation, This->mDATA.aAction.aFront, 20.0f, tMOTION, isLocalClient ); } } };Code:on exit dll function void dll_exit() { new_gsobject.Free(); }
save it to db.Quote:
@[Only registered and activated users can see links. Click Here To Register...] can u pls say something about existing other systems. Like bottle system. Why is that slipping rows on every log in? Bottle1 878 price1 30 bottle2 879 price2 30 for example in game but after relogin it shows bottle and price1 is 0 but bottle+price2 and 3 gets new value.
You need to understand the structure of 3D and animation in order to create them. If you don't understand, don't do it.Quote:
Hello,
First of all thank you for your sharing.
Can you please say to us how can we create a new SOBJECT or MOTION file
#ifndef MYHEADER_H
#define MYHEADER_H
#include <GXD.h>
#include "./CString.h"
#include "./DXHelper.h"
#ifndef MYMACRO_H
#define MYMACRO_H
#define HeapCreate( type, size ) (type)HeapAlloc( GetProcessHeap(), 0, size )
#define HeapFree( ptr ) if( ptr ){ ::HeapFree( GetProcessHeap(), 0, ptr ); ptr = NULL; }
#define LodReleaseFree( ptr ) { \
if ( ptr ) \
{ \
for ( int i = 0; i < mLODStepNum; i++ ) \
SAFE_RELEASE( ptr[i] ); \
HeapFree( ptr ); \
} \
}
#define LodHeapFree( ptr ) { \
if ( ptr ) \
{ \
for ( int i = 0; i < mLODStepNum; i++ ) { \
HeapFree( ptr[i] ); \
} \
HeapFree( ptr ); \
} \
}
#define ClassFreeArray( ptr, num ) { \
if ( ptr ) \
{ \
for ( int k = 0; k < num; k++ ) \
ptr[k].Free(); \
HeapFree( ptr ); \
} \
}
#endif
typedef void (*LOAD_CALLBACK)(void*);
template<typename T>
void ObjectLoadCallback( void* ptr )
{
T* v = (T*)ptr;
if( !v->mCheckValidState )
v->Free();
}
template<typename HEADER, typename T>
static BOOL ReadValue( HEADER h, T val, int size )
{
if( h->mReadPos + size > (int)h->tOriginal.size() )
return FALSE;
memcpy( val, &h->tOriginal[h->mReadPos], size );
h->mReadPos += size;
return TRUE;
}
template<typename HEADER, typename T>
static BOOL ReadValue( HEADER h, T val )
{
return ReadValue( h, val, sizeof(*val) );
}
typedef struct V3HEADER {
char IsValid = 0;
char IsCompress = 0;
char pad0 = 0;
char pad1 = 0;
BOOL IsReady = FALSE;
HANDLE hFile = NULL;
std::vector<BYTE> tOriginal;
std::vector<BYTE> tCompress;
int mReadPos = 0;
void* cPtr = NULL;
LOAD_CALLBACK callback = NULL;
V3HEADER( char* tFileName, const char* tVersionTex, void* ClassPointer, LOAD_CALLBACK Callback )
{
if( ClassPointer && Callback )
{
cPtr = ClassPointer;
callback = Callback;
}
hFile = CreateFileA( tFileName, 0x80000000, 1u, 0, 3u, 0x80u, 0 );
if ( hFile == INVALID_HANDLE_VALUE )
return;
DWORD dwFileSize = GetFileSize( hFile, NULL );
if( !dwFileSize )
return;
tOriginal.resize( dwFileSize );
DWORD NumberOfBytesRead;
if ( !ReadFile( hFile, &tOriginal[0], dwFileSize, &NumberOfBytesRead, 0 ) || NumberOfBytesRead != dwFileSize )
return;
size_t versionLen = strlen(tVersionTex);
if( versionLen > 0 )
{
std::vector<BYTE> tBuffer;
tBuffer.resize( versionLen );
if( !ReadValue( this, &tBuffer[0], tBuffer.size() ) )
return;
tBuffer.push_back( '\0' );//safe last char
if( strcmp( (const char*)&tBuffer[0], tVersionTex ) != 0 )
return;
if( !ReadValue( this, &IsValid, 1 ) )
return;
if( !ReadValue( this, &IsCompress, 1 ) )
return;
if( !ReadValue( this, &pad0, 1 ) )
return;
if( !ReadValue( this, &pad1, 1 ) )
return;
if( !IsValid )
return;
if ( IsCompress )
{
DWORD tOriginalSize, tCompressSize;
if ( !ReadValue( this, &tOriginalSize ) )
return;
if ( !ReadValue( this, &tCompressSize ) )
return;
tOriginal.resize( tOriginalSize );
tCompress.resize( tCompressSize );
if ( !ReadValue( this, &tCompress[0], tCompressSize ) )
return;
if ( !mGXD.Decompress( tCompressSize, (BYTE*)&tCompress[0], tOriginalSize, (BYTE*)&tOriginal[0] ) )
return;
}
}
MYDEBUG();
mReadPos = 0;
IsReady = TRUE;
}
~V3HEADER()
{
if ( hFile )
{
CloseHandle( hFile );
}
if( cPtr && callback )
callback( cPtr );
tOriginal.clear();
tCompress.clear();
}
} V3HEADER;
typedef struct UHEADER
{
BOOL IsReady = FALSE;
void* cPtr = NULL;
LOAD_CALLBACK callback = NULL;
std::vector<BYTE> tOriginal;
int mReadPos = 0;
UHEADER( V3HEADER* h, void* ClassPointer, LOAD_CALLBACK Callback )
{
if( ClassPointer && Callback )
{
cPtr = ClassPointer;
callback = Callback;
}
//for( int i = 0; i < h->tOriginal.size(); i++ )
// tOriginal.push_back( h->tOriginal[i] );
tOriginal = h->tOriginal;
mReadPos = h->mReadPos;
IsReady = TRUE;
}
~UHEADER()
{
//tOriginal.clear();
if( cPtr && callback )
callback( cPtr );
}
} UHEADER;
typedef struct THEADER {
BOOL IsReady = FALSE;
void* cPtr = NULL;
LOAD_CALLBACK callback = NULL;
std::vector<BYTE> tOriginal;
std::vector<BYTE> tCompress;
int mReadPos = 0;
THEADER( UHEADER* h, void* ClassPointer, LOAD_CALLBACK Callback )
{
if( ClassPointer && Callback )
{
cPtr = ClassPointer;
callback = Callback;
}
DWORD tOriginalSize, tCompressSize;
if ( !ReadValue( h, &tOriginalSize ) )
return;
if ( !ReadValue( h, &tCompressSize ) )
return;
tOriginal.resize( tOriginalSize );
tCompress.resize( tCompressSize );
if ( !ReadValue( h, &tCompress[0], tCompressSize ) )
return;
if ( !mGXD.Decompress( tCompressSize, (BYTE*)&tCompress[0], tOriginalSize, (BYTE*)&tOriginal[0] ) )
return;
IsReady = TRUE;
}
~THEADER()
{
tOriginal.clear();
tCompress.clear();
if( cPtr && callback )
callback( cPtr );
}
} THEADER;
typedef struct MHEADER {
BOOL IsReady = FALSE;
void* cPtr = NULL;
LOAD_CALLBACK callback = NULL;
std::vector<BYTE> tOriginal;
std::vector<BYTE> tCompress;
int mReadPos = 0;
MHEADER( V3HEADER* h, void* ClassPointer, LOAD_CALLBACK Callback )
{
if( ClassPointer && Callback )
{
cPtr = ClassPointer;
callback = Callback;
}
DWORD tOriginalSize, tCompressSize;
if ( !ReadValue( h, &tOriginalSize ) )
return;
MYDEBUG();
if ( !ReadValue( h, &tCompressSize ) )
return;
MYDEBUG();
tOriginal.resize( tOriginalSize );
tCompress.resize( tCompressSize );
if ( !ReadValue( h, &tCompress[0], tCompressSize ) )
return;
MYDEBUG();
if ( !mGXD.Decompress( tCompressSize, (BYTE*)&tCompress[0], tOriginalSize, (BYTE*)&tOriginal[0] ) )
return;
MYDEBUG();
IsReady = TRUE;
}
~MHEADER()
{
tOriginal.clear();
tCompress.clear();
if( cPtr && callback )
callback( cPtr );
}
} MHEADER;
#endif // !MYHEADER_H
struct MOTION2_MATRIX
{
D3DXQUATERNION mQuaternion;
D3DXVECTOR3 mPosition;
};
BOOL MOTION2_FOR_GXD::Load( char *tFileName )
{
if ( mCheckValidState )
return 0;
V3HEADER v3( tFileName, "MOTION3", this, (LOAD_CALLBACK)ObjectLoadCallback<MOTION2_FOR_GXD> );
if( !v3.IsReady )
return FALSE;
if( !ReadValue( &v3, &mFrameNum ) || mFrameNum < 1 )
return FALSE;
if( !ReadValue( &v3, &mBoneNum ) || mBoneNum < 1 )
return FALSE;
mKeyMatrix = (D3DXMATRIX*)HeapAlloc( GetProcessHeap(), 0, (mBoneNum * mFrameNum) * sizeof(D3DXMATRIX) );
if (!mKeyMatrix)
return FALSE;
MOTION2_MATRIX v32;
for ( int i = 0; i < mBoneNum * mFrameNum; i++ )
{
if( !ReadValue( &v3, &v32 ) )
return FALSE;
D3DXMatrixRotationQuaternion( &mKeyMatrix[i], &v32.mQuaternion );
mKeyMatrix[i]._41 = v32.mPosition.x;
mKeyMatrix[i]._42 = v32.mPosition.y;
mKeyMatrix[i]._43 = v32.mPosition.z;
}
mCheckValidState = v3.mReadPos == v3.tOriginal.size();
return mCheckValidState;
}
BOOL TEXTURE_FOR_GXD::Load( HANDLE hFile, BOOL tCheckCreateTexture, BOOL tCheckRemoveFileData )
{
BOOL result = FALSE; // eax
int v24; // [esp+8h] [ebp-A0h]
unsigned int v25; // [esp+8h] [ebp-A0h]
unsigned int v26; // [esp+8h] [ebp-A0h]
char Src[4]; // [esp+Ch] [ebp-9Ch] BYREF
int v28[31]; // [esp+10h] [ebp-98h] BYREF
if ( mCheckValidState )
return FALSE;
UHEADER* h = (UHEADER*)hFile;
if ( !ReadValue( h, &mFileDataSize ) )
return FALSE;
if ( !mFileDataSize )
return TRUE;
THEADER t( h, this, (LOAD_CALLBACK)ObjectLoadCallback<TEXTURE_FOR_GXD> );
if( !t.IsReady )
return FALSE;
mFileData = (BYTE*)HeapAlloc( GetProcessHeap(), 0, mFileDataSize );
if ( !mFileData )
return FALSE;
if( !ReadValue( &t, mFileData, mFileDataSize ) )
return FALSE;
if( !ReadValue( &t, &mProcessModeCase ) )
return FALSE;
if( !ReadValue( &t, &mAlphaModeCase ) )
return FALSE;
if (D3DXGetImageInfoFromFileInMemory( mFileData, mFileDataSize, &mTextureInfo ) >= 0 )
{
if (tCheckCreateTexture)
{
switch (mGXD.mTextureOptionValue)
{
case 0:
if (mTextureInfo.Format != D3DFMT_DXT1 && mTextureInfo.Format != D3DFMT_DXT2 && mTextureInfo.Format != D3DFMT_DXT3 && mTextureInfo.Format != D3DFMT_DXT5 ||
D3DXCreateTextureFromFileInMemoryEx(mGXD.mGraphicDevice, mFileData, mFileDataSize, mTextureInfo.Width, mTextureInfo.Height, 0, 0, mTextureInfo.Format, D3DPOOL_MANAGED, 1, 1, 0, 0, 0, &mTexture) >= 0)
goto LABEL_93;
return FALSE;
case 1:
if (!tCheckRemoveFileData || mTextureInfo.Format != D3DFMT_DXT1 && mTextureInfo.Format != D3DFMT_DXT2 && mTextureInfo.Format != D3DFMT_DXT3 && mTextureInfo.Format != D3DFMT_DXT5)
goto LABEL_93;
memcpy(Src, mFileData, sizeof(Src));
memcpy(v28, mFileData + 4, sizeof(v28));
if (v28[0] == 124 && (v28[1] & 2) != 0 && (v28[1] & 4) != 0 && (v28[1] & 0x80000) != 0 && (v28[1] & 0x20000) != 0 && v28[4] >> 2 >= 8 && (v28[6] - 1) >= 2)
{
v24 = v28[4];
v28[2] = v28[2] >> 1;
if (!v28[2])
v28[2] = 1;
v28[3] = v28[3] >> 1;
if (!v28[3])
v28[3] = 1;
v28[4] = v28[4] >> 2;
--v28[6];
memcpy(&mFileData[v24], Src, 4u);
memcpy(&mFileData[v24 + 4], v28, 124u);
D3DXCreateTextureFromFileInMemoryEx(mGXD.mGraphicDevice, &mFileData[v24], mFileDataSize - v24, v28[3], v28[2], 0, 0, mTextureInfo.Format, D3DPOOL_MANAGED, 1, 1, 0, 0, 0, &mTexture);
goto LABEL_93;
}
if (D3DXCreateTextureFromFileInMemoryEx(mGXD.mGraphicDevice, mFileData, mFileDataSize, mTextureInfo.Width, mTextureInfo.Height, 0, 0, mTextureInfo.Format, D3DPOOL_MANAGED, 1, 1, 0, 0, 0, &mTexture) >= 0)
goto LABEL_93;
break;
case 2:
if (!tCheckRemoveFileData || mTextureInfo.Format != D3DFMT_DXT1 && mTextureInfo.Format != D3DFMT_DXT2 && mTextureInfo.Format != D3DFMT_DXT3 && mTextureInfo.Format != D3DFMT_DXT5)
goto LABEL_93;
memcpy(Src, mFileData, sizeof(Src));
memcpy(v28, mFileData + 4, sizeof(v28));
if (v28[0] == 124 && (v28[1] & 2) != 0 && (v28[1] & 4) != 0 && (v28[1] & 0x80000) != 0 && (v28[1] & 0x20000) != 0 && v28[4] >> 4 >= 8 && (v28[6] - 2) >= 2)
{
v25 = v28[4] + (v28[4] >> 2);
v28[2] = v28[2] >> 2;
if (!v28[2])
v28[2] = 1;
v28[3] = v28[3] >> 2;
if (!v28[3])
v28[3] = 1;
v28[4] = v28[4] >> 4;
v28[6] -= 2;
memcpy(&mFileData[v25], Src, 4u);
memcpy(&mFileData[v25 + 4], v28, 0x7Cu);
D3DXCreateTextureFromFileInMemoryEx(mGXD.mGraphicDevice, &mFileData[v25], mFileDataSize - v25, v28[3], v28[2], 0, 0, mTextureInfo.Format, D3DPOOL_MANAGED, 1, 1, 0, 0, 0, &mTexture);
}
else if (D3DXCreateTextureFromFileInMemoryEx(mGXD.mGraphicDevice, mFileData, mFileDataSize, mTextureInfo.Width, mTextureInfo.Height, 0, 0, mTextureInfo.Format, D3DPOOL_MANAGED, 1, 1, 0, 0, 0, &mTexture) < 0)
{
break;
}
goto LABEL_93;
case 3:
if (!tCheckRemoveFileData || mTextureInfo.Format != D3DFMT_DXT1 && mTextureInfo.Format != D3DFMT_DXT2 && mTextureInfo.Format != D3DFMT_DXT3 && mTextureInfo.Format != D3DFMT_DXT5)
goto LABEL_93;
memcpy(Src, mFileData, sizeof(Src));
memcpy(v28, mFileData + 4, sizeof(v28));
if (v28[0] == 124 && (v28[1] & 2) != 0 && (v28[1] & 4) != 0 && (v28[1] & 0x80000) != 0 && (v28[1] & 0x20000) != 0 && v28[4] >> 6 >= 8 && (v28[6] - 3) >= 2)
{
v26 = (v28[4] >> 4) + v28[4] + (v28[4] >> 2);
v28[2] = v28[2] >> 3;
if (!v28[2])
v28[2] = 1;
v28[3] = v28[3] >> 3;
if (!v28[3])
v28[3] = 1;
v28[4] = v28[4] >> 6;
v28[6] -= 3;
memcpy(&mFileData[v26], Src, 4u);
memcpy(&mFileData[v26 + 4], v28, 0x7Cu);
D3DXCreateTextureFromFileInMemoryEx(mGXD.mGraphicDevice, &mFileData[v26], mFileDataSize - v26, v28[3], v28[2], 0, 0, mTextureInfo.Format, D3DPOOL_MANAGED, 1, 1, 0, 0, 0, &mTexture);
goto LABEL_93;
}
if (D3DXCreateTextureFromFileInMemoryEx(mGXD.mGraphicDevice, mFileData, mFileDataSize, mTextureInfo.Width, mTextureInfo.Height, 0, 0, mTextureInfo.Format, D3DPOOL_MANAGED, 1, 1, 0, 0, 0, &mTexture) >= 0)
goto LABEL_93;
default:
break;
}
}
else
{
LABEL_93:
mCheckValidState = TRUE;
if ( tCheckRemoveFileData )
{
HeapFree( mFileData );
}
result = TRUE;
}
}
return result;
}
BOOL SKIN2_FOR_GXD::Load( HANDLE hFile )
{
int i;
LPVOID v31;
LPVOID v32;
if ( mCheckValidState )
return 0;
UHEADER h( (V3HEADER*)hFile, this, (LOAD_CALLBACK)ObjectLoadCallback<SKIN2_FOR_GXD> );
if ( !ReadValue( &h, &mCheckValidState ) )
return 0;
if ( !mCheckValidState )
return 1;
mCheckValidState = FALSE;
if ( !ReadValue( &h,&mEffect ) )
return 0;
if ( !ReadValue( &h,&mSize ) )
return 0;
if ( !ReadValue( &h, mVertexBufferForBillboard, sizeof(mVertexBufferForBillboard) ) )
return 0;
if ( !ReadValue( &h,&mLODStepNum ) )
return 0;
int lodSize = 4 * mLODStepNum;
mVertexNum = HeapCreate( int*, lodSize );
if ( !mVertexNum )
return 0;
mVertexBuffer = HeapCreate( IDirect3DVertexBuffer9**, lodSize );
if ( !mVertexBuffer )
return 0;
for ( i = 0; i < mLODStepNum; mVertexBuffer[i++] = 0 )
;
mTrisNum = HeapCreate( int*, lodSize );
if ( !mTrisNum )
return 0;
mIndexBuffer = HeapCreate( IDirect3DIndexBuffer9**, lodSize );
if ( !mIndexBuffer )
return 0;
for ( i = 0; i < mLODStepNum; mIndexBuffer[i++] = 0 )
;
mShadowVertexBuffer = HeapCreate( SKINSHADOWVERTEX2_FOR_GXD**, lodSize );
if ( !mShadowVertexBuffer )
return 0;
for ( i = 0; i < mLODStepNum; mShadowVertexBuffer[i++] = 0 )
;
mShadowIndexBuffer = HeapCreate( WORD**, lodSize );
if ( !mShadowIndexBuffer )
return 0;
for ( i = 0; i < mLODStepNum; mShadowIndexBuffer[i++] = 0 )
;
mShadowEdgeBuffer = HeapCreate( WORD**, lodSize );
if ( !mShadowEdgeBuffer )
return 0;
for ( i = 0; i < mLODStepNum; mShadowEdgeBuffer[i++] = 0 )
;
for ( i = 0; i < mLODStepNum; ++i )
{
if ( !ReadValue( &h, &mVertexNum[i] ) )
return 0;
int vertexSize = sizeof(SKINVERTEX2_FOR_GXD) * mVertexNum[i];
if( mGXD.mGraphicDevice->CreateVertexBuffer( vertexSize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &mVertexBuffer[i], 0) < 0 || mVertexBuffer[i]->Lock( 0, 0, &v31, 0 ) < 0 )
return 0;
if ( !ReadValue( &h, v31, vertexSize ) )
{
mVertexBuffer[i]->Unlock();
return 0;
}
if ( mVertexBuffer[i]->Unlock() < 0 )
return 0;
if( !ReadValue( &h, &mTrisNum[i] ) )
return 0;
int indexSize = 6 * mTrisNum[i];
if ( mGXD.mGraphicDevice->CreateIndexBuffer( indexSize, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &mIndexBuffer[i], 0 ) < 0 || mIndexBuffer[i]->Lock( 0, 0, &v32, 0) < 0 )
return 0;
if ( !ReadValue( &h, v32, indexSize ) )
{
mIndexBuffer[i]->Unlock();
return 0;
}
if ( mIndexBuffer[i]->Unlock() < 0 )
return 0;
int v24 = sizeof(SKINSHADOWVERTEX2_FOR_GXD) * mVertexNum[i];
mShadowVertexBuffer[i] = HeapCreate( SKINSHADOWVERTEX2_FOR_GXD*, v24 );
if ( !mShadowVertexBuffer[i] )
return 0;
if ( !ReadValue( &h, mShadowVertexBuffer[i], v24 ) )
return 0;
mShadowIndexBuffer[i] = HeapCreate( WORD*, indexSize );
if ( !mShadowIndexBuffer[i] )
return 0;
if ( !ReadValue( &h, mShadowIndexBuffer[i], indexSize ) )
return 0;
mShadowEdgeBuffer[i] = HeapCreate( WORD*, indexSize );
if ( !mShadowEdgeBuffer[i] || !ReadValue( &h, mShadowEdgeBuffer[i], indexSize ) )
return 0;
}
if ( !mDiffuseMap.Load( (HANDLE)&h, 1, 1 ) || !mNormalMap.Load( (HANDLE)&h, 1, 1 ) )
return 0;
if ( !mGXD.mNormalMapOptionValue )
mNormalMap.Free();
if ( !mSpecularMap.Load( (HANDLE)&h, 1, 1 ) )
return 0;
if ( mGXD.mNormalMapOptionValue != 2 )
mSpecularMap.Free();
if ( !ReadValue( &h, &mAnimationMapNum ) )
return 0;
if ( mAnimationMapNum > 0 )
{
mAnimationMap = HeapCreate( TEXTURE_FOR_GXD*, sizeof(TEXTURE_FOR_GXD) * mAnimationMapNum );
if ( !mAnimationMap )
{
return 0;
}
for ( i = 0; i < mAnimationMapNum; ++i )
mAnimationMap[i].Init();
for ( i = 0; i < mAnimationMapNum; ++i )
{
if ( !mAnimationMap[i].Load( (HANDLE)&h, 1, 1) )
return 0;
}
}
((V3HEADER*)hFile)->mReadPos = h.mReadPos;
mCheckValidState = TRUE;
return 1;
}
BOOL SOBJECT2_FOR_GXD::Load( char* tFileName )
{
BOOL result = FALSE;
int i;
if ( mCheckValidState )
return 0;
V3HEADER v3( tFileName, "SOBJECT3", this, (LOAD_CALLBACK)ObjectLoadCallback<SKIN2_FOR_GXD> );
if( !v3.IsReady )
return FALSE;
if( !ReadValue( &v3, &mSkinNum ) || mSkinNum < 1 )
return FALSE;
mSkin = (SKIN2_FOR_GXD*)HeapAlloc( GetProcessHeap(), 0, sizeof(*mSkin) * mSkinNum );
if ( !mSkin )
return FALSE;
for ( i = 0; i < mSkinNum; i++ )
mSkin[i].Init();
for ( i = 0; i < mSkinNum; i++ )
{
mSkin[i].Init();
if ( !mSkin[i].Load( (HANDLE)&v3 ) )
return FALSE;
}
mCheckValidState = v3.mReadPos == v3.tOriginal.size();
return mCheckValidState;
}
Already saving. I have written its ints to my sql, to db.cpps, when i use a bottle price goes down it saves but as i say i use 3rd bottle but after relogin as i see i have been used 2nd bottle. [Only registered and activated users can see links. Click Here To Register...]Quote:
save it to db.
Quote:
Already saving. I have written its ints to my sql, to db.cpps, when i use a bottle price goes down it saves but as i say i use 3rd bottle but after relogin as i see i have been used 2nd bottle. [Only registered and activated users can see links. Click Here To Register...]
void __thiscall DRUNKUI::Draw(DRUNKUI *self, int mX, int mY)
{
int v3; // esi
int v4; // esi
int v5; // eax
int v6; // eax
int v7; // eax
int v8; // eax
int v9; // eax
int v10; // eax
int v11; // esi
int v12; // [esp-Ch] [ebp-414h]
int v13; // [esp-Ch] [ebp-414h]
int v14; // [esp-4h] [ebp-40Ch]
int v15; // [esp-4h] [ebp-40Ch]
int v16; // [esp-4h] [ebp-40Ch]
int v17; // [esp-4h] [ebp-40Ch]
ITEM_INFO *v19; // [esp+8h] [ebp-400h]
int sX; // [esp+Ch] [ebp-3FCh]
int sXa; // [esp+Ch] [ebp-3FCh]
char tString[1000]; // [esp+10h] [ebp-3F8h] BYREF
int i; // [esp+3FCh] [ebp-Ch]
int sY; // [esp+400h] [ebp-8h]
int v25; // [esp+404h] [ebp-4h]
if ( self->mActive )
{
i = 0;
sY = 0;
v25 = 0;
tString[0] = 0;
memset(&tString[1], 0, 999u);
v3 = mMYINFO[0].mScreenXSize / 2;
self->uX = v3 - GIMAGE2D::GetXSize(&mGDATA[0].mUI_MAIN[3945]) / 2;
v4 = mMYINFO[0].mScreenYSize / 2;
self->uY = v4 - GIMAGE2D::GetYSize(&mGDATA[0].mUI_MAIN[3945]) / 2;
if ( GIMAGE2D::CheckIn(&mGDATA[0].mUI_MAIN[3945], self->uX, self->uY, mX, mY) )
POINTER::Set(0);
GIMAGE2D::Display(&mGDATA[0].mUI_MAIN[3945], self->uX, self->uY);
for ( i = 0; i < 10; ++i )
{
if ( mMYINFO[0].mUseAvatar.aBottle[i] >= 1 )
{
sX = self->uX + 55 * (i % 5) + 19;
sY = self->uY + 55 * (i / 5) + 41;
v19 = ITEM::Search(mMYINFO[0].mUseAvatar.aBottle[i]);
if ( v19 )
{
v25 = v19->iDataNumber2D - 1;
GIMAGE2D::Display(&mGDATA[0].mUI_ITEM[v25], sX, sY);
if ( mMYINFO[0].mUseAvatar.aBottleIndex == i && mMYINFO[0].mUseAvatar.aBottleTime > 0 )
GIMAGE2D::Display(&mGDATA[0].mUI_MAIN[2792], sX, sY);
if ( mMYINFO[0].mBottleIndex == i )
GIMAGE2D::Display(&mGDATA[0].mUI_MAIN[2627], sX, sY);
}
}
}
if ( self->bIsClick[0] )
{
v15 = self->uY + DRUNKUI::GetPosY(3947);
v7 = DRUNKUI::GetPosX(3947);
GIMAGE2D::Display(&mGDATA[0].mUI_MAIN[3947], self->uX + v7, v15);
}
else
{
v12 = self->uY + DRUNKUI::GetPosY(3946);
v5 = DRUNKUI::GetPosX(3946);
if ( GIMAGE2D::CheckIn(&mGDATA[0].mUI_MAIN[3946], self->uX + v5, v12, mX, mY) )
{
v14 = self->uY + DRUNKUI::GetPosY(3946);
v6 = DRUNKUI::GetPosX(3946);
GIMAGE2D::Display(&mGDATA[0].mUI_MAIN[3946], self->uX + v6, v14);
}
}
if ( self->bIsClick[2] )
{
v17 = self->uY + DRUNKUI::GetPosY(3951);
v10 = DRUNKUI::GetPosX(3951);
GIMAGE2D::Display(&mGDATA[0].mUI_MAIN[3951], self->uX + v10, v17);
}
else
{
v13 = self->uY + DRUNKUI::GetPosY(3950);
v8 = DRUNKUI::GetPosX(3950);
if ( GIMAGE2D::CheckIn(&mGDATA[0].mUI_MAIN[3950], self->uX + v8, v13, mX, mY) )
{
v16 = self->uY + DRUNKUI::GetPosY(3950);
v9 = DRUNKUI::GetPosX(3950);
GIMAGE2D::Display(&mGDATA[0].mUI_MAIN[3950], self->uX + v9, v16);
}
}
for ( i = 0; i < 10; ++i )
{
if ( mMYINFO[0].mUseAvatar.aBottle[i] )
{
sprintf(tString, "%d / %d", mMYINFO[0].mUseAvatar.aBottleCount[i], 30);
v11 = self->uX + 55 * (i % 5) + 44;
sXa = v11 - UTIL::ReturnStringLength(tString) / 2;
sY = self->uY + 55 * (i / 5) + 77;
UTIL::DrawFont2D(tString, sXa, sY, 1);
}
}
}
}