Core commit. Compile and run on the OpenPandora
[mupen64plus-pandora.git] / source / mupen64plus-core / src / main / util.c
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 <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <string.h>
33 #include <stdarg.h>
34 #include <errno.h>
35 #include <limits.h>
36
37 #include "rom.h"
38 #include "util.h"
39 #include "osal/files.h"
40 #include "osal/preproc.h"
41
42 /**********************
43      File utilities
44  **********************/
45
46 file_status_t read_from_file(const char *filename, void *data, size_t size)
47 {
48     FILE *f = fopen(filename, "rb");
49     if (f == NULL)
50     {
51         return file_open_error;
52     }
53
54     if (fread(data, 1, size, f) != size)
55     {
56         fclose(f);
57         return file_read_error;
58     }
59
60     fclose(f);
61     return file_ok;
62 }
63
64 file_status_t write_to_file(const char *filename, const void *data, size_t size)
65 {
66     FILE *f = fopen(filename, "wb");
67     if (f == NULL)
68     {
69         return file_open_error;
70     }
71
72     if (fwrite(data, 1, size, f) != size)
73     {
74         fclose(f);
75         return file_read_error;
76     }
77
78     fclose(f);
79     return file_ok;
80 }
81
82 /**********************
83    Byte swap utilities
84  **********************/
85 void swap_buffer(void *buffer, size_t length, size_t count)
86 {
87     size_t i;
88     if (length == 2)
89     {
90         unsigned short *pun = (unsigned short *)buffer;
91         for (i = 0; i < count; i++)
92             pun[i] = m64p_swap16(pun[i]);
93     }
94     else if (length == 4)
95     {
96         unsigned int *pun = (unsigned int *)buffer;
97         for (i = 0; i < count; i++)
98             pun[i] = m64p_swap32(pun[i]);
99     }
100     else if (length == 8)
101     {
102         unsigned long long *pun = (unsigned long long *)buffer;
103         for (i = 0; i < count; i++)
104             pun[i] = m64p_swap64(pun[i]);
105     }
106 }
107
108 void to_little_endian_buffer(void *buffer, size_t length, size_t count)
109 {
110     #ifdef M64P_BIG_ENDIAN
111     swap_buffer(buffer, length, count);
112     #endif
113 }
114
115 void to_big_endian_buffer(void *buffer, size_t length, size_t count)
116 {
117     #ifndef M64P_BIG_ENDIAN
118     swap_buffer(buffer, length, count);
119     #endif
120 }
121
122 /**********************
123      GUI utilities
124  **********************/
125 void countrycodestring(unsigned short countrycode, char *string)
126 {
127     switch (countrycode)
128     {
129     case 0:    /* Demo */
130         strcpy(string, "Demo");
131         break;
132
133     case '7':  /* Beta */
134         strcpy(string, "Beta");
135         break;
136
137     case 0x41: /* Japan / USA */
138         strcpy(string, "USA/Japan");
139         break;
140
141     case 0x44: /* Germany */
142         strcpy(string, "Germany");
143         break;
144
145     case 0x45: /* USA */
146         strcpy(string, "USA");
147         break;
148
149     case 0x46: /* France */
150         strcpy(string, "France");
151         break;
152
153     case 'I':  /* Italy */
154         strcpy(string, "Italy");
155         break;
156
157     case 0x4A: /* Japan */
158         strcpy(string, "Japan");
159         break;
160
161     case 'S':  /* Spain */
162         strcpy(string, "Spain");
163         break;
164
165     case 0x55: case 0x59:  /* Australia */
166         sprintf(string, "Australia (0x%02X)", countrycode);
167         break;
168
169     case 0x50: case 0x58: case 0x20:
170     case 0x21: case 0x38: case 0x70:
171         sprintf(string, "Europe (0x%02X)", countrycode);
172         break;
173
174     default:
175         sprintf(string, "Unknown (0x%02X)", countrycode);
176         break;
177     }
178 }
179
180 void imagestring(unsigned char imagetype, char *string)
181 {
182     switch (imagetype)
183     {
184     case Z64IMAGE:
185         strcpy(string, ".z64 (native)");
186         break;
187     case V64IMAGE:
188         strcpy(string, ".v64 (byteswapped)");
189         break;
190     case N64IMAGE:
191         strcpy(string, ".n64 (wordswapped)");
192         break;
193     default:
194         string[0] = '\0';
195     }
196 }
197
198 /**********************
199      Path utilities
200  **********************/
201
202 /* Looks for an instance of ANY of the characters in 'needles' in 'haystack',
203  * starting from the end of 'haystack'. Returns a pointer to the last position
204  * of some character on 'needles' on 'haystack'. If not found, returns NULL.
205  */
206 static const char* strpbrk_reverse(const char* needles, const char* haystack)
207 {
208     size_t stringlength = strlen(haystack), counter;
209
210     for (counter = stringlength; counter > 0; --counter)
211     {
212         if (strchr(needles, haystack[counter-1]))
213             break;
214     }
215
216     if (counter == 0)
217         return NULL;
218
219     return haystack + counter - 1;
220 }
221
222 const char* namefrompath(const char* path)
223 {
224     const char* last_separator_ptr = strpbrk_reverse(OSAL_DIR_SEPARATORS, path);
225     
226     if (last_separator_ptr != NULL)
227         return last_separator_ptr + 1;
228     else
229         return path;
230 }
231
232 static int is_path_separator(char c)
233 {
234     return strchr(OSAL_DIR_SEPARATORS, c) != NULL;
235 }
236
237 char* combinepath(const char* first, const char *second)
238 {
239     size_t len_first = strlen(first), off_second = 0;
240
241     if (first == NULL || second == NULL)
242         return NULL;
243
244     while (is_path_separator(first[len_first-1]))
245         len_first--;
246
247     while (is_path_separator(second[off_second]))
248         off_second++;
249
250     return formatstr("%.*s%c%s", (int) len_first, first, OSAL_DIR_SEPARATORS[0], second + off_second);
251 }
252
253 /**********************
254     String utilities
255  **********************/
256 char *trim(char *str)
257 {
258     char *start = str, *end = str + strlen(str);
259
260     while (start < end && isspace(*start))
261         start++;
262
263     while (end > start && isspace(*(end-1)))
264         end--;
265
266     memmove(str, start, end - start);
267     str[end - start] = '\0';
268
269     return str;
270 }
271
272 int string_to_int(const char *str, int *result)
273 {
274     char *endptr;
275     long int n;
276     if (*str == '\0' || isspace(*str))
277         return 0;
278     errno = 0;
279     n = strtol(str, &endptr, 10);
280     if (*endptr != '\0' || errno != 0 || n < INT_MIN || n > INT_MAX)
281         return 0;
282     *result = (int)n;
283     return 1;
284 }
285
286 static unsigned char char2hex(char c)
287 {
288     c = tolower(c);
289     if(c >= '0' && c <= '9')
290         return c - '0';
291     else if(c >= 'a' && c <= 'f')
292         return c - 'a' + 10;
293     else
294         return 0xFF;
295 }
296
297 int parse_hex(const char *str, unsigned char *output, size_t output_size)
298 {
299     size_t i, j;
300     for (i = 0; i < output_size; i++)
301     {
302         output[i] = 0;
303         for (j = 0; j < 2; j++)
304         {
305             unsigned char h = char2hex(*str++);
306             if (h == 0xFF)
307                 return 0;
308
309             output[i] = (output[i] << 4) | h;
310         }
311     }
312
313     if (*str != '\0')
314         return 0;
315
316     return 1;
317 }
318
319 char *formatstr(const char *fmt, ...)
320 {
321         int size = 128, ret;
322         char *str = (char *)malloc(size), *newstr;
323         va_list args;
324
325         /* There are two implementations of vsnprintf we have to deal with:
326          * C99 version: Returns the number of characters which would have been written
327          *              if the buffer had been large enough, and -1 on failure.
328          * Windows version: Returns the number of characters actually written,
329          *                  and -1 on failure or truncation.
330          * NOTE: An implementation equivalent to the Windows one appears in glibc <2.1.
331          */
332         while (str != NULL)
333         {
334                 va_start(args, fmt);
335                 ret = vsnprintf(str, size, fmt, args);
336                 va_end(args);
337
338                 // Successful result?
339                 if (ret >= 0 && ret < size)
340                         return str;
341
342                 // Increment the capacity of the buffer
343                 if (ret >= size)
344                         size = ret + 1; // C99 version: We got the needed buffer size
345                 else
346                         size *= 2; // Windows version: Keep guessing
347
348                 newstr = (char *)realloc(str, size);
349                 if (newstr == NULL)
350                         free(str);
351                 str = newstr;
352         }
353
354         return NULL;
355 }
356
357 ini_line ini_parse_line(char **lineptr)
358 {
359     char *line = *lineptr, *endline = strchr(*lineptr, '\n'), *equal;
360     ini_line l;
361
362     // Null terminate the current line and point to the next line
363     if (endline != NULL)
364         *endline = '\0';
365     *lineptr = line + strlen(line) + 1;
366
367     // Parse the line contents
368     trim(line);
369
370     if (line[0] == '#' || line[0] == ';')
371     {
372         line++;
373
374         l.type = INI_COMMENT;
375         l.name = NULL;
376         l.value = trim(line);
377     }
378     else if (line[0] == '[' && line[strlen(line)-1] == ']')
379     {
380         line[strlen(line)-1] = '\0';
381         line++;
382
383         l.type = INI_SECTION;
384         l.name = trim(line);
385         l.value = NULL;
386     }
387     else if ((equal = strchr(line, '=')) != NULL)
388     {
389         char *name = line, *value = equal + 1;
390         *equal = '\0';
391
392         l.type = INI_PROPERTY;
393         l.name = trim(name);
394         l.value = trim(value);
395     }
396     else
397     {
398         l.type = (*line == '\0') ? INI_BLANK : INI_TRASH;
399         l.name = NULL;
400         l.value = NULL;
401     }
402
403     return l;
404 }