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