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