18 #ifdef XEMU_SNAPSHOT_SUPPORT
25 #include <sys/types.h>
33 static int snapfd = -1;
34 static const char *framework_ident =
"github.com/lgblgblgb/xemu";
35 static const Uint8 block_framing_id[] = {
'X',
'e',
'm',
'u',
'S',
'n',
'a',
'p' };
36 static const struct xemu_snapshot_definition_st *snapdef = NULL;
37 char xemusnap_error_buffer[XEMUSNAP_ERROR_BUFFER_SIZE * 2];
38 char xemusnap_user_error_buffer[XEMUSNAP_ERROR_BUFFER_SIZE];
39 static char *emu_ident;
40 static int last_sub_block_size_written = -1;
43 void xemusnap_close (
void )
52 void xemusnap_init (
const struct xemu_snapshot_definition_st *def )
59 atexit(xemusnap_close);
63 int xemusnap_read_file (
void *buffer,
size_t size )
69 return XSNAPERR_NODATA;
71 return XSNAPERR_TRUNCATED;
76 int xemusnap_skip_file_bytes ( off_t
size )
78 return (lseek(snapfd,
size, SEEK_CUR) ==
OFF_T_ERROR) ? XSNAPERR_IO : 0;
82 int xemusnap_write_file (
const void *buffer,
size_t size )
87 if (ret == 0 || ret !=
size)
88 return XSNAPERR_NODATA;
93 int xemusnap_read_block_header (
struct xemu_snapshot_block_st *
block )
97 int ret = xemusnap_read_file(buffer, XEMUSNAP_FIXED_HEADER_SIZE);
101 if (memcmp(buffer, block_framing_id, 8))
102 return XSNAPERR_FORMAT;
103 if (P_AS_BE32(buffer + 8) != XEMUSNAP_FRAMING_VERSION)
104 return XSNAPERR_FORMAT;
105 if (P_AS_BE32(buffer + 12))
106 return XSNAPERR_FORMAT;
107 block->block_version = P_AS_BE32(buffer + 16);
108 block->header_size = buffer[20];
109 block->idlen =
block->header_size - XEMUSNAP_FIXED_HEADER_SIZE;
110 if (
block->idlen < 1 ||
block->idlen > XEMUSNAP_MAX_IDENT_LENGTH)
111 return XSNAPERR_FORMAT;
113 ret = xemusnap_read_file(
block->idstr,
block->idlen);
121 int xemusnap_write_block_header (
const char *ident,
Uint32 version )
123 int len = strlen(ident);
124 Uint8 buffer[len + XEMUSNAP_FIXED_HEADER_SIZE];
125 memcpy(buffer, block_framing_id, 8);
126 U32_AS_BE(buffer + 8, XEMUSNAP_FRAMING_VERSION);
127 U32_AS_BE(buffer + 12, 0);
128 U32_AS_BE(buffer + 16, version);
129 buffer[20] = len + XEMUSNAP_FIXED_HEADER_SIZE;
130 memcpy(buffer + XEMUSNAP_FIXED_HEADER_SIZE, ident, len);
131 last_sub_block_size_written = -1;
132 return xemusnap_write_file(buffer, len + XEMUSNAP_FIXED_HEADER_SIZE);
136 int xemusnap_read_be32 (
Uint32 *result )
139 int ret = xemusnap_read_file(buffer, 4);
140 *result = P_AS_BE32(buffer);
145 int xemusnap_skip_sub_blocks (
int num )
149 int ret = xemusnap_read_be32(&
size);
154 ret = xemusnap_skip_file_bytes(
size);
167 if (!
size && !last_sub_block_size_written)
168 FATAL(
"Xemu internal error while saving snapshot: there can be no two zero length sub-blocks together, as one is already signals end of sub-blocks!");
169 last_sub_block_size_written =
size;
170 U32_AS_BE(sizbuf,
size);
171 ret = xemusnap_write_file(sizbuf, 4);
175 ret = xemusnap_write_file(buffer,
size);
183 #define RETURN_XSNAPERR(...) \
185 CHECK_SNPRINTF(snprintf(xemusnap_error_buffer, sizeof xemusnap_error_buffer, __VA_ARGS__), sizeof xemusnap_error_buffer); \
190 static int load_from_open_file (
void )
192 struct xemu_snapshot_block_st
block;
193 const struct xemu_snapshot_definition_st *def;
196 RETURN_XSNAPERR(
"Xemu error: snapshot format is not defined!");
198 block.sub_counter = -1;
200 ret = xemusnap_read_block_header(&
block);
201 if ((ret == XSNAPERR_NODATA) &&
block.counter)
203 block.sub_counter = -1;
206 case XSNAPERR_CALLBACK:
207 RETURN_XSNAPERR(
"Error while loading snapshot block \"%s\" (%d/%d): %s",
block.idstr,
block.counter,
block.sub_counter, xemusnap_user_error_buffer);
208 case XSNAPERR_NODATA:
209 case XSNAPERR_TRUNCATED:
210 RETURN_XSNAPERR(
"Truncated file, unexpected end of file (~ %d/%d)",
block.counter,
block.sub_counter);
211 case XSNAPERR_FORMAT:
212 RETURN_XSNAPERR(
"Snapshot format error, maybe not a snapshot file, or version mismatch");
214 RETURN_XSNAPERR(
"File I/O error while reading snapshot: %s", strerror(errno));
216 block.is_ident = !memcmp(
block.idstr, emu_ident, 6);
218 if (
block.is_ident) {
222 if (strcmp(
block.idstr + 6, emu_ident + 6))
223 RETURN_XSNAPERR(
"Not our snapshot file, format is \"%s\", expected: \"%s\"",
block.idstr + 6, emu_ident + 6);
226 RETURN_XSNAPERR(
"Invalid snapshot file, first block must be the ident block");
229 RETURN_XSNAPERR(
"Unknown block type: \"%s\"",
block.idstr);
230 if (!strcmp(def->idstr,
block.idstr))
235 block.sub_counter = 0;
237 ret = xemusnap_read_be32(&
block.sub_size);
238 if (ret)
goto handle_error;
241 if (
block.is_ident) {
242 ret = xemusnap_skip_sub_blocks(0);
243 if (ret)
goto handle_error;
247 strcpy(xemusnap_user_error_buffer,
"?");
248 ret = def->load(def, &
block);
249 if (ret)
goto handle_error;
251 ret = xemusnap_skip_sub_blocks(0);
252 if (ret)
goto handle_error;
259 FATAL(
"Xemu snapshot load internal error: unknown xemusnap_read_block() answer: %d", ret);
270 strcpy(xemusnap_user_error_buffer,
"?");
271 ret = def->load(def, NULL);
272 if (ret)
goto handle_error;
278 int xemusnap_load (
const char *filename )
280 char pathbuf[PATH_MAX];
283 RETURN_XSNAPERR(
"Cannot open file %s: %s", pathbuf, strerror(errno));
284 if (load_from_open_file()) {
293 static int save_to_open_file (
void )
295 const struct xemu_snapshot_definition_st *def = snapdef;
298 RETURN_XSNAPERR(
"Xemu error: snapshot format is not defined!");
300 ret = xemusnap_write_block_header(emu_ident, 0);
301 if (ret)
goto handle_error;
302 ret = xemusnap_write_sub_block(NULL, 0);
303 if (ret)
goto handle_error;
308 strcpy(xemusnap_user_error_buffer,
"?");
309 ret = def->save(def);
310 if (ret)
goto handle_error;
311 ret = xemusnap_write_sub_block(NULL, 0);
312 if (ret)
goto handle_error;
318 strcpy(xemusnap_user_error_buffer,
"?");
319 ret = def->save(def);
320 if (ret)
goto handle_error;
325 case XSNAPERR_NODATA:
326 case XSNAPERR_TRUNCATED:
327 RETURN_XSNAPERR(
"Cannot write file");
328 case XSNAPERR_FORMAT:
329 RETURN_XSNAPERR(
"Format violation");
331 RETURN_XSNAPERR(
"File I/O error while writing snapshot: %s", strerror(errno));
332 case XSNAPERR_CALLBACK:
333 RETURN_XSNAPERR(
"Error while saving snapshot block \"%s\": %s", def->idstr, xemusnap_user_error_buffer);
335 FATAL(
"Xemu snapshot save internal error: unknown error code: %d", ret);
340 int xemusnap_save (
const char *filename )
342 char pathbuf[PATH_MAX];
344 snapfd =
xemu_open_file(filename, O_WRONLY | O_CREAT | O_TRUNC, NULL, pathbuf);
346 RETURN_XSNAPERR(
"Cannot create file %s: %s", pathbuf, strerror(errno));
347 if (save_to_open_file()) {