def FromBinary(cls, record_data, record_count=1):
        """Create an UpdateRecord subclass from binary record data.

        This should be called with a binary record blob (including the record
        type header) and it will return the best record class match that it
        can find for that record.

        Args:
            record_data (bytearray): The raw record data that we wish to parse
                into an UpdateRecord subclass including its 4 byte record header.
            record_count (int): If we are asked to create a record from multiple
                records, the record_data will be passed to the record subclass
                with headers intact since there will be more than one header.

        Raises:
            ArgumentError: If the record_data is malformed and cannot be parsed.
            DataError: If there is no matching record type registered.

        Returns:
            UpdateRecord: A subclass of UpdateRecord based on what record
                type matches the best.
        """

        # Make sure any external record types are registered
        cls.LoadPlugins()

        if len(record_data) < UpdateRecord.HEADER_LENGTH:
            raise ArgumentError("Record data is too short to contain a record header",
                                length=len(record_data), header_length=UpdateRecord.HEADER_LENGTH)

        total_length, record_type = struct.unpack_from("<LB3x", record_data)

        if record_count == 1 and len(record_data) != total_length:
            raise ArgumentError("Record data is corrupt, embedded length does not agree with actual length",
                                length=len(record_data), embedded_length=total_length)

        record_classes = UpdateRecord.KNOWN_CLASSES.get(record_type, [])
        if len(record_classes) == 0:
            raise DataError("No matching record type found for record", record_type=record_type,
                            known_types=[x for x in UpdateRecord.KNOWN_CLASSES])

        best_match = MatchQuality.NoMatch
        matching_class = None

        for record_class in record_classes:
            match_data = record_data[UpdateRecord.HEADER_LENGTH:]

            if record_count > 1:
                match_data = record_data

            quality = record_class.MatchQuality(match_data, record_count)

            if quality > best_match:
                best_match = quality
                matching_class = record_class

        if best_match == MatchQuality.DeferMatch:
            raise DeferMatching(matching_class)
        elif best_match == MatchQuality.PartialMatch:
            raise DeferMatching(matching_class, matching_class.FromBinary(match_data, record_count))

        if matching_class is None:
            raise DataError("Record type found but no specific class reported a match",
                            record_type=record_type, considered_classes=record_classes)

        return matching_class.FromBinary(match_data, record_count)
Create an UpdateRecord subclass from binary record data.

        This should be called with a binary record blob (including the record
        type header) and it will return the best record class match that it
        can find for that record.

        Args:
            record_data (bytearray): The raw record data that we wish to parse
                into an UpdateRecord subclass including its 4 byte record header.
            record_count (int): If we are asked to create a record from multiple
                records, the record_data will be passed to the record subclass
                with headers intact since there will be more than one header.

        Raises:
            ArgumentError: If the record_data is malformed and cannot be parsed.
            DataError: If there is no matching record type registered.

        Returns:
            UpdateRecord: A subclass of UpdateRecord based on what record
                type matches the best.