mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-04-29 14:59:26 -04:00
278 lines
4.7 KiB
C++
278 lines
4.7 KiB
C++
#include "ndef.h"
|
|
|
|
#include <cassert>
|
|
|
|
namespace ndef
|
|
{
|
|
|
|
Record::Record()
|
|
: mFlags(0), mTNF(NDEF_TNF_EMPTY)
|
|
{
|
|
}
|
|
|
|
Record::~Record()
|
|
{
|
|
}
|
|
|
|
std::optional<Record> Record::FromStream(Stream& stream)
|
|
{
|
|
Record rec;
|
|
|
|
// Read record header
|
|
uint8_t recHdr;
|
|
stream >> recHdr;
|
|
rec.mFlags = recHdr & ~NDEF_TNF_MASK;
|
|
rec.mTNF = static_cast<TypeNameFormat>(recHdr & NDEF_TNF_MASK);
|
|
|
|
// Type length
|
|
uint8_t typeLen;
|
|
stream >> typeLen;
|
|
|
|
// Payload length;
|
|
uint32_t payloadLen;
|
|
if (recHdr & NDEF_SR)
|
|
{
|
|
uint8_t len;
|
|
stream >> len;
|
|
payloadLen = len;
|
|
}
|
|
else
|
|
{
|
|
stream >> payloadLen;
|
|
}
|
|
|
|
// Some sane limits for the payload size
|
|
if (payloadLen > 2 * 1024 * 1024)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
// ID length
|
|
uint8_t idLen = 0;
|
|
if (recHdr & NDEF_IL)
|
|
{
|
|
stream >> idLen;
|
|
}
|
|
|
|
// Make sure we didn't read past the end of the stream yet
|
|
if (stream.GetError() != Stream::ERROR_OK)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
// Type
|
|
rec.mType.resize(typeLen);
|
|
stream.Read(rec.mType);
|
|
|
|
// ID
|
|
rec.mID.resize(idLen);
|
|
stream.Read(rec.mID);
|
|
|
|
// Payload
|
|
rec.mPayload.resize(payloadLen);
|
|
stream.Read(rec.mPayload);
|
|
|
|
// Make sure we didn't read past the end of the stream again
|
|
if (stream.GetError() != Stream::ERROR_OK)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return rec;
|
|
}
|
|
|
|
std::vector<std::byte> Record::ToBytes(uint8_t flags) const
|
|
{
|
|
std::vector<std::byte> bytes;
|
|
VectorStream stream(bytes, std::endian::big);
|
|
|
|
// Combine flags (clear message begin and end flags)
|
|
std::uint8_t finalFlags = mFlags & ~(NDEF_MB | NDEF_ME);
|
|
finalFlags |= flags;
|
|
|
|
// Write flags + tnf
|
|
stream << std::uint8_t(finalFlags | std::uint8_t(mTNF));
|
|
|
|
// Type length
|
|
stream << std::uint8_t(mType.size());
|
|
|
|
// Payload length
|
|
if (IsShort())
|
|
{
|
|
stream << std::uint8_t(mPayload.size());
|
|
}
|
|
else
|
|
{
|
|
stream << std::uint32_t(mPayload.size());
|
|
}
|
|
|
|
// ID length
|
|
if (mFlags & NDEF_IL)
|
|
{
|
|
stream << std::uint8_t(mID.size());
|
|
}
|
|
|
|
// Type
|
|
stream.Write(mType);
|
|
|
|
// ID
|
|
stream.Write(mID);
|
|
|
|
// Payload
|
|
stream.Write(mPayload);
|
|
|
|
return bytes;
|
|
}
|
|
|
|
Record::TypeNameFormat Record::GetTNF() const
|
|
{
|
|
return mTNF;
|
|
}
|
|
|
|
const std::vector<std::byte>& Record::GetID() const
|
|
{
|
|
return mID;
|
|
}
|
|
|
|
const std::vector<std::byte>& Record::GetType() const
|
|
{
|
|
return mType;
|
|
}
|
|
|
|
const std::vector<std::byte>& Record::GetPayload() const
|
|
{
|
|
return mPayload;
|
|
}
|
|
|
|
void Record::SetTNF(TypeNameFormat tnf)
|
|
{
|
|
mTNF = tnf;
|
|
}
|
|
|
|
void Record::SetID(const std::span<const std::byte>& id)
|
|
{
|
|
cemu_assert(id.size() < 0x100);
|
|
|
|
if (id.size() > 0)
|
|
{
|
|
mFlags |= NDEF_IL;
|
|
}
|
|
else
|
|
{
|
|
mFlags &= ~NDEF_IL;
|
|
}
|
|
|
|
mID.assign(id.begin(), id.end());
|
|
}
|
|
|
|
void Record::SetType(const std::span<const std::byte>& type)
|
|
{
|
|
cemu_assert(type.size() < 0x100);
|
|
|
|
mType.assign(type.begin(), type.end());
|
|
}
|
|
|
|
void Record::SetPayload(const std::span<const std::byte>& payload)
|
|
{
|
|
// Update short record flag
|
|
if (payload.size() < 0xff)
|
|
{
|
|
mFlags |= NDEF_SR;
|
|
}
|
|
else
|
|
{
|
|
mFlags &= ~NDEF_SR;
|
|
}
|
|
|
|
mPayload.assign(payload.begin(), payload.end());
|
|
}
|
|
|
|
bool Record::IsLast() const
|
|
{
|
|
return mFlags & NDEF_ME;
|
|
}
|
|
|
|
bool Record::IsShort() const
|
|
{
|
|
return mFlags & NDEF_SR;
|
|
}
|
|
|
|
Message::Message()
|
|
{
|
|
}
|
|
|
|
Message::~Message()
|
|
{
|
|
}
|
|
|
|
std::optional<Message> Message::FromBytes(const std::span<const std::byte>& data)
|
|
{
|
|
Message msg;
|
|
SpanStream stream(data, std::endian::big);
|
|
|
|
while (stream.GetRemaining() > 0)
|
|
{
|
|
std::optional<Record> rec = Record::FromStream(stream);
|
|
if (!rec)
|
|
{
|
|
cemuLog_log(LogType::Force, "Warning: Failed to parse NDEF Record #{}."
|
|
"Ignoring the remaining {} bytes in NDEF message", msg.mRecords.size(), stream.GetRemaining());
|
|
break;
|
|
}
|
|
|
|
msg.mRecords.emplace_back(*rec);
|
|
|
|
if ((*rec).IsLast() && stream.GetRemaining() > 0)
|
|
{
|
|
cemuLog_log(LogType::Force, "Warning: Ignoring {} bytes in NDEF message", stream.GetRemaining());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (msg.mRecords.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (!msg.mRecords.back().IsLast())
|
|
{
|
|
cemuLog_log(LogType::Force, "Error: NDEF message missing end record");
|
|
return {};
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
std::vector<std::byte> Message::ToBytes() const
|
|
{
|
|
std::vector<std::byte> bytes;
|
|
|
|
for (std::size_t i = 0; i < mRecords.size(); i++)
|
|
{
|
|
std::uint8_t flags = 0;
|
|
|
|
// Add message begin flag to first record
|
|
if (i == 0)
|
|
{
|
|
flags |= Record::NDEF_MB;
|
|
}
|
|
|
|
// Add message end flag to last record
|
|
if (i == mRecords.size() - 1)
|
|
{
|
|
flags |= Record::NDEF_ME;
|
|
}
|
|
|
|
std::vector<std::byte> recordBytes = mRecords[i].ToBytes(flags);
|
|
bytes.insert(bytes.end(), recordBytes.begin(), recordBytes.end());
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
void Message::append(const Record& r)
|
|
{
|
|
mRecords.push_back(r);
|
|
}
|
|
|
|
} // namespace ndef
|