summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranklin Wei <frankhwei536@gmail.com>2014-10-13 21:00:47 -0400
committerMichael Giacomelli <giac2000@hotmail.com>2014-12-23 23:48:12 +0100
commit33cb13dee5a527ac445ea1b13d42723e4eb3e3b0 (patch)
tree3ce36ea21b53377b900049143e77e74b77ca1b0d
parentb681e932a9da797249ddc0e4ccab7ed7cf50fd41 (diff)
downloadrockbox-33cb13dee5a527ac445ea1b13d42723e4eb3e3b0.tar.gz
rockbox-33cb13dee5a527ac445ea1b13d42723e4eb3e3b0.zip
Xworld - Another World interpreter for Rockbox
Co-conspirators: Franklin Wei, Benjamin Brown -------------------------------------------------------------------- This work is based on: - Fabien Sanglard's "Fabother World" based on - Piotr Padkowski's newRaw interpreter which was based on - Gregory Montoir's reverse engineering of - Eric Chahi's assembly code -------------------------------------------------------------------- Progress: * The plugin runs pretty nicely (with sound!) on most color targets * Keymaps for color LCD targets are complete * The manual entry is finished * Grayscale/monochrome support is NOT PLANNED - the game looks horrible in grayscale! :p -------------------------------------------------------------------- Notes: * The original game strings were built-in to the executable, and were copyrighted and could not be used. * This port ships with an alternate set of strings by default, but can load the "official" strings from a file at runtime. -------------------------------------------------------------------- To be done (in descending order of importance): * vertical stride compatibility <30% done> * optimization <10% done> Change-Id: I3155b0d97c2ac470cb8a2040f40d4139ddcebfa5 Reviewed-on: http://gerrit.rockbox.org/1077 Reviewed-by: Michael Giacomelli <giac2000@hotmail.com>
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SUBDIRS6
-rw-r--r--apps/plugins/xworld/README86
-rw-r--r--apps/plugins/xworld/README.newraw4
-rw-r--r--apps/plugins/xworld/README.rockbox51
-rw-r--r--apps/plugins/xworld/SOURCES15
-rw-r--r--apps/plugins/xworld/awendian.h51
-rw-r--r--apps/plugins/xworld/bank.c153
-rw-r--r--apps/plugins/xworld/bank.h55
-rw-r--r--apps/plugins/xworld/engine.c397
-rw-r--r--apps/plugins/xworld/engine.h70
-rw-r--r--apps/plugins/xworld/file.c172
-rw-r--r--apps/plugins/xworld/file.h51
-rw-r--r--apps/plugins/xworld/intern.c34
-rw-r--r--apps/plugins/xworld/intern.h44
-rw-r--r--apps/plugins/xworld/keymaps.h183
-rw-r--r--apps/plugins/xworld/mixer.c199
-rw-r--r--apps/plugins/xworld/mixer.h69
-rw-r--r--apps/plugins/xworld/parts.c56
-rw-r--r--apps/plugins/xworld/parts.h57
-rw-r--r--apps/plugins/xworld/resource.c443
-rw-r--r--apps/plugins/xworld/resource.h107
-rw-r--r--apps/plugins/xworld/serializer.c141
-rw-r--r--apps/plugins/xworld/serializer.h84
-rw-r--r--apps/plugins/xworld/sfxplayer.c247
-rw-r--r--apps/plugins/xworld/sfxplayer.h88
-rw-r--r--apps/plugins/xworld/sys.c942
-rw-r--r--apps/plugins/xworld/sys.h146
-rw-r--r--apps/plugins/xworld/util.c82
-rw-r--r--apps/plugins/xworld/util.h59
-rw-r--r--apps/plugins/xworld/video.c1141
-rw-r--r--apps/plugins/xworld/video.h127
-rw-r--r--apps/plugins/xworld/video_data.c271
-rw-r--r--apps/plugins/xworld/video_data.h29
-rw-r--r--apps/plugins/xworld/vm.c763
-rw-r--r--apps/plugins/xworld/vm.h184
-rw-r--r--apps/plugins/xworld/xworld.c244
-rw-r--r--apps/plugins/xworld/xworld.make27
-rw-r--r--docs/CREDITS2
-rw-r--r--manual/plugins/main.tex2
-rw-r--r--manual/plugins/xworld.tex81
41 files changed, 6964 insertions, 0 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 609f359e7d..fd7a49af8f 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -134,4 +134,5 @@ wavrecord,apps
134wavview,viewers 134wavview,viewers
135wormlet,games 135wormlet,games
136xobox,games 136xobox,games
137xworld,games
137zxbox,viewers 138zxbox,viewers
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index 8e653983b7..4b4015be08 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -12,6 +12,12 @@ clock
12/* For all targets with a bitmap display */ 12/* For all targets with a bitmap display */
13#ifdef HAVE_LCD_BITMAP 13#ifdef HAVE_LCD_BITMAP
14 14
15/* XWorld only supports color horizontal stride LCDs /for now/ ;) */
16#if (defined(HAVE_LCD_COLOR) && \
17 (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
18xworld
19#endif
20
15#if (CONFIG_KEYPAD != ONDIO_PAD) /* not enough buttons */ \ 21#if (CONFIG_KEYPAD != ONDIO_PAD) /* not enough buttons */ \
16 && (CONFIG_KEYPAD != SANSA_M200_PAD) /* not enough buttons */ \ 22 && (CONFIG_KEYPAD != SANSA_M200_PAD) /* not enough buttons */ \
17 && (CONFIG_KEYPAD != HM60X_PAD) /* not enough buttons */ \ 23 && (CONFIG_KEYPAD != HM60X_PAD) /* not enough buttons */ \
diff --git a/apps/plugins/xworld/README b/apps/plugins/xworld/README
new file mode 100644
index 0000000000..3a07b1ba94
--- /dev/null
+++ b/apps/plugins/xworld/README
@@ -0,0 +1,86 @@
1This is the original readme from the "Fabother World" sources; the Rockbox port's
2readme is in README.rockbox.
3
4Franklin Wei
5
6=================================================================================
7
8This is "Fabother World": an Another World (Out Of This World in North America) interpreter codebase. This work is based on:
9
10- Piotr Padkowski's newRaw interpreter which was based on
11- Gregory Montoir's reverse engineering of
12- Eric Chahi's assembly code.
13
14I cleaned up a lot of the code, removing cryptic hexadecimal notation
15with meaningful macros name. I also cleanup a lot of the code so it has a
16C/C++ philosophy instead of an assembly structure.
17
18I also created a Visual Studio 2010 project.
19
20TODO:
21
22Create a MacOS X project.
23Add a different rendering path OpenGL support.
24
25Fabien Sanglard
26
27
28raw README
29Release version: 0.1.1 (May 15 2004)
30-------------------------------------------------------------------------------
31
32About:
33------
34
35raw is a re-implementation of the engine used in the game Another World. This
36game, released under the name Out Of This World in non-European countries, was
37written by Eric Chahi at the beginning of the '90s. More information can be
38found here : http://www.mobygames.com/game/sheet/p,2/gameId,564/.
39
40Please be aware that, currently, this implementation may contains bugs and
41non-implemented features that make it impossible to finish the game.
42
43Supported Versions:
44-------------------
45
46Currently, only the english PC DOS version is supported ("Out of this World").
47
48Compiling:
49----------
50
51Tweak the Makefile if needed and type make (only gcc3 has been tested so far).
52The SDL and zlib libraries are required.
53
54Running:
55--------
56
57You will need the original files, here is the required list :
58 BANK*
59 MEMLIST.BIN
60
61To start the game, you can either :
62- put the game's datafiles in the same directory as the executable
63- use the --datapath command line option to specify the datafiles directory
64
65Here are the various in game hotkeys :
66 Arrow Keys allow you to move Lester
67 Enter/Space allow you run/shoot with your gun
68 C allow to enter a code to jump at a specific level
69 P pause the game
70 Alt X exit the game
71 Ctrl S save game state
72 Ctrl L load game state
73 Ctrl + and - change game state slot
74 Ctrl F toggle fast mode
75 Alt Enter toggle windowed/fullscreen mode
76 Alt + and - change scaler factor
77
78Credits:
79--------
80
81Eric Chahi, obviously, for making this great game.
82
83Contact:
84--------
85
86Gregory Montoir, cyx@users.sourceforge.net
diff --git a/apps/plugins/xworld/README.newraw b/apps/plugins/xworld/README.newraw
new file mode 100644
index 0000000000..f5163c978c
--- /dev/null
+++ b/apps/plugins/xworld/README.newraw
@@ -0,0 +1,4 @@
1
2Changes:
3 Added 2x and 3x high quality scalers (ripped from Reminescence)
4
diff --git a/apps/plugins/xworld/README.rockbox b/apps/plugins/xworld/README.rockbox
new file mode 100644
index 0000000000..2ea63faa17
--- /dev/null
+++ b/apps/plugins/xworld/README.rockbox
@@ -0,0 +1,51 @@
1This is the Rockbox port of Fabien Sanglard's "Fabother World", an Another World
2interpreter.
3
4Porting process:
5----------------
6
7The original code abstracted most of the platform-specific tasks, such as file I/O,
8sound, input, and video. However, the original code was in C++, so it was converted
9to C class-by-class. The conversion was attempted to be as conservative as possible,
10so little code was rewritten during the conversion process.
11
12Notes:
13------
14
15 - Optimization is badly needed.
16 - Vertical stride support is almost there.
17 - The game looks terrible in B+W/grayscale. This was the primary reason no attempt
18 was made to support these targets.
19 - The game does not run well on devices that have an LCD with a vertical stride.
20 - The M:Robe 500 is the only color device that meets this criterion, so it is
21 disabled by default.
22 - Sound doesn't sound 100% like the PC version. Perhaps the frequency reported to
23 the mixer is incorrect, or the buffer size is too big so that short sounds are
24 being missed.
25
26To do (in no particular order):
27-------------------------------
28
29 - Support vertical stride LCD's
30 - Support grayscale/monochrome LCD's
31 - Optimize
32
33Credits:
34--------
35
36**************************************
37**************************************
38********** !!!ERIC CHAHI!!! **********
39**************************************
40**************************************
41<the original author of Another World>
42
43Gregory Montoir
44Piotr Padkowski
45Fabien Sanglard
46
47Rockbox porters:
48----------------
49
50Franklin Wei
51Benjamin Brown
diff --git a/apps/plugins/xworld/SOURCES b/apps/plugins/xworld/SOURCES
new file mode 100644
index 0000000000..98b5f14fb8
--- /dev/null
+++ b/apps/plugins/xworld/SOURCES
@@ -0,0 +1,15 @@
1bank.c
2engine.c
3file.c
4intern.c
5mixer.c
6parts.c
7resource.c
8serializer.c
9sfxplayer.c
10sys.c
11util.c
12video.c
13video_data.c
14vm.c
15xworld.c
diff --git a/apps/plugins/xworld/awendian.h b/apps/plugins/xworld/awendian.h
new file mode 100644
index 0000000000..863b19bb47
--- /dev/null
+++ b/apps/plugins/xworld/awendian.h
@@ -0,0 +1,51 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Franklin Wei, Benjamin Brown
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ***************************************************************************/
21
22#ifndef __SYS_H__
23#define __SYS_H__
24
25#include "rbendian.h"
26#include "stdint.h"
27
28
29#ifdef ROCKBOX_LITTLE_ENDIAN
30#define SYS_LITTLE_ENDIAN
31#else
32#define SYS_BIG_ENDIAN
33#endif
34
35
36#if defined SYS_LITTLE_ENDIAN
37#define READ_BE_UINT16(p) ((((const uint8_t*)p)[0] << 8) | ((const uint8_t*)p)[1])
38#define READ_BE_UINT32(p) ((((const uint8_t*)p)[0] << 24) | (((const uint8_t*)p)[1] << 16) | (((const uint8_t*)p)[2] << 8) | ((const uint8_t*)p)[3])
39
40#elif defined SYS_BIG_ENDIAN
41
42#define READ_BE_UINT16(p) (*(const uint16_t*)p)
43#define READ_BE_UINT32(p) (*(const uint32_t*)p)
44
45#else
46
47#error No endianness defined
48
49#endif
50
51#endif
diff --git a/apps/plugins/xworld/bank.c b/apps/plugins/xworld/bank.c
new file mode 100644
index 0000000000..e2cfbd4680
--- /dev/null
+++ b/apps/plugins/xworld/bank.c
@@ -0,0 +1,153 @@
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 "bank.h"
25#include "file.h"
26#include "resource.h"
27
28void bank_create(struct Bank* b, const char *dataDir)
29{
30 b->_dataDir = dataDir;
31}
32
33bool bank_read(struct Bank* b, const struct MemEntry *me, uint8_t *buf) {
34
35 bool ret = false;
36 char bankName[10];
37 rb->snprintf(bankName, 10, "bank%02x", me->bankId);
38 File f;
39 file_create(&f, false);
40 if (!file_open(&f, bankName, b->_dataDir, "rb"))
41 error("bank_read() unable to open '%s' in dir '%s'", bankName, b->_dataDir);
42
43 file_seek(&f, me->bankOffset);
44
45 /* Depending if the resource is packed or not we */
46 /* can read directly or unpack it. */
47 if (me->packedSize == me->size) {
48 file_read(&f, buf, me->packedSize);
49 ret = true;
50 } else {
51 file_read(&f, buf, me->packedSize);
52 b->_startBuf = buf;
53 b->_iBuf = buf + me->packedSize - 4;
54 ret = bank_unpack(b);
55 }
56 file_close(&f);
57 return ret;
58}
59
60void bank_decUnk1(struct Bank* b, uint8_t numChunks, uint8_t addCount) {
61 uint16_t count = bank_getCode(b, numChunks) + addCount + 1;
62 debug(DBG_BANK, "bank_decUnk1(%d, %d) count=%d", numChunks, addCount, count);
63 b->_unpCtx.datasize -= count;
64 while (count--) {
65 assert(b->_oBuf >= b->_iBuf && b->_oBuf >= b->_startBuf);
66 *b->_oBuf = (uint8_t)bank_getCode(b, 8);
67 --b->_oBuf;
68 }
69}
70
71/*
72 Note from fab: This look like run-length encoding.
73*/
74void bank_decUnk2(struct Bank* b, uint8_t numChunks) {
75 uint16_t i = bank_getCode(b, numChunks);
76 uint16_t count = b->_unpCtx.size + 1;
77 debug(DBG_BANK, "bank_decUnk2(%d) i=%d count=%d", numChunks, i, count);
78 b->_unpCtx.datasize -= count;
79 while (count--) {
80 assert(b->_oBuf >= b->_iBuf && b->_oBuf >= b->_startBuf);
81 *b->_oBuf = *(b->_oBuf + i);
82 --b->_oBuf;
83 }
84}
85
86/*
87 Most resource in the banks are compacted.
88*/
89bool bank_unpack(struct Bank* b) {
90 b->_unpCtx.size = 0;
91 b->_unpCtx.datasize = READ_BE_UINT32(b->_iBuf);
92 b->_iBuf -= 4;
93 b->_oBuf = b->_startBuf + b->_unpCtx.datasize - 1;
94 b->_unpCtx.crc = READ_BE_UINT32(b->_iBuf);
95 b->_iBuf -= 4;
96 b->_unpCtx.chk = READ_BE_UINT32(b->_iBuf);
97 b->_iBuf -= 4;
98 b->_unpCtx.crc ^= b->_unpCtx.chk;
99 do {
100 if (!bank_nextChunk(b)) {
101 b->_unpCtx.size = 1;
102 if (!bank_nextChunk(b)) {
103 bank_decUnk1(b, 3, 0);
104 } else {
105 bank_decUnk2(b, 8);
106 }
107 } else {
108 uint16_t c = bank_getCode(b, 2);
109 if (c == 3) {
110 bank_decUnk1(b, 8, 8);
111 } else {
112 if (c < 2) {
113 b->_unpCtx.size = c + 2;
114 bank_decUnk2(b, c + 9);
115 } else {
116 b->_unpCtx.size = bank_getCode(b, 8);
117 bank_decUnk2(b, 12);
118 }
119 }
120 }
121 } while (b->_unpCtx.datasize > 0);
122 return (b->_unpCtx.crc == 0);
123}
124
125uint16_t bank_getCode(struct Bank* b, uint8_t numChunks) {
126 uint16_t c = 0;
127 while (numChunks--) {
128 c <<= 1;
129 if (bank_nextChunk(b)) {
130 c |= 1;
131 }
132 }
133 return c;
134}
135
136bool bank_nextChunk(struct Bank* b) {
137 bool CF = bank_rcr(b, false);
138 if (b->_unpCtx.chk == 0) {
139 assert(b->_iBuf >= b->_startBuf);
140 b->_unpCtx.chk = READ_BE_UINT32(b->_iBuf);
141 b->_iBuf -= 4;
142 b->_unpCtx.crc ^= b->_unpCtx.chk;
143 CF = bank_rcr(b, true);
144 }
145 return CF;
146}
147
148bool bank_rcr(struct Bank* b, bool CF) {
149 bool rCF = (b->_unpCtx.chk & 1);
150 b->_unpCtx.chk >>= 1;
151 if (CF) b->_unpCtx.chk |= 0x80000000;
152 return rCF;
153}
diff --git a/apps/plugins/xworld/bank.h b/apps/plugins/xworld/bank.h
new file mode 100644
index 0000000000..6f280c5271
--- /dev/null
+++ b/apps/plugins/xworld/bank.h
@@ -0,0 +1,55 @@
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#ifndef __BANK_H__
24#define __BANK_H__
25
26#include "intern.h"
27
28struct MemEntry;
29
30struct UnpackContext {
31 uint16_t size;
32 uint32_t crc;
33 uint32_t chk;
34 int32_t datasize;
35};
36
37struct Bank
38{
39 struct UnpackContext _unpCtx;
40 const char *_dataDir;
41 uint8_t *_iBuf, *_oBuf, *_startBuf;
42};
43
44/* needs allocated memory */
45void bank_create(struct Bank*, const char *dataDir);
46
47bool bank_read(struct Bank*, const struct MemEntry *me, uint8_t *buf);
48void bank_decUnk1(struct Bank*, uint8_t numChunks, uint8_t addCount);
49void bank_decUnk2(struct Bank*, uint8_t numChunks);
50bool bank_unpack(struct Bank*);
51uint16_t bank_getCode(struct Bank*, uint8_t numChunks);
52bool bank_nextChunk(struct Bank*);
53bool bank_rcr(struct Bank*, bool CF);
54
55#endif
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}
diff --git a/apps/plugins/xworld/engine.h b/apps/plugins/xworld/engine.h
new file mode 100644
index 0000000000..a0ad2b469e
--- /dev/null
+++ b/apps/plugins/xworld/engine.h
@@ -0,0 +1,70 @@
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#ifndef __ENGINE_H__
24#define __ENGINE_H__
25
26#include "intern.h"
27#include "vm.h"
28#include "mixer.h"
29#include "sfxplayer.h"
30#include "resource.h"
31#include "video.h"
32#include "sys.h"
33
34#define STRING_TABLE_FILE "xworld.strings" /* this is relative to dataDir */
35#define STRING_TABLE_VERSION 0x03
36
37#define XWORLD_FONT_FILE "xworld.font" /* relative to dataDir */
38#define XWORLD_FONT_VERSION 0x01
39
40struct System;
41
42#define MAX_SAVE_SLOTS 1
43#define SAVE_MAGIC 0x42424657
44struct Engine {
45 struct System *sys;
46 struct VirtualMachine vm;
47 struct Mixer mixer;
48 struct Resource res;
49 struct SfxPlayer player;
50 struct Video video;
51 const char *_dataDir, *_saveDir;
52 uint8_t _stateSlot;
53};
54
55void engine_create(struct Engine* e, struct System* stub, const char* dataDir, const char* saveDir);
56
57void engine_run(struct Engine*);
58void engine_init(struct Engine*);
59void engine_finish(struct Engine*);
60void engine_processInput(struct Engine*);
61
62bool engine_loadFontFile(struct Engine*);
63bool engine_loadStringTable(struct Engine*);
64
65void engine_makeGameStateName(struct Engine*, uint8_t slot, char *buf, int sz);
66void engine_saveGameState(struct Engine*, uint8_t slot, const char *desc);
67bool engine_loadGameState(struct Engine*, uint8_t slot);
68void engine_deleteGameState(struct Engine*, uint8_t slot);
69const char* engine_getDataDir(struct Engine*);
70#endif
diff --git a/apps/plugins/xworld/file.c b/apps/plugins/xworld/file.c
new file mode 100644
index 0000000000..8353544ef5
--- /dev/null
+++ b/apps/plugins/xworld/file.c
@@ -0,0 +1,172 @@
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 "file.h"
25
26void file_create(struct File* f, bool gzipped) {
27 f->gzipped = gzipped;
28 f->fd = -1;
29 f->ioErr = false;
30}
31
32bool file_open(struct File* f, const char *filename, const char *directory, const char *mode) {
33 char buf[512];
34 rb->snprintf(buf, 512, "%s/%s", directory, filename);
35 char *p = buf + rb->strlen(directory) + 1;
36 string_lower(p);
37
38 int flags = 0;
39 for(int i = 0; mode[i]; ++i)
40 {
41 switch(mode[i])
42 {
43 case 'w':
44 flags |= O_WRONLY | O_CREAT | O_TRUNC;
45 break;
46 case 'r':
47 flags |= O_RDONLY;
48 break;
49 default:
50 break;
51 }
52 }
53 f->fd = -1;
54 debug(DBG_FILE, "trying %s first", buf);
55 f->fd = rb->open(buf, flags, 0666);
56 if (f->fd < 0) { // let's try uppercase
57 string_upper(p);
58 debug(DBG_FILE, "now trying %s uppercase", buf);
59 f->fd = rb->open(buf, flags, 0666);
60 }
61 if(f->fd > 0)
62 return true;
63 else
64 return false;
65}
66
67void file_close(struct File* f) {
68 if(f->gzipped)
69 {
70 }
71 else
72 {
73 rb->close(f->fd);
74 }
75}
76
77bool file_ioErr(struct File* f) {
78 return f->ioErr;
79}
80
81void file_seek(struct File* f, int32_t off) {
82 if(f->gzipped)
83 {
84 }
85 else
86 {
87 rb->lseek(f->fd, off, SEEK_SET);
88 }
89}
90int file_read(struct File* f, void *ptr, uint32_t size) {
91 if(f->gzipped)
92 {
93 return -1;
94 }
95 else
96 {
97 unsigned int rc = rb->read(f->fd, ptr, size);
98 if(rc != size)
99 f->ioErr = true;
100 return rc;
101 }
102}
103uint8_t file_readByte(struct File* f) {
104 uint8_t b;
105 if(f->gzipped)
106 {
107 b = 0xff;
108 }
109 else
110 {
111 if(rb->read(f->fd, &b, 1) != 1)
112 {
113 f->ioErr = true;
114 debug(DBG_FILE, "file read failed");
115 }
116 }
117 return b;
118}
119
120uint16_t file_readUint16BE(struct File* f) {
121 uint8_t hi = file_readByte(f);
122 uint8_t lo = file_readByte(f);
123 return (hi << 8) | lo;
124}
125
126uint32_t file_readUint32BE(struct File* f) {
127 uint16_t hi = file_readUint16BE(f);
128 uint16_t lo = file_readUint16BE(f);
129 return (hi << 16) | lo;
130}
131
132int file_write(struct File* f, void *ptr, uint32_t size) {
133 if(f->gzipped)
134 {
135 return 0;
136 }
137 else
138 {
139 return rb->write(f->fd, ptr, size);
140 }
141}
142
143void file_writeByte(struct File* f, uint8_t b) {
144 file_write(f, &b, 1);
145}
146
147void file_writeUint16BE(struct File* f, uint16_t n) {
148 file_writeByte(f, n >> 8);
149 file_writeByte(f, n & 0xFF);
150}
151
152void file_writeUint32BE(struct File* f, uint32_t n) {
153 file_writeUint16BE(f, n >> 16);
154 file_writeUint16BE(f, n & 0xFFFF);
155}
156
157void file_remove(const char* filename, const char* directory)
158{
159 char buf[512];
160 rb->snprintf(buf, 512, "%s/%s", directory, filename);
161 char *p = buf + rb->strlen(directory) + 1;
162 string_lower(p);
163 if(rb->file_exists(buf))
164 {
165 rb->remove(buf);
166 }
167 else
168 {
169 string_upper(p);
170 rb->remove(buf);
171 }
172}
diff --git a/apps/plugins/xworld/file.h b/apps/plugins/xworld/file.h
new file mode 100644
index 0000000000..b24cdaf19e
--- /dev/null
+++ b/apps/plugins/xworld/file.h
@@ -0,0 +1,51 @@
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#ifndef __FILE_H__
24#define __FILE_H__
25
26#include "intern.h"
27
28typedef struct File {
29 int fd;
30 bool gzipped;
31 bool ioErr;
32} File;
33
34void file_create(struct File*, bool gzipped);
35
36bool file_open(struct File*, const char *filename, const char *directory, const char *mode);
37void file_close(struct File*);
38bool file_ioErr(struct File*);
39void file_seek(struct File*, int32_t off);
40int file_read(struct File*, void *ptr, uint32_t size);
41uint8_t file_readByte(struct File*);
42uint16_t file_readUint16BE(struct File*);
43uint32_t file_readUint32BE(struct File*);
44int file_write(struct File*, void *ptr, uint32_t size);
45void file_writeByte(struct File*, uint8_t b);
46void file_writeUint16BE(struct File*, uint16_t n);
47void file_writeUint32BE(struct File*, uint32_t n);
48
49void file_remove(const char* filename, const char* directory);
50
51#endif
diff --git a/apps/plugins/xworld/intern.c b/apps/plugins/xworld/intern.c
new file mode 100644
index 0000000000..0d58100f37
--- /dev/null
+++ b/apps/plugins/xworld/intern.c
@@ -0,0 +1,34 @@
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 "intern.h"
24#include "awendian.h"
25
26uint8_t ICODE_ATTR scriptPtr_fetchByte(struct Ptr* p) {
27 return *p->pc++;
28}
29
30uint16_t ICODE_ATTR scriptPtr_fetchWord(struct Ptr* p) {
31 uint16_t i = READ_BE_UINT16(p->pc);
32 p->pc += 2;
33 return i;
34}
diff --git a/apps/plugins/xworld/intern.h b/apps/plugins/xworld/intern.h
new file mode 100644
index 0000000000..3355f28eb1
--- /dev/null
+++ b/apps/plugins/xworld/intern.h
@@ -0,0 +1,44 @@
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#ifndef __INTERN_H__
24#define __INTERN_H__
25
26#include "plugin.h"
27#include "string.h"
28#include "awendian.h"
29#include "util.h"
30
31#define assert(c) (c?(void)0:error("Assertion failed line %d, file %s", __LINE__, __FILE__))
32
33struct Ptr {
34 uint8_t* pc;
35};
36
37uint8_t scriptPtr_fetchByte(struct Ptr* p) ICODE_ATTR;
38uint16_t scriptPtr_fetchWord(struct Ptr* p) ICODE_ATTR;
39
40struct Point {
41 int16_t x, y;
42};
43
44#endif
diff --git a/apps/plugins/xworld/keymaps.h b/apps/plugins/xworld/keymaps.h
new file mode 100644
index 0000000000..edba05b9d2
--- /dev/null
+++ b/apps/plugins/xworld/keymaps.h
@@ -0,0 +1,183 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Franklin Wei, Benjamin Brown
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ***************************************************************************/
21
22#ifndef _XWORLD_KEYMAPS_H
23#define _XWORLD_KEYMAPS_H
24#endif
25
26#if (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD) || \
27 (CONFIG_KEYPAD == PHILIPS_HDD6330_PAD) || \
28 (CONFIG_KEYPAD == PHILIPS_SA9200_PAD) || \
29 (CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD) || \
30 (CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD) || \
31 (CONFIG_KEYPAD == SANSA_CONNECT_PAD) || \
32 (CONFIG_KEYPAD == SANSA_C200_PAD) || \
33 (CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
34 (CONFIG_KEYPAD == SANSA_E200_PAD) || \
35 (CONFIG_KEYPAD == SANSA_FUZE_PAD) || \
36 (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD) || \
37 (CONFIG_KEYPAD == GIGABEAT_PAD) || \
38 (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
39 (CONFIG_KEYPAD == SAMSUNG_YH920_PAD) || \
40 (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
41 (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) || \
42 (CONFIG_KEYPAD == CREATIVE_ZEN_PAD) || \
43 (CONFIG_KEYPAD == SONY_NWZ_PAD) || \
44 (CONFIG_KEYPAD == CREATIVEZVM_PAD) || \
45 (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD) || \
46 (CONFIG_KEYPAD == IRIVER_H300_PAD) || \
47 (CONFIG_KEYPAD == HM801_PAD)
48#define BTN_UP BUTTON_UP
49#define BTN_DOWN BUTTON_DOWN
50#define BTN_LEFT BUTTON_LEFT
51#define BTN_RIGHT BUTTON_RIGHT
52
53#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
54#define BTN_UP_LEFT BUTTON_BACK
55#define BTN_UP_RIGHT BUTTON_PLAYPAUSE
56#define BTN_DOWN_LEFT BUTTON_BOTTOMLEFT
57#define BTN_DOWN_RIGHT BUTTON_BOTTOMRIGHT
58#endif
59
60#if (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD) || \
61 (CONFIG_KEYPAD == PHILIPS_HDD6330_PAD) || \
62 (CONFIG_KEYPAD == PHILIPS_SA9200_PAD) || \
63 (CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD) || \
64 (CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD) || \
65 (CONFIG_KEYPAD == SANSA_CONNECT_PAD) || \
66 (CONFIG_KEYPAD == SANSA_C200_PAD) || \
67 (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD) || \
68 (CONFIG_KEYPAD == DX50_PAD) || \
69 (CONFIG_KEYPAD == ONDAVX747_PAD)
70#define BTN_FIRE BUTTON_VOL_UP
71#define BTN_PAUSE BUTTON_VOL_DOWN
72
73#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
74#define BTN_FIRE BUTTON_HOME
75#define BTN_PAUSE BUTTON_SELECT
76
77#elif (CONFIG_KEYPAD == SAMSUNG_YH920_PAD)
78#define BTN_FIRE BUTTON_FFWD
79#define BTN_PAUSE BUTTON_REW
80
81#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
82#define BTN_FIRE BUTTON_REC
83#define BTN_PAUSE BUTTON_POWER
84
85#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
86#define BTN_FIRE BUTTON_SELECT
87#define BTN_PAUSE BUTTON_POWER
88
89#elif (CONFIG_KEYPAD == CREATIVE_ZEN_PAD)
90#define BTN_FIRE BUTTON_SELECT
91#define BTN_PAUSE BUTTON_BACK
92
93#elif (CONFIG_KEYPAD == CREATIVEZVM_PAD)
94#define BTN_FIRE BUTTON_PLAY
95#define BTN_PAUSE BUTTON_MENU
96
97#elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
98#define BTN_FIRE BUTTON_USER
99#define BTN_PAUSE BUTTON_MENU
100
101#elif (CONFIG_KEYPAD == SONY_NWZ_PAD)
102#define BTN_FIRE BUTTON_PLAY
103#define BTN_PAUSE BUTTON_BACK
104
105#elif (CONFIG_KEYPAD == IRIVER_H300_PAD)
106#define BTN_FIRE BUTTON_REC
107#define BTN_PAUSE BUTTON_MODE
108
109#elif (CONFIG_KEYPAD == HM801_PAD)
110#define BTN_FIRE BUTTON_PREV
111#define BTN_PAUSE BUTTON_NEXT
112
113#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
114 (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
115#define BTN_FIRE BUTTON_REC
116#define BTN_PAUSE BUTTON_PLAY
117
118#elif (CONFIG_KEYPAD == GIGABEAT_PAD) || \
119 (CONFIG_KEYPAD == GIGABEAT_S_PAD)
120#define BTN_FIRE BUTTON_VOL_UP
121#define BTN_PAUSE BUTTON_MENU
122#endif
123
124#elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
125#define BTN_UP BUTTON_OK
126#define BTN_DOWN BUTTON_CANCEL
127#define BTN_LEFT BUTTON_MENU
128#define BTN_RIGHT BUTTON_PLAY
129#define BTN_FIRE BUTTON_POWER
130#define BTN_PAUSE BUTTON_REC
131
132#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
133#define BTN_UP BUTTON_SCROLL_UP
134#define BTN_DOWN BUTTON_SCROLL_DOWN
135#define BTN_LEFT BUTTON_LEFT
136#define BTN_RIGHT BUTTON_RIGHT
137#define BTN_FIRE BUTTON_REW
138#define BTN_PAUSE BUTTON_PLAY
139
140#elif (CONFIG_KEYPAD == MROBE500_PAD)
141#define BTN_FIRE BUTTON_POWER
142
143#elif (CONFIG_KEYPAD == MROBE_REMOTE)
144#define BTN_UP BUTTON_RC_PLAY
145#define BTN_DOWN BUTTON_RC_DOWN
146#define BTN_LEFT BUTTON_RC_REW
147#define BTN_RIGHT BUTTON_RC_FF
148
149#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
150 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
151 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
152#define BTN_UP BUTTON_MENU
153#define BTN_DOWN BUTTON_PLAY
154#define BTN_LEFT BUTTON_LEFT
155#define BTN_RIGHT BUTTON_RIGHT
156#define BTN_FIRE BUTTON_SELECT
157#define BTN_PAUSE (BUTTON_MENU | BUTTON_SELECT)
158
159#elif (CONFIG_KEYPAD == ONDAVX777_PAD)
160#define BTN_FIRE BUTTON_POWER
161
162#elif (CONFIG_KEYPAD == DX50_PAD)
163#define BTN_FIRE BUTTON_PLUS
164#define BTN_PAUSE BUTTON_MENU
165
166#else
167#error Unsupported keypad
168#endif
169
170#ifdef HAVE_TOUCHSCREEN
171#define BTN_UP BUTTON_TOPMIDDLE
172#define BTN_DOWN BUTTON_BOTTOMMIDDLE
173#define BTN_LEFT BUTTON_LEFT
174#define BTN_RIGHT BUTTON_RIGHT
175
176#if (CONFIG_KEYPAD == MROBE500_PAD)
177#define BTN_PAUSE BUTTON_BOTTOMLEFT
178
179#elif (CONFIG_KEYPAD != COWON_D2_PAD) || (CONFIG_KEYPAD != DX50_PAD)
180#define BTN_FIRE BUTTON_BOTTOMLEFT
181#define BTN_PAUSE BUTTON_TOPLEFT
182#endif
183#endif
diff --git a/apps/plugins/xworld/mixer.c b/apps/plugins/xworld/mixer.c
new file mode 100644
index 0000000000..de7536cd36
--- /dev/null
+++ b/apps/plugins/xworld/mixer.c
@@ -0,0 +1,199 @@
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 "mixer.h"
24#include "serializer.h"
25#include "sys.h"
26
27static int8_t ICODE_ATTR addclamp(int a, int b) {
28 int add = a + b;
29 if (add < -128) {
30 add = -128;
31 }
32 else if (add > 127) {
33 add = 127;
34 }
35 return (int8_t)add;
36}
37
38void mixer_create(struct Mixer* mx, struct System *stub)
39{
40 mx->sys = stub;
41}
42
43static void mixer_mixCallback(void *param, uint8_t *buf, int len);
44
45void mixer_init(struct Mixer* mx) {
46 rb->memset(mx->_channels, 0, sizeof(mx->_channels));
47 if(!mx->sys)
48 {
49 error("in mixer sys is NULL");
50 }
51 mx->_mutex = sys_createMutex(mx->sys);
52 sys_startAudio(mx->sys, mixer_mixCallback, mx);
53}
54
55void mixer_free(struct Mixer* mx) {
56 mixer_stopAll(mx);
57 sys_stopAudio(mx->sys);
58 sys_destroyMutex(mx->sys, mx->_mutex);
59}
60
61void mixer_playChannel(struct Mixer* mx, uint8_t channel, const struct MixerChunk *mc, uint16_t freq, uint8_t volume) {
62 debug(DBG_SND, "mixer_playChannel(%d, %d, %d)", channel, freq, volume);
63 assert(channel < AUDIO_NUM_CHANNELS);
64
65 /* FW: the mutex code was converted 1:1 from C++ to C, leading to the ugly calls */
66 /* to constructors/destructors as seen here */
67
68 struct MutexStack_t ms;
69 MutexStack(&ms, mx->sys, mx->_mutex);
70
71 struct MixerChannel *ch = &mx->_channels[channel];
72 ch->active = true;
73 ch->volume = volume;
74 ch->chunk = *mc;
75 ch->chunkPos = 0;
76 ch->chunkInc = (freq << 8) / sys_getOutputSampleRate(mx->sys);
77
78 MutexStack_destroy(&ms);
79}
80
81void mixer_stopChannel(struct Mixer* mx, uint8_t channel) {
82 debug(DBG_SND, "mixer_stopChannel(%d)", channel);
83 assert(channel < AUDIO_NUM_CHANNELS);
84
85 struct MutexStack_t ms;
86 MutexStack(&ms, mx->sys, mx->_mutex);
87
88 mx->_channels[channel].active = false;
89
90 MutexStack_destroy(&ms);
91}
92
93void mixer_setChannelVolume(struct Mixer* mx, uint8_t channel, uint8_t volume) {
94 debug(DBG_SND, "mixer_setChannelVolume(%d, %d)", channel, volume);
95 assert(channel < AUDIO_NUM_CHANNELS);
96
97 struct MutexStack_t ms;
98 MutexStack(&ms, mx->sys, mx->_mutex);
99
100 mx->_channels[channel].volume = volume;
101
102 MutexStack_destroy(&ms);
103}
104
105void mixer_stopAll(struct Mixer* mx) {
106 debug(DBG_SND, "mixer_stopAll()");
107
108 struct MutexStack_t ms;
109 MutexStack(&ms, mx->sys, mx->_mutex);
110
111 for (uint8_t i = 0; i < AUDIO_NUM_CHANNELS; ++i) {
112 mx->_channels[i].active = false;
113 }
114
115 MutexStack_destroy(&ms);
116}
117
118/* Mx is SDL callback. Called in order to populate the buf with len bytes. */
119/* The mixer iterates through all active channels and combine all sounds. */
120
121/* Since there is no way to know when SDL will ask for a buffer fill, we need */
122/* to synchronize with a mutex so the channels remain stable during the execution */
123/* of this method. */
124static void ICODE_ATTR mixer_mix(struct Mixer* mx, int8_t *buf, int len) {
125 int8_t *pBuf;
126
127 struct MutexStack_t ms;
128 MutexStack(&ms, mx->sys, mx->_mutex);
129
130 /* Clear the buffer since nothing guarantees we are receiving clean memory. */
131 rb->memset(buf, 0, len);
132
133 for (uint8_t i = 0; i < AUDIO_NUM_CHANNELS; ++i) {
134 struct MixerChannel *ch = &mx->_channels[i];
135 if (!ch->active)
136 continue;
137
138 pBuf = buf;
139 for (int j = 0; j < len; ++j, ++pBuf) {
140
141 uint16_t p1, p2;
142 uint16_t ilc = (ch->chunkPos & 0xFF);
143 p1 = ch->chunkPos >> 8;
144 ch->chunkPos += ch->chunkInc;
145
146 if (ch->chunk.loopLen != 0) {
147 if (p1 == ch->chunk.loopPos + ch->chunk.loopLen - 1) {
148 debug(DBG_SND, "Looping sample on channel %d", i);
149 ch->chunkPos = p2 = ch->chunk.loopPos;
150 } else {
151 p2 = p1 + 1;
152 }
153 } else {
154 if (p1 == ch->chunk.len - 1) {
155 debug(DBG_SND, "Stopping sample on channel %d", i);
156 ch->active = false;
157 break;
158 } else {
159 p2 = p1 + 1;
160 }
161 }
162 /* interpolate */
163 int8_t b1 = *(int8_t *)(ch->chunk.data + p1);
164 int8_t b2 = *(int8_t *)(ch->chunk.data + p2);
165 int8_t b = (int8_t)((b1 * (0xFF - ilc) + b2 * ilc) >> 8);
166
167 /* set volume and clamp */
168 *pBuf = addclamp(*pBuf, (int)b * ch->volume / 0x40); /* 0x40=64 */
169 }
170
171 }
172
173 MutexStack_destroy(&ms);
174}
175
176static void ICODE_ATTR mixer_mixCallback(void *param, uint8_t *buf, int len) {
177 debug(DBG_SND, "mixer_mixCallback");
178 mixer_mix((struct Mixer*)param, (int8_t *)buf, len);
179}
180
181void mixer_saveOrLoad(struct Mixer* mx, struct Serializer *ser) {
182 sys_lockMutex(mx->sys, mx->_mutex);
183 for (int i = 0; i < AUDIO_NUM_CHANNELS; ++i) {
184 struct MixerChannel *ch = &mx->_channels[i];
185 struct Entry entries[] = {
186 SE_INT(&ch->active, SES_BOOL, VER(2)),
187 SE_INT(&ch->volume, SES_INT8, VER(2)),
188 SE_INT(&ch->chunkPos, SES_INT32, VER(2)),
189 SE_INT(&ch->chunkInc, SES_INT32, VER(2)),
190 SE_PTR(&ch->chunk.data, VER(2)),
191 SE_INT(&ch->chunk.len, SES_INT16, VER(2)),
192 SE_INT(&ch->chunk.loopPos, SES_INT16, VER(2)),
193 SE_INT(&ch->chunk.loopLen, SES_INT16, VER(2)),
194 SE_END()
195 };
196 ser_saveOrLoadEntries(ser, entries);
197 }
198 sys_unlockMutex(mx->sys, mx->_mutex);
199};
diff --git a/apps/plugins/xworld/mixer.h b/apps/plugins/xworld/mixer.h
new file mode 100644
index 0000000000..9447bcfb81
--- /dev/null
+++ b/apps/plugins/xworld/mixer.h
@@ -0,0 +1,69 @@
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#ifndef __MIXER_H__
24#define __MIXER_H__
25
26#include "intern.h"
27
28struct MixerChunk {
29 const uint8_t *data;
30 uint16_t len;
31 uint16_t loopPos;
32 uint16_t loopLen;
33};
34
35struct MixerChannel {
36 uint8_t active;
37 uint8_t volume;
38 struct MixerChunk chunk;
39 uint32_t chunkPos;
40 uint32_t chunkInc;
41};
42
43struct Serializer;
44struct System;
45
46#define AUDIO_NUM_CHANNELS 4
47
48struct Mixer {
49 void *_mutex;
50 struct System *sys;
51
52 /* Since the virtual machine and SDL are running simultaneously in two different threads */
53 /* any read or write to an elements of the sound channels MUST be synchronized with a */
54 /* mutex. */
55 struct MixerChannel _channels[AUDIO_NUM_CHANNELS];
56};
57
58void mixer_create(struct Mixer*, struct System *stub);
59void mixer_init(struct Mixer*);
60void mixer_free(struct Mixer*);
61
62void mixer_playChannel(struct Mixer*, uint8_t channel, const struct MixerChunk *mc, uint16_t freq, uint8_t volume);
63void mixer_stopChannel(struct Mixer*, uint8_t channel);
64void mixer_setChannelVolume(struct Mixer*, uint8_t channel, uint8_t volume);
65void mixer_stopAll(struct Mixer*);
66
67void mixer_saveOrLoad(struct Mixer*, struct Serializer *ser);
68
69#endif
diff --git a/apps/plugins/xworld/parts.c b/apps/plugins/xworld/parts.c
new file mode 100644
index 0000000000..12b0b3b7b6
--- /dev/null
+++ b/apps/plugins/xworld/parts.c
@@ -0,0 +1,56 @@
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 "parts.h"
24
25
26/*
27 #define MEMLIST_PART_PALETTE 0
28 #define MEMLIST_PART_CODE 1
29 #define MEMLIST_PART_VIDEO1 2
30 #define MEMLIST_PART_VIDEO2 3
31*/
32
33/*
34 MEMLIST_PART_VIDEO1 and MEMLIST_PART_VIDEO2 are used to store polygons.
35
36 It seems that:
37 - MEMLIST_PART_VIDEO1 contains the cinematic polygons.
38 - MEMLIST_PART_VIDEO2 contains the polygons for player and enemies animations.
39
40 That would make sense since protection screen and cinematic game parts do not load MEMLIST_PART_VIDEO2.
41
42*/
43const uint16_t memListParts[GAME_NUM_PARTS][4] = {
44
45/* MEMLIST_PART_PALETTE MEMLIST_PART_CODE MEMLIST_PART_VIDEO1 MEMLIST_PART_VIDEO2 */
46 { 0x14, 0x15, 0x16, 0x00 }, /* protection screens */
47 { 0x17, 0x18, 0x19, 0x00 }, /* introduction cinematic */
48 { 0x1A, 0x1B, 0x1C, 0x11 },
49 { 0x1D, 0x1E, 0x1F, 0x11 },
50 { 0x20, 0x21, 0x22, 0x11 },
51 { 0x23, 0x24, 0x25, 0x00 }, /* battlechar cinematic */
52 { 0x26, 0x27, 0x28, 0x11 },
53 { 0x29, 0x2A, 0x2B, 0x11 },
54 { 0x7D, 0x7E, 0x7F, 0x00 },
55 { 0x7D, 0x7E, 0x7F, 0x00 } /* password screen */
56};
diff --git a/apps/plugins/xworld/parts.h b/apps/plugins/xworld/parts.h
new file mode 100644
index 0000000000..684306cc6d
--- /dev/null
+++ b/apps/plugins/xworld/parts.h
@@ -0,0 +1,57 @@
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#ifndef __AW_PARTS_
24#define __AW_PARTS_
25
26#include "intern.h"
27#include "awendian.h"
28
29/* The game is divided in 10 parts. */
30#define GAME_NUM_PARTS 10
31
32#define GAME_PART_FIRST 0x3E80
33#define GAME_PART1 0x3E80
34#define GAME_PART2 0x3E81 /* Introduction */
35#define GAME_PART3 0x3E82
36#define GAME_PART4 0x3E83 /* Wake up in the suspended jail */
37#define GAME_PART5 0x3E84
38#define GAME_PART6 0x3E85 /* BattleChar sequence */
39#define GAME_PART7 0x3E86
40#define GAME_PART8 0x3E87
41#define GAME_PART9 0x3E88
42#define GAME_PART10 0x3E89
43#define GAME_PART_LAST 0x3E89
44
45extern const uint16_t memListParts[GAME_NUM_PARTS][4];
46
47/* For each part of the game, four resources are referenced. */
48#define MEMLIST_PART_PALETTE 0
49#define MEMLIST_PART_CODE 1
50#define MEMLIST_PART_POLY_CINEMATIC 2
51#define MEMLIST_PART_VIDEO2 3
52
53
54#define MEMLIST_PART_NONE 0x00
55
56
57#endif
diff --git a/apps/plugins/xworld/resource.c b/apps/plugins/xworld/resource.c
new file mode 100644
index 0000000000..2820dcb998
--- /dev/null
+++ b/apps/plugins/xworld/resource.c
@@ -0,0 +1,443 @@
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 "resource.h"
25#include "bank.h"
26#include "file.h"
27#include "serializer.h"
28#include "video.h"
29#include "util.h"
30#include "parts.h"
31#include "vm.h"
32#include "sys.h"
33
34void res_create(struct Resource* res, struct Video* vid, struct System* sys, const char* dataDir)
35{
36 res->video = vid;
37 res->sys = sys;
38 res->_dataDir = dataDir;
39 res->currentPartId = 0;
40 res->requestedNextPart = 0;
41}
42
43void res_readBank(struct Resource* res, const MemEntry *me, uint8_t *dstBuf) {
44 uint16_t n = me - res->_memList;
45 debug(DBG_BANK, "res_readBank(%d)", n);
46
47 struct Bank bk;
48 bank_create(&bk, res->_dataDir);
49 if (!bank_read(&bk, me, dstBuf)) {
50 error("res_readBank() unable to unpack entry %d\n", n);
51 }
52}
53
54#ifdef XWORLD_DEBUG
55static const char *resTypeToString(struct Resource* res, unsigned int type)
56{
57 (void) res;
58 static const char* resTypes[] =
59 {
60 "RT_SOUND",
61 "RT_MUSIC",
62 "RT_POLY_ANIM",
63 "RT_PALETTE",
64 "RT_BYTECODE",
65 "RT_POLY_CINEMATIC"
66 };
67 if (type >= (sizeof(resTypes) / sizeof(const char *)))
68 return "RT_UNKNOWN";
69 return resTypes[type];
70}
71#endif
72
73#define RES_SIZE 0
74#define RES_COMPRESSED 1
75int resourceSizeStats[7][2];
76#define STATS_TOTAL_SIZE 6
77int resourceUnitStats[7][2];
78
79/*
80 Read all entries from memlist.bin. Do not load anything in memory,
81 this is just a fast way to access the data later based on their id.
82*/
83void res_readEntries(struct Resource* res) {
84 File f;
85 file_create(&f, false);
86
87 int resourceCounter = 0;
88
89 if (!file_open(&f, "memlist.bin", res->_dataDir, "rb")) {
90 error("Could not open 'MEMLIST.BIN', data files missing");
91 /* error() will exit() no need to return or do anything else. */
92 }
93
94 /* Prepare stats array */
95 rb->memset(resourceSizeStats, 0, sizeof(resourceSizeStats));
96 rb->memset(resourceUnitStats, 0, sizeof(resourceUnitStats));
97
98 res->_numMemList = 0;
99 struct MemEntry *memEntry = res->_memList;
100 while (1) {
101 assert(res->_numMemList < ARRAYLEN(res->_memList));
102 memEntry->state = file_readByte(&f);
103 memEntry->type = file_readByte(&f);
104 memEntry->bufPtr = 0;
105 file_readUint16BE(&f);
106 memEntry->unk4 = file_readUint16BE(&f);
107 memEntry->rankNum = file_readByte(&f);
108 memEntry->bankId = file_readByte(&f);
109 memEntry->bankOffset = file_readUint32BE(&f);
110 memEntry->unkC = file_readUint16BE(&f);
111 memEntry->packedSize = file_readUint16BE(&f);
112 memEntry->unk10 = file_readUint16BE(&f);
113 memEntry->size = file_readUint16BE(&f);
114
115 debug(DBG_RES, "mementry state is %d", memEntry->state);
116 if (memEntry->state == MEMENTRY_STATE_END_OF_MEMLIST) {
117 break;
118 }
119
120 /* Memory tracking */
121 if (memEntry->packedSize == memEntry->size)
122 {
123 resourceUnitStats[memEntry->type][RES_SIZE] ++;
124 resourceUnitStats[STATS_TOTAL_SIZE][RES_SIZE] ++;
125 }
126 else
127 {
128 resourceUnitStats[memEntry->type][RES_COMPRESSED] ++;
129 resourceUnitStats[STATS_TOTAL_SIZE][RES_COMPRESSED] ++;
130 }
131
132 resourceSizeStats[memEntry->type][RES_SIZE] += memEntry->size;
133 resourceSizeStats[STATS_TOTAL_SIZE][RES_SIZE] += memEntry->size;
134 resourceSizeStats[memEntry->type][RES_COMPRESSED] += memEntry->packedSize;
135 resourceSizeStats[STATS_TOTAL_SIZE][RES_COMPRESSED] += memEntry->packedSize;
136
137 debug(DBG_RES, "R:0x%02X, %-17s size=%5d (compacted gain=%2.0f%%)",
138 resourceCounter,
139 resTypeToString(res, memEntry->type),
140 memEntry->size,
141 memEntry->size ? (memEntry->size - memEntry->packedSize) / (float)memEntry->size * 100.0f : 0.0f);
142
143 resourceCounter++;
144
145 res->_numMemList++;
146 memEntry++;
147 }
148
149 debug(DBG_RES, "\n");
150 debug(DBG_RES, "Total # resources: %d", resourceCounter);
151 debug(DBG_RES, "Compressed : %d", resourceUnitStats[STATS_TOTAL_SIZE][RES_COMPRESSED]);
152 debug(DBG_RES, "Uncompressed : %d", resourceUnitStats[STATS_TOTAL_SIZE][RES_SIZE]);
153 debug(DBG_RES, "Note: %2.0f%% of resources are compressed.", 100 * resourceUnitStats[STATS_TOTAL_SIZE][RES_COMPRESSED] / (float)resourceCounter);
154 debug(DBG_RES, "\n");
155 debug(DBG_RES, "Total size (uncompressed) : %7d bytes.", resourceSizeStats[STATS_TOTAL_SIZE][RES_SIZE]);
156 debug(DBG_RES, "Total size (compressed) : %7d bytes.", resourceSizeStats[STATS_TOTAL_SIZE][RES_COMPRESSED]);
157 debug(DBG_RES, "Note: Overall compression gain is : %2.0f%%.",
158 (resourceSizeStats[STATS_TOTAL_SIZE][RES_SIZE] - resourceSizeStats[STATS_TOTAL_SIZE][RES_COMPRESSED]) / (float)resourceSizeStats[STATS_TOTAL_SIZE][RES_SIZE] * 100);
159
160 debug(DBG_RES, "\n");
161 for(int i = 0 ; i < 6 ; i++)
162 debug(DBG_RES, "Total %-17s unpacked size: %7d (%2.0f%% of total unpacked size) packedSize %7d (%2.0f%% of floppy space) gain:(%2.0f%%)",
163 resTypeToString(res, i),
164 resourceSizeStats[i][RES_SIZE],
165 resourceSizeStats[i][RES_SIZE] / (float)resourceSizeStats[STATS_TOTAL_SIZE][RES_SIZE] * 100.0f,
166 resourceSizeStats[i][RES_COMPRESSED],
167 resourceSizeStats[i][RES_COMPRESSED] / (float)resourceSizeStats[STATS_TOTAL_SIZE][RES_COMPRESSED] * 100.0f,
168 (resourceSizeStats[i][RES_SIZE] - resourceSizeStats[i][RES_COMPRESSED]) / (float)resourceSizeStats[i][RES_SIZE] * 100.0f);
169
170 debug(DBG_RES, "Note: Damn you sound compression rate!");
171
172 debug(DBG_RES, "\nTotal bank files: %d", resourceUnitStats[STATS_TOTAL_SIZE][RES_SIZE] + resourceUnitStats[STATS_TOTAL_SIZE][RES_COMPRESSED]);
173 for(int i = 0 ; i < 6 ; i++)
174 debug(DBG_RES, "Total %-17s files: %3d", resTypeToString(res, i), resourceUnitStats[i][RES_SIZE] + resourceUnitStats[i][RES_COMPRESSED]);
175
176 file_close(&f);
177}
178
179/*
180 Go over every resource and check if they are marked at "MEMENTRY_STATE_LOAD_ME".
181 Load them in memory and mark them are MEMENTRY_STATE_LOADED
182*/
183void res_loadMarkedAsNeeded(struct Resource* res) {
184
185 while (1) {
186 struct MemEntry *me = NULL;
187
188 /* get resource with max rankNum */
189 uint8_t maxNum = 0;
190 uint16_t i = res->_numMemList;
191 struct MemEntry *it = res->_memList;
192 while (i--) {
193 if (it->state == MEMENTRY_STATE_LOAD_ME && maxNum <= it->rankNum) {
194 maxNum = it->rankNum;
195 me = it;
196 }
197 it++;
198 }
199
200 if (me == NULL) {
201 break; // no entry found
202 }
203
204
205 /* At this point the resource descriptor should be pointed to "me" */
206
207 uint8_t *loadDestination = NULL;
208 if (me->type == RT_POLY_ANIM) {
209 loadDestination = res->_vidCurPtr;
210 } else {
211 loadDestination = res->_scriptCurPtr;
212 if (me->size > res->_vidBakPtr - res->_scriptCurPtr) {
213 warning("res_load() not enough memory");
214 me->state = MEMENTRY_STATE_NOT_NEEDED;
215 continue;
216 }
217 }
218
219
220 if (me->bankId == 0) {
221 warning("res_load() ec=0x%X (me->bankId == 0)", 0xF00);
222 me->state = MEMENTRY_STATE_NOT_NEEDED;
223 } else {
224 debug(DBG_BANK, "res_load() bufPos=%X size=%X type=%X pos=%X bankId=%X", loadDestination - res->_memPtrStart, me->packedSize, me->type, me->bankOffset, me->bankId);
225 res_readBank(res, me, loadDestination);
226 if(me->type == RT_POLY_ANIM) {
227 video_copyPagePtr(res->video, res->_vidCurPtr);
228 me->state = MEMENTRY_STATE_NOT_NEEDED;
229 } else {
230 me->bufPtr = loadDestination;
231 me->state = MEMENTRY_STATE_LOADED;
232 res->_scriptCurPtr += me->size;
233 }
234 }
235 }
236}
237
238void res_invalidateRes(struct Resource* res) {
239 struct MemEntry *me = res->_memList;
240 uint16_t i = res->_numMemList;
241 while (i--) {
242 if (me->type <= RT_POLY_ANIM || me->type > 6) { /* 6 WTF ?!?! ResType goes up to 5 !! */
243 me->state = MEMENTRY_STATE_NOT_NEEDED;
244 }
245 ++me;
246 }
247 res->_scriptCurPtr = res->_scriptBakPtr;
248}
249
250void res_invalidateAll(struct Resource* res) {
251 struct MemEntry *me = res->_memList;
252 uint16_t i = res->_numMemList;
253 while (i--) {
254 me->state = MEMENTRY_STATE_NOT_NEEDED;
255 ++me;
256 }
257 res->_scriptCurPtr = res->_memPtrStart;
258}
259
260/* This method serves two purpose:
261 - Load parts in memory segments (palette,code,video1,video2)
262 or
263 - Load a resource in memory
264
265 This is decided based on the resourceId. If it does not match a mementry id it is supposed to
266 be a part id. */
267void res_loadPartsOrMemoryEntry(struct Resource* res, uint16_t resourceId) {
268
269 if (resourceId > res->_numMemList) {
270
271 res->requestedNextPart = resourceId;
272
273 } else {
274
275 struct MemEntry *me = &res->_memList[resourceId];
276
277 if (me->state == MEMENTRY_STATE_NOT_NEEDED) {
278 me->state = MEMENTRY_STATE_LOAD_ME;
279 res_loadMarkedAsNeeded(res);
280 }
281 }
282
283}
284
285/* Protection screen and cinematic don't need the player and enemies polygon data
286 so _memList[video2Index] is never loaded for those parts of the game. When
287 needed (for action phrases) _memList[video2Index] is always loaded with 0x11
288 (as seen in memListParts). */
289void res_setupPart(struct Resource* res, uint16_t partId) {
290
291 if (partId == res->currentPartId)
292 return;
293
294 if (partId < GAME_PART_FIRST || partId > GAME_PART_LAST)
295 error("res_setupPart() ec=0x%X invalid partId", partId);
296
297 uint16_t memListPartIndex = partId - GAME_PART_FIRST;
298
299 uint8_t paletteIndex = memListParts[memListPartIndex][MEMLIST_PART_PALETTE];
300 uint8_t codeIndex = memListParts[memListPartIndex][MEMLIST_PART_CODE];
301 uint8_t videoCinematicIndex = memListParts[memListPartIndex][MEMLIST_PART_POLY_CINEMATIC];
302 uint8_t video2Index = memListParts[memListPartIndex][MEMLIST_PART_VIDEO2];
303
304 /* Mark all resources as located on harddrive. */
305 res_invalidateAll(res);
306
307 res->_memList[paletteIndex].state = MEMENTRY_STATE_LOAD_ME;
308 res->_memList[codeIndex].state = MEMENTRY_STATE_LOAD_ME;
309 res->_memList[videoCinematicIndex].state = MEMENTRY_STATE_LOAD_ME;
310
311 /* This is probably a cinematic or a non interactive part of the game. */
312 /* Player and enemy polygons are not needed. */
313 if (video2Index != MEMLIST_PART_NONE)
314 res->_memList[video2Index].state = MEMENTRY_STATE_LOAD_ME;
315
316
317 res_loadMarkedAsNeeded(res);
318
319 res->segPalettes = res->_memList[paletteIndex].bufPtr;
320 debug(DBG_RES, "paletteIndex is 0x%08x", res->segPalettes);
321 res->segBytecode = res->_memList[codeIndex].bufPtr;
322 res->segCinematic = res->_memList[videoCinematicIndex].bufPtr;
323
324
325
326 /* This is probably a cinematic or a non interactive part of the game. */
327 /* Player and enemy polygons are not needed. */
328 if (video2Index != MEMLIST_PART_NONE)
329 res->_segVideo2 = res->_memList[video2Index].bufPtr;
330
331 debug(DBG_RES, "");
332 debug(DBG_RES, "setupPart(%d)", partId - GAME_PART_FIRST);
333 debug(DBG_RES, "Loaded resource %d (%s) in segPalettes.", paletteIndex, resTypeToString(res, res->_memList[paletteIndex].type));
334 debug(DBG_RES, "Loaded resource %d (%s) in segBytecode.", codeIndex, resTypeToString(res, res->_memList[codeIndex].type));
335 debug(DBG_RES, "Loaded resource %d (%s) in segCinematic.", videoCinematicIndex, resTypeToString(res, res->_memList[videoCinematicIndex].type));
336
337
338 /* prevent warnings: */
339#ifdef XWORLD_DEBUG
340 if (video2Index != MEMLIST_PART_NONE)
341 debug(DBG_RES, "Loaded resource %d (%s) in _segVideo2.", video2Index, resTypeToString(res, res->_memList[video2Index].type));
342#endif
343
344
345 res->currentPartId = partId;
346
347
348 /* _scriptCurPtr is changed in res->load(); */
349 res->_scriptBakPtr = res->_scriptCurPtr;
350}
351
352void res_allocMemBlock(struct Resource* res) {
353 if(rb->audio_status())
354 rb->audio_stop();
355 /* steal the audio buffer */
356 size_t sz;
357 /* memory usage is as follows:
358 [VM memory - 600K]
359 [Framebuffers - 128K]
360 [Temporary framebuffer - 192K]
361 [String table buffer]
362 */
363 res->_memPtrStart = rb->plugin_get_audio_buffer(&sz);
364 if(sz < MEM_BLOCK_SIZE + (4 * VID_PAGE_SIZE) + 320 * 200 * sizeof(fb_data))
365 {
366 warning("res_allocMemBlock: can't allocate enough memory!");
367 }
368
369 res->sys->membuf = res->_memPtrStart + ( MEM_BLOCK_SIZE + (4 * VID_PAGE_SIZE) + 320 * 200 * sizeof(fb_data));
370 res->sys->bytes_left = sz - (MEM_BLOCK_SIZE + (4 * VID_PAGE_SIZE) + 320 * 200 * sizeof(fb_data));
371
372 debug(DBG_RES, "audiobuf is %d bytes in size", sz);
373
374 res->_scriptBakPtr = res->_scriptCurPtr = res->_memPtrStart;
375 res->_vidBakPtr = res->_vidCurPtr = res->_memPtrStart + MEM_BLOCK_SIZE - 0x800 * 16; //0x800 = 2048, so we have 32KB free for vidBack and vidCur
376 res->_useSegVideo2 = false;
377}
378
379void res_freeMemBlock(struct Resource* res) {
380 (void) res;
381 /* there's no need to do anything to free the audio buffer */
382 return;
383}
384
385void res_saveOrLoad(struct Resource* res, struct Serializer *ser) {
386 uint8_t loadedList[64];
387 if (ser->_mode == SM_SAVE) {
388 rb->memset(loadedList, 0, sizeof(loadedList));
389 uint8_t *p = loadedList;
390 uint8_t *q = res->_memPtrStart;
391 while (1) {
392 struct MemEntry *it = res->_memList;
393 struct MemEntry *me = 0;
394 uint16_t num = res->_numMemList;
395 while (num--) {
396 if (it->state == MEMENTRY_STATE_LOADED && it->bufPtr == q) {
397 me = it;
398 }
399 ++it;
400 }
401 if (me == 0) {
402 break;
403 } else {
404 assert(p < loadedList + 64);
405 *p++ = me - res->_memList;
406 q += me->size;
407 }
408 }
409 }
410
411 struct Entry entries[] = {
412 SE_ARRAY(loadedList, 64, SES_INT8, VER(1)),
413 SE_INT(&res->currentPartId, SES_INT16, VER(1)),
414 SE_PTR(&res->_scriptBakPtr, VER(1)),
415 SE_PTR(&res->_scriptCurPtr, VER(1)),
416 SE_PTR(&res->_vidBakPtr, VER(1)),
417 SE_PTR(&res->_vidCurPtr, VER(1)),
418 SE_INT(&res->_useSegVideo2, SES_BOOL, VER(1)),
419 SE_PTR(&res->segPalettes, VER(1)),
420 SE_PTR(&res->segBytecode, VER(1)),
421 SE_PTR(&res->segCinematic, VER(1)),
422 SE_PTR(&res->_segVideo2, VER(1)),
423 SE_END()
424 };
425
426 ser_saveOrLoadEntries(ser, entries);
427 if (ser->_mode == SM_LOAD) {
428 uint8_t *p = loadedList;
429 uint8_t *q = res->_memPtrStart;
430 while (*p) {
431 struct MemEntry *me = &res->_memList[*p++];
432 res_readBank(res, me, q);
433 me->bufPtr = q;
434 me->state = MEMENTRY_STATE_LOADED;
435 q += me->size;
436 }
437 }
438}
439
440const char* res_getDataDir(struct Resource* res)
441{
442 return res->_dataDir;
443}
diff --git a/apps/plugins/xworld/resource.h b/apps/plugins/xworld/resource.h
new file mode 100644
index 0000000000..3242777bc2
--- /dev/null
+++ b/apps/plugins/xworld/resource.h
@@ -0,0 +1,107 @@
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#ifndef __RESOURCE_H__
24#define __RESOURCE_H__
25
26#include "intern.h"
27
28
29#define MEMENTRY_STATE_END_OF_MEMLIST 0xFF
30#define MEMENTRY_STATE_NOT_NEEDED 0
31#define MEMENTRY_STATE_LOADED 1
32#define MEMENTRY_STATE_LOAD_ME 2
33
34/*
35 This is a directory entry. When the game starts, it loads memlist.bin and
36 populate and array of MemEntry
37*/
38typedef struct MemEntry {
39 uint8_t state; /* 0x0 */
40 uint8_t type; /* 0x1, Resource::ResType */
41 uint8_t *bufPtr; /* 0x2 */
42 uint16_t unk4; /* 0x4, unused */
43 uint8_t rankNum; /* 0x6 */
44 uint8_t bankId; /* 0x7 */
45 uint32_t bankOffset; /* 0x8 0xA */
46 uint16_t unkC; /* 0xC, unused */
47 uint16_t packedSize; /* 0xE */
48 /* All ressources are packed (for a gain of 28% according to Chahi) */
49
50 uint16_t unk10; /* 0x10, unused */
51 uint16_t size; /* 0x12 */
52} __attribute__((packed)) MemEntry;
53
54/*
55 Note: state is not a boolean, it can have value 0, 1, 2 or 255, respectively meaning:
56 0:NOT_NEEDED
57 1:LOADED
58 2:LOAD_ME
59 255:END_OF_MEMLIST
60
61 See MEMENTRY_STATE_* #defines above.
62*/
63
64struct Serializer;
65struct Video;
66
67#define MEM_BLOCK_SIZE (600 * 1024)
68#define RT_SOUND 0
69#define RT_MUSIC 1
70#define RT_POLY_ANIM 2
71#define RT_PALETTE 3
72#define RT_BYTECODE 4
73#define RT_POLY_CINEMATIC 5
74
75struct Resource {
76 struct Video *video;
77 struct System *sys;
78 const char *_dataDir;
79 struct MemEntry _memList[150];
80 uint16_t _numMemList;
81 uint16_t currentPartId, requestedNextPart;
82 uint8_t *_memPtrStart, *_scriptBakPtr, *_scriptCurPtr, *_vidBakPtr, *_vidCurPtr;
83 bool _useSegVideo2;
84
85 uint8_t *segPalettes;
86 uint8_t *segBytecode;
87 uint8_t *segCinematic;
88 uint8_t *_segVideo2;
89};
90
91
92void res_create(struct Resource*, struct Video*, struct System*, const char* dataDir);
93
94void res_readBank(struct Resource*, const MemEntry *me, uint8_t *dstBuf);
95void res_readEntries(struct Resource*);
96void res_loadMarkedAsNeeded(struct Resource*);
97void res_invalidateAll(struct Resource*);
98void res_invalidateRes(struct Resource*);
99void res_loadPartsOrMemoryEntry(struct Resource*, uint16_t num);
100void res_setupPart(struct Resource*, uint16_t ptrId);
101void res_allocMemBlock(struct Resource*);
102void res_freeMemBlock(struct Resource*);
103
104void res_saveOrLoad(struct Resource*, struct Serializer *ser);
105
106const char* res_getDataDir(struct Resource*);
107#endif
diff --git a/apps/plugins/xworld/serializer.c b/apps/plugins/xworld/serializer.c
new file mode 100644
index 0000000000..5268c2784c
--- /dev/null
+++ b/apps/plugins/xworld/serializer.c
@@ -0,0 +1,141 @@
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 "serializer.h"
24#include "file.h"
25
26
27void ser_create(struct Serializer* c, File *stream, enum Mode mode, uint8_t *ptrBlock, uint16_t saveVer)
28{
29 c->_stream = stream;
30 c->_mode = mode;
31 c->_ptrBlock = ptrBlock;
32 c->_saveVer = saveVer;
33}
34
35void ser_saveOrLoadEntries(struct Serializer* c, struct Entry *entry) {
36 debug(DBG_SER, "ser_saveOrLoadEntries() _mode=%d", c->_mode);
37 c->_bytesCount = 0;
38 switch (c->_mode) {
39 case SM_SAVE:
40 ser_saveEntries(c, entry);
41 break;
42 case SM_LOAD:
43 ser_loadEntries(c, entry);
44 break;
45 }
46 debug(DBG_SER, "ser_saveOrLoadEntries() _bytesCount=%d", c->_bytesCount);
47}
48
49void ser_saveEntries(struct Serializer* c, struct Entry *entry) {
50 debug(DBG_SER, "ser_saveEntries()");
51 for (; entry->type != SET_END; ++entry) {
52 if (entry->maxVer == CUR_VER) {
53 switch (entry->type) {
54 case SET_INT:
55 ser_saveInt(c, entry->size, entry->data);
56 c->_bytesCount += entry->size;
57 break;
58 case SET_ARRAY:
59 if (entry->size == SES_INT8) {
60 file_write(c->_stream, entry->data, entry->n);
61 c->_bytesCount += entry->n;
62 } else {
63 uint8_t *p = (uint8_t *)entry->data;
64 for (int i = 0; i < entry->n; ++i) {
65 ser_saveInt(c, entry->size, p);
66 p += entry->size;
67 c->_bytesCount += entry->size;
68 }
69 }
70 break;
71 case SET_PTR:
72 file_writeUint32BE(c->_stream, *(uint8_t **)(entry->data) - c->_ptrBlock);
73 c->_bytesCount += 4;
74 break;
75 case SET_END:
76 break;
77 }
78 }
79 }
80}
81
82void ser_loadEntries(struct Serializer* c, struct Entry *entry) {
83 debug(DBG_SER, "ser_loadEntries()");
84 for (; entry->type != SET_END; ++entry) {
85 if (c->_saveVer >= entry->minVer && c->_saveVer <= entry->maxVer) {
86 switch (entry->type) {
87 case SET_INT:
88 ser_loadInt(c, entry->size, entry->data);
89 c->_bytesCount += entry->size;
90 break;
91 case SET_ARRAY:
92 if (entry->size == SES_INT8) {
93 file_read(c->_stream, entry->data, entry->n);
94 c->_bytesCount += entry->n;
95 } else {
96 uint8_t *p = (uint8_t *)entry->data;
97 for (int i = 0; i < entry->n; ++i) {
98 ser_loadInt(c, entry->size, p);
99 p += entry->size;
100 c->_bytesCount += entry->size;
101 }
102 }
103 break;
104 case SET_PTR:
105 *(uint8_t **)(entry->data) = c->_ptrBlock + file_readUint32BE(c->_stream);
106 c->_bytesCount += 4;
107 break;
108 case SET_END:
109 break;
110 }
111 }
112 }
113}
114
115void ser_saveInt(struct Serializer* c, uint8_t es, void *p) {
116 switch (es) {
117 case 1:
118 file_writeByte(c->_stream, *(uint8_t *)p);
119 break;
120 case 2:
121 file_writeUint16BE(c->_stream, *(uint16_t *)p);
122 break;
123 case 4:
124 file_writeUint32BE(c->_stream, *(uint32_t *)p);
125 break;
126 }
127}
128
129void ser_loadInt(struct Serializer* c, uint8_t es, void *p) {
130 switch (es) {
131 case 1:
132 *(uint8_t *)p = file_readByte(c->_stream);
133 break;
134 case 2:
135 *(uint16_t *)p = file_readUint16BE(c->_stream);
136 break;
137 case 4:
138 *(uint32_t *)p = file_readUint32BE(c->_stream);
139 break;
140 }
141}
diff --git a/apps/plugins/xworld/serializer.h b/apps/plugins/xworld/serializer.h
new file mode 100644
index 0000000000..fde8f6d076
--- /dev/null
+++ b/apps/plugins/xworld/serializer.h
@@ -0,0 +1,84 @@
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#ifndef __SERIALIZER_H__
24#define __SERIALIZER_H__
25
26#include "intern.h"
27
28#define CUR_VER 2
29
30#define VER(x) x
31
32enum EntryType {
33 SET_INT,
34 SET_ARRAY,
35 SET_PTR,
36 SET_END
37};
38
39#define SE_INT(i,sz,ver) { SET_INT, sz, 1, i, ver, CUR_VER }
40#define SE_ARRAY(a,n,sz,ver) { SET_ARRAY, sz, n, a, ver, CUR_VER }
41#define SE_PTR(p,ver) { SET_PTR, 0, 0, p, ver, CUR_VER }
42#define SE_END() { SET_END, 0, 0, 0, 0, 0 }
43
44struct File;
45
46enum {
47 SES_BOOL = 1,
48 SES_INT8 = 1,
49 SES_INT16 = 2,
50 SES_INT32 = 4
51};
52
53enum Mode {
54 SM_SAVE,
55 SM_LOAD
56};
57
58struct Entry {
59 enum EntryType type;
60 uint8_t size;
61 uint16_t n;
62 void *data;
63 uint16_t minVer;
64 uint16_t maxVer;
65};
66
67struct Serializer {
68 File *_stream;
69 enum Mode _mode;
70 uint8_t *_ptrBlock;
71 uint16_t _saveVer;
72 uint32_t _bytesCount;
73};
74
75void ser_create(struct Serializer*, File *stream, enum Mode mode, uint8_t *ptrBlock, uint16_t saveVer);
76
77void ser_saveOrLoadEntries(struct Serializer*, struct Entry *entry);
78
79void ser_saveEntries(struct Serializer*, struct Entry *entry);
80void ser_loadEntries(struct Serializer*, struct Entry *entry);
81
82void ser_saveInt(struct Serializer*, uint8_t es, void *p);
83void ser_loadInt(struct Serializer*, uint8_t es, void *p);
84#endif
diff --git a/apps/plugins/xworld/sfxplayer.c b/apps/plugins/xworld/sfxplayer.c
new file mode 100644
index 0000000000..9bdc143d02
--- /dev/null
+++ b/apps/plugins/xworld/sfxplayer.c
@@ -0,0 +1,247 @@
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 "sfxplayer.h"
24#include "mixer.h"
25#include "resource.h"
26#include "serializer.h"
27#include "sys.h"
28
29void player_create(struct SfxPlayer* sfx, struct Mixer *mix, struct Resource *res, struct System *stub)
30{
31 sfx->mixer = mix;
32 sfx->res = res;
33 sfx->sys = stub;
34 sfx->_delay = 0;
35 sfx->_resNum = 0;
36}
37
38void player_init(struct SfxPlayer* sfx) {
39 debug(DBG_SND, "sys is 0x%08x", sfx->sys);
40 sfx->_mutex = sys_createMutex(sfx->sys);
41}
42
43void player_free(struct SfxPlayer* sfx) {
44 player_stop(sfx);
45 sys_destroyMutex(sfx->sys, sfx->_mutex);
46}
47
48void player_setEventsDelay(struct SfxPlayer* sfx, uint16_t delay) {
49 debug(DBG_SND, "player_setEventsDelay(%d)", delay);
50 struct MutexStack_t ms;
51 MutexStack(&ms, sfx->sys, sfx->_mutex);
52 sfx->_delay = delay * 60 / 7050;
53 MutexStack_destroy(&ms);
54}
55
56void player_loadSfxModule(struct SfxPlayer* sfx, uint16_t resNum, uint16_t delay, uint8_t pos) {
57
58 debug(DBG_SND, "player_loadSfxModule(0x%X, %d, %d)", resNum, delay, pos);
59 struct MutexStack_t ms;
60 MutexStack(&ms, sfx->sys, sfx->_mutex);
61
62
63 struct MemEntry *me = &sfx->res->_memList[resNum];
64
65 if (me->state == MEMENTRY_STATE_LOADED && me->type == RT_MUSIC) {
66 sfx->_resNum = resNum;
67 rb->memset(&sfx->_sfxMod, 0, sizeof(struct SfxModule));
68 sfx->_sfxMod.curOrder = pos;
69 sfx->_sfxMod.numOrder = READ_BE_UINT16(me->bufPtr + 0x3E);
70 debug(DBG_SND, "player_loadSfxModule() curOrder = 0x%X numOrder = 0x%X", sfx->_sfxMod.curOrder, sfx->_sfxMod.numOrder);
71 for (int i = 0; i < 0x80; ++i) {
72 sfx->_sfxMod.orderTable[i] = *(me->bufPtr + 0x40 + i);
73 }
74 if (delay == 0) {
75 sfx->_delay = READ_BE_UINT16(me->bufPtr);
76 } else {
77 sfx->_delay = delay;
78 }
79 sfx->_delay = sfx->_delay * 60 / 7050;
80 sfx->_sfxMod.data = me->bufPtr + 0xC0;
81 debug(DBG_SND, "player_loadSfxModule() eventDelay = %d ms", sfx->_delay);
82 player_prepareInstruments(sfx, me->bufPtr + 2);
83 } else {
84 warning("player_loadSfxModule() ec=0x%X", 0xF8);
85 }
86 MutexStack_destroy(&ms);
87}
88
89void player_prepareInstruments(struct SfxPlayer* sfx, const uint8_t *p) {
90
91 rb->memset(sfx->_sfxMod.samples, 0, sizeof(sfx->_sfxMod.samples));
92
93 for (int i = 0; i < 15; ++i) {
94 struct SfxInstrument *ins = &sfx->_sfxMod.samples[i];
95 uint16_t resNum = READ_BE_UINT16(p);
96 p += 2;
97 if (resNum != 0) {
98 ins->volume = READ_BE_UINT16(p);
99 struct MemEntry *me = &sfx->res->_memList[resNum];
100 if (me->state == MEMENTRY_STATE_LOADED && me->type == RT_SOUND) {
101 ins->data = me->bufPtr;
102 rb->memset(ins->data + 8, 0, 4);
103 debug(DBG_SND, "Loaded instrument 0x%X n=%d volume=%d", resNum, i, ins->volume);
104 } else {
105 error("Error loading instrument 0x%X", resNum);
106 }
107 }
108 p += 2; /* skip volume */
109 }
110}
111
112void player_start(struct SfxPlayer* sfx) {
113 debug(DBG_SND, "player_start()");
114 struct MutexStack_t ms;
115 MutexStack(&ms, sfx->sys, sfx->_mutex);
116 sfx->_sfxMod.curPos = 0;
117 sfx->_timerId = sys_addTimer(sfx->sys, sfx->_delay, player_eventsCallback, sfx);
118 MutexStack_destroy(&ms);
119}
120
121void player_stop(struct SfxPlayer* sfx) {
122 debug(DBG_SND, "player_stop()");
123 struct MutexStack_t ms;
124 MutexStack(&ms, sfx->sys, sfx->_mutex);
125 if (sfx->_resNum != 0) {
126 sfx->_resNum = 0;
127 sys_removeTimer(sfx->sys, sfx->_timerId);
128 }
129 MutexStack_destroy(&ms);
130}
131
132void player_handleEvents(struct SfxPlayer* sfx) {
133 struct MutexStack_t ms;
134 MutexStack(&ms, sfx->sys, sfx->_mutex);
135 uint8_t order = sfx->_sfxMod.orderTable[sfx->_sfxMod.curOrder];
136 const uint8_t *patternData = sfx->_sfxMod.data + sfx->_sfxMod.curPos + order * 1024;
137 for (uint8_t ch = 0; ch < 4; ++ch) {
138 player_handlePattern(sfx, ch, patternData);
139 patternData += 4;
140 }
141 sfx->_sfxMod.curPos += 4 * 4;
142 debug(DBG_SND, "player_handleEvents() order = 0x%X curPos = 0x%X", order, sfx->_sfxMod.curPos);
143 if (sfx->_sfxMod.curPos >= 1024) {
144 sfx->_sfxMod.curPos = 0;
145 order = sfx->_sfxMod.curOrder + 1;
146 if (order == sfx->_sfxMod.numOrder) {
147 sfx->_resNum = 0;
148 sys_removeTimer(sfx->sys, sfx->_timerId);
149 mixer_stopAll(sfx->mixer);
150 }
151 sfx->_sfxMod.curOrder = order;
152 }
153 MutexStack_destroy(&ms);
154}
155
156void player_handlePattern(struct SfxPlayer* sfx, uint8_t channel, const uint8_t *data) {
157 struct SfxPattern pat;
158 rb->memset(&pat, 0, sizeof(struct SfxPattern));
159 pat.note_1 = READ_BE_UINT16(data + 0);
160 pat.note_2 = READ_BE_UINT16(data + 2);
161 if (pat.note_1 != 0xFFFD) {
162 uint16_t sample = (pat.note_2 & 0xF000) >> 12;
163 if (sample != 0) {
164 uint8_t *ptr = sfx->_sfxMod.samples[sample - 1].data;
165 if (ptr != 0) {
166 debug(DBG_SND, "player_handlePattern() preparing sample %d", sample);
167 pat.sampleVolume = sfx->_sfxMod.samples[sample - 1].volume;
168 pat.sampleStart = 8;
169 pat.sampleBuffer = ptr;
170 pat.sampleLen = READ_BE_UINT16(ptr) * 2;
171 uint16_t loopLen = READ_BE_UINT16(ptr + 2) * 2;
172 if (loopLen != 0) {
173 pat.loopPos = pat.sampleLen;
174 pat.loopData = ptr;
175 pat.loopLen = loopLen;
176 } else {
177 pat.loopPos = 0;
178 pat.loopData = 0;
179 pat.loopLen = 0;
180 }
181 int16_t m = pat.sampleVolume;
182 uint8_t effect = (pat.note_2 & 0x0F00) >> 8;
183 if (effect == 5) { /* volume up */
184 uint8_t volume = (pat.note_2 & 0xFF);
185 m += volume;
186 if (m > 0x3F) {
187 m = 0x3F;
188 }
189 } else if (effect == 6) { /* volume down */
190 uint8_t volume = (pat.note_2 & 0xFF);
191 m -= volume;
192 if (m < 0) {
193 m = 0;
194 }
195 }
196 mixer_setChannelVolume(sfx->mixer, channel, m);
197 pat.sampleVolume = m;
198 }
199 }
200 }
201 if (pat.note_1 == 0xFFFD) {
202 debug(DBG_SND, "player_handlePattern() _scriptVars[0xF4] = 0x%X", pat.note_2);
203 *sfx->_markVar = pat.note_2;
204 } else if (pat.note_1 != 0) {
205 if (pat.note_1 == 0xFFFE) {
206 mixer_stopChannel(sfx->mixer, channel);
207 } else if (pat.sampleBuffer != 0) {
208 struct MixerChunk mc;
209 rb->memset(&mc, 0, sizeof(mc));
210 mc.data = pat.sampleBuffer + pat.sampleStart;
211 mc.len = pat.sampleLen;
212 mc.loopPos = pat.loopPos;
213 mc.loopLen = pat.loopLen;
214 /* convert amiga period value to hz */
215 uint16_t freq = 7159092 / (pat.note_1 * 2);
216 debug(DBG_SND, "player_handlePattern() adding sample freq = 0x%X", freq);
217 mixer_playChannel(sfx->mixer, channel, &mc, freq, pat.sampleVolume);
218 }
219 }
220}
221
222uint32_t player_eventsCallback(uint32_t interval, void *param) {
223 (void) interval;
224 debug(DBG_SND, "player_eventsCallback with interval %d ms and param 0x%08x", interval, param);
225 struct SfxPlayer *p = (struct SfxPlayer *)param;
226 player_handleEvents(p);
227 return p->_delay;
228}
229
230void player_saveOrLoad(struct SfxPlayer* sfx, struct Serializer *ser) {
231 sys_lockMutex(sfx->sys, sfx->_mutex);
232 struct Entry entries[] = {
233 SE_INT(&sfx->_delay, SES_INT8, VER(2)),
234 SE_INT(&sfx->_resNum, SES_INT16, VER(2)),
235 SE_INT(&sfx->_sfxMod.curPos, SES_INT16, VER(2)),
236 SE_INT(&sfx->_sfxMod.curOrder, SES_INT8, VER(2)),
237 SE_END()
238 };
239 ser_saveOrLoadEntries(ser, entries);
240 sys_unlockMutex(sfx->sys, sfx->_mutex);
241 if (ser->_mode == SM_LOAD && sfx->_resNum != 0) {
242 uint16_t delay = sfx->_delay;
243 player_loadSfxModule(sfx, sfx->_resNum, 0, sfx->_sfxMod.curOrder);
244 sfx->_delay = delay;
245 sfx->_timerId = sys_addTimer(sfx->sys, sfx->_delay, player_eventsCallback, sfx);
246 }
247}
diff --git a/apps/plugins/xworld/sfxplayer.h b/apps/plugins/xworld/sfxplayer.h
new file mode 100644
index 0000000000..0bf92b93d0
--- /dev/null
+++ b/apps/plugins/xworld/sfxplayer.h
@@ -0,0 +1,88 @@
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#ifndef __SFXPLAYER_H__
24#define __SFXPLAYER_H__
25
26#include "intern.h"
27
28struct SfxInstrument {
29 uint8_t *data;
30 uint16_t volume;
31};
32
33struct SfxModule {
34 const uint8_t *data;
35 uint16_t curPos;
36 uint8_t curOrder;
37 uint8_t numOrder;
38 uint8_t orderTable[0x80];
39 struct SfxInstrument samples[15];
40};
41
42struct SfxPattern {
43 uint16_t note_1;
44 uint16_t note_2;
45 uint16_t sampleStart;
46 uint8_t *sampleBuffer;
47 uint16_t sampleLen;
48 uint16_t loopPos;
49 uint8_t *loopData;
50 uint16_t loopLen;
51 uint16_t sampleVolume;
52};
53
54struct Mixer;
55struct Resource;
56struct Serializer;
57struct System;
58
59struct SfxPlayer {
60 struct Mixer *mixer;
61 struct Resource *res;
62 struct System *sys;
63
64 void *_mutex;
65 void *_timerId;
66 uint16_t _delay;
67 uint16_t _resNum;
68 struct SfxModule _sfxMod;
69 int16_t *_markVar;
70};
71
72void player_create(struct SfxPlayer*, struct Mixer *mix, struct Resource *res, struct System *stub);
73void player_init(struct SfxPlayer*);
74void player_free(struct SfxPlayer*);
75
76void player_setEventsDelay(struct SfxPlayer*, uint16_t delay);
77void player_loadSfxModule(struct SfxPlayer*, uint16_t resNum, uint16_t delay, uint8_t pos);
78void player_prepareInstruments(struct SfxPlayer*, const uint8_t *p);
79void player_start(struct SfxPlayer*);
80void player_stop(struct SfxPlayer*);
81void player_handleEvents(struct SfxPlayer*);
82void player_handlePattern(struct SfxPlayer*, uint8_t channel, const uint8_t *patternData);
83
84uint32_t player_eventsCallback(uint32_t interval, void *param);
85
86void player_saveOrLoad(struct SfxPlayer*, struct Serializer *ser);
87
88#endif
diff --git a/apps/plugins/xworld/sys.c b/apps/plugins/xworld/sys.c
new file mode 100644
index 0000000000..4bcdfafebd
--- /dev/null
+++ b/apps/plugins/xworld/sys.c
@@ -0,0 +1,942 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Franklin Wei, Benjamin Brown
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ***************************************************************************/
21
22/* TODO: */
23/* vertical stride support (as of Dec. 2014, only the M:Robe 500 has a color,
24 vertical stride LCD) */
25
26/* monochrome/grayscale support (many of these targets have vertical strides,
27 so get that working first!) */
28
29#include "plugin.h"
30#include "lib/display_text.h"
31#include "lib/helper.h"
32#include "lib/playback_control.h"
33#include "lib/pluginlib_actions.h"
34#include "lib/pluginlib_bmp.h"
35#include "lib/pluginlib_exit.h"
36#include "sys.h"
37#include "parts.h"
38#include "engine.h"
39#include "keymaps.h"
40
41static struct System* save_sys;
42
43static bool sys_save_settings(struct System* sys)
44{
45 File f;
46 file_create(&f, false);
47 if(!file_open(&f, SETTINGS_FILE, sys->e->_saveDir, "wb"))
48 {
49 return false;
50 }
51 file_write(&f, &sys->settings, sizeof(sys->settings));
52 file_close(&f);
53 return true;
54}
55
56static void sys_rotate_keymap(struct System* sys)
57{
58 switch(sys->settings.rotation_option)
59 {
60 case 0:
61 sys->keymap.up = BTN_UP;
62 sys->keymap.down = BTN_DOWN;
63 sys->keymap.left = BTN_LEFT;
64 sys->keymap.right = BTN_RIGHT;
65#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
66 sys->keymap.upleft = BTN_UP_LEFT;
67 sys->keymap.upright = BTN_UP_RIGHT;
68 sys->keymap.downleft = BTN_DOWN_RIGHT;
69 sys->keymap.downright = BTN_DOWN_LEFT;
70#endif
71 break;
72 case 1:
73 sys->keymap.up = BTN_RIGHT;
74 sys->keymap.down = BTN_LEFT;
75 sys->keymap.left = BTN_UP;
76 sys->keymap.right = BTN_DOWN;
77#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
78 sys->keymap.upleft = BTN_UP_RIGHT;
79 sys->keymap.upright = BTN_DOWN_RIGHT;
80 sys->keymap.downleft = BTN_UP_LEFT;
81 sys->keymap.downright = BTN_DOWN_LEFT;
82#endif
83 break;
84 case 2:
85 sys->keymap.up = BTN_LEFT;
86 sys->keymap.down = BTN_RIGHT;
87 sys->keymap.left = BTN_DOWN;
88 sys->keymap.right = BTN_UP;
89#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
90 sys->keymap.upleft = BTN_DOWN_LEFT;
91 sys->keymap.upright = BTN_UP_LEFT;
92 sys->keymap.downleft = BTN_DOWN_RIGHT;
93 sys->keymap.downright = BTN_DOWN_LEFT;
94#endif
95 break;
96 default:
97 error("sys_rotate_keymap: fall-through!");
98 }
99}
100
101static bool sys_load_settings(struct System* sys)
102{
103 File f;
104 file_create(&f, false);
105 if(!file_open(&f, SETTINGS_FILE, sys->e->_saveDir, "rb"))
106 {
107 return false;
108 }
109 file_read(&f, &sys->settings, sizeof(sys->settings));
110 file_close(&f);
111 sys_rotate_keymap(sys);
112 return true;
113}
114
115void exit_handler(void)
116{
117 sys_save_settings(save_sys);
118 sys_stopAudio(save_sys);
119#ifdef HAVE_ADJUSTABLE_CPU_FREQ
120 rb->cpu_boost(false);
121#endif
122 backlight_use_settings();
123}
124
125static bool sys_do_help(void)
126{
127#ifdef HAVE_LCD_COLOR
128 rb->lcd_set_foreground(LCD_WHITE);
129 rb->lcd_set_background(LCD_BLACK);
130#endif
131 rb->lcd_setfont(FONT_UI);
132 char* help_text[] = {"XWorld", "",
133 "XWorld", "is", "an", "interpreter", "for", "Another", "World,", "a", "fantastic", "game", "by", "Eric", "Chahi."
134 };
135 struct style_text style[] = {
136 {0, TEXT_CENTER | TEXT_UNDERLINE},
137 LAST_STYLE_ITEM
138 };
139 return display_text(ARRAYLEN(help_text), help_text, style, NULL, true);
140}
141
142static const struct opt_items scaling_settings[3] = {
143 { "Disabled", -1 },
144 { "Fast" , -1 },
145#ifdef HAVE_LCD_COLOR
146 { "Good" , -1 }
147#endif
148};
149
150static const struct opt_items rotation_settings[3] = {
151 { "Disabled" , -1 },
152 { "Clockwise" , -1 },
153 { "Anticlockwise", -1 }
154};
155
156static void do_video_settings(struct System* sys)
157{
158 MENUITEM_STRINGLIST(menu, "Video Settings", NULL,
159 "Negative",
160 "Scaling",
161 "Rotation",
162 "Show FPS",
163 "Zoom on code",
164 "Back");
165 int sel = 0;
166 while(1)
167 {
168 switch(rb->do_menu(&menu, &sel, NULL, false))
169 {
170 case 0:
171 rb->set_bool("Negative", &sys->settings.negative_enabled);
172 break;
173 case 1:
174 rb->set_option("Scaling", &sys->settings.scaling_quality, INT, scaling_settings,
175#ifdef HAVE_LCD_COLOR
176 3
177#else
178 2
179#endif
180 , NULL);
181 if(sys->settings.scaling_quality &&
182 sys->settings.zoom)
183 {
184 rb->splash(HZ*2, "Zoom automatically disabled.");
185 sys->settings.zoom = false;
186 }
187 break;
188 case 2:
189 rb->set_option("Rotation", &sys->settings.rotation_option, INT, rotation_settings, 3, NULL);
190 if(sys->settings.rotation_option &&
191 sys->settings.zoom)
192 {
193 rb->splash(HZ*2, "Zoom automatically disabled.");
194 sys->settings.zoom = false;
195 }
196 sys_rotate_keymap(sys);
197 break;
198 case 3:
199 rb->set_bool("Show FPS", &sys->settings.showfps);
200 break;
201 case 4:
202 rb->set_bool("Zoom on code", &sys->settings.zoom);
203 /* zoom only works with scaling and rotation disabled */
204 if(sys->settings.zoom &&
205 ( sys->settings.scaling_quality |
206 sys->settings.rotation_option))
207 {
208 rb->splash(HZ*2, "Scaling and rotation automatically disabled.");
209 sys->settings.scaling_quality = 0;
210 sys->settings.rotation_option = 0;
211 }
212 break;
213 case 5:
214 rb->lcd_clear_display();
215 sys_save_settings(sys);
216 return;
217 }
218 }
219}
220
221#define MAX_SOUNDBUF_SIZE 512
222const struct opt_items sound_bufsize_options[] = {
223 {"8 samples" , 8},
224 {"16 samples" , 16},
225 {"32 samples" , 32},
226 {"64 samples" , 64},
227 {"128 samples", 128},
228 {"256 samples", 256},
229 {"512 samples", 512},
230};
231
232static void do_sound_settings(struct System* sys)
233{
234 MENUITEM_STRINGLIST(menu, "Sound Settings", NULL,
235 "Enabled",
236 "Buffer Level",
237 "Back",
238 );
239 int sel = 0;
240 while(1)
241 {
242 switch(rb->do_menu(&menu, &sel, NULL, false))
243 {
244 case 0:
245 rb->set_bool("Enabled", &sys->settings.sound_enabled);
246 break;
247 case 1:
248 rb->set_option("Buffer Level", &sys->settings.sound_bufsize, INT,
249 sound_bufsize_options, ARRAYLEN(sound_bufsize_options), NULL);
250 break;
251 case 2:
252 sys_save_settings(sys);
253 return;
254 }
255 }
256}
257
258static void sys_reset_settings(struct System* sys)
259{
260 sys->settings.negative_enabled = false;
261 sys->settings.rotation_option = 0;
262 sys->settings.scaling_quality = 1;
263 sys->settings.sound_enabled = true;
264 sys->settings.sound_bufsize = 64;
265 sys->settings.showfps = true;
266 sys->settings.zoom = false;
267 sys_rotate_keymap(sys);
268}
269
270static struct System* mainmenu_sysptr;
271
272static int mainmenu_cb(int action, const struct menu_item_ex *this_item)
273{
274 int idx = ((intptr_t)this_item);
275 if(action == ACTION_REQUEST_MENUITEM && !mainmenu_sysptr->loaded && (idx == 0 || idx == 8 || idx == 10))
276 return ACTION_EXIT_MENUITEM;
277 return action;
278}
279
280static AudioCallback audio_callback;
281static void* audio_param;
282static struct System* audio_sys;
283
284/************************************** MAIN MENU ***************************************/
285
286void sys_menu(struct System* sys)
287{
288 sys_stopAudio(sys);
289 rb->splash(0, "Loading...");
290 sys->loaded = engine_loadGameState(sys->e, 0);
291 rb->lcd_update();
292 mainmenu_sysptr = sys;
293 int sel = 0;
294 MENUITEM_STRINGLIST(menu, "XWorld Menu", mainmenu_cb,
295 "Resume Game", /* 0 */
296 "Start New Game", /* 1 */
297 "Playback Control", /* 2 */
298 "Video Settings", /* 3 */
299 "Sound Settings", /* 4 */
300 "Fast Mode", /* 5 */
301 "Help", /* 6 */
302 "Reset Settings", /* 7 */
303 "Load", /* 8 */
304 "Save", /* 9 */
305 "Quit without Saving", /* 10 */
306 "Save and Quit"); /* 11 */
307 bool quit = false;
308 while(!quit)
309 {
310 int item;
311 switch(item = rb->do_menu(&menu, &sel, NULL, false))
312 {
313 case 0:
314 quit = true;
315 break;
316 case 1:
317 vm_initForPart(&sys->e->vm, GAME_PART_FIRST); // This game part is the protection screen
318 quit = true;
319 break;
320 case 2:
321 playback_control(NULL);
322 break;
323 case 3:
324 do_video_settings(sys);
325 break;
326 case 4:
327 do_sound_settings(sys);
328 break;
329 case 5:
330 rb->set_bool("Fast Mode", &sys->e->vm._fastMode);
331 sys_save_settings(sys);
332 break;
333 case 6:
334 sys_do_help();
335 break;
336 case 7:
337 sys_reset_settings(sys);
338 sys_save_settings(sys);
339 break;
340 case 8:
341 rb->splash(0, "Loading...");
342 sys->loaded = engine_loadGameState(sys->e, 0);
343 rb->lcd_update();
344 break;
345 case 9:
346 sys->e->_stateSlot = 0;
347 rb->splash(0, "Saving...");
348 engine_saveGameState(sys->e, sys->e->_stateSlot, "quicksave");
349 rb->lcd_update();
350 break;
351 case 10:
352 engine_deleteGameState(sys->e, 0);
353 exit(PLUGIN_OK);
354 break;
355 case 11:
356 /* saves are NOT deleted on loading */
357 exit(PLUGIN_OK);
358 break;
359 default:
360 error("sys_menu: fall-through!");
361 }
362 }
363 rb->lcd_clear_display();
364 sys_startAudio(sys, audio_callback, audio_param);
365}
366
367void sys_init(struct System* sys, const char* title)
368{
369 (void) title;
370#ifdef HAVE_ADJUSTABLE_CPU_FREQ
371 rb->cpu_boost(true);
372#endif
373 backlight_ignore_timeout();
374 rb_atexit(exit_handler);
375 save_sys = sys;
376 rb->memset(&sys->input, 0, sizeof(sys->input));
377 sys->mutex_bitfield = ~0;
378 if(!sys_load_settings(sys))
379 {
380 sys_reset_settings(sys);
381 }
382}
383
384void sys_destroy(struct System* sys)
385{
386 (void) sys;
387 rb->splash(HZ, "Exiting...");
388}
389
390void sys_setPalette(struct System* sys, uint8_t start, uint8_t n, const uint8_t *buf)
391{
392 for(int i = start; i < start + n; ++i)
393 {
394 uint8_t c[3];
395 for (int j = 0; j < 3; j++) {
396 uint8_t col = buf[i * 3 + j];
397 c[j] = (col << 2) | (col & 3);
398 }
399#if (LCD_DEPTH > 16) && (LCD_DEPTH <= 24)
400 sys->palette[i] = (fb_data) {
401 c[2], c[1], c[0]
402 };
403#elif defined(HAVE_LCD_COLOR)
404 sys->palette[i] = FB_RGBPACK(c[0], c[1], c[2]);
405#elif LCD_DEPTH > 1
406 sys->palette[i] = LCD_BRIGHTNESS((c[0] + c[1] + c[2]) / 3);
407#endif
408 }
409}
410
411/****************************** THE MAIN DRAWING METHOD ********************************/
412
413void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint8_t *buf, uint32_t pitch)
414{
415 static int last_timestamp = 1;
416
417 /* get the address of the temporary framebuffer that has been allocated in the audiobuf */
418 fb_data* framebuffer = (fb_data*) sys->e->res._memPtrStart + MEM_BLOCK_SIZE + (4 * VID_PAGE_SIZE);
419 buf += y * pitch + x;
420
421 /************************ BLIT THE TEMPORARY FRAMEBUFFER ***********************/
422
423 if(sys->settings.rotation_option)
424 {
425 /* clockwise */
426 if(sys->settings.rotation_option == 1)
427 {
428 while (h--) {
429 /* one byte gives two pixels */
430 for (int i = 0; i < w / 2; ++i) {
431 uint8_t pix1 = *(buf + i) >> 4;
432 uint8_t pix2 = *(buf + i) & 0xF;
433#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
434 framebuffer[( (h * 2) ) * 320 + i] = sys->palette[pix1];
435 framebuffer[( (h * 2) + 1) * 320 + i] = sys->palette[pix2];
436#else
437 framebuffer[( (i * 2) ) * 200 + h] = sys->palette[pix1];
438 framebuffer[( (i * 2) + 1) * 200 + h] = sys->palette[pix2];
439#endif
440 }
441 buf += pitch;
442 }
443 }
444 /* counterclockwise */
445 else
446 {
447 while (h--) {
448 /* one byte gives two pixels */
449 for (int i = 0; i < w / 2; ++i) {
450 uint8_t pix1 = *(buf + i) >> 4;
451 uint8_t pix2 = *(buf + i) & 0xF;
452#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
453 framebuffer[(200 - h * 2 ) * 320 + ( 320 - i )] = sys->palette[pix1];
454 framebuffer[(200 - h * 2 - 1) * 320 + ( 320 - i )] = sys->palette[pix2];
455#else
456 framebuffer[(320 - i * 2 ) * 200 + ( 200 - h )] = sys->palette[pix1];
457 framebuffer[(320 - i * 2 - 1) * 200 + ( 200 - h )] = sys->palette[pix2];
458#endif
459 }
460 buf += pitch;
461 }
462 }
463 }
464 /* no rotation */
465 else
466 {
467 int next = 0;
468#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
469 for(int x = 0; x < w / 2; ++x)
470 {
471 for(int y = 0; y < h; ++y)
472 {
473 uint8_t pix1 = buf[ y * w + x ] >> 4;
474 uint8_t pix2 = buf[ y * w + x ] & 0xF;
475 framebuffer[(x + 0)*h + y] = sys->palette[pix1];
476 framebuffer[(x + 1)*h + y] = sys->palette[pix2];
477 }
478 }
479#else
480 while (h--) {
481 /* one byte gives two pixels */
482 for (int i = 0; i < w / 2; ++i) {
483 uint8_t pix1 = *(buf + i) >> 4;
484 uint8_t pix2 = *(buf + i) & 0xF;
485 framebuffer[next] = sys->palette[pix1];
486 ++next;
487 framebuffer[next] = sys->palette[pix2];
488 ++next;
489 }
490 buf += pitch;
491 }
492#endif
493 }
494
495 /*************************** NOW SCALE IT! ***************************/
496
497 if(sys->settings.scaling_quality)
498 {
499 struct bitmap in_bmp;
500 if(sys->settings.rotation_option)
501 {
502#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
503 in_bmp.width = 320;
504 in_bmp.height = 200;
505#else
506 in_bmp.width = 200;
507 in_bmp.height = 320;
508#endif
509 }
510 else
511 {
512#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
513 in_bmp.width = 200;
514 in_bmp.height = 320;
515#else
516 in_bmp.width = 320;
517 in_bmp.height = 200;
518#endif
519 }
520 in_bmp.data = (unsigned char*) framebuffer;
521 struct bitmap out_bmp;
522 out_bmp.width = LCD_WIDTH;
523 out_bmp.height = LCD_HEIGHT;
524 out_bmp.data = (unsigned char*) rb->lcd_framebuffer;
525
526#ifdef HAVE_LCD_COLOR
527 if(sys->settings.scaling_quality == 1)
528#endif
529 simple_resize_bitmap(&in_bmp, &out_bmp);
530#ifdef HAVE_LCD_COLOR
531 else
532 smooth_resize_bitmap(&in_bmp, &out_bmp);
533#endif
534 }
535 else
536 {
537#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
538 for(int x = 0; x < 320; ++x)
539 {
540 for(int y = 0; y < 200; ++y)
541 {
542 rb->lcd_set_foreground(framebuffer[x * 200 + y]);
543 rb->lcd_drawpixel(x, y);
544 }
545 }
546#else
547 if(sys->settings.zoom)
548 {
549 rb->lcd_bitmap_part(framebuffer, CODE_X, CODE_Y, STRIDE(SCREEN_MAIN, 320, 200), 0, 0, 320 - CODE_X, 200 - CODE_Y);
550 }
551 else
552 {
553 if(sys->settings.rotation_option)
554 rb->lcd_bitmap(framebuffer, 0, 0, 200, 320);
555 else
556 rb->lcd_bitmap(framebuffer, 0, 0, 320, 200);
557 }
558#endif
559 }
560
561 /************************************* APPLY FILTERS ******************************************/
562
563 if(sys->settings.negative_enabled)
564 {
565 for(int y = 0; y < LCD_HEIGHT; ++y)
566 {
567 for(int x = 0; x < LCD_WIDTH; ++x)
568 {
569#ifdef HAVE_LCD_COLOR
570 int r, g, b;
571 fb_data pix = rb->lcd_framebuffer[y * LCD_WIDTH + x];
572 r = RGB_UNPACK_RED (pix);
573 g = RGB_UNPACK_GREEN(pix);
574 b = RGB_UNPACK_BLUE (pix);
575 rb->lcd_framebuffer[y * LCD_WIDTH + x] = LCD_RGBPACK(0xff - r, 0xff - g, 0xff - b);
576#else
577 rb->lcd_framebuffer[y * LCD_WIDTH + x] = LCD_BRIGHTNESS(0xff - rb->lcd_framebuffer[y * LCD_WIDTH + x]);
578#endif
579 }
580 }
581 }
582
583 /*********************** SHOW FPS *************************/
584
585 int current_time = sys_getTimeStamp(sys);
586 if(sys->settings.showfps)
587 {
588 rb->lcd_set_foreground(LCD_BLACK);
589 rb->lcd_set_background(LCD_WHITE);
590 /* use 1000 and not HZ here because getTimeStamp is in milliseconds */
591 rb->lcd_putsf(0, 0, "FPS: %d", 1000 / ((current_time - last_timestamp == 0) ? 1 : current_time - last_timestamp));
592 }
593 rb->lcd_update();
594 last_timestamp = sys_getTimeStamp(sys);
595}
596
597static void do_pause_menu(struct System* sys)
598{
599 sys_stopAudio(sys);
600 int sel = 0;
601 MENUITEM_STRINGLIST(menu, "XWorld Menu", NULL,
602 "Resume Game", /* 0 */
603 "Start New Game", /* 1 */
604 "Playback Control", /* 2 */
605 "Video Settings", /* 3 */
606 "Sound Settings", /* 4 */
607 "Fast Mode", /* 5 */
608 "Enter Code", /* 6 */
609 "Help", /* 7 */
610 "Reset Settings", /* 8 */
611 "Load", /* 9 */
612 "Save", /* 10 */
613 "Quit"); /* 11 */
614
615 bool quit = false;
616 while(!quit)
617 {
618 switch(rb->do_menu(&menu, &sel, NULL, false))
619 {
620 case 0:
621 quit = true;
622 break;
623 case 1:
624 vm_initForPart(&sys->e->vm, GAME_PART_FIRST);
625 quit = true;
626 break;
627 case 2:
628 playback_control(NULL);
629 break;
630 case 3:
631 do_video_settings(sys);
632 break;
633 case 4:
634 do_sound_settings(sys);
635 break;
636 case 5:
637 rb->set_bool("Fast Mode", &sys->e->vm._fastMode);
638 sys_save_settings(sys);
639 break;
640 case 6:
641 sys->input.code = true;
642 quit = true;
643 break;
644 case 7:
645 sys_do_help();
646 break;
647 case 8:
648 sys_reset_settings(sys);
649 sys_save_settings(sys);
650 break;
651 case 9:
652 rb->splash(0, "Loading...");
653 sys->loaded = engine_loadGameState(sys->e, 0);
654 rb->lcd_update();
655 quit = true;
656 break;
657 case 10:
658 sys->e->_stateSlot = 0;
659 rb->splash(0, "Saving...");
660 engine_saveGameState(sys->e, sys->e->_stateSlot, "quicksave");
661 rb->lcd_update();
662 break;
663 case 11:
664 exit(PLUGIN_OK);
665 break;
666 }
667 }
668 rb->lcd_clear_display();
669 sys_startAudio(sys, audio_callback, audio_param);
670}
671
672void sys_processEvents(struct System* sys)
673{
674 int btn = rb->button_get(false);
675 btn &= ~BUTTON_REDRAW;
676 debug(DBG_SYS, "button is 0x%08x", btn);
677
678 /* exit early if we can */
679 if(btn == BUTTON_NONE)
680 {
681 return;
682 }
683
684 /* Ignore some buttons that cause errant input */
685#if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
686 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
687 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
688 if(btn & 0x80000000)
689 return;
690#endif
691#if (CONFIG_KEYPAD == SANSA_E200_PAD)
692 if(btn == (BUTTON_SCROLL_FWD || BUTTON_SCROLL_BACK))
693 return;
694#endif
695#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
696 if(btn == (BUTTON_SELECT))
697 return;
698#endif
699
700 /* handle special keys first */
701 switch(btn)
702 {
703 case BTN_PAUSE:
704 do_pause_menu(sys);
705 sys->input.dirMask = 0;
706 sys->input.button = false;
707 /* exit early to avoid unwanted button presses being detected */
708 return;
709 default:
710 exit_on_usb(btn);
711 break;
712 }
713
714 /* handle releases */
715 if(btn & BUTTON_REL)
716 {
717 debug(DBG_SYS, "button_rel");
718 btn &= ~BUTTON_REL;
719
720 if(btn & BTN_FIRE)
721 sys->input.button = false;
722 if(btn & sys->keymap.up)
723 sys->input.dirMask &= ~DIR_UP;
724 if(btn & sys->keymap.down)
725 sys->input.dirMask &= ~DIR_DOWN;
726 if(btn & sys->keymap.left)
727 sys->input.dirMask &= ~DIR_LEFT;
728 if(btn & sys->keymap.right)
729 sys->input.dirMask &= ~DIR_RIGHT;
730#ifdef BTN_DOWN_LEFT
731 if(btn & sys->keymap.downleft)
732 sys->input.dirMask &= ~(DIR_DOWN | DIR_LEFT);
733#endif
734#ifdef BTN_DOWN_RIGHT
735 if(btn & sys->keymap.downright)
736 sys->input.dirMask &= ~(DIR_DOWN | DIR_RIGHT);
737#endif
738#ifdef BTN_UP_LEFT
739 if(btn & sys->keymap.upleft)
740 sys->input.dirMask &= ~(DIR_UP | DIR_LEFT);
741#endif
742#ifdef BTN_UP_RIGHT
743 if(btn & sys->keymap.upright)
744 sys->input.dirMask &= ~(DIR_UP | DIR_RIGHT);
745#endif
746 }
747 /* then handle presses */
748 else
749 {
750 if(btn & BTN_FIRE)
751 sys->input.button = true;
752 if(btn & sys->keymap.up)
753 sys->input.dirMask |= DIR_UP;
754 if(btn & sys->keymap.down)
755 sys->input.dirMask |= DIR_DOWN;
756 if(btn & sys->keymap.left)
757 sys->input.dirMask |= DIR_LEFT;
758 if(btn & sys->keymap.right)
759 sys->input.dirMask |= DIR_RIGHT;
760#ifdef BTN_DOWN_LEFT
761 if(btn & sys->keymap.downleft)
762 sys->input.dirMask |= (DIR_DOWN | DIR_LEFT);
763#endif
764#ifdef BTN_DOWN_RIGHT
765 if(btn & sys->keymap.downright)
766 sys->input.dirMask |= (DIR_DOWN | DIR_RIGHT);
767#endif
768#ifdef BTN_UP_LEFT
769 if(btn & sys->keymap.upleft)
770 sys->input.dirMask |= (DIR_UP | DIR_LEFT);
771#endif
772#ifdef BTN_UP_RIGHT
773 if(btn & sys->keymap.upright)
774 sys->input.dirMask |= (DIR_UP | DIR_RIGHT);
775#endif
776 }
777 debug(DBG_SYS, "dirMask is 0x%02x", sys->input.dirMask);
778 debug(DBG_SYS, "button is %s", sys->input.button == true ? "true" : "false");
779}
780
781void sys_sleep(struct System* sys, uint32_t duration)
782{
783 (void) sys;
784 /* duration is in ms */
785 rb->sleep(duration / 10);
786}
787
788uint32_t sys_getTimeStamp(struct System* sys)
789{
790 (void) sys;
791 return (uint32_t) (*rb->current_tick) * 10;
792}
793
794static int16_t rb_soundbuf [MAX_SOUNDBUF_SIZE] IBSS_ATTR;
795static int8_t temp_soundbuf[MAX_SOUNDBUF_SIZE] IBSS_ATTR;
796static void ICODE_ATTR get_more(const void** start, size_t* size)
797{
798 if(audio_sys->settings.sound_enabled)
799 {
800 audio_callback(audio_param, temp_soundbuf, audio_sys->settings.sound_bufsize);
801 /* convert xworld format (signed 8-bit) to rockbox format (signed 16-bit) */
802 for(int i = 0; i < audio_sys->settings.sound_bufsize; ++i)
803 {
804 rb_soundbuf[i] = temp_soundbuf[i] * 0x100;
805 }
806 *start = rb_soundbuf;
807 *size = audio_sys->settings.sound_bufsize;
808 }
809 else
810 {
811 *start = NULL;
812 *size = 0;
813 }
814}
815
816void sys_startAudio(struct System* sys, AudioCallback callback, void *param)
817{
818 (void) sys;
819 audio_callback = callback;
820 audio_param = param;
821 audio_sys = sys;
822 rb->pcm_play_data(get_more, NULL, NULL, 0);
823}
824
825void sys_stopAudio(struct System* sys)
826{
827 (void) sys;
828 rb->pcm_play_stop();
829}
830
831uint32_t sys_getOutputSampleRate(struct System* sys)
832{
833 (void) sys;
834 return rb->mixer_get_frequency();
835}
836
837/* pretty non-reentrant here, but who cares? it's not like someone
838 would want to run two instances of the game on Rockbox! :D */
839
840static uint32_t cur_delay;
841static void* cur_param;
842static TimerCallback user_callback;
843static void timer_callback(void)
844{
845 debug(DBG_SYS, "timer callback with delay of %d ms callback 0x%08x param 0x%08x", cur_delay, timer_callback, cur_param);
846 uint32_t ret = user_callback(cur_delay, cur_param);
847 if(ret != cur_delay)
848 {
849 cur_delay = ret;
850 rb->timer_register(1, NULL, TIMER_FREQ / 1000 * ret, timer_callback IF_COP(, CPU));
851 }
852}
853
854void *sys_addTimer(struct System* sys, uint32_t delay, TimerCallback callback, void *param)
855{
856 (void) sys;
857 debug(DBG_SYS, "registering timer with delay of %d ms callback 0x%08x param 0x%08x", delay, callback, param);
858 user_callback = callback;
859 cur_delay = delay;
860 cur_param = param;
861 rb->timer_register(1, NULL, TIMER_FREQ / 1000 * delay, timer_callback IF_COP(, CPU));
862 return NULL;
863}
864
865void sys_removeTimer(struct System* sys, void *timerId)
866{
867 (void) sys;
868 (void) timerId;
869 /* there's only one timer per game instance, so ignore both parameters */
870 rb->timer_unregister();
871}
872
873void *sys_createMutex(struct System* sys)
874{
875 if(!sys)
876 error("sys is NULL!");
877 for(int i = 0; i < MAX_MUTEXES; ++i)
878 {
879 if(sys->mutex_bitfield & (1 << i))
880 {
881 rb->mutex_init(&sys->mutex_memory[i]);
882 sys->mutex_bitfield |= (1 << i);
883 return &sys->mutex_memory[i];
884 }
885 }
886 warning("Out of mutexes!");
887 return NULL;
888}
889
890void sys_destroyMutex(struct System* sys, void *mutex)
891{
892 int mutex_number = ((char*)mutex - (char*)sys->mutex_memory) / sizeof(struct mutex); /* pointer arithmetic! check for bugs! */
893 sys->mutex_bitfield &= ~(1 << mutex_number);
894}
895
896void sys_lockMutex(struct System* sys, void *mutex)
897{
898 (void) sys;
899 debug(DBG_SYS, "calling mutex_lock");
900 rb->mutex_lock((struct mutex*) mutex);
901}
902
903void sys_unlockMutex(struct System* sys, void *mutex)
904{
905 (void) sys;
906 debug(DBG_SYS, "calling mutex_unlock");
907 rb->mutex_unlock((struct mutex*) mutex);
908}
909
910uint8_t* getOffScreenFramebuffer(struct System* sys)
911{
912 (void) sys;
913 return NULL;
914}
915
916void *sys_get_buffer(struct System* sys, size_t sz)
917{
918 if((signed int)sys->bytes_left - (signed int)sz >= 0)
919 {
920 void* ret = sys->membuf;
921 rb->memset(ret, 0, sz);
922 sys->membuf += sz;
923 return ret;
924 }
925 else
926 {
927 error("sys_get_buffer: out of memory!");
928 return NULL;
929 }
930}
931
932void MutexStack(struct MutexStack_t* s, struct System *stub, void *mutex)
933{
934 s->sys = stub;
935 s->_mutex = mutex;
936 sys_lockMutex(s->sys, s->_mutex);
937}
938
939void MutexStack_destroy(struct MutexStack_t* s)
940{
941 sys_unlockMutex(s->sys, s->_mutex);
942}
diff --git a/apps/plugins/xworld/sys.h b/apps/plugins/xworld/sys.h
new file mode 100644
index 0000000000..f1920acf37
--- /dev/null
+++ b/apps/plugins/xworld/sys.h
@@ -0,0 +1,146 @@
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#ifndef __XWORLD_SYS_H__
24#define __XWORLD_SYS_H__
25
26#include "intern.h"
27
28#define SYS_NEGATIVE_COLOR
29#define NUM_COLORS 16
30#define MAX_MUTEXES 16
31#define SETTINGS_FILE "settings.xfg"
32#define CODE_X 80
33#define CODE_Y 36
34
35enum {
36 DIR_LEFT = 1 << 0,
37 DIR_RIGHT = 1 << 1,
38 DIR_UP = 1 << 2,
39 DIR_DOWN = 1 << 3
40};
41
42struct PlayerInput {
43
44 uint8_t dirMask;
45 bool button;
46 bool code;
47 bool pause;
48 bool quit;
49 char lastChar;
50 bool save, load;
51 bool fastMode;
52 int8_t stateSlot;
53};
54
55
56struct keymapping_t {
57 int up;
58 int down;
59 int left;
60 int right;
61#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
62 int upleft;
63 int upright;
64 int downleft;
65 int downright;
66#endif
67};
68
69typedef void (*AudioCallback)(void *param, uint8_t *stream, int len);
70typedef uint32_t (*TimerCallback)(uint32_t delay, void *param);
71
72struct System {
73 struct mutex mutex_memory[MAX_MUTEXES];
74 uint16_t mutex_bitfield;
75 struct PlayerInput input;
76 fb_data palette[NUM_COLORS];
77 struct Engine* e;
78 struct keymapping_t keymap;
79
80 void *membuf;
81 size_t bytes_left;
82
83 bool loaded;
84
85 struct {
86 bool negative_enabled;
87 /*
88 scaling quality:
89 0: off
90 1: fast
91 2: good (color only)
92 */
93 int scaling_quality;
94 /*
95 rotation:
96 0: off
97 1: cw
98 2: ccw
99 */
100 int rotation_option;
101
102 bool showfps;
103 bool sound_enabled;
104 int sound_bufsize;
105 bool zoom;
106 } settings;
107};
108
109void sys_init(struct System*, const char *title);
110void sys_menu(struct System*);
111void sys_destroy(struct System*);
112
113void sys_setPalette(struct System*, uint8_t s, uint8_t n, const uint8_t *buf);
114void sys_copyRect(struct System*, uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint8_t *buf, uint32_t pitch);
115
116void sys_processEvents(struct System*);
117void sys_sleep(struct System*, uint32_t duration);
118uint32_t sys_getTimeStamp(struct System*);
119
120void sys_startAudio(struct System*, AudioCallback callback, void *param);
121void sys_stopAudio(struct System*);
122uint32_t sys_getOutputSampleRate(struct System*);
123
124void *sys_addTimer(struct System*, uint32_t delay, TimerCallback callback, void *param);
125void sys_removeTimer(struct System*, void *timerId);
126
127void *sys_createMutex(struct System*);
128void sys_destroyMutex(struct System*, void *mutex);
129void sys_lockMutex(struct System*, void *mutex);
130void sys_unlockMutex(struct System*, void *mutex);
131
132/* a quick 'n dirty function to get some bytes in the audio buffer after zeroing them */
133/* pretty much does the same thing as calloc, though much uglier and there's no free() */
134void *sys_get_buffer(struct System*, size_t);
135
136uint8_t* getOffScreenFramebuffer(struct System*);
137
138struct MutexStack_t {
139 struct System *sys;
140 void *_mutex;
141};
142
143void MutexStack(struct MutexStack_t*, struct System*, void*);
144void MutexStack_destroy(struct MutexStack_t*);
145
146#endif
diff --git a/apps/plugins/xworld/util.c b/apps/plugins/xworld/util.c
new file mode 100644
index 0000000000..b82691ad29
--- /dev/null
+++ b/apps/plugins/xworld/util.c
@@ -0,0 +1,82 @@
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 "lib/pluginlib_exit.h"
25#include <stdarg.h>
26#include "util.h"
27uint16_t g_debugMask;
28
29#ifdef XWORLD_DEBUG
30void debug_real(uint16_t cm, const char *msg, ...) {
31#ifdef ROCKBOX_HAS_LOGF
32 char buf[1024];
33 if (cm & g_debugMask) {
34 va_list va;
35 va_start(va, msg);
36 rb->vsnprintf(buf, 1024, msg, va);
37 va_end(va);
38 LOGF("%s", buf);
39 }
40#else
41 (void) cm;
42 (void) msg;
43#endif
44}
45#endif
46
47void error(const char *msg, ...) {
48 char buf[1024];
49 va_list va;
50 va_start(va, msg);
51 rb->vsnprintf(buf, 1024, msg, va);
52 va_end(va);
53 rb->splashf(HZ * 2, "ERROR: %s!", buf);
54 LOGF("ERROR: %s", buf);
55 exit(-1);
56}
57
58void warning(const char *msg, ...) {
59 char buf[1024];
60 va_list va;
61 va_start(va, msg);
62 rb->vsnprintf(buf, 1024, msg, va);
63 va_end(va);
64 rb->splashf(HZ * 2, "WARNING: %s!", buf);
65 LOGF("WARNING: %s", buf);
66}
67
68void string_lower(char *p) {
69 for (; *p; ++p) {
70 if (*p >= 'A' && *p <= 'Z') {
71 *p += 'a' - 'A';
72 }
73 }
74}
75
76void string_upper(char *p) {
77 for (; *p; ++p) {
78 if (*p >= 'a' && *p <= 'z') {
79 *p += 'A' - 'a';
80 }
81 }
82}
diff --git a/apps/plugins/xworld/util.h b/apps/plugins/xworld/util.h
new file mode 100644
index 0000000000..8852335661
--- /dev/null
+++ b/apps/plugins/xworld/util.h
@@ -0,0 +1,59 @@
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#ifndef __UTIL_H__
24#define __UTIL_H__
25
26#include "intern.h"
27
28/* #define XWORLD_DEBUG */
29
30#ifdef XWORLD_DEBUG
31#define debug(m,f,...) debug_real(m, f, ##__VA_ARGS__)
32#else
33#define debug(m,f,...)
34#endif
35
36enum {
37 DBG_VM = 1 << 0,
38 DBG_BANK = 1 << 1,
39 DBG_VIDEO = 1 << 2,
40 DBG_SND = 1 << 3,
41 DBG_SER = 1 << 4,
42 DBG_INFO = 1 << 5,
43 DBG_RES = 1 << 6,
44 DBG_FILE = 1 << 7,
45 DBG_SYS = 1 << 8,
46 DBG_ENG = 1 << 9
47};
48
49extern uint16_t g_debugMask;
50#ifdef XWORLD_DEBUG
51extern void debug_real(uint16_t cm, const char *msg, ...);
52#endif
53extern void error(const char *msg, ...);
54extern void warning(const char *msg, ...);
55
56extern void string_lower(char *p);
57extern void string_upper(char *p);
58
59#endif
diff --git a/apps/plugins/xworld/video.c b/apps/plugins/xworld/video.c
new file mode 100644
index 0000000000..b4182df7a7
--- /dev/null
+++ b/apps/plugins/xworld/video.c
@@ -0,0 +1,1141 @@
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 "video.h"
24#include "video_data.h"
25#include "resource.h"
26#include "serializer.h"
27#include "sys.h"
28#include "file.h"
29
30void polygon_readVertices(struct Polygon* g, const uint8_t *p, uint16_t zoom) {
31 g->bbw = (*p++) * zoom / 64;
32 g->bbh = (*p++) * zoom / 64;
33 g->numPoints = *p++;
34 assert((g->numPoints & 1) == 0 && g->numPoints < MAX_POINTS);
35
36 //Read all points, directly from bytecode segment
37 for (int i = 0; i < g->numPoints; ++i) {
38 struct Point *pt = &g->points[i];
39 pt->x = (*p++) * zoom / 64;
40 pt->y = (*p++) * zoom / 64;
41 }
42}
43
44void video_create(struct Video* v, struct Resource* res, struct System* sys)
45{
46 v->res = res;
47 v->sys = sys;
48}
49
50void video_init(struct Video* v) {
51 v->paletteIdRequested = NO_PALETTE_CHANGE_REQUESTED;
52 v->page_data = v->res->_memPtrStart + MEM_BLOCK_SIZE;
53
54 rb->memset(v->page_data, 0, 4 * VID_PAGE_SIZE);
55
56 for (int i = 0; i < 4; ++i) {
57 v->_pagePtrs[i] = v->page_data + i * VID_PAGE_SIZE;
58 }
59
60 v->_curPagePtr3 = video_getPagePtr(v, 1);
61 v->_curPagePtr2 = video_getPagePtr(v, 2);
62
63 video_changePagePtr1(v, 0xFE);
64
65 v->_interpTable[0] = 0x4000;
66
67 for (int i = 1; i < 0x400; ++i) {
68 v->_interpTable[i] = 0x4000 / i;
69 }
70}
71
72void video_setDataBuffer(struct Video* v, uint8_t *dataBuf, uint16_t offset) {
73
74 v->_dataBuf = dataBuf;
75 v->_pData.pc = dataBuf + offset;
76}
77
78
79/* A shape can be given in two different ways:
80
81 - A list of screenspace vertices.
82 - A list of objectspace vertices, based on a delta from the first vertex.
83
84 This is a recursive function. */
85void video_readAndDrawPolygon(struct Video* v, uint8_t color, uint16_t zoom, const struct Point *pt) {
86
87 uint8_t i = scriptPtr_fetchByte(&v->_pData);
88
89 //This is
90 if (i >= 0xC0) { // 0xc0 = 192
91
92 // WTF ?
93 if (color & 0x80) { //0x80 = 128 (1000 0000)
94 color = i & 0x3F; //0x3F = 63 (0011 1111)
95 }
96
97 // pc is misleading here since we are not reading bytecode but only
98 // vertices informations.
99 polygon_readVertices(&v->polygon, v->_pData.pc, zoom);
100
101 video_fillPolygon(v, color, zoom, pt);
102
103
104
105 } else {
106 i &= 0x3F; //0x3F = 63
107 if (i == 1) {
108 warning("video_readAndDrawPolygon() ec=0x%X (i != 2)", 0xF80);
109 } else if (i == 2) {
110 video_readAndDrawPolygonHierarchy(v, zoom, pt);
111
112 } else {
113 warning("video_readAndDrawPolygon() ec=0x%X (i != 2)", 0xFBB);
114 }
115 }
116
117
118
119}
120
121void video_fillPolygon(struct Video* v, uint16_t color, uint16_t zoom, const struct Point *pt) {
122
123 (void) zoom;
124
125 if (v->polygon.bbw == 0 && v->polygon.bbh == 1 && v->polygon.numPoints == 4) {
126 video_drawPoint(v, color, pt->x, pt->y);
127
128 return;
129 }
130
131 int16_t x1 = pt->x - v->polygon.bbw / 2;
132 int16_t x2 = pt->x + v->polygon.bbw / 2;
133 int16_t y1 = pt->y - v->polygon.bbh / 2;
134 int16_t y2 = pt->y + v->polygon.bbh / 2;
135
136 if (x1 > 319 || x2 < 0 || y1 > 199 || y2 < 0)
137 return;
138
139 v->_hliney = y1;
140
141 uint16_t i, j;
142 i = 0;
143 j = v->polygon.numPoints - 1;
144
145 x2 = v->polygon.points[i].x + x1;
146 x1 = v->polygon.points[j].x + x1;
147
148 ++i;
149 --j;
150
151 drawLine drawFct;
152 if (color < 0x10) {
153 drawFct = &video_drawLineN;
154 } else if (color > 0x10) {
155 drawFct = &video_drawLineP;
156 } else {
157 drawFct = &video_drawLineBlend;
158 }
159
160 uint32_t cpt1 = x1 << 16;
161 uint32_t cpt2 = x2 << 16;
162
163 while (1) {
164 v->polygon.numPoints -= 2;
165 if (v->polygon.numPoints == 0) {
166#if TRACE_FRAMEBUFFER
167 video_dumpFrameBuffers(v, "fillPolygonEnd");
168#endif
169#if TRACE_BG_BUFFER
170 video_dumpBackGroundBuffer(v);
171#endif
172 break;
173 }
174 uint16_t h;
175 int32_t step1 = video_calcStep(v, &v->polygon.points[j + 1], &v->polygon.points[j], &h);
176 int32_t step2 = video_calcStep(v, &v->polygon.points[i - 1], &v->polygon.points[i], &h);
177
178 ++i;
179 --j;
180
181 cpt1 = (cpt1 & 0xFFFF0000) | 0x7FFF;
182 cpt2 = (cpt2 & 0xFFFF0000) | 0x8000;
183
184 if (h == 0) {
185 cpt1 += step1;
186 cpt2 += step2;
187 } else {
188 for (; h != 0; --h) {
189 if (v->_hliney >= 0) {
190 x1 = cpt1 >> 16;
191 x2 = cpt2 >> 16;
192 if (x1 <= 319 && x2 >= 0) {
193 if (x1 < 0) x1 = 0;
194 if (x2 > 319) x2 = 319;
195 (*drawFct)(v, x1, x2, color);
196 }
197 }
198 cpt1 += step1;
199 cpt2 += step2;
200 ++v->_hliney;
201 if (v->_hliney > 199) return;
202 }
203 }
204
205#if TRACE_FRAMEBUFFER
206 video_dumpFrameBuffers(v, "fillPolygonChild");
207#endif
208#if TRACE_BG_BUFFER
209
210 video_dumpBackGroundBuffer(v);
211#endif
212 }
213}
214
215/*
216 What is read from the bytecode is not a pure screnspace polygon but a polygonspace polygon.
217
218*/
219void video_readAndDrawPolygonHierarchy(struct Video* v, uint16_t zoom, const struct Point *pgc) {
220
221 struct Point pt = *pgc;
222 pt.x -= scriptPtr_fetchByte(&v->_pData) * zoom / 64;
223 pt.y -= scriptPtr_fetchByte(&v->_pData) * zoom / 64;
224
225 int16_t childs = scriptPtr_fetchByte(&v->_pData);
226 debug(DBG_VIDEO, "video_readAndDrawPolygonHierarchy childs=%d", childs);
227
228 for ( ; childs >= 0; --childs) {
229
230 uint16_t off = scriptPtr_fetchWord(&v->_pData);
231
232 struct Point po;
233 po = pt;
234 po.x += scriptPtr_fetchByte(&v->_pData) * zoom / 64;
235 po.y += scriptPtr_fetchByte(&v->_pData) * zoom / 64;
236
237 uint16_t color = 0xFF;
238 uint16_t _bp = off;
239 off &= 0x7FFF;
240
241 if (_bp & 0x8000) {
242 color = *v->_pData.pc & 0x7F;
243 v->_pData.pc += 2;
244 }
245
246 uint8_t *bak = v->_pData.pc;
247 v->_pData.pc = v->_dataBuf + off * 2;
248
249
250 video_readAndDrawPolygon(v, color, zoom, &po);
251
252
253 v->_pData.pc = bak;
254 }
255
256
257}
258
259int32_t video_calcStep(struct Video* v, const struct Point *p1, const struct Point *p2, uint16_t *dy) {
260 (void) v;
261 *dy = p2->y - p1->y;
262 return (p2->x - p1->x) * v->_interpTable[*dy] * 4;
263}
264
265void video_drawString(struct Video* v, uint8_t color, uint16_t x, uint16_t y, uint16_t stringId) {
266
267 const struct StrEntry *se = video_stringsTableEng;
268
269 //Search for the location where the string is located.
270 while (se->id != END_OF_STRING_DICTIONARY && se->id != stringId)
271 ++se;
272
273 debug(DBG_VIDEO, "video_drawString(%d, %d, %d, '%s')", color, x, y, se->str);
274
275 //Not found
276 if (se->id == END_OF_STRING_DICTIONARY)
277 return;
278
279
280 //Used if the string contains a return carriage.
281 uint16_t xOrigin = x;
282 int len = rb->strlen(se->str);
283 for (int i = 0; i < len; ++i) {
284
285 if (se->str[i] == '\n') {
286 y += 8;
287 x = xOrigin;
288 continue;
289 }
290
291 video_drawChar(v, se->str[i], x, y, color, v->_curPagePtr1);
292 x++;
293
294 }
295}
296
297void video_drawChar(struct Video* v, uint8_t character, uint16_t x, uint16_t y, uint8_t color, uint8_t *buf) {
298 (void) v;
299 if (x <= 39 && y <= 192) {
300
301 /* each character is 8x8 */
302 const uint8_t *ft = video_font + (character - ' ') * 8;
303
304 /* x is multiplied by 4 and not 8 because there are two pixels per byte */
305 uint8_t *p = buf + x * 4 + y * 160;
306
307 for (int j = 0; j < 8; ++j) {
308 uint8_t ch = *(ft + j);
309 for (int i = 0; i < 4; ++i) {
310 uint8_t b = *(p + i);
311 uint8_t cmask = 0xFF;
312 uint8_t colb = 0;
313 if (ch & 0x80) {
314 colb |= color << 4;
315 cmask &= 0x0F;
316 }
317 ch <<= 1;
318 if (ch & 0x80) {
319 colb |= color;
320 cmask &= 0xF0;
321 }
322 ch <<= 1;
323 *(p + i) = (b & cmask) | colb;
324 }
325 /* skip to the next line (320 pixels = 160 bytes) */
326 p += 160;
327 }
328 }
329}
330
331void video_drawPoint(struct Video* v, uint8_t color, int16_t x, int16_t y) {
332 debug(DBG_VIDEO, "drawPoint(%d, %d, %d)", color, x, y);
333 if (x >= 0 && x <= 319 && y >= 0 && y <= 199) {
334 uint16_t off = y * 160 + x / 2;
335
336 uint8_t cmasko, cmaskn;
337 if (x & 1) {
338 cmaskn = 0x0F;
339 cmasko = 0xF0;
340 } else {
341 cmaskn = 0xF0;
342 cmasko = 0x0F;
343 }
344
345 uint8_t colb = (color << 4) | color;
346 if (color == 0x10) {
347 cmaskn &= 0x88;
348 cmasko = ~cmaskn;
349 colb = 0x88;
350 } else if (color == 0x11) {
351 colb = *(v->_pagePtrs[0] + off);
352 }
353 uint8_t b = *(v->_curPagePtr1 + off);
354 *(v->_curPagePtr1 + off) = (b & cmasko) | (colb & cmaskn);
355 }
356}
357
358/* Blend a line in the current framebuffer (v->_curPagePtr1)
359 */
360void video_drawLineBlend(struct Video* v, int16_t x1, int16_t x2, uint8_t color) {
361 /* silence warnings without XWORLD_DEBUG */
362 (void) color;
363 debug(DBG_VIDEO, "drawLineBlend(%d, %d, %d)", x1, x2, color);
364 int16_t xmax = MAX(x1, x2);
365 int16_t xmin = MIN(x1, x2);
366 uint8_t *p = v->_curPagePtr1 + v->_hliney * 160 + xmin / 2;
367
368 uint16_t w = xmax / 2 - xmin / 2 + 1;
369 uint8_t cmaske = 0;
370 uint8_t cmasks = 0;
371 if (xmin & 1) {
372 --w;
373 cmasks = 0xF7;
374 }
375 if (!(xmax & 1)) {
376 --w;
377 cmaske = 0x7F;
378 }
379
380 if (cmasks != 0) {
381 *p = (*p & cmasks) | 0x08;
382 ++p;
383 }
384 while (w--) {
385 *p = (*p & 0x77) | 0x88;
386 ++p;
387 }
388 if (cmaske != 0) {
389 *p = (*p & cmaske) | 0x80;
390 ++p;
391 }
392
393
394}
395
396void video_drawLineN(struct Video* v, int16_t x1, int16_t x2, uint8_t color) {
397 debug(DBG_VIDEO, "drawLineN(%d, %d, %d)", x1, x2, color);
398 int16_t xmax = MAX(x1, x2);
399 int16_t xmin = MIN(x1, x2);
400 uint8_t *p = v->_curPagePtr1 + v->_hliney * 160 + xmin / 2;
401
402 uint16_t w = xmax / 2 - xmin / 2 + 1;
403 uint8_t cmaske = 0;
404 uint8_t cmasks = 0;
405 if (xmin & 1) {
406 --w;
407 cmasks = 0xF0;
408 }
409 if (!(xmax & 1)) {
410 --w;
411 cmaske = 0x0F;
412 }
413
414 uint8_t colb = ((color & 0xF) << 4) | (color & 0xF);
415 if (cmasks != 0) {
416 *p = (*p & cmasks) | (colb & 0x0F);
417 ++p;
418 }
419 while (w--) {
420 *p++ = colb;
421 }
422 if (cmaske != 0) {
423 *p = (*p & cmaske) | (colb & 0xF0);
424 ++p;
425 }
426
427
428}
429
430void video_drawLineP(struct Video* v, int16_t x1, int16_t x2, uint8_t color) {
431 /* silence warnings without XWORLD_DEBUG */
432 (void) color;
433 debug(DBG_VIDEO, "drawLineP(%d, %d, %d)", x1, x2, color);
434 int16_t xmax = MAX(x1, x2);
435 int16_t xmin = MIN(x1, x2);
436 uint16_t off = v->_hliney * 160 + xmin / 2;
437 uint8_t *p = v->_curPagePtr1 + off;
438 uint8_t *q = v->_pagePtrs[0] + off;
439
440 uint8_t w = xmax / 2 - xmin / 2 + 1;
441 uint8_t cmaske = 0;
442 uint8_t cmasks = 0;
443 if (xmin & 1) {
444 --w;
445 cmasks = 0xF0;
446 }
447 if (!(xmax & 1)) {
448 --w;
449 cmaske = 0x0F;
450 }
451
452 if (cmasks != 0) {
453 *p = (*p & cmasks) | (*q & 0x0F);
454 ++p;
455 ++q;
456 }
457 while (w--) {
458 *p++ = *q++;
459 }
460 if (cmaske != 0) {
461 *p = (*p & cmaske) | (*q & 0xF0);
462 ++p;
463 ++q;
464 }
465
466}
467
468uint8_t *video_getPagePtr(struct Video* v, uint8_t page) {
469 uint8_t *p;
470 if (page <= 3) {
471 p = v->_pagePtrs[page];
472 } else {
473 switch (page) {
474 case 0xFF:
475 p = v->_curPagePtr3;
476 break;
477 case 0xFE:
478 p = v->_curPagePtr2;
479 break;
480 default:
481 p = v->_pagePtrs[0]; // XXX check
482 warning("video_getPagePtr() p != [0,1,2,3,0xFF,0xFE] == 0x%X", page);
483 break;
484 }
485 }
486 return p;
487}
488
489
490
491void video_changePagePtr1(struct Video* v, uint8_t page) {
492 debug(DBG_VIDEO, "video_changePagePtr1(%d)", page);
493 v->_curPagePtr1 = video_getPagePtr(v, page);
494}
495
496
497
498void video_fillPage(struct Video* v, uint8_t pageId, uint8_t color) {
499 debug(DBG_VIDEO, "video_fillPage(%d, %d)", pageId, color);
500 uint8_t *p = video_getPagePtr(v, pageId);
501
502 // Since a palette indice is coded on 4 bits, we need to duplicate the
503 // clearing color to the upper part of the byte.
504 uint8_t c = (color << 4) | color;
505
506 rb->memset(p, c, VID_PAGE_SIZE);
507
508#if TRACE_FRAMEBUFFER
509 video_dumpFrameBuffers(v, "-fillPage");
510#endif
511#if TRACE_BG_BUFFER
512
513 video_dumpBackGroundBuffer(v);
514#endif
515}
516
517
518
519
520
521#if TRACE_FRAMEBUFFER
522#define SCREENSHOT_BPP 3
523int traceFrameBufferCounter = 0;
524uint8_t allFrameBuffers[640 * 400 * SCREENSHOT_BPP];
525
526#endif
527
528
529
530
531
532
533/* This opcode is used once the background of a scene has been drawn in one of the framebuffer:
534 it is copied in the current framebuffer at the start of a new frame in order to improve performances. */
535void video_copyPage(struct Video* v, uint8_t srcPageId, uint8_t dstPageId, int16_t vscroll) {
536
537 debug(DBG_VIDEO, "video_copyPage(%d, %d)", srcPageId, dstPageId);
538
539 if (srcPageId == dstPageId)
540 return;
541
542 uint8_t *p;
543 uint8_t *q;
544
545 if (srcPageId >= 0xFE || !((srcPageId &= 0xBF) & 0x80)) {
546 p = video_getPagePtr(v, srcPageId);
547 q = video_getPagePtr(v, dstPageId);
548 memcpy(q, p, VID_PAGE_SIZE);
549
550 } else {
551 p = video_getPagePtr(v, srcPageId & 3);
552 q = video_getPagePtr(v, dstPageId);
553 if (vscroll >= -199 && vscroll <= 199) {
554 uint16_t h = 200;
555 if (vscroll < 0) {
556 h += vscroll;
557 p += -vscroll * 160;
558 } else {
559 h -= vscroll;
560 q += vscroll * 160;
561 }
562 memcpy(q, p, h * 160);
563 }
564 }
565
566
567#if TRACE_FRAMEBUFFER
568 char name[256];
569 rb->memset(name, 0, sizeof(name));
570 sprintf(name, "copyPage_0x%X_to_0x%X", (p - v->_pagePtrs[0]) / VID_PAGE_SIZE, (q - v->_pagePtrs[0]) / VID_PAGE_SIZE);
571 dumpFrameBuffers(name);
572#endif
573}
574
575
576
577
578void video_copyPagePtr(struct Video* v, const uint8_t *src) {
579 debug(DBG_VIDEO, "video_copyPagePtr()");
580 uint8_t *dst = v->_pagePtrs[0];
581 int h = 200;
582 while (h--) {
583 int w = 40;
584 while (w--) {
585 uint8_t p[] = {
586 *(src + 8000 * 3),
587 *(src + 8000 * 2),
588 *(src + 8000 * 1),
589 *(src + 8000 * 0)
590 };
591 for(int j = 0; j < 4; ++j) {
592 uint8_t acc = 0;
593 for (int i = 0; i < 8; ++i) {
594 acc <<= 1;
595 acc |= (p[i & 3] & 0x80) ? 1 : 0;
596 p[i & 3] <<= 1;
597 }
598 *dst++ = acc;
599 }
600 ++src;
601 }
602 }
603
604
605}
606
607/*
608 uint8_t *video_allocPage() {
609 uint8_t *buf = (uint8_t *)malloc(VID_PAGE_SIZE);
610 rb->memset(buf, 0, VID_PAGE_SIZE);
611 return buf;
612 }
613*/
614
615
616
617#if TRACE_FRAMEBUFFER
618int dumpPaletteCursor = 0;
619#endif
620
621/*
622 Note: The palettes set used to be allocated on the stack but I moved it to
623 the heap so I could dump the four framebuffer and follow how
624 frames are generated.
625*/
626uint8_t pal[NUM_COLORS * 3]; //3 = BYTES_PER_PIXEL
627void video_changePal(struct Video* v, uint8_t palNum) {
628 debug(DBG_VIDEO, "video_changePal(v=0x%08x, palNum=%d", v, palNum);
629 if (palNum >= 32)
630 return;
631
632 uint8_t *p = v->res->segPalettes + palNum * 32; //colors are coded on 2bytes (565) for 16 colors = 32
633 debug(DBG_VIDEO, "segPalettes: 0x%08x", v->res->segPalettes);
634 // Moved to the heap, legacy code used to allocate the palette
635 // on the stack.
636 //uint8_t pal[NUM_COLORS * 3]; //3 = BYTES_PER_PIXEL
637
638 for (int i = 0; i < NUM_COLORS; ++i)
639 {
640 debug(DBG_VIDEO, "i: %d", i);
641 debug(DBG_VIDEO, "p: 0x%08x", p);
642 uint8_t c1 = *(p + 0);
643 uint8_t c2 = *(p + 1);
644 p += 2;
645 pal[i * 3 + 0] = ((c1 & 0x0F) << 2) | ((c1 & 0x0F) >> 2); // r
646 pal[i * 3 + 1] = ((c2 & 0xF0) >> 2) | ((c2 & 0xF0) >> 6); // g
647 pal[i * 3 + 2] = ((c2 & 0x0F) >> 2) | ((c2 & 0x0F) << 2); // b
648 }
649
650 sys_setPalette(v->sys, 0, NUM_COLORS, pal);
651 v->currentPaletteId = palNum;
652
653
654#if TRACE_PALETTE
655 printf("\nuint8_t dumpPalette[48] = {\n");
656 for (int i = 0; i < NUM_COLORS; ++i)
657 {
658 printf("0x%X,0x%X,0x%X,", pal[i * 3 + 0], pal[i * 3 + 1], pal[i * 3 + 2]);
659 }
660 printf("\n};\n");
661#endif
662
663
664#if TRACE_FRAMEBUFFER
665 dumpPaletteCursor++;
666#endif
667}
668
669void video_updateDisplay(struct Video* v, uint8_t pageId) {
670
671 debug(DBG_VIDEO, "video_updateDisplay(%d)", pageId);
672
673 if (pageId != 0xFE) {
674 if (pageId == 0xFF) {
675 /* swap ptrs 2 and 3 */
676 uint8_t* temp = v->_curPagePtr3;
677 v->_curPagePtr3 = v->_curPagePtr2;
678 v->_curPagePtr2 = temp;
679 } else {
680 v->_curPagePtr2 = video_getPagePtr(v, pageId);
681 }
682 }
683
684 //Check if we need to change the palette
685 if (v->paletteIdRequested != NO_PALETTE_CHANGE_REQUESTED) {
686 video_changePal(v, v->paletteIdRequested);
687 v->paletteIdRequested = NO_PALETTE_CHANGE_REQUESTED;
688 }
689
690 //Q: Why 160 ?
691 //A: Because one byte gives two palette indices so
692 // we only need to move 320/2 per line.
693 sys_copyRect(v->sys, 0, 0, 320, 200, v->_curPagePtr2, 160);
694
695#if TRACE_FRAMEBUFFER
696 dumpFrameBuffer(v->_curPagePtr2, allFrameBuffers, 320, 200);
697#endif
698}
699
700void video_saveOrLoad(struct Video* v, struct Serializer *ser) {
701 uint8_t mask = 0;
702 if (ser->_mode == SM_SAVE) {
703 for (int i = 0; i < 4; ++i) {
704 if (v->_pagePtrs[i] == v->_curPagePtr1)
705 mask |= i << 4;
706 if (v->_pagePtrs[i] == v->_curPagePtr2)
707 mask |= i << 2;
708 if (v->_pagePtrs[i] == v->_curPagePtr3)
709 mask |= i << 0;
710 }
711 }
712 struct Entry entries[] = {
713 SE_INT(&v->currentPaletteId, SES_INT8, VER(1)),
714 SE_INT(&v->paletteIdRequested, SES_INT8, VER(1)),
715 SE_INT(&mask, SES_INT8, VER(1)),
716 SE_ARRAY(v->_pagePtrs[0], VID_PAGE_SIZE, SES_INT8, VER(1)),
717 SE_ARRAY(v->_pagePtrs[1], VID_PAGE_SIZE, SES_INT8, VER(1)),
718 SE_ARRAY(v->_pagePtrs[2], VID_PAGE_SIZE, SES_INT8, VER(1)),
719 SE_ARRAY(v->_pagePtrs[3], VID_PAGE_SIZE, SES_INT8, VER(1)),
720 SE_END()
721 };
722 ser_saveOrLoadEntries(ser, entries);
723
724 if (ser->_mode == SM_LOAD) {
725 v->_curPagePtr1 = v->_pagePtrs[(mask >> 4) & 0x3];
726 v->_curPagePtr2 = v->_pagePtrs[(mask >> 2) & 0x3];
727 v->_curPagePtr3 = v->_pagePtrs[(mask >> 0) & 0x3];
728 video_changePal(v, v->currentPaletteId);
729 }
730}
731
732
733
734#if TRACE_FRAMEBUFFER
735
736
737uint8_t allPalettesDump[][48] = {
738
739
740 {
741 0x4, 0x4, 0x4, 0x22, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x2E, 0x22, 0x0, 0x3F, 0x0, 0x0, 0x33, 0x26, 0x0, 0x37, 0x2A, 0x0, 0x3B, 0x33, 0x0, 0x3F, 0x3B, 0x0, 0x3F, 0x3F, 0x1D, 0x3F, 0x3F, 0x2A,
742 },
743
744 {
745 0x4, 0x4, 0x4, 0xC, 0xC, 0x11, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x2E, 0x22, 0x0, 0x15, 0x11, 0x11, 0x33, 0x26, 0x0, 0x37, 0x2A, 0x0, 0x3B, 0x33, 0x0, 0x3F, 0x3B, 0x0, 0x3F, 0x3F, 0x1D, 0x3F, 0x3F, 0x2A,
746 }
747
748 , {
749 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x1D, 0x1D, 0x1D, 0x15, 0x15, 0x15, 0xC, 0x8, 0xC, 0x11, 0x11, 0x15, 0x1D, 0x15, 0x15, 0x15, 0x0, 0x0, 0x0, 0x4, 0xC, 0x3F, 0x3F, 0x2A,
750 }
751
752 , {
753 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x1D, 0x1D, 0x1D, 0x15, 0x15, 0x15, 0xC, 0x8, 0xC, 0x11, 0x11, 0x15, 0x1D, 0x15, 0x15, 0x15, 0x0, 0x0, 0x0, 0x4, 0xC, 0x3F, 0x3F, 0x2A,
754 }
755
756 , {
757 0x0, 0x0, 0x0, 0x1D, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x1D, 0x1D, 0x1D, 0x15, 0x15, 0x15, 0xC, 0x8, 0xC, 0x15, 0x11, 0x19, 0x1D, 0x15, 0x15, 0x15, 0x0, 0x0, 0x0, 0x4, 0xC, 0x3F, 0x3F, 0x2A,
758 }
759
760 , {
761 0x0, 0x4, 0x8, 0x15, 0x1D, 0x1D, 0x0, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0xC, 0x19, 0x22, 0x11, 0x1D, 0x26, 0x8, 0x8, 0x8, 0x0, 0x0, 0x0, 0x2E, 0x2E, 0x2E, 0xC, 0xC, 0xC, 0x15, 0xC, 0x15, 0xC, 0x15, 0x15, 0x11, 0x19, 0x19, 0x1D, 0x26, 0x26,
762 }
763
764 , {
765 0x0, 0x0, 0x0, 0x0, 0x4, 0xC, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x15, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x11, 0x11, 0x11, 0x11, 0x15, 0x26, 0x0, 0x0, 0x33, 0x26, 0x0, 0x3B, 0x33, 0x11,
766 }
767
768 , {
769 0x0, 0x0, 0x0, 0x0, 0x4, 0xC, 0x0, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x15, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x11, 0x11, 0x11, 0x11, 0x15, 0x26, 0x0, 0x0, 0x26, 0x15, 0x0, 0x26, 0x1D, 0x0,
770 }
771
772 , {
773 0x0, 0x0, 0x0, 0x0, 0x4, 0xC, 0x0, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x15, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x11, 0x11, 0x11, 0x11, 0x15, 0x26, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x26, 0x1D, 0x0,
774 }
775
776 , {
777 0x0, 0x0, 0x0, 0x8, 0x4, 0xC, 0x15, 0xC, 0x11, 0x1D, 0x11, 0x0, 0xC, 0x8, 0x11, 0x2E, 0x1D, 0x0, 0x37, 0x26, 0x8, 0x3F, 0x2E, 0x0, 0x0, 0x0, 0x0, 0x11, 0xC, 0x15, 0x26, 0x15, 0x0, 0x15, 0x11, 0x19, 0x1D, 0x15, 0x1D, 0x26, 0x19, 0x19, 0x0, 0x0, 0x0, 0x3F, 0x3F, 0x3F,
778 }
779
780 , {
781 0x0, 0x0, 0x0, 0x8, 0x4, 0xC, 0x37, 0x1D, 0x1D, 0x3B, 0x2A, 0x22, 0x11, 0xC, 0x15, 0x2A, 0x0, 0x0, 0x33, 0x11, 0x0, 0x3F, 0x33, 0x1D, 0x3B, 0x19, 0x0, 0x11, 0x11, 0x19, 0x19, 0x15, 0x1D, 0x22, 0x19, 0x22, 0x2A, 0x1D, 0x26, 0x33, 0x22, 0x26, 0x37, 0x26, 0x22, 0x1D, 0x37, 0x3F,
782 }
783
784 , {
785 0x0, 0x0, 0x0, 0x0, 0x0, 0x1D, 0x4, 0x8, 0xC, 0x2A, 0x1D, 0xC, 0x3F, 0x3B, 0x26, 0x3B, 0x2A, 0x11, 0x2A, 0x0, 0x0, 0x0, 0x11, 0x15, 0x2A, 0x1D, 0x26, 0xC, 0x8, 0xC, 0x8, 0x15, 0x1D, 0x37, 0x26, 0x22, 0x33, 0x11, 0x0, 0x2E, 0x1D, 0x19, 0x22, 0x0, 0x0, 0x3F, 0x33, 0x1D,
786 }
787
788 , {
789 0x0, 0x0, 0x0, 0x0, 0x15, 0x0, 0x4, 0x8, 0xC, 0x0, 0xC, 0x11, 0x8, 0x11, 0x19, 0x0, 0x19, 0x22, 0x2A, 0x0, 0x0, 0x19, 0x15, 0x22, 0x2E, 0x26, 0x2E, 0xC, 0x8, 0xC, 0x0, 0x2E, 0x0, 0x37, 0x26, 0x22, 0x33, 0x11, 0x0, 0x2E, 0x1D, 0x19, 0x1D, 0x0, 0x0, 0x3F, 0x3F, 0x19,
790 }
791
792 , {
793 0x0, 0x0, 0x0, 0x8, 0xC, 0x11, 0x11, 0x11, 0x15, 0x19, 0x15, 0x22, 0x26, 0x19, 0x2A, 0x2E, 0x26, 0x2E, 0x4, 0x4, 0xC, 0x0, 0xC, 0x15, 0x2A, 0x1D, 0x26, 0x0, 0x19, 0x0, 0x0, 0x2A, 0x0, 0x37, 0x26, 0x22, 0x0, 0x15, 0x1D, 0x37, 0x2E, 0x1D, 0x3F, 0x3F, 0x2E, 0x37, 0x37, 0x26,
794 }
795
796 , {
797 0x0, 0x0, 0x0, 0x0, 0x15, 0x0, 0x4, 0x8, 0xC, 0x0, 0xC, 0x11, 0x8, 0x11, 0x19, 0x0, 0x19, 0x22, 0x2A, 0x0, 0x0, 0x19, 0x15, 0x22, 0x2E, 0x26, 0x2E, 0xC, 0x8, 0xC, 0x0, 0x2E, 0x0, 0x37, 0x26, 0x22, 0x33, 0x11, 0x0, 0x2E, 0x1D, 0x19, 0x1D, 0x0, 0x0, 0x3F, 0x3F, 0x19,
798 }
799
800 , {
801 0x0, 0x0, 0x0, 0x0, 0xC, 0x0, 0x8, 0x0, 0x4, 0xC, 0x8, 0xC, 0x19, 0x11, 0x11, 0x3F, 0x3F, 0x19, 0x0, 0x1D, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x4, 0x8, 0x11, 0x19, 0x11, 0x19, 0x0, 0x0, 0x2E, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x37, 0x1D, 0x15, 0x0, 0x11, 0x11, 0x0, 0x3F, 0x2A,
802 }
803
804 , {
805 0x0, 0x0, 0x0, 0x0, 0xC, 0x0, 0x0, 0x11, 0x0, 0x0, 0x19, 0x0, 0xC, 0x26, 0x0, 0x15, 0x2E, 0x0, 0x4, 0x8, 0x0, 0x26, 0x11, 0x0, 0x2E, 0x3F, 0x0, 0x0, 0x15, 0x0, 0xC, 0x1D, 0x0, 0x15, 0x2E, 0x0, 0x15, 0x37, 0x0, 0x1D, 0x3F, 0x0, 0xC, 0x1D, 0xC, 0x\
806 1D, 0x26, 0x15,
807 }
808};
809
810
811
812#include "png.h"
813int GL_FCS_SaveAsSpecifiedPNG(char* path, uint8_t* pixels, int depth = 8, int format = PNG_COLOR_TYPE_RGB)
814{
815 png_structp png_ptr = NULL;
816 png_infop info_ptr = NULL;
817 png_byte ** row_pointers = NULL;
818 int status = -1;
819 int bytePerPixel = 0;
820 int y;
821
822 int fd = open (path, "wb");
823 if (fd < 0) {
824 goto fopen_failed;
825 }
826
827 png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
828 if (png_ptr == NULL) {
829 goto png_create_write_struct_failed;
830 }
831
832 info_ptr = png_create_info_struct (png_ptr);
833 if (info_ptr == NULL) {
834 goto png_create_info_struct_failed;
835 }
836
837 if (setjmp (png_jmpbuf (png_ptr))) {
838 goto png_failure;
839 }
840
841 /* Set image attributes. */
842
843 png_set_IHDR (png_ptr,
844 info_ptr,
845 640,
846 400,
847 depth,
848 format,
849 PNG_INTERLACE_NONE,
850 PNG_COMPRESSION_TYPE_DEFAULT,
851 PNG_FILTER_TYPE_DEFAULT);
852
853 if (format == PNG_COLOR_TYPE_GRAY )
854 bytePerPixel = depth / 8 * 1;
855 else
856 bytePerPixel = depth / 8 * 3;
857
858 row_pointers = (png_byte **)png_malloc (png_ptr, 400 * sizeof (png_byte *));
859 //for (y = vid.height-1; y >=0; --y)
860 for (y = 0; y < 400; y++)
861 {
862 row_pointers[y] = (png_byte*)&pixels[640 * (400 - y) * bytePerPixel];
863 }
864
865 png_init_io (png_ptr, fp);
866 png_set_rows (png_ptr, info_ptr, row_pointers);
867 png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
868 //png_read_image (png_ptr, info_ptr);//
869
870 status = 0;
871
872 png_free (png_ptr, row_pointers);
873
874
875png_failure:
876png_create_info_struct_failed:
877 png_destroy_write_struct (&png_ptr, &info_ptr);
878
879png_create_write_struct_failed:
880 fclose (fp);
881fopen_failed:
882 return status;
883}
884
885void writeLine(uint8_t *dst, uint8_t *src, int size)
886{
887 uint8_t* dumpPalette;
888
889 if (!dumpPaletteCursor)
890 dumpPalette = allPalettesDump[dumpPaletteCursor];
891 else
892 dumpPalette = pal;
893
894 for( uint8_t twoPixels = 0 ; twoPixels < size ; twoPixels++)
895 {
896 int pixelIndex0 = (*src & 0xF0) >> 4;
897 pixelIndex0 &= 0x10 - 1;
898
899 int pixelIndex1 = (*src & 0xF);
900 pixelIndex1 &= 0x10 - 1;
901
902 //We need to write those two pixels
903 dst[0] = dumpPalette[pixelIndex0 * 3] << 2 | dumpPalette[pixelIndex0 * 3];
904 dst[1] = dumpPalette[pixelIndex0 * 3 + 1] << 2 | dumpPalette[pixelIndex0 * 3 + 1];
905 dst[2] = dumpPalette[pixelIndex0 * 3 + 2] << 2 | dumpPalette[pixelIndex0 * 3 + 2];
906 //dst[3] = 0xFF;
907 dst += SCREENSHOT_BPP;
908
909 dst[0] = dumpPalette[pixelIndex1 * 3] << 2 | dumpPalette[pixelIndex1 * 3];
910 dst[1] = dumpPalette[pixelIndex1 * 3 + 1] << 2 | dumpPalette[pixelIndex1 * 3 + 1];
911 dst[2] = dumpPalette[pixelIndex1 * 3 + 2] << 2 | dumpPalette[pixelIndex1 * 3 + 2];
912 //dst[3] = 0xFF;
913 dst += SCREENSHOT_BPP;
914
915 src++;
916 }
917}
918
919void video_dumpFrameBuffer(uint8_t *src, uint8_t *dst, int x, int y)
920{
921
922 for (int line = 199 ; line >= 0 ; line--)
923 {
924 writeLine(dst + x * SCREENSHOT_BPP + y * 640 * SCREENSHOT_BPP , src + line * 160, 160);
925 dst += 640 * SCREENSHOT_BPP;
926 }
927}
928
929void video_dumpFrameBuffers(char* comment)
930{
931
932 if (!traceFrameBufferCounter)
933 {
934 rb->memset(allFrameBuffers, 0, sizeof(allFrameBuffers));
935 }
936
937
938 dumpFrameBuffer(v->_pagePtrs[1], allFrameBuffers, 0, 0);
939 dumpFrameBuffer(v->_pagePtrs[0], allFrameBuffers, 0, 200);
940 dumpFrameBuffer(v->_pagePtrs[2], allFrameBuffers, 320, 0);
941 //dumpFrameBuffer(v->_pagePtrs[3],allFrameBuffers,320,200);
942
943
944 //if (v->_curPagePtr1 == v->_pagePtrs[3])
945 //
946
947 /*
948 uint8_t* offScreen = sys->getOffScreenFramebuffer();
949 for(int i=0 ; i < 200 ; i++)
950 writeLine(allFrameBuffers+320*3+640*i*3 + 200*640*3, offScreen+320*i/2 , 160);
951 */
952
953
954 int frameId = traceFrameBufferCounter++;
955 //Write bitmap to disk.
956
957
958
959 // Filling TGA header information
960 /*
961 char path[256];
962 sprintf(path,"test%d.tga",traceFrameBufferCounter);
963
964 #define IMAGE_WIDTH 640
965 #define IMAGE_HEIGHT 400
966
967 uint8_t tga_header[18];
968 rb->memset(tga_header, 0, 18);
969 tga_header[2] = 2;
970 tga_header[12] = (IMAGE_WIDTH & 0x00FF);
971 tga_header[13] = (IMAGE_WIDTH & 0xFF00) / 256;
972 tga_header[14] = (IMAGE_HEIGHT & 0x00FF) ;
973 tga_header[15] =(IMAGE_HEIGHT & 0xFF00) / 256;
974 tga_header[16] = 32 ;
975
976
977
978 // Open the file, write both header and payload, close, done.
979 char path[256];
980 sprintf(path,"test%d.tga",traceFrameBufferCounter);
981 FILE* pScreenshot = fopen(path, "wb");
982 fwrite(&tga_header, 18, sizeof(uint8_t), pScreenshot);
983 fwrite(allFrameBuffers, IMAGE_WIDTH * IMAGE_HEIGHT,SCREENSHOT_BPP * sizeof(uint8_t),pScreenshot);
984 fclose(pScreenshot);
985 */
986
987
988 char path[256];
989 //sprintf(path,"%4d%s.png",traceFrameBufferCounter,comment);
990 sprintf(path, "%4d.png", traceFrameBufferCounter);
991
992 GL_FCS_SaveAsSpecifiedPNG(path, allFrameBuffers);
993}
994#endif
995
996#if TRACE_BG_BUFFER
997
998
999
1000uint8_t bgPalette[48] = {
1001 0x8, 0x8, 0xC, 0xC, 0xC, 0x15, 0xC, 0x11, 0x1D, 0x15, 0x2A, 0x3F, 0x1D, 0x19, 0x19, 0x37, 0x2E, 0x2A, 0x26, 0x1D, 0x1D, 0x37, 0x26, 0x22, 0x22, 0xC, 0x0, 0x26, 0x33, 0x3F, 0x11, 0x11, 0x15, 0x11, 0x15, 0x1D, 0x15, 0x19, 0x26, 0x15, 0x1D, 0x37, 0x0, 0x26, 0x3F, 0x2E, 0x15, 0x0,
1002};
1003void bgWriteLine(uint8_t *dst, uint8_t *src, int size)
1004{
1005 uint8_t* dumpPalette;
1006
1007// if (!dumpPaletteCursor)
1008 // dumpPalette = allPalettesDump[dumpPaletteCursor];
1009// else
1010 dumpPalette = bgPalette;
1011
1012 for( uint8_t twoPixels = 0 ; twoPixels < size ; twoPixels++)
1013 {
1014 int pixelIndex0 = (*src & 0xF0) >> 4;
1015 pixelIndex0 &= 0x10 - 1;
1016
1017 int pixelIndex1 = (*src & 0xF);
1018 pixelIndex1 &= 0x10 - 1;
1019
1020 //We need to write those two pixels
1021 dst[0] = dumpPalette[pixelIndex0 * 3] << 2 | dumpPalette[pixelIndex0 * 3];
1022 dst[1] = dumpPalette[pixelIndex0 * 3 + 1] << 2 | dumpPalette[pixelIndex0 * 3 + 1];
1023 dst[2] = dumpPalette[pixelIndex0 * 3 + 2] << 2 | dumpPalette[pixelIndex0 * 3 + 2];
1024 //dst[3] = 0xFF;
1025 dst += 3;
1026
1027 dst[0] = dumpPalette[pixelIndex1 * 3] << 2 | dumpPalette[pixelIndex1 * 3];
1028 dst[1] = dumpPalette[pixelIndex1 * 3 + 1] << 2 | dumpPalette[pixelIndex1 * 3 + 1];
1029 dst[2] = dumpPalette[pixelIndex1 * 3 + 2] << 2 | dumpPalette[pixelIndex1 * 3 + 2];
1030 //dst[3] = 0xFF;
1031 dst += 3;
1032
1033 src++;
1034 }
1035}
1036
1037void bgDumpFrameBuffer(uint8_t *src, uint8_t *dst, int x, int y)
1038{
1039
1040 for (int line = 199 ; line >= 0 ; line--)
1041 {
1042 bgWriteLine(dst + x * 3 + y * 320 * 3 , src + line * 160, 160);
1043 dst += 320 * 3;
1044 }
1045}
1046
1047#include "png.h"
1048int bgSaveAsSpecifiedPNG(char* path, uint8_t* pixels, int depth = 8, int format = PNG_COLOR_TYPE_RGB)
1049{
1050#if 0
1051 FILE * fp;
1052 png_structp png_ptr = NULL;
1053 png_infop info_ptr = NULL;
1054 png_byte ** row_pointers = NULL;
1055 int status = -1;
1056 int bytePerPixel = 0;
1057 int y;
1058
1059 fp = fopen (path, "wb");
1060 if (! fp) {
1061 goto fopen_failed;
1062 }
1063
1064 png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1065 if (png_ptr == NULL) {
1066 goto png_create_write_struct_failed;
1067 }
1068
1069 info_ptr = png_create_info_struct (png_ptr);
1070 if (info_ptr == NULL) {
1071 goto png_create_info_struct_failed;
1072 }
1073
1074 if (setjmp (png_jmpbuf (png_ptr))) {
1075 goto png_failure;
1076 }
1077
1078 /* Set image attributes. */
1079
1080 png_set_IHDR (png_ptr,
1081 info_ptr,
1082 320,
1083 200,
1084 depth,
1085 format,
1086 PNG_INTERLACE_NONE,
1087 PNG_COMPRESSION_TYPE_DEFAULT,
1088 PNG_FILTER_TYPE_DEFAULT);
1089
1090 if (format == PNG_COLOR_TYPE_GRAY )
1091 bytePerPixel = depth / 8 * 1;
1092 else
1093 bytePerPixel = depth / 8 * 3;
1094
1095 row_pointers = (png_byte **)png_malloc (png_ptr, 200 * sizeof (png_byte *));
1096 //for (y = vid.height-1; y >=0; --y)
1097 for (y = 0; y < 200; y++)
1098 {
1099 row_pointers[y] = (png_byte*)&pixels[320 * (200 - y) * bytePerPixel];
1100 }
1101
1102 png_init_io (png_ptr, fp);
1103 png_set_rows (png_ptr, info_ptr, row_pointers);
1104 png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
1105 //png_read_image (png_ptr, info_ptr);//
1106
1107 status = 0;
1108
1109 png_free (png_ptr, row_pointers);
1110
1111
1112png_failure:
1113png_create_info_struct_failed:
1114 png_destroy_write_struct (&png_ptr, &info_ptr);
1115
1116png_create_write_struct_failed:
1117 fclose (fp);
1118fopen_failed:
1119 return status;
1120#endif
1121}
1122
1123int bgFrameBufferCounter = 0;
1124
1125void video_dumpBackGroundBuffer()
1126{
1127 if (v->_curPagePtr1 != v->_pagePtrs[0])
1128 return;
1129
1130 uint8_t bgBuffer[320 * 200 * 3];
1131 bgDumpFrameBuffer(v->_curPagePtr1, bgBuffer, 0, 0);
1132
1133
1134 char path[256];
1135 //sprintf(path,"%4d%s.png",traceFrameBufferCounter,comment);
1136 sprintf(path, "bg%4d.png", bgFrameBufferCounter++);
1137
1138 bgSaveAsSpecifiedPNG(path, bgBuffer);
1139}
1140
1141#endif
diff --git a/apps/plugins/xworld/video.h b/apps/plugins/xworld/video.h
new file mode 100644
index 0000000000..d4ff64f5b5
--- /dev/null
+++ b/apps/plugins/xworld/video.h
@@ -0,0 +1,127 @@
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#ifndef __VIDEO_H__
24#define __VIDEO_H__
25
26#include "intern.h"
27
28struct StrEntry {
29 uint16_t id;
30 char *str;
31};
32#define MAX_POINTS 50
33struct Polygon {
34
35 uint16_t bbw, bbh;
36 uint8_t numPoints;
37 struct Point points[MAX_POINTS];
38
39};
40void polygon_readVertices(struct Polygon*, const uint8_t *p, uint16_t zoom);
41
42struct Resource;
43struct Serializer;
44struct System;
45
46// This is used to detect the end of _stringsTableEng and _stringsTableDemo
47#define END_OF_STRING_DICTIONARY 0xFFFF
48
49// Special value when no palette change is necessary
50#define NO_PALETTE_CHANGE_REQUESTED 0xFF
51
52/* 320x200 pixels, with 2 pixels/byte */
53#define VID_PAGE_SIZE ( 320 * 200 / 2 )
54
55struct Video {
56
57 /* FW: static const uint8_t _font[];*/
58 /* FW: moved to video_data.c */
59 struct Resource *res;
60 struct System *sys;
61
62
63
64 uint8_t paletteIdRequested, currentPaletteId;
65 uint8_t *_pagePtrs[4];
66 uint8_t *page_data;
67 // I am almost sure that:
68 // _curPagePtr1 is the backbuffer
69 // _curPagePtr2 is the frontbuffer
70 // _curPagePtr3 is the background builder.
71 uint8_t *_curPagePtr1, *_curPagePtr2, *_curPagePtr3;
72
73 struct Polygon polygon;
74 int16_t _hliney;
75
76 //Precomputer division lookup table
77 uint16_t _interpTable[0x400];
78
79 struct Ptr _pData;
80 uint8_t *_dataBuf;
81
82
83};
84
85typedef void (*drawLine)(struct Video*, int16_t x1, int16_t x2, uint8_t col);
86
87//Video(Resource *res, System *stub);
88void video_create(struct Video*, struct Resource*, struct System*);
89void video_init(struct Video* v);
90
91void video_setDataBuffer(struct Video* v, uint8_t *dataBuf, uint16_t offset);
92void video_readAndDrawPolygon(struct Video* v, uint8_t color, uint16_t zoom, const struct Point *pt);
93void video_fillPolygon(struct Video* v, uint16_t color, uint16_t zoom, const struct Point *pt);
94void video_readAndDrawPolygonHierarchy(struct Video* v, uint16_t zoom, const struct Point *pt);
95int32_t video_calcStep(struct Video* v, const struct Point *p1, const struct Point *p2, uint16_t *dy);
96
97void video_drawString(struct Video* v, uint8_t color, uint16_t x, uint16_t y, uint16_t strId);
98void video_drawChar(struct Video* v, uint8_t c, uint16_t x, uint16_t y, uint8_t color, uint8_t *buf);
99void video_drawPoint(struct Video* v, uint8_t color, int16_t x, int16_t y);
100void video_drawLineBlend(struct Video* v, int16_t x1, int16_t x2, uint8_t color);
101void video_drawLineN(struct Video* v, int16_t x1, int16_t x2, uint8_t color);
102void video_drawLineP(struct Video* v, int16_t x1, int16_t x2, uint8_t color);
103uint8_t *video_getPagePtr(struct Video* v, uint8_t page);
104void video_changePagePtr1(struct Video* v, uint8_t page);
105void video_fillPage(struct Video* v, uint8_t page, uint8_t color);
106void video_copyPage(struct Video* v, uint8_t src, uint8_t dst, int16_t vscroll);
107void video_copyPagePtr(struct Video* v, const uint8_t *src);
108uint8_t *video_allocPage(struct Video* v);
109void video_changePal(struct Video* v, uint8_t pal);
110void video_updateDisplay(struct Video* v, uint8_t page);
111
112void video_saveOrLoad(struct Video* v, struct Serializer *ser);
113
114#define TRACE_PALETTE 0
115#define TRACE_FRAMEBUFFER 0
116#if TRACE_FRAMEBUFFER
117void video_dumpFrameBuffer(struct Video* v, uint8_t *src, uint8_t *dst, int x, int y);
118void video_dumpFrameBuffers(struct Video* v, char* comment);
119
120#endif
121
122#define TRACE_BG_BUFFER 0
123#if TRACE_BG_BUFFER
124void video_dumpBackGroundBuffer(struct Video* v);
125#endif
126
127#endif
diff --git a/apps/plugins/xworld/video_data.c b/apps/plugins/xworld/video_data.c
new file mode 100644
index 0000000000..e658c175d9
--- /dev/null
+++ b/apps/plugins/xworld/video_data.c
@@ -0,0 +1,271 @@
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 "video.h"
24#include "video_data.h"
25#include "stdint.h"
26
27/* this font is based off 10-Fixed.bdf with lowercase characters
28 from 09-Fixed.bdf and a handcrafted copyright symbol */
29
30uint8_t video_font[FONT_SIZE] = {
31 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */
32 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x00, /* '!' */
33 0x50, 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, /* '"' */
34 0x50, 0x50, 0xF8, 0x50, 0xF8, 0x50, 0x50, 0x00, /* '#' */
35 0x20, 0x70, 0xA0, 0x70, 0x28, 0x70, 0x20, 0x00, /* '$' */
36 0x48, 0xA8, 0x50, 0x20, 0x50, 0xA8, 0x90, 0x00, /* '%' */
37 0x40, 0xA0, 0xA0, 0x40, 0xA8, 0x90, 0x68, 0x00, /* '&' */
38 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, /* ''' */
39 0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10, 0x00, /* '(' */
40 0x40, 0x20, 0x10, 0x10, 0x10, 0x20, 0x40, 0x00, /* ')' */
41 0x00, 0x88, 0x50, 0xF8, 0x50, 0x88, 0x00, 0x00, /* '*' */
42 0x00, 0x20, 0x20, 0xF8, 0x20, 0x20, 0x00, 0x00, /* '+' */
43 0x00, 0x00, 0x00, 0x00, 0x30, 0x20, 0x40, 0x00, /* ',' */
44 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, /* '-' */
45 0x00, 0x00, 0x00, 0x00, 0x20, 0x50, 0x20, 0x00, /* '.' */
46 0x08, 0x08, 0x10, 0x20, 0x40, 0x80, 0x80, 0x00, /* '/' */
47 0x20, 0x50, 0x88, 0x88, 0x88, 0x50, 0x20, 0x00, /* '0' */
48 0x20, 0x60, 0xA0, 0x20, 0x20, 0x20, 0xF8, 0x00, /* '1' */
49 0x70, 0x88, 0x08, 0x30, 0x40, 0x80, 0xF8, 0x00, /* '2' */
50 0xF8, 0x08, 0x10, 0x30, 0x08, 0x88, 0x70, 0x00, /* '3' */
51 0x10, 0x30, 0x50, 0x90, 0xF8, 0x10, 0x10, 0x00, /* '4' */
52 0xF8, 0x80, 0xB0, 0xC8, 0x08, 0x88, 0x70, 0x00, /* '5' */
53 0x30, 0x40, 0x80, 0xB0, 0xC8, 0x88, 0x70, 0x00, /* '6' */
54 0xF8, 0x08, 0x10, 0x10, 0x20, 0x40, 0x40, 0x00, /* '7' */
55 0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70, 0x00, /* '8' */
56 0x70, 0x88, 0x98, 0x68, 0x08, 0x10, 0x60, 0x00, /* '9' */
57 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x00, /* ':' */
58 0x00, 0x30, 0x30, 0x00, 0x30, 0x20, 0x40, 0x00, /* ';' */
59 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00, /* '<' */
60 0x00, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x00, 0x00, /* '=' */
61 0x40, 0x20, 0x10, 0x08, 0x10, 0x20, 0x40, 0x00, /* '>' */
62 0x70, 0x88, 0x10, 0x20, 0x20, 0x00, 0x20, 0x00, /* '?' */
63 0x40, 0x20, 0x10, 0x10, 0x10, 0x20, 0x40, 0x00, /* ')' */
64 0x20, 0x50, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x00, /* 'A' */
65 0xF0, 0x88, 0x88, 0xF0, 0x88, 0x88, 0xF0, 0x00, /* 'B' */
66 0x70, 0x88, 0x80, 0x80, 0x80, 0x88, 0x70, 0x00, /* 'C' */
67 0xF0, 0x88, 0x88, 0x88, 0x88, 0x88, 0xF0, 0x00, /* 'D' */
68 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8, 0x00, /* 'E' */
69 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x00, /* 'F' */
70 0x70, 0x88, 0x80, 0x80, 0x98, 0x88, 0x70, 0x00, /* 'G' */
71 0x88, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88, 0x00, /* 'H' */
72 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, /* 'I' */
73 0x38, 0x10, 0x10, 0x10, 0x10, 0x90, 0x60, 0x00, /* 'J' */
74 0x88, 0x90, 0xA0, 0xC0, 0xA0, 0x90, 0x88, 0x00, /* 'K' */
75 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8, 0x00, /* 'L' */
76 0x88, 0x88, 0xD8, 0xA8, 0x88, 0x88, 0x88, 0x00, /* 'M' */
77 0x88, 0x88, 0xC8, 0xA8, 0x98, 0x88, 0x88, 0x00, /* 'N' */
78 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, /* 'O' */
79 0xF0, 0x88, 0x88, 0xF0, 0x80, 0x80, 0x80, 0x00, /* 'P' */
80 0x70, 0x88, 0x88, 0x88, 0x88, 0xA8, 0x70, 0x00, /* 'Q' */
81 0xF0, 0x88, 0x88, 0xF0, 0xA0, 0x90, 0x88, 0x00, /* 'R' */
82 0x70, 0x88, 0x80, 0x70, 0x08, 0x88, 0x70, 0x00, /* 'S' */
83 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, /* 'T' */
84 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, /* 'U' */
85 0x88, 0x88, 0x88, 0x50, 0x50, 0x50, 0x20, 0x00, /* 'V' */
86 0x88, 0x88, 0x88, 0xA8, 0xA8, 0xD8, 0x88, 0x00, /* 'W' */
87 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88, 0x00, /* 'X' */
88 0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x20, 0x00, /* 'Y' */
89 0xF8, 0x08, 0x10, 0x20, 0x40, 0x80, 0xF8, 0x00, /* 'Z' */
90 0x70, 0x40, 0x40, 0x40, 0x40, 0x70, 0x00, 0x00, /* '[' */
91 0x80, 0x80, 0x40, 0x20, 0x10, 0x10, 0x00, 0x00, /* '\' */
92 0x70, 0x10, 0x10, 0x10, 0x10, 0x70, 0x00, 0x00, /* ']' */
93 0x20, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* '^' */
94 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, /* '_' */
95 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* '`' */
96 0x00, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, /* 'a' */
97 0x80, 0x80, 0xB0, 0xC8, 0x88, 0xC8, 0xB0, 0x00, /* 'b' */
98 0x00, 0x00, 0x70, 0x88, 0x80, 0x88, 0x70, 0x00, /* 'c' */
99 0x08, 0x08, 0x68, 0x98, 0x88, 0x98, 0x68, 0x00, /* 'd' */
100 0x00, 0x00, 0x70, 0x88, 0xF8, 0x80, 0x70, 0x00, /* 'e' */
101 0x30, 0x48, 0x40, 0xF0, 0x40, 0x40, 0x40, 0x00, /* 'f' */
102 0x00, 0x00, 0x60, 0x90, 0x90, 0x70, 0x10, 0x60, /* 'g' */
103 0x80, 0x80, 0xB0, 0xC8, 0x88, 0x88, 0x88, 0x00, /* 'h' */
104 0x20, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, /* 'i' */
105 0x20, 0x00, 0x60, 0x20, 0x20, 0x20, 0xA0, 0x40, /* 'j' */
106 0x80, 0x80, 0x88, 0x90, 0xE0, 0x90, 0x88, 0x00, /* 'k' */
107 0x60, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, /* 'l' */
108 0x00, 0x00, 0xD0, 0xA8, 0xA8, 0xA8, 0x88, 0x00, /* 'm' */
109 0x00, 0x00, 0xB0, 0xC8, 0x88, 0x88, 0x88, 0x00, /* 'n' */
110 0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, /* 'o' */
111 0x00, 0x00, 0xE0, 0x90, 0x90, 0xE0, 0x80, 0x80, /* 'p' */
112 0x00, 0x00, 0x70, 0x90, 0x90, 0x70, 0x10, 0x10, /* 'q' */
113 0x00, 0x00, 0xB0, 0xC8, 0x80, 0x80, 0x80, 0x00, /* 'r' */
114 0x00, 0x00, 0x70, 0x80, 0x70, 0x08, 0xF0, 0x00, /* 's' */
115 0x40, 0x40, 0xF0, 0x40, 0x40, 0x48, 0x30, 0x00, /* 't' */
116 0x00, 0x00, 0x88, 0x88, 0x88, 0x98, 0x68, 0x00, /* 'u' */
117 0x00, 0x00, 0x88, 0x88, 0x50, 0x50, 0x20, 0x00, /* 'v' */
118 0x00, 0x00, 0x88, 0x88, 0xA8, 0xA8, 0x50, 0x00, /* 'w' */
119 0x00, 0x00, 0x88, 0x50, 0x20, 0x50, 0x88, 0x00, /* 'x' */
120 0x00, 0x00, 0x90, 0x90, 0x90, 0x70, 0x90, 0x60, /* 'y' */
121 0x00, 0x00, 0xF8, 0x10, 0x20, 0x40, 0xF8, 0x00, /* 'z' */
122 0x18, 0x20, 0x10, 0x60, 0x10, 0x20, 0x18, 0x00, /* '{' */
123 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x00, /* cursor */
124 0x38, 0x54, 0xAA, 0xA2, 0xAA, 0x54, 0x38, 0x00, /* copyright symbol */
125 0x70, 0x88, 0x88, 0x88, 0x88, 0x50, 0xD8, 0x00, /* omega */
126 0x00, 0xA0, 0x10, 0x80, 0x10, 0x80, 0x50, 0x00, /* DEL */
127};
128
129struct StrEntry video_stringsTableEng[MAX_STRING_TABLE_SIZE] = {
130 { 0x001, "B A N A N A 2000" },
131 { 0x002, "Copyright } 2014 Banana Corporation \nGPLv2\n\nBUNIX Revision 3.14" },
132 { 0x003, "1" },
133 { 0x004, "3" },
134 { 0x005, "." },
135 { 0x006, "a" },
136 { 0x007, "@" },
137 { 0x008, "BANANA 2000" },
138 { 0x00A, "R" },
139 { 0x00B, "U" },
140 { 0x00C, "N" },
141 { 0x00D, "P" },
142 { 0x00E, "R" },
143 { 0x00F, "O" },
144 { 0x010, "J" },
145 { 0x011, "E" },
146 { 0x012, "C" },
147 { 0x013, "T" },
148 { 0x014, "Fields 100.05Mf OK" },
149 { 0x015, "Lines of Flux % 14.077 OK" },
150 { 0x016, "IONS OK" },
151 { 0x017, " %%%ddd OK" },
152 { 0x018, "TEMP ok" },
153 { 0x019, "EXECUTE" },
154 { 0x01A, "V= 24%\nG: 1.05\n\nMG: 177.2l\n\nOPT: G>\n\n Field:\nI: OFF\nII: ON\nIII: ON\n\np~: I\n" },
155 { 0x01B, "on" },
156 { 0x01C, "-" },
157 { 0x021, "|" },
158 { 0x022, "--- Simulation ---" },
159 { 0x023, " TEST WILL START IN SECONDS" },
160 { 0x024, " 20" },
161 { 0x025, " 19" },
162 { 0x026, " 18" },
163 { 0x027, " 4" },
164 { 0x028, " 3" },
165 { 0x029, " 2" },
166 { 0x02A, " 1" },
167 { 0x02B, " 0" },
168 { 0x02C, "C A U T I O N" },
169 { 0x031, "- Test 0:\nGenerate electron beam\n" },
170 { 0x032, "- Test 1:\nCalculating flux coefficient\n" },
171 { 0x033, "- Test 2:\nIncrease magnetic field\n" },
172 { 0x034, "R E S U L T S" },
173 { 0x035, "- NOTE:\nChances of producing:\n Anti-matter: 34 %\n Neutrino 71: 4 %\n Positron 34: 99 %\n" },
174 { 0x036, " Continue Test y/n ?" },
175 { 0x037, "Are You Sure?" },
176 { 0x038, "Setting Configuration\n of accelerator\n'Verified'" },
177 { 0x039, " Continue ?" },
178 { 0x03C, "T___T" },
179 { 0x03D, "OOO ~" },
180 { 0x03E, ".40X13DD" },
181 { 0x03F, "ferfxwre" },
182 { 0x040, "Trfor 25%" },
183 { 0x041, "32% 56% GOOD" },
184 { 0x042, "E=2.7182818289" },
185 { 0x043, "G=330.01" },
186 { 0x044, "+" },
187 { 0x045, "*" },
188 { 0x046, "% 234" },
189 { 0x047, "Gorwle 12" },
190 { 0x048, "[[[[" },
191 { 0x049, "Elephine Soft" },
192 { 0x04A, "By Many talented People" },
193 { 0x04B, " 4" },
194 { 0x04C, " 16" },
195 { 0x12C, "0" },
196 { 0x12D, "1" },
197 { 0x12E, "2" },
198 { 0x12F, "3" },
199 { 0x130, "4" },
200 { 0x131, "5" },
201 { 0x132, "6" },
202 { 0x133, "7" },
203 { 0x134, "8" },
204 { 0x135, "9" },
205 { 0x136, "A" },
206 { 0x137, "B" },
207 { 0x138, "C" },
208 { 0x139, "D" },
209 { 0x13A, "E" },
210 { 0x13B, "F" },
211 { 0x13C, " LEVEL CODE:" },
212 { 0x13D, " PRESS ANY KEY TO CONTINUE" },
213 { 0x13E, " ENTER CODE" },
214 { 0x13F, " CODE NOT VALID!!" },
215 { 0x140, "AN NULER" },
216 { 0x141, " ??????\n\n\n\n\n\n\n\n\nANY KEY TO CONTINUE" },
217 { 0x142, " ENTER THE CODE CORRELATING TO\n POSITION\n ON THE DECODER WHEEL" },
218 { 0x143, " LOAD..." },
219 { 0x144, " ERROR" },
220 { 0x15E, "LDKD" },
221 { 0x15F, "HTDC" },
222 { 0x160, "CLLD" },
223 { 0x161, "FXLC" },
224 { 0x162, "KRFK" },
225 { 0x163, "XDDJ" },
226 { 0x164, "LBKG" },
227 { 0x165, "KLFB" },
228 { 0x166, "TTCT" },
229 { 0x167, "DDRX" },
230 { 0x168, "TBHK" },
231 { 0x169, "BRTD" },
232 { 0x16A, "CKJL" },
233 { 0x16B, "LFCK" },
234 { 0x16C, "BFLX" },
235 { 0x16D, "XJRT" },
236 { 0x16E, "HRTB" },
237 { 0x16F, "HBHK" },
238 { 0x170, "JCGB" },
239 { 0x171, "HHFL" },
240 { 0x172, "TFBB" },
241 { 0x173, "TXHF" },
242 { 0x174, "JHJL" },
243 { 0x181, " " },
244 { 0x182, " " },
245 { 0x183, " " },
246 { 0x184, " " },
247 { 0x185, " " },
248 { 0x186, " " },
249 { 0x187, " " },
250 { 0x188, " " },
251 { 0x18B, " " },
252 { 0x18C, " " },
253 { 0x18D, " " },
254 { 0x18E, " " },
255 { 0x258, " " },
256 { 0x259, " " },
257 { 0x25A, " " },
258 { 0x25B, " " },
259 { 0x25C, " " },
260 { 0x25D, " " },
261 { 0x263, " " },
262 { 0x264, " " },
263 { 0x265, " " },
264 { 0x190, "Hello Master." },
265 { 0x191, "Identifiy confirmed.\nAccess granted." },
266 { 0x192, " ACCESSING" },
267 { 0x193, " " },
268 { 0x194, "y\n" },
269 { 0x193, "!!!\n" },
270 { END_OF_STRING_DICTIONARY, "" }
271};
diff --git a/apps/plugins/xworld/video_data.h b/apps/plugins/xworld/video_data.h
new file mode 100644
index 0000000000..cd53205744
--- /dev/null
+++ b/apps/plugins/xworld/video_data.h
@@ -0,0 +1,29 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Franklin Wei
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ***************************************************************************/
21
22#include "video.h"
23#include "stdint.h"
24#define FONT_SIZE (8 * (0x80 - ' '))
25#define MAX_STRING_TABLE_SIZE 255
26
27extern uint8_t video_font[FONT_SIZE];
28
29extern struct StrEntry video_stringsTableEng[MAX_STRING_TABLE_SIZE];
diff --git a/apps/plugins/xworld/vm.c b/apps/plugins/xworld/vm.c
new file mode 100644
index 0000000000..de632d710d
--- /dev/null
+++ b/apps/plugins/xworld/vm.c
@@ -0,0 +1,763 @@
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 "vm.h"
25#include "mixer.h"
26#include "resource.h"
27#include "video.h"
28#include "serializer.h"
29#include "sfxplayer.h"
30#include "sys.h"
31#include "parts.h"
32#include "file.h"
33
34static const uint16_t vm_frequenceTable[] = {
35 0x0CFF, 0x0DC3, 0x0E91, 0x0F6F, 0x1056, 0x114E, 0x1259, 0x136C,
36 0x149F, 0x15D9, 0x1726, 0x1888, 0x19FD, 0x1B86, 0x1D21, 0x1EDE,
37 0x20AB, 0x229C, 0x24B3, 0x26D7, 0x293F, 0x2BB2, 0x2E4C, 0x3110,
38 0x33FB, 0x370D, 0x3A43, 0x3DDF, 0x4157, 0x4538, 0x4998, 0x4DAE,
39 0x5240, 0x5764, 0x5C9A, 0x61C8, 0x6793, 0x6E19, 0x7485, 0x7BBD
40};
41
42void vm_create(struct VirtualMachine* m, struct Mixer *mix, struct Resource* res, struct SfxPlayer *ply, struct Video *vid, struct System *stub)
43{
44 m->res = res;
45 m->video = vid;
46 m->sys = stub;
47 m->mixer = mix;
48 m->player = ply;
49}
50
51void vm_init(struct VirtualMachine* m) {
52
53 rb->memset(m->vmVariables, 0, sizeof(m->vmVariables));
54 m->vmVariables[0x54] = 0x81;
55 m->vmVariables[VM_VARIABLE_RANDOM_SEED] = *rb->current_tick;
56
57 m->_fastMode = false;
58 m->player->_markVar = &m->vmVariables[VM_VARIABLE_MUS_MARK];
59}
60
61void vm_op_movConst(struct VirtualMachine* m) {
62 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
63 int16_t value = scriptPtr_fetchWord(&m->_scriptPtr);
64 debug(DBG_VM, "vm_op_movConst(0x%02X, %d)", variableId, value);
65 m->vmVariables[variableId] = value;
66}
67
68void vm_op_mov(struct VirtualMachine* m) {
69 uint8_t dstVariableId = scriptPtr_fetchByte(&m->_scriptPtr);
70 uint8_t srcVariableId = scriptPtr_fetchByte(&m->_scriptPtr);
71 debug(DBG_VM, "vm_op_mov(0x%02X, 0x%02X)", dstVariableId, srcVariableId);
72 m->vmVariables[dstVariableId] = m->vmVariables[srcVariableId];
73}
74
75void vm_op_add(struct VirtualMachine* m) {
76 uint8_t dstVariableId = scriptPtr_fetchByte(&m->_scriptPtr);
77 uint8_t srcVariableId = scriptPtr_fetchByte(&m->_scriptPtr);
78 debug(DBG_VM, "vm_op_add(0x%02X, 0x%02X)", dstVariableId, srcVariableId);
79 m->vmVariables[dstVariableId] += m->vmVariables[srcVariableId];
80}
81
82void vm_op_addConst(struct VirtualMachine* m) {
83 if (m->res->currentPartId == 0x3E86 && m->_scriptPtr.pc == m->res->segBytecode + 0x6D48) {
84 warning("vm_op_addConst() hack for non-stop looping gun sound bug");
85 // the script 0x27 slot 0x17 doesn't stop the gun sound from looping, I
86 // don't really know why ; for now, let's play the 'stopping sound' like
87 // the other scripts do
88 // (0x6D43) jmp(0x6CE5)
89 // (0x6D46) break
90 // (0x6D47) VAR(6) += -50
91 vm_snd_playSound(m, 0x5B, 1, 64, 1);
92 }
93 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
94 int16_t value = scriptPtr_fetchWord(&m->_scriptPtr);
95 debug(DBG_VM, "vm_op_addConst(0x%02X, %d)", variableId, value);
96 m->vmVariables[variableId] += value;
97}
98
99void vm_op_call(struct VirtualMachine* m) {
100
101 uint16_t offset = scriptPtr_fetchWord(&m->_scriptPtr);
102 uint8_t sp = m->_stackPtr;
103
104 debug(DBG_VM, "vm_op_call(0x%X)", offset);
105 m->_scriptStackCalls[sp] = m->_scriptPtr.pc - m->res->segBytecode;
106 if (m->_stackPtr == 0xFF) {
107 error("vm_op_call() ec=0x%X stack overflow", 0x8F);
108 }
109 ++m->_stackPtr;
110 m->_scriptPtr.pc = m->res->segBytecode + offset ;
111}
112
113void vm_op_ret(struct VirtualMachine* m) {
114 debug(DBG_VM, "vm_op_ret()");
115 if (m->_stackPtr == 0) {
116 error("vm_op_ret() ec=0x%X stack underflow", 0x8F);
117 }
118 --m->_stackPtr;
119 uint8_t sp = m->_stackPtr;
120 m->_scriptPtr.pc = m->res->segBytecode + m->_scriptStackCalls[sp];
121}
122
123void vm_op_pauseThread(struct VirtualMachine* m) {
124 debug(DBG_VM, "vm_op_pauseThread()");
125 m->gotoNextThread = true;
126}
127
128void vm_op_jmp(struct VirtualMachine* m) {
129 uint16_t pcOffset = scriptPtr_fetchWord(&m->_scriptPtr);
130 debug(DBG_VM, "vm_op_jmp(0x%02X)", pcOffset);
131 m->_scriptPtr.pc = m->res->segBytecode + pcOffset;
132}
133
134void vm_op_setSetVect(struct VirtualMachine* m) {
135 uint8_t threadId = scriptPtr_fetchByte(&m->_scriptPtr);
136 uint16_t pcOffsetRequested = scriptPtr_fetchWord(&m->_scriptPtr);
137 debug(DBG_VM, "vm_op_setSetVect(0x%X, 0x%X)", threadId, pcOffsetRequested);
138 m->threadsData[REQUESTED_PC_OFFSET][threadId] = pcOffsetRequested;
139}
140
141void vm_op_jnz(struct VirtualMachine* m) {
142 uint8_t i = scriptPtr_fetchByte(&m->_scriptPtr);
143 debug(DBG_VM, "vm_op_jnz(0x%02X)", i);
144 --m->vmVariables[i];
145 if (m->vmVariables[i] != 0) {
146 vm_op_jmp(m);
147 } else {
148 scriptPtr_fetchWord(&m->_scriptPtr);
149 }
150}
151
152#define BYPASS_PROTECTION
153void vm_op_condJmp(struct VirtualMachine* m) {
154
155 //printf("Jump : %X \n",m->_scriptPtr.pc-m->res->segBytecode);
156//FCS Whoever wrote this is patching the bytecode on the fly. This is ballzy !!
157#ifdef BYPASS_PROTECTION
158
159 if (m->res->currentPartId == GAME_PART_FIRST && m->_scriptPtr.pc == m->res->segBytecode + 0xCB9) {
160
161 // (0x0CB8) condJmp(0x80, VAR(41), VAR(30), 0xCD3)
162 *(m->_scriptPtr.pc + 0x00) = 0x81;
163 *(m->_scriptPtr.pc + 0x03) = 0x0D;
164 *(m->_scriptPtr.pc + 0x04) = 0x24;
165 // (0x0D4E) condJmp(0x4, VAR(50), 6, 0xDBC)
166 *(m->_scriptPtr.pc + 0x99) = 0x0D;
167 *(m->_scriptPtr.pc + 0x9A) = 0x5A;
168 debug(DBG_VM, "vm_op_condJmp() bypassing protection");
169 debug(DBG_VM, "bytecode has been patched");
170
171 //vm_bypassProtection(m);
172 }
173
174
175#endif
176
177 uint8_t opcode = scriptPtr_fetchByte(&m->_scriptPtr);
178 int16_t b = m->vmVariables[scriptPtr_fetchByte(&m->_scriptPtr)];
179 uint8_t c = scriptPtr_fetchByte(&m->_scriptPtr);
180 int16_t a;
181
182 if (opcode & 0x80) {
183 a = m->vmVariables[c];
184 } else if (opcode & 0x40) {
185 a = c * 256 + scriptPtr_fetchByte(&m->_scriptPtr);
186 } else {
187 a = c;
188 }
189 debug(DBG_VM, "vm_op_condJmp(%d, 0x%02X, 0x%02X)", opcode, b, a);
190
191 // Check if the conditional value is met.
192 bool expr = false;
193 switch (opcode & 7) {
194 case 0: // jz
195 expr = (b == a);
196 break;
197 case 1: // jnz
198 expr = (b != a);
199 break;
200 case 2: // jg
201 expr = (b > a);
202 break;
203 case 3: // jge
204 expr = (b >= a);
205 break;
206 case 4: // jl
207 expr = (b < a);
208 break;
209 case 5: // jle
210 expr = (b <= a);
211 break;
212 default:
213 warning("vm_op_condJmp() invalid condition %d", (opcode & 7));
214 break;
215 }
216
217 if (expr) {
218 vm_op_jmp(m);
219 } else {
220 scriptPtr_fetchWord(&m->_scriptPtr);
221 }
222
223}
224
225void vm_op_setPalette(struct VirtualMachine* m) {
226 uint16_t paletteId = scriptPtr_fetchWord(&m->_scriptPtr);
227 debug(DBG_VM, "vm_op_changePalette(%d)", paletteId);
228 m->video->paletteIdRequested = paletteId >> 8;
229}
230
231void vm_op_resetThread(struct VirtualMachine* m) {
232
233 uint8_t threadId = scriptPtr_fetchByte(&m->_scriptPtr);
234 uint8_t i = scriptPtr_fetchByte(&m->_scriptPtr);
235
236 // FCS: WTF, this is cryptic as hell !!
237 // int8_t n = (i & 0x3F) - threadId; //0x3F = 0011 1111
238 // The following is so much clearer
239
240 //Make sure i is within [0-VM_NUM_THREADS-1]
241 i = i & (VM_NUM_THREADS - 1) ;
242 int8_t n = i - threadId;
243
244 if (n < 0) {
245 warning("vm_op_m->resetThread() ec=0x%X (n < 0)", 0x880);
246 return;
247 }
248 ++n;
249 uint8_t a = scriptPtr_fetchByte(&m->_scriptPtr);
250
251 debug(DBG_VM, "vm_op_m->resetThread(%d, %d, %d)", threadId, i, a);
252
253 if (a == 2) {
254 uint16_t *p = &m->threadsData[REQUESTED_PC_OFFSET][threadId];
255 while (n--) {
256 *p++ = 0xFFFE;
257 }
258 } else if (a < 2) {
259 uint8_t *p = &m->vmIsChannelActive[REQUESTED_STATE][threadId];
260 while (n--) {
261 *p++ = a;
262 }
263 }
264}
265
266void vm_op_selectVideoPage(struct VirtualMachine* m) {
267 uint8_t frameBufferId = scriptPtr_fetchByte(&m->_scriptPtr);
268 debug(DBG_VM, "vm_op_selectVideoPage(%d)", frameBufferId);
269 video_changePagePtr1(m->video, frameBufferId);
270}
271
272void vm_op_fillVideoPage(struct VirtualMachine* m) {
273 uint8_t pageId = scriptPtr_fetchByte(&m->_scriptPtr);
274 uint8_t color = scriptPtr_fetchByte(&m->_scriptPtr);
275 debug(DBG_VM, "vm_op_fillVideoPage(%d, %d)", pageId, color);
276 video_fillPage(m->video, pageId, color);
277}
278
279void vm_op_copyVideoPage(struct VirtualMachine* m) {
280 uint8_t srcPageId = scriptPtr_fetchByte(&m->_scriptPtr);
281 uint8_t dstPageId = scriptPtr_fetchByte(&m->_scriptPtr);
282 debug(DBG_VM, "vm_op_copyVideoPage(%d, %d)", srcPageId, dstPageId);
283 video_copyPage(m->video, srcPageId, dstPageId, m->vmVariables[VM_VARIABLE_SCROLL_Y]);
284}
285
286
287static uint32_t lastTimeStamp = 0;
288void vm_op_blitFramebuffer(struct VirtualMachine* m) {
289
290 uint8_t pageId = scriptPtr_fetchByte(&m->_scriptPtr);
291 debug(DBG_VM, "vm_op_blitFramebuffer(%d)", pageId);
292 vm_inp_handleSpecialKeys(m);
293
294 /* Nasty hack....was this present in the original assembly ??!! */
295 if (m->res->currentPartId == GAME_PART_FIRST && m->vmVariables[0x67] == 1)
296 m->vmVariables[0xDC] = 0x21;
297
298 if (!m->_fastMode) {
299
300 int32_t delay = sys_getTimeStamp(m->sys) - lastTimeStamp;
301 int32_t timeToSleep = m->vmVariables[VM_VARIABLE_PAUSE_SLICES] * 20 - delay;
302
303 /* The bytecode will set m->vmVariables[VM_VARIABLE_PAUSE_SLICES] from 1 to 5 */
304 /* The virtual machine hence indicates how long the image should be displayed. */
305
306 if (timeToSleep > 0)
307 {
308 sys_sleep(m->sys, timeToSleep);
309 }
310
311 lastTimeStamp = sys_getTimeStamp(m->sys);
312 }
313
314 /* WTF ? */
315 m->vmVariables[0xF7] = 0;
316
317 video_updateDisplay(m->video, pageId);
318}
319
320void vm_op_killThread(struct VirtualMachine* m) {
321 debug(DBG_VM, "vm_op_killThread()");
322 m->_scriptPtr.pc = m->res->segBytecode + 0xFFFF;
323 m->gotoNextThread = true;
324}
325
326void vm_op_drawString(struct VirtualMachine* m) {
327 uint16_t stringId = scriptPtr_fetchWord(&m->_scriptPtr);
328 uint16_t x = scriptPtr_fetchByte(&m->_scriptPtr);
329 uint16_t y = scriptPtr_fetchByte(&m->_scriptPtr);
330 uint16_t color = scriptPtr_fetchByte(&m->_scriptPtr);
331
332 debug(DBG_VM, "vm_op_drawString(0x%03X, %d, %d, %d)", stringId, x, y, color);
333
334 video_drawString(m->video, color, x, y, stringId);
335}
336
337void vm_op_sub(struct VirtualMachine* m) {
338 uint8_t i = scriptPtr_fetchByte(&m->_scriptPtr);
339 uint8_t j = scriptPtr_fetchByte(&m->_scriptPtr);
340 debug(DBG_VM, "vm_op_sub(0x%02X, 0x%02X)", i, j);
341 m->vmVariables[i] -= m->vmVariables[j];
342}
343
344void vm_op_and(struct VirtualMachine* m) {
345 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
346 uint16_t n = scriptPtr_fetchWord(&m->_scriptPtr);
347 debug(DBG_VM, "vm_op_and(0x%02X, %d)", variableId, n);
348 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] & n;
349}
350
351void vm_op_or(struct VirtualMachine* m) {
352 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
353 uint16_t value = scriptPtr_fetchWord(&m->_scriptPtr);
354 debug(DBG_VM, "vm_op_or(0x%02X, %d)", variableId, value);
355 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] | value;
356}
357
358void vm_op_shl(struct VirtualMachine* m) {
359 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
360 uint16_t leftShiftValue = scriptPtr_fetchWord(&m->_scriptPtr);
361 debug(DBG_VM, "vm_op_shl(0x%02X, %d)", variableId, leftShiftValue);
362 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] << leftShiftValue;
363}
364
365void vm_op_shr(struct VirtualMachine* m) {
366 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
367 uint16_t rightShiftValue = scriptPtr_fetchWord(&m->_scriptPtr);
368 debug(DBG_VM, "vm_op_shr(0x%02X, %d)", variableId, rightShiftValue);
369 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] >> rightShiftValue;
370}
371
372void vm_op_playSound(struct VirtualMachine* m) {
373 uint16_t resourceId = scriptPtr_fetchWord(&m->_scriptPtr);
374 uint8_t freq = scriptPtr_fetchByte(&m->_scriptPtr);
375 uint8_t vol = scriptPtr_fetchByte(&m->_scriptPtr);
376 uint8_t channel = scriptPtr_fetchByte(&m->_scriptPtr);
377 debug(DBG_VM, "vm_op_playSound(0x%X, %d, %d, %d)", resourceId, freq, vol, channel);
378 vm_snd_playSound(m, resourceId, freq, vol, channel);
379}
380
381void vm_op_updateMemList(struct VirtualMachine* m) {
382
383 uint16_t resourceId = scriptPtr_fetchWord(&m->_scriptPtr);
384 debug(DBG_VM, "vm_op_updateMemList(%d)", resourceId);
385
386 if (resourceId == 0) {
387 player_stop(m->player);
388 mixer_stopAll(m->mixer);
389 res_invalidateRes(m->res);
390 } else {
391 res_loadPartsOrMemoryEntry(m->res, resourceId);
392 }
393}
394
395void vm_op_playMusic(struct VirtualMachine* m) {
396 uint16_t resNum = scriptPtr_fetchWord(&m->_scriptPtr);
397 uint16_t delay = scriptPtr_fetchWord(&m->_scriptPtr);
398 uint8_t pos = scriptPtr_fetchByte(&m->_scriptPtr);
399 debug(DBG_VM, "vm_op_playMusic(0x%X, %d, %d)", resNum, delay, pos);
400 vm_snd_playMusic(m, resNum, delay, pos);
401}
402
403void vm_initForPart(struct VirtualMachine* m, uint16_t partId) {
404
405 player_stop(m->player);
406 mixer_stopAll(m->mixer);
407
408 /* WTF is that ? */
409 m->vmVariables[0xE4] = 0x14;
410
411 res_setupPart(m->res, partId);
412
413 /* Set all thread to inactive (pc at 0xFFFF or 0xFFFE ) */
414 rb->memset((uint8_t *)m->threadsData, 0xFF, sizeof(m->threadsData));
415
416 rb->memset((uint8_t *)m->vmIsChannelActive, 0, sizeof(m->vmIsChannelActive));
417
418 int firstThreadId = 0;
419 m->threadsData[PC_OFFSET][firstThreadId] = 0;
420}
421
422/*
423 This is called every frames in the infinite loop.
424*/
425void vm_checkThreadRequests(struct VirtualMachine* m) {
426
427 /* Check if a part switch has been requested. */
428 if (m->res->requestedNextPart != 0) {
429 vm_initForPart(m, m->res->requestedNextPart);
430 m->res->requestedNextPart = 0;
431 }
432
433
434 /* Check if a state update has been requested for any thread during the previous VM execution: */
435 /* - Pause */
436 /* - Jump */
437
438 /* JUMP: */
439 /* Note: If a jump has been requested, the jump destination is stored */
440 /* in m->threadsData[REQUESTED_PC_OFFSET]. Otherwise m->threadsData[REQUESTED_PC_OFFSET] == 0xFFFF */
441
442 /* PAUSE: */
443 /* Note: If a pause has been requested it is stored in m->vmIsChannelActive[REQUESTED_STATE][i] */
444
445 for (int threadId = 0; threadId < VM_NUM_THREADS; threadId++) {
446
447 m->vmIsChannelActive[CURR_STATE][threadId] = m->vmIsChannelActive[REQUESTED_STATE][threadId];
448
449 uint16_t n = m->threadsData[REQUESTED_PC_OFFSET][threadId];
450
451 if (n != VM_NO_SETVEC_REQUESTED) {
452
453 m->threadsData[PC_OFFSET][threadId] = (n == 0xFFFE) ? VM_INACTIVE_THREAD : n;
454 m->threadsData[REQUESTED_PC_OFFSET][threadId] = VM_NO_SETVEC_REQUESTED;
455 }
456 }
457}
458
459void vm_hostFrame(struct VirtualMachine* m) {
460
461 /* Run the Virtual Machine for every active threads (one vm frame). */
462 /* Inactive threads are marked with a thread instruction pointer set to 0xFFFF (VM_INACTIVE_THREAD). */
463 /* A thread must feature a break opcode so the interpreter can move to the next thread. */
464
465 for (int threadId = 0; threadId < VM_NUM_THREADS; threadId++) {
466
467 if (m->vmIsChannelActive[CURR_STATE][threadId])
468 continue;
469
470 uint16_t n = m->threadsData[PC_OFFSET][threadId];
471
472 if (n != VM_INACTIVE_THREAD) {
473
474 /* Set the script pointer to the right location. */
475 /* script pc is used in executeThread in order */
476 /* to get the next opcode. */
477 m->_scriptPtr.pc = m->res->segBytecode + n;
478 m->_stackPtr = 0;
479
480 m->gotoNextThread = false;
481 debug(DBG_VM, "vm_hostFrame() i=0x%02X n=0x%02X *p=0x%02X", threadId, n, *m->_scriptPtr.pc);
482 vm_executeThread(m);
483
484 /* Since .pc is going to be modified by this next loop iteration, we need to save it. */
485 m->threadsData[PC_OFFSET][threadId] = m->_scriptPtr.pc - m->res->segBytecode;
486
487
488 debug(DBG_VM, "vm_hostFrame() i=0x%02X pos=0x%X", threadId, m->threadsData[PC_OFFSET][threadId]);
489 if (m->sys->input.quit) {
490 break;
491 }
492 }
493 }
494}
495
496#define COLOR_BLACK 0xFF
497#define DEFAULT_ZOOM 0x40
498
499
500void vm_executeThread(struct VirtualMachine* m) {
501
502 while (!m->gotoNextThread) {
503 uint8_t opcode = scriptPtr_fetchByte(&m->_scriptPtr);
504
505 /* 1000 0000 is set */
506 if (opcode & 0x80)
507 {
508 uint16_t off = ((opcode << 8) | scriptPtr_fetchByte(&m->_scriptPtr)) * 2;
509 m->res->_useSegVideo2 = false;
510 int16_t x = scriptPtr_fetchByte(&m->_scriptPtr);
511 int16_t y = scriptPtr_fetchByte(&m->_scriptPtr);
512 int16_t h = y - 199;
513 if (h > 0) {
514 y = 199;
515 x += h;
516 }
517 debug(DBG_VIDEO, "vid_opcd_0x80 : opcode=0x%X off=0x%X x=%d y=%d", opcode, off, x, y);
518
519 /* This switch the polygon database to "cinematic" and probably draws a black polygon */
520 /* over all the screen. */
521 video_setDataBuffer(m->video, m->res->segCinematic, off);
522 struct Point temp;
523 temp.x = x;
524 temp.y = y;
525 video_readAndDrawPolygon(m->video, COLOR_BLACK, DEFAULT_ZOOM, &temp);
526
527 continue;
528 }
529
530 /* 0100 0000 is set */
531 if (opcode & 0x40)
532 {
533 int16_t x, y;
534 uint16_t off = scriptPtr_fetchWord(&m->_scriptPtr) * 2;
535 x = scriptPtr_fetchByte(&m->_scriptPtr);
536
537 m->res->_useSegVideo2 = false;
538
539 if (!(opcode & 0x20))
540 {
541 if (!(opcode & 0x10)) /* 0001 0000 is set */
542 {
543 x = (x << 8) | scriptPtr_fetchByte(&m->_scriptPtr);
544 } else {
545 x = m->vmVariables[x];
546 }
547 }
548 else
549 {
550 if (opcode & 0x10) { /* 0001 0000 is set */
551 x += 0x100;
552 }
553 }
554
555 y = scriptPtr_fetchByte(&m->_scriptPtr);
556
557 if (!(opcode & 8)) /* 0000 1000 is set */
558 {
559 if (!(opcode & 4)) { /* 0000 0100 is set */
560 y = (y << 8) | scriptPtr_fetchByte(&m->_scriptPtr);
561 } else {
562 y = m->vmVariables[y];
563 }
564 }
565
566 uint16_t zoom = scriptPtr_fetchByte(&m->_scriptPtr);
567
568 if (!(opcode & 2)) /* 0000 0010 is set */
569 {
570 if (!(opcode & 1)) /* 0000 0001 is set */
571 {
572 --m->_scriptPtr.pc;
573 zoom = 0x40;
574 }
575 else
576 {
577 zoom = m->vmVariables[zoom];
578 }
579 }
580 else
581 {
582
583 if (opcode & 1) { /* 0000 0001 is set */
584 m->res->_useSegVideo2 = true;
585 --m->_scriptPtr.pc;
586 zoom = 0x40;
587 }
588 }
589 debug(DBG_VIDEO, "vid_opcd_0x40 : off=0x%X x=%d y=%d", off, x, y);
590 video_setDataBuffer(m->video, m->res->_useSegVideo2 ? m->res->_segVideo2 : m->res->segCinematic, off);
591 struct Point temp;
592 temp.x = x;
593 temp.y = y;
594 video_readAndDrawPolygon(m->video, 0xFF, zoom, &temp);
595
596 continue;
597 }
598
599
600 if (opcode > 0x1A)
601 {
602 error("vm_executeThread() ec=0x%X invalid opcode=0x%X", 0xFFF, opcode);
603 }
604 else
605 {
606 (vm_opcodeTable[opcode])(m);
607 }
608
609 rb->yield();
610 }
611}
612
613void vm_inp_updatePlayer(struct VirtualMachine* m) {
614
615 sys_processEvents(m->sys);
616
617 if (m->res->currentPartId == 0x3E89) {
618 char c = m->sys->input.lastChar;
619 if (c == 8 || /*c == 0xD |*/ c == 0 || (c >= 'a' && c <= 'z')) {
620 m->vmVariables[VM_VARIABLE_LAST_KEYCHAR] = c & ~0x20;
621 m->sys->input.lastChar = 0;
622 }
623 }
624
625 int16_t lr = 0;
626 int16_t mask = 0;
627 int16_t ud = 0;
628
629 if (m->sys->input.dirMask & DIR_RIGHT) {
630 lr = 1;
631 mask |= 1;
632 }
633 if (m->sys->input.dirMask & DIR_LEFT) {
634 lr = -1;
635 mask |= 2;
636 }
637 if (m->sys->input.dirMask & DIR_DOWN) {
638 ud = 1;
639 mask |= 4;
640 }
641
642 m->vmVariables[VM_VARIABLE_HERO_POS_UP_DOWN] = ud;
643
644 if (m->sys->input.dirMask & DIR_UP) {
645 m->vmVariables[VM_VARIABLE_HERO_POS_UP_DOWN] = -1;
646 }
647
648 if (m->sys->input.dirMask & DIR_UP) { /* inpJump */
649 ud = -1;
650 mask |= 8;
651 }
652
653 m->vmVariables[VM_VARIABLE_HERO_POS_JUMP_DOWN] = ud;
654 m->vmVariables[VM_VARIABLE_HERO_POS_LEFT_RIGHT] = lr;
655 m->vmVariables[VM_VARIABLE_HERO_POS_MASK] = mask;
656 int16_t button = 0;
657
658 if (m->sys->input.button) {
659 button = 1;
660 mask |= 0x80;
661 }
662
663 m->vmVariables[VM_VARIABLE_HERO_ACTION] = button;
664 m->vmVariables[VM_VARIABLE_HERO_ACTION_POS_MASK] = mask;
665}
666
667void vm_inp_handleSpecialKeys(struct VirtualMachine* m) {
668
669 if (m->sys->input.pause) {
670
671 if (m->res->currentPartId != GAME_PART1 && m->res->currentPartId != GAME_PART2) {
672 m->sys->input.pause = false;
673 while (!m->sys->input.pause) {
674 sys_processEvents(m->sys);
675 sys_sleep(m->sys, 200);
676 }
677 }
678 m->sys->input.pause = false;
679 }
680
681 if (m->sys->input.code) {
682 m->sys->input.code = false;
683 if (m->res->currentPartId != GAME_PART_LAST && m->res->currentPartId != GAME_PART_FIRST) {
684 m->res->requestedNextPart = GAME_PART_LAST;
685 }
686 }
687
688 /* User has inputted a bad code, the "ERROR" screen is showing */
689 if (m->vmVariables[0xC9] == 1) {
690 debug(DBG_VM, "vm_inp_handleSpecialKeys() unhandled case (m->vmVariables[0xC9] == 1)");
691 }
692
693}
694
695void vm_snd_playSound(struct VirtualMachine* m, uint16_t resNum, uint8_t freq, uint8_t vol, uint8_t channel) {
696
697 debug(DBG_SND, "snd_playSound(0x%X, %d, %d, %d)", resNum, freq, vol, channel);
698
699 struct MemEntry *me = &m->res->_memList[resNum];
700
701 if (me->state != MEMENTRY_STATE_LOADED)
702 return;
703
704
705 if (vol == 0) {
706 mixer_stopChannel(m->mixer, channel);
707 } else {
708 struct MixerChunk mc;
709 rb->memset(&mc, 0, sizeof(mc));
710 mc.data = me->bufPtr + 8; /* skip header */
711 mc.len = READ_BE_UINT16(me->bufPtr) * 2;
712 mc.loopLen = READ_BE_UINT16(me->bufPtr + 2) * 2;
713 if (mc.loopLen != 0) {
714 mc.loopPos = mc.len;
715 }
716 assert(freq < 40);
717 mixer_playChannel(m->mixer, channel & 3, &mc, vm_frequenceTable[freq], MIN(vol, 0x3F));
718 }
719
720}
721
722void vm_snd_playMusic(struct VirtualMachine* m, uint16_t resNum, uint16_t delay, uint8_t pos) {
723
724 debug(DBG_SND, "snd_playMusic(0x%X, %d, %d)", resNum, delay, pos);
725
726 if (resNum != 0) {
727 player_loadSfxModule(m->player, resNum, delay, pos);
728 player_start(m->player);
729 } else if (delay != 0) {
730 player_setEventsDelay(m->player, delay);
731 } else {
732 player_stop(m->player);
733 }
734}
735
736void vm_saveOrLoad(struct VirtualMachine* m, struct Serializer *ser) {
737 struct Entry entries[] = {
738 SE_ARRAY(m->vmVariables, 0x100, SES_INT16, VER(1)),
739 SE_ARRAY(m->_scriptStackCalls, 0x100, SES_INT16, VER(1)),
740 SE_ARRAY(m->threadsData, 0x40 * 2, SES_INT16, VER(1)),
741 SE_ARRAY(m->vmIsChannelActive, 0x40 * 2, SES_INT8, VER(1)),
742 SE_END()
743 };
744 ser_saveOrLoadEntries(ser, entries);
745}
746
747void vm_bypassProtection(struct VirtualMachine* m)
748{
749 File f;
750 file_create(&f, true);
751 if (!file_open(&f, "bank0e", res_getDataDir(m->res), "rb")) {
752 warning("Unable to bypass protection: add bank0e file to datadir");
753 } else {
754 struct Serializer s;
755 ser_create(&s, &f, SM_LOAD, m->res->_memPtrStart, 2);
756 vm_saveOrLoad(m, &s);
757 res_saveOrLoad(m->res, &s);
758 video_saveOrLoad(m->video, &s);
759 player_saveOrLoad(m->player, &s);
760 mixer_saveOrLoad(m->mixer, &s);
761 }
762 file_close(&f);
763}
diff --git a/apps/plugins/xworld/vm.h b/apps/plugins/xworld/vm.h
new file mode 100644
index 0000000000..1409dd47e3
--- /dev/null
+++ b/apps/plugins/xworld/vm.h
@@ -0,0 +1,184 @@
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#ifndef __LOGIC_H__
24#define __LOGIC_H__
25
26
27
28#include "intern.h"
29
30#define VM_NUM_THREADS 64
31#define VM_NUM_VARIABLES 256
32#define VM_NO_SETVEC_REQUESTED 0xFFFF
33#define VM_INACTIVE_THREAD 0xFFFF
34
35
36#define VM_VARIABLE_RANDOM_SEED 0x3C
37
38#define VM_VARIABLE_LAST_KEYCHAR 0xDA
39
40#define VM_VARIABLE_HERO_POS_UP_DOWN 0xE5
41#define VM_VARIABLE_MUS_MARK 0xF4
42
43#define VM_VARIABLE_SCROLL_Y 0xF9
44#define VM_VARIABLE_HERO_ACTION 0xFA
45#define VM_VARIABLE_HERO_POS_JUMP_DOWN 0xFB
46#define VM_VARIABLE_HERO_POS_LEFT_RIGHT 0xFC
47#define VM_VARIABLE_HERO_POS_MASK 0xFD
48#define VM_VARIABLE_HERO_ACTION_POS_MASK 0xFE
49#define VM_VARIABLE_PAUSE_SLICES 0xFF
50
51struct Mixer;
52struct Resource;
53struct Serializer;
54struct SfxPlayer;
55struct System;
56struct Video;
57
58//For threadsData navigation
59#define PC_OFFSET 0
60#define REQUESTED_PC_OFFSET 1
61#define NUM_DATA_FIELDS 2
62
63//For vmIsChannelActive navigation
64#define CURR_STATE 0
65#define REQUESTED_STATE 1
66#define NUM_THREAD_FIELDS 2
67
68struct VirtualMachine;
69
70void vm_create(struct VirtualMachine*, struct Mixer *mix, struct Resource *res, struct SfxPlayer *ply, struct Video *vid, struct System *stub);
71void vm_init(struct VirtualMachine*);
72
73void vm_op_movConst(struct VirtualMachine*);
74void vm_op_mov(struct VirtualMachine*);
75void vm_op_add(struct VirtualMachine*);
76void vm_op_addConst(struct VirtualMachine*);
77void vm_op_call(struct VirtualMachine*);
78void vm_op_ret(struct VirtualMachine*);
79void vm_op_pauseThread(struct VirtualMachine*);
80void vm_op_jmp(struct VirtualMachine*);
81void vm_op_setSetVect(struct VirtualMachine*);
82void vm_op_jnz(struct VirtualMachine*);
83void vm_op_condJmp(struct VirtualMachine*);
84void vm_op_setPalette(struct VirtualMachine*);
85void vm_op_resetThread(struct VirtualMachine*);
86void vm_op_selectVideoPage(struct VirtualMachine*);
87void vm_op_fillVideoPage(struct VirtualMachine*);
88void vm_op_copyVideoPage(struct VirtualMachine*);
89void vm_op_blitFramebuffer(struct VirtualMachine*);
90void vm_op_killThread(struct VirtualMachine*);
91void vm_op_drawString(struct VirtualMachine*);
92void vm_op_sub(struct VirtualMachine*);
93void vm_op_and(struct VirtualMachine*);
94void vm_op_or(struct VirtualMachine*);
95void vm_op_shl(struct VirtualMachine*);
96void vm_op_shr(struct VirtualMachine*);
97void vm_op_playSound(struct VirtualMachine*);
98void vm_op_updateMemList(struct VirtualMachine*);
99void vm_op_playMusic(struct VirtualMachine*);
100
101void vm_initForPart(struct VirtualMachine*, uint16_t partId);
102void vm_setupPart(struct VirtualMachine*, uint16_t partId);
103void vm_checkThreadRequests(struct VirtualMachine*);
104void vm_hostFrame(struct VirtualMachine*);
105void vm_executeThread(struct VirtualMachine*);
106
107void vm_inp_updatePlayer(struct VirtualMachine*);
108void vm_inp_handleSpecialKeys(struct VirtualMachine*);
109
110void vm_snd_playSound(struct VirtualMachine*, uint16_t resNum, uint8_t freq, uint8_t vol, uint8_t channel);
111void vm_snd_playMusic(struct VirtualMachine*, uint16_t resNum, uint16_t delay, uint8_t pos);
112
113void vm_saveOrLoad(struct VirtualMachine*, struct Serializer *ser);
114void vm_bypassProtection(struct VirtualMachine*);
115
116typedef void (*OpcodeStub)(struct VirtualMachine*);
117
118// The type of entries in opcodeTable. This allows "fast" branching
119static const OpcodeStub vm_opcodeTable[] = {
120 /* 0x00 */
121 &vm_op_movConst,
122 &vm_op_mov,
123 &vm_op_add,
124 &vm_op_addConst,
125 /* 0x04 */
126 &vm_op_call,
127 &vm_op_ret,
128 &vm_op_pauseThread,
129 &vm_op_jmp,
130 /* 0x08 */
131 &vm_op_setSetVect,
132 &vm_op_jnz,
133 &vm_op_condJmp,
134 &vm_op_setPalette,
135 /* 0x0C */
136 &vm_op_resetThread,
137 &vm_op_selectVideoPage,
138 &vm_op_fillVideoPage,
139 &vm_op_copyVideoPage,
140 /* 0x10 */
141 &vm_op_blitFramebuffer,
142 &vm_op_killThread,
143 &vm_op_drawString,
144 &vm_op_sub,
145 /* 0x14 */
146 &vm_op_and,
147 &vm_op_or,
148 &vm_op_shl,
149 &vm_op_shr,
150 /* 0x18 */
151 &vm_op_playSound,
152 &vm_op_updateMemList,
153 &vm_op_playMusic
154};
155
156struct VirtualMachine {
157 //This table is used to play a sound
158 //static const uint16_t frequenceTable[];
159 /* FW: moved from staticres.c to vm.c */
160
161 struct Mixer *mixer;
162 struct Resource *res;
163 struct SfxPlayer *player;
164 struct Video *video;
165 struct System *sys;
166
167 int16_t vmVariables[VM_NUM_VARIABLES];
168 uint16_t _scriptStackCalls[VM_NUM_THREADS];
169
170 uint16_t threadsData[NUM_DATA_FIELDS][VM_NUM_THREADS];
171 // This array is used:
172 // 0 to save the channel's instruction pointer
173 // when the channel release control (this happens on a break).
174
175 // 1 When a setVec is requested for the next vm frame.
176
177 uint8_t vmIsChannelActive[NUM_THREAD_FIELDS][VM_NUM_THREADS];
178
179 struct Ptr _scriptPtr;
180 uint8_t _stackPtr;
181 bool gotoNextThread;
182 bool _fastMode;
183};
184#endif
diff --git a/apps/plugins/xworld/xworld.c b/apps/plugins/xworld/xworld.c
new file mode 100644
index 0000000000..932ff1c3e1
--- /dev/null
+++ b/apps/plugins/xworld/xworld.c
@@ -0,0 +1,244 @@
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 "sys.h"
26#include "util.h"
27
28/* we don't want these on the stack, they're big and could cause a stack overflow */
29static struct Engine e;
30static struct System sys;
31
32enum plugin_status plugin_start(const void* parameter)
33{
34 (void) parameter;
35
36 /* no trailing slashes */
37 const char *dataPath = "/.rockbox/xworld";
38 const char *savePath = "/.rockbox/xworld";
39 g_debugMask = 0;
40
41 engine_create(&e, &sys, dataPath, savePath);
42 engine_init(&e);
43 sys_menu(&sys);
44
45 engine_run(&e);
46
47 engine_finish(&e);
48 return PLUGIN_OK;
49}
50
51
52
53/*
54 Game was originally made with 16. SIXTEEN colors. Running on 320x200 (64,000 pixels.)
55
56 Great fan site here: https://sites.google.com/site/interlinkknight/anotherworld
57 Contains the wheelcode :P !
58
59 A lot of details can be found regarding the game and engine architecture at:
60 http://www.anotherworld.fr/anotherworld_uk/another_world.htm
61
62 The chronology of the game implementation can retraced via the ordering of the opcodes:
63 The sound and music opcode are at the end: Music and sound was done at the end.
64
65 Call tree:
66 =========
67
68 SDLSystem systemImplementaion ;
69 System *sys = & systemImplementaion ;
70
71 main
72 {
73
74 Engine *e = new Engine();
75 e->run()
76 {
77 sys->init("Out Of This World");
78 setup();
79 vm.restartAt(0x3E80); // demo starts at 0x3E81
80
81 while (!_stub->_pi.quit)
82 {
83 vm.setupScripts();
84 vm.inp_updatePlayer();
85 processInput();
86 vm.runScripts();
87 }
88
89 finish();
90 }
91 }
92
93
94 Virtual Machine:
95 ================
96
97 Seems the threading model is collaborative multi-tasking (as opposed to preemptive multitasking):
98 A thread (called a Channel on Eric Chahi's website) will release the hand to the next one via the
99 break opcode.
100
101 It seems that even when a setvec is requested by a thread, we cannot set the instruction pointer
102 yet. The thread is allowed to keep on executing its code for the remaining of the vm frame.
103
104 A virtual machine frame has a variable duration. The unit of time is 20ms and the frame can be set
105 to live for 1 (20ms ; 50Hz) up to 5 (100ms ; 10Hz).
106
107
108 There are 30 something opcodes. The graphic opcode are more complex, not only the declare the operation to perform
109 they also define where to find the vertices (segVideo1 or segVideo2).
110
111 No stack available but a thread can save its pc (Program Counter) once: One method call and return is possible.
112
113
114 Video :
115 =======
116 Double buffer architecture. AW opcodes even has a special instruction for blitting from one
117 frame buffer to an other.
118
119 Double buffering is implemented in software
120
121 According to Eric Chahi's webpage there are 4 framebuffer. Since on full screenbuffer is 320x200/2 = 32kB
122 that would mean the total size consumed is 128KB ?
123
124 Sound :
125 =======
126 Mixing is done on software.
127
128 Since the virtual machine and SDL are running simultaneously in two different threads:
129 Any read or write to an elements of the sound channels MUST be synchronized with a
130 mutex.
131
132 FastMode :
133 ==========
134
135 The game engine features a "fast-mode"...what it to be able to respond to the now defunct
136 TURBO button commonly found on 386/486 era PC ?!
137
138
139 Endianess:
140 ==========
141
142 Atari and Amiga used bigEndian CPUs. Data are hence stored within BANK in big endian format.
143 On an Intel or ARM CPU data will have to be transformed when read.
144
145
146
147 The original codebase contained a looooot of cryptic hexa values.
148 0x100 (for 256 variables)
149 0x400 (for one kilobyte)
150 0x40 (for num threads)
151 0x3F (num thread mask)
152 I cleaned that up.
153
154 Questions & Answers :
155 =====================
156
157 Q: How does the interpreter deals with the CPU speed ?! A pentium is a tad faster than a Motorola 68000
158 after all.
159 A: See vm frame time: The vm frame duration is variable. The vm actually write for how long a video frame
160 should be displayed in variable 0xFF. The value is the number of 20ms slice
161
162 Q: Why is a palette 2048 bytes if there are only 16 colors ? I would have expected 48 bytes...
163 A: ???
164
165 Q: Why does Resource::load() search for ressource to load from higher to lower....since it will load stuff
166 until no more ressources are marked as "Need to be loaded".
167 A: ???
168
169 Original DOS version :
170 ======================
171
172 Banks: 1,236,519 B
173 exe : 20,293 B
174
175
176 Total bank size: 1236519 (100%)
177 ---------------------------------
178 Total RT_SOUND size: 585052 ( 47%)
179 Total RT_MUSIC size: 3540 ( 0%)
180 Total RT_POLY_ANIM size: 106676 ( 9%)
181 Total RT_PALETTE size: 11032 ( 1%)
182 Total RT_BYTECODE size: 135948 ( 11%)
183 Total RT_POLY_CINEMATIC size: 291008 ( 24%)
184
185 As usual sounds are the most consuming assets (Quake1,Quake2 etc.....)
186
187
188 memlist.bin features 146 entries :
189 ==================================
190
191 Most important part in an entry are:
192
193 bankId : - Give the file were the resource is.
194 offset : - How much to skip in the file before hiting the resource.
195 size,packetSize : - How much to read, should we unpack what we read.
196
197
198
199 Polygons drawing :
200 =================
201
202 Polygons can be given as:
203 - a pure screenspace sequence of points: I call those screenspace polygons.
204 - a list of delta to add or substract to the first vertex. I call those: objectspace polygons.
205
206 Video :
207 =======
208
209 Q: Why 4 framebuffer ?
210 A: It seems the background is generated once (like in the introduction) and stored in a framebuffer.
211 Every frame the saved background is copied and new elements are drawn on top.
212
213
214 Trivia :
215 ========
216
217 If you are used to RGBA 32bits per pixel framebuffer you are in for a shock:
218 Another world is 16 colors palette based, making it 4bits per pixel !!
219
220 Video generation :
221 ==================
222
223 Thank god the engine sets the palette before starting to drawing instead of after bliting.
224 I would have been unable to generate screenshots otherwise.
225
226 Memory managment :
227 =================
228
229 There is 0 malloc during the game. All resources are loaded in one big buffer (Resource::load).
230 The entire buffer is "freed" at the end of a game part.
231
232
233 The renderer is actually capable of Blending a new poly in the framebuffer (Video::drawLineT)
234
235
236 I am almost sure that:
237 _curPagePtr1 is the backbuffer
238 _curPagePtr2 is the frontbuffer
239 _curPagePtr3 is the background builder.
240
241
242 * Why does memlist.bin uses a special state field 0xFF in order to mark the end of resources ??!
243 It would have been so much easier to write the number of resources at the beginning of the code.
244*/
diff --git a/apps/plugins/xworld/xworld.make b/apps/plugins/xworld/xworld.make
new file mode 100644
index 0000000000..7d6966842b
--- /dev/null
+++ b/apps/plugins/xworld/xworld.make
@@ -0,0 +1,27 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10XWORLDSRCDIR := $(APPSDIR)/plugins/xworld
11XWORLDBUILDDIR := $(BUILDDIR)/apps/plugins/xworld
12
13ROCKS += $(XWORLDBUILDDIR)/xworld.rock
14
15XWORLD_SRC := $(call preprocess, $(XWORLDSRCDIR)/SOURCES)
16XWORLD_OBJ := $(call c2obj, $(XWORLD_SRC))
17
18# add source files to OTHER_SRC to get automatic dependencies
19OTHER_SRC += $(XWORLD_SRC)
20
21XWORLDFLAGS = $(filter-out -O%,$(PLUGINFLAGS)) -O2
22
23$(XWORLDBUILDDIR)/xworld.rock: $(XWORLD_OBJ)
24
25$(XWORLDBUILDDIR)/%.o: $(XWORLDSRCDIR)/%.c $(XWORLDSRCDIR)/xworld.make
26 $(SILENT)mkdir -p $(dir $@)
27 $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(XWORLDFLAGS) -c $< -o $@
diff --git a/docs/CREDITS b/docs/CREDITS
index 96a5b784bd..0f548e2810 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -672,3 +672,5 @@ The Pure Data team (Miller Puckette and others)
672The MikMod team 672The MikMod team
673Michael McTernan (The ARM unwinder author) 673Michael McTernan (The ARM unwinder author)
674Albert Song 674Albert Song
675The New RAW team (Piotr Padkowski and others)
676The Fabother World team (Fabien Sanglard and others)
diff --git a/manual/plugins/main.tex b/manual/plugins/main.tex
index a49cfacbef..4143fd6b63 100644
--- a/manual/plugins/main.tex
+++ b/manual/plugins/main.tex
@@ -94,6 +94,8 @@ text files%
94 94
95\opt{lcd_bitmap}{\input{plugins/xobox.tex}} 95\opt{lcd_bitmap}{\input{plugins/xobox.tex}}
96 96
97\opt{lcd_bitmap}{\input{plugins/xworld.tex}}
98
97\section{Demos} 99\section{Demos}
98 100
99\opt{lcd_bitmap}{\input{plugins/bounce.tex}} 101\opt{lcd_bitmap}{\input{plugins/bounce.tex}}
diff --git a/manual/plugins/xworld.tex b/manual/plugins/xworld.tex
new file mode 100644
index 0000000000..32e3ecbf78
--- /dev/null
+++ b/manual/plugins/xworld.tex
@@ -0,0 +1,81 @@
1\subsection{XWorld}
2
3In this cinematic, award winning platform game by Éric Chahi, you must evade capture
4and do your best to escape an alien planet. After an experiment goes awry the hero
5must team up with an unlikely ally, when they both become fugitives on another world.
6XWorld requires the data files, bank* and memlist.bin, from the original "Another World"
7PC game to be copied into the .rockbox/xworld/ directory before the game can be played.
8
9\begin{btnmap}
10 %
11 \opt{RECORDER_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD,IAUDIO_X5_PAD%
12 ,SANSA_E200_PAD,SANSA_FUZE_PAD,SANSA_C200_PAD,SANSA_CLIP_PAD,GIGABEAT_PAD%
13 ,GIGABEAT_S_PAD,MROBE100_PAD,PBELL_VIBE500_PAD,SANSA_FUZEPLUS_PAD%
14 ,SAMSUNG_YH92X_PAD,SAMSUNG_YH820_PAD}
15 {\ButtonUp}
16 \opt{IPOD_4G_PAD,IPOD_3G_PAD,IPOD_1G2G_PAD}{\ButtonMenu}
17 \opt{IRIVER_H10_PAD}{\ButtonScrollUp}
18 \opt{HAVE_TOUCHSCREEN}{\TouchTopMiddle}
19 \opt{PBELL_VIBE500_PAD}{\ButtonOk}
20 \opt{HAVEREMOTEKEYMAP}{& }
21 & Up and Jump \\
22 %
23 \opt{RECORDER_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD,IAUDIO_X5_PAD%
24 ,SANSA_E200_PAD,SANSA_FUZE_PAD,SANSA_C200_PAD,SANSA_CLIP_PAD,GIGABEAT_PAD%
25 ,GIGABEAT_S_PAD,MROBE100_PAD,PBELL_VIBE500_PAD,SANSA_FUZEPLUS_PAD%
26 ,SAMSUNG_YH92X_PAD,SAMSUNG_YH820_PAD}
27 {\ButtonDown}
28 \opt{IPOD_4G_PAD,IPOD_3G_PAD,IPOD_1G2G_PAD}{\ButtonPlay}
29 \opt{IRIVER_H10_PAD}{\ButtonScrollDown}
30 \opt{HAVE_TOUCHSCREEN}{\TouchBottomMiddle}
31 \opt{PBELL_VIBE500_PAD}{\ButtonCancel}
32 \opt{HAVEREMOTEKEYMAP}{& }
33 & Down and Crouch\\
34 %
35 \opt{RECORDER_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD,IAUDIO_X5_PAD%
36 ,SANSA_E200_PAD,SANSA_FUZE_PAD,SANSA_C200_PAD,SANSA_CLIP_PAD,GIGABEAT_PAD%
37 ,GIGABEAT_S_PAD,MROBE100_PAD,PBELL_VIBE500_PAD,SANSA_FUZEPLUS_PAD%
38 ,SAMSUNG_YH92X_PAD,SAMSUNG_YH820_PAD,IPOD_4G_PAD,IPOD_3G_PAD,IPOD_1G2G_PAD%
39 ,IRIVER_H10_PAD}
40 {\ButtonLeft / \ButtonRight}
41 \opt{HAVE_TOUCHSCREEN}{\TouchMidLeft / \TouchMidRight}
42 \opt{PBELL_VIBE500_PAD}{\ButtonMenu / \ButtonPlay}
43 \opt{HAVEREMOTEKEYMAP}{& }
44 & Move Left and Right\\
45 %
46 \opt{SANSA_FUZE_PAD}{\ButtonHome}
47 \opt{SAMSUNG_YH920_PAD}{\ButtonFFWD}
48 \opt{IRIVER_H300_PAD,SANSA_E200_PAD,SAMSUNG_YH820_PAD,IAUDIO_X5M5_PAD}{\ButtonRec}
49 \opt{IPOD_4G_PAD,IPOD_3G_PAD,IPOD_1G2G_PAD,CREATIVE_ZEN_PAD,SANSA_CLIP_PAD}{\ButtonSelect}
50 \opt{SONY_NWZ_PAD,CREATIVEZVM_PAD}{\ButtonPlay}
51 \opt{ONDAVX777_PAD,MROBE500_PAD,PBELL_VIBE500_PAD}{\ButtonPower}
52 \opt{SAMSUNG_YPR0_PAD}{\ButtonUser}
53 \opt{IRIVER_H10_PAD}{\ButtonRew}
54 \opt{HM801_PAD}{\ButtonPrev}
55 \opt{SONY_NWZ_PAD,CREATIVEZVM_PAD}{\ButtonPlay}
56 \opt{MROBE500_PAD}{\ButtonPower}
57 \opt{DX50_PAD,ONDAVX747_PAD,PHILIPS_HDD1630_PAD,PHILIPS_HDD6330_PAD,PHILIPS_SA9200_PAD%
58 ,CREATIVE_ZENXFI2_PAD,CREATIVE_ZENXFI3_PAD,SANSA_CONNECT_PAD,SANSA_C200_PAD%
59 ,SANSA_FUZEPLUS_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD}{\ButtonVolUp}
60 \opt{HAVE_TOUCHSCREEN}{\ButtonBottomLeft}
61 \opt{HAVEREMOTEKEYMAP}{& }
62 & Action and Fire\\
63 %
64 \opt{DX50_PAD,ONDAVX747_PAD,PHILIPS_HDD1630_PAD,PHILIPS_HDD6330_PAD,PHILIPS_SA9200_PAD%
65 ,CREATIVE_ZENXFI2_PAD,CREATIVE_ZENXFI3_PAD,SANSA_CONNECT_PAD,SANSA_C200_PAD%
66 ,SANSA_FUZEPLUS_PAD}{\ButtonVolDown}
67 \opt{GIGABEAT_PAD,GIGABEAT_S_PAD}{\ButtonMenu}
68 \opt{SANSA_FUZE_PAD}{\ButtonSelect}
69 \opt{SAMSUNG_YH920_PAD}{\ButtonRew}
70 \opt{SAMSUNG_YH820_PAD,IAUDIO_X5M5_PAD}{\ButtonPlay}
71 \opt{SANSA_E200_PAD,SANSA_CLIP_PAD}{\ButtonPower}
72 \opt{CREATIVE_ZEN_PAD,SONY_NWZ_PAD}{\ButtonBack}
73 \opt{CREATIVEZVM_PAD,SAMSUNG_YPR0_PAD}{\ButtonMenu}
74 \opt{IRIVER_H300_PAD}{\ButtonMode}
75 \opt{HM801_PAD}{\ButtonNext}
76 \opt{PBELL_VIBE500_PAD}{\ButtonRec}
77 \opt{IRIVER_H10_PAD}{\ButtonPlay}
78 \opt{IPOD_4G_PAD,IPOD_3G_PAD,IPOD_1G2G_PAD}{\ButtonMenu / \ButtonSelect}
79 \opt{HAVEREMOTEKEYMAP}{& }
80 & Menu\\
81\end{btnmap}