cinera.c: Support limitless templates
Previously we had a template tag limit of 16. This commit lifts that
limit to support "unlimited" tags per template.
Skip already offset landmarks most efficiently when adding a new asset.
New template tags:
    __CINERA_SEARCH_URL__
    __CINERA_VOD_PLATFORM__
			
			
This commit is contained in:
		
							parent
							
								
									3da53413ce
								
							
						
					
					
						commit
						2eddd7a7c2
					
				| 
						 | 
				
			
			@ -85,6 +85,7 @@ directory. Typical operation will involve setting these flags:
 | 
			
		|||
*Optional tags available for use in your Player Template*
 | 
			
		||||
- `<!-- __CINERA_TITLE__ -->`
 | 
			
		||||
- `<!-- __CINERA_VIDEO_ID__ -->`
 | 
			
		||||
- `<!-- __CINERA_VOD_PLATFORM__ -->`
 | 
			
		||||
 | 
			
		||||
*Other tags available for use in either template*
 | 
			
		||||
- Asset tags:
 | 
			
		||||
| 
						 | 
				
			
			@ -114,6 +115,7 @@ trigger a rehash and edit of all HTML pages citing this asset.
 | 
			
		|||
 | 
			
		||||
- `<!-- __CINERA_PROJECT__ -->`
 | 
			
		||||
- `<!-- __CINERA_PROJECT_ID__ -->`
 | 
			
		||||
- `<!-- __CINERA_SEARCH_URL__ -->`
 | 
			
		||||
- `<!-- __CINERA_THEME__ -->`
 | 
			
		||||
- `<!-- __CINERA_URL__ -->` _Only really usable if BaseURL is set (-B)_
 | 
			
		||||
- `<!-- __CINERA_CUSTOM0__ -->`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										498
									
								
								cinera/cinera.c
								
								
								
								
							
							
						
						
									
										498
									
								
								cinera/cinera.c
								
								
								
								
							| 
						 | 
				
			
			@ -17,7 +17,7 @@ typedef struct
 | 
			
		|||
version CINERA_APP_VERSION = {
 | 
			
		||||
    .Major = 0,
 | 
			
		||||
    .Minor = 6,
 | 
			
		||||
    .Patch = 0
 | 
			
		||||
    .Patch = 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include <stdarg.h> // NOTE(matt): varargs
 | 
			
		||||
| 
						 | 
				
			
			@ -424,7 +424,6 @@ typedef struct
 | 
			
		|||
 | 
			
		||||
// NOTE(matt): Globals
 | 
			
		||||
arena MemoryArena;
 | 
			
		||||
arena TemplateArena;
 | 
			
		||||
config Config;
 | 
			
		||||
assets Assets;
 | 
			
		||||
int inotifyInstance;
 | 
			
		||||
| 
						 | 
				
			
			@ -473,6 +472,7 @@ typedef struct
 | 
			
		|||
    char URLSearch[MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1];
 | 
			
		||||
    char URLPlayer[MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1];
 | 
			
		||||
    char VideoID[16];
 | 
			
		||||
    char VODPlatform[16];
 | 
			
		||||
} buffers;
 | 
			
		||||
 | 
			
		||||
enum
 | 
			
		||||
| 
						 | 
				
			
			@ -509,6 +509,7 @@ enum
 | 
			
		|||
 | 
			
		||||
    TAG_TITLE,
 | 
			
		||||
    TAG_VIDEO_ID,
 | 
			
		||||
    TAG_VOD_PLATFORM,
 | 
			
		||||
 | 
			
		||||
    // Anywhere Optional
 | 
			
		||||
    TAG_ASSET,
 | 
			
		||||
| 
						 | 
				
			
			@ -517,6 +518,7 @@ enum
 | 
			
		|||
    TAG_JS,
 | 
			
		||||
    TAG_PROJECT,
 | 
			
		||||
    TAG_PROJECT_ID,
 | 
			
		||||
    TAG_SEARCH_URL,
 | 
			
		||||
    TAG_THEME,
 | 
			
		||||
    TAG_URL,
 | 
			
		||||
    TEMPLATE_TAG_COUNT,
 | 
			
		||||
| 
						 | 
				
			
			@ -551,6 +553,7 @@ char *TemplateTags[] = {
 | 
			
		|||
 | 
			
		||||
    "__CINERA_TITLE__",
 | 
			
		||||
    "__CINERA_VIDEO_ID__",
 | 
			
		||||
    "__CINERA_VOD_PLATFORM__",
 | 
			
		||||
 | 
			
		||||
    "__CINERA_ASSET__",
 | 
			
		||||
    "__CINERA_CSS__",
 | 
			
		||||
| 
						 | 
				
			
			@ -558,6 +561,7 @@ char *TemplateTags[] = {
 | 
			
		|||
    "__CINERA_JS__",
 | 
			
		||||
    "__CINERA_PROJECT__",
 | 
			
		||||
    "__CINERA_PROJECT_ID__",
 | 
			
		||||
    "__CINERA_SEARCH_URL__",
 | 
			
		||||
    "__CINERA_THEME__",
 | 
			
		||||
    "__CINERA_URL__",
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -566,21 +570,21 @@ typedef struct
 | 
			
		|||
{
 | 
			
		||||
    int Offset;
 | 
			
		||||
    uint32_t AssetIndex;
 | 
			
		||||
    enum8(template_tags) TagCode;
 | 
			
		||||
    enum8(template_tag_codes) TagCode;
 | 
			
		||||
} tag_offset;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
    int Validity; // NOTE(matt): Bitmask describing which page the template is valid for, i.e. contents and / or player page
 | 
			
		||||
    int TagCapacity;
 | 
			
		||||
    int TagCount;
 | 
			
		||||
    char Filename[256];
 | 
			
		||||
    tag_offset Tag[16];
 | 
			
		||||
    tag_offset *Tags;
 | 
			
		||||
} template_metadata;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
    file_buffer File;
 | 
			
		||||
    template_metadata Metadata;
 | 
			
		||||
    buffer Buffer;
 | 
			
		||||
} template;
 | 
			
		||||
 | 
			
		||||
// TODO(matt): Consider putting the ref_info and quote_info into linked lists on the heap, just to avoid all the hardcoded sizes
 | 
			
		||||
| 
						 | 
				
			
			@ -597,15 +601,14 @@ typedef struct
 | 
			
		|||
    int Identifier;
 | 
			
		||||
} identifier;
 | 
			
		||||
 | 
			
		||||
#define REF_MAX_IDENTIFIER 64
 | 
			
		||||
 | 
			
		||||
#define MAX_REF_IDENTIFIER_COUNT 64
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
    char RefTitle[620];
 | 
			
		||||
    char ID[512];
 | 
			
		||||
    char URL[512];
 | 
			
		||||
    char Source[256];
 | 
			
		||||
    identifier Identifier[REF_MAX_IDENTIFIER];
 | 
			
		||||
    identifier Identifier[MAX_REF_IDENTIFIER_COUNT];
 | 
			
		||||
    int IdentifierCount;
 | 
			
		||||
} ref_info;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1348,17 +1351,48 @@ LogError(int LogLevel, char *Format, ...)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
ReadFileIntoBuffer(file_buffer *File, int BufferPadding)
 | 
			
		||||
{
 | 
			
		||||
    if(!(File->Handle = fopen(File->Path, "r"))) // TODO(matt): Fuller error handling
 | 
			
		||||
    {
 | 
			
		||||
        return RC_ERROR_FILE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fseek(File->Handle, 0, SEEK_END);
 | 
			
		||||
    File->FileSize = ftell(File->Handle);
 | 
			
		||||
    File->Buffer.Size = File->FileSize + 1 + BufferPadding; // NOTE(matt): +1 to accommodate a NULL terminator
 | 
			
		||||
    fseek(File->Handle, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
    // TODO(matt): Consider using the MemoryArena? Maybe have separate ReadFileIntoMemory() and ReadFileIntoArena()
 | 
			
		||||
    if(!(File->Buffer.Location = malloc(File->Buffer.Size)))
 | 
			
		||||
    {
 | 
			
		||||
        fclose(File->Handle);
 | 
			
		||||
        return RC_ERROR_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    File->Buffer.Ptr = File->Buffer.Location;
 | 
			
		||||
 | 
			
		||||
    fread(File->Buffer.Location, File->FileSize, 1, File->Handle);
 | 
			
		||||
    File->Buffer.Location[File->FileSize] = '\0';
 | 
			
		||||
    fclose(File->Handle);
 | 
			
		||||
    File->Buffer.ID = File->Path;
 | 
			
		||||
    return RC_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
FreeBuffer(buffer *Buffer)
 | 
			
		||||
{
 | 
			
		||||
    free(Buffer->Location);
 | 
			
		||||
    Buffer->Location = 0;
 | 
			
		||||
    Buffer->Ptr = 0;
 | 
			
		||||
    Buffer->Size = 0;
 | 
			
		||||
#if DEBUG_MEM
 | 
			
		||||
    FILE *MemLog = fopen("/home/matt/cinera_mem", "a+");
 | 
			
		||||
    fprintf(MemLog, "     Freed %s\n", Buffer->ID);
 | 
			
		||||
    fclose(MemLog);
 | 
			
		||||
    printf("     Freed %s\n", Buffer->ID);
 | 
			
		||||
#endif
 | 
			
		||||
    Buffer->ID = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
| 
						 | 
				
			
			@ -1439,7 +1473,7 @@ enum
 | 
			
		|||
    TEMPLATE_SEARCH,
 | 
			
		||||
    TEMPLATE_PLAYER,
 | 
			
		||||
    TEMPLATE_BESPOKE
 | 
			
		||||
} templates;
 | 
			
		||||
} template_types;
 | 
			
		||||
 | 
			
		||||
char *
 | 
			
		||||
GetDirectoryPath(char *Filepath)
 | 
			
		||||
| 
						 | 
				
			
			@ -1477,19 +1511,19 @@ GetBaseFilename(char *Filepath,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ConstructTemplatePath(template *Template, int TemplateType)
 | 
			
		||||
ConstructTemplatePath(template *Template, enum8(template_types) Type)
 | 
			
		||||
{
 | 
			
		||||
    // NOTE(matt): Bespoke template paths are set relative to:
 | 
			
		||||
    //                  in Project Edition: ProjectDir
 | 
			
		||||
    //                  in Single  Edition: Parent directory of .hmml file
 | 
			
		||||
 | 
			
		||||
    if(Template->Metadata.Filename[0] != '/')
 | 
			
		||||
    if(Template->File.Path[0] != '/')
 | 
			
		||||
    {
 | 
			
		||||
        char Temp[256];
 | 
			
		||||
        CopyString(Temp, sizeof(Temp), "%s", Template->Metadata.Filename);
 | 
			
		||||
        char *Ptr = Template->Metadata.Filename;
 | 
			
		||||
        char *End = Template->Metadata.Filename + sizeof(Template->Metadata.Filename);
 | 
			
		||||
        if(TemplateType == TEMPLATE_BESPOKE)
 | 
			
		||||
        CopyString(Temp, sizeof(Temp), "%s", Template->File.Path);
 | 
			
		||||
        char *Ptr = Template->File.Path;
 | 
			
		||||
        char *End = Template->File.Path + sizeof(Template->File.Path);
 | 
			
		||||
        if(Type == TEMPLATE_BESPOKE)
 | 
			
		||||
        {
 | 
			
		||||
            if(Config.Edition == EDITION_SINGLE)
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			@ -1508,87 +1542,61 @@ ConstructTemplatePath(template *Template, int TemplateType)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
InitTemplate(template **Template)
 | 
			
		||||
void
 | 
			
		||||
FitTemplateTag(template *Template)
 | 
			
		||||
{
 | 
			
		||||
    if(TemplateArena.Ptr - TemplateArena.Location + sizeof(template) > TemplateArena.Size)
 | 
			
		||||
    int BlockSize = 16;
 | 
			
		||||
    if(Template->Metadata.TagCount == Template->Metadata.TagCapacity)
 | 
			
		||||
    {
 | 
			
		||||
        return RC_ARENA_FULL;
 | 
			
		||||
        Template->Metadata.TagCapacity += BlockSize;
 | 
			
		||||
        if(Template->Metadata.Tags)
 | 
			
		||||
        {
 | 
			
		||||
            Template->Metadata.Tags = realloc(Template->Metadata.Tags, Template->Metadata.TagCapacity * sizeof(*Template->Metadata.Tags));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            Template->Metadata.Tags = calloc(Template->Metadata.TagCapacity, sizeof(*Template->Metadata.Tags));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    *Template = (template *)TemplateArena.Ptr;
 | 
			
		||||
    Clear((*Template)->Metadata.Filename, 256); // NOTE(matt): template_metadata specifies Filename[256]
 | 
			
		||||
    (*Template)->Metadata.Validity = 0;
 | 
			
		||||
    (*Template)->Metadata.TagCount = 0;
 | 
			
		||||
    for(int i = 0; i < 16; ++i) // NOTE(matt): template_metadata specifies Tag[16]
 | 
			
		||||
    {
 | 
			
		||||
        (*Template)->Metadata.Tag[i].Offset = 0;
 | 
			
		||||
        (*Template)->Metadata.Tag[i].TagCode = 0;
 | 
			
		||||
    }
 | 
			
		||||
    TemplateArena.Ptr += sizeof(template);
 | 
			
		||||
    return RC_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
ClaimTemplate(template **Template, char *Location, int TemplateType)
 | 
			
		||||
void
 | 
			
		||||
PushTemplateTag(template *Template, int Offset, enum8(template_tag_types) TagType, int AssetIndex)
 | 
			
		||||
{
 | 
			
		||||
    CopyString((*Template)->Metadata.Filename, sizeof((*Template)->Metadata.Filename), "%s", Location);
 | 
			
		||||
    ConstructTemplatePath((*Template), TemplateType);
 | 
			
		||||
 | 
			
		||||
    fprintf(stderr, "%sPacking%s template: %s\n", ColourStrings[CS_ONGOING], ColourStrings[CS_END], (*Template)->Metadata.Filename);
 | 
			
		||||
 | 
			
		||||
    FILE *File;
 | 
			
		||||
    if(!(File = fopen((*Template)->Metadata.Filename, "r")))
 | 
			
		||||
    {
 | 
			
		||||
        LogError(LOG_ERROR, "Unable to open template %s: %s", (*Template)->Metadata.Filename, strerror(errno));
 | 
			
		||||
        fprintf(stderr, "Unable to open template %s: %s\n", (*Template)->Metadata.Filename, strerror(errno));
 | 
			
		||||
        Clear((*Template)->Metadata.Filename, 256); // NOTE(matt): template_metadata specifies Filename[256]
 | 
			
		||||
        return RC_ERROR_FILE;
 | 
			
		||||
    }
 | 
			
		||||
    fseek(File, 0, SEEK_END);
 | 
			
		||||
    (*Template)->Buffer.Size = ftell(File);
 | 
			
		||||
    if(TemplateArena.Ptr - TemplateArena.Location + (*Template)->Buffer.Size > TemplateArena.Size)
 | 
			
		||||
    {
 | 
			
		||||
        Clear((*Template)->Metadata.Filename, 256); // NOTE(matt): template_metadata specifies Filename[256]
 | 
			
		||||
        return RC_ARENA_FULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    (*Template)->Buffer.Location = TemplateArena.Ptr;
 | 
			
		||||
    (*Template)->Buffer.Ptr = (*Template)->Buffer.Location;
 | 
			
		||||
    (*Template)->Buffer.ID = (*Template)->Metadata.Filename;
 | 
			
		||||
 | 
			
		||||
    fseek(File, 0, SEEK_SET);
 | 
			
		||||
    fread((*Template)->Buffer.Location, (*Template)->Buffer.Size, 1, File);
 | 
			
		||||
    fclose(File);
 | 
			
		||||
 | 
			
		||||
    TemplateArena.Ptr += (*Template)->Buffer.Size;
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
    printf("  ClaimTemplate(%s): %d\n"
 | 
			
		||||
           "    Total ClaimedMemory: %ld\n\n", (*Template)->Metadata.Filename, (*Template)->Buffer.Size, TemplateArena.Ptr - TemplateArena.Location);
 | 
			
		||||
#endif
 | 
			
		||||
    return RC_SUCCESS;
 | 
			
		||||
    FitTemplateTag(Template);
 | 
			
		||||
    Template->Metadata.Tags[Template->Metadata.TagCount].Offset = Offset;
 | 
			
		||||
    Template->Metadata.Tags[Template->Metadata.TagCount].TagCode = TagType;
 | 
			
		||||
    Template->Metadata.Tags[Template->Metadata.TagCount].AssetIndex = AssetIndex;
 | 
			
		||||
    ++Template->Metadata.TagCount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
DeclaimTemplate(template *Template)
 | 
			
		||||
void
 | 
			
		||||
ClearTemplateMetadata(template *Template)
 | 
			
		||||
{
 | 
			
		||||
    Clear(Template->Metadata.Filename, 256);
 | 
			
		||||
    Template->Metadata.Validity = 0;
 | 
			
		||||
    for(int i = 0; i < Template->Metadata.TagCount; ++i)
 | 
			
		||||
    {
 | 
			
		||||
        Template->Metadata.Tag[i].Offset = 0;
 | 
			
		||||
        Template->Metadata.Tag[i].TagCode = 0;
 | 
			
		||||
    }
 | 
			
		||||
    Template->Metadata.TagCapacity = 0;
 | 
			
		||||
    Template->Metadata.TagCount = 0;
 | 
			
		||||
    TemplateArena.Ptr -= (*Template).Buffer.Size;
 | 
			
		||||
    Template->Metadata.Validity = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
    printf("DeclaimTemplate(%s)\n"
 | 
			
		||||
           "    Total ClaimedMemory: %ld\n\n",
 | 
			
		||||
           (*Template).Metadata.Filename,
 | 
			
		||||
           TemplateArena.Ptr - TemplateArena.Location);
 | 
			
		||||
#endif
 | 
			
		||||
    return RC_SUCCESS;
 | 
			
		||||
void
 | 
			
		||||
InitTemplate(template *Template, char *Location, enum8(template_types) Type)
 | 
			
		||||
{
 | 
			
		||||
    CopyStringNoFormat(Template->File.Path, sizeof(Template->File.Path), Location);
 | 
			
		||||
    ConstructTemplatePath(Template, Type);
 | 
			
		||||
    ReadFileIntoBuffer(&Template->File, 0);
 | 
			
		||||
    ClearTemplateMetadata(Template);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
FreeTemplate(template *Template)
 | 
			
		||||
{
 | 
			
		||||
    FreeBuffer(&Template->File.Buffer);
 | 
			
		||||
    Clear(Template->File.Path, sizeof(Template->File.Path));
 | 
			
		||||
    Template->File.FileSize = 0;
 | 
			
		||||
 | 
			
		||||
    free(Template->Metadata.Tags);
 | 
			
		||||
    Template->Metadata.Tags = 0;
 | 
			
		||||
    ClearTemplateMetadata(Template);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
| 
						 | 
				
			
			@ -1771,34 +1779,6 @@ enum
 | 
			
		|||
    CreditsError_NoCredentials
 | 
			
		||||
} credits_errors;
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
ReadFileIntoBuffer(file_buffer *File, int BufferPadding)
 | 
			
		||||
{
 | 
			
		||||
    if(!(File->Handle = fopen(File->Path, "r"))) // TODO(matt): Fuller error handling
 | 
			
		||||
    {
 | 
			
		||||
        return RC_ERROR_FILE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fseek(File->Handle, 0, SEEK_END);
 | 
			
		||||
    File->FileSize = ftell(File->Handle);
 | 
			
		||||
    File->Buffer.Size = File->FileSize + 1 + BufferPadding; // NOTE(matt): +1 to accommodate a NULL terminator
 | 
			
		||||
    fseek(File->Handle, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
    // TODO(matt): Consider using the MemoryArena? Maybe have separate ReadFileIntoMemory() and ReadFileIntoArena()
 | 
			
		||||
    if(!(File->Buffer.Location = malloc(File->Buffer.Size)))
 | 
			
		||||
    {
 | 
			
		||||
        fclose(File->Handle);
 | 
			
		||||
        return RC_ERROR_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    File->Buffer.Ptr = File->Buffer.Location;
 | 
			
		||||
 | 
			
		||||
    fread(File->Buffer.Location, File->FileSize, 1, File->Handle);
 | 
			
		||||
    File->Buffer.Location[File->FileSize] = '\0';
 | 
			
		||||
    fclose(File->Handle);
 | 
			
		||||
    File->Buffer.ID = File->Path;
 | 
			
		||||
    return RC_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t
 | 
			
		||||
StringToFletcher32(const char *Data, size_t Length)
 | 
			
		||||
{// https://en.wikipedia.org/wiki/Fletcher%27s_checksum
 | 
			
		||||
| 
						 | 
				
			
			@ -3556,6 +3536,7 @@ PrintUsage(char *BinaryLocation, config *DefaultConfig)
 | 
			
		|||
            "   Optional tags available for use in your Player Template:\n"
 | 
			
		||||
            "       <!-- __CINERA_TITLE__ -->\n"
 | 
			
		||||
            "       <!-- __CINERA_VIDEO_ID__ -->\n"
 | 
			
		||||
            "       <!-- __CINERA_VOD_PLATFORM__ -->\n"
 | 
			
		||||
            "\n"
 | 
			
		||||
            "   Other tags available for use in either template:\n"
 | 
			
		||||
			"       Asset tags:\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -3585,6 +3566,7 @@ PrintUsage(char *BinaryLocation, config *DefaultConfig)
 | 
			
		|||
			"\n"
 | 
			
		||||
            "       <!-- __CINERA_PROJECT__ -->\n"
 | 
			
		||||
            "       <!-- __CINERA_PROJECT_ID__ -->\n"
 | 
			
		||||
            "       <!-- __CINERA_SEARCH_URL__ -->\n"
 | 
			
		||||
            "       <!-- __CINERA_THEME__ -->\n"
 | 
			
		||||
            "       <!-- __CINERA_URL__ -->\n"
 | 
			
		||||
            "           Only really usable if BaseURL is set %s(-B)%s\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -3791,13 +3773,13 @@ StripPWDIndicators(char *Path)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
ParseAssetString(template **Template, enum8(template_tag_codes) TagIndex, uint32_t *AssetIndexPtr)
 | 
			
		||||
ParseAssetString(template *Template, enum8(template_tag_codes) TagIndex, uint32_t *AssetIndexPtr)
 | 
			
		||||
{
 | 
			
		||||
    char *Ptr = (*Template)->Buffer.Ptr + StringLength(TemplateTags[TagIndex]);
 | 
			
		||||
    ConsumeWhitespace(&(*Template)->Buffer, &Ptr);
 | 
			
		||||
    char *Ptr = Template->File.Buffer.Ptr + StringLength(TemplateTags[TagIndex]);
 | 
			
		||||
    ConsumeWhitespace(&Template->File.Buffer, &Ptr);
 | 
			
		||||
    buffer AssetString;
 | 
			
		||||
    ClaimBuffer(&AssetString, "AssetString", MAX_ASSET_FILENAME_LENGTH);
 | 
			
		||||
    if(ParseQuotedString(&AssetString, &(*Template)->Buffer, &Ptr, TagIndex) == RC_ERROR_PARSING)
 | 
			
		||||
    if(ParseQuotedString(&AssetString, &Template->File.Buffer, &Ptr, TagIndex) == RC_ERROR_PARSING)
 | 
			
		||||
    {
 | 
			
		||||
        DeclaimBuffer(&AssetString);
 | 
			
		||||
        return RC_ERROR_PARSING;
 | 
			
		||||
| 
						 | 
				
			
			@ -3820,27 +3802,18 @@ ParseAssetString(template **Template, enum8(template_tag_codes) TagIndex, uint32
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
ValidateTemplate(template **Template, char *Location, enum8(templates) TemplateType)
 | 
			
		||||
PackTemplate(template *Template, char *Location, enum8(template_types) Type)
 | 
			
		||||
{
 | 
			
		||||
    // TODO(matt): Record line numbers and contextual information:
 | 
			
		||||
    //                 <?           ?>
 | 
			
		||||
    //                 <!--         -->
 | 
			
		||||
    //                 <            >
 | 
			
		||||
    //                 <script      </script>
 | 
			
		||||
#if 1
 | 
			
		||||
    int Return = ClaimTemplate(Template, Location, TemplateType);
 | 
			
		||||
    switch(Return)
 | 
			
		||||
    {
 | 
			
		||||
        case RC_ARENA_FULL:
 | 
			
		||||
        case RC_ERROR_FILE:
 | 
			
		||||
            return Return;
 | 
			
		||||
        case RC_SUCCESS:
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    InitTemplate(Template, Location, Type);
 | 
			
		||||
 | 
			
		||||
    buffer Errors;
 | 
			
		||||
    if(ClaimBuffer(&Errors, "Errors", Kilobytes(1)) == RC_ARENA_FULL) { DeclaimTemplate(*Template); return RC_ARENA_FULL; };
 | 
			
		||||
    if(ClaimBuffer(&Errors, "Errors", Kilobytes(1)) == RC_ARENA_FULL) { FreeTemplate(Template); return RC_ARENA_FULL; };
 | 
			
		||||
 | 
			
		||||
    bool HaveErrors = FALSE;
 | 
			
		||||
    bool HaveAssetParsingErrors = FALSE;
 | 
			
		||||
| 
						 | 
				
			
			@ -3851,19 +3824,20 @@ ValidateTemplate(template **Template, char *Location, enum8(templates) TemplateT
 | 
			
		|||
    bool FoundScript = FALSE;
 | 
			
		||||
    bool FoundSearch = FALSE;
 | 
			
		||||
 | 
			
		||||
    char *Previous = (*Template)->Buffer.Location;
 | 
			
		||||
    char *Previous = Template->File.Buffer.Location;
 | 
			
		||||
 | 
			
		||||
    while((*Template)->Buffer.Ptr - (*Template)->Buffer.Location < (*Template)->Buffer.Size)
 | 
			
		||||
    while(Template->File.Buffer.Ptr - Template->File.Buffer.Location < Template->File.Buffer.Size)
 | 
			
		||||
    {
 | 
			
		||||
NextTagSearch:
 | 
			
		||||
        if(*(*Template)->Buffer.Ptr == '!' && ((*Template)->Buffer.Ptr > (*Template)->Buffer.Location && !StringsDifferT("<!--", &(*Template)->Buffer.Ptr[-1], 0)))
 | 
			
		||||
        if(*Template->File.Buffer.Ptr == '!' && (Template->File.Buffer.Ptr > Template->File.Buffer.Location && !StringsDifferT("<!--", &Template->File.Buffer.Ptr[-1], 0)))
 | 
			
		||||
        {
 | 
			
		||||
            char *CommentStart = &(*Template)->Buffer.Ptr[-1];
 | 
			
		||||
            while((*Template)->Buffer.Ptr - (*Template)->Buffer.Location < (*Template)->Buffer.Size && StringsDifferT("-->", (*Template)->Buffer.Ptr, 0))
 | 
			
		||||
            char *CommentStart = &Template->File.Buffer.Ptr[-1];
 | 
			
		||||
            Template->File.Buffer.Ptr += StringLength("!--");
 | 
			
		||||
            while(Template->File.Buffer.Ptr - Template->File.Buffer.Location < Template->File.Buffer.Size && StringsDifferT("-->", Template->File.Buffer.Ptr, 0))
 | 
			
		||||
            {
 | 
			
		||||
                for(int TagIndex = 0; TagIndex < TEMPLATE_TAG_COUNT; ++TagIndex)
 | 
			
		||||
                {
 | 
			
		||||
                    if(!(StringsDifferT(TemplateTags[TagIndex], (*Template)->Buffer.Ptr, 0)))
 | 
			
		||||
                    if(!(StringsDifferT(TemplateTags[TagIndex], Template->File.Buffer.Ptr, 0)))
 | 
			
		||||
                    {
 | 
			
		||||
                        // TODO(matt): Pack up this data for BuffersToHTML() to use
 | 
			
		||||
                        /*
 | 
			
		||||
| 
						 | 
				
			
			@ -3877,6 +3851,8 @@ NextTagSearch:
 | 
			
		|||
                         *
 | 
			
		||||
                         */
 | 
			
		||||
 | 
			
		||||
                        uint32_t AssetIndex = 0;
 | 
			
		||||
 | 
			
		||||
                        switch(TagIndex)
 | 
			
		||||
                        {
 | 
			
		||||
                            case TAG_SEARCH:
 | 
			
		||||
| 
						 | 
				
			
			@ -3924,12 +3900,7 @@ NextTagSearch:
 | 
			
		|||
                            case TAG_IMAGE:
 | 
			
		||||
                            case TAG_JS:
 | 
			
		||||
                                {
 | 
			
		||||
                                    uint32_t AI;
 | 
			
		||||
                                    if(ParseAssetString(Template, TagIndex, &AI) == RC_SUCCESS)
 | 
			
		||||
                                    {
 | 
			
		||||
                                        (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].AssetIndex = AI;
 | 
			
		||||
                                    }
 | 
			
		||||
                                    else
 | 
			
		||||
                                    if(ParseAssetString(Template, TagIndex, &AssetIndex) == RC_ERROR_PARSING)
 | 
			
		||||
                                    {
 | 
			
		||||
                                        HaveErrors = TRUE;
 | 
			
		||||
                                        HaveAssetParsingErrors = TRUE;
 | 
			
		||||
| 
						 | 
				
			
			@ -3937,60 +3908,61 @@ NextTagSearch:
 | 
			
		|||
                                } goto RecordTag;
 | 
			
		||||
                            default: // NOTE(matt): All freely usable tags should hit this case
 | 
			
		||||
RecordTag:
 | 
			
		||||
                                (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].Offset = CommentStart - Previous;
 | 
			
		||||
                                (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].TagCode = TagIndex;
 | 
			
		||||
                                (*Template)->Metadata.TagCount++;
 | 
			
		||||
                                DepartComment(&(*Template)->Buffer);
 | 
			
		||||
                                Previous = (*Template)->Buffer.Ptr;
 | 
			
		||||
                                goto NextTagSearch;
 | 
			
		||||
                                {
 | 
			
		||||
                                    int Offset = CommentStart - Previous;
 | 
			
		||||
                                    PushTemplateTag(Template, Offset, TagIndex, AssetIndex);
 | 
			
		||||
                                    DepartComment(&Template->File.Buffer);
 | 
			
		||||
                                    Previous = Template->File.Buffer.Ptr;
 | 
			
		||||
                                    goto NextTagSearch;
 | 
			
		||||
                                }
 | 
			
		||||
                        };
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                ++(*Template)->Buffer.Ptr;
 | 
			
		||||
                ++Template->File.Buffer.Ptr;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            ++(*Template)->Buffer.Ptr;
 | 
			
		||||
            ++Template->File.Buffer.Ptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(HaveAssetParsingErrors)
 | 
			
		||||
    {
 | 
			
		||||
        DeclaimBuffer(&Errors);
 | 
			
		||||
        DeclaimTemplate(*Template);
 | 
			
		||||
        FreeTemplate(Template);
 | 
			
		||||
        return RC_INVALID_TEMPLATE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(FoundSearch)
 | 
			
		||||
    {
 | 
			
		||||
        (*Template)->Metadata.Validity |= PAGE_SEARCH;
 | 
			
		||||
        Template->Metadata.Validity |= PAGE_SEARCH;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(!HaveErrors && FoundIncludes && FoundMenus && FoundPlayer && FoundScript)
 | 
			
		||||
    {
 | 
			
		||||
        (*Template)->Metadata.Validity |= PAGE_PLAYER;
 | 
			
		||||
        Template->Metadata.Validity |= PAGE_PLAYER;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(!(Config.Mode & MODE_FORCEINTEGRATION))
 | 
			
		||||
    {
 | 
			
		||||
        if(TemplateType == TEMPLATE_SEARCH && !((*Template)->Metadata.Validity & PAGE_SEARCH))
 | 
			
		||||
        if(Type == TEMPLATE_SEARCH && !(Template->Metadata.Validity & PAGE_SEARCH))
 | 
			
		||||
        {
 | 
			
		||||
            CopyStringToBuffer(&Errors, "Search template %s must include one <!-- __CINERA_SEARCH__ --> tag\n", (*Template)->Metadata.Filename);
 | 
			
		||||
            CopyStringToBuffer(&Errors, "Search template %s must include one <!-- __CINERA_SEARCH__ --> tag\n", Template->File.Path);
 | 
			
		||||
            fprintf(stderr, "%s", Errors.Location);
 | 
			
		||||
            DeclaimBuffer(&Errors);
 | 
			
		||||
            DeclaimTemplate(*Template);
 | 
			
		||||
            FreeTemplate(Template);
 | 
			
		||||
            return RC_INVALID_TEMPLATE;
 | 
			
		||||
        }
 | 
			
		||||
        else if((TemplateType == TEMPLATE_PLAYER || TemplateType == TEMPLATE_BESPOKE) && !((*Template)->Metadata.Validity & PAGE_PLAYER))
 | 
			
		||||
        else if((Type == TEMPLATE_PLAYER || Type == TEMPLATE_BESPOKE) && !(Template->Metadata.Validity & PAGE_PLAYER))
 | 
			
		||||
        {
 | 
			
		||||
            if(!FoundIncludes){ CopyStringToBuffer(&Errors, "Player template %s must include one <!-- __CINERA_INCLUDES__ --> tag\n", (*Template)->Metadata.Filename); };
 | 
			
		||||
            if(!FoundMenus){ CopyStringToBuffer(&Errors, "Player template %s must include one <!-- __CINERA_MENUS__ --> tag\n", (*Template)->Metadata.Filename); };
 | 
			
		||||
            if(!FoundPlayer){ CopyStringToBuffer(&Errors, "Player template %s must include one <!-- __CINERA_PLAYER__ --> tag\n", (*Template)->Metadata.Filename); };
 | 
			
		||||
            if(!FoundScript){ CopyStringToBuffer(&Errors, "Player template %s must include one <!-- __CINERA_SCRIPT__ --> tag\n", (*Template)->Metadata.Filename); };
 | 
			
		||||
            if(!FoundIncludes){ CopyStringToBuffer(&Errors, "Player template %s must include one <!-- __CINERA_INCLUDES__ --> tag\n", Template->File.Path); };
 | 
			
		||||
            if(!FoundMenus){ CopyStringToBuffer(&Errors, "Player template %s must include one <!-- __CINERA_MENUS__ --> tag\n", Template->File.Path); };
 | 
			
		||||
            if(!FoundPlayer){ CopyStringToBuffer(&Errors, "Player template %s must include one <!-- __CINERA_PLAYER__ --> tag\n", Template->File.Path); };
 | 
			
		||||
            if(!FoundScript){ CopyStringToBuffer(&Errors, "Player template %s must include one <!-- __CINERA_SCRIPT__ --> tag\n", Template->File.Path); };
 | 
			
		||||
            fprintf(stderr, "%s", Errors.Location);
 | 
			
		||||
            DeclaimBuffer(&Errors);
 | 
			
		||||
            DeclaimTemplate(*Template);
 | 
			
		||||
            FreeTemplate(Template);
 | 
			
		||||
            return RC_INVALID_TEMPLATE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -4506,7 +4478,7 @@ OffsetLandmarksMenus(buffer *Src)
 | 
			
		|||
//
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filename, neighbourhood *N)
 | 
			
		||||
HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, char *Filename, neighbourhood *N)
 | 
			
		||||
{
 | 
			
		||||
    RewindBuffer(&CollationBuffers->IncludesPlayer);
 | 
			
		||||
    RewindBuffer(&CollationBuffers->Menus);
 | 
			
		||||
| 
						 | 
				
			
			@ -4530,6 +4502,9 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen
 | 
			
		|||
    *CollationBuffers->Custom14 = '\0';
 | 
			
		||||
    *CollationBuffers->Custom15 = '\0';
 | 
			
		||||
    *CollationBuffers->Title = '\0';
 | 
			
		||||
    *CollationBuffers->URLPlayer = '\0';
 | 
			
		||||
    *CollationBuffers->URLSearch = '\0';
 | 
			
		||||
    *CollationBuffers->VODPlatform = '\0';
 | 
			
		||||
 | 
			
		||||
    char Filepath[256];
 | 
			
		||||
    if(Config.Edition == EDITION_PROJECT)
 | 
			
		||||
| 
						 | 
				
			
			@ -4607,7 +4582,20 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen
 | 
			
		|||
            fprintf(stderr, "Please set the id attribute in the [video] node of your .hmml file\n");
 | 
			
		||||
            HaveErrors = TRUE;
 | 
			
		||||
        }
 | 
			
		||||
        CopyString(CollationBuffers->VideoID, sizeof(CollationBuffers->VideoID), "%s", HMML.metadata.id);
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            CopyString(CollationBuffers->VideoID, sizeof(CollationBuffers->VideoID), "%s", HMML.metadata.id);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(!HMML.metadata.vod_platform)
 | 
			
		||||
        {
 | 
			
		||||
            fprintf(stderr, "Please set the vod_platform attribute in the [video] node of your .hmml file\n");
 | 
			
		||||
            HaveErrors = TRUE;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            CopyString(CollationBuffers->VODPlatform, sizeof(CollationBuffers->VODPlatform), "%s", HMML.metadata.vod_platform);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        buffer URLPlayer;
 | 
			
		||||
        ClaimBuffer(&URLPlayer, "URLPlayer", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH);
 | 
			
		||||
| 
						 | 
				
			
			@ -4691,7 +4679,7 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen
 | 
			
		|||
        {
 | 
			
		||||
            if(HMML.metadata.template)
 | 
			
		||||
            {
 | 
			
		||||
                switch(ValidateTemplate(BespokeTemplate, HMML.metadata.template, TEMPLATE_BESPOKE))
 | 
			
		||||
                switch(PackTemplate(BespokeTemplate, HMML.metadata.template, TEMPLATE_BESPOKE))
 | 
			
		||||
                {
 | 
			
		||||
                    case RC_ARENA_FULL:
 | 
			
		||||
                    case RC_INVALID_TEMPLATE: // Invalid template
 | 
			
		||||
| 
						 | 
				
			
			@ -5033,9 +5021,9 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen
 | 
			
		|||
                    {
 | 
			
		||||
                        for(int i = 0; i < UniqueRefs; ++i)
 | 
			
		||||
                        {
 | 
			
		||||
                            if(ReferencesArray[i].IdentifierCount == REF_MAX_IDENTIFIER)
 | 
			
		||||
                            if(ReferencesArray[i].IdentifierCount == MAX_REF_IDENTIFIER_COUNT)
 | 
			
		||||
                            {
 | 
			
		||||
                                LogError(LOG_EMERGENCY, "REF_MAX_IDENTIFIER (%d) reached. Contact miblodelcarpio@gmail.com", REF_MAX_IDENTIFIER);
 | 
			
		||||
                                LogError(LOG_EMERGENCY, "REF_MAX_IDENTIFIER (%d) reached. Contact miblodelcarpio@gmail.com", MAX_REF_IDENTIFIER_COUNT);
 | 
			
		||||
                                fprintf(stderr, "%s:%d: Too many timecodes associated with one reference (increase REF_MAX_IDENTIFIER)\n", Filename, Anno->line);
 | 
			
		||||
                                hmml_free(&HMML);
 | 
			
		||||
                                return RC_ERROR_MAX_REFS;
 | 
			
		||||
| 
						 | 
				
			
			@ -5737,8 +5725,6 @@ AppendedIdentifier:
 | 
			
		|||
                "            </div>\n"
 | 
			
		||||
                "        </div>");
 | 
			
		||||
 | 
			
		||||
        // TODO(matt): Maybe do something about indentation levels
 | 
			
		||||
 | 
			
		||||
        buffer URLSearch;
 | 
			
		||||
        ClaimBuffer(&URLSearch, "URLSearch", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1);
 | 
			
		||||
        ConstructSearchURL(&URLSearch);
 | 
			
		||||
| 
						 | 
				
			
			@ -5869,13 +5855,12 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
 | 
			
		|||
    fclose(MemLog);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if(StringsDiffer(Template->Metadata.Filename, ""))
 | 
			
		||||
    if(Template->File.Buffer.Location)
 | 
			
		||||
    {
 | 
			
		||||
        if((StringsDiffer(Template->Metadata.Filename, ""))
 | 
			
		||||
                && ((Template->Metadata.Validity & PageType) || Config.Mode & MODE_FORCEINTEGRATION))
 | 
			
		||||
        if((Template->Metadata.Validity & PageType) || Config.Mode & MODE_FORCEINTEGRATION)
 | 
			
		||||
        {
 | 
			
		||||
            buffer Output;
 | 
			
		||||
            Output.Size = Template->Buffer.Size + (Kilobytes(512));
 | 
			
		||||
            Output.Size = Template->File.Buffer.Size + (Kilobytes(512));
 | 
			
		||||
            Output.ID = "Output";
 | 
			
		||||
            if(!(Output.Location = malloc(Output.Size)))
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			@ -5894,19 +5879,19 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
 | 
			
		|||
            Output.Ptr = Output.Location;
 | 
			
		||||
 | 
			
		||||
            bool NeedPlayerOffset = PlayerOffset ? TRUE : FALSE;
 | 
			
		||||
            Template->Buffer.Ptr = Template->Buffer.Location;
 | 
			
		||||
            Template->File.Buffer.Ptr = Template->File.Buffer.Location;
 | 
			
		||||
            for(int i = 0; i < Template->Metadata.TagCount; ++i)
 | 
			
		||||
            {
 | 
			
		||||
                int j = 0;
 | 
			
		||||
                while(Template->Metadata.Tag[i].Offset > j)
 | 
			
		||||
                while(Template->Metadata.Tags[i].Offset > j)
 | 
			
		||||
                {
 | 
			
		||||
                    *Output.Ptr++ = *Template->Buffer.Ptr++;
 | 
			
		||||
                    *Output.Ptr++ = *Template->File.Buffer.Ptr++;
 | 
			
		||||
                    ++j;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // TODO(matt): Make this whole template stuff context-aware, so it can determine whether or not to HTML-encode
 | 
			
		||||
                //             or sanitise punctuation for CSS-safety
 | 
			
		||||
                switch(Template->Metadata.Tag[i].TagCode)
 | 
			
		||||
                switch(Template->Metadata.Tags[i].TagCode)
 | 
			
		||||
                {
 | 
			
		||||
                    case TAG_PROJECT_ID:
 | 
			
		||||
                        if(CollationBuffers->ProjectID[0] == '\0')
 | 
			
		||||
| 
						 | 
				
			
			@ -5930,12 +5915,9 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
 | 
			
		|||
                            CopyStringToBufferHTMLSafe(&Output, CollationBuffers->ProjectName);
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case TAG_THEME:
 | 
			
		||||
                        CopyStringToBufferNoFormat(&Output, CollationBuffers->Theme); // NOTE(matt): Not HTML-safe
 | 
			
		||||
                        break;
 | 
			
		||||
                    case TAG_TITLE:
 | 
			
		||||
                        CopyStringToBufferHTMLSafe(&Output, CollationBuffers->Title);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case TAG_SEARCH_URL: CopyStringToBufferNoFormat(&Output, CollationBuffers->URLSearch); break; // NOTE(matt): Not HTML-safe
 | 
			
		||||
                    case TAG_THEME: CopyStringToBufferNoFormat(&Output, CollationBuffers->Theme); break; // NOTE(matt): Not HTML-safe
 | 
			
		||||
                    case TAG_TITLE: CopyStringToBufferHTMLSafe(&Output, CollationBuffers->Title); break;
 | 
			
		||||
                    case TAG_URL:
 | 
			
		||||
                        if(PageType == PAGE_PLAYER)
 | 
			
		||||
                        {
 | 
			
		||||
| 
						 | 
				
			
			@ -5946,9 +5928,8 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
 | 
			
		|||
                            CopyStringToBufferNoFormat(&Output, CollationBuffers->URLSearch);
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case TAG_VIDEO_ID:
 | 
			
		||||
                        CopyStringToBufferNoFormat(&Output, CollationBuffers->VideoID);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case TAG_VIDEO_ID: CopyStringToBufferNoFormat(&Output, CollationBuffers->VideoID); break;
 | 
			
		||||
                    case TAG_VOD_PLATFORM: CopyStringToBufferNoFormat(&Output, CollationBuffers->VODPlatform); break;
 | 
			
		||||
                    case TAG_SEARCH:
 | 
			
		||||
                        if(Config.Edition == EDITION_SINGLE)
 | 
			
		||||
                        {
 | 
			
		||||
| 
						 | 
				
			
			@ -5989,39 +5970,39 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
 | 
			
		|||
                    case TAG_ASSET:
 | 
			
		||||
                        {
 | 
			
		||||
                            buffer URL;
 | 
			
		||||
                            ConstructResolvedAssetURL(&URL, Template->Metadata.Tag[i].AssetIndex, PageType);
 | 
			
		||||
                            ConstructResolvedAssetURL(&URL, Template->Metadata.Tags[i].AssetIndex, PageType);
 | 
			
		||||
                            CopyStringToBuffer(&Output, "%s", URL.Location);
 | 
			
		||||
                            DeclaimBuffer(&URL);
 | 
			
		||||
                            PushAssetLandmark(&Output, Template->Metadata.Tag[i].AssetIndex, PageType);
 | 
			
		||||
                            PushAssetLandmark(&Output, Template->Metadata.Tags[i].AssetIndex, PageType);
 | 
			
		||||
                        } break;
 | 
			
		||||
                    case TAG_CSS:
 | 
			
		||||
                        {
 | 
			
		||||
                            buffer URL;
 | 
			
		||||
                            ConstructResolvedAssetURL(&URL, Template->Metadata.Tag[i].AssetIndex, PageType);
 | 
			
		||||
                            ConstructResolvedAssetURL(&URL, Template->Metadata.Tags[i].AssetIndex, PageType);
 | 
			
		||||
                            CopyStringToBuffer(&Output,
 | 
			
		||||
                                    "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s",
 | 
			
		||||
                                    URL.Location);
 | 
			
		||||
                            DeclaimBuffer(&URL);
 | 
			
		||||
                            PushAssetLandmark(&Output, Template->Metadata.Tag[i].AssetIndex, PageType);
 | 
			
		||||
                            PushAssetLandmark(&Output, Template->Metadata.Tags[i].AssetIndex, PageType);
 | 
			
		||||
                            CopyStringToBuffer(&Output, "\">");
 | 
			
		||||
                        } break;
 | 
			
		||||
                    case TAG_IMAGE:
 | 
			
		||||
                        {
 | 
			
		||||
                            buffer URL;
 | 
			
		||||
                            ConstructResolvedAssetURL(&URL, Template->Metadata.Tag[i].AssetIndex, PageType);
 | 
			
		||||
                            ConstructResolvedAssetURL(&URL, Template->Metadata.Tags[i].AssetIndex, PageType);
 | 
			
		||||
                            CopyStringToBuffer(&Output, "%s", URL.Location);
 | 
			
		||||
                            DeclaimBuffer(&URL);
 | 
			
		||||
                            PushAssetLandmark(&Output, Template->Metadata.Tag[i].AssetIndex, PageType);
 | 
			
		||||
                            PushAssetLandmark(&Output, Template->Metadata.Tags[i].AssetIndex, PageType);
 | 
			
		||||
                        } break;
 | 
			
		||||
                    case TAG_JS:
 | 
			
		||||
                        {
 | 
			
		||||
                            buffer URL;
 | 
			
		||||
                            ConstructResolvedAssetURL(&URL, Template->Metadata.Tag[i].AssetIndex, PageType);
 | 
			
		||||
                            ConstructResolvedAssetURL(&URL, Template->Metadata.Tags[i].AssetIndex, PageType);
 | 
			
		||||
                            CopyStringToBuffer(&Output,
 | 
			
		||||
                                    "<script type=\"text/javascript\" src=\"%s",
 | 
			
		||||
                                    URL.Location);
 | 
			
		||||
                            DeclaimBuffer(&URL);
 | 
			
		||||
                            PushAssetLandmark(&Output, Template->Metadata.Tag[i].AssetIndex, PageType);
 | 
			
		||||
                            PushAssetLandmark(&Output, Template->Metadata.Tags[i].AssetIndex, PageType);
 | 
			
		||||
                            CopyStringToBuffer(&Output, "\"></script>");
 | 
			
		||||
                        } break;
 | 
			
		||||
                    case TAG_CUSTOM0:  CopyStringToBufferNoFormat(&Output, CollationBuffers->Custom0);  break;
 | 
			
		||||
| 
						 | 
				
			
			@ -6042,18 +6023,18 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
 | 
			
		|||
                    case TAG_CUSTOM15: CopyStringToBufferNoFormat(&Output, CollationBuffers->Custom15); break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                DepartComment(&Template->Buffer);
 | 
			
		||||
                DepartComment(&Template->File.Buffer);
 | 
			
		||||
            }
 | 
			
		||||
            while(Template->Buffer.Ptr - Template->Buffer.Location < Template->Buffer.Size)
 | 
			
		||||
            while(Template->File.Buffer.Ptr - Template->File.Buffer.Location < Template->File.Buffer.Size)
 | 
			
		||||
            {
 | 
			
		||||
                *Output.Ptr++ = *Template->Buffer.Ptr++;
 | 
			
		||||
                *Output.Ptr++ = *Template->File.Buffer.Ptr++;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            FILE *OutFile;
 | 
			
		||||
            if(!(OutFile = fopen(Config.Edition == EDITION_PROJECT ? OutputPath : Config.OutIntegratedLocation, "w")))
 | 
			
		||||
            {
 | 
			
		||||
                LogError(LOG_ERROR, "Unable to open output file %s: %s", Config.Edition == EDITION_PROJECT ? OutputPath : Config.OutIntegratedLocation, strerror(errno));
 | 
			
		||||
                free(Output.Location);
 | 
			
		||||
                FreeBuffer(&Output);
 | 
			
		||||
 | 
			
		||||
#if DEBUG_MEM
 | 
			
		||||
                MemLog = fopen("/home/matt/cinera_mem", "a+");
 | 
			
		||||
| 
						 | 
				
			
			@ -6067,7 +6048,7 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
 | 
			
		|||
            fwrite(Output.Location, Output.Ptr - Output.Location, 1, OutFile);
 | 
			
		||||
            fclose(OutFile);
 | 
			
		||||
 | 
			
		||||
            free(Output.Location);
 | 
			
		||||
            FreeBuffer(&Output);
 | 
			
		||||
 | 
			
		||||
#if DEBUG_MEM
 | 
			
		||||
            MemLog = fopen("/home/matt/cinera_mem", "a+");
 | 
			
		||||
| 
						 | 
				
			
			@ -6455,7 +6436,7 @@ SnipeEntryIntoMetadataBuffer(db_entry *Entry, int EntryIndex)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
InsertIntoDB(neighbourhood *N, buffers *CollationBuffers, template **BespokeTemplate, char *BaseFilename, bool RecheckingPrivacy, bool *Reinserting)
 | 
			
		||||
InsertIntoDB(neighbourhood *N, buffers *CollationBuffers, template *BespokeTemplate, char *BaseFilename, bool RecheckingPrivacy, bool *Reinserting)
 | 
			
		||||
{
 | 
			
		||||
    enum8(edit_types) EditType = EDIT_APPEND;
 | 
			
		||||
    int EntryInsertionStart = StringLength("---\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -6630,7 +6611,7 @@ PrintLandmarks(void *FirstLandmark, int LandmarkCount)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ProcessPrevLandmarks(neighbourhood *N, void *FirstLandmark, int ExistingLandmarkCount, landmark_range *CurrentTarget, bool OffsetLandmarks, int *RunningIndex)
 | 
			
		||||
ProcessPrevLandmarks(neighbourhood *N, void *FirstLandmark, int ExistingLandmarkCount, landmark_range *CurrentTarget, int *RunningIndex)
 | 
			
		||||
{
 | 
			
		||||
    if(N->PrevIndex >= 0)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -6641,7 +6622,7 @@ ProcessPrevLandmarks(neighbourhood *N, void *FirstLandmark, int ExistingLandmark
 | 
			
		|||
        for(int j = 0; j < FormerTarget.Length; ++j, ++*RunningIndex)
 | 
			
		||||
        {
 | 
			
		||||
            db_landmark Landmark = *(db_landmark *)(FirstLandmark + sizeof(Landmark) * *RunningIndex);
 | 
			
		||||
            if(!OffsetLandmarks && Landmark.Position >= N->PreLinkPrevOffsetTotal)
 | 
			
		||||
            if(Landmark.Position >= N->PreLinkPrevOffsetTotal)
 | 
			
		||||
            {
 | 
			
		||||
                Landmark.Position += N->PrevOffsetModifier;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -6656,7 +6637,7 @@ ProcessPrevLandmarks(neighbourhood *N, void *FirstLandmark, int ExistingLandmark
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ProcessNextLandmarks(neighbourhood *N, void *FirstLandmark, int ExistingLandmarkCount, bool OffsetLandmarks, int *RunningIndex, enum8(edit_types) EditType)
 | 
			
		||||
ProcessNextLandmarks(neighbourhood *N, void *FirstLandmark, int ExistingLandmarkCount, int *RunningIndex, enum8(edit_types) EditType)
 | 
			
		||||
{
 | 
			
		||||
    if(N->NextIndex >= 0 && *RunningIndex < ExistingLandmarkCount)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -6666,7 +6647,7 @@ ProcessNextLandmarks(neighbourhood *N, void *FirstLandmark, int ExistingLandmark
 | 
			
		|||
        for(int j = 0; j < LatterTarget.Length; ++j, ++*RunningIndex)
 | 
			
		||||
        {
 | 
			
		||||
            Landmark = *(db_landmark *)(FirstLandmark + sizeof(Landmark) * *RunningIndex);
 | 
			
		||||
            if(!OffsetLandmarks && Landmark.Position >= N->PreLinkNextOffsetTotal)
 | 
			
		||||
            if(Landmark.Position >= N->PreLinkNextOffsetTotal)
 | 
			
		||||
            {
 | 
			
		||||
                Landmark.Position += N->NextOffsetModifier;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -6763,11 +6744,11 @@ DeleteLandmarks(neighbourhood *N)
 | 
			
		|||
        DB.Asset.LandmarkCount -= DeletionTarget.Length;
 | 
			
		||||
        fwrite(&DB.Asset, sizeof(DB.Asset), 1, DB.Metadata.Handle);
 | 
			
		||||
 | 
			
		||||
        ProcessPrevLandmarks(N, FirstLandmark, ExistingLandmarkCount, &DeletionTarget, Assets.Asset[AssetIndex].OffsetLandmarks, &RunningIndex);
 | 
			
		||||
        ProcessPrevLandmarks(N, FirstLandmark, ExistingLandmarkCount, &DeletionTarget, &RunningIndex);
 | 
			
		||||
 | 
			
		||||
        RunningIndex += DeletionTarget.Length;
 | 
			
		||||
 | 
			
		||||
        ProcessNextLandmarks(N, FirstLandmark, ExistingLandmarkCount, Assets.Asset[AssetIndex].OffsetLandmarks, &RunningIndex, EDIT_DELETION);
 | 
			
		||||
        ProcessNextLandmarks(N, FirstLandmark, ExistingLandmarkCount, &RunningIndex, EDIT_DELETION);
 | 
			
		||||
        DB.Metadata.Buffer.Ptr += sizeof(db_landmark) * ExistingLandmarkCount;
 | 
			
		||||
    }
 | 
			
		||||
    CycleFile(&DB.Metadata);
 | 
			
		||||
| 
						 | 
				
			
			@ -6800,37 +6781,45 @@ AddLandmarks(neighbourhood *N, enum8(edit_types) EditType)
 | 
			
		|||
            if(!StringsDiffer(DB.Asset.Filename, Assets.Asset[i].Filename) && DB.Asset.Type == Assets.Asset[i].Type)
 | 
			
		||||
            {
 | 
			
		||||
                Assets.Asset[i].Known = TRUE;
 | 
			
		||||
                DB.Asset.LandmarkCount += Assets.Asset[i].PlayerLandmarkCount;
 | 
			
		||||
                landmark_range ThisTarget;
 | 
			
		||||
                if(ExistingLandmarkCount > 0)
 | 
			
		||||
                if(!Assets.Asset[i].OffsetLandmarks)
 | 
			
		||||
                {
 | 
			
		||||
                    ThisTarget = BinarySearchForMetadataLandmark(FirstLandmark, N->ThisIndex, ExistingLandmarkCount);
 | 
			
		||||
                    DB.Asset.LandmarkCount += Assets.Asset[i].PlayerLandmarkCount;
 | 
			
		||||
                    landmark_range ThisTarget;
 | 
			
		||||
                    if(ExistingLandmarkCount > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        ThisTarget = BinarySearchForMetadataLandmark(FirstLandmark, N->ThisIndex, ExistingLandmarkCount);
 | 
			
		||||
 | 
			
		||||
                    if(EditType == EDIT_REINSERTION) { DB.Asset.LandmarkCount -= ThisTarget.Length; }
 | 
			
		||||
                        if(EditType == EDIT_REINSERTION) { DB.Asset.LandmarkCount -= ThisTarget.Length; }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    fwrite(&DB.Asset, sizeof(DB.Asset), 1, DB.Metadata.Handle);
 | 
			
		||||
 | 
			
		||||
                    if(ExistingLandmarkCount > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        ProcessPrevLandmarks(N, FirstLandmark, ExistingLandmarkCount, &ThisTarget, &RunningIndex);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    for(int j = 0; j < Assets.Asset[i].PlayerLandmarkCount; ++j)
 | 
			
		||||
                    {
 | 
			
		||||
                        db_landmark Landmark;
 | 
			
		||||
                        Landmark.EntryIndex = N->ThisIndex;
 | 
			
		||||
                        Landmark.Position = Assets.Asset[i].PlayerLandmark[j];
 | 
			
		||||
                        fwrite(&Landmark, sizeof(Landmark), 1, DB.Metadata.Handle);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if(ExistingLandmarkCount > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        if(EditType == EDIT_REINSERTION) { RunningIndex += ThisTarget.Length; }
 | 
			
		||||
 | 
			
		||||
                        ProcessNextLandmarks(N, FirstLandmark, ExistingLandmarkCount, &RunningIndex, EditType);
 | 
			
		||||
                    }
 | 
			
		||||
                    Assets.Asset[i].OffsetLandmarks = TRUE;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                fwrite(&DB.Asset, sizeof(DB.Asset), 1, DB.Metadata.Handle);
 | 
			
		||||
 | 
			
		||||
                if(ExistingLandmarkCount > 0)
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    ProcessPrevLandmarks(N, FirstLandmark, ExistingLandmarkCount, &ThisTarget, Assets.Asset[i].OffsetLandmarks, &RunningIndex);
 | 
			
		||||
                    fwrite(&DB.Asset, sizeof(DB.Asset), 1, DB.Metadata.Handle);
 | 
			
		||||
                    fwrite(DB.Metadata.Buffer.Ptr, sizeof(db_landmark) * ExistingLandmarkCount, 1, DB.Metadata.Handle);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                for(int j = 0; j < Assets.Asset[i].PlayerLandmarkCount; ++j)
 | 
			
		||||
                {
 | 
			
		||||
                    db_landmark Landmark;
 | 
			
		||||
                    Landmark.EntryIndex = N->ThisIndex;
 | 
			
		||||
                    Landmark.Position = Assets.Asset[i].PlayerLandmark[j];
 | 
			
		||||
                    fwrite(&Landmark, sizeof(Landmark), 1, DB.Metadata.Handle);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if(ExistingLandmarkCount > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    if(EditType == EDIT_REINSERTION) { RunningIndex += ThisTarget.Length; }
 | 
			
		||||
 | 
			
		||||
                    ProcessNextLandmarks(N, FirstLandmark, ExistingLandmarkCount, Assets.Asset[i].OffsetLandmarks, &RunningIndex, EditType);
 | 
			
		||||
                }
 | 
			
		||||
                Assets.Asset[i].OffsetLandmarks = TRUE;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -7634,7 +7623,7 @@ GeneratePlayerPage(neighbourhood *N, buffers *CollationBuffers, template *Player
 | 
			
		|||
    bool SearchInTemplate = FALSE;
 | 
			
		||||
    for(int TagIndex = 0; TagIndex < PlayerTemplate->Metadata.TagCount; ++TagIndex)
 | 
			
		||||
    {
 | 
			
		||||
        if(PlayerTemplate->Metadata.Tag[TagIndex].TagCode == TAG_SEARCH)
 | 
			
		||||
        if(PlayerTemplate->Metadata.Tags[TagIndex].TagCode == TAG_SEARCH)
 | 
			
		||||
        {
 | 
			
		||||
            SearchInTemplate = TRUE;
 | 
			
		||||
            SearchToBuffer(CollationBuffers);
 | 
			
		||||
| 
						 | 
				
			
			@ -7713,13 +7702,13 @@ int
 | 
			
		|||
InsertEntry(neighbourhood *Neighbourhood, buffers *CollationBuffers, template *PlayerTemplate, template *BespokeTemplate, char *BaseFilename, bool RecheckingPrivacy)
 | 
			
		||||
{
 | 
			
		||||
    bool Reinserting = FALSE;
 | 
			
		||||
    if(InsertIntoDB(Neighbourhood, CollationBuffers, &BespokeTemplate, BaseFilename, RecheckingPrivacy, &Reinserting) == RC_SUCCESS)
 | 
			
		||||
    if(InsertIntoDB(Neighbourhood, CollationBuffers, BespokeTemplate, BaseFilename, RecheckingPrivacy, &Reinserting) == RC_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
        LinkNeighbours(Neighbourhood, LINK_INCLUDE);
 | 
			
		||||
        if(StringsDiffer(BespokeTemplate->Metadata.Filename, ""))
 | 
			
		||||
        if(BespokeTemplate->File.Buffer.Location)
 | 
			
		||||
        {
 | 
			
		||||
            GeneratePlayerPage(Neighbourhood, CollationBuffers, BespokeTemplate, BaseFilename, Reinserting);
 | 
			
		||||
            DeclaimTemplate(BespokeTemplate);
 | 
			
		||||
            FreeTemplate(BespokeTemplate);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -8799,7 +8788,6 @@ main(int ArgC, char **Args)
 | 
			
		|||
 | 
			
		||||
    // NOTE(matt): Init MemoryArenas (they are global)
 | 
			
		||||
    InitMemoryArena(&MemoryArena, Megabytes(4));
 | 
			
		||||
    InitMemoryArena(&TemplateArena, Kilobytes(16)); // TODO(matt): Consider some way of making this growable
 | 
			
		||||
 | 
			
		||||
#if DEBUG_MEM
 | 
			
		||||
    FILE *MemLog = fopen("/home/matt/cinera_mem", "a+");
 | 
			
		||||
| 
						 | 
				
			
			@ -9029,19 +9017,19 @@ main(int ArgC, char **Args)
 | 
			
		|||
        inotifyInstance = inotify_init1(IN_NONBLOCK);
 | 
			
		||||
 | 
			
		||||
        printf("┌╼ Hashing assets ╾┐\n");
 | 
			
		||||
        // NOTE(matt): This had to happen before ValidateTemplate() because those guys may need to do PushAsset() and we must
 | 
			
		||||
        // NOTE(matt): This had to happen before PackTemplate() because those guys may need to do PushAsset() and we must
 | 
			
		||||
        //             ensure that the builtin assets get placed correctly
 | 
			
		||||
        InitAssets();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    printf("┌╼ Packing templates ╾┐\n");
 | 
			
		||||
    template *SearchTemplate; InitTemplate(&SearchTemplate);
 | 
			
		||||
    template *PlayerTemplate; InitTemplate(&PlayerTemplate);
 | 
			
		||||
    template *BespokeTemplate; InitTemplate(&BespokeTemplate);
 | 
			
		||||
    template SearchTemplate;
 | 
			
		||||
    template PlayerTemplate;
 | 
			
		||||
    template BespokeTemplate;
 | 
			
		||||
 | 
			
		||||
    if(StringsDiffer(Config.TemplatePlayerLocation, ""))
 | 
			
		||||
    {
 | 
			
		||||
        switch(ValidateTemplate(&PlayerTemplate, Config.TemplatePlayerLocation, TEMPLATE_PLAYER))
 | 
			
		||||
        switch(PackTemplate(&PlayerTemplate, Config.TemplatePlayerLocation, TEMPLATE_PLAYER))
 | 
			
		||||
        {
 | 
			
		||||
            case RC_INVALID_TEMPLATE: // Invalid template
 | 
			
		||||
            case RC_ERROR_FILE: // Could not load template
 | 
			
		||||
| 
						 | 
				
			
			@ -9054,7 +9042,7 @@ main(int ArgC, char **Args)
 | 
			
		|||
 | 
			
		||||
    if(Config.Edition == EDITION_PROJECT && StringsDiffer(Config.TemplateSearchLocation, ""))
 | 
			
		||||
    {
 | 
			
		||||
        switch(ValidateTemplate(&SearchTemplate, Config.TemplateSearchLocation, TEMPLATE_SEARCH))
 | 
			
		||||
        switch(PackTemplate(&SearchTemplate, Config.TemplateSearchLocation, TEMPLATE_SEARCH))
 | 
			
		||||
        {
 | 
			
		||||
            case RC_INVALID_TEMPLATE: // Invalid template
 | 
			
		||||
            case RC_ERROR_MEMORY: // Could not allocate memory for template
 | 
			
		||||
| 
						 | 
				
			
			@ -9068,7 +9056,7 @@ main(int ArgC, char **Args)
 | 
			
		|||
    if(Config.Edition == EDITION_PROJECT)
 | 
			
		||||
    {
 | 
			
		||||
        printf("\n┌╼ Synchronising with Annotations Directory ╾┐\n");
 | 
			
		||||
        SyncDBWithInput(&CollationBuffers, SearchTemplate, PlayerTemplate, BespokeTemplate);
 | 
			
		||||
        SyncDBWithInput(&CollationBuffers, &SearchTemplate, &PlayerTemplate, &BespokeTemplate);
 | 
			
		||||
        if(Config.Mode & MODE_ONESHOT)
 | 
			
		||||
        {
 | 
			
		||||
            goto RIP;
 | 
			
		||||
| 
						 | 
				
			
			@ -9081,7 +9069,7 @@ main(int ArgC, char **Args)
 | 
			
		|||
        // NOTE(matt): Do we want to also watch IN_DELETE_SELF events?
 | 
			
		||||
        PushHMMLWatchHandle();
 | 
			
		||||
 | 
			
		||||
        while(MonitorFilesystem(&CollationBuffers, SearchTemplate, PlayerTemplate, BespokeTemplate) != RC_ARENA_FULL)
 | 
			
		||||
        while(MonitorFilesystem(&CollationBuffers, &SearchTemplate, &PlayerTemplate, &BespokeTemplate) != RC_ARENA_FULL)
 | 
			
		||||
        {
 | 
			
		||||
            // TODO(matt): Refetch the quotes and rebuild player pages if needed
 | 
			
		||||
            //
 | 
			
		||||
| 
						 | 
				
			
			@ -9095,7 +9083,7 @@ main(int ArgC, char **Args)
 | 
			
		|||
            //
 | 
			
		||||
            if(!(Config.Mode & MODE_NOPRIVACY) && time(0) - LastPrivacyCheck > 60 * 60 * 4)
 | 
			
		||||
            {
 | 
			
		||||
                RecheckPrivacy(&CollationBuffers, SearchTemplate, PlayerTemplate, BespokeTemplate);
 | 
			
		||||
                RecheckPrivacy(&CollationBuffers, &SearchTemplate, &PlayerTemplate, &BespokeTemplate);
 | 
			
		||||
            }
 | 
			
		||||
            sleep(Config.UpdateInterval);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -9141,16 +9129,16 @@ NextFile:
 | 
			
		|||
                        break;
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                HasBespokeTemplate = StringsDiffer(BespokeTemplate->Metadata.Filename, "");
 | 
			
		||||
                HasBespokeTemplate = BespokeTemplate.File.Buffer.Location != NULL;
 | 
			
		||||
 | 
			
		||||
                switch(BuffersToHTML(&CollationBuffers,
 | 
			
		||||
                            HasBespokeTemplate ? BespokeTemplate : PlayerTemplate,
 | 
			
		||||
                            HasBespokeTemplate ? &BespokeTemplate : &PlayerTemplate,
 | 
			
		||||
                            0,
 | 
			
		||||
                            PAGE_PLAYER, 0))
 | 
			
		||||
                {
 | 
			
		||||
                    // TODO(matt): Actually sort out the fatality of these cases
 | 
			
		||||
                    case RC_INVALID_TEMPLATE:
 | 
			
		||||
                        if(HasBespokeTemplate) { DeclaimTemplate(BespokeTemplate); }
 | 
			
		||||
                        if(HasBespokeTemplate) { FreeTemplate(&BespokeTemplate); }
 | 
			
		||||
                        if(FileIndex < (ArgC - 1)) { goto NextFile; }
 | 
			
		||||
                    case RC_ERROR_MEMORY:
 | 
			
		||||
                    case RC_ERROR_FILE:
 | 
			
		||||
| 
						 | 
				
			
			@ -9160,20 +9148,20 @@ NextFile:
 | 
			
		|||
#if 0
 | 
			
		||||
                        fprintf(stdout, "%sWritten%s %s\n", HasBespokeTemplate ? Config.OutIntegratedLocation : Config.OutLocation);
 | 
			
		||||
#endif
 | 
			
		||||
                        if(HasBespokeTemplate) { DeclaimTemplate(BespokeTemplate); }
 | 
			
		||||
                        if(HasBespokeTemplate) { FreeTemplate(&BespokeTemplate); }
 | 
			
		||||
                        break;
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(StringsDiffer(PlayerTemplate->Metadata.Filename, ""))
 | 
			
		||||
    if(PlayerTemplate.File.Buffer.Location)
 | 
			
		||||
    {
 | 
			
		||||
        DeclaimTemplate(PlayerTemplate);
 | 
			
		||||
        FreeTemplate(&PlayerTemplate);
 | 
			
		||||
    }
 | 
			
		||||
    if(Config.Edition == EDITION_PROJECT && StringsDiffer(SearchTemplate->Metadata.Filename, ""))
 | 
			
		||||
    if(Config.Edition == EDITION_PROJECT && SearchTemplate.File.Buffer.Location)
 | 
			
		||||
    {
 | 
			
		||||
        DeclaimTemplate(SearchTemplate);
 | 
			
		||||
        FreeTemplate(&SearchTemplate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DeclaimBuffer(&CollationBuffers.SearchEntry);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue