1755 lines
77 KiB
HTML
1755 lines
77 KiB
HTML
<!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 1fr;
|
||
gap: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
@media (max-width: 1024px) {
|
||
.constants-calculator-container {
|
||
grid-template-columns: 1fr;
|
||
gap: 15px;
|
||
}
|
||
}
|
||
|
||
/* Calculator Styles */
|
||
.hex-calculator {
|
||
background-color: #1a1a1a;
|
||
min-height: fit-content;
|
||
}
|
||
|
||
.calculator-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
}
|
||
|
||
.calc-display {
|
||
background-color: #111111;
|
||
border: 1px solid #404040;
|
||
border-radius: 6px;
|
||
padding: 15px;
|
||
}
|
||
|
||
.calc-input input {
|
||
width: 100%;
|
||
background-color: #2a2a2a;
|
||
border: 1px solid #555555;
|
||
border-radius: 4px;
|
||
color: #ffffff;
|
||
font-family: 'Consolas', monospace;
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
padding: 12px;
|
||
text-align: right;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.calc-conversions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.conversion-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 6px 0;
|
||
border-bottom: 1px solid #333333;
|
||
}
|
||
|
||
.conversion-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.conv-label {
|
||
color: #888888;
|
||
font-weight: bold;
|
||
font-size: 0.9em;
|
||
min-width: 40px;
|
||
}
|
||
|
||
.conv-value {
|
||
color: #cccccc;
|
||
font-family: 'Consolas', monospace;
|
||
font-size: 0.9em;
|
||
word-break: break-all;
|
||
text-align: right;
|
||
flex: 1;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.calc-buttons {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.calc-row {
|
||
display: grid;
|
||
grid-template-columns: repeat(5, 1fr);
|
||
gap: 6px;
|
||
}
|
||
|
||
.calc-btn {
|
||
background-color: #2a2a2a;
|
||
border: 1px solid #4a4a4a;
|
||
border-radius: 4px;
|
||
color: #ffffff;
|
||
cursor: pointer;
|
||
font-family: 'Consolas', monospace;
|
||
font-size: 0.85em;
|
||
font-weight: bold;
|
||
min-height: 40px;
|
||
padding: 8px;
|
||
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.memory {
|
||
background-color: #4a2a2a;
|
||
color: #cc8888;
|
||
font-size: 0.75em;
|
||
}
|
||
|
||
.calc-btn.memory:hover {
|
||
background-color: #5a3a3a;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.calc-btn {
|
||
min-height: 35px;
|
||
font-size: 0.8em;
|
||
padding: 6px;
|
||
}
|
||
|
||
.calc-input input {
|
||
font-size: 16px;
|
||
padding: 10px;
|
||
}
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.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-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;
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.result-item {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 5px;
|
||
}
|
||
|
||
.result-value {
|
||
text-align: left;
|
||
min-width: auto;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.header-content {
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
|
||
.nav {
|
||
justify-content: center;
|
||
}
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.main-content {
|
||
padding: 15px;
|
||
}
|
||
|
||
h1 {
|
||
font-size: 1.8em;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.section {
|
||
padding: 15px;
|
||
}
|
||
|
||
.formula-box {
|
||
padding: 12px;
|
||
font-size: 0.85em;
|
||
}
|
||
|
||
.tab {
|
||
padding: 12px 15px;
|
||
min-width: 100px;
|
||
}
|
||
}
|
||
|
||
@media (min-width: 1200px) {
|
||
.input-grid {
|
||
grid-template-columns: repeat(3, 1fr);
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
|
||
<body>
|
||
<header class="header">
|
||
<div class="header-content">
|
||
<div class="logo">Dateisystem-Offset-Rechner</div>
|
||
<nav class="nav">
|
||
<a href="#home">Home</a>
|
||
<a href="mailto:mstoeck3@hs-mittweida.de">Contact</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">Base Offset <span
|
||
class="unit-indicator">(Bytes)</span>:</label>
|
||
<input type="text" id="baseOffset1216" value="0x0" placeholder="0x0">
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="sectorSize1216">Größe eines Sektors <span
|
||
class="unit-indicator">(Bytes)</span>:</label>
|
||
<input type="text" id="sectorSize1216" value="0x200" placeholder="0x200">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section hex-calculator">
|
||
<h2>Hex Calculator</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 memory" onclick="calcMemory('MC')">MC</button>
|
||
<button class="calc-btn memory" onclick="calcMemory('MR')">MR</button>
|
||
<button class="calc-btn memory" onclick="calcMemory('M+')">M+</button>
|
||
<button class="calc-btn memory" onclick="calcMemory('M-')">M-</button>
|
||
<button class="calc-btn clear" onclick="calcClear()">C</button>
|
||
</div>
|
||
<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 operation" onclick="calcOperation('<<')"><<</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 operation" onclick="calcOperation('AND')">AND</button>
|
||
<button class="calc-btn operation" onclick="calcOperation('OR')">OR</button>
|
||
<button class="calc-btn number" onclick="calcInput('9')">9</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 operation" onclick="calcOperation('XOR')">XOR</button>
|
||
<button class="calc-btn operation" onclick="calcOperation('NOT')">NOT</button>
|
||
<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>
|
||
</div>
|
||
<div class="calc-row">
|
||
<button class="calc-btn operation" onclick="calcOperation('*')">*</button>
|
||
<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>
|
||
</div>
|
||
<div class="calc-row">
|
||
<button class="calc-btn operation" onclick="calcOperation('+')">+</button>
|
||
<button class="calc-btn number wide" onclick="calcInput('0')">0</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">Anzahl reservierter Sektoren <span
|
||
class="unit-indicator">(Sektoren)</span>:</label>
|
||
<input type="text" id="reservedSectors1216" placeholder="0x1">
|
||
</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>
|
||
<div class="input-group">
|
||
<label for="fatSizeBytes1216">Größe einer FAT <span
|
||
class="unit-indicator">(Bytes)</span>:</label>
|
||
<input type="text" id="fatSizeBytes1216" placeholder="0x1000">
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="maxRootEntries1216">Max. Wurzelverzeichnis-Einträge <span
|
||
class="unit-indicator">(Anzahl)</span>:</label>
|
||
<input type="text" id="maxRootEntries1216" placeholder="0x200">
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="partitionSizeInSectors1216">Größe Partition <span
|
||
class="unit-indicator">(Sektoren)</span>:</label>
|
||
<input type="text" id="partitionSizeInSectors1216" placeholder="0x4015">
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="clusterSizeSectors1216">Größe eines Clusters <span
|
||
class="unit-indicator">(Sektoren)</span>:</label>
|
||
<input type="text" id="clusterSizeSectors1216" placeholder="0x1">
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="clusterNumber1216">Cluster Nummer für Position <span
|
||
class="unit-indicator">(Nummer)</span>:</label>
|
||
<input type="text" id="clusterNumber1216" placeholder="0x2">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section results">
|
||
<h2>Berechnete Werte</h2>
|
||
<div id="results1216">
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="reservedSectors × sectorSize + baseOffset">Beginn
|
||
FAT-Bereich:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="fatStart1216" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('fatStart1216')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="numFATs × fatSizeBytes">Größe FAT-Bereich:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="fatSize1216" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('fatSize1216')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="fatStart + fatSize">Ende FAT-Bereich:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="fatEnd1216" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('fatEnd1216')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="fatEnd">Beginn Wurzelverzeichnis:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="rootDirStart1216" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('rootDirStart1216')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="maxRootEntries × 0x20">Größe
|
||
Wurzelverzeichnis:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="rootDirSize1216" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('rootDirSize1216')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="fatEnd + rootDirSize">Beginn Daten-Bereich:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="dataStart1216" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('dataStart1216')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label"
|
||
data-formula="(partitionSizeInSectors × sectorSize) - (dataStart - baseOffset)">Größe
|
||
Daten-Bereich:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="dataSize1216" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('dataSize1216')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="sectorSize × clusterSizeSectors">Größe eines
|
||
Clusters:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="clusterSize1216" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('clusterSize1216')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="dataSize ÷ clusterSize">Anzahl Cluster:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="numClusters1216" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('numClusters1216')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label"
|
||
data-formula="dataStart + (clusterNumber - 0x2) × clusterSize">Beginn Cluster:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="clusterStart1216" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('clusterStart1216')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section formulas">
|
||
<h2>Berechnungsformeln FAT12/16</h2>
|
||
<div class="formula-box">
|
||
<div class="formula"><span class="calculated">Beginn FAT-Bereich</span> = Anzahl reservierter
|
||
Sektoren × Größe eines Sektors + Base Offset</div>
|
||
<div class="formula"><span class="calculated">Größe FAT-Bereich</span> = Anzahl FATs × Größe
|
||
einer FAT</div>
|
||
<div class="formula"><span class="calculated">Ende FAT-Bereich</span> = Beginn FAT-Bereich +
|
||
Größe FAT-Bereich</div>
|
||
<div class="formula"><span class="calculated">Größe Wurzelverzeichnis</span> = Anzahl max.
|
||
Wurzelverzeichnis-Einträge × 0x20</div>
|
||
<div class="formula"><span class="calculated">Beginn Daten-Bereich</span> = Ende FAT-Bereich +
|
||
Größe Wurzelverzeichnis</div>
|
||
<div class="formula"><span class="calculated">Größe Daten-Bereich</span> = (Größe Partition in
|
||
Sektoren × Größe eines Sektors) - (Beginn Daten-Bereich - Base Offset)</div>
|
||
<div class="formula"><span class="calculated">Größe eines Clusters</span> = Größe eines Sektors
|
||
× Größe eines Clusters in Sektoren</div>
|
||
<div class="formula"><span class="calculated">Anzahl Cluster</span> = Größe Daten-Bereich ÷
|
||
Größe eines Clusters</div>
|
||
<div class="formula"><span class="calculated">Beginn Cluster</span> = Beginn Daten-Bereich +
|
||
(Cluster - 0x2) × Größe eines Clusters</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">Base Offset <span
|
||
class="unit-indicator">(Bytes)</span>:</label>
|
||
<input type="text" id="baseOffset32" value="0x0" placeholder="0x0">
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="sectorSize32">Größe eines Sektors <span
|
||
class="unit-indicator">(Bytes)</span>:</label>
|
||
<input type="text" id="sectorSize32" value="0x200" placeholder="0x200">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section hex-calculator">
|
||
<h2>Hex Calculator</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 memory" onclick="calcMemory('MC', 'fat32')">MC</button>
|
||
<button class="calc-btn memory" onclick="calcMemory('MR', 'fat32')">MR</button>
|
||
<button class="calc-btn memory" onclick="calcMemory('M+', 'fat32')">M+</button>
|
||
<button class="calc-btn memory" onclick="calcMemory('M-', 'fat32')">M-</button>
|
||
<button class="calc-btn clear" onclick="calcClear('fat32')">C</button>
|
||
</div>
|
||
<div class="calc-row">
|
||
<button class="calc-btn number" onclick="calcInput('A', 'fat32')">A</button>
|
||
<button class="calc-btn number" onclick="calcInput('B', 'fat32')">B</button>
|
||
<button class="calc-btn number" onclick="calcInput('C', 'fat32')">C</button>
|
||
<button class="calc-btn number" onclick="calcInput('D', 'fat32')">D</button>
|
||
<button class="calc-btn operation"
|
||
onclick="calcOperation('<<', 'fat32')"><<</button>
|
||
</div>
|
||
<div class="calc-row">
|
||
<button class="calc-btn number" onclick="calcInput('E', 'fat32')">E</button>
|
||
<button class="calc-btn number" onclick="calcInput('F', 'fat32')">F</button>
|
||
<button class="calc-btn number" onclick="calcInput('7', 'fat32')">7</button>
|
||
<button class="calc-btn number" onclick="calcInput('8', 'fat32')">8</button>
|
||
<button class="calc-btn operation"
|
||
onclick="calcOperation('>>', 'fat32')">>></button>
|
||
</div>
|
||
<div class="calc-row">
|
||
<button class="calc-btn operation"
|
||
onclick="calcOperation('AND', 'fat32')">AND</button>
|
||
<button class="calc-btn operation"
|
||
onclick="calcOperation('OR', 'fat32')">OR</button>
|
||
<button class="calc-btn number" onclick="calcInput('9', 'fat32')">9</button>
|
||
<button class="calc-btn operation" onclick="calcOperation('/', 'fat32')">/</button>
|
||
<button class="calc-btn operation"
|
||
onclick="calcOperation('MOD', 'fat32')">MOD</button>
|
||
</div>
|
||
<div class="calc-row">
|
||
<button class="calc-btn operation"
|
||
onclick="calcOperation('XOR', 'fat32')">XOR</button>
|
||
<button class="calc-btn operation"
|
||
onclick="calcOperation('NOT', 'fat32')">NOT</button>
|
||
<button class="calc-btn number" onclick="calcInput('4', 'fat32')">4</button>
|
||
<button class="calc-btn number" onclick="calcInput('5', 'fat32')">5</button>
|
||
<button class="calc-btn number" onclick="calcInput('6', 'fat32')">6</button>
|
||
</div>
|
||
<div class="calc-row">
|
||
<button class="calc-btn operation" onclick="calcOperation('*', 'fat32')">*</button>
|
||
<button class="calc-btn number" onclick="calcInput('1', 'fat32')">1</button>
|
||
<button class="calc-btn number" onclick="calcInput('2', 'fat32')">2</button>
|
||
<button class="calc-btn number" onclick="calcInput('3', 'fat32')">3</button>
|
||
<button class="calc-btn operation" onclick="calcOperation('-', 'fat32')">-</button>
|
||
</div>
|
||
<div class="calc-row">
|
||
<button class="calc-btn operation" onclick="calcOperation('+', 'fat32')">+</button>
|
||
<button class="calc-btn number wide" onclick="calcInput('0', 'fat32')">0</button>
|
||
<button class="calc-btn backspace" onclick="calcBackspace('fat32')">⌫</button>
|
||
<button class="calc-btn equals" onclick="calcEquals('fat32')">=</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<h2>Eingabeparameter</h2>
|
||
<div class="input-grid">
|
||
<div class="input-group">
|
||
<label for="reservedSectors32">Anzahl reservierter Sektoren <span
|
||
class="unit-indicator">(Sektoren)</span>:</label>
|
||
<input type="text" id="reservedSectors32" placeholder="0x20">
|
||
</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>
|
||
<div class="input-group">
|
||
<label for="fatSizeBytes32">Größe einer FAT <span
|
||
class="unit-indicator">(Bytes)</span>:</label>
|
||
<input type="text" id="fatSizeBytes32" placeholder="0x10000">
|
||
</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>
|
||
<div class="input-group">
|
||
<label for="partitionSizeInSectors32">Größe Partition <span
|
||
class="unit-indicator">(Sektoren)</span>:</label>
|
||
<input type="text" id="partitionSizeInSectors32" placeholder="0x20000">
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="clusterSizeSectors32">Größe eines Clusters <span
|
||
class="unit-indicator">(Sektoren)</span>:</label>
|
||
<input type="text" id="clusterSizeSectors32" placeholder="0x8">
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="clusterNumber32">Cluster Nummer für Position <span
|
||
class="unit-indicator">(Nummer)</span>:</label>
|
||
<input type="text" id="clusterNumber32" placeholder="0x2">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section results">
|
||
<h2>Berechnete Werte</h2>
|
||
<div id="results32">
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="reservedSectors × sectorSize + baseOffset">Beginn
|
||
FAT-Bereich:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="fatStart32" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('fatStart32')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="numFATs × fatSizeBytes">Größe FAT-Bereich:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="fatSize32" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('fatSize32')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="fatStart + fatSize">Ende FAT-Bereich:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="fatEnd32" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('fatEnd32')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="fatEnd">Beginn Daten-Bereich:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="dataStart32" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('dataStart32')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label"
|
||
data-formula="(partitionSizeInSectors × sectorSize) - (dataStart - baseOffset)">Größe
|
||
Daten-Bereich:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="dataSize32" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('dataSize32')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="sectorSize × clusterSizeSectors">Größe eines
|
||
Clusters:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="clusterSize32" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('clusterSize32')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label" data-formula="dataSize ÷ clusterSize">Anzahl Cluster:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="numClusters32" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('numClusters32')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label"
|
||
data-formula="dataStart + (clusterNumber - 0x2) × clusterSize">Beginn Cluster:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="clusterStart32" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('clusterStart32')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<span class="result-label"
|
||
data-formula="dataStart + (rootDirCluster - 0x2) × clusterSize">Root Directory
|
||
Position:</span>
|
||
<div class="result-value-container">
|
||
<span class="result-value" id="rootDirPos32" data-result-formula="">-</span>
|
||
<button class="copy-btn" onclick="copyToClipboard('rootDirPos32')"
|
||
title="Copy to clipboard">📋</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section formulas">
|
||
<h2>Berechnungsformeln FAT32</h2>
|
||
<div class="formula-box">
|
||
<div class="formula"><span class="calculated">Beginn FAT-Bereich</span> = Anzahl reservierter
|
||
Sektoren × Größe eines Sektors + Base Offset</div>
|
||
<div class="formula"><span class="calculated">Größe FAT-Bereich</span> = Anzahl FATs × Größe
|
||
einer FAT</div>
|
||
<div class="formula"><span class="calculated">Ende FAT-Bereich</span> = Beginn FAT-Bereich +
|
||
Größe FAT-Bereich</div>
|
||
<div class="formula"><span class="calculated">Beginn Daten-Bereich</span> = Ende FAT-Bereich
|
||
(kein festes Root Directory)</div>
|
||
<div class="formula"><span class="calculated">Größe Daten-Bereich</span> = (Größe Partition in
|
||
Sektoren × Größe eines Sektors) - (Beginn Daten-Bereich - Base Offset)</div>
|
||
<div class="formula"><span class="calculated">Größe eines Clusters</span> = Größe eines Sektors
|
||
× Größe eines Clusters in Sektoren</div>
|
||
<div class="formula"><span class="calculated">Anzahl Cluster</span> = Größe Daten-Bereich ÷
|
||
Größe eines Clusters</div>
|
||
<div class="formula"><span class="calculated">Beginn Cluster</span> = Beginn Daten-Bereich +
|
||
(Cluster - 0x2) × Größe eines Clusters</div>
|
||
<div class="formula"><span class="calculated">Root Directory Position</span> = Beginn
|
||
Daten-Bereich + (Root Directory Cluster - 0x2) × Größe eines Clusters</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<footer class="footer">
|
||
<div class="footer-content">
|
||
<div class="footer-section">
|
||
<h3>About</h3>
|
||
<p>Offset calculator for educational and forensic purposes.</p>
|
||
<p>Version 0.2.0</p>
|
||
</div>
|
||
<div class="footer-section">
|
||
<h3>Support</h3>
|
||
<a href="mailto:mstoeck3@hs-mittweida.de">Contact Support</a>
|
||
<a href="https://git.cc24.dev/mstoeck3/fscalc/issues/new">Report Bug</a>
|
||
</div>
|
||
<div class="footer-section">
|
||
<h3>Legal</h3>
|
||
<a href="https://git.cc24.dev/mstoeck3/fscalc/src/branch/main/LICENSE">License</a>
|
||
</div>
|
||
<div class="footer-section">
|
||
<h3>Connect</h3>
|
||
<a href="https://git.cc24.dev/mstoeck3/fscalc">Gitea Repository</a>
|
||
</div>
|
||
</div>
|
||
<div class="footer-bottom">
|
||
<p>© 2025 Offset Calculator. All rights reserved. | Built with ❤️ for computer science education.</p>
|
||
</div>
|
||
</footer>
|
||
|
||
<script>
|
||
// Global tooltip element
|
||
let tooltip = null;
|
||
|
||
// Calculator state
|
||
const calculatorState = {
|
||
fat1216: {
|
||
currentValue: 0,
|
||
previousValue: 0,
|
||
operation: null,
|
||
waitingForOperand: false,
|
||
memory: 0
|
||
},
|
||
fat32: {
|
||
currentValue: 0,
|
||
previousValue: 0,
|
||
operation: null,
|
||
waitingForOperand: false,
|
||
memory: 0
|
||
}
|
||
};
|
||
|
||
function getCurrentTab() {
|
||
const activeTab = document.querySelector('.tab-content.active');
|
||
return activeTab.id === 'fat32' ? 'fat32' : 'fat1216';
|
||
}
|
||
|
||
function getCalculatorState(tab = null) {
|
||
const currentTab = tab || getCurrentTab();
|
||
return currentTab === 'fat32' ? calculatorState.fat32 : calculatorState.fat1216;
|
||
}
|
||
|
||
function updateCalculatorDisplay(tab = null) {
|
||
const currentTab = tab || getCurrentTab();
|
||
const state = getCalculatorState(currentTab);
|
||
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}`);
|
||
|
||
// Ensure value is within JavaScript's safe integer range
|
||
const value = Math.max(0, Math.min(state.currentValue, Number.MAX_SAFE_INTEGER));
|
||
|
||
const hexStr = '0x' + value.toString(16).toUpperCase();
|
||
const decStr = value.toString(10);
|
||
const binStr = '0b' + value.toString(2);
|
||
|
||
inputElement.value = hexStr;
|
||
hexElement.textContent = hexStr;
|
||
decElement.textContent = decStr;
|
||
binElement.textContent = binStr;
|
||
}
|
||
|
||
function calcInput(digit, tab = null) {
|
||
const currentTab = tab || getCurrentTab();
|
||
const state = getCalculatorState(currentTab);
|
||
|
||
if (state.waitingForOperand) {
|
||
state.currentValue = 0;
|
||
state.waitingForOperand = false;
|
||
}
|
||
|
||
// Convert hex digit to number
|
||
const digitValue = parseInt(digit, 16);
|
||
if (isNaN(digitValue)) return;
|
||
|
||
// Prevent overflow by limiting to a reasonable hex value
|
||
const newValue = state.currentValue * 16 + digitValue;
|
||
if (newValue <= 0xFFFFFFFFFFFFFF) { // Limit to 56 bits for safety
|
||
state.currentValue = newValue;
|
||
}
|
||
|
||
updateCalculatorDisplay(currentTab);
|
||
}
|
||
|
||
function calcOperation(operation, tab = null) {
|
||
const currentTab = tab || getCurrentTab();
|
||
const state = getCalculatorState(currentTab);
|
||
|
||
if (operation === 'NOT') {
|
||
// Unary operation - apply immediately
|
||
state.currentValue = (~state.currentValue) >>> 0; // Use unsigned 32-bit
|
||
updateCalculatorDisplay(currentTab);
|
||
return;
|
||
}
|
||
|
||
if (state.previousValue !== 0 && !state.waitingForOperand) {
|
||
calcEquals(currentTab, false);
|
||
}
|
||
|
||
state.previousValue = state.currentValue;
|
||
state.operation = operation;
|
||
state.waitingForOperand = true;
|
||
}
|
||
|
||
function calcEquals(tab = null, updateDisplay = true) {
|
||
const currentTab = tab || getCurrentTab();
|
||
const state = getCalculatorState(currentTab);
|
||
|
||
if (state.operation && state.previousValue !== null) {
|
||
const prev = state.previousValue;
|
||
const curr = state.currentValue;
|
||
|
||
try {
|
||
switch (state.operation) {
|
||
case '+':
|
||
state.currentValue = prev + curr;
|
||
break;
|
||
case '-':
|
||
state.currentValue = prev - curr;
|
||
break;
|
||
case '*':
|
||
state.currentValue = prev * curr;
|
||
break;
|
||
case '/':
|
||
if (curr === 0) {
|
||
alert('Division by zero!');
|
||
return;
|
||
}
|
||
state.currentValue = Math.floor(prev / curr);
|
||
break;
|
||
case 'MOD':
|
||
if (curr === 0) {
|
||
alert('Modulo by zero!');
|
||
return;
|
||
}
|
||
state.currentValue = prev % curr;
|
||
break;
|
||
case 'AND':
|
||
state.currentValue = (prev & curr) >>> 0;
|
||
break;
|
||
case 'OR':
|
||
state.currentValue = (prev | curr) >>> 0;
|
||
break;
|
||
case 'XOR':
|
||
state.currentValue = (prev ^ curr) >>> 0;
|
||
break;
|
||
case '<<':
|
||
state.currentValue = (prev << curr) >>> 0;
|
||
break;
|
||
case '>>':
|
||
state.currentValue = (prev >> curr) >>> 0;
|
||
break;
|
||
}
|
||
|
||
// Ensure positive value
|
||
if (state.currentValue < 0) {
|
||
state.currentValue = 0;
|
||
}
|
||
|
||
} catch (error) {
|
||
alert('Calculation error: ' + error.message);
|
||
state.currentValue = 0;
|
||
}
|
||
|
||
state.operation = null;
|
||
state.previousValue = 0;
|
||
state.waitingForOperand = true;
|
||
}
|
||
|
||
if (updateDisplay) {
|
||
updateCalculatorDisplay(currentTab);
|
||
}
|
||
}
|
||
|
||
function calcClear(tab = null) {
|
||
const currentTab = tab || getCurrentTab();
|
||
const state = getCalculatorState(currentTab);
|
||
|
||
state.currentValue = 0;
|
||
state.previousValue = 0;
|
||
state.operation = null;
|
||
state.waitingForOperand = false;
|
||
|
||
updateCalculatorDisplay(currentTab);
|
||
}
|
||
|
||
function calcBackspace(tab = null) {
|
||
const currentTab = tab || getCurrentTab();
|
||
const state = getCalculatorState(currentTab);
|
||
|
||
if (!state.waitingForOperand) {
|
||
state.currentValue = Math.floor(state.currentValue / 16);
|
||
updateCalculatorDisplay(currentTab);
|
||
}
|
||
}
|
||
|
||
function calcMemory(operation, tab = null) {
|
||
const currentTab = tab || getCurrentTab();
|
||
const state = getCalculatorState(currentTab);
|
||
|
||
switch (operation) {
|
||
case 'MC':
|
||
state.memory = 0;
|
||
break;
|
||
case 'MR':
|
||
state.currentValue = state.memory;
|
||
state.waitingForOperand = false;
|
||
updateCalculatorDisplay(currentTab);
|
||
break;
|
||
case 'M+':
|
||
state.memory += state.currentValue;
|
||
break;
|
||
case 'M-':
|
||
state.memory -= state.currentValue;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Create tooltip element
|
||
function createTooltip() {
|
||
if (!tooltip) {
|
||
tooltip = document.createElement('div');
|
||
tooltip.className = 'tooltip';
|
||
tooltip.style.display = 'none';
|
||
document.body.appendChild(tooltip);
|
||
}
|
||
}
|
||
|
||
// Show 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;
|
||
|
||
// Keep tooltip within viewport
|
||
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';
|
||
}
|
||
|
||
// Hide tooltip
|
||
function hideTooltip() {
|
||
if (tooltip) {
|
||
tooltip.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
// Filesystem calculation implementations
|
||
const filesystems = {
|
||
fat1216: {
|
||
requiredFields: ['reservedSectors1216', 'numFATs1216', 'fatSizeBytes1216', 'maxRootEntries1216', 'partitionSizeInSectors1216', 'clusterSizeSectors1216'],
|
||
calculate: function () {
|
||
const baseOffset = parseHex(document.getElementById('baseOffset1216').value);
|
||
const sectorSize = parseHex(document.getElementById('sectorSize1216').value);
|
||
const reservedSectors = parseHex(document.getElementById('reservedSectors1216').value);
|
||
const numFATs = parseHex(document.getElementById('numFATs1216').value);
|
||
const fatSizeBytes = parseHex(document.getElementById('fatSizeBytes1216').value);
|
||
const maxRootEntries = parseHex(document.getElementById('maxRootEntries1216').value);
|
||
const partitionSizeInSectors = parseHex(document.getElementById('partitionSizeInSectors1216').value);
|
||
const clusterSizeSectors = parseHex(document.getElementById('clusterSizeSectors1216').value);
|
||
const clusterNumber = parseHex(document.getElementById('clusterNumber1216').value);
|
||
|
||
const fatStart = reservedSectors * sectorSize + baseOffset;
|
||
const fatSize = numFATs * fatSizeBytes;
|
||
const fatEnd = fatStart + fatSize;
|
||
const rootDirStart = fatEnd;
|
||
const rootDirSize = maxRootEntries * 0x20;
|
||
const dataStart = fatEnd + rootDirSize;
|
||
const dataSize = partitionSizeInSectors * sectorSize - (dataStart - baseOffset);
|
||
const clusterSize = sectorSize * clusterSizeSectors;
|
||
const numClusters = clusterSize > 0 ? Math.floor(dataSize / clusterSize) : 0;
|
||
const clusterStart = dataStart + (clusterNumber - 0x2) * clusterSize;
|
||
|
||
const calculations = {
|
||
fatStart: `${reservedSectors.toString(16)} × ${sectorSize.toString(16)} + ${baseOffset.toString(16)} = ${fatStart.toString(16)}`,
|
||
fatSize: `${numFATs.toString(16)} × ${fatSizeBytes.toString(16)} = ${fatSize.toString(16)}`,
|
||
fatEnd: `${fatStart.toString(16)} + ${fatSize.toString(16)} = ${fatEnd.toString(16)}`,
|
||
rootDirStart: `${fatEnd.toString(16)}`,
|
||
rootDirSize: `${maxRootEntries.toString(16)} × 0x20 = ${rootDirSize.toString(16)}`,
|
||
dataStart: `${fatEnd.toString(16)} + ${rootDirSize.toString(16)} = ${dataStart.toString(16)}`,
|
||
dataSize: `(${partitionSizeInSectors.toString(16)} × ${sectorSize.toString(16)}) - (${dataStart.toString(16)} - ${baseOffset.toString(16)}) = ${dataSize.toString(16)}`,
|
||
clusterSize: `${sectorSize.toString(16)} × ${clusterSizeSectors.toString(16)} = ${clusterSize.toString(16)}`,
|
||
numClusters: `${dataSize.toString(16)} ÷ ${clusterSize.toString(16)} = ${numClusters.toString(16)}`,
|
||
clusterStart: `${dataStart.toString(16)} + (${clusterNumber.toString(16)} - 0x2) × ${clusterSize.toString(16)} = ${clusterStart.toString(16)}`
|
||
};
|
||
|
||
return {
|
||
fatStart, fatSize, fatEnd, rootDirStart, rootDirSize, dataStart, dataSize,
|
||
clusterSize, numClusters, clusterStart, calculations
|
||
};
|
||
}
|
||
},
|
||
fat32: {
|
||
requiredFields: ['reservedSectors32', 'numFATs32', 'fatSizeBytes32', 'rootDirCluster32', 'partitionSizeInSectors32', 'clusterSizeSectors32'],
|
||
calculate: function () {
|
||
const baseOffset = parseHex(document.getElementById('baseOffset32').value);
|
||
const sectorSize = parseHex(document.getElementById('sectorSize32').value);
|
||
const reservedSectors = parseHex(document.getElementById('reservedSectors32').value);
|
||
const numFATs = parseHex(document.getElementById('numFATs32').value);
|
||
const fatSizeBytes = parseHex(document.getElementById('fatSizeBytes32').value);
|
||
const rootDirCluster = parseHex(document.getElementById('rootDirCluster32').value);
|
||
const partitionSizeInSectors = parseHex(document.getElementById('partitionSizeInSectors32').value);
|
||
const clusterSizeSectors = parseHex(document.getElementById('clusterSizeSectors32').value);
|
||
const clusterNumber = parseHex(document.getElementById('clusterNumber32').value);
|
||
|
||
const fatStart = reservedSectors * sectorSize + baseOffset;
|
||
const fatSize = numFATs * fatSizeBytes;
|
||
const fatEnd = fatStart + fatSize;
|
||
const dataStart = fatEnd;
|
||
const dataSize = partitionSizeInSectors * sectorSize - (dataStart - baseOffset);
|
||
const clusterSize = sectorSize * clusterSizeSectors;
|
||
const numClusters = clusterSize > 0 ? Math.floor(dataSize / clusterSize) : 0;
|
||
const clusterStart = dataStart + (clusterNumber - 0x2) * clusterSize;
|
||
const rootDirPos = dataStart + (rootDirCluster - 0x2) * clusterSize;
|
||
|
||
const calculations = {
|
||
fatStart: `${reservedSectors.toString(16)} × ${sectorSize.toString(16)} + ${baseOffset.toString(16)} = ${fatStart.toString(16)}`,
|
||
fatSize: `${numFATs.toString(16)} × ${fatSizeBytes.toString(16)} = ${fatSize.toString(16)}`,
|
||
fatEnd: `${fatStart.toString(16)} + ${fatSize.toString(16)} = ${fatEnd.toString(16)}`,
|
||
dataStart: `${fatEnd.toString(16)}`,
|
||
dataSize: `(${partitionSizeInSectors.toString(16)} × ${sectorSize.toString(16)}) - (${dataStart.toString(16)} - ${baseOffset.toString(16)}) = ${dataSize.toString(16)}`,
|
||
clusterSize: `${sectorSize.toString(16)} × ${clusterSizeSectors.toString(16)} = ${clusterSize.toString(16)}`,
|
||
numClusters: `${dataSize.toString(16)} ÷ ${clusterSize.toString(16)} = ${numClusters.toString(16)}`,
|
||
clusterStart: `${dataStart.toString(16)} + (${clusterNumber.toString(16)} - 0x2) × ${clusterSize.toString(16)} = ${clusterStart.toString(16)}`,
|
||
rootDirPos: `${dataStart.toString(16)} + (${rootDirCluster.toString(16)} - 0x2) × ${clusterSize.toString(16)} = ${rootDirPos.toString(16)}`
|
||
};
|
||
|
||
return {
|
||
fatStart, fatSize, fatEnd, dataStart, dataSize,
|
||
clusterSize, numClusters, clusterStart, rootDirPos, calculations
|
||
};
|
||
}
|
||
}
|
||
};
|
||
|
||
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) ? 0 : parsed;
|
||
}
|
||
|
||
function formatHex(value) {
|
||
if (isNaN(value) || value < 0) {
|
||
return '<span class="error">Fehler</span>';
|
||
}
|
||
return '0x' + value.toString(16).toUpperCase();
|
||
}
|
||
|
||
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('Failed to copy to clipboard:', 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');
|
||
|
||
// Update calculator display when switching tabs
|
||
updateCalculatorDisplay(tabId);
|
||
}
|
||
|
||
function calculate(fsType) {
|
||
try {
|
||
const fs = filesystems[fsType];
|
||
if (!fs) {
|
||
alert('Unknown filesystem type: ' + fsType);
|
||
return;
|
||
}
|
||
|
||
const results = fs.calculate();
|
||
|
||
if (fsType === 'fat1216') {
|
||
const mapping = {
|
||
'fatStart1216': ['fatStart', 'fatStart'],
|
||
'fatSize1216': ['fatSize', 'fatSize'],
|
||
'fatEnd1216': ['fatEnd', 'fatEnd'],
|
||
'rootDirStart1216': ['rootDirStart', 'rootDirStart'],
|
||
'rootDirSize1216': ['rootDirSize', 'rootDirSize'],
|
||
'dataStart1216': ['dataStart', 'dataStart'],
|
||
'dataSize1216': ['dataSize', 'dataSize'],
|
||
'clusterSize1216': ['clusterSize', 'clusterSize'],
|
||
'numClusters1216': ['numClusters', 'numClusters'],
|
||
'clusterStart1216': ['clusterStart', 'clusterStart']
|
||
};
|
||
Object.entries(mapping).forEach(([elementId, [resultKey, calcKey]]) => {
|
||
const element = document.getElementById(elementId);
|
||
element.innerHTML = formatHex(results[resultKey]);
|
||
element.setAttribute('data-result-formula', results.calculations[calcKey]);
|
||
});
|
||
} else if (fsType === 'fat32') {
|
||
const mapping = {
|
||
'fatStart32': ['fatStart', 'fatStart'],
|
||
'fatSize32': ['fatSize', 'fatSize'],
|
||
'fatEnd32': ['fatEnd', 'fatEnd'],
|
||
'dataStart32': ['dataStart', 'dataStart'],
|
||
'dataSize32': ['dataSize', 'dataSize'],
|
||
'clusterSize32': ['clusterSize', 'clusterSize'],
|
||
'numClusters32': ['numClusters', 'numClusters'],
|
||
'clusterStart32': ['clusterStart', 'clusterStart'],
|
||
'rootDirPos32': ['rootDirPos', 'rootDirPos']
|
||
};
|
||
|
||
Object.entries(mapping).forEach(([elementId, [resultKey, calcKey]]) => {
|
||
const element = document.getElementById(elementId);
|
||
element.innerHTML = formatHex(results[resultKey]);
|
||
element.setAttribute('data-result-formula', results.calculations[calcKey]);
|
||
});
|
||
}
|
||
|
||
} catch (error) {
|
||
alert('Fehler bei der Berechnung: ' + error.message);
|
||
console.error('Calculation error:', error);
|
||
}
|
||
}
|
||
|
||
// Add event listeners
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
// Initialize calculator displays
|
||
updateCalculatorDisplay('fat1216');
|
||
updateCalculatorDisplay('fat32');
|
||
|
||
// Real-time calculation for required fields
|
||
Object.keys(filesystems).forEach(fsType => {
|
||
const fs = filesystems[fsType];
|
||
fs.requiredFields.forEach(fieldId => {
|
||
const input = document.getElementById(fieldId);
|
||
if (input) {
|
||
input.addEventListener('input', function () {
|
||
const allFilled = fs.requiredFields.every(id =>
|
||
document.getElementById(id).value.trim() !== ''
|
||
);
|
||
if (allFilled) {
|
||
calculate(fsType);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
// Real-time calculation for constants fields
|
||
['baseOffset1216', 'sectorSize1216'].forEach(fieldId => {
|
||
const input = document.getElementById(fieldId);
|
||
if (input) {
|
||
input.addEventListener('input', function () {
|
||
const fs = filesystems.fat1216;
|
||
const allFilled = fs.requiredFields.every(id =>
|
||
document.getElementById(id).value.trim() !== ''
|
||
);
|
||
if (allFilled) {
|
||
calculate('fat1216');
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
['baseOffset32', 'sectorSize32'].forEach(fieldId => {
|
||
const input = document.getElementById(fieldId);
|
||
if (input) {
|
||
input.addEventListener('input', function () {
|
||
const fs = filesystems.fat32;
|
||
const allFilled = fs.requiredFields.every(id =>
|
||
document.getElementById(id).value.trim() !== ''
|
||
);
|
||
if (allFilled) {
|
||
calculate('fat32');
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
// Add separate listeners for cluster number fields
|
||
['clusterNumber1216', 'clusterNumber32'].forEach(fieldId => {
|
||
const input = document.getElementById(fieldId);
|
||
if (input) {
|
||
input.addEventListener('input', function () {
|
||
const fsType = fieldId.includes('32') ? 'fat32' : 'fat1216';
|
||
const fs = filesystems[fsType];
|
||
const allFilled = fs.requiredFields.every(id =>
|
||
document.getElementById(id).value.trim() !== ''
|
||
);
|
||
if (allFilled) {
|
||
calculate(fsType);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
// Keyboard input support for calculator
|
||
document.addEventListener('keydown', function (e) {
|
||
// Only handle keyboard input when not in an input field
|
||
if (e.target.tagName === 'INPUT') return;
|
||
|
||
const currentTab = getCurrentTab();
|
||
const key = e.key.toUpperCase();
|
||
|
||
// Handle hex digits
|
||
if (/^[0-9A-F]$/.test(key)) {
|
||
e.preventDefault();
|
||
calcInput(key, currentTab);
|
||
}
|
||
// Handle operations
|
||
else if (key === '+') {
|
||
e.preventDefault();
|
||
calcOperation('+', currentTab);
|
||
}
|
||
else if (key === '-') {
|
||
e.preventDefault();
|
||
calcOperation('-', currentTab);
|
||
}
|
||
else if (key === '*') {
|
||
e.preventDefault();
|
||
calcOperation('*', currentTab);
|
||
}
|
||
else if (key === '/') {
|
||
e.preventDefault();
|
||
calcOperation('/', currentTab);
|
||
}
|
||
else if (key === 'ENTER' || key === '=') {
|
||
e.preventDefault();
|
||
calcEquals(currentTab);
|
||
}
|
||
else if (key === 'ESCAPE' || key === 'C') {
|
||
e.preventDefault();
|
||
calcClear(currentTab);
|
||
}
|
||
else if (key === 'BACKSPACE') {
|
||
e.preventDefault();
|
||
calcBackspace(currentTab);
|
||
}
|
||
// Handle shift operations
|
||
else if (e.shiftKey && key === ',') { // < key
|
||
e.preventDefault();
|
||
calcOperation('<<', currentTab);
|
||
}
|
||
else if (e.shiftKey && key === '.') { // > key
|
||
e.preventDefault();
|
||
calcOperation('>>', currentTab);
|
||
}
|
||
});
|
||
|
||
// Tooltip event listeners
|
||
document.addEventListener('mouseover', function (e) {
|
||
if (e.target.classList.contains('result-label')) {
|
||
const formula = e.target.getAttribute('data-formula');
|
||
if (formula) {
|
||
showTooltip(e.target, `Formula: ${formula}`);
|
||
}
|
||
} else if (e.target.classList.contains('result-value')) {
|
||
const formula = e.target.getAttribute('data-result-formula');
|
||
if (formula && formula !== '') {
|
||
showTooltip(e.target, `Calculation: ${formula}`);
|
||
}
|
||
}
|
||
});
|
||
|
||
document.addEventListener('mouseout', function (e) {
|
||
if (e.target.classList.contains('result-label') || e.target.classList.contains('result-value')) {
|
||
hideTooltip();
|
||
}
|
||
});
|
||
|
||
// Hide tooltip when scrolling
|
||
document.addEventListener('scroll', hideTooltip);
|
||
});
|
||
</script>
|
||
</body>
|
||
|
||
</html> |