static async PARSE(buf, type) {
        //Maps are always at least 27 bytes in size
        if (!buf || buf.length < HEADER_SIZE) {
            return null;
        }

        if (!type) {
            type = MAP_DATA_TYPES.REGULAR;
        }

        const parsedHeader = DreameMapParser.PARSE_HEADER(buf.subarray(0, HEADER_SIZE));


        // Since P-Frame parsing is much harder than I-Frame parsing, we're skipping them
        if (parsedHeader.frame_type !== FRAME_TYPES.I) {
            return null;
        }

        const layers = [];
        const entities = [];
        const metaData = {};

        if (parsedHeader.robot_position.valid === true) {
            entities.push(
                new mapEntities.PointMapEntity({
                    points: [
                        parsedHeader.robot_position.x,
                        parsedHeader.robot_position.y
                    ],
                    metaData: {
                        angle: DreameMapParser.CONVERT_ANGLE_TO_VALETUDO(parsedHeader.robot_position.angle)
                    },
                    type: mapEntities.PointMapEntity.TYPE.ROBOT_POSITION
                })
            );
        }

        if (parsedHeader.charger_position.valid === true) {
            entities.push(
                new mapEntities.PointMapEntity({
                    points: [
                        parsedHeader.charger_position.x,
                        parsedHeader.charger_position.y
                    ],
                    metaData: {
                        angle: DreameMapParser.CONVERT_ANGLE_TO_VALETUDO(parsedHeader.charger_position.angle)
                    },
                    type: mapEntities.PointMapEntity.TYPE.CHARGER_LOCATION
                })
            );
        }


        if (buf.length >= HEADER_SIZE + parsedHeader.width * parsedHeader.height) {
            const imageData = buf.subarray(HEADER_SIZE, HEADER_SIZE + parsedHeader.width * parsedHeader.height);
            const activeSegmentIds = [];
            const segmentNames = {};
            let additionalData = {};

            try {
                additionalData = JSON.parse(buf.subarray(parsedHeader.width * parsedHeader.height + HEADER_SIZE).toString());
            } catch (e) {
                Logger.warn("Error while parsing additional map data", e);
            }

            if (additionalData.sa && Array.isArray(additionalData.sa)) {
                additionalData.sa.forEach(sa => {
                    activeSegmentIds.push(sa[0].toString());
                });
            }

            if (additionalData.seg_inf) {
                Object.keys(additionalData.seg_inf).forEach(segmentId => {
                    if (additionalData.seg_inf[segmentId].name) {
                        segmentNames[segmentId] = Buffer.from(additionalData.seg_inf[segmentId].name, "base64").toString("utf8");
                    }
                });
            }

            layers.push(...DreameMapParser.PARSE_IMAGE(parsedHeader, activeSegmentIds, segmentNames, imageData, type));

            /**
             * Contains saved map data such as virtual restrictions as well as segments
             *
             * ris 2 seems to represent that the rism data shall be applied to the map while ris 1 only appears
             * after the robot complains about being unable to use the map
             *
             * With vSLAM robots, ris doesn't automatically switch from 1 to 2 after the initial cleanup.
             * Instead, it requires the start of another cleanup
             * Because of that, we also need to check for iscleanlog, so that a vSlam user gets to see their
             * newly mapped segments without any instantly aborted second cleanups.
             */
            if (additionalData.rism && (additionalData.ris === 2 || additionalData.iscleanlog === true)) {
                const rismResult = await DreameMapParser.PARSE(await DreameMapParser.PREPROCESS(additionalData.rism), MAP_DATA_TYPES.RISM);

                if (rismResult instanceof mapEntities.ValetudoMap) {
                    rismResult.entities.forEach(e => {
                        if (e instanceof mapEntities.PointMapEntity) {
                            if (e.type === mapEntities.PointMapEntity.TYPE.ROBOT_POSITION && parsedHeader.robot_position.valid === false) {
                                entities.push(e);
                            }
                            if (e.type === mapEntities.PointMapEntity.TYPE.CHARGER_LOCATION && parsedHeader.charger_position.valid === false) {
                                entities.push(e);
                            }
                        } else if (e instanceof mapEntities.PolygonMapEntity) {
                            if (
                                e.type === mapEntities.PolygonMapEntity.TYPE.NO_GO_AREA ||
                                e.type === mapEntities.PolygonMapEntity.TYPE.NO_MOP_AREA
                            ) {
                                entities.push(e);
                            }
                        } else if (e instanceof mapEntities.LineMapEntity && e.type === mapEntities.LineMapEntity.TYPE.VIRTUAL_WALL) {
                            entities.push(e);
                        }
                    });

                    rismResult.layers.forEach(l => {
                        if (l.metaData.segmentId !== undefined) {
                            if (activeSegmentIds.includes(l.metaData.segmentId)) { //required for the 1C
                                l.metaData.active = true;
                            }

                            if (layers.findIndex(eL => {
                                return eL.metaData.segmentId === l.metaData.segmentId;
                            }) === -1) {
                                layers.push(l);
                            } else {
                                if (l.metaData.name) {
                                    layers.find(eL => {
                                        return eL.metaData.segmentId === l.metaData.segmentId;
                                    }).metaData.name = l.metaData.name;
                                }
                            }
                        } else {
                            if (layers.findIndex(eL => {
                                return eL.type === l.type;
                            }) === -1) {
                                layers.push(l);
                            }
                        }
                    });

                    if (rismResult.metaData?.dreamePendingMapChange !== undefined) {
                        metaData.dreamePendingMapChange = rismResult.metaData.dreamePendingMapChange;
                    }
                }
            }


            if (additionalData.tr) {
                const paths = DreameMapParser.PARSE_PATH(parsedHeader, additionalData.tr, additionalData.l2r === 1);

                if (paths?.length > 0) {
                    entities.push(...paths);
                }

            }

            if (Array.isArray(additionalData.da)) { //1C
                entities.push(
                    ...DreameMapParser.PARSE_AREAS(
                        parsedHeader,
                        [additionalData.da],
                        mapEntities.PolygonMapEntity.TYPE.ACTIVE_ZONE
                    )
                );
            }

            if (additionalData.da2 && Array.isArray(additionalData.da2.areas)) {
                entities.push(
                    ...DreameMapParser.PARSE_AREAS(
                        parsedHeader,
                        additionalData.da2.areas,
                        mapEntities.PolygonMapEntity.TYPE.ACTIVE_ZONE
                    )
                );
            }

            if (additionalData.vw) {
                if (Array.isArray(additionalData.vw.rect)) {
                    entities.push(
                        ...DreameMapParser.PARSE_AREAS(
                            parsedHeader,
                            additionalData.vw.rect,
                            mapEntities.PolygonMapEntity.TYPE.NO_GO_AREA
                        )
                    );
                }

                if (Array.isArray(additionalData.vw.mop)) {
                    entities.push(
                        ...DreameMapParser.PARSE_AREAS(
                            parsedHeader,
                            additionalData.vw.mop,
                            mapEntities.PolygonMapEntity.TYPE.NO_MOP_AREA
                        )
                    );
                }

                if (Array.isArray(additionalData.vw.line)) {
                    entities.push(
                        ...DreameMapParser.PARSE_LINES(
                            parsedHeader,
                            additionalData.vw.line,
                            mapEntities.LineMapEntity.TYPE.VIRTUAL_WALL
                        )
                    );
                }
            }

            /*
                TODO RESEARCH

                There can be an spoint object. No idea what that does
                There can also be multiple tpoint points. No idea when or why that happens or what it does either
             */
            if (additionalData.pointinfo && Array.isArray(additionalData.pointinfo.tpoint) && additionalData.pointinfo.tpoint.length === 1) {
                const goToPoint = DreameMapParser.CONVERT_TO_VALETUDO_COORDINATES(
                    additionalData.pointinfo.tpoint[0][0],
                    additionalData.pointinfo.tpoint[0][1],
                );

                entities.push(new mapEntities.PointMapEntity({
                    points: [
                        goToPoint.x,
                        goToPoint.y,
                    ],
                    type: mapEntities.PointMapEntity.TYPE.GO_TO_TARGET
                }));
            }

            if (additionalData.suw > 0) {
                /*
                    6 = New Map in Single-map
                    5 = New Map in Multi-map

                    other values TBD
                 */
                metaData.dreamePendingMapChange = true;
            }

            if (additionalData.ai_obstacle?.length > 0) {
                additionalData.ai_obstacle.forEach((obstacle) => {
                    const coords = DreameMapParser.CONVERT_TO_VALETUDO_COORDINATES(
                        parseFloat(obstacle[0]),
                        parseFloat(obstacle[1])
                    );
                    const type = DreameConst.AI_CLASSIFIER_IDS[obstacle[2]] ?? `Unknown ID ${obstacle[2]}`;
                    const confidence = `${Math.round(parseFloat(obstacle[3])*100)}%`;
                    const image = obstacle[5] !== undefined ? obstacle[5] : undefined;

                    if (HIDDEN_OBSTACLE_TYPES.includes(obstacle[2])) {
                        return;
                    }

                    entities.push(new mapEntities.PointMapEntity({
                        points: [
                            coords.x,
                            coords.y,
                        ],
                        type: mapEntities.PointMapEntity.TYPE.OBSTACLE,
                        metaData: {
                            label: `${type} (${confidence})`,
                            id: uuid.v5(
                                `${obstacle[2]}_${obstacle[0]}_${obstacle[1]}`,
                                OBSTACLE_ID_NAMESPACE
                            ),
                            image: image
                        }
                    }));
                });
            }
        } else {
            //Just a header
            return null;
        }

        // While the map is technically valid at this point, we still ignore it as we don't need a map with 0 pixels
        if (layers.length === 0) {
            return null;
        }

        return new mapEntities.ValetudoMap({
            metaData: metaData,
            size: {
                x: MAX_X,
                y: MAX_Y
            },
            pixelSize: parsedHeader.pixelSize,
            layers: layers,
            entities: entities
        });
    }
This expects the already inflated buffer.
Since there are no magic bytes, there's no real way to do sanity checking which is sad.

:(

@param {Buffer} buf
@param {MapDataType} [type]
@returns {Promise<null|import("../../entities/map/ValetudoMap")>}