#ifndef WIN_MAIN_UTF8_H
|
|
#define WIN_MAIN_UTF8_H
|
|
|
|
/* For Windows systems this provides a way to get UTF-8 encoded argv strings,
|
|
* and also overrides fopen to accept UTF-8 filenames. Working with wmain
|
|
* directly complicates cross-platform compatibility, while normal main() in
|
|
* Windows uses the current codepage (which has limited availability of
|
|
* characters).
|
|
*
|
|
* For MinGW, you must link with -municode
|
|
*/
|
|
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#include <shellapi.h>
|
|
#include <wchar.h>
|
|
|
|
#ifdef __cplusplus
|
|
#include <memory>
|
|
|
|
#define STATIC_CAST(...) static_cast<__VA_ARGS__>
|
|
#define REINTERPRET_CAST(...) reinterpret_cast<__VA_ARGS__>
|
|
|
|
#else
|
|
|
|
#define STATIC_CAST(...) (__VA_ARGS__)
|
|
#define REINTERPRET_CAST(...) (__VA_ARGS__)
|
|
#endif
|
|
|
|
static FILE *my_fopen(const char *fname, const char *mode)
|
|
{
|
|
wchar_t *wname=NULL, *wmode=NULL;
|
|
int namelen, modelen;
|
|
FILE *file = NULL;
|
|
errno_t err;
|
|
|
|
namelen = MultiByteToWideChar(CP_UTF8, 0, fname, -1, NULL, 0);
|
|
modelen = MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
|
|
|
|
if(namelen <= 0 || modelen <= 0)
|
|
{
|
|
fprintf(stderr, "Failed to convert UTF-8 fname \"%s\", mode \"%s\"\n", fname, mode);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
auto strbuf = std::make_unique<wchar_t[]>(static_cast<size_t>(namelen+modelen));
|
|
wname = strbuf.get();
|
|
#else
|
|
wname = (wchar_t*)calloc(sizeof(wchar_t), (size_t)(namelen+modelen));
|
|
#endif
|
|
wmode = wname + namelen;
|
|
MultiByteToWideChar(CP_UTF8, 0, fname, -1, wname, namelen);
|
|
MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, modelen);
|
|
|
|
err = _wfopen_s(&file, wname, wmode);
|
|
if(err)
|
|
{
|
|
errno = err;
|
|
file = NULL;
|
|
}
|
|
|
|
#ifndef __cplusplus
|
|
free(wname);
|
|
#endif
|
|
return file;
|
|
}
|
|
#define fopen my_fopen
|
|
|
|
|
|
/* SDL overrides main and provides UTF-8 args for us. */
|
|
#if !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE)
|
|
int my_main(int, char**);
|
|
#define main my_main
|
|
|
|
#ifdef __cplusplus
|
|
extern "C"
|
|
#endif
|
|
int wmain(int argc, wchar_t **wargv)
|
|
{
|
|
char **argv;
|
|
size_t total;
|
|
int i;
|
|
|
|
total = sizeof(*argv) * STATIC_CAST(size_t)(argc);
|
|
for(i = 0;i < argc;i++)
|
|
total += STATIC_CAST(size_t)(WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL,
|
|
NULL));
|
|
|
|
#ifdef __cplusplus
|
|
auto argbuf = std::make_unique<char[]>(total);
|
|
argv = reinterpret_cast<char**>(argbuf.get());
|
|
#else
|
|
argv = (char**)calloc(1, total);
|
|
#endif
|
|
argv[0] = REINTERPRET_CAST(char*)(argv + argc);
|
|
for(i = 0;i < argc-1;i++)
|
|
{
|
|
int len = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], 65535, NULL, NULL);
|
|
argv[i+1] = argv[i] + len;
|
|
}
|
|
WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], 65535, NULL, NULL);
|
|
|
|
#ifdef __cplusplus
|
|
return main(argc, argv);
|
|
#else
|
|
i = main(argc, argv);
|
|
|
|
free(argv);
|
|
return i;
|
|
#endif
|
|
}
|
|
#endif /* !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE) */
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
#endif /* WIN_MAIN_UTF8_H */
|