diff --git a/src/lib/components/ByteEditor.svelte b/src/lib/components/ByteEditor.svelte
new file mode 100644
index 0000000..ffff467
--- /dev/null
+++ b/src/lib/components/ByteEditor.svelte
@@ -0,0 +1,354 @@
+
+
+
+
Редактор {byteLength} байт
+
+
+
+ Формат:
+
+ Hex
+ Decimal
+ Binary
+ Octal
+
+
+
+ {#if byteLength > 1}
+
+ Порядок байт:
+
+ Big Endian
+ Little Endian
+
+
+ {/if}
+
+
+
+
+
+
+
+ {#if buffer && startIndex + byteLength <= buffer.length}
+ {#each Array.from({length: byteLength}, (_, i) => i) as i}
+
+ [{startIndex + i}]
+ 0x{buffer[startIndex + i].toString(16).padStart(2, '0').toUpperCase()}
+ ({buffer[startIndex + i]})
+
+ {/each}
+ {/if}
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/components/HexEditor.svelte b/src/lib/components/HexEditor.svelte
new file mode 100644
index 0000000..66aff6d
--- /dev/null
+++ b/src/lib/components/HexEditor.svelte
@@ -0,0 +1,180 @@
+
+
+
+
Редактор буфера:
+
+
+ {#each hexString as {hex, index}}
+ selectByte(index, e)}
+ >
+ {'0x'+hex}
+
+ {/each}
+
+
+
+
+ Очистить все
+
+
+
+ {#if showToolSelector && selectedByteIndex >= 0 && !isByteReadOnly(selectedByteIndex)}
+
+ {/if}
+
+
+
\ No newline at end of file
diff --git a/src/lib/components/ToolSelector.svelte b/src/lib/components/ToolSelector.svelte
new file mode 100644
index 0000000..bac15c6
--- /dev/null
+++ b/src/lib/components/ToolSelector.svelte
@@ -0,0 +1,197 @@
+
+
+{#if show}
+
+
+
+{/if}
+
+
\ No newline at end of file
diff --git a/src/lib/components/WiresharkView.svelte b/src/lib/components/WiresharkView.svelte
new file mode 100644
index 0000000..89d5bd6
--- /dev/null
+++ b/src/lib/components/WiresharkView.svelte
@@ -0,0 +1,174 @@
+
+
+
+
Структура Ethernet кадра
+
+
+
+
Destination MAC:
+
{formatMAC(destinationMAC)}
+
MAC адрес получателя
+
+
+
+
Source MAC:
+
{formatMAC(sourceMAC)}
+
MAC адрес отправителя
+
+
+
+
EtherType:
+
{formatEtherType(etherType)}
+
Тип протокола
+
+
+
+
Data:
+
{formatHex(frameData)}
+
Полезная нагрузка
+
+
+
+
FCS (Frame Check Sequence):
+
{formatHex(fcs)}
+
Контрольная сумма
+
+
+
+
+
Структура кадра (64 байта):
+
+
+ Destination MAC
+ 6 bytes
+
+
+ Source MAC
+ 6 bytes
+
+
+ EtherType
+ 2 bytes
+
+
+ Data
+ 46 bytes
+
+
+ FCS
+ 4 bytes
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/data/lessons.js b/src/lib/data/lessons.js
index bd450c2..adf7da0 100644
--- a/src/lib/data/lessons.js
+++ b/src/lib/data/lessons.js
@@ -6,6 +6,7 @@ export const lessons = [
category: 'Основы',
difficulty: 'Начинающий',
component: 'BitEditor',
+ additionalComponents: ['HexViewer'],
theory: `
@@ -99,12 +100,90 @@ export const lessons = [
},
{
id: 2,
+ slug: 'mac-address-edit',
+ title: 'MAC адреса - редактирование 6 байт',
+ category: 'Ethernet',
+ difficulty: 'Начинающий',
+ component: 'HexEditor',
+
+ theory: `
+
+
+ MAC адреса
+
+
+
+
Что такое MAC адрес?
+
+ MAC адрес - уникальный идентификатор сетевого устройства
+ Формат: XX:XX:XX:XX:XX:XX (6 байтов)
+ Пример: 12:34:56:78:9A:BC
+ Первые 3 байта - идентификатор производителя (OUI)
+ Последние 3 байта - серийный номер устройства
+
+
+
+
+
Как редактировать MAC-адрес?
+
+ Форматы представления чисел:
+
+ Hex (16-ричный): 0xFF, 0x1A3B
+ Decimal (10-ричный): 255, 6715
+ Binary (2-ичный): 11111111, 110100011011
+ Octal (8-ричный): 377, 15073
+
+
+ Endianness (порядок байт):
+
+ Big Endian: старший байт по младшему адресу (сетевой порядок)
+ Little Endian: младший байт по младшему адресу (x86, ARM)
+ Пример: 0x12345678
+
+ Big Endian: 12 34 56 78
+ Little Endian: 78 56 34 12
+
+
+
+
+
+`,
+
+ objective: 'Установите MAC адрес 12:34:56:78:9A:BC',
+
+ initialBuffer: new Uint8Array(6),
+
+ validate: (buffer) => {
+ const expectedMAC = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC];
+ return expectedMAC.every((expected, i) => buffer[i] === expected);
+ },
+
+ hints: [
+ 'Для HEX: разделите байт на две половинки по 4 бита. 0x34 = 00110100 → 0011(3) 0100(4)',
+ 'Кликайте на байты в HexView чтобы редактировать их',
+ 'Степени двойки по битам: 128,64,32,16,8,4,2,1'
+ ]
+ },
+ {
+ id: 3,
slug: 'ethernet-frame',
title: 'Структура Ethernet кадра',
category: 'Ethernet',
difficulty: 'Начинающий',
- component: 'EthernetBuilder',
-
+ component: 'HexEditor',
+ additionalComponents: ['WiresharkView'],
+ wiresharkFields: [
+ { name: 'Destination MAC', start: 0, length: 6, format: 'mac', color: '#e3f2fd' },
+ { name: 'Source MAC', start: 6, length: 6, format: 'mac', color: '#fff3e0' },
+ { name: 'EtherType', start: 12, length: 2, format: 'ethertype', color: '#e8f5e8' },
+ { name: 'Data', start: 14, length: 46, format: 'hex', color: '#f3e5f5' },
+ { name: 'FCS', start: 60, length: 4, format: 'hex', color: '#ffebee' }
+ ],
+ readOnlyRanges: [
+ { start: 14, end: 59 }, //Data (46 байт)
+ { start: 60, end: 63 } //FCS (4 байта) - рассчитывается автоматически
+ ],
+
theory: `
diff --git a/src/lib/utils/bufferProcessor.js b/src/lib/utils/bufferProcessor.js
new file mode 100644
index 0000000..9bd73f0
--- /dev/null
+++ b/src/lib/utils/bufferProcessor.js
@@ -0,0 +1,10 @@
+import { updateEthernetBuffer } from './protocols/ethernet.js';
+
+export function processBuffer(buffer, lessonId) {
+ switch(lessonId) {
+ case 3: //Ethernet frame lesson
+ return updateEthernetBuffer(buffer);
+ default:
+ return buffer;
+ }
+}
\ No newline at end of file
diff --git a/src/lib/utils/protocols/ethernet.js b/src/lib/utils/protocols/ethernet.js
new file mode 100644
index 0000000..30329a9
--- /dev/null
+++ b/src/lib/utils/protocols/ethernet.js
@@ -0,0 +1,38 @@
+//функции для работы с Ethernet
+export function calculateFCS(headerBuffer) {
+ //упрощенная имитация CRC32
+ let sum = 0;
+ for (let i = 0; i < headerBuffer.length; i++) {
+ sum = (sum + headerBuffer[i]) & 0xFFFFFFFF;
+ }
+
+ for (let i = 0; i < 46; i++) {
+ sum = (sum + 0x00) & 0xFFFFFFFF;
+ }
+
+ return [
+ (sum >> 24) & 0xFF,
+ (sum >> 16) & 0xFF,
+ (sum >> 8) & 0xFF,
+ sum & 0xFF
+ ];
+}
+
+export function updateEthernetBuffer(buffer, changes = {}) {
+ const updatedBuffer = new Uint8Array(buffer);
+
+ Object.keys(changes).forEach(key => {
+ const index = parseInt(key);
+ if (index >= 0 && index < updatedBuffer.length) {
+ updatedBuffer[index] = changes[key];
+ }
+ });
+
+ const headerBuffer = updatedBuffer.slice(0, 14);
+ const fcs = calculateFCS(headerBuffer);
+ fcs.forEach((byte, i) => {
+ updatedBuffer[60 + i] = byte;
+ });
+
+ return updatedBuffer;
+}
\ No newline at end of file
diff --git a/src/routes/lessons/[slug]/+page.svelte b/src/routes/lessons/[slug]/+page.svelte
index 6362b50..8dd53bd 100644
--- a/src/routes/lessons/[slug]/+page.svelte
+++ b/src/routes/lessons/[slug]/+page.svelte
@@ -3,13 +3,16 @@
import { lessons } from '$lib/data/lessons';
import { progressStorage } from '$lib/utils/storage';
import { onMount, afterUpdate } from 'svelte';
+ import { processBuffer } from '$lib/utils/bufferProcessor';
import HexViewer from '$lib/components/HexViewer.svelte';
import LessonLayout from '$lib/components/LessonLayout.svelte';
import Notification from '$lib/components/Notification.svelte';
import BitEditor from '$lib/components/BitEditor.svelte';
- import EthernetBuilder from '$lib/components/EthernetBuilder.svelte';
+ //import EthernetBuilder from '$lib/components/EthernetBuilder.svelte';
+ import HexEditor from '$lib/components/HexEditor.svelte';
+ import WiresharkView from '$lib/components/WiresharkView.svelte';
let currentBuffer;
let isCompleted = false;
@@ -18,6 +21,8 @@
let notification = { show: false, message: '', type: 'success' };
let lesson = null;
let lessonComponent = null;
+ let additionalComponents = [];
+ let readOnlyRanges = [];
afterUpdate(() => {
if ($page.params.slug) {
@@ -25,6 +30,8 @@
if (lesson) {
lessonComponent = getLessonComponent(lesson.component);
+ additionalComponents = getAdditionalComponents(lesson.additionalComponents || []);
+ readOnlyRanges = lesson.readOnlyRanges || [];
}
}
});
@@ -33,13 +40,26 @@
switch(componentName) {
case 'BitEditor':
return BitEditor;
- case 'EthernetBuilder':
- return EthernetBuilder;
+ case 'HexEditor':
+ return HexEditor;
default:
- return;
+ return null;
}
}
+ function getAdditionalComponents(componentNames) {
+ return componentNames.map(name => {
+ switch(name) {
+ case 'HexViewer':
+ return HexViewer;
+ case 'WiresharkView':
+ return WiresharkView;
+ default:
+ return null;
+ }
+ }).filter(Boolean);
+ }
+
onMount(() => {
//инициализация при загрузке на клиенте
if ($page.params.slug) {
@@ -47,6 +67,8 @@
if (lesson) {
lessonComponent = getLessonComponent(lesson.component);
+ additionalComponents = getAdditionalComponents(lesson.additionalComponents || []);
+ readOnlyRanges = lesson.readOnlyRanges || [];
currentBuffer = new Uint8Array(lesson.initialBuffer);
isCompleted = progressStorage.isCompleted(lesson.id.toString());
}
@@ -54,7 +76,13 @@
});
function handleBufferChange(newBuffer) {
- currentBuffer = newBuffer;
+ //обрабатываем буфер через отдельный процессор
+ if (lesson) {
+ const processedBuffer = processBuffer(newBuffer, lesson.id);
+ currentBuffer = processedBuffer;
+ } else {
+ currentBuffer = newBuffer;
+ }
}
function showNextHint() {
@@ -92,11 +120,17 @@
this={lessonComponent}
bind:buffer={currentBuffer}
onBufferChange={handleBufferChange}
+ readOnlyRanges={readOnlyRanges || []}
/>
- {#if currentBuffer}
-
- {/if}
+ {#each additionalComponents as Component}
+ {#if currentBuffer}
+
+ {/if}
+ {/each}