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.
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

211 строки
6.0KB

  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 (!needs_quotes(str)) fputs(str, file);
  61. else fprintf(file, "\"%s\"", str);
  62. } else if (ini_integer == schema->type) {
  63. fprintf(file, "%d", *(uint32_t*)ptr);
  64. } else if (ini_flag == schema->type) {
  65. fprintf(file, "%d", !!( *(uint32_t*)ptr &
  66. (1 << schema->shift)));
  67. }
  68. }
  69. return status;
  70. }
  71. int write_ini_file(FILE* file, const ini_datum* schema,
  72. const void* data) {
  73. return _write_ini_file(file, schema, data, 0);
  74. }
  75. static inline const char* first_char(const char* str) {
  76. while (*str && isspace(*str)) ++str;
  77. return str;
  78. }
  79. static inline const char* first_space(const char* str) {
  80. while (*str && !isspace(*str)) ++str;
  81. return str;
  82. }
  83. static inline const char* end_key(const char* str) {
  84. while (*str && '=' != *str && !isspace(*str)) ++str;
  85. return str;
  86. }
  87. static inline const char* last_char(const char* str) {
  88. int len = strlen(str);
  89. const char* end = str + len - 1;
  90. while (end > str && isspace(*end)) --end;
  91. return end;
  92. }
  93. static inline char* parse_string(const char* str) {
  94. const char* last = last_char(str);
  95. if ('"' == str[0] && '"' == *last) {
  96. ++str;
  97. --last;
  98. }
  99. return strndup(str, last - str + 1);
  100. }
  101. static inline int parse_key_value(const char* key_start,
  102. const ini_datum* section,
  103. void* data) {
  104. const char* key_end = end_key(key_start + 1);
  105. if (NULL == key_end) return -1;
  106. const ini_datum* def = find_name(section, key_start,
  107. (key_end - key_start),
  108. ini_none);
  109. if (NULL == def || def->type <= ini_section) return -1;
  110. const char* equal = first_char(key_end);
  111. if (NULL == equal || '=' != *equal) return -1;
  112. const char* val = first_char(equal + 1);
  113. if (NULL == val) return -1;
  114. const char* ptr = data + def->offset;
  115. if (ini_string == def->type) {
  116. *(char**)ptr = parse_string(val);
  117. } else if ( ini_integer == def->type ||
  118. ini_flag == def->type) {
  119. int intval = 0;
  120. if (0 >= sscanf(val, "%d", &intval)) return -1;
  121. if (ini_integer == def->type) {
  122. *(int32_t*)ptr = intval;
  123. } else {
  124. int32_t mask = (1 << def->shift);
  125. if (intval) *(uint32_t*)ptr |= mask;
  126. else *(uint32_t*)ptr &= ~mask;
  127. }
  128. }
  129. return 0;
  130. }
  131. int read_ini_file(FILE* file, const ini_datum* schema,
  132. void* data) {
  133. int status = 0;
  134. const ini_datum* section = NULL;
  135. const ini_datum* subsection = NULL;
  136. void* ptr = data;
  137. char* line = NULL;
  138. size_t sz_line = 0;
  139. while (0 == status && 0 <= getline(&line, &sz_line, file)) {
  140. const char* str = first_char(line);
  141. if ('[' == str[0]) {
  142. const char* start = &str[1];
  143. const char* end = strchr(start, ']');
  144. if (NULL != end) {
  145. ptr = data;
  146. int len = (end - start);
  147. if ('.' == start[0]) {
  148. if (NULL != section) {
  149. subsection = find_name(
  150. section, start, len, ini_section
  151. );
  152. ptr += section->offset;
  153. if (NULL != subsection) {
  154. ptr += subsection->offset;
  155. }
  156. }
  157. } else {
  158. section = find_name(
  159. schema, start, len, ini_section
  160. );
  161. subsection = section;
  162. if (NULL != section) ptr += section->offset;
  163. }
  164. }
  165. } else if (';' == str[0]) {
  166. // Ignore comments
  167. } else if (isalnum(str[0])) {
  168. // Key-value
  169. // Ignoring return value:
  170. // - Unknown sections or keys are ignored
  171. // - Just ignore malformed files, I guess
  172. parse_key_value(str, subsection, ptr);
  173. }
  174. }
  175. free(line);
  176. return status;
  177. }