AcCrtFileWrappers.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. //
  2. //////////////////////////////////////////////////////////////////////////////
  3. //
  4. // Copyright 2015 Autodesk, Inc. All rights reserved.
  5. //
  6. // Use of this software is subject to the terms of the Autodesk license
  7. // agreement provided at the time of installation or download, or which
  8. // otherwise accompanies this software in either electronic or hard copy form.
  9. //
  10. //////////////////////////////////////////////////////////////////////////////
  11. //
  12. // Name: AcCrtFileWrappers.h
  13. //
  14. // Description: Wrapper classes for clib FILE functions
  15. //
  16. //////////////////////////////////////////////////////////////////////////////
  17. //
  18. #pragma once
  19. #include "stdio.h" // for fopen, FILE, fprintf, etc
  20. #include <malloc.h> // for _alloca
  21. #include "AdCharFmt.h"
  22. #include "casuppress.h"
  23. #pragma warning(push)
  24. #pragma warning(disable:4996)
  25. #ifdef ASSERT
  26. #define AcFILE_Assert ASSERT
  27. #elif defined assert
  28. #define AcFILE_Assert assert
  29. #elif defined _ASSERTE
  30. #define AcFILE_Assert _ASSERTE
  31. #else
  32. #define AcFILE_Assert(T)
  33. #endif
  34. class AcFILE {
  35. public:
  36. // fwrite() expands LF's for us, so we set the formatter's mode
  37. // to not do the LF expansion.
  38. AcFILE()
  39. : mpFILE(NULL),
  40. mChFmtr(AdCharFormatter::kAnsi,
  41. false, // useCIF
  42. false) // expandLF
  43. {}
  44. AcFILE(FILE *pFILE)
  45. : mpFILE(pFILE),
  46. mChFmtr(AdCharFormatter::kAnsi,
  47. false, // useCIF
  48. false) // expandLF
  49. {}
  50. ~AcFILE() {
  51. AcFILE_Assert(this->mpFILE == NULL);
  52. }
  53. FILE * fopen(const TCHAR *pName, const TCHAR *pMode);
  54. FILE * fsopen(const TCHAR *pName, const TCHAR *pMode, int shflag);
  55. void attach(FILE *pFILE);
  56. FILE * detach();
  57. int fclose();
  58. int fputs(const TCHAR *pStr);
  59. TCHAR * fgets(TCHAR *pBuf, int nChars);
  60. int fprintf(const TCHAR *pFmtStr, ...);
  61. int vfprintf(const TCHAR *pFmtStr, va_list va);
  62. int fscanf(const TCHAR *pFmtStr, ...);
  63. int fputc(TCHAR c);
  64. int ungetc(TCHAR c);
  65. int fgetc();
  66. bool isOpen() const { return this->mpFILE != NULL; }
  67. FILE * pFILE() const { return this->mpFILE; }
  68. unsigned getCharFormat() const { return this->mChFmtr.getFormat(); }
  69. unsigned setCharFormat(unsigned nFmt) {
  70. return this->mChFmtr.setFormat(nFmt); }
  71. bool getUseCIF() const { return this->mChFmtr.getUseCIF(); }
  72. bool setUseCIF(bool bUseCIF) {
  73. return this->mChFmtr.setUseCIF(bUseCIF); }
  74. bool getExpandLF() const { return this->mChFmtr.getExpandLF(); }
  75. bool setExpandLF(bool bExpandLF) {
  76. return this->mChFmtr.setExpandLF(bExpandLF); }
  77. bool readBOM();
  78. bool writeBOM();
  79. private:
  80. #ifdef UNICODE
  81. bool parseAnsiOrCIF(wchar_t &wch);
  82. bool parseUtf8(wchar_t &wch);
  83. bool parseUtf16(wchar_t &wch);
  84. int fputsWorker(const wchar_t* pSrc, int nOptions);
  85. #endif
  86. FILE *mpFILE;
  87. AdCharFormatter mChFmtr;
  88. };
  89. // instance methods
  90. inline FILE * AcFILE::fopen(const TCHAR *pName, const TCHAR *pMode)
  91. {
  92. AcFILE_Assert(this->mpFILE == NULL);
  93. this->mpFILE = ::_tfopen(pName, pMode);
  94. return this->mpFILE;
  95. }
  96. inline FILE * AcFILE::fsopen(const TCHAR *pName, const TCHAR *pMode, int shflag)
  97. {
  98. AcFILE_Assert(this->mpFILE == NULL);
  99. this->mpFILE = ::_tfsopen(pName, pMode, shflag);
  100. return this->mpFILE;
  101. }
  102. inline void AcFILE::attach(FILE *pFILE)
  103. {
  104. AcFILE_Assert(this->mpFILE == NULL);
  105. this->mpFILE = pFILE;
  106. }
  107. inline FILE * AcFILE::detach()
  108. {
  109. FILE *pRet = this->mpFILE;
  110. this->mpFILE = NULL;
  111. return pRet;
  112. }
  113. inline int AcFILE::fclose()
  114. {
  115. int nRet = 0;
  116. if (this->mpFILE != NULL) {
  117. nRet = ::fclose(this->mpFILE);
  118. this->mpFILE = NULL;
  119. }
  120. return nRet;
  121. }
  122. inline int AcFILE::fputs(const TCHAR *pStr)
  123. {
  124. AcFILE_Assert(this->mpFILE != NULL);
  125. #ifndef UNICODE
  126. return ::fputs(pStr, this->mpFILE);
  127. #else
  128. return fputsWorker(pStr, 0);
  129. #endif
  130. }
  131. inline TCHAR * AcFILE::fgets(TCHAR *pBuf, int nChars)
  132. {
  133. AcFILE_Assert(this->mpFILE != NULL);
  134. // Need room for null, so it doesn't make sense to pass
  135. // a count of 1 or less.
  136. AcFILE_Assert(nChars > 1);
  137. #ifndef UNICODE
  138. return ::fgets(pBuf, nChars, this->mpFILE);
  139. #else
  140. // We don't know how many widechars the ansi chars
  141. // will turn into, so we have to read them one
  142. // at a time. Read until we hit newline, eof
  143. // or nChars.
  144. TCHAR *pSavePtr = pBuf;
  145. for (;;) {
  146. if (nChars <= 1)
  147. break;
  148. const int ch = this->fgetc();
  149. if (ch == EOF) // -1
  150. break;
  151. AcFILE_Assert((ch & ~0xffff) == 0); // only low 16 bits set
  152. AcFILE_Assert((wchar_t)ch == ch);
  153. *pSavePtr = (wchar_t)ch;
  154. pSavePtr++;
  155. if (ch == '\n')
  156. break; // stop reading after newline
  157. nChars--;
  158. }
  159. *pSavePtr = 0;
  160. if (pSavePtr == pBuf)
  161. return NULL;
  162. return pBuf;
  163. #endif
  164. }
  165. inline int AcFILE::fprintf(const TCHAR *pFmtStr, ...)
  166. {
  167. AcFILE_Assert(this->mpFILE != NULL);
  168. va_list va;
  169. va_start(va, pFmtStr);
  170. return this->vfprintf(pFmtStr, va);
  171. }
  172. inline int AcFILE::vfprintf(const TCHAR *pFmtStr, va_list va)
  173. {
  174. #ifndef UNICODE
  175. return ::vfprintf(this->mpFILE, pFmtStr, va);
  176. #else
  177. // Note: we have to format the entire string into a widechar
  178. // buffer first, then convert that to ansi.
  179. int nChar = ::_vscwprintf(pFmtStr, va);
  180. wchar_t* wBuf = new wchar_t[nChar + 1];
  181. ::wmemset(wBuf, 0, nChar + 1);
  182. ::vswprintf_s(wBuf,nChar + 1, pFmtStr, va);
  183. AcFILE_Assert(wBuf[nChar] == 0);
  184. int iRet = fputsWorker(wBuf, 1);
  185. delete [] wBuf;
  186. return iRet;
  187. #endif
  188. }
  189. #ifndef _ADESK_MAC_
  190. inline int AcFILE::fscanf(const TCHAR *pFmtStr, ...)
  191. {
  192. AcFILE_Assert(this->mpFILE != NULL);
  193. // TODO: Remove this, it's meant to shut the compiler up.
  194. pFmtStr = NULL;
  195. // UNICODE: TODO: SPAGO: There is no existing client of fscanf
  196. // method. Implement this method when time allows/need arises.
  197. }
  198. #endif
  199. inline int AcFILE::fputc(TCHAR c)
  200. {
  201. AcFILE_Assert(this->mpFILE != NULL);
  202. #ifndef UNICODE
  203. return ::fputc(c, this->mpFILE);
  204. #else
  205. char chBuf[8]; // CIF is 7 bytes, utf-32 cr-lf might be 8
  206. const unsigned nBytes = this->mChFmtr.wcharToBytes(c, chBuf,
  207. sizeof(chBuf));
  208. AcFILE_Assert(nBytes >= 1);
  209. AcFILE_Assert(nBytes <= 8); // worst case is utf-32 cr-lf?
  210. const unsigned nNumWrote = (unsigned)fwrite(chBuf, 1,
  211. nBytes, this->mpFILE);
  212. if (nNumWrote == nBytes)
  213. return c; // return the char written
  214. else
  215. return -1; // error
  216. #endif
  217. }
  218. inline int AcFILE::ungetc(TCHAR c)
  219. {
  220. AcFILE_Assert(this->mpFILE != NULL);
  221. #ifndef UNICODE
  222. return ::ungetc(c, this->mpFILE);
  223. #else
  224. char chBuf[8]; // CIF is 7 bytes, utf-32 cr-lf might be 8
  225. const unsigned nBytes = this->mChFmtr.wcharToBytes(c, chBuf,
  226. sizeof(chBuf));
  227. AcFILE_Assert(nBytes >= 1);
  228. AcFILE_Assert(nBytes <= 8);
  229. // Note: for now we can only unget a single char. So if
  230. // the wide char got converted to CIF or double-byte,
  231. // we fail. Todo: fix this if it's really needed.
  232. if (nBytes == 1 && ::ungetc(chBuf[0], this->mpFILE) == chBuf[0])
  233. return c; // on success, return the char passed in
  234. else
  235. return -1;
  236. #endif
  237. }
  238. inline int AcFILE::fgetc()
  239. {
  240. AcFILE_Assert(this->mpFILE != NULL);
  241. #ifndef UNICODE
  242. return ::fgetc(this->mpFILE);
  243. #else
  244. wchar_t wchLocal = L'\0';
  245. switch(this->getCharFormat())
  246. {
  247. case AdCharFormatter::kAnsi:
  248. if (parseAnsiOrCIF(wchLocal))
  249. return wchLocal;
  250. return EOF;
  251. break;
  252. case AdCharFormatter::kUtf8:
  253. if (parseUtf8(wchLocal))
  254. return wchLocal;
  255. return EOF;
  256. break;
  257. case AdCharFormatter::kUtf16LE:
  258. case AdCharFormatter::kUtf16BE:
  259. if (parseUtf16(wchLocal))
  260. return wchLocal;
  261. return EOF;
  262. break;
  263. case AdCharFormatter::kUtf32LE:
  264. case AdCharFormatter::kUtf32BE:
  265. AcFILE_Assert(false); // Implement me!
  266. return EOF;
  267. break;
  268. }
  269. AcFILE_Assert(false); // Should never reach here!
  270. return EOF;
  271. #endif
  272. }
  273. inline bool AcFILE::readBOM()
  274. {
  275. AcFILE_Assert(this->mpFILE != NULL);
  276. const long lFilePos = ::ftell(this->mpFILE);
  277. AcFILE_Assert(lFilePos == 0L);
  278. if (lFilePos != 0L)
  279. return false; // Can't do this unless we're at start of file.
  280. unsigned short nVal;
  281. if (::fread(&nVal, 1, 2, this->mpFILE) == 2) {
  282. if (nVal == 0xfeff) {
  283. this->setCharFormat(AdCharFormatter::kUtf16LE);
  284. return true;
  285. }
  286. if (nVal == 0xfffe) {
  287. this->setCharFormat(AdCharFormatter::kUtf16BE);
  288. return true;
  289. }
  290. if (nVal == 0xbbef) {
  291. unsigned char nByte3;
  292. if (::fread(&nByte3, 1, 1, this->mpFILE) == 1) {
  293. if (nByte3 == 0xbf) {
  294. this->setCharFormat(AdCharFormatter::kUtf8);
  295. return true;
  296. }
  297. }
  298. }
  299. else if (nVal == 0 || nVal == 0xfeff) {
  300. unsigned nVal2;
  301. if (::fread(&nVal2, 1, 2, this->mpFILE) == 2) {
  302. if (nVal == 0 && nVal2 == 0xfffe) {
  303. this->setCharFormat(AdCharFormatter::kUtf32BE);
  304. return true;
  305. }
  306. else if (nVal == 0xfeff && nVal2 == 0) {
  307. this->setCharFormat(AdCharFormatter::kUtf32LE);
  308. return true;
  309. }
  310. }
  311. }
  312. }
  313. // If got here, then no BOM found, so reset
  314. // to file beginning. Leave format what it was.
  315. ::rewind(this->mpFILE);
  316. return false;
  317. }
  318. inline bool AcFILE::writeBOM()
  319. {
  320. AcFILE_Assert(this->mpFILE != NULL);
  321. if (this->mpFILE == NULL)
  322. return false; // There must be an associated file.
  323. const long lFilePos = ::ftell(this->mpFILE);
  324. AcFILE_Assert(lFilePos == 0L);
  325. if (lFilePos != 0L)
  326. return false; // Can't do this unless we're at start of file.
  327. unsigned nBom = 0;
  328. const int cbBomSize = AdCharFormatter::getBOM(nBom,
  329. this->getCharFormat());
  330. if (cbBomSize == 0) // AdcharFormatter::getBOM would
  331. return false; // have brought up an assertion.
  332. const int cbWritten = (int) ::fwrite(&nBom, // BOM to be written.
  333. 1, // Item size.
  334. cbBomSize, // Item count.
  335. this->mpFILE); // File pointer.
  336. AcFILE_Assert(cbWritten == cbBomSize);
  337. if (cbWritten != cbBomSize)
  338. {
  339. ::rewind(this->mpFILE);
  340. return false;
  341. }
  342. return true;
  343. }
  344. #ifdef UNICODE
  345. inline bool AcFILE::parseAnsiOrCIF(wchar_t &wch)
  346. {
  347. AcFILE_Assert(this->getCharFormat() == AdCharFormatter::kAnsi);
  348. wch = L'\0';
  349. // Read in a single character from file.
  350. unsigned char cFirstChar = 0;
  351. int nBytesRead = (int)::fread(&cFirstChar, 1, 1, this->mpFILE);
  352. if (nBytesRead <= 0)
  353. return false; // Error or EOF condition.
  354. if (cFirstChar > 0x7f) // Possible double-byte character.
  355. {
  356. char chBuf[2] = {0};
  357. wchar_t wchLocal = 0;
  358. chBuf[0] = ((char) cFirstChar);
  359. const BOOL bIsDBCS = ::IsDBCSLeadByteEx(CP_ACP, cFirstChar);
  360. if (bIsDBCS != FALSE) // Attempt to read in the second character.
  361. {
  362. unsigned char cSecondChar = 0;
  363. nBytesRead = (int)::fread(&cSecondChar, 1, 1, this->mpFILE);
  364. if (nBytesRead <= 0)
  365. return false; // Error or EOF condition.
  366. chBuf[1] = ((char) cSecondChar);
  367. }
  368. // TODO: Handle the cases of surrogate pairs.
  369. const int nRet = ::MultiByteToWideChar(
  370. CP_ACP, // Conversion code page.
  371. MB_ERR_INVALID_CHARS, // Do we want this?
  372. chBuf, // DBCS character.
  373. bIsDBCS ? 2 : 1, // DBCS byte count.
  374. &wchLocal, // Converted wide character.
  375. 1); // Single wide character.
  376. AcFILE_Assert(nRet == 1); // If fails, do we need lang. pack installed?
  377. wch = (nRet == 1) ? wchLocal : L'?';
  378. return true;
  379. }
  380. AcFILE_Assert(cFirstChar <= 0x7f);
  381. wch = ((wchar_t) cFirstChar);
  382. if (cFirstChar == '\r' && (this->mChFmtr.getExpandLF())) {
  383. unsigned char cNewLinePair = 0; // Any pairing '\n'?
  384. nBytesRead = (int)::fread(&cNewLinePair, 1, 1, this->mpFILE);
  385. if (nBytesRead <= 0) // '\r' at the end of file, odd...
  386. return true; // but we are taking it as it is anyway.
  387. if (cNewLinePair != '\n') {
  388. ::fseek(this->mpFILE, -1L, SEEK_CUR);
  389. return true; // Return '\r' untranslated.
  390. }
  391. wch = L'\n'; // Translate to new line character.
  392. return true;
  393. }
  394. // If CIF not desired, return any character.
  395. if (cFirstChar != '\\' || (this->getUseCIF()) == false)
  396. return true;
  397. // When we get here, it means that there is possible CIF string
  398. // ahead of us. we will read in more from the underlying file...
  399. char chBuffer[8] = {0};
  400. chBuffer[0] = '\\';
  401. // Attempt to read 6 more bytes from file. This function might
  402. // not be reading as many bytes as we want but that's not what
  403. // we care right now, leave it to AdCharFormatter::isCIFString.
  404. nBytesRead = (int) ::fread(&chBuffer[1], // Input buffer.
  405. 1, // Unit data size.
  406. 6, // Unit count.
  407. this->mpFILE); // FILE pointer.
  408. // If it's a CIF sequence, parse into wch.
  409. if (AdCharFormatter::isCIFString(chBuffer))
  410. {
  411. AdCharFormatter::parseCIF(chBuffer, wch);
  412. return true;
  413. }
  414. // It was not CIF, we read too much, undo that.
  415. if (nBytesRead > 0)
  416. ::fseek(this->mpFILE, -nBytesRead, SEEK_CUR);
  417. wch = L'\\';
  418. return true;
  419. }
  420. inline bool AcFILE::parseUtf8(wchar_t &wch)
  421. {
  422. AcFILE_Assert(this->getUseCIF() == false);
  423. // Read in a single character from file.
  424. unsigned char cCharacter = 0;
  425. int nBytesRead = (int) ::fread(&cCharacter, 1, 1, this->mpFILE);
  426. if (nBytesRead <= 0)
  427. return false; // Error or EOF condition.
  428. if (cCharacter <= 0x7f)
  429. {
  430. wch = cCharacter;
  431. if (cCharacter == '\r' && mChFmtr.getExpandLF()) {
  432. unsigned char cNewLine = 0;
  433. nBytesRead = (int) ::fread(&cNewLine, 1, 1, this->mpFILE);
  434. if (nBytesRead <= 0) // '\r' at the end of file...
  435. return true; // ... weird but we'll take it anyway.
  436. if (cNewLine != '\n') { // Not a new line character!
  437. ::fseek(this->mpFILE, -1L, SEEK_CUR);
  438. return true; // Return '\r' untranslated.
  439. }
  440. wch = L'\n'; // Translated to new line character.
  441. }
  442. return true; // Single byte UTF8 character.
  443. }
  444. int nByteCount;
  445. char chBuffer[4] = {0};
  446. chBuffer[0] = ((char) cCharacter);
  447. if ((chBuffer[0] & 0xe0) == 0xc0)
  448. nByteCount = 2; // 110xxxxx = 2-byte code
  449. else if((chBuffer[0] & 0xf0) == 0xe0)
  450. nByteCount = 3; // 1110xxxx = 3-byte code
  451. else if((chBuffer[0] & 0xf8) == 0xf0)
  452. nByteCount = 4; // 11110xxx = 4-byte code
  453. else
  454. {
  455. // Adding this assert for now, to help with
  456. // debugging. Maybe we'll have to relax it if
  457. // we find we commonly hit malformed data...
  458. AcFILE_Assert(false);
  459. return false; // Malformed UTF8 character?
  460. }
  461. // Attempt to read one or more bytes from file...
  462. nBytesRead = (int) ::fread(&chBuffer[1], // Input buffer.
  463. 1, // Unit data size.
  464. nByteCount - 1, // Unit count.
  465. this->mpFILE); // FILE pointer.
  466. AcFILE_Assert(nBytesRead == nByteCount - 1);
  467. if (nBytesRead < nByteCount - 1)
  468. return false; // Malformed UTF8 character.
  469. const int nCvted = MultiByteToWideChar(CP_UTF8, // Conversion code page.
  470. 0, // Flags must be 0 for UTF8.
  471. chBuffer, // Input UTF8 string.
  472. nByteCount, // Byte count in UTF8 string.
  473. &wch, // Destination wide character.
  474. 1); // Expect a single wide char.
  475. AcFILE_Assert(nCvted == 1);
  476. return nCvted == 1;
  477. }
  478. inline bool AcFILE::parseUtf16(wchar_t &wch)
  479. {
  480. wchar_t wchLocal = 0;
  481. int nBytesRead = (int) ::fread(&wchLocal, 1, 2, this->mpFILE);
  482. if (nBytesRead <= 0)
  483. return false; // Error or EOF condition.
  484. const unsigned nFormat = this->mChFmtr.getFormat();
  485. if (nFormat == AdCharFormatter::kUtf16BE)
  486. wchLocal = (wchLocal >> 8) | (wchLocal << 8);
  487. wch = wchLocal;
  488. if (wchLocal == L'\r' && mChFmtr.getExpandLF()) {
  489. wchar_t wchNewLine = L'\n';
  490. nBytesRead = (int) ::fread(&wchNewLine, 1, 2, this->mpFILE);
  491. if (nBytesRead <= 0) // '\r' at the end of file...
  492. return true; // ... weird but we'll take it.
  493. if (nFormat == AdCharFormatter::kUtf16BE)
  494. wchNewLine = (wchNewLine >> 8) | (wchNewLine << 8);
  495. if (wchNewLine != L'\n') { // Not a new line character.
  496. ::fseek(this->mpFILE, -2L, SEEK_CUR);
  497. return true; // Return '\r' untranslated.
  498. }
  499. wch = L'\n'; // Translated to new line character.
  500. }
  501. return true;
  502. }
  503. // This helper function writes data to the file depending on the value of
  504. // nOptions. In case of fputs zero is returned when successful and incase
  505. // of fprintf the return value is the number of bytes.
  506. //
  507. inline int AcFILE::fputsWorker(const wchar_t* pSrc, int nOptions)
  508. {
  509. AcFILE_Assert(this->mpFILE != NULL);
  510. AcFILE_Assert(pSrc != NULL);
  511. int nNumInput = 0;
  512. for (;;) {
  513. const wchar_t wch = *pSrc;
  514. if (wch == 0)
  515. break;
  516. char chBuf[8]; // CIF is 7 bytes, utf-32 cr-lf might be 8
  517. const unsigned nBytes = this->mChFmtr.wcharToBytes(wch, chBuf,
  518. sizeof(chBuf));
  519. AcFILE_Assert(nBytes >= 1);
  520. AcFILE_Assert(nBytes <= 8);
  521. const unsigned nNumWrote = (unsigned)fwrite(chBuf, 1,
  522. nBytes, this->mpFILE);
  523. if (nNumWrote != nBytes)
  524. return -1; // error (out of disk space?)
  525. nNumInput++;
  526. pSrc++;
  527. }
  528. if (nOptions == 0) // called from puts
  529. return 0; // 0 indicates success, apparently
  530. else if (nOptions == 1) // called from printf
  531. return nNumInput;
  532. else {
  533. AcFILE_Assert(false);
  534. }
  535. return -1;
  536. }
  537. #endif // UNICODE defined.
  538. #pragma warning(pop)