summaryrefslogtreecommitdiff
path: root/apps/plugins/xworld/engine.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/xworld/engine.c')
-rw-r--r--apps/plugins/xworld/engine.c397
1 files changed, 397 insertions, 0 deletions
diff --git a/apps/plugins/xworld/engine.c b/apps/plugins/xworld/engine.c
new file mode 100644
index 0000000000..0d1c1bfa61
--- /dev/null
+++ b/apps/plugins/xworld/engine.c
@@ -0,0 +1,397 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Franklin Wei, Benjamin Brown
11 * Copyright (C) 2004 Gregory Montoir
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23#include "plugin.h"
24#include "engine.h"
25#include "file.h"
26#include "serializer.h"
27#include "sys.h"
28#include "parts.h"
29#include "video_data.h"
30#include "video.h"
31
32void engine_create(struct Engine* e, struct System* stub, const char* dataDir, const char* saveDir)
33{
34 e->sys = stub;
35 e->sys->e = e;
36 e->_dataDir = dataDir;
37 e->_saveDir = saveDir;
38
39 mixer_create(&e->mixer, e->sys);
40
41 /* this needs to be here and not engine_init() to ensure that it is not called on a reset */
42 res_create(&e->res, &e->video, e->sys, dataDir);
43
44 res_allocMemBlock(&e->res);
45
46 video_create(&e->video, &e->res, e->sys);
47
48 player_create(&e->player, &e->mixer, &e->res, e->sys);
49
50 vm_create(&e->vm, &e->mixer, &e->res, &e->player, &e->video, e->sys);
51}
52
53void engine_run(struct Engine* e) {
54
55 while (!e->sys->input.quit) {
56
57 vm_checkThreadRequests(&e->vm);
58
59 vm_inp_updatePlayer(&e->vm);
60
61 engine_processInput(e);
62
63 vm_hostFrame(&e->vm);
64 }
65
66}
67
68/*
69 * this function loads the font in XWORLD_FONT_FILE into video_font
70 */
71
72/*
73 * the file format for the font file is like this:
74 * "XFNT" magic
75 * 8-bit version number
76 * <768 bytes data>
77 * sum of data, XOR'ed by version number repeated 4 times (32-bit)
78 */
79bool engine_loadFontFile(struct Engine* e)
80{
81 uint8_t *old_font = sys_get_buffer(e->sys, sizeof(video_font));
82 rb->memcpy(old_font, video_font, sizeof(video_font));
83
84 File f;
85 file_create(&f, false);
86 if(!file_open(&f, XWORLD_FONT_FILE, e->_dataDir, "rb"))
87 {
88 goto fail;
89 }
90
91 /* read header */
92 char header[5];
93 int ret = file_read(&f, header, sizeof(header));
94 if(ret != sizeof(header) ||
95 header[0] != 'X' ||
96 header[1] != 'F' ||
97 header[2] != 'N' ||
98 header[3] != 'T')
99 {
100 warning("Invalid font file signature, falling back to alternate font");
101 goto fail;
102 }
103
104 if(header[4] != XWORLD_FONT_VERSION)
105 {
106 warning("Font file version mismatch (have=%d, need=%d), falling back to alternate font", header[4], XWORLD_FONT_VERSION);
107 goto fail;
108 }
109
110 uint32_t sum = 0;
111 for(unsigned int i = 0;i<sizeof(video_font);++i)
112 {
113 sum += video_font[i] = file_readByte(&f);
114 }
115
116 uint32_t mask = (header[4] << 24) |
117 (header[4] << 16) |
118 (header[4] << 8 ) |
119 (header[4] << 0 );
120 sum ^= mask;
121 uint32_t check = file_readUint32BE(&f);
122
123 if(check != sum)
124 {
125 warning("Bad font checksum, falling back to alternate font");
126 goto fail;
127 }
128
129 file_close(&f);
130 return true;
131
132fail:
133 file_close(&f);
134
135 memcpy(video_font, old_font, sizeof(video_font));
136 return false;
137}
138
139/*
140 * this function loads the string table in STRING_TABLE_FILE into
141 * video_stringsTableEng
142 */
143
144/*
145 * the file format for the string table is like this:
146 * "XWST" magic
147 * 8-bit version number
148 * 8-bit title length
149 * <title data (0-255 bytes, _NO NULL_)
150 * 16-bit number of string entries (currently limited to 255)
151 * entry format:
152 struct file_entry_t
153 {
154 uint16_t id;
155 uint16_t len; - length of str
156 char* str; - NO NULL
157 }
158*/
159bool engine_loadStringTable(struct Engine* e)
160{
161 File f;
162 file_create(&f, false);
163 if(!file_open(&f, STRING_TABLE_FILE, e->_dataDir, "rb"))
164 {
165 /*
166 * this gives verbose warnings while loadFontFile doesn't because the font looks similar
167 * enough to pass for the "original", but the strings don't
168 */
169 warning("Unable to find string table, falling back to alternate strings");
170 goto fail;
171 }
172
173 /* read header */
174
175 char header[5];
176 int ret = file_read(&f, header, sizeof(header));
177 if(ret != sizeof(header) ||
178 header[0] != 'X' ||
179 header[1] != 'W' ||
180 header[2] != 'S' ||
181 header[3] != 'T')
182 {
183 warning("Invalid string table signature, falling back to alternate strings");
184 goto fail;
185 }
186
187 if(header[4] != STRING_TABLE_VERSION)
188 {
189 warning("String table version mismatch (have=%d, need=%d), falling back to alternate strings", header[4], STRING_TABLE_VERSION);
190 goto fail;
191 }
192
193 /* read title */
194
195 uint8_t title_length = file_readByte(&f);
196 char *title_buf;
197 if(title_length)
198 {
199 title_buf = sys_get_buffer(e->sys, (int32_t)title_length + 1); /* make room for the NULL */
200 ret = file_read(&f, title_buf, title_length);
201 if(ret != title_length)
202 {
203 warning("Title shorter than expected, falling back to alternate strings");
204 goto fail;
205 }
206 }
207 else
208 {
209 title_buf = "UNKNOWN";
210 }
211
212 /* read entries */
213
214 uint16_t num_entries = file_readUint16BE(&f);
215 for(unsigned int i = 0; i < num_entries && i < ARRAYLEN(video_stringsTableEng); ++i)
216 {
217 video_stringsTableEng[i].id = file_readUint16BE(&f);
218 uint16_t len = file_readUint16BE(&f);
219
220 if(file_ioErr(&f))
221 {
222 warning("Unexpected EOF in while parsing entry %d, falling back to alternate strings", i);
223 goto fail;
224 }
225
226 video_stringsTableEng[i].str = sys_get_buffer(e->sys, (int32_t)len + 1);
227
228 ret = file_read(&f, video_stringsTableEng[i].str, len);
229 if(ret != len)
230 {
231 warning("Entry %d too short, falling back to alternate strings", i);
232 goto fail;
233 }
234 }
235
236 file_close(&f);
237 rb->splashf(HZ, "String table '%s' loaded", title_buf);
238 return true;
239fail:
240 file_close(&f);
241 return false;
242}
243
244void engine_init(struct Engine* e) {
245 sys_init(e->sys, "Out Of This World");
246
247 res_readEntries(&e->res);
248
249 engine_loadStringTable(e);
250
251 engine_loadFontFile(e);
252
253 video_init(&e->video);
254
255 vm_init(&e->vm);
256
257 mixer_init(&e->mixer);
258
259 player_init(&e->player);
260
261 /* Init virtual machine, legacy way */
262 /* vm_initForPart(&e->vm, GAME_PART_FIRST); // This game part is the protection screen */
263
264 /* Try to cheat here. You can jump anywhere but the VM crashes afterward. */
265 /* Starting somewhere is probably not enough, the variables and calls return are probably missing. */
266 /* vm_initForPart(&e->vm, GAME_PART2); Skip protection screen and go directly to intro */
267 /* vm_initForPart(&e->vm, GAME_PART3); CRASH */
268 /* vm_initForPart(&e->vm, GAME_PART4); Start directly in jail but then crash */
269 /* vm->initForPart(&e->vm, GAME_PART5); CRASH */
270 /* vm->initForPart(GAME_PART6); Start in the battlechar but CRASH afteward */
271 /* vm->initForPart(GAME_PART7); CRASH */
272 /* vm->initForPart(GAME_PART8); CRASH */
273 /* vm->initForPart(GAME_PART9); Green screen not doing anything */
274}
275
276void engine_finish(struct Engine* e) {
277 player_free(&e->player);
278 mixer_free(&e->mixer);
279 res_freeMemBlock(&e->res);
280}
281
282void engine_processInput(struct Engine* e) {
283 if (e->sys->input.load) {
284 engine_loadGameState(e, e->_stateSlot);
285 e->sys->input.load = false;
286 }
287 if (e->sys->input.save) {
288 engine_saveGameState(e, e->_stateSlot, "quicksave");
289 e->sys->input.save = false;
290 }
291 if (e->sys->input.fastMode) {
292 e->vm._fastMode = !&e->vm._fastMode;
293 e->sys->input.fastMode = false;
294 }
295 if (e->sys->input.stateSlot != 0) {
296 int8_t slot = e->_stateSlot + e->sys->input.stateSlot;
297 if (slot >= 0 && slot < MAX_SAVE_SLOTS) {
298 e->_stateSlot = slot;
299 debug(DBG_INFO, "Current game state slot is %d", e->_stateSlot);
300 }
301 e->sys->input.stateSlot = 0;
302 }
303}
304
305void engine_makeGameStateName(struct Engine* e, uint8_t slot, char *buf, int sz) {
306 (void) e;
307 rb->snprintf(buf, sz, "xworld_save.s%02d", slot);
308}
309
310void engine_saveGameState(struct Engine* e, uint8_t slot, const char *desc) {
311 char stateFile[20];
312 /* sizeof(char) is guaranteed to be 1 */
313 engine_makeGameStateName(e, slot, stateFile, sizeof(stateFile));
314 File f;
315 file_create(&f, false);
316 if (!file_open(&f, stateFile, e->_saveDir, "wb")) {
317 warning("Unable to save state file '%s'", stateFile);
318 } else {
319 /* header */
320 file_writeUint32BE(&f, SAVE_MAGIC);
321 file_writeUint16BE(&f, CUR_VER);
322 file_writeUint16BE(&f, 0);
323 char hdrdesc[32];
324 strncpy(hdrdesc, desc, sizeof(hdrdesc) - 1);
325 file_write(&f, hdrdesc, sizeof(hdrdesc));
326 /* contents */
327 struct Serializer s;
328 ser_create(&s, &f, SM_SAVE, e->res._memPtrStart, CUR_VER);
329 vm_saveOrLoad(&e->vm, &s);
330 res_saveOrLoad(&e->res, &s);
331 video_saveOrLoad(&e->video, &s);
332 player_saveOrLoad(&e->player, &s);
333 mixer_saveOrLoad(&e->mixer, &s);
334 if (file_ioErr(&f)) {
335 warning("I/O error when saving game state");
336 } else {
337 debug(DBG_INFO, "Saved state to slot %d", e->_stateSlot);
338 }
339 }
340 file_close(&f);
341}
342
343bool engine_loadGameState(struct Engine* e, uint8_t slot) {
344 char stateFile[20];
345 engine_makeGameStateName(e, slot, stateFile, 20);
346 File f;
347 file_create(&f, false);
348 if (!file_open(&f, stateFile, e->_saveDir, "rb")) {
349 debug(DBG_ENG, "Unable to open state file '%s'", stateFile);
350 goto fail;
351 } else {
352 uint32_t id = file_readUint32BE(&f);
353 if (id != SAVE_MAGIC) {
354 debug(DBG_ENG, "Bad savegame format");
355 goto fail;
356 } else {
357 /* mute */
358 player_stop(&e->player);
359 mixer_stopAll(&e->mixer);
360 /* header */
361 uint16_t ver = file_readUint16BE(&f);
362 file_readUint16BE(&f);
363 char hdrdesc[32];
364 file_read(&f, hdrdesc, sizeof(hdrdesc));
365 /* contents */
366 struct Serializer s;
367 ser_create(&s, &f, SM_LOAD, e->res._memPtrStart, ver);
368 vm_saveOrLoad(&e->vm, &s);
369 res_saveOrLoad(&e->res, &s);
370 video_saveOrLoad(&e->video, &s);
371 player_saveOrLoad(&e->player, &s);
372 mixer_saveOrLoad(&e->mixer, &s);
373 }
374 if (file_ioErr(&f)) {
375 debug(DBG_ENG, "I/O error when loading game state");
376 goto fail;
377 } else {
378 debug(DBG_INFO, "Loaded state from slot %d", e->_stateSlot);
379 }
380 }
381 file_close(&f);
382 return true;
383fail:
384 file_close(&f);
385 return false;
386}
387
388void engine_deleteGameState(struct Engine* e, uint8_t slot) {
389 char stateFile[20];
390 engine_makeGameStateName(e, slot, stateFile, 20);
391 file_remove(stateFile, e->_saveDir);
392}
393
394const char* engine_getDataDir(struct Engine* e)
395{
396 return e->_dataDir;
397}