161 lines
10 KiB
JavaScript
161 lines
10 KiB
JavaScript
// 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);
|
||
}
|
||
}
|
||
}
|