diff --git a/SPEC.md b/SPEC.md index 1580e3e..ba0da60 100644 --- a/SPEC.md +++ b/SPEC.md @@ -560,7 +560,7 @@ All of them warn, and none of them blocks the button: - there is not a single `.exe` in the archive (the select is empty, there is nothing to build) - the UTF-8 bit 11 is not set **and** there are non-ASCII names → Cyrillic names will arrive garbled, suggest 7-Zip -- the size is > 4 GB → a reminder about exFAT +- the size is 4 GiB or more → the installer does not fit on FAT32, which caps a file a byte short of 4 GiB and is how USB sticks and memory cards up to 32 GB usually come; exFAT has no such limit - `navigator.storage.estimate()` < the size of the archive → there will not be enough space (Chrome writes into a temporary swap file and swaps it in at the end). Only where the picker is: Firefox 157 reports the quota of the site instead, 7.4 GiB on a disk with 51 GB free - the chosen exe got a negative weight under 7.2.1 (the person picked by hand what we ranked down): warn, but do not stand in the way - the artwork is not filled in diff --git a/packages/frontend/app/services/checks/checks.spec.ts b/packages/frontend/app/services/checks/checks.spec.ts index 01e60e6..1af86c6 100644 --- a/packages/frontend/app/services/checks/checks.spec.ts +++ b/packages/frontend/app/services/checks/checks.spec.ts @@ -40,13 +40,23 @@ describe('services', () => { expect(messages.join(' ')).toContain('7-Zip') }) - it('should mention exFAT past 4 GiB', () => { + it('should warn about FAT32 once the archive reaches 4 GiB, and point to exFAT', () => { const messages = preflight({ ...CLEAN, - archiveSize: 5 * 1024 ** 3 + archiveSize: 4 * 1024 ** 3 }).map(check => check.message) - expect(messages.join(' ')).toContain('exFAT') + expect(messages.join(' ')).toContain('too big for a drive formatted as FAT32') + expect(messages.join(' ')).toContain('Use one formatted as exFAT') + }) + + it('should leave an archive of 4 GB in decimal units alone: FAT32 holds it', () => { + const messages = preflight({ + ...CLEAN, + archiveSize: 4_000_000_000 + }).map(check => check.message) + + expect(messages.join(' ')).not.toContain('FAT32') }) it('should warn when the browser has less free space than the archive', () => { diff --git a/packages/frontend/app/services/checks/checks.ts b/packages/frontend/app/services/checks/checks.ts index 042f051..6b7661d 100644 --- a/packages/frontend/app/services/checks/checks.ts +++ b/packages/frontend/app/services/checks/checks.ts @@ -4,8 +4,13 @@ import type { CheckInput } from './checks.types.ts' -/** 4 GiB: a single file past this does not fit on a default-formatted exFAT card. */ -const EXFAT_FILE_LIMIT = 4294967296 +/** + * The largest file FAT32 holds, a byte short of 4 GiB. exFAT has no such limit, + * but USB sticks and memory cards up to 32 GB usually come formatted as FAT32. + * The check goes by the archive, which the installer outgrows only by its + * header and meta: an archive that close to the limit is the one case it misses. + */ +const FAT32_FILE_LIMIT = 4294967295 /** * Everything here warns and nothing blocks (6.3). Games are strange, and a @@ -32,11 +37,12 @@ export function preflight(input: CheckInput): Check[] { }) } - if (input.archiveSize > EXFAT_FILE_LIMIT) { + if (input.archiveSize > FAT32_FILE_LIMIT) { checks.push({ level: 'note', - message: `The archive is ${formatSize(input.archiveSize)}. A single file over 4 GB does not fit ` - + 'on an exFAT card formatted with the default settings - check the card before copying.' + message: `The archive is ${formatSize(input.archiveSize)}, so the installer will be a little bigger: ` + + 'too big for a drive formatted as FAT32, as USB sticks and memory cards up to 32 GB usually are. ' + + 'Use one formatted as exFAT, or copy the file over the network.' }) }