fscalc/index.html
overcuriousity 315440e1c1 improve UX
2025-09-28 22:50:19 +02:00

1660 lines
78 KiB
HTML
Raw 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.

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dateisystem-Offset-Rechner</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Consolas', 'Courier New', monospace;
background-color: #1a1a1a;
color: #e0e0e0;
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Header */
.header {
background-color: #2a2a2a;
border-bottom: 2px solid #3a3a3a;
padding: 15px 0;
}
.header-content {
max-width: 1400px;
margin: 0 auto;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
.logo {
font-size: 1.5em;
font-weight: bold;
color: #ffffff;
}
.nav {
display: flex;
gap: 20px;
}
.nav a {
color: #cccccc;
text-decoration: none;
transition: color 0.2s;
}
.nav a:hover {
color: #ffffff;
}
/* Main Content */
.main-content {
flex: 1;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
h1 {
text-align: center;
margin-bottom: 30px;
color: #ffffff;
font-size: 2.2em;
font-weight: normal;
}
/* Tabs */
.tabs {
display: flex;
background-color: #2a2a2a;
border-radius: 6px 6px 0 0;
border: 1px solid #3a3a3a;
border-bottom: none;
overflow-x: auto;
}
.tab {
padding: 15px 25px;
cursor: pointer;
background-color: #2a2a2a;
border-right: 1px solid #3a3a3a;
color: #cccccc;
transition: all 0.2s;
white-space: nowrap;
min-width: 120px;
text-align: center;
}
.tab:last-child {
border-right: none;
}
.tab:hover {
background-color: #353535;
}
.tab.active {
background-color: #3a3a3a;
color: #ffffff;
border-bottom: 2px solid #666666;
}
.tab-content {
display: none;
background-color: #2a2a2a;
border: 1px solid #3a3a3a;
border-radius: 0 0 6px 6px;
padding: 20px;
}
.tab-content.active {
display: block;
}
/* Constants and Calculator Layout */
.constants-calculator-container {
display: grid;
grid-template-columns: 1fr 350px;
gap: 20px;
margin-bottom: 20px;
}
@media (max-width: 1024px) {
.constants-calculator-container {
grid-template-columns: 1fr;
gap: 15px;
}
}
/* Simplified Calculator Styles */
.hex-calculator {
background-color: #1a1a1a;
min-height: fit-content;
}
.calculator-container {
display: flex;
flex-direction: column;
gap: 12px;
}
.calc-display {
background-color: #111111;
border: 1px solid #404040;
border-radius: 6px;
padding: 12px;
}
.calc-input input {
width: 100%;
background-color: #2a2a2a;
border: 1px solid #555555;
border-radius: 4px;
color: #ffffff;
font-family: 'Consolas', monospace;
font-size: 16px;
font-weight: bold;
padding: 8px;
text-align: right;
margin-bottom: 8px;
}
.calc-conversions {
display: flex;
flex-direction: column;
gap: 4px;
}
.conversion-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 3px 0;
border-bottom: 1px solid #333333;
}
.conversion-row:last-child {
border-bottom: none;
}
.conv-label {
color: #888888;
font-weight: bold;
font-size: 0.8em;
min-width: 30px;
}
.conv-value {
color: #cccccc;
font-family: 'Consolas', monospace;
font-size: 0.8em;
word-break: break-all;
text-align: right;
flex: 1;
margin-left: 10px;
}
.calc-buttons {
display: flex;
flex-direction: column;
gap: 4px;
}
.calc-row {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 4px;
}
.calc-btn {
background-color: #2a2a2a;
border: 1px solid #4a4a4a;
border-radius: 3px;
color: #ffffff;
cursor: pointer;
font-family: 'Consolas', monospace;
font-size: 0.8em;
font-weight: bold;
min-height: 32px;
padding: 4px;
text-align: center;
transition: all 0.2s;
user-select: none;
}
.calc-btn:hover {
background-color: #3a3a3a;
border-color: #666666;
}
.calc-btn:active {
background-color: #444444;
transform: translateY(1px);
}
.calc-btn.number {
background-color: #333333;
}
.calc-btn.number:hover {
background-color: #444444;
}
.calc-btn.operation {
background-color: #2a4a2a;
color: #88cc88;
}
.calc-btn.operation:hover {
background-color: #3a5a3a;
}
.calc-btn.clear {
background-color: #4a2a4a;
color: #cc88cc;
}
.calc-btn.clear:hover {
background-color: #5a3a5a;
}
.calc-btn.equals {
background-color: #2a2a4a;
color: #8888cc;
}
.calc-btn.equals:hover {
background-color: #3a3a5a;
}
.calc-btn.backspace {
background-color: #4a4a2a;
color: #cccc88;
}
.calc-btn.backspace:hover {
background-color: #5a5a3a;
}
.calc-btn.wide {
grid-column: span 2;
}
/* Sections */
.section {
background-color: #252525;
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
border: 1px solid #3a3a3a;
}
.section h2 {
color: #cccccc;
margin-bottom: 15px;
border-bottom: 1px solid #3a3a3a;
padding-bottom: 8px;
font-weight: normal;
font-size: 1.3em;
}
.constants {
background-color: #202020;
}
.results {
background-color: #202020;
}
.formulas {
background-color: #1a1a1a;
}
.formula-box {
background-color: #151515;
border: 1px solid #404040;
border-radius: 4px;
padding: 15px;
margin: 10px 0;
font-family: 'Consolas', monospace;
font-size: 0.9em;
}
.formula {
margin: 8px 0;
color: #d0d0d0;
word-wrap: break-word;
}
.calculated {
color: #ffffff;
font-weight: bold;
}
.input-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 15px;
margin: 20px 0;
}
@media (max-width: 768px) {
.input-grid {
grid-template-columns: 1fr;
gap: 12px;
}
}
.input-group {
display: flex;
flex-direction: column;
position: relative;
}
.input-group label {
margin-bottom: 6px;
color: #cccccc;
font-weight: normal;
font-size: 0.95em;
}
.unit-indicator {
color: #88cc88;
font-weight: bold;
font-size: 0.85em;
}
.input-group input {
padding: 12px;
background-color: #333333;
border: 1px solid #4a4a4a;
border-radius: 4px;
color: #ffffff;
font-family: 'Consolas', monospace;
font-size: 14px;
transition: border-color 0.2s;
}
.input-group input:focus {
outline: none;
border-color: #666666;
background-color: #383838;
}
.input-group input.error {
border-color: #ff6666;
background-color: #332222;
}
.error-message {
color: #ff8888;
font-size: 0.8em;
margin-top: 4px;
font-style: italic;
}
.result-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #3a3a3a;
position: relative;
}
.result-item:last-child {
border-bottom: none;
}
.result-item.available {
opacity: 1;
}
.result-item.unavailable {
opacity: 0.5;
}
.result-label {
color: #cccccc;
font-weight: normal;
flex: 1;
cursor: help;
}
.result-value-container {
display: flex;
align-items: center;
gap: 8px;
}
.result-value {
color: #ffffff;
font-family: 'Consolas', monospace;
text-align: right;
font-weight: bold;
min-width: 120px;
cursor: help;
}
.copy-btn {
background: none;
border: none;
color: #888888;
cursor: pointer;
padding: 4px;
border-radius: 3px;
transition: all 0.2s;
font-size: 14px;
}
.copy-btn:hover {
color: #ffffff;
background-color: #404040;
}
.copy-btn:active {
color: #88cc88;
}
/* Tooltip */
.tooltip {
position: absolute;
background-color: #1a1a1a;
color: #ffffff;
padding: 8px 12px;
border-radius: 4px;
font-size: 0.85em;
border: 1px solid #555555;
z-index: 1000;
max-width: 400px;
word-wrap: break-word;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
font-family: 'Consolas', monospace;
line-height: 1.4;
}
.error {
color: #ff8888;
font-style: italic;
}
/* Footer */
.footer {
background-color: #2a2a2a;
border-top: 1px solid #3a3a3a;
padding: 20px 0;
margin-top: 40px;
}
.footer-content {
max-width: 1400px;
margin: 0 auto;
padding: 0 20px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 30px;
}
.footer-section h3 {
color: #ffffff;
margin-bottom: 10px;
font-size: 1.1em;
}
.footer-section p,
.footer-section a {
color: #cccccc;
font-size: 0.9em;
text-decoration: none;
margin-bottom: 5px;
display: block;
}
.footer-section a:hover {
color: #ffffff;
}
.footer-bottom {
text-align: center;
padding-top: 20px;
margin-top: 20px;
border-top: 1px solid #3a3a3a;
color: #888888;
font-size: 0.85em;
}
/* Result Groups */
.result-group {
margin-bottom: 20px;
border: 1px solid #3a3a3a;
border-radius: 6px;
padding: 15px;
background-color: #2a2a2a;
}
.result-group h3 {
color: #ffffff;
margin-bottom: 10px;
font-size: 1.1em;
border-bottom: 1px solid #3a3a3a;
padding-bottom: 5px;
}
@media (max-width: 768px) {
.constants-calculator-container {
grid-template-columns: 1fr;
}
.result-item {
flex-direction: column;
align-items: flex-start;
gap: 5px;
}
.result-value {
text-align: left;
min-width: auto;
}
}
</style>
</head>
<body>
<header class="header">
<div class="header-content">
<div class="logo">Dateisystem-Offset-Rechner</div>
<nav class="nav">
<a href="#home">Start</a>
<a href="mailto:mstoeck3@hs-mittweida.de">Kontakt</a>
</nav>
</div>
</header>
<main class="main-content">
<div class="container">
<h1>Dateisystem-Offset-Rechner</h1>
<div class="tabs">
<div class="tab active" onclick="switchTab('fat1216')">FAT12/16</div>
<div class="tab" onclick="switchTab('fat32')">FAT32</div>
</div>
<!-- FAT12/16 Tab -->
<div class="tab-content active" id="fat1216">
<div class="constants-calculator-container">
<div class="section constants">
<h2>Konstanten</h2>
<div class="input-grid">
<div class="input-group">
<label for="baseOffset1216">Basis-Offset <span class="unit-indicator">(Bytes)</span>:</label>
<input type="text" id="baseOffset1216" value="0x0" placeholder="0x0">
<div class="error-message" id="baseOffset1216-error"></div>
</div>
<div class="input-group">
<label for="sectorSize1216">Sektorgröße <span class="unit-indicator">(Bytes)</span>:</label>
<input type="text" id="sectorSize1216" value="0x200" placeholder="0x200">
<div class="error-message" id="sectorSize1216-error"></div>
</div>
</div>
</div>
<div class="section hex-calculator">
<h2>Hex-Rechner</h2>
<div class="calculator-container">
<div class="calc-display">
<div class="calc-input">
<input type="text" id="calcInput" placeholder="0x0" readonly>
</div>
<div class="calc-conversions">
<div class="conversion-row">
<span class="conv-label">HEX:</span>
<span class="conv-value" id="hexValue">0x0</span>
</div>
<div class="conversion-row">
<span class="conv-label">DEC:</span>
<span class="conv-value" id="decValue">0</span>
</div>
<div class="conversion-row">
<span class="conv-label">BIN:</span>
<span class="conv-value" id="binValue">0b0</span>
</div>
</div>
</div>
<div class="calc-buttons">
<div class="calc-row">
<button class="calc-btn number" onclick="calcInput('A')">A</button>
<button class="calc-btn number" onclick="calcInput('B')">B</button>
<button class="calc-btn number" onclick="calcInput('C')">C</button>
<button class="calc-btn number" onclick="calcInput('D')">D</button>
<button class="calc-btn clear" onclick="calcClear()">C</button>
</div>
<div class="calc-row">
<button class="calc-btn number" onclick="calcInput('E')">E</button>
<button class="calc-btn number" onclick="calcInput('F')">F</button>
<button class="calc-btn number" onclick="calcInput('7')">7</button>
<button class="calc-btn number" onclick="calcInput('8')">8</button>
<button class="calc-btn operation" onclick="calcOperation('/')">/</button>
</div>
<div class="calc-row">
<button class="calc-btn number" onclick="calcInput('4')">4</button>
<button class="calc-btn number" onclick="calcInput('5')">5</button>
<button class="calc-btn number" onclick="calcInput('6')">6</button>
<button class="calc-btn operation" onclick="calcOperation('*')">*</button>
<button class="calc-btn operation" onclick="calcOperation('MOD')">MOD</button>
</div>
<div class="calc-row">
<button class="calc-btn number" onclick="calcInput('1')">1</button>
<button class="calc-btn number" onclick="calcInput('2')">2</button>
<button class="calc-btn number" onclick="calcInput('3')">3</button>
<button class="calc-btn operation" onclick="calcOperation('-')">-</button>
<button class="calc-btn operation" onclick="calcOperation('+')">+</button>
</div>
<div class="calc-row">
<button class="calc-btn number wide" onclick="calcInput('0')">0</button>
<button class="calc-btn number" onclick="calcInput('9')">9</button>
<button class="calc-btn backspace" onclick="calcBackspace()"></button>
<button class="calc-btn equals" onclick="calcEquals()">=</button>
</div>
</div>
</div>
</div>
</div>
<div class="section">
<h2>Eingabeparameter</h2>
<div class="input-grid">
<div class="input-group">
<label for="reservedSectors1216">Reservierte Sektoren <span class="unit-indicator">(Anzahl)</span>:</label>
<input type="text" id="reservedSectors1216" placeholder="0x1">
<div class="error-message" id="reservedSectors1216-error"></div>
</div>
<div class="input-group">
<label for="numFATs1216">Anzahl FATs <span class="unit-indicator">(Anzahl)</span>:</label>
<input type="text" id="numFATs1216" placeholder="0x2">
<div class="error-message" id="numFATs1216-error"></div>
</div>
<div class="input-group">
<label for="fatSizeSectors1216">FAT-Größe <span class="unit-indicator">(Sektoren)</span>:</label>
<input type="text" id="fatSizeSectors1216" placeholder="0x4000">
<div class="error-message" id="fatSizeSectors1216-error"></div>
</div>
<div class="input-group">
<label for="maxRootEntries1216">Max. Root-Einträge <span class="unit-indicator">(Anzahl)</span>:</label>
<input type="text" id="maxRootEntries1216" placeholder="0x200">
<div class="error-message" id="maxRootEntries1216-error"></div>
</div>
<div class="input-group">
<label for="partitionSizeInSectors1216">Partitionsgröße <span class="unit-indicator">(Sektoren)</span>:</label>
<input type="text" id="partitionSizeInSectors1216" placeholder="0x4015">
<div class="error-message" id="partitionSizeInSectors1216-error"></div>
</div>
<div class="input-group">
<label for="clusterSizeSectors1216">Clustergröße <span class="unit-indicator">(Sektoren)</span>:</label>
<input type="text" id="clusterSizeSectors1216" placeholder="0x2">
<div class="error-message" id="clusterSizeSectors1216-error"></div>
</div>
<div class="input-group">
<label for="clusterNumber1216">Cluster-Nummer <span class="unit-indicator">(Nummer)</span>:</label>
<input type="text" id="clusterNumber1216" placeholder="0x2">
<div class="error-message" id="clusterNumber1216-error"></div>
</div>
</div>
</div>
<div class="section results">
<h2>Berechnete Werte</h2>
<div class="result-group">
<h3>FAT-Struktur</h3>
<div class="result-item" data-deps="reservedSectors1216,sectorSize1216,baseOffset1216">
<span class="result-label" data-formula="reservedSectors × sectorSize + baseOffset">FAT-Bereich Anfang (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="fatStart1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('fatStart1216')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors1216,sectorSize1216,baseOffset1216">
<span class="result-label" data-formula="fatStart ÷ sectorSize">FAT-Bereich Anfang (Sektor):</span>
<div class="result-value-container">
<span class="result-value" id="fatStartSector1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('fatStartSector1216')">📋</button>
</div>
</div>
<div class="result-item" data-deps="numFATs1216,fatSizeSectors1216,sectorSize1216">
<span class="result-label" data-formula="numFATs × fatSizeSectors × sectorSize">FAT-Bereich Größe (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="fatSize1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('fatSize1216')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors1216,sectorSize1216,baseOffset1216,fatSizeSectors1216">
<span class="result-label" data-formula="fatStart + (fatSizeSectors × sectorSize)">FAT2 Anfang (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="fat2Start1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('fat2Start1216')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors1216,sectorSize1216,baseOffset1216,numFATs1216,fatSizeSectors1216">
<span class="result-label" data-formula="fatStart + fatSize">FAT-Bereich Ende (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="fatEnd1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('fatEnd1216')">📋</button>
</div>
</div>
</div>
<div class="result-group">
<h3>Root-Directory</h3>
<div class="result-item" data-deps="reservedSectors1216,sectorSize1216,baseOffset1216,numFATs1216,fatSizeSectors1216">
<span class="result-label" data-formula="fatEnd">Root-Directory Anfang (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="rootDirStart1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('rootDirStart1216')">📋</button>
</div>
</div>
<div class="result-item" data-deps="maxRootEntries1216">
<span class="result-label" data-formula="maxRootEntries × 0x20">Root-Directory Größe (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="rootDirSize1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('rootDirSize1216')">📋</button>
</div>
</div>
</div>
<div class="result-group">
<h3>Daten-Bereich</h3>
<div class="result-item" data-deps="reservedSectors1216,sectorSize1216,baseOffset1216,numFATs1216,fatSizeSectors1216,maxRootEntries1216">
<span class="result-label" data-formula="fatEnd + rootDirSize">Daten-Bereich Anfang (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="dataStart1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('dataStart1216')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors1216,sectorSize1216,baseOffset1216,numFATs1216,fatSizeSectors1216,maxRootEntries1216">
<span class="result-label" data-formula="dataStart ÷ sectorSize">Daten-Bereich Anfang (Sektor):</span>
<div class="result-value-container">
<span class="result-value" id="dataStartSector1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('dataStartSector1216')">📋</button>
</div>
</div>
<div class="result-item" data-deps="partitionSizeInSectors1216,sectorSize1216,reservedSectors1216,baseOffset1216,numFATs1216,fatSizeSectors1216,maxRootEntries1216">
<span class="result-label" data-formula="(partitionSizeInSectors × sectorSize) - (dataStart - baseOffset)">Daten-Bereich Größe (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="dataSize1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('dataSize1216')">📋</button>
</div>
</div>
<div class="result-item" data-deps="sectorSize1216,clusterSizeSectors1216">
<span class="result-label" data-formula="sectorSize × clusterSizeSectors">Cluster-Größe (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="clusterSize1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('clusterSize1216')">📋</button>
</div>
</div>
<div class="result-item" data-deps="partitionSizeInSectors1216,sectorSize1216,reservedSectors1216,baseOffset1216,numFATs1216,fatSizeSectors1216,maxRootEntries1216,clusterSizeSectors1216">
<span class="result-label" data-formula="dataSize ÷ clusterSize">Anzahl Cluster:</span>
<div class="result-value-container">
<span class="result-value" id="numClusters1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('numClusters1216')">📋</button>
</div>
</div>
</div>
<div class="result-group">
<h3>Spezifischer Cluster</h3>
<div class="result-item" data-deps="reservedSectors1216,sectorSize1216,baseOffset1216,numFATs1216,fatSizeSectors1216,maxRootEntries1216,clusterSizeSectors1216,clusterNumber1216">
<span class="result-label" data-formula="dataStart + (clusterNumber - 0x2) × clusterSize">Cluster Anfang (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="clusterStart1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('clusterStart1216')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors1216,sectorSize1216,baseOffset1216,numFATs1216,fatSizeSectors1216,maxRootEntries1216,clusterSizeSectors1216,clusterNumber1216">
<span class="result-label" data-formula="clusterStart ÷ sectorSize">Cluster Anfang (Sektor):</span>
<div class="result-value-container">
<span class="result-value" id="clusterStartSector1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('clusterStartSector1216')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors1216,sectorSize1216,baseOffset1216,clusterNumber1216">
<span class="result-label" data-formula="fatStart + (clusterNumber × 2) für FAT16">FAT-Eintrag Position (Bytes, nur FAT16):</span>
<div class="result-value-container">
<span class="result-value" id="fatEntryPos1216">-</span>
<button class="copy-btn" onclick="copyToClipboard('fatEntryPos1216')">📋</button>
</div>
</div>
</div>
</div>
<div class="section formulas">
<h2>Berechnungsformeln FAT12/16</h2>
<div class="formula-box">
<div class="formula"><span class="calculated">FAT-Bereich Anfang</span> = Reservierte Sektoren × Sektorgröße + Basis-Offset</div>
<div class="formula"><span class="calculated">FAT-Bereich Größe</span> = Anzahl FATs × FAT-Größe in Sektoren × Sektorgröße</div>
<div class="formula"><span class="calculated">FAT2 Anfang</span> = FAT-Bereich Anfang + FAT-Größe in Bytes</div>
<div class="formula"><span class="calculated">FAT-Bereich Ende</span> = FAT-Bereich Anfang + FAT-Bereich Größe</div>
<div class="formula"><span class="calculated">Root-Directory Größe</span> = Max. Root-Einträge × 0x20</div>
<div class="formula"><span class="calculated">Daten-Bereich Anfang</span> = FAT-Bereich Ende + Root-Directory Größe</div>
<div class="formula"><span class="calculated">Daten-Bereich Größe</span> = (Partitionsgröße × Sektorgröße) - (Daten-Bereich Anfang - Basis-Offset)</div>
<div class="formula"><span class="calculated">Cluster-Größe</span> = Sektorgröße × Cluster-Größe in Sektoren</div>
<div class="formula"><span class="calculated">Anzahl Cluster</span> = Daten-Bereich Größe ÷ Cluster-Größe</div>
<div class="formula"><span class="calculated">Cluster Anfang</span> = Daten-Bereich Anfang + (Cluster-Nummer - 0x2) × Cluster-Größe</div>
<div class="formula"><span class="calculated">FAT-Eintrag Position</span> = FAT-Bereich Anfang + (Cluster-Nummer × Eintraggröße)</div>
</div>
</div>
</div>
<!-- FAT32 Tab -->
<div class="tab-content" id="fat32">
<div class="constants-calculator-container">
<div class="section constants">
<h2>Konstanten</h2>
<div class="input-grid">
<div class="input-group">
<label for="baseOffset32">Basis-Offset <span class="unit-indicator">(Bytes)</span>:</label>
<input type="text" id="baseOffset32" value="0x0" placeholder="0x0">
<div class="error-message" id="baseOffset32-error"></div>
</div>
<div class="input-group">
<label for="sectorSize32">Sektorgröße <span class="unit-indicator">(Bytes)</span>:</label>
<input type="text" id="sectorSize32" value="0x200" placeholder="0x200">
<div class="error-message" id="sectorSize32-error"></div>
</div>
</div>
</div>
<div class="section hex-calculator">
<h2>Hex-Rechner</h2>
<div class="calculator-container">
<div class="calc-display">
<div class="calc-input">
<input type="text" id="calcInput32" placeholder="0x0" readonly>
</div>
<div class="calc-conversions">
<div class="conversion-row">
<span class="conv-label">HEX:</span>
<span class="conv-value" id="hexValue32">0x0</span>
</div>
<div class="conversion-row">
<span class="conv-label">DEC:</span>
<span class="conv-value" id="decValue32">0</span>
</div>
<div class="conversion-row">
<span class="conv-label">BIN:</span>
<span class="conv-value" id="binValue32">0b0</span>
</div>
</div>
</div>
<div class="calc-buttons">
<div class="calc-row">
<button class="calc-btn number" onclick="calcInput('A')">A</button>
<button class="calc-btn number" onclick="calcInput('B')">B</button>
<button class="calc-btn number" onclick="calcInput('C')">C</button>
<button class="calc-btn number" onclick="calcInput('D')">D</button>
<button class="calc-btn clear" onclick="calcClear()">C</button>
</div>
<div class="calc-row">
<button class="calc-btn number" onclick="calcInput('E')">E</button>
<button class="calc-btn number" onclick="calcInput('F')">F</button>
<button class="calc-btn number" onclick="calcInput('7')">7</button>
<button class="calc-btn number" onclick="calcInput('8')">8</button>
<button class="calc-btn operation" onclick="calcOperation('/')">/</button>
</div>
<div class="calc-row">
<button class="calc-btn number" onclick="calcInput('4')">4</button>
<button class="calc-btn number" onclick="calcInput('5')">5</button>
<button class="calc-btn number" onclick="calcInput('6')">6</button>
<button class="calc-btn operation" onclick="calcOperation('*')">*</button>
<button class="calc-btn operation" onclick="calcOperation('MOD')">MOD</button>
</div>
<div class="calc-row">
<button class="calc-btn number" onclick="calcInput('1')">1</button>
<button class="calc-btn number" onclick="calcInput('2')">2</button>
<button class="calc-btn number" onclick="calcInput('3')">3</button>
<button class="calc-btn operation" onclick="calcOperation('-')">-</button>
<button class="calc-btn operation" onclick="calcOperation('+')">+</button>
</div>
<div class="calc-row">
<button class="calc-btn number wide" onclick="calcInput('0')">0</button>
<button class="calc-btn number" onclick="calcInput('9')">9</button>
<button class="calc-btn backspace" onclick="calcBackspace()"></button>
<button class="calc-btn equals" onclick="calcEquals()">=</button>
</div>
</div>
</div>
</div>
</div>
<div class="section">
<h2>Eingabeparameter</h2>
<div class="input-grid">
<div class="input-group">
<label for="reservedSectors32">Reservierte Sektoren <span class="unit-indicator">(Anzahl)</span>:</label>
<input type="text" id="reservedSectors32" placeholder="0x20">
<div class="error-message" id="reservedSectors32-error"></div>
</div>
<div class="input-group">
<label for="numFATs32">Anzahl FATs <span class="unit-indicator">(Anzahl)</span>:</label>
<input type="text" id="numFATs32" placeholder="0x2">
<div class="error-message" id="numFATs32-error"></div>
</div>
<div class="input-group">
<label for="fatSizeSectors32">FAT-Größe <span class="unit-indicator">(Sektoren)</span>:</label>
<input type="text" id="fatSizeSectors32" placeholder="0x20000">
<div class="error-message" id="fatSizeSectors32-error"></div>
</div>
<div class="input-group">
<label for="rootDirCluster32">Root-Directory Cluster <span class="unit-indicator">(Nummer)</span>:</label>
<input type="text" id="rootDirCluster32" placeholder="0x2">
<div class="error-message" id="rootDirCluster32-error"></div>
</div>
<div class="input-group">
<label for="partitionSizeInSectors32">Partitionsgröße <span class="unit-indicator">(Sektoren)</span>:</label>
<input type="text" id="partitionSizeInSectors32" placeholder="0x100000">
<div class="error-message" id="partitionSizeInSectors32-error"></div>
</div>
<div class="input-group">
<label for="clusterSizeSectors32">Clustergröße <span class="unit-indicator">(Sektoren)</span>:</label>
<input type="text" id="clusterSizeSectors32" placeholder="0x8">
<div class="error-message" id="clusterSizeSectors32-error"></div>
</div>
<div class="input-group">
<label for="clusterNumber32">Cluster-Nummer <span class="unit-indicator">(Nummer)</span>:</label>
<input type="text" id="clusterNumber32" placeholder="0x2">
<div class="error-message" id="clusterNumber32-error"></div>
</div>
</div>
</div>
<div class="section results">
<h2>Berechnete Werte</h2>
<div class="result-group">
<h3>FAT-Struktur</h3>
<div class="result-item" data-deps="reservedSectors32,sectorSize32,baseOffset32">
<span class="result-label" data-formula="reservedSectors × sectorSize + baseOffset">FAT-Bereich Anfang (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="fatStart32">-</span>
<button class="copy-btn" onclick="copyToClipboard('fatStart32')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors32,sectorSize32,baseOffset32">
<span class="result-label" data-formula="fatStart ÷ sectorSize">FAT-Bereich Anfang (Sektor):</span>
<div class="result-value-container">
<span class="result-value" id="fatStartSector32">-</span>
<button class="copy-btn" onclick="copyToClipboard('fatStartSector32')">📋</button>
</div>
</div>
<div class="result-item" data-deps="numFATs32,fatSizeSectors32,sectorSize32">
<span class="result-label" data-formula="numFATs × fatSizeSectors × sectorSize">FAT-Bereich Größe (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="fatSize32">-</span>
<button class="copy-btn" onclick="copyToClipboard('fatSize32')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors32,sectorSize32,baseOffset32,fatSizeSectors32">
<span class="result-label" data-formula="fatStart + (fatSizeSectors × sectorSize)">FAT2 Anfang (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="fat2Start32">-</span>
<button class="copy-btn" onclick="copyToClipboard('fat2Start32')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors32,sectorSize32,baseOffset32,numFATs32,fatSizeSectors32">
<span class="result-label" data-formula="fatStart + fatSize">FAT-Bereich Ende (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="fatEnd32">-</span>
<button class="copy-btn" onclick="copyToClipboard('fatEnd32')">📋</button>
</div>
</div>
</div>
<div class="result-group">
<h3>Daten-Bereich</h3>
<div class="result-item" data-deps="reservedSectors32,sectorSize32,baseOffset32,numFATs32,fatSizeSectors32">
<span class="result-label" data-formula="fatEnd">Daten-Bereich Anfang (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="dataStart32">-</span>
<button class="copy-btn" onclick="copyToClipboard('dataStart32')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors32,sectorSize32,baseOffset32,numFATs32,fatSizeSectors32">
<span class="result-label" data-formula="dataStart ÷ sectorSize">Daten-Bereich Anfang (Sektor):</span>
<div class="result-value-container">
<span class="result-value" id="dataStartSector32">-</span>
<button class="copy-btn" onclick="copyToClipboard('dataStartSector32')">📋</button>
</div>
</div>
<div class="result-item" data-deps="partitionSizeInSectors32,sectorSize32,reservedSectors32,baseOffset32,numFATs32,fatSizeSectors32">
<span class="result-label" data-formula="(partitionSizeInSectors × sectorSize) - (dataStart - baseOffset)">Daten-Bereich Größe (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="dataSize32">-</span>
<button class="copy-btn" onclick="copyToClipboard('dataSize32')">📋</button>
</div>
</div>
<div class="result-item" data-deps="sectorSize32,clusterSizeSectors32">
<span class="result-label" data-formula="sectorSize × clusterSizeSectors">Cluster-Größe (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="clusterSize32">-</span>
<button class="copy-btn" onclick="copyToClipboard('clusterSize32')">📋</button>
</div>
</div>
<div class="result-item" data-deps="partitionSizeInSectors32,sectorSize32,reservedSectors32,baseOffset32,numFATs32,fatSizeSectors32,clusterSizeSectors32">
<span class="result-label" data-formula="dataSize ÷ clusterSize">Anzahl Cluster:</span>
<div class="result-value-container">
<span class="result-value" id="numClusters32">-</span>
<button class="copy-btn" onclick="copyToClipboard('numClusters32')">📋</button>
</div>
</div>
</div>
<div class="result-group">
<h3>Spezifischer Cluster</h3>
<div class="result-item" data-deps="reservedSectors32,sectorSize32,baseOffset32,numFATs32,fatSizeSectors32,clusterSizeSectors32,clusterNumber32">
<span class="result-label" data-formula="dataStart + (clusterNumber - 0x2) × clusterSize">Cluster Anfang (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="clusterStart32">-</span>
<button class="copy-btn" onclick="copyToClipboard('clusterStart32')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors32,sectorSize32,baseOffset32,numFATs32,fatSizeSectors32,clusterSizeSectors32,clusterNumber32">
<span class="result-label" data-formula="clusterStart ÷ sectorSize">Cluster Anfang (Sektor):</span>
<div class="result-value-container">
<span class="result-value" id="clusterStartSector32">-</span>
<button class="copy-btn" onclick="copyToClipboard('clusterStartSector32')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors32,sectorSize32,baseOffset32,clusterNumber32">
<span class="result-label" data-formula="fatStart + (clusterNumber × 4)">FAT-Eintrag Position (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="fatEntryPos32">-</span>
<button class="copy-btn" onclick="copyToClipboard('fatEntryPos32')">📋</button>
</div>
</div>
<div class="result-item" data-deps="reservedSectors32,sectorSize32,baseOffset32,numFATs32,fatSizeSectors32,clusterSizeSectors32,rootDirCluster32">
<span class="result-label" data-formula="dataStart + (rootDirCluster - 0x2) × clusterSize">Root-Directory Position (Bytes):</span>
<div class="result-value-container">
<span class="result-value" id="rootDirPos32">-</span>
<button class="copy-btn" onclick="copyToClipboard('rootDirPos32')">📋</button>
</div>
</div>
</div>
</div>
<div class="section formulas">
<h2>Berechnungsformeln FAT32</h2>
<div class="formula-box">
<div class="formula"><span class="calculated">FAT-Bereich Anfang</span> = Reservierte Sektoren × Sektorgröße + Basis-Offset</div>
<div class="formula"><span class="calculated">FAT-Bereich Größe</span> = Anzahl FATs × FAT-Größe in Sektoren × Sektorgröße</div>
<div class="formula"><span class="calculated">FAT2 Anfang</span> = FAT-Bereich Anfang + FAT-Größe in Bytes</div>
<div class="formula"><span class="calculated">FAT-Bereich Ende</span> = FAT-Bereich Anfang + FAT-Bereich Größe</div>
<div class="formula"><span class="calculated">Daten-Bereich Anfang</span> = FAT-Bereich Ende (kein festes Root Directory)</div>
<div class="formula"><span class="calculated">Daten-Bereich Größe</span> = (Partitionsgröße × Sektorgröße) - (Daten-Bereich Anfang - Basis-Offset)</div>
<div class="formula"><span class="calculated">Cluster-Größe</span> = Sektorgröße × Cluster-Größe in Sektoren</div>
<div class="formula"><span class="calculated">Anzahl Cluster</span> = Daten-Bereich Größe ÷ Cluster-Größe</div>
<div class="formula"><span class="calculated">Cluster Anfang</span> = Daten-Bereich Anfang + (Cluster-Nummer - 0x2) × Cluster-Größe</div>
<div class="formula"><span class="calculated">FAT-Eintrag Position</span> = FAT-Bereich Anfang + (Cluster-Nummer × 4)</div>
<div class="formula"><span class="calculated">Root-Directory Position</span> = Daten-Bereich Anfang + (Root-Directory Cluster - 0x2) × Cluster-Größe</div>
</div>
</div>
</div>
</div>
</main>
<footer class="footer">
<div class="footer-content">
<div class="footer-section">
<h3>Über</h3>
<p>Dateisystem Offset-Rechner für Bildung und Forensik.</p>
<p>Version 0.3.0</p>
</div>
<div class="footer-section">
<h3>Support</h3>
<a href="mailto:mstoeck3@hs-mittweida.de">Kontakt</a>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2025 Dateisystem Offset-Rechner. Public Domain unter BSD-3-Clause.</p>
</div>
</footer>
<script>
// Global state
let tooltip = null;
const calculatorState = {
currentValue: 0,
previousValue: 0,
operation: null,
waitingForOperand: false
};
// Get current active tab
function getCurrentTab() {
const activeTab = document.querySelector('.tab-content.active');
return activeTab ? activeTab.id : 'fat1216';
}
// Calculator functions (unified for both tabs)
function updateCalculatorDisplay() {
const currentTab = getCurrentTab();
const suffix = currentTab === 'fat32' ? '32' : '';
const inputElement = document.getElementById(`calcInput${suffix}`);
const hexElement = document.getElementById(`hexValue${suffix}`);
const decElement = document.getElementById(`decValue${suffix}`);
const binElement = document.getElementById(`binValue${suffix}`);
const value = Math.max(0, Math.min(Math.floor(calculatorState.currentValue), Number.MAX_SAFE_INTEGER));
const hexStr = '0x' + value.toString(16).toUpperCase();
const decStr = value.toString(10);
const binStr = '0b' + value.toString(2);
if (inputElement) inputElement.value = hexStr;
if (hexElement) hexElement.textContent = hexStr;
if (decElement) decElement.textContent = decStr;
if (binElement) binElement.textContent = binStr;
}
function calcInput(digit) {
if (calculatorState.waitingForOperand) {
calculatorState.currentValue = 0;
calculatorState.waitingForOperand = false;
}
const digitValue = parseInt(digit, 16);
if (isNaN(digitValue)) return;
const newValue = calculatorState.currentValue * 16 + digitValue;
if (newValue <= Number.MAX_SAFE_INTEGER && newValue >= 0) {
calculatorState.currentValue = newValue;
}
updateCalculatorDisplay();
}
function calcOperation(operation) {
if (calculatorState.previousValue !== 0 && !calculatorState.waitingForOperand) {
calcEquals(false);
}
calculatorState.previousValue = calculatorState.currentValue;
calculatorState.operation = operation;
calculatorState.waitingForOperand = true;
}
function calcEquals(updateDisplay = true) {
if (calculatorState.operation && calculatorState.previousValue !== null) {
const prev = Math.floor(calculatorState.previousValue);
const curr = Math.floor(calculatorState.currentValue);
try {
let result = 0;
switch (calculatorState.operation) {
case '+':
result = prev + curr;
break;
case '-':
result = prev - curr;
break;
case '*':
result = prev * curr;
break;
case '/':
if (curr === 0) {
alert('Division durch Null!');
return;
}
result = Math.floor(prev / curr);
break;
case 'MOD':
if (curr === 0) {
alert('Modulo durch Null!');
return;
}
result = prev % curr;
break;
}
calculatorState.currentValue = Math.max(0, Math.min(result, Number.MAX_SAFE_INTEGER));
} catch (error) {
alert('Berechnungsfehler: ' + error.message);
calculatorState.currentValue = 0;
}
calculatorState.operation = null;
calculatorState.previousValue = 0;
calculatorState.waitingForOperand = true;
}
if (updateDisplay) {
updateCalculatorDisplay();
}
}
function calcClear() {
calculatorState.currentValue = 0;
calculatorState.previousValue = 0;
calculatorState.operation = null;
calculatorState.waitingForOperand = false;
updateCalculatorDisplay();
}
function calcBackspace() {
if (!calculatorState.waitingForOperand) {
calculatorState.currentValue = Math.floor(calculatorState.currentValue / 16);
updateCalculatorDisplay();
}
}
// Input validation and parsing
function parseHex(value) {
if (!value) return 0;
let cleanValue = value.toString().trim();
if (cleanValue.startsWith('0x') || cleanValue.startsWith('0X')) {
cleanValue = cleanValue.substring(2);
}
const parsed = parseInt(cleanValue, 16);
return isNaN(parsed) ? null : Math.max(0, parsed);
}
function validateInput(fieldId, value) {
const parsedValue = parseHex(value);
const errorElement = document.getElementById(fieldId + '-error');
const inputElement = document.getElementById(fieldId);
if (parsedValue === null && value.trim() !== '') {
errorElement.textContent = 'Ungültiger Hex-Wert';
inputElement.classList.add('error');
return false;
} else {
errorElement.textContent = '';
inputElement.classList.remove('error');
return true;
}
}
function formatHex(value) {
if (isNaN(value) || value < 0) {
return '<span class="error">Fehler</span>';
}
return '0x' + Math.floor(value).toString(16).toUpperCase();
}
// Dependency checking
function checkDependencies(deps, currentTab) {
return deps.every(dep => {
const element = document.getElementById(dep);
return element && element.value.trim() !== '' && validateInput(dep, element.value);
});
}
function updateResultItem(elementId, value, available, calculation = '') {
const element = document.getElementById(elementId);
const resultItem = element.closest('.result-item');
if (available) {
element.innerHTML = formatHex(value);
resultItem.classList.remove('unavailable');
resultItem.classList.add('available');
if (calculation) {
element.setAttribute('data-result-formula', calculation);
}
} else {
element.innerHTML = '-';
resultItem.classList.remove('available');
resultItem.classList.add('unavailable');
element.removeAttribute('data-result-formula');
}
}
// Calculation functions
function calculateValues(tab) {
const suffix = tab === 'fat32' ? '32' : '1216';
// Get all input values
const baseOffset = parseHex(document.getElementById(`baseOffset${suffix === '32' ? '32' : '1216'}`).value) || 0;
const sectorSize = parseHex(document.getElementById(`sectorSize${suffix === '32' ? '32' : '1216'}`).value) || 0;
const reservedSectors = parseHex(document.getElementById(`reservedSectors${suffix}`).value);
const numFATs = parseHex(document.getElementById(`numFATs${suffix}`).value);
const fatSizeSectors = parseHex(document.getElementById(`fatSizeSectors${suffix}`).value);
const partitionSizeInSectors = parseHex(document.getElementById(`partitionSizeInSectors${suffix}`).value);
const clusterSizeSectors = parseHex(document.getElementById(`clusterSizeSectors${suffix}`).value);
const clusterNumber = parseHex(document.getElementById(`clusterNumber${suffix}`).value);
// FAT12/16 specific
const maxRootEntries = tab === 'fat1216' ? parseHex(document.getElementById('maxRootEntries1216').value) : null;
// FAT32 specific
const rootDirCluster = tab === 'fat32' ? parseHex(document.getElementById('rootDirCluster32').value) : null;
// Check each calculation's dependencies and calculate if available
const results = {};
// FAT Start
if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix === '32' ? '32' : '1216'}`, `baseOffset${suffix === '32' ? '32' : '1216'}`], tab)) {
results.fatStart = reservedSectors * sectorSize + baseOffset;
results.fatStartSector = Math.floor(results.fatStart / sectorSize);
updateResultItem(`fatStart${suffix}`, results.fatStart, true,
`0x${reservedSectors.toString(16)} × 0x${sectorSize.toString(16)} + 0x${baseOffset.toString(16)} = 0x${results.fatStart.toString(16)}`);
updateResultItem(`fatStartSector${suffix}`, results.fatStartSector, true,
`0x${results.fatStart.toString(16)} ÷ 0x${sectorSize.toString(16)} = 0x${results.fatStartSector.toString(16)}`);
} else {
updateResultItem(`fatStart${suffix}`, 0, false);
updateResultItem(`fatStartSector${suffix}`, 0, false);
}
// FAT Size
if (checkDependencies([`numFATs${suffix}`, `fatSizeSectors${suffix}`, `sectorSize${suffix === '32' ? '32' : '1216'}`], tab)) {
results.fatSize = numFATs * fatSizeSectors * sectorSize;
updateResultItem(`fatSize${suffix}`, results.fatSize, true,
`0x${numFATs.toString(16)} × 0x${fatSizeSectors.toString(16)} × 0x${sectorSize.toString(16)} = 0x${results.fatSize.toString(16)}`);
} else {
updateResultItem(`fatSize${suffix}`, 0, false);
}
// FAT2 Start (second FAT copy)
if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix === '32' ? '32' : '1216'}`, `baseOffset${suffix === '32' ? '32' : '1216'}`, `fatSizeSectors${suffix}`], tab)) {
results.fat2Start = results.fatStart + (fatSizeSectors * sectorSize);
updateResultItem(`fat2Start${suffix}`, results.fat2Start, true,
`0x${results.fatStart.toString(16)} + 0x${(fatSizeSectors * sectorSize).toString(16)} = 0x${results.fat2Start.toString(16)}`);
} else {
updateResultItem(`fat2Start${suffix}`, 0, false);
}
// FAT End
if (results.fatStart !== undefined && results.fatSize !== undefined) {
results.fatEnd = results.fatStart + results.fatSize;
updateResultItem(`fatEnd${suffix}`, results.fatEnd, true,
`0x${results.fatStart.toString(16)} + 0x${results.fatSize.toString(16)} = 0x${results.fatEnd.toString(16)}`);
} else {
updateResultItem(`fatEnd${suffix}`, 0, false);
}
// Root Directory (FAT12/16 only)
if (tab === 'fat1216') {
if (checkDependencies(['maxRootEntries1216'], tab)) {
results.rootDirSize = maxRootEntries * 0x20;
updateResultItem('rootDirSize1216', results.rootDirSize, true,
`0x${maxRootEntries.toString(16)} × 0x20 = 0x${results.rootDirSize.toString(16)}`);
} else {
updateResultItem('rootDirSize1216', 0, false);
}
if (results.fatEnd !== undefined) {
results.rootDirStart = results.fatEnd;
updateResultItem('rootDirStart1216', results.rootDirStart, true,
`0x${results.fatEnd.toString(16)}`);
} else {
updateResultItem('rootDirStart1216', 0, false);
}
// Data Start for FAT12/16
if (results.fatEnd !== undefined && results.rootDirSize !== undefined) {
results.dataStart = results.fatEnd + results.rootDirSize;
results.dataStartSector = Math.floor(results.dataStart / sectorSize);
updateResultItem('dataStart1216', results.dataStart, true,
`0x${results.fatEnd.toString(16)} + 0x${results.rootDirSize.toString(16)} = 0x${results.dataStart.toString(16)}`);
updateResultItem('dataStartSector1216', results.dataStartSector, true,
`0x${results.dataStart.toString(16)} ÷ 0x${sectorSize.toString(16)} = 0x${results.dataStartSector.toString(16)}`);
} else {
updateResultItem('dataStart1216', 0, false);
updateResultItem('dataStartSector1216', 0, false);
}
} else {
// Data Start for FAT32 (no fixed root directory)
if (results.fatEnd !== undefined) {
results.dataStart = results.fatEnd;
results.dataStartSector = Math.floor(results.dataStart / sectorSize);
updateResultItem('dataStart32', results.dataStart, true,
`0x${results.fatEnd.toString(16)}`);
updateResultItem('dataStartSector32', results.dataStartSector, true,
`0x${results.dataStart.toString(16)} ÷ 0x${sectorSize.toString(16)} = 0x${results.dataStartSector.toString(16)}`);
} else {
updateResultItem('dataStart32', 0, false);
updateResultItem('dataStartSector32', 0, false);
}
}
// Data Size
const requiredForDataSize = tab === 'fat1216'
? [`partitionSizeInSectors${suffix}`, `sectorSize1216`, `reservedSectors${suffix}`, `baseOffset1216`, `numFATs${suffix}`, `fatSizeSectors${suffix}`, 'maxRootEntries1216']
: [`partitionSizeInSectors${suffix}`, `sectorSize32`, `reservedSectors${suffix}`, `baseOffset32`, `numFATs${suffix}`, `fatSizeSectors${suffix}`];
if (checkDependencies(requiredForDataSize, tab) && results.dataStart !== undefined) {
const partitionSizeBytes = partitionSizeInSectors * sectorSize;
results.dataSize = partitionSizeBytes - (results.dataStart - baseOffset);
updateResultItem(`dataSize${suffix}`, results.dataSize, true,
`(0x${partitionSizeInSectors.toString(16)} × 0x${sectorSize.toString(16)}) - (0x${results.dataStart.toString(16)} - 0x${baseOffset.toString(16)}) = 0x${results.dataSize.toString(16)}`);
} else {
updateResultItem(`dataSize${suffix}`, 0, false);
}
// Cluster Size
if (checkDependencies([`sectorSize${suffix === '32' ? '32' : '1216'}`, `clusterSizeSectors${suffix}`], tab)) {
results.clusterSize = sectorSize * clusterSizeSectors;
updateResultItem(`clusterSize${suffix}`, results.clusterSize, true,
`0x${sectorSize.toString(16)} × 0x${clusterSizeSectors.toString(16)} = 0x${results.clusterSize.toString(16)}`);
} else {
updateResultItem(`clusterSize${suffix}`, 0, false);
}
// Number of Clusters
if (results.dataSize !== undefined && results.clusterSize !== undefined && results.clusterSize > 0) {
results.numClusters = Math.floor(results.dataSize / results.clusterSize);
updateResultItem(`numClusters${suffix}`, results.numClusters, true,
`0x${results.dataSize.toString(16)} ÷ 0x${results.clusterSize.toString(16)} = 0x${results.numClusters.toString(16)}`);
} else {
updateResultItem(`numClusters${suffix}`, 0, false);
}
// Specific Cluster Start
const requiredForCluster = tab === 'fat1216'
? [`reservedSectors${suffix}`, `sectorSize1216`, `baseOffset1216`, `numFATs${suffix}`, `fatSizeSectors${suffix}`, 'maxRootEntries1216', `clusterSizeSectors${suffix}`, `clusterNumber${suffix}`]
: [`reservedSectors${suffix}`, `sectorSize32`, `baseOffset32`, `numFATs${suffix}`, `fatSizeSectors${suffix}`, `clusterSizeSectors${suffix}`, `clusterNumber${suffix}`];
if (checkDependencies(requiredForCluster, tab) && results.dataStart !== undefined && results.clusterSize !== undefined) {
results.clusterStart = results.dataStart + (clusterNumber - 0x2) * results.clusterSize;
results.clusterStartSector = Math.floor(results.clusterStart / sectorSize);
updateResultItem(`clusterStart${suffix}`, results.clusterStart, true,
`0x${results.dataStart.toString(16)} + (0x${clusterNumber.toString(16)} - 0x2) × 0x${results.clusterSize.toString(16)} = 0x${results.clusterStart.toString(16)}`);
updateResultItem(`clusterStartSector${suffix}`, results.clusterStartSector, true,
`0x${results.clusterStart.toString(16)} ÷ 0x${sectorSize.toString(16)} = 0x${results.clusterStartSector.toString(16)}`);
} else {
updateResultItem(`clusterStart${suffix}`, 0, false);
updateResultItem(`clusterStartSector${suffix}`, 0, false);
}
// FAT Entry Position
if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix === '32' ? '32' : '1216'}`, `baseOffset${suffix === '32' ? '32' : '1216'}`, `clusterNumber${suffix}`], tab) && results.fatStart !== undefined) {
const entrySize = tab === 'fat32' ? 4 : 2; // FAT32 uses 4 bytes, FAT16 uses 2 bytes per entry
results.fatEntryPos = results.fatStart + (clusterNumber * entrySize);
updateResultItem(`fatEntryPos${suffix}`, results.fatEntryPos, true,
`0x${results.fatStart.toString(16)} + (0x${clusterNumber.toString(16)} × ${entrySize}) = 0x${results.fatEntryPos.toString(16)}`);
} else {
updateResultItem(`fatEntryPos${suffix}`, 0, false);
}
// Root Directory Position (FAT32 only)
if (tab === 'fat32') {
const requiredForRootPos = [`reservedSectors${suffix}`, `sectorSize32`, `baseOffset32`, `numFATs${suffix}`, `fatSizeSectors${suffix}`, `clusterSizeSectors${suffix}`, `rootDirCluster32`];
if (checkDependencies(requiredForRootPos, tab) && results.dataStart !== undefined && results.clusterSize !== undefined) {
results.rootDirPos = results.dataStart + (rootDirCluster - 0x2) * results.clusterSize;
updateResultItem('rootDirPos32', results.rootDirPos, true,
`0x${results.dataStart.toString(16)} + (0x${rootDirCluster.toString(16)} - 0x2) × 0x${results.clusterSize.toString(16)} = 0x${results.rootDirPos.toString(16)}`);
} else {
updateResultItem('rootDirPos32', 0, false);
}
}
}
// Tooltip functions
function createTooltip() {
if (!tooltip) {
tooltip = document.createElement('div');
tooltip.className = 'tooltip';
tooltip.style.display = 'none';
document.body.appendChild(tooltip);
}
}
function showTooltip(element, text) {
createTooltip();
tooltip.innerHTML = text;
tooltip.style.display = 'block';
const rect = element.getBoundingClientRect();
const tooltipRect = tooltip.getBoundingClientRect();
let left = rect.left + (rect.width / 2) - (tooltipRect.width / 2);
let top = rect.top - tooltipRect.height - 8;
if (left < 10) left = 10;
if (left + tooltipRect.width > window.innerWidth - 10) {
left = window.innerWidth - tooltipRect.width - 10;
}
if (top < 10) {
top = rect.bottom + 8;
}
tooltip.style.left = left + 'px';
tooltip.style.top = top + 'px';
}
function hideTooltip() {
if (tooltip) {
tooltip.style.display = 'none';
}
}
function copyToClipboard(elementId) {
const element = document.getElementById(elementId);
const text = element.textContent.trim();
if (text === '-' || text.includes('Fehler')) {
return;
}
navigator.clipboard.writeText(text).then(() => {
const btn = element.parentElement.querySelector('.copy-btn');
const originalText = btn.textContent;
btn.textContent = '✓';
btn.style.color = '#88cc88';
setTimeout(() => {
btn.textContent = originalText;
btn.style.color = '';
}, 1000);
}).catch(err => {
console.error('Fehler beim Kopieren:', err);
});
}
function switchTab(tabId) {
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
document.getElementById(tabId).classList.add('active');
event.target.classList.add('active');
updateCalculatorDisplay();
}
// Event listeners
document.addEventListener('DOMContentLoaded', function () {
updateCalculatorDisplay();
// Input validation and calculation triggers
const allInputs = document.querySelectorAll('input[type="text"]:not([readonly])');
allInputs.forEach(input => {
input.addEventListener('input', function () {
const currentTab = getCurrentTab();
validateInput(this.id, this.value);
calculateValues(currentTab);
});
});
// Keyboard support for calculator
document.addEventListener('keydown', function (e) {
if (e.target.tagName === 'INPUT' && !e.target.readOnly) return;
const key = e.key.toUpperCase();
if (/^[0-9A-F]$/.test(key)) {
e.preventDefault();
calcInput(key);
} else if (key === '+') {
e.preventDefault();
calcOperation('+');
} else if (key === '-') {
e.preventDefault();
calcOperation('-');
} else if (key === '*') {
e.preventDefault();
calcOperation('*');
} else if (key === '/') {
e.preventDefault();
calcOperation('/');
} else if (key === 'ENTER' || key === '=') {
e.preventDefault();
calcEquals();
} else if (key === 'ESCAPE' || key === 'C') {
e.preventDefault();
calcClear();
} else if (key === 'BACKSPACE') {
e.preventDefault();
calcBackspace();
}
});
// Tooltip events
document.addEventListener('mouseover', function (e) {
if (e.target.classList.contains('result-label')) {
const formula = e.target.getAttribute('data-formula');
if (formula) {
showTooltip(e.target, `Formel: ${formula}`);
}
} else if (e.target.classList.contains('result-value')) {
const formula = e.target.getAttribute('data-result-formula');
if (formula && formula !== '') {
showTooltip(e.target, `Berechnung: ${formula}`);
}
}
});
document.addEventListener('mouseout', function (e) {
if (e.target.classList.contains('result-label') || e.target.classList.contains('result-value')) {
hideTooltip();
}
});
document.addEventListener('scroll', hideTooltip);
// Initial calculation
calculateValues('fat1216');
calculateValues('fat32');
});
</script>
</body>
</html>