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
35namespace love
36{
37namespace filesystem
38{
39
40love::Type DroppedFile::type("DroppedFile", &File::type);
41
42DroppedFile::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
51DroppedFile::~DroppedFile()
52{
53 if (mode != MODE_CLOSED)
54 close();
55}
56
57bool 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
91bool 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
102bool DroppedFile::isOpen() const
103{
104 return mode != MODE_CLOSED && file != nullptr;
105}
106
107int64 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
149int64 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
162bool 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
175bool 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
183bool DroppedFile::isEOF()
184{
185 return file == nullptr || tell() >= getSize();
186}
187
188int64 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
200bool 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
212bool 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
253File::BufferMode DroppedFile::getBuffer(int64 &size) const
254{
255 size = bufferSize;
256 return bufferMode;
257}
258
259const std::string &DroppedFile::getFilename() const
260{
261 return filename;
262}
263
264File::Mode DroppedFile::getMode() const
265{
266 return mode;
267}
268
269const 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