NESe (pronounced "Nessie") is a NES emulator based on the e6502 emulator, also written in C with a focus on speed and portability for use on embedded platforms, especially ARM.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
6.1KB

  1. #include <ctype.h>
  2. #include <stdint.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include "ini.h"
  7. #include "compat.h"
  8. static inline int stracmp(const char* zstr,
  9. const char* lstr, size_t len) {
  10. int diff = 0;
  11. while (0 == diff && *zstr && len > 0) {
  12. --len;
  13. diff = (*zstr - *lstr);
  14. ++zstr;
  15. ++lstr;
  16. }
  17. if (0 == diff) {
  18. if (0 == len && !*zstr) diff = *zstr;
  19. else if (0 != len && *zstr) diff = *lstr;
  20. }
  21. return diff;
  22. }
  23. static const ini_datum* find_name(const ini_datum* schema,
  24. const char* name, int len,
  25. const ini_data_type type) {
  26. if (0 == stracmp(schema->name, name, len)) {
  27. return schema;
  28. }
  29. for (int i = 0; i < schema->count; ++i) {
  30. if ( ( ini_none == type ||
  31. type == schema->data[i].type) &&
  32. 0 == stracmp(schema->data[i].name, name, len)) {
  33. return &schema->data[i];
  34. }
  35. }
  36. return NULL;
  37. }
  38. static inline int needs_quotes(const char* str) {
  39. while (*str && !isspace(*str) && ',' != *str) ++str;
  40. return !!(*str);
  41. }
  42. static int _write_ini_file(FILE* file, const ini_datum* schema,
  43. const void* data, int level) {
  44. int status = 0;
  45. const void* ptr = (data + schema->offset);
  46. if (ini_section == schema->type) {
  47. fprintf(file, "[%s]\n", schema->name);
  48. for (int i = 0; i < schema->count; ++i) {
  49. _write_ini_file(file, &schema->data[i],
  50. ptr, level + 1);
  51. if (schema->count - 1 != i) fputc('\n', file);
  52. }
  53. if (0 != level) fputc('\n', file);
  54. } else if (ini_comment == schema->type) {
  55. fprintf(file, "; %s", *(char**)ptr);
  56. } else {
  57. fprintf(file, "%s = ", schema->name);
  58. if (ini_string == schema->type) {
  59. const char* str = *(char**)ptr;
  60. if (NULL != str) {
  61. if (!needs_quotes(str)) fputs(str, file);
  62. else fprintf(file, "\"%s\"", str);
  63. }
  64. } else if (ini_integer == schema->type) {
  65. fprintf(file, "%d", *(uint32_t*)ptr);
  66. } else if (ini_flag == schema->type) {
  67. fprintf(file, "%d", !!( *(uint32_t*)ptr &
  68. (1 << schema->shift)));
  69. }
  70. }
  71. return status;
  72. }
  73. int write_ini_file(FILE* file, const ini_datum* schema,
  74. const void* data) {
  75. return _write_ini_file(file, schema, data, 0);
  76. }
  77. static inline const char* first_char(const char* str) {
  78. while (*str && isspace(*str)) ++str;
  79. return str;
  80. }
  81. static inline const char* first_space(const char* str) {
  82. while (*str && !isspace(*str)) ++str;
  83. return str;
  84. }
  85. static inline const char* end_key(const char* str) {
  86. while (*str && '=' != *str && !isspace(*str)) ++str;
  87. return str;
  88. }
  89. static inline const char* last_char(const char* str) {
  90. int len = strlen(str);
  91. const char* end = str + len - 1;
  92. while (end > str && isspace(*end)) --end;
  93. return end;
  94. }
  95. static inline char* parse_string(const char* str) {
  96. const char* last = last_char(str);
  97. if ('"' == str[0] && '"' == *last) {
  98. ++str;
  99. --last;
  100. }
  101. return strndup(str, last - str + 1);
  102. }
  103. static inline int parse_key_value(const char* key_start,
  104. const ini_datum* section,
  105. void* data) {
  106. const char* key_end = end_key(key_start + 1);
  107. if (NULL == key_end) return -1;
  108. const ini_datum* def = find_name(section, key_start,
  109. (key_end - key_start),
  110. ini_none);
  111. if (NULL == def || def->type <= ini_section) return -1;
  112. const char* equal = first_char(key_end);
  113. if (NULL == equal || '=' != *equal) return -1;
  114. const char* val = first_char(equal + 1);
  115. if (NULL == val) return -1;
  116. const char* ptr = data + def->offset;
  117. if (ini_string == def->type) {
  118. *(char**)ptr = parse_string(val);
  119. } else if ( ini_integer == def->type ||
  120. ini_flag == def->type) {
  121. int intval = 0;
  122. if (0 >= sscanf(val, "%d", &intval)) return -1;
  123. if (ini_integer == def->type) {
  124. *(int32_t*)ptr = intval;
  125. } else {
  126. int32_t mask = (1 << def->shift);
  127. if (intval) *(uint32_t*)ptr |= mask;
  128. else *(uint32_t*)ptr &= ~mask;
  129. }
  130. }
  131. return 0;
  132. }
  133. int read_ini_file(FILE* file, const ini_datum* schema,
  134. void* data) {
  135. int status = 0;
  136. const ini_datum* section = NULL;
  137. const ini_datum* subsection = NULL;
  138. void* ptr = data;
  139. char* line = NULL;
  140. size_t sz_line = 0;
  141. while (0 == status && 0 <= getline(&line, &sz_line, file)) {
  142. const char* str = first_char(line);
  143. if ('[' == str[0]) {
  144. const char* start = &str[1];
  145. const char* end = strchr(start, ']');
  146. if (NULL != end) {
  147. ptr = data;
  148. int len = (end - start);
  149. if ('.' == start[0]) {
  150. if (NULL != section) {
  151. subsection = find_name(
  152. section, start, len, ini_section
  153. );
  154. ptr += section->offset;
  155. if (NULL != subsection) {
  156. ptr += subsection->offset;
  157. }
  158. }
  159. } else {
  160. section = find_name(
  161. schema, start, len, ini_section
  162. );
  163. subsection = section;
  164. if (NULL != section) ptr += section->offset;
  165. }
  166. }
  167. } else if (';' == str[0]) {
  168. // Ignore comments
  169. } else if (isalnum(str[0])) {
  170. // Key-value
  171. // Ignoring return value:
  172. // - Unknown sections or keys are ignored
  173. // - Just ignore malformed files, I guess
  174. parse_key_value(str, subsection, ptr);
  175. }
  176. }
  177. free(line);
  178. return status;
  179. }