static
	YSI_g_sVariable;

static stock y_va_CallRemoteFunction(va_args<>)
{
	va_CallRemoteFunction("y_va_PublicTestFunction", "ii", va_start<0>);
}

static stock y_va_CallLocalFunction(va_args<>)
{
	new
		local1 = 123,
		local2 = 1,
		local3 = 7,
		local4 = 7;
	va_CallLocalFunction("y_va_PublicTestFunction", "ii", va_start<0>);
	return local1 * local2 + local3 - local4;
}

static stock y_va_CallLocalFunction_X(func[], va_args<>)
{
	new
		local1 = 61,
		local2 = 2,
		local3 = 8,
		local4 = 7;
	va_CallLocalFunction(func, "si", va_start<1>);
	return local1 * local2 + local3 - local4;
}

static stock y_va_CallRemoteFunction_X(func[], va_args<>)
{
	va_CallRemoteFunction(func, "si", va_start<1>);
}

forward y_va_PublicTestFunction(vara, varb);

public y_va_PublicTestFunction(vara, varb)
{
	new
		varc = 55,
		vard = 101;
	if (vara)
	{
		YSI_g_sVariable = varb;
	}
	else
	{
		++YSI_g_sVariable;
	}
	// Just use them so they can't be optimised out.
	vara = clamp(varb, varc, vard);
}

forward y_va_PublicRecurseFunction(func[], vara);

public y_va_PublicRecurseFunction(func[], vara)
{
	// "" Passed to "CallLocalFunction" crashes it.
	ASSERT(123 == y_va_CallLocalFunction_X(func, "\1", vara));
}

forward y_va_PublicCallFunction(func[], vara);

public y_va_PublicCallFunction(func[], vara)
{
	y_va_PublicTestFunction(vara, 123456);
}

Test:y_va_CallRemoteFunction()
{
	YSI_g_sVariable = 5;
	y_va_CallRemoteFunction(0, 11);
	ASSERT(YSI_g_sVariable == 6);
	y_va_CallRemoteFunction(1, 11);
	ASSERT(YSI_g_sVariable == 11);
}

static stock y_va_printf_0(va_args<>)
{
	va_printf("", va_start<0>);
}

static stock y_va_printf_1(num, va_args<>)
{
	#pragma unused num
	va_printf("", va_start<1>);
}

static stock y_va_printf_2(num, other, va_args<>)
{
	#pragma unused num, other
	va_printf("", va_start<2>);
}

static stock y_va_printf_3(num, other, str[], va_args<>)
{
	#pragma unused num, other, str
	va_printf("", va_start<3>);
}

Test:y_va_printf()
{
	y_va_printf_0(42, 1, "hi", I@);
	y_va_printf_1(42, 1, "hi", I@);
	y_va_printf_2(42, 1, "hi", I@);
	y_va_printf_3(42, 1, "hi", I@);
}

Test:y_va_recurse_local()
{
	YSI_g_sVariable = 0;
	y_va_CallLocalFunction_X("y_va_PublicRecurseFunction", "y_va_PublicCallFunction", true);
	ASSERT(YSI_g_sVariable == 123456);
	y_va_CallLocalFunction_X("y_va_PublicRecurseFunction", "y_va_PublicCallFunction", false);
	ASSERT(YSI_g_sVariable == 123457);
}

Test:y_va_recurse_remote()
{
	YSI_g_sVariable = 0;
	y_va_CallRemoteFunction_X("y_va_PublicRecurseFunction", "y_va_PublicCallFunction", true);
	ASSERT(YSI_g_sVariable == 123456);
	y_va_CallRemoteFunction_X("y_va_PublicRecurseFunction", "y_va_PublicCallFunction", false);
	ASSERT(YSI_g_sVariable == 123457);
}

Test:y_va_CallLocalFunction()
{
	YSI_g_sVariable = 8;
	y_va_CallLocalFunction(0, 45);
	ASSERT(YSI_g_sVariable == 9);
	ASSERT(123 == y_va_CallLocalFunction(1, 45));
	ASSERT(YSI_g_sVariable == 45);
}

static stock y_va_SetTimerEx(va_args<>)
{
	return va_SetTimerEx("y_va_SetTimerExPublic", 1000, false, "iii", va_start<0>);
}

Test:y_va_SetTimerEx()
{
	ASSERT(y_va_SetTimerEx(5, 6, 7) != 0);
}

static stock y_va_format(dest[], size, fmat[], va_args<>)
{
	va_format(dest, size, fmat, va_start<3>);
}

Test:y_va_format()
{
	new
		str[64];
	y_va_format(str, sizeof (str), "Hello %d %04x %s", 99, 0x1F, "woop");
	ASSERT(!strcmp(str, "Hello 99 001F woop"));
}

static stock y_va_return(dest[], size, fmat[], va_args<>)
{
	strcpy(dest, va_return(fmat, va_start<3>), size);
}

Test:y_va_return()
{
	new
		str[YSI_MAX_STRING];
	y_va_return(str, sizeof (str), "Hi %.3f %8.8s %8.8s", 5.5, "this is a very long string", "short");
	ASSERT(!strcmp(str, "Hi 5.500 this is  short   "));
}

static stock y_va_DoubleIndirection(dest[YSI_MAX_STRING], fmat[], var0, var1, var2, va_args<>) //var3, var4, size = sizeof (dest))
{
	#pragma unused var0, var1, var2
	// va_format(dest, 32, fmat, va_start<5>);
	//dest = "hello 03/06/17";
	dest = va_return(fmat, va_start<5>);
	//print(dest);
}

Test:y_va_DoubleIndirection()
{
	new
		dest[YSI_MAX_STRING],
		var0 = 44,
		var1 = 55,
		var2 = 66,
		var3 = 77,
		var4 = 88;
	y_va_DoubleIndirection(dest, "%d %d", var0, var1, var2, var3, var4);
	ASSERT(var0 == 44);
	ASSERT(var1 == 55);
	ASSERT(var2 == 66);
	ASSERT(var3 == 77);
	ASSERT(var4 == 88);
	ASSERT(!strcmp(dest, "77 88"));
}

static stock bool:y_va_Locals(real[], fmat[], va_args<>)
{
	new
		dest[128];
	va_format(dest, sizeof (dest), fmat, va_start<2>);
	return !strcmp(dest, real);
}

Test:y_va_Locals()
{
	ASSERT(y_va_Locals("42", "%d", 42));
	ASSERT(y_va_Locals("42 43 44", "%d %d %d", 42, 43, 44));
	ASSERT(y_va_Locals("hi", "%s", "hi"));
	ASSERT(y_va_Locals("he -1", "%.2s %d", "hello", -1));
}

static stock y_va2_printf_0(GLOBAL_TAG_TYPES:...)
{
	printf("", ___);
}

static stock y_va2_printf_1(num, GLOBAL_TAG_TYPES:...)
{
	#pragma unused num
	printf("", ___(1));
}

static stock y_va2_printf_2(num, other, GLOBAL_TAG_TYPES:...)
{
	#pragma unused num, other
	printf("", ___2);
}

static stock y_va2_printf_3(num, other, str[], GLOBAL_TAG_TYPES:...)
{
	#pragma unused num, other, str
	printf("", ___(3));
}

Test:y_va2_printf()
{
	y_va2_printf_0(42, 1, "hi", I@);
	y_va2_printf_1(42, 1, "hi", I@);
	y_va2_printf_2(42, 1, "hi", I@);
	y_va2_printf_3(42, 1, "hi", I@);
}

static stock y_va2_Extract(num, ...)
{
	new
		ret[YSI_MAX_STRING];
	va_getstring(ret, num + 1);
	__COMPILER_STRING_RETURN(ret);
}

static stock y_va2_Three(...)
{
	return 3;
}

static stock y_va2_Nest2(fmat[YSI_MAX_STRING], ...)
{
	new
		dest[YSI_MAX_STRING];
	format(dest, sizeof (dest), fmat, ___1);
	ASSERT(!strcmp(dest, "second param second param final param"));
}

static stock y_va2_Nest1(...)
{
	y_va2_Nest2(y_va2_Extract(1, ___), y_va2_Extract(y_va2_Three(___), ___), y_va2_Extract(2, ___(1)), ___);
}

Test:y_va2_Nesting()
{
	y_va2_Nest1("final param", "%s %s %s", "third param", "second param");
}

static stock y_va2_format(dest[], size, fmat[], GLOBAL_TAG_TYPES:...)
{
	format(dest, size, fmat, ___(3));
}

Test:y_va2_format()
{
	new
		str[64];
	y_va2_format(str, sizeof (str), "Hello %d %04x %s", 99, 0x1F, "woop");
	ASSERT(!strcmp(str, "Hello 99 001F woop"));
}

static stock y_va2_return(dest[], size, fmat[], GLOBAL_TAG_TYPES:...)
{
	format(dest, size, fmat, ___(3));
}

Test:y_va2_return()
{
	new
		str[YSI_MAX_STRING];
	y_va2_return(str, sizeof (str), "Hi %.3f %8.8s %8.8s", 5.5, "this is a very long string", "short");
	ASSERT(!strcmp(str, "Hi 5.500 this is  short   "));
}

static stock y_va2_DoubleIndirection(dest[1040], fmat[], var0, var1, var2, GLOBAL_TAG_TYPES:...) //var3, var4, size = sizeof (dest))
{
	#pragma unused var0, var1, var2
	// va_format(dest, 32, fmat, ___(5));
	format(dest, sizeof (dest), fmat, ___(5));
}

Test:y_va2_DoubleIndirection()
{
	new
		dest[1040],
		var0 = 44,
		var1 = 55,
		var2 = 66,
		var3 = 77,
		var4 = 88;
	y_va2_DoubleIndirection(dest, "%d %d", var0, var1, var2, var3, var4);
	ASSERT(var0 == 44);
	ASSERT(var1 == 55);
	ASSERT(var2 == 66);
	ASSERT(var3 == 77);
	ASSERT(var4 == 88);
	ASSERT(!strcmp(dest, "77 88"));
}

static stock y_va2_TripleIndirectionC(dest[1040], fmat[], var0, var1, var2, GLOBAL_TAG_TYPES:...)
{
	#pragma unused var0, var1, var2
	format(dest, sizeof (dest), fmat, ___(5));
	setarg(5, 0, 101);
}

static stock y_va2_TripleIndirectionB(dest[1040], fmat[], var0, var1, var2, GLOBAL_TAG_TYPES:...)
{
	y_va2_TripleIndirectionC(dest, fmat, var0, var1, var2, ___(5));
}

static stock y_va2_TripleIndirectionA(dest[1040], fmat[], var0, var1, var2, GLOBAL_TAG_TYPES:...)
{
	y_va2_TripleIndirectionB(dest, fmat, var0, var1, var2, ___(5));
}

Test:y_va2_TripleIndirection()
{
	new
		dest[1040],
		var0 = 44,
		var1 = 55,
		var2 = 66,
		var3 = 77,
		var4 = 88;
	y_va2_TripleIndirectionA(dest, "%d %d", var0, var1, var2, var3, var4);
	ASSERT(var0 == 44);
	ASSERT(var1 == 55);
	ASSERT(var2 == 66);
	ASSERT(var3 == 101);
	ASSERT(var4 == 88);
	ASSERT(!strcmp(dest, "77 88"));
}

static stock bool:y_va2_Locals(real[], fmat[], GLOBAL_TAG_TYPES:...)
{
	new
		dest[128];
	format(dest, sizeof (dest), fmat, ___2);
	return !strcmp(dest, real);
}

Test:y_va2_Locals()
{
	ASSERT(y_va2_Locals("42", "%d", 42));
	ASSERT(y_va2_Locals("42 43 44", "%d %d %d", 42, 43, 44));
	ASSERT(y_va2_Locals("hi", "%s", "hi"));
	ASSERT(y_va2_Locals("he -1", "%.2s %d", "hello", -1));
}

Test:y_va2_Types()
{
	ASSERT(y_va2_Locals("4.67", "%.2f", 4.672));
	ASSERT(y_va2_Locals("Hello World", "%s", "Hello World"));
}

Test:y_va2_NoParameters()
{
	ASSERT(y_va2_Locals("hello", "hello"));
	ASSERT(y_va2_Locals("Hello World", "Hello World"));
	ASSERT(y_va2_Locals("42 43 44 45 46 47", "42 43 44 45 46 47"));
}

static stock
	YSI_g_sDestString[YSI_MAX_STRING];

Test:y_va2_PassThree()
{
	y_va2_MyFunc1("Hello", "there", "world");
	ASSERT(!strcmp(YSI_g_sDestString, "woo there world"));
	y_va2_MyFunc1("In", "the", "world");
	ASSERT(!strcmp(YSI_g_sDestString, "woo the world"));
	y_va2_MyFunc1("Hello", "one", "world");
	ASSERT(!strcmp(YSI_g_sDestString, "woo one world"));
	y_va2_MyFunc1("Hello", "there", "yeah");
	ASSERT(!strcmp(YSI_g_sDestString, "woo there yeah"));
	y_va2_MyFunc1("Hello", "there", "5");
	ASSERT(!strcmp(YSI_g_sDestString, "woo there 5"));
	y_va2_MyFunc1("Hello", "there", "C");
	ASSERT(!strcmp(YSI_g_sDestString, "woo there C"));
}

static stock y_va2_MyFunc3(...)
{
	new woo[32] = "woo";
	__COMPILER_STRING_RETURN(woo);
	//__COMPILER_STRING_RETURN("woo", 32);
}

static stock y_va2_MyFunc1(...)
{
	y_va2_MyFunc2(y_va2_MyFunc3(___), ___(1));
}

static stock y_va2_MyFunc2(...)
{
	format(YSI_g_sDestString, sizeof (YSI_g_sDestString), "%s %s %s", ___);
}

static stock y_va_BlankFormat1(...)
{
	new string[144];
	va_format(string, sizeof (string), "%s", ___);
//	print(string);
}

Test:y_va_BlankFormat1()
{
	new abc[32] = "WAT";
	y_va_BlankFormat1(abc);
	y_va_BlankFormat1(abc);
	y_va_BlankFormat1(abc);
	y_va_BlankFormat1(abc);
}

static stock
	YSI_g_sString[144];

static stock y_va_BlankFormat2(...)
{
	new string[144];
	va_format(YSI_g_sString, sizeof (YSI_g_sString), "%s", ___);
	#pragma unused string
//	print(string);
}

Test:y_va_BlankFormat2()
{
	new abc[32] = "WAT";
	YSI_g_sString[0] = '\0';
	y_va_BlankFormat2(abc);
	ASSERT(!strcmp(YSI_g_sString, "WAT"));
	YSI_g_sString[0] = '\0';
	y_va_BlankFormat2(abc);
	ASSERT(!strcmp(YSI_g_sString, "WAT"));
	YSI_g_sString[0] = '\0';
	y_va_BlankFormat2(abc);
	ASSERT(!strcmp(YSI_g_sString, "WAT"));
	YSI_g_sString[0] = '\0';
	y_va_BlankFormat2(abc);
	ASSERT(!strcmp(YSI_g_sString, "WAT"));
}

