1 /*************************************************************************/
2 /*  file_access_windows.cpp                                              */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 #ifdef WINDOWS_ENABLED
31 
32 #include "file_access_windows.h"
33 #include "shlwapi.h"
34 #include <windows.h>
35 
36 #include "print_string.h"
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <tchar.h>
40 #include <wchar.h>
41 
42 #ifdef _MSC_VER
43 #define S_ISREG(m) ((m)&_S_IFREG)
44 #endif
45 
check_errors() const46 void FileAccessWindows::check_errors() const {
47 
48 	ERR_FAIL_COND(!f);
49 
50 	if (feof(f)) {
51 
52 		last_error = ERR_FILE_EOF;
53 	}
54 }
55 
_open(const String & p_filename,int p_mode_flags)56 Error FileAccessWindows::_open(const String &p_filename, int p_mode_flags) {
57 
58 	String filename = fix_path(p_filename);
59 	if (f)
60 		close();
61 
62 	const wchar_t *mode_string;
63 
64 	if (p_mode_flags == READ)
65 		mode_string = L"rb";
66 	else if (p_mode_flags == WRITE)
67 		mode_string = L"wb";
68 	else if (p_mode_flags == READ_WRITE)
69 		mode_string = L"rb+";
70 	else if (p_mode_flags == WRITE_READ)
71 		mode_string = L"wb+";
72 	else
73 		return ERR_INVALID_PARAMETER;
74 
75 	/* pretty much every implementation that uses fopen as primary
76 	   backend supports utf8 encoding */
77 
78 	struct _stat st;
79 	if (_wstat(filename.c_str(), &st) == 0) {
80 
81 		if (!S_ISREG(st.st_mode))
82 			return ERR_FILE_CANT_OPEN;
83 	};
84 
85 	if (is_backup_save_enabled() && p_mode_flags & WRITE && !(p_mode_flags & READ)) {
86 		save_path = filename;
87 		filename = filename + ".tmp";
88 		//print_line("saving instead to "+path);
89 	}
90 
91 	f = _wfopen(filename.c_str(), mode_string);
92 
93 	if (f == NULL) {
94 		last_error = ERR_FILE_CANT_OPEN;
95 		return ERR_FILE_CANT_OPEN;
96 	} else {
97 		last_error = OK;
98 		flags = p_mode_flags;
99 		return OK;
100 	}
101 }
close()102 void FileAccessWindows::close() {
103 
104 	if (!f)
105 		return;
106 
107 	fclose(f);
108 	f = NULL;
109 
110 	if (save_path != "") {
111 
112 		//unlink(save_path.utf8().get_data());
113 		//print_line("renaming..");
114 		//_wunlink(save_path.c_str()); //unlink if exists
115 		//int rename_error = _wrename((save_path+".tmp").c_str(),save_path.c_str());
116 
117 		bool rename_error;
118 
119 #ifdef WINRT_ENABLED
120 		// WinRT has no PathFileExists, so we check attributes instead
121 		DWORD fileAttr;
122 
123 		fileAttr = GetFileAttributesW(save_path.c_str());
124 		if (INVALID_FILE_ATTRIBUTES == fileAttr) {
125 #else
126 		if (!PathFileExistsW(save_path.c_str())) {
127 #endif
128 			//creating new file
129 			rename_error = _wrename((save_path + ".tmp").c_str(), save_path.c_str()) != 0;
130 		} else {
131 			//atomic replace for existing file
132 			rename_error = !ReplaceFileW(save_path.c_str(), (save_path + ".tmp").c_str(), NULL, 2 | 4, NULL, NULL);
133 		}
134 		if (rename_error && close_fail_notify) {
135 			close_fail_notify(save_path);
136 		}
137 
138 		save_path = "";
139 		ERR_FAIL_COND(rename_error);
140 	}
141 }
142 
143 bool FileAccessWindows::is_open() const {
144 
145 	return (f != NULL);
146 }
147 void FileAccessWindows::seek(size_t p_position) {
148 
149 	ERR_FAIL_COND(!f);
150 	last_error = OK;
151 	if (fseek(f, p_position, SEEK_SET))
152 		check_errors();
153 }
154 void FileAccessWindows::seek_end(int64_t p_position) {
155 
156 	ERR_FAIL_COND(!f);
157 	if (fseek(f, p_position, SEEK_END))
158 		check_errors();
159 }
160 size_t FileAccessWindows::get_pos() const {
161 
162 	size_t aux_position = 0;
163 	if (!(aux_position = ftell(f))) {
164 		check_errors();
165 	};
166 	return aux_position;
167 }
168 size_t FileAccessWindows::get_len() const {
169 
170 	ERR_FAIL_COND_V(!f, 0);
171 
172 	size_t pos = get_pos();
173 	fseek(f, 0, SEEK_END);
174 	int size = get_pos();
175 	fseek(f, pos, SEEK_SET);
176 
177 	return size;
178 }
179 
180 bool FileAccessWindows::eof_reached() const {
181 
182 	check_errors();
183 	return last_error == ERR_FILE_EOF;
184 }
185 
186 uint8_t FileAccessWindows::get_8() const {
187 
188 	ERR_FAIL_COND_V(!f, 0);
189 	uint8_t b;
190 	if (fread(&b, 1, 1, f) == 0) {
191 		check_errors();
192 	};
193 
194 	return b;
195 }
196 
197 int FileAccessWindows::get_buffer(uint8_t *p_dst, int p_length) const {
198 
199 	ERR_FAIL_COND_V(!f, -1);
200 	int read = fread(p_dst, 1, p_length, f);
201 	check_errors();
202 	return read;
203 };
204 
205 Error FileAccessWindows::get_error() const {
206 
207 	return last_error;
208 }
209 
210 void FileAccessWindows::store_8(uint8_t p_dest) {
211 
212 	ERR_FAIL_COND(!f);
213 	fwrite(&p_dest, 1, 1, f);
214 }
215 
216 void FileAccessWindows::store_buffer(const uint8_t *p_src, int p_length) {
217 	ERR_FAIL_COND(!f);
218 	ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != p_length);
219 }
220 
221 bool FileAccessWindows::file_exists(const String &p_name) {
222 
223 	FILE *g;
224 	//printf("opening file %s\n", p_fname.c_str());
225 	String filename = fix_path(p_name);
226 	g = _wfopen(filename.c_str(), L"rb");
227 	if (g == NULL) {
228 
229 		return false;
230 	} else {
231 
232 		fclose(g);
233 		return true;
234 	}
235 }
236 
237 uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
238 
239 	String file = fix_path(p_file);
240 	if (file.ends_with("/") && file != "/")
241 		file = file.substr(0, file.length() - 1);
242 
243 	struct _stat st;
244 	int rv = _wstat(file.c_str(), &st);
245 
246 	if (rv == 0) {
247 
248 		return st.st_mtime;
249 	} else {
250 		print_line("no access to " + file);
251 	}
252 
253 	ERR_FAIL_V(0);
254 };
255 
256 FileAccessWindows::FileAccessWindows() {
257 
258 	f = NULL;
259 	flags = 0;
260 	last_error = OK;
261 }
262 FileAccessWindows::~FileAccessWindows() {
263 
264 	close();
265 }
266 
267 #endif
268