An emulator, assembler, and disassembler for the Sega Game Gear
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.
 
 
 
 
 

240 lines
6.4 KiB

  1. /* Copyright (C) 2014-2017 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. Released under the terms of the MIT License. See LICENSE for details. */
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <sys/mman.h>
  8. #include <sys/stat.h>
  9. #include <sys/types.h>
  10. #include <unistd.h>
  11. #include "save.h"
  12. #include "mmu.h"
  13. #include "util.h"
  14. static const char *MAGIC = "CRATER GAMEGEAR SAVE FILE\n";
  15. static size_t HEADER_LEN = 64;
  16. /*
  17. Log an error while trying to load the save file.
  18. */
  19. static void log_error(const char *action, const char *path, const char *reason)
  20. {
  21. ERROR("couldn't %s save file '%s': %s", action, path, reason)
  22. }
  23. /*
  24. Log an error in a standard library function.
  25. */
  26. static void log_stdlib_error(const char *action, const char *path,
  27. const char *func)
  28. {
  29. ERROR("couldn't %s save file '%s': %s(): %s",
  30. action, path, func, strerror(errno))
  31. }
  32. /*
  33. Parse the header of a save file, and return whether it is valid.
  34. If the load succeeds and off is not NULL, it will be set to the address
  35. of the first byte of non-header data.
  36. */
  37. static bool parse_save_header(void *ptr, size_t size, size_t *off,
  38. const ROM *rom, const char *path)
  39. {
  40. const char *str = ptr;
  41. if (size < HEADER_LEN) {
  42. log_error("load", path, "too short");
  43. return false;
  44. }
  45. if (strncmp(str, MAGIC, strlen(MAGIC))) {
  46. log_error("load", path,
  47. "invalid header (was this save created by crater?)");
  48. return false;
  49. }
  50. str += strlen(MAGIC);
  51. int version;
  52. uint32_t prodcode;
  53. uint16_t checksum;
  54. if (sscanf(str, "%d:%06d:0x%04hX\n", &version, &prodcode, &checksum) < 3) {
  55. log_error("load", path, "invalid header (failed to parse)");
  56. return false;
  57. }
  58. if (version != 1) {
  59. log_error("load", path, "unknown or unsupported save file version");
  60. return false;
  61. }
  62. if (prodcode != rom->product_code || checksum != rom->expected_checksum) {
  63. log_error("load", path, "save was created for a different ROM");
  64. return false;
  65. }
  66. if (size != HEADER_LEN + MMU_CART_RAM_SIZE) {
  67. log_error("load", path, "cart RAM size is wrong; file may be corrupt");
  68. return false;
  69. }
  70. if (off)
  71. *off = HEADER_LEN;
  72. return true;
  73. }
  74. /*
  75. Initialize a save object, which represents persistent RAM.
  76. The given path will be used to store save data. If it already exists,
  77. it will be loaded here. The return value indicates whether the load was
  78. successful; if it is false, then a file exists in the save location but is
  79. not valid, and this save should not be used. save_free() does not need to
  80. be called in this case.
  81. */
  82. bool save_init(Save *save, const char *path, const ROM *rom)
  83. {
  84. save->path = NULL;
  85. save->rom = rom;
  86. save->map = NULL;
  87. save->mapsize = 0;
  88. save->cart_ram_offset = 0;
  89. save->has_cart_ram = false;
  90. if (!path)
  91. return true;
  92. int fd = open(path, O_RDWR);
  93. if (fd < 0) {
  94. if (errno == ENOENT) {
  95. save->path = cr_strdup(path);
  96. return true;
  97. }
  98. log_stdlib_error("load", path, "open");
  99. return false;
  100. }
  101. struct stat s;
  102. if (fstat(fd, &s) < 0) {
  103. close(fd);
  104. log_stdlib_error("load", path, "fstat");
  105. return false;
  106. }
  107. size_t size = s.st_size;
  108. void *ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  109. close(fd);
  110. if (ptr == MAP_FAILED) {
  111. log_stdlib_error("load", path, "mmap");
  112. return false;
  113. }
  114. size_t offset;
  115. if (!parse_save_header(ptr, size, &offset, rom, path))
  116. return false;
  117. save->map = ptr;
  118. save->mapsize = size;
  119. save->cart_ram_offset = offset;
  120. save->has_cart_ram = true;
  121. return true;
  122. }
  123. /*
  124. Free memory previously allocated by the save.
  125. Will flush the save data to disk if necessary.
  126. */
  127. void save_free(Save *save)
  128. {
  129. free(save->path);
  130. if (save->map) {
  131. msync(save->map, save->mapsize, MS_SYNC);
  132. munmap(save->map, save->mapsize);
  133. }
  134. }
  135. /*
  136. Return whether the save has existing cartridge RAM.
  137. */
  138. bool save_has_cart_ram(const Save *save)
  139. {
  140. return save->has_cart_ram;
  141. }
  142. /*
  143. Return a readable and writable pointer to existing cartridge RAM.
  144. */
  145. uint8_t* save_get_cart_ram(Save *save)
  146. {
  147. if (!save->has_cart_ram)
  148. return NULL;
  149. return ((uint8_t*) save->map) + save->cart_ram_offset;
  150. }
  151. /*
  152. Initialize the save file with fresh cartridge RAM as appropriate.
  153. If the save file is already loaded, return true. Otherwise, the return
  154. value indicates whether the save file creation was successful.
  155. */
  156. bool save_init_cart_ram(Save *save)
  157. {
  158. if (save->has_cart_ram)
  159. return true;
  160. if (!save->path || save->map) // This should not happen normally
  161. return false;
  162. DEBUG("Creating new save file at %s", save->path)
  163. int fd = open(save->path, O_RDWR|O_CREAT|O_EXCL, 0644);
  164. if (fd < 0) {
  165. log_stdlib_error("create", save->path, "open");
  166. return false;
  167. }
  168. // Write header
  169. static const int VERSION = 1;
  170. int header_len = dprintf(fd, "%s%d:%06d:0x%04hX\n", MAGIC, VERSION,
  171. save->rom->product_code, save->rom->expected_checksum);
  172. if (header_len < 0 || (unsigned) header_len > HEADER_LEN) {
  173. if (header_len < 0)
  174. log_stdlib_error("create", save->path, "dprintf");
  175. else
  176. log_error("create", save->path, "header was unexpectedly long");
  177. close(fd);
  178. unlink(save->path);
  179. return false;
  180. }
  181. // Zero out space for the cartridge RAM
  182. char buf[4096];
  183. memset(buf, 0, sizeof(buf));
  184. size_t rem = MMU_CART_RAM_SIZE + (HEADER_LEN - header_len);
  185. while (rem > 0) {
  186. ssize_t chunk = rem > sizeof(buf) ? sizeof(buf) : rem;
  187. if (write(fd, buf, chunk) < chunk) {
  188. log_stdlib_error("create", save->path, "write");
  189. close(fd);
  190. unlink(save->path);
  191. return false;
  192. }
  193. rem -= chunk;
  194. }
  195. // Try to MMAP
  196. size_t size = HEADER_LEN + MMU_CART_RAM_SIZE;
  197. lseek(fd, 0, SEEK_SET);
  198. void *ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  199. close(fd);
  200. if (ptr == MAP_FAILED) {
  201. log_stdlib_error("create", save->path, "mmap");
  202. unlink(save->path);
  203. return false;
  204. }
  205. save->map = ptr;
  206. save->mapsize = size;
  207. save->cart_ram_offset = HEADER_LEN;
  208. save->has_cart_ram = true;
  209. return true;
  210. }