diff --git a/extensions/cstrike/natives.cpp b/extensions/cstrike/natives.cpp index b0844224..b7c862e8 100644 --- a/extensions/cstrike/natives.cpp +++ b/extensions/cstrike/natives.cpp @@ -34,6 +34,7 @@ #include "forwards.h" #include "util_cstrike.h" #include +#include #if SOURCE_ENGINE == SE_CSGO #include "itemdef-hash.h" @@ -178,12 +179,8 @@ static cell_t CS_SwitchTeam(IPluginContext *pContext, const cell_t *params) return pContext->ThrowNativeError("Client index %d is not valid", params[1]); } - unsigned char vstk[sizeof(CBaseEntity *) + sizeof(int)]; - unsigned char *vptr = vstk; + ArgBuffer vstk(pEntity, params[2]); - *(CBaseEntity **)vptr = pEntity; - vptr += sizeof(CBaseEntity *); - *(int *)vptr = params[2]; pWrapper->Execute(vstk, NULL); #else if (g_pSDKTools == NULL) @@ -273,20 +270,10 @@ static cell_t CS_DropWeapon(IPluginContext *pContext, const cell_t *params) if (params[4] == 1 && g_pCSWeaponDropDetoured) g_pIgnoreCSWeaponDropDetour = true; - unsigned char vstk[sizeof(CBaseEntity *) * 2 + sizeof(bool) * 2]; - unsigned char *vptr = vstk; - // first one is always false. second is true to toss, false to just drop - *(CBaseEntity **)vptr = pEntity; - vptr += sizeof(CBaseEntity *); - *(CBaseEntity **)vptr = pWeapon; - vptr += sizeof(CBaseEntity *); - *(bool *)vptr = false; - vptr += sizeof(bool); - *(bool *)vptr = (params[3]) ? true : false; - - pWrapper->Execute(vstk, NULL); + ArgBuffer vstk(pEntity, pWeapon, false, (params[3]) ? true : false); + pWrapper->Execute(vstk, NULL); return 1; } @@ -333,14 +320,7 @@ static cell_t CS_TerminateRound(IPluginContext *pContext, const cell_t *params) if (params[3] == 1 && g_pTerminateRoundDetoured) g_pIgnoreTerminateDetour = true; - unsigned char vstk[sizeof(void *) + sizeof(float)+ sizeof(int)]; - unsigned char *vptr = vstk; - - *(void **)vptr = gamerules; - vptr += sizeof(void *); - *(float *)vptr = sp_ctof(params[1]); - vptr += sizeof(float); - *(int*)vptr = reason; + ArgBuffer vstk(gamerules, sp_ctof(params[1]), reason); pWrapper->Execute(vstk, NULL); #elif SOURCE_ENGINE == SE_CSGO && !defined(WIN32) @@ -368,18 +348,7 @@ static cell_t CS_TerminateRound(IPluginContext *pContext, const cell_t *params) if (params[3] == 1 && g_pTerminateRoundDetoured) g_pIgnoreTerminateDetour = true; - unsigned char vstk[sizeof(void *) + sizeof(float) + (sizeof(int)*3)]; - unsigned char *vptr = vstk; - - *(void **)vptr = gamerules; - vptr += sizeof(void *); - *(float *)vptr = sp_ctof(params[1]); - vptr += sizeof(float); - *(int*)vptr = reason; - vptr += sizeof(int); - *(int*)vptr = 0; - vptr += sizeof(int); - *(int*)vptr = 0; + ArgBuffer vstk(gamerules, sp_ctof(params[1]), reason, 0, 0); pWrapper->Execute(vstk, NULL); #else // CSGO Win32 @@ -881,15 +850,9 @@ static cell_t CS_SetClientClanTag(IPluginContext *pContext, const cell_t *params char *szNewTag; pContext->LocalToString(params[2], &szNewTag); - unsigned char vstk[sizeof(CBaseEntity *) + sizeof(char *)]; - unsigned char *vptr = vstk; - - *(CBaseEntity **)vptr = pEntity; - vptr += sizeof(CBaseEntity *); - *(char **)vptr = szNewTag; + ArgBuffer vstk(pEntity, szNewTag); pWrapper->Execute(vstk, NULL); - return 1; #endif } diff --git a/extensions/cstrike/util_cstrike.cpp b/extensions/cstrike/util_cstrike.cpp index ae2932ec..90f879df 100644 --- a/extensions/cstrike/util_cstrike.cpp +++ b/extensions/cstrike/util_cstrike.cpp @@ -35,6 +35,7 @@ #include #if SOURCE_ENGINE == SE_CSGO #include "itemdef-hash.h" +#include ClassnameMap g_mapClassToDefIdx; ItemIndexMap g_mapDefIdxToClass; @@ -134,16 +135,9 @@ CEconItemView *GetEconItemView(CBaseEntity *pEntity, int iSlot) if (team != 2 && team != 3) return NULL; - CEconItemView *ret; - unsigned char vstk[sizeof(void *) + sizeof(int) * 2]; - unsigned char *vptr = vstk; - - *(void **)vptr = (void *)((intptr_t)pEntity + thisPtrOffset); - vptr += sizeof(void *); - *(int *)vptr = team; - vptr += sizeof(int); - *(int *)vptr = iSlot; + ArgBuffer vstk(reinterpret_cast(((intptr_t)pEntity + thisPtrOffset)), iSlot); + CEconItemView *ret = nullptr; pWrapper->Execute(vstk, &ret); return ret; @@ -163,13 +157,9 @@ CCSWeaponData *GetCCSWeaponData(CEconItemView *view) pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, &retpass, NULL, 0)) } - unsigned char vstk[sizeof(CEconItemView *)]; - unsigned char *vptr = vstk; - - *(CEconItemView **)vptr = view; - - CCSWeaponData *pWpnData = NULL; + ArgBuffer vstk(view); + CCSWeaponData *pWpnData = nullptr; pWrapper->Execute(vstk, &pWpnData); return pWpnData; @@ -234,14 +224,9 @@ CEconItemDefinition *GetItemDefintionByName(const char *classname) g_RegNatives.Register(pWrapper); } - unsigned char vstk[sizeof(void *) + sizeof(const char *)]; - unsigned char *vptr = vstk; + ArgBuffer vstk(pSchema, classname); - *(void **)vptr = pSchema; - vptr += sizeof(void *); - *(const char **)vptr = classname; - - CEconItemDefinition *pItemDef = NULL; + CEconItemDefinition *pItemDef = nullptr; pWrapper->Execute(vstk, &pItemDef); return pItemDef; @@ -390,8 +375,6 @@ ItemDefHashValue *GetHashValueFromWeapon(const char *szWeapon) #if SOURCE_ENGINE != SE_CSGO void *GetWeaponInfo(int weaponID) { - void *info; - static ICallWrapper *pWrapper = NULL; if (!pWrapper) { @@ -407,11 +390,9 @@ void *GetWeaponInfo(int weaponID) pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retpass, pass, 1)) } - unsigned char vstk[sizeof(int)]; - unsigned char *vptr = vstk; - - *(int *)vptr = weaponID; + ArgBuffer vstk(weaponID); + void *info = nullptr; pWrapper->Execute(vstk, &info); return info; @@ -435,7 +416,6 @@ const char *GetWeaponNameFromClassname(const char *weapon) const char *GetTranslatedWeaponAlias(const char *weapon) { #if SOURCE_ENGINE != SE_CSGO - const char *alias = NULL; static ICallWrapper *pWrapper = NULL; @@ -453,12 +433,11 @@ const char *GetTranslatedWeaponAlias(const char *weapon) pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retpass, pass, 1)) } - unsigned char vstk[sizeof(const char *)]; - unsigned char *vptr = vstk; - - *(const char **)vptr = GetWeaponNameFromClassname(weapon); + ArgBuffer vstk(GetWeaponNameFromClassname(weapon)); + const char *alias = nullptr; pWrapper->Execute(vstk, &alias); + return alias; #else //this should work for both games maybe replace both? static const char *szAliases[] = @@ -492,8 +471,6 @@ const char *GetTranslatedWeaponAlias(const char *weapon) int AliasToWeaponID(const char *weapon) { #if SOURCE_ENGINE != SE_CSGO - int weaponID = 0; - static ICallWrapper *pWrapper = NULL; if (!pWrapper) @@ -510,11 +487,9 @@ int AliasToWeaponID(const char *weapon) pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retpass, pass, 1)) } - unsigned char vstk[sizeof(const char *)]; - unsigned char *vptr = vstk; - - *(const char **)vptr = GetWeaponNameFromClassname(weapon); + ArgBuffer vstk(GetWeaponNameFromClassname(weapon)); + int weaponID = 0; pWrapper->Execute(vstk, &weaponID); return weaponID; @@ -531,10 +506,8 @@ int AliasToWeaponID(const char *weapon) const char *WeaponIDToAlias(int weaponID) { #if SOURCE_ENGINE != SE_CSGO - const char *alias = NULL; static ICallWrapper *pWrapper = NULL; - if (!pWrapper) { REGISTER_ADDR("WeaponIDToAlias", 0, @@ -549,11 +522,9 @@ const char *WeaponIDToAlias(int weaponID) pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retpass, pass, 1)) } - unsigned char vstk[sizeof(int)]; - unsigned char *vptr = vstk; - - *(int *)vptr = weaponID; + ArgBuffer vstk(weaponID); + const char *alias = nullptr; pWrapper->Execute(vstk, &alias); return alias; diff --git a/extensions/sdktools/inputnatives.cpp b/extensions/sdktools/inputnatives.cpp index d64233f8..9f6a64ac 100644 --- a/extensions/sdktools/inputnatives.cpp +++ b/extensions/sdktools/inputnatives.cpp @@ -32,6 +32,7 @@ #include "extension.h" #include "variant-t.h" #include +#include ICallWrapper *g_pAcceptInput = NULL; @@ -79,9 +80,6 @@ static cell_t AcceptEntityInput(IPluginContext *pContext, const cell_t *params) CBaseEntity *pActivator, *pCaller, *pDest; char *inputname; - unsigned char vstk[sizeof(void *) + sizeof(const char *) + sizeof(CBaseEntity *)*2 + SIZEOF_VARIANT_T + sizeof(int)]; - unsigned char *vptr = vstk; - ENTINDEX_TO_CBASEENTITY(params[1], pDest); pContext->LocalToString(params[2], &inputname); if (params[3] == -1) @@ -97,19 +95,9 @@ static cell_t AcceptEntityInput(IPluginContext *pContext, const cell_t *params) ENTINDEX_TO_CBASEENTITY(params[4], pCaller); } - *(void **)vptr = pDest; - vptr += sizeof(void *); - *(const char **)vptr = inputname; - vptr += sizeof(const char *); - *(CBaseEntity **)vptr = pActivator; - vptr += sizeof(CBaseEntity *); - *(CBaseEntity **)vptr = pCaller; - vptr += sizeof(CBaseEntity *); - memcpy(vptr, g_Variant_t, SIZEOF_VARIANT_T); - vptr += SIZEOF_VARIANT_T; - *(int *)vptr = params[5]; + ArgBuffer vstk(pDest, inputname, pActivator, pCaller, g_Variant_t, params[5]); - bool ret; + bool ret = false; g_pAcceptInput->Execute(vstk, &ret); _init_variant_t(); diff --git a/extensions/sdktools/outputnatives.cpp b/extensions/sdktools/outputnatives.cpp index 0784b120..f9b3a896 100644 --- a/extensions/sdktools/outputnatives.cpp +++ b/extensions/sdktools/outputnatives.cpp @@ -32,6 +32,7 @@ #include "extension.h" #include "variant-t.h" #include "output.h" +#include ICallWrapper *g_pFireOutput = NULL; @@ -365,9 +366,6 @@ static cell_t FireEntityOutput(IPluginContext *pContext, const cell_t *params) void *pOutput = NULL; char *outputname; - unsigned char vstk[sizeof(void *) + sizeof(CBaseEntity *)*2 + SIZEOF_VARIANT_T + sizeof(float)]; - unsigned char *vptr = vstk; - ENTINDEX_TO_CBASEENTITY(params[1], pCaller); pContext->LocalToString(params[2], &outputname); @@ -382,17 +380,9 @@ static cell_t FireEntityOutput(IPluginContext *pContext, const cell_t *params) ENTINDEX_TO_CBASEENTITY(params[3], pActivator); } - *(void **)vptr = pOutput; - vptr += sizeof(void *); - memcpy(vptr, g_Variant_t, SIZEOF_VARIANT_T); - vptr += SIZEOF_VARIANT_T; - *(CBaseEntity **)vptr = pActivator; - vptr += sizeof(CBaseEntity *); - *(CBaseEntity **)vptr = pCaller; - vptr += sizeof(CBaseEntity *); - *(float *)vptr = sp_ctof(params[4]); + ArgBuffer vstk(pOutput, g_Variant_t, pActivator, pCaller, sp_ctof(params[4])); - g_pFireOutput->Execute(vstk, NULL); + g_pFireOutput->Execute(vstk, nullptr); _init_variant_t(); return 1; diff --git a/extensions/tf2/natives.cpp b/extensions/tf2/natives.cpp index d7715d32..84ac233d 100644 --- a/extensions/tf2/natives.cpp +++ b/extensions/tf2/natives.cpp @@ -35,6 +35,7 @@ #include "RegNatives.h" #include +#include // native TF2_MakeBleed(client, attacker, Float:duration) cell_t TF2_MakeBleed(IPluginContext *pContext, const cell_t *params) @@ -80,26 +81,14 @@ cell_t TF2_MakeBleed(IPluginContext *pContext, const cell_t *params) } void *obj = (void *)((uint8_t *)pEntity + playerSharedOffset->actual_offset); + ArgBuffer // Custom Damage type (bleeding) + vstk(obj, pAttacker, NULL, sp_ctof(params[3]), 4, false, 32); - unsigned char vstk[sizeof(void *) + 2*sizeof(CBaseEntity *) + sizeof(float) + sizeof(int) + sizeof(bool) + sizeof(int)]; - unsigned char *vptr = vstk; - - *(void **)vptr = obj; - vptr += sizeof(void *); - *(CBaseEntity **)vptr = pAttacker; - vptr += sizeof(CBaseEntity *); - *(CBaseEntity **)vptr = NULL; - vptr += sizeof(CBaseEntity *); - *(float *)vptr = sp_ctof(params[3]); - vptr += sizeof(float); - *(int *)vptr = 4; // Damage amount - vptr += sizeof(int); - *(bool *)vptr = false; // Permanent - vptr += sizeof(bool); - *(int *)vptr = 34; // Custom Damage type (bleeding) - - pWrapper->Execute(vstk, NULL); - + pWrapper->Execute(vstk, nullptr); return 1; } @@ -138,20 +127,11 @@ cell_t TF2_Burn(IPluginContext *pContext, const cell_t *params) } void *obj = (void *)((uint8_t *)pEntity + playerSharedOffset->actual_offset); + ArgBuffer //duration + vstk(obj, pTarget, nullptr, 10.0f); - unsigned char vstk[sizeof(void *) + 2*sizeof(CBaseEntity *) + sizeof(float)]; - unsigned char *vptr = vstk; - - *(void **)vptr = obj; - vptr += sizeof(void *); - *(CBaseEntity **)vptr = pTarget; - vptr += sizeof(CBaseEntity *); - *(CBaseEntity **)vptr = NULL; - vptr += sizeof(CBaseEntity *); - *(float *)vptr = 10.0f; // duration - - pWrapper->Execute(vstk, NULL); - + pWrapper->Execute(vstk, nullptr); return 1; } @@ -200,22 +180,9 @@ cell_t TF2_Disguise(IPluginContext *pContext, const cell_t *params) return pContext->ThrowNativeError("Target client index %d is not valid", params[4]); } - unsigned char vstk[sizeof(void *) + 2*sizeof(int) + sizeof(CBaseEntity *) + sizeof(bool)]; - unsigned char *vptr = vstk; - - - *(void **)vptr = obj; - vptr += sizeof(void *); - *(int *)vptr = params[2]; - vptr += sizeof(int); - *(int *)vptr = params[3]; - vptr += sizeof(int); - *(CBaseEntity **)vptr = pTarget; - vptr += sizeof(CBaseEntity *); - *(bool *)vptr = true; - - pWrapper->Execute(vstk, NULL); + ArgBuffer vstk(obj, params[2], params[3], pTarget, true); + pWrapper->Execute(vstk, nullptr); return 1; } @@ -237,15 +204,9 @@ cell_t TF2_RemoveDisguise(IPluginContext *pContext, const cell_t *params) } void *obj = (void *)((uint8_t *)pEntity + playerSharedOffset->actual_offset); + ArgBuffer vstk(obj); - unsigned char vstk[sizeof(void *)]; - unsigned char *vptr = vstk; - - - *(void **)vptr = obj; - - pWrapper->Execute(vstk, NULL); - + pWrapper->Execute(vstk, nullptr); return 1; } @@ -284,20 +245,9 @@ cell_t TF2_AddCondition(IPluginContext *pContext, const cell_t *params) } void *obj = (void *)((uint8_t *)pEntity + playerSharedOffset->actual_offset); + ArgBuffer vstk(obj, params[2], params[3], pInflictor); - unsigned char vstk[sizeof(void *) + sizeof(int) + sizeof(float) + sizeof(CBaseEntity *)]; - unsigned char *vptr = vstk; - - *(void **)vptr = obj; - vptr += sizeof(void *); - *(int *)vptr = params[2]; - vptr += sizeof(int); - *(float *)vptr = *(float *)¶ms[3]; - vptr += sizeof(float); - *(CBaseEntity **)vptr = pInflictor; - - pWrapper->Execute(vstk, NULL); - + pWrapper->Execute(vstk, nullptr); return 1; } @@ -326,18 +276,9 @@ cell_t TF2_RemoveCondition(IPluginContext *pContext, const cell_t *params) } void *obj = (void *)((uint8_t *)pEntity + playerSharedOffset->actual_offset); + ArgBuffer vstk(obj, params[2], true); - unsigned char vstk[sizeof(void *) + sizeof(int) + sizeof(bool)]; - unsigned char *vptr = vstk; - - *(void **)vptr = obj; - vptr += sizeof(void *); - *(int *)vptr = params[2]; - vptr += sizeof(int); - *(bool *)vptr = true; - - pWrapper->Execute(vstk, NULL); - + pWrapper->Execute(vstk, nullptr); return 1; } @@ -379,22 +320,9 @@ cell_t TF2_StunPlayer(IPluginContext *pContext, const cell_t *params) } void *obj = (void *)((uint8_t *)pEntity + playerSharedOffset->actual_offset); + ArgBuffer vstk(obj, sp_ctof(params[2]), sp_ctof(params[3]), params[4], pAttacker); - unsigned char vstk[sizeof(void *) + 2*sizeof(float) + sizeof(int) + sizeof(CBaseEntity *)]; - unsigned char *vptr = vstk; - - *(void **)vptr = obj; - vptr += sizeof(void *); - *(float *)vptr = sp_ctof(params[2]); - vptr += sizeof(float); - *(float *)vptr = sp_ctof(params[3]); - vptr += sizeof(float); - *(int *)vptr = params[4]; - vptr += sizeof(int); - *(CBaseEntity **)vptr = pAttacker; - - pWrapper->Execute(vstk, NULL); - + pWrapper->Execute(vstk, nullptr); return 1; } @@ -419,21 +347,9 @@ cell_t TF2_SetPowerplayEnabled(IPluginContext *pContext, const cell_t *params) return pContext->ThrowNativeError("Client index %d is not valid", params[1]); } - bool bEnablePP = false; - if (params[2] != 0) - { - bEnablePP = true; - } - - unsigned char vstk[sizeof(void *) + sizeof(bool)]; - unsigned char *vptr = vstk; - - *(void **)vptr = (void *)pEntity; - vptr += sizeof(void *); - *(bool *)vptr = bEnablePP; - - pWrapper->Execute(vstk, NULL); + ArgBuffer vstk(pEntity, params[2] != 0); + pWrapper->Execute(vstk, nullptr); return 1; } @@ -468,14 +384,9 @@ cell_t TF2_Respawn(IPluginContext *pContext, const cell_t *params) return pContext->ThrowNativeError("Client index %d is not valid", params[1]); } - unsigned char vstk[sizeof(void *)]; - unsigned char *vptr = vstk; - - - *(void **)vptr = (void *)pEntity; - - pWrapper->Execute(vstk, NULL); + ArgBuffer vstk(pEntity); + pWrapper->Execute(vstk, nullptr); return 1; } @@ -501,15 +412,9 @@ cell_t TF2_Regenerate(IPluginContext *pContext, const cell_t *params) return pContext->ThrowNativeError("Client index %d is not valid", params[1]); } - unsigned char vstk[sizeof(void *) + sizeof(bool)]; - unsigned char *vptr = vstk; - - *(void **)vptr = (void *)pEntity; - vptr += sizeof(void *); - *(bool *)vptr = true; - - pWrapper->Execute(vstk, NULL); - + ArgBuffer vstk(pEntity, true); + + pWrapper->Execute(vstk, nullptr); return 1; } @@ -552,10 +457,8 @@ cell_t TF2_IsPlayerInDuel(IPluginContext *pContext, const cell_t *params) return pContext->ThrowNativeError("Client index %d is not valid", params[1]); } - unsigned char vstk[sizeof(CBaseEntity *)]; - unsigned char *vptr = vstk; - *(CBaseEntity **)vptr = pPlayer; - + ArgBuffer vstk(pPlayer); + bool retValue; pWrapper->Execute(vstk, &retValue); @@ -596,14 +499,9 @@ cell_t TF2_IsHolidayActive(IPluginContext *pContext, const cell_t *params) g_RegNatives.Register(pWrapper); } - unsigned char vstk[sizeof(void *) + sizeof(int)]; - unsigned char *vptr = vstk; - *(void **)vptr = pGameRules; - vptr += sizeof(void *); - *(int *)vptr = params[1]; - - bool retValue; + ArgBuffer vstk(pGameRules, params[1]); + bool retValue; pWrapper->Execute(vstk, &retValue); return (retValue) ? 1 : 0; @@ -646,15 +544,9 @@ cell_t TF2_RemoveWearable(IPluginContext *pContext, const cell_t *params) return pContext->ThrowNativeError("Wearable index %d is not valid", params[2]); } - unsigned char vstk[sizeof(void *) + sizeof(CBaseEntity *)]; - unsigned char *vptr = vstk; - - *(void **)vptr = (void *)pEntity; - vptr += sizeof(void *); - *(CBaseEntity **)vptr = pWearable; - - pWrapper->Execute(vstk, NULL); + ArgBuffer vstk(pEntity, pWearable); + pWrapper->Execute(vstk, nullptr); return 1; } diff --git a/public/sm_argbuffer.h b/public/sm_argbuffer.h new file mode 100644 index 00000000..f951f9f8 --- /dev/null +++ b/public/sm_argbuffer.h @@ -0,0 +1,77 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2019 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program 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 + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +/** + * A type-safe abstraction for creating a contiguous blob of memory used for + * vtable function calls. This blob contains the parameters to be passed into + * the function call. + */ +template +class ArgBuffer { +public: + ArgBuffer(T t, Rest... rest) { + unsigned char *ptr = buff; + buildbuffer(&ptr, t, rest...); + } + + operator void*() { return buff; } + operator unsigned char*() { return buff; } + + constexpr int size() const { + return sizeof(buff); + } + +private: + template + constexpr static int sizetypes() { + return sizeof(K); + } + template + constexpr static int sizetypes() { + return sizeof(K) + sizetypes(); + } + + template + void buildbuffer(unsigned char **ptr, K k) { + memcpy(*ptr, &k, sizeof(k)); + *ptr += sizeof(K); + } + + template + void buildbuffer(unsigned char **ptr, K k, Kn... kn) { + buildbuffer(ptr, k); + if (sizeof...(kn)!=0) + buildbuffer(ptr, kn...); + } + +private: + unsigned char buff[sizetypes()]; +};