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,118 @@
1
+ import { useDispatch } from "react-redux";
2
+ import { dosSlice } from "../store/dos";
3
+ import { useT } from "../i18n";
4
+ import { loadBundleFromFile, loadBundleFromUrl, loadEmptyBundle } from "../load";
5
+ import { useState } from "preact/hooks";
6
+ import { uiSlice } from "../store/ui";
7
+
8
+ const fileInput = document.createElement("input");
9
+ fileInput.type = "file";
10
+
11
+ export function SelectWindow() {
12
+ const t = useT();
13
+ const dispatch = useDispatch();
14
+ const [useUrl, setUseUrl] = useState<boolean>(false);
15
+
16
+ if (useUrl) {
17
+ return <div class="select-window overflow-hidden flex-grow flex flex-col items-center justify-center px-8 py-8">
18
+ <Load />
19
+ </div>;
20
+ }
21
+
22
+ async function createEmpty() {
23
+ try {
24
+ await loadEmptyBundle(dispatch)
25
+ .catch(console.error);
26
+ } catch (e: any) {
27
+ dispatch(dosSlice.actions.bndError(e.message ?? "unexpected error"));
28
+ }
29
+ }
30
+
31
+ return <div class="select-window overflow-hidden flex-grow flex flex-col items-center justify-center px-8">
32
+ <div class="mb-4 text-center underline cursor-pointer hover:text-accent"
33
+ onClick={() => setUseUrl(true)}>{t("load_by_url")}</div>
34
+ <Upload />
35
+ <div class="mt-4 text-center">{t("upload_file")}</div>
36
+ <div class="mt-4 text-center underline cursor-pointer hover:text-accent"
37
+ onClick={createEmpty}>{t("create_empty")}</div>
38
+ </div>;
39
+ }
40
+
41
+ function Load() {
42
+ const t = useT();
43
+ const dispatch = useDispatch();
44
+ const [url, setUrl] = useState<string>("");
45
+ const [error, setError] = useState<string | null>(null);
46
+
47
+ async function loadBundle(url: string) {
48
+ dispatch(uiSlice.actions.frameNone());
49
+
50
+ let validUrl;
51
+ try {
52
+ validUrl = new URL(url);
53
+ } catch (e: any) {
54
+ setError(e.message);
55
+ return;
56
+ }
57
+
58
+ try {
59
+ await loadBundleFromUrl(validUrl.toString(), dispatch);
60
+ } catch (e: any) {
61
+ dispatch(dosSlice.actions.bndError(e.message ?? "unexpected error"));
62
+ }
63
+ }
64
+
65
+ return <>
66
+ <div class="form-control w-3/4 mb-4">
67
+ <label class="label">
68
+ <span class="label-text">{t("enter_url")}:</span>
69
+ </label>
70
+ <input type="text"
71
+ class="input w-full input-sm input-bordered"
72
+ onChange={(e) => setUrl(e.currentTarget.value ?? "")}
73
+ value={url}></input>
74
+ </div>
75
+ <div class="mt-4 text-center underline cursor-pointer hover:text-accent"
76
+ onClick={() => loadBundle(url)}>{t("load")}</div>
77
+ {error !== null &&
78
+ <div class="mt-8 text-center text-error">
79
+ {error}
80
+ </div>
81
+ }
82
+ </>;
83
+ }
84
+
85
+ function Upload() {
86
+ const dispatch = useDispatch();
87
+
88
+ async function onFileChange() {
89
+ fileInput.removeEventListener("change", onFileChange);
90
+
91
+ if (fileInput.files === null || fileInput.files.length === 0) {
92
+ return;
93
+ }
94
+
95
+ const file = fileInput.files[0];
96
+ try {
97
+ await loadBundleFromFile(file, dispatch)
98
+ .catch((e) => dispatch(dosSlice.actions.bndError(e.message)));
99
+ } catch (e: any) {
100
+ dispatch(dosSlice.actions.bndError(e.message ?? "unexpected error"));
101
+ }
102
+ }
103
+
104
+ function onUpload() {
105
+ fileInput.addEventListener("change", onFileChange);
106
+ fileInput.click();
107
+ }
108
+
109
+ return <div class="cursor-pointer" onClick={onUpload}>
110
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none"
111
+ viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-48 h-48 play-button">
112
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 13.5l3 3m0 0l3-3m-3
113
+ 3v-6m1.06-4.19l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25
114
+ 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5
115
+ 1.5 0 01-1.06-.44z" />
116
+ </svg>
117
+ </div>;
118
+ }
@@ -0,0 +1,78 @@
1
+ .jsdos-rso {
2
+ .window {
3
+ @apply overflow-hidden;
4
+
5
+ .background-image {
6
+ @apply absolute left-12 right-0 h-full pointer-events-none;
7
+ background-position: center;
8
+ background-size: cover;
9
+ background-repeat: no-repeat;
10
+ &::after {
11
+ position: relative;
12
+ content: "";
13
+ display: block;
14
+ width: 100%;
15
+ height: 100%;
16
+ background-color: hsl(var(--b1)/var(--tw-bg-opacity));
17
+ opacity: 0.75;
18
+ }
19
+ }
20
+
21
+ .play-button {
22
+ &:hover {
23
+ color: hsl(var(--af));
24
+ }
25
+ }
26
+
27
+ .dhry2-window {
28
+ @apply absolute left-0 top-0 w-full h-full flex flex-col items-center justify-center;
29
+ @apply bg-black bg-opacity-80 text-2xl px-8 py-4 text-white;
30
+
31
+ .title {
32
+ @apply mb-4 text-center text-4xl;
33
+ }
34
+
35
+ .backend {
36
+ @apply mb-8 text-center;
37
+ }
38
+
39
+ .results {
40
+ @apply grid grid-cols-2 gap-4;
41
+
42
+ div:nth-child(even) {
43
+ @apply text-green-300;
44
+
45
+ span {
46
+ @apply text-white;
47
+ }
48
+ }
49
+
50
+ div:nth-child(2),
51
+ div:last-child {
52
+ @apply text-yellow-300;
53
+ }
54
+
55
+ }
56
+ }
57
+
58
+ .pre-run-window {
59
+ @apply overflow-x-hidden overflow-y-auto flex-grow flex flex-col items-center justify-center px-8 m-auto;
60
+
61
+ .slider {
62
+ @apply justify-center w-80 max-w-none;
63
+
64
+ .label {
65
+ @apply flex-grow;
66
+ }
67
+
68
+ .touch {
69
+ @apply w-40 flex-grow-0;
70
+ }
71
+ }
72
+ }
73
+
74
+ .select-window {
75
+ @apply m-auto;
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,49 @@
1
+ import { useSelector } from "react-redux";
2
+ import { State } from "../store";
3
+ import { DosWindow } from "./dos/dos-window";
4
+ import { ErrorWindow } from "./error-window";
5
+ import { LoadingWindow } from "./loading-window";
6
+ import { PreRunWindow } from "./prerun-window";
7
+ import { SelectWindow } from "./select-window";
8
+
9
+ export function Window(props: {}) {
10
+ const frameOpened = useSelector((state: State) => state.ui.frame) !== "none";
11
+ const frameXs = useSelector((state: State) => state.ui.frameXs);
12
+ const window = useSelector((state: State) => state.ui.window);
13
+ const background = useSelector((state: State) => state.ui.background);
14
+
15
+ let windowComponent = <Loading />;
16
+ switch (window) {
17
+ case "error": {
18
+ windowComponent = <ErrorWindow />;
19
+ } break;
20
+ case "loading": {
21
+ windowComponent = <LoadingWindow />;
22
+ } break;
23
+ case "prerun": {
24
+ windowComponent = <PreRunWindow />;
25
+ } break;
26
+ case "run": {
27
+ windowComponent = <DosWindow />;
28
+ } break;
29
+ case "select": {
30
+ windowComponent = <SelectWindow />;
31
+ } break;
32
+ default: ;
33
+ };
34
+
35
+ return <div class="window absolute w-full h-full bg-base-100">
36
+ <div class="background-image"
37
+ style={{ backgroundImage: (background ? "url(" + background + ")" : undefined ) }} />
38
+ <div class="relative flex flex-row h-full w-full items-start justify-start overflow-auto">
39
+ <div class={(frameOpened ? (frameXs ? "w-24" : "w-96") : "w-12") + " flex-shrink-0"}></div>
40
+ {windowComponent}
41
+ </div>
42
+ </div>;
43
+ }
44
+
45
+ function Loading() {
46
+ return <div class="w-full h-full flex justify-center items-center">
47
+ <div class="radial-progress animate-spin" style="--value:70;"></div>
48
+ </div>;
49
+ }
@@ -0,0 +1,102 @@
1
+ import { Handle, Stats } from "../sockdrive/js/src/sockdrive/types";
2
+ import { Drive } from "../sockdrive/js/src/sockdrive/drive";
3
+
4
+ interface Template {
5
+ name: string,
6
+ size: number,
7
+ heads: number,
8
+ cylinders: number,
9
+ sectors: number,
10
+ sectorSize: number,
11
+ }
12
+
13
+ export interface ReadResponse {
14
+ buffer?: Uint8Array,
15
+ code: number,
16
+ }
17
+
18
+ export function createSockdrive(
19
+ onOpen: (drive: string, read: boolean, write: boolean) => void,
20
+ onError: (e: Error) => void) {
21
+ let seq = 0;
22
+ const mapping: { [handle: Handle]: Drive } = {};
23
+ const templates: { [handle: number]: Template } = {};
24
+ const memory: { [handle: number]: Uint8Array } = {};
25
+ const stats: Stats = {
26
+ read: 0,
27
+ write: 0,
28
+ readTotalTime: 0,
29
+ cacheHit: 0,
30
+ cacheMiss: 0,
31
+ cacheUsed: 0,
32
+ };
33
+ const sockdrive = {
34
+ stats,
35
+ open: async (url: string, owner: string, name: string, token: string, aheadRange: number): Promise<Handle> => {
36
+ const response = await fetch(url.replace("wss://", "https://")
37
+ .replace("ws://", "http://") + "/template/" + owner + "/" + name);
38
+ const template = await response.json();
39
+ if (template.error) {
40
+ throw new Error(template.error);
41
+ }
42
+ seq++;
43
+ templates[seq] = {
44
+ name: template.name,
45
+ size: template.size ?? 0,
46
+ heads: template.heads ?? 1,
47
+ cylinders: template.cylinders ?? 520,
48
+ sectors: template.sectors ?? 63,
49
+ sectorSize: template.sector_size ?? 512,
50
+ };
51
+
52
+ const sectorSize = templates[seq].sectorSize;
53
+ memory[seq] = new Uint8Array(sectorSize + sectorSize * aheadRange);
54
+ mapping[seq] = new Drive(url, owner, name, token, stats,
55
+ { HEAPU8: memory[seq] }, sectorSize, aheadRange, sectorSize, 1); // cache is on server
56
+ mapping[seq].onOpen((read, write) => {
57
+ onOpen(owner + "/" + name, read, write);
58
+ });
59
+ mapping[seq].onError(onError);
60
+ return seq;
61
+ },
62
+ read: (handle: Handle, sector: number): Promise<ReadResponse> => {
63
+ if (mapping[handle]) {
64
+ return (mapping[handle].read(sector, 0, false) as Promise<number>).then((code) => {
65
+ return {
66
+ code,
67
+ buffer: memory[handle].slice(templates[seq].sectorSize),
68
+ };
69
+ });
70
+ }
71
+
72
+ console.error("ERROR! sockdrive handle", handle, "not found");
73
+ return Promise.resolve({ code: 1 });
74
+ },
75
+ write: (handle: Handle, sector: number, buffer: Uint8Array): number => {
76
+ if (buffer.byteLength != templates[handle].sectorSize) {
77
+ onError(new Error("sockdrive write buffer size " + buffer.byteLength +
78
+ " != sector size " + templates[handle].sectorSize));
79
+ return 1;
80
+ }
81
+ if (mapping[handle]) {
82
+ memory[handle].set(buffer, 0);
83
+ return mapping[handle].write(sector, 0);
84
+ }
85
+ console.error("ERROR! sockdrive handle", handle, "not found");
86
+ return 1;
87
+ },
88
+ close: (handle: Handle) => {
89
+ if (mapping[handle]) {
90
+ mapping[handle].close();
91
+ delete memory[handle];
92
+ delete templates[handle];
93
+ delete mapping[handle];
94
+ }
95
+ },
96
+ template: (handle: Handle) => {
97
+ return templates[handle];
98
+ },
99
+ };
100
+
101
+ return sockdrive;
102
+ }