// formatex.inc by Slice #include #if defined FORMAT_EXTRA_TAGS #define FORMAT_TAGS _, PlayerText3D, Text, Text3D, Menu, DB, DBResult, File, Float, FORMAT_EXTRA_TAGS #else #define FORMAT_TAGS _, PlayerText3D, Text, Text3D, Menu, DB, DBResult, File, Float #endif #if !defined FORMAT_BUFFER_SIZE #define FORMAT_BUFFER_SIZE 2048 #endif #if !defined FORMAT_PRINT_BUFFER_SIZE #define FORMAT_PRINT_BUFFER_SIZE 512 #endif #if !defined FORMAT_CUSTOM_SPEC_BUFFER_SIZE #define FORMAT_CUSTOM_SPEC_BUFFER_SIZE 512 #endif #if !defined FORMAT_REPLACE_NATIVES #define FORMAT_REPLACE_NATIVES true #endif #define FormatSpecifier<'%1'>(%2[%3],%4) FMAT@1:F@%1(%2[FORMAT_CUSTOM_SPEC_BUFFER_SIZE],FMAT@2:___unused,%4) #define FMAT@2:___unused,%1[ %1[ #define FMAT@1:%1(%2) forward %1(%2); public %1(%2) static stock gs_CustomFormatFunctions[127] = {-1, ...}, bool:gs_bIsInitialized = false ; forward __fmt_funcinc(); public __fmt_funcinc() { new szOutput[1]; format(szOutput, 0, ""); } static stock InitializeFormatSpecifiers() { new szFunctionName[4 char] = !"F@_", iIndex, iFunctionAddress ; gs_bIsInitialized = true; for (new c = '@'; c <= 'z'; c++) { // Skip the chars that can't be used in function names if (c == 'Z' + 1) c = '_'; else if (c == '_' + 1) c = 'a'; szFunctionName{2} = c; // Get the function's address if it exists if (-1 != (iIndex = funcidx(szFunctionName))) { #emit LCTRL 1 #emit NEG #emit MOVE.alt #emit ADD.C 32 #emit STOR.S.pri iFunctionAddress #emit LREF.S.pri iFunctionAddress #emit ADD #emit LOAD.S.alt iIndex #emit SHL.C.alt 3 #emit ADD #emit STOR.S.pri iFunctionAddress #emit LREF.S.pri iFunctionAddress #emit STOR.S.pri iFunctionAddress gs_CustomFormatFunctions[c] = iFunctionAddress; } } } stock formatex(szOutput[], iLength = sizeof(szOutput), const szFormatString[], {FORMAT_TAGS}:...) { static s_szBuffer[FORMAT_BUFFER_SIZE], bool:s_bIsInCustomSpecifier = false ; // If formatex is called inside a custom specifier, the original "format" will be used // to prevent having s_szBuffer overwritten. Specifiers shouldn't depend on other specifiers, anyway. if (s_bIsInCustomSpecifier) { new iNumArgs, i ; #emit LOAD.S.pri 8 #emit STOR.S.pri iNumArgs #emit SHR.C.pri 2 #emit STOR.S.pri i while (--i >= 0) { #emit LOAD.S.pri i #emit SHL.C.pri 2 #emit ADD.C 12 #emit MOVE.alt #emit LCTRL 5 #emit ADD #emit LOAD.I #emit PUSH.pri } #emit LOAD.S.pri iNumArgs #emit PUSH.pri #emit MOVE.alt #emit SYSREQ.C format #emit CONST.pri 4 #emit ADD #emit MOVE.alt #emit LCTRL 4 #emit ADD #emit SCTRL 4 } else { new iPos = -1, iArg = 12 + (3 * 4), iArgCount, iAddress, iArgValue, aiArgs[128], i ; if (!gs_bIsInitialized) InitializeFormatSpecifiers(); iLength = min(FORMAT_BUFFER_SIZE, iLength); s_szBuffer[0] = 0; strunpack(s_szBuffer, szFormatString); while (-1 != (iPos = strfind(s_szBuffer, !"%", _, ++iPos))) { while (s_szBuffer[++iPos]) { // Look for custom formats if (1 <= s_szBuffer[iPos] < sizeof(gs_CustomFormatFunctions) && gs_CustomFormatFunctions[s_szBuffer[iPos]] != -1) { new iFunc = gs_CustomFormatFunctions[s_szBuffer[iPos]] ; static s_szCustomFormatBuffer[FORMAT_CUSTOM_SPEC_BUFFER_SIZE] ; strdel(s_szBuffer, iPos - 1, iPos + 1); s_szCustomFormatBuffer[0] = 0; #emit LCTRL 5 #emit LOAD.S.alt iArg #emit ADD #emit LOAD.I #emit MOVE.alt #emit LOAD.I #emit PUSH.pri #emit PUSH.alt iArg += 4; s_bIsInCustomSpecifier = true; #emit PUSH.C s_szCustomFormatBuffer #emit PUSH.C 12 #emit LCTRL 6 #emit ADD.C 28 #emit PUSH.pri #emit LOAD.S.pri iFunc #emit SCTRL 6 s_bIsInCustomSpecifier = false; strins(s_szBuffer, s_szCustomFormatBuffer, iPos - 1); break; } switch (s_szBuffer[iPos]) { // Handled by the original format function case '*', 'i', 'd', 'x', 'h', 'c', 's', 'f', 'b': { // Get the argument address and save it for later #emit LCTRL 5 #emit LOAD.S.alt iArg #emit ADD #emit LOAD.I #emit STOR.S.pri iAddress #emit MOVE.pri #emit ADD.C 4 #emit STOR.S.pri iArg aiArgs[iArgCount++] = iAddress; if (s_szBuffer[iPos] == '*') continue; break; } // Unsigned numbers case 'u': { new szBuffer[11] ; #emit LCTRL 5 #emit LOAD.S.alt iArg #emit ADD #emit LOAD.I #emit LOAD.I #emit STOR.S.pri iArgValue #emit MOVE.pri #emit ADD.C 4 #emit STOR.S.pri iArg strdel(s_szBuffer, iPos - 1, iPos + 1); if (!iArgValue) { strins(s_szBuffer, "0", iPos - 1); } else { new j = sizeof(szBuffer) - 1 ; while (iArgValue) { // szBuffer[--i] #emit ADDR.alt szBuffer // alt = *szBuffer #emit LOAD.S.pri j // pri = i #emit DEC.pri // pri -= 1 #emit STOR.S.pri j // i = pri #emit IDXADDR // pri = alt + i * 4 #emit PUSH.pri // Store pri for later // Now do an unsigned divide on uValue then use both the quotient and remainder! #emit LOAD.S.pri iArgValue // pri = uValue #emit CONST.alt 10 #emit UDIV // pri = uValue / 10; alt = uValue % 10 #emit STOR.S.pri iArgValue // uValue = pri #emit CONST.pri '0' #emit ADD // pri = '0' + (uValue % 10) #emit POP.alt // alt = szBuffer[i] #emit STOR.I // szBuffer[i] = pri } strins(s_szBuffer, szBuffer[j], iPos - 1); } } case '0' .. '9', ' ', '.': continue; case '%': break; default: { break; } } } } i = iArgCount; // Push the arguments we stored above while (--i >= 0) { #emit ADDR.alt aiArgs #emit LOAD.S.pri i #emit LIDX #emit PUSH.pri #emit STOR.S.pri iAddress } // New format specifier #emit PUSH.C s_szBuffer // Max length #emit PUSH.S iLength // Output string #emit PUSH.S szOutput // Argument count #emit LOAD.S.pri iArgCount #emit SHL.C.pri 2 #emit ADD.C 12 #emit PUSH.pri // Save the argument count for later #emit MOVE.alt // Call format (duh) #emit SYSREQ.C format // Add 4 to the argument count #emit CONST.pri 4 #emit ADD #emit MOVE.alt // Remove from the stack #emit LCTRL 4 #emit ADD #emit SCTRL 4 } // Return in case anyone uses it return 1; } stock printfex(const szFormatString[], {FORMAT_TAGS}:...) { const iBufferSize = FORMAT_PRINT_BUFFER_SIZE ; static s_szBuffer[FORMAT_PRINT_BUFFER_SIZE] ; new iNumArgs = numargs(), i = iNumArgs - 1 ; while (--i >= 0) { #emit LOAD.S.pri i #emit SHL.C.pri 2 #emit ADD.C 16 #emit MOVE.alt #emit LCTRL 5 #emit ADD #emit LOAD.I #emit PUSH.pri } #emit PUSH.S szFormatString #emit PUSH.C iBufferSize #emit PUSH.C s_szBuffer #emit LOAD.S.pri iNumArgs #emit SHL.C.pri 2 #emit ADD.C 8 #emit PUSH.pri #emit LCTRL 6 #emit ADD.C 28 #emit PUSH.pri #emit CONST.pri formatex #emit SCTRL 6 print(s_szBuffer); } // ----------------------------------------------------------------------------------------------------- // Specifiers // ----------------------------------------------------------------------------------------------------- // Inline color FormatSpecifier<'C'>(output[], color) { format(output, sizeof(output), "{%06x}", color >>> 8); } // Player name FormatSpecifier<'p'>(output[], playerid) { if (0 <= playerid < GetMaxPlayers() && IsPlayerConnected(playerid)) GetPlayerName(playerid, output, sizeof(output)); else strcat(output, ""); } // Player name and color FormatSpecifier<'P'>(output[], playerid) { if (0 <= playerid < GetMaxPlayers() && IsPlayerConnected(playerid)) { format(output, sizeof(output), "{%06x}", GetPlayerColor(playerid) >>> 8); GetPlayerName(playerid, output[8], sizeof(output) - 8); } else strcat(output, "{FFFFFF}"); } // Weapon name FormatSpecifier<'W'>(output[], weapon) { static const s_WeaponNames[][] = { {!"Fists" }, {!"Knuckles" }, {!"Golfclub" }, // 0, 1, 2 {!"Nightstick" }, {!"Knife" }, {!"Bat" }, // 3, 4, 5 {!"Shovel" }, {!"Pool Cue" }, {!"Katana" }, // 6, 7, 8 {!"Chainsaw" }, {!"Dildo" }, {!"Vibrator" }, // 9, 10, 11 {!"Vibrator" }, {!"Vibrator" }, {!"Flowers" }, // 12, 13, 14 {!"Cane" }, {!"Grenade" }, {!"Teargas" }, // 15, 16, 17 {!"Molotov" }, {!"" }, {!"" }, // 18, , {!"" }, {!"9mm" }, {!"Silenced Pistol"}, // , 22, 23 {!"Deagle" }, {!"Shotgun" }, {!"Sawnoffs" }, // 24, 25, 26 {!"Combat Shotgun" }, {!"Mac-10" }, {!"MP5" }, // 27, 28, 29 {!"AK-47" }, {!"M4" }, {!"Tec-9" }, // 30, 31, 32 {!"Cuntgun" }, {!"Sniper" }, {!"Rocketlauncher" }, // 33, 34, 35 {!"Heatseeking RPG"}, {!"Flamethrower"}, {!"Minigun" }, // 36, 37, 38 {!"Satchel" }, {!"Detonator" }, {!"Spraycan" }, // 39, 40, 41 {!"Extinguisher" }, {!"Camera" }, {!"Nightvision" }, // 42, 43, 44 {!"Infrared" }, {!"Parachute" }, {!"Fake Pistol" }, // 45, 46, 47 {!"" }, {!"Vehicle" }, {!"Helikill" }, // , 49, 50 {!"Explosion" }, {!"" }, {!"Drown" }, // 51, , 53 {!"Collision" }, {!"Splat" }, {!"Unknown" } // 54, 55, 56 } ; if (0 <= weapon < sizeof(s_WeaponNames)) strcat(output, s_WeaponNames[weapon]); else strcat(output, "Unknown"); } // Weapon name, lower-case singular (for sentences) FormatSpecifier<'w'>(output[], weapon) { static const s_WeaponNamesLowercaseSingular[][] = { {!"fists" }, {!"knuckles" }, {!"a golfclub" }, // 0, 1, 2 {!"a nightstick" }, {!"a knife" }, {!"a bat" }, // 3, 4, 5 {!"a shovel" }, {!"a pool cue" }, {!"a katana" }, // 6, 7, 8 {!"a chainsaw" }, {!"a dildo" }, {!"a vibrator" }, // 9, 10, 11 {!"a vibrator" }, {!"a vibrator" }, {!"flowers" }, // 12, 13, 14 {!"a cane" }, {!"a grenade" }, {!"teargas" }, // 15, 16, 17 {!"a molotov" }, {!"" }, {!"" }, // 18, , {!"" }, {!"a 9mm" }, {!"a silenced pistol"}, // , 22, 23 {!"a deagle" }, {!"a shotgun" }, {!"sawnoffs" }, // 24, 25, 26 {!"a combat shotgun" }, {!"a mac-10" }, {!"an mp5" }, // 27, 28, 29 {!"an ak-47" }, {!"an m4" }, {!"a tec-9" }, // 30, 31, 32 {!"a cuntgun" }, {!"a sniper" }, {!"a rocketlauncher" }, // 33, 34, 35 {!"a heatseeking rpg" }, {!"a flamethrower"}, {!"a minigun" }, // 36, 37, 38 {!"a satchel" }, {!"a detonator" }, {!"a spraycan" }, // 39, 40, 41 {!"an extinguisher" }, {!"a camera" }, {!"nightvision" }, // 42, 43, 44 {!"an infrared" }, {!"a parachute" }, {!"a fake pistol" }, // 45, 46, 47 {!"" }, {!"a vehicle" }, {!"a helikill" }, // , 49, 50 {!"an explosion" }, {!"" }, {!"drowning" }, // 51, , 53 {!"a collision" }, {!"a splat" }, {!"something unknown"} // 54, 55, 56 } ; if (0 <= weapon < sizeof(s_WeaponNamesLowercaseSingular)) strcat(output, s_WeaponNamesLowercaseSingular[weapon]); else strcat(output, "something unknown"); } // Vehicle name FormatSpecifier<'v'>(output[], modelid) { static const s_VehicleNames[][] = { {!"Landstalker" }, {!"Bravura" }, {!"Buffalo" }, {!"Linerunner" }, {!"Perrenial" }, {!"Sentinel" }, {!"Dumper" }, {!"Firetruck" }, {!"Trashmaster" }, {!"Stretch" }, {!"Manana" }, {!"Infernus" }, {!"Voodoo" }, {!"Pony" }, {!"Mule" }, {!"Cheetah" }, {!"Ambulance" }, {!"Leviathan" }, {!"Moonbeam" }, {!"Esperanto" }, {!"Taxi" }, {!"Washington" }, {!"Bobcat" }, {!"Mr Whoopee" }, {!"BF Injection" }, {!"Hunter" }, {!"Premier" }, {!"Enforcer" }, {!"Securicar" }, {!"Banshee" }, {!"Predator" }, {!"Bus" }, {!"Rhino" }, {!"Barracks" }, {!"Hotknife" }, {!"Trailer 1" }, {!"Previon" }, {!"Coach" }, {!"Cabbie" }, {!"Stallion" }, {!"Rumpo" }, {!"RC Bandit" }, {!"Romero" }, {!"Packer" }, {!"Monster" }, {!"Admiral" }, {!"Squalo" }, {!"Seasparrow" }, {!"Pizzaboy" }, {!"Tram" }, {!"Trailer 2" }, {!"Turismo" }, {!"Speeder" }, {!"Reefer" }, {!"Tropic" }, {!"Flatbed" }, {!"Yankee" }, {!"Caddy" }, {!"Solair" }, {!"Berkley's RC Van" }, {!"Skimmer" }, {!"PCJ-600" }, {!"Faggio" }, {!"Freeway" }, {!"RC Baron" }, {!"RC Raider" }, {!"Glendale" }, {!"Oceanic" }, {!"Sanchez" }, {!"Sparrow" }, {!"Patriot" }, {!"Quad" }, {!"Coastguard" }, {!"Dinghy" }, {!"Hermes" }, {!"Sabre" }, {!"Rustler" }, {!"ZR-350" }, {!"Walton" }, {!"Regina" }, {!"Comet" }, {!"BMX" }, {!"Burrito" }, {!"Camper" }, {!"Marquis" }, {!"Baggage" }, {!"Dozer" }, {!"Maverick" }, {!"News Chopper" }, {!"Rancher" }, {!"FBI Rancher" }, {!"Virgo" }, {!"Greenwood" }, {!"Jetmax" }, {!"Hotring" }, {!"Sandking" }, {!"Blista Compact" }, {!"Police Maverick" }, {!"Boxville" }, {!"Benson" }, {!"Mesa" }, {!"RC Goblin" }, {!"Hotring Racer A" }, {!"Hotring Racer B" }, {!"Bloodring Banger" }, {!"Rancher" }, {!"Super GT" }, {!"Elegant" }, {!"Journey" }, {!"Bike" }, {!"Mountain Bike" }, {!"Beagle" }, {!"Cropdust" }, {!"Stunt" }, {!"Tanker" }, {!"Roadtrain" }, {!"Nebula" }, {!"Majestic" }, {!"Buccaneer" }, {!"Shamal" }, {!"Hydra" }, {!"FCR-900" }, {!"NRG-500" }, {!"HPV1000" }, {!"Cement Truck" }, {!"Tow Truck" }, {!"Fortune" }, {!"Cadrona" }, {!"FBI Truck" }, {!"Willard" }, {!"Forklift" }, {!"Tractor" }, {!"Combine" }, {!"Feltzer" }, {!"Remington" }, {!"Slamvan" }, {!"Blade" }, {!"Freight" }, {!"Streak" }, {!"Vortex" }, {!"Vincent" }, {!"Bullet" }, {!"Clover" }, {!"Sadler" }, {!"Firetruck LA" }, {!"Hustler" }, {!"Intruder" }, {!"Primo" }, {!"Cargobob" }, {!"Tampa" }, {!"Sunrise" }, {!"Merit" }, {!"Utility" }, {!"Nevada" }, {!"Yosemite" }, {!"Windsor" }, {!"Monster A" }, {!"Monster B" }, {!"Uranus" }, {!"Jester" }, {!"Sultan" }, {!"Stratum" }, {!"Elegy" }, {!"Raindance" }, {!"RC Tiger" }, {!"Flash" }, {!"Tahoma" }, {!"Savanna" }, {!"Bandito" }, {!"Freight Flat" }, {!"Streak Carriage" }, {!"Kart" }, {!"Mower" }, {!"Duneride" }, {!"Sweeper" }, {!"Broadway" }, {!"Tornado" }, {!"AT-400" }, {!"DFT-30" }, {!"Huntley" }, {!"Stafford" }, {!"BF-400" }, {!"Newsvan" }, {!"Tug" }, {!"Trailer 3" }, {!"Emperor" }, {!"Wayfarer" }, {!"Euros" }, {!"Hotdog" }, {!"Club" }, {!"Freight Carriage" }, {!"Trailer 3" }, {!"Andromada" }, {!"Dodo" }, {!"RC Cam" }, {!"Launch" }, {!"Police Car (LSPD)"}, {!"Police Car (SFPD)"}, {!"Police Car (LVPD)"}, {!"Police Ranger" }, {!"Picador" }, {!"S.W.A.T. Van" }, {!"Alpha" }, {!"Phoenix" }, {!"Glendale" }, {!"Sadler" }, {!"Luggage Trailer A"}, {!"Luggage Trailer B"}, {!"Stair Trailer" }, {!"Boxville" }, {!"Farm Plow" }, {!"Utility Trailer" } } ; if (0 <= (modelid -= 400) < sizeof(s_VehicleNames)) strcat(output, s_VehicleNames[modelid]); else strcat(output, "Unknown"); } // Unsigned 4-byte hex FormatSpecifier<'X'>(output[], value) { format(output, sizeof(output), "%02x%06x", value >>> 24, value & 0xFFFFFF); } // Do this last so the specifiers always use the native function #if FORMAT_REPLACE_NATIVES #define format formatex #define printf printfex #endif