/// <summary> /// Create the payload based on the data stored in the properties. Usually called /// automatically when changing properties, but you might need to call this manually /// if you modify the list of alternative carrier records directly without using /// accessor method provided by this class. /// </summary> /// <exception cref="NdefException">Thrown if unable to assemble the payload. /// The exception message contains further details about the issue.</exception> public void AssemblePayload() { if (_handoverVersion == null) { throw new NdefException(NdefExceptionMessages.ExHandoverInvalidVersion); } // Convert child records to message var childMsg = new NdefMessage(); if (_handoverAlternativeCarrierRecords != null) { childMsg.AddRange(_handoverAlternativeCarrierRecords); } if (_handoverErrorRecord != null) { childMsg.Add(_handoverErrorRecord); } var childBytes = childMsg.ToByteArray(); var newPayload = new byte[childBytes.Length + 1]; // Frist byte: handover version newPayload[0] = _handoverVersion.Version; // Rest of the payload: child message containing Alternative Carrier records + Error record Array.Copy(childBytes, 0, newPayload, 1, childBytes.Length); _payload = newPayload; }
/// <summary> /// Reverse function to parseRecords() - this one takes /// the information stored in the individual record instances and assembles /// it into the payload of the base class. /// </summary> /// <remarks> /// As the URI is mandatory, the payload will not be assembled /// if no URI is defined. /// </remarks> /// <returns>Whether assembling the payload was successful.</returns> private bool AssemblePayload() { // Uri is mandatory - don't assemble the payload if it's not set if (RecordUri == null) { return(false); } // URI (mandatory) var message = new NdefMessage { RecordUri }; // Title(s) (optional) if (Titles != null && Titles.Count > 0) { message.AddRange(Titles); } // Action (optional) if (ActionInUse()) { message.Add(_recordAction); } // Size (optional) if (SizeInUse()) { message.Add(_recordSize); } // Mime Type (optional) if (MimeTypeInUse()) { message.Add(_recordMimeType); } // Image (optional) if (ImageInUse()) { message.Add(_recordImage); } SetPayloadAndParse(message.ToByteArray(), false); return(true); }
/// <summary> /// Returns the NDEF message parsed from the contents of <paramref name="message"/>. /// </summary> /// <remarks> /// The <paramref name="message"/> parameter is interpreted as the raw message format /// defined in the NFC Forum specifications. /// </remarks> /// <param name="message">Raw byte array containing the NDEF message, which consists /// of 0 or more NDEF records.</param> /// <exception cref="NdefException">Thrown if there is an error parsing the NDEF /// message out of the byte array.</exception> /// <returns>If parsing was successful, the NDEF message containing 0 or more NDEF /// records.</returns> public static NdefMessage FromByteArray(byte[] message) { var result = new NdefMessage(); var seenMessageBegin = false; var seenMessageEnd = false; var partialChunk = new MemoryStream(); var record = new NdefRecord(); uint i = 0; while (i < message.Length) { //Debug.WriteLine("Parsing byte[] to NDEF message. New record starts at {0}", i); // Parse flags out of NDEF message header // The MB flag is a 1-bit field that when set indicates the start of an NDEF message. bool messageBegin = (message[i] & 0x80) != 0; // The ME flag is a 1-bit field that when set indicates the end of an NDEF message. // Note, that in case of a chunked payload, the ME flag is set only in the terminating record chunk of that chunked payload. bool messageEnd = (message[i] & 0x40) != 0; // The CF flag is a 1-bit field indicating that this is either the first record chunk or a middle record chunk of a chunked payload. bool cf = (message[i] & 0x20) != 0; // The SR flag is a 1-bit field indicating, if set, that the PAYLOAD_LENGTH field is a single octet. bool sr = (message[i] & 0x10) != 0; // The IL flag is a 1-bit field indicating, if set, that the ID_LENGTH field is present in the header as a single octet. // If the IL flag is zero, the ID_LENGTH field is omitted from the record header and the ID field is also omitted from the record. bool il = (message[i] & 0x08) != 0; var typeNameFormat = (NdefRecord.TypeNameFormatType)(message[i] & 0x07); //Debug.WriteLine("ShortRecord: " + (sr ? "yes" : "no")); //Debug.WriteLine("Id Length present: " + (il ? "yes" : "no")); if (messageBegin && seenMessageBegin) { throw new NdefException(NdefExceptionMessages.ExMessageBeginLate); } else if (!messageBegin && !seenMessageBegin) { throw new NdefException(NdefExceptionMessages.ExMessageBeginMissing); } else if (messageBegin && !seenMessageBegin) { seenMessageBegin = true; } if (messageEnd && seenMessageEnd) { throw new NdefException(NdefExceptionMessages.ExMessageEndLate); } else if (messageEnd && !seenMessageEnd) { seenMessageEnd = true; } if (cf && (typeNameFormat != NdefRecord.TypeNameFormatType.Unchanged) && partialChunk.Length > 0) { throw new NdefException(NdefExceptionMessages.ExMessagePartialChunk); } // Header length int headerLength = 1; headerLength += (sr) ? 1 : 4; headerLength += (il) ? 1 : 0; if (i + headerLength >= message.Length) { throw new NdefException(NdefExceptionMessages.ExMessageUnexpectedEnd); } // Type length byte typeLength = message[++i]; if ((typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) && (typeLength != 0)) { throw new NdefException(NdefExceptionMessages.ExMessageInvalidChunkedType); } // Payload length (short record?) uint payloadLength; if (sr) { // Short record - payload length is a single octet payloadLength = message[++i]; } else { // No short record - payload length is four octets representing a 32 bit unsigned integer (MSB-first) payloadLength = (uint)((message[++i]) << 24); payloadLength |= (uint)((message[++i]) << 16); payloadLength |= (uint)((message[++i]) << 8); payloadLength |= (uint)((message[++i]) << 0); } // ID length byte idLength; idLength = (byte)(il ? message[++i] : 0); // Total length of content (= type + payload + ID) uint contentLength = typeLength + payloadLength + idLength; if (i + contentLength >= message.Length) { throw new NdefException(NdefExceptionMessages.ExMessageUnexpectedEnd); } if ((typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) && (idLength != 0)) { throw new NdefException(NdefExceptionMessages.ExMessageInvalidChunkedId); } if (typeNameFormat != NdefRecord.TypeNameFormatType.Unchanged) { record.TypeNameFormat = typeNameFormat; } // Read type if (typeLength > 0) { record.Type = new byte[typeLength]; Array.Copy(message, (int)(++i), record.Type, 0, typeLength); i += (uint)typeLength - 1; } // Read ID if (idLength > 0) { record.Id = new byte[idLength]; Array.Copy(message, (int)(++i), record.Id, 0, idLength); i += (uint)idLength - 1; } // Read payload if (payloadLength > 0) { var payload = new byte[payloadLength]; Array.Copy(message, (int)(++i), payload, 0, (int)payloadLength); if (cf) { // chunked payload, except last partialChunk.Write(payload, 0, payload.Length); } else if (typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) { // last chunk of chunked payload partialChunk.Write(payload, 0, payload.Length); record.Payload = partialChunk.ToArray(); } else { // non-chunked payload record.Payload = payload; } i += payloadLength - 1; } if (!cf) { // Add record to the message and create a new record for the next loop iteration result.Add(record); record = new NdefRecord(); } if (!cf && seenMessageEnd) { break; } // move to start of next record ++i; } if (!seenMessageBegin && !seenMessageEnd) { throw new NdefException(NdefExceptionMessages.ExMessageNoBeginOrEnd); } return(result); }
/// <summary> /// Returns the NDEF message parsed from the contents of <paramref name="message"/>. /// </summary> /// <remarks> /// The <paramref name="message"/> parameter is interpreted as the raw message format /// defined in the NFC Forum specifications. /// </remarks> /// <param name="message">Raw byte array containing the NDEF message, which consists /// of 0 or more NDEF records.</param> /// <exception cref="NdefException">Thrown if there is an error parsing the NDEF /// message out of the byte array.</exception> /// <returns>If parsing was successful, the NDEF message containing 0 or more NDEF /// records.</returns> public static NdefMessage FromByteArray(byte[] message) { var result = new NdefMessage(); var seenMessageBegin = false; var seenMessageEnd = false; var partialChunk = new MemoryStream(); var record = new NdefRecord(); uint i = 0; while (i < message.Length) { //Debug.WriteLine("Parsing byte[] to NDEF message. New record starts at {0}", i); // Parse flags out of NDEF message header bool messageBegin = (message[i] & 0x80) != 0; bool messageEnd = (message[i] & 0x40) != 0; bool cf = (message[i] & 0x20) != 0; bool sr = (message[i] & 0x10) != 0; bool il = (message[i] & 0x08) != 0; var typeNameFormat = (NdefRecord.TypeNameFormatType)(message[i] & 0x07); Debug.WriteLine("ShortRecord: " + (sr ? "yes" : "no")); Debug.WriteLine("Id Length present: " + (il ? "yes" : "no")); if (messageBegin && seenMessageBegin) { throw new NdefException("Message Begin Late"); } else if (!messageBegin && !seenMessageBegin) { throw new NdefException("Begin Missing"); } else if (messageBegin && !seenMessageBegin) { seenMessageBegin = true; } if (messageEnd && seenMessageEnd) { throw new NdefException("End Late"); } else if (messageEnd && !seenMessageEnd) { seenMessageEnd = true; } if (cf && (typeNameFormat != NdefRecord.TypeNameFormatType.Unchanged) && partialChunk.Length > 0) { throw new NdefException("Partial Chunk"); } int headerLength = 1; headerLength += (sr) ? 1 : 4; headerLength += (il) ? 1 : 0; if (i + headerLength >= message.Length) { throw new NdefException("NdefExceptionMessages.ExMessageUnexpectedEnd"); } byte typeLength = message[++i]; if ((typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) && (typeLength != 0)) { throw new NdefException("Invalid Chunk"); } uint payloadLength; if (sr) { // Short record - payload length is a single octet payloadLength = message[++i]; } else { // No short record - payload length is four octets representing a 32 bit unsigned integer (MSB-first) payloadLength = (uint)((message[++i]) << 24); payloadLength |= (uint)((message[++i]) << 16); payloadLength |= (uint)((message[++i]) << 8); payloadLength |= (uint)((message[++i]) << 0); } //Debug.WriteLine("Payload length: " + payloadLength); byte idLength; idLength = (byte)(il ? message[++i] : 0); uint contentLength = typeLength + payloadLength + idLength; if (i + contentLength >= message.Length) { throw new NdefException("Unexpected End"); } //Debug.WriteLine("Content length: {0} (Start at: {1})", contentLength, i); if ((typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) && (idLength != 0)) { throw new NdefException("Invalid Chunk Id"); } if (typeNameFormat != NdefRecord.TypeNameFormatType.Unchanged) { record.TypeNameFormat = typeNameFormat; } if (typeLength > 0) { record.Type = new byte[typeLength]; Array.Copy(message, (int)(++i), record.Type, 0, typeLength); i += (uint)typeLength - 1; } if (idLength > 0) { record.Id = new byte[idLength]; Array.Copy(message, (int)(++i), record.Id, 0, idLength); i += (uint)idLength - 1; } if (payloadLength > 0) { var payload = new byte[payloadLength]; Array.Copy(message, (int)(++i), payload, 0, (int)payloadLength); if (cf) { // chunked payload, except last partialChunk.Write(payload, 0, payload.Length); } else if (typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) { // last chunk of chunked payload partialChunk.Write(payload, 0, payload.Length); record.Payload = partialChunk.ToArray(); } else { // non-chunked payload record.Payload = payload; } i += payloadLength - 1; } if (!cf) { result.Add(record); record = new NdefRecord(); } if (!cf && seenMessageEnd) { break; } // move to start of next record ++i; } if (!seenMessageBegin && !seenMessageEnd) { throw new NdefException("No Begin Or End"); } return(result); }