Index: /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/FitPlugin.java
===================================================================
--- /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/FitPlugin.java	(revision 36157)
+++ /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/FitPlugin.java	(revision 36158)
@@ -6,10 +6,11 @@
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
+import java.lang.reflect.RecordComponent;
 import java.nio.file.Files;
-import java.time.Instant;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
-import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
 
 import org.openstreetmap.josm.actions.ExtensionFileFilter;
@@ -26,4 +27,5 @@
 import org.openstreetmap.josm.plugins.fit.lib.FitReader;
 import org.openstreetmap.josm.plugins.fit.lib.FitReaderOptions;
+import org.openstreetmap.josm.plugins.fit.lib.global.FitEvent;
 import org.openstreetmap.josm.plugins.fit.lib.global.HeartRateCadenceDistanceSpeed;
 
@@ -53,33 +55,66 @@
         @Override
         public void importData(File file, ProgressMonitor progressMonitor) throws IOException {
-            try (InputStream inputStream = Files.newInputStream(file.toPath())) {
+            try (var inputStream = Files.newInputStream(file.toPath())) {
                 final var records = FitReader.read(inputStream, FitReaderOptions.TRY_TO_FINISH);
                 final var gpxData = new GpxData(true);
                 progressMonitor.beginTask(tr("Processing FIT records"), records.length);
                 final var waypoints = new ArrayList<WayPoint>(records.length % 1000);
-                for (int i = 0; i < records.length; i++) {
+                for (var i = 0; i < records.length; i++) {
                     var r = records[i];
                     if (i % 1000 == 0) {
                         progressMonitor.worked(1);
                     }
-                    if (r instanceof HeartRateCadenceDistanceSpeed(
-                            Instant timestamp, double lat, double lon, double ele, short heartRate, short cadence,
-                            int distance, int speed, long[][] unknown,
-                            org.openstreetmap.josm.plugins.fit.lib.global.FitDevDataRecord devData
-                    )) {
-                        final var waypoint = new WayPoint(new LatLon(lat, lon));
-                        waypoint.setInstant(timestamp);
-                        waypoint.attr.putAll(Map.of("ele", ele, "heart_rate", heartRate,
-                                "cadence", cadence, "distance", distance, "speed", speed,
-                                "unknown", unknown, "devData", devData));
-                        waypoints.add(waypoint);
+                    if (r instanceof HeartRateCadenceDistanceSpeed heartRateCadenceDistanceSpeed) {
+                        final var lat = heartRateCadenceDistanceSpeed.lat();
+                        final var lon = heartRateCadenceDistanceSpeed.lon();
+                        if (!Double.isNaN(lat) && !Double.isNaN(lon)) {
+                            final var waypoint = new WayPoint(new LatLon(lat, lon));
+                            waypoint.setInstant(heartRateCadenceDistanceSpeed.timestamp());
+                            // Use a sorted map for consistency
+                            final var map = new TreeMap<String, Object>();
+                            for (RecordComponent component : HeartRateCadenceDistanceSpeed.class
+                                    .getRecordComponents()) {
+                                if (Arrays.asList("lat", "lon", "timestamp", "unknown").contains(component.getName())) {
+                                    continue; // skip information that has specific fields
+                                }
+                                try {
+                                    map.put(component.getName(),
+                                            component.getAccessor().invoke(heartRateCadenceDistanceSpeed));
+                                } catch (ReflectiveOperationException e) {
+                                    // This should never happen; the component accessors should _always_ be public.
+                                    throw new IOException(e);
+                                }
+                            }
+                            map.put("unknown", Arrays.deepToString(heartRateCadenceDistanceSpeed.unknown()));
+                            waypoint.attr.putAll(map);
+                            if (!waypoints.isEmpty() && Math.abs(waypoints.getLast().getInstant().getEpochSecond()
+                                    - waypoint.getInstant().getEpochSecond()) > TimeUnit.DAYS.toDays(365)) {
+                                createTrack(gpxData, new ArrayList<>(waypoints));
+                                waypoints.clear();
+                            } else {
+                                waypoints.add(waypoint);
+                            }
+                        }
+                    } else if (r instanceof FitEvent) {
+                        // break up the events. It would be better to only do this on lap events.
+                        gpxData.addTrack(new GpxTrack(Collections.singleton(new ArrayList<>(waypoints)),
+                                Collections.emptyMap()));
+                        waypoints.clear();
                     }
                 }
                 waypoints.trimToSize();
-                gpxData.addTrack(new GpxTrack(Collections.singleton(waypoints), Collections.emptyMap()));
+                createTrack(gpxData, waypoints);
                 gpxData.endUpdate();
                 MainApplication.getLayerManager().addLayer(new GpxLayer(gpxData, file.getName(), true));
             }
         }
+
+        private static void createTrack(GpxData gpxData, ArrayList<WayPoint> waypoints) {
+            if (waypoints.size() > 1) {
+                gpxData.addTrack(new GpxTrack(Collections.singleton(waypoints), Collections.emptyMap()));
+            } else if (!waypoints.isEmpty()) {
+                gpxData.addWaypoint(waypoints.get(0));
+            }
+        }
     }
 }
Index: /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FileCreator.java
===================================================================
--- /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FileCreator.java	(revision 36158)
+++ /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FileCreator.java	(revision 36158)
@@ -0,0 +1,40 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.fit.lib.global;
+
+import static org.openstreetmap.josm.plugins.fit.lib.global.HeartRateCadenceDistanceSpeed.NO_UNKNOWNS;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+
+import org.openstreetmap.josm.plugins.fit.lib.FitBaseType;
+import org.openstreetmap.josm.plugins.fit.lib.records.internal.FitDeveloperField;
+import org.openstreetmap.josm.plugins.fit.lib.records.internal.FitDeveloperFieldDescriptionMessage;
+import org.openstreetmap.josm.plugins.fit.lib.records.internal.FitField;
+import org.openstreetmap.josm.plugins.fit.lib.utils.DevDataUtils;
+import org.openstreetmap.josm.plugins.fit.lib.utils.NumberUtils;
+
+public record FileCreator(short softwareVersion, short hardwareVersion, long[][] unknown, FitDevDataRecord devData)
+        implements FitData {
+    public static FileCreator parse(boolean littleEndian, List<FitField> fieldList, List<FitDeveloperField> developerFieldList,
+                                    FitDeveloperFieldDescriptionMessage[] developerFields, InputStream inputStream) throws IOException {
+        var hardwareVersion = FitBaseType.uint8.invalidValue();
+        var softwareVersion = FitBaseType.uint8.invalidValue();
+        var unknowns = NO_UNKNOWNS;
+        for (FitField fitField : fieldList) {
+            final var size = fitField.size();
+            switch (fitField.fieldDefinitionNumber()) {
+                case 0 -> softwareVersion = NumberUtils.decodeShort(size, littleEndian, inputStream);
+                case 1 -> hardwareVersion = NumberUtils.decodeShort(size, littleEndian, inputStream);
+                default -> {
+                    unknowns = Arrays.copyOf(unknowns, unknowns.length + 1);
+                    unknowns[unknowns.length - 1] = new long[]{fitField.fieldDefinitionNumber(),
+                            NumberUtils.decodeLong(size, littleEndian, inputStream)};
+                }
+            }
+        }
+        return new FileCreator((short) hardwareVersion, (short) softwareVersion, unknowns,
+                DevDataUtils.parseDevFields(littleEndian, developerFieldList, developerFields, inputStream));
+    }
+}
Index: /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FitData.java
===================================================================
--- /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FitData.java	(revision 36157)
+++ /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FitData.java	(revision 36158)
@@ -7,5 +7,5 @@
 public sealed
 interface FitData
-permits FitDeveloperDataIdMessage, FitDevice, FitUnknownRecord, HeartRateCadenceDistanceSpeed
+permits FileCreator, FitDeveloperDataIdMessage, FitDevice, FitEvent, FitUnknownRecord, HeartRateCadenceDistanceSpeed
 {
 
Index: /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FitEvent.java
===================================================================
--- /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FitEvent.java	(revision 36158)
+++ /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FitEvent.java	(revision 36158)
@@ -0,0 +1,53 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.fit.lib.global;
+
+import static org.openstreetmap.josm.plugins.fit.lib.global.HeartRateCadenceDistanceSpeed.NO_UNKNOWNS;
+import static org.openstreetmap.josm.plugins.fit.lib.global.HeartRateCadenceDistanceSpeed.decodeInstant;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.List;
+
+import org.openstreetmap.josm.plugins.fit.lib.FitBaseType;
+import org.openstreetmap.josm.plugins.fit.lib.records.internal.FitDeveloperField;
+import org.openstreetmap.josm.plugins.fit.lib.records.internal.FitDeveloperFieldDescriptionMessage;
+import org.openstreetmap.josm.plugins.fit.lib.records.internal.FitField;
+import org.openstreetmap.josm.plugins.fit.lib.utils.DevDataUtils;
+import org.openstreetmap.josm.plugins.fit.lib.utils.NumberUtils;
+
+public record FitEvent(Instant timestamp, byte event, byte eventType, short eventGroup, long data, long[][] unknown,
+                       FitDevDataRecord devData) implements FitData, IFitTimestamp<FitEvent> {
+    @Override
+    public FitEvent withTimestamp(Instant timestamp) {
+        return new FitEvent(timestamp, this.event, this.eventType, this.eventGroup, this.data, this.unknown, this.devData);
+    }
+
+    public static FitEvent parse(boolean littleEndian, List<FitField> fieldList, List<FitDeveloperField> developerFieldList,
+                                 FitDeveloperFieldDescriptionMessage[] developerFields, InputStream inputStream) throws IOException {
+        var event = FitBaseType.enum_.invalidValue();
+        var eventType = FitBaseType.enum_.invalidValue();
+        var eventGroup = FitBaseType.enum_.invalidValue();
+        var timestamp = Instant.EPOCH;
+        var data = FitBaseType.uint32.invalidValue();
+        var unknowns = NO_UNKNOWNS;
+        for (FitField fitField : fieldList) {
+            final var size = fitField.size();
+            switch (fitField.fieldDefinitionNumber()) {
+                case 0 -> event = NumberUtils.decodeByte(size, littleEndian, inputStream);
+                case 1 -> eventType = NumberUtils.decodeByte(size, littleEndian, inputStream);
+                case 3 -> data = NumberUtils.decodeLong(size, littleEndian, inputStream);
+                case 4 -> eventGroup = NumberUtils.decodeShort(size, littleEndian, inputStream);
+                case 253 -> timestamp = decodeInstant(size, littleEndian, inputStream);
+                default -> {
+                    unknowns = Arrays.copyOf(unknowns, unknowns.length + 1);
+                    unknowns[unknowns.length - 1] = new long[]{fitField.fieldDefinitionNumber(),
+                            NumberUtils.decodeLong(size, littleEndian, inputStream)};
+                }
+            }
+        }
+        return new FitEvent(timestamp, (byte) event, (byte) eventType, (short) eventGroup, data, unknowns,
+                DevDataUtils.parseDevFields(littleEndian, developerFieldList, developerFields, inputStream));
+    }
+}
Index: /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FitUnknownRecord.java
===================================================================
--- /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FitUnknownRecord.java	(revision 36157)
+++ /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/FitUnknownRecord.java	(revision 36158)
@@ -2,8 +2,9 @@
 package org.openstreetmap.josm.plugins.fit.lib.global;
 
+import org.openstreetmap.josm.plugins.fit.lib.records.IFitDevData;
 import org.openstreetmap.josm.plugins.fit.lib.records.internal.IField;
 
-public record FitUnknownRecord(FieldData[] data, FitDevDataRecord devData) implements FitData {
+public record FitUnknownRecord(int globalMessageNumber, FieldData[] data, FitDevDataRecord devData) implements FitData {
 
-    public record FieldData(IField field, byte[] data) {}
+    public record FieldData(IField field, IFitDevData<?> data) {}
 }
Index: /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/Global.java
===================================================================
--- /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/Global.java	(revision 36157)
+++ /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/Global.java	(revision 36158)
@@ -2,8 +2,11 @@
 package org.openstreetmap.josm.plugins.fit.lib.global;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
 
+import org.openstreetmap.josm.plugins.fit.lib.FitBaseType;
+import org.openstreetmap.josm.plugins.fit.lib.records.FitDevStringData;
 import org.openstreetmap.josm.plugins.fit.lib.records.internal.FitDefinitionMessage;
 import org.openstreetmap.josm.plugins.fit.lib.records.internal.FitDeveloperField;
@@ -11,4 +14,5 @@
 import org.openstreetmap.josm.plugins.fit.lib.records.internal.FitField;
 import org.openstreetmap.josm.plugins.fit.lib.utils.DevDataUtils;
+import org.openstreetmap.josm.plugins.fit.lib.utils.StringUtils;
 
 /**
@@ -41,15 +45,24 @@
             case 20 ->
                     HeartRateCadenceDistanceSpeed.parse(littleEndian, fieldList, developerFieldList, developerFields, inputStream);
+            case 21 -> FitEvent.parse(littleEndian, fieldList, developerFieldList, developerFields, inputStream);
             case MESSAGE_NUMBER_DEV_FIELD_DESCRIPTION -> FitDeveloperFieldDescriptionMessage
                     .parse(littleEndian, fieldList, developerFieldList, developerFields, inputStream);
             case MESSAGE_NUMBER_APP_ID ->
                     FitDeveloperDataIdMessage.parse(littleEndian, fieldList, developerFieldList, developerFields, inputStream);
+            case 49 -> FileCreator.parse(littleEndian, fieldList, developerFieldList, developerFields, inputStream);
             default -> {
                 final var fieldData = new FitUnknownRecord.FieldData[fieldList.size()];
                 var index = 0;
                 for (FitField field : fieldList) {
-                    fieldData[index++] = new FitUnknownRecord.FieldData(field, inputStream.readNBytes(field.size()));
+                    if (field.fitBaseType() == FitBaseType.string) {
+                        fieldData[index++] = new FitUnknownRecord.FieldData(field,
+                                new FitDevStringData("", "",
+                                        StringUtils.decodeString(new ByteArrayInputStream(inputStream.readNBytes(field.size())))));
+                    } else {
+                        fieldData[index++] = new FitUnknownRecord.FieldData(field, DevDataUtils.getData(field.fitBaseType(),
+                                "", "", field.size(), littleEndian, inputStream));
+                    }
                 }
-                yield new FitUnknownRecord(fieldData,
+                yield new FitUnknownRecord(globalMessageNumber, fieldData,
                         DevDataUtils.parseDevFields(littleEndian, developerFieldList, developerFields, inputStream));
             }
Index: /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/HeartRateCadenceDistanceSpeed.java
===================================================================
--- /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/HeartRateCadenceDistanceSpeed.java	(revision 36157)
+++ /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/HeartRateCadenceDistanceSpeed.java	(revision 36158)
@@ -20,5 +20,5 @@
                                             int distance, int speed, long[][] unknown,
                                             FitDevDataRecord devData) implements FitData, IFitTimestamp<HeartRateCadenceDistanceSpeed> {
-    private static final long[][] NO_UNKNOWNS = new long[0][];
+    static final long[][] NO_UNKNOWNS = new long[0][];
 
     // Using the 2023-09-09-12-0016.fit.gpx file provided by richlv:
@@ -65,5 +65,7 @@
                 case 5 -> distance = NumberUtils.decodeInt(size, littleEndian, inputStream);
                 case 6 -> speed = NumberUtils.decodeInt(size, littleEndian, inputStream);
-                case 253 -> timestamp = Instant.ofEpochSecond(EPOCH_DIFFERENCE + NumberUtils.decodeLong(size, littleEndian, inputStream));
+                // 13 seems to be either -1 (invalid entry?), or 23-27
+                // 107 seems to always be -1 (invalid entry?), 0, and 1.
+                case 253 -> timestamp = decodeInstant(size, littleEndian, inputStream);
                 default -> {
                     unknowns = Arrays.copyOf(unknowns, unknowns.length + 1);
@@ -77,5 +79,13 @@
     }
 
+    static Instant decodeInstant(short size, boolean littleEndian, InputStream inputStream) throws IOException {
+        final var timestamp = NumberUtils.decodeLong(size, littleEndian, inputStream);
+        return Instant.ofEpochSecond(EPOCH_DIFFERENCE + timestamp);
+    }
+
     private static double decodeDegrees(long original) {
+        if (original == FitBaseType.sint32.invalidValue()) {
+            return Double.NaN;
+        }
         // signed ints, no need to look into zigzag decoding
         // 0 -> 0 (assumed)
Index: /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/IFitTimestamp.java
===================================================================
--- /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/IFitTimestamp.java	(revision 36157)
+++ /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/global/IFitTimestamp.java	(revision 36158)
@@ -7,5 +7,5 @@
 
 interface IFitTimestamp<T>
-permits HeartRateCadenceDistanceSpeed
+permits FitEvent, HeartRateCadenceDistanceSpeed
 {
 
Index: /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/utils/DevDataUtils.java
===================================================================
--- /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/utils/DevDataUtils.java	(revision 36157)
+++ /applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/utils/DevDataUtils.java	(revision 36158)
@@ -38,18 +38,24 @@
             final String units = devField.units();
             final short fieldSize = fitField.size();
-            arrayData[index++] = switch (FitBaseType.fromBaseTypeField(devField.fitBaseTypeId())) {
-            case float64 -> new FitDevDoubleData(fieldName, units,
-                    NumberUtils.decodeDouble(fieldSize, littleEndian, inputStream));
-            case float32 -> new FitDevFloatData(fieldName, units,
-                    NumberUtils.decodeFloat(fieldSize, littleEndian, inputStream));
-            case enum_, sint8, uint8, uint8z, sint16, uint16, uint16z, sint32 -> new FitDevIntData(fieldName, units,
-                    NumberUtils.decodeInt(fieldSize, littleEndian, inputStream));
-            case uint32, uint32z, sint64, uint64, uint64z -> new FitDevLongData(fieldName, units,
-                    NumberUtils.decodeLong(fieldSize, littleEndian, inputStream));
-            case string -> new FitDevStringData(fieldName, units, StringUtils.decodeString(inputStream));
-            case byte_, UNKNOWN -> new FitDevUnknown(fieldName, units, inputStream.readNBytes(fieldSize));
-            };
+            arrayData[index++] = getData(FitBaseType.fromBaseTypeField(devField.fitBaseTypeId()), fieldName, units,
+                    fieldSize, littleEndian, inputStream);
         }
         return new FitDevDataRecord(arrayData);
     }
+
+    public static IFitDevData<?> getData(FitBaseType type, String fieldName, String units, short fieldSize,
+            boolean littleEndian, InputStream inputStream) throws IOException {
+        return switch (type) {
+        case float64 -> new FitDevDoubleData(fieldName, units,
+                NumberUtils.decodeDouble(fieldSize, littleEndian, inputStream));
+        case float32 -> new FitDevFloatData(fieldName, units,
+                NumberUtils.decodeFloat(fieldSize, littleEndian, inputStream));
+        case enum_, sint8, uint8, uint8z, sint16, uint16, uint16z, sint32 -> new FitDevIntData(fieldName, units,
+                NumberUtils.decodeInt(fieldSize, littleEndian, inputStream));
+        case uint32, uint32z, sint64, uint64, uint64z -> new FitDevLongData(fieldName, units,
+                NumberUtils.decodeLong(fieldSize, littleEndian, inputStream));
+        case string -> new FitDevStringData(fieldName, units, StringUtils.decodeString(inputStream));
+        case byte_, UNKNOWN -> new FitDevUnknown(fieldName, units, inputStream.readNBytes(fieldSize));
+        };
+    }
 }
