// exFAT filesystem implementation import { BaseFilesystem } from './base.js'; import { checkDependencies, updateResultItem } from '../utils.js'; export class ExFATFilesystem extends BaseFilesystem { constructor() { super('exFAT', [ { id: 'exfat', name: 'exFAT', constants: [ { id: 'partitionStartExfat', label: 'Partitions-Start (GPT-Offset)', unit: 'Bytes', default: '0x100000' }, { id: 'sectorSizeExfat', label: 'Sektor-Größe 2^n (Offset 0x6C)', unit: 'Bytes (2^n)', default: '0x09' }, { id: 'clusterSizeSectorsExfat', label: 'Cluster-Größe 2^n (Offset 0x6D)', unit: 'Sektoren (2^n)', default: '0x03' }, { id: 'numFATsExfat', label: 'Anzahl FATs (Offset 0x6E)', unit: 'Anzahl', default: '0x01' } ], inputs: [ { id: 'fatPositionExfat', label: 'FAT-Position (Offset 0x50)', unit: 'Sektoren', placeholder: '0x80' }, { id: 'dataAreaPositionExfat', label: 'Daten-Bereich Position (Offset 0x58)', unit: 'Sektoren', placeholder: '0x90' }, { id: 'rootDirClusterExfat', label: 'Wurzelverzeichnis Cluster (Offset 0x60)', unit: 'Cluster', placeholder: '0x05' }, { id: 'clusterNumberExfat', label: 'Cluster-Nummer', unit: 'Nummer', placeholder: '0x05' } ], resultGroups: [ { name: 'Boot-Struktur', results: [ { id: 'backupBootExfat', label: 'Backup Boot-Sektor', dependencies: ['partitionStartExfat'], formula: 'Partitions-Start + (12 Sektoren × Sektorgröße)' } ] }, { name: 'FAT-Bereich', results: [ { id: 'fatStartExfat', label: 'FAT-Bereich Anfang', dependencies: ['partitionStartExfat', 'fatPositionExfat', 'sectorSizeExfat'], formula: 'Partitions-Start + (FAT-Position × Sektorgröße)' }, { id: 'dataAreaStartExfat', label: 'Daten-Bereich Anfang', dependencies: ['partitionStartExfat', 'dataAreaPositionExfat', 'sectorSizeExfat'], formula: 'Partitions-Start + (Daten-Position × Sektorgröße)' } ] }, { name: 'Cluster-Berechnungen', results: [ { id: 'clusterSizeBytesExfat', label: 'Cluster-Größe (Bytes)', dependencies: ['sectorSizeExfat', 'clusterSizeSectorsExfat'], formula: '2^Sektor-Größe × 2^Cluster-Größe' }, { id: 'rootDirOffsetExfat', label: 'Wurzelverzeichnis Offset', dependencies: ['partitionStartExfat', 'dataAreaPositionExfat', 'sectorSizeExfat', 'clusterSizeSectorsExfat', 'rootDirClusterExfat'], formula: 'Daten-Bereich + (Cluster - 2) × Cluster-Größe' }, { id: 'clusterOffsetExfat', label: 'Spezifischer Cluster Offset', dependencies: ['partitionStartExfat', 'dataAreaPositionExfat', 'sectorSizeExfat', 'clusterSizeSectorsExfat', 'clusterNumberExfat'], formula: 'Daten-Bereich + (Cluster - 2) × Cluster-Größe' }, { id: 'fatEntryOffsetExfat', label: 'FAT-Eintrag Offset', dependencies: ['partitionStartExfat', 'fatPositionExfat', 'sectorSizeExfat', 'clusterNumberExfat'], formula: 'FAT-Start + (Cluster × 4)' } ] } ], formulas: [ { name: 'Backup Boot-Sektor', expression: 'Partitions-Start + (12 × Sektorgröße)' }, { name: 'Sektorgröße (Bytes)', expression: '2^n (aus Offset 0x6C)' }, { name: 'Cluster-Größe (Sektoren)', expression: '2^n (aus Offset 0x6D)' }, { name: 'Cluster-Größe (Bytes)', expression: 'Sektorgröße × Cluster-Größe (Sektoren)' }, { name: 'FAT-Bereich Anfang', expression: 'Partitions-Start + (FAT-Position × Sektorgröße)' }, { name: 'Daten-Bereich Anfang', expression: 'Partitions-Start + (Daten-Position × Sektorgröße)' }, { name: 'Wurzelverzeichnis Offset', expression: 'Daten-Bereich + ((Cluster - 2) × Cluster-Größe)' }, { name: 'Cluster Offset', expression: 'Daten-Bereich + ((Cluster-Nummer - 2) × Cluster-Größe)' }, { name: 'FAT-Eintrag Offset', expression: 'FAT-Start + (Cluster-Nummer × 4)' } ] } ]); } calculate(variantId) { if (variantId !== 'exfat') return; const values = {}; const results = {}; // Gather all input values for this variant const variant = this.variants.find(v => v.id === variantId); if (!variant) return; [...variant.constants, ...variant.inputs].forEach(field => { const el = document.getElementById(field.id); if (el) { let val = el.value; if (val && val.startsWith('0x')) { values[field.id] = parseInt(val, 16); } else { values[field.id] = Number(val) || 0; } } }); this.calculateExFAT(values, results); } calculateExFAT(values, results) { // Calculate actual sector size from 2^n const sectorSize = Math.pow(2, values.sectorSizeExfat); // Calculate actual cluster size in sectors from 2^n const clusterSizeSectors = Math.pow(2, values.clusterSizeSectorsExfat); // Backup Boot-Sektor (12 sectors after partition start) if (checkDependencies(['partitionStartExfat'])) { results.backupBoot = values.partitionStartExfat + (12 * sectorSize); updateResultItem('backupBootExfat', results.backupBoot, true, `0x${values.partitionStartExfat.toString(16)} + (12 × 0x${sectorSize.toString(16)}) = 0x${results.backupBoot.toString(16)}`); } else { updateResultItem('backupBootExfat', 0, false); } // FAT-Bereich Anfang if (checkDependencies(['partitionStartExfat', 'fatPositionExfat', 'sectorSizeExfat'])) { results.fatStart = values.partitionStartExfat + (values.fatPositionExfat * sectorSize); results.fatStartSector = values.fatPositionExfat; updateResultItem('fatStartExfat', { bytes: results.fatStart, sectors: results.fatStartSector }, true, `0x${values.partitionStartExfat.toString(16)} + (0x${values.fatPositionExfat.toString(16)} × 0x${sectorSize.toString(16)}) = 0x${results.fatStart.toString(16)}`); } else { updateResultItem('fatStartExfat', 0, false); } // Daten-Bereich Anfang if (checkDependencies(['partitionStartExfat', 'dataAreaPositionExfat', 'sectorSizeExfat'])) { results.dataAreaStart = values.partitionStartExfat + (values.dataAreaPositionExfat * sectorSize); results.dataAreaStartSector = values.dataAreaPositionExfat; updateResultItem('dataAreaStartExfat', { bytes: results.dataAreaStart, sectors: results.dataAreaStartSector }, true, `0x${values.partitionStartExfat.toString(16)} + (0x${values.dataAreaPositionExfat.toString(16)} × 0x${sectorSize.toString(16)}) = 0x${results.dataAreaStart.toString(16)}`); } else { updateResultItem('dataAreaStartExfat', 0, false); } // Cluster-Größe in Bytes if (checkDependencies(['sectorSizeExfat', 'clusterSizeSectorsExfat'])) { results.clusterSizeBytes = sectorSize * clusterSizeSectors; updateResultItem('clusterSizeBytesExfat', results.clusterSizeBytes, true, `2^${values.sectorSizeExfat} × 2^${values.clusterSizeSectorsExfat} = 0x${sectorSize.toString(16)} × 0x${clusterSizeSectors.toString(16)} = 0x${results.clusterSizeBytes.toString(16)}`); } else { updateResultItem('clusterSizeBytesExfat', 0, false); } // Wurzelverzeichnis Offset (beachtet Index-Verschiebung von -2) if (checkDependencies(['partitionStartExfat', 'dataAreaPositionExfat', 'sectorSizeExfat', 'clusterSizeSectorsExfat', 'rootDirClusterExfat']) && results.dataAreaStart !== undefined && results.clusterSizeBytes !== undefined) { results.rootDirOffset = results.dataAreaStart + ((values.rootDirClusterExfat - 2) * results.clusterSizeBytes); updateResultItem('rootDirOffsetExfat', results.rootDirOffset, true, `0x${results.dataAreaStart.toString(16)} + ((0x${values.rootDirClusterExfat.toString(16)} - 0x2) × 0x${results.clusterSizeBytes.toString(16)}) = 0x${results.rootDirOffset.toString(16)}`); } else { updateResultItem('rootDirOffsetExfat', 0, false); } // Spezifischer Cluster Offset (beachtet Index-Verschiebung von -2) if (checkDependencies(['partitionStartExfat', 'dataAreaPositionExfat', 'sectorSizeExfat', 'clusterSizeSectorsExfat', 'clusterNumberExfat']) && results.dataAreaStart !== undefined && results.clusterSizeBytes !== undefined) { results.clusterOffset = results.dataAreaStart + ((values.clusterNumberExfat - 2) * results.clusterSizeBytes); updateResultItem('clusterOffsetExfat', results.clusterOffset, true, `0x${results.dataAreaStart.toString(16)} + ((0x${values.clusterNumberExfat.toString(16)} - 0x2) × 0x${results.clusterSizeBytes.toString(16)}) = 0x${results.clusterOffset.toString(16)}`); } else { updateResultItem('clusterOffsetExfat', 0, false); } // FAT-Eintrag Offset (32-Bit = 4 Bytes pro Eintrag) if (checkDependencies(['partitionStartExfat', 'fatPositionExfat', 'sectorSizeExfat', 'clusterNumberExfat']) && results.fatStart !== undefined) { const entrySize = 4; // exFAT uses 4 bytes (32-bit) per FAT entry results.fatEntryOffset = results.fatStart + (values.clusterNumberExfat * entrySize); updateResultItem('fatEntryOffsetExfat', results.fatEntryOffset, true, `0x${results.fatStart.toString(16)} + (0x${values.clusterNumberExfat.toString(16)} × ${entrySize}) = 0x${results.fatEntryOffset.toString(16)}`); } else { updateResultItem('fatEntryOffsetExfat', 0, false); } } }