diff --git a/cinera/cinera.c b/cinera/cinera.c index 7ceaee9..3d63bef 100644 --- a/cinera/cinera.c +++ b/cinera/cinera.c @@ -14,7 +14,7 @@ typedef struct version CINERA_APP_VERSION = { .Major = 0, .Minor = 5, - .Patch = 41 + .Patch = 42 }; // TODO(matt): Copy in the DB 3 stuff from cinera_working.c @@ -716,6 +716,18 @@ StringsDiffer(char *A, char *B) // NOTE(matt): Two null-terminated strings return *A - *B; } +int +StringsDifferCaseInsensitive(char *A, char *B) // NOTE(matt): Two null-terminated strings +{ + while(*A && *B && + ((*A >= 'A' && *A <= 'Z') ? *A + ('a' - 'A') : *A) == + ((*B >= 'A' && *B <= 'Z') ? *B + ('a' - 'A') : *B)) + { + ++A, ++B; + } + return *A - *B; +} + bool StringsDifferT(char *A, // NOTE(matt): Null-terminated string char *B, // NOTE(matt): Not null-terminated string (e.g. one mid-buffer) @@ -1146,7 +1158,7 @@ CharToColour(char Char) return Colour; } -hsl_colour * +void StringToColourHash(hsl_colour *Colour, char *String) { Colour->Hue = 0; @@ -1162,7 +1174,6 @@ StringToColourHash(hsl_colour *Colour, char *String) Colour->Hue = Colour->Hue % 360; Colour->Saturation = Colour->Saturation % 26 + 74; - return(Colour); } char * @@ -1242,6 +1253,20 @@ ConstructURLPrefix(buffer *URLPrefix, int IncludeType, int PageType) } } +typedef struct +{ + char Abbreviation[32]; + hsl_colour Colour; + credential_info *Credential; + bool Seen; +} speaker; + +typedef struct +{ + speaker Speaker[16]; + int Count; +} speakers; + enum { CreditsError_NoHost, @@ -1250,13 +1275,18 @@ enum } credits_errors; int -SearchCredentials(buffer *CreditsMenu, bool *HasCreditsMenu, char *Person, char *Role) +SearchCredentials(buffer *CreditsMenu, bool *HasCreditsMenu, char *Person, char *Role, speakers *Speakers) { bool Found = FALSE; for(int CredentialIndex = 0; CredentialIndex < ArrayCount(Credentials); ++CredentialIndex) { if(!StringsDiffer(Person, Credentials[CredentialIndex].Username)) { + if(Speakers) + { + Speakers->Speaker[Speakers->Count].Credential = &Credentials[CredentialIndex]; + ++Speakers->Count; + } Found = TRUE; if(*HasCreditsMenu == FALSE) { @@ -1310,41 +1340,159 @@ SearchCredentials(buffer *CreditsMenu, bool *HasCreditsMenu, char *Person, char " \n"); } } - return Found ? 0 : CreditsError_NoCredentials; + return Found ? RC_SUCCESS : CreditsError_NoCredentials; +} + +void +ClearString(char *String) +{ + while(*String) + { + *String++ = '\0'; + } +} + +void +InitialString(char *Dest, char *Src) +{ + ClearString(Dest); + *Dest++ = *Src++; + while(*Src++) + { + if(*Src == ' ') + { + ++Src; + if(*Src) + { + *Dest++ = *Src; + } + } + } +} + +void +GetFirstSubstring(char *Dest, char *Src) +{ + ClearString(Dest); + while(*Src && *Src != ' ') + { + *Dest++ = *Src++; + } +} + +void +InitialAndGetFinalString(char *Dest, char *Src) +{ + ClearString(Dest); + int SrcLength = StringLength(Src); + char *SrcPtr = Src + SrcLength - 1; + while(SrcPtr > Src && *SrcPtr != ' ') + { + --SrcPtr; + } + if(*SrcPtr == ' ' && SrcPtr - Src < SrcLength - 1) + { + ++SrcPtr; + } + + if(Src < SrcPtr) + { + *Dest++ = *Src++; + *Dest++ = '.'; + *Dest++ = ' '; + + while(Src < SrcPtr - 1) + { + if(*Src == ' ') + { + ++Src; + if(*Src) + { + *Dest++ = *Src; + *Dest++ = '.'; + *Dest++ = ' '; + } + } + ++Src; + } + } + + CopyString(Dest, SrcPtr); +} + +bool +AbbreviationsClash(speakers *Speakers) +{ + for(int i = 0; i < Speakers->Count; ++i) + { + for(int j = i + 1; j < Speakers->Count; ++j) + { + if(!StringsDiffer(Speakers->Speaker[i].Abbreviation, Speakers->Speaker[j].Abbreviation)) + { + return TRUE; + } + } + } + return FALSE; +} + +void +SortAndAbbreviateSpeakers(speakers *Speakers) +{ + for(int i = 0; i < Speakers->Count; ++i) + { + for(int j = i + 1; j < Speakers->Count; ++j) + { + if(StringsDiffer(Speakers->Speaker[i].Credential->Username, Speakers->Speaker[j].Credential->Username) > 0) + { + credential_info *Temp = Speakers->Speaker[j].Credential; + Speakers->Speaker[j].Credential = Speakers->Speaker[i].Credential; + Speakers->Speaker[i].Credential = Temp; + break; + } + } + } + + for(int i = 0; i < Speakers->Count; ++i) + { + StringToColourHash(&Speakers->Speaker[i].Colour, Speakers->Speaker[i].Credential->Username); + InitialString(Speakers->Speaker[i].Abbreviation, Speakers->Speaker[i].Credential->CreditedName); + } + + int Attempt = 0; + while(AbbreviationsClash(Speakers)) + { + for(int i = 0; i < Speakers->Count; ++i) + { + switch(Attempt) + { + case 0: GetFirstSubstring(Speakers->Speaker[i].Abbreviation, Speakers->Speaker[i].Credential->CreditedName); break; + case 1: InitialAndGetFinalString(Speakers->Speaker[i].Abbreviation, Speakers->Speaker[i].Credential->CreditedName); break; + case 2: ClearCopyStringNoFormat(Speakers->Speaker[i].Abbreviation, sizeof(Speakers->Speaker[i].Abbreviation), Speakers->Speaker[i].Credential->Username); break; + } + } + ++Attempt; + } } int -BuildCredits(buffer *CreditsMenu, bool *HasCreditsMenu, HMML_VideoMetaData *Metadata) +BuildCredits(buffer *CreditsMenu, bool *HasCreditsMenu, HMML_VideoMetaData *Metadata, speakers *Speakers) // TODO(matt): Make this take the Credentials, once we are parsing them from a config { - if(Metadata->member) + if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->member, "Host", Speakers) == CreditsError_NoCredentials) { - if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->member, "Host")) - { - printf("No credentials for member %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->member); - return CreditsError_NoCredentials; - } - } - else - { - if(*HasCreditsMenu == TRUE) - { - CopyStringToBuffer(CreditsMenu, - " \n" - " \n"); - } - fprintf(stderr, "Missing \"member\" in the [video] node\n"); - return CreditsError_NoHost; + printf("No credentials for member %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->member); + return CreditsError_NoCredentials; } if(Metadata->co_host_count > 0) { for(int i = 0; i < Metadata->co_host_count; ++i) { - if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->co_hosts[i], "Co-host")) + if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->co_hosts[i], "Co-host", Speakers) == CreditsError_NoCredentials) { printf("No credentials for co-host %s. Please contact matt@handmadedev.org with their:\n" " Full name\n" @@ -1359,7 +1507,7 @@ BuildCredits(buffer *CreditsMenu, bool *HasCreditsMenu, HMML_VideoMetaData *Meta { for(int i = 0; i < Metadata->guest_count; ++i) { - if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->guests[i], "Guest")) + if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->guests[i], "Guest", Speakers) == CreditsError_NoCredentials) { printf("No credentials for guest %s. Please contact matt@handmadedev.org with their:\n" " Full name\n" @@ -1370,11 +1518,16 @@ BuildCredits(buffer *CreditsMenu, bool *HasCreditsMenu, HMML_VideoMetaData *Meta } } + if(Speakers->Count > 1) + { + SortAndAbbreviateSpeakers(Speakers); + } + if(Metadata->annotator_count > 0) { for(int i = 0; i < Metadata->annotator_count; ++i) { - if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->annotators[i], "Annotator")) + if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->annotators[i], "Annotator", 0) == CreditsError_NoCredentials) { printf("No credentials for annotator %s. Please contact matt@handmadedev.org with their:\n" " Full name\n" @@ -2713,6 +2866,32 @@ typedef struct short int PrevIndex, ThisIndex, NextIndex; } neighbourhood; +int +LinearSearchForSpeaker(speakers Speakers, char *Username) +{ + for(int i = 0; i < Speakers.Count; ++i) + { + if(!StringsDifferCaseInsensitive(Speakers.Speaker[i].Credential->Username, Username)) + { + return i; + } + } + return -1; +} + +bool +IsCategorisedAFK(HMML_Annotation Anno) +{ + for(int i = 0; i < Anno.marker_count; ++i) + { + if(!StringsDiffer(Anno.markers[i].marker, "afk")) + { + return TRUE; + } + } + return FALSE; +} + int HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filename, neighbourhood *N) { @@ -3019,7 +3198,8 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen CopyStringToBuffer(&CollationBuffers->Player, "