aboutsummaryrefslogtreecommitdiffstats
path: root/common/win_main_utf8.h
blob: 81734242fc719846bd46234630fc1f0a6edaaa99 (plain)
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
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#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__>
#define MAYBE_UNUSED [[maybe_unused]]

#else

#define STATIC_CAST(...) (__VA_ARGS__)
#define REINTERPRET_CAST(...) (__VA_ARGS__)
#ifdef __GNUC__
#define MAYBE_UNUSED __attribute__((__unused__))
#else
#define MAYBE_UNUSED
#endif
#endif

MAYBE_UNUSED 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) +
        static_cast<size_t>(modelen));
    wname = strbuf.get();
#else
    wname = (wchar_t*)calloc(sizeof(wchar_t), (size_t)namelen + (size_t)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 */