eeuz fea Place Name Gazetteer - krcenov/RNS510-Map-Viewer GitHub Wiki

eeuz.fea β€” Place-Name Gazetteer

Status: 🟑 Partially cracked Β· 553 MB Β· FEATURE_COMPRESSED (compressionType 3 β€” a different directory encoding from MAP_COMPRESSED, despite sharing the same 80-byte header and zlib-stream-packing shell) Β· 4 of the file's directory tables now located and validated (T1–T4). A still later session disassembled real DBAL.OUT firmware code (not just symbol names) and fully cracked the relative point-geometry encoding β€” a real getCoordinates() function, cumulative 16-bit delta-chain, scaled by per-layer factors β€” but found strong, direct disassembly evidence that the absolute anchor a chain is positioned against is never supplied by this file's own code at all; see "Cracked via real DBAL.OUT disassembly" below for the full picture and why this means per-entry coordinates may not be recoverable from .fea bytes alone.

This is the file that answers "why has no cracked road tile ever shown a lake, river, park, or coastline" β€” it's not road/water-polygon geometry at all.

What it contains (confirmed)

A general multi-language named-place gazetteer: cities/towns/villages at every scale, named water bodies (Mediterranean, Black, Baltic, North, Caspian Seas; Gulf of Bothnia), islands (the whole Greek archipelago), and β€” not part of the original hypothesis β€” road/route shield labels (European route numbers E50/E58/..., national route codes R22/A375/M4/..., Asian Highway numbers AH30/AH61/...). None of the already-cracked road-geometry files carry this content.

Container

Same 80-byte (*$SIEMENS&^)/Copyright 1998 header as MAP_COMPRESSED, then an ~11.7MB directory region (byte 80 β†’ file offset 11,727,784), then standard RFC1950 zlib streams packed back-to-back to EOF β€” chaining verified byte-exact across thousands of consecutive entries, with occasional small non-zlib "gaps" between entries (not understood; skipped via local brute-force re-scan, which works reliably).

Entry content

Unrelated to MAP_COMPRESSED's tile schema (no 0x0146 magic, no anchor+delta blocks). Entries are much smaller than tiles (42–63,348 bytes decompressed, mean ~380 bytes) and pack multiple small sub-records. Most are compact, near-identical "stub" placeholder records; a minority carry a multi-language name table: [1-byte language id][1-byte UTF-8 length][UTF-8 name bytes], repeated once per language (20–26 languages for a major place).

Validated: an entry decodes a 26-language name table unambiguously "Mediterranean Sea" in every language on the disc's market. A wide scan (11 sample windows across the whole file, 33,628 entries, 922 with a decodable name) found hundreds more real matches, including a IaΘ™i, Romania entry decoding IASI, JASY, IASIO, JÁSZVÁSÁR, JASSY, YASSY, YAS β€” every one a genuine historical/foreign exonym for that same real city already validated via eeu.rd and eeu.cty.

The directory format β€” cracked (12-byte big-endian offset/length index)

The ~11.7MB directory region divides exactly into 977,308 fixed 12-byte records plus an 8-byte trailer. The crack: each record is 3 big-endian uint32 fields β€” (offset, declen, complen) β€” a keyed index into the file's own zlib entries, NOT little-endian and NOT a simple sequential/spatial array. Validated at scale: a dense 1,191-record run scores 1,008/1,191 (84.7%) exact byte-for-byte decompression-and- length matches; a random 5,000-record sample scores 1,478/5,000 (29.6%); searching for the LITERAL (offset, declen, complen) of 4 independently- verified real places (Mediterranean Sea, IaΘ™i, Bratislava, Nisyros) finds an exact, correctly-ordered 12-byte match every time. Two field-order variants exist ([declen,complen,offset] and [offset,declen,complen]) β€” a rotation, not a different encoding.

This is a hash/keyed index, not a spatial or sequential table: 42.7% of consecutive records are byte-identical (collision/padding runs), only 57% of values are distinct, and a directory table can index an entry far outside its own physical byte range (one table at 306–311MB indexes an entry at 363MB).

Also found: this same 12-byte-record directory shape recurs inside the payload too β€” a full-file enumeration hit a 5,001,156-byte gap (exactly 416,763 Γ— 12 + 0) with the identical structural signature, most likely marking a section/region boundary. So this file is not [header][one directory][one payload run] β€” it embeds several more directory-shaped regions mid-payload (see the next section β€” 2 more were found and confirmed).

A later session: tables T3 and T4 found β€” resolves Turku/Trondheim/Oslo completely

The open item above (3 test cities β€” Turku, Trondheim, Oslo, all ~550MB β€” had no record in either known table, evidence of at least one more unlocated directory region) is now resolved. A whole-file stride-12 self-similarity scan (arr[:-12] == arr[12:], blocked and averaged with numpy β€” a few seconds over the whole 553MB file, far faster than sequential entry-by-entry walking) located every directory-shaped region in one pass:

Table Byte range Density Records
T1 (known) 80 – 11,800,000 ~0.865 977,308
T2 (known) 306,200,000 – 311,200,000 ~0.838 416,763
T4 (new) 529,687,345 – 529,889,749 ~0.75 16,867
T3 (new) 546,882,678 – 547,158,126 ~0.63–0.79 22,954

Both new tables share T1/T2's exact structure β€” real zlib-entry chain walking confirms a genuine non-payload gap at each, with a real zlib header resuming immediately after β€” plus a small twist: each starts with an undecoded 6-byte prefix before its 12-byte-aligned records begin (gap_len - 6 divides evenly by 12 for both). Both verify at HIGH rates with the existing decode_directory_table()/verify_index_triple() machinery, no new code needed: T3: 17,107/22,954 (74.5%), T4: 9,968/16,867 (59.1%) β€” both well above T1's own average (2.7%–73.5% per block).

Direct resolution: the payload immediately following T3 contains Turku (offset 550,612,987), Trondheim (550,725,854), and Oslo (550,550,064) β€” the exact 3 cities confirmed absent from T1/T2. find_index_record_for_offset() against T3 alone finds all 3 immediately, and the found (declen, complen) fields match a REAL decompression at each city's own offset EXACTLY, 3/3, both fields β€” Turku (16,028/10,596), Trondheim (34,972/24,283), Oslo (40,665/28,358), no tolerance needed. This was never a hole in the offset/declen/complen model, just an unlocated table.

A 5th, weaker candidate at the very end of the file (~551.16M–EOF, where real zlib chaining genuinely stops for good) was also found by the same scan but does NOT verify cleanly at any byte phase tried (best: 3.4%, vs. 59–75% for T3/T4) β€” left as an honest, low-confidence lead, not claimed as a validated 5th table.

What's NOT cracked

  • Per-record ABSOLUTE coordinates. Exhaustive scans for a plausible embedded lon/lat pair, and for an embedded pointer to the same place's eeu.cty self-index (14 encodings Γ— 6 independent real cities, all zero hits, plus a directory-wide check) β€” both directly refuted, not just unconfirmed. Now backed by a direct disassembly reason why: see "Cracked via real DBAL.OUT disassembly" below β€” FeaCoordReceiverInternal::getCoordinates()'s own accumulators are proven (by its init() code) to always start at (0, 0); the RELATIVE delta-chain format feeding them is fully cracked, but no absolute anchor is ever supplied anywhere in this file's own decode path. A .fea entry can currently only be geolocated via its embedded name (cross-referenced against real-world geography or eeu.cty), not a stored position.
  • How many directory/section-boundary regions exist in total β€” 4 are now confirmed (T1/T2/T3/T4) plus a 5th weaker, unconfirmed candidate at EOF β€” and what the small non-directory-shaped non-zlib gaps between ordinary entries actually are.

A later session's new lead: eeu.mod's own schema, in tension with the hash-table finding

eeu.mod's feature block (never checked against eeuz.fea specifically before) is the single largest in the whole schema β€” ~77 real fields, describing:

MapHeader{db_cover bbox, parcel_width/height/cnt_x/cnt_y/norm_x/norm_y grid,
          feaType, scaleCnt, scales[]}
  -> ParcelHeader{offset, byteCnt, byteCntZip}          -- 12 bytes!
  -> FeatureDataHeader{category, type, flags, feaPointCnt, ...}
     [geometry variant: line | poly | point | road, each with delta_long/delta_lat]

ParcelHeader{offset, byteCnt, byteCntZip} independently confirms and names the already-cracked 12-byte directory triple above β€” found two different ways in the same session (offset=offset, byteCnt=declen, byteCntZip=complen).

Tested and refuted: the schema's parcel_cnt_xΓ—parcel_cnt_y product, on the guess it should equal the directory's own 977,308-record count β€” 977,308 = 929 Γ— 1,052 β€” neither value appears anywhere as a uint32 (either endianness) in the first 12MB of the file.

The single most actionable new lead, not yet tested: the schema names delta_long/delta_lat as real per-geometry-point fields β€” coordinates in this format are RELATIVE deltas, not absolute values, directly explaining why the exhaustive absolute-coordinate search above found nothing. A future session should search for a small delta pair near an anchor (a parcel's own coordinate, or eeu.cty's already-known real position for that place) rather than a standalone absolute value.

This is a genuine, unresolved tension, not a resolved contradiction: the schema's parcel-grid/delta-coordinate design sits uneasily next to the independently well-evidenced "hash table, no locatable coordinate" finding above. Possible reconciliations, none tested: this disc's build may use only a subset of the schema's full generality (matching the pattern seen elsewhere, e.g. eeuz.pca's always-zero clusterOffset/clusterCount) β€” note that T3/T4 (found and confirmed above, including the Turku/Trondheim/Oslo table) show the same offset/declen/complen index-triple shape as T1/T2, not a grid/delta- coordinate one, so this reconciliation option remains open rather than resolved; or the entries tested so far (Mediterranean Sea, IaΘ™i, ...) may be a type/category that doesn't carry the point/road geometry sub-structure at all.

A still later session's first concrete attempt, inconclusive β€” mild new evidence toward the last reconciliation above: located IaΘ™i's own name table precisely inside its 15,687-byte entry (byte 1336) and inspected the 20 bytes immediately before it β€” the natural place for a FeatureDataHeader/geometry block. Naively read as category/type/ flags/feaPointCnt, the 4th byte is 7 β€” implausible for a single city label (should be a 1-point feature) β€” and no small int16 value in that window resembles a plausible coordinate delta. Doesn't refute the delta-coordinate theory (the anchor position was a guess, not derived from the schema), but is a real data point against "IaΘ™i's geometry sits directly adjacent to its name table," and toward "this entry may not carry point geometry for IaΘ™i at all." A proper test needs an actual sub-record walker (respecting feaPointCnt-driven variable lengths), not yet built.

Cracked via real DBAL.OUT disassembly: the relative point-geometry format β€” and strong evidence the absolute anchor lives outside this file's own code entirely

A still later session went beyond debug-string name matching and actually disassembled real firmware code, the same technique that cracked eeu.iof's type field: dbal/V010_015/DBAL.OUT is a small, UNLINKED ET_REL PowerPC object with every function's full machine code physically present in the file (no FHDD6.FLI-style disassembly wall) β€” used capstone (PowerPC, big-endian) plus a hand-rolled ELF32 BE section/symbol/relocation table parser (the relocation table is what makes this session's result trustworthy β€” it resolves every lis/addi global-address pair to its REAL linker symbol name, removing the guesswork that address-only disassembly would otherwise require).

A real, dedicated db_feature.cpp handler, with real class names

String-mining DBAL.OUT (beyond the db_intersection* strings already documented for eeu.iof) found a dedicated handler for this exact file: db_fea_init_V005, db_fea_get_file_header_V005, db_fea_read_parcels_V005, db_fea_get_layer_properties_V005, etc., all in db_feature.cpp. Relocation-table resolution of the functions these call turned up real, internal C++ class and type names never seen before in this project: FeaRequest, FeaDataReceiver, FeaCoordReceiverInternal (with a vtable, _vt$24FeaCoordReceiverInternal), FeaLayerPropertiesInternal, LocalPoint, FeaCrossOver, and a nested FeaCoordReceiverInternal::ThinoutAccuracy enum β€” plus parseExtractedParcel__FPUcR15FeaDataReceiverR10FeaRequestT0i ("parse extracted parcel"), the real per-entry content parser, and getFeatureName__FUcPUcT1Ri ("get feature name" β€” the real function behind this project's own scan_names()), DBTransCodeChecker (text validation), and tt_ara/tt_rus (translation tables β€” confirming the multi-language name handling independently).

FeaCoordReceiverInternal::getCoordinates() β€” the real per-point decoder, disassembled and understood instruction-by-instruction

getCoordinates() (784 bytes) has almost no external calls (only one, to thinout() β€” a real Douglas-Peucker-style line-simplification routine, matching ThinoutAccuracy), meaning the actual coordinate math lives directly in its own instructions β€” nothing hidden behind another indirect call. The core loop, read field-by-field from the real machine code:

for each 4-byte record in the chain:
    dx = read_int16_BE(ptr[0:2])        # a big-endian signed 16-bit delta
    dy = read_int16_BE(ptr[2:4])
    if dx != 0 and dy != 0 and <a mode flag>:
        break                            # conditional sentinel/terminator
    accum_x += dx                        # this+0x18, a 16-bit accumulator
    accum_y += dy                        # this+0x1a, a 16-bit accumulator
    scale_x = layer_properties->field_0x50   # FeaLayerPropertiesInternal*
    scale_y = layer_properties->field_0x54   # at this+0x2c
    output.x = (int16)(accum_x * scale_x)    # ONE LocalPoint per record
    output.y = (int16)(accum_y * scale_y)

This is the same cumulative-delta-chain shape as MAP_COMPRESSED's own already-cracked decode_vertex_chain() (research/ map_compressed_reader.py) β€” consistent with eeu.mod's own schema declaring delta_long/delta_lat fields on this table too β€” but it is a different, eeuz.fea-specific implementation: 16-bit (not 32-bit) accumulators, and a final per-axis multiply by a scale factor pulled from a separate FeaLayerPropertiesInternal object rather than a fixed /100000 constant.

The 2 float constructor parameters are NOT an anchor β€” disassembly proves it

The natural hope, given init()'s real signature (init__ 24FeaCoordReceiverInternalPUcUlPciQ224FeaCoordReceiverInternal15ThinoutAccuracyP26FeaLayerPropertiesInternalff β€” note the trailing ff, 2 floats) was that these 2 floats are a real- world anchor coordinate (lon/lat) this entry's relative chain starts from. Disassembled directly and refuted: init() (72 bytes) stores the 2 floats verbatim into this+0x24/this+0x28 β€” fields getCoordinates() never reads β€” and explicitly zeroes this+0x18/ this+0x1a (the x/y accumulators) and this+0x1c (the "first iteration" flag). The accumulators always start at exactly (0, 0) β€” proven by the init code itself, not inferred. Tracing one call site (parseExtractedParcel) further found where those 2 floats actually come from: a ratio of 4 different FeaLayerPropertiesInternal fields (field_0x10/field_0x14 vs. field_0x20/field_0x24), computed via the classic PowerPC int-to-double bit-trick then divided β€” the shape of an accuracy/unit-conversion ratio (consistent with the real s_pulAccuracyTable/s_pulAccuracyTableOrg/ThinoutAccuracy globals found via relocations elsewhere in this same handler), not a geographic coordinate.

What this means: eeuz.fea's own decode code, traced end to end from db_fea_read_parcels_V005 through parseExtractedParcel into FeaCoordReceiverInternal::getCoordinates(), never once supplies or receives an absolute real-world position β€” only relative deltas scaled by accuracy/unit factors. Combined with the type itself being named LocalPoint (not GeoPoint/Coordinate) and this project's own already-exhaustive, independently-refuted search for an absolute coordinate anywhere in a decompressed entry's bytes (see "What's NOT cracked" above), the most consistent reading is that an absolute anchor is supplied externally, by whatever caller already knows where on the map this entry belongs (plausibly the rendering/placement code, using a tile or viewport position it already has from MAP_COMPRESSED) β€” not something .fea's own bytes or DBAL.OUT's own code ever carry. Tracing that external caller further would mean stepping outside DBAL.OUT into the main FHDD6.FLI application image, which this project has already established has its own, different disassembly wall (known function names whose code addresses fall outside the disassemblable region, confirmed across 3 independent firmware builds β€” see eeu.iof's own page and research/swl_5238_reader.py) β€” so this may not be resolvable by further firmware disassembly at all, not just "not yet resolved."

Not pursued further

parseExtractedParcel's own header-parsing logic (where exactly, within one entry's bytes, the point/delta chain begins, and how feaPointCnt gates its length) was not fully traced β€” the function is 2820 bytes and only its first ~300 bytes (the accuracy-ratio computation above) were disassembled in detail. A future session could trace the rest to at least pin down the exact starting byte offset for a chain within a known entry (e.g., IaΘ™i's), which would let the relative-delta decode above be validated directly against real file bytes β€” not yet done this session.

Practical fallback (validated, useful on its own)

enumerate_entries() β€” brute-force but efficient back-to-back zlib walking with automatic (now escalating-window) gap recovery β€” reliably enumerates real entries across the whole file; scan_names() extracts the multi-language name table from any given entry. Together these are, right now, the only working way to enumerate and approximately geolocate .fea content (via names), without a full coordinate decode.

Reader code

research/feature_reader.py (full validation numbers, every refuted hypothesis, and remaining candidate leads in its module docstring) β€” decode_directory_table(), verify_index_triple(), find_index_record_for_offset(), enumerate_entries(), scan_names().

The DBAL.OUT disassembly above (real function/class names: FeaCoordReceiverInternal::getCoordinates()/init(), parseExtractedParcel, db_fea_read_parcels_V005, db_fea_init_V005) was done ad hoc for this session β€” a short ELF32 big-endian section/ symbol/relocation-table parser plus capstone (CS_ARCH_PPC/CS_MODE_BIG_ENDIAN/CS_MODE_32) against dbal/ V010_015/DBAL.OUT's raw .text bytes, same technique as eeu.iof's own crack β€” not saved as a new research/ module (a one-off targeted read of specific functions, not a general- purpose reader); reproducible directly from the function names/addresses cited above.