File formats
As of 2026-10-19 (vulture-ios 3176c4cc4, vulture-android 85fd9cc6).
IGC is the only flight format. Waypoints and tasks use SeeYou CUP. Airspace comes as OpenAir text, SeeYou CUB (binary) or openAIP XML. There is no Vulture file format of its own: no .vulture type exists.
Overview
Section titled “Overview”| Format | Extension | Content | iOS in | iOS out | Android in | Android out |
|---|---|---|---|---|---|---|
| IGC flight log | .igc | text, A/H/I/B/C/E/K/L records | Yes | Yes (share, recording, backup) | Yes | Yes (share, export) |
| SeeYou waypoints and tasks | .cup | CSV-like text | Yes | Yes (waypoint share, task export, backup) | Decoder only, no import screen found | Tasks in the export ZIP |
| SeeYou airspace | .cub | binary | Yes | Yes (share) | Yes | Yes (export ZIP) |
| OpenAir airspace | .txt | text | Yes | Yes (share) | Yes | Yes (export ZIP) |
| openAIP airspace | .aip (an openAIP _asp.xml renamed by vulture-web) | XML | Yes | Yes (share) | Yes | Yes (export ZIP) |
| XCTrack task | QR code, XCTSK: or XCTSKZ: | JSON, deflated for XCTSKZ: | Yes (task scanner) | No | Decoder only | No |
| LXNAV Nano declaration | file named decl | text | No | Yes (task export) | No | No |
| FLARM declaration | flarmcfg.txt | text | No | Yes (task export, also sent over Wi-Fi) | No | Removed |
| Backup / export all data | .zip | folder tree, see below | No | Yes | Yes (any ZIP of flights, including the iOS backup) | Yes |
| Map image | .png | image | No | Yes | No | Yes |
Parsers and writers
Section titled “Parsers and writers”| Format | iOS | Android |
|---|---|---|
| IGC | vulture-ios/Packages/Flights/Sources/Flights/Decorder/FlightDecoder.swift (A, H, I, B, E, C and K records, I-record extensions such as ENL, MOP and FXA) | vulture-android/domain/flight/src/main/kotlin/be/fousa/vulture/domain/flight/igc/IgcParser.kt |
| CUP | vulture-ios/Packages/Waypoints/Sources/Waypoints/WaypointsDecoder.swift, WaypointsEncoder.swift | vulture-android/domain/waypoints/.../WaypointsDecoder.kt, WaypointsEncoder.kt |
| CUB | vulture-ios/Packages/Airspace/Sources/Airspace/Decoder/AirspaceCUBDecoder.swift | vulture-android/domain/airspace decoders |
| OpenAir | .../Airspace/Decoder/AirspaceOpenAirDecoder.swift | vulture-android/domain/airspace decoders |
| openAIP | .../Airspace/Decoder/AirspaceOpenAipDecoder.swift | vulture-android/domain/airspace decoders |
| XCTrack QR | vulture-ios/Packages/Waypoints/Sources/Waypoints/XCTrackTaskDecoder.swift, used by TaskScreens/.../TaskScannerViewController.swift | vulture-android/domain/waypoints/.../XCTrackTaskDecoder.kt |
| Task exports | vulture-ios/Packages/Formatting/Sources/Formatting/TaskExportType.swift (cup, nano, flarm) | CUP inside the export ZIP only |
The parsers are held to the same output by the golden data. See Golden data and Flight analysis.
Telling airspace files apart
Section titled “Telling airspace files apart”Both platforms decide the airspace type the same way. First they look at the extension (txt, aip or cub, in any case). If the extension does not say, they look at the content. The CUB format has a binary signature.
- iOS:
AirspaceFileType.swift,File+Airspace.swift - Android:
vulture-android/domain/airspace/.../model/AirspaceFileType.kt(fromExtension,detect)
On iOS the extension switch is repeated in several places: SceneDelegate.swift, SplitViewModel.swift, DroppedFileLoader.swift, the QuickLook and Thumbnail view models, and AirspaceFileType.swift. A new format has to be added to all of them.
IGC files written by Vulture
Section titled “IGC files written by Vulture”The iOS recorder (iPhone and Watch) writes the file in vulture-ios/Packages/FlightRecording/Sources/FlightRecording/Location/Extensions/FixRecord+IGC.swift. Lines are joined with \n. Android does not record flights in v1. When it does, it must write the same records, so that the backend, the services and the analysis treat both recordings alike.
| Record | Value | Notes |
|---|---|---|
A | AVUL<identifierForVendor> | The vendor UUID of the device, or 000 |
HFDTE | the date of the first fix, DDMMYY | |
HFPLTPILOT | declared pilot | Falls back to a localised “unknown pilot” text |
HFCM2CREW2 | declared passenger | Only when set |
HFGTYGLIDERTYPE | declared glider type | Falls back to a localised “unknown glider” text |
HFGIDGLIDERID | registration | Only when set |
HFCIDCOMPETITIONID | competition id | Only when set |
HFCCLCOMPETITIONCLASS | competition class | Only when set |
HFRFWFIRMWAREVERSION | the app’s marketing version | |
HFFTYFRTYPE | Vulture, or Vulture Watch on the Watch | |
I013638FXA | FXA in bytes 36 to 38 | Every B record writes 007 there |
C… | the task declaration, when a task is set | Date, time, 000000, 0001, turnpoint count, then a C0000000N00000000E line, the waypoints, and another C0000000N00000000E line |
LNAVOZN=<n>,Style=…,R1=…m,R2=…m,A1=…,A2=…,A12=…,Line=1,AAT=1 | one per zone | LXNAV observation-zone lines; values that are zero are left out |
E<time>PEV | pilot event | |
LLXVSPO2<SpO2×10>HR<bpm> | oxygen saturation and heart rate | When health data is recorded (Watch) |
B | time, lat, lon, A, GPS altitude, then GPS altitude again in the pressure column, 007 | No barometric altitude is written |
The declared values come from the synced declared* settings. See persistence and settings §3.
The Nano (decl) and FLARM (flarmcfg.txt) exports start with their own header lines (vulture-ios/Packages/Formatting/Sources/Formatting/String+Nano.swift, String+Flarm.swift). For example, the Nano export writes HFDTE, HFFXA999, HFDTMDTM100GPSDATUM:WGS-1984, HFFTYFRTYPE:Vulture and the declared pilot.
Backup and “Export all data” ZIP
Section titled “Backup and “Export all data” ZIP”Both platforms write the same layout:
- iOS:
vulture-ios/Modules/App/Sources/Services/System/Backup.swift - Android:
vulture-android/feature/settings/src/main/kotlin/be/fousa/vulture/feature/settings/export/AllDataArchive.kt
<yyyy-MM-dd>.zip└─ <yyyy-MM-dd>/ the day of the export, device time zone ├─ <year>/<flight>.igc each logbook flight, in its section (takeoff year), under its file name ├─ <task>.cup every task in one SeeYou file, if there are any ├─ Waypoints/<file> waypoint files (folder name translated: file.export.directory.waypoints) └─ Airspace/<file> airspace files (folder name translated: file.export.directory.airspace)The two platforms differ in three ways:
- Missing name or takeoff time. iOS skips a flight without a file name or section. Android names it as the share sheet does and files it under the year it was imported.
- IGC bytes. iOS writes its stored text re-encoded as UTF-8. Android writes the bytes as stored.
- Import. Only Android imports a ZIP or a folder (decision §6 in
vulture-android/docs/decisions.md), so an iOS backup can be read on Android.
iOS: UTIs and document types
Section titled “iOS: UTIs and document types”These are declared in vulture-ios/Modules/App/Resources/Info.plist. Every imported type conforms to public.content and public.data.
UTI (UTImportedTypeDeclarations) | Extension | Description | Document type (CFBundleDocumentTypes) |
|---|---|---|---|
public.igc | igc | IGC File | IGC File, rank Default |
public.cup | cup | CUP File | CUP File, rank Default |
public.cub | cub | CUB File | CUB File, rank Default |
public.aip | aip | OpenAip File | OpenAIP File, rank Default |
public.plain-text (system type) | txt | n/a | OpenAir File, rank Default |
public.igc, public.cup, public.cub and public.aip are imported declarations in Apple’s reserved public. namespace, not types that Vulture exports. Opening a file goes through SceneDelegate (switch firstURL.pathExtension.lowercased()), which reports openFlightInVulture, openWaypointsInVulture or openAirspaceInVulture (see Analytics events). Quick Look previews and thumbnails exist for IGC, CUP, CUB and AIP files (vulture-ios/Modules/QuickLook, Modules/Thumbnail).
Android: MIME types and intent filters
Section titled “Android: MIME types and intent filters”These are on MainActivity in vulture-android/app/src/main/AndroidManifest.xml:
| Filter | Action | Data | Purpose |
|---|---|---|---|
| Files by MIME type | VIEW | schemes content and file; application/vnd.fai.igc, application/x-igc, application/octet-stream, text/plain, application/zip, application/x-zip-compressed | “Open with” for IGC, airspace text and ZIP files |
| Files by extension | VIEW | schemes content and file, host *, */*, pathPattern .*\.igc, .*\..*\.igc, .*\..*\..*\.igc, .*\.IGC, .*\..*\.IGC | Files whose provider gives no useful MIME type |
| Share sheet | SEND, SEND_MULTIPLE | application/vnd.fai.igc, application/x-igc, application/octet-stream, application/zip, application/x-zip-compressed | Share into Vulture. text/plain goes to ShareLinkActivity instead (see Deep links) |
The MIME type does not decide the format. Content is sniffed after the file is copied in (ExternalFileIntents, :data:db import, AirspaceFileType.detect). A raw import reads at most 64 MiB per file or ZIP entry (FlightImporter.MAX_FILE_BYTES). Without the several flights Pro feature, a batch opened from another app keeps only its first flight.
Outgoing files are shared through the FileProvider in :core:share.
Limits that matter for sync
Section titled “Limits that matter for sync”The sync schema keeps the IGC gzipped in Cloud Storage and caps each stored object at 32 MB. IGC compresses about 5×, so this sits above the 64 MiB raw import cap. See Sync schema, “Storage objects” in §2.
