1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus - util.c *
3 * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4 * Copyright (C) 2012 CasualJames *
5 * Copyright (C) 2002 Hacktarux *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
21 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22
23 /**
24 * Provides common utilities to the rest of the code:
25 * -String functions
26 */
27
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <string.h>
34 #include <stdarg.h>
35 #include <errno.h>
36 #include <limits.h>
37
38 #include "rom.h"
39 #include "util.h"
40 #include "osal/preproc.h"
41
42 /**********************
43 Byte swap utilities
44 **********************/
swap_buffer(void * buffer,size_t length,size_t count)45 void swap_buffer(void *buffer, size_t length, size_t count)
46 {
47 size_t i;
48 if (length == 2)
49 {
50 uint16_t *pun = (uint16_t*)buffer;
51 for (i = 0; i < count; i++)
52 pun[i] = m64p_swap16(pun[i]);
53 }
54 else if (length == 4)
55 {
56 uint32_t *pun = (uint32_t*)buffer;
57 for (i = 0; i < count; i++)
58 pun[i] = m64p_swap32(pun[i]);
59 }
60 else if (length == 8)
61 {
62 uint64_t *pun = (uint64_t*)buffer;
63 for (i = 0; i < count; i++)
64 pun[i] = m64p_swap64(pun[i]);
65 }
66 }
67
to_little_endian_buffer(void * buffer,size_t length,size_t count)68 void to_little_endian_buffer(void *buffer, size_t length, size_t count)
69 {
70 #ifdef MSB_FIRST
71 swap_buffer(buffer, length, count);
72 #endif
73 }
74
to_big_endian_buffer(void * buffer,size_t length,size_t count)75 void to_big_endian_buffer(void *buffer, size_t length, size_t count)
76 {
77 #ifndef MSB_FIRST
78 swap_buffer(buffer, length, count);
79 #endif
80 }
81
82 /**********************
83 GUI utilities
84 **********************/
countrycodestring(char countrycode,char * string)85 void countrycodestring(char countrycode, char *string)
86 {
87 switch (countrycode)
88 {
89 case '\0': /* Demo */
90 strcpy(string, "Demo");
91 break;
92
93 case '7': /* Beta */
94 strcpy(string, "Beta");
95 break;
96
97 case 'A': /* Japan / USA */
98 strcpy(string, "USA/Japan");
99 break;
100
101 case 'D': /* Germany */
102 strcpy(string, "Germany");
103 break;
104
105 case 'E': /* USA */
106 strcpy(string, "USA");
107 break;
108
109 case 'F': /* France */
110 strcpy(string, "France");
111 break;
112
113 case 'I': /* Italy */
114 strcpy(string, "Italy");
115 break;
116
117 case 'J': /* Japan */
118 strcpy(string, "Japan");
119 break;
120
121 case 'S': /* Spain */
122 strcpy(string, "Spain");
123 break;
124
125 case 'U': case 'Y': /* Australia */
126 sprintf(string, "Australia (%c)", countrycode);
127 break;
128
129 case 0x50: case 0x58: case 0x20:
130 case 0x21: case 0x38: case 0x70:
131 sprintf(string, "Europe (%c)", countrycode);
132 break;
133
134 default:
135 sprintf(string, "Unknown (0x%02X)", countrycode);
136 break;
137 }
138 }
139
imagestring(unsigned char imagetype,char * string)140 void imagestring(unsigned char imagetype, char *string)
141 {
142 switch (imagetype)
143 {
144 case Z64IMAGE:
145 strcpy(string, ".z64 (native)");
146 break;
147 case V64IMAGE:
148 strcpy(string, ".v64 (byteswapped)");
149 break;
150 case N64IMAGE:
151 strcpy(string, ".n64 (wordswapped)");
152 break;
153 default:
154 string[0] = '\0';
155 }
156 }
157
158 /**********************
159 Path utilities
160 **********************/
161
162 /* Looks for an instance of ANY of the characters in 'needles' in 'haystack',
163 * starting from the end of 'haystack'. Returns a pointer to the last position
164 * of some character on 'needles' on 'haystack'. If not found, returns NULL.
165 */
strpbrk_reverse(const char * needles,const char * haystack)166 static const char* strpbrk_reverse(const char* needles, const char* haystack)
167 {
168 size_t stringlength = strlen(haystack), counter;
169
170 for (counter = stringlength; counter > 0; --counter)
171 {
172 if (strchr(needles, haystack[counter-1]))
173 break;
174 }
175
176 if (counter == 0)
177 return NULL;
178
179 return haystack + counter - 1;
180 }
181
namefrompath(const char * path)182 const char* namefrompath(const char* path)
183 {
184 const char* last_separator_ptr = strpbrk_reverse(OSAL_DIR_SEPARATORS, path);
185
186 if (last_separator_ptr != NULL)
187 return last_separator_ptr + 1;
188 return path;
189 }
190
is_path_separator(char c)191 static int is_path_separator(char c)
192 {
193 return strchr(OSAL_DIR_SEPARATORS, c) != NULL;
194 }
195
combinepath(const char * first,const char * second)196 char* combinepath(const char* first, const char *second)
197 {
198 size_t len_first = strlen(first), off_second = 0;
199
200 if (first == NULL || second == NULL)
201 return NULL;
202
203 while (is_path_separator(first[len_first-1]))
204 len_first--;
205
206 while (is_path_separator(second[off_second]))
207 off_second++;
208
209 return formatstr("%.*s%c%s", (int) len_first, first, OSAL_DIR_SEPARATORS[0], second + off_second);
210 }
211
212 /**********************
213 String utilities
214 **********************/
trim(char * str)215 char *trim(char *str)
216 {
217 char *start = str, *end = str + strlen(str);
218
219 while (start < end && isspace((unsigned char)(*start)))
220 start++;
221
222 while (end > start && isspace((unsigned char)(*(end-1))))
223 end--;
224
225 memmove(str, start, end - start);
226 str[end - start] = '\0';
227
228 return str;
229 }
230
string_to_int(const char * str,int * result)231 int string_to_int(const char *str, int *result)
232 {
233 char *endptr;
234 long int n;
235 if (*str == '\0' || isspace((unsigned char)(*str)))
236 return 0;
237 errno = 0;
238 n = strtol(str, &endptr, 10);
239 if (*endptr != '\0' || errno != 0 || n < INT_MIN || n > INT_MAX)
240 return 0;
241 *result = (int)n;
242 return 1;
243 }
244
char2hex(char c)245 static unsigned char char2hex(char c)
246 {
247 c = tolower(c);
248 if(c >= '0' && c <= '9')
249 return c - '0';
250 else if(c >= 'a' && c <= 'f')
251 return c - 'a' + 10;
252 return 0xFF;
253 }
254
parse_hex(const char * str,unsigned char * output,size_t output_size)255 int parse_hex(const char *str, unsigned char *output, size_t output_size)
256 {
257 size_t i, j;
258 for (i = 0; i < output_size; i++)
259 {
260 output[i] = 0;
261 for (j = 0; j < 2; j++)
262 {
263 unsigned char h = char2hex(*str++);
264 if (h == 0xFF)
265 return 0;
266
267 output[i] = (output[i] << 4) | h;
268 }
269 }
270
271 if (*str != '\0')
272 return 0;
273
274 return 1;
275 }
276
formatstr(const char * fmt,...)277 char *formatstr(const char *fmt, ...)
278 {
279 va_list args;
280 int size = 128;
281 char *str = (char *)malloc(size), *newstr;
282
283 /* There are two implementations of vsnprintf we have to deal with:
284 * C99 version: Returns the number of characters which would have been written
285 * if the buffer had been large enough, and -1 on failure.
286 * Windows version: Returns the number of characters actually written,
287 * and -1 on failure or truncation.
288 * NOTE: An implementation equivalent to the Windows one appears in glibc <2.1.
289 */
290 while (str != NULL)
291 {
292 int ret;
293
294 va_start(args, fmt);
295 ret = vsnprintf(str, size, fmt, args);
296 va_end(args);
297
298 // Successful result?
299 if (ret >= 0 && ret < size)
300 return str;
301
302 // Increment the capacity of the buffer
303 if (ret >= size)
304 size = ret + 1; // C99 version: We got the needed buffer size
305 else
306 size *= 2; // Windows version: Keep guessing
307
308 newstr = (char *)realloc(str, size);
309 if (newstr == NULL)
310 free(str);
311 str = newstr;
312 }
313
314 return NULL;
315 }
316
ini_parse_line(char ** lineptr)317 ini_line ini_parse_line(char **lineptr)
318 {
319 char *line = *lineptr, *endline = strchr(*lineptr, '\n'), *equal;
320 ini_line l;
321
322 // Null terminate the current line and point to the next line
323 if (endline != NULL)
324 *endline = '\0';
325 *lineptr = line + strlen(line) + 1;
326
327 // Parse the line contents
328 trim(line);
329
330 if (line[0] == '#' || line[0] == ';')
331 {
332 line++;
333
334 l.type = INI_COMMENT;
335 l.name = NULL;
336 l.value = trim(line);
337 }
338 else if (line[0] == '[' && line[strlen(line)-1] == ']')
339 {
340 line[strlen(line)-1] = '\0';
341 line++;
342
343 l.type = INI_SECTION;
344 l.name = trim(line);
345 l.value = NULL;
346 }
347 else if ((equal = strchr(line, '=')) != NULL)
348 {
349 char *name = line, *value = equal + 1;
350 *equal = '\0';
351
352 l.type = INI_PROPERTY;
353 l.name = trim(name);
354 l.value = trim(value);
355 }
356 else
357 {
358 l.type = (*line == '\0') ? INI_BLANK : INI_TRASH;
359 l.name = NULL;
360 l.value = NULL;
361 }
362
363 return l;
364 }
365