@pkg-nec/emulatorjs 4.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.gitattributes +1 -0
  2. package/CHANGES.md +528 -0
  3. package/CODE_OF_CONDUCT.md +128 -0
  4. package/CONTRIBUTING.md +31 -0
  5. package/LICENSE +674 -0
  6. package/README.md +284 -0
  7. package/data/compression/README.md +11 -0
  8. package/data/compression/extract7z.js +1 -0
  9. package/data/compression/extractzip.js +1 -0
  10. package/data/compression/libunrar.js +519 -0
  11. package/data/compression/libunrar.wasm +0 -0
  12. package/data/cores/README.md +22 -0
  13. package/data/cores/package.json +65 -0
  14. package/data/emulator.css +1614 -0
  15. package/data/loader.js +170 -0
  16. package/data/localization/README.md +63 -0
  17. package/data/localization/af-FR.json +339 -0
  18. package/data/localization/ar-AR.json +301 -0
  19. package/data/localization/ben-BEN.json +301 -0
  20. package/data/localization/de-GER.json +301 -0
  21. package/data/localization/el-GR.json +301 -0
  22. package/data/localization/en-US.json +339 -0
  23. package/data/localization/es-ES.json +310 -0
  24. package/data/localization/fa-AF.json +310 -0
  25. package/data/localization/hi-HI.json +301 -0
  26. package/data/localization/it-IT.json +302 -0
  27. package/data/localization/ja-JA.json +301 -0
  28. package/data/localization/jv-JV.json +301 -0
  29. package/data/localization/ko-KO.json +336 -0
  30. package/data/localization/pt-BR.json +361 -0
  31. package/data/localization/retroarch.json +617 -0
  32. package/data/localization/ro-RO.json +310 -0
  33. package/data/localization/ru-RU.json +301 -0
  34. package/data/localization/tr-TR.json +310 -0
  35. package/data/localization/vi-VN.json +302 -0
  36. package/data/localization/zh-CN.json +351 -0
  37. package/data/src/GameManager.js +464 -0
  38. package/data/src/compression.js +142 -0
  39. package/data/src/emulator.js +6158 -0
  40. package/data/src/gamepad.js +167 -0
  41. package/data/src/nipplejs.js +1 -0
  42. package/data/src/shaders.js +236 -0
  43. package/data/src/socket.io.min.js +7 -0
  44. package/data/src/storage.js +125 -0
  45. package/data/version.json +5 -0
  46. package/docs/Logo-light.png +0 -0
  47. package/docs/Logo.png +0 -0
  48. package/docs/Logo.svg +4487 -0
  49. package/docs/contributors.json +46 -0
  50. package/docs/contributors.md +125 -0
  51. package/docs/favicon.ico +0 -0
  52. package/index.html +269 -0
  53. package/package.json +35 -0
@@ -0,0 +1,464 @@
1
+ class EJS_GameManager {
2
+ constructor(Module, EJS) {
3
+ this.EJS = EJS;
4
+ this.Module = Module;
5
+ this.FS = this.Module.FS;
6
+ this.functions = {
7
+ restart: this.Module.cwrap("system_restart", "", []),
8
+ saveStateInfo: this.Module.cwrap("save_state_info", "string", []),
9
+ loadState: this.Module.cwrap("load_state", "number", ["string", "number"]),
10
+ screenshot: this.Module.cwrap("cmd_take_screenshot", "", []),
11
+ simulateInput: this.Module.cwrap("simulate_input", "null", ["number", "number", "number"]),
12
+ toggleMainLoop: this.Module.cwrap("toggleMainLoop", "null", ["number"]),
13
+ getCoreOptions: this.Module.cwrap("get_core_options", "string", []),
14
+ setVariable: this.Module.cwrap("ejs_set_variable", "null", ["string", "string"]),
15
+ setCheat: this.Module.cwrap("set_cheat", "null", ["number", "number", "string"]),
16
+ resetCheat: this.Module.cwrap("reset_cheat", "null", []),
17
+ toggleShader: this.Module.cwrap("shader_enable", "null", ["number"]),
18
+ getDiskCount: this.Module.cwrap("get_disk_count", "number", []),
19
+ getCurrentDisk: this.Module.cwrap("get_current_disk", "number", []),
20
+ setCurrentDisk: this.Module.cwrap("set_current_disk", "null", ["number"]),
21
+ getSaveFilePath: this.Module.cwrap("save_file_path", "string", []),
22
+ saveSaveFiles: this.Module.cwrap("cmd_savefiles", "", []),
23
+ supportsStates: this.Module.cwrap("supports_states", "number", []),
24
+ loadSaveFiles: this.Module.cwrap("refresh_save_files", "null", []),
25
+ toggleFastForward: this.Module.cwrap("toggle_fastforward", "null", ["number"]),
26
+ setFastForwardRatio: this.Module.cwrap("set_ff_ratio", "null", ["number"]),
27
+ toggleRewind: this.Module.cwrap("toggle_rewind", "null", ["number"]),
28
+ setRewindGranularity: this.Module.cwrap("set_rewind_granularity", "null", ["number"]),
29
+ toggleSlowMotion: this.Module.cwrap("toggle_slow_motion", "null", ["number"]),
30
+ setSlowMotionRatio: this.Module.cwrap("set_sm_ratio", "null", ["number"]),
31
+ getFrameNum: this.Module.cwrap("get_current_frame_count", "number", [""]),
32
+ setVSync: this.Module.cwrap("set_vsync", "null", ["number"]),
33
+ setVideoRoation: this.Module.cwrap("set_video_rotation", "null", ["number"]),
34
+ getVideoDimensions: this.Module.cwrap("get_video_dimensions", "number", ["string"]),
35
+ setKeyboardEnabled: this.Module.cwrap("ejs_set_keyboard_enabled", "null", ["number"])
36
+ }
37
+
38
+ this.writeFile("/home/web_user/.config/retroarch/retroarch.cfg", this.getRetroArchCfg());
39
+
40
+ this.writeConfigFile();
41
+ this.initShaders();
42
+ this.setupPreLoadSettings();
43
+
44
+ this.EJS.on("exit", () => {
45
+ if (!this.EJS.failedToStart) {
46
+ this.saveSaveFiles();
47
+ this.functions.restart();
48
+ this.saveSaveFiles();
49
+ }
50
+ this.toggleMainLoop(0);
51
+ this.FS.unmount("/data/saves");
52
+ setTimeout(() => {
53
+ try {
54
+ this.Module.abort();
55
+ } catch(e) {
56
+ console.warn(e);
57
+ };
58
+ }, 1000);
59
+ })
60
+ }
61
+ setupPreLoadSettings() {
62
+ this.Module.callbacks.setupCoreSettingFile = (filePath) => {
63
+ if (this.EJS.debug) console.log("Setting up core settings with path:", filePath);
64
+ this.writeFile(filePath, this.EJS.getCoreSettings());
65
+ }
66
+ }
67
+ mountFileSystems() {
68
+ return new Promise(async resolve => {
69
+ this.mkdir("/data");
70
+ this.mkdir("/data/saves");
71
+ this.FS.mount(this.FS.filesystems.IDBFS, { autoPersist: true }, "/data/saves");
72
+ this.FS.syncfs(true, resolve);
73
+ });
74
+ }
75
+ writeConfigFile() {
76
+ if (!this.EJS.defaultCoreOpts.file || !this.EJS.defaultCoreOpts.settings) {
77
+ return;
78
+ }
79
+ let output = "";
80
+ for (const k in this.EJS.defaultCoreOpts.settings) {
81
+ output += k + ' = "' + this.EJS.defaultCoreOpts.settings[k] + '"\n';
82
+ }
83
+
84
+ this.writeFile("/home/web_user/retroarch/userdata/config/" + this.EJS.defaultCoreOpts.file, output);
85
+ }
86
+ loadExternalFiles() {
87
+ return new Promise(async (resolve, reject) => {
88
+ if (this.EJS.config.externalFiles && this.EJS.config.externalFiles.constructor.name === "Object") {
89
+ for (const key in this.EJS.config.externalFiles) {
90
+ await new Promise(done => {
91
+ this.EJS.downloadFile(this.EJS.config.externalFiles[key], null, true, { responseType: "arraybuffer", method: "GET" }).then(async (res) => {
92
+ if (res === -1) {
93
+ if (this.EJS.debug) console.warn("Failed to fetch file from '" + this.EJS.config.externalFiles[key] + "'. Make sure the file exists.");
94
+ return done();
95
+ }
96
+ let path = key;
97
+ if (key.trim().endsWith("/")) {
98
+ const invalidCharacters = /[#<$+%>!`&*'|{}/\\?"=@:^\r\n]/ig;
99
+ let name = this.EJS.config.externalFiles[key].split("/").pop().split("#")[0].split("?")[0].replace(invalidCharacters, "").trim();
100
+ if (!name) return done();
101
+ const files = await this.EJS.checkCompression(new Uint8Array(res.data), this.EJS.localization("Decompress Game Assets"));
102
+ if (files["!!notCompressedData"]) {
103
+ path += name;
104
+ } else {
105
+ for (const k in files) {
106
+ this.writeFile(path + k, files[k]);
107
+ }
108
+ return done();
109
+ }
110
+ }
111
+ try {
112
+ this.writeFile(path, res.data);
113
+ } catch(e) {
114
+ if (this.EJS.debug) console.warn("Failed to write file to '" + path + "'. Make sure there are no conflicting files.");
115
+ }
116
+ done();
117
+ });
118
+ })
119
+ }
120
+ }
121
+ resolve();
122
+ });
123
+ }
124
+ writeFile(path, data) {
125
+ const parts = path.split("/");
126
+ let current = "/";
127
+ for (let i = 0; i < parts.length - 1; i++) {
128
+ if (!parts[i].trim()) continue;
129
+ current += parts[i] + "/";
130
+ this.mkdir(current);
131
+ }
132
+ this.FS.writeFile(path, data);
133
+ }
134
+ mkdir(path) {
135
+ try {
136
+ this.FS.mkdir(path);
137
+ } catch(e) {}
138
+ }
139
+ getRetroArchCfg() {
140
+ let cfg = "autosave_interval = 60\n" +
141
+ "screenshot_directory = \"/\"\n" +
142
+ "block_sram_overwrite = false\n" +
143
+ "video_gpu_screenshot = false\n" +
144
+ "audio_latency = 64\n" +
145
+ "video_top_portrait_viewport = true\n" +
146
+ "video_vsync = true\n" +
147
+ "video_smooth = false\n" +
148
+ "fastforward_ratio = 3.0\n" +
149
+ "slowmotion_ratio = 3.0\n" +
150
+ (this.EJS.rewindEnabled ? "rewind_enable = true\n" : "") +
151
+ (this.EJS.rewindEnabled ? "rewind_granularity = 6\n" : "") +
152
+ "savefile_directory = \"/data/saves\"\n";
153
+
154
+ if (this.EJS.retroarchOpts && Array.isArray(this.EJS.retroarchOpts)) {
155
+ this.EJS.retroarchOpts.forEach(option => {
156
+ let selected = this.EJS.preGetSetting(option.name);
157
+ console.log(selected);
158
+ if (!selected) {
159
+ selected = option.default;
160
+ }
161
+ const value = option.isString === false ? selected : '"' + selected + '"';
162
+ cfg += option.name + " = " + value + "\n"
163
+ })
164
+ }
165
+ return cfg;
166
+ }
167
+ initShaders() {
168
+ if (!this.EJS.config.shaders) return;
169
+ this.mkdir("/shader");
170
+ for (const shaderFileName in this.EJS.config.shaders) {
171
+ const shader = this.EJS.config.shaders[shaderFileName];
172
+ if (typeof shader === "string") {
173
+ this.FS.writeFile(`/shader/${shaderFileName}`, shader);
174
+ }
175
+ }
176
+ }
177
+ clearEJSResetTimer() {
178
+ if (this.EJS.resetTimeout) {
179
+ clearTimeout(this.EJS.resetTimeout);
180
+ delete this.EJS.resetTimeout;
181
+ }
182
+ }
183
+ restart() {
184
+ this.clearEJSResetTimer();
185
+ this.functions.restart();
186
+ }
187
+ getState() {
188
+ const state = this.functions.saveStateInfo().split("|");
189
+ if (state[2] !== "1") {
190
+ console.error(state[0]);
191
+ throw new Error(state[0]);
192
+ }
193
+ const size = parseInt(state[0]);
194
+ const dataStart = parseInt(state[1]);
195
+ const data = this.Module.HEAPU8.subarray(dataStart, dataStart + size);
196
+ return new Uint8Array(data);
197
+ }
198
+ loadState(state) {
199
+ try {
200
+ this.FS.unlink("game.state");
201
+ } catch(e) {}
202
+ this.FS.writeFile("/game.state", state);
203
+ this.clearEJSResetTimer();
204
+ this.functions.loadState("game.state", 0);
205
+ setTimeout(() => {
206
+ try {
207
+ this.FS.unlink("game.state");
208
+ } catch(e) {}
209
+ }, 5000)
210
+ }
211
+ screenshot() {
212
+ try {
213
+ this.FS.unlink("screenshot.png");
214
+ } catch(e) {}
215
+ this.functions.screenshot();
216
+ return new Promise(async resolve => {
217
+ while (1) {
218
+ try {
219
+ this.FS.stat("/screenshot.png");
220
+ return resolve(this.FS.readFile("/screenshot.png"));
221
+ } catch(e) {}
222
+ await new Promise(res => setTimeout(res, 50));
223
+ }
224
+ })
225
+ }
226
+ quickSave(slot) {
227
+ if (!slot) slot = 1;
228
+ let name = slot + "-quick.state";
229
+ try {
230
+ this.FS.unlink(name);
231
+ } catch(e) {}
232
+ try {
233
+ let data = this.getState();
234
+ this.FS.writeFile("/" + name, data);
235
+ } catch(e) {
236
+ return false;
237
+ }
238
+ return true;
239
+ }
240
+ quickLoad(slot) {
241
+ if (!slot) slot = 1;
242
+ (async () => {
243
+ let name = slot + "-quick.state";
244
+ this.clearEJSResetTimer();
245
+ this.functions.loadState(name, 0);
246
+ })();
247
+ }
248
+ simulateInput(player, index, value) {
249
+ if (this.EJS.isNetplay) {
250
+ this.EJS.netplay.simulateInput(player, index, value);
251
+ return;
252
+ }
253
+ if ([24, 25, 26, 27, 28, 29].includes(index)) {
254
+ if (index === 24 && value === 1) {
255
+ const slot = this.EJS.settings["save-state-slot"] ? this.EJS.settings["save-state-slot"] : "1";
256
+ if (this.quickSave(slot)) {
257
+ this.EJS.displayMessage(this.EJS.localization("SAVED STATE TO SLOT") + " " + slot);
258
+ } else {
259
+ this.EJS.displayMessage(this.EJS.localization("FAILED TO SAVE STATE"));
260
+ }
261
+ }
262
+ if (index === 25 && value === 1) {
263
+ const slot = this.EJS.settings["save-state-slot"] ? this.EJS.settings["save-state-slot"] : "1";
264
+ this.quickLoad(slot);
265
+ this.EJS.displayMessage(this.EJS.localization("LOADED STATE FROM SLOT") + " " + slot);
266
+ }
267
+ if (index === 26 && value === 1) {
268
+ let newSlot;
269
+ try {
270
+ newSlot = parseFloat(this.EJS.settings["save-state-slot"] ? this.EJS.settings["save-state-slot"] : "1") + 1;
271
+ } catch(e) {
272
+ newSlot = 1;
273
+ }
274
+ if (newSlot > 9) newSlot = 1;
275
+ this.EJS.displayMessage(this.EJS.localization("SET SAVE STATE SLOT TO") + " " + newSlot);
276
+ this.EJS.changeSettingOption("save-state-slot", newSlot.toString());
277
+ }
278
+ if (index === 27) {
279
+ this.functions.toggleFastForward(this.EJS.isFastForward ? !value : value);
280
+ }
281
+ if (index === 29) {
282
+ this.functions.toggleSlowMotion(this.EJS.isSlowMotion ? !value : value);
283
+ }
284
+ if (index === 28) {
285
+ if (this.EJS.rewindEnabled) {
286
+ this.functions.toggleRewind(value);
287
+ }
288
+ }
289
+ return;
290
+ }
291
+ this.functions.simulateInput(player, index, value);
292
+ }
293
+ getFileNames() {
294
+ if (this.EJS.getCore() === "picodrive") {
295
+ return ["bin", "gen", "smd", "md", "32x", "cue", "iso", "sms", "68k", "chd"];
296
+ } else {
297
+ return ["toc", "ccd", "exe", "pbp", "chd", "img", "bin", "iso"];
298
+ }
299
+ }
300
+ createCueFile(fileNames) {
301
+ try {
302
+ if (fileNames.length > 1) {
303
+ fileNames = fileNames.filter((item) => {
304
+ return this.getFileNames().includes(item.split(".").pop().toLowerCase());
305
+ })
306
+ fileNames = fileNames.sort((a, b) => {
307
+ if (isNaN(a.charAt()) || isNaN(b.charAt())) throw new Error("Incorrect file name format");
308
+ return (parseInt(a.charAt()) > parseInt(b.charAt())) ? 1 : -1;
309
+ })
310
+ }
311
+ } catch(e) {
312
+ if (fileNames.length > 1) {
313
+ console.warn("Could not auto-create cue file(s).");
314
+ return null;
315
+ }
316
+ }
317
+ for (let i = 0; i < fileNames.length; i++) {
318
+ if (fileNames[i].split(".").pop().toLowerCase() === "ccd") {
319
+ console.warn("Did not auto-create cue file(s). Found a ccd.");
320
+ return null;
321
+ }
322
+ }
323
+ if (fileNames.length === 0) {
324
+ console.warn("Could not auto-create cue file(s).");
325
+ return null;
326
+ }
327
+ let baseFileName = fileNames[0].split("/").pop();
328
+ if (baseFileName.includes(".")) {
329
+ baseFileName = baseFileName.substring(0, baseFileName.length - baseFileName.split(".").pop().length - 1);
330
+ }
331
+ for (let i = 0; i < fileNames.length; i++) {
332
+ const contents = " FILE \"" + fileNames[i] + "\" BINARY\n TRACK 01 MODE1/2352\n INDEX 01 00:00:00";
333
+ this.FS.writeFile("/" + baseFileName + "-" + i + ".cue", contents);
334
+ }
335
+ if (fileNames.length > 1) {
336
+ let contents = "";
337
+ for (let i = 0; i < fileNames.length; i++) {
338
+ contents += "/" + baseFileName + "-" + i + ".cue\n";
339
+ }
340
+ this.FS.writeFile("/" + baseFileName + ".m3u", contents);
341
+ }
342
+ return (fileNames.length === 1) ? baseFileName + "-0.cue" : baseFileName + ".m3u";
343
+ }
344
+ loadPpssppAssets() {
345
+ return new Promise(resolve => {
346
+ this.EJS.downloadFile("cores/ppsspp-assets.zip", null, false, { responseType: "arraybuffer", method: "GET" }).then((res) => {
347
+ this.EJS.checkCompression(new Uint8Array(res.data), this.EJS.localization("Decompress Game Data")).then((pspassets) => {
348
+ if (pspassets === -1) {
349
+ this.EJS.textElem.innerText = this.localization("Network Error");
350
+ this.EJS.textElem.style.color = "red";
351
+ return;
352
+ }
353
+ this.mkdir("/PPSSPP");
354
+
355
+ for (const file in pspassets) {
356
+ const data = pspassets[file];
357
+ const path = "/PPSSPP/" + file;
358
+ const paths = path.split("/");
359
+ let cp = "";
360
+ for (let i = 0; i < paths.length - 1; i++) {
361
+ if (paths[i] === "") continue;
362
+ cp += "/" + paths[i];
363
+ if (!this.FS.analyzePath(cp).exists) {
364
+ this.FS.mkdir(cp);
365
+ }
366
+ }
367
+ if (!path.endsWith("/")) {
368
+ this.FS.writeFile(path, data);
369
+ }
370
+ }
371
+ resolve();
372
+ })
373
+ });
374
+ })
375
+ }
376
+ setVSync(enabled) {
377
+ this.functions.setVSync(enabled);
378
+ }
379
+ toggleMainLoop(playing) {
380
+ this.functions.toggleMainLoop(playing);
381
+ }
382
+ getCoreOptions() {
383
+ return this.functions.getCoreOptions();
384
+ }
385
+ setVariable(option, value) {
386
+ this.functions.setVariable(option, value);
387
+ }
388
+ setCheat(index, enabled, code) {
389
+ this.functions.setCheat(index, enabled, code);
390
+ }
391
+ resetCheat() {
392
+ this.functions.resetCheat();
393
+ }
394
+ toggleShader(active) {
395
+ this.functions.toggleShader(active);
396
+ }
397
+ getDiskCount() {
398
+ return this.functions.getDiskCount();
399
+ }
400
+ getCurrentDisk() {
401
+ return this.functions.getCurrentDisk();
402
+ }
403
+ setCurrentDisk(disk) {
404
+ this.functions.setCurrentDisk(disk);
405
+ }
406
+ getSaveFilePath() {
407
+ return this.functions.getSaveFilePath();
408
+ }
409
+ saveSaveFiles() {
410
+ this.functions.saveSaveFiles();
411
+ this.EJS.callEvent("saveSaveFiles", this.getSaveFile(false));
412
+ //this.FS.syncfs(false, () => {});
413
+ }
414
+ supportsStates() {
415
+ return !!this.functions.supportsStates();
416
+ }
417
+ getSaveFile(save) {
418
+ if (save !== false) {
419
+ this.saveSaveFiles();
420
+ }
421
+ const exists = this.FS.analyzePath(this.getSaveFilePath()).exists;
422
+ return (exists ? this.FS.readFile(this.getSaveFilePath()) : null);
423
+ }
424
+ loadSaveFiles() {
425
+ this.clearEJSResetTimer();
426
+ this.functions.loadSaveFiles();
427
+ }
428
+ setFastForwardRatio(ratio) {
429
+ this.functions.setFastForwardRatio(ratio);
430
+ }
431
+ toggleFastForward(active) {
432
+ this.functions.toggleFastForward(active);
433
+ }
434
+ setSlowMotionRatio(ratio) {
435
+ this.functions.setSlowMotionRatio(ratio);
436
+ }
437
+ toggleSlowMotion(active) {
438
+ this.functions.toggleSlowMotion(active);
439
+ }
440
+ setRewindGranularity(value) {
441
+ this.functions.setRewindGranularity(value);
442
+ }
443
+ getFrameNum() {
444
+ return this.functions.getFrameNum();
445
+ }
446
+ setVideoRotation(rotation) {
447
+ this.functions.setVideoRoation(rotation);
448
+ }
449
+ getVideoDimensions(type) {
450
+ try {
451
+ return this.functions.getVideoDimensions(type);
452
+ } catch(e) {
453
+ console.warn(e);
454
+ }
455
+ }
456
+ setKeyboardEnabled(enabled) {
457
+ this.functions.setKeyboardEnabled(enabled === true ? 1 : 0);
458
+ }
459
+ setAltKeyEnabled(enabled) {
460
+ this.functions.setKeyboardEnabled(enabled === true ? 3 : 2);
461
+ }
462
+ }
463
+
464
+ window.EJS_GameManager = EJS_GameManager;
@@ -0,0 +1,142 @@
1
+ class EJS_COMPRESSION {
2
+ constructor(EJS) {
3
+ this.EJS = EJS;
4
+ }
5
+ isCompressed(data) { //https://www.garykessler.net/library/file_sigs.html
6
+ //todo. Use hex instead of numbers
7
+ if ((data[0] === 80 && data[1] === 75) && ((data[2] === 3 && data[3] === 4) || (data[2] === 5 && data[3] === 6) || (data[2] === 7 && data[3] === 8))) {
8
+ return "zip";
9
+ } else if (data[0] === 55 && data[1] === 122 && data[2] === 188 && data[3] === 175 && data[4] === 39 && data[5] === 28) {
10
+ return "7z";
11
+ } else if ((data[0] === 82 && data[1] === 97 && data[2] === 114 && data[3] === 33 && data[4] === 26 && data[5] === 7) && ((data[6] === 0) || (data[6] === 1 && data[7] == 0))) {
12
+ return "rar";
13
+ }
14
+ return null;
15
+ }
16
+ decompress(data, updateMsg, fileCbFunc) {
17
+ const compressed = this.isCompressed(data.slice(0, 10));
18
+ if (compressed === null) {
19
+ if (typeof fileCbFunc === "function") {
20
+ fileCbFunc("!!notCompressedData", data);
21
+ }
22
+ return new Promise(resolve => resolve({ "!!notCompressedData": data }));
23
+ }
24
+ return this.decompressFile(compressed, data, updateMsg, fileCbFunc);
25
+ }
26
+ getWorkerFile(method) {
27
+ return new Promise(async (resolve, reject) => {
28
+ let path, obj;
29
+ if (method === "7z") {
30
+ path = "compression/extract7z.js";
31
+ obj = "sevenZip";
32
+ } else if (method === "zip") {
33
+ path = "compression/extractzip.js";
34
+ obj = "zip";
35
+ } else if (method === "rar") {
36
+ path = "compression/libunrar.js";
37
+ obj = "rar";
38
+ }
39
+ const res = await this.EJS.downloadFile(path, null, false, { responseType: "text", method: "GET" });
40
+ if (res === -1) {
41
+ this.EJS.startGameError(this.EJS.localization("Network Error"));
42
+ return;
43
+ }
44
+ if (method === "rar") {
45
+ const res2 = await this.EJS.downloadFile("compression/libunrar.wasm", null, false, { responseType: "arraybuffer", method: "GET" });
46
+ if (res2 === -1) {
47
+ this.EJS.startGameError(this.EJS.localization("Network Error"));
48
+ return;
49
+ }
50
+ const path = URL.createObjectURL(new Blob([res2.data], { type: "application/wasm" }));
51
+ let script = `
52
+ let dataToPass = [];
53
+ Module = {
54
+ monitorRunDependencies: function(left) {
55
+ if (left == 0) {
56
+ setTimeout(function() {
57
+ unrar(dataToPass, null);
58
+ }, 100);
59
+ }
60
+ },
61
+ onRuntimeInitialized: function() {},
62
+ locateFile: function(file) {
63
+ console.log("locateFile");
64
+ return "` + path + `";
65
+ }
66
+ };
67
+ ` + res.data + `
68
+ let unrar = function(data, password) {
69
+ let cb = function(fileName, fileSize, progress) {
70
+ postMessage({ "t": 4, "current": progress, "total": fileSize, "name": fileName });
71
+ };
72
+ let rarContent = readRARContent(data.map(function(d) {
73
+ return {
74
+ name: d.name,
75
+ content: new Uint8Array(d.content)
76
+ }
77
+ }), password, cb)
78
+ let rec = function(entry) {
79
+ if (!entry) return;
80
+ if (entry.type === "file") {
81
+ postMessage({ "t": 2, "file": entry.fullFileName, "size": entry.fileSize, "data": entry.fileContent });
82
+ } else if (entry.type === "dir") {
83
+ Object.keys(entry.ls).forEach(function(k) {
84
+ rec(entry.ls[k]);
85
+ });
86
+ } else {
87
+ throw "Unknown type";
88
+ }
89
+ }
90
+ rec(rarContent);
91
+ postMessage({ "t": 1 });
92
+ return rarContent;
93
+ };
94
+ onmessage = function(data) {
95
+ dataToPass.push({ name: "test.rar", content: data.data });
96
+ };
97
+ `;
98
+ const blob = new Blob([script], {
99
+ type: "application/javascript"
100
+ })
101
+ resolve(blob);
102
+ } else {
103
+ const blob = new Blob([res.data], {
104
+ type: "application/javascript"
105
+ })
106
+ resolve(blob);
107
+ }
108
+ })
109
+ }
110
+ decompressFile(method, data, updateMsg, fileCbFunc) {
111
+ return new Promise(async callback => {
112
+ const file = await this.getWorkerFile(method);
113
+ const worker = new Worker(URL.createObjectURL(file));
114
+ const files = {};
115
+ worker.onmessage = (data) => {
116
+ if (!data.data) return;
117
+ //data.data.t/ 4=progress, 2 is file, 1 is zip done
118
+ if (data.data.t === 4) {
119
+ const pg = data.data;
120
+ const num = Math.floor(pg.current / pg.total * 100);
121
+ if (isNaN(num)) return;
122
+ const progress = " " + num.toString() + "%";
123
+ updateMsg(progress, true);
124
+ }
125
+ if (data.data.t === 2) {
126
+ if (typeof fileCbFunc === "function") {
127
+ fileCbFunc(data.data.file, data.data.data);
128
+ files[data.data.file] = true;
129
+ } else {
130
+ files[data.data.file] = data.data.data;
131
+ }
132
+ }
133
+ if (data.data.t === 1) {
134
+ callback(files);
135
+ }
136
+ }
137
+ worker.postMessage(data);
138
+ });
139
+ }
140
+ }
141
+
142
+ window.EJS_COMPRESSION = EJS_COMPRESSION;