1 /**
2  * Copyright (c) 2006-2016 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 
DroppedFile(const std::string & filename)40 DroppedFile::DroppedFile(const std::string &filename)
41 	: filename(filename)
42 	, file(nullptr)
43 	, mode(MODE_CLOSED)
44 	, bufferMode(BUFFER_NONE)
45 	, bufferSize(0)
46 {
47 }
48 
~DroppedFile()49 DroppedFile::~DroppedFile()
50 {
51 	if (mode != MODE_CLOSED)
52 		close();
53 }
54 
open(Mode newmode)55 bool DroppedFile::open(Mode newmode)
56 {
57 	if (newmode == MODE_CLOSED)
58 		return true;
59 
60 	// File already open?
61 	if (file != nullptr)
62 		return false;
63 
64 #ifdef LOVE_WINDOWS
65 	// make sure non-ASCII filenames work.
66 	std::wstring modestr = to_widestr(getModeString(newmode));
67 	std::wstring wfilename = to_widestr(filename);
68 
69 	file = _wfopen(wfilename.c_str(), modestr.c_str());
70 #else
71 	file = fopen(filename.c_str(), getModeString(newmode));
72 #endif
73 
74 	if (newmode == MODE_READ && file == nullptr)
75 		throw love::Exception("Could not open file %s. Does not exist.", filename.c_str());
76 
77 	mode = newmode;
78 
79 	if (file != nullptr && !setBuffer(bufferMode, bufferSize))
80 	{
81 		// Revert to buffer defaults if we don't successfully set the buffer.
82 		bufferMode = BUFFER_NONE;
83 		bufferSize = 0;
84 	}
85 
86 	return file != nullptr;
87 }
88 
close()89 bool DroppedFile::close()
90 {
91 	if (file == nullptr || fclose(file) != 0)
92 		return false;
93 
94 	mode = MODE_CLOSED;
95 	file = nullptr;
96 
97 	return true;
98 }
99 
isOpen() const100 bool DroppedFile::isOpen() const
101 {
102 	return mode != MODE_CLOSED && file != nullptr;
103 }
104 
getSize()105 int64 DroppedFile::getSize()
106 {
107 #ifdef LOVE_WINDOWS
108 
109 	// make sure non-ASCII filenames work.
110 	std::wstring wfilename = to_widestr(filename);
111 
112 	struct _stat buf;
113 	if (_wstat(wfilename.c_str(), &buf) != 0)
114 		return -1;
115 
116 	return (int64) buf.st_size;
117 
118 #else
119 
120 	// Assume POSIX support...
121 	struct stat buf;
122 	if (stat(filename.c_str(), &buf) != 0)
123 		return -1;
124 
125 	return (int64) buf.st_size;
126 
127 #endif
128 }
129 
read(void * dst,int64 size)130 int64 DroppedFile::read(void *dst, int64 size)
131 {
132 	if (!file || mode != MODE_READ)
133 		throw love::Exception("File is not opened for reading.");
134 
135 	if (size < 0)
136 		throw love::Exception("Invalid read size.");
137 
138 	size_t read = fread(dst, 1, (size_t) size, file);
139 
140 	return (int64) read;
141 }
142 
write(const void * data,int64 size)143 bool DroppedFile::write(const void *data, int64 size)
144 {
145 	if (!file || (mode != MODE_WRITE && mode != MODE_APPEND))
146 		throw love::Exception("File is not opened for writing.");
147 
148 	if (size < 0)
149 		throw love::Exception("Invalid write size.");
150 
151 	int64 written = (int64) fwrite(data, 1, (size_t) size, file);
152 
153 	return written == size;
154 }
155 
flush()156 bool DroppedFile::flush()
157 {
158 	if (!file || (mode != MODE_WRITE && mode != MODE_APPEND))
159 		throw love::Exception("File is not opened for writing.");
160 
161 	return fflush(file) == 0;
162 }
163 
isEOF()164 bool DroppedFile::isEOF()
165 {
166 	return file == nullptr || feof(file) != 0;
167 }
168 
tell()169 int64 DroppedFile::tell()
170 {
171 	if (file == nullptr)
172 		return -1;
173 
174 	return (int64) ftell(file);
175 }
176 
seek(uint64 pos)177 bool DroppedFile::seek(uint64 pos)
178 {
179 	return file != nullptr && fseek(file, (long) pos, SEEK_SET) == 0;
180 }
181 
setBuffer(BufferMode bufmode,int64 size)182 bool DroppedFile::setBuffer(BufferMode bufmode, int64 size)
183 {
184 	if (size < 0)
185 		return false;
186 
187 	if (bufmode == BUFFER_NONE)
188 		size = 0;
189 
190 	// If the file isn't open, we'll make sure the buffer values are set in
191 	// DroppedFile::open.
192 	if (!isOpen())
193 	{
194 		bufferMode = bufmode;
195 		bufferSize = size;
196 		return true;
197 	}
198 
199 	int vbufmode;
200 	switch (bufmode)
201 	{
202 	case File::BUFFER_NONE:
203 	default:
204 		vbufmode = _IONBF;
205 		break;
206 	case File::BUFFER_LINE:
207 		vbufmode = _IOLBF;
208 		break;
209 	case File::BUFFER_FULL:
210 		vbufmode = _IOFBF;
211 		break;
212 	}
213 
214 	if (setvbuf(file, nullptr, vbufmode, (size_t) size) != 0)
215 		return false;
216 
217 	bufferMode = bufmode;
218 	bufferSize = size;
219 
220 	return true;
221 }
222 
getBuffer(int64 & size) const223 File::BufferMode DroppedFile::getBuffer(int64 &size) const
224 {
225 	size = bufferSize;
226 	return bufferMode;
227 }
228 
getFilename() const229 const std::string &DroppedFile::getFilename() const
230 {
231 	return filename;
232 }
233 
getMode() const234 File::Mode DroppedFile::getMode() const
235 {
236 	return mode;
237 }
238 
getModeString(Mode mode)239 const char *DroppedFile::getModeString(Mode mode)
240 {
241 	switch (mode)
242 	{
243 	case File::MODE_CLOSED:
244 	default:
245 		return "c";
246 	case File::MODE_READ:
247 		return "rb";
248 	case File::MODE_WRITE:
249 		return "wb";
250 	case File::MODE_APPEND:
251 		return "ab";
252 	}
253 }
254 
255 } // filesystem
256 } // love
257