js-dos 0.80.25

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 (119) hide show
  1. package/.eslintrc.json +43 -0
  2. package/.github/workflows/build.yml +43 -0
  3. package/.gitmodules +4 -0
  4. package/README.md +24 -0
  5. package/index.html +106 -0
  6. package/package.json +48 -0
  7. package/postcss.config.cjs +8 -0
  8. package/src/app.css +14 -0
  9. package/src/base.css +10 -0
  10. package/src/components/checkbox.tsx +41 -0
  11. package/src/components/close-button.tsx +17 -0
  12. package/src/components/components.css +28 -0
  13. package/src/components/dos-option-checkbox.tsx +72 -0
  14. package/src/components/dos-option-select.tsx +94 -0
  15. package/src/components/dos-option-slider.tsx +35 -0
  16. package/src/components/error.tsx +16 -0
  17. package/src/components/loading.tsx +26 -0
  18. package/src/components/lock.tsx +10 -0
  19. package/src/components/select.tsx +33 -0
  20. package/src/components/slider.tsx +69 -0
  21. package/src/download-file.ts +18 -0
  22. package/src/frame/account-frame.tsx +155 -0
  23. package/src/frame/editor/defaults.ts +344 -0
  24. package/src/frame/editor/editor-conf-frame.tsx +42 -0
  25. package/src/frame/editor/editor-frame.css +55 -0
  26. package/src/frame/editor/editor-fs-frame.tsx +315 -0
  27. package/src/frame/frame.css +127 -0
  28. package/src/frame/frame.tsx +34 -0
  29. package/src/frame/host-cache-frame.tsx +124 -0
  30. package/src/frame/image-rendering-frame.tsx +8 -0
  31. package/src/frame/network-frame.tsx +157 -0
  32. package/src/frame/quick-save-frame.tsx +95 -0
  33. package/src/frame/settings-frame.tsx +14 -0
  34. package/src/frame/stats-frame.tsx +82 -0
  35. package/src/host/bundle-storage.ts +91 -0
  36. package/src/host/fullscreen.ts +36 -0
  37. package/src/host/lcache.ts +203 -0
  38. package/src/host/lstorage.ts +156 -0
  39. package/src/i18n.ts +167 -0
  40. package/src/index.css +7 -0
  41. package/src/load.ts +139 -0
  42. package/src/login/login.css +9 -0
  43. package/src/login/login.tsx +77 -0
  44. package/src/main.tsx +195 -0
  45. package/src/non-serializable-store.ts +35 -0
  46. package/src/public/types.ts +39 -0
  47. package/src/sidebar/account-button.tsx +32 -0
  48. package/src/sidebar/cloud-save-button.tsx +89 -0
  49. package/src/sidebar/diskette-icon.tsx +14 -0
  50. package/src/sidebar/fullscreen-button.tsx +44 -0
  51. package/src/sidebar/network-button.tsx +39 -0
  52. package/src/sidebar/sidebar-button.tsx +243 -0
  53. package/src/sidebar/sidebar.css +47 -0
  54. package/src/sidebar/sidebar.tsx +30 -0
  55. package/src/sockdrive/LICENSE +674 -0
  56. package/src/sockdrive/README.md +22 -0
  57. package/src/sockdrive/SDL2/LRUCache11.hpp +240 -0
  58. package/src/sockdrive/SDL2/sockdrive.cpp +207 -0
  59. package/src/sockdrive/include/sockdrive.h +23 -0
  60. package/src/sockdrive/js/.eslintrc.json +43 -0
  61. package/src/sockdrive/js/README.md +70 -0
  62. package/src/sockdrive/js/package.json +29 -0
  63. package/src/sockdrive/js/src/fatfs/chains.js +318 -0
  64. package/src/sockdrive/js/src/fatfs/dir.js +385 -0
  65. package/src/sockdrive/js/src/fatfs/helpers.js +210 -0
  66. package/src/sockdrive/js/src/fatfs/index.js +606 -0
  67. package/src/sockdrive/js/src/fatfs/structs.js +255 -0
  68. package/src/sockdrive/js/src/fatfs/vol.js +166 -0
  69. package/src/sockdrive/js/src/sockdrive/cache.ts +133 -0
  70. package/src/sockdrive/js/src/sockdrive/drive.ts +364 -0
  71. package/src/sockdrive/js/src/sockdrive/lru.ts +221 -0
  72. package/src/sockdrive/js/src/sockdrive/types.ts +10 -0
  73. package/src/sockdrive/js/src/sockdrive-fat.d.ts +64 -0
  74. package/src/sockdrive/js/src/sockdrive-fat.ts +228 -0
  75. package/src/sockdrive/js/src/sockdrive-native.ts +98 -0
  76. package/src/sockdrive/js/src/test/test.ts +186 -0
  77. package/src/sockdrive/js/tsconfig.json +11 -0
  78. package/src/sockdrive/js/webpack.config.js +68 -0
  79. package/src/sockdrive/js/yarn.lock +1808 -0
  80. package/src/sockdrive/lz4/lz4.c +2773 -0
  81. package/src/sockdrive/lz4/lz4.h +862 -0
  82. package/src/store/auth.ts +104 -0
  83. package/src/store/dos.ts +284 -0
  84. package/src/store/editor.ts +59 -0
  85. package/src/store/storage.ts +30 -0
  86. package/src/store/ui.ts +183 -0
  87. package/src/store.ts +27 -0
  88. package/src/ui.tsx +103 -0
  89. package/src/v8/changes.ts +56 -0
  90. package/src/v8/config.ts +18 -0
  91. package/src/v8/subscriptions.ts +62 -0
  92. package/src/vite-env.d.ts +2 -0
  93. package/src/window/dos/controls/keyboard.ts +32 -0
  94. package/src/window/dos/controls/keys.ts +331 -0
  95. package/src/window/dos/controls/mouse/mount.ts +99 -0
  96. package/src/window/dos/controls/mouse/mouse-locked.ts +51 -0
  97. package/src/window/dos/controls/mouse/mouse-not-locked.ts +84 -0
  98. package/src/window/dos/controls/mouse/mouse-swipe.ts +57 -0
  99. package/src/window/dos/controls/mouse/pointer.ts +85 -0
  100. package/src/window/dos/controls/mouse.ts +21 -0
  101. package/src/window/dos/dos-dhry2.tsx +191 -0
  102. package/src/window/dos/dos-runtime.tsx +185 -0
  103. package/src/window/dos/dos-window.tsx +82 -0
  104. package/src/window/dos/render/canvas.ts +65 -0
  105. package/src/window/dos/render/resize.ts +31 -0
  106. package/src/window/dos/render/webgl.ts +173 -0
  107. package/src/window/dos/sound/audio-node.ts +148 -0
  108. package/src/window/error-window.tsx +9 -0
  109. package/src/window/loading-window.tsx +54 -0
  110. package/src/window/prerun-window.tsx +98 -0
  111. package/src/window/select-window.tsx +118 -0
  112. package/src/window/window.css +78 -0
  113. package/src/window/window.tsx +49 -0
  114. package/src/ws/ws-sockdrive.ts +102 -0
  115. package/src/ws/ws-transport-layer.ts +457 -0
  116. package/tailwind.config.cjs +48 -0
  117. package/tsconfig.json +32 -0
  118. package/tsconfig.node.json +9 -0
  119. package/vite.config.ts +22 -0
@@ -0,0 +1,318 @@
1
+ const S = require("./structs.js");
2
+ const _ = require("./helpers.js");
3
+
4
+ function _baseChain(vol) {
5
+ const chain = {};
6
+
7
+ chain.sectorSize = vol._sectorSize;
8
+
9
+ function posFromOffset(off) {
10
+ const secSize = chain.sectorSize;
11
+ const offset = off % secSize;
12
+ const sector = (off - offset) / secSize;
13
+ return { sector: sector, offset: offset };
14
+ }
15
+
16
+ chain.cacheAdvice = "NORMAL";
17
+ chain._vol_readSectors = vol._readSectors.bind(vol);
18
+ chain._vol_writeSectors = vol._writeSectors.bind(vol);
19
+
20
+ // cb(error, bytesRead, buffer)
21
+ chain.readFromPosition = function(targetPos, buffer, cb) {
22
+ if (typeof targetPos === "number") targetPos = posFromOffset(targetPos);
23
+ if (typeof buffer === "number") buffer = _.allocBuffer(buffer);
24
+ /* NOTE: to keep our contract with the volume driver, we need to read on _full_ sector boundaries!
25
+ So we divide the read into [up to] three parts: {preface, main, trailer}
26
+ This is kind of unfortunate, but in practice should often still be reasonably efficient. */
27
+ if (targetPos.offset) {
28
+ chain.readSectors(targetPos.sector, _.allocBuffer(chain.sectorSize), function(e, d) {
29
+ if (e || !d) cb(e, 0, buffer);
30
+ else { // copy preface into `buffer`
31
+ const dBeg = targetPos.offset;
32
+ const dEnd = dBeg + buffer.length;
33
+ d.copy(buffer, 0, dBeg, dEnd);
34
+ if (dEnd > d.length) readMain();
35
+ else cb(null, buffer.length, buffer);
36
+ }
37
+ });
38
+ } else readMain();
39
+ function readMain() {
40
+ const prefaceLen = targetPos.offset && (chain.sectorSize - targetPos.offset);
41
+ const trailerLen = (buffer.length - prefaceLen) % chain.sectorSize;
42
+ const mainSector = (prefaceLen) ? targetPos.sector + 1 : targetPos.sector;
43
+ const mainBuffer = (trailerLen) ? buffer.slice(prefaceLen, -trailerLen) : buffer.slice(prefaceLen);
44
+ if (mainBuffer.length) {
45
+ chain.readSectors(mainSector, mainBuffer, function(e, d) {
46
+ if (e || !d) cb(e, prefaceLen, buffer);
47
+ else if (!trailerLen) cb(null, buffer.length, buffer);
48
+ else readTrailer();
49
+ });
50
+ } else readTrailer();
51
+ function readTrailer() {
52
+ const trailerSector = mainSector + (mainBuffer.length / chain.sectorSize);
53
+ chain.readSectors(trailerSector, _.allocBuffer(chain.sectorSize), function(e, d) {
54
+ if (e || !d) cb(e, buffer.length-trailerLen, buffer);
55
+ else {
56
+ d.copy(buffer, buffer.length-trailerLen, 0, trailerLen);
57
+ cb(null, buffer.length, buffer);
58
+ }
59
+ });
60
+ }
61
+ }
62
+ };
63
+
64
+ // cb(error)
65
+ chain.writeToPosition = function(targetPos, data, cb) {
66
+ _.log(_.log.DBG, "WRITING", data.length, "bytes at", targetPos, "in", this.toJSON(), data);
67
+ if (typeof targetPos === "number") targetPos = posFromOffset(targetPos);
68
+
69
+ const prefaceBuffer = (targetPos.offset) ? data.slice(0, chain.sectorSize-targetPos.offset) : null;
70
+ if (prefaceBuffer) {
71
+ _modifySector(targetPos.sector, targetPos.offset, prefaceBuffer, function(e) {
72
+ if (e) cb(e);
73
+ else if (prefaceBuffer.length < data.length) writeMain();
74
+ else cb();
75
+ });
76
+ } else writeMain();
77
+ function writeMain() {
78
+ const prefaceLen = (prefaceBuffer) ? prefaceBuffer.length : 0;
79
+ const trailerLen = (data.length - prefaceLen) % chain.sectorSize;
80
+ const mainSector = (prefaceLen) ? targetPos.sector + 1 : targetPos.sector;
81
+ const mainBuffer = (trailerLen) ? data.slice(prefaceLen, -trailerLen) : data.slice(prefaceLen);
82
+ if (mainBuffer.length) {
83
+ chain.writeSectors(mainSector, mainBuffer, function(e) {
84
+ if (e) cb(e);
85
+ else if (!trailerLen) cb();
86
+ else writeTrailer();
87
+ });
88
+ } else writeTrailer();
89
+ function writeTrailer() {
90
+ const trailerSector = mainSector + (mainBuffer.length / chain.sectorSize);
91
+ const trailerBuffer = data.slice(data.length-trailerLen); // WORKAROUND: https://github.com/tessel/runtime/issues/721
92
+ _modifySector(trailerSector, 0, trailerBuffer, cb);
93
+ }
94
+ }
95
+ function _modifySector(sec, off, data, cb) {
96
+ chain.readSectors(sec, _.allocBuffer(chain.sectorSize), function(e, orig) {
97
+ if (e) return cb(e);
98
+ orig || (orig = _.allocBuffer(chain.sectorSize, 0));
99
+ data.copy(orig, off);
100
+ chain.writeSectors(sec, orig, cb);
101
+ });
102
+ }
103
+ };
104
+
105
+ return chain;
106
+ };
107
+
108
+
109
+ exports.clusterChain = function(vol, firstCluster, _parent) {
110
+ const chain = _baseChain(vol);
111
+ const cache = [firstCluster];
112
+
113
+ chain.firstCluster = firstCluster;
114
+
115
+ function _cacheIsComplete() {
116
+ return cache[cache.length-1] === "eof";
117
+ }
118
+
119
+ function extendCacheToInclude(i, cb) { // NOTE: may `cb()` before returning!
120
+ if (i < cache.length) cb(null, cache[i]);
121
+ else if (_cacheIsComplete()) cb(null, "eof");
122
+ else {
123
+ vol.fetchFromFAT(cache[cache.length-1], function(e, d) {
124
+ if (e) cb(e);
125
+ else if (typeof d === "string" && d !== "eof") cb(S.err.IO());
126
+ else {
127
+ cache.push(d);
128
+ extendCacheToInclude(i, cb);
129
+ }
130
+ });
131
+ }
132
+ }
133
+
134
+ function expandChainToLength(clusterCount, cb) {
135
+ if (!_cacheIsComplete()) throw Error("Must be called only when cache is complete!");
136
+ else cache.pop(); // remove 'eof' entry until finished
137
+
138
+ function addCluster(clustersNeeded, lastCluster) {
139
+ if (!clustersNeeded) cache.push("eof"), cb();
140
+ else {
141
+ vol.allocateInFAT(lastCluster, function(e, newCluster) {
142
+ if (e) cb(e);
143
+ else {
144
+ vol.storeToFAT(lastCluster, newCluster, function(e) {
145
+ if (e) return cb(e);
146
+
147
+ cache.push(newCluster);
148
+ addCluster(clustersNeeded-1, newCluster);
149
+ });
150
+ }
151
+ });
152
+ }
153
+ }
154
+ addCluster(clusterCount - cache.length, cache[cache.length - 1]);
155
+ }
156
+
157
+ function shrinkChainToLength(clusterCount, cb) {
158
+ if (!_cacheIsComplete()) throw Error("Must be called only when cache is complete!");
159
+ else cache.pop(); // remove 'eof' entry until finished
160
+
161
+ function removeClusters(count, cb) {
162
+ if (!count) cache.push("eof"), cb();
163
+ else {
164
+ vol.storeToFAT(cache.pop(), "free", function(e) {
165
+ if (e) cb(e);
166
+ else removeClusters(count - 1, cb);
167
+ });
168
+ }
169
+ }
170
+ // NOTE: for now, we don't remove the firstCluster ourselves; we should though!
171
+ if (clusterCount) removeClusters(cache.length - clusterCount, cb);
172
+ else removeClusters(cache.length - 1, cb);
173
+ }
174
+
175
+ // [{firstSector,numSectors},{firstSector,numSectors},…]
176
+ function determineSectorGroups(sectorIdx, numSectors, alloc, cb) {
177
+ let sectorOffset = sectorIdx % vol._sectorsPerCluster;
178
+ const clusterIdx = (sectorIdx - sectorOffset) / vol._sectorsPerCluster;
179
+ const numClusters = Math.ceil((numSectors + sectorOffset) / vol._sectorsPerCluster);
180
+ const chainLength = clusterIdx + numClusters;
181
+ extendCacheToInclude(chainLength-1, function(e, c) {
182
+ if (e) cb(e);
183
+ else if (c === "eof" && alloc) {
184
+ expandChainToLength(chainLength, function(e) {
185
+ if (e) cb(e);
186
+ else _determineSectorGroups();
187
+ });
188
+ } else _determineSectorGroups();
189
+ });
190
+ function _determineSectorGroups() {
191
+ // …now we have a complete cache
192
+ const groups = [];
193
+ let _group = null;
194
+ for (var i = clusterIdx; i < chainLength; ++i) {
195
+ const c = (i < cache.length) ? cache[i] : "eof";
196
+ if (c === "eof") break;
197
+ else if (_group && c !== _group._nextCluster) {
198
+ groups.push(_group);
199
+ _group = null;
200
+ }
201
+ if (!_group) {
202
+ _group = {
203
+ _nextCluster: c+1,
204
+ firstSector: vol._firstSectorOfCluster(c) + sectorOffset,
205
+ numSectors: vol._sectorsPerCluster - sectorOffset,
206
+ };
207
+ } else {
208
+ _group._nextCluster += 1;
209
+ _group.numSectors += vol._sectorsPerCluster;
210
+ }
211
+ sectorOffset = 0; // only first group is offset
212
+ }
213
+ if (_group) groups.push(_group);
214
+ cb(null, groups, i === chainLength);
215
+ }
216
+ }
217
+
218
+ chain.readSectors = function(i, dest, cb) {
219
+ let groupOffset = 0; let groupsPending;
220
+ determineSectorGroups(i, dest.length / chain.sectorSize, false, function(e, groups, complete) {
221
+ if (e) cb(e);
222
+ else if (!complete) groupsPending = -1, _pastEOF(cb);
223
+ else if ((groupsPending = groups.length)) {
224
+ const process = (group) => new Promise((resolve) => {
225
+ const groupLength = group.numSectors * chain.sectorSize;
226
+ const groupBuffer = dest.slice(groupOffset, groupOffset += groupLength);
227
+ chain._vol_readSectors(group.firstSector, groupBuffer, function(e, d) {
228
+ if (e && groupsPending !== -1) groupsPending = -1, cb(e);
229
+ else if (--groupsPending === 0) cb(null, dest);
230
+ resolve();
231
+ });
232
+ });
233
+
234
+ (async () => {
235
+ for (const group of groups) {
236
+ await process(group);
237
+ }
238
+ })().catch((e) => cb(e));
239
+ } else cb(null, dest); // 0-length destination case
240
+ });
241
+ };
242
+
243
+ // TODO: does this handle NOSPC condition?
244
+ chain.writeSectors = function(i, data, cb) {
245
+ let groupOffset = 0; let groupsPending;
246
+ determineSectorGroups(i, data.length / chain.sectorSize, true, function(e, groups) {
247
+ if (e) cb(e);
248
+ else if ((groupsPending = groups.length)) {
249
+ const process = (group) => new Promise((resolve) => {
250
+ const groupLength = group.numSectors * chain.sectorSize;
251
+ const groupBuffer = data.slice(groupOffset, groupOffset += groupLength);
252
+ chain._vol_writeSectors(group.firstSector, groupBuffer, function(e) {
253
+ if (e && groupsPending !== -1) groupsPending = -1, cb(e);
254
+ else if (--groupsPending === 0) cb();
255
+ resolve();
256
+ });
257
+ });
258
+
259
+ (async () => {
260
+ for (const group of groups) {
261
+ await process(group);
262
+ }
263
+ })().catch((e) => cb(e));
264
+ } else cb(); // 0-length data case
265
+ });
266
+ };
267
+
268
+ chain.truncate = function(numSectors, cb) {
269
+ extendCacheToInclude(Infinity, function(e, c) {
270
+ if (e) return cb(e);
271
+
272
+ const currentLength = cache.length-1;
273
+ const clustersNeeded = Math.ceil(numSectors / vol._sectorsPerCluster);
274
+ if (clustersNeeded < currentLength) shrinkChainToLength(clustersNeeded, cb);
275
+ else if (clustersNeeded > currentLength) expandChainToLength(clustersNeeded, cb);
276
+ else cb();
277
+ });
278
+ };
279
+
280
+
281
+ chain.toJSON = function() {
282
+ return { firstCluster: firstCluster };
283
+ };
284
+
285
+ return chain;
286
+ };
287
+
288
+ exports.sectorChain = function(vol, firstSector, numSectors) {
289
+ const chain = _baseChain(vol);
290
+
291
+ chain.firstSector = firstSector;
292
+ chain.numSectors = numSectors;
293
+
294
+ chain.readSectors = function(i, dest, cb) {
295
+ if (i < numSectors) chain._vol_readSectors(firstSector+i, dest, cb);
296
+ else _pastEOF(cb);
297
+ };
298
+
299
+ chain.writeSectors = function(i, data, cb) {
300
+ if (i < numSectors) chain._vol_writeSectors(firstSector+i, data, cb);
301
+ else _.delayedCall(cb, S.err.NOSPC());
302
+ };
303
+
304
+ chain.truncate = function(i, cb) {
305
+ _.delayedCall(cb, S.err.INVAL());
306
+ };
307
+
308
+ chain.toJSON = function() {
309
+ return { firstSector: firstSector, numSectors: numSectors };
310
+ };
311
+
312
+ return chain;
313
+ };
314
+
315
+ // NOTE: used with mixed feelings, broken out to mark uses
316
+ function _pastEOF(cb) {
317
+ _.delayedCall(cb, null, null);
318
+ }