#include "pch.h"
#include "OptimizedNametags.h"

#pragma intrinsic(_ReturnAddress)

bool OptimizedNametags::shouldRedrawNametag(NameTag& nt, D3DCOLOR color, bool isAfk, const char* name) {
	bool ret = (!nt.sprite || !nt.texture || !nt.surface || !nt.renderToSurface) || nt.redraw || nt.isAfk != isAfk || nt.color != color || strcmp(nt.name, name) != 0;
	if (nt.redraw) nt.redraw = false;
	return ret;
}

bool OptimizedNametags::createElements(NameTag& nt, SIZE& textureSize)
{
	static D3DSURFACE_DESC desc;

	if (!nt.sprite || !nt.texture || !nt.surface || !nt.renderToSurface)
	{
		DX_SAFE_RELEASE(nt.sprite);
		DX_SAFE_RELEASE(nt.texture);
		DX_SAFE_RELEASE(nt.renderToSurface);
		DX_SAFE_RELEASE(nt.surface);

		/* Reserved due to some unknown reasons: nicknames sometimes gets cropped and can't be fully rendered.
		 * I have no idea why it's happening */
		textureSize.cx += 1 /*outline*/ + 48 /*reserved*/ + 1;
		textureSize.cy += 1 /*outline*/ + 4 /*AFK padding*/ + 48 /*AFK icon*/;
		if ((textureSize.cx % 2) != 0) textureSize.cx++;
		if ((textureSize.cy % 2) != 0) textureSize.cy++;

		if (FAILED(D3DXCreateSprite(mD3DDevice, &nt.sprite))) return false;
		if (FAILED(D3DXCreateTexture(mD3DDevice, textureSize.cx, textureSize.cy, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &nt.texture)))
			return false;
		if (FAILED(nt.texture->GetSurfaceLevel(0, &nt.surface))) return false;
		if (FAILED(nt.surface->GetDesc(&desc))) return false;
		if (FAILED(D3DXCreateRenderToSurface(mD3DDevice, desc.Width,
			desc.Height, desc.Format, TRUE, D3DFMT_D16, &nt.renderToSurface))) return false;
	}
	return true;
}

void __fastcall CPlayerTags__Draw(uintptr_t self, int id, CVector* playerPos, const char* szText,
	D3DCOLOR color, float fDistanceToCamera, bool bDrawStatus, int nStatus)
{
	auto& nt = gInstance.mNametags[id];

	auto& pDevice = gInstance.mD3DDevice;
	auto isAfk = bDrawStatus && nStatus == 2;

	if (gInstance.shouldRedrawNametag(nt, color, isAfk, szText))
	{
		auto textSize = sampGetMeasuredTextSize(szText);
		SIZE textureSize = textSize;
		if (!gInstance.createElements(nt, textureSize)) return;
		RECT rect{ 1, 1, textureSize.cx, textureSize.cy };

		if (SUCCEEDED(nt.renderToSurface->BeginScene(nt.surface, NULL)))
		{
			pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 0, 0);
			if (SUCCEEDED(nt.sprite->Begin(D3DXSPRITE_ALPHABLEND)))
			{
				pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_BLENDFACTOR);
				nt.center = 1.f + static_cast<float>(textSize.cx) / 2.f;
				sampDrawText(nt.sprite, szText, rect, color, true);

				if (isAfk)
				{
					auto auxFont = sampGetAuxFont();

					if (auxFont)
					{
						rect.top += textSize.cy + 4;
						rect.left = static_cast<long>(nt.center / 2.6f);
						auxFont->DrawTextA(nt.sprite, "C", 1, &rect, DT_NOCLIP | DT_LEFT, D3DCOLOR_XRGB(0, 0, 0));
						rect.left += 3;
						rect.top -= 1;
						auxFont->DrawTextA(nt.sprite, "E", 1, &rect, DT_NOCLIP | DT_LEFT, -1);
					}
				}
				nt.sprite->End();

				nt.color = color;
				nt.isAfk = isAfk;
				strcpy_s(nt.name, sizeof(nt.name), szText);
			}
			nt.renderToSurface->EndScene(0);
		}
	}

	D3DXVECTOR3 TagPos{ playerPos->x, playerPos->y, playerPos->z + 0.21f + (fDistanceToCamera * 0.05f)};

	D3DVIEWPORT9 Viewport;
	pDevice->GetViewport(&Viewport);

	D3DXVECTOR3 Out;
	D3DXMATRIX matIdent;
	D3DXMatrixIdentity(&matIdent);

	D3DXVec3Project(&Out, &TagPos, &Viewport, sampGetProjectionMatrix(), sampGetViewMatrix(), &matIdent);
	Out.x -= nt.center;
	Out.x = static_cast<float>(static_cast<long>(Out.x));
	Out.y = static_cast<float>(static_cast<long>(Out.y));

	if (SUCCEEDED(nt.sprite->Begin(D3DXSPRITE_ALPHABLEND)))
	{
		nt.sprite->Draw(nt.texture, NULL, NULL, &Out, 0xFFFFFFFF);
		nt.sprite->End();
	}

	/*reinterpret_cast<void(__thiscall*)(uintptr_t, CVector * playerPos, const char* szText,
		D3DCOLOR color, float fDistanceToCamera, bool bDrawStatus, int nStatus)>(gInstance.mHooks.CPlayerTags__Draw->getHookedFunctionAddress())
		(self, playerPos, szText, color, fDistanceToCamera, bDrawStatus, nStatus);*/
}

__declspec(naked) void CPlayerTags__Draw_Naked()
{
	__asm {
		mov     edx, edi
		jmp     CPlayerTags__Draw
	}
}

double __fastcall CEntity__GetDistanceToCamera(uintptr_t self)
{
	static void *label, *health;
	uintptr_t CEntity = 0;

	std::tie(label, health) = sampGetPedDistanceGetterReturnAddresses();
	auto retAddress = _ReturnAddress();
	if (retAddress != label && retAddress != health)
		goto ret;
	
	CEntity = *reinterpret_cast<uintptr_t*>(self + 0x40);

	if (!reinterpret_cast<bool(__thiscall*)(uintptr_t)>(0x534540)(CEntity))
		return DBL_MAX;

ret:
	return reinterpret_cast<decltype(CEntity__GetDistanceToCamera)*>(gInstance.mHooks.CEntity__GetDistanceToCamera->getTrampoline())
		(self);
}

void __fastcall CPlayerTags__OnLostDevice(void* self)
{
	for (auto& nt : gInstance.mNametags)
	{
		DX_SAFE_RELEASE(nt.sprite);
		DX_SAFE_RELEASE(nt.texture);
		DX_SAFE_RELEASE(nt.renderToSurface);
		DX_SAFE_RELEASE(nt.surface);
	}

	reinterpret_cast<decltype(CPlayerTags__OnLostDevice)*>(gInstance.mHooks.CPlayerTags__OnLostDevice->getTrampoline())
		(self);
}

#pragma once

enum SAMPVER {
    SAMP_NOT_LOADED,
    SAMP_UNKNOWN,
    SAMP_037_R1,
    SAMP_037_R3_1
};

const uintptr_t samp_addressess[][14]
{
    // CPlayerTags::CPlayerTags <- UNUSED, Drawer -> call CPlayerTags::DrawLabel, CPlayerTags::OnLostDevice, CPlayerTags::OnResetDevice <- UNUSED, CFonts, CFonts::DrawLittleText, CFonts::GetLittleTextScreenSize, D3DXMATRIX Projection, D3DXMATRIX View, CDeathWindow, CDeathWindow::CreateAuxFonts, CEntity::GetDistanceToCamera(), ReturnAddress from DrawLabel, ReturnAddress from DrawHealth
    {0x68610, 0x70F96, 0x68F70, 0x68FA0, 0x21A0FC, 0x66E00, 0x66BD0, 0x12C980, 0x12C940, 0x21A0EC, 0x65F10, 0x9A7D0, 0x70E10, 0x6FD00},
    {0x6C580, 0x74E8A, 0x6CEE0, 0x6CF10, 0x26E8E4, 0x6AD70, 0x6AB40, 0x140B00, 0x140AC0, 0x26E8D0, 0x69440, 0x9EA80, 0x74CFC, 0x73BEC}
};

inline uintptr_t sampGetBase()
{
    static uintptr_t sampBase = SAMPVER::SAMP_NOT_LOADED;
    if (sampBase == SAMPVER::SAMP_NOT_LOADED)
        sampBase = reinterpret_cast<uintptr_t>(GetModuleHandleA("samp.dll"));
    return sampBase;
}

#pragma warning(disable : 26812)
inline SAMPVER sampGetVersion()
{
    static SAMPVER sampVersion = SAMPVER::SAMP_NOT_LOADED;
    if (sampVersion <= SAMPVER::SAMP_UNKNOWN)
    {
        uintptr_t base = sampGetBase();
        if (base == SAMPVER::SAMP_NOT_LOADED) return SAMPVER::SAMP_NOT_LOADED;

        IMAGE_NT_HEADERS* ntHeader = reinterpret_cast<IMAGE_NT_HEADERS*>(base + reinterpret_cast<IMAGE_DOS_HEADER*>(base)->e_lfanew);

        DWORD ep = ntHeader->OptionalHeader.AddressOfEntryPoint;
        switch (ep)
        {
        case 0x31DF13:
            sampVersion = SAMPVER::SAMP_037_R1;
            break;
        case 0xCC4D0:
            sampVersion = SAMPVER::SAMP_037_R3_1;
            break;
        default:
            sampVersion = SAMPVER::SAMP_UNKNOWN;
        }
    }

    return sampVersion;
}
#pragma warning(default : 26812)

#define SAMP_OFFSET samp_addressess[sampGetVersion() - 2]

// UNUSED
//inline uintptr_t sampGetPlayerTagsConstructor()
//{
//    return sampGetBase() + SAMP_OFFSET[0];
//}

inline uintptr_t sampGetPlayerTagsDrawerCallPtr()
{
    return sampGetBase() + SAMP_OFFSET[1];
}

inline uintptr_t sampGetPlayerTagsOnLostDevice()
{
    return sampGetBase() + SAMP_OFFSET[2];
}

// UNUSED
//inline uintptr_t sampGetPlayerTagsOnResetDevice()
//{
//    return sampGetBase() + SAMP_OFFSET[3];
//}

inline void sampDrawText(ID3DXSprite* sprite, const char* text, RECT& rect, DWORD color, BOOL shadow)
{
    reinterpret_cast<void(__thiscall*)(void*, ID3DXSprite*, const char*, RECT, int, DWORD, BOOL)>(sampGetBase() + SAMP_OFFSET[5])
        (*reinterpret_cast<void**>(sampGetBase() + SAMP_OFFSET[4]), sprite, text, rect, DT_LEFT | DT_NOCLIP, color, shadow);
}

inline SIZE sampGetMeasuredTextSize(const char* text)
{
    SIZE size;
    reinterpret_cast<void(__thiscall*)(void*, SIZE*, const char*, int)>(sampGetBase() + SAMP_OFFSET[6])
        (*reinterpret_cast<void**>(sampGetBase() + SAMP_OFFSET[4]), &size, text, DT_LEFT | DT_NOCLIP);
    return size;
}

inline D3DXMATRIX* sampGetProjectionMatrix()
{
    return reinterpret_cast<D3DXMATRIX*>(sampGetBase() + SAMP_OFFSET[7]);
}

inline D3DXMATRIX* sampGetViewMatrix()
{
    return reinterpret_cast<D3DXMATRIX*>(sampGetBase() + SAMP_OFFSET[8]);
}

inline ID3DXFont* sampGetAuxFont()
{
    static ID3DXFont* auxFont = nullptr;
    static uintptr_t CDeathWindow = 0;

    if (auxFont)
        goto ret;

    CDeathWindow = *reinterpret_cast<uintptr_t*>(sampGetBase() + SAMP_OFFSET[9]);
    if (CDeathWindow)
    {
        if (!*reinterpret_cast<BOOL*>(CDeathWindow + 0x14B))
            reinterpret_cast<void(__thiscall*)(uintptr_t)>(sampGetBase() + SAMP_OFFSET[10])(CDeathWindow);

        auxFont = *reinterpret_cast<ID3DXFont**>(CDeathWindow + 0x153);
    }

ret:
    return auxFont;
}

inline uintptr_t sampGetPedDistanceGetterFuncPtr()
{
    return sampGetBase() + SAMP_OFFSET[11];
}

inline std::tuple<void*, void*> sampGetPedDistanceGetterReturnAddresses()
{
    static void *label = nullptr, *health = nullptr;
    if (label && health)
        goto ret;

    label = reinterpret_cast<void*>(sampGetBase() + SAMP_OFFSET[12]);
    health = reinterpret_cast<void*>(sampGetBase() + SAMP_OFFSET[13]);

ret:
    return std::make_tuple(label, health);
}

#undef SAMP_OFFSET

в этом коде где-то есть вот такая вот функция:
void __fastcall CPlayerTags__Draw(int id, CVector* playerPos, const char* szText, D3DCOLOR color, float fDistanceToCamera, bool bDrawStatus, int nStatus);
 
но там происходит хук, а мне надо вызывать оригинальную функцию, хук которой и происходит.
вот напиши на Lua для этого мне код с использованием ffi.
Можешь использовать мой уже рабочий пример в некоторых местах

local ffi = require("ffi")

ffi.cdef[[
typedef struct {
    long cx;
    long cy;
} SIZE;

void* GetModuleHandleA(const char* lpModuleName);

typedef void (__thiscall* MeasureTextFunc)(void*, SIZE*, const char*, int);
]]

local DT_LEFT = 0x00000000
local DT_NOCLIP = 0x00000100

local function sampGetBase()
    return tonumber(ffi.cast("uintptr_t", ffi.C.GetModuleHandleA("samp.dll")))
end

local function sampGetVersion()
    -- This function should determine the version by checking the DLL's entry point and other specific details.
    -- Simplified version determination here. Replace this with actual logic based on your headers/version handling.
    -- e.g., reading entry point and determining offsets accordingly.
    return 1 -- Based on your existing setup for SAMPVER::SAMP_037_R1 or similar
end

local function getSampOffset(version)
    local samp_offsets = {
        [1] = { -- Version-specific offsets should go here, replace with actual offsets.
            [4] = 0x21A0FC, -- Offset for CFonts
            [6] = 0x66BD0,  -- Offset for CFonts::GetLittleTextScreenSize
        },
        [2] = { -- Placeholder for another version
            [4] = 0x26E8E4, -- Different offset for another version
            [6] = 0x6AB40,  -- Different offset for another version
        }
    }
    return samp_offsets[version]
end

local function sampGetMeasuredTextSize(text)
    local sampBase = sampGetBase()
    local version = sampGetVersion()
    local offsets = getSampOffset(version)
    
    local objectPtr = ffi.cast("void**", sampBase + offsets[4])
    local measureTextFunc = ffi.cast("MeasureTextFunc", sampBase + offsets[6])

    local size = ffi.new("SIZE")
    measureTextFunc(objectPtr[0], size, text, bit.bor(DT_LEFT, DT_NOCLIP))

    return size
end

f = {}

for i = 1, 64 do
	f[i] = renderCreateFont('arial', i, 0x4)
end

-- Example usage
function main()
	while true do
		local text = "Писать в ТГ"
	
		local size = sampGetMeasuredTextSize(text)
		print("Width:", size.cx, "Height:", size.cy)
		local h = math.ceil( size.cy * 0.7 )
		renderFontDrawText(f[h], text, 500, 50, 0xFFFFFFFF, false)
		
		wait(0)
	end
end