home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2006 March / Gamestar_82_2006-03_dvd.iso / DVDStar / Editace / quake4_sdkv10.exe / source / idlib / Str.cpp < prev    next >
C/C++ Source or Header  |  2005-11-14  |  46KB  |  2,215 lines

  1.  
  2. #include "precompiled.h"
  3. #pragma hdrstop
  4.  
  5. // TTimo - don't do anything funky if you're on a real OS ;-)
  6. #if defined(_WINDOWS) || defined(_XENON)
  7.  
  8. // RAVEN BEGIN
  9. // jsinger: attempt at removing the DLL cross boundary issue
  10. // mwhitlock: Dynamic memory consolidation - may want to consier making a totally separate string allocator
  11. #if ( !defined(ID_REDIRECT_NEWDELETE) && !defined(RV_UNIFIED_ALLOCATOR) ) || defined(_RV_MEM_SYS_SUPPORT)
  12. // RAVEN END
  13.     #define USE_STRING_DATA_ALLOCATOR
  14. #endif
  15.  
  16. #endif
  17.  
  18. #ifdef USE_STRING_DATA_ALLOCATOR
  19. static idDynamicBlockAlloc<char, 1<<18, 128, MA_STRING>    stringDataAllocator;
  20. #endif
  21.  
  22. idVec4    g_color_table[16] =
  23. {
  24.     idVec4(0.0f, 0.0f, 0.0f, 1.0f),
  25.     idVec4(1.0f, 0.0f, 0.0f, 1.0f), // S_COLOR_RED
  26.     idVec4(0.0f, 1.0f, 0.0f, 1.0f), // S_COLOR_GREEN
  27.     idVec4(1.0f, 1.0f, 0.0f, 1.0f), // S_COLOR_YELLOW
  28.     idVec4(0.0f, 0.0f, 1.0f, 1.0f), // S_COLOR_BLUE
  29.     idVec4(0.0f, 1.0f, 1.0f, 1.0f), // S_COLOR_CYAN
  30.     idVec4(1.0f, 0.0f, 1.0f, 1.0f), // S_COLOR_MAGENTA
  31.     idVec4(1.0f, 1.0f, 1.0f, 1.0f), // S_COLOR_WHITE
  32.     idVec4(0.5f, 0.5f, 0.5f, 1.0f), // S_COLOR_GRAY
  33.     idVec4(0.0f, 0.0f, 0.0f, 1.0f), // S_COLOR_BLACK
  34. // RAVEN BEGIN
  35. // bdube: console color
  36.     idVec4(0.94f, 0.62f, 0.05f, 1.0f),    // S_COLOR_CONSOLE
  37. // RAVEN END    
  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.  
  45. const char *units[2][4] =
  46. {
  47.     { "B", "KB", "MB", "GB" },
  48.     { "B/s", "KB/s", "MB/s", "GB/s" }
  49. };
  50.  
  51. const bool idStr::printableCharacter[256] =
  52. {
  53. //
  54.     false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
  55. //
  56.     false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
  57. //           !      "      #      $      &      %      '      (      )      *      +      ,      -      .      /
  58.     true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
  59. //  0      1      2      3      4      5      6      7      8      9      :      ;      <      =      >      ?
  60.     true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
  61. //  @      A      B      C      D      E      F      G      H      I      J      K      L      M      N      O
  62.     true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
  63. //  P      Q      R      S      T      U      V      W      X      Y      Z      [      \      ]      ^      _
  64.     true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
  65. //  `      a      b      c      d      e      f      g      h      i      j      k      l      m      n      o
  66.     true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
  67. //  p      q      r      s      t      u      v      w      x      y      z      {      |      }      ~
  68.     true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  false,
  69. //  Ç             X                                                                                                                                                        î
  70.     true,  true,  false, false, false, false, false, false, false, false, false, false, true,  false, false, false,
  71. //                                                                 Ö                    £
  72.     false, false, false, false, false, false, false, false, false, true,  false, false, true,  false, false, false,
  73. //         í             ú      ñ      Ñ             º             ⌐             ½                    «
  74.     false, true,  false, true,  true,  true,  false, true,  false, true,  false, true,  false, false, true,  false,
  75. //  ░                           ┤      ╡             ╖                           ╗                           ┐
  76.     true,  false, false, false, true,  true,  false, true,  false, false, false, true,  false, false, false, true,
  77. //  └      ┴      ┬      ├      ─      ┼      ╞      ╟      ╚      ╔      ╩      ╦      ╠      ═      ╬      ╧
  78.     true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
  79. //  ╨      ╤      ╥      ╙      ╘      ╒      ╓      ╫      ╪      ┘      ┌      █      ▄      ▌      ▐      ▀
  80.     true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
  81. //  α      ß      Γ      π      Σ      σ      µ      τ      Φ      Θ      Ω      δ      ∞      φ      ε      ∩
  82.     true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
  83. //  ≡      ±      ≥      ≤      ⌠      ⌡      ÷      ≈      °      ∙      ·      √      ⁿ      ²      ■       
  84.     true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
  85. };
  86.  
  87. const char idStr::upperCaseCharacter[256] =
  88. {
  89. //
  90.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  91. //
  92.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  93. //         !    "    #    $    &    %    '    (    )    *    +    ,    -    .    /
  94.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  95. //  0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?
  96.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  97. //  @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O
  98.     0,   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
  99. //  P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _
  100.     'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0,   0,   0,   0,   0,
  101. //  `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o
  102.     0,   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
  103. //  p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~
  104.     'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0,   0,   0,   0,   0,
  105. //  Ç         X                                                                                                             î
  106.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   'î', 0,   0,   0,
  107. //                                               TM             £
  108.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   'î', 0,   0,   0,
  109. //       í         ú    ñ    Ñ         º         ⌐         ½              «
  110.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  111. //  ░                   ┤    ╡         ╖                   ╗                   ┐
  112.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  113. //  └    ┴    ┬    ├    ─    ┼    ╞    ╟    ╚    ╔    ╩    ╦    ╠    ═    ╬    ╧
  114.     '└', '┴', '┬', '├', '─', '┼', '╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧',
  115. //  ╨    ╤    ╥    ╙    ╘    ╒    ╓    ╫    ╪    ┘    ┌    █    ▄    ▌    ▐    ▀
  116.     '╨', '╤', '╥', '╙', '╘', '╒', '╓', 0,   '╪', '┘', '┌', '█', '▄', '▌', '▐', '▀',
  117. //  α    ß    Γ    π    Σ    σ    µ    τ    Φ    Θ    Ω    δ    ∞    φ    ε    ∩
  118.     '└', '┴', '┬', '├', '─', '┼', '╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧',
  119. //  ≡    ±    ≥    ≤    ⌠    ⌡    ÷    ≈    °    ∙    ·    √    ⁿ    ²    ■     
  120.     '╨', '╤', '╥', '╙', '╘', '╒', '╓', 0,   '╪', '┘', '┌', '█', '▄', '▌', '▐', '▀',
  121. };
  122.  
  123. const char idStr::lowerCaseCharacter[256] =
  124. {
  125. //
  126.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  127. //
  128.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  129. //         !    "    #    $    &    %    '    (    )    *    +    ,    -    .    /
  130.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  131. //  0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?
  132.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  133. //  @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O
  134.     0,   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
  135. //  P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _
  136.     'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0,   0,   0,   0,   0,
  137. //  `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o
  138.     0,   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
  139. //  p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~
  140.     'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0,   0,   0,   0,   0,
  141. //  Ç         X                                                                                                             î
  142.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   '£', 0,   0,   0,
  143. //                                               TM             £
  144.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   '£', 0,   0,   0,
  145. //       í         ú    ñ    Ñ         º         ⌐         ½              «
  146.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  147. //  ░                   ┤    ╡         ╖                   ╗                   ┐
  148.     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  149. //  └    ┴    ┬    ├    ─    ┼    ╞    ╟    ╚    ╔    ╩    ╦    ╠    ═    ╬    ╧
  150.     'α', 'ß', 'Γ', 'π', 'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ', 'ε', '∩',
  151. //  ╨    ╤    ╥    ╙    ╘    ╒    ╓    ╫    ╪    ┘    ┌    █    ▄    ▌    ▐    ▀
  152.     '≡', '±', '≥', '≤', '⌠', '⌡', '÷', 0,   '°', '∙', '·', '√', 'ⁿ', '²', '■', '▀',
  153. //  α    ß    Γ    π    Σ    σ    µ    τ    Φ    Θ    Ω    δ    ∞    φ    ε    ∩
  154.     'α', 'ß', 'Γ', 'π', 'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ', 'ε', '∩',
  155. //  ≡    ±    ≥    ≤    ⌠    ⌡    ÷    ≈    °    ∙    ·    √    ⁿ    ²    ■     
  156.     '≡', '±', '≥', '≤', '⌠', '⌡', '÷', 0,   '°', '∙', '·', '√', 'ⁿ', '²', '■', '▀',
  157. };
  158. // RAVEN END
  159.  
  160. /*
  161. ============
  162. idStr::ColorForIndex
  163. ============
  164. */
  165. idVec4 & idStr::ColorForIndex( int i ) {
  166.     return g_color_table[ i & 15 ];
  167. }
  168.  
  169. /*
  170. ============
  171. idStr::ReAllocate
  172. ============
  173. */
  174. void idStr::ReAllocate( int amount, bool keepold ) {
  175.     char    *newbuffer;
  176.     int        newsize;
  177.     int        mod;
  178.  
  179.     //assert( data );
  180.     assert( amount > 0 );
  181.  
  182.     mod = amount % STR_ALLOC_GRAN;
  183.     if ( !mod ) {
  184.         newsize = amount;
  185.     }
  186.     else {
  187.         newsize = amount + STR_ALLOC_GRAN - mod;
  188.     }
  189.     alloced = newsize;
  190.  
  191. #ifdef USE_STRING_DATA_ALLOCATOR
  192.  
  193. // RAVEN BEGIN
  194. // mwhitlock: Dynamic memory consolidation
  195. #if defined(_RV_MEM_SYS_SUPPORT)
  196.     RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_PERMANENT);
  197. #endif
  198. // RAVEN END
  199.  
  200.     newbuffer = stringDataAllocator.Alloc( alloced );
  201.  
  202. // RAVEN BEGIN
  203. // mwhitlock: Dynamic memory consolidation
  204. #if defined(_RV_MEM_SYS_SUPPORT)
  205.     RV_POP_HEAP();
  206. #endif
  207. // RAVEN END
  208.  
  209. #else
  210.     newbuffer = new char[ alloced ];
  211. #endif
  212.     if ( keepold && data ) {
  213.         data[ len ] = '\0';
  214.         strcpy( newbuffer, data );
  215.     }
  216.  
  217.     if ( data && data != baseBuffer ) {
  218. #ifdef USE_STRING_DATA_ALLOCATOR
  219.         stringDataAllocator.Free( data );
  220. #else
  221.         delete [] data;
  222. #endif
  223.     }
  224.  
  225.     data = newbuffer;
  226. }
  227.  
  228. /*
  229. ============
  230. idStr::FreeData
  231. ============
  232. */
  233. void idStr::FreeData( void ) {
  234.     if ( data && data != baseBuffer ) {
  235. #ifdef USE_STRING_DATA_ALLOCATOR
  236. // RAVEN BEGIN
  237. // jnewquist: Ignore free request if allocator is empty, probably shutdown
  238.         if ( stringDataAllocator.GetNumUsedBlocks() > 0 ) {
  239.             stringDataAllocator.Free( data );
  240.         }
  241. // RAVEN END
  242. #else
  243.         delete[] data;
  244. #endif
  245.  
  246. // RAVEN BEGIN
  247. // jsinger: was exhibiting a buffer overrun when an idStr was contained in an idList
  248. // due to having the wrong alloced value.  This corrects that
  249.         Init();
  250. // RAVEN END
  251.     }
  252. }
  253.  
  254. /*
  255. ============
  256. idStr::operator=
  257. ============
  258. */
  259. void idStr::operator=( const char *text ) {
  260.     int l;
  261.     int diff;
  262.     int i;
  263.  
  264.     if ( !text ) {
  265.         // safe behaviour if NULL
  266.         EnsureAlloced( 1, false );
  267.         data[ 0 ] = '\0';
  268.         len = 0;
  269.         return;
  270.     }
  271.  
  272.     if ( text == data ) {
  273.         return; // copying same thing
  274.     }
  275.  
  276.     // check if we're aliasing
  277.     if ( text >= data && text <= data + len ) {
  278.         diff = text - data;
  279.  
  280.         assert( strlen( text ) < (unsigned)len );
  281.  
  282.         for ( i = 0; text[ i ]; i++ ) {
  283.             data[ i ] = text[ i ];
  284.         }
  285.  
  286.         data[ i ] = '\0';
  287.  
  288.         len -= diff;
  289.  
  290.         return;
  291.     }
  292.  
  293.     l = strlen( text );
  294.     EnsureAlloced( l + 1, false );
  295.     strcpy( data, text );
  296.     len = l;
  297. }
  298.  
  299. /*
  300. ============
  301. idStr::FindChar
  302.  
  303. returns -1 if not found otherwise the index of the char
  304. ============
  305. */
  306. int idStr::FindChar( const char *str, const char c, int start, int end ) {
  307.     int i;
  308.  
  309.     if ( end == -1 ) {
  310.         end = strlen( str ) - 1;
  311.     }
  312.     for ( i = start; i <= end; i++ ) {
  313.         if ( str[i] == c ) {
  314.             return i;
  315.         }
  316.     }
  317.     return -1;
  318. }
  319.  
  320. /*
  321. ============
  322. idStr::FindText
  323.  
  324. returns -1 if not found otherwise the index of the text
  325. ============
  326. */
  327. int idStr::FindText( const char *str, const char *text, bool casesensitive, int start, int end ) {
  328.     int l, i, j;
  329.  
  330.     if ( end == -1 ) {
  331.         end = strlen( str );
  332.     }
  333.     l = end - strlen( text );
  334.     for ( i = start; i <= l; i++ ) {
  335.         if ( casesensitive ) {
  336.             for ( j = 0; text[j]; j++ ) {
  337.                 if ( str[i+j] != text[j] ) {
  338.                     break;
  339.                 }
  340.             }
  341.         } else {
  342.             for ( j = 0; text[j]; j++ ) {
  343.                 if ( ::toupper( str[i+j] ) != ::toupper( text[j] ) ) {
  344.                     break;
  345.                 }
  346.             }
  347.         }
  348.         if ( !text[j] ) {
  349.             return i;
  350.         }
  351.     }
  352.     return -1;
  353. }
  354.  
  355. /*
  356. ============
  357. idStr::Filter
  358.  
  359. Returns true if the string conforms the given filter.
  360. Several metacharacter may be used in the filter.
  361.  
  362. *          match any string of zero or more characters
  363. ?          match any single character
  364. [abc...]   match any of the enclosed characters; a hyphen can
  365.            be used to specify a range (e.g. a-z, A-Z, 0-9)
  366.  
  367. ============
  368. */
  369. bool idStr::Filter( const char *filter, const char *name, bool casesensitive ) {
  370.     idStr buf;
  371.     int i, found, index;
  372.  
  373.     while(*filter) {
  374.         if (*filter == '*') {
  375.             filter++;
  376.             buf.Empty();
  377.             for (i = 0; *filter; i++) {
  378.                 if ( *filter == '*' || *filter == '?' || (*filter == '[' && *(filter+1) != '[') ) {
  379.                     break;
  380.                 }
  381.                 buf += *filter;
  382.                 if ( *filter == '[' ) {
  383.                     filter++;
  384.                 }
  385.                 filter++;
  386.             }
  387.             if ( buf.Length() ) {
  388.                 index = idStr(name).Find( buf.c_str(), casesensitive );
  389.                 if ( index == -1 ) {
  390.                     return false;
  391.                 }
  392.                 name += index + strlen(buf);
  393.             }
  394.         }
  395.         else if (*filter == '?') {
  396.             filter++;
  397.             name++;
  398.         }
  399.         else if (*filter == '[') {
  400.             if ( *(filter+1) == '[' ) {
  401.                 if ( *name != '[' ) {
  402.                     return false;
  403.                 }
  404.                 filter += 2;
  405.                 name++;
  406.             }
  407.             else {
  408.                 filter++;
  409.                 found = false;
  410.                 while(*filter && !found) {
  411.                     if (*filter == ']' && *(filter+1) != ']') {
  412.                         break;
  413.                     }
  414.                     if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) {
  415.                         if (casesensitive) {
  416.                             if (*name >= *filter && *name <= *(filter+2)) {
  417.                                 found = true;
  418.                             }
  419.                         }
  420.                         else {
  421.                             if ( ::toupper(*name) >= ::toupper(*filter) && ::toupper(*name) <= ::toupper(*(filter+2)) ) {
  422.                                 found = true;
  423.                             }
  424.                         }
  425.                         filter += 3;
  426.                     }
  427.                     else {
  428.                         if (casesensitive) {
  429.                             if (*filter == *name) {
  430.                                 found = true;
  431.                             }
  432.                         }
  433.                         else {
  434.                             if ( ::toupper(*filter) == ::toupper(*name) ) {
  435.                                 found = true;
  436.                             }
  437.                         }
  438.                         filter++;
  439.                     }
  440.                 }
  441.                 if (!found) {
  442.                     return false;
  443.                 }
  444.                 while(*filter) {
  445.                     if ( *filter == ']' && *(filter+1) != ']' ) {
  446.                         break;
  447.                     }
  448.                     filter++;
  449.                 }
  450.                 filter++;
  451.                 name++;
  452.             }
  453.         }
  454.         else {
  455.             if (casesensitive) {
  456.                 if (*filter != *name) {
  457.                     return false;
  458.                 }
  459.             }
  460.             else {
  461.                 if ( ::toupper(*filter) != ::toupper(*name) ) {
  462.                     return false;
  463.                 }
  464.             }
  465.             filter++;
  466.             name++;
  467.         }
  468.     }
  469.     return true;
  470. }
  471.  
  472. /*
  473. =============
  474. idStr::StripMediaName
  475.  
  476.   makes the string lower case, replaces backslashes with forward slashes, and removes extension
  477. =============
  478. */
  479. void idStr::StripMediaName( const char *name, idStr &mediaName ) {
  480.     char c;
  481.  
  482.     mediaName.Empty();
  483.  
  484.     for ( c = *name; c; c = *(++name) ) {
  485.         // truncate at an extension
  486.         if ( c == '.' ) {
  487.             break;
  488.         }
  489.         // convert backslashes to forward slashes
  490.         if ( c == '\\' ) {
  491.             mediaName.Append( '/' );
  492.         } else {
  493.             mediaName.Append( idStr::ToLower( c ) );
  494.         }
  495.     }
  496. }
  497.  
  498. /*
  499. =============
  500. idStr::CheckExtension
  501. =============
  502. */
  503. bool idStr::CheckExtension( const char *name, const char *ext ) {
  504.     const char *s1 = name + Length( name ) - 1;
  505.     const char *s2 = ext + Length( ext ) - 1;
  506.     int c1, c2, d;
  507.  
  508.     do {
  509.         c1 = *s1--;
  510.         c2 = *s2--;
  511.  
  512.         d = c1 - c2;
  513.         while( d ) {
  514.             if ( c1 <= 'Z' && c1 >= 'A' ) {
  515.                 d += ('a' - 'A');
  516.                 if ( !d ) {
  517.                     break;
  518.                 }
  519.             }
  520.             if ( c2 <= 'Z' && c2 >= 'A' ) {
  521.                 d -= ('a' - 'A');
  522.                 if ( !d ) {
  523.                     break;
  524.                 }
  525.             }
  526.             return false;
  527.         }
  528.     } while( s1 > name && s2 > ext );
  529.  
  530.     return ( s1 >= name );
  531. }
  532.  
  533. /*
  534. =============
  535. idStr::FloatArrayToString
  536. =============
  537. */
  538. const char *idStr::FloatArrayToString( const float *array, const int length, const int precision ) {
  539.     static int index = 0;
  540.     static char str[4][16384];    // in case called by nested functions
  541.     int i, n;
  542.     char format[16], *s;
  543.  
  544.     // use an array of string so that multiple calls won't collide
  545.     s = str[ index ];
  546.     index = (index + 1) & 3;
  547.  
  548.     idStr::snPrintf( format, sizeof( format ), "%%.%df", precision );
  549.     n = idStr::snPrintf( s, sizeof( str[0] ), format, array[0] );
  550.     if ( precision > 0 ) {
  551.         while( n > 0 && s[n-1] == '0' ) s[--n] = '\0';
  552.         while( n > 0 && s[n-1] == '.' ) s[--n] = '\0';
  553.     }
  554.     idStr::snPrintf( format, sizeof( format ), " %%.%df", precision );
  555.     for ( i = 1; i < length; i++ ) {
  556.         n += idStr::snPrintf( s + n, sizeof( str[0] ) - n, format, array[i] );
  557.         if ( precision > 0 ) {
  558.             while( n > 0 && s[n-1] == '0' ) s[--n] = '\0';
  559.             while( n > 0 && s[n-1] == '.' ) s[--n] = '\0';
  560.         }
  561.     }
  562.     return s;
  563. }
  564.  
  565. /*
  566. ============
  567. idStr::Last
  568.  
  569. returns -1 if not found otherwise the index of the char
  570. ============
  571. */
  572. int idStr::Last( const char c ) const {
  573.     int i;
  574.     
  575.     for( i = Length(); i > 0; i-- ) {
  576.         if ( data[ i - 1 ] == c ) {
  577.             return i - 1;
  578.         }
  579.     }
  580.  
  581.     return -1;
  582. }
  583.  
  584. /*
  585. ============
  586. idStr::StripLeading
  587. ============
  588. */
  589. void idStr::StripLeading( const char c ) {
  590.     while( data[ 0 ] == c ) {
  591.         memmove( &data[ 0 ], &data[ 1 ], len );
  592.         len--;
  593.     }
  594. }
  595.  
  596. /*
  597. ============
  598. idStr::StripLeading
  599. ============
  600. */
  601. void idStr::StripLeading( const char *string ) {
  602.     int l;
  603.  
  604.     l = strlen( string );
  605.     if ( l > 0 ) {
  606.         while ( !Cmpn( string, l ) ) {
  607.             memmove( data, data + l, len - l + 1 );
  608.             len -= l;
  609.         }
  610.     }
  611. }
  612.  
  613. /*
  614. ============
  615. idStr::StripLeadingOnce
  616. ============
  617. */
  618. bool idStr::StripLeadingOnce( const char *string ) {
  619.     int l;
  620.  
  621.     l = strlen( string );
  622.     if ( ( l > 0 ) && !Cmpn( string, l ) ) {
  623.         memmove( data, data + l, len - l + 1 );
  624.         len -= l;
  625.         return true;
  626.     }
  627.     return false;
  628. }
  629.  
  630. /*
  631. ============
  632. idStr::StripTrailing
  633. ============
  634. */
  635. void idStr::StripTrailing( const char c ) {
  636.     int i;
  637.     
  638.     for( i = Length(); i > 0 && data[ i - 1 ] == c; i-- ) {
  639.         data[ i - 1 ] = '\0';
  640.         len--;
  641.     }
  642. }
  643.  
  644. /*
  645. ============
  646. idStr::StripLeading
  647. ============
  648. */
  649. void idStr::StripTrailing( const char *string ) {
  650.     int l;
  651.  
  652.     l = strlen( string );
  653.     if ( l > 0 ) {
  654.         while ( ( len >= l ) && !Cmpn( string, data + len - l, l ) ) {
  655.             len -= l;
  656.             data[len] = '\0';
  657.         }
  658.     }
  659. }
  660.  
  661. /*
  662. ============
  663. idStr::StripTrailingOnce
  664. ============
  665. */
  666. bool idStr::StripTrailingOnce( const char *string ) {
  667.     int l;
  668.  
  669.     l = strlen( string );
  670.     if ( ( l > 0 ) && ( len >= l ) && !Cmpn( string, data + len - l, l ) ) {
  671.         len -= l;
  672.         data[len] = '\0';
  673.         return true;
  674.     }
  675.     return false;
  676. }
  677.  
  678. /*
  679. ============
  680. idStr::Replace
  681. ============
  682. */
  683. // RAVEN BEGIN
  684. // scork: this needs to return an int of the replacement count, like MS CString etc, otherwise you can't do things like this:
  685. //
  686. // idStr str;
  687. //  while (str.Replace("\n\n","\n")) {};
  688. //
  689. // ... to remove blank lines from an output string if you have 3 blank lines in a row. Without the while(), you'd just be guessing about
  690. //    converting (eg) 3 blank lines to 2, then 2 to 1, depending on how many times you straight-line the Replace() call.
  691. //
  692. int idStr::Replace( const char *old, const char *nw ) {
  693.     int        iReplaced = 0;
  694.     int        oldLen, newLen, i, j, count;
  695.     idStr    oldString( data );
  696.  
  697.     oldLen = strlen( old );
  698.     newLen = strlen( nw );
  699.  
  700.     // Work out how big the new string will be
  701.     count = 0;
  702.     for( i = 0; i < oldString.Length(); i++ ) {
  703.         if( !idStr::Cmpn( &oldString[i], old, oldLen ) ) {
  704.             count++;
  705.             i += oldLen - 1;
  706.         }
  707.     }
  708.  
  709.     if( count ) {
  710.         EnsureAlloced( len + ( ( newLen - oldLen ) * count ) + 2, false );
  711.  
  712.         // Replace the old data with the new data
  713.         for( i = 0, j = 0; i < oldString.Length(); i++ ) {
  714.             if( !idStr::Cmpn( &oldString[i], old, oldLen ) ) {
  715.                 memcpy( data + j, nw, newLen );
  716.                 i += oldLen - 1;
  717.                 j += newLen;
  718.                 iReplaced++;
  719.             } else {
  720.                 data[j] = oldString[i];
  721.                 j++;
  722.             }
  723.         }
  724.         data[j] = 0;
  725.         len = strlen( data );
  726.     }
  727.     return iReplaced;
  728. }
  729. // RAVEN END
  730.  
  731. /*
  732. ============
  733. idStr::Mid
  734. ============
  735. */
  736. const char *idStr::Mid( int start, int len, idStr &result ) const {
  737.     int i;
  738.  
  739.     result.Empty();
  740.  
  741.     i = Length();
  742.     if ( i == 0 || len <= 0 || start >= i ) {
  743.         return NULL;
  744.     }
  745.  
  746.     if ( start + len >= i ) {
  747.         len = i - start;
  748.     }
  749.  
  750.     result.Append( &data[ start ], len );
  751.     return result;
  752. }
  753.  
  754. /*
  755. ============
  756. idStr::Mid
  757. ============
  758. */
  759. idStr idStr::Mid( int start, int len ) const {
  760.     int i;
  761.     idStr result;
  762.  
  763.     i = Length();
  764.     if ( i == 0 || len <= 0 || start >= i ) {
  765.         return result;
  766.     }
  767.  
  768.     if ( start + len >= i ) {
  769.         len = i - start;
  770.     }
  771.  
  772.     result.Append( &data[ start ], len );
  773.     return result;
  774. }
  775.  
  776. /*
  777. ============
  778. idStr::StripTrailingWhitespace
  779. ============
  780. */
  781. void idStr::StripTrailingWhitespace( void ) {
  782.     int i;
  783.     
  784.     // cast to unsigned char to prevent stripping off high-ASCII characters
  785.     for( i = Length(); i > 0 && (unsigned char)(data[ i - 1 ]) <= ' '; i-- ) {
  786.         data[ i - 1 ] = '\0';
  787.         len--;
  788.     }
  789. }
  790.  
  791. // RAVEN BEGIN
  792. /*
  793. ============
  794. idStr::StripUntil
  795. ============
  796. */
  797. void idStr::StripUntil( const char c )
  798. {
  799.     while( data[ 0 ] != c && len ) {
  800.         memmove( &data[ 0 ], &data[ 1 ], len );
  801.         len--;
  802.     }
  803. }
  804. // RAVEN END
  805.  
  806. /*
  807. =====================================================================
  808.  
  809.   info strings
  810.  
  811. =====================================================================
  812. */
  813.  
  814. /*
  815. ============
  816. idStr::StripQuotes
  817.  
  818. Removes the quotes from the beginning and end of the string
  819. ============
  820. */
  821. idStr& idStr::StripQuotes ( void )
  822. {
  823.     if ( data[0] != '\"' )
  824.     {
  825.         return *this;
  826.     }
  827.     
  828.     // Remove the trailing quote first
  829.     if ( data[len-1] == '\"' )
  830.     {
  831.         data[len-1] = '\0';
  832.         len--;
  833.     }
  834.  
  835.     // Strip the leading quote now
  836.     len--;    
  837.     memmove( &data[ 0 ], &data[ 1 ], len );
  838.     data[len] = '\0';
  839.     
  840.     return *this;
  841. }
  842.  
  843. /*
  844. =====================================================================
  845.  
  846.   filename methods
  847.  
  848. =====================================================================
  849. */
  850.  
  851. /*
  852. ============
  853. idStr::FileNameHash
  854. ============
  855. */
  856. int idStr::FileNameHash( void ) const {
  857.     int        i;
  858.     long    hash;
  859.     char    letter;
  860.  
  861.     hash = 0;
  862.     i = 0;
  863.     while( data[i] != '\0' ) {
  864.         letter = idStr::ToLower( data[i] );
  865.         if ( letter == '.' ) {
  866.             break;                // don't include extension
  867.         }
  868.         if ( letter =='\\' ) {
  869.             letter = '/';
  870.         }
  871.         hash += (long)(letter)*(i+119);
  872.         i++;
  873.     }
  874.     hash &= (FILE_HASH_SIZE-1);
  875.     return hash;
  876. }
  877.  
  878. /*
  879. ============
  880. idStr::BackSlashesToSlashes
  881. ============
  882. */
  883. idStr &idStr::BackSlashesToSlashes( void ) {
  884.     int i;
  885.  
  886.     for ( i = 0; i < len; i++ ) {
  887.         if ( data[ i ] == '\\' ) {
  888.             data[ i ] = '/';
  889.         }
  890.     }
  891.     return *this;
  892. }
  893.  
  894. // RAVEN BEGIN
  895. // jscott: for file systems that require backslashes
  896. /*
  897. ============
  898. idStr::SlashesToBackSlashes
  899. ============
  900. */
  901. idStr &idStr::SlashesToBackSlashes( void ) {
  902.     int i;
  903.  
  904.     for ( i = 0; i < len; i++ ) {
  905.         if ( data[ i ] == '/' ) {
  906.             data[ i ] = '\\';
  907.         }
  908.     }
  909.     return *this;
  910. }
  911.  
  912. // nmckenzie: char replacing routine
  913.  
  914. /*
  915. ============
  916. idStr::SlashesToBackSlashes
  917. ============
  918. */
  919.  
  920. bool idStr::HasChar( const char check ){
  921.     int i;
  922.  
  923.     for ( i = 0; i < len; i++ ) {
  924.         if ( data[ i ] == check ) {
  925.             return true;
  926.         }
  927.     }
  928.     return false;
  929. }
  930.  
  931. /*
  932. ============
  933. idStr::HasChars
  934. ============
  935. */
  936.  
  937. bool idStr::HasChars( const char *check ){
  938.     int i;
  939.  
  940.     while( *check ){
  941.         for ( i = 0; i < len; i++ ) {
  942.             if ( data[ i ] == *check ) {
  943.                 return true;
  944.             }
  945.         }
  946.         check++;
  947.     }
  948.     return false;
  949. }
  950.  
  951. /*
  952. ============
  953. idStr::ReplaceChar
  954. ============
  955. */
  956.  
  957. idStr &idStr::ReplaceChar( const char from, const char to ) {
  958.     int i;
  959.  
  960.     for ( i = 0; i < len; i++ ) {
  961.         if ( data[ i ] == from ) {
  962.             data[ i ] = to;
  963.         }
  964.     }
  965.     return *this;
  966. }
  967.  
  968. /*
  969. ============
  970. idStr::ReplaceChars
  971. ============
  972. */
  973.  
  974. idStr &idStr::ReplaceChars( const char *from, const char to ) {
  975.     int i;
  976.  
  977.     while( *from ){
  978.         for ( i = 0; i < len; i++ ) {
  979.             if ( data[ i ] == *from ) {
  980.                 data[ i ] = to;
  981.             }
  982.         }
  983.         from++;
  984.     }
  985.     return *this;
  986. }
  987.  
  988. // RAVEN END
  989.  
  990. /*
  991. ============
  992. idStr::SetFileExtension
  993. ============
  994. */
  995. idStr &idStr::SetFileExtension( const char *extension ) {
  996.     StripFileExtension();
  997.     if ( *extension != '.' ) {
  998.         Append( '.' );
  999.     }
  1000.     Append( extension );
  1001.     return *this;
  1002. }
  1003.  
  1004. /*
  1005. ============
  1006. idStr::StripFileExtension
  1007. ============
  1008. */
  1009. idStr &idStr::StripFileExtension( void ) {
  1010.     int i;
  1011.  
  1012.     for ( i = len-1; i >= 0; i-- ) {
  1013.         if ( data[i] == '.' ) {
  1014.             data[i] = '\0';
  1015.             len = i;
  1016.             break;
  1017.         }
  1018.     }
  1019.     return *this;
  1020. }
  1021.  
  1022. /*
  1023. ============
  1024. idStr::StripAbsoluteFileExtension
  1025. ============
  1026. */
  1027. idStr &idStr::StripAbsoluteFileExtension( void ) {
  1028.     int i;
  1029.  
  1030.     for ( i = 0; i < len; i++ ) {
  1031.         if ( data[i] == '.' ) {
  1032.             data[i] = '\0';
  1033.             len = i;
  1034.             break;
  1035.         }
  1036.     }
  1037.  
  1038.     return *this;
  1039. }
  1040.  
  1041. /*
  1042. ==================
  1043. idStr::DefaultFileExtension
  1044. ==================
  1045. */
  1046. idStr &idStr::DefaultFileExtension( const char *extension ) {
  1047.     int i;
  1048.  
  1049.     // do nothing if the string already has an extension
  1050.     for ( i = len-1; i >= 0; i-- ) {
  1051.         if ( data[i] == '.' ) {
  1052.             return *this;
  1053.         }
  1054.     }
  1055.     if ( *extension != '.' ) {
  1056.         Append( '.' );
  1057.     }
  1058.     Append( extension );
  1059.     return *this;
  1060. }
  1061.  
  1062. /*
  1063. ==================
  1064. idStr::DefaultPath
  1065. ==================
  1066. */
  1067. idStr &idStr::DefaultPath( const char *basepath ) {
  1068.     if ( ( ( *this )[ 0 ] == '/' ) || ( ( *this )[ 0 ] == '\\' ) ) {
  1069.         // absolute path location
  1070.         return *this;
  1071.     }
  1072.  
  1073.     *this = basepath + *this;
  1074.     return *this;
  1075. }
  1076.  
  1077. /*
  1078. ====================
  1079. idStr::AppendPath
  1080. ====================
  1081. */
  1082. void idStr::AppendPath( const char *text ) {
  1083.     int pos;
  1084.     int i = 0;
  1085.  
  1086.     if ( text && text[i] ) {
  1087.         pos = len;
  1088.         EnsureAlloced( len + strlen( text ) + 2 );
  1089.  
  1090.         if ( pos ) {
  1091.             if ( data[ pos-1 ] != '/' ) {
  1092.                 data[ pos++ ] = '/';
  1093.             }
  1094.         }
  1095.         if ( text[i] == '/' ) {
  1096.             i++;
  1097.         }
  1098.  
  1099.         for ( ; text[ i ]; i++ ) {
  1100.             if ( text[ i ] == '\\' ) {
  1101.                 data[ pos++ ] = '/';
  1102.             } else {
  1103.                 data[ pos++ ] = text[ i ];
  1104.             }
  1105.         }
  1106.         len = pos;
  1107.         data[ pos ] = '\0';
  1108.     }
  1109. }
  1110.  
  1111. /*
  1112. ==================
  1113. idStr::StripFilename
  1114. ==================
  1115. */
  1116. idStr &idStr::StripFilename( void ) {
  1117.     int pos;
  1118.  
  1119.     pos = Length() - 1;
  1120.     while( ( pos > 0 ) && ( ( *this )[ pos ] != '/' ) && ( ( *this )[ pos ] != '\\' ) ) {
  1121.         pos--;
  1122.     }
  1123.  
  1124.     if ( pos < 0 ) {
  1125.         pos = 0;
  1126.     }
  1127.  
  1128.     CapLength( pos );
  1129.     return *this;
  1130. }
  1131.  
  1132. /*
  1133. ==================
  1134. idStr::StripPath
  1135. ==================
  1136. */
  1137. idStr &idStr::StripPath( void ) {
  1138.     int pos;
  1139.  
  1140.     pos = Length();
  1141.     while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
  1142.         pos--;
  1143.     }
  1144.  
  1145.     *this = Right( Length() - pos );
  1146.     return *this;
  1147. }
  1148.  
  1149. /*
  1150. ====================
  1151. idStr::ExtractFilePath
  1152. ====================
  1153. */
  1154. void idStr::ExtractFilePath( idStr &dest ) const {
  1155.     int pos;
  1156.  
  1157.     //
  1158.     // back up until a \ or the start
  1159.     //
  1160.     pos = Length();
  1161.     while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
  1162.         pos--;
  1163.     }
  1164.  
  1165.     Left( pos, dest );
  1166. }
  1167.  
  1168. /*
  1169. ====================
  1170. idStr::ExtractFileName
  1171. ====================
  1172. */
  1173. void idStr::ExtractFileName( idStr &dest ) const {
  1174.     int pos;
  1175.  
  1176.     //
  1177.     // back up until a \ or the start
  1178.     //
  1179.     pos = Length() - 1;
  1180.     while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
  1181.         pos--;
  1182.     }
  1183.  
  1184.     Right( Length() - pos, dest );
  1185. }
  1186.  
  1187. /*
  1188. ====================
  1189. idStr::ExtractFileBase
  1190. ====================
  1191. */
  1192. void idStr::ExtractFileBase( idStr &dest ) const {
  1193.     int pos;
  1194.     int start;
  1195.  
  1196.     //
  1197.     // back up until a \ or the start
  1198.     //
  1199.     pos = Length() - 1;
  1200.     while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
  1201.         pos--;
  1202.     }
  1203.  
  1204.     start = pos;
  1205. // RAVEN BEGIN
  1206. // jscott: for getting the file base out of addnormals() style filenames
  1207.     while( ( pos < Length() ) && ( ( *this )[ pos ] != '.' ) && ( ( *this )[ pos ] != ',' ) ) {
  1208. // RAVEN END
  1209.         pos++;
  1210.     }
  1211.  
  1212.     Mid( start, pos - start, dest );
  1213. }
  1214.  
  1215. /*
  1216. ====================
  1217. idStr::ExtractFileExtension
  1218. ====================
  1219. */
  1220. void idStr::ExtractFileExtension( idStr &dest ) const {
  1221.     int pos;
  1222.  
  1223.     //
  1224.     // back up until a . or the start
  1225.     //
  1226.     pos = Length() - 1;
  1227.     while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '.' ) ) {
  1228.         pos--;
  1229.     }
  1230.  
  1231.     if ( !pos ) {
  1232.         // no extension
  1233.         dest.Empty();
  1234.     } else {
  1235.         Right( Length() - pos, dest );
  1236.     }
  1237. }
  1238.  
  1239. // RAVEN BEGIN
  1240. // twhitaker: Turns a bad file name into a good one or your money back
  1241. void idStr::ScrubFileName( void )
  1242. {
  1243.     int i;
  1244.  
  1245.     RemoveEscapes();
  1246.     StripFileExtension();
  1247.  
  1248.     for ( i = 0; i < len; i++ ) {
  1249.         if( !idStr::upperCaseCharacter[data[i]] && !isdigit(data[i]) ) {
  1250.             data[i] = '_';
  1251.         }
  1252.     }
  1253. }
  1254.  
  1255. // jscott: like the declManager version, but globally accessable
  1256. void idStr::MakeNameCanonical( void )
  1257. {
  1258.     ToLower();
  1259.     BackSlashesToSlashes();
  1260.     StripFileExtension();
  1261. }
  1262.  
  1263. void idStr::EnsurePrintable( void ) {
  1264.  
  1265.     int        i;
  1266.  
  1267.     for( i = 0; i < len; i++ ) {
  1268.  
  1269.         if( !CharIsPrintable( data[i] ) ) {
  1270.  
  1271.             memmove( &data[i], &data[i + 1], len - i );
  1272.             len--;
  1273.         }
  1274.     }
  1275. }
  1276. // RAVEN END
  1277.  
  1278. /*
  1279. =====================================================================
  1280.  
  1281.   char * methods to replace library functions
  1282.  
  1283. =====================================================================
  1284. */
  1285.  
  1286. /*
  1287. ============
  1288. idStr::IsNumeric
  1289.  
  1290. Checks a string to see if it contains only numerical values.
  1291. ============
  1292. */
  1293. bool idStr::IsNumeric( const char *s ) {
  1294.     int        i;
  1295.     bool    dot;
  1296.  
  1297.     if ( *s == '-' ) {
  1298.         s++;
  1299.     }
  1300.  
  1301.     dot = false;
  1302.     for ( i = 0; s[i]; i++ ) {
  1303.         if ( !isdigit( ( byte )s[i] ) ) {
  1304.             if ( ( s[ i ] == '.' ) && !dot ) {
  1305.                 dot = true;
  1306.                 continue;
  1307.             }
  1308.             return false;
  1309.         }
  1310.     }
  1311.  
  1312.     return true;
  1313. }
  1314.  
  1315. /*
  1316. ============
  1317. idStr::HasLower
  1318.  
  1319. Checks if a string has any lowercase chars
  1320. ============
  1321. */
  1322. bool idStr::HasLower( const char *s ) {
  1323.     if ( !s ) {
  1324.         return false;
  1325.     }
  1326.     
  1327.     while ( *s ) {
  1328.         if ( CharIsLower( *s ) ) {
  1329.             return true;
  1330.         }
  1331.         s++;
  1332.     }
  1333.     
  1334.     return false;
  1335. }
  1336.  
  1337. /*
  1338. ============
  1339. idStr::HasUpper
  1340.     
  1341. Checks if a string has any uppercase chars
  1342. ============
  1343. */
  1344. bool idStr::HasUpper( const char *s ) {
  1345.     if ( !s ) {
  1346.         return false;
  1347.     }
  1348.     
  1349.     while ( *s ) {
  1350.         if ( CharIsUpper( *s ) ) {
  1351.             return true;
  1352.         }
  1353.         s++;
  1354.     }
  1355.     
  1356.     return false;
  1357. }
  1358.  
  1359. /*
  1360. ================
  1361. idStr::Cmp
  1362. ================
  1363. */
  1364. int idStr::Cmp( const char *s1, const char *s2 ) {
  1365.     int c1, c2, d;
  1366.  
  1367.     do {
  1368.         c1 = *s1++;
  1369.         c2 = *s2++;
  1370.  
  1371.         d = c1 - c2;
  1372.         if ( d ) {
  1373.             return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
  1374.         }
  1375.     } while( c1 );
  1376.  
  1377.     return 0;        // strings are equal
  1378. }
  1379.  
  1380. /*
  1381. ================
  1382. idStr::Cmpn
  1383. ================
  1384. */
  1385. int idStr::Cmpn( const char *s1, const char *s2, int n ) {
  1386.     int c1, c2, d;
  1387.  
  1388.     assert( n >= 0 );
  1389.  
  1390.     do {
  1391.         c1 = *s1++;
  1392.         c2 = *s2++;
  1393.  
  1394.         if ( !n-- ) {
  1395.             return 0;        // strings are equal until end point
  1396.         }
  1397.  
  1398.         d = c1 - c2;
  1399.         if ( d ) {
  1400.             return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
  1401.         }
  1402.     } while( c1 );
  1403.  
  1404.     return 0;        // strings are equal
  1405. }
  1406.  
  1407. /*
  1408. ================
  1409. idStr::Icmp
  1410. ================
  1411. */
  1412. int idStr::Icmp( const char *s1, const char *s2 ) {
  1413.     int c1, c2, d;
  1414.  
  1415.     do {
  1416.         c1 = *s1++;
  1417.         c2 = *s2++;
  1418.  
  1419.         d = c1 - c2;
  1420.         while( d ) {
  1421.             if ( c1 <= 'Z' && c1 >= 'A' ) {
  1422.                 d += ('a' - 'A');
  1423.                 if ( !d ) {
  1424.                     break;
  1425.                 }
  1426.             }
  1427.             if ( c2 <= 'Z' && c2 >= 'A' ) {
  1428.                 d -= ('a' - 'A');
  1429.                 if ( !d ) {
  1430.                     break;
  1431.                 }
  1432.             }
  1433.             return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
  1434.         }
  1435.     } while( c1 );
  1436.  
  1437.     return 0;        // strings are equal
  1438. }
  1439.  
  1440. /*
  1441. ================
  1442. idStr::Icmpn
  1443. ================
  1444. */
  1445. int idStr::Icmpn( const char *s1, const char *s2, int n ) {
  1446.     int c1, c2, d;
  1447.  
  1448.     assert( n >= 0 );
  1449.  
  1450.     do {
  1451.         c1 = *s1++;
  1452.         c2 = *s2++;
  1453.  
  1454.         if ( !n-- ) {
  1455.             return 0;        // strings are equal until end point
  1456.         }
  1457.  
  1458.         d = c1 - c2;
  1459.         while( d ) {
  1460.             if ( c1 <= 'Z' && c1 >= 'A' ) {
  1461.                 d += ('a' - 'A');
  1462.                 if ( !d ) {
  1463.                     break;
  1464.                 }
  1465.             }
  1466.             if ( c2 <= 'Z' && c2 >= 'A' ) {
  1467.                 d -= ('a' - 'A');
  1468.                 if ( !d ) {
  1469.                     break;
  1470.                 }
  1471.             }
  1472.             return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
  1473.         }
  1474.     } while( c1 );
  1475.  
  1476.     return 0;        // strings are equal
  1477. }
  1478.  
  1479. /*
  1480. ================
  1481. idStr::Icmp
  1482. ================
  1483. */
  1484. // RAVEN BEGIN
  1485. // bdube: escape codes
  1486. int idStr::IcmpNoEscape ( const char *s1, const char *s2 ) {
  1487.     int c1, c2, d;
  1488.  
  1489.     do {
  1490.         for ( d = idStr::IsEscape( s1 ); d; d = idStr::IsEscape( s1 ) ) {
  1491.             s1 += d;
  1492.         }
  1493.         for ( d = idStr::IsEscape( s2 ); d; d = idStr::IsEscape( s2 ) ) {
  1494.             s2 += d;
  1495.         }
  1496. // RAVEN END
  1497.         c1 = *s1++;
  1498.         c2 = *s2++;
  1499.  
  1500.         d = c1 - c2;
  1501.         while( d ) {
  1502.             if ( c1 <= 'Z' && c1 >= 'A' ) {
  1503.                 d += ('a' - 'A');
  1504.                 if ( !d ) {
  1505.                     break;
  1506.                 }
  1507.             }
  1508.             if ( c2 <= 'Z' && c2 >= 'A' ) {
  1509.                 d -= ('a' - 'A');
  1510.                 if ( !d ) {
  1511.                     break;
  1512.                 }
  1513.             }
  1514.             return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
  1515.         }
  1516.     } while( c1 );
  1517.  
  1518.     return 0;        // strings are equal
  1519. }
  1520.  
  1521. /*
  1522. ================
  1523. idStr::IcmpPath
  1524. ================
  1525. */
  1526. int idStr::IcmpPath( const char *s1, const char *s2 ) {
  1527.     int c1, c2, d;
  1528.  
  1529. #if 0
  1530. //#if !defined( _WIN32 )
  1531.     idLib::common->Printf( "WARNING: IcmpPath used on a case-sensitive filesystem?\n" );
  1532. #endif
  1533.  
  1534.     do {
  1535.         c1 = *s1++;
  1536.         c2 = *s2++;
  1537.  
  1538.         d = c1 - c2;
  1539.         while( d ) {
  1540.             if ( c1 <= 'Z' && c1 >= 'A' ) {
  1541.                 d += ('a' - 'A');
  1542.                 if ( !d ) {
  1543.                     break;
  1544.                 }
  1545.             }
  1546.             if ( c1 == '\\' ) {
  1547.                 d += ('/' - '\\');
  1548.                 if ( !d ) {
  1549.                     break;
  1550.                 }
  1551.             }
  1552.             if ( c2 <= 'Z' && c2 >= 'A' ) {
  1553.                 d -= ('a' - 'A');
  1554.                 if ( !d ) {
  1555.                     break;
  1556.                 }
  1557.             }
  1558.             if ( c2 == '\\' ) {
  1559.                 d -= ('/' - '\\');
  1560.                 if ( !d ) {
  1561.                     break;
  1562.                 }
  1563.             }
  1564.             // make sure folders come first
  1565.             while( c1 ) {
  1566.                 if ( c1 == '/' || c1 == '\\' ) {
  1567.                     break;
  1568.                 }
  1569.                 c1 = *s1++;
  1570.             }
  1571.             while( c2 ) {
  1572.                 if ( c2 == '/' || c2 == '\\' ) {
  1573.                     break;
  1574.                 }
  1575.                 c2 = *s2++;
  1576.             }
  1577.             if ( c1 && !c2 ) {
  1578.                 return -1;
  1579.             } else if ( !c1 && c2 ) {
  1580.                 return 1;
  1581.             }
  1582.             // same folder depth so use the regular compare
  1583.             return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
  1584.         }
  1585.     } while( c1 );
  1586.  
  1587.     return 0;
  1588. }
  1589.  
  1590. /*
  1591. ================
  1592. idStr::IcmpnPath
  1593. ================
  1594. */
  1595. int idStr::IcmpnPath( const char *s1, const char *s2, int n ) {
  1596.     int c1, c2, d;
  1597.  
  1598. #if 0
  1599. //#if !defined( _WIN32 )
  1600.     idLib::common->Printf( "WARNING: IcmpPath used on a case-sensitive filesystem?\n" );
  1601. #endif
  1602.  
  1603.     assert( n >= 0 );
  1604.  
  1605.     do {
  1606.         c1 = *s1++;
  1607.         c2 = *s2++;
  1608.  
  1609.         if ( !n-- ) {
  1610.             return 0;        // strings are equal until end point
  1611.         }
  1612.  
  1613.         d = c1 - c2;
  1614.         while( d ) {
  1615.             if ( c1 <= 'Z' && c1 >= 'A' ) {
  1616.                 d += ('a' - 'A');
  1617.                 if ( !d ) {
  1618.                     break;
  1619.                 }
  1620.             }
  1621.             if ( c1 == '\\' ) {
  1622.                 d += ('/' - '\\');
  1623.                 if ( !d ) {
  1624.                     break;
  1625.                 }
  1626.             }
  1627.             if ( c2 <= 'Z' && c2 >= 'A' ) {
  1628.                 d -= ('a' - 'A');
  1629.                 if ( !d ) {
  1630.                     break;
  1631.                 }
  1632.             }
  1633.             if ( c2 == '\\' ) {
  1634.                 d -= ('/' - '\\');
  1635.                 if ( !d ) {
  1636.                     break;
  1637.                 }
  1638.             }
  1639.             // make sure folders come first
  1640.             while( c1 ) {
  1641.                 if ( c1 == '/' || c1 == '\\' ) {
  1642.                     break;
  1643.                 }
  1644.                 c1 = *s1++;
  1645.             }
  1646.             while( c2 ) {
  1647.                 if ( c2 == '/' || c2 == '\\' ) {
  1648.                     break;
  1649.                 }
  1650.                 c2 = *s2++;
  1651.             }
  1652.             if ( c1 && !c2 ) {
  1653.                 return -1;
  1654.             } else if ( !c1 && c2 ) {
  1655.                 return 1;
  1656.             }
  1657.             // same folder depth so use the regular compare
  1658.             return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
  1659.         }
  1660.     } while( c1 );
  1661.  
  1662.     return 0;
  1663. }
  1664.  
  1665. /*
  1666. =============
  1667. idStr::Copynz
  1668.  
  1669. Safe strncpy that ensures a trailing zero
  1670. =============
  1671. */
  1672. void idStr::Copynz( char *dest, const char *src, int destsize ) {
  1673.     if ( !src ) {
  1674.         idLib::common->Warning( "idStr::Copynz: NULL src" );
  1675.         return;
  1676.     }
  1677.     if ( destsize < 1 ) {
  1678.         idLib::common->Warning( "idStr::Copynz: destsize < 1" ); 
  1679.         return;
  1680.     }
  1681.  
  1682.     strncpy( dest, src, destsize-1 );
  1683.     dest[destsize-1] = 0;
  1684. }
  1685.  
  1686. /*
  1687. ================
  1688. idStr::Append
  1689.  
  1690.   never goes past bounds or leaves without a terminating 0
  1691. ================
  1692. */
  1693. void idStr::Append( char *dest, int size, const char *src ) {
  1694.     int        l1;
  1695.  
  1696.     l1 = strlen( dest );
  1697.     if ( l1 >= size ) {
  1698.         idLib::common->Error( "idStr::Append: already overflowed" );
  1699.     }
  1700.     idStr::Copynz( dest + l1, src, size - l1 );
  1701. }
  1702.  
  1703. // bdube: escape codes
  1704. /*
  1705. ================
  1706. idStr::LengthWithoutEscapes
  1707. ================
  1708. */
  1709. int idStr::LengthWithoutEscapes( const char *s ) {
  1710.     int len;
  1711.     const char *p;
  1712.  
  1713.     if ( !s ) {
  1714.         return 0;
  1715.     }
  1716.  
  1717.     len = 0;
  1718.     p = s;
  1719.     while( *p ) {
  1720.         int esc;
  1721.         esc = idStr::IsEscape ( p );
  1722.         if ( esc ) {
  1723.             p += esc;
  1724.             continue;
  1725.         }
  1726.         p++;
  1727.         len++;
  1728.     }
  1729.  
  1730.     return len;
  1731. }
  1732.  
  1733. /*
  1734. ================
  1735. idStr::RemoveEscapes
  1736. ================
  1737. */
  1738. char *idStr::RemoveEscapes( char *string, int escapes ) {
  1739.     char *d;
  1740.     char *s;
  1741.     int c;
  1742.  
  1743.     s = string;
  1744.     d = string;
  1745.     while( (c = *s) != 0 ) {
  1746.         int esc;
  1747.         int type;
  1748.         esc = idStr::IsEscape( s, &type );
  1749.         if ( esc && (type & escapes) ) {
  1750.             s += esc;
  1751.             continue;
  1752.         }        
  1753.         else {
  1754.             *d++ = c;
  1755.             if ( c == C_COLOR_ESCAPE && *(s+1) ) {
  1756.                 s++;
  1757.             }
  1758.         }
  1759.         s++;
  1760.     }
  1761.     *d = '\0';
  1762.  
  1763.     return string;
  1764. }
  1765.  
  1766. /*
  1767. ================
  1768. idStr::IsEscape
  1769. ================
  1770. */
  1771. int idStr::IsEscape ( const char *s, int* type )  {
  1772.     if ( !s || *s != C_COLOR_ESCAPE || *(s+1) == C_COLOR_ESCAPE ) {
  1773.         return 0;
  1774.     }
  1775.     if ( type ) {
  1776.         *type = S_ESCAPE_UNKNOWN;
  1777.     }
  1778.     switch ( *(s+1) ) {
  1779.         case '0': case '1': case '2': case '3': case '4':
  1780.         case '5': case '6': case '7': case '8': case '9':
  1781.         case ':':
  1782.             if ( type ) {
  1783.                 *type = S_ESCAPE_COLORINDEX;
  1784.             }
  1785.             return 2;
  1786.  
  1787.         case '-': case '+':
  1788.             if ( type ) {
  1789.                 *type = S_ESCAPE_COLOR;
  1790.             }
  1791.             return 2;
  1792.             
  1793.         case 'r': case 'R': 
  1794.             if ( type ) {            
  1795.                 *type = S_ESCAPE_COMMAND;
  1796.             }
  1797.             return 2;
  1798.             
  1799.         case 'c': case 'C':
  1800.             if ( *(s+2) ) {
  1801.                 if ( *(s+3) ) {
  1802.                     if ( *(s+4) ) {
  1803.                         if ( type ) {
  1804.                             *type = S_ESCAPE_COLOR;
  1805.                         }
  1806.                         return 5;
  1807.                     }
  1808.                 }
  1809.             }
  1810.             return 0;
  1811.             
  1812.         case 'n': case 'N':
  1813.             if ( type ) {
  1814.                 *type = S_ESCAPE_COMMAND;
  1815.             }
  1816.             if ( *(s+2) ) {
  1817.                 return 3;
  1818.             }
  1819.             return 0;
  1820.             
  1821.         case 'i': case 'I':
  1822.             if ( *(s+2) && *(s+3) && *(s+4) ) {
  1823.                 if ( type ) {
  1824.                     *type = S_ESCAPE_ICON;
  1825.                 }
  1826.                 return 5;
  1827.             }
  1828.             return 0;        
  1829.     }            
  1830.     return 0;
  1831. }
  1832.  
  1833. // RAVEN END
  1834.  
  1835. /*
  1836. ================
  1837. idStr::snPrintf
  1838. ================
  1839. */
  1840. int idStr::snPrintf( char *dest, int size, const char *fmt, ...) {
  1841.     int len;
  1842.     va_list argptr;
  1843.     char buffer[32000];    // big, but small enough to fit in PPC stack
  1844.  
  1845.     va_start( argptr, fmt );
  1846.     len = vsprintf( buffer, fmt, argptr );
  1847.     va_end( argptr );
  1848.     if ( len >= sizeof( buffer ) ) {
  1849.         idLib::common->Error( "idStr::snPrintf: overflowed buffer" );
  1850.     }
  1851.     if ( len >= size ) {
  1852.         idLib::common->Warning( "idStr::snPrintf: overflow of %i in %i\n", len, size );
  1853.         len = size;
  1854.     }
  1855.     idStr::Copynz( dest, buffer, size );
  1856.     return len;
  1857. }
  1858.  
  1859. /*
  1860. ============
  1861. idStr::vsnPrintf
  1862.  
  1863. vsnprintf portability:
  1864.  
  1865. C99 standard: vsnprintf returns the number of characters (excluding the trailing
  1866. '\0') which would have been written to the final string if enough space had been available
  1867. snprintf and vsnprintf do not write more than size bytes (including the trailing '\0')
  1868.  
  1869. win32: _vsnprintf returns the number of characters written, not including the terminating null character,
  1870. or a negative value if an output error occurs. If the number of characters to write exceeds count, then count 
  1871. characters are written and -1 is returned and no trailing '\0' is added.
  1872.  
  1873. idStr::vsnPrintf: always appends a trailing '\0', returns number of characters written (not including terminal \0)
  1874. or returns -1 on failure or if the buffer would be overflowed.
  1875. ============
  1876. */
  1877. int idStr::vsnPrintf( char *dest, int size, const char *fmt, va_list argptr ) {
  1878.     int ret;
  1879.  
  1880. #ifdef _WIN32
  1881. #undef _vsnprintf
  1882.     ret = _vsnprintf( dest, size-1, fmt, argptr );
  1883. #define _vsnprintf    use_idStr_vsnPrintf
  1884. #else
  1885. #undef vsnprintf
  1886.     ret = vsnprintf( dest, size, fmt, argptr );
  1887. #define vsnprintf    use_idStr_vsnPrintf
  1888. #endif
  1889.     dest[size-1] = '\0';
  1890.     if ( ret < 0 || ret >= size ) {
  1891.         return -1;
  1892.     }
  1893.     return ret;
  1894. }
  1895.  
  1896. /*
  1897. ============
  1898. sprintf
  1899.  
  1900. Sets the value of the string using a printf interface.
  1901. ============
  1902. */
  1903. int sprintf( idStr &string, const char *fmt, ... ) {
  1904.     int l;
  1905.     va_list argptr;
  1906.     char buffer[32000];
  1907.     
  1908.     va_start( argptr, fmt );
  1909.     l = idStr::vsnPrintf( buffer, sizeof(buffer)-1, fmt, argptr );
  1910.     va_end( argptr );
  1911.     buffer[sizeof(buffer)-1] = '\0';
  1912.  
  1913.     string = buffer;
  1914.     return l;
  1915. }
  1916.  
  1917. /*
  1918. ============
  1919. vsprintf
  1920.  
  1921. Sets the value of the string using a vprintf interface.
  1922. ============
  1923. */
  1924. int vsprintf( idStr &string, const char *fmt, va_list argptr ) {
  1925.     int l;
  1926.     char buffer[32000];
  1927.     
  1928.     l = idStr::vsnPrintf( buffer, sizeof(buffer)-1, fmt, argptr );
  1929.     buffer[sizeof(buffer)-1] = '\0';
  1930.     
  1931.     string = buffer;
  1932.     return l;
  1933. }
  1934.  
  1935. /*
  1936. ============
  1937. va
  1938.  
  1939. does a varargs printf into a temp buffer
  1940. NOTE: not thread safe
  1941. ============
  1942. */
  1943. char *va( const char *fmt, ... ) {
  1944.     va_list argptr;
  1945.     static int index = 0;
  1946. // RAVEN BEGIN
  1947. // scork: tweaked from 4 to 8, since one of my funcs uses it twice. Better safe than sorry
  1948.     static char string[VA_NUM_BUFS][VA_BUF_LEN];    // in case called by nested functions
  1949.     char *buf;
  1950.  
  1951.     buf = string[index];
  1952.     index = (index + 1) & 7;
  1953. // RAVEN END
  1954.  
  1955.     va_start( argptr, fmt );
  1956.     vsprintf( buf, fmt, argptr );
  1957.     va_end( argptr );
  1958.  
  1959.     return buf;
  1960. }
  1961.  
  1962. #ifdef _WIN32
  1963. /*
  1964. ============
  1965. fe
  1966.  
  1967. Used for formatting windows errors
  1968. NOTE: not thread safe
  1969. ============
  1970. */
  1971. // RAVEN BEGIN
  1972. // abahr
  1973. char *fe( int errorId ) {
  1974.     static int index = 0;
  1975.     static char string[4][256];    // in case called by nested functions
  1976.     char *buf;
  1977.  
  1978.     buf = string[index];
  1979.     index = (index + 1) & 3;
  1980. #ifndef _XBOX
  1981.     FormatMessage( 
  1982.         FORMAT_MESSAGE_FROM_SYSTEM | 
  1983.         FORMAT_MESSAGE_IGNORE_INSERTS,
  1984.         NULL,
  1985.         errorId,
  1986.         0, // Default language
  1987.         (LPTSTR) buf,
  1988.         256,
  1989.         NULL 
  1990.     );
  1991. #endif
  1992.     return buf;
  1993. }
  1994. #endif
  1995. // RAVEN END
  1996.  
  1997. /*
  1998. ============
  1999. idStr::BestUnit
  2000. ============
  2001. */
  2002. int idStr::BestUnit( const char *format, float value, Measure_t measure ) {
  2003.     int unit = 1;
  2004.     while ( unit <= 3 && ( 1 << ( unit * 10 ) < value ) ) {
  2005.         unit++;
  2006.     }
  2007.     unit--;
  2008.     value /= 1 << ( unit * 10 );
  2009.     sprintf( *this, format, value );
  2010.     *this += " ";
  2011.     *this += units[ measure ][ unit ];
  2012.     return unit;
  2013. }
  2014.  
  2015. /*
  2016. ============
  2017. idStr::SetUnit
  2018. ============
  2019. */
  2020. void idStr::SetUnit( const char *format, float value, int unit, Measure_t measure ) {
  2021.     value /= 1 << ( unit * 10 );
  2022.     sprintf( *this, format, value );
  2023.     *this += " ";
  2024.     *this += units[ measure ][ unit ];    
  2025. }
  2026.  
  2027. /*
  2028. ================
  2029. idStr::InitMemory
  2030. ================
  2031. */
  2032. void idStr::InitMemory( void ) {
  2033. #ifdef USE_STRING_DATA_ALLOCATOR
  2034. // RAVEN BEGIN
  2035. // jnewquist: Tag scope and callees to track allocations using "new".
  2036.     MEM_SCOPED_TAG(tag,MA_STRING);
  2037. // RAVEN END
  2038.     stringDataAllocator.Init();
  2039. #endif
  2040. }
  2041.  
  2042. /*
  2043. ================
  2044. idStr::ShutdownMemory
  2045. ================
  2046. */
  2047. void idStr::ShutdownMemory( void ) {
  2048. #ifdef USE_STRING_DATA_ALLOCATOR
  2049.     stringDataAllocator.Shutdown();
  2050. #endif
  2051. }
  2052.  
  2053. /*
  2054. ================
  2055. idStr::PurgeMemory
  2056. ================
  2057. */
  2058. void idStr::PurgeMemory( void ) {
  2059. #ifdef USE_STRING_DATA_ALLOCATOR
  2060.     stringDataAllocator.FreeEmptyBaseBlocks();
  2061. #endif
  2062. }
  2063.  
  2064. /*
  2065. ================
  2066. idStr::ShowMemoryUsage_f
  2067. ================
  2068. */
  2069. void idStr::ShowMemoryUsage_f( const idCmdArgs &args ) {
  2070. #ifdef USE_STRING_DATA_ALLOCATOR
  2071.     idLib::common->Printf( "%6d KB string memory (%d KB free in %d blocks, %d empty base blocks)\n",
  2072.         stringDataAllocator.GetBaseBlockMemory() >> 10, stringDataAllocator.GetFreeBlockMemory() >> 10,
  2073.             stringDataAllocator.GetNumFreeBlocks(), stringDataAllocator.GetNumEmptyBaseBlocks() );
  2074. #endif
  2075. }
  2076.  
  2077. /*
  2078. ================
  2079. idStr::FormatNumber
  2080. ================
  2081. */
  2082. struct formatList_t {
  2083.     int            gran;
  2084.     int            count;
  2085. };
  2086.  
  2087. // elements of list need to decend in size
  2088. formatList_t formatList[] = {
  2089.     { 1000000000, 0 },
  2090.     { 1000000, 0 },
  2091.     { 1000, 0 }
  2092. };
  2093.  
  2094. int numFormatList = sizeof(formatList) / sizeof( formatList[0] );
  2095.  
  2096.  
  2097. idStr idStr::FormatNumber( int number ) {
  2098.     idStr string;
  2099.     bool hit;
  2100.  
  2101.     // reset
  2102.     for ( int i = 0; i < numFormatList; i++ ) {
  2103.         formatList_t *li = formatList + i;
  2104.         li->count = 0;
  2105.     }
  2106.  
  2107.     // main loop
  2108.     do {
  2109.         hit = false;
  2110.  
  2111.         for ( int i = 0; i < numFormatList; i++ ) {
  2112.             formatList_t *li = formatList + i;
  2113.  
  2114.             if ( number >= li->gran ) {
  2115.                 li->count++;
  2116.                 number -= li->gran;
  2117.                 hit = true;
  2118.                 break;
  2119.             }
  2120.         }
  2121.     } while ( hit );
  2122.  
  2123.     // print out
  2124.     bool found = false;
  2125.  
  2126.     for ( int i = 0; i < numFormatList; i++ ) {
  2127.         formatList_t *li = formatList + i;
  2128.  
  2129.         if ( li->count ) {
  2130.             if ( !found ) {
  2131.                 string += va( "%i,", li->count );
  2132.             } else {
  2133.                 string += va( "%3.3i,", li->count );
  2134.             }
  2135.             found = true;
  2136.         }
  2137.         else if ( found ) {
  2138.             string += va( "%3.3i,", li->count );
  2139.         }
  2140.     }
  2141.  
  2142.     if ( found ) {
  2143.         string += va( "%3.3i", number );
  2144.     }
  2145.     else {
  2146.         string += va( "%i", number );
  2147.     }
  2148.  
  2149.     // pad to proper size
  2150.     int count = 11 - string.Length();
  2151.  
  2152.     for ( int i = 0; i < count; i++ ) {
  2153.         string.Insert( " ", 0 );
  2154.     }
  2155.  
  2156.     return string;
  2157. }
  2158.  
  2159. // RAVEN BEGIN
  2160. // abahr
  2161. /*
  2162. ================
  2163. idStr::Split
  2164. ================
  2165. */
  2166. void idStr::Split( const char* source, idList<idStr>& list, const char delimiter, const char groupDelimiter  ) {
  2167.     const idStr localSource( source );
  2168.     int sourceLength = localSource.Length();
  2169.     idStr element;
  2170.     int startIndex = 0;
  2171.     int endIndex = -1;
  2172.     char currentChar = '\0';
  2173.  
  2174.     list.Clear();
  2175.     while( startIndex < sourceLength ) {
  2176.         currentChar = localSource[ startIndex ];
  2177.         if( currentChar == groupDelimiter ) {
  2178.             endIndex = localSource.Find( groupDelimiter, ++startIndex );
  2179.             if( endIndex == -1 ) {
  2180.                 common->Error( "Couldn't find expected char %c in idStr::Split\n", groupDelimiter );
  2181.             }
  2182.             element = localSource.Mid( startIndex, endIndex );
  2183.             element.Strip( groupDelimiter );
  2184.             list.Append( element );
  2185.             element.Clear();
  2186.             startIndex = endIndex + 1;
  2187.             continue;
  2188.         } else if( currentChar == delimiter ) {
  2189.             element += '\0';
  2190.             list.Append( element );
  2191.             element.Clear();
  2192.             endIndex = ++startIndex;
  2193.             continue;
  2194.         }
  2195.  
  2196.         startIndex++;
  2197.         element += currentChar;
  2198.     }
  2199.  
  2200.     if( element.Length() ) {
  2201.         element += '\0';
  2202.         list.Append( element );
  2203.     }
  2204. }
  2205.  
  2206. /*
  2207. ================
  2208. idStr::Split
  2209. ================
  2210. */
  2211. void idStr::Split( idList<idStr>& list, const char delimiter, const char groupDelimiter ) {
  2212.     Split( c_str(), list, delimiter, groupDelimiter );
  2213. }
  2214. // RAVEN END
  2215.