1 /*
2 * aym.cc
3 * Misc. functions
4 * AYM 1997-??-??
5 */
6
7
8 /*
9 This file is part of Yadex.
10
11 Yadex incorporates code from DEU 5.21 that was put in the public domain in
12 1994 by Rapha�l Quinet and Brendon Wyber.
13
14 The rest of Yadex is Copyright � 1997-2003 Andr� Majorel and others.
15
16 This program is free software; you can redistribute it and/or modify it under
17 the terms of the GNU General Public License as published by the Free Software
18 Foundation; either version 2 of the License, or (at your option) any later
19 version.
20
21 This program is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License along with
26 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
27 Place, Suite 330, Boston, MA 02111-1307, USA.
28 */
29
30
31 #include "yadex.h"
32 #include "game.h"
33
34
35 /*
36 * levelname2levelno
37 * Used to know if directory entry is ExMy or MAPxy
38 * For "ExMy" (case-insensitive), returns 10x + y
39 * For "ExMyz" (case-insensitive), returns 100*x + 10y + z
40 * For "MAPxy" (case-insensitive), returns 1000 + 10x + y
41 * E0My, ExM0, E0Myz, ExM0z are not considered valid names.
42 * MAP00 is considered a valid name.
43 * For other names, returns 0.
44 */
levelname2levelno(const char * name)45 int levelname2levelno (const char *name)
46 {
47 const unsigned char *s = (const unsigned char *) name;
48 if (toupper (s[0]) == 'E'
49 && isdigit (s[1])
50 && s[1] != '0'
51 && toupper (s[2]) == 'M'
52 && isdigit (s[3])
53 && s[3] != '0'
54 && s[4] == '\0')
55 return 10 * dectoi (s[1]) + dectoi (s[3]);
56 if (yg_level_name == YGLN_E1M10
57 && toupper (s[0]) == 'E'
58 && isdigit (s[1])
59 && s[1] != '0'
60 && toupper (s[2]) == 'M'
61 && isdigit (s[3])
62 && s[3] != '0'
63 && isdigit (s[4])
64 && s[5] == '\0')
65 return 100 * dectoi (s[1]) + 10 * dectoi (s[3]) + dectoi (s[4]);
66 if (toupper (s[0]) == 'M'
67 && toupper (s[1]) == 'A'
68 && toupper (s[2]) == 'P'
69 && isdigit (s[3])
70 && isdigit (s[4])
71 && s[5] == '\0')
72 return 1000 + 10 * dectoi (s[3]) + dectoi (s[4]);
73 return 0;
74 }
75
76
77 /*
78 * levelname2rank
79 * Used to sort level names.
80 * Identical to levelname2levelno except that, for "ExMy",
81 * it returns 100x + y, so that
82 * - f("E1M10") = f("E1M9") + 1
83 * - f("E2M1") > f("E1M99")
84 * - f("E2M1") > f("E1M99") + 1
85 * - f("MAPxy") > f("ExMy")
86 * - f("MAPxy") > f("ExMyz")
87 */
levelname2rank(const char * name)88 int levelname2rank (const char *name)
89 {
90 const unsigned char *s = (const unsigned char *) name;
91 if (toupper (s[0]) == 'E'
92 && isdigit (s[1])
93 && s[1] != '0'
94 && toupper (s[2]) == 'M'
95 && isdigit (s[3])
96 && s[3] != '0'
97 && s[4] == '\0')
98 return 100 * dectoi (s[1]) + dectoi (s[3]);
99 if (yg_level_name == YGLN_E1M10
100 && toupper (s[0]) == 'E'
101 && isdigit (s[1])
102 && s[1] != '0'
103 && toupper (s[2]) == 'M'
104 && isdigit (s[3])
105 && s[3] != '0'
106 && isdigit (s[4])
107 && s[5] == '\0')
108 return 100 * dectoi (s[1]) + 10 * dectoi (s[3]) + dectoi (s[4]);
109 if (toupper (s[0]) == 'M'
110 && toupper (s[1]) == 'A'
111 && toupper (s[2]) == 'P'
112 && isdigit (s[3])
113 && isdigit (s[4])
114 && s[5] == '\0')
115 return 1000 + 10 * dectoi (s[3]) + dectoi (s[4]);
116 return 0;
117 }
118
119
120 /*
121 * spec_path
122 * Extract the path of a spec
123 */
spec_path(const char * spec)124 const char *spec_path (const char *spec)
125 {
126 static char path[Y_PATH + 1];
127 size_t n;
128
129 *path = '\0';
130 strncat (path, spec, sizeof path - 1);
131 for (n = strlen (path); n > 0 && ! al_fisps (path[n-1]); n--)
132 ;
133 path[n] = '\0';
134 return path;
135 }
136
137
138 /*
139 * fncmp
140 * Compare two filenames
141 * For Unix, it's a simple strcmp.
142 * For DOS, it's case insensitive and "/" and "\" are equivalent.
143 * FIXME: should canonicalize both names and compare that.
144 */
fncmp(const char * name1,const char * name2)145 int fncmp (const char *name1, const char *name2)
146 {
147 #if defined Y_DOS
148 char c1, c2;
149 for (;;)
150 {
151 c1 = tolower ((unsigned char) *name1++);
152 c2 = tolower ((unsigned char) *name2++);
153 if (c1=='\\')
154 c1 = '/';
155 if (c2=='\\')
156 c2 = '/';
157 if (c1 != c2)
158 return c1-c2;
159 if (!c1)
160 return 0;
161 }
162 #elif defined Y_UNIX
163 return strcmp (name1, name2);
164 #endif
165 }
166
167
168 /*
169 * is_absolute
170 * Tell whether a file name is absolute or relative.
171 *
172 * Note: for DOS, a filename of the form "d:foo" is
173 * considered absolute, even though it's technically
174 * relative to the current working directory of "d:".
175 * My reasoning is that someone who wants to specify a
176 * name that's relative to one of the standard
177 * directories is not going to put a "d:" in front of it.
178 */
is_absolute(const char * filename)179 int is_absolute (const char *filename)
180 {
181 #if defined Y_UNIX
182 return *filename == '/';
183 #elif defined Y_DOS
184 return *filename == '/'
185 || *filename == '\\'
186 || isalpha (*filename) && filename[1] == ':';
187 #endif
188 }
189
190
191 /*
192 * y_stricmp
193 * A case-insensitive strcmp()
194 * (same thing as DOS stricmp() or GNU strcasecmp())
195 */
y_stricmp(const char * s1,const char * s2)196 int y_stricmp (const char *s1, const char *s2)
197 {
198 for (;;)
199 {
200 if (tolower (*s1) != tolower (*s2))
201 return (unsigned char) *s1 - (unsigned char) *s2;
202 if (! *s1)
203 if (! *s2)
204 return 0;
205 else
206 return -1;
207 if (! *s2)
208 return 1;
209 s1++;
210 s2++;
211 }
212 }
213
214
215 /*
216 * y_strnicmp
217 * A case-insensitive strncmp()
218 * (same thing as DOS strnicmp() or GNU strncasecmp())
219 */
y_strnicmp(const char * s1,const char * s2,size_t len)220 int y_strnicmp (const char *s1, const char *s2, size_t len)
221 {
222 while (len-- > 0)
223 {
224 if (tolower (*s1) != tolower (*s2))
225 return (unsigned char) *s1 - (unsigned char) *s2;
226 if (! *s1)
227 if (! *s2)
228 return 0;
229 else
230 return -1;
231 if (! *s2)
232 return 1;
233 s1++;
234 s2++;
235 }
236 return 0;
237 }
238
239
240 /*
241 * y_snprintf
242 * If available, snprintf(). Else sprintf().
243 */
y_snprintf(char * buf,size_t size,const char * fmt,...)244 int y_snprintf (char *buf, size_t size, const char *fmt, ...)
245 {
246 va_list args;
247 va_start (args, fmt);
248 #ifdef Y_SNPRINTF
249 return vsnprintf (buf, size, fmt, args);
250 #else
251 return vsprintf (buf, fmt, args);
252 #endif
253 }
254
255
256 /*
257 * y_vsnprintf
258 * If available, vsnprintf(). Else vsprintf().
259 */
y_vsnprintf(char * buf,size_t size,const char * fmt,va_list args)260 int y_vsnprintf (char *buf, size_t size, const char *fmt, va_list args)
261 {
262 #ifdef Y_SNPRINTF
263 return vsnprintf (buf, size, fmt, args);
264 #else
265 return vsprintf (buf, fmt, args);
266 #endif
267 }
268
269
270 /*
271 * y_strupr
272 * Upper-case a string
273 */
y_strupr(char * string)274 void y_strupr (char *string)
275 {
276 while (*string)
277 {
278 *string = toupper (*string);
279 string++;
280 }
281 }
282
283 /*
284 * is_one_of
285 * Return non-zero if <s> is equal (in the strcmp() sense)
286 * to one of the other strings (retrieved from the argument
287 * list as const char *). The last string must be followed
288 * by (const char *) 0.
289 */
is_one_of(const char * needle,...)290 int is_one_of (const char *needle, ...)
291 {
292 va_list args;
293 va_start (args, needle);
294 for (;;)
295 {
296 const char *haystack = va_arg (args, const char *);
297 if (haystack == Y_NULL)
298 break;
299 if (! strcmp (needle, haystack))
300 return 1;
301 }
302 return 0;
303 }
304
305
306
307 /*
308 * file_exists
309 * Check whether a file exists and is readable.
310 * Returns true if it is, false if it isn't.
311 */
file_exists(const char * filename)312 bool file_exists (const char *filename)
313 {
314 FILE *test;
315
316 if ((test = fopen (filename, "rb")) == NULL)
317 return 0;
318 fclose (test);
319 return 1;
320 }
321
322
323 /*
324 * y_filename
325 * Copies into <buf> a string that is a close as possible
326 * to <filename> but is guaranteed to be no longer than
327 * <size> - 1 and contain only printable characters. Non
328 * printable characters are replaced by question marks.
329 * Excess characters are replaced by an ellipsis.
330 */
y_filename(char * buf,size_t size,const char * filename)331 void y_filename (char *buf, size_t size, const char *filename)
332 {
333 if (size == 0)
334 return;
335 if (size == 1)
336 {
337 *buf = '\0';
338 return;
339 }
340 size_t len = strlen (filename);
341 size_t maxlen = size - 1;
342
343 if (len > 3 && maxlen <= 3) // Pathological case, fill with dots
344 {
345 memset (buf, '.', maxlen);
346 buf[maxlen] = '\0';
347 return;
348 }
349
350 size_t len1 = len;
351 size_t len2 = 0;
352 if (len > maxlen)
353 {
354 len1 = (maxlen - 3) / 2;
355 len2 = maxlen - 3 - len1;
356 }
357 char *p = buf;
358 for (size_t n = 0; n < len1; n++)
359 {
360 *p++ = y_isprint (*filename) ? *filename : '?';
361 filename++;
362 }
363 if (len2 > 0)
364 {
365 *p++ = '.';
366 *p++ = '.';
367 *p++ = '.';
368 filename += len - len1 - len2;
369 for (size_t n = 0; n < len2; n++)
370 {
371 *p++ = y_isprint (*filename) ? *filename : '?';
372 filename++;
373 }
374 }
375 *p++ = '\0';
376 }
377
378
379