y_text_impl.inc 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293
  1. /*
  2. Legal:
  3. Version: MPL 1.1
  4. The contents of this file are subject to the Mozilla Public License Version
  5. 1.1 the "License"; you may not use this file except in compliance with
  6. the License. You may obtain a copy of the License at
  7. http://www.mozilla.org/MPL/
  8. Software distributed under the License is distributed on an "AS IS" basis,
  9. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10. for the specific language governing rights and limitations under the
  11. License.
  12. The Original Code is the YSI framework.
  13. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  14. Portions created by the Initial Developer are Copyright C 2011
  15. the Initial Developer. All Rights Reserved.
  16. Contributors:
  17. Y_Less
  18. koolk
  19. JoeBullet/Google63
  20. g_aSlice/Slice
  21. Misiur
  22. samphunter
  23. tianmeta
  24. maddinat0r
  25. spacemud
  26. Crayder
  27. Dayvison
  28. Ahmad45123
  29. Zeex
  30. irinel1996
  31. Yiin-
  32. Chaprnks
  33. Konstantinos
  34. Masterchen09
  35. Southclaws
  36. PatchwerkQWER
  37. m0k1
  38. paulommu
  39. udan111
  40. Thanks:
  41. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  42. ZeeX - Very productive conversations.
  43. koolk - IsPlayerinAreaEx code.
  44. TheAlpha - Danish translation.
  45. breadfish - German translation.
  46. Fireburn - Dutch translation.
  47. yom - French translation.
  48. 50p - Polish translation.
  49. Zamaroht - Spanish translation.
  50. Los - Portuguese translation.
  51. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
  52. me to strive to better.
  53. Pixels^ - Running XScripters where the idea was born.
  54. Matite - Pestering me to release it and using it.
  55. Very special thanks to:
  56. Thiadmer - PAWN, whose limits continue to amaze me!
  57. Kye/Kalcor - SA:MP.
  58. SA:MP Team past, present and future - SA:MP.
  59. Optional plugins:
  60. Gamer_Z - GPS.
  61. Incognito - Streamer.
  62. Me - sscanf2, fixes2, Whirlpool.
  63. */
  64. enum e_TEXT_DISPLAY_TYPE:
  65. {
  66. text_type_print = cellmin,
  67. text_type_3d = -4,
  68. text_type_player = -3,
  69. text_type_alert = -2,
  70. text_type_td = -1,
  71. text_type_client = 0,
  72. text_type_game1,
  73. text_type_game2,
  74. text_type_game3,
  75. text_type_game4,
  76. text_type_game5,
  77. text_type_game6
  78. }
  79. //#include "..\y_master"
  80. // End conditions for the recursive calls (strings and publics).
  81. #define _YT@LE@E%0>
  82. #define _YT@LT@E%0> ;
  83. // Recursive wanted text definition. Needs two levels of indirection to strip
  84. // the excess commas (,%0,%1).
  85. #define _YT@LO(,%0,%1) UNIQUE_FUNCTION<@yX_%0@%1@...>();public UNIQUE_FUNCTION<@yX_%0@%1@...>(){}
  86. #define _YT@LE%0[%1]%2> _YT@LO(%0,%1) _YT@LE%2>
  87. #define @yX_%0\32;%1() @yX_%0%1()
  88. // Recursive local default string definition.
  89. #define _YT@LJ(,%0,%1) #%0":"#%1"|"
  90. #define _YT@LT%0[%1]%2> _YT@LJ(%0,%1)_YT@LT%2>
  91. // Recursive macro with clever ending to load many items from 1 "loadtext"
  92. // keyword. NOW RENAMED FROM JUST "text" - WORST NAMING EVER!
  93. #define loadtext%0[%1]%2; _YT@LE,%0[%1]%2@E>static stock DEFAULT_TEXT_SET[]=_YT@LT,%0[%1]%2@E>
  94. #define Text_RegisterTag(%1) \
  95. loadtext core[%1]
  96. // Clever macros to load default values when not specified.
  97. //#define Text_Send(%0,%1) _Text_Send(%0,DEFAULT_TEXT_SET,#%1)
  98. #define DO_TEXT_SET: _:HAS_TEXT_SET:NO_TEXT_SET:
  99. #define HAS_TEXT_SET:NO_TEXT_SET:%0$%1||| DEFAULT_TEXT_SET,#%1|||
  100. #define NO_TEXT_SET: "\3",
  101. #define DEFAULT_TEXT_SET,#%1->%2||| #core#:#%1#|,#%2|||
  102. // WTF does this do? I can't even remember what slots are!
  103. //#define core":"#%1:%2"|",%3) %1":"#%2"|",%3)
  104. #define core#:#%1[%2]%9#|,%3) %1#:#%2#|,%3)
  105. // This holds the data for all entry points.
  106. enum E_TEXT_SETS
  107. {
  108. E_TEXT_SETS_NAME[28],
  109. E_TEXT_SETS_HASH,
  110. bool:E_TEXT_SETS_LOAD
  111. }
  112. enum E_TEXT_ENTRY
  113. {
  114. E_TEXT_ENTRY_NAME[MAX_INI_ENTRY_NAME],
  115. E_TEXT_ENTRY_HASHMAP[HASH_MAP_DATA],
  116. E_TEXT_ENTRY_POINTERS[YSI_MAX_LANGUAGES]
  117. }
  118. #define TEXT_MASTER YSI_g_sDistributionID
  119. #if !defined MAX_SINGLE_TEXT_ITEM
  120. #define MAX_SINGLE_TEXT_ITEM (1024)
  121. #endif
  122. static stock
  123. HashMap:YSI_g_sTextMap<>,
  124. YSI_g_sTextLength,
  125. YSI_g_sTextSets[Y_TEXT_MAX_SETS][E_TEXT_SETS],
  126. YSI_g_sTextEntries[MAX_TEXT_ENTRIES][E_TEXT_ENTRY],
  127. YSI_g_sTextStrings[MAX_TEXT_ENTRIES * _:YSI_MAX_LANGUAGES][MAX_INI_ENTRY_TEXT / 2],
  128. YSI_g_sDistributionID = -1,
  129. YSI_g_sUnusedSlot = 0,
  130. YSI_g_sRemaining = sizeof (YSI_g_sTextStrings) * sizeof (YSI_g_sTextStrings[]);
  131. P:D(Text_FindEntry(set, name, hash));
  132. #define Text_FindEntry(%0,%1,%2) HashMap_GetWithHash(YSI_g_sTextMap, (%1), (%0) + (%2))
  133. /*-------------------------------------------------------------------------*//**
  134. * <param name="len">Length to make the array slot.</param>
  135. * <remarks>
  136. * Adjusts the first index table in "YSI_g_sTextStrings" such that the next
  137. * call to "Text_GetFreeSlot" will point to a non-zero length target string.
  138. * </remarks>
  139. *//*------------------------------------------------------------------------**/
  140. static stock bool:Text_UpdateSlotSize(slot, len)
  141. {
  142. return Jagged_Resize(YSI_g_sTextStrings, { slot, len });
  143. }
  144. //
  145. // 88 ,ad8888ba, db 88888888ba, 88 888b 88 ,ad8888ba,
  146. // 88 d8"' `"8b d88b 88 `"8b 88 8888b 88 d8"' `"8b
  147. // 88 d8' `8b d8'`8b 88 `8b 88 88 `8b 88 d8'
  148. // 88 88 88 d8' `8b 88 88 88 88 `8b 88 88
  149. // 88 88 88 d8YaaaaY8b 88 88 88 88 `8b 88 88 88888
  150. // 88 Y8, ,8P d8""""""""8b 88 8P 88 88 `8b 88 Y8, 88
  151. // 88 Y8a. .a8P d8' `8b 88 .a8P 88 88 `8888 Y8a. .a88
  152. // 88888888888 `"Y8888Y"' d8' `8b 88888888Y"' 88 88 `888 `"Y88888P"
  153. //
  154. /*-------------------------------------------------------------------------*//**
  155. * <remarks>
  156. * Called after most other initialisations, so that languages can be selected.
  157. * </remarks>
  158. *//*------------------------------------------------------------------------**/
  159. public OnGameModeInit()
  160. {
  161. P:1("TextInt_OnGameModeInit called");
  162. #if defined TextInt_OnGameModeInit
  163. TextInt_OnGameModeInit();
  164. #endif
  165. if (!YSI_FILTERSCRIPT)
  166. {
  167. //Text_SpecialInit();
  168. Text_LoadAll();
  169. _Styles_SpecialInit();
  170. }
  171. P:1("TextInt_OnGameModeInit ended");
  172. return 1;
  173. }
  174. #if defined _ALS_OnGameModeInit
  175. #undef OnGameModeInit
  176. #else
  177. #define _ALS_OnGameModeInit
  178. #endif
  179. #define OnGameModeInit TextInt_OnGameModeInit
  180. #if defined TextInt_OnGameModeInit
  181. forward TextInt_OnGameModeInit();
  182. #endif
  183. public OnFilterScriptInit()
  184. {
  185. P:1("TextInt_OnFilterScriptInit called");
  186. // DO ALL (MOST) OTHER INITS FIRST. ENSURE WE COME LATER.
  187. #if defined TextInt_OnFilterScriptInit
  188. TextInt_OnFilterScriptInit();
  189. #endif
  190. //Text_SpecialInit();
  191. Text_LoadAll();
  192. _Styles_SpecialInit();
  193. P:1("TextInt_OnFilterScriptInit ended");
  194. return 1;
  195. }
  196. #if defined _ALS_OnFilterScriptInit
  197. #undef OnFilterScriptInit
  198. #else
  199. #define _ALS_OnFilterScriptInit
  200. #endif
  201. #define OnFilterScriptInit TextInt_OnFilterScriptInit
  202. #if defined TextInt_OnFilterScriptInit
  203. forward TextInt_OnFilterScriptInit();
  204. #endif
  205. /*-------------------------------------------------------------------------*//**
  206. * <transition keep="true" target="y_text_get_text : n" />
  207. * <transition keep="true" target="y_render_show : y_render_show_print" />
  208. *//*------------------------------------------------------------------------**/
  209. HOOK__ OnScriptInit()
  210. {
  211. new
  212. fakePtrs[YSI_MAX_LANGUAGES] = { -1, ... };
  213. P:1("Text_OnScriptInit called");
  214. HashMap_Init(YSI_g_sTextMap, YSI_g_sTextEntries, E_TEXT_ENTRY_HASHMAP);
  215. for (new i = 0; i != sizeof (YSI_g_sTextEntries); ++i)
  216. YSI_g_sTextEntries[i][E_TEXT_ENTRY_POINTERS] = fakePtrs;
  217. state y_render_show : y_render_show_print;
  218. }
  219. /*-------------------------------------------------------------------------*//**
  220. * <remarks>
  221. * Loops through all text definition functions in the mode (defined as:
  222. * "file@section@unique@yX_" (note that "@unique" is optional but irrelevant).
  223. * If any are found and the property "file:section" isn't defined claims
  224. * ownership of that section so that the text can be loaded in to this mode.
  225. * </remarks>
  226. *//*------------------------------------------------------------------------**/
  227. static stock Text_LoadAll()
  228. {
  229. P:4("Text_LoadAll called");
  230. //printf("Text_LoadAll: n");
  231. if (Langs_GetLanguageCount() == Language:0)
  232. {
  233. P:E("No languages found - Did you add \"Langs_AddLanguage\" to the script init (NOT \"main\")?");
  234. }
  235. new
  236. idx = 0,
  237. buffer[32],
  238. pos;
  239. while ((idx = AMX_GetPublicNamePrefix(idx, buffer, _A<@yX_>)))
  240. {
  241. strunpack(buffer, buffer);
  242. P:6("Text_LoadAll: Adding %s to list", buffer);
  243. // Get rid of the end parts, doesn't matter if "Y_TEXT_UNIQUE" is
  244. // defined or not as EVERYTHING later is dropped. Find the SECOND "@" -
  245. // there will always be at least one.
  246. if ((pos = strfind(buffer, "@", false, strfind(buffer, "@", false, 4) + 1)) != -1)
  247. buffer[pos] = '\0';
  248. Text_AddLocal(buffer[4]);
  249. }
  250. // I am trying very hard NOT to rewrite this code right now. It might not
  251. // be as "nice" as I'd like, but it is well tested and works. Rewriting it
  252. // would only introduce new bugs. I do have to update it slightly to use
  253. // y_hashmap, but that should be a small edit, not a ground-up rewrite.
  254. Text_LoadLocals();
  255. }
  256. /*-------------------------------------------------------------------------*//**
  257. *//*------------------------------------------------------------------------**/
  258. static stock Text_AddLocal(const buffer[])
  259. {
  260. P:4("Text_AddLocal called: \"%s\", %i", buffer);
  261. // Check if this already exists. If not, add it to a found free slot.
  262. new
  263. slot = -1;
  264. for (new i = 0; i != Y_TEXT_MAX_SETS; ++i)
  265. {
  266. if (YSI_g_sTextSets[i][E_TEXT_SETS_NAME][0] == '\0')
  267. slot = i;
  268. else if (!strcmp(buffer, YSI_g_sTextSets[i][E_TEXT_SETS_NAME]))
  269. return;
  270. }
  271. if (slot != -1)
  272. {
  273. YSI_g_sTextSets[slot][E_TEXT_SETS_HASH] = bernstein(buffer);
  274. YSI_g_sTextSets[slot][E_TEXT_SETS_LOAD] = true;
  275. strcpy(YSI_g_sTextSets[slot][E_TEXT_SETS_NAME], buffer, 28);
  276. }
  277. }
  278. static stock bool:Text_InsertString(target[][], const str[], slot, number)
  279. {
  280. // "target" is passed as a separate string, rather than just used as
  281. // "YSI_g_sTextStrings[slot]" because that way we do not generate the
  282. // "BOUNDS" OpCode for the code here, because the compiler can't know the
  283. // size of any arbitrary passed string. Plus, it is slightly faster. We
  284. // could not do any other slot in this manner because we resize the slot,
  285. // which moves other slots, but crucially not this one.
  286. P:4("Text_InsertString called (%s, %d, %d) (%d)", str, slot, number, YSI_g_sUnusedSlot);
  287. new
  288. newlen = strlen(str);
  289. if (number == 0)
  290. {
  291. P:5("Text_InsertString: A %d (%d)", newlen, YSI_g_sUnusedSlot);
  292. // If there is no number, there can be no multiple strings.
  293. if (!Jagged_UnsafeResizeOne(target, slot, newlen + 1))
  294. return false;
  295. P:5("Text_InsertString: A %d (%d)", newlen, YSI_g_sUnusedSlot);
  296. strcpy(target[slot], str, newlen + 1);
  297. return true;
  298. }
  299. // Prepend "\05;\number;" to the string, for later compression.
  300. new
  301. insert[3] = { '\05', 0, 0 },
  302. oldlen = strlen(target[slot]) + 1,
  303. largest;
  304. printf("%d %d %d %d %d", target[slot][0], target[slot][1], target[slot][2], target[slot][3], target[slot][4]);
  305. // "oldlen" is +1 to cater for "NULL". If the existing string doesn't
  306. // begin with `\05`, it was not an extendable string.
  307. if (oldlen == 1)
  308. largest = 0;
  309. else if (target[slot][0] == '\05')
  310. largest = target[slot][oldlen - 2];
  311. else
  312. return false;
  313. if (largest == number)
  314. {
  315. P:5("Text_InsertString: B %d %d %d (%d)", newlen, oldlen, largest, YSI_g_sUnusedSlot);
  316. if (!Jagged_UnsafeResizeOne(target, slot, oldlen + newlen + 2))
  317. return false;
  318. P:5("Text_InsertString: B %d %d %d (%d)", newlen, oldlen, largest, YSI_g_sUnusedSlot);
  319. --oldlen,
  320. insert[1] = number + 1,
  321. strcat(target[slot][oldlen], str, cellmax),
  322. strcat(target[slot][oldlen + newlen], insert, cellmax);
  323. }
  324. else if (largest < number)
  325. {
  326. P:5("Text_InsertString: C %d %d %d (%d)", newlen, oldlen, largest, YSI_g_sUnusedSlot);
  327. if (!Jagged_UnsafeResizeOne(target, slot, oldlen + newlen + 2 + 2 * (number - largest)))
  328. return false;
  329. P:5("Text_InsertString: C %d %d %d (%d)", newlen, oldlen, largest, YSI_g_sUnusedSlot);
  330. --oldlen;
  331. do
  332. {
  333. insert[1] = ++largest,
  334. strcat(target[slot][oldlen], insert, cellmax);
  335. }
  336. while (largest < number);
  337. insert[1] = number + 1,
  338. strcat(target[slot][oldlen], str, cellmax),
  339. strcat(target[slot][oldlen + newlen], insert, cellmax);
  340. }
  341. else
  342. {
  343. P:5("Text_InsertString: D %d %d %d (%d)", newlen, oldlen, largest, YSI_g_sUnusedSlot);
  344. new
  345. p0 = strfind(target[slot], (insert[1] = number, insert), false, 0),
  346. p1 = strfind(target[slot], (insert[1] = number + 1, insert), false, p0 + 1);
  347. // Don't know what to do in this case!
  348. if (p0 == -1 || p1 == -1)
  349. return false;
  350. P:5("Text_InsertString: D %d %d %d (%d)", newlen, oldlen, largest, YSI_g_sUnusedSlot);
  351. newlen += oldlen;
  352. oldlen = p1 - p0 - 2;
  353. if (oldlen)
  354. {
  355. P:5("Text_InsertString: E %d %d %d (%d)", newlen, oldlen, largest, YSI_g_sUnusedSlot);
  356. if (!Jagged_UnsafeResizeOne(target, slot, newlen - oldlen))
  357. return false;
  358. P:5("Text_InsertString: E %d %d %d (%d)", newlen, oldlen, largest, YSI_g_sUnusedSlot);
  359. strdel(target[slot][p0 + 2], 0, oldlen);
  360. // Delete any existing text in this position.
  361. strins(target[slot][p0], str, 2, cellmax);
  362. }
  363. else
  364. {
  365. P:5("Text_InsertString: F %d %d %d (%d)", newlen, oldlen, largest, YSI_g_sUnusedSlot);
  366. // Nothing to delete, just insert.
  367. if (!Jagged_UnsafeResizeOne(target, slot, newlen))
  368. return false;
  369. P:5("Text_InsertString: F %d %d %d (%d)", newlen, oldlen, largest, YSI_g_sUnusedSlot);
  370. strins(target[slot][p0], str, 2, cellmax);
  371. }
  372. }
  373. printf("%d %d %d %d %d", target[slot][0], target[slot][1], target[slot][2], target[slot][3], target[slot][4]);
  374. return true;
  375. }
  376. /*-------------------------------------------------------------------------*//**
  377. *//*------------------------------------------------------------------------**/
  378. forward Text_INILoad(Language:langIndex, file[], tag[], name[], value[]);
  379. public Text_INILoad(Language:langIndex, file[], tag[], name[], value[])
  380. {
  381. // Get the path.
  382. // Get the file.
  383. // Get the extension.
  384. P:5("Text_INILoad called: %s, %s, %s, %s", file, tag, name, value);
  385. P:5("Text_INILoad: IsPacked? %08x%08x", value[0], value[1]);
  386. new
  387. fname[32];
  388. format(fname, sizeof (fname), "%s:%s", file, tag);
  389. for (new i = 0; i != Y_TEXT_MAX_SETS; ++i)
  390. {
  391. if (!strcmp(YSI_g_sTextSets[i][E_TEXT_SETS_NAME], fname, true))
  392. {
  393. YSI_g_sTextSets[i][E_TEXT_SETS_LOAD] = false;
  394. new
  395. fhash = YSI_g_sTextSets[i][E_TEXT_SETS_HASH],
  396. len = strlen(name),
  397. slot = name[--len],
  398. number,
  399. hash = 1;
  400. P:6("Text_INILoad: Get Number");
  401. // Find trailing numbers - they are used to concatenate strings, not
  402. // differentiate. This should really be a y_ini feature (and then I
  403. // can add ordering guarantees to simplify the code (unless people
  404. // manually edit the files)).
  405. while ('0' <= slot <= '9')
  406. {
  407. // name[--len] = '\0';
  408. number += (slot - '0') * hash;
  409. hash *= 10;
  410. slot = name[--len];
  411. }
  412. #if !defined Y_TEXT_ACCEPT_BREAKING_CHANGE_1
  413. if (number)
  414. P:W("y_text line extensions must now use \"NAME-part\" not \"NAME_part\".\n\tTo acknowledge this change and disable this warning, recompile with:\n\n\t\t#define Y_TEXT_ACCEPT_BREAKING_CHANGE_1\n.");
  415. #endif
  416. // So "MY_TEXT-1" becomes "MY_TEXT", not "MY_TEXT-".
  417. if (slot == '-')
  418. name[len] = '\0';
  419. else
  420. number = 0;
  421. // `len` is now useless.
  422. hash = bernstein(name);
  423. slot = Text_FindEntry(fhash, name, hash);
  424. P:6("Text_INILoad: Check slot %d", slot);
  425. if (slot == -1)
  426. {
  427. slot = Text_GetFreeEntry();
  428. if (slot == -1 || !HashMap_AddWithHash(YSI_g_sTextMap, name, fhash + hash, slot))
  429. {
  430. P:E("Text buffer full!");
  431. return;
  432. }
  433. // Add this string to the hashmap. We identify it by name, but
  434. // the hash is a combination of the string's hash and the search
  435. // space's hash (through addition).
  436. }
  437. P:6("Text_INILoad: Check Collision");
  438. new
  439. ss = YSI_g_sTextEntries[slot][E_TEXT_ENTRY_POINTERS][langIndex];
  440. if (ss == -1)
  441. {
  442. ss = Text_GetFreeSlot();
  443. if (ss == -1)
  444. {
  445. P:E("Text buffer full!");
  446. return;
  447. }
  448. YSI_g_sTextEntries[slot][E_TEXT_ENTRY_POINTERS][langIndex] = ss;
  449. }
  450. Text_InsertString(YSI_g_sTextStrings, value, ss, number);
  451. break;
  452. }
  453. }
  454. }
  455. /*-------------------------------------------------------------------------*//**
  456. *//*------------------------------------------------------------------------**/
  457. static stock Text_CompressLoaded(strings[][])
  458. {
  459. Jagged_PrintHeader(YSI_g_sTextStrings);
  460. P:4("Text_CompressLoaded called");
  461. // Remove all the "\05;\number;"s from the strings, now they are all loaded.
  462. for (new i = 0, pos; i != YSI_g_sUnusedSlot; ++i)
  463. {
  464. while ((pos = strfind(strings[i], "\05;")) != -1)
  465. strdel(strings[i], pos, pos + 2);
  466. printf(" \"%s\"", strings[i]);
  467. }
  468. P:4("Text_CompressLoaded ended");
  469. }
  470. static stock Text_LoadLocals()
  471. {
  472. P:4("Text_LoadLocals called");
  473. // This is the interesting part. Load all the sets which have a name not
  474. // '\1' and an index of -1.
  475. new
  476. loadTextFile[64],
  477. langs[YSI_MAX_STRING];
  478. langs = Langs_GetLanguageCodes();
  479. for (new i = 0; i != Y_TEXT_MAX_SETS; ++i)
  480. {
  481. P:6("Text_LoadLocals: i = %d, text = %s, %d, %d", i, YSI_g_sTextSets[i][E_TEXT_SETS_NAME], YSI_g_sTextSets[i][E_TEXT_SETS_LOAD], YSI_g_sTextSets[i][E_TEXT_SETS_HASH]);
  482. if (YSI_g_sTextSets[i][E_TEXT_SETS_LOAD])
  483. {
  484. // This is in a file that has apparently not been loaded yet.
  485. new
  486. Language:langIndex = Language:0,
  487. pos = -1,
  488. offset = strfind(YSI_g_sTextSets[i][E_TEXT_SETS_NAME], "@");
  489. // Loop through all the language extensions.
  490. while ((pos = strfind(langs, "|", false, pos + 1)) != -1)
  491. {
  492. format(loadTextFile, sizeof (loadTextFile), "YSI/text/%.*s.%.2s", offset, YSI_g_sTextSets[i][E_TEXT_SETS_NAME], langs[pos - 2]);
  493. P:4("Text_LoadLocals: %s", loadTextFile);
  494. INI_ParseFile(loadTextFile, "Text_INILoad",
  495. .bExtra = true,
  496. .extra = _:langIndex,
  497. .bPassTag = true,
  498. .bPassFile = true);
  499. ++langIndex;
  500. // We can add code here to load the data from y_styles.
  501. }
  502. //format(YSI_g_sCurLoadTextFile, sizeof (YSI_g_sCurLoadTextFile), "%.*s", offset, YSI_g_sTextSets[i][E_TEXT_SETS_NAME]);
  503. P:6("Text_LoadLocals: Loading style %s (%d)", loadTextFile, offset);
  504. //_Styles_ParseOne(YSI_g_sCurLoadTextFile);
  505. //YSI_g_sTextSets[i][E_TEXT_SETS_NAME][offset] = ':';
  506. }
  507. }
  508. Text_CompressLoaded(YSI_g_sTextStrings);
  509. }
  510. /*-------------------------------------------------------------------------*//**
  511. * <param name="file">File to check.</param>
  512. * <param name="str">Section name to check.</param>
  513. * <returns>
  514. * Does this script own the named section in the current file?
  515. * </returns>
  516. * <remarks>
  517. * Sets "YSI_g_sReturnText" for use in "_Text_LookupName" too.
  518. * </remarks>
  519. *//*------------------------------------------------------------------------**/
  520. stock _Text_CheckOwnership(file[], str[])
  521. {
  522. // TODO...
  523. // P:4("_Text_CheckOwnership called: %s, %s", file, str);
  524. // format(YSI_g_sReturnText, sizeof (YSI_g_sReturnText), "%s:%s", file, str);
  525. return 1;
  526. }
  527. /*-------------------------------------------------------------------------*//**
  528. * <param name="name">Name of the string to look up.</param>
  529. * <returns>
  530. * Slot storing the pointers for the named section.
  531. * </returns>
  532. * <remarks>
  533. * Assumes this script owns the section based on previously having called
  534. * "_Text_CheckOwnership". This is assumed as both functions are called from
  535. * only y_styles (hence the private naming convention). Note that the string
  536. * "YSI_g_sReturnText" is set in "_Text_CheckOwnership" because we make
  537. * guarantees about the order in which these functions are called.
  538. * </remarks>
  539. *//*------------------------------------------------------------------------**/
  540. stock _Text_LookupName(name[])
  541. {
  542. // TODO...
  543. // P:4("_Text_LookupName called: %s, %s", name, YSI_g_sReturnText);
  544. // new
  545. // sh = bernstein(YSI_g_sReturnText);
  546. // for (new i = 0; i != Y_TEXT_MAX_SETS; ++i)
  547. // {
  548. // if (sh == YSI_g_sTextSets[i][E_TEXT_SETS_HASH] && !strcmp(YSI_g_sReturnText, YSI_g_sTextSets[i][E_TEXT_SETS_NAME]))
  549. // {
  550. // // Got the start slot.
  551. // return Text_FindEntry(YSI_g_sTextSets[i][E_TEXT_SETS_HASH], name, bernstein(name));
  552. // }
  553. // }
  554. return -1;
  555. }
  556. /*-------------------------------------------------------------------------*//**
  557. * <transition keep="true" target="y_text_get_text : y" />
  558. * <transition keep="true" target="y_text_get_text : n" />
  559. *//*------------------------------------------------------------------------**/
  560. stock _Text_GetPointer(master, id)
  561. {
  562. #pragma unused master
  563. #emit CONST.alt YSI_g_sTextStrings
  564. #emit LOAD.S.pri id
  565. #emit IDXADDR
  566. #emit MOVE.alt
  567. #emit LOAD.i
  568. #emit ADD
  569. #emit RETN
  570. return 0;
  571. }
  572. /*-------------------------------------------------------------------------*//**
  573. * <param name="search">Search blocks to look in.</param>
  574. * <param name="nh">Hash of the string name to find.</param>
  575. * <param name="l">Language in which to get the text.</param>
  576. * <remarks>
  577. * Gets a string in a language from a hash and a text set.
  578. * </remarks>
  579. *//*------------------------------------------------------------------------**/
  580. static stock Text_GetStandard(search[], nh, Language:l)
  581. {
  582. if (getproperty(8, YSIM_TXTFND) != -1)
  583. {
  584. return;
  585. }
  586. if (getproperty(7, search) != TEXT_MASTER)
  587. {
  588. return;
  589. }
  590. P:4("Text_GetStandard called");
  591. new
  592. sh = bernstein(search);//,
  593. //fail[MAX_INI_ENTRY_TEXT] = "String not found";
  594. for (new i = 0; i != Y_TEXT_MAX_SETS; ++i)
  595. {
  596. P:7("Text_GetStandard: %d, %d, %d", i, sh, YSI_g_sTextSets[i][E_TEXT_SETS_HASH]);
  597. if (sh == YSI_g_sTextSets[i][E_TEXT_SETS_HASH] && !strcmp(search, YSI_g_sTextSets[i][E_TEXT_SETS_NAME]))
  598. {
  599. // Got the start slot.
  600. new
  601. slot = |(YSI_g_sTextSets[i][E_TEXT_SETS_INDEX], nh);
  602. P:7("Text_GetStandard: found %d, %d, %d", YSI_g_sTextSets[i][E_TEXT_SETS_INDEX], nh, slot);
  603. if (slot != -1)
  604. {
  605. new
  606. pointer = YSI_g_sTextEntries[slot][E_TEXT_ENTRY_POINTERS][l];
  607. P:5("Text_GetStandard: %d, %d, %d, %d", slot, pointer, getproperty(8, YSIM_TXTIND), Master_ID());
  608. if (pointer != -1)
  609. {
  610. //P:5("Text_GetStandard: %08x%08x%08x%08x%08x", YSI_g_sTextStrings[pointer][0], YSI_g_sTextStrings[pointer][1], YSI_g_sTextStrings[pointer][2], YSI_g_sTextStrings[pointer][3], YSI_g_sTextStrings[pointer][4]);
  611. //setproperty(9, "", YSIM_STRING, YSI_g_sTextStrings[pointer]);
  612. setproperty(8, YSIM_TXTFND, Master_ID());
  613. // I reckon this is the only time these functions have ever
  614. // been used together directly...
  615. P:5("Text_GetStandard: %s", YSI_g_sTextStrings[pointer]);
  616. //SetPVarString(getproperty(8, YSIM_TXTIND), "YSI_g_sString", YSI_g_sTextStrings[pointer]);
  617. //printf("%d %d %d %d %d = %d", YSI_g_sTextStrings[pointer][0], YSI_g_sTextStrings[pointer][1], YSI_g_sTextStrings[pointer][2], YSI_g_sTextStrings[pointer][3], YSI_g_sTextStrings[pointer][4], strlen(YSI_g_sTextStrings[pointer]));
  618. // TODO: Fix this for the new parameter list.
  619. new
  620. style[E_STYLE_DATA],
  621. label[E_3D_DATA];
  622. Styles_GetData(slot, style, label);
  623. // CallRemoteFunction("Text_ReturnTheText", "iiaiaiai", TEXT_MASTER, pointer, YSI_g_sTextStrings[pointer], MAX_SINGLE_TEXT_ITEM /*sizeof (YSI_g_sTextStrings[])*/, style, _:E_STYLE_DATA, label, _:E_3D_DATA);
  624. //format(fail, sizeof (fail), "%s", YSI_g_sTextStrings[pointer]);
  625. //printf("fail: %s", fail);
  626. //break;
  627. return;
  628. }
  629. }
  630. break;
  631. }
  632. }
  633. //static const
  634. // fail[MAX_INI_ENTRY_TEXT] = "\1";
  635. //setproperty(9, "", YSIM_STRING, "\1");
  636. //return fail;
  637. }
  638. /*-------------------------------------------------------------------------*//**
  639. * <param name="search">A "|" separated list of files and sections to search in.</param>
  640. * <param name="name">The text entry to look for.</param>
  641. * <param name="l">The language to get.</param>
  642. * <returns>
  643. * The specified string in the specified language.
  644. * </returns>
  645. * <remarks>
  646. * Uses "YSI_g_sReturnText" instead of a normal return because this may call
  647. * remote scripts which will need to return a standardised string as an array
  648. * to preserve all the non-standard characters (passing data as a string using
  649. * __CallRemoteFunction packs the string, which we don't want).
  650. * </remarks>
  651. * <transition keep="true" target="y_text_get_text : y" />
  652. * <transition keep="true" target="y_text_get_text : n" />
  653. * <transition keep="true" target="y_text_get_text : n" />
  654. *//*------------------------------------------------------------------------**/
  655. // Get the loaded standardised version of this string.
  656. //forward Text_GetStandard(start[] name[], Language:l);
  657. static stock Text_GetText(search[], name[], Language:l)
  658. {
  659. /*new
  660. start = Text_GetProvider(search),
  661. slot = Text_FindEntry(*/
  662. // TODO: Parse multiple search strings.
  663. //search[strlen(search) - 1] = '\0';
  664. //new loops = 0;
  665. //static
  666. // ret[MAX_INI_ENTRY_TEXT];
  667. new
  668. pos,
  669. lst;
  670. state y_text_get_text:y;
  671. while (search[lst])
  672. {
  673. pos = strfind(search[lst], "|");
  674. P:7("Text_GetText: loop1 %s %d %d %s %d", search, lst, pos, search[lst], search[lst + pos + 1]);
  675. search[lst + pos] = '\0';
  676. if (existproperty(7, search[lst]))
  677. {
  678. // Only call other scripts if ANYONE owns this search.
  679. CallRemoteFunction("Text_GetStandard", "sii", search[lst], bernstein(name), _:l);
  680. //P:7("Text_GetText: loop2 %s %d %d %s %d %d", search, lst, pos, search[lst], search[pos + 1], ret[0]);
  681. search[lst + pos] = '|';
  682. //if (ret[0] != '\1')
  683. if (getproperty(8, YSIM_TXTFND) != -1)
  684. {
  685. //getproperty(9, "", YSIM_STRING, ret);
  686. //strunpack(ret, ret)
  687. //GetPVarString(index, "YSI_g_sString", ret, MAX_INI_ENTRY_TEXT);
  688. state y_text_get_text:n;
  689. return;// ret;
  690. }
  691. }
  692. lst += pos + 1;
  693. //if (++loops == 10) break;
  694. //break;
  695. }
  696. //if (ret[0] == '\1')
  697. {
  698. // Need better error reporting here.
  699. YSI_g_sReturnText = "Text Not Found";
  700. }
  701. state y_text_get_text:n;
  702. return; // ret;
  703. }
  704. /*-------------------------------------------------------------------------*//**
  705. *//*------------------------------------------------------------------------**/
  706. // To be public.
  707. //stock Text_GetProvider(search[])
  708. //{
  709. //}
  710. //static stock Text_FindEntry(start, hash)
  711. //{
  712. // P:4("Text_FindEntry called: %i, %i", start, hash);
  713. // while (start != -1)
  714. // {
  715. // new
  716. // cmp = YSI_g_sTextEntries[start][E_TEXT_ENTRY_HASH] - hash;
  717. // if (cmp < 0)
  718. // {
  719. // start = YSI_g_sTextEntries[start][E_TEXT_ENTRY_RIGHT];
  720. // }
  721. // else if (cmp > 0)
  722. // {
  723. // start = YSI_g_sTextEntries[start][E_TEXT_ENTRY_LEFT];
  724. // }
  725. // else
  726. // {
  727. // return start;
  728. // }
  729. // }
  730. // return -1;
  731. //}
  732. /*-------------------------------------------------------------------------*//**
  733. *//*------------------------------------------------------------------------**/
  734. static stock Text_AddEntry(set, name[], value[], hash)
  735. {
  736. P:4("Text_AddEntry called: %i, \"%s\", \"%s\", %i", set, name, value, hash);
  737. new
  738. entry = Text_GetFreeEntry(),
  739. slot = Text_GetFreeSlot();
  740. if (entry == -1 || slot == -1)
  741. {
  742. P:E("Text buffer full!");
  743. return;
  744. }
  745. // Save the string.
  746. //strpack(YSI_g_sTextStrings[slot], value, MAX_INI_ENTRY_TEXT * 4);
  747. Text_UpdateSlotSize(Format_Standardise(value, YSI_g_sTextStrings[slot], YSI_g_sRemaining));
  748. //strpack(YSI_g_sTextStrings[slot], value, MAX_INI_ENTRY_TEXT * 4);
  749. //strcpy(YSI_g_sTextStrings[slot], value, MAX_INI_ENTRY_TEXT * 4);
  750. // Save the identifier.
  751. P:5("Text_AddEntry: slot = %d %d,%d,%d,%d,%d", slot, YSI_g_sTextStrings[slot][0], YSI_g_sTextStrings[slot][1], YSI_g_sTextStrings[slot][2], YSI_g_sTextStrings[slot][3], YSI_g_sTextStrings[slot][4]);
  752. strpack(YSI_g_sTextEntries[entry][E_TEXT_ENTRY_NAME], name, MAX_INI_ENTRY_NAME * 4);
  753. //strcpy(YSI_g_sTextEntries[entry][E_TEXT_ENTRY_NAME], name, MAX_INI_ENTRY_NAME * 4);
  754. YSI_g_sTextEntries[entry][E_TEXT_ENTRY_HASH] = hash;
  755. YSI_g_sTextEntries[entry][E_TEXT_ENTRY_POINTERS][langIndex] = slot;
  756. // Set the default style.
  757. _Style_Init(entry);
  758. // Insert the identifier.
  759. new
  760. idx = YSI_g_sTextSets[set][E_TEXT_SETS_INDEX],
  761. next;
  762. if (idx == -1)
  763. {
  764. YSI_g_sTextSets[set][E_TEXT_SETS_INDEX] = entry;
  765. }
  766. else
  767. {
  768. for ( ; ; )
  769. {
  770. new
  771. cmp = YSI_g_sTextEntries[idx][E_TEXT_ENTRY_HASH] - hash;
  772. if (cmp < 0)
  773. {
  774. next = YSI_g_sTextEntries[idx][E_TEXT_ENTRY_RIGHT];
  775. if (next == -1)
  776. {
  777. YSI_g_sTextEntries[idx][E_TEXT_ENTRY_RIGHT] = entry;
  778. return;
  779. }
  780. }
  781. else if (cmp > 0)
  782. {
  783. next = YSI_g_sTextEntries[idx][E_TEXT_ENTRY_LEFT];
  784. if (next == -1)
  785. {
  786. YSI_g_sTextEntries[idx][E_TEXT_ENTRY_LEFT] = entry;
  787. return;
  788. }
  789. }
  790. idx = next;
  791. }
  792. }
  793. }
  794. /*-------------------------------------------------------------------------*//**
  795. *//*------------------------------------------------------------------------**/
  796. static stock Text_GetFreeSlot()
  797. {
  798. if (YSI_g_sUnusedSlot == sizeof (YSI_g_sTextStrings))
  799. return -1;
  800. return YSI_g_sUnusedSlot++;
  801. }
  802. /*-------------------------------------------------------------------------*//**
  803. * <remarks>
  804. * Prints all the strings loaded by the system, including their data offsets
  805. * and storage array lengths (cunning tightly packed array).
  806. * </remarks>
  807. *//*------------------------------------------------------------------------**/
  808. stock Text_DebugAllText()
  809. {
  810. for (new i = 0; i != YSI_g_sUnusedSlot; ++i)
  811. {
  812. new
  813. next,
  814. cur;
  815. printf("%d %d %d %d", i, cur, next, YSI_g_sUnusedSlot);
  816. Text_GetCurNextOffset(i, cur, next);
  817. printf("Offset: %d, Length: %d", cur, (next - cur) / 4);
  818. printf("Text: %s", YSI_g_sTextStrings[i]);
  819. }
  820. }
  821. static stock Text_GetCurNextOffset(i, &cur, &next)
  822. {
  823. // Get the address of the previous slot's pointer.
  824. #emit LOAD.S.pri i
  825. #emit SHL.C.pri 2
  826. #emit CONST.alt YSI_g_sTextStrings
  827. #emit ADD
  828. // Get the pointer to the start of the data.
  829. #emit LOAD.I
  830. #emit SREF.S.pri cur
  831. // Get the address of the previous slot's pointer.
  832. #emit LOAD.S.pri i
  833. #emit ADD.C 0x1
  834. #emit SHL.C.pri 2
  835. #emit CONST.alt YSI_g_sTextStrings
  836. #emit ADD
  837. // Get the pointer to the start of the data.
  838. #emit LOAD.I
  839. #emit SREF.S.pri next
  840. }
  841. /*-------------------------------------------------------------------------*//**
  842. *//*------------------------------------------------------------------------**/
  843. static stock Text_GetFreeEntry()
  844. {
  845. P:4("Text_GetFreeEntry called");
  846. new
  847. ret = HashMap_GetUnused(YSI_g_sTextMap);
  848. if (ret == -1)
  849. return -1;
  850. // Reset text storage pointers.
  851. for (new Language:i = Language:0; i != YSI_MAX_LANGUAGES; ++i)
  852. {
  853. YSI_g_sTextEntries[ret][E_TEXT_ENTRY_POINTERS][i] = -1;
  854. }
  855. return ret;
  856. }
  857. /*-------------------------------------------------------------------------*//**
  858. * <remarks>
  859. * Checks if the string we are trying to display is owned by the local script,
  860. * and if so just use that pointer and text directly.
  861. * </remarks>
  862. *//*------------------------------------------------------------------------**/
  863. static stock Text_IsLocalOwner(search[], const ident[], &len)
  864. {
  865. // First, find out if this script owns it's own text for speed reasons, and
  866. // if it does, save the handle to the text set.
  867. new
  868. pos,
  869. lst,
  870. hash;
  871. while (search[lst])
  872. {
  873. pos = strfind(search[lst], "|");
  874. search[lst + pos] = '\0';
  875. hash = bernstein(search[lst]);
  876. for (new i = 0; i != Y_TEXT_MAX_SETS; ++i)
  877. {
  878. if (hash == YSI_g_sTextSets[i][E_TEXT_SETS_HASH] && !strcmp(search[lst], YSI_g_sTextSets[i][E_TEXT_SETS_NAME]))
  879. {
  880. new
  881. ret = Text_FindEntry(YSI_g_sTextSets[i][E_TEXT_SETS_HASH], ident, bernstein(ident));
  882. if (ret != -1)
  883. {
  884. search[lst + pos] = '|';
  885. new
  886. style[E_STYLE_DATA],
  887. label[E_3D_DATA];
  888. Styles_GetData(ret, style, label);
  889. len = _Format_SetStyleData(TEXT_MASTER, -1, style, label) - 1;
  890. return ret;
  891. }
  892. }
  893. }
  894. search[lst + pos] = '|';
  895. lst += pos + 1;
  896. }
  897. return -1;
  898. }
  899. /*-------------------------------------------------------------------------*//**
  900. * <param name="players">A representation of players to show to.</param>
  901. * <param name="search">Text sets to look in for this string.</param>
  902. * <param name="ident">The name of the string to look for (or the string itself).</param>
  903. * <param name="">All the parameters to pass to the string.</param>
  904. * <remarks>
  905. * Main entry point for showing any sort of code to anyone.
  906. *
  907. * TODO: Change the code to push the parameters to Format_Render only once and
  908. * reuse the resulting stack.
  909. * </remarks>
  910. *//*------------------------------------------------------------------------**/
  911. static stock
  912. YSI_g_sLangBuffer[YSI_MAX_LANGUAGES][MAX_SINGLE_TEXT_ITEM];
  913. stock _Text_Send(@PlayerSet:players, search[], ident[], GLOBAL_TAG_TYPES:...)
  914. {
  915. YSI_g_sTextLength = MAX_SINGLE_TEXT_ITEM - 1;
  916. new
  917. bool:wasOnce[YSI_MAX_LANGUAGES] = {false, ...},
  918. maxlen,
  919. source,
  920. ret;
  921. switch (search[0])
  922. {
  923. case '\1':
  924. {
  925. // Null - error.
  926. P:E("Text_Send called with NULL");
  927. return -1;
  928. }
  929. case '\2':
  930. source = cellmin;
  931. default:
  932. source = Text_IsLocalOwner(search, ident, maxlen);
  933. }
  934. // Loop through all the players passed to the function (however they were
  935. // passed to the function). This uses "@PlayerArray" instead of
  936. // "@PlayerVar" so that we can optimised for multiple languages. Now it
  937. // uses "@PlayerSet" instead because that's the new one designed for
  938. // situations like this in which we want direct access to the variable.
  939. FOREACH__ (new playerid : PS(players))
  940. //new playerid = 0;
  941. {
  942. new
  943. Language:lang = Langs_GetPlayerLanguage(playerid);
  944. if (lang == NO_LANGUAGE)
  945. {
  946. lang = Language:0;
  947. }
  948. if (wasOnce[lang])
  949. {
  950. // Optimisation for sending messages to multiple players.
  951. Format_JustShow(playerid, YSI_g_sLangBuffer[lang]);
  952. continue;
  953. }
  954. switch (source)
  955. {
  956. case cellmin:
  957. {
  958. ret = Format_Render(playerid, lang, YSI_g_sLangBuffer[lang], YSI_g_sTextLength, 0, 0, ident, ___(3));
  959. }
  960. default:
  961. {
  962. ret = YSI_g_sTextEntries[source][E_TEXT_ENTRY_POINTERS][lang];
  963. if (ret == -1)
  964. {
  965. ret = 0;
  966. YSI_g_sLangBuffer[lang] = "Language text not found";
  967. }
  968. else
  969. {
  970. ret = Format_Render(playerid, lang, YSI_g_sLangBuffer[lang], maxlen, 0, 0, YSI_g_sTextStrings[ret], ___(3));
  971. }
  972. }
  973. }
  974. wasOnce[lang] = !ret;
  975. }
  976. Format_SetListSeparator(", ");
  977. return Text_GetLastID();
  978. }
  979. /*-------------------------------------------------------------------------*//**
  980. * <param name="search">Text sets to look in for this string.</param>
  981. * <param name="ident">The name of the string to look for (or the string itself).</param>
  982. * <param name="">All the parameters to pass to the string.</param>
  983. * <remarks>
  984. * This function renders some text to an array, but doesn't display it.
  985. * </remarks>
  986. *//*------------------------------------------------------------------------**/
  987. stock Text_GetPreRender(Language:lang)
  988. {
  989. return YSI_g_sLangBuffer[lang];
  990. }
  991. stock _Text_Render(playerid, search[], ident[], GLOBAL_TAG_TYPES:...)
  992. {
  993. YSI_g_sTextLength = MAX_SINGLE_TEXT_ITEM - 1;
  994. new
  995. maxlen,
  996. source,
  997. ret;
  998. switch (search[0])
  999. {
  1000. case '\1':
  1001. {
  1002. // Null - error.
  1003. P:E("Text_Send called with NULL");
  1004. return;
  1005. }
  1006. case '\2':
  1007. source = cellmin;
  1008. default:
  1009. source = Text_IsLocalOwner(search, ident, maxlen);
  1010. }
  1011. for (new Language:lang; lang != YSI_MAX_LANGUAGES; ++lang) if (Langs_IsValid(lang))
  1012. {
  1013. switch (source)
  1014. {
  1015. case cellmin:
  1016. {
  1017. ret = Format_Render(playerid, lang, YSI_g_sLangBuffer[lang], YSI_g_sTextLength, 0, e_FORMAT_FLAGS_NONE, ident, ___(3));
  1018. }
  1019. default:
  1020. {
  1021. ret = YSI_g_sTextEntries[source][E_TEXT_ENTRY_POINTERS][lang];
  1022. if (ret == -1)
  1023. {
  1024. ret = 0;
  1025. YSI_g_sLangBuffer[lang] = "Language text not found";
  1026. }
  1027. else
  1028. {
  1029. ret = Format_Render(playerid, lang, YSI_g_sLangBuffer[lang], maxlen, 0, e_FORMAT_FLAGS_NONE, YSI_g_sTextStrings[ret], ___(3));
  1030. }
  1031. }
  1032. }
  1033. }
  1034. Format_SetListSeparator(", ");
  1035. }
  1036. static stock
  1037. YSI_g_sCaptionText[YSI_MAX_LANGUAGES][64],
  1038. YSI_g_sButton1Text[YSI_MAX_LANGUAGES][32],
  1039. YSI_g_sButton2Text[YSI_MAX_LANGUAGES][32];
  1040. /*-------------------------------------------------------------------------*//**
  1041. *//*------------------------------------------------------------------------**/
  1042. stock _Text_DialogBox(@PlayerSet:players, style, callback:callback, csearch[], cident[], isearch[], iident[], b1search[], b1ident[], b2search[], b2ident[], GLOBAL_TAG_TYPES:...)
  1043. {
  1044. // This is what the "@PlayerArray" macro expands to, but I needed more
  1045. // control to allow me to return a value. The fact that I can't ATM is a
  1046. // serious problem, but not serious enough to warrant fixing.
  1047. new
  1048. bool:wasOnce[YSI_MAX_LANGUAGES] = {false, ...},
  1049. bool:gotExtras[YSI_MAX_LANGUAGES] = {false, ...},
  1050. dialog = Dialog_ObtainID(),
  1051. maxlen,
  1052. source,
  1053. ret;
  1054. if (isearch[0] == '\1' || csearch[0] == '\1' || b1search[0] == '\1' || (b2ident[0] && b2search[0] == '\1')
  1055. {
  1056. // Null - error.
  1057. P:E("Text_Send called with NULL");
  1058. return -1;
  1059. }
  1060. if (isearch[0] == '\2')
  1061. source = cellmin;
  1062. else
  1063. source = Text_IsLocalOwner(isearch, iident, maxlen);
  1064. YSI_g_sTextLength = MAX_SINGLE_TEXT_ITEM - 1;
  1065. // Loop through all the players passed to the function (however they were
  1066. // passed to the function). This uses "@PlayerArray" instead of
  1067. // "@PlayerVar" so that we can optimised for multiple languages.
  1068. FOREACH__ (new playerid : PS(players))
  1069. {
  1070. new
  1071. Language:lang = Langs_GetPlayerLanguage(playerid);
  1072. if (lang == NO_LANGUAGE)
  1073. {
  1074. lang = Language:0;
  1075. }
  1076. // Only ever do this part once per language.
  1077. if (!gotExtras[lang])
  1078. {
  1079. // Get the text for the caption in this language.
  1080. switch (csearch[0])
  1081. {
  1082. case '\1': P:E("Text_Send called with NULL");
  1083. case '\2': strcpy(YSI_g_sCaptionText[lang], cident, sizeof (YSI_g_sCaptionText[]));
  1084. default:
  1085. {
  1086. Text_GetText(csearch, cident, lang);
  1087. strcpy(YSI_g_sCaptionText[lang], YSI_g_sReturnText, sizeof (YSI_g_sCaptionText[]));
  1088. }
  1089. }
  1090. // Get the text for button 1 in this language.
  1091. switch (b1search[0])
  1092. {
  1093. case '\1': P:E("Text_Send called with NULL");
  1094. case '\2': strcpy(YSI_g_sButton1Text[lang], b1ident, sizeof (YSI_g_sButton1Text[]));
  1095. default:
  1096. {
  1097. Text_GetText(b1search, b1ident, lang);
  1098. strcpy(YSI_g_sButton1Text[lang], YSI_g_sReturnText, sizeof (YSI_g_sButton1Text[]));
  1099. }
  1100. }
  1101. // Get the text for button 1 in this language.
  1102. if (b2ident[0])
  1103. {
  1104. switch (b2search[0])
  1105. {
  1106. case '\1': P:E("Text_Send called with NULL");
  1107. case '\2': strcpy(YSI_g_sButton2Text[lang], b2ident, sizeof (YSI_g_sButton2Text[]));
  1108. default:
  1109. {
  1110. Text_GetText(b2search, b2ident, lang);
  1111. strcpy(YSI_g_sButton2Text[lang], YSI_g_sReturnText, sizeof (YSI_g_sButton2Text[]));
  1112. }
  1113. }
  1114. }
  1115. else
  1116. YSI_g_sButton2Text[lang][0] = '\0';
  1117. gotExtras[lang] = true;
  1118. }
  1119. if (!wasOnce[lang])
  1120. {
  1121. switch (source)
  1122. {
  1123. case cellmin:
  1124. {
  1125. _Text_SetDialogMode();
  1126. ret = Format_Render(playerid, lang, YSI_g_sLangBuffer[lang], YSI_g_sTextLength, 0, e_FORMAT_FLAGS_NONE, iident, ___(11));
  1127. }
  1128. default:
  1129. {
  1130. ret = YSI_g_sTextEntries[source][E_TEXT_ENTRY_POINTERS][lang];
  1131. if (ret == -1)
  1132. {
  1133. ret = 0;
  1134. YSI_g_sLangBuffer[lang] = "Language text not found";
  1135. }
  1136. else
  1137. {
  1138. _Text_SetDialogMode();
  1139. ret = Format_Render(playerid, lang, YSI_g_sLangBuffer[lang], maxlen, 0, e_FORMAT_FLAGS_NONE, YSI_g_sTextStrings[ret], ___(11));
  1140. }
  1141. }
  1142. }
  1143. wasOnce[lang] = !ret;
  1144. }
  1145. // Display the message.
  1146. Dialog_Show(playerid, style, YSI_g_sCaptionText[lang], YSI_g_sLangBuffer[lang], YSI_g_sButton1Text[lang], YSI_g_sButton2Text[lang], dialog);
  1147. }
  1148. // Sort out the callbacks etc for the dialog return.
  1149. new
  1150. cd[E_CALLBACK_DATA];
  1151. Callback_Get(callback, cd, _F<iiiis>);
  1152. Dialog_SetCallbackData(dialog, cd); //AMX_GetPublicPointer(callback));
  1153. // Automatically free the dialog when we're done.
  1154. Dialog_Garbage(dialog);
  1155. Format_SetListSeparator(", ");
  1156. return dialog;
  1157. }
  1158. stock _Text_Format(dest[], size, Language:lang, e_STYLE_TYPE:style, search[], ident[], ...)
  1159. {
  1160. P:4("Text_Format: %s %d %d %d %s %s", dest, size, _:lang, style, search, ident);
  1161. Text_GetText(search, ident, lang);
  1162. new
  1163. styleData[E_STYLE_DATA],
  1164. label[E_3D_DATA],
  1165. bool:foundStyle = false
  1166. ;
  1167. if (style != e_STYLE_TYPE_DIALOG)
  1168. {
  1169. new hash = bernstein(ident);
  1170. for (new i = 0; i != Y_TEXT_MAX_SETS; ++i)
  1171. {
  1172. new
  1173. ret = Text_FindEntry(YSI_g_sTextSets[i][E_TEXT_SETS_HASH], ident, hash)
  1174. ;
  1175. P:7("Text_Format: Textset %d entry %d", i, ret);
  1176. if (ret != -1)
  1177. {
  1178. Styles_GetData(ret, styleData, label);
  1179. if ((styleData[E_STYLE_DATA_TYPE] & e_STYLE_TYPE_MASK) != style)
  1180. {
  1181. P:7("Text_Format: No matching style data. Types differ 0x%08x == 0x%08x", (styleData[E_STYLE_DATA_TYPE] & e_STYLE_TYPE_MASK), style);
  1182. break;
  1183. }
  1184. foundStyle = true;
  1185. break;
  1186. }
  1187. }
  1188. if (!foundStyle)
  1189. {
  1190. P:7("Text_Format: Style not found, using default");
  1191. }
  1192. _Format_SetStyleData(TEXT_MASTER, 0, styleData, label);
  1193. }
  1194. else
  1195. {
  1196. P:7("Text_Format: Toggling dialog mode");
  1197. _Text_SetDialogMode();
  1198. }
  1199. Format_Render(INVALID_PLAYER_ID, lang, dest, size, 0, e_FORMAT_FLAGS_NONE, YSI_g_sReturnText, ___(6));
  1200. return 1;
  1201. }