win_main_utf8.h 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #ifndef WIN_MAIN_UTF8_H
  2. #define WIN_MAIN_UTF8_H
  3. /* For Windows systems this provides a way to get UTF-8 encoded argv strings,
  4. * and also overrides fopen to accept UTF-8 filenames. Working with wmain
  5. * directly complicates cross-platform compatibility, while normal main() in
  6. * Windows uses the current codepage (which has limited availability of
  7. * characters).
  8. *
  9. * For MinGW, you must link with -municode
  10. */
  11. #ifdef _WIN32
  12. #define WIN32_LEAN_AND_MEAN
  13. #include <windows.h>
  14. #include <shellapi.h>
  15. #include <wchar.h>
  16. #ifdef __cplusplus
  17. #include <memory>
  18. #define STATIC_CAST(...) static_cast<__VA_ARGS__>
  19. #define REINTERPRET_CAST(...) reinterpret_cast<__VA_ARGS__>
  20. #define MAYBE_UNUSED [[maybe_unused]]
  21. #else
  22. #define STATIC_CAST(...) (__VA_ARGS__)
  23. #define REINTERPRET_CAST(...) (__VA_ARGS__)
  24. #ifdef __GNUC__
  25. #define MAYBE_UNUSED __attribute__((__unused__))
  26. #else
  27. #define MAYBE_UNUSED
  28. #endif
  29. #endif
  30. MAYBE_UNUSED static FILE *my_fopen(const char *fname, const char *mode)
  31. {
  32. wchar_t *wname=NULL, *wmode=NULL;
  33. int namelen, modelen;
  34. FILE *file = NULL;
  35. errno_t err;
  36. namelen = MultiByteToWideChar(CP_UTF8, 0, fname, -1, NULL, 0);
  37. modelen = MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
  38. if(namelen <= 0 || modelen <= 0)
  39. {
  40. fprintf(stderr, "Failed to convert UTF-8 fname \"%s\", mode \"%s\"\n", fname, mode);
  41. return NULL;
  42. }
  43. #ifdef __cplusplus
  44. auto strbuf = std::make_unique<wchar_t[]>(static_cast<size_t>(namelen) +
  45. static_cast<size_t>(modelen));
  46. wname = strbuf.get();
  47. #else
  48. wname = (wchar_t*)calloc(sizeof(wchar_t), (size_t)namelen + (size_t)modelen);
  49. #endif
  50. wmode = wname + namelen;
  51. MultiByteToWideChar(CP_UTF8, 0, fname, -1, wname, namelen);
  52. MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, modelen);
  53. err = _wfopen_s(&file, wname, wmode);
  54. if(err)
  55. {
  56. errno = err;
  57. file = NULL;
  58. }
  59. #ifndef __cplusplus
  60. free(wname);
  61. #endif
  62. return file;
  63. }
  64. #define fopen my_fopen
  65. /* SDL overrides main and provides UTF-8 args for us. */
  66. #if !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE)
  67. int my_main(int, char**);
  68. #define main my_main
  69. #ifdef __cplusplus
  70. extern "C"
  71. #endif
  72. int wmain(int argc, wchar_t **wargv)
  73. {
  74. char **argv;
  75. size_t total;
  76. int i;
  77. total = sizeof(*argv) * STATIC_CAST(size_t)(argc);
  78. for(i = 0;i < argc;i++)
  79. total += STATIC_CAST(size_t)(WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL,
  80. NULL));
  81. #ifdef __cplusplus
  82. auto argbuf = std::make_unique<char[]>(total);
  83. argv = reinterpret_cast<char**>(argbuf.get());
  84. #else
  85. argv = (char**)calloc(1, total);
  86. #endif
  87. argv[0] = REINTERPRET_CAST(char*)(argv + argc);
  88. for(i = 0;i < argc-1;i++)
  89. {
  90. int len = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], 65535, NULL, NULL);
  91. argv[i+1] = argv[i] + len;
  92. }
  93. WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], 65535, NULL, NULL);
  94. #ifdef __cplusplus
  95. return main(argc, argv);
  96. #else
  97. i = main(argc, argv);
  98. free(argv);
  99. return i;
  100. #endif
  101. }
  102. #endif /* !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE) */
  103. #endif /* _WIN32 */
  104. #endif /* WIN_MAIN_UTF8_H */