56 lines
1.8 KiB
JavaScript
56 lines
1.8 KiB
JavaScript
/**
|
|
* Write a firmware file directly to a removable mass-storage drive
|
|
* selected by the user (e.g. MICROBIT or RPI-RP2).
|
|
*/
|
|
export async function flashFileToDrive(onLog, onProgress, options = {}) {
|
|
const { firmwareUrl, outputName = 'firmware.bin', driveHint = 'device drive' } = options;
|
|
if (!firmwareUrl) throw new Error('No firmware URL provided');
|
|
|
|
onLog?.('Fetching firmware...\n');
|
|
const resp = await fetch(firmwareUrl);
|
|
if (!resp.ok) throw new Error(`Firmware fetch failed: ${resp.status}`);
|
|
const fileData = new Uint8Array(await resp.arrayBuffer());
|
|
onLog?.(`Firmware: ${(fileData.length / 1024).toFixed(0)} KB\n\n`);
|
|
|
|
onLog?.(`Select the ${driveHint} in the folder picker.\n\n`);
|
|
|
|
let dirHandle;
|
|
try {
|
|
dirHandle = await window.showDirectoryPicker({ mode: 'readwrite' });
|
|
} catch (err) {
|
|
if (err?.name === 'AbortError') throw new Error('Cancelled - no folder selected');
|
|
throw err;
|
|
}
|
|
|
|
onLog?.(`Writing ${outputName} to ${dirHandle.name}...\n`);
|
|
const fileHandle = await dirHandle.getFileHandle(outputName, { create: true });
|
|
const writable = await fileHandle.createWritable();
|
|
|
|
const CHUNK_SIZE = 64 * 1024;
|
|
let written = 0;
|
|
try {
|
|
while (written < fileData.length) {
|
|
const end = Math.min(written + CHUNK_SIZE, fileData.length);
|
|
await writable.write({
|
|
type: 'write',
|
|
position: written,
|
|
data: fileData.slice(written, end),
|
|
});
|
|
written = end;
|
|
onProgress?.(Math.round((written / fileData.length) * 95));
|
|
}
|
|
await writable.close();
|
|
} catch (err) {
|
|
// Some bootloaders unmount as soon as the file is fully written.
|
|
if (written >= fileData.length) {
|
|
onLog?.('(Drive disconnected - device is rebooting with new firmware)\n');
|
|
} else {
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
onProgress?.(100);
|
|
onLog?.('Done!\n');
|
|
}
|
|
|