1 | /** |
2 | * Copyright (c) 2006-2023 LOVE Development Team |
3 | * |
4 | * This software is provided 'as-is', without any express or implied |
5 | * warranty. In no event will the authors be held liable for any damages |
6 | * arising from the use of this software. |
7 | * |
8 | * Permission is granted to anyone to use this software for any purpose, |
9 | * including commercial applications, and to alter it and redistribute it |
10 | * freely, subject to the following restrictions: |
11 | * |
12 | * 1. The origin of this software must not be misrepresented; you must not |
13 | * claim that you wrote the original software. If you use this software |
14 | * in a product, an acknowledgment in the product documentation would be |
15 | * appreciated but is not required. |
16 | * 2. Altered source versions must be plainly marked as such, and must not be |
17 | * misrepresented as being the original software. |
18 | * 3. This notice may not be removed or altered from any source distribution. |
19 | **/ |
20 | |
21 | // LOVE |
22 | #include "DroppedFile.h" |
23 | #include "common/utf8.h" |
24 | |
25 | // Assume POSIX or Visual Studio. |
26 | #include <sys/types.h> |
27 | #include <sys/stat.h> |
28 | |
29 | #ifdef LOVE_WINDOWS |
30 | #include <wchar.h> |
31 | #else |
32 | #include <unistd.h> // POSIX. |
33 | #endif |
34 | |
35 | namespace love |
36 | { |
37 | namespace filesystem |
38 | { |
39 | |
40 | love::Type DroppedFile::type("DroppedFile" , &File::type); |
41 | |
42 | DroppedFile::DroppedFile(const std::string &filename) |
43 | : filename(filename) |
44 | , file(nullptr) |
45 | , mode(MODE_CLOSED) |
46 | , bufferMode(BUFFER_NONE) |
47 | , bufferSize(0) |
48 | { |
49 | } |
50 | |
51 | DroppedFile::~DroppedFile() |
52 | { |
53 | if (mode != MODE_CLOSED) |
54 | close(); |
55 | } |
56 | |
57 | bool DroppedFile::open(Mode newmode) |
58 | { |
59 | if (newmode == MODE_CLOSED) |
60 | return true; |
61 | |
62 | // File already open? |
63 | if (file != nullptr) |
64 | return false; |
65 | |
66 | #ifdef LOVE_WINDOWS |
67 | // make sure non-ASCII filenames work. |
68 | std::wstring modestr = to_widestr(getModeString(newmode)); |
69 | std::wstring wfilename = to_widestr(filename); |
70 | |
71 | file = _wfopen(wfilename.c_str(), modestr.c_str()); |
72 | #else |
73 | file = fopen(filename.c_str(), getModeString(newmode)); |
74 | #endif |
75 | |
76 | if (newmode == MODE_READ && file == nullptr) |
77 | throw love::Exception("Could not open file %s. Does not exist." , filename.c_str()); |
78 | |
79 | mode = newmode; |
80 | |
81 | if (file != nullptr && !setBuffer(bufferMode, bufferSize)) |
82 | { |
83 | // Revert to buffer defaults if we don't successfully set the buffer. |
84 | bufferMode = BUFFER_NONE; |
85 | bufferSize = 0; |
86 | } |
87 | |
88 | return file != nullptr; |
89 | } |
90 | |
91 | bool DroppedFile::close() |
92 | { |
93 | if (file == nullptr || fclose(file) != 0) |
94 | return false; |
95 | |
96 | mode = MODE_CLOSED; |
97 | file = nullptr; |
98 | |
99 | return true; |
100 | } |
101 | |
102 | bool DroppedFile::isOpen() const |
103 | { |
104 | return mode != MODE_CLOSED && file != nullptr; |
105 | } |
106 | |
107 | int64 DroppedFile::getSize() |
108 | { |
109 | int fd = file ? fileno(file) : -1; |
110 | |
111 | #ifdef LOVE_WINDOWS |
112 | |
113 | struct _stat64 buf; |
114 | |
115 | if (fd != -1) |
116 | { |
117 | if (_fstat64(fd, &buf) != 0) |
118 | return -1; |
119 | } |
120 | else |
121 | { |
122 | // make sure non-ASCII filenames work. |
123 | std::wstring wfilename = to_widestr(filename); |
124 | |
125 | if (_wstat64(wfilename.c_str(), &buf) != 0) |
126 | return -1; |
127 | } |
128 | |
129 | return (int64) buf.st_size; |
130 | |
131 | #else |
132 | |
133 | // Assume POSIX support... |
134 | struct stat buf; |
135 | |
136 | if (fd != -1) |
137 | { |
138 | if (fstat(fd, &buf) != 0) |
139 | return -1; |
140 | } |
141 | else if (stat(filename.c_str(), &buf) != 0) |
142 | return -1; |
143 | |
144 | return (int64) buf.st_size; |
145 | |
146 | #endif |
147 | } |
148 | |
149 | int64 DroppedFile::read(void *dst, int64 size) |
150 | { |
151 | if (!file || mode != MODE_READ) |
152 | throw love::Exception("File is not opened for reading." ); |
153 | |
154 | if (size < 0) |
155 | throw love::Exception("Invalid read size." ); |
156 | |
157 | size_t read = fread(dst, 1, (size_t) size, file); |
158 | |
159 | return (int64) read; |
160 | } |
161 | |
162 | bool DroppedFile::write(const void *data, int64 size) |
163 | { |
164 | if (!file || (mode != MODE_WRITE && mode != MODE_APPEND)) |
165 | throw love::Exception("File is not opened for writing." ); |
166 | |
167 | if (size < 0) |
168 | throw love::Exception("Invalid write size." ); |
169 | |
170 | int64 written = (int64) fwrite(data, 1, (size_t) size, file); |
171 | |
172 | return written == size; |
173 | } |
174 | |
175 | bool DroppedFile::flush() |
176 | { |
177 | if (!file || (mode != MODE_WRITE && mode != MODE_APPEND)) |
178 | throw love::Exception("File is not opened for writing." ); |
179 | |
180 | return fflush(file) == 0; |
181 | } |
182 | |
183 | bool DroppedFile::isEOF() |
184 | { |
185 | return file == nullptr || tell() >= getSize(); |
186 | } |
187 | |
188 | int64 DroppedFile::tell() |
189 | { |
190 | if (file == nullptr) |
191 | return -1; |
192 | |
193 | #ifdef LOVE_WINDOWS |
194 | return (int64) _ftelli64(file); |
195 | #else |
196 | return (int64) ftello(file); |
197 | #endif |
198 | } |
199 | |
200 | bool DroppedFile::seek(uint64 pos) |
201 | { |
202 | if (file == nullptr) |
203 | return false; |
204 | |
205 | #ifdef LOVE_WINDOWS |
206 | return _fseeki64(file, (int64) pos, SEEK_SET) == 0; |
207 | #else |
208 | return fseeko(file, (off_t) pos, SEEK_SET) == 0; |
209 | #endif |
210 | } |
211 | |
212 | bool DroppedFile::setBuffer(BufferMode bufmode, int64 size) |
213 | { |
214 | if (size < 0) |
215 | return false; |
216 | |
217 | if (bufmode == BUFFER_NONE) |
218 | size = 0; |
219 | |
220 | // If the file isn't open, we'll make sure the buffer values are set in |
221 | // DroppedFile::open. |
222 | if (!isOpen()) |
223 | { |
224 | bufferMode = bufmode; |
225 | bufferSize = size; |
226 | return true; |
227 | } |
228 | |
229 | int vbufmode; |
230 | switch (bufmode) |
231 | { |
232 | case File::BUFFER_NONE: |
233 | default: |
234 | vbufmode = _IONBF; |
235 | break; |
236 | case File::BUFFER_LINE: |
237 | vbufmode = _IOLBF; |
238 | break; |
239 | case File::BUFFER_FULL: |
240 | vbufmode = _IOFBF; |
241 | break; |
242 | } |
243 | |
244 | if (setvbuf(file, nullptr, vbufmode, (size_t) size) != 0) |
245 | return false; |
246 | |
247 | bufferMode = bufmode; |
248 | bufferSize = size; |
249 | |
250 | return true; |
251 | } |
252 | |
253 | File::BufferMode DroppedFile::getBuffer(int64 &size) const |
254 | { |
255 | size = bufferSize; |
256 | return bufferMode; |
257 | } |
258 | |
259 | const std::string &DroppedFile::getFilename() const |
260 | { |
261 | return filename; |
262 | } |
263 | |
264 | File::Mode DroppedFile::getMode() const |
265 | { |
266 | return mode; |
267 | } |
268 | |
269 | const char *DroppedFile::getModeString(Mode mode) |
270 | { |
271 | switch (mode) |
272 | { |
273 | case File::MODE_CLOSED: |
274 | default: |
275 | return "c" ; |
276 | case File::MODE_READ: |
277 | return "rb" ; |
278 | case File::MODE_WRITE: |
279 | return "wb" ; |
280 | case File::MODE_APPEND: |
281 | return "ab" ; |
282 | } |
283 | } |
284 | |
285 | } // filesystem |
286 | } // love |
287 | |