Str.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "precompiled.h"
  21. #pragma hdrstop
  22. #ifdef USE_STRING_DATA_ALLOCATOR
  23. static idDynamicBlockAlloc<char, 1<<18, 128, TAG_STRING> stringDataAllocator;
  24. #endif
  25. idVec4 g_color_table[16] =
  26. {
  27. idVec4(0.0f, 0.0f, 0.0f, 1.0f),
  28. idVec4(1.0f, 0.0f, 0.0f, 1.0f), // S_COLOR_RED
  29. idVec4(0.0f, 1.0f, 0.0f, 1.0f), // S_COLOR_GREEN
  30. idVec4(1.0f, 1.0f, 0.0f, 1.0f), // S_COLOR_YELLOW
  31. idVec4(0.0f, 0.0f, 1.0f, 1.0f), // S_COLOR_BLUE
  32. idVec4(0.0f, 1.0f, 1.0f, 1.0f), // S_COLOR_CYAN
  33. idVec4(1.0f, 0.5f, 0.0f, 1.0f), // S_COLOR_ORANGE
  34. idVec4(1.0f, 1.0f, 1.0f, 1.0f), // S_COLOR_WHITE
  35. idVec4(0.5f, 0.5f, 0.5f, 1.0f), // S_COLOR_GRAY
  36. idVec4(0.0f, 0.0f, 0.0f, 1.0f), // S_COLOR_BLACK
  37. idVec4(0.0f, 0.0f, 0.0f, 1.0f),
  38. idVec4(0.0f, 0.0f, 0.0f, 1.0f),
  39. idVec4(0.0f, 0.0f, 0.0f, 1.0f),
  40. idVec4(0.0f, 0.0f, 0.0f, 1.0f),
  41. idVec4(0.0f, 0.0f, 0.0f, 1.0f),
  42. idVec4(0.0f, 0.0f, 0.0f, 1.0f),
  43. };
  44. const char *units[2][4] =
  45. {
  46. { "B", "KB", "MB", "GB" },
  47. { "B/s", "KB/s", "MB/s", "GB/s" }
  48. };
  49. /*
  50. ============
  51. idStr::ColorForIndex
  52. ============
  53. */
  54. idVec4 & idStr::ColorForIndex( int i ) {
  55. return g_color_table[ i & 15 ];
  56. }
  57. /*
  58. ============
  59. idStr::ReAllocate
  60. ============
  61. */
  62. void idStr::ReAllocate( int amount, bool keepold ) {
  63. char *newbuffer;
  64. int newsize;
  65. int mod;
  66. //assert( data );
  67. assert( amount > 0 );
  68. mod = amount % STR_ALLOC_GRAN;
  69. if ( !mod ) {
  70. newsize = amount;
  71. }
  72. else {
  73. newsize = amount + STR_ALLOC_GRAN - mod;
  74. }
  75. SetAlloced( newsize );
  76. #ifdef USE_STRING_DATA_ALLOCATOR
  77. newbuffer = stringDataAllocator.Alloc( GetAlloced() );
  78. #else
  79. newbuffer = new (TAG_STRING) char[ GetAlloced() ];
  80. #endif
  81. if ( keepold && data ) {
  82. data[ len ] = '\0';
  83. strcpy( newbuffer, data );
  84. }
  85. if ( data && data != baseBuffer ) {
  86. #ifdef USE_STRING_DATA_ALLOCATOR
  87. stringDataAllocator.Free( data );
  88. #else
  89. delete [] data;
  90. #endif
  91. }
  92. data = newbuffer;
  93. }
  94. /*
  95. ============
  96. idStr::FreeData
  97. ============
  98. */
  99. void idStr::FreeData() {
  100. if ( IsStatic() ) {
  101. return;
  102. }
  103. if ( data && data != baseBuffer ) {
  104. #ifdef USE_STRING_DATA_ALLOCATOR
  105. stringDataAllocator.Free( data );
  106. #else
  107. delete[] data;
  108. #endif
  109. data = baseBuffer;
  110. }
  111. }
  112. /*
  113. ============
  114. idStr::operator=
  115. ============
  116. */
  117. void idStr::operator=( const char *text ) {
  118. int l;
  119. int diff;
  120. int i;
  121. if ( !text ) {
  122. // safe behavior if NULL
  123. EnsureAlloced( 1, false );
  124. data[ 0 ] = '\0';
  125. len = 0;
  126. return;
  127. }
  128. if ( text == data ) {
  129. return; // copying same thing
  130. }
  131. // check if we're aliasing
  132. if ( text >= data && text <= data + len ) {
  133. diff = text - data;
  134. assert( strlen( text ) < (unsigned)len );
  135. for ( i = 0; text[ i ]; i++ ) {
  136. data[ i ] = text[ i ];
  137. }
  138. data[ i ] = '\0';
  139. len -= diff;
  140. return;
  141. }
  142. l = strlen( text );
  143. EnsureAlloced( l + 1, false );
  144. strcpy( data, text );
  145. len = l;
  146. }
  147. /*
  148. ============
  149. idStr::FindChar
  150. returns -1 if not found otherwise the index of the char
  151. ============
  152. */
  153. int idStr::FindChar( const char *str, const char c, int start, int end ) {
  154. int i;
  155. if ( end == -1 ) {
  156. end = strlen( str ) - 1;
  157. }
  158. for ( i = start; i <= end; i++ ) {
  159. if ( str[i] == c ) {
  160. return i;
  161. }
  162. }
  163. return -1;
  164. }
  165. /*
  166. ============
  167. idStr::FindText
  168. returns -1 if not found otherwise the index of the text
  169. ============
  170. */
  171. int idStr::FindText( const char *str, const char *text, bool casesensitive, int start, int end ) {
  172. int l, i, j;
  173. if ( end == -1 ) {
  174. end = strlen( str );
  175. }
  176. l = end - strlen( text );
  177. for ( i = start; i <= l; i++ ) {
  178. if ( casesensitive ) {
  179. for ( j = 0; text[j]; j++ ) {
  180. if ( str[i+j] != text[j] ) {
  181. break;
  182. }
  183. }
  184. } else {
  185. for ( j = 0; text[j]; j++ ) {
  186. if ( ::toupper( str[i+j] ) != ::toupper( text[j] ) ) {
  187. break;
  188. }
  189. }
  190. }
  191. if ( !text[j] ) {
  192. return i;
  193. }
  194. }
  195. return -1;
  196. }
  197. /*
  198. ============
  199. idStr::Filter
  200. Returns true if the string conforms the given filter.
  201. Several metacharacter may be used in the filter.
  202. * match any string of zero or more characters
  203. ? match any single character
  204. [abc...] match any of the enclosed characters; a hyphen can
  205. be used to specify a range (e.g. a-z, A-Z, 0-9)
  206. ============
  207. */
  208. bool idStr::Filter( const char *filter, const char *name, bool casesensitive ) {
  209. idStr buf;
  210. int i, found, index;
  211. while(*filter) {
  212. if (*filter == '*') {
  213. filter++;
  214. buf.Empty();
  215. for (i = 0; *filter; i++) {
  216. if ( *filter == '*' || *filter == '?' || (*filter == '[' && *(filter+1) != '[') ) {
  217. break;
  218. }
  219. buf += *filter;
  220. if ( *filter == '[' ) {
  221. filter++;
  222. }
  223. filter++;
  224. }
  225. if ( buf.Length() ) {
  226. index = idStr(name).Find( buf.c_str(), casesensitive );
  227. if ( index == -1 ) {
  228. return false;
  229. }
  230. name += index + strlen(buf);
  231. }
  232. }
  233. else if (*filter == '?') {
  234. filter++;
  235. name++;
  236. }
  237. else if (*filter == '[') {
  238. if ( *(filter+1) == '[' ) {
  239. if ( *name != '[' ) {
  240. return false;
  241. }
  242. filter += 2;
  243. name++;
  244. }
  245. else {
  246. filter++;
  247. found = false;
  248. while(*filter && !found) {
  249. if (*filter == ']' && *(filter+1) != ']') {
  250. break;
  251. }
  252. if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) {
  253. if (casesensitive) {
  254. if (*name >= *filter && *name <= *(filter+2)) {
  255. found = true;
  256. }
  257. }
  258. else {
  259. if ( ::toupper(*name) >= ::toupper(*filter) && ::toupper(*name) <= ::toupper(*(filter+2)) ) {
  260. found = true;
  261. }
  262. }
  263. filter += 3;
  264. }
  265. else {
  266. if (casesensitive) {
  267. if (*filter == *name) {
  268. found = true;
  269. }
  270. }
  271. else {
  272. if ( ::toupper(*filter) == ::toupper(*name) ) {
  273. found = true;
  274. }
  275. }
  276. filter++;
  277. }
  278. }
  279. if (!found) {
  280. return false;
  281. }
  282. while(*filter) {
  283. if ( *filter == ']' && *(filter+1) != ']' ) {
  284. break;
  285. }
  286. filter++;
  287. }
  288. filter++;
  289. name++;
  290. }
  291. }
  292. else {
  293. if (casesensitive) {
  294. if (*filter != *name) {
  295. return false;
  296. }
  297. }
  298. else {
  299. if ( ::toupper(*filter) != ::toupper(*name) ) {
  300. return false;
  301. }
  302. }
  303. filter++;
  304. name++;
  305. }
  306. }
  307. return true;
  308. }
  309. /*
  310. =============
  311. idStr::StripMediaName
  312. makes the string lower case, replaces backslashes with forward slashes, and removes extension
  313. =============
  314. */
  315. void idStr::StripMediaName( const char *name, idStr &mediaName ) {
  316. char c;
  317. mediaName.Empty();
  318. for ( c = *name; c; c = *(++name) ) {
  319. // truncate at an extension
  320. if ( c == '.' ) {
  321. break;
  322. }
  323. // convert backslashes to forward slashes
  324. if ( c == '\\' ) {
  325. mediaName.Append( '/' );
  326. } else {
  327. mediaName.Append( idStr::ToLower( c ) );
  328. }
  329. }
  330. }
  331. /*
  332. =============
  333. idStr::CheckExtension
  334. =============
  335. */
  336. bool idStr::CheckExtension( const char *name, const char *ext ) {
  337. const char *s1 = name + Length( name ) - 1;
  338. const char *s2 = ext + Length( ext ) - 1;
  339. int c1, c2, d;
  340. do {
  341. c1 = *s1--;
  342. c2 = *s2--;
  343. d = c1 - c2;
  344. while( d ) {
  345. if ( c1 <= 'Z' && c1 >= 'A' ) {
  346. d += ('a' - 'A');
  347. if ( !d ) {
  348. break;
  349. }
  350. }
  351. if ( c2 <= 'Z' && c2 >= 'A' ) {
  352. d -= ('a' - 'A');
  353. if ( !d ) {
  354. break;
  355. }
  356. }
  357. return false;
  358. }
  359. } while( s1 > name && s2 > ext );
  360. return ( s1 >= name );
  361. }
  362. /*
  363. =============
  364. idStr::FloatArrayToString
  365. =============
  366. */
  367. const char *idStr::FloatArrayToString( const float *array, const int length, const int precision ) {
  368. static int index = 0;
  369. static char str[4][16384]; // in case called by nested functions
  370. int i, n;
  371. char format[16], *s;
  372. // use an array of string so that multiple calls won't collide
  373. s = str[ index ];
  374. index = (index + 1) & 3;
  375. idStr::snPrintf( format, sizeof( format ), "%%.%df", precision );
  376. n = idStr::snPrintf( s, sizeof( str[0] ), format, array[0] );
  377. if ( precision > 0 ) {
  378. while( n > 0 && s[n-1] == '0' ) s[--n] = '\0';
  379. while( n > 0 && s[n-1] == '.' ) s[--n] = '\0';
  380. }
  381. idStr::snPrintf( format, sizeof( format ), " %%.%df", precision );
  382. for ( i = 1; i < length; i++ ) {
  383. n += idStr::snPrintf( s + n, sizeof( str[0] ) - n, format, array[i] );
  384. if ( precision > 0 ) {
  385. while( n > 0 && s[n-1] == '0' ) s[--n] = '\0';
  386. while( n > 0 && s[n-1] == '.' ) s[--n] = '\0';
  387. }
  388. }
  389. return s;
  390. }
  391. /*
  392. ========================
  393. idStr::CStyleQuote
  394. ========================
  395. */
  396. const char *idStr::CStyleQuote( const char *str ) {
  397. static int index = 0;
  398. static char buffers[4][16384]; // in case called by nested functions
  399. unsigned int i;
  400. char *buf;
  401. buf = buffers[index];
  402. index = ( index + 1 ) & 3;
  403. buf[0] = '\"';
  404. for ( i = 1; i < sizeof( buffers[0] ) - 2; i++ ) {
  405. int c = *str++;
  406. switch( c ) {
  407. case '\0': buf[i++] = '\"'; buf[i] = '\0'; return buf;
  408. case '\\': buf[i++] = '\\'; buf[i] = '\\'; break;
  409. case '\n': buf[i++] = '\\'; buf[i] = 'n'; break;
  410. case '\r': buf[i++] = '\\'; buf[i] = 'r'; break;
  411. case '\t': buf[i++] = '\\'; buf[i] = 't'; break;
  412. case '\v': buf[i++] = '\\'; buf[i] = 'v'; break;
  413. case '\b': buf[i++] = '\\'; buf[i] = 'b'; break;
  414. case '\f': buf[i++] = '\\'; buf[i] = 'f'; break;
  415. case '\a': buf[i++] = '\\'; buf[i] = 'a'; break;
  416. case '\'': buf[i++] = '\\'; buf[i] = '\''; break;
  417. case '\"': buf[i++] = '\\'; buf[i] = '\"'; break;
  418. case '\?': buf[i++] = '\\'; buf[i] = '\?'; break;
  419. default: buf[i] = c; break;
  420. }
  421. }
  422. buf[i++] = '\"';
  423. buf[i] = '\0';
  424. return buf;
  425. }
  426. /*
  427. ========================
  428. idStr::CStyleUnQuote
  429. ========================
  430. */
  431. const char *idStr::CStyleUnQuote( const char *str ) {
  432. if ( str[0] != '\"' ) {
  433. return str;
  434. }
  435. static int index = 0;
  436. static char buffers[4][16384]; // in case called by nested functions
  437. unsigned int i;
  438. char *buf;
  439. buf = buffers[index];
  440. index = ( index + 1 ) & 3;
  441. str++;
  442. for ( i = 0; i < sizeof( buffers[0] ) - 1; i++ ) {
  443. int c = *str++;
  444. if ( c == '\0' ) {
  445. break;
  446. } else if ( c == '\\' ) {
  447. c = *str++;
  448. switch( c ) {
  449. case '\\': buf[i] = '\\'; break;
  450. case 'n': buf[i] = '\n'; break;
  451. case 'r': buf[i] = '\r'; break;
  452. case 't': buf[i] = '\t'; break;
  453. case 'v': buf[i] = '\v'; break;
  454. case 'b': buf[i] = '\b'; break;
  455. case 'f': buf[i] = '\f'; break;
  456. case 'a': buf[i] = '\a'; break;
  457. case '\'': buf[i] = '\''; break;
  458. case '\"': buf[i] = '\"'; break;
  459. case '\?': buf[i] = '\?'; break;
  460. }
  461. } else {
  462. buf[i] = c;
  463. }
  464. }
  465. assert( buf[i-1] == '\"' );
  466. buf[i-1] = '\0';
  467. return buf;
  468. }
  469. /*
  470. ============
  471. idStr::Last
  472. returns -1 if not found otherwise the index of the char
  473. ============
  474. */
  475. int idStr::Last( const char c ) const {
  476. int i;
  477. for( i = Length(); i > 0; i-- ) {
  478. if ( data[ i - 1 ] == c ) {
  479. return i - 1;
  480. }
  481. }
  482. return -1;
  483. }
  484. /*
  485. ========================
  486. idStr::Format
  487. perform a threadsafe sprintf to the string
  488. ========================
  489. */
  490. void idStr::Format( const char *fmt, ... ) {
  491. va_list argptr;
  492. char text[MAX_PRINT_MSG];
  493. va_start( argptr, fmt );
  494. int len = idStr::vsnPrintf( text, sizeof( text ) - 1, fmt, argptr );
  495. va_end( argptr );
  496. text[ sizeof( text ) - 1 ] = '\0';
  497. if ( (size_t)len >= sizeof( text ) - 1 ) {
  498. idLib::common->FatalError( "Tried to set a large buffer using %s", fmt );
  499. }
  500. *this = text;
  501. }
  502. /*
  503. ========================
  504. idStr::FormatInt
  505. Formats integers with commas for readability.
  506. ========================
  507. */
  508. idStr idStr::FormatInt( const int num, bool isCash ) {
  509. idStr val = va( "%d", num );
  510. int len = val.Length();
  511. for ( int i = 0 ; i < ( ( len - 1 ) / 3 ); i++ ) {
  512. int pos = val.Length() - ( ( i + 1 ) * 3 + i );
  513. if ( pos > 1 || val[0] != '-' ) {
  514. val.Insert( ',', pos );
  515. }
  516. }
  517. if ( isCash ) {
  518. val.Insert( '$', val[0] == '-' ? 1 : 0 );
  519. }
  520. return val;
  521. }
  522. /*
  523. ============
  524. idStr::StripLeading
  525. ============
  526. */
  527. void idStr::StripLeading( const char c ) {
  528. while( data[ 0 ] == c ) {
  529. memmove( &data[ 0 ], &data[ 1 ], len );
  530. len--;
  531. }
  532. }
  533. /*
  534. ============
  535. idStr::StripLeading
  536. ============
  537. */
  538. void idStr::StripLeading( const char *string ) {
  539. int l;
  540. l = strlen( string );
  541. if ( l > 0 ) {
  542. while ( !Cmpn( string, l ) ) {
  543. memmove( data, data + l, len - l + 1 );
  544. len -= l;
  545. }
  546. }
  547. }
  548. /*
  549. ============
  550. idStr::StripLeadingOnce
  551. ============
  552. */
  553. bool idStr::StripLeadingOnce( const char *string ) {
  554. int l;
  555. l = strlen( string );
  556. if ( ( l > 0 ) && !Cmpn( string, l ) ) {
  557. memmove( data, data + l, len - l + 1 );
  558. len -= l;
  559. return true;
  560. }
  561. return false;
  562. }
  563. /*
  564. ============
  565. idStr::StripTrailing
  566. ============
  567. */
  568. void idStr::StripTrailing( const char c ) {
  569. int i;
  570. for( i = Length(); i > 0 && data[ i - 1 ] == c; i-- ) {
  571. data[ i - 1 ] = '\0';
  572. len--;
  573. }
  574. }
  575. /*
  576. ============
  577. idStr::StripLeading
  578. ============
  579. */
  580. void idStr::StripTrailing( const char *string ) {
  581. int l;
  582. l = strlen( string );
  583. if ( l > 0 ) {
  584. while ( ( len >= l ) && !Cmpn( string, data + len - l, l ) ) {
  585. len -= l;
  586. data[len] = '\0';
  587. }
  588. }
  589. }
  590. /*
  591. ============
  592. idStr::StripTrailingOnce
  593. ============
  594. */
  595. bool idStr::StripTrailingOnce( const char *string ) {
  596. int l;
  597. l = strlen( string );
  598. if ( ( l > 0 ) && ( len >= l ) && !Cmpn( string, data + len - l, l ) ) {
  599. len -= l;
  600. data[len] = '\0';
  601. return true;
  602. }
  603. return false;
  604. }
  605. /*
  606. ============
  607. idStr::Replace
  608. ============
  609. */
  610. bool idStr::ReplaceChar( const char old, const char nw ) {
  611. bool replaced = false;
  612. for ( int i = 0; i < Length(); i++ ) {
  613. if ( data[i] == old ) {
  614. data[i] = nw;
  615. replaced = true;
  616. }
  617. }
  618. return replaced;
  619. }
  620. /*
  621. ============
  622. idStr::Replace
  623. ============
  624. */
  625. bool idStr::Replace( const char *old, const char *nw ) {
  626. int oldLen = strlen( old );
  627. int newLen = strlen( nw );
  628. // Work out how big the new string will be
  629. int count = 0;
  630. for ( int i = 0; i < Length(); i++ ) {
  631. if ( idStr::Cmpn( &data[i], old, oldLen ) == 0 ) {
  632. count++;
  633. i += oldLen - 1;
  634. }
  635. }
  636. if ( count ) {
  637. idStr oldString( data );
  638. EnsureAlloced( len + ( ( newLen - oldLen ) * count ) + 2, false );
  639. // Replace the old data with the new data
  640. int j = 0;
  641. for ( int i = 0; i < oldString.Length(); i++ ) {
  642. if ( idStr::Cmpn( &oldString[i], old, oldLen ) == 0 ) {
  643. memcpy( data + j, nw, newLen );
  644. i += oldLen - 1;
  645. j += newLen;
  646. } else {
  647. data[j] = oldString[i];
  648. j++;
  649. }
  650. }
  651. data[j] = 0;
  652. len = strlen( data );
  653. return true;
  654. }
  655. return false;
  656. }
  657. /*
  658. ============
  659. idStr::Mid
  660. ============
  661. */
  662. const char *idStr::Mid( int start, int len, idStr &result ) const {
  663. int i;
  664. result.Empty();
  665. i = Length();
  666. if ( i == 0 || len <= 0 || start >= i ) {
  667. return NULL;
  668. }
  669. if ( start + len >= i ) {
  670. len = i - start;
  671. }
  672. result.Append( &data[ start ], len );
  673. return result;
  674. }
  675. /*
  676. ============
  677. idStr::Mid
  678. ============
  679. */
  680. idStr idStr::Mid( int start, int len ) const {
  681. int i;
  682. idStr result;
  683. i = Length();
  684. if ( i == 0 || len <= 0 || start >= i ) {
  685. return result;
  686. }
  687. if ( start + len >= i ) {
  688. len = i - start;
  689. }
  690. result.Append( &data[ start ], len );
  691. return result;
  692. }
  693. /*
  694. ============
  695. idStr::StripTrailingWhitespace
  696. ============
  697. */
  698. void idStr::StripTrailingWhitespace() {
  699. int i;
  700. // cast to unsigned char to prevent stripping off high-ASCII characters
  701. for( i = Length(); i > 0 && (unsigned char)(data[ i - 1 ]) <= ' '; i-- ) {
  702. data[ i - 1 ] = '\0';
  703. len--;
  704. }
  705. }
  706. /*
  707. ============
  708. idStr::StripQuotes
  709. Removes the quotes from the beginning and end of the string
  710. ============
  711. */
  712. idStr& idStr::StripQuotes ()
  713. {
  714. if ( data[0] != '\"' )
  715. {
  716. return *this;
  717. }
  718. // Remove the trailing quote first
  719. if ( data[len-1] == '\"' )
  720. {
  721. data[len-1] = '\0';
  722. len--;
  723. }
  724. // Strip the leading quote now
  725. len--;
  726. memmove( &data[ 0 ], &data[ 1 ], len );
  727. data[len] = '\0';
  728. return *this;
  729. }
  730. /*
  731. =====================================================================
  732. filename methods
  733. =====================================================================
  734. */
  735. /*
  736. ============
  737. idStr::FileNameHash
  738. ============
  739. */
  740. int idStr::FileNameHash() const {
  741. int i;
  742. long hash;
  743. char letter;
  744. hash = 0;
  745. i = 0;
  746. while( data[i] != '\0' ) {
  747. letter = idStr::ToLower( data[i] );
  748. if ( letter == '.' ) {
  749. break; // don't include extension
  750. }
  751. if ( letter =='\\' ) {
  752. letter = '/';
  753. }
  754. hash += (long)(letter)*(i+119);
  755. i++;
  756. }
  757. hash &= (FILE_HASH_SIZE-1);
  758. return hash;
  759. }
  760. /*
  761. ============
  762. idStr::BackSlashesToSlashes
  763. ============
  764. */
  765. idStr &idStr::BackSlashesToSlashes() {
  766. int i;
  767. for ( i = 0; i < len; i++ ) {
  768. if ( data[ i ] == '\\' ) {
  769. data[ i ] = '/';
  770. }
  771. }
  772. return *this;
  773. }
  774. /*
  775. ============
  776. idStr::SlashesToBackSlashes
  777. ============
  778. */
  779. idStr &idStr::SlashesToBackSlashes() {
  780. int i;
  781. for ( i = 0; i < len; i++ ) {
  782. if ( data[ i ] == '/' ) {
  783. data[ i ] = '\\';
  784. }
  785. }
  786. return *this;
  787. }
  788. /*
  789. ============
  790. idStr::SetFileExtension
  791. ============
  792. */
  793. idStr &idStr::SetFileExtension( const char *extension ) {
  794. StripFileExtension();
  795. if ( *extension != '.' ) {
  796. Append( '.' );
  797. }
  798. Append( extension );
  799. return *this;
  800. }
  801. /*
  802. ============
  803. idStr::StripFileExtension
  804. ============
  805. */
  806. idStr &idStr::StripFileExtension() {
  807. int i;
  808. for ( i = len-1; i >= 0; i-- ) {
  809. if ( data[i] == '.' ) {
  810. data[i] = '\0';
  811. len = i;
  812. break;
  813. }
  814. }
  815. return *this;
  816. }
  817. /*
  818. ============
  819. idStr::StripAbsoluteFileExtension
  820. ============
  821. */
  822. idStr &idStr::StripAbsoluteFileExtension() {
  823. int i;
  824. for ( i = 0; i < len; i++ ) {
  825. if ( data[i] == '.' ) {
  826. data[i] = '\0';
  827. len = i;
  828. break;
  829. }
  830. }
  831. return *this;
  832. }
  833. /*
  834. ==================
  835. idStr::DefaultFileExtension
  836. ==================
  837. */
  838. idStr &idStr::DefaultFileExtension( const char *extension ) {
  839. int i;
  840. // do nothing if the string already has an extension
  841. for ( i = len-1; i >= 0; i-- ) {
  842. if ( data[i] == '.' ) {
  843. return *this;
  844. }
  845. }
  846. if ( *extension != '.' ) {
  847. Append( '.' );
  848. }
  849. Append( extension );
  850. return *this;
  851. }
  852. /*
  853. ==================
  854. idStr::DefaultPath
  855. ==================
  856. */
  857. idStr &idStr::DefaultPath( const char *basepath ) {
  858. if ( ( ( *this )[ 0 ] == '/' ) || ( ( *this )[ 0 ] == '\\' ) ) {
  859. // absolute path location
  860. return *this;
  861. }
  862. *this = basepath + *this;
  863. return *this;
  864. }
  865. /*
  866. ====================
  867. idStr::AppendPath
  868. ====================
  869. */
  870. void idStr::AppendPath( const char *text ) {
  871. int pos;
  872. int i = 0;
  873. if ( text && text[i] ) {
  874. pos = len;
  875. EnsureAlloced( len + strlen( text ) + 2 );
  876. if ( pos ) {
  877. if ( data[ pos-1 ] != '/' ) {
  878. data[ pos++ ] = '/';
  879. }
  880. }
  881. if ( text[i] == '/' ) {
  882. i++;
  883. }
  884. for ( ; text[ i ]; i++ ) {
  885. if ( text[ i ] == '\\' ) {
  886. data[ pos++ ] = '/';
  887. } else {
  888. data[ pos++ ] = text[ i ];
  889. }
  890. }
  891. len = pos;
  892. data[ pos ] = '\0';
  893. }
  894. }
  895. /*
  896. ==================
  897. idStr::StripFilename
  898. ==================
  899. */
  900. idStr &idStr::StripFilename() {
  901. int pos;
  902. pos = Length() - 1;
  903. while( ( pos > 0 ) && ( ( *this )[ pos ] != '/' ) && ( ( *this )[ pos ] != '\\' ) ) {
  904. pos--;
  905. }
  906. if ( pos < 0 ) {
  907. pos = 0;
  908. }
  909. CapLength( pos );
  910. return *this;
  911. }
  912. /*
  913. ==================
  914. idStr::StripPath
  915. ==================
  916. */
  917. idStr &idStr::StripPath() {
  918. int pos;
  919. pos = Length();
  920. while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
  921. pos--;
  922. }
  923. *this = Right( Length() - pos );
  924. return *this;
  925. }
  926. /*
  927. ====================
  928. idStr::ExtractFilePath
  929. ====================
  930. */
  931. void idStr::ExtractFilePath( idStr &dest ) const {
  932. int pos;
  933. //
  934. // back up until a \ or the start
  935. //
  936. pos = Length();
  937. while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
  938. pos--;
  939. }
  940. Left( pos, dest );
  941. }
  942. /*
  943. ====================
  944. idStr::ExtractFileName
  945. ====================
  946. */
  947. void idStr::ExtractFileName( idStr &dest ) const {
  948. int pos;
  949. //
  950. // back up until a \ or the start
  951. //
  952. pos = Length() - 1;
  953. while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
  954. pos--;
  955. }
  956. Right( Length() - pos, dest );
  957. }
  958. /*
  959. ====================
  960. idStr::ExtractFileBase
  961. ====================
  962. */
  963. void idStr::ExtractFileBase( idStr &dest ) const {
  964. int pos;
  965. int start;
  966. //
  967. // back up until a \ or the start
  968. //
  969. pos = Length() - 1;
  970. while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
  971. pos--;
  972. }
  973. start = pos;
  974. while( ( pos < Length() ) && ( ( *this )[ pos ] != '.' ) ) {
  975. pos++;
  976. }
  977. Mid( start, pos - start, dest );
  978. }
  979. /*
  980. ====================
  981. idStr::ExtractFileExtension
  982. ====================
  983. */
  984. void idStr::ExtractFileExtension( idStr &dest ) const {
  985. int pos;
  986. //
  987. // back up until a . or the start
  988. //
  989. pos = Length() - 1;
  990. while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '.' ) ) {
  991. pos--;
  992. }
  993. if ( !pos ) {
  994. // no extension
  995. dest.Empty();
  996. } else {
  997. Right( Length() - pos, dest );
  998. }
  999. }
  1000. /*
  1001. =====================================================================
  1002. char * methods to replace library functions
  1003. =====================================================================
  1004. */
  1005. /*
  1006. ============
  1007. idStr::IsNumeric
  1008. Checks a string to see if it contains only numerical values.
  1009. ============
  1010. */
  1011. bool idStr::IsNumeric( const char *s ) {
  1012. int i;
  1013. bool dot;
  1014. if ( *s == '-' ) {
  1015. s++;
  1016. }
  1017. dot = false;
  1018. for ( i = 0; s[i]; i++ ) {
  1019. if ( !isdigit( (const unsigned char)s[i] ) ) {
  1020. if ( ( s[ i ] == '.' ) && !dot ) {
  1021. dot = true;
  1022. continue;
  1023. }
  1024. return false;
  1025. }
  1026. }
  1027. return true;
  1028. }
  1029. /*
  1030. ============
  1031. idStr::HasLower
  1032. Checks if a string has any lowercase chars
  1033. ============
  1034. */
  1035. bool idStr::HasLower( const char *s ) {
  1036. if ( !s ) {
  1037. return false;
  1038. }
  1039. while ( *s ) {
  1040. if ( CharIsLower( *s ) ) {
  1041. return true;
  1042. }
  1043. s++;
  1044. }
  1045. return false;
  1046. }
  1047. /*
  1048. ============
  1049. idStr::HasUpper
  1050. Checks if a string has any uppercase chars
  1051. ============
  1052. */
  1053. bool idStr::HasUpper( const char *s ) {
  1054. if ( !s ) {
  1055. return false;
  1056. }
  1057. while ( *s ) {
  1058. if ( CharIsUpper( *s ) ) {
  1059. return true;
  1060. }
  1061. s++;
  1062. }
  1063. return false;
  1064. }
  1065. /*
  1066. ================
  1067. idStr::Cmp
  1068. ================
  1069. */
  1070. int idStr::Cmp( const char *s1, const char *s2 ) {
  1071. int c1, c2, d;
  1072. do {
  1073. c1 = *s1++;
  1074. c2 = *s2++;
  1075. d = c1 - c2;
  1076. if ( d ) {
  1077. return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1;
  1078. }
  1079. } while( c1 );
  1080. return 0; // strings are equal
  1081. }
  1082. /*
  1083. ================
  1084. idStr::Cmpn
  1085. ================
  1086. */
  1087. int idStr::Cmpn( const char *s1, const char *s2, int n ) {
  1088. int c1, c2, d;
  1089. assert( n >= 0 );
  1090. do {
  1091. c1 = *s1++;
  1092. c2 = *s2++;
  1093. if ( !n-- ) {
  1094. return 0; // strings are equal until end point
  1095. }
  1096. d = c1 - c2;
  1097. if ( d ) {
  1098. return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1;
  1099. }
  1100. } while( c1 );
  1101. return 0; // strings are equal
  1102. }
  1103. /*
  1104. ================
  1105. idStr::Icmp
  1106. ================
  1107. */
  1108. int idStr::Icmp( const char *s1, const char *s2 ) {
  1109. int c1, c2, d;
  1110. do {
  1111. c1 = *s1++;
  1112. c2 = *s2++;
  1113. d = c1 - c2;
  1114. while( d ) {
  1115. if ( c1 <= 'Z' && c1 >= 'A' ) {
  1116. d += ('a' - 'A');
  1117. if ( !d ) {
  1118. break;
  1119. }
  1120. }
  1121. if ( c2 <= 'Z' && c2 >= 'A' ) {
  1122. d -= ('a' - 'A');
  1123. if ( !d ) {
  1124. break;
  1125. }
  1126. }
  1127. return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1;
  1128. }
  1129. } while( c1 );
  1130. return 0; // strings are equal
  1131. }
  1132. /*
  1133. ================
  1134. idStr::Icmpn
  1135. ================
  1136. */
  1137. int idStr::Icmpn( const char *s1, const char *s2, int n ) {
  1138. int c1, c2, d;
  1139. assert( n >= 0 );
  1140. do {
  1141. c1 = *s1++;
  1142. c2 = *s2++;
  1143. if ( !n-- ) {
  1144. return 0; // strings are equal until end point
  1145. }
  1146. d = c1 - c2;
  1147. while( d ) {
  1148. if ( c1 <= 'Z' && c1 >= 'A' ) {
  1149. d += ('a' - 'A');
  1150. if ( !d ) {
  1151. break;
  1152. }
  1153. }
  1154. if ( c2 <= 'Z' && c2 >= 'A' ) {
  1155. d -= ('a' - 'A');
  1156. if ( !d ) {
  1157. break;
  1158. }
  1159. }
  1160. return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1;
  1161. }
  1162. } while( c1 );
  1163. return 0; // strings are equal
  1164. }
  1165. /*
  1166. ================
  1167. idStr::Icmp
  1168. ================
  1169. */
  1170. int idStr::IcmpNoColor( const char *s1, const char *s2 ) {
  1171. int c1, c2, d;
  1172. do {
  1173. while ( idStr::IsColor( s1 ) ) {
  1174. s1 += 2;
  1175. }
  1176. while ( idStr::IsColor( s2 ) ) {
  1177. s2 += 2;
  1178. }
  1179. c1 = *s1++;
  1180. c2 = *s2++;
  1181. d = c1 - c2;
  1182. while( d ) {
  1183. if ( c1 <= 'Z' && c1 >= 'A' ) {
  1184. d += ('a' - 'A');
  1185. if ( !d ) {
  1186. break;
  1187. }
  1188. }
  1189. if ( c2 <= 'Z' && c2 >= 'A' ) {
  1190. d -= ('a' - 'A');
  1191. if ( !d ) {
  1192. break;
  1193. }
  1194. }
  1195. return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1;
  1196. }
  1197. } while( c1 );
  1198. return 0; // strings are equal
  1199. }
  1200. /*
  1201. ================
  1202. idStr::IcmpPath
  1203. ================
  1204. */
  1205. int idStr::IcmpPath( const char *s1, const char *s2 ) {
  1206. int c1, c2, d;
  1207. #if 0
  1208. //#if !defined( ID_PC_WIN )
  1209. idLib::common->Printf( "WARNING: IcmpPath used on a case-sensitive filesystem?\n" );
  1210. #endif
  1211. do {
  1212. c1 = *s1++;
  1213. c2 = *s2++;
  1214. d = c1 - c2;
  1215. while( d ) {
  1216. if ( c1 <= 'Z' && c1 >= 'A' ) {
  1217. d += ('a' - 'A');
  1218. if ( !d ) {
  1219. break;
  1220. }
  1221. }
  1222. if ( c1 == '\\' ) {
  1223. d += ('/' - '\\');
  1224. if ( !d ) {
  1225. break;
  1226. }
  1227. }
  1228. if ( c2 <= 'Z' && c2 >= 'A' ) {
  1229. d -= ('a' - 'A');
  1230. if ( !d ) {
  1231. break;
  1232. }
  1233. }
  1234. if ( c2 == '\\' ) {
  1235. d -= ('/' - '\\');
  1236. if ( !d ) {
  1237. break;
  1238. }
  1239. }
  1240. // make sure folders come first
  1241. while( c1 ) {
  1242. if ( c1 == '/' || c1 == '\\' ) {
  1243. break;
  1244. }
  1245. c1 = *s1++;
  1246. }
  1247. while( c2 ) {
  1248. if ( c2 == '/' || c2 == '\\' ) {
  1249. break;
  1250. }
  1251. c2 = *s2++;
  1252. }
  1253. if ( c1 && !c2 ) {
  1254. return -1;
  1255. } else if ( !c1 && c2 ) {
  1256. return 1;
  1257. }
  1258. // same folder depth so use the regular compare
  1259. return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1;
  1260. }
  1261. } while( c1 );
  1262. return 0;
  1263. }
  1264. /*
  1265. ================
  1266. idStr::IcmpnPath
  1267. ================
  1268. */
  1269. int idStr::IcmpnPath( const char *s1, const char *s2, int n ) {
  1270. int c1, c2, d;
  1271. #if 0
  1272. //#if !defined( ID_PC_WIN )
  1273. idLib::common->Printf( "WARNING: IcmpPath used on a case-sensitive filesystem?\n" );
  1274. #endif
  1275. assert( n >= 0 );
  1276. do {
  1277. c1 = *s1++;
  1278. c2 = *s2++;
  1279. if ( !n-- ) {
  1280. return 0; // strings are equal until end point
  1281. }
  1282. d = c1 - c2;
  1283. while( d ) {
  1284. if ( c1 <= 'Z' && c1 >= 'A' ) {
  1285. d += ('a' - 'A');
  1286. if ( !d ) {
  1287. break;
  1288. }
  1289. }
  1290. if ( c1 == '\\' ) {
  1291. d += ('/' - '\\');
  1292. if ( !d ) {
  1293. break;
  1294. }
  1295. }
  1296. if ( c2 <= 'Z' && c2 >= 'A' ) {
  1297. d -= ('a' - 'A');
  1298. if ( !d ) {
  1299. break;
  1300. }
  1301. }
  1302. if ( c2 == '\\' ) {
  1303. d -= ('/' - '\\');
  1304. if ( !d ) {
  1305. break;
  1306. }
  1307. }
  1308. // make sure folders come first
  1309. while( c1 ) {
  1310. if ( c1 == '/' || c1 == '\\' ) {
  1311. break;
  1312. }
  1313. c1 = *s1++;
  1314. }
  1315. while( c2 ) {
  1316. if ( c2 == '/' || c2 == '\\' ) {
  1317. break;
  1318. }
  1319. c2 = *s2++;
  1320. }
  1321. if ( c1 && !c2 ) {
  1322. return -1;
  1323. } else if ( !c1 && c2 ) {
  1324. return 1;
  1325. }
  1326. // same folder depth so use the regular compare
  1327. return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1;
  1328. }
  1329. } while( c1 );
  1330. return 0;
  1331. }
  1332. /*
  1333. =============
  1334. idStr::Copynz
  1335. Safe strncpy that ensures a trailing zero
  1336. =============
  1337. */
  1338. void idStr::Copynz( char *dest, const char *src, int destsize ) {
  1339. if ( !src ) {
  1340. idLib::common->Warning( "idStr::Copynz: NULL src" );
  1341. return;
  1342. }
  1343. if ( destsize < 1 ) {
  1344. idLib::common->Warning( "idStr::Copynz: destsize < 1" );
  1345. return;
  1346. }
  1347. strncpy( dest, src, destsize-1 );
  1348. dest[destsize-1] = 0;
  1349. }
  1350. /*
  1351. ================
  1352. idStr::Append
  1353. never goes past bounds or leaves without a terminating 0
  1354. ================
  1355. */
  1356. void idStr::Append( char *dest, int size, const char *src ) {
  1357. int l1;
  1358. l1 = strlen( dest );
  1359. if ( l1 >= size ) {
  1360. idLib::common->Error( "idStr::Append: already overflowed" );
  1361. }
  1362. idStr::Copynz( dest + l1, src, size - l1 );
  1363. }
  1364. /*
  1365. ========================
  1366. idStr::IsValidUTF8
  1367. ========================
  1368. */
  1369. bool idStr::IsValidUTF8( const uint8 * s, const int maxLen, utf8Encoding_t & encoding ) {
  1370. struct local_t {
  1371. static int GetNumEncodedUTF8Bytes( const uint8 c ) {
  1372. if ( c < 0x80 ) {
  1373. return 1;
  1374. } else if ( ( c >> 5 ) == 0x06 ) {
  1375. // 2 byte encoding - the next byte must begin with
  1376. return 2;
  1377. } else if ( ( c >> 4 ) == 0x0E ) {
  1378. // 3 byte encoding
  1379. return 3;
  1380. } else if ( ( c >> 5 ) == 0x1E ) {
  1381. // 4 byte encoding
  1382. return 4;
  1383. }
  1384. // this isnt' a valid UTF-8 precursor character
  1385. return 0;
  1386. }
  1387. static bool RemainingCharsAreUTF8FollowingBytes( const uint8 * s, const int curChar, const int maxLen, const int num ) {
  1388. if ( maxLen - curChar < num ) {
  1389. return false;
  1390. }
  1391. for ( int i = curChar + 1; i <= curChar + num; i++ ) {
  1392. if ( s[ i ] == '\0' ) {
  1393. return false;
  1394. }
  1395. if ( ( s[ i ] >> 6 ) != 0x02 ) {
  1396. return false;
  1397. }
  1398. }
  1399. return true;
  1400. }
  1401. };
  1402. // check for byte-order-marker
  1403. encoding = UTF8_PURE_ASCII;
  1404. utf8Encoding_t utf8Type = UTF8_ENCODED_NO_BOM;
  1405. if ( maxLen > 3 && s[ 0 ] == 0xEF && s[ 1 ] == 0xBB && s[ 2 ] == 0xBF ) {
  1406. utf8Type = UTF8_ENCODED_BOM;
  1407. }
  1408. for ( int i = 0; s[ i ] != '\0' && i < maxLen; i++ ) {
  1409. int numBytes = local_t::GetNumEncodedUTF8Bytes( s[ i ] );
  1410. if ( numBytes == 1 ) {
  1411. continue; // just low ASCII
  1412. } else if ( numBytes == 2 ) {
  1413. // 2 byte encoding - the next byte must begin with bit pattern 10
  1414. if ( !local_t::RemainingCharsAreUTF8FollowingBytes( s, i, maxLen, 1 ) ) {
  1415. return false;
  1416. }
  1417. // skip over UTF-8 character
  1418. i += 1;
  1419. encoding = utf8Type;
  1420. } else if ( numBytes == 3 ) {
  1421. // 3 byte encoding - the next 2 bytes must begin with bit pattern 10
  1422. if ( !local_t::RemainingCharsAreUTF8FollowingBytes( s, i, maxLen, 2 ) ) {
  1423. return false;
  1424. }
  1425. // skip over UTF-8 character
  1426. i += 2;
  1427. encoding = utf8Type;
  1428. } else if ( numBytes == 4 ) {
  1429. // 4 byte encoding - the next 3 bytes must begin with bit pattern 10
  1430. if ( !local_t::RemainingCharsAreUTF8FollowingBytes( s, i, maxLen, 3 ) ) {
  1431. return false;
  1432. }
  1433. // skip over UTF-8 character
  1434. i += 3;
  1435. encoding = utf8Type;
  1436. } else {
  1437. // this isnt' a valid UTF-8 character
  1438. if ( utf8Type == UTF8_ENCODED_BOM ) {
  1439. encoding = UTF8_INVALID_BOM;
  1440. } else {
  1441. encoding = UTF8_INVALID;
  1442. }
  1443. return false;
  1444. }
  1445. }
  1446. return true;
  1447. }
  1448. /*
  1449. ========================
  1450. idStr::UTF8Length
  1451. ========================
  1452. */
  1453. int idStr::UTF8Length( const byte * s ) {
  1454. int mbLen = 0;
  1455. int charLen = 0;
  1456. while ( s[ mbLen ] != '\0' ) {
  1457. uint32 cindex;
  1458. cindex = s[ mbLen ];
  1459. if ( cindex < 0x80 ) {
  1460. mbLen++;
  1461. } else {
  1462. int trailing = 0;
  1463. if ( cindex >= 0xc0 ) {
  1464. static const byte trailingBytes[ 64 ] = {
  1465. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1466. 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
  1467. };
  1468. trailing = trailingBytes[ cindex - 0xc0 ];
  1469. }
  1470. mbLen += trailing + 1;
  1471. }
  1472. charLen++;
  1473. }
  1474. return charLen;
  1475. }
  1476. /*
  1477. ========================
  1478. idStr::AppendUTF8Char
  1479. ========================
  1480. */
  1481. void idStr::AppendUTF8Char( uint32 c ) {
  1482. if ( c < 0x80 ) {
  1483. Append( ( char)c );
  1484. } else if ( c < 0x800 ) { // 11 bits
  1485. Append( (char)( 0xC0 | ( c >> 6 ) ) );
  1486. Append( (char)( 0x80 | ( c & 0x3F ) ) );
  1487. } else if ( c < 0x10000 ) { // 16 bits
  1488. Append( (char)( 0xE0 | ( c >> 12 ) ) );
  1489. Append( (char)( 0x80 | ( ( c >> 6 ) & 0x3F ) ) );
  1490. Append( (char)( 0x80 | ( c & 0x3F ) ) );
  1491. } else if ( c < 0x200000 ) { // 21 bits
  1492. Append( (char)( 0xF0 | ( c >> 18 ) ) );
  1493. Append( (char)( 0x80 | ( ( c >> 12 ) & 0x3F ) ) );
  1494. Append( (char)( 0x80 | ( ( c >> 6 ) & 0x3F ) ) );
  1495. Append( (char)( 0x80 | ( c & 0x3F ) ) );
  1496. } else {
  1497. // UTF-8 can encode up to 6 bytes. Why don't we support that?
  1498. // This is an invalid Unicode character
  1499. Append( '?' );
  1500. }
  1501. }
  1502. /*
  1503. ========================
  1504. idStr::UTF8Char
  1505. ========================
  1506. */
  1507. uint32 idStr::UTF8Char( const byte * s, int & idx ) {
  1508. if ( idx >= 0 ) {
  1509. while ( s[ idx ] != '\0' ) {
  1510. uint32 cindex = s[ idx ];
  1511. if ( cindex < 0x80 ) {
  1512. idx++;
  1513. return cindex;
  1514. }
  1515. int trailing = 0;
  1516. if ( cindex >= 0xc0 ) {
  1517. static const byte trailingBytes[ 64 ] = {
  1518. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1519. 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
  1520. };
  1521. trailing = trailingBytes[ cindex - 0xc0 ];
  1522. }
  1523. static const uint32 trailingMask[ 6 ] = { 0x0000007f, 0x0000001f, 0x0000000f, 0x00000007, 0x00000003, 0x00000001 };
  1524. cindex &= trailingMask[ trailing ];
  1525. while ( trailing-- > 0 ) {
  1526. cindex <<= 6;
  1527. cindex += s[ ++idx ] & 0x0000003f;
  1528. }
  1529. idx++;
  1530. return cindex;
  1531. }
  1532. }
  1533. idx++;
  1534. return 0; // return a null terminator if out of range
  1535. }
  1536. /*
  1537. ================
  1538. idStr::LengthWithoutColors
  1539. ================
  1540. */
  1541. int idStr::LengthWithoutColors( const char *s ) {
  1542. int len;
  1543. const char *p;
  1544. if ( !s ) {
  1545. return 0;
  1546. }
  1547. len = 0;
  1548. p = s;
  1549. while( *p ) {
  1550. if ( idStr::IsColor( p ) ) {
  1551. p += 2;
  1552. continue;
  1553. }
  1554. p++;
  1555. len++;
  1556. }
  1557. return len;
  1558. }
  1559. /*
  1560. ================
  1561. idStr::RemoveColors
  1562. ================
  1563. */
  1564. char *idStr::RemoveColors( char *string ) {
  1565. char *d;
  1566. char *s;
  1567. int c;
  1568. s = string;
  1569. d = string;
  1570. while( (c = *s) != 0 ) {
  1571. if ( idStr::IsColor( s ) ) {
  1572. s++;
  1573. }
  1574. else {
  1575. *d++ = c;
  1576. }
  1577. s++;
  1578. }
  1579. *d = '\0';
  1580. return string;
  1581. }
  1582. /*
  1583. ================
  1584. idStr::snPrintf
  1585. ================
  1586. */
  1587. int idStr::snPrintf( char *dest, int size, const char *fmt, ...) {
  1588. int len;
  1589. va_list argptr;
  1590. char buffer[32000]; // big, but small enough to fit in PPC stack
  1591. va_start( argptr, fmt );
  1592. len = vsprintf( buffer, fmt, argptr );
  1593. va_end( argptr );
  1594. if ( len >= sizeof( buffer ) ) {
  1595. idLib::common->Error( "idStr::snPrintf: overflowed buffer" );
  1596. }
  1597. if ( len >= size ) {
  1598. idLib::common->Warning( "idStr::snPrintf: overflow of %i in %i\n", len, size );
  1599. len = size;
  1600. }
  1601. idStr::Copynz( dest, buffer, size );
  1602. return len;
  1603. }
  1604. /*
  1605. ============
  1606. idStr::vsnPrintf
  1607. vsnprintf portability:
  1608. C99 standard: vsnprintf returns the number of characters (excluding the trailing
  1609. '\0') which would have been written to the final string if enough space had been available
  1610. snprintf and vsnprintf do not write more than size bytes (including the trailing '\0')
  1611. win32: _vsnprintf returns the number of characters written, not including the terminating null character,
  1612. or a negative value if an output error occurs. If the number of characters to write exceeds count, then count
  1613. characters are written and -1 is returned and no trailing '\0' is added.
  1614. idStr::vsnPrintf: always appends a trailing '\0', returns number of characters written (not including terminal \0)
  1615. or returns -1 on failure or if the buffer would be overflowed.
  1616. ============
  1617. */
  1618. int idStr::vsnPrintf( char *dest, int size, const char *fmt, va_list argptr ) {
  1619. int ret;
  1620. #undef _vsnprintf
  1621. ret = _vsnprintf( dest, size-1, fmt, argptr );
  1622. #define _vsnprintf use_idStr_vsnPrintf
  1623. dest[size-1] = '\0';
  1624. if ( ret < 0 || ret >= size ) {
  1625. return -1;
  1626. }
  1627. return ret;
  1628. }
  1629. /*
  1630. ============
  1631. sprintf
  1632. Sets the value of the string using a printf interface.
  1633. ============
  1634. */
  1635. int sprintf( idStr &string, const char *fmt, ... ) {
  1636. int l;
  1637. va_list argptr;
  1638. char buffer[32000];
  1639. va_start( argptr, fmt );
  1640. l = idStr::vsnPrintf( buffer, sizeof(buffer)-1, fmt, argptr );
  1641. va_end( argptr );
  1642. buffer[sizeof(buffer)-1] = '\0';
  1643. string = buffer;
  1644. return l;
  1645. }
  1646. /*
  1647. ============
  1648. vsprintf
  1649. Sets the value of the string using a vprintf interface.
  1650. ============
  1651. */
  1652. int vsprintf( idStr &string, const char *fmt, va_list argptr ) {
  1653. int l;
  1654. char buffer[32000];
  1655. l = idStr::vsnPrintf( buffer, sizeof(buffer)-1, fmt, argptr );
  1656. buffer[sizeof(buffer)-1] = '\0';
  1657. string = buffer;
  1658. return l;
  1659. }
  1660. /*
  1661. ============
  1662. va
  1663. does a varargs printf into a temp buffer
  1664. NOTE: not thread safe
  1665. ============
  1666. */
  1667. char *va( const char *fmt, ... ) {
  1668. va_list argptr;
  1669. static int index = 0;
  1670. static char string[4][16384]; // in case called by nested functions
  1671. char *buf;
  1672. buf = string[index];
  1673. index = (index + 1) & 3;
  1674. va_start( argptr, fmt );
  1675. vsprintf( buf, fmt, argptr );
  1676. va_end( argptr );
  1677. return buf;
  1678. }
  1679. /*
  1680. ============
  1681. idStr::BestUnit
  1682. ============
  1683. */
  1684. int idStr::BestUnit( const char *format, float value, Measure_t measure ) {
  1685. int unit = 1;
  1686. while ( unit <= 3 && ( 1 << ( unit * 10 ) < value ) ) {
  1687. unit++;
  1688. }
  1689. unit--;
  1690. value /= 1 << ( unit * 10 );
  1691. sprintf( *this, format, value );
  1692. *this += " ";
  1693. *this += units[ measure ][ unit ];
  1694. return unit;
  1695. }
  1696. /*
  1697. ============
  1698. idStr::SetUnit
  1699. ============
  1700. */
  1701. void idStr::SetUnit( const char *format, float value, int unit, Measure_t measure ) {
  1702. value /= 1 << ( unit * 10 );
  1703. sprintf( *this, format, value );
  1704. *this += " ";
  1705. *this += units[ measure ][ unit ];
  1706. }
  1707. /*
  1708. ================
  1709. idStr::InitMemory
  1710. ================
  1711. */
  1712. void idStr::InitMemory() {
  1713. #ifdef USE_STRING_DATA_ALLOCATOR
  1714. stringDataAllocator.Init();
  1715. #endif
  1716. }
  1717. /*
  1718. ================
  1719. idStr::ShutdownMemory
  1720. ================
  1721. */
  1722. void idStr::ShutdownMemory() {
  1723. #ifdef USE_STRING_DATA_ALLOCATOR
  1724. stringDataAllocator.Shutdown();
  1725. #endif
  1726. }
  1727. /*
  1728. ================
  1729. idStr::PurgeMemory
  1730. ================
  1731. */
  1732. void idStr::PurgeMemory() {
  1733. #ifdef USE_STRING_DATA_ALLOCATOR
  1734. stringDataAllocator.FreeEmptyBaseBlocks();
  1735. #endif
  1736. }
  1737. /*
  1738. ================
  1739. idStr::ShowMemoryUsage_f
  1740. ================
  1741. */
  1742. void idStr::ShowMemoryUsage_f( const idCmdArgs &args ) {
  1743. #ifdef USE_STRING_DATA_ALLOCATOR
  1744. idLib::common->Printf( "%6d KB string memory (%d KB free in %d blocks, %d empty base blocks)\n",
  1745. stringDataAllocator.GetBaseBlockMemory() >> 10, stringDataAllocator.GetFreeBlockMemory() >> 10,
  1746. stringDataAllocator.GetNumFreeBlocks(), stringDataAllocator.GetNumEmptyBaseBlocks() );
  1747. #endif
  1748. }
  1749. /*
  1750. ================
  1751. idStr::FormatNumber
  1752. ================
  1753. */
  1754. struct formatList_t {
  1755. int gran;
  1756. int count;
  1757. };
  1758. // elements of list need to decend in size
  1759. formatList_t formatList[] = {
  1760. { 1000000000, 0 },
  1761. { 1000000, 0 },
  1762. { 1000, 0 }
  1763. };
  1764. int numFormatList = sizeof(formatList) / sizeof( formatList[0] );
  1765. idStr idStr::FormatNumber( int number ) {
  1766. idStr string;
  1767. bool hit;
  1768. // reset
  1769. for ( int i = 0; i < numFormatList; i++ ) {
  1770. formatList_t *li = formatList + i;
  1771. li->count = 0;
  1772. }
  1773. // main loop
  1774. do {
  1775. hit = false;
  1776. for ( int i = 0; i < numFormatList; i++ ) {
  1777. formatList_t *li = formatList + i;
  1778. if ( number >= li->gran ) {
  1779. li->count++;
  1780. number -= li->gran;
  1781. hit = true;
  1782. break;
  1783. }
  1784. }
  1785. } while ( hit );
  1786. // print out
  1787. bool found = false;
  1788. for ( int i = 0; i < numFormatList; i++ ) {
  1789. formatList_t *li = formatList + i;
  1790. if ( li->count ) {
  1791. if ( !found ) {
  1792. string += va( "%i,", li->count );
  1793. } else {
  1794. string += va( "%3.3i,", li->count );
  1795. }
  1796. found = true;
  1797. }
  1798. else if ( found ) {
  1799. string += va( "%3.3i,", li->count );
  1800. }
  1801. }
  1802. if ( found ) {
  1803. string += va( "%3.3i", number );
  1804. }
  1805. else {
  1806. string += va( "%i", number );
  1807. }
  1808. // pad to proper size
  1809. int count = 11 - string.Length();
  1810. for ( int i = 0; i < count; i++ ) {
  1811. string.Insert( " ", 0 );
  1812. }
  1813. return string;
  1814. }
  1815. CONSOLE_COMMAND( testStrId, "prints a localized string", 0 ) {
  1816. if ( args.Argc() != 2 ) {
  1817. idLib::Printf( "need a str id like 'STR_SWF_ACCEPT' without the hash, it gets parsed as a separate argument\n" );
  1818. return;
  1819. }
  1820. idStrId str( va( "#%s", args.Argv( 1 ) ) );
  1821. idLib::Printf( "%s = %s\n", args.Argv( 1 ), str.GetLocalizedString() );
  1822. }