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
49file_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
67file_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
85file_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 */
144close_file:
145 fclose(fd);
146 return ret;
147}
148
149
150/**********************
151 Byte swap utilities
152 **********************/
153void 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
176void 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
183void 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 **********************/
193void 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
248void 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 */
274static 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
290const 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
300static int is_path_separator(char c)
301{
302 return strchr(OSAL_DIR_SEPARATORS, c) != NULL;
303}
304
305char* 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 **********************/
326char *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
342int 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
356static 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
367int 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
389char *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
427ini_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