GucciManeLaFlare
Member
- Joined
- Jul 30, 2023
- Messages
- 5
- Reaction score
- 4
Salutations, niggas.
I am GUWOP, coming from the trap, putting forward code for those who may find use for it.
Below, I attach the source code for a SA:MP skin changer, it automatically re-applies itself upon death, and can be toggled off, in which instance it'll reset to the skin you were wearing before.
The following code is for SA:MP 0.3.7-R5 (MD5 5BA5F0BE7AF99DFD03FB39E88A970A2B) and gta_sa (MD5 170B3A9108687B26DA2D8901C6948A18).
First, we need some state for our skin changer. I will provide a stripped down version of my context class.
Now, the code for the class
Now using this logic in some hooks which handle it
And a simple example of how to set it up:
Kind Regards,
GUWOP.
I am GUWOP, coming from the trap, putting forward code for those who may find use for it.
Below, I attach the source code for a SA:MP skin changer, it automatically re-applies itself upon death, and can be toggled off, in which instance it'll reset to the skin you were wearing before.
The following code is for SA:MP 0.3.7-R5 (MD5 5BA5F0BE7AF99DFD03FB39E88A970A2B) and gta_sa (MD5 170B3A9108687B26DA2D8901C6948A18).
First, we need some state for our skin changer. I will provide a stripped down version of my context class.
Code:
class CPlayerController;
class CPlayer;
typedef CPlayer* (__thiscall* GetPlayerFn_t)(CPlayerController*);
enum class DebugMode : bool
{
On = true,
Off = false
};
enum class SkinChangerMode : bool
{
On = true,
Off = false
};
enum class UpdateSkinMode : uint8_t
{
None = 0,
Backup = 1,
New = 2
};
class CData
{
public:
CData();
HMODULE GetSampDll() const;
HMODULE GetGtaExe() const;
// allows you extra commands and unlocks some functions
void SetDebugMode(DebugMode);
DebugMode GetDebugMode() const;
// skin changer functionality
void SetSkinChangerMode(SkinChangerMode);
SkinChangerMode GetSkinChangerMode() const;
void SetBackupSkin(int);
int GetBackupSkin();
void SetSkin(int);
int GetSkin() const;
void UpdateSkinIfNeeded();
CPlayer* GetPlayer(CPlayerController*) const;
CPlayer* GetLocalPlayer() const;
private:
HMODULE m_hmSampDll;
HMODULE m_hmGtaExe;
int* m_iDebugMode;
SkinChangerMode m_skinChangerMode;
int m_iSkin;
int m_iBackupSkin;
UpdateSkinMode m_updateSkinMode;
GetPlayerFn_t m_fnGetPlayer;
CPlayerController* m_pLocalPlayerController;
};
Now, the code for the class
Code:
// Offsets
static constexpr DWORD g_debugMode = 0x26DFE8;
static constexpr DWORD g_fnGetPlayer = 0x1010;
static constexpr DWORD g_localPlayer = 0x26EBAC;
CData::CData()
{
m_hmSampDll = GetModuleHandle(L"samp");
m_hmGtaExe = GetModuleHandle(L"gta_sa");
m_iDebugMode = (int*)((DWORD)(m_hmSampDll)+g_debugMode);
m_skinChangerMode = SkinChangerMode::Off;
m_iSkin = -1;
m_iBackupSkin = -1;
m_updateSkinMode = UpdateSkinMode::None;
m_fnGetPlayer = (GetPlayerFn_t)((DWORD)(m_hmSampDll)+g_fnGetPlayer);
m_pLocalPlayerController = *(CPlayerController**)((DWORD)(m_hmSampDll)+g_localPlayer);
}
HMODULE CData::GetSampDll() const
{
return m_hmSampDll;
}
HMODULE CData::GetGtaExe() const
{
return m_hmGtaExe;
}
CContext* CData::GetContext() const
{
return m_pContext;
}
void CData::SetDebugMode(DebugMode s)
{
*m_iDebugMode = (s == DebugMode::On) ? 1 : 0;
}
DebugMode CData::GetDebugMode() const
{
if ((*m_iDebugMode) > 0)
return DebugMode::On;
return DebugMode::Off;
}
void CData::SetSkinChangerMode(SkinChangerMode s)
{
SkinChangerMode modeOld = m_skinChangerMode;
m_skinChangerMode = s;
if (m_skinChangerMode != modeOld)
{
if (modeOld == SkinChangerMode::On)
m_updateSkinMode = UpdateSkinMode::Backup;
else
m_updateSkinMode = UpdateSkinMode::New;
}
}
SkinChangerMode CData::GetSkinChangerMode() const
{
return m_skinChangerMode;
}
void CData::SetBackupSkin(int skin)
{
m_iBackupSkin = skin;
}
int CData::GetBackupSkin()
{
return m_iBackupSkin;
}
void CData::SetSkin(int skin)
{
m_iSkin = skin;
if (GetSkinChangerMode() == SkinChangerMode::On)
m_updateSkinMode = UpdateSkinMode::New;
}
int CData::GetSkin() const
{
return m_iSkin;
}
void CData::UpdateSkinIfNeeded()
{
static constexpr DWORD g_setPlayerSkin = 0x68D00;
if (m_updateSkinMode != UpdateSkinMode::None)
{
int skin = (m_updateSkinMode == UpdateSkinMode::Backup) ? m_iBackupSkin : m_iSkin;
// we use this for a return address
// we could also just encode our intent in skin instead to make sure we are looking at our own call...
// but that isn't really worth it.
typedef void(__cdecl* SetPlayerSkinFn_t)(const char*);
static SetPlayerSkinFn_t fnSetPlayerSkin = (SetPlayerSkinFn_t)((DWORD)(m_hmSampDll)+g_setPlayerSkin);
fnSetPlayerSkin(std::to_string(skin).c_str());
#if _DEBUG
WriteToChat("{ffff00}SkinChanger: {ff00ff}Updated skin to {ffffff}%d", skin);
#endif
m_updateSkinMode = UpdateSkinMode::None;
}
}
CPlayer* CData::GetPlayer(CPlayerController* pPlayerController) const
{
return m_fnGetPlayer(pPlayerController);
}
CPlayer* CData::GetLocalPlayer() const
{
return GetPlayer(m_pLocalPlayerController);
}
Now using this logic in some hooks which handle it
Code:
static constexpr DWORD g_localPlayerSkinRetaddrs[] = { 0x3d37, 0x192e9 };
static constexpr DWORD g_localPlayerSkinBackupIgnoreRetaddrs[] = { 0x68D6A };
typedef int(__thiscall* SetPlayerSkinHook_t)(CPlayer*, int);
static SetPlayerSkinHook_t g_ogSetPlayerSkinHook = nullptr;
static int __fastcall SetPlayerSkinHook(CPlayer* self, DWORD, int skin)
{
// these are probably a bit pedantic... just wanted to make sure there's no oddities
if (IS_RETADDR_INSIDE(g_localPlayerSkinRetaddrs) && (self == g_data.GetLocalPlayer()))
{
// don't update backup if we are setting skin ourselves
if (!IS_RETADDR_INSIDE(g_localPlayerSkinBackupIgnoreRetaddrs))
g_data.SetBackupSkin(skin);
if (g_data.GetSkinChangerMode() == SkinChangerMode::On)
skin = g_data.GetSkin();
}
Hook::Remove(SetPlayerSkinHook);
int r = g_ogSetPlayerSkinHook(self, skin);
Hook::Reinstall(SetPlayerSkinHook);
return r;
}
typedef int(__cdecl* GtaSaEveryFrame_t)(int, int, int);
static GtaSaEveryFrame_t g_ogGtaSaEveryFrame = nullptr;
int __cdecl GtaSaEveryFrameHook(int a, int b, int c)
{
// events
g_data.UpdateSkinIfNeeded();
// whatever else u be doing cuh... removed for obv reasons
Hook::Remove(GtaSaEveryFrameHook);
int r = g_ogGtaSaEveryFrame(a, b, c);
Hook::Reinstall(GtaSaEveryFrameHook);
return r;
}
static constexpr DWORD g_fnGtaSaEveryFrame = 0x7F99B0; // can't disassemble in IDA, try ghidra decompiler
// it's also stored inside "dword_C97B24 + 152" but
// going there after, the fp is null
static constexpr DWORD g_fnSetPlayerSkin = 0xaff50;
void Hooks::Install()
{
Hook::Install((void*)((DWORD)(g_data.GetGtaExe()) + g_fnGtaSaEveryFrame), GtaSaEveryFrameHook, (void**)&g_ogGtaSaEveryFrame);
Hook::Install((void*)((DWORD)(g_data.GetSampDll()) + g_fnSetPlayerSkin), SetPlayerSkinHook, (void**)&g_ogSetPlayerSkinHook);
}
And a simple example of how to set it up:
Code:
g_data.SetDebugMode(DebugMode::On);
g_data.SetSkinChangerMode(SkinChangerMode::On);
g_data.SetSkin(294);
Hooks::Install();
Kind Regards,
GUWOP.