up
, maybe it will help you
Pretty much you have to reconstruct C_EconItemView::GenerateStickerMaterials.
PHP Code:
Code:
class C_EconItemView
{
public:
void GenerateStickerMaterials()
{
if ( !GetItemSchema() )
return;
int iSupportedStickerSlots = 0;
CCStrike15ItemDefinition* pItemDefinition = GetItemDefinition();
if ( pItemDefinition )
iSupportedStickerSlots = pItemDefinition->GetNumSupportedStickerSlots();
m_vStickerMaterialReferences.RemoveAll();
if ( !pItemDefinition || iSupportedStickerSlots < 1 )
return;
CUnknown* pUnknown = reinterpret_cast<CUnknown*>( this + 0xC );
for ( int i = 0; i < iSupportedStickerSlots; ++i )
{
int iStickerID = pUnknown->iGetStickerIDBySlot( i, 0, 0 );
if ( !iStickerID )
continue;
float fWearProgress = pUnknown->fGetStickerInfo( i, InfoTypes::WearProgress, 0.f );
float fPatternScale = pUnknown->fGetStickerInfo( i, InfoTypes::PatternScale, 1.f );
float fPatternRotation = pUnknown->fGetStickerInfo( i, InfoTypes::PatternRotation, 0.f );
const char* pszStickerSlotMaterial = pItemDefinition->GetStickerSlotMaterialBySlotIndex( i );
if ( !pszStickerSlotMaterial || !pszStickerSlotMaterial[0] )
continue;
char szWeaponDecal[32];
V_FileBase( szWeaponDecal, pszStickerSlotMaterial, 32 );
CStickerKit* pSK = reinterpret_cast<CCStrike15ItemSystem*>( GetItemSchema() + 4 )->GetStickerKitDefinition( iStickerID );
if ( !pSK )
continue;
if ( !pSK->sticker_material.m_iLength )
continue;
if ( !pSK->sticker_material.m_pszString || !pSK->sticker_material.m_pszString[0] )
continue;
char szPath[MAX_PATH];
if ( pSK->bCustomStickerMaterial ) // Doesn't seem to be set to true for any of the stickers currently in-game. Used for the people that want to get their stickers in the Workshop?
V_snprintf( szPath, sizeof szPath, "%s", pSK->sticker_material.m_pszString );
else
V_snprintf( szPath, sizeof szPath, "materials/models/weapons/customization/stickers/%s.vmt", pSK->sticker_material.m_pszString );
char szHash[32];
V_snprintf( szHash, sizeof szHash, "%f_%f_%f", fPatternRotation, fPatternScale, fWearProgress );
unsigned uHash = HashStringCaseless( szHash );
char szMaterialName1st[128]; V_snprintf( szMaterialName1st, sizeof szMaterialName1st, "sticker_%s_%s_%i_1st", szWeaponDecal, pSK->sticker_material.m_pszString, uHash );
char szMaterialName3rd[128]; V_snprintf( szMaterialName3rd, sizeof szMaterialName3rd, "sticker_%s_%s_%i_3rd", szWeaponDecal, pSK->sticker_material.m_pszString, uHash );
CMaterial* pFirstPersonMaterial = materials->FindProceduralMaterial( szMaterialName1st, "Other textures", 0 );
CMaterial* pThirdPersonMaterial = materials->FindProceduralMaterial( szMaterialName3rd, "Other textures", 0 );
if ( pFirstPersonMaterial->IsErrorMaterial() || pThirdPersonMaterial->IsErrorMaterial() )
{
KeyValues* kv = new KeyValues( "vmt" );
if ( kv->LoadFromFile( g_pFullFileSystem /* + 4*/, szPath, "GAME", 0 ) )
{
kv->SetFloat( "$wearprogress", fWearProgress );
kv->SetFloat( "$patternrotation", fPatternRotation );
kv->SetFloat( "$patternscale", fPatternScale );
if ( pFirstPersonMaterial->IsErrorMaterial() )
{
KeyValues* kv2 = new KeyValues( "vmt" );
if ( kv2->LoadFromFile( g_pFullFileSystem /* + 4*/, pszStickerSlotMaterial, "GAME", 0 ) )
{
if ( !cl_righthand->GetInt() )
kv2->SetInt( "$mirrorhorizontal", 1 );
kv2->MergeFrom( kv, KeyValues::MERGE_KV_UPDATE );
char szPath[MAX_PATH];
V_snprintf( szPath, sizeof szPath, "materials/models/weapons/customization/stickers/default/sticker_proxies_%i.vmt", i );
KeyValues* kv3 = new KeyValues( "vmt" );
if ( kv3->LoadFromFile g_pFullFileSystem /* + 4*/, szPath, "GAME", 0 )
kv2->MergeFrom( kv3, KeyValues::MERGE_KV_UPDATE );
pFirstPersonMaterial = materials->CreateMaterial( szMaterialName1st, kv2 );
pFirstPersonMaterial->Refresh();
delete kv3;
}
delete kv2;
}
if ( pThirdPersonMaterial->IsErrorMaterial() )
{
KeyValues* kv2 = new KeyValues( "WeaponDecal" );
kv2->MergeFrom( kv, KeyValues::MERGE_KV_UPDATE );
kv2->SetInt( "$thirdperson", 1 );
pThirdPersonMaterial = materials->CreateMaterial( szMaterialName3rd, kv2 );
pThirdPersonMaterial->Refresh();
//delete kv2; - I spent ~1 fucking day trying to figure out what was causing the fucking crash just to see all I have to do is comment 1 line. Why do I have to do this? I don't know and I don't want to fucking know. fuck fucking fuck. I hope you understand I'm mad. fuck.
}
}
delete kv;
}
stickerMaterialReference_t* stickerMaterialReference = &m_vStickerMaterialReferences[m_vStickerMaterialReferences.AddToTail()];
stickerMaterialReference->FirstPerson.Init( pFirstPersonMaterial );
stickerMaterialReference->ThirdPerson.Init( pThirdPersonMaterial );
stickerMaterialReference->iPosition = i;
}
}
CCStrike15ItemDefinition* GetItemDefinition();
private:
struct stickerMaterialReference_t
{
CMaterialReference FirstPerson;
CMaterialReference ThirdPerson;
unsigned int iPosition;
};
CUtlVector<stickerMaterialReference_t> m_vStickerMaterialReferences;
};
class CMaterial
{
public:
virtual void IncrementReferenceCount();
virtual void DecrementReferenceCount();
virtual void Refresh();
virtual bool IsErrorMaterial();
};
class CMaterialReference; // You can find this in the SDK.
class CMaterialSystem
{
public:
virtual CMaterial* CreateMaterial( const char* pMaterialName, KeyValues* pVMTKeyValues );
virtual CMaterial* FindProceduralMaterial( const char* pMaterialName, const char* pTextureGroupName, KeyValues* pVMTKeyValues )
};
enum InfoTypes
{
WearProgress = 1,
PatternScale,
PatternRotation
};
class CUnknown
{
public:
virtual int iGetStickerIDBySlot( int iSlot, int iUnknown, int iDefaultReturn );
virtual float fGetStickerInfo( int iSlot, InfoTypes Type, float fDefaultReturn );
};
class CCStrike15ItemDefinition
{
public:
virtual int GetNumSupportedStickerSlots();
virtual const char* GetStickerSlotMaterialBySlotIndex();
};
class CCustomString // CUtlString
{
public:
char* m_pszString;
int m_iUnknown0, m_iUnknown1;
// ^ = CUtlMemory<unsigned char> m_Memory;
int m_iLength; // int m_nActualLength
};
class CStickerKit // I came up with this some months ago, still seems to be up-to-date...
{
public:
int id;
int item_rarity;
CCustomString name;
CCustomString description_string;
CCustomString item_name;
CCustomString sticker_material;
CCustomString image_inventory;
int tournament_event_id;
int tournament_team_id;
int tournament_player_id;
bool bCustomStickerMaterial;
float rotate_end;
float rotate_start;
float scale_min;
float scale_max;
float wear_min;
float wear_max;
CCustomString image_inventory2;
CCustomString image_inventory_large;
DWORD N00000B91; // I don't really remember what's this, maybe a pointer?
private:
DWORD pad0[3];
};
class CCStrike15ItemSystem
{
public:
CStickerKit* GetStickerKitDefinition( int iStickerID );
};
CCStrike15ItemSystem* GetItemSchema();
void V_FileBase( char* out, const char* in, int maxlen ); // In the SDK it's void V_FileBase( const char *in, char *out,int maxlen ); but in the game it seems to be different... I don't actually call this function so I'm not sure if it works so if you don't want to call the one in the game just paste it from the SDK
unsigned FASTCALL HashStringCaseless( const char* pszKey );
You can call this function whenever and wherever you want. But you'll have to change m_iItemIDHigh just like you do with skins. The first time you call it there's no need for a ForceFullUpdate but for the next ones you can just switch to another weapon and back to the first one or call ForceFullUpdate. Since I'm calling ForceFullUpdate whenever any sticker changes, I'm calling GenerateStickerMaterials inside FrameStageNotify.
Now, I didn't want this to be that easy to paste, you'll have to do some reversing for yourself. There are plenty of strings in the function. You'll have to find virtual function's indexes, m_vStickerMaterialReferences's offset and other's function addresses and look through the SDK for other classes.
Here's a minimalistic CUtlVector class:
PHP Code:
Code:
inline int UtlMemory_CalcNewAllocationCount( int nAllocationCount, int nGrowSize, int nNewSize, int nBytesItem )
{
if ( nGrowSize )
nAllocationCount = ( ( 1 + ( ( nNewSize - 1 ) / nGrowSize ) ) * nGrowSize );
else
{
if ( !nAllocationCount )
nAllocationCount = ( 31 + nBytesItem ) / nBytesItem;
while ( nAllocationCount < nNewSize )
nAllocationCount *= 2;
}
return nAllocationCount;
}
template< class T, class I = int >
class CUtlMemory
{
public:
T& operator[]( I i )
{
return m_pMemory[i];
}
T* Base()
{
return m_pMemory;
}
int NumAllocated() const
{
return m_nAllocationCount;
}
void Grow( int num = 1 )
{
if ( IsExternallyAllocated() )
return;
int nAllocationRequested = m_nAllocationCount + num;
int nNewAllocationCount = UtlMemory_CalcNewAllocationCount( m_nAllocationCount, m_nGrowSize, nAllocationRequested, sizeof( T ) );
if ( (int) (I) nNewAllocationCount < nAllocationRequested )
{
if ( (int) (I) nNewAllocationCount == 0 && (int) (I) ( nNewAllocationCount - 1 ) >= nAllocationRequested )
--nNewAllocationCount;
else
{
if ( (int) (I) nAllocationRequested != nAllocationRequested )
return;
while ( (int) (I) nNewAllocationCount < nAllocationRequested )
nNewAllocationCount = ( nNewAllocationCount + nAllocationRequested ) / 2;
}
}
m_nAllocationCount = nNewAllocationCount;
if ( m_pMemory )
m_pMemory = (T*) g_pMemAlloc->Realloc( m_pMemory, m_nAllocationCount * sizeof( T ) );
else
m_pMemory = (T*) g_pMemAlloc->Alloc( m_nAllocationCount * sizeof( T ) );
}
bool IsExternallyAllocated() const
{
return m_nGrowSize < 0;
}
protected:
T* m_pMemory;
int m_nAllocationCount;
int m_nGrowSize;
};
template <class T>
inline T* Construct( T* pMemory )
{
return ::new( pMemory ) T;
}
template <class T>
inline void Destruct( T* pMemory )
{
pMemory->~T();
}
template< class T, class A = CUtlMemory<T> >
class CUtlVector
{
typedef A CAllocator;
public:
T& operator[]( int i )
{
return m_Memory[i];
}
T& Element( int i )
{
return m_Memory[i];
}
T* Base()
{
return m_Memory.Base();
}
int Count() const
{
return m_Size;
}
void RemoveAll()
{
for ( int i = m_Size; --i >= 0; )
Destruct( &Element( i ) );
m_Size = 0;
}
int AddToTail()
{
return InsertBefore( m_Size );
}
int InsertBefore( int elem )
{
GrowVector();
ShiftElementsRight( elem );
Construct( &Element( elem ) );
return elem;
}
protected:
void GrowVector( int num = 1 )
{
if ( m_Size + num > m_Memory.NumAllocated() )
m_Memory.Grow( m_Size + num - m_Memory.NumAllocated() );
m_Size += num;
ResetDbgInfo();
}
void ShiftElementsRight( int elem, int num = 1 )
{
int numToMove = m_Size - elem - num;
if ( ( numToMove > 0 ) && ( num > 0 ) )
memmove( &Element( elem + num ), &Element( elem ), numToMove * sizeof( T ) );
}
CAllocator m_Memory;
int m_Size;
T* m_pElements;
inline void ResetDbgInfo()
{
m_pElements = Base();
}
};
And my KeyValues class:
PHP Code:
Code:
class KeyValues
{
public:
KeyValues( const char* setName )
{
reinterpret_cast<void( __thiscall* )( PVOID, const char* )>( g_Offsets.dwKeyValuesConstructor )( this, setName );
}
bool LoadFromFile( DWORD filesystem, const char* resourceName, const char* pathID = NULL, DWORD pfnEvaluateSymbolProc = NULL )
{
return reinterpret_cast<bool( __thiscall* )( PVOID, DWORD, const char*, const char*, DWORD )>( g_Offsets.dwKeyValuesLoadFromFile )( this, filesystem ? filesystem + 4 : NULL, resourceName, pathID, pfnEvaluateSymbolProc );
}
KeyValues* FindKey( const char* keyName, bool bCreate = false )
{
return reinterpret_cast<KeyValues*( __thiscall* )( PVOID, const char*, bool )>( g_Offsets.dwKeyValuesFindKey )( this, keyName, bCreate );
}
void SetInt( const char* keyName, int value )
{
KeyValues* dat = FindKey( keyName, true );
if ( dat )
{
dat->m_iValue = value;
dat->m_iDataType = TYPE_INT;
}
}
void SetFloat( const char* keyName, float value )
{
KeyValues* dat = FindKey( keyName, true );
if ( dat )
{
dat->m_flValue = value;
dat->m_iDataType = TYPE_FLOAT;
}
}
PVOID operator new( size_t iAllocSize )
{
return getvfunc<PVOID( __thiscall* )( PVOID, size_t )>( KeyValuesSystem(), 1 )( KeyValuesSystem(), iAllocSize );
}
void operator delete( PVOID pMem )
{
getvfunc<void( __thiscall* )( PVOID, PVOID )>( KeyValuesSystem(), 2 )( KeyValuesSystem(), pMem );
}
enum types_t
{
TYPE_NONE = 0,
TYPE_STRING,
TYPE_INT,
TYPE_FLOAT,
TYPE_PTR,
TYPE_WSTRING,
TYPE_COLOR,
TYPE_UINT64,
TYPE_COMPILED_INT_BYTE,
TYPE_COMPILED_INT_0,
TYPE_COMPILED_INT_1,
TYPE_NUMTYPES,
};
//types_t GetDataType( const char *keyName = NULL );
enum MergeKeyValuesOp_t
{
MERGE_KV_ALL,
MERGE_KV_UPDATE,
MERGE_KV_DELETE,
MERGE_KV_BORROW
};
void MergeFrom( KeyValues* kvMerge, MergeKeyValuesOp_t eOp = MERGE_KV_ALL )
{
reinterpret_cast<void( __thiscall* )( PVOID, KeyValues*, MergeKeyValuesOp_t )>( g_Offsets.dwKeyValuesMergeFrom )( this, kvMerge, eOp );
}
private:
static PVOID KeyValuesSystem()
{
return reinterpret_cast<PVOID( __cdecl* )( )>( g_Offsets.dwKeyValuesKeyValuesSystem )( );
}
DWORD m_iKeyName : 24;
DWORD m_iKeyNameCaseSensitive1 : 8;
char* m_sValue;
wchar_t* m_wsValue;
union
{
int m_iValue;
float m_flValue;
PVOID m_pValue;
BYTE m_Color[4];
};
char m_iDataType;
char m_bHasEscapeSequences;
WORD m_iKeyNameCaseSensitive2;
KeyValues* m_pPeer;
KeyValues* m_pSub;
KeyValues* m_pChain;
DWORD pad0; // cba to figure out if it's at the bottom of the class or somewhere else. it works, i don't care.
};
Grabbing a C_EconItemView:
PHP Code:
Code:
C_EconItemView* C_BaseCombatWeapon::GetEconItemView()
{
PVOID pSomething = this + g_Offsets.dwGetEconItemViewSomething; // currently 0x2D60
return getvfunc<C_EconItemView*( __thiscall* )( PVOID )>( pSomething, 1 )( pSomething ) + 64;
}
I found that 0x2D60 by reversing C_BaseCombatWeapon::ApplyThirdPersonStickers( search for sticker_a ) but you can find it by looking at most calls to C_EconItemView::GetItemDefinition.
Now, once you have all of this set up you can play around with my GenerateStickerMaterials function.
You can make it take a iStickerID argument. Maybe also some fWearProgress, fPatternScale and fPatternRotation arguments ( I didn't play with those much, you should try to. Setting fWearProgress < 0.f and > 1.f seems to make the sticker brighter and darker. fPatternScale does what it's name says, it scales the pattern. fPatternRotation uses degrees and not radians )
Example:
PHP Code:
Code:
struct StickerInfo
{
int iStickerID;
float fWearProgress;
float fPatternScale;
float fPatternRotation
}
void GenerateStickerMaterials( const std::vector<StickerInfo>& vStickers )
{
int iSupportedStickerSlots = vStickers.size();
m_vStickerMaterialReferences.RemoveAll();
CUnknown* pUnknown = reinterpret_cast<CUnknown*>( this + 0xC );
for ( int i = 0; i < iSupportedStickerSlots; ++i )
{
StickerInfo si = vStickers[i];
int iStickerID = si.iStickerID;
float fWearProgress = si.fWearProgress;
float fPatternScale = si.fPatternScale
float fPatternRotation = si.fPatternRotation;
const char* pszStickerSlotMaterial = pItemDefinition->GetStickerSlotMaterialBySlotIndex( i );
if ( !pszStickerSlotMaterial || !pszStickerSlotMaterial[0] )
continue;
char szWeaponDecal[32];
V_FileBase( szWeaponDecal, pszStickerSlotMaterial, 32 );
CStickerKit* pSK = reinterpret_cast<CCStrike15ItemSystem*>( GetItemSchema() + 4 )->GetStickerKitDefinition( iStickerID );
if ( !pSK )
continue;
if ( !pSK->sticker_material.m_iLength )
continue;
if ( !pSK->sticker_material.m_pszString || !pSK->sticker_material.m_pszString[0] )
continue;
char szPath[MAX_PATH];
if ( pSK->bCustomStickerMaterial ) // Doesn't seem to be set to true for any of the stickers currently in-game. Used for the people that want to get their stickers in the Workshop?
V_snprintf( szPath, sizeof szPath, "%s", pSK->sticker_material.m_pszString );
else
V_snprintf( szPath, sizeof szPath, "materials/models/weapons/customization/stickers/%s.vmt", pSK->sticker_material.m_pszString );
char szHash[32];
V_snprintf( szHash, sizeof szHash, "%f_%f_%f", fPatternRotation, fPatternScale, fWearProgress );
unsigned uHash = HashStringCaseless( szHash );
char szMaterialName1st[128]; V_snprintf( szMaterialName1st, sizeof szMaterialName1st, "sticker_%s_%s_%i_1st", szWeaponDecal, pSK->sticker_material.m_pszString, uHash );
char szMaterialName3rd[128]; V_snprintf( szMaterialName3rd, sizeof szMaterialName3rd, "sticker_%s_%s_%i_3rd", szWeaponDecal, pSK->sticker_material.m_pszString, uHash );
CMaterial* pFirstPersonMaterial = materials->FindProceduralMaterial( szMaterialName1st, "Other textures", 0 );
CMaterial* pThirdPersonMaterial = materials->FindProceduralMaterial( szMaterialName3rd, "Other textures", 0 );
if ( pFirstPersonMaterial->IsErrorMaterial() || pThirdPersonMaterial->IsErrorMaterial() )
{
KeyValues* kv = new KeyValues( "vmt" );
if ( kv->LoadFromFile( g_pFullFileSystem /* + 4*/, szPath, "GAME", 0 ) )
{
kv->SetFloat( "$wearprogress", fWearProgress );
kv->SetFloat( "$patternrotation", fPatternRotation );
kv->SetFloat( "$patternscale", fPatternScale );
if ( pFirstPersonMaterial->IsErrorMaterial() )
{
KeyValues* kv2 = new KeyValues( "vmt" );
if ( kv2->LoadFromFile( g_pFullFileSystem /* + 4*/, pszStickerSlotMaterial, "GAME", 0 ) )
{
if ( !cl_righthand->GetInt() )
kv2->SetInt( "$mirrorhorizontal", 1 );
kv2->MergeFrom( kv, KeyValues::MERGE_KV_UPDATE );
char szPath[MAX_PATH];
V_snprintf( szPath, sizeof szPath, "materials/models/weapons/customization/stickers/default/sticker_proxies_%i.vmt", i );
KeyValues* kv3 = new KeyValues( "vmt" );
if ( kv3->LoadFromFile g_pFullFileSystem /* + 4*/, szPath, "GAME", 0 )
kv2->MergeFrom( kv3, KeyValues::MERGE_KV_UPDATE );
pFirstPersonMaterial = materials->CreateMaterial( szMaterialName1st, kv2 );
pFirstPersonMaterial->Refresh();
delete kv3;
}
delete kv2;
}
if ( pThirdPersonMaterial->IsErrorMaterial() )
{
KeyValues* kv2 = new KeyValues( "WeaponDecal" );
kv2->MergeFrom( kv, KeyValues::MERGE_KV_UPDATE );
kv2->SetInt( "$thirdperson", 1 );
pThirdPersonMaterial = materials->CreateMaterial( szMaterialName3rd, kv2 );
pThirdPersonMaterial->Refresh();
//delete kv2; - I spent ~1 fucking day trying to figure out what was causing the fucking crash just to see all I have to do is comment 1 line. Why do I have to do this? I don't know and I don't want to fucking know. fuck fucking fuck. I hope you understand I'm mad. fuck.
}
}
delete kv;
}
stickerMaterialReference_t* stickerMaterialReference = &m_vStickerMaterialReferences[m_vStickerMaterialReferences.AddToTail()];
stickerMaterialReference->FirstPerson.Init( pFirstPersonMaterial );
stickerMaterialReference->ThirdPerson.Init( pThirdPersonMaterial );
stickerMaterialReference->iPosition = i;
}
}
Here are some dumps wich might help you:
Sticker Materials & IDs:
-> [ 3DMAX (Foil) | Katowice 2014 ] | emskatowice2014/3dmax_foil | [83]
-> [ 3DMAX (Foil) | Katowice 2015 ] | eslkatowice2015/3dmax_foil | [288]
-> [ 3DMAX (Gold) | DreamHack 2014 ] | dhw2014/3dmax_gold | [237]
-> [ 3DMAX (Gold) | Katowice 2015 ] | eslkatowice2015/3dmax_gold | [289]
-> [ 3DMAX (Holo) | Katowice 2014 ] | emskatowice2014/3dmax_holo | [49]
-> [ 3DMAX (Holo) | Katowice 2015 ] | eslkatowice2015/3dmax_holo | [287]
-> [ 3DMAX | DreamHack 2014 ] | dhw2014/3dmax | [232]
-> [ 3DMAX | Katowice 2014 ] | emskatowice2014/3dmax | [48]
-> [ 3DMAX | Katowice 2015 ] | eslkatowice2015/3dmax | [286]
-> [ Aces High ] | standard/aces_high | [14]
-> [ Aces High (Holo) ] | standard/aces_high_holo | [15]
-> [ adreN (Foil) | Cluj-Napoca 2015 ] | cluj2015/sig_adren_foil | [837]
-> [ adreN (Gold) | Cluj-Napoca 2015 ] | cluj2015/sig_adren_gold | [838]
......
What you can use instead of that szWeaponDecal and pszStickerSlotMaterial:
[ weapon_tec9 ]
-> pist_tec9_decal_a | materials/models/weapons/customization/pist_tec9/pist_tec9_decal_a.vmt
-> pist_tec9_decal_b | materials/models/weapons/customization/pist_tec9/pist_tec9_decal_b.vmt
-> pist_tec9_decal_c | materials/models/weapons/customization/pist_tec9/pist_tec9_decal_c.vmt
-> pist_tec9_decal_d | materials/models/weapons/customization/pist_tec9/pist_tec9_decal_d.vmt
[ weapon_ak47 ]
-> rif_ak47_decal_a | materials/models/weapons/customization/rif_ak47/rif_ak47_decal_a.vmt
-> rif_ak47_decal_b | materials/models/weapons/customization/rif_ak47/rif_ak47_decal_b.vmt
-> rif_ak47_decal_c | materials/models/weapons/customization/rif_ak47/rif_ak47_decal_c.vmt
-> rif_ak47_decal_d | materials/models/weapons/customization/rif_ak47/rif_ak47_decal_d.vmt
[ weapon_m4a1 ]
-> rif_m4a1_decal_a | materials/models/weapons/customization/rif_m4a1/rif_m4a1_decal_a.vmt
-> rif_m4a1_decal_b | materials/models/weapons/customization/rif_m4a1/rif_m4a1_decal_b.vmt
-> rif_m4a1_decal_c | materials/models/weapons/customization/rif_m4a1/rif_m4a1_decal_c.vmt
-> rif_m4a1_decal_d | materials/models/weapons/customization/rif_m4a1/rif_m4a1_decal_d.vmt
[ weapon_sawedoff ]
-> shot_sawedoff_decal_a | materials/models/weapons/customization/shot_sawedoff/shot_sawedoff_decal_a.vmt
-> shot_sawedoff_decal_b | materials/models/weapons/customization/shot_sawedoff/shot_sawedoff_decal_b.vmt
.....