From 445969aee2d0862b9d8cbb8b56fb6ed4dc5c2161 Mon Sep 17 00:00:00 2001 From: Matt Mascarenhas Date: Tue, 29 Aug 2017 21:35:28 +0100 Subject: [PATCH] hmml_to_html.c: Generate a full project --- hmml_to_html/hmml_to_html.c | 4152 +++++++++++++++++------------------ 1 file changed, 2061 insertions(+), 2091 deletions(-) diff --git a/hmml_to_html/hmml_to_html.c b/hmml_to_html/hmml_to_html.c index 53023e5..9d66036 100644 --- a/hmml_to_html/hmml_to_html.c +++ b/hmml_to_html/hmml_to_html.c @@ -32,7 +32,30 @@ enum EDITION_SINGLE, EDITION_PROJECT, EDITION_NETWORK -}; +} editions; + +enum +{ + MODE_BARE, + MODE_INTEGRATE +} modes; + +typedef struct +{ + char *BaseDir; + char CacheDir[255]; + char *CSSDir; + int Edition; + char *ImagesDir; + char *JSDir; + char *DefaultMedium; + int Mode; + char *OutLocation; + char *OutIntegratedLocation; + bool ForceIntegration; + char *ProjectDir; + char *TemplateLocation; +} config; typedef struct { @@ -42,6 +65,17 @@ typedef struct int Size; } buffer; +typedef struct +{ + buffer IncludesIndex; + buffer Index; + buffer IncludesPlayer; + buffer Menus; + buffer Player; + buffer Script; + char Title[255]; +} buffers; + // TODO(matt): Consider putting the ref_info and quote_info into linked lists on the heap, just to avoid all the hardcoded sizes typedef struct @@ -125,8 +159,6 @@ category_medium CategoryMedium[] = { "trivia", "🎲", "Trivia"}, }; -int EDITION = EDITION_SINGLE; - #define ArrayCount(A) sizeof(A)/sizeof(*(A)) __attribute__ ((format (printf, 2, 3))) @@ -182,6 +214,7 @@ CopyStringNoFormat(char *Dest, char *String) return Length; } +// TODO(matt): Maybe do a version of this that takes a string as a Terminator int CopyStringNoFormatT(char *Dest, char *String, char Terminator) { @@ -255,45 +288,6 @@ CopyStringToBufferHTMLSafe(buffer *Dest, char *String) } } -void -CopyStringToBufferCSVSafe(buffer *Dest, char *String) -{ - while(*String) - { - if(Dest->Ptr - Dest->Location >= Dest->Size) - { - fprintf(stderr, "CopyStringToBufferHTMLSafe: %s cannot accommodate %d-character string\n", Dest->ID, StringLength(String)); - __asm__("int3"); - } - switch(*String) - { - case '<': - CopyStringToBuffer(Dest, "<"); - String++; - break; - case '>': - CopyStringToBuffer(Dest, ">"); - String++; - break; - case '&': - CopyStringToBuffer(Dest, "&"); - String++; - break; - case '\"': - CopyStringToBuffer(Dest, """); - String += 2; - break; - case '\'': - CopyStringToBuffer(Dest, "'"); - String++; - break; - default: - *Dest->Ptr++ = *String++; - break; - } - } -} - int StringsDiffer(char *A, char *B) // NOTE(matt): Two null-terminated strings { @@ -364,30 +358,32 @@ FreeBuffer(buffer *Buffer) free(Buffer->Location); } -#define ClaimBuffer(MemoryArena, ClaimedMemory, Buffer, ID, Size) if(__ClaimBuffer(MemoryArena, ClaimedMemory, Buffer, ID, Size))\ +#if 0 +#define ClaimBuffer(MemoryArena, Buffer, ID, Size) if(__ClaimBuffer(MemoryArena, Buffer, ID, Size))\ {\ fprintf(stderr, "%s:%d: MemoryArena cannot contain %s of size %d\n", __FILE__, __LINE__, ID, Size);\ hmml_free(&HMML);\ FreeBuffer(MemoryArena);\ return 1;\ }; +#endif int -__ClaimBuffer(buffer *MemoryArena, int *ClaimedMemory, buffer *Buffer, char *ID, int Size) +ClaimBuffer(buffer *MemoryArena, buffer *Buffer, char *ID, int Size) { - if(*ClaimedMemory + Size > MemoryArena->Size) + if(MemoryArena->Ptr - MemoryArena->Location + Size > MemoryArena->Size) { return 1; } - Buffer->Location = MemoryArena->Location + *ClaimedMemory; + Buffer->Location = MemoryArena->Ptr; Buffer->Size = Size; Buffer->ID = ID; - *ClaimedMemory += Buffer->Size; + MemoryArena->Ptr += Buffer->Size; *Buffer->Location = '\0'; Buffer->Ptr = Buffer->Location; #if DEBUG printf(" Claimed: %s: %d\n" - " Total ClaimedMemory: %d\n\n", Buffer->ID, Buffer->Size, *ClaimedMemory); + " Total ClaimedMemory: %ld\n\n", Buffer->ID, Buffer->Size, MemoryArena->Ptr - MemoryArena->Location); #endif return 0; } @@ -415,26 +411,26 @@ LogUsage(buffer Buffer, char *CacheDir) fclose(LogFile); } -#define DeclaimBuffer(Buffer, ClaimedMemory) __DeclaimBuffer(Buffer, ClaimedMemory, CacheDir) +#define DeclaimBuffer(MemoryArena, Buffer) __DeclaimBuffer(MemoryArena, Buffer, Config) void -__DeclaimBuffer(buffer *Buffer, int *ClaimedMemory, char *CacheDir) +__DeclaimBuffer(buffer *MemoryArena, buffer *Buffer, config Config) { *Buffer->Location = '\0'; - *ClaimedMemory -= Buffer->Size; + MemoryArena->Ptr -= Buffer->Size; float PercentageUsed = (float)(Buffer->Ptr - Buffer->Location) / Buffer->Size * 100; #if DEBUG printf("Declaimed: %s\n" " Used: %ld / %d (%.2f%%)\n" "\n" - " Total ClaimedMemory: %d\n\n", + " Total ClaimedMemory: %ld\n\n", Buffer->ID, Buffer->Ptr - Buffer->Location, Buffer->Size, PercentageUsed, - *ClaimedMemory); + MemoryArena->Ptr - MemoryArena->Location); #endif - LogUsage(*Buffer, CacheDir); + LogUsage(*Buffer, Config.CacheDir); if(PercentageUsed >= 80.0f) { // TODO(matt): Implement either dynamically growing buffers, or phoning home to matt@handmadedev.org @@ -557,7 +553,7 @@ enum }; int -SearchCredentials(buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, char *Person, char* Role) +SearchCredentials(config Config, buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, char *Person, char* Role) { bool Found = FALSE; for(int CredentialIndex = 0; CredentialIndex < ArrayCount(Credentials); ++CredentialIndex) @@ -602,11 +598,22 @@ SearchCredentials(buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, ch if(*Credentials[CredentialIndex].SupportIcon && *Credentials[CredentialIndex].SupportURL) { - CopyStringToBuffer(CreditsMenu, - " \n", - Credentials[CredentialIndex].SupportURL, - ImagesDir, - Credentials[CredentialIndex].SupportIcon); + if(Config.Edition == EDITION_PROJECT) + { + CopyStringToBuffer(CreditsMenu, + " \n", + Credentials[CredentialIndex].SupportURL, + ImagesDir, + Credentials[CredentialIndex].SupportIcon); + } + else + { + CopyStringToBuffer(CreditsMenu, + " \n", + Credentials[CredentialIndex].SupportURL, + ImagesDir, + Credentials[CredentialIndex].SupportIcon); + } } CopyStringToBuffer(CreditsMenu, @@ -617,12 +624,12 @@ SearchCredentials(buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, ch } int -BuildCredits(buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, HMML_VideoMetaData Metadata) +BuildCredits(config Config, buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, HMML_VideoMetaData Metadata) // TODO(matt): Make this take the Credentials, once we are parsing them from a config { if(Metadata.member) { - if(SearchCredentials(CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.member, "Host")) + if(SearchCredentials(Config, CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.member, "Host")) { printf("No credentials for %s. Please contact matt@handmadedev.org with their:\n" " Full name\n" @@ -645,7 +652,7 @@ BuildCredits(buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, HMML_Vi { for(int i = 0; i < Metadata.co_host_count; ++i) { - if(SearchCredentials(CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.co_hosts[i], "Co-host")) + if(SearchCredentials(Config, 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" @@ -659,7 +666,7 @@ BuildCredits(buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, HMML_Vi { for(int i = 0; i < Metadata.guest_count; ++i) { - if(SearchCredentials(CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.guests[i], "Guest")) + if(SearchCredentials(Config, CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.guests[i], "Guest")) { printf("No credentials for %s. Please contact matt@handmadedev.org with their:\n" " Full name\n" @@ -673,7 +680,7 @@ BuildCredits(buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, HMML_Vi { for(int i = 0; i < Metadata.annotator_count; ++i) { - if(SearchCredentials(CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.annotators[i], "Annotator")) + if(SearchCredentials(Config, CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.annotators[i], "Annotator")) { printf("No credentials for %s. Please contact matt@handmadedev.org with their:\n" " Full name\n" @@ -1016,6 +1023,7 @@ BuildQuote(quote_info *Info, char *Speaker, int ID, char *CacheDir) CopyString(QuoteCachePath, "%s/%s", QuoteCacheDir, Speaker); FILE *QuoteCache; char QuotesURL[256]; + // TODO(matt): Make the URL configurable CopyString(QuotesURL, "https://dev.abaines.me.uk/quotes/%s.raw", Speaker); bool CacheAvailable = FALSE; @@ -1156,274 +1164,8 @@ GenerateTopicColours(char *Topic, char *TopicsDir) } } -#define CONFIG 0 - -#if CONFIG -typedef struct -{ - char *Username; - char *Display_Name; - char *Homepage; - char *Funding_Platform; - char *Funding_Username; - unsigned int Index; -} credentials; - -typedef struct -{ - credentials Credentials; -} config; - -int -ParseConfig(buffer *Buffer, char *Username) -{ - /* - Essentially, I want to pass a Username to this, and have it write the credentials into the Config buffer - Let's start by just grabbing the stuff and printing it out - */ - - // TODO(matt): Actually figure out the "standard" config location - char Config_Location[255]; - if(getenv("XDG_CONFIG_HOME")) - { - sprintf(Config_Location, "%s/hmml.conf", getenv("XDG_CONFIG_HOME")); - } - else if(getenv("HOME")) - { - sprintf(Config_Location, "%s/.config/hmml.conf", getenv("HOME")); - } - else - { - fprintf(stderr, "Config file location not set"); - return 1; - } - - FILE *InFile; - if(!(InFile = fopen(Config_Location, "r"))) - { - perror(Config_Location); - return 2; - } - - printf("Reading: %s\n", Config_Location); - - fseek(InFile, 0, SEEK_END); - int InSize = ftell(InFile); - fseek(InFile, 0, SEEK_SET); - - char *InBuffer; - //config Config = { 0 }; - - if(!(InBuffer = malloc(InSize))) - { - perror("ParseConfig"); - return 3; - } - - fread(InBuffer, InSize, 1, InFile); - fclose(InFile); - - char *InPtr = InBuffer; - char OutBuffer[256]; - //char *OutPtr = Config.Credentials.Display_Name; - char *OutPtr = OutBuffer; - bool Quoted = FALSE; - bool FoundCredentials, ParsingUsername = FALSE; - unsigned int ScopeDepth = 0; - - while(InPtr - InBuffer < InSize) - { - switch(*InPtr) - { - case '#': - { - if(!Quoted) - { - printf(" We are commenting\n"); - while(InPtr - InBuffer < InSize && *InPtr != '\n') - { - ++InPtr; - } - ++InPtr; - while(InPtr - InBuffer < InSize && (*InPtr == ' ' || *InPtr == '\n')) - { - ++InPtr; - } - } - else - { - *OutPtr++ = *InPtr++; - } - break; - } - case '{': - { - if(!Quoted) - { - ++ScopeDepth; - ++InPtr; - while(*InPtr == '\n' || *InPtr == ' ') - { - ++InPtr; - } - printf(" We have entered a scope\n"); - } - else - { - *OutPtr++ = *InPtr++; - } - break; - } - case '}': - { - if(!Quoted) - { - --ScopeDepth; - ++InPtr; - printf(" We have left a scope\n"); - } - else - { - *OutPtr++ = *InPtr++; - } - break; - } -#if 1 - case ' ': - { - if(!Quoted) - { - ++InPtr; - *OutPtr = '\0'; - OutPtr = OutBuffer; - printf("%s\n", OutBuffer); - - // TODO(matt): Switch on the OutBuffer? I have a feeling that isn't actually possible, though - if(!StringsDiffer("credentials", OutBuffer)) - { - FoundCredentials = TRUE; - printf(" We have found the credentials block\n"); - } - if(ParsingUsername) - { - printf(" The username is %s\n", OutBuffer); - ParsingUsername = FALSE; - } - if(FoundCredentials && (!StringsDiffer("username", OutBuffer))) - { - ParsingUsername = TRUE; - printf(" We have found the username\n"); - } - } - else - { - *OutPtr++ = *InPtr++; - } - break; - } -#endif - case '"': - { - if(!Quoted) - { - Quoted = TRUE; - printf(" We are quoting!\n"); - } - else - { - Quoted = FALSE; - printf(" We are no longer quoting!\n"); - } - ++InPtr; - break; - } - case ';': - { - if(!Quoted) - { - printf(" We have reached the end of a setting\n"); - ++InPtr; - } - else - { - *OutPtr++ = *InPtr++; - } - } - case '\n': - { - if(!Quoted) - { - if(InPtr - InBuffer < InSize) - { - *OutPtr = '\0'; - OutPtr = OutBuffer; - // TODO(matt) - if(!StringsDiffer("credentials", OutBuffer)) - { - FoundCredentials = TRUE; - printf(" We have found the credentials block\n"); - } - if(ParsingUsername) - { - printf(" The username is %s\n", OutBuffer); - ParsingUsername = FALSE; - } - if(FoundCredentials && (!StringsDiffer("username", OutBuffer))) - { - ParsingUsername = TRUE; - printf(" We have found the username\n"); - } - printf("%s\n", OutBuffer); - ++InPtr; - while(InPtr - InBuffer < InSize && *InPtr == ' ') // NOTE(matt): Skip indentation whitespace - { - ++InPtr; - } - } - } - else - { - *OutPtr++ = *InPtr++; - } - - if(InPtr - InBuffer == InSize) - { - printf(" We have reached the EOF\n"); - } - break; - } - default: - { - *OutPtr++ = *InPtr++; - break; - } - } - } - - -#if 0 - while(InPtr - InBuffer < InSize) - { - while(*InPtr != '\n') - { - *OutPtr++ = *InPtr++; - } - *OutPtr = '\0'; - printf("%s\n", Config.Credentials.Display_Name); - } -#endif - - free(InBuffer); - - // Reading from the config file, parsing it inline (on the stack) and writing into the buffer *Config - - return 0; -} - -#endif - void -PrintUsage(char *BinaryLocation, char *DefaultBaseDirectory, char *DefaultCSSDir, char *DefaultImagesDir, char *DefaultJSDir, char *DefaultDefaultMedium, char *DefaultOutLocation, char *DefaultProjectDirectory, char *DefaultTemplateLocation) +PrintUsage(char *BinaryLocation, config DefaultConfig) { fprintf(stderr, "Usage: %s [option(s)] filename(s)\n" "\n" @@ -1467,60 +1209,1875 @@ PrintUsage(char *BinaryLocation, char *DefaultBaseDirectory, char *DefaultCSSDir "\n" "HMML Specification:\n" " https://git.handmade.network/Annotation-Pushers/Annotation-System/wikis/hmmlspec\n", - BinaryLocation, DefaultBaseDirectory, DefaultCSSDir, DefaultImagesDir, DefaultJSDir, DefaultDefaultMedium, DefaultOutLocation, DefaultProjectDirectory, DefaultTemplateLocation); + BinaryLocation, DefaultConfig.BaseDir, DefaultConfig.CSSDir, DefaultConfig.ImagesDir, DefaultConfig.JSDir, DefaultConfig.DefaultMedium, DefaultConfig.OutLocation, DefaultConfig.ProjectDir, DefaultConfig.TemplateLocation); +} + +enum +{ + TAG_INDEX, + + TAG_INCLUDES, + TAG_MENUS, + TAG_PLAYER, + TAG_SCRIPT, + + TAG_TITLE +} template_tags; + +typedef struct +{ + int Code; // template_tags + char *Tag; +} tag; + +tag Tags[] = { + { TAG_INDEX, "__CINERA_INDEX__" }, + { TAG_INCLUDES, "__CINERA_INCLUDES__" }, + { TAG_MENUS, "__CINERA_MENUS__" }, + { TAG_PLAYER, "__CINERA_PLAYER__" }, + { TAG_SCRIPT, "__CINERA_SCRIPT__" }, + { TAG_TITLE, "__CINERA_TITLE__" }, +}; + +typedef struct +{ + int Offset; + int TagCode; +} tag_offset; + +typedef struct +{ + char *Filename; + int Validity; // NOTE(matt): Bitmask describing which page the template is valid for, i.e. contents and / or player page + tag_offset Tag[16]; +} template; + +int +ValidateTemplate(buffer *Template, config Config) +{ + // TODO(matt): Put the Errors in the MemoryArena + buffer Errors; + Errors.ID = "Errors"; + Errors.Size = Kilobytes(1); + if(!(Errors.Location = malloc(Errors.Size))) + { + // TODO(matt): Return code + //perror(Args[0]); return 1; + } + Errors.Ptr = Errors.Location; + bool HaveErrors = FALSE; + + FILE *TemplateFile; + if(!(TemplateFile = fopen(Config.TemplateLocation, "r"))) + { + // TODO(matt): Return code + //perror(Args[0]); return 1; + } + + fseek(TemplateFile, 0, SEEK_END); + Template->Size = ftell(TemplateFile); + fseek(TemplateFile, 0, SEEK_SET); + if(!(Template->Location = malloc(Template->Size))) + { + // TODO(matt): Return code + //perror(Args[0]); return 1; + } + Template->Ptr = Template->Location; + fread(Template->Location, Template->Size, 1, TemplateFile); + fclose(TemplateFile); + + char *IncludesTag = "__CINERA_INCLUDES__"; + char *TitleTag = "__CINERA_TITLE__"; + char *MenusTag = "__CINERA_MENUS__"; + char *PlayerTag = "__CINERA_PLAYER__"; + char *ScriptTag = "__CINERA_SCRIPT__"; + + bool FoundIncludes = FALSE; + bool FoundMenus = FALSE; + bool FoundPlayer = FALSE; + bool FoundScript = FALSE; + + while(Template->Ptr - Template->Location < Template->Size) + { + if(*Template->Ptr == '!' && (Template->Ptr > Template->Location && !StringsDifferT(" tag\n"); + HaveErrors = TRUE; + } + FoundIncludes = TRUE; + while(Template->Ptr - Template->Location < Template->Size) + { + if(!StringsDifferT("-->", Template->Ptr, 0)) + { + Template->Ptr += StringLength("-->"); + break; + } + ++Template->Ptr; + } + break; + } + + else if(!(StringsDifferT(TitleTag, Template->Ptr, 0))) + { + while(Template->Ptr - Template->Location < Template->Size) + { + if(!StringsDifferT("-->", Template->Ptr, 0)) + { + Template->Ptr += StringLength("-->"); + break; + } + ++Template->Ptr; + } + break; + } + else if(!(StringsDifferT(MenusTag, Template->Ptr, 0))) + { + if(!Config.ForceIntegration && FoundMenus == TRUE) + { + CopyStringToBuffer(&Errors, "Template contains more than one tag\n"); + HaveErrors = TRUE; + } + FoundMenus = TRUE; + while(Template->Ptr - Template->Location < Template->Size) + { + if(!StringsDifferT("-->", Template->Ptr, 0)) + { + Template->Ptr += StringLength("-->"); + break; + } + ++Template->Ptr; + } + break; + } + else if(!(StringsDifferT(PlayerTag, Template->Ptr, 0))) + { + if(!Config.ForceIntegration && FoundPlayer == TRUE) + { + CopyStringToBuffer(&Errors, "Template contains more than one tag\n"); + HaveErrors = TRUE; + } + FoundPlayer = TRUE; + while(Template->Ptr - Template->Location < Template->Size) + { + if(!StringsDifferT("-->", Template->Ptr, 0)) + { + Template->Ptr += StringLength("-->"); + break; + } + ++Template->Ptr; + } + break; + } + else if(!(StringsDifferT(ScriptTag, Template->Ptr, 0))) + { + if(!Config.ForceIntegration && FoundPlayer == FALSE) + { + CopyStringToBuffer(&Errors, " must come after \n"); + HaveErrors = TRUE; + } + if(!Config.ForceIntegration && FoundScript == TRUE) + { + CopyStringToBuffer(&Errors, "Template contains more than one tag\n"); + HaveErrors = TRUE; + } + FoundScript = TRUE; + while(Template->Ptr - Template->Location < Template->Size) + { + if(!StringsDifferT("-->", Template->Ptr, 0)) + { + Template->Ptr += StringLength("-->"); + break; + } + ++Template->Ptr; + } + break; + } + else if(!StringsDifferT("-->", Template->Ptr, 0)) + { + break; + } + ++Template->Ptr; + // NOTE(matt): We should have replaced up to here + } + } + else + { + ++Template->Ptr; + } + } + + if(!HaveErrors && FoundIncludes && FoundMenus && FoundPlayer && FoundScript) + { + Template->Ptr = Template->Location; + free(Errors.Location); + } + else + { + if(!Config.ForceIntegration) + { + if(!FoundIncludes){ CopyStringToBuffer(&Errors, "Template must include one tag\n"); }; + if(!FoundMenus){ CopyStringToBuffer(&Errors, "Template must include one tag\n"); }; + if(!FoundPlayer){ CopyStringToBuffer(&Errors, "Template must include one tag\n"); }; + if(!FoundScript){ CopyStringToBuffer(&Errors, "Template must include one tag\n"); }; + fprintf(stderr, "%s", Errors.Location); + free(Errors.Location); return 1; + } + } + return 0; +} + +int +HMMLToBuffers(buffer *MemoryArena, buffers *CollationBuffers, config Config, char *Filename) +{ + CollationBuffers->IncludesPlayer.Ptr = CollationBuffers->IncludesPlayer.Location; + CollationBuffers->Menus.Ptr = CollationBuffers->Menus.Location; + CollationBuffers->Player.Ptr = CollationBuffers->Player.Location; + CollationBuffers->Script.Ptr = CollationBuffers->Script.Location; + CollationBuffers->IncludesIndex.Ptr = CollationBuffers->IncludesIndex.Location; + + char Filepath[255]; + if(Config.Edition == EDITION_PROJECT) + { + CopyString(Filepath, "%s/%s", Config.ProjectDir, Filename); + } + else + { + CopyString(Filepath, "%s", Filename); + } + + FILE *InFile; + if(!(InFile = fopen(Filepath, "r"))) + { + perror(Filename); + //free(MemoryArena->Location); + return 1; + } + + HMML_Output HMML = hmml_parse_file(InFile); + fclose(InFile); + + if(HMML.well_formed) + { + CopyString(CollationBuffers->Title, HMML.metadata.title); +#if DEBUG + printf( + "================================================================================\n" + "%s\n" + "================================================================================\n", + Filename); +#endif + // NOTE(matt): Tree structure of "global" buffer dependencies + // Master + // IncludesPlayer + // Menus + // QuoteMenu + // ReferenceMenu + // FilterMenu + // FilterTopics + // FilterMedia + // CreditsMenu + // Player + // Script + // FilterState + + buffer QuoteMenu; + buffer ReferenceMenu; + buffer FilterMenu; + buffer FilterTopics; + buffer FilterMedia; + buffer CreditsMenu; + + buffer Annotation; + buffer AnnotationHeader; + buffer AnnotationClass; + buffer AnnotationData; + buffer Text; + buffer TopicDots; + + buffer FilterState; + + ClaimBuffer(MemoryArena, &QuoteMenu, "QuoteMenu", Kilobytes(16)); + ClaimBuffer(MemoryArena, &ReferenceMenu, "ReferenceMenu", Kilobytes(16)); + ClaimBuffer(MemoryArena, &FilterMenu, "FilterMenu", Kilobytes(16)); + ClaimBuffer(MemoryArena, &FilterTopics, "FilterTopics", Kilobytes(8)); + ClaimBuffer(MemoryArena, &FilterMedia, "FilterMedia", Kilobytes(8)); + ClaimBuffer(MemoryArena, &CreditsMenu, "CreditsMenu", Kilobytes(8)); + + ClaimBuffer(MemoryArena, &FilterState, "FilterState", Kilobytes(4)); + + ref_info ReferencesArray[200] = { 0 }; + categories Topics = { 0 }; + categories Media = { 0 }; + + bool HasQuoteMenu = FALSE; + bool HasReferenceMenu = FALSE; + bool HasFilterMenu = FALSE; + bool HasCreditsMenu = FALSE; + + int QuoteIdentifier = 0x3b1; + int RefIdentifier = 1; + int UniqueRefs = 0; + + CopyStringToBuffer(&CollationBuffers->Menus, + "
\n" + " ", HMML.metadata.project); + CopyStringToBufferHTMLSafe(&CollationBuffers->Menus, HMML.metadata.title); + CopyStringToBuffer(&CollationBuffers->Menus, "\n" + " ⚠ Click here to regain focus ⚠\n"); + + CopyStringToBuffer(&CollationBuffers->Player, + "
\n" + "
\n" + "
\n", HMML.metadata.id, HMML.metadata.project); + + int CreditsErrorCode = BuildCredits(Config, &CreditsMenu, &HasCreditsMenu, Config.ImagesDir, HMML.metadata); + if(CreditsErrorCode) + { + switch(CreditsErrorCode) + { + case CreditsError_NoHost: + fprintf(stderr, "%s: Missing \"member\" in the [video] node. Skipping...\n", Filename); + goto Cleanup; + break; + case CreditsError_NoAnnotator: + fprintf(stderr, "%s: Missing \"annotator\" in the [video] node. Skipping...\n", Filename); + goto Cleanup; + break; + default: + break; + } + } + +#if DEBUG + printf("\n\n --- Entering Annotations Loop ---\n\n\n\n"); +#endif + for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex) + { +#if DEBUG + printf("%d\n", AnnotationIndex); +#endif + HMML_Annotation *Anno = HMML.annotations + AnnotationIndex; + categories LocalTopics = { 0 }; + categories LocalMedia = { 0 }; + bool HasQuote = FALSE; + bool HasReference = FALSE; + + quote_info QuoteInfo = { 0 }; + + // NOTE(matt): Tree structure of "annotation local" buffer dependencies + // Annotation + // AnnotationHeader + // AnnotationClass + // AnnotationData + // Text + // TopicDots + + ClaimBuffer(MemoryArena, &Annotation, "Annotation", Kilobytes(8)); + ClaimBuffer(MemoryArena, &AnnotationHeader, "AnnotationHeader", 512); + ClaimBuffer(MemoryArena, &AnnotationClass, "AnnotationClass", 256); + ClaimBuffer(MemoryArena, &AnnotationData, "AnnotationData", 512); + ClaimBuffer(MemoryArena, &Text, "Text", Kilobytes(4)); + ClaimBuffer(MemoryArena, &TopicDots, "TopicDots", 512); + + CopyStringToBuffer(&AnnotationHeader, + "
time)); + + CopyStringToBuffer(&AnnotationClass, + " class=\"marker"); + + if(Anno->author) + { + if(!HasFilterMenu) + { + HasFilterMenu = TRUE; + } + InsertCategory(&Topics, &Media, "authored"); + CopyStringToBuffer(&AnnotationClass, " authored"); + hsl_colour AuthorColour; + StringToColourHash(&AuthorColour, Anno->author); + if(Config.Edition == EDITION_NETWORK) + { + fprintf(stderr, "%s:%d - TODO(matt): Implement author hoverbox\n", __FILE__, __LINE__); + // NOTE(matt): We should get instructions on how to get this info in the config + CopyStringToBuffer(&Text, + "%s ", + Anno->author, + AuthorColour.Hue, AuthorColour.Saturation, AuthorColour.Lightness, + AuthorColour.Hue, AuthorColour.Saturation, + Anno->author); + } + else + { + CopyStringToBuffer(&Text, + "%s ", + AuthorColour.Hue, AuthorColour.Saturation, AuthorColour.Lightness, + AuthorColour.Hue, AuthorColour.Saturation, + Anno->author); + } + + } + + char *InPtr = Anno->text; + + int MarkerIndex = 0, RefIndex = 0; + while(*InPtr || RefIndex < Anno->reference_count) + { + if(MarkerIndex < Anno->marker_count && + InPtr - Anno->text == Anno->markers[MarkerIndex].offset) + { + char *Readable = Anno->markers[MarkerIndex].parameter + ? Anno->markers[MarkerIndex].parameter + : Anno->markers[MarkerIndex].marker; + if(Anno->markers[MarkerIndex].type == HMML_MEMBER) + { + hsl_colour MemberColour; + StringToColourHash(&MemberColour, Anno->markers[MarkerIndex].marker); + if(Config.Edition == EDITION_NETWORK) + { + fprintf(stderr, "%s:%d - TODO(matt): Implement member hoverbox\n", __FILE__, __LINE__); + // NOTE(matt): We should get instructions on how to get this info in the config + CopyStringToBuffer(&Text, + "%.*s", + Anno->markers[MarkerIndex].marker, + MemberColour.Hue, MemberColour.Saturation, MemberColour.Lightness, + MemberColour.Hue, MemberColour.Saturation, + StringLength(Readable), InPtr); + } + else + { + CopyStringToBuffer(&Text, + "%.*s", + MemberColour.Hue, MemberColour.Saturation, MemberColour.Lightness, + MemberColour.Hue, MemberColour.Saturation, + StringLength(Readable), InPtr); + } + + InPtr += StringLength(Readable); + ++MarkerIndex; + } + else if(Anno->markers[MarkerIndex].type == HMML_PROJECT) + { + hsl_colour ProjectColour; + StringToColourHash(&ProjectColour, Anno->markers[MarkerIndex].marker); + if(Config.Edition == EDITION_NETWORK) + { + fprintf(stderr, "%s:%d - TODO(matt): Implement project hoverbox\n", __FILE__, __LINE__); + // NOTE(matt): We should get instructions on how to get this info in the config + CopyStringToBuffer(&Text, + "%s", + Anno->markers[MarkerIndex].marker, + ProjectColour.Hue, ProjectColour.Saturation, ProjectColour.Lightness, + ProjectColour.Hue, ProjectColour.Saturation, + Readable); + } + else + { + CopyStringToBuffer(&Text, + "%s", + ProjectColour.Hue, ProjectColour.Saturation, ProjectColour.Lightness, + ProjectColour.Hue, ProjectColour.Saturation, + Readable); + } + InPtr += StringLength(Readable); + ++MarkerIndex; + } + else if(Anno->markers[MarkerIndex].type == HMML_CATEGORY) + { + GenerateTopicColours(Anno->markers[MarkerIndex].marker, Config.CSSDir); + if(!HasFilterMenu) + { + HasFilterMenu = TRUE; + } + InsertCategory(&Topics, &Media, Anno->markers[MarkerIndex].marker); // Global + InsertCategory(&LocalTopics, &LocalMedia, Anno->markers[MarkerIndex].marker); // Local + } + } + + while(RefIndex < Anno->reference_count && + InPtr - Anno->text == Anno->references[RefIndex].offset) + { + HMML_Reference *CurrentRef = Anno->references + RefIndex; + if(!HasReferenceMenu) + { + CopyStringToBuffer(&ReferenceMenu, + "
\n" + " References ▼\n" + "
\n" + "
\n"); + + if(BuildReference(ReferencesArray, RefIdentifier, UniqueRefs, *CurrentRef, *Anno) == 1) + { + fprintf(stderr, "%s:%d: Cannot process new combination of reference info\n" + "\n" + "Either tweak your annotation, or contact matt@handmadedev.org\n" + "mentioning the ref node you want to write and how you want it to\n" + "appear in the references menu\n", Filename, Anno->line); + hmml_free(&HMML); + //free(MemoryArena->Location); + return 1; + } + ++ReferencesArray[RefIdentifier - 1].IdentifierCount; + ++UniqueRefs; + + HasReferenceMenu = TRUE; + } + else + { + for(int i = 0; i < UniqueRefs; ++i) + { + if(ReferencesArray[i].IdentifierCount == REF_MAX_IDENTIFIER) + { + fprintf(stderr, "%s:%d: Too many timecodes associated with one reference (increase REF_MAX_IDENTIFIER)\n", Filename, Anno->line); + hmml_free(&HMML); + //free(MemoryArena.Location); + return 1; + } + if(CurrentRef->isbn) + { + if(!StringsDiffer(CurrentRef->isbn, ReferencesArray[i].ID)) + { + CopyString(ReferencesArray[i].Identifier[ReferencesArray[i].IdentifierCount].Timecode, Anno->time); + ReferencesArray[i].Identifier[ReferencesArray[i].IdentifierCount].Identifier = RefIdentifier; + ++ReferencesArray[i].IdentifierCount; + goto AppendedIdentifier; + } + } + else if(CurrentRef->url) + { + if(!StringsDiffer(CurrentRef->url, ReferencesArray[i].ID)) + { + CopyString(ReferencesArray[i].Identifier[ReferencesArray[i].IdentifierCount].Timecode, Anno->time); + ReferencesArray[i].Identifier[ReferencesArray[i].IdentifierCount].Identifier = RefIdentifier; + ++ReferencesArray[i].IdentifierCount; + goto AppendedIdentifier; + } + } + else + { + fprintf(stderr, "%s:%d: Reference must have an ISBN or URL\n", Filename, Anno->line); + hmml_free(&HMML); + //free(MemoryArena.Location); + return 1; + } + } + + if(BuildReference(ReferencesArray, RefIdentifier, UniqueRefs, *CurrentRef, *Anno) == 1) + { + fprintf(stderr, "%s:%d: Cannot process new combination of reference info\n" + "\n" + "Either tweak your annotation, or contact matt@handmadedev.org\n" + "mentioning the ref node you want to write and how you want it to\n" + "appear in the references menu\n", Filename, Anno->line); + hmml_free(&HMML); + //free(MemoryArena.Location); + return 1; + } + ++ReferencesArray[UniqueRefs].IdentifierCount; + ++UniqueRefs; + } +AppendedIdentifier: + if(!HasReference) + { + if(CurrentRef->isbn) + { + CopyStringToBuffer(&AnnotationData, " data-ref=\"%s", CurrentRef->isbn); + } + else if(CurrentRef->url) + { + CopyStringToBuffer(&AnnotationData, " data-ref=\"%s", CurrentRef->url); + } + else + { + fprintf(stderr, "%s:%d: Reference must have an ISBN or URL\n", Filename, Anno->line); + hmml_free(&HMML); + //free(MemoryArena.Location); + return 1; + } + + HasReference = TRUE; + } + else + { + if(CurrentRef->isbn) + { + CopyStringToBuffer(&AnnotationData, ",%s", CurrentRef->isbn); + } + else if(CurrentRef->url) + { + CopyStringToBuffer(&AnnotationData, ",%s", CurrentRef->url); + } + else + { + fprintf(stderr, "%s:%d: Reference must have an ISBN or URL", Filename, Anno->line); + hmml_free(&HMML); + //free(MemoryArena.Location); + return 1; + } + } + + if(Anno->references[RefIndex].offset == Anno->references[RefIndex-1].offset) + { + CopyStringToBuffer(&Text, ",%d", RefIdentifier); + } + else + { + CopyStringToBuffer(&Text, "%d", RefIdentifier); + } + + ++RefIndex; + ++RefIdentifier; + } + + if(*InPtr) + { + switch(*InPtr) + { + case '<': + CopyStringToBuffer(&Text, "<"); + InPtr++; + break; + case '>': + CopyStringToBuffer(&Text, ">"); + InPtr++; + break; + case '&': + CopyStringToBuffer(&Text, "&"); + InPtr++; + break; + case '\"': + CopyStringToBuffer(&Text, """); + InPtr++; + break; + case '\'': + CopyStringToBuffer(&Text, "'"); + InPtr++; + break; + default: + *Text.Ptr++ = *InPtr++; + *Text.Ptr = '\0'; + break; + } + } + } + + if(Anno->is_quote) + { + if(!HasQuoteMenu) + { + CopyStringToBuffer(&QuoteMenu, + "
\n" + " Quotes ▼\n" + "
\n" + "
\n"); + + HasQuoteMenu = TRUE; + } + + if(!HasReference) + { + CopyStringToBuffer(&AnnotationData, " data-ref=\"&#%d;", QuoteIdentifier); + } + else + { + CopyStringToBuffer(&AnnotationData, ",&#%d;", QuoteIdentifier); + } + + HasQuote = TRUE; + + char *Speaker = Anno->quote.author ? Anno->quote.author : HMML.metadata.stream_username ? HMML.metadata.stream_username : HMML.metadata.member; + if(BuildQuote(&QuoteInfo, + Speaker, + Anno->quote.id, + Config.CacheDir) == 1) + { + fprintf(stderr, "%s:%d: Quote #%s %d not found. Unlucky!\n", + Filename, + Anno->line, + Speaker, + Anno->quote.id); + hmml_free(&HMML); + //free(MemoryArena.Location); + return 1; + } + + CopyStringToBuffer(&QuoteMenu, + " \n" + " \n" + " \n" + "
Quote %d
\n" + "
", + Speaker, + Anno->quote.id, + QuoteIdentifier, + Anno->quote.id); + + CopyStringToBufferHTMLSafe(&QuoteMenu, QuoteInfo.Text); + + CopyStringToBuffer(&QuoteMenu, "
\n" + " \n" + "
\n" + "
\n" + " [&#%d;]%s\n" + "
\n" + "
\n" + "
\n", + Speaker, + QuoteInfo.Date, + TimecodeToSeconds(Anno->time), + QuoteIdentifier, + Anno->time); + if(!Anno->text[0]) + { + CopyStringToBuffer(&Text, "“"); + CopyStringToBufferHTMLSafe(&Text, QuoteInfo.Text); + CopyStringToBuffer(&Text, "”"); + } + CopyStringToBuffer(&Text, "&#%d;", QuoteIdentifier); + ++QuoteIdentifier; + } + + while(MarkerIndex < Anno->marker_count) + { + GenerateTopicColours(Anno->markers[MarkerIndex].marker, Config.CSSDir); + if(!HasFilterMenu) + { + HasFilterMenu = TRUE; + } + if(Anno->markers[MarkerIndex].marker) + { + InsertCategory(&Topics, &Media, Anno->markers[MarkerIndex].marker); + } + InsertCategory(&LocalTopics, &LocalMedia, Anno->markers[MarkerIndex].marker); + ++MarkerIndex; + } + + if(LocalMedia.Count == 0) + { + InsertCategory(&Topics, &Media, Config.DefaultMedium); + InsertCategory(&LocalTopics, &LocalMedia, Config.DefaultMedium); + } + + BuildCategories(&AnnotationClass, &TopicDots, LocalTopics, LocalMedia, &MarkerIndex); + CopyBuffer(&AnnotationHeader, &AnnotationClass); + + if(HasQuote || HasReference) + { + CopyStringToBuffer(&AnnotationData, "\""); + CopyBuffer(&AnnotationHeader, &AnnotationData); + } + CopyStringToBuffer(&AnnotationHeader, ">\n"); + + CopyBuffer(&Annotation, &AnnotationHeader); + CopyStringToBuffer(&Annotation, + "
%s", + Anno->time); + + CopyBuffer(&Annotation, &Text); + + if(LocalTopics.Count > 0) + { + CopyBuffer(&Annotation, &TopicDots); + } + + CopyStringToBuffer(&Annotation, "
\n" + "
\n" + "
%s", + Anno->time); + + CopyBuffer(&Annotation, &Text); + + CopyStringToBuffer(&Annotation, "
\n" + "
\n" + "
\n" + "
%s", + Anno->time); + + CopyBuffer(&Annotation, &Text); + + CopyStringToBuffer(&Annotation, "
\n" + "
\n" + "
\n"); + + CopyBuffer(&CollationBuffers->Player, &Annotation); + + // NOTE(matt): Tree structure of "annotation local" buffer dependencies + // TopicDots + // Text + // AnnotationData + // AnnotationClass + // AnnotationHeader + // Annotation + + DeclaimBuffer(MemoryArena, &TopicDots); + DeclaimBuffer(MemoryArena, &Text); + DeclaimBuffer(MemoryArena, &AnnotationData); + DeclaimBuffer(MemoryArena, &AnnotationClass); + DeclaimBuffer(MemoryArena, &AnnotationHeader); + DeclaimBuffer(MemoryArena, &Annotation); + } + +#if DEBUG + printf("\n\n --- End of Annotations Loop ---\n\n\n\n"); +#endif + if(HasQuoteMenu) + { + CopyStringToBuffer(&QuoteMenu, + "
\n" + "
\n"); + CopyBuffer(&CollationBuffers->Menus, &QuoteMenu); + } + + if(HasReferenceMenu) + { + for(int i = 0; i < UniqueRefs; ++i) + { + CopyStringToBuffer(&ReferenceMenu, + " \n" + " \n", + ReferencesArray[i].ID, + ReferencesArray[i].URL); + + if(*ReferencesArray[i].Source) + { + CopyStringToBuffer(&ReferenceMenu, + "
"); + CopyStringToBufferHTMLSafe(&ReferenceMenu, ReferencesArray[i].Source); + CopyStringToBuffer(&ReferenceMenu, "
\n" + "
"); + CopyStringToBufferHTMLSafe(&ReferenceMenu, ReferencesArray[i].RefTitle); + CopyStringToBuffer(&ReferenceMenu, "
\n"); + } + else + { + CopyStringToBuffer(&ReferenceMenu, + "
"); + CopyStringToBufferHTMLSafe(&ReferenceMenu, ReferencesArray[i].RefTitle); + CopyStringToBuffer(&ReferenceMenu, "
\n"); + } + CopyStringToBuffer(&ReferenceMenu, + "
\n"); + + for(int j = 0; j < ReferencesArray[i].IdentifierCount;) + { + CopyStringToBuffer(&ReferenceMenu, + "
\n "); + for(int k = 0; k < 3 && j < ReferencesArray[i].IdentifierCount; ++k, ++j) + { + CopyStringToBuffer(&ReferenceMenu, + "[%d]%s", + TimecodeToSeconds(ReferencesArray[i].Identifier[j].Timecode), + ReferencesArray[i].Identifier[j].Identifier, + ReferencesArray[i].Identifier[j].Timecode); + } + CopyStringToBuffer(&ReferenceMenu, "\n" + "
\n"); + } + + CopyStringToBuffer(&ReferenceMenu, + "
\n"); + } + + CopyStringToBuffer(&ReferenceMenu, + "
\n" + "
\n"); + CopyBuffer(&CollationBuffers->Menus, &ReferenceMenu); + } + + if(HasFilterMenu) + { + CopyStringToBuffer(&FilterState, + " var filterState = {\n"); + for(int i = 0; i < Topics.Count; ++i) + { + CopyStringToBuffer(&FilterState, "\"%s\":\t{ \"type\": \"%s\",\t\"off\": false },\n", + Topics.Category[i].Marker, "topic"); + } + for(int i = 0; i < Media.Count; ++i) + { + CopyStringToBuffer(&FilterState, "\"%s\":\t{ \"type\": \"%s\",\t\"off\": false },\n", + Media.Category[i].Marker, "medium"); + } + CopyStringToBuffer(&FilterState, + " };\n"); + + if(Config.Edition == EDITION_PROJECT) + { + CopyStringToBuffer(&FilterMenu, + "
\n" + " \n" + "
\n" + "
Filter mode:
\n" + "
\n", Config.ImagesDir); + } + else + { + CopyStringToBuffer(&FilterMenu, + "
\n" + " \n" + "
\n" + "
Filter mode:
\n" + "
\n", Config.ImagesDir); + } + + if(Topics.Count > 0) + { + CopyStringToBuffer(&FilterMenu, + "
\n" + "
Topics
\n"); + for(int i = 0; i < Topics.Count; ++i) + { + CopyStringToBuffer(&FilterTopics, + "
\n" + " %s\n" + "
\n", + Topics.Category[i].Marker, + Topics.Category[i].Marker, + Topics.Category[i].Marker); + } + CopyStringToBuffer(&FilterTopics, + "
\n"); + CopyBuffer(&FilterMenu, &FilterTopics); + } + + if(Media.Count > 0) + { + CopyStringToBuffer(&FilterMedia, + "
\n" + "
Media
\n"); + for(int i = 0; i < Media.Count; ++i) + { + int j; + for(j = 0; j < ArrayCount(CategoryMedium); ++j) + { + if(!StringsDiffer(Media.Category[i].Marker, CategoryMedium[j].Medium)) + { + break; + } + } + + CopyStringToBuffer(&FilterMedia, + "
\n" + " %s%s\n" + "
\n", + Media.Category[i].Marker, + CategoryMedium[j].Icon, + CategoryMedium[j].WrittenName + ); + } + CopyStringToBuffer(&FilterMedia, + "
\n"); + CopyBuffer(&FilterMenu, &FilterMedia); + } + + CopyStringToBuffer(&FilterMenu, + "
\n" + "
\n" + "
\n"); + + CopyBuffer(&CollationBuffers->Menus, &FilterMenu); + + } + + if(HasCreditsMenu) + { + CopyBuffer(&CollationBuffers->Menus, &CreditsMenu); + } + + CopyStringToBuffer(&CollationBuffers->Menus, + "
\n" + " ?\n" + "
\n" + " ?

Keyboard Navigation

\n" + "\n" + "

Global Keys

\n" + " W, A, P / S, D, N Jump to previous / next marker
\n"); + + if(HasFilterMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, + " z Toggle filter mode V Revert filter to original state\n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, + " z Toggle filter mode V Revert filter to original state\n"); + } + + CopyStringToBuffer(&CollationBuffers->Menus, + "\n" + "

Menu toggling

\n"); + + if(HasQuoteMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, + " q Quotes\n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, + " q Quotes\n"); + } + + if(HasReferenceMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, + " r References\n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, + " r References\n"); + } + + if(HasFilterMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, + " f Filter\n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, + " f Filter\n"); + } + + if(HasCreditsMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, + " c Credits\n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, + " c Credits\n"); + } + + CopyStringToBuffer(&CollationBuffers->Menus, + "\n" + "

Movement

\n" + "
\n" + "
\n" + "
\n" + " a\n" + "
\n" + "
\n" + " w
\n" + " s\n" + "
\n" + "
\n" + " d\n" + "
\n" + "
\n" + "
\n" + " h\n" + " j\n" + " k\n" + " l\n" + "
\n" + "
\n" + "
\n" + " \n" + "
\n" + "
\n" + "
\n" + " \n" + "
\n" + "
\n" + " \n" + "
\n" + "
\n" + "
\n" + "
\n"); + + if(HasQuoteMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, + "

Quotes "); + if(HasReferenceMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, "and References Menus

\n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, "and References Menus\n"); + } + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, + "

Quotes"); + if(HasReferenceMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, " and References Menus

\n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, " and References Menus\n"); + } + } + + if(HasQuoteMenu || HasReferenceMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, + " Enter Jump to timecode
\n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, + " Enter Jump to timecode
\n"); + } + + CopyStringToBuffer(&CollationBuffers->Menus, "\n"); + + if(HasQuoteMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, + "

Quotes"); + if(HasReferenceMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, ", References "); + if(HasCreditsMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, "and Credits Menus

"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, "and Credits Menus"); + } + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, ", References "); + if(HasCreditsMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, "and Credits Menus"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, "and Credits Menus"); + } + } + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, + "

Quotes"); + if(HasReferenceMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, ", References "); + if(HasCreditsMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, "and Credits Menus

"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, "and Credits Menus"); + } + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, ", References "); + if(HasCreditsMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, "and Credits Menus"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, "and Credits Menus"); + } + } + } + + CopyStringToBuffer(&CollationBuffers->Menus, "\n"); + + if(HasQuoteMenu || HasReferenceMenu || HasCreditsMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, + " o Open URL (in new tab)\n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, + " o Open URL (in new tab)\n"); + } + + CopyStringToBuffer(&CollationBuffers->Menus, + "\n"); + + if(HasFilterMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, + "

Filter Menu

\n" + " x, Space Toggle category and focus next
\n" + " X, ShiftSpace Toggle category and focus previous
\n" + " v Invert topics / media as per focus\n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, + "

Filter Menu

\n" + " x, Space Toggle category and focus next
\n" + " X, ShiftSpace Toggle category and focus previous
\n" + " v Invert topics / media as per focus\n"); + } + + CopyStringToBuffer(&CollationBuffers->Menus, "\n"); + + if(HasCreditsMenu) + { + CopyStringToBuffer(&CollationBuffers->Menus, + "

Credits Menu

\n" + " Enter Open URL (in new tab)
\n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Menus, + "

Credits Menu

\n" + " Enter Open URL (in new tab)
\n"); + } + + CopyStringToBuffer(&CollationBuffers->Menus, + "
\n" + "
\n" + + "
"); + + CopyStringToBuffer(&CollationBuffers->Player, + "
\n" + "
"); + + // TODO(matt): Maybe do something about indentation levels + // TODO(matt): We may need to do some actual logic here to figure out + // where the style paths are in relation to us, rather than assuming them + // to be one directory up the tree + if(Config.Edition == EDITION_PROJECT) + { + CopyStringToBuffer(&CollationBuffers->IncludesIndex, + "\n" + " \n" + " \n", + Config.CSSDir, + Config.CSSDir, + HMML.metadata.project, + Config.CSSDir); + + CopyStringToBuffer(&CollationBuffers->IncludesPlayer, + "\n" + " \n" + " \n", + Config.CSSDir, + Config.CSSDir, + HMML.metadata.project, + Config.CSSDir); + } + else + { + CopyStringToBuffer(&CollationBuffers->IncludesPlayer, + "\n" + " \n" + " \n", + Config.CSSDir, + Config.CSSDir, + HMML.metadata.project, + Config.CSSDir); + } + + CopyStringToBuffer(&CollationBuffers->IncludesPlayer, + "\n" + " \n" + " \n"); + + if(Topics.Count || Media.Count) + { + CopyStringToBuffer(&CollationBuffers->IncludesPlayer, + " 0) + { + for(int i = 0; i < Topics.Count; ++i) + { + CopyStringToBuffer(&CollationBuffers->IncludesPlayer, "%s, ", Topics.Category[i].Marker); + } + } + + if(Media.Count > 0) + { + for(int i = 0; i < Media.Count; ++i) + { + CopyStringToBuffer(&CollationBuffers->IncludesPlayer, "%s, ", Media.Category[i].WrittenText); + } + } + + CollationBuffers->IncludesPlayer.Ptr -= 2; + CopyStringToBuffer(&CollationBuffers->IncludesPlayer, "\">\n\n"); + } + + if(Config.Edition == EDITION_PROJECT) + { + CopyStringToBuffer(&CollationBuffers->IncludesPlayer, + " \n", + Config.JSDir); + } + else + { + CopyStringToBuffer(&CollationBuffers->IncludesPlayer, + " \n", + Config.JSDir); + } + + CopyStringToBuffer(&CollationBuffers->Script, + " "); + + // NOTE(matt): Tree structure of "global" buffer dependencies + // FilterState + // CreditsMenu + // FilterMedia + // FilterTopics + // FilterMenu + // ReferenceMenu + // QuoteMenu + +Cleanup: + DeclaimBuffer(MemoryArena, &FilterState); + + DeclaimBuffer(MemoryArena, &CreditsMenu); + DeclaimBuffer(MemoryArena, &FilterMedia); + DeclaimBuffer(MemoryArena, &FilterTopics); + DeclaimBuffer(MemoryArena, &FilterMenu); + DeclaimBuffer(MemoryArena, &ReferenceMenu); + DeclaimBuffer(MemoryArena, &QuoteMenu); + } + else + { + fprintf(stderr, "%s:%d: %s\n", Filename, HMML.error.line, HMML.error.message); + } + hmml_free(&HMML); + return 0; +} + +void +BuffersToHTML(buffer *MemoryArena, buffers CollationBuffers, buffer Template, config Config, char *OutputPath) +{ + buffer Master; + ClaimBuffer(MemoryArena, &Master, "Master", Kilobytes(512)); +#if DEBUG + printf("\n\n --- Buffer Collation ---\n\n\n\n"); +#endif + + if(Config.Mode == MODE_INTEGRATE) + { + buffer Output; + Output.Size = Template.Size + Master.Size; + Output.ID = "Output"; + if(!(Output.Location = malloc(Output.Size))) + { + // TODO(matt): Error code + //perror(Args[0]); free(Template.Location); hmml_free(&HMML); free(MemoryArena.Location); return 1; + } + Output.Ptr = Output.Location; + + char *IncludesTag = "__CINERA_INCLUDES__"; + char *TitleTag = "__CINERA_TITLE__"; + char *MenusTag = "__CINERA_MENUS__"; + char *PlayerTag = "__CINERA_PLAYER__"; + char *ScriptTag = "__CINERA_SCRIPT__"; + + while(Template.Ptr - Template.Location < Template.Size) + { + if(*Template.Ptr == '!' && (Template.Ptr > Template.Location && !StringsDifferT("", Template.Ptr, 0)) + { + Template.Ptr += StringLength("-->"); + break; + } + ++Template.Ptr; + } + break; + } + + else if(!(StringsDifferT(TitleTag, Template.Ptr, 0))) + { + Output.Ptr = CommentStart; + CopyStringToBuffer(&Output, CollationBuffers.Title); + while(Template.Ptr - Template.Location < Template.Size) + { + if(!StringsDifferT("-->", Template.Ptr, 0)) + { + Template.Ptr += StringLength("-->"); + break; + } + ++Template.Ptr; + } + break; + } + else if(!(StringsDifferT(MenusTag, Template.Ptr, 0))) + { + Output.Ptr = CommentStart; + CopyBuffer(&Output, &CollationBuffers.Menus); + while(Template.Ptr - Template.Location < Template.Size) + { + if(!StringsDifferT("-->", Template.Ptr, 0)) + { + Template.Ptr += StringLength("-->"); + break; + } + ++Template.Ptr; + } + break; + } + else if(!(StringsDifferT(PlayerTag, Template.Ptr, 0))) + { + Output.Ptr = CommentStart; + CopyBuffer(&Output, &CollationBuffers.Player); + while(Template.Ptr - Template.Location < Template.Size) + { + if(!StringsDifferT("-->", Template.Ptr, 0)) + { + Template.Ptr += StringLength("-->"); + break; + } + ++Template.Ptr; + } + break; + } + else if(!(StringsDifferT(ScriptTag, Template.Ptr, 0))) + { + Output.Ptr = CommentStart; + CopyBuffer(&Output, &CollationBuffers.Script); + while(Template.Ptr - Template.Location < Template.Size) + { + if(!StringsDifferT("-->", Template.Ptr, 0)) + { + Template.Ptr += StringLength("-->"); + break; + } + ++Template.Ptr; + } + break; + } + else if(!StringsDifferT("-->", Template.Ptr, 0)) + { + break; + } + *Output.Ptr++ = *Template.Ptr++; + } + } + else + { + *Output.Ptr++ = *Template.Ptr++; + } + } + + FILE *OutFile; + if(!(OutFile = fopen(Config.Edition == EDITION_PROJECT ? OutputPath : Config.OutIntegratedLocation, "w"))) + { + // TODO(matt): Return code + //perror(Config.OutIntegratedLocation); free(Template.Location); free(Output.Location); hmml_free(&HMML); free(MemoryArena.Location); return 1; + } + fwrite(Output.Location, Output.Ptr - Output.Location, 1, OutFile); + fclose(OutFile); + + free(Output.Location); + } + else + { + // NOTE(matt): Perform the normal collation into Master, and write-out to out.html + CopyStringToBuffer(&Master, + "\n" + " \n"); + + //NOTE(matt): Here is where we do all our CopyBuffer() calls + CopyBuffer(&Master, &CollationBuffers.IncludesPlayer); + CopyStringToBuffer(&Master, + "\n" + " \n" + " \n"); + CopyBuffer(&Master, &CollationBuffers.Menus); + CopyStringToBuffer(&Master, "\n"); + CopyBuffer(&Master, &CollationBuffers.Player); + CopyStringToBuffer(&Master, "\n"); + CopyBuffer(&Master, &CollationBuffers.Script); + CopyStringToBuffer(&Master, "\n"); + // + + CopyStringToBuffer(&Master, + " \n" + "\n"); + + FILE *OutFile; + if(!(OutFile = fopen(Config.Edition == EDITION_PROJECT ? OutputPath : Config.OutLocation, "w"))) + { + // TODO(matt): Error code + //perror(OutLocation); hmml_free(&HMML); free(MemoryArena.Location); return 1; + } + fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile); + fclose(OutFile); + } + + DeclaimBuffer(MemoryArena, &Master); +} + +int +BuildIndex(buffer *MemoryArena, buffers *CollationBuffers, config Config, char *BaseFilename, char *Title) +{ + char IndexPath[255]; + CopyString(IndexPath, "%s/index", Config.CacheDir); + FILE *IndexFile; + if(!(IndexFile = fopen(IndexPath, "a+"))) + { + perror(IndexPath); + // TODO(matt): Actual error code + return 1; + } + + buffer Index; + fseek(IndexFile, 0, SEEK_END); + Index.Size = ftell(IndexFile); + fseek(IndexFile, 0, SEEK_SET); + + if(!(Index.Location = malloc(Index.Size))) + { + perror("BuildIndex"); + } + Index.Ptr = Index.Location; + fread(Index.Location, Index.Size, 1, IndexFile); + + bool Found = FALSE; + + int EntryCount = 0; + while(Index.Ptr - Index.Location < Index.Size) + { + char IndexedFile[32]; + char *Ptr = IndexedFile; + Index.Ptr += CopyStringNoFormatT(Ptr, Index.Ptr, ','); + if(!StringsDiffer(IndexedFile, BaseFilename)) + { + Found = TRUE; + break; + } + else + { + while(Index.Ptr - Index.Location < Index.Size && *Index.Ptr != '\n') + { + ++Index.Ptr; + } + ++Index.Ptr; + ++EntryCount; + } + } + + if(Found == FALSE) + { + ++EntryCount; + // TODO(matt): Write the EntryCount into the index + fprintf(IndexFile, "%s,%s\n", BaseFilename, Title); + { + char IndexPagePath[255]; + CopyString(IndexPagePath, "%s/index.html", Config.BaseDir); + CollationBuffers->Index.Ptr = CollationBuffers->Index.Location; + CopyStringToBuffer(&CollationBuffers->Index, "
\n"); + Index.Ptr = Index.Location; + while(Index.Ptr - Index.Location < Index.Size) + { + char IndexedFile[32]; + char *Ptr = IndexedFile; + Index.Ptr += CopyStringNoFormatT(Ptr, Index.Ptr, ',') + 1; + + char Title[255]; + Ptr = Title; + Index.Ptr += CopyStringNoFormatT(Ptr, Index.Ptr, '\n') + 1; + + CopyStringToBuffer(&CollationBuffers->Index, +"
\n" +" %s\n" +"
\n", +IndexedFile, Title); + + } + CopyStringToBuffer(&CollationBuffers->Index, "
\n"); + + if(Config.Mode == MODE_INTEGRATE) + { + } + else + { + } + + buffer Master; + ClaimBuffer(MemoryArena, &Master, "Master", Kilobytes(16)); + CopyStringToBuffer(&Master, + "\n" + " \n"); + CopyBuffer(&Master, &CollationBuffers->IncludesIndex); + + CopyStringToBuffer(&Master, + "\n" + " \n" + " \n"); + CopyBuffer(&Master, &CollationBuffers->Index); + CopyStringToBuffer(&Master, + "\n" + " \n" + "\n"); + + FILE *ContentsPage; + if(!(ContentsPage = fopen(IndexPagePath, "w"))) + { + perror(IndexPagePath); + } + fwrite(Master.Location, Master.Ptr - Master.Location, 1, ContentsPage); + fclose(ContentsPage); + + DeclaimBuffer(MemoryArena, &Master); + } + } + + fclose(IndexFile); + FreeBuffer(&Index); + return 0; } int main(int ArgC, char **Args) { // TODO(matt): Read all defaults from the config - char *DefaultBaseOutputDir = "."; - char *BaseOutputDir = DefaultBaseOutputDir; + config DefaultConfig = { + .BaseDir = ".", + .CSSDir = ".", + .Edition = EDITION_SINGLE, + .ImagesDir = ".", + .JSDir = ".", + .DefaultMedium = "programming", + .Mode = getenv("CINERA_MODE") ? MODE_INTEGRATE : MODE_BARE, + .OutLocation = "out.html", + .OutIntegratedLocation = "out_integrated.html", + .ForceIntegration = FALSE, + .ProjectDir = ".", + .TemplateLocation = "template.html" + }; - char *DefaultCSSDir = "."; - char *CSSDir = DefaultCSSDir; - - char *DefaultImagesDir = "."; - char *ImagesDir = DefaultImagesDir; - - char *DefaultJSDir = "."; - char *JSDir = DefaultJSDir; - - char *DefaultDefaultMedium = "programming"; - char *DefaultMedium = DefaultDefaultMedium; - - bool HasTemplate = FALSE; - char *DefaultTemplateLocation = "template.html"; - char *TemplateLocation = DefaultTemplateLocation; - HasTemplate = TRUE; - - char *DefaultOutLocation = "out.html"; - char *OutLocation = DefaultOutLocation; - - char *DefaultOutIntegratedLocation = "out_integrated.html"; - char *OutIntegratedLocation = DefaultOutIntegratedLocation; - - char *DefaultProjectDir = "."; - char *ProjectDir = DefaultProjectDir; - - char *CINERA_MODE = getenv("CINERA_MODE"); - - bool DefaultForceIntegration = FALSE; - bool ForceIntegration = DefaultForceIntegration; - - char CacheDir[255] = { 0 }; if(getenv("XDG_CACHE_HOME")) { - CopyString(CacheDir, "%s/cinera", getenv("XDG_CACHE_HOME")); + CopyString(DefaultConfig.CacheDir, "%s/cinera", getenv("XDG_CACHE_HOME")); } else { - CopyString(CacheDir, "%s/.cache/cinera", getenv("HOME")); + CopyString(DefaultConfig.CacheDir, "%s/.cache/cinera", getenv("HOME")); } + config Config = DefaultConfig; + if(ArgC < 2) { - PrintUsage(Args[0], DefaultBaseOutputDir, DefaultCSSDir, DefaultImagesDir, DefaultJSDir, DefaultDefaultMedium, DefaultOutLocation, DefaultProjectDir, DefaultTemplateLocation); + PrintUsage(Args[0], DefaultConfig); return 1; } @@ -1530,48 +3087,48 @@ main(int ArgC, char **Args) switch(CommandLineArg) { case 'b': - BaseOutputDir = optarg; + Config.BaseDir = optarg; break; case 'c': - CSSDir = optarg; + Config.CSSDir = optarg; break; case 'f': - ForceIntegration = TRUE; + Config.ForceIntegration = TRUE; break; case 'i': - ImagesDir = optarg; + Config.ImagesDir = optarg; break; case 'j': - JSDir = optarg; + Config.JSDir = optarg; break; case 'm': - DefaultMedium = optarg; + Config.DefaultMedium = optarg; break; case 'o': - OutLocation = optarg; - OutIntegratedLocation = optarg; + Config.OutLocation = optarg; + Config.OutIntegratedLocation = optarg; break; case 'p': - ProjectDir = optarg; + Config.ProjectDir = optarg; break; case 't': - TemplateLocation = optarg; - CINERA_MODE="INTEGRATE"; + Config.TemplateLocation = optarg; + Config.Mode = MODE_INTEGRATE; break; //case 'c': // Override config path, once we even have a default! case 'h': default: - PrintUsage(Args[0], DefaultBaseOutputDir, DefaultCSSDir, DefaultImagesDir, DefaultJSDir, DefaultDefaultMedium, DefaultOutLocation, DefaultProjectDir, DefaultTemplateLocation); + PrintUsage(Args[0], DefaultConfig); return 1; } } - if(StringsDiffer(BaseOutputDir, ".") || StringsDiffer(ProjectDir, ".")) + if(StringsDiffer(Config.BaseDir, ".") || StringsDiffer(Config.ProjectDir, ".")) { - if(StringsDiffer(BaseOutputDir, ".") && StringsDiffer(ProjectDir, ".")) + if(StringsDiffer(Config.BaseDir, ".") && StringsDiffer(Config.ProjectDir, ".")) { - EDITION = EDITION_PROJECT; + Config.Edition = EDITION_PROJECT; } else { @@ -1580,56 +3137,15 @@ main(int ArgC, char **Args) } } - DIR *ProjectDirHandle; - if(!(ProjectDirHandle = opendir(ProjectDir))) + if(Config.CSSDir[StringLength(Config.CSSDir) - 1] == '/') { - perror(Args[0]); - } - - struct dirent *ProjectFiles; - char Filenames[255][255]; // TODO(matt): Figure out how to allow an arbitrary number of filenames here - int FileIndex = 0; - while((ProjectFiles = readdir(ProjectDirHandle))) - { - // TODO(matt): - // - // Test the ProjectFiles->d_name against ".hmml" and, if the filename ends in that string, then stuff that filename into an array - char *Ptr = ProjectFiles->d_name; - Ptr += (StringLength(ProjectFiles->d_name) - StringLength(".hmml")); - if(!(StringsDiffer(Ptr, ".hmml"))) - { - CopyString(Filenames[FileIndex], ProjectFiles->d_name); - ++FileIndex; - } - } - closedir(ProjectDirHandle); - for(int i = 0; i < ArrayCount(Filenames); ++i) - { - if(StringsDiffer(Filenames[i], "")) - { - printf("%s\n", Filenames[i]); - } - } - return 1; - - if((EDITION == EDITION_SINGLE && optind == ArgC) || - // TODO(matt): Test against the number of .hmml files in the project directory - (EDITION == EDITION_PROJECT && 1)) - { - fprintf(stderr, "%s: requires at least one input .hmml file\n", Args[0]); - PrintUsage(Args[0], DefaultBaseOutputDir, DefaultCSSDir, DefaultImagesDir, DefaultJSDir, DefaultDefaultMedium, DefaultOutLocation, DefaultProjectDir, DefaultTemplateLocation); - return 1; - } - - if(CSSDir[StringLength(CSSDir) - 1] == '/') - { - CSSDir[StringLength(CSSDir) - 1] = '\0'; + Config.CSSDir[StringLength(Config.CSSDir) - 1] = '\0'; } bool ValidDefaultMedium = FALSE; for(int i = 0; i < ArrayCount(CategoryMedium); ++i) { - if(!StringsDiffer(DefaultMedium, CategoryMedium[i].Medium)) + if(!StringsDiffer(Config.DefaultMedium, CategoryMedium[i].Medium)) { ValidDefaultMedium = TRUE; break; @@ -1637,202 +3153,31 @@ main(int ArgC, char **Args) } if(!ValidDefaultMedium) { - fprintf(stderr, "Specified default medium \"%s\" not available. Valid media are:\n", DefaultMedium); + fprintf(stderr, "Specified default medium \"%s\" not available. Valid media are:\n", Config.DefaultMedium); for(int i = 0; i < ArrayCount(CategoryMedium); ++i) { fprintf(stderr, " %s\n", CategoryMedium[i].Medium); } - fprintf(stderr, "To have \"%s\" added to the list, contact matt@handmadedev.org\n", DefaultMedium); + fprintf(stderr, "To have \"%s\" added to the list, contact matt@handmadedev.org\n", Config.DefaultMedium); return 1; } - if(ImagesDir[StringLength(ImagesDir) - 1] == '/') + if(Config.ImagesDir[StringLength(Config.ImagesDir) - 1] == '/') { - ImagesDir[StringLength(ImagesDir) - 1] = '\0'; + Config.ImagesDir[StringLength(Config.ImagesDir) - 1] = '\0'; } - if(JSDir[StringLength(JSDir) - 1] == '/') + if(Config.JSDir[StringLength(Config.JSDir) - 1] == '/') { - JSDir[StringLength(JSDir) - 1] = '\0'; + Config.JSDir[StringLength(Config.JSDir) - 1] = '\0'; } - buffer Template; - Template.ID = "Template"; - if(HasTemplate) - { - if(CINERA_MODE && !StringsDiffer(CINERA_MODE, "INTEGRATE")) - { - // TODO(matt): Put the Errors in the MemoryArena - buffer Errors; - Errors.ID = "Errors"; - Errors.Size = Kilobytes(1); - if(!(Errors.Location = malloc(Errors.Size))) - { - perror(Args[0]); return 1; - } - Errors.Ptr = Errors.Location; - bool HaveErrors = FALSE; - - FILE *TemplateFile; - if(!(TemplateFile = fopen(TemplateLocation, "r"))) - { - perror(Args[0]); return 1; - } - - fseek(TemplateFile, 0, SEEK_END); - Template.Size = ftell(TemplateFile); - fseek(TemplateFile, 0, SEEK_SET); - if(!(Template.Location = malloc(Template.Size))) - { - perror(Args[0]); return 1; - } - Template.Ptr = Template.Location; - fread(Template.Location, Template.Size, 1, TemplateFile); - fclose(TemplateFile); - - char *IncludesTag = "__CINERA_INCLUDES__"; - char *TitleTag = "__CINERA_TITLE__"; - char *MenusTag = "__CINERA_MENUS__"; - char *PlayerTag = "__CINERA_PLAYER__"; - char *ScriptTag = "__CINERA_SCRIPT__"; - - bool FoundIncludes = FALSE; - bool FoundMenus = FALSE; - bool FoundPlayer = FALSE; - bool FoundScript = FALSE; - - while(Template.Ptr - Template.Location < Template.Size) - { - if(*Template.Ptr == '!' && (Template.Ptr > Template.Location && !StringsDifferT(" tag\n"); - HaveErrors = TRUE; - } - FoundIncludes = TRUE; - while(Template.Ptr - Template.Location < Template.Size) - { - if(!StringsDifferT("-->", Template.Ptr, 0)) - { - Template.Ptr += StringLength("-->"); - break; - } - ++Template.Ptr; - } - break; - } - - else if(!(StringsDifferT(TitleTag, Template.Ptr, 0))) - { - while(Template.Ptr - Template.Location < Template.Size) - { - if(!StringsDifferT("-->", Template.Ptr, 0)) - { - Template.Ptr += StringLength("-->"); - break; - } - ++Template.Ptr; - } - break; - } - else if(!(StringsDifferT(MenusTag, Template.Ptr, 0))) - { - if(!ForceIntegration && FoundMenus == TRUE) - { - CopyStringToBuffer(&Errors, "Template contains more than one tag\n"); - HaveErrors = TRUE; - } - FoundMenus = TRUE; - while(Template.Ptr - Template.Location < Template.Size) - { - if(!StringsDifferT("-->", Template.Ptr, 0)) - { - Template.Ptr += StringLength("-->"); - break; - } - ++Template.Ptr; - } - break; - } - else if(!(StringsDifferT(PlayerTag, Template.Ptr, 0))) - { - if(!ForceIntegration && FoundPlayer == TRUE) - { - CopyStringToBuffer(&Errors, "Template contains more than one tag\n"); - HaveErrors = TRUE; - } - FoundPlayer = TRUE; - while(Template.Ptr - Template.Location < Template.Size) - { - if(!StringsDifferT("-->", Template.Ptr, 0)) - { - Template.Ptr += StringLength("-->"); - break; - } - ++Template.Ptr; - } - break; - } - else if(!(StringsDifferT(ScriptTag, Template.Ptr, 0))) - { - if(!ForceIntegration && FoundPlayer == FALSE) - { - CopyStringToBuffer(&Errors, " must come after \n"); - HaveErrors = TRUE; - } - if(!ForceIntegration && FoundScript == TRUE) - { - CopyStringToBuffer(&Errors, "Template contains more than one tag\n"); - HaveErrors = TRUE; - } - FoundScript = TRUE; - while(Template.Ptr - Template.Location < Template.Size) - { - if(!StringsDifferT("-->", Template.Ptr, 0)) - { - Template.Ptr += StringLength("-->"); - break; - } - ++Template.Ptr; - } - break; - } - else if(!StringsDifferT("-->", Template.Ptr, 0)) - { - break; - } - ++Template.Ptr; - } - } - else - { - ++Template.Ptr; - } - } - - if(!HaveErrors && FoundIncludes && FoundMenus && FoundPlayer && FoundScript) - { - Template.Ptr = Template.Location; - free(Errors.Location); - } - else - { - if(!ForceIntegration) - { - if(!FoundIncludes){ CopyStringToBuffer(&Errors, "Template must include one tag\n"); }; - if(!FoundMenus){ CopyStringToBuffer(&Errors, "Template must include one tag\n"); }; - if(!FoundPlayer){ CopyStringToBuffer(&Errors, "Template must include one tag\n"); }; - if(!FoundScript){ CopyStringToBuffer(&Errors, "Template must include one tag\n"); }; - fprintf(stderr, "%s", Errors.Location); - free(Template.Location); free(Errors.Location); return 1; - } - } - } - } + // NOTE(matt): Templating + // + // Config will contain paths of multiple templates + // App is running all the time, and picking up changes to the config as we go + // If we find a new template, we first of all validate it + // In our case here, we just want to straight up validate a template if Config.Mode == MODE_INTEGRATE + // And, in that same state, we gotta keep a Template buffer around // NOTE(matt): Init MemoryArena buffer MemoryArena; @@ -1840,1501 +3185,126 @@ main(int ArgC, char **Args) if(!(MemoryArena.Location = calloc(MemoryArena.Size, 1))) { perror(Args[0]); - free(Template.Location); + //free(Template.Location); return 1; } - int ClaimedMemory = 0; + MemoryArena.Ptr = MemoryArena.Location; // NOTE(matt): Setup buffers and ptrs - char *InPtr; - -#if CONFIG - buffer Config; -#endif + // TODO(matt) + //char *InPtr; // NOTE(matt): Tree structure of buffer dependencies - // Master - // Includes + // IncludesPlayer // Menus - // QuoteMenu - // ReferenceMenu - // FilterMenu - // FilterTopics - // FilterMedia - // CreditsMenu // Player - // Annotation - // AnnotationHeader - // AnnotationClass - // AnnotationData - // Text - // Category // Script - // FilterState - buffer Master; - buffer Includes; + // TODO(matt) +#if 1 + buffers CollationBuffers; + ClaimBuffer(&MemoryArena, &CollationBuffers.IncludesPlayer, "IncludesPlayer", Kilobytes(1)); + ClaimBuffer(&MemoryArena, &CollationBuffers.Menus, "Menus", Kilobytes(24)); + ClaimBuffer(&MemoryArena, &CollationBuffers.Player, "Player", Kilobytes(256)); + ClaimBuffer(&MemoryArena, &CollationBuffers.Script, "Script", Kilobytes(8)); - buffer Menus; - buffer QuoteMenu; - buffer ReferenceMenu; - buffer FilterMenu; - buffer FilterTopics; - buffer FilterMedia; - buffer CreditsMenu; + ClaimBuffer(&MemoryArena, &CollationBuffers.IncludesIndex, "IncludesIndex", Kilobytes(1)); + ClaimBuffer(&MemoryArena, &CollationBuffers.Index, "Index", Kilobytes(8)); + *CollationBuffers.Title = '\0'; - buffer Player; - buffer Annotation; - buffer AnnotationHeader; - buffer AnnotationClass; - buffer AnnotationData; - buffer Text; - buffer TopicDots; + buffer Template; + Template.ID = "Template"; - buffer Script; - buffer FilterState; - - //HMMLToBuffers(&CollationBuffers, Filename); - - for(int FileIndex = optind; FileIndex < ArgC; ++FileIndex) + if(Config.Mode == MODE_INTEGRATE) { - // NOTE(matt): Start of HMMLToBuffers() - FILE *InFile; - if(!(InFile = fopen(Args[FileIndex], "r"))) + ValidateTemplate(&Template, Config); + } + + // NOTE(matt) + // + // Single Edition == Loop over Args[FileIndex] + // Project Edition == Loop over Config.ProjectDir + // + // Integrating or not + + if(Config.Edition == EDITION_PROJECT) + { + DIR *ProjectDirHandle; + if(!(ProjectDirHandle = opendir(Config.ProjectDir))) { - perror(Args[FileIndex]); - free(MemoryArena.Location); - free(Template.Location); + perror(Config.ProjectDir); + // TODO(matt): + // Cleanup + } + + struct dirent *ProjectFiles; + int FileIndex = 0; + while((ProjectFiles = readdir(ProjectDirHandle))) + { + // TODO(matt): + char *Ptr = ProjectFiles->d_name; + Ptr += (StringLength(ProjectFiles->d_name) - StringLength(".hmml")); + if(!(StringsDiffer(Ptr, ".hmml"))) + { + *Ptr = '\0'; + char BaseFilename[255]; + CopyString(BaseFilename, ProjectFiles->d_name); + *Ptr = '.'; + + char OutputDir[255]; + CopyString(OutputDir, "%s/%s", Config.BaseDir, BaseFilename); + // TODO(matt): Check + char OutputPath[255]; + CopyString(OutputPath, "%s/index.html", OutputDir); + DIR *OutputDirectoryHandle; + if(!(OutputDirectoryHandle = opendir(OutputDir))) + { + MakeDir(OutputDir); + } + closedir(OutputDirectoryHandle); + + HMMLToBuffers(&MemoryArena, &CollationBuffers, Config, ProjectFiles->d_name); + BuildIndex(&MemoryArena, &CollationBuffers, Config, BaseFilename, CollationBuffers.Title); + ++FileIndex; + BuffersToHTML(&MemoryArena, CollationBuffers, Template, Config, OutputPath); + } + } + closedir(ProjectDirHandle); + + if(Config.Edition == EDITION_SINGLE && optind == ArgC) + { + fprintf(stderr, "%s: requires at least one input .hmml file\n", Args[0]); + PrintUsage(Args[0], DefaultConfig); return 1; } - - HMML_Output HMML = hmml_parse_file(InFile); - fclose(InFile); - -#if CONFIG - ClaimBuffer(MemoryArena, &ClaimedMemory, &Config, "Config", Kilobytes(1)); -#endif - - if(HMML.well_formed) - { -#if DEBUG - printf( - "================================================================================\n" - "%s\n" - "================================================================================\n", - Args[FileIndex]); -#endif - // NOTE(matt): Tree structure of "global" buffer dependencies - // Master - // Includes - // Menus - // QuoteMenu - // ReferenceMenu - // FilterMenu - // FilterTopics - // FilterMedia - // CreditsMenu - // Player - // Script - // FilterState - - ClaimBuffer(&MemoryArena, &ClaimedMemory, &Master, "Master", Kilobytes(512)); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &Includes, "Includes", Kilobytes(1)); - - ClaimBuffer(&MemoryArena, &ClaimedMemory, &Menus, "Menus", Kilobytes(24)); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &QuoteMenu, "QuoteMenu", Kilobytes(16)); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &ReferenceMenu, "ReferenceMenu", Kilobytes(16)); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &FilterMenu, "FilterMenu", Kilobytes(16)); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &FilterTopics, "FilterTopics", Kilobytes(8)); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &FilterMedia, "FilterMedia", Kilobytes(8)); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &CreditsMenu, "CreditsMenu", Kilobytes(8)); - - ClaimBuffer(&MemoryArena, &ClaimedMemory, &Player, "Player", Kilobytes(256)); - - ClaimBuffer(&MemoryArena, &ClaimedMemory, &Script, "Script", Kilobytes(8)); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &FilterState, "FilterState", Kilobytes(4)); - - ref_info ReferencesArray[200] = { 0 }; - categories Topics = { 0 }; - categories Media = { 0 }; - - bool HasQuoteMenu = FALSE; - bool HasReferenceMenu = FALSE; - bool HasFilterMenu = FALSE; - bool HasCreditsMenu = FALSE; - - int QuoteIdentifier = 0x3b1; - int RefIdentifier = 1; - int UniqueRefs = 0; - - CopyStringToBuffer(&Menus, - "
\n" - " ", HMML.metadata.project); - CopyStringToBufferHTMLSafe(&Menus, HMML.metadata.title); - CopyStringToBuffer(&Menus, "\n" - " ⚠ Click here to regain focus ⚠\n"); - - CopyStringToBuffer(&Player, - "
\n" - "
\n" - "
\n", HMML.metadata.id, HMML.metadata.project); - - int CreditsErrorCode = BuildCredits(&CreditsMenu, &HasCreditsMenu, ImagesDir, HMML.metadata); - if(CreditsErrorCode) - { - 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 - printf(" --- Entering Annotations Loop ---\n"); -#endif - - for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex) - { -#if DEBUG - printf("%d\n", AnnotationIndex); -#endif - HMML_Annotation *Anno = HMML.annotations + AnnotationIndex; - categories LocalTopics = { 0 }; - categories LocalMedia = { 0 }; - bool HasQuote = FALSE; - bool HasReference = FALSE; - - quote_info QuoteInfo = { 0 }; - - // NOTE(matt): Tree structure of "annotation local" buffer dependencies - // Annotation - // AnnotationHeader - // AnnotationClass - // AnnotationData - // Text - // TopicDots - - ClaimBuffer(&MemoryArena, &ClaimedMemory, &Annotation, "Annotation", Kilobytes(8)); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationHeader, "AnnotationHeader", 512); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationClass, "AnnotationClass", 256); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationData, "AnnotationData", 512); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &Text, "Text", Kilobytes(4)); - ClaimBuffer(&MemoryArena, &ClaimedMemory, &TopicDots, "TopicDots", 512); - - CopyStringToBuffer(&AnnotationHeader, - "
time)); - - CopyStringToBuffer(&AnnotationClass, - " class=\"marker"); - - if(Anno->author) - { - if(!HasFilterMenu) - { - HasFilterMenu = TRUE; - } - InsertCategory(&Topics, &Media, "authored"); - CopyStringToBuffer(&AnnotationClass, " authored"); - hsl_colour AuthorColour; - StringToColourHash(&AuthorColour, Anno->author); - if(EDITION == EDITION_NETWORK) - { - fprintf(stderr, "%s:%d - TODO(matt): Implement author hoverbox\n", __FILE__, __LINE__); - // NOTE(matt): We should get instructions on how to get this info in the config - CopyStringToBuffer(&Text, - "%s ", - Anno->author, - AuthorColour.Hue, AuthorColour.Saturation, AuthorColour.Lightness, - AuthorColour.Hue, AuthorColour.Saturation, - Anno->author); - } - else - { - CopyStringToBuffer(&Text, - "%s ", - AuthorColour.Hue, AuthorColour.Saturation, AuthorColour.Lightness, - AuthorColour.Hue, AuthorColour.Saturation, - Anno->author); - } - - } - - InPtr = Anno->text; - - int MarkerIndex = 0, RefIndex = 0; - while(*InPtr || RefIndex < Anno->reference_count) - { - if(MarkerIndex < Anno->marker_count && - InPtr - Anno->text == Anno->markers[MarkerIndex].offset) - { - char *Readable = Anno->markers[MarkerIndex].parameter - ? Anno->markers[MarkerIndex].parameter - : Anno->markers[MarkerIndex].marker; - if(Anno->markers[MarkerIndex].type == HMML_MEMBER) - { - hsl_colour MemberColour; - StringToColourHash(&MemberColour, Anno->markers[MarkerIndex].marker); - if(EDITION == EDITION_NETWORK) - { - fprintf(stderr, "%s:%d - TODO(matt): Implement member hoverbox\n", __FILE__, __LINE__); - // NOTE(matt): We should get instructions on how to get this info in the config - CopyStringToBuffer(&Text, - "%.*s", - Anno->markers[MarkerIndex].marker, - MemberColour.Hue, MemberColour.Saturation, MemberColour.Lightness, - MemberColour.Hue, MemberColour.Saturation, - StringLength(Readable), InPtr); - } - else - { - CopyStringToBuffer(&Text, - "%.*s", - MemberColour.Hue, MemberColour.Saturation, MemberColour.Lightness, - MemberColour.Hue, MemberColour.Saturation, - StringLength(Readable), InPtr); - } - - InPtr += StringLength(Readable); - ++MarkerIndex; - } - else if(Anno->markers[MarkerIndex].type == HMML_PROJECT) - { - hsl_colour ProjectColour; - StringToColourHash(&ProjectColour, Anno->markers[MarkerIndex].marker); - if(EDITION == EDITION_NETWORK) - { - fprintf(stderr, "%s:%d - TODO(matt): Implement project hoverbox\n", __FILE__, __LINE__); - // NOTE(matt): We should get instructions on how to get this info in the config - CopyStringToBuffer(&Text, - "%s", - Anno->markers[MarkerIndex].marker, - ProjectColour.Hue, ProjectColour.Saturation, ProjectColour.Lightness, - ProjectColour.Hue, ProjectColour.Saturation, - Readable); - } - else - { - CopyStringToBuffer(&Text, - "%s", - ProjectColour.Hue, ProjectColour.Saturation, ProjectColour.Lightness, - ProjectColour.Hue, ProjectColour.Saturation, - Readable); - } - InPtr += StringLength(Readable); - ++MarkerIndex; - } - else if(Anno->markers[MarkerIndex].type == HMML_CATEGORY) - { - GenerateTopicColours(Anno->markers[MarkerIndex].marker, CSSDir); - if(!HasFilterMenu) - { - HasFilterMenu = TRUE; - } - InsertCategory(&Topics, &Media, Anno->markers[MarkerIndex].marker); // Global - InsertCategory(&LocalTopics, &LocalMedia, Anno->markers[MarkerIndex].marker); // Local - } - } - - while(RefIndex < Anno->reference_count && - InPtr - Anno->text == Anno->references[RefIndex].offset) - { - HMML_Reference *CurrentRef = Anno->references + RefIndex; - if(!HasReferenceMenu) - { - CopyStringToBuffer(&ReferenceMenu, - "
\n" - " References ▼\n" - "
\n" - "
\n"); - - if(BuildReference(ReferencesArray, RefIdentifier, UniqueRefs, *CurrentRef, *Anno) == 1) - { - fprintf(stderr, "%s:%d: Cannot process new combination of reference info\n" - "\n" - "Either tweak your annotation, or contact matt@handmadedev.org\n" - "mentioning the ref node you want to write and how you want it to\n" - "appear in the references menu\n", Args[FileIndex], Anno->line); - hmml_free(&HMML); - free(MemoryArena.Location); - free(Template.Location); - return 1; - } - ++ReferencesArray[RefIdentifier - 1].IdentifierCount; - ++UniqueRefs; - - HasReferenceMenu = TRUE; - } - else - { - for(int i = 0; i < UniqueRefs; ++i) - { - if(ReferencesArray[i].IdentifierCount == REF_MAX_IDENTIFIER) - { - fprintf(stderr, "%s:%d: Too many timecodes associated with one reference (increase REF_MAX_IDENTIFIER)\n", Args[FileIndex], Anno->line); - hmml_free(&HMML); - free(MemoryArena.Location); - free(Template.Location); - return 1; - } - if(CurrentRef->isbn) - { - if(!StringsDiffer(CurrentRef->isbn, ReferencesArray[i].ID)) - { - CopyString(ReferencesArray[i].Identifier[ReferencesArray[i].IdentifierCount].Timecode, Anno->time); - ReferencesArray[i].Identifier[ReferencesArray[i].IdentifierCount].Identifier = RefIdentifier; - ++ReferencesArray[i].IdentifierCount; - goto AppendedIdentifier; - } - } - else if(CurrentRef->url) - { - if(!StringsDiffer(CurrentRef->url, ReferencesArray[i].ID)) - { - CopyString(ReferencesArray[i].Identifier[ReferencesArray[i].IdentifierCount].Timecode, Anno->time); - ReferencesArray[i].Identifier[ReferencesArray[i].IdentifierCount].Identifier = RefIdentifier; - ++ReferencesArray[i].IdentifierCount; - goto AppendedIdentifier; - } - } - else - { - fprintf(stderr, "%s:%d: Reference must have an ISBN or URL\n", Args[FileIndex], Anno->line); - hmml_free(&HMML); - free(MemoryArena.Location); - free(Template.Location); - return 1; - } - } - - if(BuildReference(ReferencesArray, RefIdentifier, UniqueRefs, *CurrentRef, *Anno) == 1) - { - fprintf(stderr, "%s:%d: Cannot process new combination of reference info\n" - "\n" - "Either tweak your annotation, or contact matt@handmadedev.org\n" - "mentioning the ref node you want to write and how you want it to\n" - "appear in the references menu\n", Args[FileIndex], Anno->line); - hmml_free(&HMML); - free(MemoryArena.Location); - free(Template.Location); - return 1; - } - ++ReferencesArray[UniqueRefs].IdentifierCount; - ++UniqueRefs; - } - AppendedIdentifier: - if(!HasReference) - { - if(CurrentRef->isbn) - { - CopyStringToBuffer(&AnnotationData, " data-ref=\"%s", CurrentRef->isbn); - } - else if(CurrentRef->url) - { - CopyStringToBuffer(&AnnotationData, " data-ref=\"%s", CurrentRef->url); - } - else - { - fprintf(stderr, "%s:%d: Reference must have an ISBN or URL\n", Args[FileIndex], Anno->line); - hmml_free(&HMML); - free(MemoryArena.Location); - free(Template.Location); - return 1; - } - - HasReference = TRUE; - } - else - { - if(CurrentRef->isbn) - { - CopyStringToBuffer(&AnnotationData, ",%s", CurrentRef->isbn); - } - else if(CurrentRef->url) - { - CopyStringToBuffer(&AnnotationData, ",%s", CurrentRef->url); - } - else - { - fprintf(stderr, "%s:%d: Reference must have an ISBN or URL", Args[FileIndex], Anno->line); - hmml_free(&HMML); - free(MemoryArena.Location); - free(Template.Location); - return 1; - } - } - - if(Anno->references[RefIndex].offset == Anno->references[RefIndex-1].offset) - { - CopyStringToBuffer(&Text, ",%d", RefIdentifier); - } - else - { - CopyStringToBuffer(&Text, "%d", RefIdentifier); - } - - ++RefIndex; - ++RefIdentifier; - } - - if(*InPtr) - { - switch(*InPtr) - { - case '<': - CopyStringToBuffer(&Text, "<"); - InPtr++; - break; - case '>': - CopyStringToBuffer(&Text, ">"); - InPtr++; - break; - case '&': - CopyStringToBuffer(&Text, "&"); - InPtr++; - break; - case '\"': - CopyStringToBuffer(&Text, """); - InPtr++; - break; - case '\'': - CopyStringToBuffer(&Text, "'"); - InPtr++; - break; - default: - *Text.Ptr++ = *InPtr++; - *Text.Ptr = '\0'; - break; - } - } - } - - if(Anno->is_quote) - { - if(!HasQuoteMenu) - { - CopyStringToBuffer(&QuoteMenu, - "
\n" - " Quotes ▼\n" - "
\n" - "
\n"); - - HasQuoteMenu = TRUE; - } - - if(!HasReference) - { - CopyStringToBuffer(&AnnotationData, " data-ref=\"&#%d;", QuoteIdentifier); - } - else - { - CopyStringToBuffer(&AnnotationData, ",&#%d;", QuoteIdentifier); - } - - HasQuote = TRUE; - - char *Speaker = Anno->quote.author ? Anno->quote.author : HMML.metadata.stream_username ? HMML.metadata.stream_username : HMML.metadata.member; - if(BuildQuote(&QuoteInfo, - Speaker, - Anno->quote.id, - CacheDir) == 1) - { - fprintf(stderr, "%s:%d: Quote #%s %d not found. Unlucky!\n", - Args[FileIndex], - Anno->line, - Speaker, - Anno->quote.id); - hmml_free(&HMML); - free(MemoryArena.Location); - free(Template.Location); - return 1; - } - - CopyStringToBuffer(&QuoteMenu, - " \n" - " \n" - " \n" - "
Quote %d
\n" - "
", - Speaker, - Anno->quote.id, - QuoteIdentifier, - Anno->quote.id); - - CopyStringToBufferCSVSafe(&QuoteMenu, QuoteInfo.Text); - - CopyStringToBuffer(&QuoteMenu, "
\n" - " \n" - "
\n" - "
\n" - " [&#%d;]%s\n" - "
\n" - "
\n" - "
\n", - Speaker, - QuoteInfo.Date, - TimecodeToSeconds(Anno->time), - QuoteIdentifier, - Anno->time); - if(!Anno->text[0]) - { - CopyStringToBuffer(&Text, "“"); - CopyStringToBufferHTMLSafe(&Text, QuoteInfo.Text); - CopyStringToBuffer(&Text, "”"); - } - CopyStringToBuffer(&Text, "&#%d;", QuoteIdentifier); - ++QuoteIdentifier; - } - - while(MarkerIndex < Anno->marker_count) - { - GenerateTopicColours(Anno->markers[MarkerIndex].marker, CSSDir); - if(!HasFilterMenu) - { - HasFilterMenu = TRUE; - } - if(Anno->markers[MarkerIndex].marker) - { - InsertCategory(&Topics, &Media, Anno->markers[MarkerIndex].marker); - } - InsertCategory(&LocalTopics, &LocalMedia, Anno->markers[MarkerIndex].marker); - ++MarkerIndex; - } - - if(LocalMedia.Count == 0) - { - InsertCategory(&Topics, &Media, DefaultMedium); - InsertCategory(&LocalTopics, &LocalMedia, DefaultMedium); - } - - BuildCategories(&AnnotationClass, &TopicDots, LocalTopics, LocalMedia, &MarkerIndex); - CopyBuffer(&AnnotationHeader, &AnnotationClass); - - if(HasQuote || HasReference) - { - CopyStringToBuffer(&AnnotationData, "\""); - CopyBuffer(&AnnotationHeader, &AnnotationData); - } - CopyStringToBuffer(&AnnotationHeader, ">\n"); - - CopyBuffer(&Annotation, &AnnotationHeader); - CopyStringToBuffer(&Annotation, - "
%s", - Anno->time); - - CopyBuffer(&Annotation, &Text); - - if(LocalTopics.Count > 0) - { - CopyBuffer(&Annotation, &TopicDots); - } - - CopyStringToBuffer(&Annotation, "
\n" - "
\n" - "
%s", - Anno->time); - - CopyBuffer(&Annotation, &Text); - - CopyStringToBuffer(&Annotation, "
\n" - "
\n" - "
\n" - "
%s", - Anno->time); - - CopyBuffer(&Annotation, &Text); - - CopyStringToBuffer(&Annotation, "
\n" - "
\n" - "
\n"); - - CopyBuffer(&Player, &Annotation); - - // NOTE(matt): Tree structure of "annotation local" buffer dependencies - // TopicDots - // Text - // AnnotationData - // AnnotationClass - // AnnotationHeader - // Annotation - - DeclaimBuffer(&TopicDots, &ClaimedMemory); - DeclaimBuffer(&Text, &ClaimedMemory); - DeclaimBuffer(&AnnotationData, &ClaimedMemory); - DeclaimBuffer(&AnnotationClass, &ClaimedMemory); - DeclaimBuffer(&AnnotationHeader, &ClaimedMemory); - DeclaimBuffer(&Annotation, &ClaimedMemory); - } - -#if DEBUG - printf(" --- End of Annotations Loop ---\n\n"); -#endif - if(HasQuoteMenu) - { - CopyStringToBuffer(&QuoteMenu, - "
\n" - "
\n"); - CopyBuffer(&Menus, &QuoteMenu); - } - - if(HasReferenceMenu) - { - for(int i = 0; i < UniqueRefs; ++i) - { - CopyStringToBuffer(&ReferenceMenu, - " \n" - " \n", - ReferencesArray[i].ID, - ReferencesArray[i].URL); - - if(*ReferencesArray[i].Source) - { - CopyStringToBuffer(&ReferenceMenu, - "
"); - CopyStringToBufferHTMLSafe(&ReferenceMenu, ReferencesArray[i].Source); - CopyStringToBuffer(&ReferenceMenu, "
\n" - "
"); - CopyStringToBufferHTMLSafe(&ReferenceMenu, ReferencesArray[i].RefTitle); - CopyStringToBuffer(&ReferenceMenu, "
\n"); - } - else - { - CopyStringToBuffer(&ReferenceMenu, - "
"); - CopyStringToBufferHTMLSafe(&ReferenceMenu, ReferencesArray[i].RefTitle); - CopyStringToBuffer(&ReferenceMenu, "
\n"); - } - CopyStringToBuffer(&ReferenceMenu, - "
\n"); - - for(int j = 0; j < ReferencesArray[i].IdentifierCount;) - { - CopyStringToBuffer(&ReferenceMenu, - "
\n "); - for(int k = 0; k < 3 && j < ReferencesArray[i].IdentifierCount; ++k, ++j) - { - CopyStringToBuffer(&ReferenceMenu, - "[%d]%s", - TimecodeToSeconds(ReferencesArray[i].Identifier[j].Timecode), - ReferencesArray[i].Identifier[j].Identifier, - ReferencesArray[i].Identifier[j].Timecode); - } - CopyStringToBuffer(&ReferenceMenu, "\n" - "
\n"); - } - - CopyStringToBuffer(&ReferenceMenu, - "
\n"); - } - - CopyStringToBuffer(&ReferenceMenu, - "
\n" - "
\n"); - CopyBuffer(&Menus, &ReferenceMenu); - } - - if(HasFilterMenu) - { - CopyStringToBuffer(&FilterState, - " var filterState = {\n"); - for(int i = 0; i < Topics.Count; ++i) - { - CopyStringToBuffer(&FilterState, "\"%s\":\t{ \"type\": \"%s\",\t\"off\": false },\n", - Topics.Category[i].Marker, "topic"); - } - for(int i = 0; i < Media.Count; ++i) - { - CopyStringToBuffer(&FilterState, "\"%s\":\t{ \"type\": \"%s\",\t\"off\": false },\n", - Media.Category[i].Marker, "medium"); - } - CopyStringToBuffer(&FilterState, - " };\n"); - - CopyStringToBuffer(&FilterMenu, - "
\n" - " \n" - "
\n" - "
Filter mode:
\n" - "
\n", ImagesDir); - - - if(Topics.Count > 0) - { - CopyStringToBuffer(&FilterMenu, - "
\n" - "
Topics
\n"); - for(int i = 0; i < Topics.Count; ++i) - { - CopyStringToBuffer(&FilterTopics, - "
\n" - " %s\n" - "
\n", - Topics.Category[i].Marker, - Topics.Category[i].Marker, - Topics.Category[i].Marker); - } - CopyStringToBuffer(&FilterTopics, - "
\n"); - CopyBuffer(&FilterMenu, &FilterTopics); - } - - if(Media.Count > 0) - { - CopyStringToBuffer(&FilterMedia, - "
\n" - "
Media
\n"); - for(int i = 0; i < Media.Count; ++i) - { - int j; - for(j = 0; j < ArrayCount(CategoryMedium); ++j) - { - if(!StringsDiffer(Media.Category[i].Marker, CategoryMedium[j].Medium)) - { - break; - } - } - - CopyStringToBuffer(&FilterMedia, - "
\n" - " %s%s\n" - "
\n", - Media.Category[i].Marker, - CategoryMedium[j].Icon, - CategoryMedium[j].WrittenName - ); - } - CopyStringToBuffer(&FilterMedia, - "
\n"); - CopyBuffer(&FilterMenu, &FilterMedia); - } - - CopyStringToBuffer(&FilterMenu, - "
\n" - "
\n" - "
\n"); - - CopyBuffer(&Menus, &FilterMenu); - - } - - if(HasCreditsMenu) - { - CopyBuffer(&Menus, &CreditsMenu); - } - -#if CONFIG - // TODO(matt): Here is where I test ParseConfig - ParseConfig(&Config, HMML.metadata.annotator); -#endif - CopyStringToBuffer(&Menus, - "
\n" - " ?\n" - "
\n" - " ?

Keyboard Navigation

\n" - "\n" - /* NOTE(matt) */ - "

Global Keys

\n" - " W, A, P / S, D, N Jump to previous / next marker
\n"); - - if(HasFilterMenu) - { - CopyStringToBuffer(&Menus, - " z Toggle filter mode V Revert filter to original state\n"); - } - else - { - CopyStringToBuffer(&Menus, - " z Toggle filter mode V Revert filter to original state\n"); - } - - CopyStringToBuffer(&Menus, - "\n" - "

Menu toggling

\n"); - - if(HasQuoteMenu) - { - CopyStringToBuffer(&Menus, - " q Quotes\n"); - } - else - { - CopyStringToBuffer(&Menus, - " q Quotes\n"); - } - - if(HasReferenceMenu) - { - CopyStringToBuffer(&Menus, - " r References\n"); - } - else - { - CopyStringToBuffer(&Menus, - " r References\n"); - } - - if(HasFilterMenu) - { - CopyStringToBuffer(&Menus, - " f Filter\n"); - } - else - { - CopyStringToBuffer(&Menus, - " f Filter\n"); - } - - if(HasCreditsMenu) - { - CopyStringToBuffer(&Menus, - " c Credits\n"); - } - else - { - CopyStringToBuffer(&Menus, - " c Credits\n"); - } - - CopyStringToBuffer(&Menus, - "\n" - "

Movement

\n" - "
\n" - "
\n" - "
\n" - " a\n" - "
\n" - "
\n" - " w
\n" - " s\n" - "
\n" - "
\n" - " d\n" - "
\n" - "
\n" - "
\n" - " h\n" - " j\n" - " k\n" - " l\n" - "
\n" - "
\n" - "
\n" - " \n" - "
\n" - "
\n" - "
\n" - " \n" - "
\n" - "
\n" - " \n" - "
\n" - "
\n" - "
\n" - "
\n"); - - // NOTE(matt); - - if(HasQuoteMenu) - { - CopyStringToBuffer(&Menus, - "

Quotes "); - if(HasReferenceMenu) - { - CopyStringToBuffer(&Menus, "and References Menus

\n"); - } - else - { - CopyStringToBuffer(&Menus, "and References Menus\n"); - } - } - else - { - CopyStringToBuffer(&Menus, - "

Quotes"); - if(HasReferenceMenu) - { - CopyStringToBuffer(&Menus, " and References Menus

\n"); - } - else - { - CopyStringToBuffer(&Menus, " and References Menus\n"); - } - } - - if(HasQuoteMenu || HasReferenceMenu) - { - CopyStringToBuffer(&Menus, - " Enter Jump to timecode
\n"); - } - else - { - CopyStringToBuffer(&Menus, - " Enter Jump to timecode
\n"); - } - - CopyStringToBuffer(&Menus, "\n"); - - if(HasQuoteMenu) - { - CopyStringToBuffer(&Menus, - "

Quotes"); - if(HasReferenceMenu) - { - CopyStringToBuffer(&Menus, ", References "); - if(HasCreditsMenu) - { - CopyStringToBuffer(&Menus, "and Credits Menus

"); - } - else - { - CopyStringToBuffer(&Menus, "and Credits Menus"); - } - } - else - { - CopyStringToBuffer(&Menus, ", References "); - if(HasCreditsMenu) - { - CopyStringToBuffer(&Menus, "and Credits Menus"); - } - else - { - CopyStringToBuffer(&Menus, "and Credits Menus"); - } - } - } - else - { - CopyStringToBuffer(&Menus, - "

Quotes"); - if(HasReferenceMenu) - { - CopyStringToBuffer(&Menus, ", References "); - if(HasCreditsMenu) - { - CopyStringToBuffer(&Menus, "and Credits Menus

"); - } - else - { - CopyStringToBuffer(&Menus, "and Credits Menus"); - } - } - else - { - CopyStringToBuffer(&Menus, ", References "); - if(HasCreditsMenu) - { - CopyStringToBuffer(&Menus, "and Credits Menus"); - } - else - { - CopyStringToBuffer(&Menus, "and Credits Menus"); - } - } - } - - CopyStringToBuffer(&Menus, "\n"); - - if(HasQuoteMenu || HasReferenceMenu || HasCreditsMenu) - { - CopyStringToBuffer(&Menus, - " o Open URL (in new tab)\n"); - } - else - { - CopyStringToBuffer(&Menus, - " o Open URL (in new tab)\n"); - } - - CopyStringToBuffer(&Menus, - "\n"); - - if(HasFilterMenu) - { - CopyStringToBuffer(&Menus, - "

Filter Menu

\n" - " x, Space Toggle category and focus next
\n" - " X, ShiftSpace Toggle category and focus previous
\n" - " v Invert topics / media as per focus\n"); - } - else - { - CopyStringToBuffer(&Menus, - "

Filter Menu

\n" - " x, Space Toggle category and focus next
\n" - " X, ShiftSpace Toggle category and focus previous
\n" - " v Invert topics / media as per focus\n"); - } - - CopyStringToBuffer(&Menus, "\n"); - - if(HasCreditsMenu) - { - CopyStringToBuffer(&Menus, - "

Credits Menu

\n" - " Enter Open URL (in new tab)
\n"); - } - else - { - CopyStringToBuffer(&Menus, - "

Credits Menu

\n" - " Enter Open URL (in new tab)
\n"); - } - - CopyStringToBuffer(&Menus, - "
\n" - "
\n" - - "
"); - - CopyStringToBuffer(&Player, - "
\n" - "
"); - - // TODO(matt): Maybe do something about indentation levels - CopyStringToBuffer(&Includes, - "\n" - " \n" - " \n" - "\n" - " \n" - " \n", - CSSDir, - CSSDir, - HMML.metadata.project, - CSSDir); - - if(Topics.Count || Media.Count) - { - CopyStringToBuffer(&Includes, - " 0) - { - for(int i = 0; i < Topics.Count; ++i) - { - CopyStringToBuffer(&Includes, "%s, ", Topics.Category[i].Marker); - } - } - - if(Media.Count > 0) - { - for(int i = 0; i < Media.Count; ++i) - { - CopyStringToBuffer(&Includes, "%s, ", Media.Category[i].WrittenText); - } - } - - Includes.Ptr -= 2; - CopyStringToBuffer(&Includes, "\">\n\n"); - } - - CopyStringToBuffer(&Includes, - " \n", - JSDir); - - CopyStringToBuffer(&Script, - " "); - - // NOTE(matt): Don't include this in HMMLToBuffers() -#if DEBUG - printf("Buffer Collation\n\n"); -#endif - - if(CINERA_MODE && !StringsDiffer(CINERA_MODE, "INTEGRATE")) - { - buffer Output; - Output.Size = Template.Size + Master.Size; - Output.ID = "Output"; - if(!(Output.Location = malloc(Output.Size))) - { - perror(Args[0]); free(Template.Location); hmml_free(&HMML); free(MemoryArena.Location); return 1; - } - Output.Ptr = Output.Location; - - char *IncludesTag = "__CINERA_INCLUDES__"; - char *TitleTag = "__CINERA_TITLE__"; - char *MenusTag = "__CINERA_MENUS__"; - char *PlayerTag = "__CINERA_PLAYER__"; - char *ScriptTag = "__CINERA_SCRIPT__"; - - while(Template.Ptr - Template.Location < Template.Size) - { - if(*Template.Ptr == '!' && (Template.Ptr > Template.Location && !StringsDifferT("", Template.Ptr, 0)) - { - Template.Ptr += StringLength("-->"); - break; - } - ++Template.Ptr; - } - break; - } - - else if(!(StringsDifferT(TitleTag, Template.Ptr, 0))) - { - Output.Ptr = CommentStart; - CopyStringToBuffer(&Output, HMML.metadata.title); - while(Template.Ptr - Template.Location < Template.Size) - { - if(!StringsDifferT("-->", Template.Ptr, 0)) - { - Template.Ptr += StringLength("-->"); - break; - } - ++Template.Ptr; - } - break; - } - else if(!(StringsDifferT(MenusTag, Template.Ptr, 0))) - { - Output.Ptr = CommentStart; - CopyBuffer(&Output, &Menus); - while(Template.Ptr - Template.Location < Template.Size) - { - if(!StringsDifferT("-->", Template.Ptr, 0)) - { - Template.Ptr += StringLength("-->"); - break; - } - ++Template.Ptr; - } - break; - } - else if(!(StringsDifferT(PlayerTag, Template.Ptr, 0))) - { - Output.Ptr = CommentStart; - CopyBuffer(&Output, &Player); - while(Template.Ptr - Template.Location < Template.Size) - { - if(!StringsDifferT("-->", Template.Ptr, 0)) - { - Template.Ptr += StringLength("-->"); - break; - } - ++Template.Ptr; - } - break; - } - else if(!(StringsDifferT(ScriptTag, Template.Ptr, 0))) - { - Output.Ptr = CommentStart; - CopyBuffer(&Output, &Script); - while(Template.Ptr - Template.Location < Template.Size) - { - if(!StringsDifferT("-->", Template.Ptr, 0)) - { - Template.Ptr += StringLength("-->"); - break; - } - ++Template.Ptr; - } - break; - } - else if(!StringsDifferT("-->", Template.Ptr, 0)) - { - break; - } - *Output.Ptr++ = *Template.Ptr++; - } - } - else - { - *Output.Ptr++ = *Template.Ptr++; - } - } - - FILE *OutFile; - if(!(OutFile = fopen(OutIntegratedLocation, "w"))) - { - perror(OutIntegratedLocation); free(Template.Location); free(Output.Location); hmml_free(&HMML); free(MemoryArena.Location); return 1; - } - fwrite(Output.Location, Output.Ptr - Output.Location, 1, OutFile); - fclose(OutFile); - - free(Template.Location); - free(Output.Location); - } - else - { - // NOTE(matt): Perform the normal collation into Master, and write-out to out.html - CopyStringToBuffer(&Master, - "\n" - " \n"); - - //NOTE(matt): Here is where we do all our CopyBuffer() calls - CopyBuffer(&Master, &Includes); - CopyStringToBuffer(&Master, - "\n" - " \n" - " \n"); - CopyBuffer(&Master, &Menus); - CopyStringToBuffer(&Master, "\n"); - CopyBuffer(&Master, &Player); - CopyStringToBuffer(&Master, "\n"); - CopyBuffer(&Master, &Script); - CopyStringToBuffer(&Master, "\n"); - // - - CopyStringToBuffer(&Master, - " \n" - "\n"); - - FILE *OutFile; - if(!(OutFile = fopen(OutLocation, "w"))) - { - perror(OutLocation); hmml_free(&HMML); free(MemoryArena.Location); return 1; - } - fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile); - fclose(OutFile); - } - - // NOTE(matt): Start to include this stuff in HMMLToBuffers() - - // NOTE(matt): Tree structure of "global" buffer dependencies - // FilterState - // Script - // Player - // CreditsMenu - // FilterMedia - // FilterTopics - // FilterMenu - // ReferenceMenu - // QuoteMenu - // Menus - // Includes - // Master - -Cleanup: - DeclaimBuffer(&FilterState, &ClaimedMemory); - DeclaimBuffer(&Script, &ClaimedMemory); - DeclaimBuffer(&Player, &ClaimedMemory); - - DeclaimBuffer(&CreditsMenu, &ClaimedMemory); - DeclaimBuffer(&FilterMedia, &ClaimedMemory); - DeclaimBuffer(&FilterTopics, &ClaimedMemory); - DeclaimBuffer(&FilterMenu, &ClaimedMemory); - DeclaimBuffer(&ReferenceMenu, &ClaimedMemory); - DeclaimBuffer(&QuoteMenu, &ClaimedMemory); - DeclaimBuffer(&Menus, &ClaimedMemory); - - DeclaimBuffer(&Includes, &ClaimedMemory); - DeclaimBuffer(&Master, &ClaimedMemory); - } - else - { - fprintf(stderr, "%s:%d: %s\n", Args[FileIndex], HMML.error.line, HMML.error.message); - } - hmml_free(&HMML); - // NOTE(matt): End of HMMLToBuffers() } + else + { + for(int FileIndex = optind; FileIndex < ArgC; ++FileIndex) + { + HMMLToBuffers(&MemoryArena, &CollationBuffers, Config, Args[FileIndex]); + BuffersToHTML(&MemoryArena, CollationBuffers, Template, Config, Args[FileIndex]); + } + } + + if(Config.Mode == MODE_INTEGRATE) + { + FreeBuffer(&Template); + } + + // TODO(matt): Handle return codes +#endif + + + // TODO(matt) +#if 1 +#endif + + DeclaimBuffer(&MemoryArena, &CollationBuffers.Index); + DeclaimBuffer(&MemoryArena, &CollationBuffers.IncludesIndex); + DeclaimBuffer(&MemoryArena, &CollationBuffers.Script); + DeclaimBuffer(&MemoryArena, &CollationBuffers.Player); + DeclaimBuffer(&MemoryArena, &CollationBuffers.Menus); + DeclaimBuffer(&MemoryArena, &CollationBuffers.IncludesPlayer); free(MemoryArena.Location); }