Common.cpp 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  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. (void)readSize;
  760. fclose(fP);
  761. if (size)
  762. *size = aSize;
  763. return data;
  764. }
  765. char* Beefy::LoadTextData(const StringImpl& path, int* size)
  766. {
  767. FILE* fP = fopen(path.c_str(), "rb");
  768. if (fP == NULL)
  769. return NULL;
  770. fseek(fP, 0, SEEK_END);
  771. int fileSize = (int32)ftell(fP);
  772. int strLen = fileSize;
  773. fseek(fP, 0, SEEK_SET);
  774. uint8 charHeader[3] = {0};
  775. int readSize = (int)fread(charHeader, 1, 3, fP);
  776. if ((charHeader[0] == 0xFF) && (charHeader[1] == 0xFE))
  777. {
  778. //UTF16 LE
  779. int dataLen = fileSize - 2;
  780. char* data = new char[dataLen + 2];
  781. data[0] = (char)charHeader[2];
  782. data[dataLen] = 0;
  783. data[dataLen + 1] = 0;
  784. int readSize = (int)fread(data + 1, 1, dataLen - 1, fP);
  785. (void)readSize;
  786. fclose(fP);
  787. // UTF16
  788. UTF16String str;
  789. str.Set((wchar_t*)data);
  790. delete [] data;
  791. String utf8Str = UTF8Encode(str);
  792. char* utf8Data = new char[utf8Str.length() + 1];
  793. strLen = (int)utf8Str.length();
  794. if (size != NULL)
  795. *size = strLen;
  796. memcpy(utf8Data, utf8Str.c_str(), strLen);
  797. return utf8Data;
  798. }
  799. else if ((charHeader[0] == 0xEF) && (charHeader[1] == 0xBB) && (charHeader[2] == 0xBF))
  800. {
  801. strLen = fileSize - 3;
  802. char* data = new char[strLen + 1];
  803. data[strLen] = 0;
  804. if (size != NULL)
  805. *size = strLen;
  806. int readSize = (int)fread(data, 1, strLen, fP);
  807. (void)readSize;
  808. fclose(fP);
  809. return data;
  810. }
  811. if (size != NULL)
  812. *size = strLen;
  813. char* data = new char[strLen + 1];
  814. data[strLen] = 0;
  815. for (int i = 0; i < BF_MIN(3, strLen); i++)
  816. data[i] = charHeader[i];
  817. if (strLen > 3)
  818. {
  819. int readSize = (int)fread(data + 3, 1, strLen - 3, fP);
  820. (void)readSize;
  821. }
  822. fclose(fP);
  823. return data;
  824. }
  825. #ifdef BF_MINGW
  826. unsigned long long __cdecl _byteswap_uint64(unsigned long long _Int64)
  827. {
  828. #ifdef _WIN64
  829. unsigned long long retval;
  830. __asm__ __volatile__ ("bswapq %[retval]" : [retval] "=rm" (retval) : "[retval]" (_Int64));
  831. return retval;
  832. #else
  833. union {
  834. long long int64part;
  835. struct {
  836. unsigned long lowpart;
  837. unsigned long hipart;
  838. };
  839. } retval;
  840. retval.int64part = _Int64;
  841. __asm__ __volatile__ ("bswapl %[lowpart]\n"
  842. "bswapl %[hipart]\n"
  843. : [lowpart] "=rm" (retval.hipart), [hipart] "=rm" (retval.lowpart) : "[lowpart]" (retval.lowpart), "[hipart]" (retval.hipart));
  844. return retval.int64part;
  845. #endif
  846. }
  847. #endif
  848. #ifdef _WIN32
  849. int64 Beefy::EndianSwap(int64 val)
  850. {
  851. return _byteswap_uint64(val);
  852. }
  853. #endif
  854. int32 Beefy::EndianSwap(int val)
  855. {
  856. return ((val & 0x000000FF) << 24) | ((val & 0x0000FF00) << 8) |
  857. ((val & 0x00FF0000) >> 8) | ((val & 0xFF000000) >> 24);
  858. }
  859. int16 Beefy::EndianSwap(int16 val)
  860. {
  861. return ((val & 0x00FF) << 8) | ((val & 0xFF00) >> 8);
  862. }
  863. int32 Beefy::Rand()
  864. {
  865. return (rand() & 0xFFFF) | ((rand() & 0x7FFF) << 16);
  866. }
  867. int32 Beefy::GetHighestBitSet(int32 n)
  868. {
  869. int i = 0;
  870. for (; n; n >>= 1, i++)
  871. ; /* empty */
  872. return i;
  873. }
  874. String Beefy::GetFileDir(const StringImpl& path)
  875. {
  876. int slashPos = BF_MAX((int)path.LastIndexOf('\\'), (int)path.LastIndexOf('/'));
  877. if (slashPos == -1)
  878. return "";
  879. return path.Substring(0, slashPos);
  880. }
  881. String Beefy::GetFileName(const StringImpl& path)
  882. {
  883. int slashPos = BF_MAX((int)path.LastIndexOf('\\'), (int)path.LastIndexOf('/'));
  884. if (slashPos == -1)
  885. return path;
  886. return path.Substring(slashPos + 1);
  887. }
  888. String Beefy::GetFileExtension(const StringImpl& path)
  889. {
  890. int dotPos = (int)path.LastIndexOf('.');
  891. if (dotPos == -1)
  892. return path;
  893. return path.Substring(dotPos);
  894. }
  895. String Beefy::GetAbsPath(const StringImpl& relPathIn, const StringImpl& dir)
  896. {
  897. String relPath = relPathIn;
  898. String driveString = "";
  899. String newPath;
  900. newPath = dir;
  901. for (int i = 0; i < (int)newPath.length(); i++)
  902. if (newPath[i] == DIR_SEP_CHAR_ALT)
  903. newPath[i] = DIR_SEP_CHAR;
  904. for (int i = 0; i < (int)relPath.length(); i++)
  905. if (relPath[i] == DIR_SEP_CHAR_ALT)
  906. relPath[i] = DIR_SEP_CHAR;
  907. if ((relPath.length() >= 2) && (relPath[1] == ':'))
  908. return relPath;
  909. char slashChar = DIR_SEP_CHAR;
  910. if ((newPath.length() >= 2) && (newPath[1] == ':'))
  911. {
  912. driveString = newPath.Substring(0, 2);
  913. newPath = newPath.Substring(2);
  914. }
  915. // Append a trailing slash if necessary
  916. if ((newPath.length() > 0) && (newPath[newPath.length() - 1] != '\\') && (newPath[newPath.length() - 1] != '/'))
  917. newPath += slashChar;
  918. int relIdx = 0;
  919. for (;;)
  920. {
  921. if (newPath.length() == 0)
  922. break;
  923. int firstSlash = -1;
  924. for (int32 i = relIdx; i < (int)relPath.length(); i++)
  925. if ((relPath[i] == '\\') || (relPath[i] == '/'))
  926. {
  927. firstSlash = i;
  928. break;
  929. }
  930. if (firstSlash == -1)
  931. break;
  932. String chDir = relPath.Substring(relIdx, firstSlash - relIdx);
  933. relIdx = firstSlash + 1;
  934. if (chDir == "..")
  935. {
  936. int lastDirStart = (int)newPath.length() - 1;
  937. while ((lastDirStart > 0) && (newPath[lastDirStart - 1] != '\\') && (newPath[lastDirStart - 1] != '/'))
  938. lastDirStart--;
  939. String lastDir = newPath.Substring(lastDirStart, newPath.length() - lastDirStart - 1);
  940. if (lastDir == "..")
  941. {
  942. newPath += "..";
  943. newPath += DIR_SEP_CHAR;
  944. }
  945. else
  946. {
  947. newPath.RemoveToEnd(lastDirStart);
  948. }
  949. }
  950. else if (chDir == "")
  951. {
  952. newPath = DIR_SEP_CHAR;
  953. break;
  954. }
  955. else if (chDir != ".")
  956. {
  957. newPath += chDir + slashChar;
  958. break;
  959. }
  960. }
  961. //newPath = driveString + newPath + tempRelPath;
  962. newPath = driveString + newPath;
  963. newPath += relPath.Substring(relIdx);
  964. return newPath;
  965. }
  966. String Beefy::FixPath(const StringImpl& pathIn)
  967. {
  968. String path = pathIn;
  969. for (int i = 0; i < (int)path.length(); i++)
  970. {
  971. if (path[i] == DIR_SEP_CHAR_ALT)
  972. path[i] = DIR_SEP_CHAR;
  973. if ((i > 0) && (path[i - 1] == '.') && (path[i] == '.'))
  974. {
  975. for (int checkIdx = i - 3; checkIdx >= 0; checkIdx--)
  976. {
  977. if ((path[checkIdx] == '\\') || (path[checkIdx] == '/'))
  978. {
  979. path = path.Substring(0, checkIdx) + path.Substring(i + 1);
  980. i = checkIdx;
  981. break;
  982. }
  983. }
  984. }
  985. }
  986. return path;
  987. }
  988. String Beefy::FixPathAndCase(const StringImpl& pathIn)
  989. {
  990. String path = FixPath(pathIn);
  991. #ifdef _WIN32
  992. for (int i = 0; i < (int)path.length(); ++i)
  993. path[i] = toupper(path[i]);
  994. #endif
  995. return path;
  996. }
  997. String Beefy::EnsureEndsInSlash(const StringImpl& dir)
  998. {
  999. if ((dir[dir.length() - 1] != '/') && (dir[dir.length() - 1] != '\\'))
  1000. return dir + "/";
  1001. return dir;
  1002. }
  1003. String Beefy::RemoveTrailingSlash(const StringImpl& str)
  1004. {
  1005. if (str.length() != 0)
  1006. {
  1007. char c = str[str.length() - 1];
  1008. if ((c == '\\') || (c == '/'))
  1009. return str.Substring(0, str.length() - 1);
  1010. }
  1011. return str;
  1012. }
  1013. bool Beefy::FileNameEquals(const StringImpl& filePathA, const StringImpl& filePathB)
  1014. {
  1015. #ifdef _WIN32
  1016. //return _stricmp(filePathA.c_str(), filePathB.c_str()) == 0;
  1017. if (filePathA.length() != filePathB.length())
  1018. return false;
  1019. const char* aPtr = filePathA.c_str();
  1020. const char* bPtr = filePathB.c_str();
  1021. while (true)
  1022. {
  1023. char a = *(aPtr++);
  1024. char b = *(bPtr++);
  1025. if (a == 0)
  1026. return true;
  1027. if (a == b)
  1028. continue;
  1029. if (a == '/')
  1030. a = '\\';
  1031. if (b == '/')
  1032. b = '\\';
  1033. if (a == b)
  1034. continue;
  1035. if (::toupper(a) == ::toupper(b))
  1036. continue;
  1037. return false;
  1038. }
  1039. #else
  1040. return strcmp(filePathA.c_str(), filePathB.c_str()) == 0;
  1041. #endif
  1042. }
  1043. bool Beefy::RecursiveCreateDirectory(const StringImpl& dirName)
  1044. {
  1045. int slashPos = -1;
  1046. for (int i = (int)dirName.length() - 1; i >= 0; i--)
  1047. {
  1048. char c = dirName[i];
  1049. if ((c == '\\') || (c == '/'))
  1050. {
  1051. slashPos = i;
  1052. break;
  1053. }
  1054. }
  1055. if (slashPos != -1)
  1056. {
  1057. RecursiveCreateDirectory(dirName.Substring(0, slashPos));
  1058. }
  1059. BfpFileResult result;
  1060. BfpDirectory_Create(dirName.c_str(), &result);
  1061. return result == BfpFileResult_Ok;
  1062. }
  1063. bool Beefy::RecursiveDeleteDirectory(const StringImpl& dirPath)
  1064. {
  1065. String findSpec = dirPath + "/*.*";
  1066. bool failed = false;
  1067. BfpFileResult result;
  1068. BfpFindFileData* findFileData = BfpFindFileData_FindFirstFile(findSpec.c_str(), (BfpFindFileFlags)(BfpFindFileFlag_Directories | BfpFindFileFlag_Files), &result);
  1069. if (result == BfpFileResult_Ok)
  1070. {
  1071. while (true)
  1072. {
  1073. Beefy::String fileName;
  1074. BFP_GETSTR_HELPER(fileName, result, BfpFindFileData_GetFileName(findFileData, __STR, __STRLEN, &result));
  1075. String filePath = dirPath + "/" + fileName;
  1076. if ((BfpFindFileData_GetFileAttributes(findFileData) & BfpFileAttribute_Directory) != 0)
  1077. {
  1078. if (!RecursiveDeleteDirectory(filePath))
  1079. failed = true;
  1080. }
  1081. else
  1082. {
  1083. BfpFile_Delete(filePath.c_str(), &result);
  1084. if (result != BfpFileResult_Ok)
  1085. failed = true;
  1086. }
  1087. if (!BfpFindFileData_FindNextFile(findFileData))
  1088. break;
  1089. }
  1090. BfpFindFileData_Release(findFileData);
  1091. }
  1092. else if (result != BfpFileResult_NoResults)
  1093. {
  1094. return false;
  1095. }
  1096. BfpDirectory_Delete(dirPath.c_str(), &result);
  1097. return (result == BfpFileResult_Ok) && (!failed);
  1098. }
  1099. void Beefy::BFFatalError(const char* message, const char* file, int line)
  1100. {
  1101. BFFatalError(String(message), String(file), line);
  1102. }