1 /* Copyright  (C) 2010-2018 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (nbio_windowsmmap.c).
5  * ---------------------------------------------------------------------------------------
6  *
7  * Permission is hereby granted, free of charge,
8  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include <file/nbio.h>
24 
25 #if defined(_WIN32) && !defined(_XBOX)
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 
30 #include <encodings/utf.h>
31 
32 #include <windows.h>
33 
34 /* Assume W-functions do not work below Win2K and Xbox platforms */
35 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
36 
37 #ifndef LEGACY_WIN32
38 #define LEGACY_WIN32
39 #endif
40 
41 #endif
42 
43 #ifndef FILE_SHARE_ALL
44 #define FILE_SHARE_ALL (FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE)
45 #endif
46 
47 struct nbio_mmap_win32_t
48 {
49    HANDLE file;
50    bool is_write;
51    size_t len;
52    void* ptr;
53 };
54 
nbio_mmap_win32_open(const char * filename,unsigned mode)55 static void *nbio_mmap_win32_open(const char * filename, unsigned mode)
56 {
57    static const DWORD dispositions[] = { OPEN_EXISTING, CREATE_ALWAYS, OPEN_ALWAYS, OPEN_EXISTING, CREATE_ALWAYS };
58    HANDLE mem;
59 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
60    LARGE_INTEGER len;
61 #else
62    SIZE_T len;
63 #endif
64    struct nbio_mmap_win32_t* handle  = NULL;
65    void* ptr                         = NULL;
66    bool is_write                     = (mode == NBIO_WRITE || mode == NBIO_UPDATE || mode == BIO_WRITE);
67    DWORD access                      = (is_write ? GENERIC_READ|GENERIC_WRITE : GENERIC_READ);
68 #if !defined(_WIN32) || defined(LEGACY_WIN32)
69    HANDLE file                       = CreateFile(filename, access, FILE_SHARE_ALL, NULL, dispositions[mode], FILE_ATTRIBUTE_NORMAL, NULL);
70 #else
71    wchar_t *filename_wide            = utf8_to_utf16_string_alloc(filename);
72 #ifdef __WINRT__
73    HANDLE file                       = CreateFile2(filename_wide, access, FILE_SHARE_ALL, dispositions[mode], NULL);
74 #else
75    HANDLE file                       = CreateFileW(filename_wide, access, FILE_SHARE_ALL, NULL, dispositions[mode], FILE_ATTRIBUTE_NORMAL, NULL);
76 #endif
77 
78    if (filename_wide)
79       free(filename_wide);
80 #endif
81 
82    if (file == INVALID_HANDLE_VALUE)
83       return NULL;
84 
85 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
86    /* GetFileSizeEx is new for Windows 2000 */
87    GetFileSizeEx(file, &len);
88    mem = CreateFileMapping(file, NULL, is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL);
89    ptr = MapViewOfFile(mem, is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len.QuadPart);
90 #else
91    GetFileSize(file, &len);
92    mem = CreateFileMapping(file, NULL, is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL);
93    ptr = MapViewOfFile(mem, is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len);
94 #endif
95 
96    CloseHandle(mem);
97 
98    handle           = (struct nbio_mmap_win32_t*)malloc(sizeof(struct nbio_mmap_win32_t));
99 
100    handle->file     = file;
101    handle->is_write = is_write;
102 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
103    handle->len      = len.QuadPart;
104 #else
105    handle->len      = len;
106 #endif
107    handle->ptr      = ptr;
108 
109    return handle;
110 }
111 
nbio_mmap_win32_begin_read(void * data)112 static void nbio_mmap_win32_begin_read(void *data)
113 {
114    /* not needed */
115 }
116 
nbio_mmap_win32_begin_write(void * data)117 static void nbio_mmap_win32_begin_write(void *data)
118 {
119    /* not needed */
120 }
121 
nbio_mmap_win32_iterate(void * data)122 static bool nbio_mmap_win32_iterate(void *data)
123 {
124    /* not needed */
125    return true;
126 }
127 
nbio_mmap_win32_resize(void * data,size_t len)128 static void nbio_mmap_win32_resize(void *data, size_t len)
129 {
130 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
131    LARGE_INTEGER len_li;
132 #else
133    SIZE_T len_li;
134 #endif
135    HANDLE mem;
136    struct nbio_mmap_win32_t* handle  = (struct nbio_mmap_win32_t*)data;
137 
138    if (!handle)
139       return;
140 
141    if (len < handle->len)
142    {
143       /* this works perfectly fine if this check is removed,
144        * but it won't work on other nbio implementations */
145       /* therefore, it's blocked so nobody accidentally
146        * relies on it. */
147       puts("ERROR - attempted file shrink operation, not implemented");
148       abort();
149    }
150 
151 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
152    /* SetFilePointerEx is new for Windows 2000 */
153    len_li.QuadPart = len;
154    SetFilePointerEx(handle->file, len_li, NULL, FILE_BEGIN);
155 #else
156    len_li = len;
157    SetFilePointer(handle->file, len_li, NULL, FILE_BEGIN);
158 #endif
159 
160    if (!SetEndOfFile(handle->file))
161    {
162       puts("ERROR - couldn't resize file (SetEndOfFile)");
163       abort(); /* this one returns void and I can't find any other way for it to report failure */
164    }
165    handle->len = len;
166 
167    UnmapViewOfFile(handle->ptr);
168    mem = CreateFileMapping(handle->file, NULL, handle->is_write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL);
169    handle->ptr = MapViewOfFile(mem, handle->is_write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, 0, 0, len);
170    CloseHandle(mem);
171 
172    if (!handle->ptr)
173    {
174       puts("ERROR - couldn't resize file (MapViewOfFile)");
175       abort();
176    }
177 }
178 
nbio_mmap_win32_get_ptr(void * data,size_t * len)179 static void *nbio_mmap_win32_get_ptr(void *data, size_t* len)
180 {
181    struct nbio_mmap_win32_t* handle  = (struct nbio_mmap_win32_t*)data;
182    if (!handle)
183       return NULL;
184    if (len)
185       *len = handle->len;
186    return handle->ptr;
187 }
188 
nbio_mmap_win32_cancel(void * data)189 static void nbio_mmap_win32_cancel(void *data)
190 {
191    /* not needed */
192 }
193 
nbio_mmap_win32_free(void * data)194 static void nbio_mmap_win32_free(void *data)
195 {
196    struct nbio_mmap_win32_t* handle  = (struct nbio_mmap_win32_t*)data;
197    if (!handle)
198       return;
199    CloseHandle(handle->file);
200    UnmapViewOfFile(handle->ptr);
201    free(handle);
202 }
203 
204 nbio_intf_t nbio_mmap_win32 = {
205    nbio_mmap_win32_open,
206    nbio_mmap_win32_begin_read,
207    nbio_mmap_win32_begin_write,
208    nbio_mmap_win32_iterate,
209    nbio_mmap_win32_resize,
210    nbio_mmap_win32_get_ptr,
211    nbio_mmap_win32_cancel,
212    nbio_mmap_win32_free,
213    "nbio_mmap_win32",
214 };
215 #else
216 nbio_intf_t nbio_mmap_win32 = {
217    NULL,
218    NULL,
219    NULL,
220    NULL,
221    NULL,
222    NULL,
223    NULL,
224    NULL,
225    "nbio_mmap_win32",
226 };
227 
228 #endif
229