🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
6.9 KiB

  1. #ifndef AL_COMPAT_H
  2. #define AL_COMPAT_H
  3. #ifdef __cplusplus
  4. #ifdef _WIN32
  5. #define WIN32_LEAN_AND_MEAN
  6. #include <windows.h>
  7. #include <array>
  8. #include <string>
  9. #include <fstream>
  10. inline std::string wstr_to_utf8(const WCHAR *wstr)
  11. {
  12. std::string ret;
  13. int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
  14. if(len > 0)
  15. {
  16. ret.resize(len);
  17. WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &ret[0], len, nullptr, nullptr);
  18. ret.pop_back();
  19. }
  20. return ret;
  21. }
  22. inline std::wstring utf8_to_wstr(const char *str)
  23. {
  24. std::wstring ret;
  25. int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
  26. if(len > 0)
  27. {
  28. ret.resize(len);
  29. MultiByteToWideChar(CP_UTF8, 0, str, -1, &ret[0], len);
  30. ret.pop_back();
  31. }
  32. return ret;
  33. }
  34. namespace al {
  35. // Windows' std::ifstream fails with non-ANSI paths since the standard only
  36. // specifies names using const char* (or std::string). MSVC has a non-standard
  37. // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
  38. // but not all Windows compilers support it. So we have to make our own istream
  39. // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
  40. class filebuf final : public std::streambuf {
  41. std::array<char_type,4096> mBuffer;
  42. HANDLE mFile{INVALID_HANDLE_VALUE};
  43. int_type underflow() override
  44. {
  45. if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr())
  46. {
  47. // Read in the next chunk of data, and set the pointers on success
  48. DWORD got = 0;
  49. if(ReadFile(mFile, mBuffer.data(), (DWORD)mBuffer.size(), &got, nullptr))
  50. setg(mBuffer.data(), mBuffer.data(), mBuffer.data()+got);
  51. }
  52. if(gptr() == egptr())
  53. return traits_type::eof();
  54. return traits_type::to_int_type(*gptr());
  55. }
  56. pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override
  57. {
  58. if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
  59. return traits_type::eof();
  60. LARGE_INTEGER fpos;
  61. switch(whence)
  62. {
  63. case std::ios_base::beg:
  64. fpos.QuadPart = offset;
  65. if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
  66. return traits_type::eof();
  67. break;
  68. case std::ios_base::cur:
  69. // If the offset remains in the current buffer range, just
  70. // update the pointer.
  71. if((offset >= 0 && offset < off_type(egptr()-gptr())) ||
  72. (offset < 0 && -offset <= off_type(gptr()-eback())))
  73. {
  74. // Get the current file offset to report the correct read
  75. // offset.
  76. fpos.QuadPart = 0;
  77. if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
  78. return traits_type::eof();
  79. setg(eback(), gptr()+offset, egptr());
  80. return fpos.QuadPart - off_type(egptr()-gptr());
  81. }
  82. // Need to offset for the file offset being at egptr() while
  83. // the requested offset is relative to gptr().
  84. offset -= off_type(egptr()-gptr());
  85. fpos.QuadPart = offset;
  86. if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
  87. return traits_type::eof();
  88. break;
  89. case std::ios_base::end:
  90. fpos.QuadPart = offset;
  91. if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END))
  92. return traits_type::eof();
  93. break;
  94. default:
  95. return traits_type::eof();
  96. }
  97. setg(nullptr, nullptr, nullptr);
  98. return fpos.QuadPart;
  99. }
  100. pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override
  101. {
  102. // Simplified version of seekoff
  103. if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
  104. return traits_type::eof();
  105. LARGE_INTEGER fpos;
  106. fpos.QuadPart = pos;
  107. if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
  108. return traits_type::eof();
  109. setg(nullptr, nullptr, nullptr);
  110. return fpos.QuadPart;
  111. }
  112. public:
  113. bool open(const wchar_t *filename, std::ios_base::openmode mode)
  114. {
  115. if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
  116. return false;
  117. HANDLE f{CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, nullptr,
  118. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)};
  119. if(f == INVALID_HANDLE_VALUE) return false;
  120. if(mFile != INVALID_HANDLE_VALUE)
  121. CloseHandle(mFile);
  122. mFile = f;
  123. setg(nullptr, nullptr, nullptr);
  124. return true;
  125. }
  126. bool open(const char *filename, std::ios_base::openmode mode)
  127. {
  128. std::wstring wname{utf8_to_wstr(filename)};
  129. return open(wname.c_str(), mode);
  130. }
  131. bool is_open() const noexcept { return mFile != INVALID_HANDLE_VALUE; }
  132. filebuf() = default;
  133. ~filebuf() override
  134. {
  135. if(mFile != INVALID_HANDLE_VALUE)
  136. CloseHandle(mFile);
  137. mFile = INVALID_HANDLE_VALUE;
  138. }
  139. };
  140. // Inherit from std::istream to use our custom streambuf
  141. class ifstream final : public std::istream {
  142. filebuf mStreamBuf;
  143. public:
  144. ifstream(const std::wstring &filename, std::ios_base::openmode mode = std::ios_base::in)
  145. : ifstream(filename.c_str(), mode) { }
  146. ifstream(const wchar_t *filename, std::ios_base::openmode mode = std::ios_base::in)
  147. : std::istream{nullptr}
  148. {
  149. init(&mStreamBuf);
  150. // Set the failbit if the file failed to open.
  151. if((mode&std::ios_base::out) ||
  152. !mStreamBuf.open(filename, mode|std::ios_base::in))
  153. clear(failbit);
  154. }
  155. ifstream(const std::string &filename, std::ios_base::openmode mode = std::ios_base::in)
  156. : ifstream(filename.c_str(), mode) { }
  157. ifstream(const char *filename, std::ios_base::openmode mode = std::ios_base::in)
  158. : std::istream{nullptr}
  159. {
  160. init(&mStreamBuf);
  161. // Set the failbit if the file failed to open.
  162. if((mode&std::ios_base::out) ||
  163. !mStreamBuf.open(filename, mode|std::ios_base::in))
  164. clear(failbit);
  165. }
  166. bool is_open() const noexcept { return mStreamBuf.is_open(); }
  167. };
  168. } // namespace al
  169. #define HAVE_DYNLOAD 1
  170. #else /* _WIN32 */
  171. #include <fstream>
  172. namespace al {
  173. using filebuf = std::filebuf;
  174. using ifstream = std::ifstream;
  175. } // namespace al
  176. #if defined(HAVE_DLFCN_H)
  177. #define HAVE_DYNLOAD 1
  178. #endif
  179. #endif /* _WIN32 */
  180. #include <string>
  181. struct PathNamePair { std::string path, fname; };
  182. const PathNamePair &GetProcBinary(void);
  183. #ifdef HAVE_DYNLOAD
  184. void *LoadLib(const char *name);
  185. void CloseLib(void *handle);
  186. void *GetSymbol(void *handle, const char *name);
  187. #endif
  188. #endif /* __cplusplus */
  189. #endif /* AL_COMPAT_H */