diff --git a/hmml_to_html/cinera.css b/hmml_to_html/cinera.css
index 8342f20..b0fe468 100644
--- a/hmml_to_html/cinera.css
+++ b/hmml_to_html/cinera.css
@@ -171,12 +171,16 @@
flex-direction: column;
}
+.title > .menu > .credits_container .credit {
+ cursor: default;
+}
+
.title > .menu > .credits_container .credit .person {
+ flex-grow: 1;
text-decoration: none;
}
.title > .menu > .credits_container .credit .support {
- flex-grow: 1;
text-align: right;
padding: 16px;
}
@@ -409,6 +413,7 @@
/* CUSTOM PAGE STYLE */
+/*
body {
background-color: #000;
font-family: sans-serif;
@@ -416,6 +421,7 @@ body {
margin: 0;
padding: 0;
}
+*/
/*
Open menu: ▾ ▾
diff --git a/hmml_to_html/hmml_to_html.c b/hmml_to_html/hmml_to_html.c
index 9499289..d1d79b1 100644
--- a/hmml_to_html/hmml_to_html.c
+++ b/hmml_to_html/hmml_to_html.c
@@ -73,7 +73,16 @@ typedef struct
} category_info;
// TODO(matt): Parse this stuff out of a config file
-char *Credentials[ ][5] =
+typedef struct
+{
+ char *Username;
+ char *CreditedName;
+ char *HomepageURL;
+ char *SupportIcon;
+ char *SupportURL;
+} credential_info;
+
+credential_info Credentials[] =
{
{ "Miblo", "Matt Mascarenhas", "http://miblodelcarpio.co.uk", "cinera_icon_patreon.png", "https://patreon.com/miblo"},
{ "miotatsu", "Mio Iwakura", "http://riscy.tv/", "cinera_icon_patreon.png", "https://patreon.com/miotatsu"},
@@ -81,6 +90,31 @@ char *Credentials[ ][5] =
{ "cmuratori", "Casey Muratori", "https://handmadehero.org", "cinera_icon_patreon.png", "https://patreon.com/cmuratori"},
{ "fierydrake", "Mike Tunnicliffe", "", "", ""},
{ "abnercoimbre", "Abner Coimbre", "https://handmade.network/m/abnercoimbre", "cinera_icon_patreon.png", "https://patreon.com/handmade_dev"},
+ { "/y_lee", "Yunsup Lee", "https://www.linkedin.com/in/yunsup-lee-385b692b/", "", ""},
+ { "/a_waterman", "Andrew Waterman", "https://www.linkedin.com/in/andrew-waterman-76805788", "", ""},
+ { "debiatan", "Miguel Lechón", "http://blog.debiatan.net/", "", ""},
+};
+
+typedef struct
+{
+ char *Medium;
+ char *Icon;
+ char *WrittenName;
+} category_medium;
+
+category_medium CategoryMedium[] =
+{
+ // medium icon written name
+ { "afk", "…" , "Away from Keyboard"}, // TODO(matt): Filter this out by default
+ { "authored", "🗪", "Chat Comment"}, // TODO(matt): Conditionally handle Chat vs Guest Comments
+ { "blackboard", "🖌", "Blackboard"},
+ { "experience", "🍷", "Experience"},
+ { "owl", "🦉", "Owl of Shame"},
+ { "programming", "🖮", "Programming"}, // TODO(matt): Potentially make this configurable per project
+ { "rant", "💢", "Rant"},
+ { "research", "📖", "Research"},
+ { "run", "🏃", "In-Game"}, // TODO(matt): Potentially make this configurable per project
+ { "trivia", "🎲", "Trivia"},
};
#define EDITION EDITION_SINGLE
@@ -205,6 +239,19 @@ CopyStringNoFormat(char *Dest, char *String)
return Length;
}
+int
+CopyStringNoFormatT(char *Dest, char *String, char Terminator)
+{
+ int Length = 0;
+ while(*String != Terminator)
+ {
+ *Dest++ = *String++;
+ ++Length;
+ }
+ *Dest = '\0';
+ return Length;
+}
+
int
StringLength(char *String)
{
@@ -423,118 +470,157 @@ SanitisePunctuation(char *String)
return String;
}
-int
-BuildCredits(buffer *CreditsMenu, buffer *HostInfo, buffer *AnnotatorInfo, bool *HasCreditsMenu, char *ImagesDir, char *Host, char *Annotator)
+enum
{
- // TODO(matt): Handle co-hosts and guests
- bool FoundHost = FALSE;
- bool FoundAnnotator = FALSE;
+ CreditsError_NoHost = 1 << 0,
+ CreditsError_NoAnnotator = 1 << 1,
+ CreditsError_NoCredentials = 1 << 2
+} CreditsErrorCodes;
+int
+SearchCredentials(buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, char *Person, char* Role)
+{
+ bool Found = FALSE;
for(int CredentialIndex = 0; CredentialIndex < ArrayCount(Credentials); ++CredentialIndex)
{
- if(!StringsDiffer(Host, Credentials[CredentialIndex][0]))
+ if(!StringsDiffer(Person, Credentials[CredentialIndex].Username))
{
- FoundHost = TRUE; // TODO(matt): Check if this is actually necessary...
- CopyStringToBuffer(HostInfo,
- " \n");
- if(*Credentials[CredentialIndex][2])
+ Found = TRUE;
+ if(*HasCreditsMenu == FALSE)
{
- CopyStringToBuffer(HostInfo,
- " \n"
- " Host
\n"
- " %s
\n"
- " \n",
- Credentials[CredentialIndex][2],
- Credentials[CredentialIndex][1]);
- }
- else
- {
- CopyStringToBuffer(HostInfo,
- " \n"
- "
Host
\n"
- "
%s
\n"
- "
\n",
- Credentials[CredentialIndex][1]);
- }
-
- if(*Credentials[CredentialIndex][4] && *Credentials[CredentialIndex][3])
- {
- CopyStringToBuffer(HostInfo,
- " \n",
- Credentials[CredentialIndex][4],
- ImagesDir,
- Credentials[CredentialIndex][3]);
- }
-
- CopyStringToBuffer(HostInfo,
- " \n");
- }
-
- if(!StringsDiffer(Annotator, Credentials[CredentialIndex][0]))
- {
- FoundAnnotator = TRUE; // TODO(matt): Check if this is actually necessary...
- CopyStringToBuffer(AnnotatorInfo,
- " \n");
- if(*Credentials[CredentialIndex][2])
- {
- CopyStringToBuffer(AnnotatorInfo,
- " \n"
- " Annotator
\n"
- " %s
\n"
- " \n",
- Credentials[CredentialIndex][2],
- Credentials[CredentialIndex][1]);
- }
- else
- {
- CopyStringToBuffer(AnnotatorInfo,
- " \n"
- "
Annotator
\n"
- "
%s
\n"
- "
\n",
- Credentials[CredentialIndex][1]);
- }
-
- if(*Credentials[CredentialIndex][4] && *Credentials[CredentialIndex][3])
- {
- CopyStringToBuffer(AnnotatorInfo,
- " \n",
- Credentials[CredentialIndex][4],
- ImagesDir,
- Credentials[CredentialIndex][3]);
- }
-
- CopyStringToBuffer(AnnotatorInfo,
- " \n");
- }
- }
-
- if(FoundHost || FoundAnnotator)
- {
- CopyStringToBuffer(CreditsMenu,
+ CopyStringToBuffer(CreditsMenu,
"
\n");
}
else
{
- return 1;
+ if(*HasCreditsMenu == TRUE)
+ {
+ CopyStringToBuffer(CreditsMenu,
+ " \n"
+ " \n");
+ }
+ return CreditsError_NoHost;
+ }
+
+ if(Metadata.co_host_count > 0)
+ {
+ for(int i = 0; i < Metadata.co_host_count; ++i)
+ {
+ if(SearchCredentials(CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.co_hosts[i], "Co-host"))
+ {
+ printf("No credentials for %s. Please contact matt@handmadedev.org with their:\n"
+ " Full name\n"
+ " Homepage URL (optional)\n"
+ " Financial support info, e.g. Patreon URL (optional)\n", Metadata.co_hosts[i]);
+ }
+ }
+ }
+
+ if(Metadata.guest_count > 0)
+ {
+ for(int i = 0; i < Metadata.guest_count; ++i)
+ {
+ if(SearchCredentials(CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.guests[i], "Guest"))
+ {
+ printf("No credentials for %s. Please contact matt@handmadedev.org with their:\n"
+ " Full name\n"
+ " Homepage URL (optional)\n"
+ " Financial support info, e.g. Patreon URL (optional)\n", Metadata.guests[i]);
+ }
+ }
+ }
+
+ if(Metadata.annotator_count > 0)
+ {
+ for(int i = 0; i < Metadata.annotator_count; ++i)
+ {
+ if(SearchCredentials(CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.annotators[i], "Annotator"))
+ {
+ printf("No credentials for %s. Please contact matt@handmadedev.org with their:\n"
+ " Full name\n"
+ " Homepage URL (optional)\n"
+ " Financial support info, e.g. Patreon URL (optional)\n", Metadata.annotators[i]);
+ }
+ }
+ }
+ else
+ {
+ if(*HasCreditsMenu == TRUE)
+ {
+ CopyStringToBuffer(CreditsMenu,
+ " \n"
+ " \n");
+ }
+ return CreditsError_NoAnnotator;
+ }
+
+ if(*HasCreditsMenu == TRUE)
+ {
+ CopyStringToBuffer(CreditsMenu,
+ " \n"
+ " \n");
}
- *HasCreditsMenu = TRUE;
return 0;
}
@@ -643,21 +729,6 @@ BuildReference(ref_info *ReferencesArray, int RefIdentifier, int UniqueRefs, HMM
return 0;
}
-char *CategoryMedium[][3] =
-{
- // medium icon written name
- { "afk", "…" , "Away from Keyboard"}, // TODO(matt): Filter this out by default
- { "authored", "🗪", "Chat Comment"}, // TODO(matt): Conditionally handle Chat vs Guest Comments
- { "blackboard", "🖌", "Blackboard"},
- { "experience", "🍷", "Experience"},
- { "owl", "🦉", "Owl of Shame"},
- { "programming", "🖮", "Programming"}, // TODO(matt): Potentially make this configurable per project
- { "rant", "💢", "Rant"},
- { "research", "📖", "Research"},
- { "run", "🏃", "In-Game"}, // TODO(matt): Potentially make this configurable per project
- { "trivia", "🎲", "Trivia"},
-};
-
void
BuildFilter(category_info *TopicsArray, int *UniqueTopics, category_info *MediaArray, int *UniqueMedia, char *Marker)
{
@@ -666,7 +737,7 @@ BuildFilter(category_info *TopicsArray, int *UniqueTopics, category_info *MediaA
int i = 0;
for(i = 0; i < ArrayCount(CategoryMedium); ++i)
{
- if(!StringsDiffer(CategoryMedium[i][0], Marker))
+ if(!StringsDiffer(CategoryMedium[i].Medium, Marker))
{
IsMedium = TRUE;
break;
@@ -679,11 +750,11 @@ BuildFilter(category_info *TopicsArray, int *UniqueTopics, category_info *MediaA
int j = 0;
for(j = 0; j < *UniqueMedia; ++j)
{
- if(!StringsDiffer(CategoryMedium[i][0], MediaArray[j].Marker))
+ if(!StringsDiffer(CategoryMedium[i].Medium, MediaArray[j].Marker))
{
return;
}
- if((Offset = StringsDiffer(CategoryMedium[i][2], MediaArray[j].WrittenText)) < 0)
+ if((Offset = StringsDiffer(CategoryMedium[i].WrittenName, MediaArray[j].WrittenText)) < 0)
{
int k;
for(k = *UniqueMedia; k > j; --k)
@@ -692,16 +763,16 @@ BuildFilter(category_info *TopicsArray, int *UniqueTopics, category_info *MediaA
CopyString(MediaArray[k].WrittenText, MediaArray[k-1].WrittenText);
}
- CopyString(MediaArray[k].Marker, CategoryMedium[i][0]);
- CopyString(MediaArray[k].WrittenText, CategoryMedium[i][2]);
+ CopyString(MediaArray[k].Marker, CategoryMedium[i].Medium);
+ CopyString(MediaArray[k].WrittenText, CategoryMedium[i].WrittenName);
break;
}
}
if(j == *UniqueMedia)
{
- CopyString(MediaArray[j].Marker, CategoryMedium[i][0]);
- CopyString(MediaArray[j].WrittenText, CategoryMedium[i][2]);
+ CopyString(MediaArray[j].Marker, CategoryMedium[i].Medium);
+ CopyString(MediaArray[j].WrittenText, CategoryMedium[i].WrittenName);
}
++*UniqueMedia;
@@ -748,7 +819,7 @@ BuildCategories(buffer *AnnotationClass, buffer *Category, int *MarkerIndex, boo
// The code in the "annotation loop" would then have to write both the head and tail of the category stuff
for(int i = 0; i < ArrayCount(CategoryMedium); ++i)
{
- if(!StringsDiffer(CategoryMedium[i][0], Marker))
+ if(!StringsDiffer(CategoryMedium[i].Medium, Marker))
{
CopyStringToBuffer(AnnotationClass, " %s", SanitisePunctuation(Marker));
*HasMedium = TRUE;
@@ -812,70 +883,6 @@ StringToInt(char *String)
return Result;
}
-size_t
-CurlIntoBuffer(char *InPtr, size_t CharLength, size_t Chars, char **OutputPtr)
-{
- int Length = CharLength * Chars;
- int i;
- for(i = 0; InPtr[i] && i < Length; ++i)
- {
- *((*OutputPtr)++) = InPtr[i];
- }
- **OutputPtr = '\0';
- return Length;
-};
-
-int
-SearchQuotes(buffer QuoteStaging, int CacheSize, quote_info *Info, int ID)
-{
- QuoteStaging.Ptr = QuoteStaging.Location;
- while(QuoteStaging.Ptr - QuoteStaging.Location < CacheSize)
- {
- char InID[4] = { 0 };
- char InTime[16] = { 0 };
- char *OutPtr = InID;
- while(*QuoteStaging.Ptr != ',')
- {
- *OutPtr++ = *QuoteStaging.Ptr++;
- }
- *OutPtr = '\0';
-
- if(StringToInt(InID) == ID)
- {
- QuoteStaging.Ptr += 1;
- OutPtr = InTime;
- while(*QuoteStaging.Ptr != ',')
- {
- *OutPtr++ = *QuoteStaging.Ptr++;
- }
- *OutPtr = '\0';
-
- long int Time = StringToInt(InTime);
- strftime(Info->Date, 32, "%d %B, %Y", gmtime(&Time));
-
- QuoteStaging.Ptr += 1;
- OutPtr = Info->Text;
- while(*QuoteStaging.Ptr != '\n')
- {
- *OutPtr++ = *QuoteStaging.Ptr++;
- }
- *OutPtr = '\0';
-
- FreeBuffer(&QuoteStaging);
- return 0;
- }
- else
- {
- while(*QuoteStaging.Ptr != '\n')
- {
- ++QuoteStaging.Ptr;
- }
- ++QuoteStaging.Ptr;
- }
- }
- return 1;
-}
-
int
MakeDir(char *Path)
{
@@ -907,6 +914,19 @@ MakeDir(char *Path)
return 0;
}
+size_t
+CurlIntoBuffer(char *InPtr, size_t CharLength, size_t Chars, char **OutputPtr)
+{
+ int Length = CharLength * Chars;
+ int i;
+ for(i = 0; InPtr[i] && i < Length; ++i)
+ {
+ *((*OutputPtr)++) = InPtr[i];
+ }
+ **OutputPtr = '\0';
+ return Length;
+};
+
void
CurlQuotes(buffer *QuoteStaging, char *QuotesURL)
{
@@ -924,6 +944,56 @@ CurlQuotes(buffer *QuoteStaging, char *QuotesURL)
}
}
+int
+SearchQuotes(buffer QuoteStaging, int CacheSize, quote_info *Info, int ID)
+{
+ QuoteStaging.Ptr = QuoteStaging.Location;
+ while(QuoteStaging.Ptr - QuoteStaging.Location < CacheSize)
+ {
+ char InID[4] = { 0 };
+ char InTime[16] = { 0 };
+ char *OutPtr = InID;
+ QuoteStaging.Ptr += CopyStringNoFormatT(OutPtr, QuoteStaging.Ptr, ',');
+
+ if(StringToInt(InID) == ID)
+ {
+ QuoteStaging.Ptr += 1;
+ OutPtr = InTime;
+ QuoteStaging.Ptr += CopyStringNoFormatT(OutPtr, QuoteStaging.Ptr, ',');
+
+ long int Time = StringToInt(InTime);
+ char DayString[3];
+ strftime(DayString, 3, "%d", gmtime(&Time));
+ int Day = StringToInt(DayString);
+
+ char DaySuffix[3]; if(DayString[1] == '1' && Day != 11) { CopyString(DaySuffix, "st"); }
+ else if(DayString[1] == '2' && Day != 12) { CopyString(DaySuffix, "nd"); }
+ else if(DayString[1] == '3' && Day != 13) { CopyString(DaySuffix, "rd"); }
+ else { CopyString(DaySuffix, "th"); }
+
+ char MonthYear[32];
+ strftime(MonthYear, 32, "%B, %Y", gmtime(&Time));
+ CopyString(Info->Date, "%d%s %s", Day, DaySuffix, MonthYear);
+
+ QuoteStaging.Ptr += 1;
+ OutPtr = Info->Text;
+ QuoteStaging.Ptr += CopyStringNoFormatT(OutPtr, QuoteStaging.Ptr, '\n');
+
+ FreeBuffer(&QuoteStaging);
+ return 0;
+ }
+ else
+ {
+ while(*QuoteStaging.Ptr != '\n')
+ {
+ ++QuoteStaging.Ptr;
+ }
+ ++QuoteStaging.Ptr;
+ }
+ }
+ return 1;
+}
+
int
BuildQuote(quote_info *Info, char *Speaker, int ID, char *CacheDir)
{
@@ -963,7 +1033,7 @@ BuildQuote(quote_info *Info, char *Speaker, int ID, char *CacheDir)
QuoteStaging.Size = Kilobytes(256);
if(!(QuoteStaging.Location = malloc(QuoteStaging.Size)))
{
- perror("malloc");
+ perror("BuildQuote");
}
QuoteStaging.Ptr = QuoteStaging.Location;
@@ -1016,7 +1086,7 @@ GenerateTopicColours(buffer *Colour, char *Topic, char *TopicsDir)
{
for(int i = 0; i < ArrayCount(CategoryMedium); ++i)
{
- if(!StringsDiffer(Topic, CategoryMedium[i][0]))
+ if(!StringsDiffer(Topic, CategoryMedium[i].Medium))
{
return;
}
@@ -1482,7 +1552,7 @@ main(int ArgC, char **Args)
bool ValidDefaultMedium = FALSE;
for(int i = 0; i < ArrayCount(CategoryMedium); ++i)
{
- if(!StringsDiffer(DefaultMedium, CategoryMedium[i][0]))
+ if(!StringsDiffer(DefaultMedium, CategoryMedium[i].Medium))
{
ValidDefaultMedium = TRUE;
break;
@@ -1493,7 +1563,7 @@ main(int ArgC, char **Args)
fprintf(stderr, "Specified default medium \"%s\" not available. Valid media are:\n", DefaultMedium);
for(int i = 0; i < ArrayCount(CategoryMedium); ++i)
{
- fprintf(stderr, " %s\n", CategoryMedium[i][0]);
+ fprintf(stderr, " %s\n", CategoryMedium[i].Medium);
}
fprintf(stderr, "To have \"%s\" added to the list, contact matt@handmadedev.org\n", DefaultMedium);
return 1;
@@ -1714,6 +1784,7 @@ main(int ArgC, char **Args)
// FilterMenu
// FilterTopics
// FilterMedia
+ // CreditsMenu
// Player
// Colour
// Annotation
@@ -1735,8 +1806,6 @@ main(int ArgC, char **Args)
buffer FilterTopics;
buffer FilterMedia;
buffer CreditsMenu;
- buffer HostInfo;
- buffer AnnotatorInfo;
buffer Player;
buffer Colour;
@@ -1803,8 +1872,6 @@ main(int ArgC, char **Args)
ClaimBuffer(&MemoryArena, &ClaimedMemory, &FilterTopics, "FilterTopics", Kilobytes(8));
ClaimBuffer(&MemoryArena, &ClaimedMemory, &FilterMedia, "FilterMedia", Kilobytes(8));
ClaimBuffer(&MemoryArena, &ClaimedMemory, &CreditsMenu, "CreditsMenu", Kilobytes(8));
- ClaimBuffer(&MemoryArena, &ClaimedMemory, &HostInfo, "HostInfo", Kilobytes(1));
- ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotatorInfo, "AnnotatorInfo", Kilobytes(1));
ClaimBuffer(&MemoryArena, &ClaimedMemory, &Player, "Player", Kilobytes(256));
ClaimBuffer(&MemoryArena, &ClaimedMemory, &Colour, "Colour", 32);
@@ -1840,18 +1907,22 @@ main(int ArgC, char **Args)
" \n"
" \n", HMML.metadata.id, HMML.metadata.project);
- // TODO(matt): Handle multiple annotators
- if(HMML.metadata.annotator_count > 0)
+ int CreditsErrorCode = BuildCredits(&CreditsMenu, &HasCreditsMenu, ImagesDir, HMML.metadata);
+ if(CreditsErrorCode)
{
- BuildCredits(&CreditsMenu, &HostInfo, &AnnotatorInfo, &HasCreditsMenu, ImagesDir, HMML.metadata.member, HMML.metadata.annotators[0]);
- }
- else
- {
- fprintf(stderr, "%s: Missing annotator in [video] node\n", Args[FileIndex]);
- hmml_free(&HMML);
- free(MemoryArena.Location);
- free(Template.Location);
- return 1;
+ switch(CreditsErrorCode)
+ {
+ case CreditsError_NoHost:
+ fprintf(stderr, "%s: Missing \"member\" in the [video] node. Skipping...\n", Args[FileIndex]);
+goto Cleanup;
+ break;
+ case CreditsError_NoAnnotator:
+ fprintf(stderr, "%s: Missing \"annotator\" in the [video] node. Skipping...\n", Args[FileIndex]);
+goto Cleanup;
+ break;
+ default:
+ break;
+ }
}
#if DEBUG
@@ -2216,20 +2287,6 @@ main(int ArgC, char **Args)
free(Template.Location);
return 1;
}
- if(BuildQuote(&QuoteInfo,
- Speaker,
- Anno->quote.id,
- CacheDir) == 2)
- {
- fprintf(stderr, "%s:%d: Failed to create quote cache at %s/quotes/%s for Quote #%s %d.\n"
- "Proceeding anyway, without a cache!\n",
- Args[FileIndex],
- Anno->line,
- CacheDir,
- Speaker,
- Speaker,
- Anno->quote.id);
- }
CopyStringToBuffer(&QuoteMenu,
"
\n"
@@ -2478,7 +2535,7 @@ main(int ArgC, char **Args)
int j;
for(j = 0; j < ArrayCount(CategoryMedium); ++j)
{
- if(!StringsDiffer(MediaArray[i].Marker, CategoryMedium[j][0]))
+ if(!StringsDiffer(MediaArray[i].Marker, CategoryMedium[j].Medium))
{
break;
}
@@ -2489,8 +2546,8 @@ main(int ArgC, char **Args)
" %s%s\n"
" \n",
MediaArray[i].Marker,
- CategoryMedium[j][1],
- CategoryMedium[j][2]
+ CategoryMedium[j].Icon,
+ CategoryMedium[j].WrittenName
);
}
@@ -3158,8 +3215,6 @@ main(int ArgC, char **Args)
// Annotation
// Colour
// Player
- // AnnotatorInfo
- // HostInfo
// CreditsMenu
// FilterMedia
// FilterTopics
@@ -3170,14 +3225,13 @@ main(int ArgC, char **Args)
// Includes
// Master
+Cleanup:
DeclaimBuffer(&FilterState, &ClaimedMemory);
DeclaimBuffer(&Script, &ClaimedMemory);
DeclaimBuffer(&Annotation, &ClaimedMemory);
DeclaimBuffer(&Colour, &ClaimedMemory);
DeclaimBuffer(&Player, &ClaimedMemory);
- DeclaimBuffer(&AnnotatorInfo, &ClaimedMemory);
- DeclaimBuffer(&HostInfo, &ClaimedMemory);
DeclaimBuffer(&CreditsMenu, &ClaimedMemory);
DeclaimBuffer(&FilterMedia, &ClaimedMemory);
DeclaimBuffer(&FilterTopics, &ClaimedMemory);