updates to UX and NTFS
This commit is contained in:
		
							parent
							
								
									8cf8900784
								
							
						
					
					
						commit
						8fdb737ea8
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					CheatSheet.md
 | 
				
			||||||
@ -7,14 +7,14 @@
 | 
				
			|||||||
.calculator-container {
 | 
					.calculator-container {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
    gap: 12px;
 | 
					    gap: 10px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.calc-display {
 | 
					.calc-display {
 | 
				
			||||||
    background-color: #111111;
 | 
					    background-color: #111111;
 | 
				
			||||||
    border: 1px solid #404040;
 | 
					    border: 1px solid #404040;
 | 
				
			||||||
    border-radius: 6px;
 | 
					    border-radius: 6px;
 | 
				
			||||||
    padding: 12px;
 | 
					    padding: 10px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.calc-input input {
 | 
					.calc-input input {
 | 
				
			||||||
@ -24,24 +24,24 @@
 | 
				
			|||||||
    border-radius: 4px;
 | 
					    border-radius: 4px;
 | 
				
			||||||
    color: #ffffff;
 | 
					    color: #ffffff;
 | 
				
			||||||
    font-family: 'Consolas', monospace;
 | 
					    font-family: 'Consolas', monospace;
 | 
				
			||||||
    font-size: 16px;
 | 
					    font-size: 15px;
 | 
				
			||||||
    font-weight: bold;
 | 
					    font-weight: bold;
 | 
				
			||||||
    padding: 8px;
 | 
					    padding: 7px;
 | 
				
			||||||
    text-align: right;
 | 
					    text-align: right;
 | 
				
			||||||
    margin-bottom: 8px;
 | 
					    margin-bottom: 6px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.calc-conversions {
 | 
					.calc-conversions {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
    gap: 4px;
 | 
					    gap: 3px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.conversion-row {
 | 
					.conversion-row {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    justify-content: space-between;
 | 
					    justify-content: space-between;
 | 
				
			||||||
    align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
    padding: 3px 0;
 | 
					    padding: 2px 0;
 | 
				
			||||||
    border-bottom: 1px solid #333333;
 | 
					    border-bottom: 1px solid #333333;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -52,30 +52,30 @@
 | 
				
			|||||||
.conv-label {
 | 
					.conv-label {
 | 
				
			||||||
    color: #888888;
 | 
					    color: #888888;
 | 
				
			||||||
    font-weight: bold;
 | 
					    font-weight: bold;
 | 
				
			||||||
    font-size: 0.8em;
 | 
					    font-size: 0.75em;
 | 
				
			||||||
    min-width: 30px;
 | 
					    min-width: 30px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.conv-value {
 | 
					.conv-value {
 | 
				
			||||||
    color: #cccccc;
 | 
					    color: #cccccc;
 | 
				
			||||||
    font-family: 'Consolas', monospace;
 | 
					    font-family: 'Consolas', monospace;
 | 
				
			||||||
    font-size: 0.8em;
 | 
					    font-size: 0.75em;
 | 
				
			||||||
    word-break: break-all;
 | 
					    word-break: break-all;
 | 
				
			||||||
    text-align: right;
 | 
					    text-align: right;
 | 
				
			||||||
    flex: 1;
 | 
					    flex: 1;
 | 
				
			||||||
    margin-left: 10px;
 | 
					    margin-left: 8px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.calc-buttons {
 | 
					.calc-buttons {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
    gap: 4px;
 | 
					    gap: 3px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.calc-row {
 | 
					.calc-row {
 | 
				
			||||||
    display: grid;
 | 
					    display: grid;
 | 
				
			||||||
    grid-template-columns: repeat(5, 1fr);
 | 
					    grid-template-columns: repeat(5, 1fr);
 | 
				
			||||||
    gap: 4px;
 | 
					    gap: 3px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.calc-btn {
 | 
					.calc-btn {
 | 
				
			||||||
@ -85,10 +85,10 @@
 | 
				
			|||||||
    color: #ffffff;
 | 
					    color: #ffffff;
 | 
				
			||||||
    cursor: pointer;
 | 
					    cursor: pointer;
 | 
				
			||||||
    font-family: 'Consolas', monospace;
 | 
					    font-family: 'Consolas', monospace;
 | 
				
			||||||
    font-size: 0.8em;
 | 
					    font-size: 0.75em;
 | 
				
			||||||
    font-weight: bold;
 | 
					    font-weight: bold;
 | 
				
			||||||
    min-height: 32px;
 | 
					    min-height: 30px;
 | 
				
			||||||
    padding: 4px;
 | 
					    padding: 3px;
 | 
				
			||||||
    text-align: center;
 | 
					    text-align: center;
 | 
				
			||||||
    transition: all 0.2s;
 | 
					    transition: all 0.2s;
 | 
				
			||||||
    user-select: none;
 | 
					    user-select: none;
 | 
				
			||||||
@ -160,20 +160,37 @@
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.input-group label {
 | 
					.input-group label {
 | 
				
			||||||
    margin-bottom: 6px;
 | 
					    margin-bottom: 5px;
 | 
				
			||||||
    color: #cccccc;
 | 
					    color: #cccccc;
 | 
				
			||||||
    font-weight: normal;
 | 
					    font-weight: normal;
 | 
				
			||||||
    font-size: 0.95em;
 | 
					    font-size: 0.95em;
 | 
				
			||||||
 | 
					    white-space: nowrap;
 | 
				
			||||||
 | 
					    overflow: hidden;
 | 
				
			||||||
 | 
					    text-overflow: ellipsis;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.input-group label:hover {
 | 
				
			||||||
 | 
					    white-space: normal;
 | 
				
			||||||
 | 
					    overflow: visible;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.unit-indicator {
 | 
					.unit-indicator {
 | 
				
			||||||
    color: #88cc88;
 | 
					    color: #88cc88;
 | 
				
			||||||
    font-weight: bold;
 | 
					    font-weight: bold;
 | 
				
			||||||
    font-size: 0.85em;
 | 
					    font-size: 0.85em;
 | 
				
			||||||
 | 
					    white-space: nowrap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.offset-info {
 | 
				
			||||||
 | 
					    color: #8899cc;
 | 
				
			||||||
 | 
					    font-weight: normal;
 | 
				
			||||||
 | 
					    font-size: 0.85em;
 | 
				
			||||||
 | 
					    opacity: 0.8;
 | 
				
			||||||
 | 
					    font-style: italic;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.input-group input {
 | 
					.input-group input {
 | 
				
			||||||
    padding: 12px;
 | 
					    padding: 10px;
 | 
				
			||||||
    background-color: #333333;
 | 
					    background-color: #333333;
 | 
				
			||||||
    border: 1px solid #4a4a4a;
 | 
					    border: 1px solid #4a4a4a;
 | 
				
			||||||
    border-radius: 4px;
 | 
					    border-radius: 4px;
 | 
				
			||||||
@ -197,7 +214,7 @@
 | 
				
			|||||||
.error-message {
 | 
					.error-message {
 | 
				
			||||||
    color: #ff8888;
 | 
					    color: #ff8888;
 | 
				
			||||||
    font-size: 0.8em;
 | 
					    font-size: 0.8em;
 | 
				
			||||||
    margin-top: 4px;
 | 
					    margin-top: 3px;
 | 
				
			||||||
    font-style: italic;
 | 
					    font-style: italic;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -206,7 +223,7 @@
 | 
				
			|||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    justify-content: space-between;
 | 
					    justify-content: space-between;
 | 
				
			||||||
    align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
    padding: 10px 0;
 | 
					    padding: 8px 0;
 | 
				
			||||||
    border-bottom: 1px solid #3a3a3a;
 | 
					    border-bottom: 1px solid #3a3a3a;
 | 
				
			||||||
    position: relative;
 | 
					    position: relative;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -233,7 +250,7 @@
 | 
				
			|||||||
.result-value-container {
 | 
					.result-value-container {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
    gap: 8px;
 | 
					    gap: 6px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.result-value {
 | 
					.result-value {
 | 
				
			||||||
@ -241,7 +258,7 @@
 | 
				
			|||||||
    font-family: 'Consolas', monospace;
 | 
					    font-family: 'Consolas', monospace;
 | 
				
			||||||
    text-align: right;
 | 
					    text-align: right;
 | 
				
			||||||
    font-weight: bold;
 | 
					    font-weight: bold;
 | 
				
			||||||
    min-width: 120px;
 | 
					    min-width: 100px;
 | 
				
			||||||
    cursor: help;
 | 
					    cursor: help;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -250,10 +267,10 @@
 | 
				
			|||||||
    border: none;
 | 
					    border: none;
 | 
				
			||||||
    color: #888888;
 | 
					    color: #888888;
 | 
				
			||||||
    cursor: pointer;
 | 
					    cursor: pointer;
 | 
				
			||||||
    padding: 4px;
 | 
					    padding: 3px;
 | 
				
			||||||
    border-radius: 3px;
 | 
					    border-radius: 3px;
 | 
				
			||||||
    transition: all 0.2s;
 | 
					    transition: all 0.2s;
 | 
				
			||||||
    font-size: 14px;
 | 
					    font-size: 13px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.copy-btn:hover {
 | 
					.copy-btn:hover {
 | 
				
			||||||
@ -267,19 +284,19 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* Result Groups */
 | 
					/* Result Groups */
 | 
				
			||||||
.result-group {
 | 
					.result-group {
 | 
				
			||||||
    margin-bottom: 20px;
 | 
					    margin-bottom: 15px;
 | 
				
			||||||
    border: 1px solid #3a3a3a;
 | 
					    border: 1px solid #3a3a3a;
 | 
				
			||||||
    border-radius: 6px;
 | 
					    border-radius: 6px;
 | 
				
			||||||
    padding: 15px;
 | 
					    padding: 12px;
 | 
				
			||||||
    background-color: #2a2a2a;
 | 
					    background-color: #2a2a2a;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.result-group h3 {
 | 
					.result-group h3 {
 | 
				
			||||||
    color: #ffffff;
 | 
					    color: #ffffff;
 | 
				
			||||||
    margin-bottom: 10px;
 | 
					    margin-bottom: 8px;
 | 
				
			||||||
    font-size: 1.1em;
 | 
					    font-size: 1.05em;
 | 
				
			||||||
    border-bottom: 1px solid #3a3a3a;
 | 
					    border-bottom: 1px solid #3a3a3a;
 | 
				
			||||||
    padding-bottom: 5px;
 | 
					    padding-bottom: 4px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Formula Styles */
 | 
					/* Formula Styles */
 | 
				
			||||||
@ -287,14 +304,14 @@
 | 
				
			|||||||
    background-color: #151515;
 | 
					    background-color: #151515;
 | 
				
			||||||
    border: 1px solid #404040;
 | 
					    border: 1px solid #404040;
 | 
				
			||||||
    border-radius: 4px;
 | 
					    border-radius: 4px;
 | 
				
			||||||
    padding: 15px;
 | 
					    padding: 12px;
 | 
				
			||||||
    margin: 10px 0;
 | 
					    margin: 8px 0;
 | 
				
			||||||
    font-family: 'Consolas', monospace;
 | 
					    font-family: 'Consolas', monospace;
 | 
				
			||||||
    font-size: 0.9em;
 | 
					    font-size: 0.9em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.formula {
 | 
					.formula {
 | 
				
			||||||
    margin: 8px 0;
 | 
					    margin: 6px 0;
 | 
				
			||||||
    color: #d0d0d0;
 | 
					    color: #d0d0d0;
 | 
				
			||||||
    word-wrap: break-word;
 | 
					    word-wrap: break-word;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -309,7 +326,7 @@
 | 
				
			|||||||
    position: absolute;
 | 
					    position: absolute;
 | 
				
			||||||
    background-color: #1a1a1a;
 | 
					    background-color: #1a1a1a;
 | 
				
			||||||
    color: #ffffff;
 | 
					    color: #ffffff;
 | 
				
			||||||
    padding: 8px 12px;
 | 
					    padding: 6px 10px;
 | 
				
			||||||
    border-radius: 4px;
 | 
					    border-radius: 4px;
 | 
				
			||||||
    font-size: 0.85em;
 | 
					    font-size: 0.85em;
 | 
				
			||||||
    border: 1px solid #555555;
 | 
					    border: 1px solid #555555;
 | 
				
			||||||
@ -321,12 +338,45 @@
 | 
				
			|||||||
    line-height: 1.4;
 | 
					    line-height: 1.4;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Info Toast */
 | 
				
			||||||
 | 
					.info-toast {
 | 
				
			||||||
 | 
					    background: linear-gradient(135deg, #2c5aa0 0%, #1e4077 100%);
 | 
				
			||||||
 | 
					    border-bottom: 2px solid #4a7bc8;
 | 
				
			||||||
 | 
					    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
 | 
				
			||||||
 | 
					    padding: 10px 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.toast-content {
 | 
				
			||||||
 | 
					    max-width: 1400px;
 | 
				
			||||||
 | 
					    margin: 0 auto;
 | 
				
			||||||
 | 
					    padding: 0 15px;
 | 
				
			||||||
 | 
					    color: #ffffff;
 | 
				
			||||||
 | 
					    font-size: 0.95em;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    line-height: 1.5;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.toast-content a {
 | 
				
			||||||
 | 
					    color: #ffd700;
 | 
				
			||||||
 | 
					    text-decoration: none;
 | 
				
			||||||
 | 
					    border-bottom: 1px solid transparent;
 | 
				
			||||||
 | 
					    transition: border-color 0.2s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.toast-content a:hover {
 | 
				
			||||||
 | 
					    border-bottom-color: #ffd700;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.toast-content strong {
 | 
				
			||||||
 | 
					    font-weight: bold;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Mobile Responsive */
 | 
					/* Mobile Responsive */
 | 
				
			||||||
@media (max-width: 768px) {
 | 
					@media (max-width: 768px) {
 | 
				
			||||||
    .result-item {
 | 
					    .result-item {
 | 
				
			||||||
        flex-direction: column;
 | 
					        flex-direction: column;
 | 
				
			||||||
        align-items: flex-start;
 | 
					        align-items: flex-start;
 | 
				
			||||||
        gap: 5px;
 | 
					        gap: 4px;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .result-value {
 | 
					    .result-value {
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ body {
 | 
				
			|||||||
    font-family: 'Consolas', 'Courier New', monospace;
 | 
					    font-family: 'Consolas', 'Courier New', monospace;
 | 
				
			||||||
    background-color: #1a1a1a;
 | 
					    background-color: #1a1a1a;
 | 
				
			||||||
    color: #e0e0e0;
 | 
					    color: #e0e0e0;
 | 
				
			||||||
    line-height: 1.6;
 | 
					    line-height: 1.5;
 | 
				
			||||||
    min-height: 100vh;
 | 
					    min-height: 100vh;
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
@ -18,13 +18,13 @@ body {
 | 
				
			|||||||
.header {
 | 
					.header {
 | 
				
			||||||
    background-color: #2a2a2a;
 | 
					    background-color: #2a2a2a;
 | 
				
			||||||
    border-bottom: 2px solid #3a3a3a;
 | 
					    border-bottom: 2px solid #3a3a3a;
 | 
				
			||||||
    padding: 15px 0;
 | 
					    padding: 10px 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.header-content {
 | 
					.header-content {
 | 
				
			||||||
    max-width: 1400px;
 | 
					    max-width: 1400px;
 | 
				
			||||||
    margin: 0 auto;
 | 
					    margin: 0 auto;
 | 
				
			||||||
    padding: 0 20px;
 | 
					    padding: 0 15px;
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    justify-content: space-between;
 | 
					    justify-content: space-between;
 | 
				
			||||||
    align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
@ -32,14 +32,14 @@ body {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.logo {
 | 
					.logo {
 | 
				
			||||||
    font-size: 1.5em;
 | 
					    font-size: 1.3em;
 | 
				
			||||||
    font-weight: bold;
 | 
					    font-weight: bold;
 | 
				
			||||||
    color: #ffffff;
 | 
					    color: #ffffff;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.nav {
 | 
					.nav {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    gap: 20px;
 | 
					    gap: 15px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.nav a {
 | 
					.nav a {
 | 
				
			||||||
@ -55,7 +55,7 @@ body {
 | 
				
			|||||||
/* Main Content */
 | 
					/* Main Content */
 | 
				
			||||||
.main-content {
 | 
					.main-content {
 | 
				
			||||||
    flex: 1;
 | 
					    flex: 1;
 | 
				
			||||||
    padding: 20px;
 | 
					    padding: 12px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.container {
 | 
					.container {
 | 
				
			||||||
@ -65,9 +65,9 @@ body {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
h1 {
 | 
					h1 {
 | 
				
			||||||
    text-align: center;
 | 
					    text-align: center;
 | 
				
			||||||
    margin-bottom: 30px;
 | 
					    margin-bottom: 15px;
 | 
				
			||||||
    color: #ffffff;
 | 
					    color: #ffffff;
 | 
				
			||||||
    font-size: 2.2em;
 | 
					    font-size: 1.8em;
 | 
				
			||||||
    font-weight: normal;
 | 
					    font-weight: normal;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -82,14 +82,14 @@ h1 {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.tab {
 | 
					.tab {
 | 
				
			||||||
    padding: 15px 25px;
 | 
					    padding: 10px 20px;
 | 
				
			||||||
    cursor: pointer;
 | 
					    cursor: pointer;
 | 
				
			||||||
    background-color: #2a2a2a;
 | 
					    background-color: #2a2a2a;
 | 
				
			||||||
    border-right: 1px solid #3a3a3a;
 | 
					    border-right: 1px solid #3a3a3a;
 | 
				
			||||||
    color: #cccccc;
 | 
					    color: #cccccc;
 | 
				
			||||||
    transition: all 0.2s;
 | 
					    transition: all 0.2s;
 | 
				
			||||||
    white-space: nowrap;
 | 
					    white-space: nowrap;
 | 
				
			||||||
    min-width: 120px;
 | 
					    min-width: 100px;
 | 
				
			||||||
    text-align: center;
 | 
					    text-align: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -112,7 +112,7 @@ h1 {
 | 
				
			|||||||
    background-color: #2a2a2a;
 | 
					    background-color: #2a2a2a;
 | 
				
			||||||
    border: 1px solid #3a3a3a;
 | 
					    border: 1px solid #3a3a3a;
 | 
				
			||||||
    border-radius: 0 0 6px 6px;
 | 
					    border-radius: 0 0 6px 6px;
 | 
				
			||||||
    padding: 20px;
 | 
					    padding: 12px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.tab-content.active {
 | 
					.tab-content.active {
 | 
				
			||||||
@ -123,25 +123,22 @@ h1 {
 | 
				
			|||||||
.section {
 | 
					.section {
 | 
				
			||||||
    background-color: #252525;
 | 
					    background-color: #252525;
 | 
				
			||||||
    border-radius: 6px;
 | 
					    border-radius: 6px;
 | 
				
			||||||
    padding: 20px;
 | 
					    padding: 12px;
 | 
				
			||||||
    margin-bottom: 20px;
 | 
					    margin-bottom: 12px;
 | 
				
			||||||
    border: 1px solid #3a3a3a;
 | 
					    border: 1px solid #3a3a3a;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.section h2 {
 | 
					.section h2 {
 | 
				
			||||||
    color: #cccccc;
 | 
					    color: #cccccc;
 | 
				
			||||||
    margin-bottom: 15px;
 | 
					    margin-bottom: 10px;
 | 
				
			||||||
    border-bottom: 1px solid #3a3a3a;
 | 
					    border-bottom: 1px solid #3a3a3a;
 | 
				
			||||||
    padding-bottom: 8px;
 | 
					    padding-bottom: 6px;
 | 
				
			||||||
    font-weight: normal;
 | 
					    font-weight: normal;
 | 
				
			||||||
    font-size: 1.3em;
 | 
					    font-size: 1.2em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.constants {
 | 
					/* Merged constant and result sections - same background */
 | 
				
			||||||
    background-color: #202020;
 | 
					.constants, .results {
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.results {
 | 
					 | 
				
			||||||
    background-color: #202020;
 | 
					    background-color: #202020;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -152,30 +149,36 @@ h1 {
 | 
				
			|||||||
/* Layout */
 | 
					/* Layout */
 | 
				
			||||||
.constants-calculator-container {
 | 
					.constants-calculator-container {
 | 
				
			||||||
    display: grid;
 | 
					    display: grid;
 | 
				
			||||||
    grid-template-columns: 1fr 350px;
 | 
					    grid-template-columns: 1fr 320px;
 | 
				
			||||||
    gap: 20px;
 | 
					    gap: 12px;
 | 
				
			||||||
    margin-bottom: 20px;
 | 
					    margin-bottom: 12px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@media (max-width: 1024px) {
 | 
					@media (max-width: 1024px) {
 | 
				
			||||||
    .constants-calculator-container {
 | 
					    .constants-calculator-container {
 | 
				
			||||||
        grid-template-columns: 1fr;
 | 
					        grid-template-columns: 1fr;
 | 
				
			||||||
        gap: 15px;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Input Grid */
 | 
					/* Input Grid */
 | 
				
			||||||
.input-grid {
 | 
					.input-grid {
 | 
				
			||||||
    display: grid;
 | 
					    display: grid;
 | 
				
			||||||
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
 | 
					    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
 | 
				
			||||||
    gap: 15px;
 | 
					    gap: 10px;
 | 
				
			||||||
    margin: 20px 0;
 | 
					    margin: 12px 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.constants .input-grid {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    gap: 10px;
 | 
				
			||||||
 | 
					    max-width: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@media (max-width: 768px) {
 | 
					@media (max-width: 768px) {
 | 
				
			||||||
    .input-grid {
 | 
					    .input-grid {
 | 
				
			||||||
        grid-template-columns: 1fr;
 | 
					        grid-template-columns: 1fr;
 | 
				
			||||||
        gap: 12px;
 | 
					        gap: 10px;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    .constants-calculator-container {
 | 
					    .constants-calculator-container {
 | 
				
			||||||
@ -187,23 +190,23 @@ h1 {
 | 
				
			|||||||
.footer {
 | 
					.footer {
 | 
				
			||||||
    background-color: #2a2a2a;
 | 
					    background-color: #2a2a2a;
 | 
				
			||||||
    border-top: 1px solid #3a3a3a;
 | 
					    border-top: 1px solid #3a3a3a;
 | 
				
			||||||
    padding: 20px 0;
 | 
					    padding: 15px 0;
 | 
				
			||||||
    margin-top: 40px;
 | 
					    margin-top: 20px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.footer-content {
 | 
					.footer-content {
 | 
				
			||||||
    max-width: 1400px;
 | 
					    max-width: 1400px;
 | 
				
			||||||
    margin: 0 auto;
 | 
					    margin: 0 auto;
 | 
				
			||||||
    padding: 0 20px;
 | 
					    padding: 0 15px;
 | 
				
			||||||
    display: grid;
 | 
					    display: grid;
 | 
				
			||||||
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
 | 
					    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
 | 
				
			||||||
    gap: 30px;
 | 
					    gap: 20px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.footer-section h3 {
 | 
					.footer-section h3 {
 | 
				
			||||||
    color: #ffffff;
 | 
					    color: #ffffff;
 | 
				
			||||||
    margin-bottom: 10px;
 | 
					    margin-bottom: 8px;
 | 
				
			||||||
    font-size: 1.1em;
 | 
					    font-size: 1.05em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.footer-section p,
 | 
					.footer-section p,
 | 
				
			||||||
@ -211,7 +214,7 @@ h1 {
 | 
				
			|||||||
    color: #cccccc;
 | 
					    color: #cccccc;
 | 
				
			||||||
    font-size: 0.9em;
 | 
					    font-size: 0.9em;
 | 
				
			||||||
    text-decoration: none;
 | 
					    text-decoration: none;
 | 
				
			||||||
    margin-bottom: 5px;
 | 
					    margin-bottom: 4px;
 | 
				
			||||||
    display: block;
 | 
					    display: block;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -221,8 +224,8 @@ h1 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.footer-bottom {
 | 
					.footer-bottom {
 | 
				
			||||||
    text-align: center;
 | 
					    text-align: center;
 | 
				
			||||||
    padding-top: 20px;
 | 
					    padding-top: 15px;
 | 
				
			||||||
    margin-top: 20px;
 | 
					    margin-top: 15px;
 | 
				
			||||||
    border-top: 1px solid #3a3a3a;
 | 
					    border-top: 1px solid #3a3a3a;
 | 
				
			||||||
    color: #888888;
 | 
					    color: #888888;
 | 
				
			||||||
    font-size: 0.85em;
 | 
					    font-size: 0.85em;
 | 
				
			||||||
 | 
				
			|||||||
@ -20,9 +20,16 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </header>
 | 
					    </header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="info-toast">
 | 
				
			||||||
 | 
					        <div class="toast-content">
 | 
				
			||||||
 | 
					            💡 <strong>Tipp:</strong> Erstellen Sie eigene Dateisystem-Images mit 
 | 
				
			||||||
 | 
					            <a href="https://github.com/overcuriousity/pseudodisk" target="_blank" rel="noopener"><strong>pseudodisk</strong></a> 
 | 
				
			||||||
 | 
					            – einem interaktiven Bash-Script zum Erstellen, Mounten und Analysieren von Dateisystemen via mkfs und Hex-Editor.
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <main class="main-content">
 | 
					    <main class="main-content">
 | 
				
			||||||
        <div class="container">
 | 
					        <div class="container">
 | 
				
			||||||
            <h1>Dateisystem-Offset-Rechner</h1>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Dynamic tabs will be inserted here -->
 | 
					            <!-- Dynamic tabs will be inserted here -->
 | 
				
			||||||
            <div id="filesystem-tabs" class="tabs"></div>
 | 
					            <div id="filesystem-tabs" class="tabs"></div>
 | 
				
			||||||
@ -37,7 +44,7 @@
 | 
				
			|||||||
            <div class="footer-section">
 | 
					            <div class="footer-section">
 | 
				
			||||||
                <h3>Über</h3>
 | 
					                <h3>Über</h3>
 | 
				
			||||||
                <p>Dateisystem Offset-Rechner für Bildung und Forensik.</p>
 | 
					                <p>Dateisystem Offset-Rechner für Bildung und Forensik.</p>
 | 
				
			||||||
                <p>Version 0.4.0</p>
 | 
					                <p>Version 0.5.0</p>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div class="footer-section">
 | 
					            <div class="footer-section">
 | 
				
			||||||
                <h3>Support</h3>
 | 
					                <h3>Support</h3>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,6 @@
 | 
				
			|||||||
// Base filesystem class that defines the common interface for all filesystem implementations
 | 
					
 | 
				
			||||||
 | 
					// Base filesystem class that defines the common interface for all filesystem implementations.
 | 
				
			||||||
 | 
					// Now supports multi-value result rows (bytes, sectors, etc.) in a single output line.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { parseHex, validateInput, checkDependencies, updateResultItem } from '../utils.js';
 | 
					import { parseHex, validateInput, checkDependencies, updateResultItem } from '../utils.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -18,13 +20,20 @@ export class BaseFilesystem {
 | 
				
			|||||||
        const variant = this.variants.find(v => v.id === variantId);
 | 
					        const variant = this.variants.find(v => v.id === variantId);
 | 
				
			||||||
        if (!variant) return '';
 | 
					        if (!variant) return '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Helper function to format labels with offset information
 | 
				
			||||||
 | 
					        const formatLabel = (label) => {
 | 
				
			||||||
 | 
					            // Match patterns like "(Boot-Offset 0x00)", "(MFT-Header-Offset 0x14)", "(Offset 0x00)" and wrap offset info
 | 
				
			||||||
 | 
					            const offsetPattern = /(\((Boot-Offset|MFT-Header-Offset|Offset) [^)]+\))/g;
 | 
				
			||||||
 | 
					            return label.replace(offsetPattern, '<span class="offset-info">$1</span>');
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return `
 | 
					        return `
 | 
				
			||||||
            <div class="section constants">
 | 
					            <div class="section constants">
 | 
				
			||||||
                <h2>Konstanten</h2>
 | 
					                <h2>Konstanten</h2>
 | 
				
			||||||
                <div class="input-grid">
 | 
					                <div class="input-grid">
 | 
				
			||||||
                    ${variant.constants.map(constant => `
 | 
					                    ${variant.constants.map(constant => `
 | 
				
			||||||
                        <div class="input-group">
 | 
					                        <div class="input-group">
 | 
				
			||||||
                            <label for="${constant.id}">${constant.label} <span class="unit-indicator">(${constant.unit})</span>:</label>
 | 
					                            <label for="${constant.id}">${formatLabel(constant.label)} <span class="unit-indicator">(${constant.unit})</span>:</label>
 | 
				
			||||||
                            <input type="text" id="${constant.id}" value="${constant.default}" placeholder="${constant.default}">
 | 
					                            <input type="text" id="${constant.id}" value="${constant.default}" placeholder="${constant.default}">
 | 
				
			||||||
                            <div class="error-message" id="${constant.id}-error"></div>
 | 
					                            <div class="error-message" id="${constant.id}-error"></div>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
@ -34,18 +43,32 @@ export class BaseFilesystem {
 | 
				
			|||||||
        `;
 | 
					        `;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Generate HTML for timestamp converter section (to be overridden by subclasses)
 | 
				
			||||||
 | 
					    generateTimestampConverterHTML(variantId) {
 | 
				
			||||||
 | 
					        // Default implementation returns empty string
 | 
				
			||||||
 | 
					        // Subclasses can override to provide filesystem-specific converters
 | 
				
			||||||
 | 
					        return '';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Generate HTML for input parameters section
 | 
					    // Generate HTML for input parameters section
 | 
				
			||||||
    generateInputsHTML(variantId) {
 | 
					    generateInputsHTML(variantId) {
 | 
				
			||||||
        const variant = this.variants.find(v => v.id === variantId);
 | 
					        const variant = this.variants.find(v => v.id === variantId);
 | 
				
			||||||
        if (!variant) return '';
 | 
					        if (!variant) return '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Helper function to format labels with offset information
 | 
				
			||||||
 | 
					        const formatLabel = (label) => {
 | 
				
			||||||
 | 
					            // Match patterns like "(Boot-Offset 0x00)", "(MFT-Header-Offset 0x14)", "(Offset 0x00)" and wrap offset info
 | 
				
			||||||
 | 
					            const offsetPattern = /(\((Boot-Offset|MFT-Header-Offset|Offset) [^)]+\))/g;
 | 
				
			||||||
 | 
					            return label.replace(offsetPattern, '<span class="offset-info">$1</span>');
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return `
 | 
					        return `
 | 
				
			||||||
            <div class="section">
 | 
					            <div class="section">
 | 
				
			||||||
                <h2>Eingabeparameter</h2>
 | 
					                <h2>Eingabeparameter</h2>
 | 
				
			||||||
                <div class="input-grid">
 | 
					                <div class="input-grid">
 | 
				
			||||||
                    ${variant.inputs.map(input => `
 | 
					                    ${variant.inputs.map(input => `
 | 
				
			||||||
                        <div class="input-group">
 | 
					                        <div class="input-group">
 | 
				
			||||||
                            <label for="${input.id}">${input.label} <span class="unit-indicator">(${input.unit})</span>:</label>
 | 
					                            <label for="${input.id}">${formatLabel(input.label)} <span class="unit-indicator">(${input.unit})</span>:</label>
 | 
				
			||||||
                            <input type="text" id="${input.id}" placeholder="${input.placeholder}">
 | 
					                            <input type="text" id="${input.id}" placeholder="${input.placeholder}">
 | 
				
			||||||
                            <div class="error-message" id="${input.id}-error"></div>
 | 
					                            <div class="error-message" id="${input.id}-error"></div>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
@ -60,13 +83,27 @@ export class BaseFilesystem {
 | 
				
			|||||||
        const variant = this.variants.find(v => v.id === variantId);
 | 
					        const variant = this.variants.find(v => v.id === variantId);
 | 
				
			||||||
        if (!variant) return '';
 | 
					        if (!variant) return '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Helper: get display units for a result (bytes, sectors, ...)
 | 
				
			||||||
 | 
					        function getDisplayUnits(result) {
 | 
				
			||||||
 | 
					            // Heuristic: if label contains (Bytes) or (Sektor), show both
 | 
				
			||||||
 | 
					            const label = result.label.toLowerCase();
 | 
				
			||||||
 | 
					            const units = [];
 | 
				
			||||||
 | 
					            if (label.includes('bytes') || label.includes('byte')) units.push('bytes');
 | 
				
			||||||
 | 
					            if (label.includes('sektor')) units.push('sectors');
 | 
				
			||||||
 | 
					            // Add more as needed
 | 
				
			||||||
 | 
					            return units;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return `
 | 
					        return `
 | 
				
			||||||
            <div class="section results">
 | 
					            <div class="section results">
 | 
				
			||||||
                <h2>Berechnete Werte</h2>
 | 
					                <h2>Berechnete Werte</h2>
 | 
				
			||||||
                ${variant.resultGroups.map(group => `
 | 
					                ${variant.resultGroups.map(group => `
 | 
				
			||||||
                    <div class="result-group">
 | 
					                    <div class="result-group">
 | 
				
			||||||
                        <h3>${group.name}</h3>
 | 
					                        <h3>${group.name}</h3>
 | 
				
			||||||
                        ${group.results.map(result => `
 | 
					                        ${group.results.map(result => {
 | 
				
			||||||
 | 
					                            // For each result, show all representations in one row
 | 
				
			||||||
 | 
					                            // The calculation logic must fill in all values in the result-value element, separated by //
 | 
				
			||||||
 | 
					                            return `
 | 
				
			||||||
                                <div class="result-item" data-deps="${result.dependencies.join(',')}">
 | 
					                                <div class="result-item" data-deps="${result.dependencies.join(',')}">
 | 
				
			||||||
                                    <span class="result-label" data-formula="${result.formula}">${result.label}:</span>
 | 
					                                    <span class="result-label" data-formula="${result.formula}">${result.label}:</span>
 | 
				
			||||||
                                    <div class="result-value-container">
 | 
					                                    <div class="result-value-container">
 | 
				
			||||||
@ -74,7 +111,8 @@ export class BaseFilesystem {
 | 
				
			|||||||
                                        <button class="copy-btn" onclick="copyToClipboard('${result.id}')">📋</button>
 | 
					                                        <button class="copy-btn" onclick="copyToClipboard('${result.id}')">📋</button>
 | 
				
			||||||
                                    </div>
 | 
					                                    </div>
 | 
				
			||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
                        `).join('')}
 | 
					                            `;
 | 
				
			||||||
 | 
					                        }).join('')}
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                `).join('')}
 | 
					                `).join('')}
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
@ -100,10 +138,15 @@ export class BaseFilesystem {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Generate complete tab content HTML
 | 
					    // Generate complete tab content HTML
 | 
				
			||||||
    generateTabContentHTML(variantId, calculatorHTML) {
 | 
					    generateTabContentHTML(variantId, calculatorHTML) {
 | 
				
			||||||
 | 
					        const timestampHTML = this.generateTimestampConverterHTML(variantId);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        return `
 | 
					        return `
 | 
				
			||||||
            <div class="tab-content" id="${variantId}">
 | 
					            <div class="tab-content" id="${variantId}">
 | 
				
			||||||
                <div class="constants-calculator-container">
 | 
					                <div class="constants-calculator-container">
 | 
				
			||||||
 | 
					                    <div class="constants-tools-wrapper">
 | 
				
			||||||
                        ${this.generateConstantsHTML(variantId)}
 | 
					                        ${this.generateConstantsHTML(variantId)}
 | 
				
			||||||
 | 
					                        ${timestampHTML}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
                    <div class="section hex-calculator">
 | 
					                    <div class="section hex-calculator">
 | 
				
			||||||
                        <h2>Hex-Rechner</h2>
 | 
					                        <h2>Hex-Rechner</h2>
 | 
				
			||||||
                        ${calculatorHTML}
 | 
					                        ${calculatorHTML}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,61 +1,80 @@
 | 
				
			|||||||
// FAT filesystem implementation (FAT12/16 and FAT32)
 | 
					// FAT filesystem implementation (FAT12/16 and FAT32)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FAT filesystem implementation (FAT12/16 and FAT32 split)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { BaseFilesystem } from './base.js';
 | 
					import { BaseFilesystem } from './base.js';
 | 
				
			||||||
import { checkDependencies, updateResultItem } from '../utils.js';
 | 
					import { checkDependencies, updateResultItem } from '../utils.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class FATFilesystem extends BaseFilesystem {
 | 
					export class FAT12_16Filesystem extends BaseFilesystem {
 | 
				
			||||||
 | 
					    calculate(variantId) {
 | 
				
			||||||
 | 
					        if (variantId !== 'fat1216') return;
 | 
				
			||||||
 | 
					        const values = {};
 | 
				
			||||||
 | 
					        const results = {};
 | 
				
			||||||
 | 
					        // Gather all input values for this variant
 | 
				
			||||||
 | 
					        const variant = this.variants.find(v => v.id === variantId);
 | 
				
			||||||
 | 
					        if (!variant) return;
 | 
				
			||||||
 | 
					        [...variant.constants, ...variant.inputs].forEach(field => {
 | 
				
			||||||
 | 
					            const el = document.getElementById(field.id);
 | 
				
			||||||
 | 
					            if (el) {
 | 
				
			||||||
 | 
					                let val = el.value;
 | 
				
			||||||
 | 
					                if (val && val.startsWith('0x')) {
 | 
				
			||||||
 | 
					                    values[field.id] = parseInt(val, 16);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    values[field.id] = Number(val) || 0;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        this.calculateFAT1216(values, results);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        super('FAT', [
 | 
					        super('FAT12/16', [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                id: 'fat1216',
 | 
					                id: 'fat1216',
 | 
				
			||||||
                name: 'FAT12/16',
 | 
					                name: 'FAT12/16',
 | 
				
			||||||
                constants: [
 | 
					                constants: [
 | 
				
			||||||
                    { id: 'baseOffset1216', label: 'Basis-Offset', unit: 'Bytes', default: '0x0' },
 | 
					                    { id: 'baseOffset1216', label: 'Basis-Offset', unit: 'Bytes', default: '0x0' },
 | 
				
			||||||
                    { id: 'sectorSize1216', label: 'Sektorgröße', unit: 'Bytes', default: '0x200' }
 | 
					                    { id: 'sectorSize1216', label: 'Sektor-Größe (Boot-Offset 0x0B)', unit: 'Bytes', default: '0x200' }
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
                inputs: [
 | 
					                inputs: [
 | 
				
			||||||
                    { id: 'reservedSectors1216', label: 'Reservierte Sektoren', unit: 'Anzahl', placeholder: '0x1' },
 | 
					                    { id: 'reservedSektoren1216', label: 'Reservierte Sektoren (Boot-Offset 0x0E)', unit: 'Anzahl', placeholder: '0x1' },
 | 
				
			||||||
                    { id: 'numFATs1216', label: 'Anzahl FATs', unit: 'Anzahl', placeholder: '0x2' },
 | 
					                    { id: 'numFATs1216', label: 'Anzahl FATs (Boot-Offset 0x10)', unit: 'Anzahl', placeholder: '0x2' },
 | 
				
			||||||
                    { id: 'fatSizeSectors1216', label: 'FAT-Größe', unit: 'Sektoren', placeholder: '0x4000' },
 | 
					                    { id: 'fatSizeSectors1216', label: 'FAT-Größe (Boot-Offset 0x16)', unit: 'Sektoren', placeholder: '0x4000' },
 | 
				
			||||||
                    { id: 'maxRootEntries1216', label: 'Max. Root-Einträge', unit: 'Anzahl', placeholder: '0x200' },
 | 
					                    { id: 'maxRootEntries1216', label: 'Max. Root-Einträge (Boot-Offset 0x11)', unit: 'Anzahl', placeholder: '0x200' },
 | 
				
			||||||
                    { id: 'partitionSizeInSectors1216', label: 'Partitionsgröße', unit: 'Sektoren', placeholder: '0x4015' },
 | 
					                    { id: 'partitionSizeInSectors1216', label: 'Partitionsgröße (Boot-Offset 0x13 oder 0x20)', unit: 'Sektoren', placeholder: '0x4015' },
 | 
				
			||||||
                    { id: 'clusterSizeSectors1216', label: 'Clustergröße', unit: 'Sektoren', placeholder: '0x2' },
 | 
					                    { id: 'clusterSizeSectors1216', label: 'Clustergröße (Boot-Offset 0x0D)', unit: 'Sektoren', placeholder: '0x2' },
 | 
				
			||||||
                    { id: 'clusterNumber1216', label: 'Cluster-Nummer', unit: 'Nummer', placeholder: '0x2' }
 | 
					                    { id: 'clusterNumber1216', label: 'Cluster-Nummer', unit: 'Nummer', placeholder: '0x2' }
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
                resultGroups: [
 | 
					                resultGroups: [
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'FAT-Struktur',
 | 
					                        name: 'FAT-Struktur',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'fatStart1216', label: 'FAT-Bereich Anfang (Bytes)', dependencies: ['reservedSectors1216', 'sectorSize1216', 'baseOffset1216'], formula: 'reservedSectors × sectorSize + baseOffset' },
 | 
					                            { id: 'fatStart1216', label: 'FAT-Bereich Anfang', dependencies: ['reservedSektoren1216', 'sectorSize1216', 'baseOffset1216'], formula: 'reservedSektoren × sectorSize + baseOffset' },
 | 
				
			||||||
                            { id: 'fatStartSector1216', label: 'FAT-Bereich Anfang (Sektor)', dependencies: ['reservedSectors1216', 'sectorSize1216', 'baseOffset1216'], formula: 'fatStart ÷ sectorSize' },
 | 
					                            { id: 'fatSize1216', label: 'FAT-Bereich Größe', dependencies: ['numFATs1216', 'fatSizeSectors1216', 'sectorSize1216'], formula: 'numFATs × fatSizeSectors × sectorSize' },
 | 
				
			||||||
                            { id: 'fatSize1216', label: 'FAT-Bereich Größe (Bytes)', dependencies: ['numFATs1216', 'fatSizeSectors1216', 'sectorSize1216'], formula: 'numFATs × fatSizeSectors × sectorSize' },
 | 
					                            { id: 'fat2Start1216', label: 'FAT2 Anfang', dependencies: ['reservedSektoren1216', 'sectorSize1216', 'baseOffset1216', 'fatSizeSectors1216'], formula: 'fatStart + (fatSizeSectors × sectorSize)' },
 | 
				
			||||||
                            { id: 'fat2Start1216', label: 'FAT2 Anfang (Bytes)', dependencies: ['reservedSectors1216', 'sectorSize1216', 'baseOffset1216', 'fatSizeSectors1216'], formula: 'fatStart + (fatSizeSectors × sectorSize)' },
 | 
					                            { id: 'fatEnd1216', label: 'FAT-Bereich Ende', dependencies: ['reservedSektoren1216', 'sectorSize1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216'], formula: 'fatStart + fatSize' }
 | 
				
			||||||
                            { id: 'fatEnd1216', label: 'FAT-Bereich Ende (Bytes)', dependencies: ['reservedSectors1216', 'sectorSize1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216'], formula: 'fatStart + fatSize' }
 | 
					 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'Root-Directory',
 | 
					                        name: 'Root-Directory',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'rootDirStart1216', label: 'Root-Directory Anfang (Bytes)', dependencies: ['reservedSectors1216', 'sectorSize1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216'], formula: 'fatEnd' },
 | 
					                            { id: 'rootDirStart1216', label: 'Root-Directory Anfang', dependencies: ['reservedSektoren1216', 'sectorSize1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216'], formula: 'fatEnd' },
 | 
				
			||||||
                            { id: 'rootDirSize1216', label: 'Root-Directory Größe (Bytes)', dependencies: ['maxRootEntries1216'], formula: 'maxRootEntries × 0x20' }
 | 
					                            { id: 'rootDirSize1216', label: 'Root-Directory Größe', dependencies: ['maxRootEntries1216'], formula: 'maxRootEntries × 0x20' }
 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'Daten-Bereich',
 | 
					                        name: 'Daten-Bereich',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'dataStart1216', label: 'Daten-Bereich Anfang (Bytes)', dependencies: ['reservedSectors1216', 'sectorSize1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216'], formula: 'fatEnd + rootDirSize' },
 | 
					                            { id: 'dataStart1216', label: 'Daten-Bereich Anfang', dependencies: ['reservedSektoren1216', 'sectorSize1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216'], formula: 'fatEnd + rootDirSize' },
 | 
				
			||||||
                            { id: 'dataStartSector1216', label: 'Daten-Bereich Anfang (Sektor)', dependencies: ['reservedSectors1216', 'sectorSize1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216'], formula: 'dataStart ÷ sectorSize' },
 | 
					                            { id: 'dataSize1216', label: 'Daten-Bereich Größe', dependencies: ['partitionSizeInSectors1216', 'sectorSize1216', 'reservedSektoren1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216'], formula: '(partitionSizeInSectors × sectorSize) - (dataStart - baseOffset)' },
 | 
				
			||||||
                            { id: 'dataSize1216', label: 'Daten-Bereich Größe (Bytes)', dependencies: ['partitionSizeInSectors1216', 'sectorSize1216', 'reservedSectors1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216'], formula: '(partitionSizeInSectors × sectorSize) - (dataStart - baseOffset)' },
 | 
					                            { id: 'clusterSize1216', label: 'Cluster-Größe', dependencies: ['sectorSize1216', 'clusterSizeSectors1216'], formula: 'sectorSize × clusterSizeSectors' },
 | 
				
			||||||
                            { id: 'clusterSize1216', label: 'Cluster-Größe (Bytes)', dependencies: ['sectorSize1216', 'clusterSizeSectors1216'], formula: 'sectorSize × clusterSizeSectors' },
 | 
					                            { id: 'numClusters1216', label: 'Anzahl Cluster', dependencies: ['partitionSizeInSectors1216', 'sectorSize1216', 'reservedSektoren1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216', 'clusterSizeSectors1216'], formula: 'dataSize ÷ clusterSize' }
 | 
				
			||||||
                            { id: 'numClusters1216', label: 'Anzahl Cluster', dependencies: ['partitionSizeInSectors1216', 'sectorSize1216', 'reservedSectors1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216', 'clusterSizeSectors1216'], formula: 'dataSize ÷ clusterSize' }
 | 
					 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'Spezifischer Cluster',
 | 
					                        name: 'Spezifischer Cluster',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'clusterStart1216', label: 'Cluster Anfang (Bytes)', dependencies: ['reservedSectors1216', 'sectorSize1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216', 'clusterSizeSectors1216', 'clusterNumber1216'], formula: 'dataStart + (clusterNumber - 0x2) × clusterSize' },
 | 
					                            { id: 'clusterStart1216', label: 'Cluster Anfang', dependencies: ['reservedSektoren1216', 'sectorSize1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216', 'clusterSizeSectors1216', 'clusterNumber1216'], formula: 'dataStart + (clusterNumber - 0x2) × clusterSize' },
 | 
				
			||||||
                            { id: 'clusterStartSector1216', label: 'Cluster Anfang (Sektor)', dependencies: ['reservedSectors1216', 'sectorSize1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216', 'clusterSizeSectors1216', 'clusterNumber1216'], formula: 'clusterStart ÷ sectorSize' },
 | 
					                            { id: 'fatEntryPos1216', label: 'FAT-Eintrag Position (nur FAT16)', dependencies: ['reservedSektoren1216', 'sectorSize1216', 'baseOffset1216', 'clusterNumber1216'], formula: 'fatStart + (clusterNumber × 2) für FAT16' }
 | 
				
			||||||
                            { id: 'fatEntryPos1216', label: 'FAT-Eintrag Position (Bytes, nur FAT16)', dependencies: ['reservedSectors1216', 'sectorSize1216', 'baseOffset1216', 'clusterNumber1216'], formula: 'fatStart + (clusterNumber × 2) für FAT16' }
 | 
					 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
@ -72,51 +91,187 @@ export class FATFilesystem extends BaseFilesystem {
 | 
				
			|||||||
                    { name: 'Cluster Anfang', expression: 'Daten-Bereich Anfang + (Cluster-Nummer - 0x2) × Cluster-Größe' },
 | 
					                    { name: 'Cluster Anfang', expression: 'Daten-Bereich Anfang + (Cluster-Nummer - 0x2) × Cluster-Größe' },
 | 
				
			||||||
                    { name: 'FAT-Eintrag Position', expression: 'FAT-Bereich Anfang + (Cluster-Nummer × Eintraggröße)' }
 | 
					                    { name: 'FAT-Eintrag Position', expression: 'FAT-Bereich Anfang + (Cluster-Nummer × Eintraggröße)' }
 | 
				
			||||||
                ]
 | 
					                ]
 | 
				
			||||||
            },
 | 
					            }
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    calculateFAT1216(values, results) {
 | 
				
			||||||
 | 
					        const suffix = '1216';
 | 
				
			||||||
 | 
					        // FAT Start (show both bytes and sector in one row)
 | 
				
			||||||
 | 
					        if (checkDependencies([`reservedSektoren${suffix}`, `sectorSize${suffix}`, `baseOffset${suffix}`])) {
 | 
				
			||||||
 | 
					            results.fatStart = values.reservedSektoren1216 * values.sectorSize1216 + values.baseOffset1216;
 | 
				
			||||||
 | 
					            results.fatStartSector = Math.floor(results.fatStart / values.sectorSize1216);
 | 
				
			||||||
 | 
					            updateResultItem(`fatStart${suffix}`, { bytes: results.fatStart, sectors: results.fatStartSector }, true,
 | 
				
			||||||
 | 
					                `0x${values.reservedSektoren1216.toString(16)} × 0x${values.sectorSize1216.toString(16)} + 0x${values.baseOffset1216.toString(16)} = 0x${results.fatStart.toString(16)}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem(`fatStart${suffix}`, 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // FAT Size (bytes and sectors)
 | 
				
			||||||
 | 
					        if (checkDependencies([`numFATs${suffix}`, `fatSizeSectors${suffix}`, `sectorSize${suffix}`])) {
 | 
				
			||||||
 | 
					            results.fatSize = values.numFATs1216 * values.fatSizeSectors1216 * values.sectorSize1216;
 | 
				
			||||||
 | 
					            results.fatSizeSectors = values.numFATs1216 * values.fatSizeSectors1216;
 | 
				
			||||||
 | 
					            updateResultItem(`fatSize${suffix}`, { bytes: results.fatSize, sectors: results.fatSizeSectors }, true,
 | 
				
			||||||
 | 
					                `0x${values.numFATs1216.toString(16)} × 0x${values.fatSizeSectors1216.toString(16)} × 0x${values.sectorSize1216.toString(16)} = 0x${results.fatSize.toString(16)}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem(`fatSize${suffix}`, 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // FAT2 Start (bytes and sector)
 | 
				
			||||||
 | 
					        if (checkDependencies([`reservedSektoren${suffix}`, `sectorSize${suffix}`, `baseOffset${suffix}`, `fatSizeSectors${suffix}`]) && results.fatStart !== undefined) {
 | 
				
			||||||
 | 
					            results.fat2Start = results.fatStart + (values.fatSizeSectors1216 * values.sectorSize1216);
 | 
				
			||||||
 | 
					            results.fat2StartSector = Math.floor(results.fat2Start / values.sectorSize1216);
 | 
				
			||||||
 | 
					            updateResultItem(`fat2Start${suffix}`, { bytes: results.fat2Start, sectors: results.fat2StartSector }, true,
 | 
				
			||||||
 | 
					                `0x${results.fatStart.toString(16)} + 0x${(values.fatSizeSectors1216 * values.sectorSize1216).toString(16)} = 0x${results.fat2Start.toString(16)}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem(`fat2Start${suffix}`, 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // FAT End (bytes and sector)
 | 
				
			||||||
 | 
					        if (results.fatStart !== undefined && results.fatSize !== undefined) {
 | 
				
			||||||
 | 
					            results.fatEnd = results.fatStart + results.fatSize;
 | 
				
			||||||
 | 
					            results.fatEndSector = Math.floor(results.fatEnd / values.sectorSize1216);
 | 
				
			||||||
 | 
					            updateResultItem(`fatEnd${suffix}`, { bytes: results.fatEnd, sectors: results.fatEndSector }, true,
 | 
				
			||||||
 | 
					                `0x${results.fatStart.toString(16)} + 0x${results.fatSize.toString(16)} = 0x${results.fatEnd.toString(16)}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem(`fatEnd${suffix}`, 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Root Directory Size
 | 
				
			||||||
 | 
					        if (checkDependencies(['maxRootEntries1216'])) {
 | 
				
			||||||
 | 
					            results.rootDirSize = values.maxRootEntries1216 * 0x20;
 | 
				
			||||||
 | 
					            updateResultItem('rootDirSize1216', results.rootDirSize, true,
 | 
				
			||||||
 | 
					                `0x${values.maxRootEntries1216.toString(16)} × 0x20 = 0x${results.rootDirSize.toString(16)}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem('rootDirSize1216', 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Root Directory Start (bytes and sector)
 | 
				
			||||||
 | 
					        if (results.fatEnd !== undefined) {
 | 
				
			||||||
 | 
					            results.rootDirStart = results.fatEnd;
 | 
				
			||||||
 | 
					            results.rootDirStartSector = Math.floor(results.rootDirStart / values.sectorSize1216);
 | 
				
			||||||
 | 
					            updateResultItem('rootDirStart1216', { bytes: results.rootDirStart, sectors: results.rootDirStartSector }, true,
 | 
				
			||||||
 | 
					                `0x${results.fatEnd.toString(16)}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem('rootDirStart1216', 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Data Start (bytes and sector)
 | 
				
			||||||
 | 
					        if (results.fatEnd !== undefined && results.rootDirSize !== undefined) {
 | 
				
			||||||
 | 
					            results.dataStart = results.fatEnd + results.rootDirSize;
 | 
				
			||||||
 | 
					            results.dataStartSector = Math.floor(results.dataStart / values.sectorSize1216);
 | 
				
			||||||
 | 
					            updateResultItem('dataStart1216', { bytes: results.dataStart, sectors: results.dataStartSector }, true,
 | 
				
			||||||
 | 
					                `0x${results.fatEnd.toString(16)} + 0x${results.rootDirSize.toString(16)} = 0x${results.dataStart.toString(16)}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem('dataStart1216', 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Data Size (Daten-Bereich Größe)
 | 
				
			||||||
 | 
					        if (checkDependencies(['partitionSizeInSectors1216', 'sectorSize1216', 'reservedSektoren1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216'])) {
 | 
				
			||||||
 | 
					            // RootDirGröße in Sektoren = Math.ceil(maxRootEntries × 32 / sectorSize)
 | 
				
			||||||
 | 
					            const rootDirSectors = Math.ceil((values.maxRootEntries1216 * 32) / values.sectorSize1216);
 | 
				
			||||||
 | 
					            const dataAreaSectors = values.partitionSizeInSectors1216 - (values.reservedSektoren1216 + (values.numFATs1216 * values.fatSizeSectors1216) + rootDirSectors);
 | 
				
			||||||
 | 
					            results.dataSize = dataAreaSectors * values.sectorSize1216;
 | 
				
			||||||
 | 
					            results.dataAreaSectors = dataAreaSectors;
 | 
				
			||||||
 | 
					            updateResultItem('dataSize1216', results.dataSize, true,
 | 
				
			||||||
 | 
					                `(${values.partitionSizeInSectors1216} - (${values.reservedSektoren1216} + (${values.numFATs1216} × ${values.fatSizeSectors1216}) + ${rootDirSectors})) × ${values.sectorSize1216} = ${results.dataSize}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem('dataSize1216', 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Cluster Size
 | 
				
			||||||
 | 
					        if (checkDependencies(['sectorSize1216', 'clusterSizeSectors1216'])) {
 | 
				
			||||||
 | 
					            results.clusterSize = values.sectorSize1216 * values.clusterSizeSectors1216;
 | 
				
			||||||
 | 
					            updateResultItem('clusterSize1216', results.clusterSize, true,
 | 
				
			||||||
 | 
					                `0x${values.sectorSize1216.toString(16)} × 0x${values.clusterSizeSectors1216.toString(16)} = 0x${results.clusterSize.toString(16)}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem('clusterSize1216', 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Number of Clusters (Anzahl Cluster)
 | 
				
			||||||
 | 
					        if (checkDependencies(['partitionSizeInSectors1216', 'sectorSize1216', 'reservedSektoren1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216', 'clusterSizeSectors1216']) && results.dataAreaSectors !== undefined && values.clusterSizeSectors1216 > 0) {
 | 
				
			||||||
 | 
					            results.numClusters = Math.floor(results.dataAreaSectors / values.clusterSizeSectors1216);
 | 
				
			||||||
 | 
					            updateResultItem('numClusters1216', results.numClusters, true,
 | 
				
			||||||
 | 
					                `${results.dataAreaSectors} ÷ ${values.clusterSizeSectors1216} = ${results.numClusters}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem('numClusters1216', 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Specific Cluster Start (bytes and sector)
 | 
				
			||||||
 | 
					        if (checkDependencies(['reservedSektoren1216', 'sectorSize1216', 'baseOffset1216', 'numFATs1216', 'fatSizeSectors1216', 'maxRootEntries1216', 'clusterSizeSectors1216', 'clusterNumber1216']) && results.dataStart !== undefined && results.clusterSize !== undefined) {
 | 
				
			||||||
 | 
					            results.clusterStart = results.dataStart + (values.clusterNumber1216 - 0x2) * results.clusterSize;
 | 
				
			||||||
 | 
					            results.clusterStartSector = Math.floor(results.clusterStart / values.sectorSize1216);
 | 
				
			||||||
 | 
					            updateResultItem('clusterStart1216', { bytes: results.clusterStart, sectors: results.clusterStartSector }, true,
 | 
				
			||||||
 | 
					                `0x${results.dataStart.toString(16)} + (0x${values.clusterNumber1216.toString(16)} - 0x2) × 0x${results.clusterSize.toString(16)} = 0x${results.clusterStart.toString(16)}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem('clusterStart1216', 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // FAT Entry Position (FAT16 only)
 | 
				
			||||||
 | 
					        if (checkDependencies(['reservedSektoren1216', 'sectorSize1216', 'baseOffset1216', 'clusterNumber1216']) && results.fatStart !== undefined) {
 | 
				
			||||||
 | 
					            const entrySize = 2; // FAT16 uses 2 bytes per entry
 | 
				
			||||||
 | 
					            results.fatEntryPos = results.fatStart + (values.clusterNumber1216 * entrySize);
 | 
				
			||||||
 | 
					            updateResultItem('fatEntryPos1216', results.fatEntryPos, true,
 | 
				
			||||||
 | 
					                `0x${results.fatStart.toString(16)} + (0x${values.clusterNumber1216.toString(16)} × ${entrySize}) = 0x${results.fatEntryPos.toString(16)}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem('fatEntryPos1216', 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class FAT32Filesystem extends BaseFilesystem {
 | 
				
			||||||
 | 
					    calculate(variantId) {
 | 
				
			||||||
 | 
					        if (variantId !== 'fat32') return;
 | 
				
			||||||
 | 
					        const values = {};
 | 
				
			||||||
 | 
					        const results = {};
 | 
				
			||||||
 | 
					        // Gather all input values for this variant
 | 
				
			||||||
 | 
					        const variant = this.variants.find(v => v.id === variantId);
 | 
				
			||||||
 | 
					        if (!variant) return;
 | 
				
			||||||
 | 
					        [...variant.constants, ...variant.inputs].forEach(field => {
 | 
				
			||||||
 | 
					            const el = document.getElementById(field.id);
 | 
				
			||||||
 | 
					            if (el) {
 | 
				
			||||||
 | 
					                let val = el.value;
 | 
				
			||||||
 | 
					                if (val && val.startsWith('0x')) {
 | 
				
			||||||
 | 
					                    values[field.id] = parseInt(val, 16);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    values[field.id] = Number(val) || 0;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        this.calculateFAT32(values, results);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super('FAT32', [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                id: 'fat32',
 | 
					                id: 'fat32',
 | 
				
			||||||
                name: 'FAT32',
 | 
					                name: 'FAT32',
 | 
				
			||||||
                constants: [
 | 
					                constants: [
 | 
				
			||||||
                    { id: 'baseOffset32', label: 'Basis-Offset', unit: 'Bytes', default: '0x0' },
 | 
					                    { id: 'baseOffset32', label: 'Basis-Offset', unit: 'Bytes', default: '0x0' },
 | 
				
			||||||
                    { id: 'sectorSize32', label: 'Sektorgröße', unit: 'Bytes', default: '0x200' }
 | 
					                    { id: 'sectorSize32', label: 'Sektor-Größe (Boot-Offset 0x0B)', unit: 'Bytes', default: '0x200' }
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
                inputs: [
 | 
					                inputs: [
 | 
				
			||||||
                    { id: 'reservedSectors32', label: 'Reservierte Sektoren', unit: 'Anzahl', placeholder: '0x20' },
 | 
					                    { id: 'reservedSectors32', label: 'Reservierte Sektoren (Boot-Offset 0x0E)', unit: 'Anzahl', placeholder: '0x20' },
 | 
				
			||||||
                    { id: 'numFATs32', label: 'Anzahl FATs', unit: 'Anzahl', placeholder: '0x2' },
 | 
					                    { id: 'numFATs32', label: 'Anzahl FATs (Boot-Offset 0x10)', unit: 'Anzahl', placeholder: '0x2' },
 | 
				
			||||||
                    { id: 'fatSizeSectors32', label: 'FAT-Größe', unit: 'Sektoren', placeholder: '0x20000' },
 | 
					                    { id: 'fatSizeSectors32', label: 'FAT-Größe (Boot-Offset 0x24)', unit: 'Sektoren', placeholder: '0x20000' },
 | 
				
			||||||
                    { id: 'rootDirCluster32', label: 'Root-Directory Cluster', unit: 'Nummer', placeholder: '0x2' },
 | 
					                    { id: 'rootDirCluster32', label: 'Root-Directory Cluster (Boot-Offset 0x2C)', unit: 'Nummer', placeholder: '0x2' },
 | 
				
			||||||
                    { id: 'partitionSizeInSectors32', label: 'Partitionsgröße', unit: 'Sektoren', placeholder: '0x100000' },
 | 
					                    { id: 'partitionSizeInSectors32', label: 'Partitionsgröße (Boot-Offset 0x20)', unit: 'Sektoren', placeholder: '0x100000' },
 | 
				
			||||||
                    { id: 'clusterSizeSectors32', label: 'Clustergröße', unit: 'Sektoren', placeholder: '0x8' },
 | 
					                    { id: 'clusterSizeSectors32', label: 'Clustergröße (Boot-Offset 0x0D)', unit: 'Sektoren', placeholder: '0x8' },
 | 
				
			||||||
                    { id: 'clusterNumber32', label: 'Cluster-Nummer', unit: 'Nummer', placeholder: '0x2' }
 | 
					                    { id: 'clusterNumber32', label: 'Cluster-Nummer', unit: 'Nummer', placeholder: '0x2' }
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
                resultGroups: [
 | 
					                resultGroups: [
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'FAT-Struktur',
 | 
					                        name: 'FAT-Struktur',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'fatStart32', label: 'FAT-Bereich Anfang (Bytes)', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32'], formula: 'reservedSectors × sectorSize + baseOffset' },
 | 
					                            { id: 'fatStart32', label: 'FAT-Bereich Anfang', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32'], formula: 'reservedSectors × sectorSize + baseOffset' },
 | 
				
			||||||
                            { id: 'fatStartSector32', label: 'FAT-Bereich Anfang (Sektor)', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32'], formula: 'fatStart ÷ sectorSize' },
 | 
					                            { id: 'fatSize32', label: 'FAT-Bereich Größe', dependencies: ['numFATs32', 'fatSizeSectors32', 'sectorSize32'], formula: 'numFATs × fatSizeSectors × sectorSize' },
 | 
				
			||||||
                            { id: 'fatSize32', label: 'FAT-Bereich Größe (Bytes)', dependencies: ['numFATs32', 'fatSizeSectors32', 'sectorSize32'], formula: 'numFATs × fatSizeSectors × sectorSize' },
 | 
					                            { id: 'fat2Start32', label: 'FAT2 Anfang', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'fatSizeSectors32'], formula: 'fatStart + (fatSizeSectors × sectorSize)' },
 | 
				
			||||||
                            { id: 'fat2Start32', label: 'FAT2 Anfang (Bytes)', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'fatSizeSectors32'], formula: 'fatStart + (fatSizeSectors × sectorSize)' },
 | 
					                            { id: 'fatEnd32', label: 'FAT-Bereich Ende', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32'], formula: 'fatStart + fatSize' }
 | 
				
			||||||
                            { id: 'fatEnd32', label: 'FAT-Bereich Ende (Bytes)', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32'], formula: 'fatStart + fatSize' }
 | 
					 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'Daten-Bereich',
 | 
					                        name: 'Daten-Bereich',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'dataStart32', label: 'Daten-Bereich Anfang (Bytes)', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32'], formula: 'fatEnd' },
 | 
					                            { id: 'dataStart32', label: 'Daten-Bereich Anfang', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32'], formula: 'fatEnd' },
 | 
				
			||||||
                            { id: 'dataStartSector32', label: 'Daten-Bereich Anfang (Sektor)', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32'], formula: 'dataStart ÷ sectorSize' },
 | 
					                            { id: 'dataSize32', label: 'Daten-Bereich Größe', dependencies: ['partitionSizeInSectors32', 'sectorSize32', 'reservedSectors32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32'], formula: '(partitionSizeInSectors × sectorSize) - (dataStart - baseOffset)' },
 | 
				
			||||||
                            { id: 'dataSize32', label: 'Daten-Bereich Größe (Bytes)', dependencies: ['partitionSizeInSectors32', 'sectorSize32', 'reservedSectors32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32'], formula: '(partitionSizeInSectors × sectorSize) - (dataStart - baseOffset)' },
 | 
					                            { id: 'clusterSize32', label: 'Cluster-Größe', dependencies: ['sectorSize32', 'clusterSizeSectors32'], formula: 'sectorSize × clusterSizeSectors' },
 | 
				
			||||||
                            { id: 'clusterSize32', label: 'Cluster-Größe (Bytes)', dependencies: ['sectorSize32', 'clusterSizeSectors32'], formula: 'sectorSize × clusterSizeSectors' },
 | 
					 | 
				
			||||||
                            { id: 'numClusters32', label: 'Anzahl Cluster', dependencies: ['partitionSizeInSectors32', 'sectorSize32', 'reservedSectors32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32', 'clusterSizeSectors32'], formula: 'dataSize ÷ clusterSize' }
 | 
					                            { id: 'numClusters32', label: 'Anzahl Cluster', dependencies: ['partitionSizeInSectors32', 'sectorSize32', 'reservedSectors32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32', 'clusterSizeSectors32'], formula: 'dataSize ÷ clusterSize' }
 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'Spezifischer Cluster',
 | 
					                        name: 'Spezifischer Cluster',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'clusterStart32', label: 'Cluster Anfang (Bytes)', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32', 'clusterSizeSectors32', 'clusterNumber32'], formula: 'dataStart + (clusterNumber - 0x2) × clusterSize' },
 | 
					                            { id: 'clusterStart32', label: 'Cluster Anfang', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32', 'clusterSizeSectors32', 'clusterNumber32'], formula: 'dataStart + (clusterNumber - 0x2) × clusterSize' },
 | 
				
			||||||
                            { id: 'clusterStartSector32', label: 'Cluster Anfang (Sektor)', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32', 'clusterSizeSectors32', 'clusterNumber32'], formula: 'clusterStart ÷ sectorSize' },
 | 
					                            { id: 'fatEntryPos32', label: 'FAT-Eintrag Position', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'clusterNumber32'], formula: 'fatStart + (clusterNumber × 4)' },
 | 
				
			||||||
                            { id: 'fatEntryPos32', label: 'FAT-Eintrag Position (Bytes)', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'clusterNumber32'], formula: 'fatStart + (clusterNumber × 4)' },
 | 
					                            { id: 'rootDirPos32', label: 'Root-Directory Position', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32', 'clusterSizeSectors32', 'rootDirCluster32'], formula: 'dataStart + (rootDirCluster - 0x2) × clusterSize' }
 | 
				
			||||||
                            { id: 'rootDirPos32', label: 'Root-Directory Position (Bytes)', dependencies: ['reservedSectors32', 'sectorSize32', 'baseOffset32', 'numFATs32', 'fatSizeSectors32', 'clusterSizeSectors32', 'rootDirCluster32'], formula: 'dataStart + (rootDirCluster - 0x2) × clusterSize' }
 | 
					 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
@ -137,262 +292,102 @@ export class FATFilesystem extends BaseFilesystem {
 | 
				
			|||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    calculate(variantId) {
 | 
					 | 
				
			||||||
        const values = this.getInputValues(variantId);
 | 
					 | 
				
			||||||
        const results = {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (variantId === 'fat1216') {
 | 
					 | 
				
			||||||
            this.calculateFAT1216(values, results);
 | 
					 | 
				
			||||||
        } else if (variantId === 'fat32') {
 | 
					 | 
				
			||||||
            this.calculateFAT32(values, results);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    calculateFAT1216(values, results) {
 | 
					 | 
				
			||||||
        const suffix = '1216';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // FAT Start
 | 
					 | 
				
			||||||
        if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix}`, `baseOffset${suffix}`])) {
 | 
					 | 
				
			||||||
            results.fatStart = values.reservedSectors1216 * values.sectorSize1216 + values.baseOffset1216;
 | 
					 | 
				
			||||||
            results.fatStartSector = Math.floor(results.fatStart / values.sectorSize1216);
 | 
					 | 
				
			||||||
            updateResultItem(`fatStart${suffix}`, results.fatStart, true, 
 | 
					 | 
				
			||||||
                `0x${values.reservedSectors1216.toString(16)} × 0x${values.sectorSize1216.toString(16)} + 0x${values.baseOffset1216.toString(16)} = 0x${results.fatStart.toString(16)}`);
 | 
					 | 
				
			||||||
            updateResultItem(`fatStartSector${suffix}`, results.fatStartSector, true,
 | 
					 | 
				
			||||||
                `0x${results.fatStart.toString(16)} ÷ 0x${values.sectorSize1216.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}`])) {
 | 
					 | 
				
			||||||
            results.fatSize = values.numFATs1216 * values.fatSizeSectors1216 * values.sectorSize1216;
 | 
					 | 
				
			||||||
            updateResultItem(`fatSize${suffix}`, results.fatSize, true,
 | 
					 | 
				
			||||||
                `0x${values.numFATs1216.toString(16)} × 0x${values.fatSizeSectors1216.toString(16)} × 0x${values.sectorSize1216.toString(16)} = 0x${results.fatSize.toString(16)}`);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            updateResultItem(`fatSize${suffix}`, 0, false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // FAT2 Start
 | 
					 | 
				
			||||||
        if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix}`, `baseOffset${suffix}`, `fatSizeSectors${suffix}`]) && results.fatStart !== undefined) {
 | 
					 | 
				
			||||||
            results.fat2Start = results.fatStart + (values.fatSizeSectors1216 * values.sectorSize1216);
 | 
					 | 
				
			||||||
            updateResultItem(`fat2Start${suffix}`, results.fat2Start, true,
 | 
					 | 
				
			||||||
                `0x${results.fatStart.toString(16)} + 0x${(values.fatSizeSectors1216 * values.sectorSize1216).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 Size
 | 
					 | 
				
			||||||
        if (checkDependencies(['maxRootEntries1216'])) {
 | 
					 | 
				
			||||||
            results.rootDirSize = values.maxRootEntries1216 * 0x20;
 | 
					 | 
				
			||||||
            updateResultItem('rootDirSize1216', results.rootDirSize, true,
 | 
					 | 
				
			||||||
                `0x${values.maxRootEntries1216.toString(16)} × 0x20 = 0x${results.rootDirSize.toString(16)}`);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            updateResultItem('rootDirSize1216', 0, false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Root Directory Start
 | 
					 | 
				
			||||||
        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
 | 
					 | 
				
			||||||
        if (results.fatEnd !== undefined && results.rootDirSize !== undefined) {
 | 
					 | 
				
			||||||
            results.dataStart = results.fatEnd + results.rootDirSize;
 | 
					 | 
				
			||||||
            results.dataStartSector = Math.floor(results.dataStart / values.sectorSize1216);
 | 
					 | 
				
			||||||
            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${values.sectorSize1216.toString(16)} = 0x${results.dataStartSector.toString(16)}`);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            updateResultItem('dataStart1216', 0, false);
 | 
					 | 
				
			||||||
            updateResultItem('dataStartSector1216', 0, false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Data Size
 | 
					 | 
				
			||||||
        const requiredForDataSize = [`partitionSizeInSectors${suffix}`, `sectorSize1216`, `reservedSectors${suffix}`, `baseOffset1216`, `numFATs${suffix}`, `fatSizeSectors${suffix}`, 'maxRootEntries1216'];
 | 
					 | 
				
			||||||
        if (checkDependencies(requiredForDataSize) && results.dataStart !== undefined) {
 | 
					 | 
				
			||||||
            const partitionSizeBytes = values.partitionSizeInSectors1216 * values.sectorSize1216;
 | 
					 | 
				
			||||||
            results.dataSize = partitionSizeBytes - (results.dataStart - values.baseOffset1216);
 | 
					 | 
				
			||||||
            updateResultItem(`dataSize${suffix}`, results.dataSize, true,
 | 
					 | 
				
			||||||
                `(0x${values.partitionSizeInSectors1216.toString(16)} × 0x${values.sectorSize1216.toString(16)}) - (0x${results.dataStart.toString(16)} - 0x${values.baseOffset1216.toString(16)}) = 0x${results.dataSize.toString(16)}`);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            updateResultItem(`dataSize${suffix}`, 0, false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Cluster Size
 | 
					 | 
				
			||||||
        if (checkDependencies([`sectorSize1216`, `clusterSizeSectors${suffix}`])) {
 | 
					 | 
				
			||||||
            results.clusterSize = values.sectorSize1216 * values.clusterSizeSectors1216;
 | 
					 | 
				
			||||||
            updateResultItem(`clusterSize${suffix}`, results.clusterSize, true,
 | 
					 | 
				
			||||||
                `0x${values.sectorSize1216.toString(16)} × 0x${values.clusterSizeSectors1216.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 = [`reservedSectors${suffix}`, `sectorSize1216`, `baseOffset1216`, `numFATs${suffix}`, `fatSizeSectors${suffix}`, 'maxRootEntries1216', `clusterSizeSectors${suffix}`, `clusterNumber${suffix}`];
 | 
					 | 
				
			||||||
        if (checkDependencies(requiredForCluster) && results.dataStart !== undefined && results.clusterSize !== undefined) {
 | 
					 | 
				
			||||||
            results.clusterStart = results.dataStart + (values.clusterNumber1216 - 0x2) * results.clusterSize;
 | 
					 | 
				
			||||||
            results.clusterStartSector = Math.floor(results.clusterStart / values.sectorSize1216);
 | 
					 | 
				
			||||||
            updateResultItem(`clusterStart${suffix}`, results.clusterStart, true,
 | 
					 | 
				
			||||||
                `0x${results.dataStart.toString(16)} + (0x${values.clusterNumber1216.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${values.sectorSize1216.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}`, `sectorSize1216`, `baseOffset1216`, `clusterNumber${suffix}`]) && results.fatStart !== undefined) {
 | 
					 | 
				
			||||||
            const entrySize = 2; // FAT16 uses 2 bytes per entry
 | 
					 | 
				
			||||||
            results.fatEntryPos = results.fatStart + (values.clusterNumber1216 * entrySize);
 | 
					 | 
				
			||||||
            updateResultItem(`fatEntryPos${suffix}`, results.fatEntryPos, true,
 | 
					 | 
				
			||||||
                `0x${results.fatStart.toString(16)} + (0x${values.clusterNumber1216.toString(16)} × ${entrySize}) = 0x${results.fatEntryPos.toString(16)}`);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            updateResultItem(`fatEntryPos${suffix}`, 0, false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    calculateFAT32(values, results) {
 | 
					    calculateFAT32(values, results) {
 | 
				
			||||||
        const suffix = '32';
 | 
					        const suffix = '32';
 | 
				
			||||||
 | 
					        // FAT Start (show both bytes and sector in one row)
 | 
				
			||||||
        // FAT Start
 | 
					 | 
				
			||||||
        if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix}`, `baseOffset${suffix}`])) {
 | 
					        if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix}`, `baseOffset${suffix}`])) {
 | 
				
			||||||
            results.fatStart = values.reservedSectors32 * values.sectorSize32 + values.baseOffset32;
 | 
					            results.fatStart = values[`reservedSectors${suffix}`] * values[`sectorSize${suffix}`] + values[`baseOffset${suffix}`];
 | 
				
			||||||
            results.fatStartSector = Math.floor(results.fatStart / values.sectorSize32);
 | 
					            results.fatStartSector = Math.floor(results.fatStart / values[`sectorSize${suffix}`]);
 | 
				
			||||||
            updateResultItem(`fatStart${suffix}`, results.fatStart, true, 
 | 
					            updateResultItem(`fatStart${suffix}`, { bytes: results.fatStart, sectors: results.fatStartSector }, true,
 | 
				
			||||||
                `0x${values.reservedSectors32.toString(16)} × 0x${values.sectorSize32.toString(16)} + 0x${values.baseOffset32.toString(16)} = 0x${results.fatStart.toString(16)}`);
 | 
					                `0x${values[`reservedSectors${suffix}`].toString(16)} × 0x${values[`sectorSize${suffix}`].toString(16)} + 0x${values[`baseOffset${suffix}`].toString(16)} = 0x${results.fatStart.toString(16)}`);
 | 
				
			||||||
            updateResultItem(`fatStartSector${suffix}`, results.fatStartSector, true,
 | 
					 | 
				
			||||||
                `0x${results.fatStart.toString(16)} ÷ 0x${values.sectorSize32.toString(16)} = 0x${results.fatStartSector.toString(16)}`);
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem(`fatStart${suffix}`, 0, false);
 | 
					            updateResultItem(`fatStart${suffix}`, 0, false);
 | 
				
			||||||
            updateResultItem(`fatStartSector${suffix}`, 0, false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        // FAT Size (bytes and sectors)
 | 
				
			||||||
        // FAT Size
 | 
					 | 
				
			||||||
        if (checkDependencies([`numFATs${suffix}`, `fatSizeSectors${suffix}`, `sectorSize${suffix}`])) {
 | 
					        if (checkDependencies([`numFATs${suffix}`, `fatSizeSectors${suffix}`, `sectorSize${suffix}`])) {
 | 
				
			||||||
            results.fatSize = values.numFATs32 * values.fatSizeSectors32 * values.sectorSize32;
 | 
					            results.fatSize = values[`numFATs${suffix}`] * values[`fatSizeSectors${suffix}`] * values[`sectorSize${suffix}`];
 | 
				
			||||||
            updateResultItem(`fatSize${suffix}`, results.fatSize, true,
 | 
					            results.fatSizeSectors = values[`numFATs${suffix}`] * values[`fatSizeSectors${suffix}`];
 | 
				
			||||||
                `0x${values.numFATs32.toString(16)} × 0x${values.fatSizeSectors32.toString(16)} × 0x${values.sectorSize32.toString(16)} = 0x${results.fatSize.toString(16)}`);
 | 
					            updateResultItem(`fatSize${suffix}`, { bytes: results.fatSize, sectors: results.fatSizeSectors }, true,
 | 
				
			||||||
 | 
					                `0x${values[`numFATs${suffix}`].toString(16)} × 0x${values[`fatSizeSectors${suffix}`].toString(16)} × 0x${values[`sectorSize${suffix}`].toString(16)} = 0x${results.fatSize.toString(16)}`);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem(`fatSize${suffix}`, 0, false);
 | 
					            updateResultItem(`fatSize${suffix}`, 0, false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        // FAT2 Start (bytes and sector)
 | 
				
			||||||
        // FAT2 Start
 | 
					 | 
				
			||||||
        if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix}`, `baseOffset${suffix}`, `fatSizeSectors${suffix}`]) && results.fatStart !== undefined) {
 | 
					        if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix}`, `baseOffset${suffix}`, `fatSizeSectors${suffix}`]) && results.fatStart !== undefined) {
 | 
				
			||||||
            results.fat2Start = results.fatStart + (values.fatSizeSectors32 * values.sectorSize32);
 | 
					            results.fat2Start = results.fatStart + (values[`fatSizeSectors${suffix}`] * values[`sectorSize${suffix}`]);
 | 
				
			||||||
            updateResultItem(`fat2Start${suffix}`, results.fat2Start, true,
 | 
					            results.fat2StartSector = Math.floor(results.fat2Start / values[`sectorSize${suffix}`]);
 | 
				
			||||||
                `0x${results.fatStart.toString(16)} + 0x${(values.fatSizeSectors32 * values.sectorSize32).toString(16)} = 0x${results.fat2Start.toString(16)}`);
 | 
					            updateResultItem(`fat2Start${suffix}`, { bytes: results.fat2Start, sectors: results.fat2StartSector }, true,
 | 
				
			||||||
 | 
					                `0x${results.fatStart.toString(16)} + 0x${(values[`fatSizeSectors${suffix}`] * values[`sectorSize${suffix}`]).toString(16)} = 0x${results.fat2Start.toString(16)}`);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem(`fat2Start${suffix}`, 0, false);
 | 
					            updateResultItem(`fat2Start${suffix}`, 0, false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        // FAT End (bytes and sector)
 | 
				
			||||||
        // FAT End
 | 
					 | 
				
			||||||
        if (results.fatStart !== undefined && results.fatSize !== undefined) {
 | 
					        if (results.fatStart !== undefined && results.fatSize !== undefined) {
 | 
				
			||||||
            results.fatEnd = results.fatStart + results.fatSize;
 | 
					            results.fatEnd = results.fatStart + results.fatSize;
 | 
				
			||||||
            updateResultItem(`fatEnd${suffix}`, results.fatEnd, true,
 | 
					            results.fatEndSector = Math.floor(results.fatEnd / values[`sectorSize${suffix}`]);
 | 
				
			||||||
 | 
					            updateResultItem(`fatEnd${suffix}`, { bytes: results.fatEnd, sectors: results.fatEndSector }, true,
 | 
				
			||||||
                `0x${results.fatStart.toString(16)} + 0x${results.fatSize.toString(16)} = 0x${results.fatEnd.toString(16)}`);
 | 
					                `0x${results.fatStart.toString(16)} + 0x${results.fatSize.toString(16)} = 0x${results.fatEnd.toString(16)}`);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem(`fatEnd${suffix}`, 0, false);
 | 
					            updateResultItem(`fatEnd${suffix}`, 0, false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        // Data Start (bytes and sector)
 | 
				
			||||||
        // Data Start (FAT32 has no fixed root directory)
 | 
					 | 
				
			||||||
        if (results.fatEnd !== undefined) {
 | 
					        if (results.fatEnd !== undefined) {
 | 
				
			||||||
            results.dataStart = results.fatEnd;
 | 
					            results.dataStart = results.fatEnd;
 | 
				
			||||||
            results.dataStartSector = Math.floor(results.dataStart / values.sectorSize32);
 | 
					            results.dataStartSector = Math.floor(results.dataStart / values[`sectorSize${suffix}`]);
 | 
				
			||||||
            updateResultItem('dataStart32', results.dataStart, true,
 | 
					            updateResultItem(`dataStart${suffix}`, { bytes: results.dataStart, sectors: results.dataStartSector }, true,
 | 
				
			||||||
                `0x${results.fatEnd.toString(16)}`);
 | 
					                `0x${results.fatEnd.toString(16)}`);
 | 
				
			||||||
            updateResultItem('dataStartSector32', results.dataStartSector, true,
 | 
					 | 
				
			||||||
                `0x${results.dataStart.toString(16)} ÷ 0x${values.sectorSize32.toString(16)} = 0x${results.dataStartSector.toString(16)}`);
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem('dataStart32', 0, false);
 | 
					            updateResultItem(`dataStart${suffix}`, 0, false);
 | 
				
			||||||
            updateResultItem('dataStartSector32', 0, false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Data Size
 | 
					        // Data Size
 | 
				
			||||||
        const requiredForDataSize = [`partitionSizeInSectors${suffix}`, `sectorSize32`, `reservedSectors${suffix}`, `baseOffset32`, `numFATs${suffix}`, `fatSizeSectors${suffix}`];
 | 
					        if (checkDependencies([`partitionSizeInSectors${suffix}`, `sectorSize${suffix}`, `reservedSectors${suffix}`, `baseOffset${suffix}`, `numFATs${suffix}`, `fatSizeSectors${suffix}`]) && results.dataStart !== undefined) {
 | 
				
			||||||
        if (checkDependencies(requiredForDataSize) && results.dataStart !== undefined) {
 | 
					            results.dataSize = (values[`partitionSizeInSectors${suffix}`] * values[`sectorSize${suffix}`]) - (results.dataStart - values[`baseOffset${suffix}`]);
 | 
				
			||||||
            const partitionSizeBytes = values.partitionSizeInSectors32 * values.sectorSize32;
 | 
					 | 
				
			||||||
            results.dataSize = partitionSizeBytes - (results.dataStart - values.baseOffset32);
 | 
					 | 
				
			||||||
            updateResultItem(`dataSize${suffix}`, results.dataSize, true,
 | 
					            updateResultItem(`dataSize${suffix}`, results.dataSize, true,
 | 
				
			||||||
                `(0x${values.partitionSizeInSectors32.toString(16)} × 0x${values.sectorSize32.toString(16)}) - (0x${results.dataStart.toString(16)} - 0x${values.baseOffset32.toString(16)}) = 0x${results.dataSize.toString(16)}`);
 | 
					                `(0x${values[`partitionSizeInSectors${suffix}`].toString(16)} × 0x${values[`sectorSize${suffix}`].toString(16)}) - (0x${results.dataStart.toString(16)} - 0x${values[`baseOffset${suffix}`].toString(16)}) = 0x${results.dataSize.toString(16)}`);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem(`dataSize${suffix}`, 0, false);
 | 
					            updateResultItem(`dataSize${suffix}`, 0, false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Cluster Size
 | 
					        // Cluster Size
 | 
				
			||||||
        if (checkDependencies([`sectorSize32`, `clusterSizeSectors${suffix}`])) {
 | 
					        if (checkDependencies([`sectorSize${suffix}`, `clusterSizeSectors${suffix}`])) {
 | 
				
			||||||
            results.clusterSize = values.sectorSize32 * values.clusterSizeSectors32;
 | 
					            results.clusterSize = values[`sectorSize${suffix}`] * values[`clusterSizeSectors${suffix}`];
 | 
				
			||||||
            updateResultItem(`clusterSize${suffix}`, results.clusterSize, true,
 | 
					            updateResultItem(`clusterSize${suffix}`, results.clusterSize, true,
 | 
				
			||||||
                `0x${values.sectorSize32.toString(16)} × 0x${values.clusterSizeSectors32.toString(16)} = 0x${results.clusterSize.toString(16)}`);
 | 
					                `0x${values[`sectorSize${suffix}`].toString(16)} × 0x${values[`clusterSizeSectors${suffix}`].toString(16)} = 0x${results.clusterSize.toString(16)}`);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem(`clusterSize${suffix}`, 0, false);
 | 
					            updateResultItem(`clusterSize${suffix}`, 0, false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Number of Clusters
 | 
					        // Number of Clusters
 | 
				
			||||||
        if (results.dataSize !== undefined && results.clusterSize !== undefined && results.clusterSize > 0) {
 | 
					        if (checkDependencies([`partitionSizeInSectors${suffix}`, `sectorSize${suffix}`, `reservedSectors${suffix}`, `baseOffset${suffix}`, `numFATs${suffix}`, `fatSizeSectors${suffix}`, `clusterSizeSectors${suffix}`]) && results.dataSize !== undefined && results.clusterSize !== undefined) {
 | 
				
			||||||
            results.numClusters = Math.floor(results.dataSize / results.clusterSize);
 | 
					            results.numClusters = Math.floor(results.dataSize / results.clusterSize);
 | 
				
			||||||
            updateResultItem(`numClusters${suffix}`, results.numClusters, true,
 | 
					            updateResultItem(`numClusters${suffix}`, results.numClusters, true,
 | 
				
			||||||
                `0x${results.dataSize.toString(16)} ÷ 0x${results.clusterSize.toString(16)} = 0x${results.numClusters.toString(16)}`);
 | 
					                `0x${results.dataSize.toString(16)} ÷ 0x${results.clusterSize.toString(16)} = 0x${results.numClusters.toString(16)}`);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem(`numClusters${suffix}`, 0, false);
 | 
					            updateResultItem(`numClusters${suffix}`, 0, false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        // Specific Cluster Start (bytes and sector)
 | 
				
			||||||
        // Specific Cluster Start
 | 
					        if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix}`, `baseOffset${suffix}`, `numFATs${suffix}`, `fatSizeSectors${suffix}`, `clusterSizeSectors${suffix}`, `clusterNumber${suffix}`]) && results.dataStart !== undefined && results.clusterSize !== undefined) {
 | 
				
			||||||
        const requiredForCluster = [`reservedSectors${suffix}`, `sectorSize32`, `baseOffset32`, `numFATs${suffix}`, `fatSizeSectors${suffix}`, `clusterSizeSectors${suffix}`, `clusterNumber${suffix}`];
 | 
					            results.clusterStart = results.dataStart + (values[`clusterNumber${suffix}`] - 0x2) * results.clusterSize;
 | 
				
			||||||
        if (checkDependencies(requiredForCluster) && results.dataStart !== undefined && results.clusterSize !== undefined) {
 | 
					            results.clusterStartSector = Math.floor(results.clusterStart / values[`sectorSize${suffix}`]);
 | 
				
			||||||
            results.clusterStart = results.dataStart + (values.clusterNumber32 - 0x2) * results.clusterSize;
 | 
					            updateResultItem(`clusterStart${suffix}`, { bytes: results.clusterStart, sectors: results.clusterStartSector }, true,
 | 
				
			||||||
            results.clusterStartSector = Math.floor(results.clusterStart / values.sectorSize32);
 | 
					                `0x${results.dataStart.toString(16)} + (0x${values[`clusterNumber${suffix}`].toString(16)} - 0x2) × 0x${results.clusterSize.toString(16)} = 0x${results.clusterStart.toString(16)}`);
 | 
				
			||||||
            updateResultItem(`clusterStart${suffix}`, results.clusterStart, true,
 | 
					 | 
				
			||||||
                `0x${results.dataStart.toString(16)} + (0x${values.clusterNumber32.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${values.sectorSize32.toString(16)} = 0x${results.clusterStartSector.toString(16)}`);
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem(`clusterStart${suffix}`, 0, false);
 | 
					            updateResultItem(`clusterStart${suffix}`, 0, false);
 | 
				
			||||||
            updateResultItem(`clusterStartSector${suffix}`, 0, false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // FAT Entry Position
 | 
					        // FAT Entry Position
 | 
				
			||||||
        if (checkDependencies([`reservedSectors${suffix}`, `sectorSize32`, `baseOffset32`, `clusterNumber${suffix}`]) && results.fatStart !== undefined) {
 | 
					        if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix}`, `baseOffset${suffix}`, `clusterNumber${suffix}`]) && results.fatStart !== undefined) {
 | 
				
			||||||
            const entrySize = 4; // FAT32 uses 4 bytes per entry
 | 
					            const entrySize = 4; // FAT32 uses 4 bytes per entry
 | 
				
			||||||
            results.fatEntryPos = results.fatStart + (values.clusterNumber32 * entrySize);
 | 
					            results.fatEntryPos = results.fatStart + (values[`clusterNumber${suffix}`] * entrySize);
 | 
				
			||||||
            updateResultItem(`fatEntryPos${suffix}`, results.fatEntryPos, true,
 | 
					            updateResultItem(`fatEntryPos${suffix}`, results.fatEntryPos, true,
 | 
				
			||||||
                `0x${results.fatStart.toString(16)} + (0x${values.clusterNumber32.toString(16)} × ${entrySize}) = 0x${results.fatEntryPos.toString(16)}`);
 | 
					                `0x${results.fatStart.toString(16)} + (0x${values[`clusterNumber${suffix}`].toString(16)} × ${entrySize}) = 0x${results.fatEntryPos.toString(16)}`);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem(`fatEntryPos${suffix}`, 0, false);
 | 
					            updateResultItem(`fatEntryPos${suffix}`, 0, false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Root Directory Position (FAT32 specific)
 | 
					        // Root Directory Position (FAT32 specific)
 | 
				
			||||||
        const requiredForRootPos = [`reservedSectors${suffix}`, `sectorSize32`, `baseOffset32`, `numFATs${suffix}`, `fatSizeSectors${suffix}`, `clusterSizeSectors${suffix}`, `rootDirCluster32`];
 | 
					        if (checkDependencies([`reservedSectors${suffix}`, `sectorSize${suffix}`, `baseOffset${suffix}`, `numFATs${suffix}`, `fatSizeSectors${suffix}`, `clusterSizeSectors${suffix}`, `rootDirCluster${suffix}`]) && results.dataStart !== undefined && results.clusterSize !== undefined) {
 | 
				
			||||||
        if (checkDependencies(requiredForRootPos) && results.dataStart !== undefined && results.clusterSize !== undefined) {
 | 
					            results.rootDirPos = results.dataStart + (values[`rootDirCluster${suffix}`] - 0x2) * results.clusterSize;
 | 
				
			||||||
            results.rootDirPos = results.dataStart + (values.rootDirCluster32 - 0x2) * results.clusterSize;
 | 
					            updateResultItem(`rootDirPos${suffix}`, results.rootDirPos, true,
 | 
				
			||||||
            updateResultItem('rootDirPos32', results.rootDirPos, true,
 | 
					                `0x${results.dataStart.toString(16)} + (0x${values[`rootDirCluster${suffix}`].toString(16)} - 0x2) × 0x${results.clusterSize.toString(16)} = 0x${results.rootDirPos.toString(16)}`);
 | 
				
			||||||
                `0x${results.dataStart.toString(16)} + (0x${values.rootDirCluster32.toString(16)} - 0x2) × 0x${results.clusterSize.toString(16)} = 0x${results.rootDirPos.toString(16)}`);
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem('rootDirPos32', 0, false);
 | 
					            updateResultItem(`rootDirPos${suffix}`, 0, false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -8,67 +8,95 @@ export class NTFSFilesystem extends BaseFilesystem {
 | 
				
			|||||||
        super('NTFS', [
 | 
					        super('NTFS', [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                id: 'ntfs',
 | 
					                id: 'ntfs',
 | 
				
			||||||
                name: 'NTFS (Beta)',
 | 
					                name: 'NTFS',
 | 
				
			||||||
                constants: [
 | 
					                constants: [
 | 
				
			||||||
                    { id: 'baseOffsetNTFS', label: 'Basis-Offset (Partitionsstart)', unit: 'Bytes', default: '0x10000' },
 | 
					                    { id: 'baseOffsetNTFS', label: 'Basis-Offset (Partitionsstart)', unit: 'Bytes', default: '0x10000' },
 | 
				
			||||||
                    { id: 'sectorSizeNTFS', label: 'Sektor-Größe', unit: 'Bytes', default: '0x200' }
 | 
					                    { id: 'sectorSizeNTFS', label: 'Sektor-Größe (Boot-Offset 0x0B)', unit: 'Bytes', default: '0x200' }
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
                inputs: [
 | 
					                inputs: [
 | 
				
			||||||
                    { id: 'clusterSizeSectorsNTFS', label: 'Cluster-Größe', unit: 'Sektoren', placeholder: '0x8' },
 | 
					                    { id: 'clusterSizeSectorsNTFS', label: 'Cluster-Größe (Boot-Offset 0x0D)', unit: 'Sektoren', placeholder: '0x8' },
 | 
				
			||||||
                    { id: 'mftStartClusterNTFS', label: 'MFT Start', unit: 'Cluster', placeholder: '0xC0000' },
 | 
					                    { id: 'mftStartClusterNTFS', label: 'MFT Start (Boot-Offset 0x30)', unit: 'Cluster', placeholder: '0xC0000' },
 | 
				
			||||||
                    { id: 'mftEntrySizeRawNTFS', label: 'Größe MFT-Eintrag (Roh-Wert)', unit: 'Hex von Offset 0x40', placeholder: '0xF6' },
 | 
					                    { id: 'mftMirrStartClusterNTFS', label: 'Backup-MFT Start (Boot-Offset 0x38)', unit: 'Cluster', placeholder: '0x2' },
 | 
				
			||||||
                    { id: 'partitionSizeSectorsNTFS', label: 'Partitionsgröße', unit: 'Sektoren', placeholder: '0x3E7FFF' },
 | 
					                    { id: 'mftEntrySizeRawNTFS', label: 'Größe MFT-Eintrag (Boot-Offset 0x40)', unit: 'Roh-Wert', placeholder: '0xF6' },
 | 
				
			||||||
 | 
					                    { id: 'indexSizeRawNTFS', label: 'Größe Index (Boot-Offset 0x44)', unit: 'Roh-Wert', placeholder: '0x01' },
 | 
				
			||||||
 | 
					                    { id: 'partitionSizeSectorsNTFS', label: 'Partitionsgröße (Boot-Offset 0x28)', unit: 'Sektoren', placeholder: '0x3E7FFF' },
 | 
				
			||||||
                    { id: 'mftEntryNumberNTFS', label: 'MFT-Eintrags-Nummer', unit: 'Nummer', placeholder: '0x27' },
 | 
					                    { id: 'mftEntryNumberNTFS', label: 'MFT-Eintrags-Nummer', unit: 'Nummer', placeholder: '0x27' },
 | 
				
			||||||
                    { id: 'clusterNumberNTFS', label: 'Cluster-Nummer', unit: 'Nummer', placeholder: '0x1234' }
 | 
					                    { id: 'attributeListPosValueNTFS', label: 'Attribut-Start-Offset (MFT-Header-Offset 0x14)', unit: 'Bytes', placeholder: '0x38', tooltip: 'Der 2-Byte-Wert, den Sie an Position 0x14 innerhalb des MFT-Eintrags lesen. Gibt an, wo die Attribut-Liste relativ zum MFT-Eintrag beginnt.' },
 | 
				
			||||||
 | 
					                    { id: 'clusterNumberNTFS', label: 'Cluster-Nummer', unit: 'Nummer', placeholder: '0x1234' },
 | 
				
			||||||
 | 
					                    { id: 'attributeOffsetNTFS', label: 'Attribut-Offset (relativ zum MFT-Eintrag)', unit: 'Bytes', placeholder: '0x50' },
 | 
				
			||||||
 | 
					                    { id: 'attributeTypeNTFS', label: 'Attribut-Typ (optional)', unit: 'Hex', placeholder: '0x80' }
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
                resultGroups: [
 | 
					                resultGroups: [
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'Basis-Berechnungen',
 | 
					                        name: 'Basis-Berechnungen',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'clusterSizeBytesNTFS', label: 'Cluster-Größe (Bytes)', dependencies: ['sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Sektor-Größe × Cluster-Größe in Sektoren' },
 | 
					                            { id: 'clusterSizeBytesNTFS', label: 'Cluster-Größe', dependencies: ['sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Sektor-Größe × Cluster-Größe in Sektoren' },
 | 
				
			||||||
                            { id: 'mftEntrySizeBytesNTFS', label: 'MFT-Eintrag Größe (Bytes)', dependencies: ['mftEntrySizeRawNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Wenn positiv: Wert × Cluster-Größe, wenn negativ: 2^|Wert|' },
 | 
					                            { id: 'mftEntrySizeBytesNTFS', label: 'MFT-Eintrag Größe', dependencies: ['mftEntrySizeRawNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Wenn positiv: Wert × Cluster-Größe, wenn negativ: 2^|Wert|' },
 | 
				
			||||||
                            { id: 'mftEntrySizeCalculationNTFS', label: 'MFT-Eintrag Berechnung', dependencies: ['mftEntrySizeRawNTFS'], formula: 'Erklärung der Kodierung' }
 | 
					                            { id: 'indexSizeBytesNTFS', label: 'Index-Größe', dependencies: ['indexSizeRawNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Wenn positiv: Wert × Cluster-Größe, wenn negativ: 2^|Wert|' }
 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'MFT-Struktur',
 | 
					                        name: 'MFT-Struktur',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'mftStartOffsetNTFS', label: 'MFT Start (Bytes)', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Basis-Offset + (MFT Start Cluster × Cluster-Größe)' },
 | 
					                            { id: 'mftStartOffsetNTFS', label: 'MFT Start', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Basis-Offset + (MFT Start Cluster × Cluster-Größe)' },
 | 
				
			||||||
                            { id: 'mftStartSectorNTFS', label: 'MFT Start (Sektor)', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'MFT Start Offset ÷ Sektor-Größe' }
 | 
					                            { id: 'mftMirrStartOffsetNTFS', label: 'Backup-MFT Start (MFTMirr)', dependencies: ['baseOffsetNTFS', 'mftMirrStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Basis-Offset + (Backup-MFT Cluster × Cluster-Größe)' }
 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'Spezifischer MFT-Eintrag',
 | 
					                        name: 'Spezifischer MFT-Eintrag',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'mftEntryOffsetNTFS', label: 'MFT-Eintrag Offset (Bytes)', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS', 'mftEntryNumberNTFS'], formula: 'MFT Start + (MFT-Eintrags-Nummer × MFT-Eintrag Größe)' },
 | 
					                            { id: 'mftEntryOffsetNTFS', label: 'MFT-Eintrag Offset', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS', 'mftEntryNumberNTFS'], formula: 'MFT Start + (MFT-Eintrags-Nummer × MFT-Eintrag Größe)' },
 | 
				
			||||||
                            { id: 'attributeListOffsetNTFS', label: 'Attribut-Liste Position', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS', 'mftEntryNumberNTFS'], formula: 'MFT-Eintrag Offset + 0x14 (aus Tabelle A2)' }
 | 
					                            { id: 'mftHeaderAttributeListPtrNTFS', label: 'Position 0x14 im MFT-Header (wo zu lesen)', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS', 'mftEntryNumberNTFS'], formula: 'MFT-Eintrag Offset + 0x14' },
 | 
				
			||||||
 | 
					                            { id: 'calculatedAttributeStartNTFS', label: 'Tatsächlicher Attribut-Start (berechnet)', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS', 'mftEntryNumberNTFS', 'attributeListPosValueNTFS'], formula: 'MFT-Eintrag Offset + (gelesener Wert)' },
 | 
				
			||||||
 | 
					                            { id: 'specificAttributeOffsetNTFS', label: 'Spezifisches Attribut Offset', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS', 'mftEntryNumberNTFS', 'attributeOffsetNTFS'], formula: 'MFT-Eintrag Offset + Attribut-Offset' }
 | 
				
			||||||
 | 
					                        ]
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        name: 'Reservierte MFT-Einträge (Systemdateien)',
 | 
				
			||||||
 | 
					                        results: [
 | 
				
			||||||
 | 
					                            { id: 'mftSelfOffsetNTFS', label: '$Mft (0) - Master File Table', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (0 × MFT-Eintrag Größe)' },
 | 
				
			||||||
 | 
					                            { id: 'mftMirrOffsetNTFS', label: '$MftMirr (1) - Backup erste 4 Einträge', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (1 × MFT-Eintrag Größe)' },
 | 
				
			||||||
 | 
					                            { id: 'logFileOffsetNTFS', label: '$LogFile (2) - Transaktionslog', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (2 × MFT-Eintrag Größe)' },
 | 
				
			||||||
 | 
					                            { id: 'volumeOffsetNTFS', label: '$Volume (3) - Volume-Informationen', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (3 × MFT-Eintrag Größe)' },
 | 
				
			||||||
 | 
					                            { id: 'attrDefOffsetNTFS', label: '$AttrDef (4) - Attributbeschreibungen', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (4 × MFT-Eintrag Größe)' },
 | 
				
			||||||
 | 
					                            { id: 'rootDirOffsetNTFS', label: '. (5) - Wurzelverzeichnis', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (5 × MFT-Eintrag Größe)' },
 | 
				
			||||||
 | 
					                            { id: 'bitmapOffsetNTFS', label: '$Bitmap (6) - Cluster-Belegung', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (6 × MFT-Eintrag Größe)' },
 | 
				
			||||||
 | 
					                            { id: 'bootOffsetNTFS', label: '$Boot (7) - Bootsektor', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (7 × MFT-Eintrag Größe)' },
 | 
				
			||||||
 | 
					                            { id: 'badClusOffsetNTFS', label: '$BadClus (8) - Fehlerhafte Cluster', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (8 × MFT-Eintrag Größe)' },
 | 
				
			||||||
 | 
					                            { id: 'secureOffsetNTFS', label: '$Secure (9) - Sicherheitsinformationen', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (9 × MFT-Eintrag Größe)' },
 | 
				
			||||||
 | 
					                            { id: 'upCaseOffsetNTFS', label: '$UpCase (10) - Zeichenkonvertierung', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (10 × MFT-Eintrag Größe)' },
 | 
				
			||||||
 | 
					                            { id: 'extendOffsetNTFS', label: '$Extend (11) - Erweiterungen', dependencies: ['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'], formula: 'MFT Start + (11 × MFT-Eintrag Größe)' }
 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'Spezifischer Cluster',
 | 
					                        name: 'Spezifischer Cluster',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'clusterOffsetNTFS', label: 'Cluster Offset (Bytes)', dependencies: ['baseOffsetNTFS', 'clusterNumberNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Basis-Offset + (Cluster-Nummer × Cluster-Größe)' },
 | 
					                            { id: 'clusterOffsetNTFS', label: 'Cluster Offset', dependencies: ['baseOffsetNTFS', 'clusterNumberNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Basis-Offset + (Cluster-Nummer × Cluster-Größe)' }
 | 
				
			||||||
                            { id: 'clusterSectorNTFS', label: 'Cluster Start (Sektor)', dependencies: ['baseOffsetNTFS', 'clusterNumberNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Cluster Offset ÷ Sektor-Größe' }
 | 
					 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        name: 'Partitions-Übersicht',
 | 
					                        name: 'Partitions-Übersicht',
 | 
				
			||||||
                        results: [
 | 
					                        results: [
 | 
				
			||||||
                            { id: 'partitionSizeBytesNTFS', label: 'Partitionsgröße (Bytes)', dependencies: ['partitionSizeSectorsNTFS', 'sectorSizeNTFS'], formula: 'Partitionsgröße in Sektoren × Sektor-Größe' },
 | 
					                            { id: 'partitionSizeBytesNTFS', label: 'Partitionsgröße', dependencies: ['partitionSizeSectorsNTFS', 'sectorSizeNTFS'], formula: 'Partitionsgröße in Sektoren × Sektor-Größe' },
 | 
				
			||||||
                            { id: 'totalClustersNTFS', label: 'Anzahl Cluster', dependencies: ['partitionSizeSectorsNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Partitionsgröße ÷ Cluster-Größe' },
 | 
					                            { id: 'totalClustersNTFS', label: 'Anzahl Cluster', dependencies: ['partitionSizeSectorsNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS'], formula: 'Partitionsgröße ÷ Cluster-Größe' },
 | 
				
			||||||
                            { id: 'partitionEndOffsetNTFS', label: 'Partitionsende (Bytes)', dependencies: ['baseOffsetNTFS', 'partitionSizeSectorsNTFS', 'sectorSizeNTFS'], formula: 'Basis-Offset + Partitionsgröße' }
 | 
					                            { id: 'partitionEndOffsetNTFS', label: 'Partitionsende', dependencies: ['baseOffsetNTFS', 'partitionSizeSectorsNTFS', 'sectorSizeNTFS'], formula: 'Basis-Offset + Partitionsgröße' }
 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
                formulas: [
 | 
					                formulas: [
 | 
				
			||||||
                    { name: 'Cluster-Größe (Bytes)', expression: 'Sektor-Größe × Cluster-Größe in Sektoren' },
 | 
					                    { name: 'Cluster-Größe (Bytes)', expression: 'Sektor-Größe × Cluster-Größe in Sektoren' },
 | 
				
			||||||
                    { name: 'MFT-Eintrag Größe', expression: 'Wenn Wert positiv (0x00-0x7F): Wert × Cluster-Größe; Wenn Wert negativ (0x80-0xFF): 2^|Wert| Bytes' },
 | 
					                    { name: 'MFT-Eintrag Größe', expression: 'Wenn Wert positiv (0x00-0x7F): Wert × Cluster-Größe; Wenn Wert negativ (0x80-0xFF): 2^|Wert| Bytes' },
 | 
				
			||||||
 | 
					                    { name: 'Index-Größe', expression: 'Wenn Wert positiv (0x00-0x7F): Wert × Cluster-Größe; Wenn Wert negativ (0x80-0xFF): 2^|Wert| Bytes' },
 | 
				
			||||||
                    { name: 'MFT Start Offset', expression: 'Basis-Offset + (MFT Start Cluster × Cluster-Größe in Bytes)' },
 | 
					                    { name: 'MFT Start Offset', expression: 'Basis-Offset + (MFT Start Cluster × Cluster-Größe in Bytes)' },
 | 
				
			||||||
 | 
					                    { name: 'Backup-MFT Start', expression: 'Basis-Offset + (Backup-MFT Cluster × Cluster-Größe in Bytes)' },
 | 
				
			||||||
                    { name: 'MFT-Eintrag Offset', expression: 'MFT Start + (MFT-Eintrags-Nummer × MFT-Eintrag Größe)' },
 | 
					                    { name: 'MFT-Eintrag Offset', expression: 'MFT Start + (MFT-Eintrags-Nummer × MFT-Eintrag Größe)' },
 | 
				
			||||||
                    { name: 'Attribut-Liste Position', expression: 'MFT-Eintrag Offset + 0x14 (Position aus Tabelle A2)' },
 | 
					                    { name: 'Zeiger auf Attribut-Liste', expression: 'MFT-Eintrag Offset + 0x14 (Adresse des Zeigers im Header)' },
 | 
				
			||||||
 | 
					                    { name: 'Berechneter Attribut-Start', expression: 'MFT-Eintrag Offset + (Wert der an Offset 0x14 gelesen wurde)' },
 | 
				
			||||||
 | 
					                    { name: 'Spezifisches Attribut', expression: 'MFT-Eintrag Offset + Attribut-Offset (relativ)' },
 | 
				
			||||||
                    { name: 'Cluster Offset', expression: 'Basis-Offset + (Cluster-Nummer × Cluster-Größe in Bytes)' },
 | 
					                    { name: 'Cluster Offset', expression: 'Basis-Offset + (Cluster-Nummer × Cluster-Größe in Bytes)' },
 | 
				
			||||||
                    { name: 'Partitionsgröße', expression: 'Partitionsgröße in Sektoren × Sektor-Größe' },
 | 
					                    { name: 'Partitionsgröße', expression: 'Partitionsgröße in Sektoren × Sektor-Größe' },
 | 
				
			||||||
                    { name: 'Anzahl Cluster', expression: 'Partitionsgröße in Bytes ÷ Cluster-Größe in Bytes' }
 | 
					                    { name: 'Anzahl Cluster', expression: 'Partitionsgröße in Bytes ÷ Cluster-Größe in Bytes' },
 | 
				
			||||||
 | 
					                    { name: 'Reservierte MFT-Einträge', expression: 'MFT Start + (Eintragsnummer 0-15 × MFT-Eintrag Größe)' }
 | 
				
			||||||
                ]
 | 
					                ]
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
@ -84,7 +112,7 @@ export class NTFSFilesystem extends BaseFilesystem {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    calculateMFTEntrySize(rawValue, clusterSizeBytes) {
 | 
					    calculateMFTEntrySize(rawValue, clusterSizeBytes) {
 | 
				
			||||||
        // Special encoding from offset 0x40
 | 
					        // Special encoding from offset 0x40 and 0x44
 | 
				
			||||||
        // If positive (0x00-0x7F): size = value * cluster size
 | 
					        // If positive (0x00-0x7F): size = value * cluster size
 | 
				
			||||||
        // If negative (0x80-0xFF): size = 2^|value| bytes
 | 
					        // If negative (0x80-0xFF): size = 2^|value| bytes
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@ -94,23 +122,46 @@ export class NTFSFilesystem extends BaseFilesystem {
 | 
				
			|||||||
        if (rawValue >= 0x00 && rawValue <= 0x7F) {
 | 
					        if (rawValue >= 0x00 && rawValue <= 0x7F) {
 | 
				
			||||||
            // Positive value: multiply by cluster size
 | 
					            // Positive value: multiply by cluster size
 | 
				
			||||||
            size = rawValue * clusterSizeBytes;
 | 
					            size = rawValue * clusterSizeBytes;
 | 
				
			||||||
            explanation = `Positiv (0x${rawValue.toString(16).toUpperCase()}): 0x${rawValue.toString(16).toUpperCase()} × 0x${clusterSizeBytes.toString(16).toUpperCase()} = 0x${size.toString(16).toUpperCase()}`;
 | 
					            explanation = `Positiv (0x${rawValue.toString(16).toUpperCase()}): 0x${rawValue.toString(16).toUpperCase()} × 0x${clusterSizeBytes.toString(16).toUpperCase()} = 0x${size.toString(16).toUpperCase()} (${size} Bytes)`;
 | 
				
			||||||
        } else if (rawValue >= 0x80 && rawValue <= 0xFF) {
 | 
					        } else if (rawValue >= 0x80 && rawValue <= 0xFF) {
 | 
				
			||||||
            // Negative value: 2^|value| bytes
 | 
					            // Negative value: 2^|value| bytes
 | 
				
			||||||
            // Calculate two's complement for 1 byte: 0x100 - value
 | 
					            // Calculate two's complement for 1 byte: 0x100 - value
 | 
				
			||||||
            const negativeValue = 0x100 - rawValue;
 | 
					            const negativeValue = 0x100 - rawValue;
 | 
				
			||||||
            size = Math.pow(2, negativeValue);
 | 
					            size = Math.pow(2, negativeValue);
 | 
				
			||||||
            explanation = `Negativ (0x${rawValue.toString(16).toUpperCase()}): 2^${negativeValue} = 0x${size.toString(16).toUpperCase()} Bytes`;
 | 
					            explanation = `Negativ (0x${rawValue.toString(16).toUpperCase()}): 2^${negativeValue} = 0x${size.toString(16).toUpperCase()} (${size} Bytes)`;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        return { size, explanation };
 | 
					        return { size, explanation };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getAttributeTypeName(typeCode) {
 | 
				
			||||||
 | 
					        const types = {
 | 
				
			||||||
 | 
					            0x10: '$STANDARD_INFORMATION',
 | 
				
			||||||
 | 
					            0x20: '$ATTRIBUTE_LIST',
 | 
				
			||||||
 | 
					            0x30: '$FILE_NAME',
 | 
				
			||||||
 | 
					            0x40: '$OBJECT_ID',
 | 
				
			||||||
 | 
					            0x50: '$SECURITY_DESCRIPTOR',
 | 
				
			||||||
 | 
					            0x60: '$VOLUME_NAME',
 | 
				
			||||||
 | 
					            0x70: '$VOLUME_INFORMATION',
 | 
				
			||||||
 | 
					            0x80: '$DATA',
 | 
				
			||||||
 | 
					            0x90: '$INDEX_ROOT',
 | 
				
			||||||
 | 
					            0xA0: '$INDEX_ALLOCATION',
 | 
				
			||||||
 | 
					            0xB0: '$BITMAP',
 | 
				
			||||||
 | 
					            0xC0: '$REPARSE_POINT',
 | 
				
			||||||
 | 
					            0xD0: '$EA_INFORMATION',
 | 
				
			||||||
 | 
					            0xE0: '$EA',
 | 
				
			||||||
 | 
					            0xF0: '$PROPERTY_SET',
 | 
				
			||||||
 | 
					            0x100: '$LOGGED_UTILITY_STREAM'
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        return types[typeCode] || `0x${typeCode.toString(16).toUpperCase()}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    calculateNTFS(values, results) {
 | 
					    calculateNTFS(values, results) {
 | 
				
			||||||
        // Cluster Size in Bytes
 | 
					        // Cluster Size (bytes and sectors)
 | 
				
			||||||
        if (checkDependencies(['sectorSizeNTFS', 'clusterSizeSectorsNTFS'])) {
 | 
					        if (checkDependencies(['sectorSizeNTFS', 'clusterSizeSectorsNTFS'])) {
 | 
				
			||||||
            results.clusterSizeBytes = values.sectorSizeNTFS * values.clusterSizeSectorsNTFS;
 | 
					            results.clusterSizeBytes = values.sectorSizeNTFS * values.clusterSizeSectorsNTFS;
 | 
				
			||||||
            updateResultItem('clusterSizeBytesNTFS', results.clusterSizeBytes, true,
 | 
					            results.clusterSizeSectors = values.clusterSizeSectorsNTFS;
 | 
				
			||||||
 | 
					            updateResultItem('clusterSizeBytesNTFS', { bytes: results.clusterSizeBytes, sectors: results.clusterSizeSectors }, true,
 | 
				
			||||||
                `0x${values.sectorSizeNTFS.toString(16).toUpperCase()} × 0x${values.clusterSizeSectorsNTFS.toString(16).toUpperCase()} = 0x${results.clusterSizeBytes.toString(16).toUpperCase()}`);
 | 
					                `0x${values.sectorSizeNTFS.toString(16).toUpperCase()} × 0x${values.clusterSizeSectorsNTFS.toString(16).toUpperCase()} = 0x${results.clusterSizeBytes.toString(16).toUpperCase()}`);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem('clusterSizeBytesNTFS', 0, false);
 | 
					            updateResultItem('clusterSizeBytesNTFS', 0, false);
 | 
				
			||||||
@ -124,75 +175,129 @@ export class NTFSFilesystem extends BaseFilesystem {
 | 
				
			|||||||
            
 | 
					            
 | 
				
			||||||
            updateResultItem('mftEntrySizeBytesNTFS', results.mftEntrySizeBytes, true,
 | 
					            updateResultItem('mftEntrySizeBytesNTFS', results.mftEntrySizeBytes, true,
 | 
				
			||||||
                mftEntryCalc.explanation);
 | 
					                mftEntryCalc.explanation);
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Display explanation as text
 | 
					 | 
				
			||||||
            const explanationElement = document.getElementById('mftEntrySizeCalculationNTFS');
 | 
					 | 
				
			||||||
            if (explanationElement) {
 | 
					 | 
				
			||||||
                explanationElement.innerHTML = mftEntryCalc.explanation;
 | 
					 | 
				
			||||||
                const resultItem = explanationElement.closest('.result-item');
 | 
					 | 
				
			||||||
                resultItem.classList.remove('unavailable');
 | 
					 | 
				
			||||||
                resultItem.classList.add('available');
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem('mftEntrySizeBytesNTFS', 0, false);
 | 
					            updateResultItem('mftEntrySizeBytesNTFS', 0, false);
 | 
				
			||||||
            updateResultItem('mftEntrySizeCalculationNTFS', 0, false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // MFT Start Offset
 | 
					        // Index Size (with special encoding, same as MFT entry size)
 | 
				
			||||||
 | 
					        if (checkDependencies(['indexSizeRawNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS']) && results.clusterSizeBytes !== undefined) {
 | 
				
			||||||
 | 
					            const indexSizeCalc = this.calculateMFTEntrySize(values.indexSizeRawNTFS, results.clusterSizeBytes);
 | 
				
			||||||
 | 
					            results.indexSizeBytes = indexSizeCalc.size;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            updateResultItem('indexSizeBytesNTFS', results.indexSizeBytes, true,
 | 
				
			||||||
 | 
					                indexSizeCalc.explanation);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem('indexSizeBytesNTFS', 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // MFT Start Offset (bytes and sector)
 | 
				
			||||||
        if (checkDependencies(['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS']) && results.clusterSizeBytes !== undefined) {
 | 
					        if (checkDependencies(['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS']) && results.clusterSizeBytes !== undefined) {
 | 
				
			||||||
            results.mftStartOffset = values.baseOffsetNTFS + (values.mftStartClusterNTFS * results.clusterSizeBytes);
 | 
					            results.mftStartOffset = values.baseOffsetNTFS + (values.mftStartClusterNTFS * results.clusterSizeBytes);
 | 
				
			||||||
            results.mftStartSector = Math.floor(results.mftStartOffset / values.sectorSizeNTFS);
 | 
					            results.mftStartSector = Math.floor(results.mftStartOffset / values.sectorSizeNTFS);
 | 
				
			||||||
            
 | 
					            updateResultItem('mftStartOffsetNTFS', { bytes: results.mftStartOffset, sectors: results.mftStartSector }, true,
 | 
				
			||||||
            updateResultItem('mftStartOffsetNTFS', results.mftStartOffset, true,
 | 
					 | 
				
			||||||
                `0x${values.baseOffsetNTFS.toString(16).toUpperCase()} + (0x${values.mftStartClusterNTFS.toString(16).toUpperCase()} × 0x${results.clusterSizeBytes.toString(16).toUpperCase()}) = 0x${results.mftStartOffset.toString(16).toUpperCase()}`);
 | 
					                `0x${values.baseOffsetNTFS.toString(16).toUpperCase()} + (0x${values.mftStartClusterNTFS.toString(16).toUpperCase()} × 0x${results.clusterSizeBytes.toString(16).toUpperCase()}) = 0x${results.mftStartOffset.toString(16).toUpperCase()}`);
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            updateResultItem('mftStartSectorNTFS', results.mftStartSector, true,
 | 
					 | 
				
			||||||
                `0x${results.mftStartOffset.toString(16).toUpperCase()} ÷ 0x${values.sectorSizeNTFS.toString(16).toUpperCase()} = 0x${results.mftStartSector.toString(16).toUpperCase()}`);
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem('mftStartOffsetNTFS', 0, false);
 | 
					            updateResultItem('mftStartOffsetNTFS', 0, false);
 | 
				
			||||||
            updateResultItem('mftStartSectorNTFS', 0, false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Specific MFT Entry Offset
 | 
					        // Backup MFT (MFTMirr) Start Offset (bytes and sector)
 | 
				
			||||||
 | 
					        if (checkDependencies(['baseOffsetNTFS', 'mftMirrStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS']) && results.clusterSizeBytes !== undefined) {
 | 
				
			||||||
 | 
					            results.mftMirrStartOffset = values.baseOffsetNTFS + (values.mftMirrStartClusterNTFS * results.clusterSizeBytes);
 | 
				
			||||||
 | 
					            results.mftMirrStartSector = Math.floor(results.mftMirrStartOffset / values.sectorSizeNTFS);
 | 
				
			||||||
 | 
					            updateResultItem('mftMirrStartOffsetNTFS', { bytes: results.mftMirrStartOffset, sectors: results.mftMirrStartSector }, true,
 | 
				
			||||||
 | 
					                `0x${values.baseOffsetNTFS.toString(16).toUpperCase()} + (0x${values.mftMirrStartClusterNTFS.toString(16).toUpperCase()} × 0x${results.clusterSizeBytes.toString(16).toUpperCase()}) = 0x${results.mftMirrStartOffset.toString(16).toUpperCase()}`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem('mftMirrStartOffsetNTFS', 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Specific MFT Entry Offset (bytes and sector for offset)
 | 
				
			||||||
        if (checkDependencies(['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS', 'mftEntryNumberNTFS']) 
 | 
					        if (checkDependencies(['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS', 'mftEntryNumberNTFS']) 
 | 
				
			||||||
            && results.mftStartOffset !== undefined && results.mftEntrySizeBytes !== undefined) {
 | 
					            && results.mftStartOffset !== undefined && results.mftEntrySizeBytes !== undefined) {
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            results.mftEntryOffset = results.mftStartOffset + (values.mftEntryNumberNTFS * results.mftEntrySizeBytes);
 | 
					            results.mftEntryOffset = results.mftStartOffset + (values.mftEntryNumberNTFS * results.mftEntrySizeBytes);
 | 
				
			||||||
            
 | 
					            results.mftEntryOffsetSector = Math.floor(results.mftEntryOffset / values.sectorSizeNTFS);
 | 
				
			||||||
            updateResultItem('mftEntryOffsetNTFS', results.mftEntryOffset, true,
 | 
					            updateResultItem('mftEntryOffsetNTFS', { bytes: results.mftEntryOffset, sectors: results.mftEntryOffsetSector }, true,
 | 
				
			||||||
                `0x${results.mftStartOffset.toString(16).toUpperCase()} + (0x${values.mftEntryNumberNTFS.toString(16).toUpperCase()} × 0x${results.mftEntrySizeBytes.toString(16).toUpperCase()}) = 0x${results.mftEntryOffset.toString(16).toUpperCase()}`);
 | 
					                `0x${results.mftStartOffset.toString(16).toUpperCase()} + (0x${values.mftEntryNumberNTFS.toString(16).toUpperCase()} × 0x${results.mftEntrySizeBytes.toString(16).toUpperCase()}) = 0x${results.mftEntryOffset.toString(16).toUpperCase()}`);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Attribute List Position (from Table A2, offset 0x14)
 | 
					            // Offset 0x14: Address of the pointer to attribute list
 | 
				
			||||||
            results.attributeListOffset = results.mftEntryOffset + 0x14;
 | 
					            results.mftHeaderAttributeListPtr = results.mftEntryOffset + 0x14;
 | 
				
			||||||
            updateResultItem('attributeListOffsetNTFS', results.attributeListOffset, true,
 | 
					            updateResultItem('mftHeaderAttributeListPtrNTFS', results.mftHeaderAttributeListPtr, true,
 | 
				
			||||||
                `0x${results.mftEntryOffset.toString(16).toUpperCase()} + 0x14 = 0x${results.attributeListOffset.toString(16).toUpperCase()}`);
 | 
					                `0x${results.mftEntryOffset.toString(16).toUpperCase()} + 0x14 = 0x${results.mftHeaderAttributeListPtr.toString(16).toUpperCase()} (lies 2 Bytes an dieser Adresse)`);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Calculated Attribute Start (if user provides the value read from 0x14)
 | 
				
			||||||
 | 
					            if (checkDependencies(['attributeListPosValueNTFS'])) {
 | 
				
			||||||
 | 
					                results.calculatedAttributeStart = results.mftEntryOffset + values.attributeListPosValueNTFS;
 | 
				
			||||||
 | 
					                updateResultItem('calculatedAttributeStartNTFS', results.calculatedAttributeStart, true,
 | 
				
			||||||
 | 
					                    `0x${results.mftEntryOffset.toString(16).toUpperCase()} + 0x${values.attributeListPosValueNTFS.toString(16).toUpperCase()} = 0x${results.calculatedAttributeStart.toString(16).toUpperCase()}`);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
            updateResultItem('mftEntryOffsetNTFS', 0, false);
 | 
					                updateResultItem('calculatedAttributeStartNTFS', 0, false);
 | 
				
			||||||
            updateResultItem('attributeListOffsetNTFS', 0, false);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        // Specific Cluster Offset
 | 
					            // Specific Attribute Offset
 | 
				
			||||||
 | 
					            if (checkDependencies(['attributeOffsetNTFS'])) {
 | 
				
			||||||
 | 
					                results.specificAttributeOffset = results.mftEntryOffset + values.attributeOffsetNTFS;
 | 
				
			||||||
 | 
					                let attrInfo = '';
 | 
				
			||||||
 | 
					                if (checkDependencies(['attributeTypeNTFS']) && values.attributeTypeNTFS) {
 | 
				
			||||||
 | 
					                    attrInfo = ` (${this.getAttributeTypeName(values.attributeTypeNTFS)})`;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                updateResultItem('specificAttributeOffsetNTFS', results.specificAttributeOffset, true,
 | 
				
			||||||
 | 
					                    `0x${results.mftEntryOffset.toString(16).toUpperCase()} + 0x${values.attributeOffsetNTFS.toString(16).toUpperCase()} = 0x${results.specificAttributeOffset.toString(16).toUpperCase()}${attrInfo}`);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                updateResultItem('specificAttributeOffsetNTFS', 0, false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            updateResultItem('mftEntryOffsetNTFS', 0, false);
 | 
				
			||||||
 | 
					            updateResultItem('mftHeaderAttributeListPtrNTFS', 0, false);
 | 
				
			||||||
 | 
					            updateResultItem('calculatedAttributeStartNTFS', 0, false);
 | 
				
			||||||
 | 
					            updateResultItem('specificAttributeOffsetNTFS', 0, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Reserved MFT Entries (System files 0-11)
 | 
				
			||||||
 | 
					        if (checkDependencies(['baseOffsetNTFS', 'mftStartClusterNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS', 'mftEntrySizeRawNTFS'])
 | 
				
			||||||
 | 
					            && results.mftStartOffset !== undefined && results.mftEntrySizeBytes !== undefined) {
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            const systemFiles = [
 | 
				
			||||||
 | 
					                { id: 'mftSelfOffsetNTFS', num: 0, name: '$Mft' },
 | 
				
			||||||
 | 
					                { id: 'mftMirrOffsetNTFS', num: 1, name: '$MftMirr' },
 | 
				
			||||||
 | 
					                { id: 'logFileOffsetNTFS', num: 2, name: '$LogFile' },
 | 
				
			||||||
 | 
					                { id: 'volumeOffsetNTFS', num: 3, name: '$Volume' },
 | 
				
			||||||
 | 
					                { id: 'attrDefOffsetNTFS', num: 4, name: '$AttrDef' },
 | 
				
			||||||
 | 
					                { id: 'rootDirOffsetNTFS', num: 5, name: 'Root (.)' },
 | 
				
			||||||
 | 
					                { id: 'bitmapOffsetNTFS', num: 6, name: '$Bitmap' },
 | 
				
			||||||
 | 
					                { id: 'bootOffsetNTFS', num: 7, name: '$Boot' },
 | 
				
			||||||
 | 
					                { id: 'badClusOffsetNTFS', num: 8, name: '$BadClus' },
 | 
				
			||||||
 | 
					                { id: 'secureOffsetNTFS', num: 9, name: '$Secure' },
 | 
				
			||||||
 | 
					                { id: 'upCaseOffsetNTFS', num: 10, name: '$UpCase' },
 | 
				
			||||||
 | 
					                { id: 'extendOffsetNTFS', num: 11, name: '$Extend' }
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            systemFiles.forEach(file => {
 | 
				
			||||||
 | 
					                const offset = results.mftStartOffset + (file.num * results.mftEntrySizeBytes);
 | 
				
			||||||
 | 
					                updateResultItem(file.id, offset, true,
 | 
				
			||||||
 | 
					                    `0x${results.mftStartOffset.toString(16).toUpperCase()} + (${file.num} × 0x${results.mftEntrySizeBytes.toString(16).toUpperCase()}) = 0x${offset.toString(16).toUpperCase()}`);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            const systemFileIds = ['mftSelfOffsetNTFS', 'mftMirrOffsetNTFS', 'logFileOffsetNTFS', 'volumeOffsetNTFS',
 | 
				
			||||||
 | 
					                'attrDefOffsetNTFS', 'rootDirOffsetNTFS', 'bitmapOffsetNTFS', 'bootOffsetNTFS',
 | 
				
			||||||
 | 
					                'badClusOffsetNTFS', 'secureOffsetNTFS', 'upCaseOffsetNTFS', 'extendOffsetNTFS'];
 | 
				
			||||||
 | 
					            systemFileIds.forEach(id => updateResultItem(id, 0, false));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Specific Cluster Offset (bytes and sector)
 | 
				
			||||||
        if (checkDependencies(['baseOffsetNTFS', 'clusterNumberNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS']) && results.clusterSizeBytes !== undefined) {
 | 
					        if (checkDependencies(['baseOffsetNTFS', 'clusterNumberNTFS', 'sectorSizeNTFS', 'clusterSizeSectorsNTFS']) && results.clusterSizeBytes !== undefined) {
 | 
				
			||||||
            results.clusterOffset = values.baseOffsetNTFS + (values.clusterNumberNTFS * results.clusterSizeBytes);
 | 
					            results.clusterOffset = values.baseOffsetNTFS + (values.clusterNumberNTFS * results.clusterSizeBytes);
 | 
				
			||||||
            results.clusterSector = Math.floor(results.clusterOffset / values.sectorSizeNTFS);
 | 
					            results.clusterSector = Math.floor(results.clusterOffset / values.sectorSizeNTFS);
 | 
				
			||||||
            
 | 
					            updateResultItem('clusterOffsetNTFS', { bytes: results.clusterOffset, sectors: results.clusterSector }, true,
 | 
				
			||||||
            updateResultItem('clusterOffsetNTFS', results.clusterOffset, true,
 | 
					 | 
				
			||||||
                `0x${values.baseOffsetNTFS.toString(16).toUpperCase()} + (0x${values.clusterNumberNTFS.toString(16).toUpperCase()} × 0x${results.clusterSizeBytes.toString(16).toUpperCase()}) = 0x${results.clusterOffset.toString(16).toUpperCase()}`);
 | 
					                `0x${values.baseOffsetNTFS.toString(16).toUpperCase()} + (0x${values.clusterNumberNTFS.toString(16).toUpperCase()} × 0x${results.clusterSizeBytes.toString(16).toUpperCase()}) = 0x${results.clusterOffset.toString(16).toUpperCase()}`);
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            updateResultItem('clusterSectorNTFS', results.clusterSector, true,
 | 
					 | 
				
			||||||
                `0x${results.clusterOffset.toString(16).toUpperCase()} ÷ 0x${values.sectorSizeNTFS.toString(16).toUpperCase()} = 0x${results.clusterSector.toString(16).toUpperCase()}`);
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            updateResultItem('clusterOffsetNTFS', 0, false);
 | 
					            updateResultItem('clusterOffsetNTFS', 0, false);
 | 
				
			||||||
            updateResultItem('clusterSectorNTFS', 0, false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Partition Overview
 | 
					        // Partition Overview (bytes and sectors)
 | 
				
			||||||
        if (checkDependencies(['partitionSizeSectorsNTFS', 'sectorSizeNTFS'])) {
 | 
					        if (checkDependencies(['partitionSizeSectorsNTFS', 'sectorSizeNTFS'])) {
 | 
				
			||||||
            results.partitionSizeBytes = values.partitionSizeSectorsNTFS * values.sectorSizeNTFS;
 | 
					            results.partitionSizeBytes = values.partitionSizeSectorsNTFS * values.sectorSizeNTFS;
 | 
				
			||||||
            
 | 
					            results.partitionSizeSectors = values.partitionSizeSectorsNTFS;
 | 
				
			||||||
            updateResultItem('partitionSizeBytesNTFS', results.partitionSizeBytes, true,
 | 
					            updateResultItem('partitionSizeBytesNTFS', { bytes: results.partitionSizeBytes, sectors: results.partitionSizeSectors }, true,
 | 
				
			||||||
                `0x${values.partitionSizeSectorsNTFS.toString(16).toUpperCase()} × 0x${values.sectorSizeNTFS.toString(16).toUpperCase()} = 0x${results.partitionSizeBytes.toString(16).toUpperCase()}`);
 | 
					                `0x${values.partitionSizeSectorsNTFS.toString(16).toUpperCase()} × 0x${values.sectorSizeNTFS.toString(16).toUpperCase()} = 0x${results.partitionSizeBytes.toString(16).toUpperCase()}`);
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (checkDependencies(['baseOffsetNTFS'])) {
 | 
					            if (checkDependencies(['baseOffsetNTFS'])) {
 | 
				
			||||||
                results.partitionEndOffset = values.baseOffsetNTFS + results.partitionSizeBytes;
 | 
					                results.partitionEndOffset = values.baseOffsetNTFS + results.partitionSizeBytes;
 | 
				
			||||||
                updateResultItem('partitionEndOffsetNTFS', results.partitionEndOffset, true,
 | 
					                updateResultItem('partitionEndOffsetNTFS', results.partitionEndOffset, true,
 | 
				
			||||||
 | 
				
			|||||||
@ -2,13 +2,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { setupTooltips, setupCopyButtons } from './utils.js';
 | 
					import { setupTooltips, setupCopyButtons } from './utils.js';
 | 
				
			||||||
import { Calculator } from './calculator.js';
 | 
					import { Calculator } from './calculator.js';
 | 
				
			||||||
import { FATFilesystem } from './filesystems/fat.js';
 | 
					import { FAT12_16Filesystem, FAT32Filesystem } from './filesystems/fat.js';
 | 
				
			||||||
import { NTFSFilesystem } from './filesystems/ntfs.js';
 | 
					import { NTFSFilesystem } from './filesystems/ntfs.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FilesystemCalculator {
 | 
					class FilesystemCalculator {
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        this.filesystems = [
 | 
					        this.filesystems = [
 | 
				
			||||||
            new FATFilesystem(),
 | 
					            new FAT12_16Filesystem(),
 | 
				
			||||||
 | 
					            new FAT32Filesystem(),
 | 
				
			||||||
            new NTFSFilesystem()
 | 
					            new NTFSFilesystem()
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
        this.calculator = new Calculator();
 | 
					        this.calculator = new Calculator();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,18 @@
 | 
				
			|||||||
 | 
					// Format a result with multiple representations (e.g., bytes, sectors, etc.)
 | 
				
			||||||
 | 
					// values: { bytes: number, sectors?: number, other?: string }
 | 
				
			||||||
 | 
					export function formatMultiValue(values) {
 | 
				
			||||||
 | 
					    const parts = [];
 | 
				
			||||||
 | 
					    if (typeof values.bytes === 'number') {
 | 
				
			||||||
 | 
					        parts.push(formatHex(values.bytes) + ' Bytes');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (typeof values.sectors === 'number') {
 | 
				
			||||||
 | 
					        parts.push(formatHex(values.sectors) + ' Sektoren');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (values.other) {
 | 
				
			||||||
 | 
					        parts.push(values.other);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return parts.join(' // ');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
// Shared utility functions for the filesystem calculator
 | 
					// Shared utility functions for the filesystem calculator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function parseHex(value) {
 | 
					export function parseHex(value) {
 | 
				
			||||||
@ -43,11 +58,14 @@ export function checkDependencies(deps) {
 | 
				
			|||||||
export function updateResultItem(elementId, value, available, calculation = '') {
 | 
					export function updateResultItem(elementId, value, available, calculation = '') {
 | 
				
			||||||
    const element = document.getElementById(elementId);
 | 
					    const element = document.getElementById(elementId);
 | 
				
			||||||
    if (!element) return;
 | 
					    if (!element) return;
 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    const resultItem = element.closest('.result-item');
 | 
					    const resultItem = element.closest('.result-item');
 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    if (available) {
 | 
					    if (available) {
 | 
				
			||||||
 | 
					        // If value is an object with bytes/sectors/other, use formatMultiValue
 | 
				
			||||||
 | 
					        if (value && typeof value === 'object' && (value.bytes !== undefined || value.sectors !== undefined || value.other !== undefined)) {
 | 
				
			||||||
 | 
					            element.innerHTML = formatMultiValue(value);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
            element.innerHTML = formatHex(value);
 | 
					            element.innerHTML = formatHex(value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        resultItem.classList.remove('unavailable');
 | 
					        resultItem.classList.remove('unavailable');
 | 
				
			||||||
        resultItem.classList.add('available');
 | 
					        resultItem.classList.add('available');
 | 
				
			||||||
        if (calculation) {
 | 
					        if (calculation) {
 | 
				
			||||||
@ -64,13 +82,15 @@ export function updateResultItem(elementId, value, available, calculation = '')
 | 
				
			|||||||
export function copyToClipboard(elementId) {
 | 
					export function copyToClipboard(elementId) {
 | 
				
			||||||
    const element = document.getElementById(elementId);
 | 
					    const element = document.getElementById(elementId);
 | 
				
			||||||
    if (!element) return;
 | 
					    if (!element) return;
 | 
				
			||||||
    
 | 
					    let text = element.textContent.trim();
 | 
				
			||||||
    const text = element.textContent.trim();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (text === '-' || text.includes('Fehler')) {
 | 
					    if (text === '-' || text.includes('Fehler')) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    // Only copy the first hex value (e.g., 0x1004200)
 | 
				
			||||||
 | 
					    const match = text.match(/0x[0-9a-fA-F]+/);
 | 
				
			||||||
 | 
					    if (match) {
 | 
				
			||||||
 | 
					        text = match[0];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    navigator.clipboard.writeText(text).then(() => {
 | 
					    navigator.clipboard.writeText(text).then(() => {
 | 
				
			||||||
        const btn = element.parentElement.querySelector('.copy-btn');
 | 
					        const btn = element.parentElement.querySelector('.copy-btn');
 | 
				
			||||||
        if (btn) {
 | 
					        if (btn) {
 | 
				
			||||||
@ -99,20 +119,20 @@ export function createTooltip() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function showTooltipAt(x, y, text) {
 | 
					export function showTooltipAt(pageX, pageY, text) {
 | 
				
			||||||
    createTooltip();
 | 
					    createTooltip();
 | 
				
			||||||
    tooltip.innerHTML = text;
 | 
					    tooltip.innerHTML = text;
 | 
				
			||||||
    tooltip.style.display = 'block';
 | 
					    tooltip.style.display = 'block';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const tooltipRect = tooltip.getBoundingClientRect();
 | 
					    const tooltipRect = tooltip.getBoundingClientRect();
 | 
				
			||||||
    let left = x + 12; // offset from mouse pointer
 | 
					    let left = pageX + 12; // offset from mouse pointer
 | 
				
			||||||
    let top = y + 12;
 | 
					    let top = pageY + 12;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (left + tooltipRect.width > window.innerWidth - 10) {
 | 
					    if (left + tooltipRect.width > window.innerWidth - 10) {
 | 
				
			||||||
        left = window.innerWidth - tooltipRect.width - 10;
 | 
					        left = window.innerWidth - tooltipRect.width - 10;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (top + tooltipRect.height > window.innerHeight - 10) {
 | 
					    if (top + tooltipRect.height > window.innerHeight + window.scrollY - 10) {
 | 
				
			||||||
        top = y - tooltipRect.height - 12;
 | 
					        top = pageY - tooltipRect.height - 12;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (left < 10) left = 10;
 | 
					    if (left < 10) left = 10;
 | 
				
			||||||
    if (top < 10) top = 10;
 | 
					    if (top < 10) top = 10;
 | 
				
			||||||
@ -129,19 +149,40 @@ export function hideTooltip() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Setup tooltip event listeners
 | 
					// Setup tooltip event listeners
 | 
				
			||||||
export function setupTooltips() {
 | 
					export function setupTooltips() {
 | 
				
			||||||
    document.addEventListener('mousemove', function (e) {
 | 
					    document.addEventListener('mouseover', function (e) {
 | 
				
			||||||
        if (e.target.classList.contains('result-label')) {
 | 
					        if (e.target.classList.contains('result-label')) {
 | 
				
			||||||
            const formula = e.target.getAttribute('data-formula');
 | 
					            const formula = e.target.getAttribute('data-formula');
 | 
				
			||||||
            if (formula) {
 | 
					            if (formula) {
 | 
				
			||||||
                showTooltipAt(e.clientX, e.clientY, `Formel: ${formula}`);
 | 
					                showTooltipAt(e.pageX, e.pageY, `Formel: ${formula}`);
 | 
				
			||||||
 | 
					                e.target.addEventListener('mousemove', mouseMoveHandler);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else if (e.target.classList.contains('result-value')) {
 | 
					        } else if (e.target.classList.contains('result-value')) {
 | 
				
			||||||
            const formula = e.target.getAttribute('data-result-formula');
 | 
					            const formula = e.target.getAttribute('data-result-formula');
 | 
				
			||||||
            if (formula && formula !== '') {
 | 
					            if (formula && formula !== '') {
 | 
				
			||||||
                showTooltipAt(e.clientX, e.clientY, `Berechnung: ${formula}`);
 | 
					                showTooltipAt(e.pageX, e.pageY, `Berechnung: ${formula}`);
 | 
				
			||||||
 | 
					                e.target.addEventListener('mousemove', mouseMoveHandler);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function mouseMoveHandler(e) {
 | 
				
			||||||
 | 
					        if (e.target.classList.contains('result-label')) {
 | 
				
			||||||
 | 
					            const formula = e.target.getAttribute('data-formula');
 | 
				
			||||||
 | 
					            if (formula) {
 | 
				
			||||||
 | 
					                showTooltipAt(e.pageX, e.pageY, `Formel: ${formula}`);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else if (e.target.classList.contains('result-value')) {
 | 
				
			||||||
 | 
					            const formula = e.target.getAttribute('data-result-formula');
 | 
				
			||||||
 | 
					            if (formula && formula !== '') {
 | 
				
			||||||
 | 
					                showTooltipAt(e.pageX, e.pageY, `Berechnung: ${formula}`);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    document.addEventListener('mouseout', function (e) {
 | 
				
			||||||
 | 
					        if (e.target.classList.contains('result-label') || e.target.classList.contains('result-value')) {
 | 
				
			||||||
            hideTooltip();
 | 
					            hideTooltip();
 | 
				
			||||||
 | 
					            e.target.removeEventListener('mousemove', mouseMoveHandler);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user