Files
fscalc/webroot/js/filesystems/exfat.js

181 lines
11 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 = this.getInputValues(variantId);
// Validate input ranges
const validation = this.validateFilesystemSpecific(variantId, values);
if (!validation.valid) {
console.warn('exFAT validation errors:', validation.errors);
}
const results = {};
this.calculateExFAT(values, results);
}
validateFilesystemSpecific(variantId, values) {
if (variantId !== 'exfat') return { valid: true, errors: [] };
const errors = [];
// Validate cluster number is reasonable
if (values.clusterNumberExfat !== undefined && values.clusterNumberExfat < 2) {
errors.push('clusterNumberExfat: Cluster-Nummer muss >= 2 sein');
}
// Validate sector and cluster size exponents
if (values.sectorSizeExfat !== undefined) {
if (values.sectorSizeExfat < 0 || values.sectorSizeExfat > 15) {
errors.push('sectorSizeExfat: Exponent muss zwischen 0 und 15 liegen');
}
}
if (values.clusterSizeSectorsExfat !== undefined) {
if (values.clusterSizeSectorsExfat < 0 || values.clusterSizeSectorsExfat > 25) {
errors.push('clusterSizeSectorsExfat: Exponent muss zwischen 0 und 25 liegen');
}
}
return {
valid: errors.length === 0,
errors: errors
};
}
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);
}
}
}