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