/** * 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'); }