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