js-dos 7.4.9 → 8.1.0

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 (155) hide show
  1. package/.github/workflows/build.yml +51 -0
  2. package/.gitmodules +4 -0
  3. package/README.deployment.md +11 -0
  4. package/README.md +29 -89
  5. package/index.html +106 -0
  6. package/package.json +46 -71
  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 +94 -0
  14. package/src/components/dos-option-select.tsx +108 -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 +156 -0
  23. package/src/frame/editor/defaults.ts +419 -0
  24. package/src/frame/editor/editor-conf-frame.tsx +226 -0
  25. package/src/frame/editor/editor-frame.css +55 -0
  26. package/src/frame/editor/editor-fs-frame.tsx +314 -0
  27. package/src/frame/fat-drives-frame.tsx +135 -0
  28. package/src/frame/frame.css +119 -0
  29. package/src/frame/frame.tsx +34 -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 +96 -0
  33. package/src/frame/settings-frame.tsx +14 -0
  34. package/src/frame/stats-frame.tsx +87 -0
  35. package/src/host/bundle-storage.ts +91 -0
  36. package/src/host/fullscreen.ts +35 -0
  37. package/src/host/lcache.ts +203 -0
  38. package/src/host/lstorage.ts +156 -0
  39. package/src/i18n.ts +179 -0
  40. package/src/index.css +7 -0
  41. package/src/load.ts +132 -0
  42. package/src/login/login.css +9 -0
  43. package/src/login/login.tsx +77 -0
  44. package/src/main.tsx +197 -0
  45. package/src/public/types.ts +41 -0
  46. package/src/sidebar/account-button.tsx +33 -0
  47. package/src/sidebar/cloud-save-button.tsx +89 -0
  48. package/src/sidebar/diskette-icon.tsx +14 -0
  49. package/src/sidebar/fullscreen-button.tsx +44 -0
  50. package/src/sidebar/network-button.tsx +39 -0
  51. package/src/sidebar/sidebar-button.tsx +244 -0
  52. package/src/sidebar/sidebar.css +47 -0
  53. package/src/sidebar/sidebar.tsx +30 -0
  54. package/src/sockdrive/LICENSE +674 -0
  55. package/src/sockdrive/README.md +45 -0
  56. package/src/sockdrive/include/sockdrive.h +23 -0
  57. package/src/sockdrive/js/.eslintrc.json +43 -0
  58. package/src/sockdrive/js/package.json +29 -0
  59. package/src/sockdrive/js/src/fatfs/chains.js +318 -0
  60. package/src/sockdrive/js/src/fatfs/dir.js +385 -0
  61. package/src/sockdrive/js/src/fatfs/helpers.js +210 -0
  62. package/src/sockdrive/js/src/fatfs/index.js +606 -0
  63. package/src/sockdrive/js/src/fatfs/structs.js +255 -0
  64. package/src/sockdrive/js/src/fatfs/vol.js +166 -0
  65. package/src/sockdrive/js/src/sockdrive/cache.ts +133 -0
  66. package/src/sockdrive/js/src/sockdrive/drive.ts +457 -0
  67. package/src/sockdrive/js/src/sockdrive/lru.ts +221 -0
  68. package/src/sockdrive/js/src/sockdrive/types.ts +14 -0
  69. package/src/sockdrive/js/src/sockdrive-fat.d.ts +64 -0
  70. package/src/sockdrive/js/src/sockdrive-fat.ts +226 -0
  71. package/src/sockdrive/js/src/sockdrive-native.ts +100 -0
  72. package/src/sockdrive/js/src/test/test.ts +288 -0
  73. package/src/sockdrive/js/tsconfig.json +11 -0
  74. package/src/sockdrive/js/webpack.config.js +68 -0
  75. package/src/sockdrive/js/yarn.lock +1808 -0
  76. package/src/store/auth.ts +115 -0
  77. package/src/store/dos.ts +305 -0
  78. package/src/store/editor.ts +61 -0
  79. package/src/store/init.ts +48 -0
  80. package/src/store/storage.ts +32 -0
  81. package/src/store/ui.ts +186 -0
  82. package/src/store.tsx +108 -0
  83. package/src/ui.tsx +104 -0
  84. package/src/v8/changes.ts +56 -0
  85. package/src/v8/config.ts +18 -0
  86. package/src/v8/subscriptions.ts +62 -0
  87. package/src/vite-env.d.ts +2 -0
  88. package/src/window/dos/controls/keyboard.ts +32 -0
  89. package/src/window/dos/controls/keys.ts +331 -0
  90. package/src/window/dos/controls/mouse/mount.ts +99 -0
  91. package/src/window/dos/controls/mouse/mouse-locked.ts +51 -0
  92. package/src/window/dos/controls/mouse/mouse-not-locked.ts +84 -0
  93. package/src/window/dos/controls/mouse/mouse-swipe.ts +57 -0
  94. package/src/window/dos/controls/mouse/pointer.ts +85 -0
  95. package/src/window/dos/controls/mouse.ts +21 -0
  96. package/src/window/dos/dos-dhry2.tsx +191 -0
  97. package/src/window/dos/dos-runtime.tsx +214 -0
  98. package/src/window/dos/dos-window.tsx +84 -0
  99. package/src/window/dos/render/canvas.ts +65 -0
  100. package/src/window/dos/render/resize.ts +31 -0
  101. package/src/window/dos/render/webgl.ts +173 -0
  102. package/src/window/dos/sound/audio-node.ts +148 -0
  103. package/src/window/error-window.tsx +9 -0
  104. package/src/window/loading-window.tsx +54 -0
  105. package/src/window/prerun-window.tsx +123 -0
  106. package/src/window/select-window.tsx +118 -0
  107. package/src/window/window.css +78 -0
  108. package/src/window/window.tsx +49 -0
  109. package/src/ws/ws-sockdrive.ts +101 -0
  110. package/src/ws/ws-transport-layer.ts +455 -0
  111. package/tailwind.config.cjs +48 -0
  112. package/tsconfig.json +32 -0
  113. package/tsconfig.node.json +9 -0
  114. package/vite.config.ts +22 -0
  115. package/LICENSE +0 -339
  116. package/dist/emulators-ui-loader.png +0 -0
  117. package/dist/js-dos.css +0 -1690
  118. package/dist/js-dos.js +0 -17
  119. package/dist/js-dos.js.map +0 -1
  120. package/dist/types/src/backend/v7/latency.d.ts +0 -8
  121. package/dist/types/src/backend/v7/personal.d.ts +0 -2
  122. package/dist/types/src/backend/v7/v7-config.d.ts +0 -16
  123. package/dist/types/src/components/action-bar.d.ts +0 -2
  124. package/dist/types/src/components/action-hide.d.ts +0 -4
  125. package/dist/types/src/components/action-save-or-exit.d.ts +0 -4
  126. package/dist/types/src/components/client.d.ts +0 -6
  127. package/dist/types/src/components/controls.d.ts +0 -8
  128. package/dist/types/src/components/cpu-control.d.ts +0 -2
  129. package/dist/types/src/components/horizontal-slider.d.ts +0 -13
  130. package/dist/types/src/components/region.d.ts +0 -6
  131. package/dist/types/src/components/scale-control.d.ts +0 -3
  132. package/dist/types/src/components/sensitivity-control.d.ts +0 -3
  133. package/dist/types/src/components/sidebar/latency-info.d.ts +0 -7
  134. package/dist/types/src/components/sidebar/main.d.ts +0 -2
  135. package/dist/types/src/components/sidebar/networking.d.ts +0 -2
  136. package/dist/types/src/components/sidebar/token/token-add-time.d.ts +0 -2
  137. package/dist/types/src/components/sidebar/token/token-select.d.ts +0 -2
  138. package/dist/types/src/components/sidebar/token/token.d.ts +0 -17
  139. package/dist/types/src/components/sidebar.d.ts +0 -2
  140. package/dist/types/src/components/sync-control.d.ts +0 -2
  141. package/dist/types/src/components/tip.d.ts +0 -2
  142. package/dist/types/src/components/vertical-slider.d.ts +0 -12
  143. package/dist/types/src/components/volume-control.d.ts +0 -3
  144. package/dist/types/src/dom.d.ts +0 -8
  145. package/dist/types/src/hardware-transport-layer.d.ts +0 -18
  146. package/dist/types/src/icons.d.ts +0 -95
  147. package/dist/types/src/player-app.d.ts +0 -50
  148. package/dist/types/src/player.d.ts +0 -29
  149. package/dist/types/src/request.d.ts +0 -1
  150. package/dist/types/src/xhr.d.ts +0 -6
  151. package/dist/types/v7-services/src/personal.d.ts +0 -3
  152. package/dist/wdosbox.js +0 -55
  153. package/dist/wdosbox.js.symbols +0 -3174
  154. package/dist/wdosbox.wasm +0 -0
  155. package/examples/digger/index.html +0 -71
@@ -0,0 +1,45 @@
1
+ # sockdrive client api sdk
2
+
3
+ # js
4
+
5
+ Plain js api ready to use in browser.
6
+
7
+ Building:
8
+
9
+ ```js
10
+ cd js
11
+ yarn
12
+ yarn run webpack
13
+ ```
14
+
15
+ Js api is in `js/dist/sockdriveFat.js`. The api is node-like fs api.
16
+
17
+ Testing:
18
+
19
+ 1. Install dosfstools
20
+ 2. cd js/dist/test && make-img-linux.sh
21
+ 3. Serve dist folder
22
+ 4. Open test.html in browser to run tests.
23
+
24
+ ## 3rdparty code
25
+
26
+ ### LRU cache
27
+
28
+ `src/sockdrive/lru.js` is from https://github.com/rsms/js-lru
29
+ Licensed under MIT. Copyright (c) 2010 Rasmus Andersson <http://hunch.se/>
30
+
31
+ ### fatfs (fork)
32
+
33
+ `src/fatfs` is a fork of https://github.com/natevw/fatfs
34
+
35
+ License information:
36
+
37
+ ```
38
+ © 2014 Nathan Vander Wilt.
39
+ Funding for this work was provided by Technical Machine, Inc.
40
+
41
+ Reuse under your choice of:
42
+
43
+ * BSD-2-Clause
44
+ * Apache 2.0
45
+ ```
@@ -0,0 +1,23 @@
1
+ //
2
+ // Created by caiii on 30.08.2023.
3
+ //
4
+
5
+ #ifndef SOCKDRIVE_H
6
+ #define SOCKDRIVE_H
7
+
8
+ #include <stddef.h>
9
+ #include <stdint.h>
10
+
11
+ extern "C" {
12
+ size_t sockdrive_open(const char* url, const char* owner, const char* name, const char* token);
13
+ uint8_t sockdrive_read(size_t handle, uint32_t sector, uint8_t * buffer);
14
+ uint8_t sockdrive_write(size_t handle, uint32_t sector, uint8_t* buffer);
15
+ uint32_t sockdrive_size(size_t handle);
16
+ uint32_t sockdrive_heads(size_t handle);
17
+ uint32_t sockdrive_sectors(size_t handle);
18
+ uint32_t sockdrive_cylinders(size_t handle);
19
+ uint32_t sockdrive_sector_size(size_t handle);
20
+ void sockdrive_close(size_t handle);
21
+ }
22
+
23
+ #endif //SOCKDRIVE_H
@@ -0,0 +1,43 @@
1
+ {
2
+ "env": {
3
+ "browser": true,
4
+ "es2021": true
5
+ },
6
+ "extends": [
7
+ "google"
8
+ ],
9
+ "parser": "@typescript-eslint/parser",
10
+ "parserOptions": {
11
+ "ecmaVersion": 12,
12
+ "sourceType": "module"
13
+ },
14
+ "plugins": [
15
+ "@typescript-eslint"
16
+ ],
17
+ "rules": {
18
+ "object-curly-spacing": [
19
+ "error",
20
+ "always"
21
+ ],
22
+ "require-jsdoc": "off",
23
+ "quotes": [
24
+ "error",
25
+ "double"
26
+ ],
27
+ "indent": [
28
+ "error",
29
+ 4,
30
+ {
31
+ "SwitchCase": 1,
32
+ "FunctionDeclaration": {
33
+ "parameters": "first"
34
+ }
35
+ }
36
+ ],
37
+ "max-len": [
38
+ "error",
39
+ 120
40
+ ]
41
+ },
42
+ "ignorePatterns": ["dist/**/*"]
43
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "sockdrive-asyncify",
3
+ "version": "1.0.0",
4
+ "description": "The sockdrive support for emscripten",
5
+ "license": "GPLv3",
6
+ "private": true,
7
+ "dependencies": {
8
+ "buffer": "^6.0.3",
9
+ "fifolock": "^1.0.0",
10
+ "stream-browserify": "^3.0.0",
11
+ "struct-fu": "^1.2.1",
12
+ "typedarray-to-buffer": "^4.0.0",
13
+ "xok": "^1.0.0"
14
+ },
15
+ "devDependencies": {
16
+ "@types/chai": "^4.3.6",
17
+ "@types/mocha": "^10.0.1",
18
+ "@typescript-eslint/eslint-plugin": "^6.7.2",
19
+ "@typescript-eslint/parser": "^6.7.2",
20
+ "eslint": "^8.48.0",
21
+ "eslint-config-google": "^0.14.0",
22
+ "eslint-webpack-plugin": "^4.0.1",
23
+ "process": "^0.11.10",
24
+ "ts-loader": "^9.4.4",
25
+ "typescript": "^5.2.2",
26
+ "webpack": "^5.88.2",
27
+ "webpack-cli": "^5.1.4"
28
+ }
29
+ }
@@ -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
+ }