-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinit.js
More file actions
200 lines (185 loc) · 7.31 KB
/
init.js
File metadata and controls
200 lines (185 loc) · 7.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// gVar -> my prof is malding rn.
var patchMapping = {};
//load the worker (which loads the wasm + js wrapper)
function init() {
var status = document.querySelector("#status");
var output = document.getElementById("output");
var worker = new Worker("worker.js");
// inform the user that the worker is not ready. loading from network.
patchButtonState("Loading...", true);
worker.onmessage = function (e) {
switch (e.data.type) {
case "wasmProgress":
//console.log(`Wasm loading progress: ${e.data.progress}%`);
const progressBar = document.getElementById("wasm-progress-bar");
const progressText = document.getElementById("wasm-progress-text");
if (progressBar && progressText) {
progressBar.style.width = e.data.progress + "%";
progressText.textContent = `Loading Wasm: ${e.data.progress}%`;
// Hide the progress elements when complete
if (e.data.progress >= 100) {
progressBar.parentElement.style.display = "none";
progressText.style.display = "none";
document.querySelectorAll('#wasm-progress-container').forEach(el => {
el.style.display = 'none';
});
}
}
break;
case "ready":
// enable the patch button
patchButtonState("Patch", false);
break;
case "error":
patchButtonState("Patch", false);
status.innerHTML = e.data.text;
break;
case "stdout":
case "stderr":
output.innerHTML += e.data.text + "\n";
break;
case "complete":
// when we are done, extract the output file from the worker, save it to the user's machine
// then reset the system for the next patch by enabling the patch button.
var blob = new Blob([e.data.data], {
type: "application/octet-stream",
});
// get file name from the input rom file name and add _patched to the filename before extension
var inputRomName = document.getElementById("input-rom").files[0].name;
var outputRomName = inputRomName.replace(/(\.[^/.]+)+$/, "_patched$1");
var url = URL.createObjectURL(blob);
var a = document.createElement("a");
a.href = url;
a.download = outputRomName;
a.click();
URL.revokeObjectURL(url);
patchButtonState("Patch", false);
break;
}
};
// pull the patches json.
// if you dont like this, feel free to block this request with your browser, and you can supply ur own patches.
// but as you can see later, it will always show the contents of the pending patches giving the user a chance
// to audit it as they see fit. A second however: the json is injected as raw html, so a script could be loaded
// to hide this behaviour.
fetch("patches.json")
.then((response) => response.json())
.then((data) => {
buildPatches(data.patches);
refreshPatches();
})
.catch((error) => {
console.error("Error loading patches:", error);
status.innerHTML = "Error loading patches.json";
});
// EVENT LISTENERS
// patch button listener
document.getElementById("run-patch").addEventListener("click", function () {
output.innerText = "";
status.innerText = "";
var inputRom = document.getElementById("input-rom").files[0];
var patchesTxt = document.getElementById("patches-pending").innerText;
if (inputRom) { //do we have a rom to patch? yes: patch it
var reader = new FileReader();
reader.onload = function (e) {
var inputRomArray = new Uint8Array(e.target.result);
patchButtonState("Patching...", true);
worker.postMessage({
type: "runPatch",
inputRomArray: inputRomArray,
patchesTxt: patchesTxt,
});
};
reader.readAsArrayBuffer(inputRom);
} else { // no: throw error
alert("Please select an INPUT.ROM file.");
}
});
// patch checkbox listener -> update patches
document
.getElementById("patches-selector")
.addEventListener("click", function (e) {
if (e.target.type === "checkbox") {
refreshPatches();
}
});
// when custom patches textbox is updated, refresh the patches.
document
.querySelector('textarea[data-patchname="custom"]')
.addEventListener("input", function () {
//console.log("Custom patches updated");
refreshPatches();
});
// drag file listener
document
.addEventListener("dragover", (e) => {
e.preventDefault();
if (e.dataTransfer.types.includes("Files")) {
document.body.style.backgroundColor = "#0827F5"; // bluescreen color
}
});
// watch for leaving the drop zone
document
.addEventListener("dragleave", (e) => {
document.body.style.backgroundColor = ""; // reset to original color
});
// dropped file listener
document
.addEventListener("drop", (e) => {
e.preventDefault();
document.body.style.backgroundColor = "";
document.getElementById("input-rom").files = e.dataTransfer.files;
});
}
function refreshPatches() {
//console.log("Refreshing patches...");
patchMapping["custom"] = document.querySelector(
'textarea[data-patchname="custom"]',
).value;
var patchesTxt = "";
//loop through all the checkboxes with data-patchname, and add the corresponding patch to patchesTxt
//patch name is a variable that is the same as the data-patchname attribute
//so we can use it to avoid hardcoding the patch names in our loop.
var checkboxes = document.querySelectorAll('input[type="checkbox"]');
for (var i = 0; i < checkboxes.length; i++) {
var patchName = checkboxes[i].getAttribute("data-patchname");
if (checkboxes[i].checked && patchMapping[patchName]) {
patchesTxt += patchMapping[patchName];
}
}
document.getElementById("patches-pending").innerText = patchesTxt;
// Show/hide custom patches textarea based on the custom checkbox
const customCheckbox = document.querySelector(
'input[type="checkbox"][data-patchname="custom"]',
);
const customTextarea = document.querySelector(
'textarea[data-patchname="custom"]',
);
if (customTextarea) {
customTextarea.style.display = customCheckbox.checked ? "block" : "none";
}
}
// given some fetched patches, build the UI elements for each patch
function buildPatches(patches) {
const container = document.getElementById("preset-patches");
patches.forEach((patch) => {
const patchDiv = document.createElement("div");
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.setAttribute("data-patchname", patch.name);
patchDiv.appendChild(checkbox);
const nameElement = document.createElement("strong"); //bolded title
nameElement.textContent = patch.name + ": "; //separated by a colon
patchDiv.appendChild(nameElement);
const description = document.createElement("span");
description.innerHTML = patch.description; //followed by the raw (from json) patch description
patchDiv.appendChild(description);
patchDiv.appendChild(document.createElement("br"));
container.appendChild(patchDiv);
patchMapping[patch.name] = patch.content;
});
}
function patchButtonState(string, disabled) {
document.getElementById("run-patch").disabled = disabled;
document.getElementById("run-patch").innerText = string;
}