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.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

213 linhas
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. }