aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/adrift/sxfile.cpp
blob: 992b93bb539285a54ce044ed8eee14750e1402b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "glk/adrift/scare.h"
#include "glk/adrift/sxprotos.h"
#include "common/textconsole.h"

namespace Glk {
namespace Adrift {

/*
 * Structure for representing a fake game save/restore file.  Used to catch
 * a serialized gamestate, and return it later on restore.  For now we allow
 * only one of these to exist.
 */
struct sx_scr_stream_t {
	sc_byte *data;
	sc_int length;
	sc_bool is_open;
	sc_bool is_writable;
};
static sx_scr_stream_t scr_serialization_stream = {NULL, 0, FALSE, FALSE};


/*
 * file_open_file_callback()
 * file_read_file_callback()
 * file_write_file_callback()
 * file_close_file_callback()
 *
 * Fake a single gamestate save/restore file.  Used to satisfy requests from
 * the script to serialize and restore a gamestate.  Only one "file" can
 * exist, meaning that a script must restore a saved game before trying to
 * save another.
 */
void *file_open_file_callback(sc_bool is_save) {
	sx_scr_stream_t *const stream = &scr_serialization_stream;

	/* Detect any problems due to scripting limitations. */
	if (stream->is_open) {
		error("File open error: %s",
		                "stream is in use (script limitation)");
		return NULL;
	} else if (is_save && stream->data) {
		error("File open error: %s",
		                "stream has not been read (script limitation)");
		return NULL;
	}

	/*
	 * Set up the stream for the requested mode.  Act as if no such file if
	 * no data available for a read-only open.
	 */
	if (is_save) {
		stream->data = NULL;
		stream->length = 0;
	} else if (!stream->data)
		return NULL;

	stream->is_open = TRUE;
	stream->is_writable = is_save;
	return stream;
}

sc_int file_read_file_callback(void *opaque, sc_byte *buffer, sc_int length) {
	sx_scr_stream_t *const stream = (sx_scr_stream_t *)opaque;
	sc_int bytes;
	assert(opaque && buffer && length > 0);

	/* Detect any problems with the callback parameters. */
	if (stream != &scr_serialization_stream) {
		error("File read error: %s", "stream is invalid");
		return 0;
	} else if (!stream->is_open) {
		error("File read error: %s", "stream is not open");
		return 0;
	} else if (stream->is_writable) {
		error("File read error: %s", "stream is not open for read");
		return 0;
	}

	/* Read and remove the first block of data (or all if less than length). */
	bytes = (stream->length < length) ? stream->length : length;
	memcpy(buffer, stream->data, bytes);
	memmove(stream->data, stream->data + bytes, stream->length - bytes);
	stream->length -= bytes;
	return bytes;
}

void file_write_file_callback(void *opaque, const sc_byte *buffer, sc_int length) {
	sx_scr_stream_t *const stream = (sx_scr_stream_t *)opaque;
	assert(opaque && buffer && length > 0);

	/* Detect any problems with the callback parameters. */
	if (stream != &scr_serialization_stream) {
		error("File write error: %s", "stream is invalid");
		return;
	} else if (!stream->is_open) {
		error("File write error: %s", "stream is not open");
		return;
	} else if (!stream->is_writable) {
		error("File write error: %s", "stream is not open for write");
		return;
	}

	/* Reallocate, then add this block of data to the buffer. */
	stream->data = (sc_byte *)sx_realloc(stream->data, stream->length + length);
	memcpy(stream->data + stream->length, buffer, length);
	stream->length += length;
}

void file_close_file_callback(void *opaque) {
	sx_scr_stream_t *const stream = (sx_scr_stream_t *)opaque;
	assert(opaque);

	/* Detect any problems with the callback parameters. */
	if (stream != &scr_serialization_stream) {
		error("File close error: %s", "stream is invalid");
		return;
	} else if (!stream->is_open) {
		error("File close error: %s", "stream is not open");
		return;
	}

	/*
	 * If closing after a read, free allocations, and return the stream to
	 * its empty state; if after write, leave the data for the later read.
	 */
	if (!stream->is_writable) {
		sx_free(stream->data);
		stream->data = NULL;
		stream->length = 0;
	}
	stream->is_writable = FALSE;
	stream->is_open = FALSE;
}


/*
 * file_cleanup()
 *
 * Free any pending allocations and clean up on completion of a script.
 */
void file_cleanup(void) {
	sx_scr_stream_t *const stream = &scr_serialization_stream;

	sx_free(stream->data);
	stream->data = NULL;
	stream->length = 0;
	stream->is_writable = FALSE;
	stream->is_open = FALSE;
}

} // End of namespace Adrift
} // End of namespace Glk