Common.cpp 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261
  1. #include "Common.h"
  2. #include "util/BumpAllocator.h"
  3. #include "util/UTF8.h"
  4. #include "util/String.h"
  5. //#include "farts.z"
  6. #include <string.h>
  7. #include "platform/PlatformHelper.h"
  8. #ifndef BF_SMALL
  9. #define STB_SPRINTF_IMPLEMENTATION
  10. #include "../third_party/stb/stb_sprintf.h"
  11. #endif
  12. extern "C"
  13. {
  14. #include "../third_party/utf8proc/utf8proc.h"
  15. }
  16. #ifdef BF_PLATFORM_WINDOWS
  17. #include <shellapi.h>
  18. #include <direct.h>
  19. #endif
  20. #pragma warning(disable:4996)
  21. #pragma comment(lib, "winmm.lib")
  22. int gBFArgC;
  23. char** gBFArgV;
  24. #ifdef BUMPALLOC_ETW_TRACING
  25. VSHeapTracker::CHeapTracker* Beefy::BumpAllocator::mVSHeapTracker = NULL;
  26. #pragma comment(lib, "VSCustomNativeHeapEtwProvider.lib")
  27. #endif
  28. USING_NS_BF;
  29. UTF16String Beefy::ToWString(const StringImpl& theString)
  30. {
  31. UTF16String aString;
  32. aString.Reserve(theString.length() + 1);
  33. for (intptr i = 0; i < theString.length(); ++i)
  34. aString.Add((unsigned char)theString[i]);
  35. aString.Add(0);
  36. return aString;
  37. }
  38. // String Beefy::ToString(const UTF16String& theString)
  39. // {
  40. // String newString;
  41. // newString.Reserve((intptr)theString.length());
  42. // for (int i = 0; i < (int)theString.length(); ++i)
  43. // {
  44. // const unsigned int c = (unsigned int)theString[i];
  45. // newString.Append(char(c & 0xFF));
  46. // }
  47. // return newString;
  48. // }
  49. String Beefy::ToUpper(const StringImpl& theString)
  50. {
  51. String aString = theString;
  52. for (int i = 0; i < (int)aString.length(); ++i)
  53. aString[i] = toupper(aString[i]);
  54. return aString;
  55. }
  56. UTF16String Beefy::ToUpper(const UTF16String& theString)
  57. {
  58. UTF16String aString = theString;
  59. for (int i = 0; i < (int)aString.length(); ++i)
  60. aString[i] = toupper(aString[i]);
  61. return aString;
  62. }
  63. UTF16String Beefy::ToLower(const UTF16String& theString)
  64. {
  65. UTF16String aString = theString;
  66. for (int i = 0; i < (int)aString.length(); ++i)
  67. aString[i] = tolower(aString[i]);
  68. return aString;
  69. }
  70. String Beefy::ToLower(const StringImpl& theString)
  71. {
  72. String aString = theString;
  73. for (int i = 0; i < (int)aString.length(); ++i)
  74. aString[i] = (char)tolower((int)(uint8)aString[i]);
  75. return aString;
  76. }
  77. // UTF16String Beefy::Trim(const UTF16String& theString)
  78. // {
  79. // int left = 0;
  80. // while ((left < (int) theString.length() - 1) && (iswspace(theString[left])))
  81. // left++;
  82. //
  83. // int right = (int)theString.length() - 1;
  84. // while ((right >= left) && (iswspace(theString[right])))
  85. // right--;
  86. //
  87. // if ((left == 0) && (right == theString.length() - 1))
  88. // return theString;
  89. //
  90. // return theString.substr(left, right - left + 1);
  91. // }
  92. String Beefy::Trim(const StringImpl& theString)
  93. {
  94. int left = 0;
  95. while ((left < (int) theString.length() - 1) && (iswspace(theString[left])))
  96. left++;
  97. int right = (int)theString.length() - 1;
  98. while ((right >= left) && (iswspace(theString[right])))
  99. right--;
  100. if ((left == 0) && (right == theString.length() - 1))
  101. return theString;
  102. return theString.Substring(left, right - left + 1);
  103. }
  104. bool Beefy::StrReplace(StringImpl& str, const StringImpl& from, const StringImpl& to)
  105. {
  106. str.Replace(from, to);
  107. return true;
  108. }
  109. bool Beefy::StrStartsWith(const StringImpl& str, const StringImpl& subStr)
  110. {
  111. if (subStr.length() < str.length())
  112. return false;
  113. return strncmp(str.c_str(), subStr.c_str(), subStr.length()) == 0;
  114. }
  115. bool Beefy::StrEndsWith(const StringImpl& str, const StringImpl& subStr)
  116. {
  117. if (subStr.length() < str.length())
  118. return false;
  119. return strncmp(str.c_str() - (str.length() - subStr.length()), subStr.c_str(), subStr.length()) == 0;
  120. }
  121. #ifndef BF_SMALL
  122. String Beefy::SlashString(const StringImpl& str, bool utf8decode, bool utf8encode, bool beefString)
  123. {
  124. bool prevEndedInSlashedNum = false;
  125. String outStr;
  126. bool noNextHex = false;
  127. bool lastWasVisibleChar = false;
  128. for (int idx = 0; idx < (int)str.length(); idx++)
  129. {
  130. bool endingInSlashedNum = false;
  131. char c = str[idx];
  132. if (noNextHex)
  133. {
  134. if (((c >= '0') && (c <= '9')) ||
  135. ((c >= 'a') && (c <= 'f')) ||
  136. ((c >= 'A') && (c <= 'F')))
  137. {
  138. outStr += "\"\"";
  139. }
  140. noNextHex = false;
  141. }
  142. bool isVisibleChar = false;
  143. switch (c)
  144. {
  145. case '\0':
  146. outStr += "\\0";
  147. endingInSlashedNum = true;
  148. break;
  149. case '\a':
  150. outStr += "\\a";
  151. break;
  152. case '\b':
  153. outStr += "\\b";
  154. break;
  155. case '\t':
  156. outStr += "\\t";
  157. break;
  158. case '\n':
  159. outStr += "\\n";
  160. break;
  161. case '\v':
  162. outStr += "\\v";
  163. break;
  164. case '\f':
  165. outStr += "\\f";
  166. break;
  167. case '\r':
  168. outStr += "\\r";
  169. break;
  170. case '\"':
  171. outStr += "\\\"";
  172. break;
  173. case '\\':
  174. outStr += "\\\\";
  175. break;
  176. default:
  177. {
  178. if ((c >= '0') && (c <= '9'))
  179. {
  180. // Need to break string to allow proper evaluation of slashed string
  181. if (prevEndedInSlashedNum)
  182. outStr += "\" \"";
  183. }
  184. // May be UTF8 byte. Make sure it's valid before we add it, otherwise write the bytes in direct "\x" style
  185. if (((uint8)c >= 0x80) && (utf8decode))
  186. {
  187. int seqLen = u8_seqlen((char*)&str[idx]);
  188. if (seqLen > 1)
  189. {
  190. if (idx + seqLen <= str.length())
  191. {
  192. uint32 c32 = u8_toucs((char*)&str[idx], (int)str.length() - idx);
  193. bool wantHex = false;
  194. // Combining mark without previous visible char?
  195. if ((((c32 >= L'\u0300') && (c32 <= L'\u036F')) || ((c32 >= L'\u1DC0') && (c32 <= L'\u1DFF'))) &&
  196. (!lastWasVisibleChar))
  197. wantHex = true;
  198. utf8proc_category_t cat = utf8proc_category(c32);
  199. switch (cat)
  200. {
  201. case UTF8PROC_CATEGORY_ZS:
  202. case UTF8PROC_CATEGORY_ZL:
  203. case UTF8PROC_CATEGORY_ZP:
  204. case UTF8PROC_CATEGORY_CC:
  205. case UTF8PROC_CATEGORY_CF:
  206. case UTF8PROC_CATEGORY_CO:
  207. wantHex = true;
  208. break;
  209. default:
  210. break;
  211. }
  212. if (wantHex)
  213. {
  214. if (beefString)
  215. {
  216. char tempStr[32];
  217. sprintf(tempStr, "\\u{%x}", c32);
  218. outStr += tempStr;
  219. idx += seqLen - 1;
  220. continue;
  221. }
  222. else
  223. {
  224. char tempStr[32];
  225. sprintf(tempStr, "\\u%04x", c32);
  226. outStr += tempStr;
  227. idx += seqLen - 1;
  228. noNextHex = true;
  229. continue;
  230. }
  231. }
  232. bool passed = true;
  233. for (int checkOfs = 0; checkOfs < seqLen - 1; checkOfs++)
  234. {
  235. char checkC = str[idx + checkOfs + 1];
  236. if ((checkC & 0xC0) != 0x80) // Has trailing signature (0x10xxxxxx)?
  237. passed = false;
  238. }
  239. if (passed)
  240. {
  241. outStr += c;
  242. for (int checkOfs = 0; checkOfs < seqLen - 1; checkOfs++)
  243. {
  244. char checkC = str[idx + checkOfs + 1];
  245. outStr += checkC;
  246. }
  247. idx += seqLen - 1;
  248. continue;
  249. }
  250. }
  251. }
  252. }
  253. if (((uint8)c >= 0x80) && (utf8encode))
  254. {
  255. outStr += (char)(0xC0 | (((uint8)c & 0xFF) >> 6));
  256. outStr += (char)(0x80 | ((uint8)c & 0x3F));
  257. isVisibleChar = true;
  258. continue;
  259. }
  260. if (((uint8)c >= 32) && ((uint8)c < 0x80))
  261. {
  262. outStr += c;
  263. isVisibleChar = true;
  264. }
  265. else
  266. {
  267. char tempStr[32];
  268. sprintf(tempStr, "\\x%02x", (uint8)c);
  269. outStr += tempStr;
  270. endingInSlashedNum = true;
  271. if (!beefString)
  272. noNextHex = true;
  273. }
  274. }
  275. break;
  276. }
  277. prevEndedInSlashedNum = endingInSlashedNum;
  278. lastWasVisibleChar = isVisibleChar;
  279. }
  280. return outStr;
  281. }
  282. #endif
  283. UTF16String Beefy::UTF8Decode(const StringImpl& theString)
  284. {
  285. UTF16String strOut;
  286. int strLen = 0;
  287. char* cPtr = (char*)theString.c_str();
  288. int lenLeft = (int)theString.length();
  289. while (lenLeft > 0)
  290. {
  291. int seqLen = 0;
  292. uint32 c32 = u8_toucs(cPtr, lenLeft, &seqLen);
  293. if ((c32 >= 0x10000) && (sizeof(wchar_t) == 2))
  294. strLen += 2;
  295. else
  296. strLen += 1;
  297. cPtr += seqLen;
  298. lenLeft -= seqLen;
  299. }
  300. strOut.ResizeRaw(strLen + 1);
  301. strOut[strLen] = 0;
  302. cPtr = (char*)theString.c_str();
  303. uint16* wcPtr = strOut.mVals;
  304. int wcLenLeft = (int)strOut.length() + 1;
  305. lenLeft = (int)theString.length();
  306. while (lenLeft > 0)
  307. {
  308. int seqLen = 0;
  309. uint32 c32 = u8_toucs(cPtr, lenLeft, &seqLen);
  310. if ((c32 >= 0x10000) && (sizeof(wchar_t) == 2))
  311. {
  312. *(wcPtr++) = (wchar_t)(((c32 - 0x10000) >> 10) + 0xD800);
  313. *(wcPtr++) = (wchar_t)(((c32 - 0x10000) & 0x3FF) + 0xDC00);
  314. wcLenLeft -= 2;
  315. }
  316. else
  317. {
  318. *(wcPtr++) = (wchar_t)c32;
  319. wcLenLeft -= 1;
  320. }
  321. cPtr += seqLen;
  322. lenLeft -= seqLen;
  323. }
  324. return strOut;
  325. }
  326. String Beefy::UTF8Encode(const uint16* theString, int length)
  327. {
  328. if (length == 0)
  329. return String();
  330. String strOut;
  331. int utf8Len = 0;
  332. uint16 utf16hi = 0;
  333. for (int i = 0; i < length; i++)
  334. {
  335. uint16 c = theString[i];
  336. uint32 c32 = c;
  337. if ((c >= 0xD800) && (c < 0xDC00))
  338. {
  339. utf16hi = (uint16)c;
  340. continue;
  341. }
  342. else if ((c >= 0xDC00) && (c < 0xE000))
  343. {
  344. uint16 utf16lo = c;
  345. c32 = 0x10000 + ((uint32)(utf16hi - 0xD800) << 10) | (uint32)(utf16lo - 0xDC00);
  346. }
  347. utf8Len += u8_seqlen(c32);
  348. }
  349. strOut.Append('?', utf8Len);
  350. char* cPtr = (char*)&strOut[0];
  351. int lenLeft = (int)strOut.length() + 1;
  352. for (int i = 0; i < length; i++)
  353. {
  354. uint16 c = theString[i];
  355. uint32 c32 = c;
  356. if ((c >= 0xD800) && (c < 0xDC00))
  357. {
  358. utf16hi = (uint16)c;
  359. continue;
  360. }
  361. else if ((c >= 0xDC00) && (c < 0xE000))
  362. {
  363. uint16 utf16lo = c;
  364. c32 = 0x10000 + ((uint32)(utf16hi - 0xD800) << 10) | (uint32)(utf16lo - 0xDC00);
  365. }
  366. int len = u8_toutf8(cPtr, lenLeft, c32);
  367. cPtr += len;
  368. lenLeft -= len;
  369. }
  370. return strOut;
  371. }
  372. String Beefy::UTF8Encode(const UTF16String& theString)
  373. {
  374. return UTF8Encode((uint16*)theString.c_str(), (int)theString.length());
  375. }
  376. UTF16String Beefy::UTF16Decode(const uint16* theString)
  377. {
  378. if (sizeof(wchar_t) == 2)
  379. {
  380. UTF16String str;
  381. str.Set((wchar_t*)theString);
  382. return str;
  383. }
  384. else
  385. {
  386. UTF16String str;
  387. int len = 0;
  388. while (theString[len] != 0)
  389. len++;
  390. str.ResizeRaw(len + 1);
  391. str[len] = 0;
  392. for (int pos = 0; pos < len; pos++)
  393. str[pos] = (wchar_t)theString[pos];
  394. return str;
  395. }
  396. }
  397. static const char gHexChar[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
  398. String Beefy::FileNameToURI(const StringImpl& fileName)
  399. {
  400. #ifdef _WIN32
  401. String out = "file:///";
  402. #else
  403. String out = "file://";
  404. #endif
  405. for (int i = 0; i < (int)fileName.length(); i++)
  406. {
  407. char c = out[i];
  408. bool isValid =
  409. ((c >= '@' && c <= 'Z') ||
  410. (c >= 'a' && c <= 'z') ||
  411. (c >= '&' && c < 0x3b) ||
  412. (c == '!') || (c == '$') || (c == '_') || (c == '=') || (c == '~'));
  413. if ((((unsigned char)c) >= 0x80) || (!isValid))
  414. {
  415. out += '%';
  416. out += gHexChar[((unsigned char)(c)) >> 4];
  417. out += gHexChar[((unsigned char)(c)) & 0xf];
  418. }
  419. else if (c == '\\')
  420. out += '/';
  421. else
  422. out += c;
  423. }
  424. return out;
  425. }
  426. static const uint8 cCharTo64b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  427. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0,
  428. 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 63,
  429. 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
  430. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  431. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  432. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  433. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  434. int64 Beefy::DecodeULEB32(const char*& p)
  435. {
  436. uint64 val = 0;
  437. unsigned shift = 0;
  438. uint8 charVal;
  439. do
  440. {
  441. charVal = cCharTo64b[(uint8)*(p++)];
  442. val += uint64(charVal & 0x1f) << shift;
  443. shift += 5;
  444. } while ((charVal & 0x20) != 0);
  445. return val;
  446. }
  447. static const char c64bToChar[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
  448. 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
  449. 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
  450. 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_'};
  451. void Beefy::EncodeULEB32(uint64 value, StringImpl& buffer)
  452. {
  453. do
  454. {
  455. uint8 byteVal = value & 0x1f;
  456. value >>= 5;
  457. if (value != 0)
  458. byteVal |= 0x20; // Mark this byte to show that more bytes will follow
  459. buffer.Append(c64bToChar[byteVal]);
  460. }
  461. while (value != 0);
  462. }
  463. #ifndef BF_SMALL
  464. void Beefy::ExactMinimalFloatToStr(float f, char* str)
  465. {
  466. sprintf(str, "%1.9g", f);
  467. char* cPtrLast = str;
  468. while (true)
  469. {
  470. char c = cPtrLast[1];
  471. if ((c == 0) || (c == 'e'))
  472. break;
  473. cPtrLast++;
  474. }
  475. char* cStrEnd = cPtrLast + 1;
  476. int baseLen = (int)(cPtrLast - str) + 1;
  477. if (baseLen < 9)
  478. return; // Isn't "full" precision
  479. char removeChar;
  480. char lastChar = *cPtrLast;
  481. char secondToLast = cPtrLast[-1];
  482. if (secondToLast == '9')
  483. {
  484. removeChar = '9';
  485. }
  486. else if (secondToLast == '0')
  487. {
  488. removeChar = '0';
  489. }
  490. else
  491. {
  492. return; // Not a 'transitional' representation
  493. }
  494. do
  495. {
  496. *cPtrLast = 0;
  497. cPtrLast--;
  498. } while (*cPtrLast == removeChar);
  499. strcpy(cPtrLast + 1, cStrEnd);
  500. if (removeChar == '9')
  501. (*cPtrLast)++;
  502. // Verify that our pretty representation is equivalent
  503. float checkF = 0;
  504. sscanf(str, "%f", &checkF);
  505. if (f == checkF)
  506. return;
  507. // Rollback
  508. int endLen = (int)strlen(cPtrLast + 1);
  509. memmove(cStrEnd, cPtrLast + 1, endLen);
  510. if (removeChar == '9')
  511. (*cPtrLast)--;
  512. cPtrLast++;
  513. while (cPtrLast < cStrEnd - 1)
  514. {
  515. *cPtrLast = removeChar;
  516. cPtrLast++;
  517. }
  518. *cPtrLast = lastChar;
  519. }
  520. void Beefy::ExactMinimalDoubleToStr(double d, char* str)
  521. {
  522. sprintf(str, "%1.17g", d);
  523. char* cPtrLast = str;
  524. while (true)
  525. {
  526. char c = cPtrLast[1];
  527. if ((c == 0) || (c == 'e'))
  528. break;
  529. cPtrLast++;
  530. }
  531. char* cStrEnd = cPtrLast + 1;
  532. int baseLen = (int)(cPtrLast - str) + 1;
  533. if (baseLen < 18)
  534. return; // Isn't "full" precision
  535. char removeChar;
  536. char lastChar = *cPtrLast;
  537. if ((lastChar == '9') || (lastChar == '8'))
  538. {
  539. removeChar = '9';
  540. }
  541. else if ((lastChar == '1') || (lastChar == '2'))
  542. {
  543. removeChar = '0';
  544. }
  545. else
  546. {
  547. return; // Not a 'transitional' representation
  548. }
  549. do
  550. {
  551. *cPtrLast = 0;
  552. cPtrLast--;
  553. }
  554. while (*cPtrLast == removeChar);
  555. strcpy(cPtrLast + 1, cStrEnd);
  556. if (removeChar == '9')
  557. (*cPtrLast)++;
  558. // Verify that our pretty representation is equivalent
  559. double checkD = 0;
  560. sscanf(str, "%lf", &checkD);
  561. if (d == checkD)
  562. return;
  563. // Rollback
  564. int endLen = (int)strlen(cPtrLast + 1);
  565. memmove(cStrEnd, cPtrLast + 1, endLen);
  566. if (removeChar == '9')
  567. (*cPtrLast)--;
  568. cPtrLast++;
  569. while (cPtrLast < cStrEnd - 1)
  570. {
  571. *cPtrLast = removeChar;
  572. cPtrLast++;
  573. }
  574. *cPtrLast = lastChar;
  575. }
  576. #endif
  577. static char* StbspCallback(char *buf, void *user, int len)
  578. {
  579. ((String*)user)->Append(buf, len);
  580. return buf;
  581. }
  582. //extern "C" int _vsnprintf(char* const _Buffer, size_t const _BufferCount, char const* const _Format, va_list _ArgList, int zz);
  583. #ifdef BF_SMALL
  584. String Beefy::vformat(const char* fmt, va_list argPtr)
  585. {
  586. // We draw the line at a 1MB string.
  587. const int maxSize = 1000000;
  588. //va_list checkArgPtr = argPtr;
  589. //va_start(checkArgPtr, fmt);
  590. int argIdx = 0;
  591. char* newFmt = NULL;
  592. char tempBuff[2048];
  593. char* tempBuffPtr = tempBuff;
  594. for (int i = 0; fmt[i] != 0; i++)
  595. {
  596. if (fmt[i] == '%')
  597. {
  598. if (fmt[i + 1] == '%')
  599. {
  600. i++;
  601. continue;
  602. }
  603. #ifdef BF32
  604. bool isLongAddr = false;
  605. #else
  606. bool isLongAddr = true;
  607. #endif
  608. if ((fmt[i + 1] == 'l') && (fmt[i + 2] == '@'))
  609. {
  610. isLongAddr = true;
  611. i++;
  612. }
  613. if (fmt[i + 1] == '@')
  614. {
  615. if (newFmt == NULL)
  616. {
  617. newFmt = (char*)malloc(strlen(fmt) + 1);
  618. strcpy(newFmt, fmt);
  619. //newFmt = strdup(fmt);
  620. }
  621. newFmt[i + 1] = 's';
  622. const char*& argValPtr = *(const char**)((intptr*)argPtr + argIdx);
  623. if (isLongAddr)
  624. {
  625. int64 iVal = *(int64*)((intptr*)argPtr + argIdx);
  626. #ifdef BF32
  627. argIdx++; // Takes two spots
  628. #endif
  629. argValPtr = tempBuffPtr;
  630. int leftVal = (int)(iVal >> 32);
  631. if (leftVal != 0)
  632. {
  633. sprintf(tempBuffPtr, "%x", leftVal);
  634. tempBuffPtr += strlen(tempBuffPtr);
  635. }
  636. tempBuffPtr[0] = '\'';
  637. tempBuffPtr++;
  638. sprintf(tempBuffPtr, "%08x", (int)iVal);
  639. tempBuffPtr += strlen(tempBuffPtr) + 1;
  640. }
  641. else
  642. {
  643. int32 iVal = (int32)(intptr)argValPtr;
  644. argValPtr = tempBuffPtr;
  645. sprintf(tempBuffPtr, "%08x", iVal);
  646. tempBuffPtr += strlen(tempBuffPtr) + 1;
  647. }
  648. if (newFmt[i] == 'l')
  649. newFmt[i] = '+';
  650. }
  651. else
  652. {
  653. //const char*& argValPtr = va_arg(checkArgPtr, const char*);
  654. }
  655. argIdx++;
  656. }
  657. }
  658. if (newFmt != NULL)
  659. {
  660. Beefy::String retVal = vformat(newFmt, argPtr);
  661. free(newFmt);
  662. return retVal;
  663. }
  664. // If the string is less than 161 characters,
  665. // allocate it on the stack because this saves
  666. // the malloc/free time.
  667. const int bufSize = 161;
  668. char stackBuffer[bufSize];
  669. int attemptedSize = bufSize - 1;
  670. int numChars = 0;
  671. #ifdef _WIN32
  672. numChars = _vsnprintf(stackBuffer, attemptedSize, fmt, argPtr);
  673. #else
  674. numChars = vsnprintf(stackBuffer, attemptedSize, fmt, argPtr);
  675. #endif
  676. if ((numChars >= 0) && (numChars <= attemptedSize))
  677. {
  678. // Needed for case of 160-character printf thing
  679. stackBuffer[numChars] = '\0';
  680. // Got it on the first try.
  681. return String(stackBuffer);
  682. }
  683. // Now use the heap.
  684. char* heapBuffer = NULL;
  685. while (((numChars == -1) || (numChars > attemptedSize)) &&
  686. (attemptedSize < maxSize))
  687. {
  688. // Try a bigger size
  689. attemptedSize *= 2;
  690. heapBuffer = (char*)realloc(heapBuffer, (attemptedSize + 1));
  691. #ifdef _WIN32
  692. numChars = _vsnprintf(heapBuffer, attemptedSize, fmt, argPtr);
  693. #else
  694. numChars = vsnprintf(heapBuffer, attemptedSize, fmt, argPtr);
  695. #endif
  696. }
  697. if (numChars == -1)
  698. {
  699. free(heapBuffer);
  700. return "";
  701. }
  702. heapBuffer[numChars] = 0;
  703. Beefy::String aResult = Beefy::String(heapBuffer);
  704. free(heapBuffer);
  705. return aResult;
  706. }
  707. #else
  708. String Beefy::vformat(const char* fmt, va_list argPtr)
  709. {
  710. String str;
  711. char buf[STB_SPRINTF_MIN];
  712. stbsp_vsprintfcb(StbspCallback, (void*)&str, buf, fmt, argPtr);
  713. return str;
  714. }
  715. #endif
  716. String Beefy::StrFormat(const char* fmt ...)
  717. {
  718. va_list argList;
  719. va_start(argList, fmt);
  720. String aResult = vformat(fmt, argList);
  721. va_end(argList);
  722. return aResult;
  723. }
  724. String Beefy::IntPtrDynAddrFormat(intptr addr)
  725. {
  726. if ((addr & 0xFFFFFFFF00000000LL) == 0)
  727. return StrFormat("%08X", addr);
  728. return StrFormat("%p", addr);
  729. }
  730. void Beefy::OutputDebugStr(const StringImpl& theString)
  731. {
  732. const int maxLen = 0xFFFE;
  733. if (theString.length() > maxLen)
  734. {
  735. OutputDebugStr(theString.Substring(0, maxLen));
  736. OutputDebugStr(theString.Substring(maxLen));
  737. return;
  738. }
  739. BfpOutput_DebugString(theString.c_str());
  740. }
  741. void Beefy::OutputDebugStrF(const char* fmt ...)
  742. {
  743. va_list argList;
  744. va_start(argList, fmt);
  745. String aResult = vformat(fmt, argList);
  746. va_end(argList);
  747. OutputDebugStr(aResult);
  748. }
  749. uint8* Beefy::LoadBinaryData(const StringImpl& path, int* size)
  750. {
  751. FILE* fP = fopen(path.c_str(), "rb");
  752. if (fP == NULL)
  753. return NULL;
  754. fseek(fP, 0, SEEK_END);
  755. int aSize = (int32)ftell(fP);
  756. fseek(fP, 0, SEEK_SET);
  757. uint8* data = new uint8[aSize];
  758. int readSize = (int)fread(data, 1, aSize, fP);
  759. fclose(fP);
  760. if (size)
  761. *size = aSize;
  762. return data;
  763. }
  764. char* Beefy::LoadTextData(const StringImpl& path, int* size)
  765. {
  766. FILE* fP = fopen(path.c_str(), "rb");
  767. if (fP == NULL)
  768. return NULL;
  769. fseek(fP, 0, SEEK_END);
  770. int fileSize = (int32)ftell(fP);
  771. int strLen = fileSize;
  772. fseek(fP, 0, SEEK_SET);
  773. uint8 charHeader[3] = {0};
  774. int readSize = (int)fread(charHeader, 1, 3, fP);
  775. if ((charHeader[0] == 0xFF) && (charHeader[1] == 0xFE))
  776. {
  777. //UTF16 LE
  778. int dataLen = fileSize - 2;
  779. char* data = new char[dataLen + 2];
  780. data[0] = (char)charHeader[2];
  781. data[dataLen] = 0;
  782. data[dataLen + 1] = 0;
  783. int readSize = (int)fread(data + 1, 1, dataLen - 1, fP);
  784. fclose(fP);
  785. // UTF16
  786. UTF16String str;
  787. str.Set((wchar_t*)data);
  788. delete [] data;
  789. String utf8Str = UTF8Encode(str);
  790. char* utf8Data = new char[utf8Str.length() + 1];
  791. strLen = (int)utf8Str.length();
  792. if (size != NULL)
  793. *size = strLen;
  794. memcpy(utf8Data, utf8Str.c_str(), strLen);
  795. return utf8Data;
  796. }
  797. else if ((charHeader[0] == 0xEF) && (charHeader[1] == 0xBB) && (charHeader[2] == 0xBF))
  798. {
  799. strLen = fileSize - 3;
  800. char* data = new char[strLen + 1];
  801. data[strLen] = 0;
  802. if (size != NULL)
  803. *size = strLen;
  804. int readSize = (int)fread(data, 1, strLen, fP);
  805. fclose(fP);
  806. return data;
  807. }
  808. if (size != NULL)
  809. *size = strLen;
  810. char* data = new char[strLen + 1];
  811. data[strLen] = 0;
  812. for (int i = 0; i < BF_MIN(3, strLen); i++)
  813. data[i] = charHeader[i];
  814. if (strLen > 3)
  815. {
  816. int readSize = (int)fread(data + 3, 1, strLen - 3, fP);
  817. }
  818. fclose(fP);
  819. return data;
  820. }
  821. #ifdef BF_MINGW
  822. unsigned long long __cdecl _byteswap_uint64(unsigned long long _Int64)
  823. {
  824. #ifdef _WIN64
  825. unsigned long long retval;
  826. __asm__ __volatile__ ("bswapq %[retval]" : [retval] "=rm" (retval) : "[retval]" (_Int64));
  827. return retval;
  828. #else
  829. union {
  830. long long int64part;
  831. struct {
  832. unsigned long lowpart;
  833. unsigned long hipart;
  834. };
  835. } retval;
  836. retval.int64part = _Int64;
  837. __asm__ __volatile__ ("bswapl %[lowpart]\n"
  838. "bswapl %[hipart]\n"
  839. : [lowpart] "=rm" (retval.hipart), [hipart] "=rm" (retval.lowpart) : "[lowpart]" (retval.lowpart), "[hipart]" (retval.hipart));
  840. return retval.int64part;
  841. #endif
  842. }
  843. #endif
  844. #ifdef _WIN32
  845. int64 Beefy::EndianSwap(int64 val)
  846. {
  847. return _byteswap_uint64(val);
  848. }
  849. #endif
  850. int32 Beefy::EndianSwap(int val)
  851. {
  852. return ((val & 0x000000FF) << 24) | ((val & 0x0000FF00) << 8) |
  853. ((val & 0x00FF0000) >> 8) | ((val & 0xFF000000) >> 24);
  854. }
  855. int16 Beefy::EndianSwap(int16 val)
  856. {
  857. return ((val & 0x00FF) << 8) | ((val & 0xFF00) >> 8);
  858. }
  859. int32 Beefy::Rand()
  860. {
  861. return (rand() & 0xFFFF) | ((rand() & 0x7FFF) << 16);
  862. }
  863. int32 Beefy::GetHighestBitSet(int32 n)
  864. {
  865. int i = 0;
  866. for (; n; n >>= 1, i++)
  867. ; /* empty */
  868. return i;
  869. }
  870. String Beefy::GetFileDir(const StringImpl& path)
  871. {
  872. int slashPos = BF_MAX((int)path.LastIndexOf('\\'), (int)path.LastIndexOf('/'));
  873. if (slashPos == -1)
  874. return "";
  875. return path.Substring(0, slashPos);
  876. }
  877. String Beefy::GetFileName(const StringImpl& path)
  878. {
  879. int slashPos = BF_MAX((int)path.LastIndexOf('\\'), (int)path.LastIndexOf('/'));
  880. if (slashPos == -1)
  881. return path;
  882. return path.Substring(slashPos + 1);
  883. }
  884. String Beefy::GetFileExtension(const StringImpl& path)
  885. {
  886. int dotPos = (int)path.LastIndexOf('.');
  887. if (dotPos == -1)
  888. return path;
  889. return path.Substring(dotPos);
  890. }
  891. String Beefy::GetAbsPath(const StringImpl& relPathIn, const StringImpl& dir)
  892. {
  893. String relPath = relPathIn;
  894. String driveString = "";
  895. String newPath;
  896. newPath = dir;
  897. for (int i = 0; i < (int)newPath.length(); i++)
  898. if (newPath[i] == DIR_SEP_CHAR_ALT)
  899. newPath[i] = DIR_SEP_CHAR;
  900. for (int i = 0; i < (int)relPath.length(); i++)
  901. if (relPath[i] == DIR_SEP_CHAR_ALT)
  902. relPath[i] = DIR_SEP_CHAR;
  903. if ((relPath.length() >= 2) && (relPath[1] == ':'))
  904. return relPath;
  905. char slashChar = DIR_SEP_CHAR;
  906. if ((newPath.length() >= 2) && (newPath[1] == ':'))
  907. {
  908. driveString = newPath.Substring(0, 2);
  909. newPath = newPath.Substring(2);
  910. }
  911. // Append a trailing slash if necessary
  912. if ((newPath.length() > 0) && (newPath[newPath.length() - 1] != '\\') && (newPath[newPath.length() - 1] != '/'))
  913. newPath += slashChar;
  914. int relIdx = 0;
  915. for (;;)
  916. {
  917. if (newPath.length() == 0)
  918. break;
  919. int firstSlash = -1;
  920. for (int32 i = relIdx; i < (int)relPath.length(); i++)
  921. if ((relPath[i] == '\\') || (relPath[i] == '/'))
  922. {
  923. firstSlash = i;
  924. break;
  925. }
  926. if (firstSlash == -1)
  927. break;
  928. String chDir = relPath.Substring(relIdx, firstSlash - relIdx);
  929. relIdx = firstSlash + 1;
  930. if (chDir == "..")
  931. {
  932. int lastDirStart = (int)newPath.length() - 1;
  933. while ((lastDirStart > 0) && (newPath[lastDirStart - 1] != '\\') && (newPath[lastDirStart - 1] != '/'))
  934. lastDirStart--;
  935. String lastDir = newPath.Substring(lastDirStart, newPath.length() - lastDirStart - 1);
  936. if (lastDir == "..")
  937. {
  938. newPath += "..";
  939. newPath += DIR_SEP_CHAR;
  940. }
  941. else
  942. {
  943. newPath.RemoveToEnd(lastDirStart);
  944. }
  945. }
  946. else if (chDir == "")
  947. {
  948. newPath = DIR_SEP_CHAR;
  949. break;
  950. }
  951. else if (chDir != ".")
  952. {
  953. newPath += chDir + slashChar;
  954. break;
  955. }
  956. }
  957. //newPath = driveString + newPath + tempRelPath;
  958. newPath = driveString + newPath;
  959. newPath += relPath.Substring(relIdx);
  960. return newPath;
  961. }
  962. String Beefy::FixPath(const StringImpl& pathIn)
  963. {
  964. String path = pathIn;
  965. for (int i = 0; i < (int)path.length(); i++)
  966. {
  967. if (path[i] == DIR_SEP_CHAR_ALT)
  968. path[i] = DIR_SEP_CHAR;
  969. if ((i > 0) && (path[i - 1] == '.') && (path[i] == '.'))
  970. {
  971. for (int checkIdx = i - 3; checkIdx >= 0; checkIdx--)
  972. {
  973. if ((path[checkIdx] == '\\') || (path[checkIdx] == '/'))
  974. {
  975. path = path.Substring(0, checkIdx) + path.Substring(i + 1);
  976. i = checkIdx;
  977. break;
  978. }
  979. }
  980. }
  981. }
  982. return path;
  983. }
  984. String Beefy::FixPathAndCase(const StringImpl& pathIn)
  985. {
  986. String path = FixPath(pathIn);
  987. #ifdef _WIN32
  988. for (int i = 0; i < (int)path.length(); ++i)
  989. path[i] = toupper(path[i]);
  990. #endif
  991. return path;
  992. }
  993. String Beefy::EnsureEndsInSlash(const StringImpl& dir)
  994. {
  995. if ((dir[dir.length() - 1] != '/') && (dir[dir.length() - 1] != '\\'))
  996. return dir + "/";
  997. return dir;
  998. }
  999. String Beefy::RemoveTrailingSlash(const StringImpl& str)
  1000. {
  1001. if (str.length() != 0)
  1002. {
  1003. char c = str[str.length() - 1];
  1004. if ((c == '\\') || (c == '/'))
  1005. return str.Substring(0, str.length() - 1);
  1006. }
  1007. return str;
  1008. }
  1009. bool Beefy::FileNameEquals(const StringImpl& filePathA, const StringImpl& filePathB)
  1010. {
  1011. #ifdef _WIN32
  1012. //return _stricmp(filePathA.c_str(), filePathB.c_str()) == 0;
  1013. if (filePathA.length() != filePathB.length())
  1014. return false;
  1015. const char* aPtr = filePathA.c_str();
  1016. const char* bPtr = filePathB.c_str();
  1017. while (true)
  1018. {
  1019. char a = *(aPtr++);
  1020. char b = *(bPtr++);
  1021. if (a == 0)
  1022. return true;
  1023. if (a == b)
  1024. continue;
  1025. if (a == '/')
  1026. a = '\\';
  1027. if (b == '/')
  1028. b = '\\';
  1029. if (a == b)
  1030. continue;
  1031. if (::toupper(a) == ::toupper(b))
  1032. continue;
  1033. return false;
  1034. }
  1035. #else
  1036. return strcmp(filePathA.c_str(), filePathB.c_str()) == 0;
  1037. #endif
  1038. }
  1039. bool Beefy::RecursiveCreateDirectory(const StringImpl& dirName)
  1040. {
  1041. int slashPos = -1;
  1042. for (int i = (int)dirName.length() - 1; i >= 0; i--)
  1043. {
  1044. char c = dirName[i];
  1045. if ((c == '\\') || (c == '/'))
  1046. {
  1047. slashPos = i;
  1048. break;
  1049. }
  1050. }
  1051. if (slashPos != -1)
  1052. {
  1053. RecursiveCreateDirectory(dirName.Substring(0, slashPos));
  1054. }
  1055. BfpFileResult result;
  1056. BfpDirectory_Create(dirName.c_str(), &result);
  1057. return result == BfpFileResult_Ok;
  1058. }
  1059. bool Beefy::RecursiveDeleteDirectory(const StringImpl& dirPath)
  1060. {
  1061. String findSpec = dirPath + "/*.*";
  1062. bool failed = false;
  1063. BfpFileResult result;
  1064. BfpFindFileData* findFileData = BfpFindFileData_FindFirstFile(findSpec.c_str(), (BfpFindFileFlags)(BfpFindFileFlag_Directories | BfpFindFileFlag_Files), &result);
  1065. if (result == BfpFileResult_Ok)
  1066. {
  1067. while (true)
  1068. {
  1069. Beefy::String fileName;
  1070. BFP_GETSTR_HELPER(fileName, result, BfpFindFileData_GetFileName(findFileData, __STR, __STRLEN, &result));
  1071. String filePath = dirPath + "/" + fileName;
  1072. if ((BfpFindFileData_GetFileAttributes(findFileData) & BfpFileAttribute_Directory) != 0)
  1073. {
  1074. if (!RecursiveDeleteDirectory(filePath))
  1075. failed = true;
  1076. }
  1077. else
  1078. {
  1079. BfpFile_Delete(filePath.c_str(), &result);
  1080. if (result != BfpFileResult_Ok)
  1081. failed = true;
  1082. }
  1083. if (!BfpFindFileData_FindNextFile(findFileData))
  1084. break;
  1085. }
  1086. BfpFindFileData_Release(findFileData);
  1087. }
  1088. else if (result != BfpFileResult_NoResults)
  1089. {
  1090. return false;
  1091. }
  1092. BfpDirectory_Delete(dirPath.c_str(), &result);
  1093. return (result == BfpFileResult_Ok) && (!failed);
  1094. }
  1095. void Beefy::BFFatalError(const char* message, const char* file, int line)
  1096. {
  1097. BFFatalError(String(message), String(file), line);
  1098. }