451ab91e |
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 | } |