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,21 +83,36 @@ 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 => {
|
||||||
<div class="result-item" data-deps="${result.dependencies.join(',')}">
|
// For each result, show all representations in one row
|
||||||
<span class="result-label" data-formula="${result.formula}">${result.label}:</span>
|
// The calculation logic must fill in all values in the result-value element, separated by //
|
||||||
<div class="result-value-container">
|
return `
|
||||||
<span class="result-value" id="${result.id}">-</span>
|
<div class="result-item" data-deps="${result.dependencies.join(',')}">
|
||||||
<button class="copy-btn" onclick="copyToClipboard('${result.id}')">📋</button>
|
<span class="result-label" data-formula="${result.formula}">${result.label}:</span>
|
||||||
|
<div class="result-value-container">
|
||||||
|
<span class="result-value" id="${result.id}">-</span>
|
||||||
|
<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">
|
||||||
${this.generateConstantsHTML(variantId)}
|
<div class="constants-tools-wrapper">
|
||||||
|
${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 {
|
||||||
|
updateResultItem('calculatedAttributeStartNTFS', 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
} else {
|
||||||
updateResultItem('mftEntryOffsetNTFS', 0, false);
|
updateResultItem('mftEntryOffsetNTFS', 0, false);
|
||||||
updateResultItem('attributeListOffsetNTFS', 0, false);
|
updateResultItem('mftHeaderAttributeListPtrNTFS', 0, false);
|
||||||
|
updateResultItem('calculatedAttributeStartNTFS', 0, false);
|
||||||
|
updateResultItem('specificAttributeOffsetNTFS', 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specific Cluster Offset
|
// 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) {
|
||||||
element.innerHTML = formatHex(value);
|
// 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);
|
||||||
|
}
|
||||||
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