# Format pliku EU7B (`.eu7`) > Dokumentacja oparta na kodzie źródłowym z: > - `scene/eu7/` — runtime reader/loader (maszyna-fresh) > - `parser/include/eu07/scene/binary/` — writer/reader (narzędzie CLI) > > Stan: **czerwiec 2026**, obejmuje wersje v4–v8. --- ## Spis treści 1. [Przegląd ogólny](#1-przegląd-ogólny) 2. [Nagłówek pliku i wersje](#2-nagłówek-pliku-i-wersje) 3. [Struktura chunku](#3-struktura-chunku) 4. [Kolejność i reguły chunkowania](#4-kolejność-i-reguły-chunkowania) 5. [Typy podstawowe i kodowanie](#5-typy-podstawowe-i-kodowanie) 6. [Slim Node — wspólny nagłówek węzła](#6-slim-node--wspólny-nagłówek-węzła) 7. [Transform Context](#7-transform-context) 8. [Packed vertex — pakowany wierzchołek](#8-packed-vertex--pakowany-wierzchołek) 9. [Lighting block — blok oświetlenia](#9-lighting-block--blok-oświetlenia) 10. [Catalog chunkowy — zestawienie](#10-catalog-chunkowy--zestawienie) 11. [STRS — tablica łańcuchów](#11-strs--tablica-łańcuchów) 12. [INCL — referencje do podmodułów](#12-incl--referencje-do-podmodułów) 13. [TRAK — tory](#13-trak--tory) 14. [TRAC — przewody trakcyjne](#14-trac--przewody-trakcyjne) 15. [PWRS — źródła zasilania trakcji](#15-pwrs--źródła-zasilania-trakcji) 16. [TERR — teren (terrain shapes)](#16-terr--teren-terrain-shapes) 17. [MESH — kształty geometryczne](#17-mesh--kształty-geometryczne) 18. [LINE — linie](#18-line--linie) 19. [MODL — instancje modeli 3D](#19-modl--instancje-modeli-3d) 20. [MEMC — komórki pamięci](#20-memc--komórki-pamięci) 21. [LAUN — wyzwalacze zdarzeń](#21-laun--wyzwalacze-zdarzeń) 22. [DYNM — pojazdy dynamiczne](#22-dynm--pojazdy-dynamiczne) 23. [SOND — dźwięki otoczenia](#23-sond--dźwięki-otoczenia) 24. [TRSE — zestawy wagonowe](#24-trse--zestawy-wagonowe) 25. [EVNT — zdarzenia](#25-evnt--zdarzenia) 26. [FINT — licznik first-init](#26-fint--licznik-first-init) 27. [PLAC — parametry umieszczenia include](#27-plac--parametry-umieszczenia-include) 28. [PIDX — indeks sekcji PACK (v7+)](#28-pidx--indeks-sekcji-pack-v7) 29. [PACK — instancje modeli per sekcja 1 km (v7+)](#29-pack--instancje-modeli-per-sekcja-1-km-v7) 30. [PROT — prototypy modeli (v8+)](#30-prot--prototypy-modeli-v8) 31. [Semantyka strumieniowania PACK w runtime](#31-semantyka-strumieniowania-pack-w-runtime) 32. [Historia wersji](#32-historia-wersji) 33. [Ścieżki plików i konwencje nazewnictwa](#33-ścieżki-plików-i-konwencje-nazewnictwa) --- ## 1. Przegląd ogólny Plik `.eu7` (EU7B — **EU7 Binary**) to binarny format zapisu skompilowanego modułu scenerii symulatora EU07. Zastępuje tekst source (`*.scm`, `*.inc`, `*.scn`) jednym zoptymalizowanym plikiem binarnym gotowym do szybkiego wczytania przez silnik. **Kluczowe cechy:** - **Little-endian** dla wszystkich liczb całkowitych i zmiennoprzecinkowych - **Chunki samoopisowe** — nieznane chunki mogą być pomijane (`seekg` po `chunk_size`) - **Tablica łańcuchów (STRS)** — wszystkie stringi współdzielone przez indeks `uint32` - **Strumieniowanie sekcji 1 km** — w plikach głównych scenariusza modele są dzielone na kafelki 1 km × 1 km (chunki PIDX + PACK), wczytywane asynchronicznie - **Format skaluje się** od małych modułów include (kilka KB) do głównych scenariuszy (setki MB w PACK) --- ## 2. Nagłówek pliku i wersje ### Layout nagłówka ``` Offset Rozmiar Typ Opis ------ ------- -------- ------------------------- 0 4 char[4] Magic = 'EU7B' (0x45 0x55 0x37 0x42) 4 4 uint32 Version (patrz tabela poniżej) 8 ... chunks Strumień chunkowy (do EOF) ``` Nie ma pola "łączny rozmiar pliku" w nagłówku. Po nagłówku następują chunki aż do EOF. ### Tabela wersji | Wartość | Stała (maszyna-fresh) | Stała (parser CLI) | Opis | |---------|-------------------------|---------------------------|----------------------------------------------------------------| | `1` | — | `kVersionLegacy` | Legacy — **nieobsługiwane** w CLI ani runtime | | `2` | — | `kVersionRuntimeF64` | Skalary i Vec3 jako `double` (float64) na dysku | | `3` | — | `kVersionRuntimeF32` | Skalary i Vec3 jako `float32` na dysku | | `4` | `kEu7VersionV4` | `kVersionRuntimeV4` | Slim node + packed mesh (snorm16 normy + half-float UV) | | `5` | `kEu7VersionV5` | `kVersionRuntimeV5` | Adds TERR z batched terrain per sekcja 1 km | | `6` | `kEu7VersionV6` | `kVersionRuntime` | Adds site transform w rekordach INCL | | `7` | `kEu7VersionV7` | `kVersionRuntimeV7` | Adds PIDX + PACK — modele scenerii per sekcja 1 km (streaming) | | `8` | `kEu7VersionV8` | — | Adds PROT (prototypy) + nowy format sekcji PACK v8 | **Runtime maszyna-fresh obsługuje wersje 4–8.** Wersje 1–3 nie są obsługiwane przez żaden z dostępnych loaderów. --- ## 3. Struktura chunku ``` Offset Rozmiar Typ Opis ------ ------- ------- ----------------------------------------- 0 4 uint32 Chunk ID — FourCC little-endian (4 znaki ASCII) 4 4 uint32 Chunk total size — ŁĄCZNIE z nagłówkiem (≥ 8) 8 ... bytes Payload (rozmiar = chunk_total_size - 8) ``` > **Uwaga:** pole `chunk_total_size` **wlicza** 8 bajtów własnego nagłówka. Aby obliczyć rozmiar payloadu: `payload_size = chunk_total_size - 8`. Strumień chunkowy nie ma terminator record. Parsowanie trwa do momentu napotkania EOF. ### FourCC — sposób kodowania ID chunku jest 4-znakowym ASCII wpisanym w kolejności little-endian do `uint32`: ```cpp constexpr uint32_t make_id4(char a, char b, char c, char d) { return (uint32(d) << 24) | (uint32(c) << 16) | (uint32(b) << 8) | uint32(a); } // Np. kChunkPack = make_id4('P','A','C','K') // Bytes w pliku: 50 41 43 4B → odczytane jako uint32 LE = 0x4B434150 ``` Przy odczycie człowiek widzi w hex-dumpie bajty w kolejności `P A C K`, co odpowiada oczytaniu `'PACK'` wprost. --- ## 4. Kolejność i reguły chunkowania 1. **STRS** musi wystąpić **przed** wszystkimi chunkiami używającymi indeksów string (w praktyce jest zawsze pierwszym chunkiem). 2. **PACK** jest **pomijany przy pełnym parse** — runtime zapamiętuje jedynie offset początku payloadu i rozmiar. Faktyczne dane są czytane on-demand przez `read_pack_section()`. 3. **PROT** musi wystąpić **przed** PACK (prototypy muszą być załadowane do pamięci zanim sekcje PACK będą rozwijane). 4. **PIDX** musi wystąpić przed próbą streamingu (runtime czyta PIDX przy załadowaniu modułu). 5. Nierozpoznane chunki są **ignorowane** — reader skacze do `chunk_start + payload_size`. --- ## 5. Typy podstawowe i kodowanie Wszystkie typy całkowite i zmiennoprzecinkowe — **little-endian**. | Typ w pliku | C++ typ docelowy | Rozmiar | Opis | |-------------|------------------|---------|---------------------------------------------------| | `uint8` | `uint8_t` | 1 B | Bajt bez znaku | | `int16` | `int16_t` | 2 B | LE, ze znakiem | | `uint16` | `uint16_t` | 2 B | LE, bez znaku | | `int32` | `int32_t` | 4 B | LE, ze znakiem | | `uint32` | `uint32_t` | 4 B | LE, bez znaku | | `uint64` | `uint64_t` | 8 B | LE, bez znaku (lo-word najpierw) | | `float32` | `float` | 4 B | IEEE 754 single precision | | `f64_disk` | `double` | 4 B | **float32 na dysku** — wczytywany jako double w RAM | | `Vec3` | `glm::dvec3` | 12 B | 3× `f64_disk` (= 3× float32, razem 12 B) | | `snorm16` | `float` | 2 B | int16 / 32767.0 → zakres [-1, +1] | | `half16` | `float` | 2 B | IEEE 754 half precision | | `string_id` | `uint32_t` | 4 B | Indeks do tablicy STRS; `0xFFFFFFFF` = pusty | > **`f64_disk`**: Od wersji v3 wzwyż skalary (dystanse, kąty, napięcia itp.) i współrzędne Vec3 są zapisywane jako **float32** (4 B), mimo że runtime przechowuje je jako `double`. Patrz: `io::writeF64()` w `io.hpp` — wewnętrznie rzutuje do `float` przed zapisem. --- ## 6. Slim Node — wspólny nagłówek węzła Każdy obiekt sceny (tor, model, dźwięk itp.) poprzedzony jest **slim node** — skompresowanym nagłówkiem z opcjonalnymi polami sterowanymi bajtem flag. ### Layout ``` Offset Rozmiar Typ Opis ------ ------- --------- ------------------------------------------- 0 1 uint8 flags (bitmaska, patrz poniżej) --- poniższe pola są OPCJONALNE, zależne od flags --- +0 4 string_id name (jeśli bit 0 set) +0 4 f64_disk range_squared_min (jeśli bit 1 set) +0 4 f64_disk range_squared_max (jeśli bit 2 set; domyślnie +∞) +0 16 Vec3+f32 bounding sphere: center(Vec3) + radius(float32) (jeśli bit 3) +0 4 uint32 group_handle (jeśli bit 4 set) +0 ... Transform transform context (jeśli bit 5 set) ``` ### Bitmaska `flags` | Bit | Stała | Znaczenie | |-----|--------------------------|-----------------------------------------------------| | 0 | `kNodeFlagHasName` | pole `name` (string_id) jest obecne | | 1 | `kNodeFlagHasRangeMin` | pole `range_squared_min` (f64_disk) jest obecne | | 2 | `kNodeFlagHasRangeMax` | pole `range_squared_max` jest obecne (inaczej +∞) | | 3 | `kNodeFlagHasBounds` | bounding sphere: Vec3 center + float32 radius | | 4 | `kNodeFlagHasGroup` | pole `group_handle` (uint32) jest obecne | | 5 | `kNodeFlagHasTransform` | transform context jest obecny (patrz sekcja 7) | | 6 | `kNodeFlagNotVisible` | węzeł domyślnie niewidoczny (`visible = false`) | ### Bounding sphere ``` Vec3 center (12 B) float32 radius (4 B) ``` Łącznie 16 B, obecne tylko gdy bit 3 set. --- ## 7. Transform Context Kontekst transformacji lokacji węzła — stos offsetów i skal + obrót. ``` Offset Rozmiar Typ Opis ------ ---------------- -------- ---------------------------------------- 0 1 uint8 origin_count (maks. 255) 1 12 × origin_count Vec3[] origin_stack (offsety translacji) + 1 uint8 scale_count (maks. 255) + 12 × scale_count Vec3[] scale_stack (skala) + 12 Vec3 rotation (kąty Eulera w stopniach, XYZ) + 1 uint8 group_depth ``` Kolejność stosowania transformacji (w writer: `transform_point()`): 1. Obrót Y (o `rotation.y` stopni) 2. Skalowanie (`scale_stack.back()`) 3. Translacja (`origin_stack.back()`) --- ## 8. Packed vertex — pakowany wierzchołek Używany w chunkach MESH i TERR (od v4). ``` Offset Rozmiar Typ Opis ------ ------- ------- ------------------------------------------ 0 4 float32 position.x 4 4 float32 position.y 8 4 float32 position.z 12 2 int16 normal.x jako snorm16 (/ 32767) 14 2 int16 normal.y jako snorm16 16 2 int16 normal.z jako snorm16 18 2 uint16 u jako half-float (IEEE 754 fp16) 20 2 uint16 v jako half-float ``` Łącznie: **22 bajty** na wierzchołek. W chunkach LINE wierzchołki to **tylko pozycja** (Vec3 = 12 B; brak normalnych i UV). --- ## 9. Lighting block — blok oświetlenia Opcjonalny blok oświetlenia materiału węzła (48 bajtów). ``` Offset Rozmiar Typ Opis ------ ------- ------- ------------------ 0 16 4×f32 diffuse RGBA 16 16 4×f32 ambient RGBA 32 16 4×f32 specular RGBA ``` Domyślne wartości (gdy brak bloku): - `diffuse = (0.8, 0.8, 0.8, 1.0)` - `ambient = (0.2, 0.2, 0.2, 1.0)` - `specular = (0.0, 0.0, 0.0, 1.0)` Blok jest poprzedzony znacznikiem `uint8` (`0` = brak, `!0` = blok obecny) w rekordach MESH i LINE. --- ## 10. Catalog chunkowy — zestawienie | FourCC | Stała C++ | Wersja min | Opis | |---------|------------------|------------|------------------------------------------------| | `STRS` | `kChunkStrs` | v4 | Tablica shared strings (musi być pierwsza) | | `INCL` | `kChunkIncl` | v4 | Referencje do submodułów `.eu7` | | `TRAK` | `kChunkTrak` | v4 | Tory (Track) | | `TRAC` | `kChunkTrac` | v4 | Przewody trakcyjne (Traction) | | `PWRS` | `kChunkPwrs` | v4 | Źródła zasilania trakcji | | `TERR` | `kChunkTerr` | v5 | Teren (batched triangles per sekcja) | | `MESH` | `kChunkMesh` | v4 | Kształty geometryczne (triangles/strip/fan) | | `LINE` | `kChunkLine` | v4 | Linie (lines/strip/loop) | | `MODL` | `kChunkModl` | v4 | Instancje modeli 3D (flat list) | | `MEMC` | `kChunkMemc` | v4 | Komórki pamięci (MemCell) | | `LAUN` | `kChunkLaun` | v4 | Wyzwalacze zdarzeń (EventLauncher) | | `DYNM` | `kChunkDynm` | v4 | Pojazdy dynamiczne (Dynamic) | | `SOND` | `kChunkSond` | v4 | Dźwięki otoczenia (Sound) | | `TRSE` | `kChunkTrset` | v4 | Zestawy wagonowe (Trainset) | | `EVNT` | `kChunkEvnt` | v4 | Zdarzenia (Event) | | `FINT` | `kChunkFint` | v4 | Licznik obiektów first-init | | `PLAC` | `kChunkPlac` | v4 | Parametry umieszczenia include | | `PIDX` | `kChunkPidx` | **v7** | Indeks sekcji 1 km → offset w PACK | | `PACK` | `kChunkPack` | **v7** | Strumień modeli per sekcja 1 km | | `PROT` | `kChunkProt` | **v8** | Prototypy modeli (współdzielone definicje) | --- ## 11. STRS — tablica łańcuchów Wszystkie łańcuchy tekstu w pliku są przechowywane w jednej tablicy i referowane przez `string_id` (indeks `uint32`). Specjalny indeks `0xFFFFFFFF` oznacza pusty string. ``` uint32 count Powtórzone count razy: uint32 length (w bajtach, bez null-terminatora) char[] data (length bajtów UTF-8/ASCII, bez null) ``` Każdy kolejny string ma numer indeksu 0, 1, 2, … --- ## 12. INCL — referencje do podmodułów Przechowuje listę `include` wskazujących na inne pliki `.eu7` — submoduły scenerii. ``` uint32 count Powtórzone count razy: uint32 source_line — numer linii w pliku tekstowym źródłowym string_id source_path — ścieżka do pliku tekstowego (.scm/.inc) string_id binary_path — ścieżka do pliku .eu7 uint32 param_count string_id[] parameters — param_count × string_id (parametry include) [v6+] TransformContext site_transform (nieobecne w v4/v5) ``` `site_transform` przechowuje pełny kontekst transformacji miejsca osadzenia (origin/scale/rotation) i jest używany do złożenia transformacji przy nakładaniu submodułu. --- ## 13. TRAK — tory ``` uint32 count Powtórzone count razy: SlimNode node uint8 track_type — patrz enum Eu7TrackType uint8 category — patrz enum Eu7TrackCategory float32 length [m] float32 track_width [m] float32 friction float32 sound_distance int32 quality_flag int32 damage_flag uint8 environment — wartość enum + 1 (Unknown = 0, Flat = 1, ...) uint8 has_visibility — 0 = brak, ≠0 = blok widoczności następuje [jeśli has_visibility]: string_id material1 float32 tex_length string_id material2 float32 tex_height1 float32 tex_width float32 tex_slope uint32 path_count (maks. 65536) Powtórzone path_count razy: Vec3 p_start f64_disk roll_start [stopnie] Vec3 cp_out — punkt kontrolny wyjściowy Béziera Vec3 cp_in — punkt kontrolny wejściowy Béziera Vec3 p_end f64_disk roll_end f64_disk radius [m], 0 = prosta uint32 tail_count (maks. 256) Powtórzone tail_count razy: uint8 code — 1–18 = predefiniowane kw., 255 = custom [jeśli code==255]: string_id custom_key string_id value ``` ### Enum Eu7TrackType (uint8) | Wartość | Nazwa | |---------|-------------| | 0 | Unknown | | 1 | Normal | | 2 | Switch | | 3 | Table | | 4 | Cross | | 5 | Tributary | ### Enum Eu7TrackCategory (uint8) | Wartość | Nazwa | |---------|-------| | 1 | Rail | | 2 | Road | | 4 | Water | ### Enum Eu7TrackEnvironment (uint8 w pliku = enum + 1) | Wartość w pliku | Enum | Nazwa | |-----------------|-------|-----------| | 0 | -1 | Unknown | | 1 | 0 | Flat | | 2 | 1 | Mountains | | 3 | 2 | Canyon | | 4 | 3 | Tunnel | | 5 | 4 | Bridge | | 6 | 5 | Bank | ### Kody tail keywords (uint8) | Kod | Słowo kluczowe | |-----|----------------| | 1 | `event0` | | 2 | `eventall0` | | 3 | `event1` | | 4 | `eventall1` | | 5 | `event2` | | 6 | `eventall2` | | 7 | `velocity` | | 8 | `isolated` | | 9 | `overhead` | | 10 | `vradius` | | 11 | `railprofile` | | 12 | `trackbed` | | 13 | `friction` | | 14 | `fouling1` | | 15 | `fouling2` | | 16 | `sleepermodel` | | 17 | `angle1` | | 18 | `angle2` | | 255 | *custom* | --- ## 14. TRAC — przewody trakcyjne ``` uint32 count Powtórzone count razy: SlimNode node string_id power_supply_name uint8 material — Eu7TractionWireMaterial (0=None,1=Copper,2=Aluminium) float32 nominal_voltage [V] float32 max_current [A] float32 resistivity_ohm_per_m f64_disk resistivity_legacy string_id material_raw — oryginalny string materiału ("cu", "al", ...) float32 wire_thickness [m] int32 damage_flag Vec3 wire_p1 Vec3 wire_p2 Vec3 wire_p3 Vec3 wire_p4 f64_disk min_height [m] f64_disk segment_length [m] int32 wire_count float32 wire_offset uint8 has_parallel — 0 = brak, ≠0 = nazwa równoległego sekcji [jeśli has_parallel]: string_id parallel_name ``` --- ## 15. PWRS — źródła zasilania trakcji ``` uint32 count Powtórzone count razy: SlimNode node Vec3 position float32 nominal_voltage [V] float32 voltage_frequency [Hz] f64_disk internal_resistance_legacy float32 internal_resistance [Ω] float32 max_output_current [A] float32 fast_fuse_timeout [s] float32 fast_fuse_repetition [s] float32 slow_fuse_timeout [s] uint8 modifier — Eu7PowerSourceModifier (0=None,1=Recuperation,2=Section) ``` --- ## 16. TERR — teren (terrain shapes) Chunk TERR przechowuje trójkąty terenu. Może się pojawić wielokrotnie (po jednym per materiał lub per sekcja). ``` uint8 flags — bitmaska: bit 0: translucent bit 1: non-default lighting (blok lighting poniżej) bit 2: batched (grupowany per sekcja 1km) string_id material [jeśli bit 1 set]: LightingBlock lighting (48 B) [jeśli bit 2 set — tryb batched]: uint32 batch_count Powtórzone batch_count razy: int32 section_x_coord — współrzędna X sekcji (odczytane, ale ignorowane przez runtime) int32 section_z_coord uint32 vertex_count — musi być wielokrotnością 3 PackedVertex[] vertices — vertex_count wierzchołków [else — tryb legacy]: uint32 count — liczba trójkątów Powtórzone count razy: PackedVertex[3] vertices — dokładnie 3 wierzchołki (1 trójkąt) ``` > **Uwaga:** tryb batched (bit 2) jest charakterystyczny dla v5+. Pola `section_x_coord` i `section_z_coord` są odczytywane przez reader, ale ich wartości są ignorowane w runtime maszyna-fresh (batche traktowane jako płaskie listy kształtów). --- ## 17. MESH — kształty geometryczne ``` uint32 count Powtórzone count razy: uint8 subtype — 0=triangles, 1=triangle_strip, 2=triangle_fan SlimNode node — typ węzła wyznaczony przez subtype uint8 translucent — 0/1 string_id material_path uint8 has_lighting — 0/1 [jeśli has_lighting]: LightingBlock lighting Vec3 origin — lokalny punkt odniesienia wierzchołków uint32 vertex_count PackedVertex[] vertices ``` Nazwa węzła (`node.node_type`) wyznaczona przez subtype: `"triangles"`, `"triangle_strip"`, `"triangle_fan"`. --- ## 18. LINE — linie ``` uint32 count Powtórzone count razy: uint8 subtype — 0=lines, 1=line_strip, 2=line_loop SlimNode node uint8 has_lighting — 0/1 [jeśli has_lighting]: LightingBlock lighting float32 line_width Vec3 origin uint32 vertex_count Powtórzone vertex_count razy: Vec3 position — TYLKO pozycja (brak normalnych/UV) ``` --- ## 19. MODL — instancje modeli 3D Używane w submodułach include. W plikach głównych z PACK chunk, MODL z podmodułów jest zazwyczaj pomijane (sterowane flagą `pack_scenery_active()`). ``` uint32 count Powtórzone count razy: SlimNode node uint8 is_terrain — 0/1 uint8 transition — 0/1 (animowany przejazd) Vec3 location — world-space, 3×float32 Vec3 angles — kąty Eulera (stopnie), 3×float32 Vec3 scale — skala XYZ, domyślnie (1,1,1) string_id model_file — ścieżka do pliku .e3d string_id texture_file — override tekstury (opcjonalnie pusty) uint32 light_count float32[] light_states — light_count wartości uint32 color_count uint32[] light_colors — color_count kolorów RGBA packed ``` ### Aplikacja w runtime (maszyna-fresh) Rekord MODL/PACK nie trafia przez parser tekstowy SCM. Ścieżka **instancingu GPU** (nie per-model drip): 1. `preload_unique_pack_meshes()` — jeden `GetModel()` na unikalny plik `.e3d` w całej sekcji 2. Dla każdego rekordu: `TAnimModel` + `LoadEu7()` (cache hit → szybkie `Init()`) 3. `update_instanceable_flag()` — modele bez świateł/animacji → `m_instanceable = true` 4. `Region->insert()` → `m_instancebuckets_opaque` → renderer: **`Render_Instanced()`** PACK **nie** rejestruje instancji w `Instances` / `Hierarchy` (anonimowa sceneria, bez eventów po nazwie). Deduplikacja meshów: **`TModelsManager`** (współdzielony `TModel3d`). GPU instancing: **`opengl33_renderer::Render_Instanced`** — jeden draw na submodel × N instancji tego samego mesha. --- ## 20. MEMC — komórki pamięci ``` uint32 count Powtórzone count razy: SlimNode node string_id text f64_disk value1 f64_disk value2 string_id track_name — 0xFFFFFFFF jeśli brak ``` --- ## 21. LAUN — wyzwalacze zdarzeń ``` uint32 count Powtórzone count razy: SlimNode node Vec3 location f64_disk radius_squared [m²] string_id activation_key_raw int32 activation_key f64_disk delta_time [s], -1 = bez limitu string_id event1_name string_id event2_name int32 launch_hour — -1 = bez warunku godzinowego int32 launch_minute uint8 has_condition — 0/1 [jeśli has_condition]: string_id memcell_name string_id compare_text f64_disk compare_value1 f64_disk compare_value2 int32 check_mask uint8 train_triggered — 0/1 ``` --- ## 22. DYNM — pojazdy dynamiczne ``` uint32 count Powtórzone count razy: SlimNode node string_id data_folder string_id skin_file string_id mmd_file string_id track_name string_id driver_type string_id load_type string_id coupling_params string_id coupling_raw — tekstowa reprezentacja sprzęgu (np. "3.automat") f64_disk offset [m od początku toru], -1 = auto int32 coupling — wartość sprzęgu (0-3) int32 load_count float32 velocity [km/h] uint8 has_destination — 0/1 [jeśli has_destination]: string_id destination uint8 has_trainset_index — 0/1 [jeśli has_trainset_index]: uint32 trainset_index ``` --- ## 23. SOND — dźwięki otoczenia ``` uint32 count Powtórzone count razy: SlimNode node Vec3 location string_id wav_file ``` --- ## 24. TRSE — zestawy wagonowe ``` uint32 count Powtórzone count razy: string_id name string_id track float32 offset [m] float32 velocity [km/h] uint32 assignment_count Powtórzone assignment_count razy: string_id key string_id value uint32 vehicle_count uint32[] vehicle_indices — indeksy do tablicy DYNM w module uint32 coupling_count int32[] couplings uint32 driver_index — indeks pojazdu-prowadzącego ``` --- ## 25. EVNT — zdarzenia ``` uint32 count Powtórzone count razy: string_id name uint8 type — Eu7EventType (patrz tabela) f64_disk delay [s] uint32 target_count string_id[] targets f64_disk delay_random [s] f64_disk delay_departure [s] uint8 ignored — 0/1 uint8 passive — 0/1 uint32 payload_count Powtórzone payload_count razy: string_id key string_id value ``` ### Enum Eu7EventType (uint8) | Wartość | Nazwa | |---------|---------------| | 0 | AddValues | | 1 | UpdateValues | | 2 | CopyValues | | 3 | GetValues | | 4 | PutValues | | 5 | Whois | | 6 | LogValues | | 7 | Multiple | | 8 | Switch | | 9 | TrackVel | | 10 | Sound | | 11 | Texture | | 12 | Animation | | 13 | Lights | | 14 | Voltage | | 15 | Visible | | 16 | Friction | | 17 | Message | | 18 | Unknown | --- ## 26. FINT — licznik first-init Prosty licznik — ile obiektów wymaga obsługi pierwszego inicjowania. ``` uint32 first_init_count ``` --- ## 27. PLAC — parametry umieszczenia include Mapowanie parametrów wywołania include na składowe transformacji. ``` uint8 origin_x_param — numer parametru dla origin.x (0 = brak) uint8 origin_y_param — numer parametru dla origin.y uint8 origin_z_param — numer parametru dla origin.z uint8 rotation_y_param — numer parametru dla rotation.y ``` Gdy wszystkie cztery pola są `0`, plik nie eksponuje parametryzowanego umieszczenia. --- ## 28. PIDX — indeks sekcji PACK (v7+) Katalog mapujący sekcje 1 km × 1 km na offsety w payloadzie chunku PACK. ``` uint32 count Powtórzone count razy: uint16 row — indeks wiersza sekcji (siatka regionu) uint16 column — indeks kolumny sekcji uint32 model_count — liczba modeli w tej sekcji uint64 pack_offset — offset w bajtach od początku payloadu PACK ``` **Uwagi:** - `pack_offset` jest **względny** od początku payloadu PACK (nie od początku pliku). - Sekcje bez modeli mogą mieć `model_count = 0` i być pominięte w PIDX. - Indeksowanie sekcji: `row/column` odpowiadają siatce `kRegionSideSectionCount × kRegionSideSectionCount`, centrum siatki to `(0, 0)` w world-space. - Runtime oblicza absolutny offset jako: `pack_payload_offset + entry.pack_offset`. --- ## 29. PACK — instancje modeli per sekcja 1 km (v7+) Payload PACK jest **zbiorem sekcji** ustawionych sekwencyjnie wg offsetów z PIDX. Chunk PACK jest pomijany przy pełnym wczytaniu — reader zapamiętuje jedynie `pack_payload_offset`. ### Format sekcji — v7 (domyślny) Każda sekcja to `model_count` rekordów `RuntimeModelInstance` (identyczny format jak rekordy MODL): ``` Powtórzone model_count razy: (pełny rekord RuntimeModelInstance — patrz sekcja 19 MODL) ``` Transformacja węzła (`node.transform`) jest **zerowana** po wczytaniu — dane w PACK są zawsze w world-space (transform był bake'owany przy generacji). ### Format sekcji — v8 (`kPackSectionFormatV8 = 1`) ``` uint8 section_format — musi być = 1 uint32 solo_count — modele pełne (bez prototypu) uint32 inst_count — instancje odwołujące się do PROT Powtórzone solo_count razy: (pełny rekord RuntimeModelInstance, transformacja zerowana) Powtórzone inst_count razy: uint32 proto_id — indeks do tablicy PROT Vec3 location Vec3 angles Vec3 scale string_id name ``` Instancje v8 są rozwijane przez `expand_prototype_instance()` łącząc dane z PROT z lokalizacją instancji. --- ## 30. PROT — prototypy modeli (v8+) Wspólne definicje modeli (bez informacji o lokalizacji instancji). Muszą być obecne przed odczytaniem sekcji PACK v8. ``` uint32 count Powtórzone count razy: SlimNode node — name/range/bounds jak MODL, ale BEZ location/angles/scale uint8 is_terrain uint8 transition string_id model_file string_id texture_file uint32 light_count float32[] light_states uint32 color_count uint32[] light_colors ``` Różnica względem MODL: brak pól `location`, `angles`, `scale` — te są zapisane per instancja w PACK. --- ## 31. Semantyka strumieniowania PACK w runtime Gdy root scenariusz zawiera chunk PACK, silnik maszyna-fresh uruchamia asynchroniczny **sekcja-stream**: ### Parametry strumienia | Parametr | Wartość | Opis | |----------------------------|------------------|----------------------------------------------------| | `kSectionSizeM` | 1000 m | Rozmiar sekcji | | `kInitialBootstrapRadius` | 3 sekcje | Promień wstępnego ładowania wokół pozycji startowej| | `kStreamRadius` | 9 sekcji | Bieżący promień streamingu | | `kMovementLookahead` | 4 sekcje | Prefetch w kierunku jazdy | | `kMaxInFlightSections` | 72 | Maks. sekcji w toku (backpressure) | | `kMaxReadySections` | 36 | Maks. gotowych sekcji w kolejce | | `kBootstrapTimeoutMs` | 120 000 ms | Timeout bootstrapu (tylko ścieżka blokująca) | W **jazdzie** (`driver_mode`): **jedna sekcja PACK na klatkę** — `drain_section_stream()` → `apply_pending_section()` (cała sekcja naraz, bez fałszywych budżetów ms per model). ### Dysk vs wątek główny | Etap | Wątek | API | |------|-------|-----| | Deserialize MODL z `.eu7` | Worker | `read_pack_section()` | | Page cache `.e3d` | Worker | `prefetch_pack_models()` | | `GetModel()` unikalnych meshów + instancje | **Main** | `insert_eu7_pack_models()` | | GPU draw | **Main** | `Render_Instanced()` dla `m_instanceable` | `TModel3d::LoadFromFile()` + `GfxRenderer` — **tylko main**. W sekcji: najpierw wszystkie unikalne meshe, potem tysiące lekkich `TAnimModel` trafiających do bucketów instancingu. ### Przepływ ładowania ``` 1. read_module() → PIDX w RAM, PACK jako offset (seek) 2. init_section_stream() → worker pool 3. preload_section_stream() [loader, blokująco] → bootstrap + drain sekcji 4. W jazdzie (is_ready): kick_section_stream_bootstrap() — enqueue, bez spin-wait drain_section_stream() — jedna gotowa sekcja → apply_pending_section() update_section_stream(camera) ``` ### Wątki robocze Po `read_pack_section()`: `prefetch_pack_models()` → `PackSectionReady` w kolejce. ### Drain (wątek główny) `apply_pending_section()` — **cała sekcja** w jednym `insert_eu7_pack_models()`: 1. `preload_unique_pack_meshes` — cold load tylko dla nowych `.e3d` 2. Pętla instancji → `Region->insert` → GPU instancing bucket Jedna sekcja na wywołanie `drain_section_stream()` (jedna klatka = jedna sekcja, nie jeden model). ### Diagnostyka (`eu7_load_stats`) | Pole | Znaczenie | |------|-----------| | `model_ms` / `models` | Czas i liczba `TAnimModel` z PACK | | `pack_sections_loaded` / `pack_models` | Sekcje / instancje ze streamingu | Overlay renderera: `inst-pool:` — ile instancji zakwalifikowanych do `Render_Instanced`. ### Pliki implementacji (runtime) | Temat | Plik | |-------|------| | Streaming sekcji | `scene/eu7/eu7_section_stream.cpp` | | Prefetch dysku | `scene/eu7/eu7_model_prefetch.cpp` | | Instancje PACK | `simulation/simulationstateserializer.cpp` | | `LoadEu7` + `m_instanceable` | `model/AnimModel.cpp` | | `Render_Instanced` | `rendering/opengl33renderer.cpp` | | Cache meshów | `model/MdlMngr.cpp` | --- ## 32. Historia wersji | Wersja | Zmiany | |--------|--------------------------------------------------------------------------------------------------| | v1 | Format legacy — szczegóły nieokreślone, nieobsługiwany | | v2 | Skalary i Vec3 jako float64 (8 B) na dysku | | v3 | Skalary i Vec3 jako float32 (4 B) na dysku — `writeF64()` → rzutuje do float32 | | v4 | **Slim node** (flagowany nagłówek węzła) + **packed vertex** (snorm16 normy + half16 UV) | | v5 | Chunk **TERR** z trybem batched (terrain per sekcja 1 km); TERR może być w osobnym pliku `.eu7` | | v6 | Chunk INCL rozszerzony o **site transform** (pełny TransformContext per include) | | v7 | Chunki **PIDX + PACK** — modele scenerii podzielone na kafelki 1 km, asynchroniczny streaming | | v8 | Chunk **PROT** (prototypy) + nowy format sekcji PACK z `solo_count` + `inst_count` | --- ## 33. Ścieżki plików i konwencje nazewnictwa ### Rozszerzenie Pliki EU7B używają rozszerzenia `.eu7`. Identyfikacja pliku odbywa się przez: 1. Sprawdzenie rozszerzenia (`isSceneBinaryPath()`) 2. Sondowanie magic bytes `EU7B` na pozycji 0 (`probeSceneBinaryMagic()`) ### Konwersja ścieżek source → binary - `*.scm` → `*.eu7` (te same katalog i stem) - `*.inc` → `*.eu7` - `*.scn` → `*.eu7` - `*.sbt` → terrain `*.eu7` (odpowiednik binarny terenu) Funkcja `binary_path()` w loaderze zamienia rozszerzenie przy tym samym stemie. ### Warianty pliku terenu Plik `.eu7` może zawierać chunk TERR (teren). Loader sprawdza obecność TERR przez `probe_terrain_file()` (pełne wczytanie nagłówków). Funkcja `is_scenario_terrain()` sprawdza czy obok scenariusza `.scn` istnieje `.eu7` z chunkiem TERR. ### Pliki include Submoduły include są wczytywane jeden raz per ścieżka (deduplication przez `is_module_loaded()`). Ścieżka pliku `.eu7` includowanego jest wyznaczana przez `include_eu7_path()` — ten sam katalog co plik SCM/INC. --- *Dokumentacja wygenerowana na podstawie kodu źródłowego; stan na 11 czerwca 2026.*