-
Notifications
You must be signed in to change notification settings - Fork 28
/
formats.js
274 lines (251 loc) · 9.09 KB
/
formats.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
'use strict';
/* This module is used by both the main process (main.js) and the game
process (apphooks.js). And the prefs window too, come to think of it.
It contains all the information about the game formats which Lectrote
understands.
The bulk of this module is a list of format entries, each of which
contains (zero or more) engine entries.
A format entry contains:
- id: key
- longname: long description of file type (used in Windows file dialogs)
- name: one-word description of file type (used in loading error
messages)
- extensions: list of file suffixes
- docicon: Windows document icon filename
- identify: function which looks at the first 16 bytes of the file and
returns whether that file matches this format
- engines: list of engines
An engine entry contains:
- id: key
- name: engine name (used in page titles)
- html: HTML file used for the game page
- load: function which takes the loading args, the loaded file (as an
array), and an options object. The function must add any needed options
to the options object, and then return the loaded file with any
modifications needed for load_run().
- get_signature: function which returns the signature of the loaded
file (as a string).
Note that the load() and get_signature() functions are only called in
the game process. They may require modules which are not available in
the main process.
*/
/* TODO: Switch to the new module format, where modules are provided as classes rather than window.Module.
I think the engine will provide a getlibrary() call. Then this function can do:
opts.Dialog = engine.getlibrary('Dialog');
opts.GlkOte = engine.getlibrary('GlkOte');
...and so on.
(Referring to window.GiLoad will remain valid.)
*/
const fs = require('fs')
function common_emglken_load(file, buf, opts) {
const data = Uint8Array.from(buf);
const vm_class = require(`./emglken/${file}.js`).default
const vm = opts.vm = window.engine = new vm_class()
opts.Dialog = window.Dialog;
opts.Glk = opts.io = {
fatal_error: window.GlkOte.error,
init(opts) {
// Call init here because GiLoad will send extracted Glulx files rather than the whole blorb
vm.init(data, opts);
// start will call GlkOte later on
vm.start(opts);
},
};
opts.GlkOte = window.GlkOte;
opts.wasmBinary = fs.readFileSync(`${__dirname}/emglken/${file}-core.wasm`)
if (file === 'git' || file === 'glulxe') {
opts.blorb_gamechunk_type = 'GLUL';
}
return data;
}
const formatlist = [
{
id: 'blorb',
longname: 'Blorbed Game File',
extensions: ['blorb', 'blb'],
/* No engine; this exists solely to supply file suffixes for the
open-game dialog. */
},
{
id: 'adrift4',
longname: 'Adrift 4 Game File',
name: 'Adrift 4',
extensions: [ 'taf' ],
docicon: 'docicon-adrift.ico',
engines: [
{
id: 'scare',
name: 'Scare',
html: 'emglkenplay.html',
load: (arg, buf, opts) => common_emglken_load('scare', buf, opts),
//get_signature: () => window.engine.get_signature(),
},
],
},
{
id: 'glulx',
longname: 'Glulx Game File',
name: 'Glulx',
extensions: ['ulx', 'gblorb', 'glb'],
docicon: 'docicon-glulx.ico',
identify: buf => (buf[0] == 0x47 && buf[1] == 0x6C && buf[2] == 0x75 && buf[3] == 0x6C),
engines: [
{
id: 'quixe',
name: 'Quixe',
html: 'play.html',
load: (arg, buf, opts) => {
opts.vm = Quixe;
/* Further Glulx options are set up by gi_load.js. */
/* Convert to a generic Array of byte values. */
var arr = new Array(buf.length);
for (var ix=0; ix<buf.length; ix++)
arr[ix] = buf[ix];
return arr;
},
get_signature: () => Quixe.get_signature(),
},
{
id: 'git',
name: 'Git',
html: 'emglkenplay.html',
load: (arg, buf, opts) => common_emglken_load('git', buf, opts),
//get_signature: () => window.engine.get_signature(),
},
{
id: 'glulxe',
name: 'Glulxe',
html: 'emglkenplay.html',
load: (arg, buf, opts) => common_emglken_load('glulxe', buf, opts),
//get_signature: () => window.engine.get_signature(),
},
/*{
id: 'glulxe-profiler',
name: 'Glulxe (Profiler)',
html: 'emglkenplay.html',
load: (arg, buf, opts) => {
var engine = new ( require('./emglken/glulxe-profiler.js') )();
opts.vm = window.engine = engine;
opts.Glk = window.Glk;
opts.GiDispa = window.GiDispa;
opts.blorb_gamechunk_type = 'GLUL';
return Uint8Array.from(buf);
},
get_signature: () => window.engine.get_signature(),
},*/
],
},
{
id: 'zcode',
longname: 'Z-Code Game File',
name: 'Z-Code',
extensions: ['z3', 'z4', 'z5', 'z8', 'zblorb', 'zlb'],
docicon: 'docicon-zcode.ico',
identify: buf => (buf[0] >= 3 && buf[0] <= 8),
engines: [
{
id: 'zvm',
name: 'ZVM',
html: 'zplay.html',
load: (arg, buf, opts) => {
/* TODO: switch to module format. See comment on common_emglken_load() above. */
opts.blorb_gamechunk_type = 'ZCOD';
opts.vm = window.engine = new window.ZVM();
opts.vm.init = opts.vm.prepare;
opts.Glk = window.Glk;
opts.Dialog = window.Dialog;
return Uint8Array.from(buf);
},
/* ### this doesn't work, because the engine does not
set its signature until some time after load_run is
called. */
get_signature: () => window.engine.get_signature(),
},
],
},
{
id: 'hugo',
longname: 'Hugo Game File',
name: 'Hugo',
extensions: [ 'hex' ],
docicon: 'docicon-hugo.ico',
engines: [
{
id: 'hugo',
name: 'Hugo',
html: 'emglkenplay.html',
load: (arg, buf, opts) => common_emglken_load('hugo', buf, opts),
//get_signature: () => window.engine.get_signature(),
},
],
},
{
id: 'ink-json',
longname: 'Ink JSON File',
name: 'Ink',
extensions: [ 'json' ],
docicon: 'docicon-json.ico',
identify: buf => {
/* Ink is a text (JSON) format, which is hard to check. We skip
whitespace and non-ASCII characters and look for '{"ink'. */
var checkascii = [ 0x7B, 0x22, 0x69, 0x6E, 0x6B ];
var pos = 0;
for (var ix=0; ix<buf.length; ix++) {
var ch = buf[ix];
if (!(ch > 32 && ch < 127))
continue;
if (ch != checkascii[pos])
break;
pos++;
if (pos >= checkascii.length)
return true;
}
},
engines: [
{
id: 'inkjs',
name: 'InkJS',
html: 'inkplay.html',
load: (arg, buf, opts) => {
/* Does not use gi_load.js, so no additional options needed */
/* Pass the Buffer directly to the load_run function. */
return buf;
},
get_signature: () => GiLoad.get_game_signature(),
},
],
},
{
id: 'tads',
longname: 'TADS Game File',
name: 'TADS',
extensions: [ 'gam', 't3' ],
docicon: 'docicon-tads.ico',
engines: [
{
id: 'tads',
name: 'TADS',
html: 'emglkenplay.html',
load: (arg, buf, opts) => common_emglken_load('tads', buf, opts),
//get_signature: () => window.engine.get_signature(),
},
],
},
];
/* Create the maps. */
const formatmap = {};
const enginemap = {};
for (let i = 0; i < formatlist.length; i++) {
var entry = formatlist[i];
formatmap[entry.id] = entry;
if (entry.engines) {
for (let j = 0; j < entry.engines.length; j++) {
var engine = entry.engines[j];
engine.format = entry.id;
enginemap[engine.id] = engine;
}
}
}
exports.formatlist = formatlist;
exports.formatmap = formatmap;
exports.enginemap = enginemap;