js-dos 7.5.0 → 8.1.1
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.
- package/.github/workflows/build.yml +51 -0
- package/.gitmodules +4 -0
- package/README.deployment.md +11 -0
- package/README.md +29 -89
- package/index.html +106 -0
- package/package.json +46 -71
- package/postcss.config.cjs +8 -0
- package/src/app.css +14 -0
- package/src/base.css +10 -0
- package/src/components/checkbox.tsx +41 -0
- package/src/components/close-button.tsx +17 -0
- package/src/components/components.css +28 -0
- package/src/components/dos-option-checkbox.tsx +94 -0
- package/src/components/dos-option-select.tsx +108 -0
- package/src/components/dos-option-slider.tsx +35 -0
- package/src/components/error.tsx +16 -0
- package/src/components/loading.tsx +26 -0
- package/src/components/lock.tsx +10 -0
- package/src/components/select.tsx +33 -0
- package/src/components/slider.tsx +69 -0
- package/src/download-file.ts +18 -0
- package/src/frame/account-frame.tsx +156 -0
- package/src/frame/editor/defaults.ts +419 -0
- package/src/frame/editor/editor-conf-frame.tsx +226 -0
- package/src/frame/editor/editor-frame.css +55 -0
- package/src/frame/editor/editor-fs-frame.tsx +314 -0
- package/src/frame/fat-drives-frame.tsx +135 -0
- package/src/frame/frame.css +119 -0
- package/src/frame/frame.tsx +34 -0
- package/src/frame/image-rendering-frame.tsx +8 -0
- package/src/frame/network-frame.tsx +157 -0
- package/src/frame/quick-save-frame.tsx +96 -0
- package/src/frame/settings-frame.tsx +14 -0
- package/src/frame/stats-frame.tsx +87 -0
- package/src/host/bundle-storage.ts +91 -0
- package/src/host/fullscreen.ts +35 -0
- package/src/host/lcache.ts +203 -0
- package/src/host/lstorage.ts +156 -0
- package/src/i18n.ts +183 -0
- package/src/index.css +7 -0
- package/src/load.ts +132 -0
- package/src/login/login.css +9 -0
- package/src/login/login.tsx +77 -0
- package/src/main.tsx +197 -0
- package/src/public/types.ts +41 -0
- package/src/sidebar/account-button.tsx +33 -0
- package/src/sidebar/cloud-save-button.tsx +89 -0
- package/src/sidebar/diskette-icon.tsx +14 -0
- package/src/sidebar/fullscreen-button.tsx +44 -0
- package/src/sidebar/network-button.tsx +39 -0
- package/src/sidebar/sidebar-button.tsx +244 -0
- package/src/sidebar/sidebar.css +47 -0
- package/src/sidebar/sidebar.tsx +30 -0
- package/src/sockdrive/LICENSE +674 -0
- package/src/sockdrive/README.md +45 -0
- package/src/sockdrive/include/sockdrive.h +23 -0
- package/src/sockdrive/js/.eslintrc.json +43 -0
- package/src/sockdrive/js/package.json +29 -0
- package/src/sockdrive/js/src/fatfs/chains.js +318 -0
- package/src/sockdrive/js/src/fatfs/dir.js +385 -0
- package/src/sockdrive/js/src/fatfs/helpers.js +210 -0
- package/src/sockdrive/js/src/fatfs/index.js +606 -0
- package/src/sockdrive/js/src/fatfs/structs.js +255 -0
- package/src/sockdrive/js/src/fatfs/vol.js +166 -0
- package/src/sockdrive/js/src/sockdrive/cache.ts +56 -0
- package/src/sockdrive/js/src/sockdrive/drive.ts +467 -0
- package/src/sockdrive/js/src/sockdrive/lru.ts +221 -0
- package/src/sockdrive/js/src/sockdrive/types.ts +14 -0
- package/src/sockdrive/js/src/sockdrive-fat.d.ts +64 -0
- package/src/sockdrive/js/src/sockdrive-fat.ts +226 -0
- package/src/sockdrive/js/src/sockdrive-native.ts +100 -0
- package/src/sockdrive/js/src/test/test.ts +289 -0
- package/src/sockdrive/js/tsconfig.json +11 -0
- package/src/sockdrive/js/webpack.config.js +68 -0
- package/src/sockdrive/js/yarn.lock +1808 -0
- package/src/store/auth.ts +115 -0
- package/src/store/dos.ts +305 -0
- package/src/store/editor.ts +61 -0
- package/src/store/init.ts +48 -0
- package/src/store/storage.ts +32 -0
- package/src/store/ui.ts +191 -0
- package/src/store.tsx +108 -0
- package/src/ui.tsx +140 -0
- package/src/v8/changes.ts +56 -0
- package/src/v8/config.ts +20 -0
- package/src/v8/subscriptions.ts +62 -0
- package/src/vite-env.d.ts +2 -0
- package/src/window/dos/controls/keyboard.ts +32 -0
- package/src/window/dos/controls/keys.ts +331 -0
- package/src/window/dos/controls/mouse/mount.ts +99 -0
- package/src/window/dos/controls/mouse/mouse-locked.ts +51 -0
- package/src/window/dos/controls/mouse/mouse-not-locked.ts +84 -0
- package/src/window/dos/controls/mouse/mouse-swipe.ts +57 -0
- package/src/window/dos/controls/mouse/pointer.ts +85 -0
- package/src/window/dos/controls/mouse.ts +21 -0
- package/src/window/dos/dos-dhry2.tsx +191 -0
- package/src/window/dos/dos-runtime.tsx +214 -0
- package/src/window/dos/dos-window.tsx +91 -0
- package/src/window/dos/render/canvas.ts +65 -0
- package/src/window/dos/render/resize.ts +31 -0
- package/src/window/dos/render/webgl.ts +173 -0
- package/src/window/dos/sound/audio-node.ts +148 -0
- package/src/window/error-window.tsx +9 -0
- package/src/window/loading-window.tsx +54 -0
- package/src/window/prerun-window.tsx +123 -0
- package/src/window/select-window.tsx +118 -0
- package/src/window/window.css +78 -0
- package/src/window/window.tsx +49 -0
- package/src/ws/ws-sockdrive.ts +123 -0
- package/src/ws/ws-transport-layer.ts +468 -0
- package/tailwind.config.cjs +48 -0
- package/tsconfig.json +32 -0
- package/tsconfig.node.json +9 -0
- package/vite.config.ts +22 -0
- package/LICENSE +0 -339
- package/dist/emulators-ui-loader.png +0 -0
- package/dist/js-dos.css +0 -1690
- package/dist/js-dos.js +0 -17
- package/dist/js-dos.js.map +0 -1
- package/dist/types/src/backend/v7/latency.d.ts +0 -8
- package/dist/types/src/backend/v7/personal.d.ts +0 -2
- package/dist/types/src/backend/v7/v7-config.d.ts +0 -16
- package/dist/types/src/components/action-bar.d.ts +0 -2
- package/dist/types/src/components/action-hide.d.ts +0 -4
- package/dist/types/src/components/action-save-or-exit.d.ts +0 -4
- package/dist/types/src/components/client.d.ts +0 -6
- package/dist/types/src/components/controls.d.ts +0 -8
- package/dist/types/src/components/cpu-control.d.ts +0 -2
- package/dist/types/src/components/horizontal-slider.d.ts +0 -13
- package/dist/types/src/components/region.d.ts +0 -6
- package/dist/types/src/components/scale-control.d.ts +0 -3
- package/dist/types/src/components/sensitivity-control.d.ts +0 -3
- package/dist/types/src/components/sidebar/latency-info.d.ts +0 -7
- package/dist/types/src/components/sidebar/main.d.ts +0 -2
- package/dist/types/src/components/sidebar/networking.d.ts +0 -2
- package/dist/types/src/components/sidebar/token/token-add-time.d.ts +0 -2
- package/dist/types/src/components/sidebar/token/token-select.d.ts +0 -2
- package/dist/types/src/components/sidebar/token/token.d.ts +0 -17
- package/dist/types/src/components/sidebar.d.ts +0 -2
- package/dist/types/src/components/sync-control.d.ts +0 -2
- package/dist/types/src/components/tip.d.ts +0 -2
- package/dist/types/src/components/vertical-slider.d.ts +0 -12
- package/dist/types/src/components/volume-control.d.ts +0 -3
- package/dist/types/src/dom.d.ts +0 -8
- package/dist/types/src/hardware-transport-layer.d.ts +0 -18
- package/dist/types/src/icons.d.ts +0 -95
- package/dist/types/src/player-app.d.ts +0 -50
- package/dist/types/src/player.d.ts +0 -29
- package/dist/types/src/request.d.ts +0 -1
- package/dist/types/src/xhr.d.ts +0 -6
- package/dist/types/v7-services/src/personal.d.ts +0 -3
- package/dist/wdosbox.js +0 -55
- package/dist/wdosbox.js.symbols +0 -3174
- package/dist/wdosbox.wasm +0 -0
- package/examples/digger/index.html +0 -71
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// see http://staff.washington.edu/dittrich/misc/fatgen103.pdf
|
|
2
|
+
// and http://www.cse.scu.edu/~tschwarz/COEN252_09/Lectures/FAT.html
|
|
3
|
+
|
|
4
|
+
const _ = require("struct-fu");
|
|
5
|
+
const __ = require("./helpers.js");
|
|
6
|
+
|
|
7
|
+
const bootBase = _.struct([
|
|
8
|
+
_.byte("jmpBoot", 3),
|
|
9
|
+
_.char("OEMName", 8),
|
|
10
|
+
_.uint16le("BytsPerSec"),
|
|
11
|
+
_.uint8("SecPerClus"),
|
|
12
|
+
_.uint16le("ResvdSecCnt"), // Rsvd in table, but Resvd in calcs…
|
|
13
|
+
_.uint8("NumFATs"),
|
|
14
|
+
_.uint16le("RootEntCnt"),
|
|
15
|
+
_.uint16le("TotSec16"),
|
|
16
|
+
_.uint8("Media"),
|
|
17
|
+
_.uint16le("FATSz16"),
|
|
18
|
+
_.uint16le("SecPerTrk"),
|
|
19
|
+
_.uint16le("NumHeads"),
|
|
20
|
+
_.uint32le("HiddSec"),
|
|
21
|
+
_.uint32le("TotSec32"),
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const bootInfo = _.struct([
|
|
25
|
+
_.uint8("DrvNum"),
|
|
26
|
+
_.uint8("Reserved1"),
|
|
27
|
+
_.uint8("BootSig"),
|
|
28
|
+
_.uint32le("VolID"),
|
|
29
|
+
_.char("VolLab", 11),
|
|
30
|
+
_.char("FilSysType", 8),
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
exports.boot16 = _.struct([
|
|
34
|
+
bootBase,
|
|
35
|
+
bootInfo,
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
exports.boot32 = _.struct([
|
|
39
|
+
bootBase,
|
|
40
|
+
_.uint32le("FATSz32"),
|
|
41
|
+
_.struct("ExtFlags", [
|
|
42
|
+
_.ubit("NumActiveFAT", 4),
|
|
43
|
+
_.ubit("_reserved1", 3),
|
|
44
|
+
_.bool("MirroredFAT"),
|
|
45
|
+
_.ubit("_reserved2", 8),
|
|
46
|
+
].reverse()),
|
|
47
|
+
_.struct("FSVer", [
|
|
48
|
+
_.uint8("Major"),
|
|
49
|
+
_.uint8("Minor"),
|
|
50
|
+
]),
|
|
51
|
+
_.uint32le("RootClus"),
|
|
52
|
+
_.uint16le("FSInfo"),
|
|
53
|
+
_.uint16le("BkBootSec"),
|
|
54
|
+
_.byte("Reserved", 12),
|
|
55
|
+
bootInfo,
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
const _time = _.struct([
|
|
60
|
+
_.ubit("hours", 5),
|
|
61
|
+
_.ubit("minutes", 6),
|
|
62
|
+
_.ubit("seconds_2", 5),
|
|
63
|
+
]); const time = {
|
|
64
|
+
valueFromBytes: function(buf, off) {
|
|
65
|
+
off || (off = { bytes: 0 });
|
|
66
|
+
|
|
67
|
+
const _buf = __.bufferFrom([buf[off.bytes+1], buf[off.bytes+0]]);
|
|
68
|
+
const val = _time.valueFromBytes(_buf);
|
|
69
|
+
off.bytes += this.size;
|
|
70
|
+
return val;
|
|
71
|
+
},
|
|
72
|
+
bytesFromValue: function(val, buf, off) {
|
|
73
|
+
val || (val = { hours: 0, minutes: 0, seconds_2: 0 });
|
|
74
|
+
buf || (buf = __.allocBuffer(this.size));
|
|
75
|
+
off || (off = { bytes: 0 });
|
|
76
|
+
|
|
77
|
+
const _buf = _time.bytesFromValue(val);
|
|
78
|
+
buf[off.bytes+1] = _buf[0];
|
|
79
|
+
buf[off.bytes+0] = _buf[1];
|
|
80
|
+
off.bytes += this.size;
|
|
81
|
+
return buf;
|
|
82
|
+
},
|
|
83
|
+
size: _time.size,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const _date = _.struct([
|
|
87
|
+
_.ubit("year", 7),
|
|
88
|
+
_.ubit("month", 4),
|
|
89
|
+
_.ubit("day", 5),
|
|
90
|
+
]); const date = {
|
|
91
|
+
valueFromBytes: function(buf, off) {
|
|
92
|
+
off || (off = { bytes: 0 });
|
|
93
|
+
|
|
94
|
+
const _buf = __.bufferFrom([buf[off.bytes+1], buf[off.bytes+0]]);
|
|
95
|
+
const val = _date.valueFromBytes(_buf);
|
|
96
|
+
off.bytes += this.size;
|
|
97
|
+
return val;
|
|
98
|
+
},
|
|
99
|
+
bytesFromValue: function(val, buf, off) {
|
|
100
|
+
val || (val = { year: 0, month: 0, day: 0 });
|
|
101
|
+
buf || (buf = __.allocBuffer(this.size));
|
|
102
|
+
off || (off = { bytes: 0 });
|
|
103
|
+
|
|
104
|
+
const _buf = _date.bytesFromValue(val);
|
|
105
|
+
buf[off.bytes+1] = _buf[0];
|
|
106
|
+
buf[off.bytes+0] = _buf[1];
|
|
107
|
+
off.bytes += this.size;
|
|
108
|
+
return buf;
|
|
109
|
+
},
|
|
110
|
+
size: _date.size,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
exports.dirEntry = _.struct([
|
|
114
|
+
_.struct("Name", [
|
|
115
|
+
_.char("filename", 8),
|
|
116
|
+
_.char("extension", 3),
|
|
117
|
+
]),
|
|
118
|
+
_.struct("Attr", [
|
|
119
|
+
_.bool("readonly"),
|
|
120
|
+
_.bool("hidden"),
|
|
121
|
+
_.bool("system"),
|
|
122
|
+
_.bool("volume_id"),
|
|
123
|
+
_.bool("directory"),
|
|
124
|
+
_.bool("archive"),
|
|
125
|
+
_.ubit("reserved", 2),
|
|
126
|
+
].reverse()),
|
|
127
|
+
_.byte("NTRes", 1),
|
|
128
|
+
_.uint8("CrtTimeTenth"),
|
|
129
|
+
_.struct("CrtTime", [time]),
|
|
130
|
+
_.struct("CrtDate", [date]),
|
|
131
|
+
_.struct("LstAccDate", [date]),
|
|
132
|
+
_.uint16le("FstClusHI"),
|
|
133
|
+
_.struct("WrtTime", [time]),
|
|
134
|
+
_.struct("WrtDate", [date]),
|
|
135
|
+
_.uint16le("FstClusLO"),
|
|
136
|
+
_.uint32le("FileSize"),
|
|
137
|
+
]);
|
|
138
|
+
exports.entryDoneFlag = 0x00;
|
|
139
|
+
exports.entryFreeFlag = 0xE5;
|
|
140
|
+
exports.entryIsE5Flag = 0x05;
|
|
141
|
+
|
|
142
|
+
exports.dirEntry_simple = _.struct([
|
|
143
|
+
_.struct("Name", [
|
|
144
|
+
_.char("filename", 8),
|
|
145
|
+
_.char("extension", 3),
|
|
146
|
+
]),
|
|
147
|
+
_.padTo(exports.dirEntry.size),
|
|
148
|
+
/*
|
|
149
|
+
_.uint8('Attr_raw'),
|
|
150
|
+
_.byte('NTRes', 1),
|
|
151
|
+
_.byte('Crt_raw', 1+2+2),
|
|
152
|
+
_.byte('Lst_raw', 2),
|
|
153
|
+
_.uint16le('FstClusHI'),
|
|
154
|
+
_.byte('Wrt_raw', 2+2),
|
|
155
|
+
_.uint16le('FstClusLO'),
|
|
156
|
+
_.uint32le('FileSize')
|
|
157
|
+
*/
|
|
158
|
+
]);
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
exports.lastLongFlag = 0x40;
|
|
162
|
+
exports.longDirFlag = 0x0F;
|
|
163
|
+
exports.longDirEntry = _.struct([
|
|
164
|
+
_.uint8("Ord"),
|
|
165
|
+
_.char16le("Name1", 10),
|
|
166
|
+
_.uint8("Attr_raw"),
|
|
167
|
+
_.uint8("Type"),
|
|
168
|
+
_.uint8("Chksum"),
|
|
169
|
+
_.char16le("Name2", 12),
|
|
170
|
+
_.uint16le("FstClusLO"),
|
|
171
|
+
_.char16le("Name3", 4),
|
|
172
|
+
]);
|
|
173
|
+
|
|
174
|
+
if (exports.longDirEntry.size !== exports.dirEntry.size) throw Error("Structs ain't right!");
|
|
175
|
+
|
|
176
|
+
exports.fatField = {
|
|
177
|
+
"fat12": _.struct("Status", [
|
|
178
|
+
_.ubit("field0bc", 8),
|
|
179
|
+
_.ubit("field1c", 4),
|
|
180
|
+
_.ubit("field0a", 4),
|
|
181
|
+
_.ubit("field1ab", 8),
|
|
182
|
+
]),
|
|
183
|
+
"fat16": _.uint16le("Status"),
|
|
184
|
+
"fat32": _.uint32le("Status"), // more properly this 4 bits reserved + uint28le
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
exports.fatPrefix = {
|
|
188
|
+
"fat12": 0xF00,
|
|
189
|
+
"fat16": 0xFF00,
|
|
190
|
+
"fat32": 0x0FFFFF00,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
exports.fatStat = {
|
|
194
|
+
free: 0x00,
|
|
195
|
+
_undef: 0x01,
|
|
196
|
+
rsvMin: 0xF0,
|
|
197
|
+
bad: 0xF7,
|
|
198
|
+
eofMin: 0xF8,
|
|
199
|
+
eof: 0xFF,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
exports._I = {
|
|
203
|
+
RUSR: 0o400,
|
|
204
|
+
WUSR: 0o200,
|
|
205
|
+
XUSR: 0o100,
|
|
206
|
+
|
|
207
|
+
RGRP: 0o040,
|
|
208
|
+
WGRP: 0o020,
|
|
209
|
+
XGRP: 0o010,
|
|
210
|
+
|
|
211
|
+
ROTH: 0o004,
|
|
212
|
+
WOTH: 0o002,
|
|
213
|
+
XOTH: 0o001,
|
|
214
|
+
|
|
215
|
+
SUID: 0o4000,
|
|
216
|
+
SGID: 0o2000,
|
|
217
|
+
SVTX: 0o1000,
|
|
218
|
+
|
|
219
|
+
FDIR: 0o40000,
|
|
220
|
+
FREG: 0o100000,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
exports._I.RWXU = exports._I.RUSR | exports._I.WUSR | exports._I.XUSR;
|
|
224
|
+
exports._I.RWXG = exports._I.RGRP | exports._I.WGRP | exports._I.XGRP;
|
|
225
|
+
exports._I.RWXO = exports._I.ROTH | exports._I.WOTH | exports._I.XOTH;
|
|
226
|
+
exports._I._sss = exports._I.SUID | exports._I.SGID | exports._I.SVTX;
|
|
227
|
+
exports._I._chmoddable = exports._I.RWXU | exports._I.RWXG | exports._I.RWXO | exports._I._sss;
|
|
228
|
+
|
|
229
|
+
const _errors = {
|
|
230
|
+
IO: "Input/output error",
|
|
231
|
+
NOENT: "No such file or directory",
|
|
232
|
+
INVAL: "Invalid argument",
|
|
233
|
+
EXIST: "File exists",
|
|
234
|
+
NAMETOOLONG: "Filename too long",
|
|
235
|
+
NOSPC: "No space left on device",
|
|
236
|
+
NOSYS: "Function not supported",
|
|
237
|
+
ROFS: "ROFLCopter file system",
|
|
238
|
+
NOTDIR: "Not a directory",
|
|
239
|
+
BADF: "Bad file descriptor",
|
|
240
|
+
EXIST: "File exists",
|
|
241
|
+
ISDIR: "Is a directory",
|
|
242
|
+
ACCES: "Permission denied",
|
|
243
|
+
NOSYS: "Function not implemented",
|
|
244
|
+
_TODO: "Not implemented yet!",
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
exports.err = {};
|
|
248
|
+
Object.keys(_errors).forEach(function(sym) {
|
|
249
|
+
const msg = _errors[sym];
|
|
250
|
+
exports.err[sym] = function() {
|
|
251
|
+
const e = new Error(msg);
|
|
252
|
+
e.code = sym;
|
|
253
|
+
return e;
|
|
254
|
+
};
|
|
255
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const S = require("./structs.js");
|
|
2
|
+
const c = require("./chains.js");
|
|
3
|
+
const _ = require("./helpers.js");
|
|
4
|
+
|
|
5
|
+
exports.init = function(volume, opts, bootSector) {
|
|
6
|
+
if (bootSector[510] !== 0x55 || bootSector[511] !== 0xAA) throw Error("Invalid volume signature!");
|
|
7
|
+
|
|
8
|
+
const isFAT16 = bootSector.readUInt16LE(S.boot16.fields["FATSz16"].offset);
|
|
9
|
+
const bootStruct = (isFAT16) ? S.boot16 : S.boot32;
|
|
10
|
+
const BS = bootStruct.valueFromBytes(bootSector);
|
|
11
|
+
_.log(_.log.DBG, "Boot sector info:", BS);
|
|
12
|
+
bootSector = null; // allow GC
|
|
13
|
+
if (!BS.BytsPerSec) throw Error("This looks like an ExFAT volume! (unsupported)");
|
|
14
|
+
else if (BS.BytsPerSec !== volume.sectorSize) throw Error("Sector size mismatch with FAT table.");
|
|
15
|
+
|
|
16
|
+
const FATSz = (isFAT16) ? BS.FATSz16 : BS.FATSz32;
|
|
17
|
+
const rootDirSectors = Math.ceil((BS.RootEntCnt * 32) / BS.BytsPerSec);
|
|
18
|
+
const firstDataSector = BS.ResvdSecCnt + (BS.NumFATs * FATSz) + rootDirSectors;
|
|
19
|
+
const totSec = (BS.TotSec16) ? BS.TotSec16 : BS.TotSec32;
|
|
20
|
+
const dataSec = totSec - firstDataSector;
|
|
21
|
+
const countofClusters = Math.floor(dataSec / BS.SecPerClus);
|
|
22
|
+
// avoid corrupting sectors from other partitions or whatnot
|
|
23
|
+
if (totSec > volume.numSectors) throw Error("Volume size mismatch!");
|
|
24
|
+
|
|
25
|
+
let fatType;
|
|
26
|
+
if (countofClusters < 4085) {
|
|
27
|
+
fatType = "fat12";
|
|
28
|
+
} else if (countofClusters < 65525) {
|
|
29
|
+
fatType = "fat16";
|
|
30
|
+
} else {
|
|
31
|
+
fatType = "fat32";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_.log(_.log.DBG, "rootDirSectors", rootDirSectors, "firstDataSector", firstDataSector, "countofClusters", countofClusters, "=>", fatType);
|
|
35
|
+
|
|
36
|
+
const vol = {};
|
|
37
|
+
|
|
38
|
+
vol.opts = opts;
|
|
39
|
+
|
|
40
|
+
vol._sectorSize = BS.BytsPerSec;
|
|
41
|
+
vol._sectorsPerCluster = BS.SecPerClus;
|
|
42
|
+
vol._firstSectorOfCluster = function(n) {
|
|
43
|
+
return firstDataSector + (n-2)*vol._sectorsPerCluster;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
vol._readSectors = function(secNum, dest, cb) {
|
|
47
|
+
if (typeof dest === "function") {
|
|
48
|
+
cb = dest;
|
|
49
|
+
dest = _.allocBuffer(vol._sectorSize);
|
|
50
|
+
}
|
|
51
|
+
_.log(_.log.DBG, "vol._readSectors", secNum, dest.length);
|
|
52
|
+
if (secNum < volume.numSectors) {
|
|
53
|
+
volume.readSectors(secNum, dest, function(e) {
|
|
54
|
+
cb(e, dest);
|
|
55
|
+
});
|
|
56
|
+
} else throw Error("Invalid sector number!");
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
vol._writeSectors = function(secNum, data, cb) {
|
|
60
|
+
_.log(_.log.DBG, "vol._writeSectors", secNum, data.length);
|
|
61
|
+
// NOTE: these are internal assertions, public API will get proper `S.err`s
|
|
62
|
+
if (data.length % volume.sectorSize) throw Error("Buffer length not a multiple of sector size");
|
|
63
|
+
else if (opts.ro) throw Error("Read-only filesystem");
|
|
64
|
+
else if (secNum < volume.numSectors) volume.writeSectors(secNum, data, cb);
|
|
65
|
+
else throw Error("Invalid sector number!");
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function fatInfoForCluster(n) {
|
|
69
|
+
const entryStruct = S.fatField[fatType];
|
|
70
|
+
const FATOffset = (fatType === "fat12") ? Math.floor(n/2) * entryStruct.size : n * entryStruct.size;
|
|
71
|
+
const SecNum = BS.ResvdSecCnt + Math.floor(FATOffset / BS.BytsPerSec);
|
|
72
|
+
const EntOffset = FATOffset % BS.BytsPerSec;
|
|
73
|
+
return { sector: SecNum-BS.ResvdSecCnt, offset: EntOffset, struct: entryStruct };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// TODO: all this FAT manipulation is crazy inefficient! needs read caching *and* write caching
|
|
77
|
+
// …the best place for cache might be in `volume` handler, though. add a `flush` method to that spec?
|
|
78
|
+
// TODO: how should we handle redundant FATs? mirror every write? just ignore completely? copy-on-eject?
|
|
79
|
+
|
|
80
|
+
const fatChain = c.sectorChain(vol, BS.ResvdSecCnt, FATSz);
|
|
81
|
+
fatChain.cacheAdvice = "RANDOM";
|
|
82
|
+
|
|
83
|
+
vol.fetchFromFAT = function(clusterNum, cb) {
|
|
84
|
+
const info = fatInfoForCluster(clusterNum);
|
|
85
|
+
fatChain.readFromPosition(info, info.struct.size, function(e, n, d) {
|
|
86
|
+
if (e) return cb(e);
|
|
87
|
+
let status = info.struct.valueFromBytes(d); var prefix;
|
|
88
|
+
if (fatType === "fat12") {
|
|
89
|
+
if (clusterNum % 2) {
|
|
90
|
+
status = (status.field1ab << 4) + status.field1c;
|
|
91
|
+
} else {
|
|
92
|
+
status = (status.field0a << 8) + status.field0bc;
|
|
93
|
+
}
|
|
94
|
+
} else if (fatType === "fat32") {
|
|
95
|
+
status &= 0x0FFFFFFF;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
var prefix = S.fatPrefix[fatType];
|
|
99
|
+
if (status === S.fatStat.free) cb(null, "free");
|
|
100
|
+
else if (status === S.fatStat._undef) cb(null, "-invalid-");
|
|
101
|
+
else if (status > prefix+S.fatStat.eofMin) cb(null, "eof");
|
|
102
|
+
else if (status === prefix+S.fatStat.bad) cb(null, "bad");
|
|
103
|
+
else if (status > prefix+S.fatStat.rsvMin) cb(null, "reserved");
|
|
104
|
+
else cb(null, status);
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
vol.storeToFAT = function(clusterNum, status, cb) {
|
|
109
|
+
if (typeof status === "string") {
|
|
110
|
+
status = S.fatStat[status];
|
|
111
|
+
status += S.fatPrefix[fatType];
|
|
112
|
+
}
|
|
113
|
+
const info = fatInfoForCluster(clusterNum);
|
|
114
|
+
// TODO: technically fat32 needs to *preserve* the high 4 bits
|
|
115
|
+
if (fatType === "fat12") {
|
|
116
|
+
fatChain.readFromPosition(info, info.struct.size, function(e, n, d) {
|
|
117
|
+
const value = info.struct.valueFromBytes(d);
|
|
118
|
+
if (clusterNum % 2) {
|
|
119
|
+
value.field1ab = status >>> 4;
|
|
120
|
+
value.field1c = status & 0x0F;
|
|
121
|
+
} else {
|
|
122
|
+
value.field0a = status >>> 8;
|
|
123
|
+
value.field0bc = status & 0xFF;
|
|
124
|
+
}
|
|
125
|
+
const entry = info.struct.bytesFromValue(value);
|
|
126
|
+
fatChain.writeToPosition(info, entry, cb);
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
const entry = info.struct.bytesFromValue(status);
|
|
130
|
+
fatChain.writeToPosition(info, entry, cb);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
vol.allocateInFAT = function(hint, cb) {
|
|
135
|
+
if (typeof hint === "function") {
|
|
136
|
+
cb = hint;
|
|
137
|
+
hint = 2; // TODO: cache a better starting point?
|
|
138
|
+
}
|
|
139
|
+
function searchForFreeCluster(num, cb) {
|
|
140
|
+
if (num < countofClusters) {
|
|
141
|
+
vol.fetchFromFAT(num, function(e, status) {
|
|
142
|
+
if (e) cb(e);
|
|
143
|
+
else if (status === "free") cb(null, num);
|
|
144
|
+
else searchForFreeCluster(num+1, cb);
|
|
145
|
+
});
|
|
146
|
+
} else cb(S.err.NOSPC()); // TODO: try searching backwards from hint…
|
|
147
|
+
}
|
|
148
|
+
searchForFreeCluster(hint, function(e, clusterNum) {
|
|
149
|
+
if (e) cb(e);
|
|
150
|
+
else vol.storeToFAT(clusterNum, "eof", cb.bind(null, null, clusterNum));
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
vol.rootDirectoryChain = (isFAT16) ?
|
|
155
|
+
c.sectorChain(vol, firstDataSector - rootDirSectors, rootDirSectors) :
|
|
156
|
+
c.clusterChain(vol, BS.RootClus);
|
|
157
|
+
vol.rootDirectoryChain.cacheAdvice = "WILLNEED";
|
|
158
|
+
vol.chainForCluster = c.clusterChain.bind(c, vol);
|
|
159
|
+
vol.chainFromJSON = function(d) {
|
|
160
|
+
return ("numSectors" in d) ?
|
|
161
|
+
c.sectorChain(vol, d.firstSector, d.numSectors) :
|
|
162
|
+
c.clusterChain(vol, d.firstCluster);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
return vol;
|
|
166
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { LRUMap } from "./lru";
|
|
2
|
+
|
|
3
|
+
export interface Cache {
|
|
4
|
+
read(sector: number, originReadMode: boolean): Uint8Array | null;
|
|
5
|
+
write(sector: number, buffer: Uint8Array): boolean;
|
|
6
|
+
create(origin: number, buffer: Uint8Array): void;
|
|
7
|
+
getOrigin(sector: number): number;
|
|
8
|
+
memUsed(): number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class BlockCache implements Cache {
|
|
12
|
+
aheadRange: number;
|
|
13
|
+
sectorSize: number;
|
|
14
|
+
lru: LRUMap;
|
|
15
|
+
|
|
16
|
+
constructor(sectorSize: number, aheadRange: number, memoryLimit: number) {
|
|
17
|
+
this.aheadRange = aheadRange;
|
|
18
|
+
this.sectorSize = sectorSize;
|
|
19
|
+
this.lru = new LRUMap(Math.floor(memoryLimit / (aheadRange * sectorSize)));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public read(sector: number, originReadMode: boolean): Uint8Array | null {
|
|
23
|
+
const origin = this.getOrigin(sector);
|
|
24
|
+
const cached = this.lru.get(origin) as Uint8Array;
|
|
25
|
+
if (cached) {
|
|
26
|
+
if (originReadMode) {
|
|
27
|
+
return cached;
|
|
28
|
+
} else {
|
|
29
|
+
const offset = sector - origin;
|
|
30
|
+
return cached.slice(offset * this.sectorSize, (offset + 1) * this.sectorSize);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public write(sector: number, buffer: Uint8Array) {
|
|
37
|
+
const origin = this.getOrigin(sector);
|
|
38
|
+
const cached = this.lru.get(origin);
|
|
39
|
+
if (cached) {
|
|
40
|
+
cached.set(buffer, (sector - origin) * this.sectorSize);
|
|
41
|
+
}
|
|
42
|
+
return cached !== undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public create(origin: number, buffer: Uint8Array) {
|
|
46
|
+
this.lru.set(origin, buffer.slice(0));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public getOrigin(sector: number) {
|
|
50
|
+
return sector - sector % this.aheadRange;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public memUsed() {
|
|
54
|
+
return this.lru.size * this.aheadRange * this.sectorSize;
|
|
55
|
+
}
|
|
56
|
+
}
|