1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 2005-2020  Warzone 2100 Project
4 
5 	Warzone 2100 is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation; either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	Warzone 2100 is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with Warzone 2100; if not, write to the Free Software
17 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 #ifndef _physfs_ext_h
20 #define _physfs_ext_h
21 
22 #if defined(__MACOSX__)
23 # include <PhysFS/physfs.h>
24 #else
25 # include <physfs.h>
26 #endif
27 #include "wzstring.h"
28 
29 #include "wzglobal.h"
30 
31 #include <functional>
32 
33 #define PHYSFS_APPEND 1
34 #define PHYSFS_PREPEND 0
35 
36 // Detect the version of PhysFS
37 #if PHYSFS_VER_MAJOR > 2 || (PHYSFS_VER_MAJOR == 2 && PHYSFS_VER_MINOR >= 1)
38 	#define WZ_PHYSFS_2_1_OR_GREATER
39 #elif (PHYSFS_VER_MAJOR == 2 && PHYSFS_VER_MINOR == 0)
40 	#define WZ_PHYSFS_2_0_OR_GREATER
41 #else
42 	#error WZ requires PhysFS 2.0+
43 #endif
44 
45 // WZ PHYSFS wrappers to provide consistent naming (and functionality) on PhysFS 2.0 and 2.1+
46 
47 // NOTE: This uses PHYSFS_uint32 for `len` because PHYSFS_read takes a PHYSFS_uint32 objCount
WZ_PHYSFS_readBytes(PHYSFS_File * handle,void * buffer,PHYSFS_uint32 len)48 static inline PHYSFS_sint64 WZ_PHYSFS_readBytes (PHYSFS_File * handle, void * buffer, PHYSFS_uint32 len)
49 {
50 #if defined(WZ_PHYSFS_2_1_OR_GREATER)
51 	return PHYSFS_readBytes(handle, buffer, len);
52 #else
53 	return PHYSFS_read(handle, buffer, 1, len);
54 #endif
55 }
56 
57 // NOTE: This uses PHYSFS_uint32 for `len` because PHYSFS_write takes a PHYSFS_uint32 objCount
WZ_PHYSFS_writeBytes(PHYSFS_File * handle,const void * buffer,PHYSFS_uint32 len)58 static inline PHYSFS_sint64 WZ_PHYSFS_writeBytes (PHYSFS_File * handle, const void * buffer, PHYSFS_uint32 len)
59 {
60 #if defined(WZ_PHYSFS_2_1_OR_GREATER)
61 	return PHYSFS_writeBytes(handle, buffer, len);
62 #else
63 	return PHYSFS_write(handle, buffer, 1, len);
64 #endif
65 }
66 
WZ_PHYSFS_unmount(const char * oldDir)67 static inline int WZ_PHYSFS_unmount (const char * oldDir)
68 {
69 #if defined(WZ_PHYSFS_2_1_OR_GREATER)
70 	return PHYSFS_unmount(oldDir);
71 #else
72 	// PHYSFS_unmount is functionally equivalent to PHYSFS_removeFromSearchPath (the vocabulary just changed)
73 	return PHYSFS_removeFromSearchPath(oldDir);
74 #endif
75 }
76 
77 #if defined(WZ_PHYSFS_2_1_OR_GREATER)
78 	#define WZ_PHYSFS_getLastError() \
79 		PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())
80 #else
81 	#define WZ_PHYSFS_getLastError() \
82 		PHYSFS_getLastError()
83 #endif
84 
WZ_PHYSFS_getLastModTime(const char * filename)85 static inline PHYSFS_sint64 WZ_PHYSFS_getLastModTime (const char *filename)
86 {
87 #if defined(WZ_PHYSFS_2_1_OR_GREATER)
88 	PHYSFS_Stat metaData;
89 	PHYSFS_stat(filename, &metaData);
90 	return metaData.modtime;
91 #else
92 	return PHYSFS_getLastModTime(filename);
93 #endif
94 }
95 
WZ_PHYSFS_isDirectory(const char * fname)96 static inline int WZ_PHYSFS_isDirectory (const char * fname)
97 {
98 #if defined(WZ_PHYSFS_2_1_OR_GREATER)
99 	PHYSFS_Stat metaData;
100 	PHYSFS_stat(fname, &metaData);
101 	return (metaData.filetype == PHYSFS_FILETYPE_DIRECTORY) ? 1 : 0;
102 #else
103 	return PHYSFS_isDirectory(fname);
104 #endif
105 }
106 
WZ_PHYSFS_getRealDir_String(const char * filename)107 static inline std::string WZ_PHYSFS_getRealDir_String(const char *filename)
108 {
109 	// PHYSFS_getRealDir can return null
110 	const char* pResultStr = PHYSFS_getRealDir(filename);
111 	if (!pResultStr)
112 	{
113 		return std::string();
114 	}
115 	return std::string(pResultStr);
116 }
117 
PHYSFS_exists(const WzString & filename)118 static inline bool PHYSFS_exists(const WzString &filename)
119 {
120 	return PHYSFS_exists(filename.toUtf8().c_str());
121 }
122 
123 #if defined(WZ_PHYSFS_2_1_OR_GREATER)
_WZ_PHYSFS_setBuffer(PHYSFS_File * fileHandle,PHYSFS_uint64 bufsize)124 static inline PHYSFS_ErrorCode _WZ_PHYSFS_setBuffer(PHYSFS_File *fileHandle, PHYSFS_uint64 bufsize)
125 {
126 	// Check for PHYSFS >= 3.0.2, because it includes this important fix: https://hg.icculus.org/icculus/physfs/rev/c17f025e7a92
127 	PHYSFS_Version linked;
128 	PHYSFS_getLinkedVersion(&linked);
129 	if (linked.major > 3 || (linked.major == 3 && (linked.minor > 0 || (linked.minor == 0 && linked.patch >= 2))))
130 	{
131 		if (PHYSFS_setBuffer(fileHandle, bufsize) == 0)
132 		{
133 			// Failed to set up buffered write handle, so call again to definitively disable
134 			PHYSFS_ErrorCode err = PHYSFS_getLastErrorCode();
135 			PHYSFS_setBuffer(fileHandle, 0);
136 			return err;
137 		}
138 		return PHYSFS_ERR_OK;
139 	}
140 	return PHYSFS_ERR_UNSUPPORTED;
141 }
142 #define WZ_PHYSFS_SETBUFFER(fileHandle, bufsize) \
143 	PHYSFS_ErrorCode err = _WZ_PHYSFS_setBuffer(fileHandle, bufsize); \
144 	if (err != PHYSFS_ERR_OK && err != PHYSFS_ERR_UNSUPPORTED) \
145 	{ \
146 		debug(LOG_ERROR, "PHYSFS_setBuffer failed with error code: %d", (int)err); \
147 	}
148 #else
149 #define WZ_PHYSFS_SETBUFFER(fileHandle, bufsize)	// no-op
150 #endif
151 
152 // enumFunc receives each enumerated file, and returns true to continue enumeration, or false to shortcut / stop enumeration
153 bool WZ_PHYSFS_enumerateFiles(const char *dir, const std::function<bool (char* file)>& enumFunc);
154 
155 // Older wrappers
156 
PHYSFS_writeSLE8(PHYSFS_file * file,int8_t val)157 static inline bool PHYSFS_writeSLE8(PHYSFS_file *file, int8_t val)
158 {
159 	return (WZ_PHYSFS_writeBytes(file, &val, sizeof(int8_t)) == sizeof(int8_t));
160 }
161 
PHYSFS_writeULE8(PHYSFS_file * file,uint8_t val)162 static inline bool PHYSFS_writeULE8(PHYSFS_file *file, uint8_t val)
163 {
164 	return (WZ_PHYSFS_writeBytes(file, &val, sizeof(uint8_t)) == sizeof(uint8_t));
165 }
166 
PHYSFS_readSLE8(PHYSFS_file * file,int8_t * val)167 static inline bool PHYSFS_readSLE8(PHYSFS_file *file, int8_t *val)
168 {
169 	return (WZ_PHYSFS_readBytes(file, val, sizeof(int8_t)) == sizeof(int8_t));
170 }
171 
PHYSFS_readULE8(PHYSFS_file * file,uint8_t * val)172 static inline bool PHYSFS_readULE8(PHYSFS_file *file, uint8_t *val)
173 {
174 	return (WZ_PHYSFS_readBytes(file, val, sizeof(uint8_t)) == sizeof(uint8_t));
175 }
176 
PHYSFS_writeSBE8(PHYSFS_file * file,int8_t val)177 static inline bool PHYSFS_writeSBE8(PHYSFS_file *file, int8_t val)
178 {
179 	return (WZ_PHYSFS_writeBytes(file, &val, sizeof(int8_t)) == sizeof(int8_t));
180 }
181 
PHYSFS_writeUBE8(PHYSFS_file * file,uint8_t val)182 static inline bool PHYSFS_writeUBE8(PHYSFS_file *file, uint8_t val)
183 {
184 	return (WZ_PHYSFS_writeBytes(file, &val, sizeof(uint8_t)) == sizeof(uint8_t));
185 }
186 
PHYSFS_readSBE8(PHYSFS_file * file,int8_t * val)187 static inline bool PHYSFS_readSBE8(PHYSFS_file *file, int8_t *val)
188 {
189 	return (WZ_PHYSFS_readBytes(file, val, sizeof(int8_t)) == sizeof(int8_t));
190 }
191 
PHYSFS_readUBE8(PHYSFS_file * file,uint8_t * val)192 static inline bool PHYSFS_readUBE8(PHYSFS_file *file, uint8_t *val)
193 {
194 	return (WZ_PHYSFS_readBytes(file, val, sizeof(uint8_t)) == sizeof(uint8_t));
195 }
196 
PHYSFS_writeBEFloat(PHYSFS_file * file,float val)197 static inline bool PHYSFS_writeBEFloat(PHYSFS_file *file, float val)
198 {
199 	// For the purpose of endian conversions a IEEE754 float can be considered
200 	// the same to a 32bit integer.
201 	// We're using a union here to prevent type punning of pointers.
202 	union
203 	{
204 		float f;
205 		uint32_t i;
206 	} writeValue;
207 	writeValue.f = val;
208 	return (PHYSFS_writeUBE32(file, writeValue.i) != 0);
209 }
210 
PHYSFS_readBEFloat(PHYSFS_file * file,float * val)211 static inline bool PHYSFS_readBEFloat(PHYSFS_file *file, float *val)
212 {
213 	// For the purpose of endian conversions a IEEE754 float can be considered
214 	// the same to a 32bit integer.
215 	uint32_t *readValue = (uint32_t *)val;
216 	return (PHYSFS_readUBE32(file, readValue) != 0);
217 }
218 
219 #ifdef WZ_CC_MINGW
220 bool PHYSFS_printf(PHYSFS_file *file, const char *format, ...) WZ_DECL_FORMAT(__MINGW_PRINTF_FORMAT, 2, 3);
221 #else
222 bool PHYSFS_printf(PHYSFS_file *file, const char *format, ...) WZ_DECL_FORMAT(printf, 2, 3);
223 #endif
224 
225 /**
226  * @brief      fgets() implemented using PHYSFS.
227  * @param      s Pointer to an array where the read chars will be stored.
228  * @param      size Maximum number of chars to read (includes terminating null character).
229  * @param      stream PHYSFS file handle.
230  * @return     s on success, NULL on error or if no characters were read.
231  * @note       WZ_PHYSFS_getLastError() or PHYSFS_eof() can help find the source
232  *                     of the error.
233  * @note       If a EOF is encountered before any chars are read, the chars
234  *                     pointed by s are not changed.
235  *
236  * This function reads in at most size - 1 characters from stream
237  * and stores them into the buffer pointed to by s.
238  * Reading stops after an EOF or a newline and a null char is automatically appended.
239  * If a newline is read, it is stored into the buffer.
240  */
PHYSFS_fgets(char * s,int size,PHYSFS_file * stream)241 static inline char *PHYSFS_fgets(char *s, int size, PHYSFS_file *stream)
242 {
243 	char c;
244 	int i = 0;
245 	PHYSFS_sint64 retval;
246 
247 	if (size <= 0 || !stream || !s || PHYSFS_eof(stream))
248 	{
249 		return nullptr;
250 	}
251 	do
252 	{
253 		retval = WZ_PHYSFS_readBytes(stream, &c, 1);
254 
255 		if (retval < 1)
256 		{
257 			break;
258 		}
259 		s[i++] = c;
260 	}
261 	while (c != '\n' && i < size - 1);
262 	s[i] = '\0';
263 
264 	// Success conditions
265 	if (retval == 1 // Read maximum chars or newline
266 	    || (i != 0 && PHYSFS_eof(stream) != 0)) /* Read something and stopped at EOF
267 	                                          * (note: i!=0 *should* be redundant) */
268 	{
269 		return s;
270 	}
271 
272 	// Complete failure
273 	return nullptr;
274 }
275 
276 #endif // _physfs_ext_h
277