| 1 | 
 | 
|---|
| 2 | /***********************************************************************
 | 
|---|
| 3 | 
 | 
|---|
| 4 |   $Id: strutil.c 1460 2009-09-19 21:11:12Z gyoung $
 | 
|---|
| 5 | 
 | 
|---|
| 6 |   External strings support - stored in STRINGTABLE
 | 
|---|
| 7 | 
 | 
|---|
| 8 |   Copyright (c) 1993-98 M. Kimes
 | 
|---|
| 9 |   Copyright (c) 2006, 2009 Steven H. Levine
 | 
|---|
| 10 | 
 | 
|---|
| 11 |   22 Jul 06 SHL Comments
 | 
|---|
| 12 |   20 Aug 07 GKY Move #pragma alloc_text to end for OpenWatcom compat
 | 
|---|
| 13 |   05 Jan 08 SHL Rename from string.c to avoid string.h conflict
 | 
|---|
| 14 |   03 Feb 09 SHL Switch to STRINGTABLE and const return
 | 
|---|
| 15 |   19 Sep 09 GKY Make GetPString more SMP safe
 | 
|---|
| 16 | 
 | 
|---|
| 17 | ***********************************************************************/
 | 
|---|
| 18 | 
 | 
|---|
| 19 | #include <stdio.h>
 | 
|---|
| 20 | #include <share.h>
 | 
|---|
| 21 | #include <string.h>
 | 
|---|
| 22 | 
 | 
|---|
| 23 | #define INCL_DOSPROCESS                 // DosSleep
 | 
|---|
| 24 | 
 | 
|---|
| 25 | #include "fm3dll.h"
 | 
|---|
| 26 | #include "fm3str.h"
 | 
|---|
| 27 | #include "init.h"                       // Data declaration(s)
 | 
|---|
| 28 | #include "mainwnd.h"                    // FM3ModHandle
 | 
|---|
| 29 | #include "wrappers.h"
 | 
|---|
| 30 | #include "errutil.h"
 | 
|---|
| 31 | #include "strutil.h"
 | 
|---|
| 32 | #include "version.h"
 | 
|---|
| 33 | 
 | 
|---|
| 34 | static PSZ pszSrcFile = __FILE__;
 | 
|---|
| 35 | 
 | 
|---|
| 36 | //== GetPString() return a readonly pointer to the requested string in memory ==
 | 
|---|
| 37 | 
 | 
|---|
| 38 | PCSZ GetPString(ULONG id)
 | 
|---|
| 39 | {
 | 
|---|
| 40 |   PSZ psz;
 | 
|---|
| 41 |   LONG l;
 | 
|---|
| 42 |   CHAR sz[257];
 | 
|---|
| 43 |   ULONG ulNewFirstId;
 | 
|---|
| 44 |   ULONG ulNewLastId;
 | 
|---|
| 45 | 
 | 
|---|
| 46 |   static PSZ *pLoadedStrings;
 | 
|---|
| 47 |   static ULONG ulFirstId;
 | 
|---|
| 48 |   static ULONG ulLastId;
 | 
|---|
| 49 | 
 | 
|---|
| 50 |   // Strings that must be combined because stringtable items limited to 256
 | 
|---|
| 51 |   static struct LongString {
 | 
|---|
| 52 |     ULONG id;
 | 
|---|
| 53 |     ULONG sub_id;
 | 
|---|
| 54 |   } LongStrings[] = {
 | 
|---|
| 55 |     {IDS_SUGGEST1TEXT, IDS_SUGGEST1TEXT1},
 | 
|---|
| 56 |     {IDS_SUGGEST1TEXT, IDS_SUGGEST1TEXT2},
 | 
|---|
| 57 |     {IDS_ARCHIVERBB2TEXT, IDS_ARCHIVERBB2TEXT1},
 | 
|---|
| 58 |     {IDS_ARCHIVERBB2TEXT, IDS_ARCHIVERBB2TEXT2},
 | 
|---|
| 59 |     {IDS_ARCHIVERBB2TEXT, IDS_ARCHIVERBB2TEXT3},
 | 
|---|
| 60 |     {IDS_ARCHIVERBB2TEXT, IDS_ARCHIVERBB2TEXT4},
 | 
|---|
| 61 |     {IDS_ARCHIVERBB2TEXT, IDS_ARCHIVERBB2TEXT5},
 | 
|---|
| 62 |     {IDS_ARCHIVERBB2TEXT, IDS_ARCHIVERBB2TEXT6},
 | 
|---|
| 63 |     {IDS_ARCHIVERBB2TEXT, IDS_ARCHIVERBB2TEXT7},
 | 
|---|
| 64 |     {IDS_INIBINARYDATASKIPTEXT, IDS_INIBINARYDATASKIPTEXT1},
 | 
|---|
| 65 |     {IDS_INIBINARYDATASKIPTEXT, IDS_INIBINARYDATASKIPTEXT2},
 | 
|---|
| 66 |     {IDS_INSTANTHELPTEXT, IDS_INSTANTHELPTEXT1},
 | 
|---|
| 67 |     {IDS_INSTANTHELPTEXT, IDS_INSTANTHELPTEXT2},
 | 
|---|
| 68 |     {IDS_FSDERRORTEXT, IDS_FSDERRORTEXT1},
 | 
|---|
| 69 |     {IDS_FSDERRORTEXT, IDS_FSDERRORTEXT2},
 | 
|---|
| 70 |     {IDS_LANERRORTEXT, IDS_LANERRORTEXT1},
 | 
|---|
| 71 |     {IDS_LANERRORTEXT, IDS_LANERRORTEXT2},
 | 
|---|
| 72 |     {IDS_MAKESHADOWHELPTEXT, IDS_MAKESHADOWHELPTEXT1},
 | 
|---|
| 73 |     {IDS_MAKESHADOWHELPTEXT, IDS_MAKESHADOWHELPTEXT2},
 | 
|---|
| 74 |     {IDS_UNDELETEHELPTEXT, IDS_UNDELETEHELPTEXT1},
 | 
|---|
| 75 |     {IDS_UNDELETEHELPTEXT, IDS_UNDELETEHELPTEXT2},
 | 
|---|
| 76 |     {IDS_KILLPROCHELPTEXT, IDS_KILLPROCHELPTEXT1},
 | 
|---|
| 77 |     {IDS_KILLPROCHELPTEXT, IDS_KILLPROCHELPTEXT2},
 | 
|---|
| 78 |     {IDS_ARCNOTTHERETEXT, IDS_ARCNOTTHERETEXT1},
 | 
|---|
| 79 |     {IDS_ARCNOTTHERETEXT, IDS_ARCNOTTHERETEXT2},
 | 
|---|
| 80 |     {IDS_FM2CMDHELPTEXT, IDS_FM2CMDHELPTEXT1},
 | 
|---|
| 81 |     {IDS_FM2CMDHELPTEXT, IDS_FM2CMDHELPTEXT2},
 | 
|---|
| 82 |     {IDS_FM2CMDHELPTEXT, IDS_FM2CMDHELPTEXT3}
 | 
|---|
| 83 |   };
 | 
|---|
| 84 | 
 | 
|---|
| 85 |   static UINT cLongStrings = sizeof(LongStrings) / sizeof(struct LongString);
 | 
|---|
| 86 | 
 | 
|---|
| 87 |   static volatile INT cBusy;            // Need to be MT-safe
 | 
|---|
| 88 |   static ULONG ulDbgId;                 // 13 Jan 09 SHL fixme to be gone?
 | 
|---|
| 89 |   static UINT uDbgState;                // 03 Feb 09 SHL fixme to be gone?
 | 
|---|
| 90 |   static ULONG ulDbgTid;                // 13 Jan 09 SHL fixme to be gone?
 | 
|---|
| 91 | 
 | 
|---|
| 92 |   UINT c;
 | 
|---|
| 93 |   // 23 Jan 09 SHL fixme to use SMP safe inc/dec?
 | 
|---|
| 94 |   extern void SMPSafeInc(void);
 | 
|---|
| 95 |   extern void SMPSafeDec(void);
 | 
|---|
| 96 |   #pragma aux SMPSafeInc = "lock inc cBusy" modify exact [];
 | 
|---|
| 97 |   #pragma aux SMPSafeDec = "lock dec cBusy" modify exact [];
 | 
|---|
| 98 |   // SMPSafeInc();
 | 
|---|
| 99 |   for (c = 0; ; c++) {
 | 
|---|
| 100 |     if (SMPSafeInc(), cBusy == 1)
 | 
|---|
| 101 |       break;
 | 
|---|
| 102 |     SMPSafeDec();
 | 
|---|
| 103 |     // Hold off 1 cycle before reporting since some contention expected
 | 
|---|
| 104 |     if (c == 1)
 | 
|---|
| 105 |       DbgMsg(pszSrcFile, __LINE__, "GetPString(%lu) waiting for tid %lu GetPString(%lu), state=%u", id, ulDbgTid, ulDbgId, uDbgState);
 | 
|---|
| 106 |     DosSleep(1);                        // Let current owner finish
 | 
|---|
| 107 |   }
 | 
|---|
| 108 |   if (c > 1)
 | 
|---|
| 109 |     DbgMsg(pszSrcFile, __LINE__, "continuing with GetPString(%lu) after tid %lu GetPString(%lu), state=%u", id, ulDbgTid, ulDbgId, uDbgState);
 | 
|---|
| 110 | 
 | 
|---|
| 111 |   // Remember id and thread ordinal for diagnosing MT hangs
 | 
|---|
| 112 |   // Use fast DosGetInfoBlocks to ensure debug logic does not change timing
 | 
|---|
| 113 |   {
 | 
|---|
| 114 |     extern PTIB2 GetPTIB2(void);
 | 
|---|
| 115 |     #pragma aux GetPTIB2 = "mov eax,fs:[12]" value [eax];
 | 
|---|
| 116 |     // PIB *ppib;
 | 
|---|
| 117 |     // TIB *ptib;
 | 
|---|
| 118 |     TIB2 *ptib2 = GetPTIB2();
 | 
|---|
| 119 |     // APIRET apiret = DosGetInfoBlocks(&ptib, &ppib);
 | 
|---|
| 120 |     ulDbgId = id;
 | 
|---|
| 121 |     // ulDbgTid = apiret == 0 ? ptib->tib_ptib2->tib2_ultid : 0;
 | 
|---|
| 122 |     ulDbgTid = ptib2->tib2_ultid;
 | 
|---|
| 123 |   }
 | 
|---|
| 124 | 
 | 
|---|
| 125 |   // DbgMsg(pszSrcFile, __LINE__, "Fetching %lu", id);
 | 
|---|
| 126 | 
 | 
|---|
| 127 |   // If string already loaded, return it now
 | 
|---|
| 128 |   if (id >= ulFirstId &&
 | 
|---|
| 129 |       id <= ulLastId &&
 | 
|---|
| 130 |       pLoadedStrings &&
 | 
|---|
| 131 |       (psz = pLoadedStrings[id - ulFirstId]) != NULL) {
 | 
|---|
| 132 |     cBusy--;
 | 
|---|
| 133 |     if (((ULONG)psz & 0xffff0000) == 0)
 | 
|---|
| 134 |       DbgMsg(pszSrcFile, __LINE__, "id %lu corrupted %p", id, psz);
 | 
|---|
| 135 |     // DbgMsg(pszSrcFile, __LINE__, "id %lu \"%s\"", id, psz ? psz : "(null)");
 | 
|---|
| 136 |     return psz;
 | 
|---|
| 137 |   }
 | 
|---|
| 138 | 
 | 
|---|
| 139 |   // Try to load
 | 
|---|
| 140 |   // 11 Jan 09 SHL fixme to use global HAB?
 | 
|---|
| 141 |   uDbgState = 1;
 | 
|---|
| 142 |   l = WinLoadString((HAB)NULL, FM3ModHandle, id, sizeof(sz), sz);
 | 
|---|
| 143 |   uDbgState = 2;
 | 
|---|
| 144 | 
 | 
|---|
| 145 |   if (l != 0) {
 | 
|---|
| 146 |     psz = xstrdup(sz, pszSrcFile, __LINE__);
 | 
|---|
| 147 |     if (!psz) {
 | 
|---|
| 148 |       cBusy--;
 | 
|---|
| 149 |       return NullStr;
 | 
|---|
| 150 |     }
 | 
|---|
| 151 |   }
 | 
|---|
| 152 |   else {
 | 
|---|
| 153 |     // Assume string must be built from multiple strings - find first
 | 
|---|
| 154 |     UINT i;
 | 
|---|
| 155 |     psz = NULL;
 | 
|---|
| 156 |     for (i = 0; i < cLongStrings && LongStrings[i].id != id; i++);      // Scan
 | 
|---|
| 157 | 
 | 
|---|
| 158 |     if (i < cLongStrings) {
 | 
|---|
| 159 |       // Combine stringtable items to build long string
 | 
|---|
| 160 |       // DbgMsg(pszSrcFile, __LINE__, "Building long string %lu", id);
 | 
|---|
| 161 |       for (; LongStrings[i].id == id; i++) {
 | 
|---|
| 162 |         uDbgState = 3;
 | 
|---|
| 163 |         l = WinLoadString((HAB)NULL, FM3ModHandle, LongStrings[i].sub_id, sizeof(sz), sz);
 | 
|---|
| 164 |         uDbgState = 4;
 | 
|---|
| 165 |         if (l == 0) {
 | 
|---|
| 166 |           cBusy--;
 | 
|---|
| 167 |           Runtime_Error(pszSrcFile, __LINE__, "string %lu missing", LongStrings[i].sub_id);
 | 
|---|
| 168 |           xfree(psz, pszSrcFile, __LINE__);
 | 
|---|
| 169 |           return NullStr;
 | 
|---|
| 170 |         }
 | 
|---|
| 171 |         if (!psz) {
 | 
|---|
| 172 |           // Remember 1st string
 | 
|---|
| 173 |           psz = strdup(sz);
 | 
|---|
| 174 |           if (!psz) {
 | 
|---|
| 175 |             cBusy--;
 | 
|---|
| 176 |             return NullStr;
 | 
|---|
| 177 |           }
 | 
|---|
| 178 |         }
 | 
|---|
| 179 |         else {
 | 
|---|
| 180 |           // Append string
 | 
|---|
| 181 |           UINT curLen = strlen(psz);
 | 
|---|
| 182 |           PSZ psz2 = xrealloc(psz, curLen + l + 1, pszSrcFile, __LINE__);
 | 
|---|
| 183 |           if (!psz2) {
 | 
|---|
| 184 |             xfree(psz, pszSrcFile, __LINE__);
 | 
|---|
| 185 |             cBusy--;
 | 
|---|
| 186 |             return NullStr;
 | 
|---|
| 187 |           }
 | 
|---|
| 188 |           memcpy(psz2 + curLen, sz, l); // Append
 | 
|---|
| 189 |           *(psz2 + curLen + l) = 0;     // Terminate
 | 
|---|
| 190 |           psz = psz2;                   // Remember
 | 
|---|
| 191 |           l += curLen;
 | 
|---|
| 192 |         }
 | 
|---|
| 193 |       } // while
 | 
|---|
| 194 |     } // if long
 | 
|---|
| 195 |   } // if loaded
 | 
|---|
| 196 | 
 | 
|---|
| 197 |   if (l == 0) {
 | 
|---|
| 198 |     DbgMsg(pszSrcFile, __LINE__, "Error loading %lu", id);
 | 
|---|
| 199 |     sprintf(sz, "** Error loading id %lu **", id);
 | 
|---|
| 200 |     psz = xstrdup(sz, pszSrcFile, __LINE__);
 | 
|---|
| 201 |     if (psz)
 | 
|---|
| 202 |       l = strlen(sz);
 | 
|---|
| 203 |     else
 | 
|---|
| 204 |       psz = NullStr;            // Oh heck
 | 
|---|
| 205 |   }
 | 
|---|
| 206 | 
 | 
|---|
| 207 |   uDbgState = 5;
 | 
|---|
| 208 |   // Add to cache
 | 
|---|
| 209 |   // DbgMsg(pszSrcFile, __LINE__, "Caching %lu", id);
 | 
|---|
| 210 | 
 | 
|---|
| 211 |   // Calculate new array limits
 | 
|---|
| 212 |   if (!pLoadedStrings) {
 | 
|---|
| 213 |     ulNewFirstId = id;
 | 
|---|
| 214 |     ulNewLastId = id;
 | 
|---|
| 215 |     ulFirstId = id;
 | 
|---|
| 216 |     ulLastId = id;
 | 
|---|
| 217 |   }
 | 
|---|
| 218 |   else {
 | 
|---|
| 219 |     ulNewFirstId = id < ulFirstId ? id : ulFirstId;
 | 
|---|
| 220 |     ulNewLastId = id > ulLastId ? id : ulLastId;
 | 
|---|
| 221 |   }
 | 
|---|
| 222 | 
 | 
|---|
| 223 |   if (ulNewFirstId != ulFirstId ||
 | 
|---|
| 224 |       ulNewLastId != ulLastId ||
 | 
|---|
| 225 |       !pLoadedStrings) {
 | 
|---|
| 226 |     PSZ *pNewLoadedStrings;
 | 
|---|
| 227 |     // DbgMsg(pszSrcFile, __LINE__, "Reallocating for %lu", id);
 | 
|---|
| 228 |     pNewLoadedStrings = xrealloc(pLoadedStrings,
 | 
|---|
| 229 |                                  (ulNewLastId - ulNewFirstId + 1) * sizeof(PSZ),
 | 
|---|
| 230 |                                  pszSrcFile, __LINE__);
 | 
|---|
| 231 |     if (!pNewLoadedStrings) {
 | 
|---|
| 232 |       cBusy--;
 | 
|---|
| 233 |       Runtime_Error(pszSrcFile, __LINE__, "realloc failed");
 | 
|---|
| 234 |       xfree(psz, pszSrcFile, __LINE__);
 | 
|---|
| 235 |       return NullStr;
 | 
|---|
| 236 |     }
 | 
|---|
| 237 |     // Align existing entries and zero fill unused entries as needed
 | 
|---|
| 238 |     if (ulNewFirstId < ulFirstId) {
 | 
|---|
| 239 |       // Move room for new entries at head of array
 | 
|---|
| 240 |       memmove(pNewLoadedStrings + (ulFirstId - ulNewFirstId),
 | 
|---|
| 241 |              pNewLoadedStrings,
 | 
|---|
| 242 |              (ulLastId - ulFirstId + 1) * sizeof(PSZ));
 | 
|---|
| 243 |       // Null unused placeholder entries
 | 
|---|
| 244 |       if (ulFirstId - ulNewFirstId > 1)
 | 
|---|
| 245 |         memset(pNewLoadedStrings + 1, 0, (ulFirstId - ulNewFirstId - 1) * sizeof(PSZ));
 | 
|---|
| 246 |     }
 | 
|---|
| 247 |     if (ulNewLastId - ulLastId > 1) {
 | 
|---|
| 248 |       // Null unused placeholder entries
 | 
|---|
| 249 |       memset(pNewLoadedStrings + (ulLastId - ulNewFirstId + 1),
 | 
|---|
| 250 |              0,
 | 
|---|
| 251 |              (ulNewLastId - ulLastId - 1) * sizeof(PSZ));
 | 
|---|
| 252 |     }
 | 
|---|
| 253 |     pLoadedStrings = pNewLoadedStrings;
 | 
|---|
| 254 |     ulFirstId = ulNewFirstId;
 | 
|---|
| 255 |     ulLastId = ulNewLastId;
 | 
|---|
| 256 |   }
 | 
|---|
| 257 | 
 | 
|---|
| 258 |   uDbgState = 6;
 | 
|---|
| 259 |   pLoadedStrings[id - ulFirstId] = psz;
 | 
|---|
| 260 |   cBusy--;
 | 
|---|
| 261 |   // DbgMsg(pszSrcFile, __LINE__, "id %lu \"%s\"", id, psz ? psz : "(null)");
 | 
|---|
| 262 |   return psz;
 | 
|---|
| 263 | }
 | 
|---|
| 264 | 
 | 
|---|
| 265 | #pragma alloc_text(STRINGS,LoadStrings,GetPString)
 | 
|---|