#include "Cafe/HW/Latte/Core/LatteConst.h"
#include "Cafe/HW/Latte/Core/LatteShaderAssembly.h"
#include "Cafe/HW/Latte/ISA/RegDefines.h"
#include "Cafe/HW/Latte/Core/Latte.h"
#include "Cafe/HW/Latte/Core/LatteDraw.h"
#include "Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h"
#include "Cafe/HW/Latte/Core/FetchShader.h"
#include "Cafe/HW/Latte/Renderer/Renderer.h"
#include "util/helpers/StringBuf.h"

#define _CRLF	"\r\n"

void _readLittleEndianAttributeU32x4(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder = attrDataSem{};" _CRLF, attributeInputIndex);
}

void _readLittleEndianAttributeU32x3(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder = uvec4(attrDataSem{}.xyz,0);" _CRLF, attributeInputIndex);
}

void _readLittleEndianAttributeU32x2(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder = uvec4(attrDataSem{}.xy,0,0);" _CRLF, attributeInputIndex);
}

void _readLittleEndianAttributeU32x1(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder = uvec4(attrDataSem{}.x,0,0,0);" _CRLF, attributeInputIndex);
}

void _readLittleEndianAttributeU16x2(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder = uvec4(attrDataSem{}.xy,0,0);" _CRLF, attributeInputIndex);
}

void _readLittleEndianAttributeU16x4(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder = attrDataSem{};" _CRLF, attributeInputIndex);
}

void _readBigEndianAttributeU32x4(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder = attrDataSem{};" _CRLF, attributeInputIndex);
	src->add("attrDecoder = (attrDecoder>>24)|((attrDecoder>>8)&0xFF00)|((attrDecoder<<8)&0xFF0000)|((attrDecoder<<24));" _CRLF);
}

void _readBigEndianAttributeU32x3(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder.xyz = attrDataSem{}.xyz;" _CRLF, attributeInputIndex);
	src->add("attrDecoder.xyz = (attrDecoder.xyz>>24)|((attrDecoder.xyz>>8)&0xFF00)|((attrDecoder.xyz<<8)&0xFF0000)|((attrDecoder.xyz<<24));" _CRLF);
	src->add("attrDecoder.w = 0;" _CRLF);
}

void _readBigEndianAttributeU32x2(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder.xy = attrDataSem{}.xy;" _CRLF, attributeInputIndex);
	src->add("attrDecoder.xy = (attrDecoder.xy>>24)|((attrDecoder.xy>>8)&0xFF00)|((attrDecoder.xy<<8)&0xFF0000)|((attrDecoder.xy<<24));" _CRLF);
	src->add("attrDecoder.z = 0;" _CRLF);
	src->add("attrDecoder.w = 0;" _CRLF);
}

void _readBigEndianAttributeU32x1(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder.x = attrDataSem{}.x;" _CRLF, attributeInputIndex);
	src->add("attrDecoder.x = (attrDecoder.x>>24)|((attrDecoder.x>>8)&0xFF00)|((attrDecoder.x<<8)&0xFF0000)|((attrDecoder.x<<24));" _CRLF);
	src->add("attrDecoder.y = 0;" _CRLF);
	src->add("attrDecoder.z = 0;" _CRLF);
	src->add("attrDecoder.w = 0;" _CRLF);
}

void _readBigEndianAttributeU16x1(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder.xy = attrDataSem{}.xy;" _CRLF, attributeInputIndex);
	src->add("attrDecoder.x = ((attrDecoder.x>>8)&0xFF)|((attrDecoder.x<<8)&0xFF00);" _CRLF);
	src->add("attrDecoder.y = 0;" _CRLF);
	src->add("attrDecoder.z = 0;" _CRLF);
	src->add("attrDecoder.w = 0;" _CRLF);
}

void _readBigEndianAttributeU16x2(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder.xy = attrDataSem{}.xy;" _CRLF, attributeInputIndex);
	src->add("attrDecoder.xy = ((attrDecoder.xy>>8)&0xFF)|((attrDecoder.xy<<8)&0xFF00);" _CRLF);
	src->add("attrDecoder.z = 0;" _CRLF);
	src->add("attrDecoder.w = 0;" _CRLF);
}

void _readBigEndianAttributeU16x4(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
{
	src->addFmt("attrDecoder.xyzw = attrDataSem{}.xyzw;" _CRLF, attributeInputIndex);
	src->add("attrDecoder = ((attrDecoder>>8)&0xFF)|((attrDecoder<<8)&0xFF00);" _CRLF);
}

void LatteDecompiler_emitAttributeDecodeGLSL(LatteDecompilerShader* shaderContext, StringBuf* src, LatteParsedFetchShaderAttribute_t* attrib)
{
	if (attrib->attributeBufferIndex >= Latte::GPU_LIMITS::NUM_VERTEX_BUFFERS)
	{
		src->add("attrDecoder = ivec4(0);" _CRLF);
		return;
	}

	uint32 attributeInputIndex = attrib->semanticId;
	if( attrib->endianSwap == LatteConst::VertexFetchEndianMode::SWAP_U32 )
	{
		if( attrib->format == FMT_32_32_32_32_FLOAT && attrib->nfa == 2 )
		{
			_readBigEndianAttributeU32x4(shaderContext, src, attributeInputIndex);
		}
		else if( attrib->format == FMT_32_32_32_FLOAT && attrib->nfa == 2 )
		{
			_readBigEndianAttributeU32x3(shaderContext, src, attributeInputIndex);
		}
		else if( attrib->format == FMT_32_32_FLOAT && attrib->nfa == 2 )
		{
			_readBigEndianAttributeU32x2(shaderContext, src, attributeInputIndex);
		}
		else if( attrib->format == FMT_32_FLOAT && attrib->nfa == 2 )
		{
			_readBigEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
		}
		else if( attrib->format == FMT_2_10_10_10 && attrib->nfa == 0 )
		{
			_readBigEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
			// Bayonetta 2 uses this format to store normals
			src->add("attrDecoder.xyzw = uvec4((attrDecoder.x>>0)&0x3FF,(attrDecoder.x>>10)&0x3FF,(attrDecoder.x>>20)&0x3FF,(attrDecoder.x>>30)&0x3);" _CRLF);
			if (attrib->isSigned != 0)
			{
				src->add("if( (attrDecoder.x&0x200) != 0 ) attrDecoder.x |= 0xFFFFFC00;" _CRLF);
				src->add("if( (attrDecoder.y&0x200) != 0 ) attrDecoder.y |= 0xFFFFFC00;" _CRLF);
				src->add("if( (attrDecoder.z&0x200) != 0 ) attrDecoder.z |= 0xFFFFFC00;" _CRLF);
				src->add("attrDecoder.x = floatBitsToUint(max(float(int(attrDecoder.x))/511.0,-1.0));" _CRLF);
				src->add("attrDecoder.y = floatBitsToUint(max(float(int(attrDecoder.y))/511.0,-1.0));" _CRLF);
				src->add("attrDecoder.z = floatBitsToUint(max(float(int(attrDecoder.z))/511.0,-1.0));" _CRLF);
			}
			else
			{
				src->add("attrDecoder.x = floatBitsToUint(max(float(int(attrDecoder.x))/1023.0,-1.0));" _CRLF);
				src->add("attrDecoder.y = floatBitsToUint(max(float(int(attrDecoder.y))/1023.0,-1.0));" _CRLF);
				src->add("attrDecoder.z = floatBitsToUint(max(float(int(attrDecoder.z))/1023.0,-1.0));" _CRLF);
			}
			src->add("attrDecoder.w = floatBitsToUint(float(attrDecoder.w));" _CRLF); // unsure?

		}
		else if( attrib->format == FMT_32_32_32_32 && attrib->nfa == 1 && attrib->isSigned == 0 )
		{
			_readBigEndianAttributeU32x4(shaderContext, src, attributeInputIndex);
		}
		else if( attrib->format == FMT_32_32_32 && attrib->nfa == 1 && attrib->isSigned == 0 )
		{
			_readBigEndianAttributeU32x3(shaderContext, src, attributeInputIndex);
		}
		else if( attrib->format == FMT_32_32 && attrib->nfa == 1 && attrib->isSigned == 0 )
		{
			_readBigEndianAttributeU32x2(shaderContext, src, attributeInputIndex);
		}
		else if (attrib->format == FMT_32 && attrib->nfa == 1 && attrib->isSigned == 0)
		{
			_readBigEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
		}
		else if (attrib->format == FMT_32 && attrib->nfa == 1 && attrib->isSigned == 1)
		{
			// we can just read the signed s32 as a u32 since no sign-extension is necessary
			_readBigEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
		}
		else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 0 && attrib->isSigned == 0 )
		{
			// seen in Minecraft Wii U Edition
			src->addFmt("attrDecoder.xyzw = floatBitsToUint(vec4(attrDataSem{}.wzyx)/255.0);" _CRLF, attributeInputIndex);
		}
		else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 0 && attrib->isSigned != 0 )
		{
			// seen in Minecraft Wii U Edition
			src->addFmt("attrDecoder.xyzw = attrDataSem{}.wzyx;" _CRLF, attributeInputIndex);
			src->add("if( (attrDecoder.x&0x80) != 0 ) attrDecoder.x |= 0xFFFFFF00;" _CRLF); 
			src->add("if( (attrDecoder.y&0x80) != 0 ) attrDecoder.y |= 0xFFFFFF00;" _CRLF); 
			src->add("if( (attrDecoder.z&0x80) != 0 ) attrDecoder.z |= 0xFFFFFF00;" _CRLF); 
			src->add("if( (attrDecoder.w&0x80) != 0 ) attrDecoder.w |= 0xFFFFFF00;" _CRLF); 
			src->add("attrDecoder.x = floatBitsToUint(max(float(int(attrDecoder.x))/127.0,-1.0));" _CRLF); 
			src->add("attrDecoder.y = floatBitsToUint(max(float(int(attrDecoder.y))/127.0,-1.0));" _CRLF); 
			src->add("attrDecoder.z = floatBitsToUint(max(float(int(attrDecoder.z))/127.0,-1.0));" _CRLF); 
			src->add("attrDecoder.w = floatBitsToUint(max(float(int(attrDecoder.w))/127.0,-1.0));" _CRLF); 
		}
		else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 1 && attrib->isSigned == 0 )
		{
			// seen in Minecraft Wii U Edition
			src->addFmt("attrDecoder.xyzw = attrDataSem{}.wzyx;" _CRLF, attributeInputIndex);
		}
		else if (attrib->format == FMT_8_8_8_8 && attrib->nfa == 2 && attrib->isSigned == 0)
		{
			// seen in Ben 10 Omniverse
			src->addFmt("attrDecoder.xyzw = floatBitsToUint(vec4(attrDataSem{}.wzyx));" _CRLF, attributeInputIndex);
		}
		else
		{
			cemuLog_log(LogType::Force, "_emitAttributeDecodeGLSL(): Unsupported fmt {:02x} nfa {} signed {} endian {}\n", attrib->format, attrib->nfa, attrib->isSigned, attrib->endianSwap);
			cemu_assert_unimplemented();
		}
	}
	else if( attrib->endianSwap == LatteConst::VertexFetchEndianMode::SWAP_NONE )
	{
		if( attrib->format == FMT_32_32_32_32_FLOAT && attrib->nfa == 2 )
		{
			_readLittleEndianAttributeU32x4(shaderContext, src, attributeInputIndex);
		}
		else if (attrib->format == FMT_32_32_32_FLOAT && attrib->nfa == 2)
		{
			_readLittleEndianAttributeU32x3(shaderContext, src, attributeInputIndex);
		}
		else if (attrib->format == FMT_32_32_FLOAT && attrib->nfa == 2)
		{
			// seen in Cities of Gold
			_readLittleEndianAttributeU32x2(shaderContext, src, attributeInputIndex);
		}
		else if (attrib->format == FMT_32 && attrib->nfa == 1 && attrib->isSigned == 0)
		{
			// seen in Nano Assault Neo
			_readLittleEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
		}
		else if (attrib->format == FMT_2_10_10_10 && attrib->nfa == 0 && attrib->isSigned == 0)
		{
			// seen in Fast Racing Neo
			_readLittleEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
			src->add("attrDecoder.xyzw = uvec4((attrDecoder.x>>0)&0x3FF,(attrDecoder.x>>10)&0x3FF,(attrDecoder.x>>20)&0x3FF,(attrDecoder.x>>30)&0x3);" _CRLF);
			src->add("attrDecoder.x = floatBitsToUint(max(float(int(attrDecoder.x))/1023.0,-1.0));" _CRLF);
			src->add("attrDecoder.y = floatBitsToUint(max(float(int(attrDecoder.y))/1023.0,-1.0));" _CRLF);
			src->add("attrDecoder.z = floatBitsToUint(max(float(int(attrDecoder.z))/1023.0,-1.0));" _CRLF);
			src->add("attrDecoder.w = floatBitsToUint(float(attrDecoder.w));" _CRLF); // todo - is this correct?
		}
		else if (attrib->format == FMT_16_16_16_16 && attrib->nfa == 0 && attrib->isSigned != 0)
		{
			// seen in CoD ghosts
			_readLittleEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
			src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
			src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
			src->add("if( (attrDecoder.z&0x8000) != 0 ) attrDecoder.z |= 0xFFFF0000;" _CRLF);
			src->add("if( (attrDecoder.w&0x8000) != 0 ) attrDecoder.w |= 0xFFFF0000;" _CRLF);
			src->add("attrDecoder.x = floatBitsToUint(max(float(int(attrDecoder.x))/32767.0,-1.0));" _CRLF);
			src->add("attrDecoder.y = floatBitsToUint(max(float(int(attrDecoder.y))/32767.0,-1.0));" _CRLF);
			src->add("attrDecoder.z = floatBitsToUint(max(float(int(attrDecoder.z))/32767.0,-1.0));" _CRLF);
			src->add("attrDecoder.w = floatBitsToUint(max(float(int(attrDecoder.w))/32767.0,-1.0));" _CRLF);
		}
		else if (attrib->format == FMT_16_16_16_16_FLOAT && attrib->nfa == 2)
		{
			// seen in Giana Sisters: Twisted Dreams
			_readLittleEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
			src->add("attrDecoder.xyzw = floatBitsToInt(vec4(unpackHalf2x16(attrDecoder.x|(attrDecoder.y<<16)),unpackHalf2x16(attrDecoder.z|(attrDecoder.w<<16))));" _CRLF);
		}
		else if (attrib->format == FMT_16_16 && attrib->nfa == 0 && attrib->isSigned != 0)
		{
			// seen in Nano Assault Neo
			_readLittleEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
			src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
			src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
			src->add("attrDecoder.x = floatBitsToUint(max(float(int(attrDecoder.x))/32767.0,-1.0));" _CRLF);
			src->add("attrDecoder.y = floatBitsToUint(max(float(int(attrDecoder.y))/32767.0,-1.0));" _CRLF);
		}
		else if (attrib->format == FMT_16_16_FLOAT && attrib->nfa == 2)
		{
			// seen in Giana Sisters: Twisted Dreams
			_readLittleEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
			src->add("attrDecoder.xy = floatBitsToUint(unpackHalf2x16(attrDecoder.x|(attrDecoder.y<<16)));" _CRLF);
			src->add("attrDecoder.zw = uvec2(0);" _CRLF);
		}
		else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 0 && attrib->isSigned == 0 )
		{
			src->addFmt("attrDecoder.xyzw = floatBitsToUint(vec4(attrDataSem{}.xyzw)/255.0);" _CRLF, attributeInputIndex);
		}
		else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 0 && attrib->isSigned != 0 )
		{
			src->addFmt("attrDecoder.xyzw = attrDataSem{}.xyzw;" _CRLF, attributeInputIndex);
			src->add("if( (attrDecoder.x&0x80) != 0 ) attrDecoder.x |= 0xFFFFFF00;" _CRLF); 
			src->add("if( (attrDecoder.y&0x80) != 0 ) attrDecoder.y |= 0xFFFFFF00;" _CRLF); 
			src->add("if( (attrDecoder.z&0x80) != 0 ) attrDecoder.z |= 0xFFFFFF00;" _CRLF); 
			src->add("if( (attrDecoder.w&0x80) != 0 ) attrDecoder.w |= 0xFFFFFF00;" _CRLF); 
			src->add("attrDecoder.x = floatBitsToUint(max(float(int(attrDecoder.x))/127.0,-1.0));" _CRLF); 
			src->add("attrDecoder.y = floatBitsToUint(max(float(int(attrDecoder.y))/127.0,-1.0));" _CRLF); 
			src->add("attrDecoder.z = floatBitsToUint(max(float(int(attrDecoder.z))/127.0,-1.0));" _CRLF); 
			src->add("attrDecoder.w = floatBitsToUint(max(float(int(attrDecoder.w))/127.0,-1.0));" _CRLF); 
		}
		else if (attrib->format == FMT_8_8_8_8 && attrib->nfa == 1 && attrib->isSigned == 0)
		{
			src->addFmt("attrDecoder.xyzw = attrDataSem{}.xyzw;" _CRLF, attributeInputIndex);
		}
		else if (attrib->format == FMT_8_8_8_8 && attrib->nfa == 1 && attrib->isSigned != 0)
		{
			// seen in Sonic Lost World
			src->addFmt("attrDecoder.xyzw = attrDataSem{}.xyzw;" _CRLF, attributeInputIndex);
			src->add("if( (attrDecoder.x&0x80) != 0 ) attrDecoder.x |= 0xFFFFFF00;" _CRLF);
			src->add("if( (attrDecoder.y&0x80) != 0 ) attrDecoder.y |= 0xFFFFFF00;" _CRLF);
			src->add("if( (attrDecoder.z&0x80) != 0 ) attrDecoder.z |= 0xFFFFFF00;" _CRLF);
			src->add("if( (attrDecoder.w&0x80) != 0 ) attrDecoder.w |= 0xFFFFFF00;" _CRLF);
		}
		else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 2 && attrib->isSigned == 0 )
		{
			// seen in One Piece
			src->addFmt("attrDecoder.xyzw = floatBitsToInt(vec4(attrDataSem{}.xyzw));" _CRLF, attributeInputIndex);
		}
		else if (attrib->format == FMT_8_8 && attrib->nfa == 0 && attrib->isSigned == 0)
		{
			if( (attrib->offset&3) == 2 && LatteGPUState.glVendor == GLVENDOR_AMD && g_renderer->GetType() == RendererAPI::OpenGL )
			{ 
				// AMD workaround
				src->addFmt("attrDecoder.xy = floatBitsToUint(vec2(attrDataSem{}.zw)/255.0);" _CRLF, attributeInputIndex);
				src->add("attrDecoder.zw = uvec2(0);" _CRLF);
			}
			else
			{
				src->addFmt("attrDecoder.xy = floatBitsToUint(vec2(attrDataSem{}.xy)/255.0);" _CRLF, attributeInputIndex);
				src->add("attrDecoder.zw = uvec2(0);" _CRLF);
			}
		}
		else if (attrib->format == FMT_8_8 && attrib->nfa == 2 && attrib->isSigned == 0)
		{
			// seen in BotW
			if ((attrib->offset & 3) == 2 && LatteGPUState.glVendor == GLVENDOR_AMD && g_renderer->GetType() == RendererAPI::OpenGL)
			{
				// AMD workaround
				src->addFmt("attrDecoder.xy = floatBitsToUint(vec2(attrDataSem{}.zw));" _CRLF, attributeInputIndex);
				src->add("attrDecoder.zw = uvec2(0);" _CRLF);
			}
			else
			{
				src->addFmt("attrDecoder.xy = floatBitsToUint(vec2(attrDataSem{}.xy));" _CRLF, attributeInputIndex);
				src->add("attrDecoder.zw = uvec2(0);" _CRLF);
			}
		}
		else if (attrib->format == FMT_8_8 && attrib->nfa == 0 && attrib->isSigned != 0)
		{
			if ((attrib->offset & 3) == 2 && LatteGPUState.glVendor == GLVENDOR_AMD && g_renderer->GetType() == RendererAPI::OpenGL)
			{
				// AMD workaround
				src->addFmt("attrDecoder.xy = attrDataSem{}.zw;" _CRLF, attributeInputIndex);
				src->add("if( (attrDecoder.x&0x80) != 0 ) attrDecoder.x |= 0xFFFFFF00;" _CRLF);
				src->add("if( (attrDecoder.y&0x80) != 0 ) attrDecoder.y |= 0xFFFFFF00;" _CRLF);
				src->add("attrDecoder.x = floatBitsToUint(max(float(int(attrDecoder.x))/127.0,-1.0));" _CRLF);
				src->add("attrDecoder.y = floatBitsToUint(max(float(int(attrDecoder.y))/127.0,-1.0));" _CRLF);
				src->add("attrDecoder.zw = uvec2(0);" _CRLF);
			}
			else
			{
				src->addFmt("attrDecoder.xy = attrDataSem{}.xy;" _CRLF, attributeInputIndex);
				src->add("if( (attrDecoder.x&0x80) != 0 ) attrDecoder.x |= 0xFFFFFF00;" _CRLF);
				src->add("if( (attrDecoder.y&0x80) != 0 ) attrDecoder.y |= 0xFFFFFF00;" _CRLF);
				src->add("attrDecoder.x = floatBitsToUint(max(float(int(attrDecoder.x))/127.0,-1.0));" _CRLF);
				src->add("attrDecoder.y = floatBitsToUint(max(float(int(attrDecoder.y))/127.0,-1.0));" _CRLF);
				src->add("attrDecoder.zw = uvec2(0);" _CRLF);
			}
		}
		else if (attrib->format == FMT_8_8 && attrib->nfa == 1 && attrib->isSigned == 0)
		{
			if ((attrib->offset & 3) == 2 && LatteGPUState.glVendor == GLVENDOR_AMD && g_renderer->GetType() == RendererAPI::OpenGL)
			{
				// AMD workaround
				src->addFmt("attrDecoder.xyzw = uvec4(attrDataSem{}.zw,0,0);" _CRLF, attributeInputIndex);
			}
			else
			{
				src->addFmt("attrDecoder.xyzw = uvec4(attrDataSem{}.xy,0,0);" _CRLF, attributeInputIndex);
			}
		}
		else if( attrib->format == FMT_8 && attrib->nfa == 0 && attrib->isSigned == 0 )
		{
			// seen in Pikmin 3
			src->addFmt("attrDecoder.x = floatBitsToUint(float(attrDataSem{}.x)/255.0);" _CRLF, attributeInputIndex);
			src->add("attrDecoder.yzw = uvec3(0);" _CRLF);
		}
		else if( attrib->format == FMT_8 && attrib->nfa == 1 && attrib->isSigned == 0 )
		{
			src->addFmt("attrDecoder.xyzw = uvec4(attrDataSem{}.x,0,0,0);" _CRLF, attributeInputIndex);
		}
		else
		{
			cemuLog_log(LogType::Force, "_emitAttributeDecodeGLSL(): Unsupported fmt {:02x} nfa {} signed {} endian {}\n", attrib->format, attrib->nfa, attrib->isSigned, attrib->endianSwap);
			cemu_assert_debug(false);
		}
	}
	else if( attrib->endianSwap == LatteConst::VertexFetchEndianMode::SWAP_U16 )
	{
		if( attrib->format == FMT_16_16_16_16_FLOAT && attrib->nfa == 2 )
		{
			_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
			src->add("attrDecoder.xyzw = floatBitsToInt(vec4(unpackHalf2x16(attrDecoder.x|(attrDecoder.y<<16)),unpackHalf2x16(attrDecoder.z|(attrDecoder.w<<16))));" _CRLF);
		}
		else if (attrib->format == FMT_16_16_16_16 && attrib->nfa == 0 && attrib->isSigned != 0)
		{
			_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
			src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
			src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
			src->add("if( (attrDecoder.z&0x8000) != 0 ) attrDecoder.z |= 0xFFFF0000;" _CRLF);
			src->add("if( (attrDecoder.w&0x8000) != 0 ) attrDecoder.w |= 0xFFFF0000;" _CRLF);
			src->add("attrDecoder.x = floatBitsToUint(max(float(int(attrDecoder.x))/32767.0,-1.0));" _CRLF);
			src->add("attrDecoder.y = floatBitsToUint(max(float(int(attrDecoder.y))/32767.0,-1.0));" _CRLF);
			src->add("attrDecoder.z = floatBitsToUint(max(float(int(attrDecoder.z))/32767.0,-1.0));" _CRLF);
			src->add("attrDecoder.w = floatBitsToUint(max(float(int(attrDecoder.w))/32767.0,-1.0));" _CRLF);
		}
		else if (attrib->format == FMT_16_16_16_16 && attrib->nfa == 0 && attrib->isSigned == 0)
		{
			// seen in BotW
			_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
			src->add("attrDecoder.x = floatBitsToUint(float(int(attrDecoder.x))/65535.0);" _CRLF);
			src->add("attrDecoder.y = floatBitsToUint(float(int(attrDecoder.y))/65535.0);" _CRLF);
			src->add("attrDecoder.z = floatBitsToUint(float(int(attrDecoder.z))/65535.0);" _CRLF);
			src->add("attrDecoder.w = floatBitsToUint(float(int(attrDecoder.w))/65535.0);" _CRLF);
		}
		else if( attrib->format == FMT_16_16_16_16 && attrib->nfa == 2 && attrib->isSigned != 0 )
		{
			// seen in Minecraft Wii U Edition
			_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
			src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF); 
			src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF); 
			src->add("if( (attrDecoder.z&0x8000) != 0 ) attrDecoder.z |= 0xFFFF0000;" _CRLF); 
			src->add("if( (attrDecoder.w&0x8000) != 0 ) attrDecoder.w |= 0xFFFF0000;" _CRLF); 
			src->add("attrDecoder.x = floatBitsToUint(float(int(attrDecoder.x)));" _CRLF);
			src->add("attrDecoder.y = floatBitsToUint(float(int(attrDecoder.y)));" _CRLF);
			src->add("attrDecoder.z = floatBitsToUint(float(int(attrDecoder.z)));" _CRLF);
			src->add("attrDecoder.w = floatBitsToUint(float(int(attrDecoder.w)));" _CRLF);
		}
		else if( attrib->format == FMT_16_16_16_16 && attrib->nfa == 1 && attrib->isSigned != 0 )
		{
			// seen in Minecraft Wii U Edition
			_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
			src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF); 
			src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF); 
			src->add("if( (attrDecoder.z&0x8000) != 0 ) attrDecoder.z |= 0xFFFF0000;" _CRLF); 
			src->add("if( (attrDecoder.w&0x8000) != 0 ) attrDecoder.w |= 0xFFFF0000;" _CRLF); 
		}
		else if( attrib->format == FMT_16_16_16_16 && attrib->nfa == 1 && attrib->isSigned == 0 )
		{
			_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
		}
		else if( attrib->format == FMT_16_16_FLOAT && attrib->nfa == 2 )
		{
			_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
			src->add("attrDecoder.xy = floatBitsToUint(unpackHalf2x16(attrDecoder.x|(attrDecoder.y<<16)));" _CRLF);
			src->add("attrDecoder.zw = uvec2(0);" _CRLF);
		}
		else if( attrib->format == FMT_16_16 && attrib->nfa == 0 && attrib->isSigned == 0 )
		{
			_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
			src->add("attrDecoder.xy = floatBitsToUint(vec2(float(attrDecoder.x), float(attrDecoder.y))/65535.0);" _CRLF);
			src->add("attrDecoder.zw = uvec2(0);" _CRLF);
		}
		else if( attrib->format == FMT_16_16 && attrib->nfa == 0 && attrib->isSigned != 0 )
		{
			_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
			src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF); 
			src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF); 
			src->add("attrDecoder.x = floatBitsToUint(max(float(int(attrDecoder.x))/32767.0,-1.0));" _CRLF); 
			src->add("attrDecoder.y = floatBitsToUint(max(float(int(attrDecoder.y))/32767.0,-1.0));" _CRLF); 
			src->add("attrDecoder.zw = uvec2(0);" _CRLF);
		}
		else if( attrib->format == FMT_16_16 && attrib->nfa == 1 && attrib->isSigned == 0 )
		{
			_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
		}
		else if( attrib->format == FMT_16_16 && attrib->nfa == 1 && attrib->isSigned != 0 )
		{
			_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
			src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF); 
			src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF); 
			src->add("attrDecoder.zw = uvec2(0);" _CRLF);
		}
		else if( attrib->format == FMT_16_16 && attrib->nfa == 2 && attrib->isSigned == 0 )
		{
			_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
			src->add("attrDecoder.xy = floatBitsToUint(vec2(float(attrDecoder.x), float(attrDecoder.y)));" _CRLF);
			src->add("attrDecoder.zw = uvec2(0);" _CRLF);
		}
		else if( attrib->format == FMT_16_16 && attrib->nfa == 2 && attrib->isSigned != 0 )
		{
			_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
			src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF); 
			src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF); 
			src->add("attrDecoder.xy = floatBitsToUint(vec2(float(int(attrDecoder.x)), float(int(attrDecoder.y))));" _CRLF);
			src->add("attrDecoder.zw = uvec2(0);" _CRLF);
		}
		else if (attrib->format == FMT_16 && attrib->nfa == 1 && attrib->isSigned == 0)
		{
			_readBigEndianAttributeU16x1(shaderContext, src, attributeInputIndex);
		}
		else if (attrib->format == FMT_16 && attrib->nfa == 0 && attrib->isSigned == 0)
		{
			// seen in CoD ghosts
			_readBigEndianAttributeU16x1(shaderContext, src, attributeInputIndex);
			src->add("attrDecoder.x = floatBitsToUint(float(int(attrDecoder.x))/65535.0);" _CRLF);
		}
		else
		{
			cemuLog_logDebug(LogType::Force, "_emitAttributeDecodeGLSL(): Unsupported fmt {:02x} nfa {} signed {} endian {}", attrib->format, attrib->nfa, attrib->isSigned, attrib->endianSwap);
		}
	}
	else
	{
		cemu_assert_debug(false);
	}
}
