misc/libphysfs/physfs_unicode.c
changeset 13881 99b265e0d1d0
parent 13880 5f819b90d479
child 13882 b172a5d40eee
equal deleted inserted replaced
13880:5f819b90d479 13881:99b265e0d1d0
     1 #define __PHYSICSFS_INTERNAL__
       
     2 #include "physfs_internal.h"
       
     3 
       
     4 
       
     5 /*
       
     6  * From rfc3629, the UTF-8 spec:
       
     7  *  https://www.ietf.org/rfc/rfc3629.txt
       
     8  *
       
     9  *   Char. number range  |        UTF-8 octet sequence
       
    10  *      (hexadecimal)    |              (binary)
       
    11  *   --------------------+---------------------------------------------
       
    12  *   0000 0000-0000 007F | 0xxxxxxx
       
    13  *   0000 0080-0000 07FF | 110xxxxx 10xxxxxx
       
    14  *   0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
       
    15  *   0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
       
    16  */
       
    17 
       
    18 
       
    19 /*
       
    20  * This may not be the best value, but it's one that isn't represented
       
    21  *  in Unicode (0x10FFFF is the largest codepoint value). We return this
       
    22  *  value from utf8codepoint() if there's bogus bits in the
       
    23  *  stream. utf8codepoint() will turn this value into something
       
    24  *  reasonable (like a question mark), for text that wants to try to recover,
       
    25  *  whereas utf8valid() will use the value to determine if a string has bad
       
    26  *  bits.
       
    27  */
       
    28 #define UNICODE_BOGUS_CHAR_VALUE 0xFFFFFFFF
       
    29 
       
    30 /*
       
    31  * This is the codepoint we currently return when there was bogus bits in a
       
    32  *  UTF-8 string. May not fly in Asian locales?
       
    33  */
       
    34 #define UNICODE_BOGUS_CHAR_CODEPOINT '?'
       
    35 
       
    36 static PHYSFS_uint32 utf8codepoint(const char **_str)
       
    37 {
       
    38     const char *str = *_str;
       
    39     PHYSFS_uint32 retval = 0;
       
    40     PHYSFS_uint32 octet = (PHYSFS_uint32) ((PHYSFS_uint8) *str);
       
    41     PHYSFS_uint32 octet2, octet3, octet4;
       
    42 
       
    43     if (octet == 0)  /* null terminator, end of string. */
       
    44         return 0;
       
    45 
       
    46     else if (octet < 128)  /* one octet char: 0 to 127 */
       
    47     {
       
    48         (*_str)++;  /* skip to next possible start of codepoint. */
       
    49         return octet;
       
    50     } /* else if */
       
    51 
       
    52     else if ((octet > 127) && (octet < 192))  /* bad (starts with 10xxxxxx). */
       
    53     {
       
    54         /*
       
    55          * Apparently each of these is supposed to be flagged as a bogus
       
    56          *  char, instead of just resyncing to the next valid codepoint.
       
    57          */
       
    58         (*_str)++;  /* skip to next possible start of codepoint. */
       
    59         return UNICODE_BOGUS_CHAR_VALUE;
       
    60     } /* else if */
       
    61 
       
    62     else if (octet < 224)  /* two octets */
       
    63     {
       
    64         (*_str)++;  /* advance at least one byte in case of an error */
       
    65         octet -= (128+64);
       
    66         octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
    67         if ((octet2 & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
    68             return UNICODE_BOGUS_CHAR_VALUE;
       
    69 
       
    70         *_str += 1;  /* skip to next possible start of codepoint. */
       
    71         retval = ((octet << 6) | (octet2 - 128));
       
    72         if ((retval >= 0x80) && (retval <= 0x7FF))
       
    73             return retval;
       
    74     } /* else if */
       
    75 
       
    76     else if (octet < 240)  /* three octets */
       
    77     {
       
    78         (*_str)++;  /* advance at least one byte in case of an error */
       
    79         octet -= (128+64+32);
       
    80         octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
    81         if ((octet2 & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
    82             return UNICODE_BOGUS_CHAR_VALUE;
       
    83 
       
    84         octet3 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
    85         if ((octet3 & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
    86             return UNICODE_BOGUS_CHAR_VALUE;
       
    87 
       
    88         *_str += 2;  /* skip to next possible start of codepoint. */
       
    89         retval = ( ((octet << 12)) | ((octet2-128) << 6) | ((octet3-128)) );
       
    90 
       
    91         /* There are seven "UTF-16 surrogates" that are illegal in UTF-8. */
       
    92         switch (retval)
       
    93         {
       
    94             case 0xD800:
       
    95             case 0xDB7F:
       
    96             case 0xDB80:
       
    97             case 0xDBFF:
       
    98             case 0xDC00:
       
    99             case 0xDF80:
       
   100             case 0xDFFF:
       
   101                 return UNICODE_BOGUS_CHAR_VALUE;
       
   102         } /* switch */
       
   103 
       
   104         /* 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge. */
       
   105         if ((retval >= 0x800) && (retval <= 0xFFFD))
       
   106             return retval;
       
   107     } /* else if */
       
   108 
       
   109     else if (octet < 248)  /* four octets */
       
   110     {
       
   111         (*_str)++;  /* advance at least one byte in case of an error */
       
   112         octet -= (128+64+32+16);
       
   113         octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   114         if ((octet2 & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   115             return UNICODE_BOGUS_CHAR_VALUE;
       
   116 
       
   117         octet3 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   118         if ((octet3 & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   119             return UNICODE_BOGUS_CHAR_VALUE;
       
   120 
       
   121         octet4 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   122         if ((octet4 & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   123             return UNICODE_BOGUS_CHAR_VALUE;
       
   124 
       
   125         *_str += 3;  /* skip to next possible start of codepoint. */
       
   126         retval = ( ((octet << 18)) | ((octet2 - 128) << 12) |
       
   127                    ((octet3 - 128) << 6) | ((octet4 - 128)) );
       
   128         if ((retval >= 0x10000) && (retval <= 0x10FFFF))
       
   129             return retval;
       
   130     } /* else if */
       
   131 
       
   132     /*
       
   133      * Five and six octet sequences became illegal in rfc3629.
       
   134      *  We throw the codepoint away, but parse them to make sure we move
       
   135      *  ahead the right number of bytes and don't overflow the buffer.
       
   136      */
       
   137 
       
   138     else if (octet < 252)  /* five octets */
       
   139     {
       
   140         (*_str)++;  /* advance at least one byte in case of an error */
       
   141         octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   142         if ((octet & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   143             return UNICODE_BOGUS_CHAR_VALUE;
       
   144 
       
   145         octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   146         if ((octet & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   147             return UNICODE_BOGUS_CHAR_VALUE;
       
   148 
       
   149         octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   150         if ((octet & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   151             return UNICODE_BOGUS_CHAR_VALUE;
       
   152 
       
   153         octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   154         if ((octet & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   155             return UNICODE_BOGUS_CHAR_VALUE;
       
   156 
       
   157         *_str += 4;  /* skip to next possible start of codepoint. */
       
   158         return UNICODE_BOGUS_CHAR_VALUE;
       
   159     } /* else if */
       
   160 
       
   161     else  /* six octets */
       
   162     {
       
   163         (*_str)++;  /* advance at least one byte in case of an error */
       
   164         octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   165         if ((octet & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   166             return UNICODE_BOGUS_CHAR_VALUE;
       
   167 
       
   168         octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   169         if ((octet & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   170             return UNICODE_BOGUS_CHAR_VALUE;
       
   171 
       
   172         octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   173         if ((octet & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   174             return UNICODE_BOGUS_CHAR_VALUE;
       
   175 
       
   176         octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   177         if ((octet & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   178             return UNICODE_BOGUS_CHAR_VALUE;
       
   179 
       
   180         octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
       
   181         if ((octet & (128+64)) != 128)  /* Format isn't 10xxxxxx? */
       
   182             return UNICODE_BOGUS_CHAR_VALUE;
       
   183 
       
   184         *_str += 6;  /* skip to next possible start of codepoint. */
       
   185         return UNICODE_BOGUS_CHAR_VALUE;
       
   186     } /* else if */
       
   187 
       
   188     return UNICODE_BOGUS_CHAR_VALUE;
       
   189 } /* utf8codepoint */
       
   190 
       
   191 
       
   192 void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, PHYSFS_uint64 len)
       
   193 {
       
   194     len -= sizeof (PHYSFS_uint32);   /* save room for null char. */
       
   195     while (len >= sizeof (PHYSFS_uint32))
       
   196     {
       
   197         PHYSFS_uint32 cp = utf8codepoint(&src);
       
   198         if (cp == 0)
       
   199             break;
       
   200         else if (cp == UNICODE_BOGUS_CHAR_VALUE)
       
   201             cp = UNICODE_BOGUS_CHAR_CODEPOINT;
       
   202         *(dst++) = cp;
       
   203         len -= sizeof (PHYSFS_uint32);
       
   204     } /* while */
       
   205 
       
   206     *dst = 0;
       
   207 } /* PHYSFS_utf8ToUcs4 */
       
   208 
       
   209 
       
   210 void PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len)
       
   211 {
       
   212     len -= sizeof (PHYSFS_uint16);   /* save room for null char. */
       
   213     while (len >= sizeof (PHYSFS_uint16))
       
   214     {
       
   215         PHYSFS_uint32 cp = utf8codepoint(&src);
       
   216         if (cp == 0)
       
   217             break;
       
   218         else if (cp == UNICODE_BOGUS_CHAR_VALUE)
       
   219             cp = UNICODE_BOGUS_CHAR_CODEPOINT;
       
   220 
       
   221         if (cp > 0xFFFF)  /* UTF-16 surrogates (bogus chars in UCS-2) */
       
   222             cp = UNICODE_BOGUS_CHAR_CODEPOINT;
       
   223 
       
   224         *(dst++) = cp;
       
   225         len -= sizeof (PHYSFS_uint16);
       
   226     } /* while */
       
   227 
       
   228     *dst = 0;
       
   229 } /* PHYSFS_utf8ToUcs2 */
       
   230 
       
   231 
       
   232 void PHYSFS_utf8ToUtf16(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len)
       
   233 {
       
   234     len -= sizeof (PHYSFS_uint16);   /* save room for null char. */
       
   235     while (len >= sizeof (PHYSFS_uint16))
       
   236     {
       
   237         PHYSFS_uint32 cp = utf8codepoint(&src);
       
   238         if (cp == 0)
       
   239             break;
       
   240         else if (cp == UNICODE_BOGUS_CHAR_VALUE)
       
   241             cp = UNICODE_BOGUS_CHAR_CODEPOINT;
       
   242 
       
   243         if (cp > 0xFFFF)  /* encode as surrogate pair */
       
   244         {
       
   245             if (len < (sizeof (PHYSFS_uint16) * 2))
       
   246                 break;  /* not enough room for the pair, stop now. */
       
   247 
       
   248             cp -= 0x10000;  /* Make this a 20-bit value */
       
   249 
       
   250             *(dst++) = 0xD800 + ((cp >> 10) & 0x3FF);
       
   251             len -= sizeof (PHYSFS_uint16);
       
   252 
       
   253             cp = 0xDC00 + (cp & 0x3FF);
       
   254         } /* if */
       
   255 
       
   256         *(dst++) = cp;
       
   257         len -= sizeof (PHYSFS_uint16);
       
   258     } /* while */
       
   259 
       
   260     *dst = 0;
       
   261 } /* PHYSFS_utf8ToUtf16 */
       
   262 
       
   263 static void utf8fromcodepoint(PHYSFS_uint32 cp, char **_dst, PHYSFS_uint64 *_len)
       
   264 {
       
   265     char *dst = *_dst;
       
   266     PHYSFS_uint64 len = *_len;
       
   267 
       
   268     if (len == 0)
       
   269         return;
       
   270 
       
   271     if (cp > 0x10FFFF)
       
   272         cp = UNICODE_BOGUS_CHAR_CODEPOINT;
       
   273     else if ((cp == 0xFFFE) || (cp == 0xFFFF))  /* illegal values. */
       
   274         cp = UNICODE_BOGUS_CHAR_CODEPOINT;
       
   275     else
       
   276     {
       
   277         /* There are seven "UTF-16 surrogates" that are illegal in UTF-8. */
       
   278         switch (cp)
       
   279         {
       
   280             case 0xD800:
       
   281             case 0xDB7F:
       
   282             case 0xDB80:
       
   283             case 0xDBFF:
       
   284             case 0xDC00:
       
   285             case 0xDF80:
       
   286             case 0xDFFF:
       
   287                 cp = UNICODE_BOGUS_CHAR_CODEPOINT;
       
   288         } /* switch */
       
   289     } /* else */
       
   290 
       
   291     /* Do the encoding... */
       
   292     if (cp < 0x80)
       
   293     {
       
   294         *(dst++) = (char) cp;
       
   295         len--;
       
   296     } /* if */
       
   297 
       
   298     else if (cp < 0x800)
       
   299     {
       
   300         if (len < 2)
       
   301             len = 0;
       
   302         else
       
   303         {
       
   304             *(dst++) = (char) ((cp >> 6) | 128 | 64);
       
   305             *(dst++) = (char) (cp & 0x3F) | 128;
       
   306             len -= 2;
       
   307         } /* else */
       
   308     } /* else if */
       
   309 
       
   310     else if (cp < 0x10000)
       
   311     {
       
   312         if (len < 3)
       
   313             len = 0;
       
   314         else
       
   315         {
       
   316             *(dst++) = (char) ((cp >> 12) | 128 | 64 | 32);
       
   317             *(dst++) = (char) ((cp >> 6) & 0x3F) | 128;
       
   318             *(dst++) = (char) (cp & 0x3F) | 128;
       
   319             len -= 3;
       
   320         } /* else */
       
   321     } /* else if */
       
   322 
       
   323     else
       
   324     {
       
   325         if (len < 4)
       
   326             len = 0;
       
   327         else
       
   328         {
       
   329             *(dst++) = (char) ((cp >> 18) | 128 | 64 | 32 | 16);
       
   330             *(dst++) = (char) ((cp >> 12) & 0x3F) | 128;
       
   331             *(dst++) = (char) ((cp >> 6) & 0x3F) | 128;
       
   332             *(dst++) = (char) (cp & 0x3F) | 128;
       
   333             len -= 4;
       
   334         } /* else if */
       
   335     } /* else */
       
   336 
       
   337     *_dst = dst;
       
   338     *_len = len;
       
   339 } /* utf8fromcodepoint */
       
   340 
       
   341 #define UTF8FROMTYPE(typ, src, dst, len) \
       
   342     if (len == 0) return; \
       
   343     len--;  \
       
   344     while (len) \
       
   345     { \
       
   346         const PHYSFS_uint32 cp = (PHYSFS_uint32) ((typ) (*(src++))); \
       
   347         if (cp == 0) break; \
       
   348         utf8fromcodepoint(cp, &dst, &len); \
       
   349     } \
       
   350     *dst = '\0'; \
       
   351 
       
   352 void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, PHYSFS_uint64 len)
       
   353 {
       
   354     UTF8FROMTYPE(PHYSFS_uint32, src, dst, len);
       
   355 } /* PHYSFS_utf8FromUcs4 */
       
   356 
       
   357 void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len)
       
   358 {
       
   359     UTF8FROMTYPE(PHYSFS_uint64, src, dst, len);
       
   360 } /* PHYSFS_utf8FromUcs2 */
       
   361 
       
   362 /* latin1 maps to unicode codepoints directly, we just utf-8 encode it. */
       
   363 void PHYSFS_utf8FromLatin1(const char *src, char *dst, PHYSFS_uint64 len)
       
   364 {
       
   365     UTF8FROMTYPE(PHYSFS_uint8, src, dst, len);
       
   366 } /* PHYSFS_utf8FromLatin1 */
       
   367 
       
   368 #undef UTF8FROMTYPE
       
   369 
       
   370 
       
   371 void PHYSFS_utf8FromUtf16(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len)
       
   372 {
       
   373     if (len == 0)
       
   374         return;
       
   375 
       
   376     len--;
       
   377     while (len)
       
   378     {
       
   379         PHYSFS_uint32 cp = (PHYSFS_uint32) *(src++);
       
   380         if (cp == 0)
       
   381             break;
       
   382 
       
   383         /* Orphaned second half of surrogate pair? */
       
   384         if ((cp >= 0xDC00) && (cp <= 0xDFFF))
       
   385             cp = UNICODE_BOGUS_CHAR_CODEPOINT;
       
   386         else if ((cp >= 0xD800) && (cp <= 0xDBFF))  /* start surrogate pair! */
       
   387         {
       
   388             const PHYSFS_uint32 pair = (PHYSFS_uint32) *src;
       
   389             if ((pair < 0xDC00) || (pair > 0xDFFF))
       
   390                 cp = UNICODE_BOGUS_CHAR_CODEPOINT;
       
   391             else
       
   392             {
       
   393                 src++;  /* eat the other surrogate. */
       
   394                 cp = (((cp - 0xD800) << 10) | (pair - 0xDC00));
       
   395             } /* else */
       
   396         } /* else if */
       
   397 
       
   398         utf8fromcodepoint(cp, &dst, &len);
       
   399     } /* while */
       
   400 
       
   401     *dst = '\0';
       
   402 } /* PHYSFS_utf8FromUtf16 */
       
   403 
       
   404 
       
   405 typedef struct CaseFoldMapping
       
   406 {
       
   407     PHYSFS_uint32 from;
       
   408     PHYSFS_uint32 to0;
       
   409     PHYSFS_uint32 to1;
       
   410     PHYSFS_uint32 to2;
       
   411 } CaseFoldMapping;
       
   412 
       
   413 typedef struct CaseFoldHashBucket
       
   414 {
       
   415     const PHYSFS_uint8 count;
       
   416     const CaseFoldMapping *list;
       
   417 } CaseFoldHashBucket;
       
   418 
       
   419 #include "physfs_casefolding.h"
       
   420 
       
   421 static void locate_case_fold_mapping(const PHYSFS_uint32 from,
       
   422                                      PHYSFS_uint32 *to)
       
   423 {
       
   424     PHYSFS_uint32 i;
       
   425     const PHYSFS_uint8 hashed = ((from ^ (from >> 8)) & 0xFF);
       
   426     const CaseFoldHashBucket *bucket = &case_fold_hash[hashed];
       
   427     const CaseFoldMapping *mapping = bucket->list;
       
   428 
       
   429     for (i = 0; i < bucket->count; i++, mapping++)
       
   430     {
       
   431         if (mapping->from == from)
       
   432         {
       
   433             to[0] = mapping->to0;
       
   434             to[1] = mapping->to1;
       
   435             to[2] = mapping->to2;
       
   436             return;
       
   437         } /* if */
       
   438     } /* for */
       
   439 
       
   440     /* Not found...there's no remapping for this codepoint. */
       
   441     to[0] = from;
       
   442     to[1] = 0;
       
   443     to[2] = 0;
       
   444 } /* locate_case_fold_mapping */
       
   445 
       
   446 
       
   447 static int utf8codepointcmp(const PHYSFS_uint32 cp1, const PHYSFS_uint32 cp2)
       
   448 {
       
   449     PHYSFS_uint32 folded1[3], folded2[3];
       
   450 
       
   451     if (cp1 == cp2)
       
   452         return 0;  /* obviously matches. */
       
   453 
       
   454     locate_case_fold_mapping(cp1, folded1);
       
   455     locate_case_fold_mapping(cp2, folded2);
       
   456 
       
   457     if (folded1[0] < folded2[0])
       
   458         return -1;
       
   459     else if (folded1[0] > folded2[0])
       
   460         return 1;
       
   461     else if (folded1[1] < folded2[1])
       
   462         return -1;
       
   463     else if (folded1[1] > folded2[1])
       
   464         return 1;
       
   465     else if (folded1[2] < folded2[2])
       
   466         return -1;
       
   467     else if (folded1[2] > folded2[2])
       
   468         return 1;
       
   469 
       
   470     return 0;  /* complete match. */
       
   471 } /* utf8codepointcmp */
       
   472 
       
   473 
       
   474 int __PHYSFS_utf8stricmp(const char *str1, const char *str2)
       
   475 {
       
   476     while (1)
       
   477     {
       
   478         const PHYSFS_uint32 cp1 = utf8codepoint(&str1);
       
   479         const PHYSFS_uint32 cp2 = utf8codepoint(&str2);
       
   480         const int rc = utf8codepointcmp(cp1, cp2);
       
   481         if (rc != 0)
       
   482             return rc;
       
   483         else if (cp1 == 0)
       
   484             break;  /* complete match. */
       
   485     } /* while */
       
   486 
       
   487     return 0;
       
   488 } /* __PHYSFS_utf8stricmp */
       
   489 
       
   490 
       
   491 int __PHYSFS_utf8strnicmp(const char *str1, const char *str2, PHYSFS_uint32 n)
       
   492 {
       
   493     while (n > 0)
       
   494     {
       
   495         const PHYSFS_uint32 cp1 = utf8codepoint(&str1);
       
   496         const PHYSFS_uint32 cp2 = utf8codepoint(&str2);
       
   497         const int rc = utf8codepointcmp(cp1, cp2);
       
   498         if (rc != 0)
       
   499             return rc;
       
   500         else if (cp1 == 0)
       
   501             return 0;
       
   502         n--;
       
   503     } /* while */
       
   504 
       
   505     return 0;  /* matched to n chars. */
       
   506 } /* __PHYSFS_utf8strnicmp */
       
   507 
       
   508 
       
   509 int __PHYSFS_stricmpASCII(const char *str1, const char *str2)
       
   510 {
       
   511     while (1)
       
   512     {
       
   513         const char ch1 = *(str1++);
       
   514         const char ch2 = *(str2++);
       
   515         const char cp1 = ((ch1 >= 'A') && (ch1 <= 'Z')) ? (ch1+32) : ch1;
       
   516         const char cp2 = ((ch2 >= 'A') && (ch2 <= 'Z')) ? (ch2+32) : ch2;
       
   517         if (cp1 < cp2)
       
   518             return -1;
       
   519         else if (cp1 > cp2)
       
   520             return 1;
       
   521         else if (cp1 == 0)  /* they're both null chars? */
       
   522             break;
       
   523     } /* while */
       
   524 
       
   525     return 0;
       
   526 } /* __PHYSFS_stricmpASCII */
       
   527 
       
   528 
       
   529 int __PHYSFS_strnicmpASCII(const char *str1, const char *str2, PHYSFS_uint32 n)
       
   530 {
       
   531     while (n-- > 0)
       
   532     {
       
   533         const char ch1 = *(str1++);
       
   534         const char ch2 = *(str2++);
       
   535         const char cp1 = ((ch1 >= 'A') && (ch1 <= 'Z')) ? (ch1+32) : ch1;
       
   536         const char cp2 = ((ch2 >= 'A') && (ch2 <= 'Z')) ? (ch2+32) : ch2;
       
   537         if (cp1 < cp2)
       
   538             return -1;
       
   539         else if (cp1 > cp2)
       
   540             return 1;
       
   541         else if (cp1 == 0)  /* they're both null chars? */
       
   542             return 0;
       
   543     } /* while */
       
   544 
       
   545     return 0;
       
   546 } /* __PHYSFS_strnicmpASCII */
       
   547 
       
   548 
       
   549 /* end of physfs_unicode.c ... */
       
   550