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:
Matt Mascarenhas 2018-09-19 20:50:21 +01:00
parent 3da53413ce
commit 2eddd7a7c2
2 changed files with 245 additions and 255 deletions

View File

@ -85,6 +85,7 @@ directory. Typical operation will involve setting these flags:
*Optional tags available for use in your Player Template* *Optional tags available for use in your Player Template*
- `<!-- __CINERA_TITLE__ -->` - `<!-- __CINERA_TITLE__ -->`
- `<!-- __CINERA_VIDEO_ID__ -->` - `<!-- __CINERA_VIDEO_ID__ -->`
- `<!-- __CINERA_VOD_PLATFORM__ -->`
*Other tags available for use in either template* *Other tags available for use in either template*
- Asset tags: - Asset tags:
@ -114,6 +115,7 @@ trigger a rehash and edit of all HTML pages citing this asset.
- `<!-- __CINERA_PROJECT__ -->` - `<!-- __CINERA_PROJECT__ -->`
- `<!-- __CINERA_PROJECT_ID__ -->` - `<!-- __CINERA_PROJECT_ID__ -->`
- `<!-- __CINERA_SEARCH_URL__ -->`
- `<!-- __CINERA_THEME__ -->` - `<!-- __CINERA_THEME__ -->`
- `<!-- __CINERA_URL__ -->` _Only really usable if BaseURL is set (-B)_ - `<!-- __CINERA_URL__ -->` _Only really usable if BaseURL is set (-B)_
- `<!-- __CINERA_CUSTOM0__ -->` - `<!-- __CINERA_CUSTOM0__ -->`

View File

@ -17,7 +17,7 @@ typedef struct
version CINERA_APP_VERSION = { version CINERA_APP_VERSION = {
.Major = 0, .Major = 0,
.Minor = 6, .Minor = 6,
.Patch = 0 .Patch = 1
}; };
#include <stdarg.h> // NOTE(matt): varargs #include <stdarg.h> // NOTE(matt): varargs
@ -424,7 +424,6 @@ typedef struct
// NOTE(matt): Globals // NOTE(matt): Globals
arena MemoryArena; arena MemoryArena;
arena TemplateArena;
config Config; config Config;
assets Assets; assets Assets;
int inotifyInstance; int inotifyInstance;
@ -473,6 +472,7 @@ typedef struct
char URLSearch[MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1]; 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 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 VideoID[16];
char VODPlatform[16];
} buffers; } buffers;
enum enum
@ -509,6 +509,7 @@ enum
TAG_TITLE, TAG_TITLE,
TAG_VIDEO_ID, TAG_VIDEO_ID,
TAG_VOD_PLATFORM,
// Anywhere Optional // Anywhere Optional
TAG_ASSET, TAG_ASSET,
@ -517,6 +518,7 @@ enum
TAG_JS, TAG_JS,
TAG_PROJECT, TAG_PROJECT,
TAG_PROJECT_ID, TAG_PROJECT_ID,
TAG_SEARCH_URL,
TAG_THEME, TAG_THEME,
TAG_URL, TAG_URL,
TEMPLATE_TAG_COUNT, TEMPLATE_TAG_COUNT,
@ -551,6 +553,7 @@ char *TemplateTags[] = {
"__CINERA_TITLE__", "__CINERA_TITLE__",
"__CINERA_VIDEO_ID__", "__CINERA_VIDEO_ID__",
"__CINERA_VOD_PLATFORM__",
"__CINERA_ASSET__", "__CINERA_ASSET__",
"__CINERA_CSS__", "__CINERA_CSS__",
@ -558,6 +561,7 @@ char *TemplateTags[] = {
"__CINERA_JS__", "__CINERA_JS__",
"__CINERA_PROJECT__", "__CINERA_PROJECT__",
"__CINERA_PROJECT_ID__", "__CINERA_PROJECT_ID__",
"__CINERA_SEARCH_URL__",
"__CINERA_THEME__", "__CINERA_THEME__",
"__CINERA_URL__", "__CINERA_URL__",
}; };
@ -566,21 +570,21 @@ typedef struct
{ {
int Offset; int Offset;
uint32_t AssetIndex; uint32_t AssetIndex;
enum8(template_tags) TagCode; enum8(template_tag_codes) TagCode;
} tag_offset; } tag_offset;
typedef struct typedef struct
{ {
int Validity; // NOTE(matt): Bitmask describing which page the template is valid for, i.e. contents and / or player page int Validity; // NOTE(matt): Bitmask describing which page the template is valid for, i.e. contents and / or player page
int TagCapacity;
int TagCount; int TagCount;
char Filename[256]; tag_offset *Tags;
tag_offset Tag[16];
} template_metadata; } template_metadata;
typedef struct typedef struct
{ {
file_buffer File;
template_metadata Metadata; template_metadata Metadata;
buffer Buffer;
} template; } template;
// TODO(matt): Consider putting the ref_info and quote_info into linked lists on the heap, just to avoid all the hardcoded sizes // 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; int Identifier;
} identifier; } identifier;
#define REF_MAX_IDENTIFIER 64 #define MAX_REF_IDENTIFIER_COUNT 64
typedef struct typedef struct
{ {
char RefTitle[620]; char RefTitle[620];
char ID[512]; char ID[512];
char URL[512]; char URL[512];
char Source[256]; char Source[256];
identifier Identifier[REF_MAX_IDENTIFIER]; identifier Identifier[MAX_REF_IDENTIFIER_COUNT];
int IdentifierCount; int IdentifierCount;
} ref_info; } 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 void
FreeBuffer(buffer *Buffer) FreeBuffer(buffer *Buffer)
{ {
free(Buffer->Location); free(Buffer->Location);
Buffer->Location = 0; Buffer->Location = 0;
Buffer->Ptr = 0;
Buffer->Size = 0;
#if DEBUG_MEM #if DEBUG_MEM
FILE *MemLog = fopen("/home/matt/cinera_mem", "a+"); FILE *MemLog = fopen("/home/matt/cinera_mem", "a+");
fprintf(MemLog, " Freed %s\n", Buffer->ID); fprintf(MemLog, " Freed %s\n", Buffer->ID);
fclose(MemLog); fclose(MemLog);
printf(" Freed %s\n", Buffer->ID); printf(" Freed %s\n", Buffer->ID);
#endif #endif
Buffer->ID = 0;
} }
int int
@ -1439,7 +1473,7 @@ enum
TEMPLATE_SEARCH, TEMPLATE_SEARCH,
TEMPLATE_PLAYER, TEMPLATE_PLAYER,
TEMPLATE_BESPOKE TEMPLATE_BESPOKE
} templates; } template_types;
char * char *
GetDirectoryPath(char *Filepath) GetDirectoryPath(char *Filepath)
@ -1477,19 +1511,19 @@ GetBaseFilename(char *Filepath,
} }
void void
ConstructTemplatePath(template *Template, int TemplateType) ConstructTemplatePath(template *Template, enum8(template_types) Type)
{ {
// NOTE(matt): Bespoke template paths are set relative to: // NOTE(matt): Bespoke template paths are set relative to:
// in Project Edition: ProjectDir // in Project Edition: ProjectDir
// in Single Edition: Parent directory of .hmml file // in Single Edition: Parent directory of .hmml file
if(Template->Metadata.Filename[0] != '/') if(Template->File.Path[0] != '/')
{ {
char Temp[256]; char Temp[256];
CopyString(Temp, sizeof(Temp), "%s", Template->Metadata.Filename); CopyString(Temp, sizeof(Temp), "%s", Template->File.Path);
char *Ptr = Template->Metadata.Filename; char *Ptr = Template->File.Path;
char *End = Template->Metadata.Filename + sizeof(Template->Metadata.Filename); char *End = Template->File.Path + sizeof(Template->File.Path);
if(TemplateType == TEMPLATE_BESPOKE) if(Type == TEMPLATE_BESPOKE)
{ {
if(Config.Edition == EDITION_SINGLE) if(Config.Edition == EDITION_SINGLE)
{ {
@ -1508,87 +1542,61 @@ ConstructTemplatePath(template *Template, int TemplateType)
} }
} }
int void
InitTemplate(template **Template) 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 void
ClaimTemplate(template **Template, char *Location, int TemplateType) PushTemplateTag(template *Template, int Offset, enum8(template_tag_types) TagType, int AssetIndex)
{ {
CopyString((*Template)->Metadata.Filename, sizeof((*Template)->Metadata.Filename), "%s", Location); FitTemplateTag(Template);
ConstructTemplatePath((*Template), TemplateType); Template->Metadata.Tags[Template->Metadata.TagCount].Offset = Offset;
Template->Metadata.Tags[Template->Metadata.TagCount].TagCode = TagType;
fprintf(stderr, "%sPacking%s template: %s\n", ColourStrings[CS_ONGOING], ColourStrings[CS_END], (*Template)->Metadata.Filename); Template->Metadata.Tags[Template->Metadata.TagCount].AssetIndex = AssetIndex;
++Template->Metadata.TagCount;
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;
} }
int void
DeclaimTemplate(template *Template) ClearTemplateMetadata(template *Template)
{ {
Clear(Template->Metadata.Filename, 256); Template->Metadata.TagCapacity = 0;
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.TagCount = 0; Template->Metadata.TagCount = 0;
TemplateArena.Ptr -= (*Template).Buffer.Size; Template->Metadata.Validity = 0;
}
#if DEBUG void
printf("DeclaimTemplate(%s)\n" InitTemplate(template *Template, char *Location, enum8(template_types) Type)
" Total ClaimedMemory: %ld\n\n", {
(*Template).Metadata.Filename, CopyStringNoFormat(Template->File.Path, sizeof(Template->File.Path), Location);
TemplateArena.Ptr - TemplateArena.Location); ConstructTemplatePath(Template, Type);
#endif ReadFileIntoBuffer(&Template->File, 0);
return RC_SUCCESS; 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 int
@ -1771,34 +1779,6 @@ enum
CreditsError_NoCredentials CreditsError_NoCredentials
} credits_errors; } 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 size_t
StringToFletcher32(const char *Data, size_t Length) StringToFletcher32(const char *Data, size_t Length)
{// https://en.wikipedia.org/wiki/Fletcher%27s_checksum {// 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" " Optional tags available for use in your Player Template:\n"
" <!-- __CINERA_TITLE__ -->\n" " <!-- __CINERA_TITLE__ -->\n"
" <!-- __CINERA_VIDEO_ID__ -->\n" " <!-- __CINERA_VIDEO_ID__ -->\n"
" <!-- __CINERA_VOD_PLATFORM__ -->\n"
"\n" "\n"
" Other tags available for use in either template:\n" " Other tags available for use in either template:\n"
" Asset tags:\n" " Asset tags:\n"
@ -3585,6 +3566,7 @@ PrintUsage(char *BinaryLocation, config *DefaultConfig)
"\n" "\n"
" <!-- __CINERA_PROJECT__ -->\n" " <!-- __CINERA_PROJECT__ -->\n"
" <!-- __CINERA_PROJECT_ID__ -->\n" " <!-- __CINERA_PROJECT_ID__ -->\n"
" <!-- __CINERA_SEARCH_URL__ -->\n"
" <!-- __CINERA_THEME__ -->\n" " <!-- __CINERA_THEME__ -->\n"
" <!-- __CINERA_URL__ -->\n" " <!-- __CINERA_URL__ -->\n"
" Only really usable if BaseURL is set %s(-B)%s\n" " Only really usable if BaseURL is set %s(-B)%s\n"
@ -3791,13 +3773,13 @@ StripPWDIndicators(char *Path)
} }
int 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]); char *Ptr = Template->File.Buffer.Ptr + StringLength(TemplateTags[TagIndex]);
ConsumeWhitespace(&(*Template)->Buffer, &Ptr); ConsumeWhitespace(&Template->File.Buffer, &Ptr);
buffer AssetString; buffer AssetString;
ClaimBuffer(&AssetString, "AssetString", MAX_ASSET_FILENAME_LENGTH); 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); DeclaimBuffer(&AssetString);
return RC_ERROR_PARSING; return RC_ERROR_PARSING;
@ -3820,27 +3802,18 @@ ParseAssetString(template **Template, enum8(template_tag_codes) TagIndex, uint32
} }
int 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: // TODO(matt): Record line numbers and contextual information:
// <? ?> // <? ?>
// <!-- --> // <!-- -->
// < > // < >
// <script </script> // <script </script>
#if 1
int Return = ClaimTemplate(Template, Location, TemplateType); InitTemplate(Template, Location, Type);
switch(Return)
{
case RC_ARENA_FULL:
case RC_ERROR_FILE:
return Return;
case RC_SUCCESS:
break;
}
#endif
buffer Errors; 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 HaveErrors = FALSE;
bool HaveAssetParsingErrors = FALSE; bool HaveAssetParsingErrors = FALSE;
@ -3851,19 +3824,20 @@ ValidateTemplate(template **Template, char *Location, enum8(templates) TemplateT
bool FoundScript = FALSE; bool FoundScript = FALSE;
bool FoundSearch = 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: 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]; char *CommentStart = &Template->File.Buffer.Ptr[-1];
while((*Template)->Buffer.Ptr - (*Template)->Buffer.Location < (*Template)->Buffer.Size && StringsDifferT("-->", (*Template)->Buffer.Ptr, 0)) 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) 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 // TODO(matt): Pack up this data for BuffersToHTML() to use
/* /*
@ -3877,6 +3851,8 @@ NextTagSearch:
* *
*/ */
uint32_t AssetIndex = 0;
switch(TagIndex) switch(TagIndex)
{ {
case TAG_SEARCH: case TAG_SEARCH:
@ -3924,12 +3900,7 @@ NextTagSearch:
case TAG_IMAGE: case TAG_IMAGE:
case TAG_JS: case TAG_JS:
{ {
uint32_t AI; if(ParseAssetString(Template, TagIndex, &AssetIndex) == RC_ERROR_PARSING)
if(ParseAssetString(Template, TagIndex, &AI) == RC_SUCCESS)
{
(*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].AssetIndex = AI;
}
else
{ {
HaveErrors = TRUE; HaveErrors = TRUE;
HaveAssetParsingErrors = TRUE; HaveAssetParsingErrors = TRUE;
@ -3937,60 +3908,61 @@ NextTagSearch:
} goto RecordTag; } goto RecordTag;
default: // NOTE(matt): All freely usable tags should hit this case default: // NOTE(matt): All freely usable tags should hit this case
RecordTag: RecordTag:
(*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].Offset = CommentStart - Previous; {
(*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].TagCode = TagIndex; int Offset = CommentStart - Previous;
(*Template)->Metadata.TagCount++; PushTemplateTag(Template, Offset, TagIndex, AssetIndex);
DepartComment(&(*Template)->Buffer); DepartComment(&Template->File.Buffer);
Previous = (*Template)->Buffer.Ptr; Previous = Template->File.Buffer.Ptr;
goto NextTagSearch; goto NextTagSearch;
}
}; };
} }
} }
++(*Template)->Buffer.Ptr; ++Template->File.Buffer.Ptr;
} }
} }
else else
{ {
++(*Template)->Buffer.Ptr; ++Template->File.Buffer.Ptr;
} }
} }
if(HaveAssetParsingErrors) if(HaveAssetParsingErrors)
{ {
DeclaimBuffer(&Errors); DeclaimBuffer(&Errors);
DeclaimTemplate(*Template); FreeTemplate(Template);
return RC_INVALID_TEMPLATE; return RC_INVALID_TEMPLATE;
} }
if(FoundSearch) if(FoundSearch)
{ {
(*Template)->Metadata.Validity |= PAGE_SEARCH; Template->Metadata.Validity |= PAGE_SEARCH;
} }
if(!HaveErrors && FoundIncludes && FoundMenus && FoundPlayer && FoundScript) if(!HaveErrors && FoundIncludes && FoundMenus && FoundPlayer && FoundScript)
{ {
(*Template)->Metadata.Validity |= PAGE_PLAYER; Template->Metadata.Validity |= PAGE_PLAYER;
} }
if(!(Config.Mode & MODE_FORCEINTEGRATION)) 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); fprintf(stderr, "%s", Errors.Location);
DeclaimBuffer(&Errors); DeclaimBuffer(&Errors);
DeclaimTemplate(*Template); FreeTemplate(Template);
return RC_INVALID_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(!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)->Metadata.Filename); }; 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)->Metadata.Filename); }; 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)->Metadata.Filename); }; if(!FoundScript){ CopyStringToBuffer(&Errors, "Player template %s must include one <!-- __CINERA_SCRIPT__ --> tag\n", Template->File.Path); };
fprintf(stderr, "%s", Errors.Location); fprintf(stderr, "%s", Errors.Location);
DeclaimBuffer(&Errors); DeclaimBuffer(&Errors);
DeclaimTemplate(*Template); FreeTemplate(Template);
return RC_INVALID_TEMPLATE; return RC_INVALID_TEMPLATE;
} }
} }
@ -4506,7 +4478,7 @@ OffsetLandmarksMenus(buffer *Src)
// //
int int
HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filename, neighbourhood *N) HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, char *Filename, neighbourhood *N)
{ {
RewindBuffer(&CollationBuffers->IncludesPlayer); RewindBuffer(&CollationBuffers->IncludesPlayer);
RewindBuffer(&CollationBuffers->Menus); RewindBuffer(&CollationBuffers->Menus);
@ -4530,6 +4502,9 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen
*CollationBuffers->Custom14 = '\0'; *CollationBuffers->Custom14 = '\0';
*CollationBuffers->Custom15 = '\0'; *CollationBuffers->Custom15 = '\0';
*CollationBuffers->Title = '\0'; *CollationBuffers->Title = '\0';
*CollationBuffers->URLPlayer = '\0';
*CollationBuffers->URLSearch = '\0';
*CollationBuffers->VODPlatform = '\0';
char Filepath[256]; char Filepath[256];
if(Config.Edition == EDITION_PROJECT) 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"); fprintf(stderr, "Please set the id attribute in the [video] node of your .hmml file\n");
HaveErrors = TRUE; 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; 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); 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) 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_ARENA_FULL:
case RC_INVALID_TEMPLATE: // Invalid template case RC_INVALID_TEMPLATE: // Invalid template
@ -5033,9 +5021,9 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen
{ {
for(int i = 0; i < UniqueRefs; ++i) 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); fprintf(stderr, "%s:%d: Too many timecodes associated with one reference (increase REF_MAX_IDENTIFIER)\n", Filename, Anno->line);
hmml_free(&HMML); hmml_free(&HMML);
return RC_ERROR_MAX_REFS; return RC_ERROR_MAX_REFS;
@ -5737,8 +5725,6 @@ AppendedIdentifier:
" </div>\n" " </div>\n"
" </div>"); " </div>");
// TODO(matt): Maybe do something about indentation levels
buffer URLSearch; buffer URLSearch;
ClaimBuffer(&URLSearch, "URLSearch", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1); ClaimBuffer(&URLSearch, "URLSearch", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1);
ConstructSearchURL(&URLSearch); ConstructSearchURL(&URLSearch);
@ -5869,13 +5855,12 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
fclose(MemLog); fclose(MemLog);
#endif #endif
if(StringsDiffer(Template->Metadata.Filename, "")) if(Template->File.Buffer.Location)
{ {
if((StringsDiffer(Template->Metadata.Filename, "")) if((Template->Metadata.Validity & PageType) || Config.Mode & MODE_FORCEINTEGRATION)
&& ((Template->Metadata.Validity & PageType) || Config.Mode & MODE_FORCEINTEGRATION))
{ {
buffer Output; buffer Output;
Output.Size = Template->Buffer.Size + (Kilobytes(512)); Output.Size = Template->File.Buffer.Size + (Kilobytes(512));
Output.ID = "Output"; Output.ID = "Output";
if(!(Output.Location = malloc(Output.Size))) if(!(Output.Location = malloc(Output.Size)))
{ {
@ -5894,19 +5879,19 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
Output.Ptr = Output.Location; Output.Ptr = Output.Location;
bool NeedPlayerOffset = PlayerOffset ? TRUE : FALSE; 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) for(int i = 0; i < Template->Metadata.TagCount; ++i)
{ {
int j = 0; 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; ++j;
} }
// TODO(matt): Make this whole template stuff context-aware, so it can determine whether or not to HTML-encode // 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 // or sanitise punctuation for CSS-safety
switch(Template->Metadata.Tag[i].TagCode) switch(Template->Metadata.Tags[i].TagCode)
{ {
case TAG_PROJECT_ID: case TAG_PROJECT_ID:
if(CollationBuffers->ProjectID[0] == '\0') if(CollationBuffers->ProjectID[0] == '\0')
@ -5930,12 +5915,9 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
CopyStringToBufferHTMLSafe(&Output, CollationBuffers->ProjectName); CopyStringToBufferHTMLSafe(&Output, CollationBuffers->ProjectName);
} }
break; break;
case TAG_THEME: case TAG_SEARCH_URL: CopyStringToBufferNoFormat(&Output, CollationBuffers->URLSearch); break; // NOTE(matt): Not HTML-safe
CopyStringToBufferNoFormat(&Output, CollationBuffers->Theme); // NOTE(matt): Not HTML-safe case TAG_THEME: CopyStringToBufferNoFormat(&Output, CollationBuffers->Theme); break; // NOTE(matt): Not HTML-safe
break; case TAG_TITLE: CopyStringToBufferHTMLSafe(&Output, CollationBuffers->Title); break;
case TAG_TITLE:
CopyStringToBufferHTMLSafe(&Output, CollationBuffers->Title);
break;
case TAG_URL: case TAG_URL:
if(PageType == PAGE_PLAYER) if(PageType == PAGE_PLAYER)
{ {
@ -5946,9 +5928,8 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
CopyStringToBufferNoFormat(&Output, CollationBuffers->URLSearch); CopyStringToBufferNoFormat(&Output, CollationBuffers->URLSearch);
} }
break; break;
case TAG_VIDEO_ID: case TAG_VIDEO_ID: CopyStringToBufferNoFormat(&Output, CollationBuffers->VideoID); break;
CopyStringToBufferNoFormat(&Output, CollationBuffers->VideoID); case TAG_VOD_PLATFORM: CopyStringToBufferNoFormat(&Output, CollationBuffers->VODPlatform); break;
break;
case TAG_SEARCH: case TAG_SEARCH:
if(Config.Edition == EDITION_SINGLE) if(Config.Edition == EDITION_SINGLE)
{ {
@ -5989,39 +5970,39 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i
case TAG_ASSET: case TAG_ASSET:
{ {
buffer URL; buffer URL;
ConstructResolvedAssetURL(&URL, Template->Metadata.Tag[i].AssetIndex, PageType); ConstructResolvedAssetURL(&URL, Template->Metadata.Tags[i].AssetIndex, PageType);
CopyStringToBuffer(&Output, "%s", URL.Location); CopyStringToBuffer(&Output, "%s", URL.Location);
DeclaimBuffer(&URL); DeclaimBuffer(&URL);
PushAssetLandmark(&Output, Template->Metadata.Tag[i].AssetIndex, PageType); PushAssetLandmark(&Output, Template->Metadata.Tags[i].AssetIndex, PageType);
} break; } break;
case TAG_CSS: case TAG_CSS:
{ {
buffer URL; buffer URL;
ConstructResolvedAssetURL(&URL, Template->Metadata.Tag[i].AssetIndex, PageType); ConstructResolvedAssetURL(&URL, Template->Metadata.Tags[i].AssetIndex, PageType);
CopyStringToBuffer(&Output, CopyStringToBuffer(&Output,
"<link rel=\"stylesheet\" type=\"text/css\" href=\"%s", "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s",
URL.Location); URL.Location);
DeclaimBuffer(&URL); DeclaimBuffer(&URL);
PushAssetLandmark(&Output, Template->Metadata.Tag[i].AssetIndex, PageType); PushAssetLandmark(&Output, Template->Metadata.Tags[i].AssetIndex, PageType);
CopyStringToBuffer(&Output, "\">"); CopyStringToBuffer(&Output, "\">");
} break; } break;
case TAG_IMAGE: case TAG_IMAGE:
{ {
buffer URL; buffer URL;
ConstructResolvedAssetURL(&URL, Template->Metadata.Tag[i].AssetIndex, PageType); ConstructResolvedAssetURL(&URL, Template->Metadata.Tags[i].AssetIndex, PageType);
CopyStringToBuffer(&Output, "%s", URL.Location); CopyStringToBuffer(&Output, "%s", URL.Location);
DeclaimBuffer(&URL); DeclaimBuffer(&URL);
PushAssetLandmark(&Output, Template->Metadata.Tag[i].AssetIndex, PageType); PushAssetLandmark(&Output, Template->Metadata.Tags[i].AssetIndex, PageType);
} break; } break;
case TAG_JS: case TAG_JS:
{ {
buffer URL; buffer URL;
ConstructResolvedAssetURL(&URL, Template->Metadata.Tag[i].AssetIndex, PageType); ConstructResolvedAssetURL(&URL, Template->Metadata.Tags[i].AssetIndex, PageType);
CopyStringToBuffer(&Output, CopyStringToBuffer(&Output,
"<script type=\"text/javascript\" src=\"%s", "<script type=\"text/javascript\" src=\"%s",
URL.Location); URL.Location);
DeclaimBuffer(&URL); DeclaimBuffer(&URL);
PushAssetLandmark(&Output, Template->Metadata.Tag[i].AssetIndex, PageType); PushAssetLandmark(&Output, Template->Metadata.Tags[i].AssetIndex, PageType);
CopyStringToBuffer(&Output, "\"></script>"); CopyStringToBuffer(&Output, "\"></script>");
} break; } break;
case TAG_CUSTOM0: CopyStringToBufferNoFormat(&Output, CollationBuffers->Custom0); 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; 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; FILE *OutFile;
if(!(OutFile = fopen(Config.Edition == EDITION_PROJECT ? OutputPath : Config.OutIntegratedLocation, "w"))) 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)); 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 #if DEBUG_MEM
MemLog = fopen("/home/matt/cinera_mem", "a+"); 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); fwrite(Output.Location, Output.Ptr - Output.Location, 1, OutFile);
fclose(OutFile); fclose(OutFile);
free(Output.Location); FreeBuffer(&Output);
#if DEBUG_MEM #if DEBUG_MEM
MemLog = fopen("/home/matt/cinera_mem", "a+"); MemLog = fopen("/home/matt/cinera_mem", "a+");
@ -6455,7 +6436,7 @@ SnipeEntryIntoMetadataBuffer(db_entry *Entry, int EntryIndex)
} }
int 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; enum8(edit_types) EditType = EDIT_APPEND;
int EntryInsertionStart = StringLength("---\n"); int EntryInsertionStart = StringLength("---\n");
@ -6630,7 +6611,7 @@ PrintLandmarks(void *FirstLandmark, int LandmarkCount)
} }
void 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) if(N->PrevIndex >= 0)
{ {
@ -6641,7 +6622,7 @@ ProcessPrevLandmarks(neighbourhood *N, void *FirstLandmark, int ExistingLandmark
for(int j = 0; j < FormerTarget.Length; ++j, ++*RunningIndex) for(int j = 0; j < FormerTarget.Length; ++j, ++*RunningIndex)
{ {
db_landmark Landmark = *(db_landmark *)(FirstLandmark + sizeof(Landmark) * *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; Landmark.Position += N->PrevOffsetModifier;
} }
@ -6656,7 +6637,7 @@ ProcessPrevLandmarks(neighbourhood *N, void *FirstLandmark, int ExistingLandmark
} }
void 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) 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) for(int j = 0; j < LatterTarget.Length; ++j, ++*RunningIndex)
{ {
Landmark = *(db_landmark *)(FirstLandmark + sizeof(Landmark) * *RunningIndex); Landmark = *(db_landmark *)(FirstLandmark + sizeof(Landmark) * *RunningIndex);
if(!OffsetLandmarks && Landmark.Position >= N->PreLinkNextOffsetTotal) if(Landmark.Position >= N->PreLinkNextOffsetTotal)
{ {
Landmark.Position += N->NextOffsetModifier; Landmark.Position += N->NextOffsetModifier;
} }
@ -6763,11 +6744,11 @@ DeleteLandmarks(neighbourhood *N)
DB.Asset.LandmarkCount -= DeletionTarget.Length; DB.Asset.LandmarkCount -= DeletionTarget.Length;
fwrite(&DB.Asset, sizeof(DB.Asset), 1, DB.Metadata.Handle); 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; 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; DB.Metadata.Buffer.Ptr += sizeof(db_landmark) * ExistingLandmarkCount;
} }
CycleFile(&DB.Metadata); 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) if(!StringsDiffer(DB.Asset.Filename, Assets.Asset[i].Filename) && DB.Asset.Type == Assets.Asset[i].Type)
{ {
Assets.Asset[i].Known = TRUE; Assets.Asset[i].Known = TRUE;
DB.Asset.LandmarkCount += Assets.Asset[i].PlayerLandmarkCount; if(!Assets.Asset[i].OffsetLandmarks)
landmark_range ThisTarget;
if(ExistingLandmarkCount > 0)
{ {
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;
} }
else
fwrite(&DB.Asset, sizeof(DB.Asset), 1, DB.Metadata.Handle);
if(ExistingLandmarkCount > 0)
{ {
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; break;
} }
} }
@ -7634,7 +7623,7 @@ GeneratePlayerPage(neighbourhood *N, buffers *CollationBuffers, template *Player
bool SearchInTemplate = FALSE; bool SearchInTemplate = FALSE;
for(int TagIndex = 0; TagIndex < PlayerTemplate->Metadata.TagCount; ++TagIndex) 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; SearchInTemplate = TRUE;
SearchToBuffer(CollationBuffers); SearchToBuffer(CollationBuffers);
@ -7713,13 +7702,13 @@ int
InsertEntry(neighbourhood *Neighbourhood, buffers *CollationBuffers, template *PlayerTemplate, template *BespokeTemplate, char *BaseFilename, bool RecheckingPrivacy) InsertEntry(neighbourhood *Neighbourhood, buffers *CollationBuffers, template *PlayerTemplate, template *BespokeTemplate, char *BaseFilename, bool RecheckingPrivacy)
{ {
bool Reinserting = FALSE; 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); LinkNeighbours(Neighbourhood, LINK_INCLUDE);
if(StringsDiffer(BespokeTemplate->Metadata.Filename, "")) if(BespokeTemplate->File.Buffer.Location)
{ {
GeneratePlayerPage(Neighbourhood, CollationBuffers, BespokeTemplate, BaseFilename, Reinserting); GeneratePlayerPage(Neighbourhood, CollationBuffers, BespokeTemplate, BaseFilename, Reinserting);
DeclaimTemplate(BespokeTemplate); FreeTemplate(BespokeTemplate);
} }
else else
{ {
@ -8799,7 +8788,6 @@ main(int ArgC, char **Args)
// NOTE(matt): Init MemoryArenas (they are global) // NOTE(matt): Init MemoryArenas (they are global)
InitMemoryArena(&MemoryArena, Megabytes(4)); InitMemoryArena(&MemoryArena, Megabytes(4));
InitMemoryArena(&TemplateArena, Kilobytes(16)); // TODO(matt): Consider some way of making this growable
#if DEBUG_MEM #if DEBUG_MEM
FILE *MemLog = fopen("/home/matt/cinera_mem", "a+"); FILE *MemLog = fopen("/home/matt/cinera_mem", "a+");
@ -9029,19 +9017,19 @@ main(int ArgC, char **Args)
inotifyInstance = inotify_init1(IN_NONBLOCK); inotifyInstance = inotify_init1(IN_NONBLOCK);
printf("┌╼ Hashing assets ╾┐\n"); 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 // ensure that the builtin assets get placed correctly
InitAssets(); InitAssets();
} }
printf("┌╼ Packing templates ╾┐\n"); printf("┌╼ Packing templates ╾┐\n");
template *SearchTemplate; InitTemplate(&SearchTemplate); template SearchTemplate;
template *PlayerTemplate; InitTemplate(&PlayerTemplate); template PlayerTemplate;
template *BespokeTemplate; InitTemplate(&BespokeTemplate); template BespokeTemplate;
if(StringsDiffer(Config.TemplatePlayerLocation, "")) 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_INVALID_TEMPLATE: // Invalid template
case RC_ERROR_FILE: // Could not load 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, "")) 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_INVALID_TEMPLATE: // Invalid template
case RC_ERROR_MEMORY: // Could not allocate memory for 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) if(Config.Edition == EDITION_PROJECT)
{ {
printf("\n┌╼ Synchronising with Annotations Directory ╾┐\n"); printf("\n┌╼ Synchronising with Annotations Directory ╾┐\n");
SyncDBWithInput(&CollationBuffers, SearchTemplate, PlayerTemplate, BespokeTemplate); SyncDBWithInput(&CollationBuffers, &SearchTemplate, &PlayerTemplate, &BespokeTemplate);
if(Config.Mode & MODE_ONESHOT) if(Config.Mode & MODE_ONESHOT)
{ {
goto RIP; goto RIP;
@ -9081,7 +9069,7 @@ main(int ArgC, char **Args)
// NOTE(matt): Do we want to also watch IN_DELETE_SELF events? // NOTE(matt): Do we want to also watch IN_DELETE_SELF events?
PushHMMLWatchHandle(); 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 // 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) if(!(Config.Mode & MODE_NOPRIVACY) && time(0) - LastPrivacyCheck > 60 * 60 * 4)
{ {
RecheckPrivacy(&CollationBuffers, SearchTemplate, PlayerTemplate, BespokeTemplate); RecheckPrivacy(&CollationBuffers, &SearchTemplate, &PlayerTemplate, &BespokeTemplate);
} }
sleep(Config.UpdateInterval); sleep(Config.UpdateInterval);
} }
@ -9141,16 +9129,16 @@ NextFile:
break; break;
}; };
HasBespokeTemplate = StringsDiffer(BespokeTemplate->Metadata.Filename, ""); HasBespokeTemplate = BespokeTemplate.File.Buffer.Location != NULL;
switch(BuffersToHTML(&CollationBuffers, switch(BuffersToHTML(&CollationBuffers,
HasBespokeTemplate ? BespokeTemplate : PlayerTemplate, HasBespokeTemplate ? &BespokeTemplate : &PlayerTemplate,
0, 0,
PAGE_PLAYER, 0)) PAGE_PLAYER, 0))
{ {
// TODO(matt): Actually sort out the fatality of these cases // TODO(matt): Actually sort out the fatality of these cases
case RC_INVALID_TEMPLATE: case RC_INVALID_TEMPLATE:
if(HasBespokeTemplate) { DeclaimTemplate(BespokeTemplate); } if(HasBespokeTemplate) { FreeTemplate(&BespokeTemplate); }
if(FileIndex < (ArgC - 1)) { goto NextFile; } if(FileIndex < (ArgC - 1)) { goto NextFile; }
case RC_ERROR_MEMORY: case RC_ERROR_MEMORY:
case RC_ERROR_FILE: case RC_ERROR_FILE:
@ -9160,20 +9148,20 @@ NextFile:
#if 0 #if 0
fprintf(stdout, "%sWritten%s %s\n", HasBespokeTemplate ? Config.OutIntegratedLocation : Config.OutLocation); fprintf(stdout, "%sWritten%s %s\n", HasBespokeTemplate ? Config.OutIntegratedLocation : Config.OutLocation);
#endif #endif
if(HasBespokeTemplate) { DeclaimTemplate(BespokeTemplate); } if(HasBespokeTemplate) { FreeTemplate(&BespokeTemplate); }
break; 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); DeclaimBuffer(&CollationBuffers.SearchEntry);