607 lines
18 KiB
C
607 lines
18 KiB
C
|
#define NETWORKINTID_INVALID -1
|
||
|
|
||
|
#define DEFAULT_STREAMING_ACN_PORT 5568
|
||
|
|
||
|
#define IP_ADDRESS_BYTES 16
|
||
|
#define STARTCODE_DMX 0
|
||
|
|
||
|
/*
|
||
|
* a description of the address space being used
|
||
|
*/
|
||
|
#define PREAMBLE_SIZE_ADDR 0
|
||
|
#define POSTAMBLE_SIZE_ADDR 2
|
||
|
#define ACN_IDENTIFIER_ADDR 4
|
||
|
#define ROOT_FLAGS_AND_LENGTH_ADDR 16
|
||
|
#define ROOT_VECTOR_ADDR 18
|
||
|
#define CID_ADDR 22
|
||
|
#define FRAMING_FLAGS_AND_LENGTH_ADDR 38
|
||
|
#define FRAMING_VECTOR_ADDR 40
|
||
|
#define SOURCE_NAME_ADDR 44
|
||
|
#define PRIORITY_ADDR 108
|
||
|
#define RESERVED_ADDR 109
|
||
|
#define SEQ_NUM_ADDR 111
|
||
|
#define OPTIONS_ADDR 112
|
||
|
#define UNIVERSE_ADDR 113
|
||
|
#define DMP_FLAGS_AND_LENGTH_ADDR 115
|
||
|
#define DMP_VECTOR_ADDR 117
|
||
|
#define DMP_ADDRESS_AND_DATA_ADDR 118
|
||
|
#define FIRST_PROPERTY_ADDRESS_ADDR 119
|
||
|
#define ADDRESS_INC_ADDR 121
|
||
|
#define PROP_COUNT_ADDR 123
|
||
|
#define START_CODE_ADDR 125
|
||
|
#define PROP_VALUES_ADDR (START_CODE_ADDR + 1)
|
||
|
|
||
|
/*
|
||
|
* common sizes
|
||
|
*/
|
||
|
#define STREAM_HEADER_SIZE 126
|
||
|
#define STREAM_BODY_SIZE 512
|
||
|
|
||
|
#define SOURCE_NAME_SIZE 64
|
||
|
#define RLP_PREAMBLE_SIZE 16
|
||
|
#define RLP_POSTAMBLE_SIZE 0
|
||
|
#define ACN_IDENTIFIER_SIZE 12
|
||
|
|
||
|
/*
|
||
|
* data definitions
|
||
|
*/
|
||
|
#define ACN_IDENTIFIER "ASC-E1.17\0\0\0"
|
||
|
#define ROOT_VECTOR 4
|
||
|
#define FRAMING_VECTOR 2
|
||
|
#define DMP_VECTOR 2
|
||
|
#define ADDRESS_AND_DATA_FORMAT 0xa1
|
||
|
#define ADDRESS_INC 1
|
||
|
#define DMP_FIRST_PROPERTY_ADDRESS_FORCE 0
|
||
|
#define RESERVED_VALUE 0
|
||
|
|
||
|
//for support of the early draft
|
||
|
#define DRAFT_STREAM_HEADER_SIZE 90
|
||
|
#define DRAFT_SOURCE_NAME_SIZE 32
|
||
|
|
||
|
//for support of the early draft
|
||
|
#define DRAFT_ROOT_VECTOR 3
|
||
|
|
||
|
const u32 VHD_MAXFLAGBYTES = 7; //The maximum amount of bytes used to pack the flags, len, and vector
|
||
|
const u32 VHD_MAXLEN = 0x0fffff; //The maximum packet length is 20 bytes long
|
||
|
const u32 VHD_MAXMINLENGTH = 4095; //The highest length that will fit in the "smallest" length pack
|
||
|
|
||
|
//Defines for the VHD flags
|
||
|
const u8 VHD_L_FLAG = 0x80;
|
||
|
const u8 VHD_V_FLAG = 0x40;
|
||
|
const u8 VHD_H_FLAG = 0x20;
|
||
|
const u8 VHD_D_FLAG = 0x10;
|
||
|
|
||
|
#define CID_Bytes 16
|
||
|
struct cid
|
||
|
{
|
||
|
u8 Bytes[CID_Bytes];
|
||
|
};
|
||
|
|
||
|
struct sacn_universe
|
||
|
{
|
||
|
s16 Universe;
|
||
|
|
||
|
u8* StartPositionInSendBuffer;
|
||
|
s32 SizeInSendBuffer;
|
||
|
s32 OffsetInSendBuffer;
|
||
|
|
||
|
s32 BeginPixelCopyFromOffset;
|
||
|
|
||
|
platform_network_address_handle SendAddress;
|
||
|
};
|
||
|
|
||
|
struct sacn_send_buffer
|
||
|
{
|
||
|
u8* Memory;
|
||
|
s32 Size;
|
||
|
sacn_send_buffer* Next;
|
||
|
};
|
||
|
|
||
|
struct sacn_universe_buffer
|
||
|
{
|
||
|
sacn_universe* Universes;
|
||
|
s32 Used;
|
||
|
s32 Max;
|
||
|
sacn_universe_buffer* Next;
|
||
|
};
|
||
|
|
||
|
struct streaming_acn
|
||
|
{
|
||
|
memory_arena Memory;
|
||
|
|
||
|
// These get created and freed together
|
||
|
sacn_universe_buffer* UniverseBuffer;
|
||
|
sacn_send_buffer* SendBuffer;
|
||
|
|
||
|
platform_socket_handle SendSocket;
|
||
|
cid CID;
|
||
|
|
||
|
s32 SequenceIterator;
|
||
|
};
|
||
|
|
||
|
|
||
|
// SACN Data Header Functions
|
||
|
internal void InitStreamHeader (u8* Buffer, s32 BufferSize, u16 SlotCount, u8 StartCode, u16 Universe, u8 Priority, u16 Reserved, u8 Options, const char* SourceName, cid CID);
|
||
|
internal void SetStreamHeaderSequence_ (u8* Buffer, u8 Sequence, b32 Draft);
|
||
|
internal void VHD_PackFlags_(u8* Buffer, b32 InheritVec, b32 InheritHead, b32 InheritData);
|
||
|
internal u8* VHD_PackLength_(u8* Buffer, u32 Length, b32 IncludeLength);
|
||
|
internal cid StringToCID_ (const char* String);
|
||
|
|
||
|
#define CalculateSendBufferSize(UniverseCount) ((UniverseCount * (STREAM_HEADER_SIZE + STREAM_BODY_SIZE)) + sizeof(sacn_send_buffer))
|
||
|
#define CalculateUniverseBufferSize(UniverseCount) ((UniverseCount * sizeof(sacn_universe)) + sizeof(sacn_universe_buffer))
|
||
|
|
||
|
// Utility
|
||
|
|
||
|
struct sacn_pixel
|
||
|
{
|
||
|
u8 R;
|
||
|
u8 G;
|
||
|
u8 B;
|
||
|
};
|
||
|
|
||
|
//
|
||
|
|
||
|
internal sacn_universe*
|
||
|
SACNGetUniverse (s32 UniverseNumber, streaming_acn* SACN)
|
||
|
{
|
||
|
sacn_universe* Result = 0;
|
||
|
|
||
|
sacn_universe_buffer* Header = SACN->UniverseBuffer;
|
||
|
while (Header)
|
||
|
{
|
||
|
sacn_universe* Cursor = Header->Universes;
|
||
|
for (s32 i = 0; i < Header->Used; i++)
|
||
|
{
|
||
|
if (Cursor->Universe == UniverseNumber)
|
||
|
{
|
||
|
Result = Cursor;
|
||
|
break;
|
||
|
}
|
||
|
Cursor++;
|
||
|
}
|
||
|
Header = Header->Next;
|
||
|
}
|
||
|
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
SACNPushSendBufferOnList (sacn_send_buffer* ListHead, sacn_send_buffer* NewBuffer)
|
||
|
{
|
||
|
if (ListHead->Next)
|
||
|
{
|
||
|
SACNPushSendBufferOnList(ListHead->Next, NewBuffer);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ListHead->Next = NewBuffer;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal sacn_send_buffer*
|
||
|
SACNRemoveSendBufferFromList (sacn_send_buffer* List, sacn_send_buffer* Entry)
|
||
|
{
|
||
|
sacn_send_buffer* ListHead = 0;
|
||
|
if (List != Entry && List->Next)
|
||
|
{
|
||
|
ListHead = SACNRemoveSendBufferFromList(List->Next, Entry);
|
||
|
}
|
||
|
else if (List == Entry)
|
||
|
{
|
||
|
ListHead = Entry->Next;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// NOTE(Peter): Trying to remove an entry from a list that doesn't contain it
|
||
|
InvalidCodePath;
|
||
|
}
|
||
|
return ListHead;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
SACNPushUniverseBufferOnList (sacn_universe_buffer* ListHead, sacn_universe_buffer* NewBuffer)
|
||
|
{
|
||
|
if (ListHead->Next)
|
||
|
{
|
||
|
SACNPushUniverseBufferOnList(ListHead->Next, NewBuffer);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ListHead->Next = NewBuffer;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal sacn_universe_buffer*
|
||
|
SACNRemoveUniverseBufferFromList (sacn_universe_buffer* List, sacn_universe_buffer* Entry)
|
||
|
{
|
||
|
sacn_universe_buffer* ListHead = 0;
|
||
|
if (List != Entry && List->Next)
|
||
|
{
|
||
|
ListHead = SACNRemoveUniverseBufferFromList(List->Next, Entry);
|
||
|
}
|
||
|
else if (List == Entry)
|
||
|
{
|
||
|
ListHead = Entry->Next;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// NOTE(Peter): Trying to remove an entry from a list that doesn't contain it
|
||
|
InvalidCodePath;
|
||
|
}
|
||
|
return ListHead;
|
||
|
}
|
||
|
|
||
|
struct sacn_add_universes_result
|
||
|
{
|
||
|
sacn_send_buffer* NewSendBuffer;
|
||
|
sacn_universe_buffer* NewUniverseBuffer;
|
||
|
};
|
||
|
internal sacn_add_universes_result
|
||
|
SACNAddUniverses(s32* Universes, s32 UniversesLength, streaming_acn* SACN, context Context)
|
||
|
{
|
||
|
sacn_add_universes_result Result = {};
|
||
|
|
||
|
// Determine which universes are already registered and not to be readded.
|
||
|
// NOTE(Peter): This might create funky behaviour if two sculptures start sending data to the same universe
|
||
|
// but I'm not sure its incorrect behavior. I think, eventually, we will want to spit out a report from
|
||
|
// this function that says what universes were duplicated. We might want to display this information to the user
|
||
|
// in a way that they don't have to exit out of every single time they load the software. Not sure
|
||
|
s32 UniversesToAdd = 0;
|
||
|
for (s32 i = 0; i < UniversesLength; i++)
|
||
|
{
|
||
|
sacn_universe* UniverseExists = SACNGetUniverse(Universes[i], SACN);
|
||
|
if (UniverseExists)
|
||
|
{
|
||
|
Universes[i] = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UniversesToAdd++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Push On New Send and Universe Buffers
|
||
|
s32 SendBufferSize = CalculateSendBufferSize(UniversesToAdd);
|
||
|
u8* SendBufferMemory = PushArray(&SACN->Memory, u8, SendBufferSize);
|
||
|
sacn_send_buffer* SendBufferHeader = (sacn_send_buffer*)SendBufferMemory;
|
||
|
SendBufferHeader->Memory = (u8*)(SendBufferHeader + 1);
|
||
|
SendBufferHeader->Size = SendBufferSize - sizeof(sacn_send_buffer);
|
||
|
SendBufferHeader->Next = 0;
|
||
|
if (SACN->SendBuffer)
|
||
|
{
|
||
|
SACNPushSendBufferOnList(SACN->SendBuffer, SendBufferHeader);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SACN->SendBuffer = SendBufferHeader;
|
||
|
}
|
||
|
|
||
|
s32 UniverseBufferSize = CalculateUniverseBufferSize(UniversesToAdd);
|
||
|
u8* UniverseBufferMemory = PushArray(&SACN->Memory, u8, UniverseBufferSize);
|
||
|
sacn_universe_buffer* UniverseBufferHeader = (sacn_universe_buffer*)UniverseBufferMemory;
|
||
|
UniverseBufferHeader->Universes = (sacn_universe*)(UniverseBufferHeader + 1);
|
||
|
UniverseBufferHeader->Used = 0;
|
||
|
UniverseBufferHeader->Max = UniversesToAdd;
|
||
|
if (SACN->UniverseBuffer)
|
||
|
{
|
||
|
SACNPushUniverseBufferOnList(SACN->UniverseBuffer, UniverseBufferHeader);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SACN->UniverseBuffer = UniverseBufferHeader;
|
||
|
}
|
||
|
|
||
|
// Add each of the valid universes
|
||
|
for (s32 j = 0; j < UniversesLength; j++)
|
||
|
{
|
||
|
if (Universes[j] >= 0)
|
||
|
{
|
||
|
Assert(UniverseBufferHeader->Used < UniverseBufferHeader->Max);
|
||
|
s32 Index = UniverseBufferHeader->Used++;
|
||
|
s32 UniverseID = Universes[j];
|
||
|
|
||
|
UniverseBufferHeader->Universes[Index].Universe = UniverseID;
|
||
|
UniverseBufferHeader->Universes[Index].SizeInSendBuffer = STREAM_HEADER_SIZE + STREAM_BODY_SIZE;
|
||
|
UniverseBufferHeader->Universes[Index].BeginPixelCopyFromOffset = -1;
|
||
|
|
||
|
// Configure how the universe looks into the pixel color buffer
|
||
|
s32 SendBufferOffset = (Index * (STREAM_HEADER_SIZE + STREAM_BODY_SIZE));
|
||
|
u8* SendBufferStartPosition = SendBufferHeader->Memory + SendBufferOffset;
|
||
|
UniverseBufferHeader->Universes[Index].OffsetInSendBuffer = SendBufferOffset;
|
||
|
UniverseBufferHeader->Universes[Index].StartPositionInSendBuffer = SendBufferStartPosition;
|
||
|
|
||
|
// Set up the Send Address
|
||
|
u8 MulticastAddressBuffer[IP_ADDRESS_BYTES];
|
||
|
GSMemSet(MulticastAddressBuffer, 0, IP_ADDRESS_BYTES);
|
||
|
MulticastAddressBuffer[12] = 239;
|
||
|
MulticastAddressBuffer[13] = 255;
|
||
|
PackB2(MulticastAddressBuffer + 14, UniverseID);
|
||
|
u_long V4Address = (u_long)UpackB4(MulticastAddressBuffer + IP_ADDRESS_BYTES - sizeof(u32));
|
||
|
|
||
|
GSMemSet(&UniverseBufferHeader->Universes[Index].SendAddress, 0, sizeof(sockaddr_in));
|
||
|
UniverseBufferHeader->Universes[Index].SendAddress = Context.PlatformGetSendAddress(
|
||
|
AF_INET,
|
||
|
HostToNetU16(DEFAULT_STREAMING_ACN_PORT),
|
||
|
HostToNetU32(V4Address));
|
||
|
|
||
|
#if 0 // Old Net Code
|
||
|
UniverseBufferHeader->Universes[Index].SendAddress.sin_family = AF_INET;
|
||
|
UniverseBufferHeader->Universes[Index].SendAddress.sin_port = HostToNetU16(DEFAULT_STREAMING_ACN_PORT);
|
||
|
UniverseBufferHeader->Universes[Index].SendAddress.sin_addr.s_addr = HostToNetU32(V4Address);
|
||
|
#endif
|
||
|
|
||
|
s32 SlotCount = 512;
|
||
|
InitStreamHeader(UniverseBufferHeader->Universes[Index].StartPositionInSendBuffer,
|
||
|
UniverseBufferHeader->Universes[Index].SizeInSendBuffer,
|
||
|
SlotCount,
|
||
|
STARTCODE_DMX,
|
||
|
UniverseID,
|
||
|
0,
|
||
|
0, // Reserved
|
||
|
0, // Options
|
||
|
"Source 1",
|
||
|
SACN->CID
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Result.NewUniverseBuffer = UniverseBufferHeader;
|
||
|
Result.NewSendBuffer= SendBufferHeader;
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
SACNRemoveUniverseAndSendBuffer(streaming_acn* SACN, sacn_universe_buffer* Universes, sacn_send_buffer* SendBuffer)
|
||
|
{
|
||
|
SACN->UniverseBuffer = SACNRemoveUniverseBufferFromList(SACN->UniverseBuffer, Universes);
|
||
|
SACN->SendBuffer = SACNRemoveSendBufferFromList(SACN->SendBuffer, SendBuffer);
|
||
|
}
|
||
|
|
||
|
internal streaming_acn
|
||
|
InitializeSACN (platform_alloc* PlatformAlloc, context Context)
|
||
|
{
|
||
|
streaming_acn SACN = {};
|
||
|
|
||
|
InitMemoryArena(&SACN.Memory, 0, 0, PlatformAlloc);
|
||
|
|
||
|
SACN.SendSocket = Context.PlatformGetSocketHandle(AF_INET, SOCK_DGRAM, 0);
|
||
|
int Multicast_TimeToLive = 20;
|
||
|
int Error = Context.PlatformSetSocketOption(SACN.SendSocket, IPPROTO_IP, IP_MULTICAST_TTL,
|
||
|
(const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive));
|
||
|
SACN.CID = StringToCID_ ("{67F9D986-544E-4abb-8986-D5F79382586C}");
|
||
|
|
||
|
SACN.UniverseBuffer = 0;
|
||
|
SACN.SendBuffer = 0;
|
||
|
|
||
|
return SACN;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
SACNSendDataToUniverse (streaming_acn* SACN, sacn_universe* Universe, platform_send_to* PlatformSendTo)
|
||
|
{
|
||
|
//DEBUG_TRACK_FUNCTION;
|
||
|
|
||
|
u8* StartPositionInSendBuffer = (u8*)Universe->StartPositionInSendBuffer;
|
||
|
SetStreamHeaderSequence_(StartPositionInSendBuffer, SACN->SequenceIterator, false);
|
||
|
|
||
|
PlatformSendTo(SACN->SendSocket, Universe->SendAddress, (const char*)StartPositionInSendBuffer, Universe->SizeInSendBuffer, 0);
|
||
|
#if 0 // Old Network Code
|
||
|
// TODO(Peter): HUGE NOTE!!!!!!!!
|
||
|
// This needs to be put on a separate thread. The sendto call is really slowing us down.
|
||
|
s32 LengthSent = sendto(SACN->SendSocket, (const char*)StartPositionInSendBuffer, Universe->SizeInSendBuffer,
|
||
|
0, (sockaddr*)(&Universe->SendAddress), sizeof(sockaddr_in));
|
||
|
|
||
|
if (LengthSent == SOCKET_ERROR)
|
||
|
{
|
||
|
s32 LastSocketError = WSAGetLastError();
|
||
|
InvalidCodePath;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
SACNCleanup(streaming_acn* SACN, context Context)
|
||
|
{
|
||
|
Context.PlatformCloseSocket(SACN->SendSocket);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////
|
||
|
//
|
||
|
// SACN Data Header Functions
|
||
|
//
|
||
|
///////////////////////////////////////////////
|
||
|
|
||
|
internal void
|
||
|
InitStreamHeader (u8* Buffer, s32 BufferSize,
|
||
|
u16 SlotCount,
|
||
|
u8 StartCode,
|
||
|
u16 Universe,
|
||
|
u8 Priority,
|
||
|
u16 Reserved,
|
||
|
u8 Options,
|
||
|
const char* SourceName,
|
||
|
cid CID
|
||
|
)
|
||
|
{
|
||
|
|
||
|
u8* Cursor = Buffer;
|
||
|
|
||
|
// Preamble Size
|
||
|
Cursor = PackB2(Cursor, RLP_PREAMBLE_SIZE);
|
||
|
Cursor = PackB2(Cursor, RLP_POSTAMBLE_SIZE);
|
||
|
|
||
|
memcpy(Cursor, ACN_IDENTIFIER, ACN_IDENTIFIER_SIZE);
|
||
|
Cursor += ACN_IDENTIFIER_SIZE;
|
||
|
|
||
|
// TODO(Peter): If you never use this anywhere else, go back and remove the parameters
|
||
|
VHD_PackFlags_(Cursor, false, false, false);
|
||
|
Cursor = VHD_PackLength_(Cursor,
|
||
|
STREAM_HEADER_SIZE - RLP_PREAMBLE_SIZE + SlotCount,
|
||
|
false);
|
||
|
|
||
|
// root vector
|
||
|
Cursor = PackB4(Cursor, ROOT_VECTOR);
|
||
|
|
||
|
// CID Pack
|
||
|
for (s32 i = 0; i < CID_Bytes; i++)
|
||
|
{
|
||
|
*Cursor++ = CID.Bytes[i];
|
||
|
}
|
||
|
|
||
|
VHD_PackFlags_(Cursor, false, false, false);
|
||
|
Cursor = VHD_PackLength_(Cursor,
|
||
|
STREAM_HEADER_SIZE - FRAMING_FLAGS_AND_LENGTH_ADDR + SlotCount,
|
||
|
false);
|
||
|
|
||
|
// framing vector
|
||
|
Cursor = PackB4(Cursor, FRAMING_VECTOR);
|
||
|
|
||
|
// framing source name
|
||
|
strncpy((char*)Cursor, SourceName, SOURCE_NAME_SIZE);
|
||
|
Cursor[SOURCE_NAME_SIZE - 1] = '\0';
|
||
|
Cursor += SOURCE_NAME_SIZE;
|
||
|
|
||
|
// priority
|
||
|
Cursor = PackB1(Cursor, Priority);
|
||
|
|
||
|
// reserved
|
||
|
Cursor = PackB2(Cursor, Reserved);
|
||
|
|
||
|
// Sequence # is always set to 0/NONE at the beginning, but it is incremented when sending data
|
||
|
Cursor = PackB1(Cursor, 0);
|
||
|
|
||
|
// Options
|
||
|
Cursor = PackB1(Cursor, Options);
|
||
|
|
||
|
// Universe
|
||
|
Cursor = PackB2(Cursor, Universe);
|
||
|
|
||
|
VHD_PackFlags_(Cursor, false, false, false);
|
||
|
Cursor = VHD_PackLength_(Cursor,
|
||
|
STREAM_HEADER_SIZE - DMP_FLAGS_AND_LENGTH_ADDR + SlotCount,
|
||
|
false);
|
||
|
|
||
|
// DMP Vector
|
||
|
Cursor = PackB1(Cursor, DMP_VECTOR);
|
||
|
|
||
|
// DMP Address and data type
|
||
|
Cursor = PackB1(Cursor, ADDRESS_AND_DATA_FORMAT);
|
||
|
|
||
|
// DMP first property address
|
||
|
Cursor = PackB2(Cursor, 0);
|
||
|
|
||
|
// DMP Address Increment
|
||
|
Cursor = PackB2(Cursor, ADDRESS_INC);
|
||
|
|
||
|
// Property Value Count -- Includes one byte for start code
|
||
|
Cursor = PackB2(Cursor, SlotCount + 1);
|
||
|
|
||
|
Cursor = PackB1(Cursor, StartCode);
|
||
|
|
||
|
s32 DiffSize = Cursor - Buffer;
|
||
|
if (Cursor - Buffer != STREAM_HEADER_SIZE)
|
||
|
{
|
||
|
InvalidCodePath;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
SetStreamHeaderSequence_ (u8* Buffer, u8 Sequence, b32 Draft)
|
||
|
{
|
||
|
DEBUG_TRACK_FUNCTION;
|
||
|
PackB1(Buffer + SEQ_NUM_ADDR, Sequence);
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
VHD_PackFlags_(u8* Buffer, b32 InheritVec, b32 InheritHead, b32 InheritData)
|
||
|
{
|
||
|
u8* Cursor = Buffer;
|
||
|
u8 NewByte = UpackB1(Cursor) & 0x8f;
|
||
|
|
||
|
if (!InheritVec) { NewByte |= VHD_V_FLAG; }
|
||
|
if (!InheritHead) { NewByte |= VHD_H_FLAG; }
|
||
|
if (!InheritData) { NewByte |= VHD_D_FLAG; }
|
||
|
|
||
|
PackB1(Cursor, NewByte);
|
||
|
}
|
||
|
|
||
|
internal u8*
|
||
|
VHD_PackLength_(u8* Buffer, u32 Length, b32 IncludeLength)
|
||
|
{
|
||
|
u8* Cursor = Buffer;
|
||
|
u32 AdjustedLength = Length;
|
||
|
if (IncludeLength)
|
||
|
{
|
||
|
if (Length + 1 > VHD_MAXMINLENGTH)
|
||
|
{
|
||
|
AdjustedLength += 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AdjustedLength += 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Mask out the length bits to keep flags intact
|
||
|
u8 NewByte = UpackB1(Cursor) & 0x70;
|
||
|
if (AdjustedLength > VHD_MAXMINLENGTH)
|
||
|
{
|
||
|
NewByte |= VHD_L_FLAG;
|
||
|
}
|
||
|
|
||
|
u8 PackBuffer[4];
|
||
|
PackB4(PackBuffer, AdjustedLength);
|
||
|
if (AdjustedLength <= VHD_MAXMINLENGTH)
|
||
|
{
|
||
|
NewByte |= (PackBuffer[2] & 0x0f);
|
||
|
Cursor = PackB1(Cursor, NewByte);
|
||
|
Cursor = PackB1(Cursor, PackBuffer[3]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NewByte |= (PackBuffer[1] & 0x0f);
|
||
|
Cursor = PackB1(Cursor, PackBuffer[2]);
|
||
|
Cursor = PackB1(Cursor, PackBuffer[3]);
|
||
|
}
|
||
|
|
||
|
return Cursor;
|
||
|
}
|
||
|
|
||
|
internal cid
|
||
|
StringToCID_ (const char* String)
|
||
|
{
|
||
|
cid Result = {};
|
||
|
|
||
|
const char* Src = String;
|
||
|
u8* Dest = &Result.Bytes[0];
|
||
|
b32 FirstNibble = true;
|
||
|
|
||
|
while(*Src && (Dest - &Result.Bytes[0] < CID_Bytes))
|
||
|
{
|
||
|
u8 Offset = 0;
|
||
|
if ((*Src >= 0x30) && (*Src <= 0x39)){ Offset = 0x30; }
|
||
|
else if ((*Src >= 0x41) && (*Src <= 0x46)) { Offset = 0x37; }
|
||
|
else if ((*Src >= 0x61) && (*Src <= 0x66)) { Offset = 0x66; }
|
||
|
|
||
|
if (Offset != 0)
|
||
|
{
|
||
|
if (FirstNibble)
|
||
|
{
|
||
|
*Dest = (u8)(*Src - Offset);
|
||
|
*Dest <<= 4;
|
||
|
FirstNibble = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*Dest |= (*Src - Offset);
|
||
|
Dest++;
|
||
|
FirstNibble = true;
|
||
|
}
|
||
|
}
|
||
|
Src++;
|
||
|
}
|
||
|
|
||
|
return Result;
|
||
|
}
|