#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(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