formatex.inc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. // formatex.inc by Slice
  2. #include <a_samp>
  3. //Dependencies added by dy1zan;
  4. #include <sarputil>
  5. #if defined FORMAT_EXTRA_TAGS
  6. #define FORMAT_TAGS _, PlayerText3D, Text, Text3D, Menu, DB, DBResult, File, Float, FORMAT_EXTRA_TAGS
  7. #else
  8. #define FORMAT_TAGS _, PlayerText3D, Text, Text3D, Menu, DB, DBResult, File, Float
  9. #endif
  10. #if !defined FORMAT_BUFFER_SIZE
  11. #define FORMAT_BUFFER_SIZE 2048
  12. #endif
  13. #if !defined FORMAT_PRINT_BUFFER_SIZE
  14. #define FORMAT_PRINT_BUFFER_SIZE 512
  15. #endif
  16. #if !defined FORMAT_CUSTOM_SPEC_BUFFER_SIZE
  17. #define FORMAT_CUSTOM_SPEC_BUFFER_SIZE 512
  18. #endif
  19. #if !defined FORMAT_REPLACE_NATIVES
  20. #define FORMAT_REPLACE_NATIVES true
  21. #endif
  22. #define FormatSpecifier<'%1'>(%2[%3],%4) FMAT@1:F@%1(%2[FORMAT_CUSTOM_SPEC_BUFFER_SIZE],FMAT@2:___unused,%4)
  23. #define FMAT@2:___unused,%1[ %1[
  24. #define FMAT@1:%1(%2) forward %1(%2); public %1(%2)
  25. static stock
  26. gs_CustomFormatFunctions[127] = {-1, ...},
  27. bool:gs_bIsInitialized = false
  28. ;
  29. forward __fmt_funcinc();
  30. public __fmt_funcinc() {
  31. new szOutput[1];
  32. format(szOutput, 0, "");
  33. }
  34. static stock InitializeFormatSpecifiers() {
  35. new
  36. szFunctionName[4 char] = !"F@_",
  37. iIndex,
  38. iFunctionAddress
  39. ;
  40. gs_bIsInitialized = true;
  41. for (new c = '@'; c <= 'z'; c++) {
  42. // Skip the chars that can't be used in function names
  43. if (c == 'Z' + 1)
  44. c = '_';
  45. else if (c == '_' + 1)
  46. c = 'a';
  47. szFunctionName{2} = c;
  48. // Get the function's address if it exists
  49. if (-1 != (iIndex = funcidx(szFunctionName))) {
  50. #emit LCTRL 1
  51. #emit NEG
  52. #emit MOVE.alt
  53. #emit ADD.C 32
  54. #emit STOR.S.pri iFunctionAddress
  55. #emit LREF.S.pri iFunctionAddress
  56. #emit ADD
  57. #emit LOAD.S.alt iIndex
  58. #emit SHL.C.alt 3
  59. #emit ADD
  60. #emit STOR.S.pri iFunctionAddress
  61. #emit LREF.S.pri iFunctionAddress
  62. #emit STOR.S.pri iFunctionAddress
  63. gs_CustomFormatFunctions[c] = iFunctionAddress;
  64. }
  65. }
  66. }
  67. stock formatex(szOutput[], iLength = sizeof(szOutput), const szFormatString[], {FORMAT_TAGS}:...) {
  68. static
  69. s_szBuffer[FORMAT_BUFFER_SIZE],
  70. bool:s_bIsInCustomSpecifier = false
  71. ;
  72. // If formatex is called inside a custom specifier, the original "format" will be used
  73. // to prevent having s_szBuffer overwritten. Specifiers shouldn't depend on other specifiers, anyway.
  74. if (s_bIsInCustomSpecifier) {
  75. new
  76. iNumArgs,
  77. i
  78. ;
  79. #emit LOAD.S.pri 8
  80. #emit STOR.S.pri iNumArgs
  81. #emit SHR.C.pri 2
  82. #emit STOR.S.pri i
  83. while (--i >= 0) {
  84. #emit LOAD.S.pri i
  85. #emit SHL.C.pri 2
  86. #emit ADD.C 12
  87. #emit MOVE.alt
  88. #emit LCTRL 5
  89. #emit ADD
  90. #emit LOAD.I
  91. #emit PUSH.pri
  92. }
  93. #emit LOAD.S.pri iNumArgs
  94. #emit PUSH.pri
  95. #emit MOVE.alt
  96. #emit SYSREQ.C format
  97. #emit CONST.pri 4
  98. #emit ADD
  99. #emit MOVE.alt
  100. #emit LCTRL 4
  101. #emit ADD
  102. #emit SCTRL 4
  103. } else {
  104. new
  105. iPos = -1,
  106. iArg = 12 + (3 * 4),
  107. iArgCount,
  108. iAddress,
  109. iArgValue,
  110. aiArgs[128],
  111. i
  112. ;
  113. if (!gs_bIsInitialized)
  114. InitializeFormatSpecifiers();
  115. iLength = min(FORMAT_BUFFER_SIZE, iLength);
  116. s_szBuffer[0] = 0;
  117. strunpack(s_szBuffer, szFormatString);
  118. while (-1 != (iPos = strfind(s_szBuffer, !"%", _, ++iPos))) {
  119. while (s_szBuffer[++iPos]) {
  120. // Look for custom formats
  121. if (1 <= s_szBuffer[iPos] < sizeof(gs_CustomFormatFunctions) && gs_CustomFormatFunctions[s_szBuffer[iPos]] != -1) {
  122. new
  123. iFunc = gs_CustomFormatFunctions[s_szBuffer[iPos]]
  124. ;
  125. static
  126. s_szCustomFormatBuffer[FORMAT_CUSTOM_SPEC_BUFFER_SIZE]
  127. ;
  128. strdel(s_szBuffer, iPos - 1, iPos + 1);
  129. s_szCustomFormatBuffer[0] = 0;
  130. #emit LCTRL 5
  131. #emit LOAD.S.alt iArg
  132. #emit ADD
  133. #emit LOAD.I
  134. #emit MOVE.alt
  135. #emit LOAD.I
  136. #emit PUSH.pri
  137. #emit PUSH.alt
  138. iArg += 4;
  139. s_bIsInCustomSpecifier = true;
  140. #emit PUSH.C s_szCustomFormatBuffer
  141. #emit PUSH.C 12
  142. #emit LCTRL 6
  143. #emit ADD.C 28
  144. #emit PUSH.pri
  145. #emit LOAD.S.pri iFunc
  146. #emit SCTRL 6
  147. s_bIsInCustomSpecifier = false;
  148. strins(s_szBuffer, s_szCustomFormatBuffer, iPos - 1);
  149. break;
  150. }
  151. switch (s_szBuffer[iPos]) {
  152. // Handled by the original format function
  153. case '*', 'i', 'd', 'x', 'h', 'c', 's', 'f', 'b', 'q': {
  154. // Get the argument address and save it for later
  155. #emit LCTRL 5
  156. #emit LOAD.S.alt iArg
  157. #emit ADD
  158. #emit LOAD.I
  159. #emit STOR.S.pri iAddress
  160. #emit MOVE.pri
  161. #emit ADD.C 4
  162. #emit STOR.S.pri iArg
  163. aiArgs[iArgCount++] = iAddress;
  164. if (s_szBuffer[iPos] == '*')
  165. continue;
  166. break;
  167. }
  168. // Unsigned numbers
  169. case 'u': {
  170. new
  171. szBuffer[11]
  172. ;
  173. #emit LCTRL 5
  174. #emit LOAD.S.alt iArg
  175. #emit ADD
  176. #emit LOAD.I
  177. #emit LOAD.I
  178. #emit STOR.S.pri iArgValue
  179. #emit MOVE.pri
  180. #emit ADD.C 4
  181. #emit STOR.S.pri iArg
  182. strdel(s_szBuffer, iPos - 1, iPos + 1);
  183. if (!iArgValue) {
  184. strins(s_szBuffer, "0", iPos - 1);
  185. } else {
  186. new
  187. j = sizeof(szBuffer) - 1
  188. ;
  189. while (iArgValue) {
  190. // szBuffer[--i]
  191. #emit ADDR.alt szBuffer // alt = *szBuffer
  192. #emit LOAD.S.pri j // pri = i
  193. #emit DEC.pri // pri -= 1
  194. #emit STOR.S.pri j // i = pri
  195. #emit IDXADDR // pri = alt + i * 4
  196. #emit PUSH.pri // Store pri for later
  197. // Now do an unsigned divide on uValue then use both the quotient and remainder!
  198. #emit LOAD.S.pri iArgValue // pri = uValue
  199. #emit CONST.alt 10
  200. #emit UDIV // pri = uValue / 10; alt = uValue % 10
  201. #emit STOR.S.pri iArgValue // uValue = pri
  202. #emit CONST.pri '0'
  203. #emit ADD // pri = '0' + (uValue % 10)
  204. #emit POP.alt // alt = szBuffer[i]
  205. #emit STOR.I // szBuffer[i] = pri
  206. }
  207. strins(s_szBuffer, szBuffer[j], iPos - 1);
  208. }
  209. }
  210. case '0' .. '9', ' ', '.':
  211. continue;
  212. case '%':
  213. break;
  214. default: {
  215. break;
  216. }
  217. }
  218. }
  219. }
  220. i = iArgCount;
  221. // Push the arguments we stored above
  222. while (--i >= 0) {
  223. #emit ADDR.alt aiArgs
  224. #emit LOAD.S.pri i
  225. #emit LIDX
  226. #emit PUSH.pri
  227. #emit STOR.S.pri iAddress
  228. }
  229. // New format specifier
  230. #emit PUSH.C s_szBuffer
  231. // Max length
  232. #emit PUSH.S iLength
  233. // Output string
  234. #emit PUSH.S szOutput
  235. // Argument count
  236. #emit LOAD.S.pri iArgCount
  237. #emit SHL.C.pri 2
  238. #emit ADD.C 12
  239. #emit PUSH.pri
  240. // Save the argument count for later
  241. #emit MOVE.alt
  242. // Call format (duh)
  243. #emit SYSREQ.C format
  244. // Add 4 to the argument count
  245. #emit CONST.pri 4
  246. #emit ADD
  247. #emit MOVE.alt
  248. // Remove <argument count> from the stack
  249. #emit LCTRL 4
  250. #emit ADD
  251. #emit SCTRL 4
  252. }
  253. // Return in case anyone uses it
  254. return 1;
  255. }
  256. stock printfex(const szFormatString[], {FORMAT_TAGS}:...) {
  257. const
  258. iBufferSize = FORMAT_PRINT_BUFFER_SIZE
  259. ;
  260. static
  261. s_szBuffer[FORMAT_PRINT_BUFFER_SIZE]
  262. ;
  263. new
  264. iNumArgs = numargs(),
  265. i = iNumArgs - 1
  266. ;
  267. while (--i >= 0) {
  268. #emit LOAD.S.pri i
  269. #emit SHL.C.pri 2
  270. #emit ADD.C 16
  271. #emit MOVE.alt
  272. #emit LCTRL 5
  273. #emit ADD
  274. #emit LOAD.I
  275. #emit PUSH.pri
  276. }
  277. #emit PUSH.S szFormatString
  278. #emit PUSH.C iBufferSize
  279. #emit PUSH.C s_szBuffer
  280. #emit LOAD.S.pri iNumArgs
  281. #emit SHL.C.pri 2
  282. #emit ADD.C 8
  283. #emit PUSH.pri
  284. #emit LCTRL 6
  285. #emit ADD.C 28
  286. #emit PUSH.pri
  287. #emit CONST.pri formatex
  288. #emit SCTRL 6
  289. print(s_szBuffer);
  290. return 1;
  291. }
  292. // -----------------------------------------------------------------------------------------------------
  293. // Specifiers
  294. // -----------------------------------------------------------------------------------------------------
  295. // Inline color
  296. FormatSpecifier<'C'>(output[], color) {
  297. format(output, sizeof(output), "{%06x}", color >>> 8);
  298. }
  299. // Player name
  300. FormatSpecifier<'p'>(output[], playerid) {
  301. if (0 <= playerid < GetMaxPlayers() && IsPlayerConnected(playerid))
  302. format(output, sizeof output, "%s", Util:PlayerICName(playerid));
  303. else
  304. strcat(output, "<UNKNOWN>");
  305. }
  306. // Player name and color
  307. FormatSpecifier<'P'>(output[], playerid) {
  308. if (0 <= playerid < GetMaxPlayers() && IsPlayerConnected(playerid)) {
  309. format(output, sizeof(output), "{%06x}", GetPlayerColor(playerid) >>> 8);
  310. format(output, sizeof output, "%s%s", output, Util:PlayerICName(playerid));
  311. } else
  312. strcat(output, "{FFFFFF}<UNKNOWN>");
  313. }
  314. // Weapon name
  315. FormatSpecifier<'W'>(output[], weapon) {
  316. static const
  317. s_WeaponNames[][] = {
  318. {!"Fists" }, {!"Brass Knuckles" }, {!"Golf club" }, // 0, 1, 2
  319. {!"Nightstick" }, {!"Knife" }, {!"Baseball Bat" }, // 3, 4, 5
  320. {!"Shovel" }, {!"Pool Cue" }, {!"Katana" }, // 6, 7, 8
  321. {!"Chainsaw" }, {!"Purple Dildo" }, {!"Dildo" }, // 9, 10, 11
  322. {!"Vibrator" }, {!"Silver Vibrator" }, {!"Flowers" }, // 12, 13, 14
  323. {!"Cane" }, {!"Grenade" }, {!"Tear Gas" }, // 15, 16, 17
  324. {!"Molotov Cocktail" }, {!"" }, {!"" }, // 18, ,
  325. {!"" }, {!"9mm" }, {!"Silenced 9mm" }, // , 22, 23
  326. {!"Desert Eagle" }, {!"Shotgun" }, {!"Sawnoff Shotgun" }, // 24, 25, 26
  327. {!"Combat Shotgun" }, {!"Micro Uzi" }, {!"MP5" }, // 27, 28, 29
  328. {!"AK-47" }, {!"M4" }, {!"Tec-9" }, // 30, 31, 32
  329. {!"Country Rifle" }, {!"Sniper Rifle" }, {!"RPG" }, // 33, 34, 35
  330. {!"HS Rocket" }, {!"Flamethrower" }, {!"Minigun" }, // 36, 37, 38
  331. {!"Satchel Charge" }, {!"Detonator" }, {!"Spraycan" }, // 39, 40, 41
  332. {!"Fire Extinguisher" }, {!"Camera" }, {!"Nightvision Goggles" }, // 42, 43, 44
  333. {!"Thermal Goggles" }, {!"Parachute" }, {!"Fake Pistol" }, // 45, 46, 47
  334. {!"" }, {!"Vehicle" }, {!"Helikill" }, // , 49, 50
  335. {!"Explosion" }, {!"" }, {!"Drown" }, // 51, , 53
  336. {!"Collision" }, {!"Splat" }, {!"Unknown" } // 54, 55, 56
  337. }
  338. ;
  339. if (0 <= weapon < sizeof(s_WeaponNames))
  340. strcat(output, s_WeaponNames[weapon]);
  341. else
  342. strcat(output, "Unknown");
  343. }
  344. // Weapon name, lower-case singular (for sentences)
  345. FormatSpecifier<'w'>(output[], weapon) {
  346. static const
  347. s_WeaponNamesLowercaseSingular[][] = {
  348. {!"fists" }, {!"brass knuckles" }, {!"a golfclub" }, // 0, 1, 2
  349. {!"a nightstick" }, {!"a knife" }, {!"a baseball bat" }, // 3, 4, 5
  350. {!"a shovel" }, {!"a pool cue" }, {!"a katana" }, // 6, 7, 8
  351. {!"a chainsaw" }, {!"a purple dildo" }, {!"a dildo" }, // 9, 10, 11
  352. {!"a vibrator" }, {!"a silver vibrator" }, {!"flowers" }, // 12, 13, 14
  353. {!"a cane" }, {!"a grenade" }, {!"tear gas" }, // 15, 16, 17
  354. {!"a molotov cocktail" }, {!"" }, {!"" }, // 18, ,
  355. {!"" }, {!"a 9mm" }, {!"a silenced 9mm"}, // , 22, 23
  356. {!"a desert eagle" }, {!"a shotgun" }, {!"swanoff shotgun" }, // 24, 25, 26
  357. {!"a combat shotgun" }, {!"a micro uzi" }, {!"an mp5" }, // 27, 28, 29
  358. {!"an ak-47" }, {!"an m4" }, {!"a tec-9" }, // 30, 31, 32
  359. {!"a country rifle" }, {!"a sniper rifle" }, {!"an rpg" }, // 33, 34, 35
  360. {!"a hs rpg" }, {!"a flamethrower" }, {!"a minigun" }, // 36, 37, 38
  361. {!"a satchel charge" }, {!"a detonator" }, {!"a spraycan" }, // 39, 40, 41
  362. {!"a fire extinguisher" }, {!"a camera" }, {!"nightvision" }, // 42, 43, 44
  363. {!"an infrared" }, {!"a parachute" }, {!"a fake pistol" }, // 45, 46, 47
  364. {!"" }, {!"a vehicle" }, {!"a helikill" }, // , 49, 50
  365. {!"an explosion" }, {!"" }, {!"drowning" }, // 51, , 53
  366. {!"a collision" }, {!"a splat" }, {!"something unknown"} // 54, 55, 56
  367. }
  368. ;
  369. if (0 <= weapon < sizeof(s_WeaponNamesLowercaseSingular))
  370. strcat(output, s_WeaponNamesLowercaseSingular[weapon]);
  371. else
  372. strcat(output, "something unknown");
  373. }
  374. // Vehicle name
  375. FormatSpecifier<'v'>(output[], modelid) {
  376. static const
  377. s_VehicleNames[][] = {
  378. {!"Landstalker" }, {!"Bravura" }, {!"Buffalo" }, {!"Linerunner" },
  379. {!"Perrenial" }, {!"Sentinel" }, {!"Dumper" }, {!"Firetruck" },
  380. {!"Trashmaster" }, {!"Stretch" }, {!"Manana" }, {!"Infernus" },
  381. {!"Voodoo" }, {!"Pony" }, {!"Mule" }, {!"Cheetah" },
  382. {!"Ambulance" }, {!"Leviathan" }, {!"Moonbeam" }, {!"Esperanto" },
  383. {!"Taxi" }, {!"Washington" }, {!"Bobcat" }, {!"Mr Whoopee" },
  384. {!"BF Injection" }, {!"Hunter" }, {!"Premier" }, {!"Enforcer" },
  385. {!"Securicar" }, {!"Banshee" }, {!"Predator" }, {!"Bus" },
  386. {!"Rhino" }, {!"Barracks" }, {!"Hotknife" }, {!"Trailer 1" },
  387. {!"Previon" }, {!"Coach" }, {!"Cabbie" }, {!"Stallion" },
  388. {!"Rumpo" }, {!"RC Bandit" }, {!"Romero" }, {!"Packer" },
  389. {!"Monster" }, {!"Admiral" }, {!"Squalo" }, {!"Seasparrow" },
  390. {!"Pizzaboy" }, {!"Tram" }, {!"Trailer 2" }, {!"Turismo" },
  391. {!"Speeder" }, {!"Reefer" }, {!"Tropic" }, {!"Flatbed" },
  392. {!"Yankee" }, {!"Caddy" }, {!"Solair" }, {!"Berkley's RC Van" },
  393. {!"Skimmer" }, {!"PCJ-600" }, {!"Faggio" }, {!"Freeway" },
  394. {!"RC Baron" }, {!"RC Raider" }, {!"Glendale" }, {!"Oceanic" },
  395. {!"Sanchez" }, {!"Sparrow" }, {!"Patriot" }, {!"Quad" },
  396. {!"Coastguard" }, {!"Dinghy" }, {!"Hermes" }, {!"Sabre" },
  397. {!"Rustler" }, {!"ZR-350" }, {!"Walton" }, {!"Regina" },
  398. {!"Comet" }, {!"BMX" }, {!"Burrito" }, {!"Camper" },
  399. {!"Marquis" }, {!"Baggage" }, {!"Dozer" }, {!"Maverick" },
  400. {!"News Chopper" }, {!"Rancher" }, {!"FBI Rancher" }, {!"Virgo" },
  401. {!"Greenwood" }, {!"Jetmax" }, {!"Hotring" }, {!"Sandking" },
  402. {!"Blista Compact" }, {!"Police Maverick" }, {!"Boxville" }, {!"Benson" },
  403. {!"Mesa" }, {!"RC Goblin" }, {!"Hotring Racer A" }, {!"Hotring Racer B" },
  404. {!"Bloodring Banger" }, {!"Rancher" }, {!"Super GT" }, {!"Elegant" },
  405. {!"Journey" }, {!"Bike" }, {!"Mountain Bike" }, {!"Beagle" },
  406. {!"Cropdust" }, {!"Stunt" }, {!"Tanker" }, {!"Roadtrain" },
  407. {!"Nebula" }, {!"Majestic" }, {!"Buccaneer" }, {!"Shamal" },
  408. {!"Hydra" }, {!"FCR-900" }, {!"NRG-500" }, {!"HPV1000" },
  409. {!"Cement Truck" }, {!"Tow Truck" }, {!"Fortune" }, {!"Cadrona" },
  410. {!"FBI Truck" }, {!"Willard" }, {!"Forklift" }, {!"Tractor" },
  411. {!"Combine" }, {!"Feltzer" }, {!"Remington" }, {!"Slamvan" },
  412. {!"Blade" }, {!"Freight" }, {!"Streak" }, {!"Vortex" },
  413. {!"Vincent" }, {!"Bullet" }, {!"Clover" }, {!"Sadler" },
  414. {!"Firetruck LA" }, {!"Hustler" }, {!"Intruder" }, {!"Primo" },
  415. {!"Cargobob" }, {!"Tampa" }, {!"Sunrise" }, {!"Merit" },
  416. {!"Utility" }, {!"Nevada" }, {!"Yosemite" }, {!"Windsor" },
  417. {!"Monster A" }, {!"Monster B" }, {!"Uranus" }, {!"Jester" },
  418. {!"Sultan" }, {!"Stratum" }, {!"Elegy" }, {!"Raindance" },
  419. {!"RC Tiger" }, {!"Flash" }, {!"Tahoma" }, {!"Savanna" },
  420. {!"Bandito" }, {!"Freight Flat" }, {!"Streak Carriage" }, {!"Kart" },
  421. {!"Mower" }, {!"Duneride" }, {!"Sweeper" }, {!"Broadway" },
  422. {!"Tornado" }, {!"AT-400" }, {!"DFT-30" }, {!"Huntley" },
  423. {!"Stafford" }, {!"BF-400" }, {!"Newsvan" }, {!"Tug" },
  424. {!"Trailer 3" }, {!"Emperor" }, {!"Wayfarer" }, {!"Euros" },
  425. {!"Hotdog" }, {!"Club" }, {!"Freight Carriage" }, {!"Trailer 3" },
  426. {!"Andromada" }, {!"Dodo" }, {!"RC Cam" }, {!"Launch" },
  427. {!"Police Car (LSPD)"}, {!"Police Car (SFPD)"}, {!"Police Car (LVPD)"}, {!"Police Ranger" },
  428. {!"Picador" }, {!"S.W.A.T. Van" }, {!"Alpha" }, {!"Phoenix" },
  429. {!"Glendale" }, {!"Sadler" }, {!"Luggage Trailer A"}, {!"Luggage Trailer B"},
  430. {!"Stair Trailer" }, {!"Boxville" }, {!"Farm Plow" }, {!"Utility Trailer" }
  431. }
  432. ;
  433. if (0 <= (modelid -= 400) < sizeof(s_VehicleNames))
  434. strcat(output, s_VehicleNames[modelid]);
  435. else
  436. strcat(output, "Unknown");
  437. }
  438. // Unsigned 4-byte hex
  439. FormatSpecifier<'X'>(output[], value) {
  440. format(output, sizeof(output), "%02x%06x", value >>> 24, value & 0xFFFFFF);
  441. }
  442. // Do this last so the specifiers always use the native function
  443. #if FORMAT_REPLACE_NATIVES
  444. #define format formatex
  445. #define printf printfex
  446. #endif