2017-03-10 14:19:25 +00:00
#if 0
ctime - begin $ { 0 % . * } . ctm
2017-08-23 20:30:08 +00:00
gcc - g - fsanitize = address - Wall - std = c99 - pipe $ 0 - o $ { 0 % . * } hmml . a - lcurl
2017-03-10 14:19:25 +00:00
ctime - end $ { 0 % . * } . ctm
exit
# endif
2017-05-22 21:37:00 +00:00
# define DEBUG 0
2017-05-21 06:35:16 +00:00
2017-03-23 00:34:59 +00:00
typedef unsigned int bool ;
2017-03-10 14:19:25 +00:00
# define TRUE 1
# define FALSE 0
2017-04-13 00:21:04 +00:00
# include <stdarg.h> // NOTE(matt): varargs
# include <stdio.h> // NOTE(matt): printf, sprintf, vsprintf, fprintf, perror
# include <stdlib.h> // NOTE(matt): calloc, malloc, free
2017-03-23 00:34:59 +00:00
# include "hmmlib.h"
2017-06-25 18:05:58 +00:00
# include <getopt.h> // NOTE(matt): getopts
2017-05-25 20:28:52 +00:00
//#include "config.h" // TODO(matt): Implement config.h
2017-08-09 00:57:09 +00:00
# include <curl/curl.h>
2017-08-10 01:05:41 +00:00
# include <time.h>
# include <sys/stat.h>
2017-08-23 20:30:08 +00:00
# include <sys/types.h>
# include <dirent.h>
2017-09-07 21:41:08 +00:00
# include <string.h> // NOTE(matt): strerror
# include <errno.h> //NOTE(matt): errno
2017-09-15 01:11:39 +00:00
# include <sys/inotify.h> // NOTE(matt): inotify
# include <unistd.h> // NOTE(matt): sleep()
2017-03-23 00:34:59 +00:00
2017-05-24 21:56:36 +00:00
# define Kilobytes(Bytes) Bytes << 10
# define Megabytes(Bytes) Bytes << 20
2017-06-10 16:47:47 +00:00
enum
{
2017-08-19 21:41:51 +00:00
EDITION_SINGLE ,
EDITION_PROJECT ,
EDITION_NETWORK
2017-08-29 20:35:28 +00:00
} editions ;
2017-09-07 21:41:08 +00:00
enum
{
// NOTE(matt): https://tools.ietf.org/html/rfc5424#section-6.2.1
LOG_EMERGENCY ,
LOG_ALERT ,
LOG_CRITICAL ,
LOG_ERROR ,
LOG_WARNING ,
LOG_NOTICE ,
LOG_INFORMATIONAL ,
LOG_DEBUG
} log_levels ;
2017-08-29 20:35:28 +00:00
enum
{
MODE_BARE ,
MODE_INTEGRATE
} modes ;
2017-09-07 21:41:08 +00:00
enum
{
RC_ARENA_FULL ,
RC_ERROR_DIRECTORY ,
RC_ERROR_FATAL ,
RC_ERROR_FILE ,
RC_ERROR_HMML ,
RC_ERROR_MAX_REFS ,
RC_ERROR_MEMORY ,
RC_ERROR_QUOTE ,
RC_ERROR_TEMPLATE ,
RC_FAILURE ,
RC_FOUND ,
RC_UNFOUND ,
RC_INVALID_TEMPLATE ,
RC_INVALID_REFERENCE ,
RC_NOOP ,
RC_REFRESHED ,
RC_RIP ,
RC_SUCCESS
} returns ;
2017-08-29 20:35:28 +00:00
typedef struct
{
char * BaseDir ;
char CacheDir [ 255 ] ;
char * CSSDir ;
int Edition ;
char * ImagesDir ;
char * JSDir ;
2017-09-07 21:41:08 +00:00
int LogLevel ;
2017-08-29 20:35:28 +00:00
char * DefaultMedium ;
int Mode ;
char * OutLocation ;
char * OutIntegratedLocation ;
bool ForceIntegration ;
char * ProjectDir ;
2017-09-07 21:41:08 +00:00
char * TemplateIndexLocation ;
char * TemplatePlayerLocation ;
2017-08-29 20:35:28 +00:00
} config ;
2017-06-10 16:47:47 +00:00
2017-09-07 21:41:08 +00:00
typedef struct
{
void * Location ;
void * Ptr ;
char * ID ;
int Size ;
} arena ;
2017-03-10 14:19:25 +00:00
typedef struct
{
char * Location ;
char * Ptr ;
2017-05-21 06:35:16 +00:00
char * ID ;
2017-03-10 14:19:25 +00:00
int Size ;
} buffer ;
2017-09-07 21:41:08 +00:00
enum
{
TAG_INDEX ,
TAG_INCLUDES ,
TAG_MENUS ,
TAG_PLAYER ,
TAG_SCRIPT ,
TAG_TITLE
} template_tags ;
typedef struct
{
int Code ; // template_tags
char * Tag ;
} tag ;
tag Tags [ ] = {
{ TAG_INDEX , " __CINERA_INDEX__ " } ,
{ TAG_INCLUDES , " __CINERA_INCLUDES__ " } ,
{ TAG_MENUS , " __CINERA_MENUS__ " } ,
{ TAG_PLAYER , " __CINERA_PLAYER__ " } ,
{ TAG_SCRIPT , " __CINERA_SCRIPT__ " } ,
{ TAG_TITLE , " __CINERA_TITLE__ " } ,
} ;
typedef struct
{
int Offset ;
int TagCode ;
} tag_offset ;
typedef struct
{
char Filename [ 120 ] ;
tag_offset Tag [ 16 ] ;
int Validity ; // NOTE(matt): Bitmask describing which page the template is valid for, i.e. contents and / or player page
int TagCount ;
} template ;
2017-08-29 20:35:28 +00:00
typedef struct
{
buffer IncludesIndex ;
buffer Index ;
buffer IncludesPlayer ;
buffer Menus ;
buffer Player ;
buffer Script ;
char Title [ 255 ] ;
2017-09-07 21:41:08 +00:00
char Project [ 32 ] ;
2017-08-29 20:35:28 +00:00
} buffers ;
2017-05-21 06:35:16 +00:00
// TODO(matt): Consider putting the ref_info and quote_info into linked lists on the heap, just to avoid all the hardcoded sizes
typedef struct
{
char Date [ 32 ] ;
char Text [ 512 ] ;
} quote_info ;
2017-03-19 01:23:44 +00:00
typedef struct
{
2017-03-30 02:57:04 +00:00
char Timecode [ 8 ] ;
int Identifier ;
} identifier ;
2017-07-29 23:01:39 +00:00
# define REF_MAX_IDENTIFIER 64
2017-05-13 15:38:10 +00:00
2017-03-30 02:57:04 +00:00
typedef struct
{
2017-04-13 00:21:04 +00:00
char RefTitle [ 620 ] ;
char ID [ 512 ] ;
char URL [ 512 ] ;
char Source [ 256 ] ;
2017-05-13 15:38:10 +00:00
identifier Identifier [ REF_MAX_IDENTIFIER ] ;
2017-03-30 02:57:04 +00:00
int IdentifierCount ;
2017-03-19 01:23:44 +00:00
} ref_info ;
2017-04-23 02:47:42 +00:00
typedef struct
{
2017-05-24 21:56:36 +00:00
char Marker [ 32 ] ;
char WrittenText [ 32 ] ;
2017-05-21 06:35:16 +00:00
} category_info ;
2017-04-23 02:47:42 +00:00
2017-08-19 21:41:51 +00:00
typedef struct
{
category_info Category [ 64 ] ;
int Count ;
} categories ;
2017-05-25 20:28:52 +00:00
// TODO(matt): Parse this stuff out of a config file
2017-08-18 22:46:58 +00:00
typedef struct
{
char * Username ;
char * CreditedName ;
char * HomepageURL ;
char * SupportIcon ;
char * SupportURL ;
} credential_info ;
credential_info Credentials [ ] =
2017-05-25 20:28:52 +00:00
{
2017-06-21 19:38:57 +00:00
{ " Miblo " , " Matt Mascarenhas " , " http://miblodelcarpio.co.uk " , " cinera_icon_patreon.png " , " https://patreon.com/miblo " } ,
{ " miotatsu " , " Mio Iwakura " , " http://riscy.tv/ " , " cinera_icon_patreon.png " , " https://patreon.com/miotatsu " } ,
2017-05-25 20:28:52 +00:00
{ " nothings " , " Sean Barrett " , " https://nothings.org/ " , " " , " " } ,
2017-06-21 19:38:57 +00:00
{ " cmuratori " , " Casey Muratori " , " https://handmadehero.org " , " cinera_icon_patreon.png " , " https://patreon.com/cmuratori " } ,
2017-05-25 20:28:52 +00:00
{ " fierydrake " , " Mike Tunnicliffe " , " " , " " , " " } ,
2017-06-21 19:38:57 +00:00
{ " abnercoimbre " , " Abner Coimbre " , " https://handmade.network/m/abnercoimbre " , " cinera_icon_patreon.png " , " https://patreon.com/handmade_dev " } ,
2017-08-18 22:46:58 +00:00
{ " /y_lee " , " Yunsup Lee " , " https://www.linkedin.com/in/yunsup-lee-385b692b/ " , " " , " " } ,
{ " /a_waterman " , " Andrew Waterman " , " https://www.linkedin.com/in/andrew-waterman-76805788 " , " " , " " } ,
{ " debiatan " , " Miguel Lechón " , " http://blog.debiatan.net/ " , " " , " " } ,
} ;
typedef struct
{
char * Medium ;
char * Icon ;
char * WrittenName ;
} category_medium ;
category_medium CategoryMedium [ ] =
{
// medium icon written name
{ " afk " , " … " , " Away from Keyboard " } , // TODO(matt): Filter this out by default
{ " authored " , " 🗪 " , " Chat Comment " } , // TODO(matt): Conditionally handle Chat vs Guest Comments
{ " blackboard " , " 🖌 " , " Blackboard " } ,
{ " experience " , " 🍷 " , " Experience " } ,
{ " owl " , " 🦉 " , " Owl of Shame " } ,
{ " programming " , " 🖮 " , " Programming " } , // TODO(matt): Potentially make this configurable per project
{ " rant " , " 💢 " , " Rant " } ,
{ " research " , " 📖 " , " Research " } ,
{ " run " , " 🏃 " , " In-Game " } , // TODO(matt): Potentially make this configurable per project
{ " trivia " , " 🎲 " , " Trivia " } ,
2017-05-25 20:28:52 +00:00
} ;
2017-03-25 02:07:55 +00:00
# define ArrayCount(A) sizeof(A) / sizeof(*(A))
2017-08-20 21:23:15 +00:00
__attribute__ ( ( format ( printf , 2 , 3 ) ) )
2017-05-21 06:35:16 +00:00
void
2017-08-20 21:23:15 +00:00
CopyString ( char Dest [ ] , char * Format , . . . )
2017-05-21 06:35:16 +00:00
{
2017-08-20 21:23:15 +00:00
va_list Args ;
va_start ( Args , Format ) ;
vsprintf ( Dest , Format , Args ) ;
va_end ( Args ) ;
2017-03-16 00:56:58 +00:00
}
2017-03-10 14:19:25 +00:00
int
2017-08-20 21:23:15 +00:00
StringLength ( char * String )
2017-03-10 14:19:25 +00:00
{
2017-08-20 21:23:15 +00:00
int i = 0 ;
while ( String [ i ] )
2017-03-10 14:19:25 +00:00
{
2017-08-20 21:23:15 +00:00
+ + i ;
2017-03-10 14:19:25 +00:00
}
2017-08-20 21:23:15 +00:00
return i ;
2017-03-10 14:19:25 +00:00
}
2017-03-16 00:56:58 +00:00
void
2017-03-30 02:57:04 +00:00
CopyBuffer ( buffer * Dest , buffer * Src )
2017-03-16 00:56:58 +00:00
{
Src - > Ptr = Src - > Location ;
while ( * Src - > Ptr )
{
2017-05-21 06:35:16 +00:00
// TODO(matt)
{
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
2017-05-24 21:56:36 +00:00
fprintf ( stderr , " CopyBuffer: %s cannot accommodate %s \n " , Dest - > ID , Src - > ID ) ;
2017-05-21 06:35:16 +00:00
__asm__ ( " int3 " ) ;
}
}
2017-03-16 00:56:58 +00:00
* Dest - > Ptr + + = * Src - > Ptr + + ;
}
2017-08-19 21:41:51 +00:00
* Dest - > Ptr = ' \0 ' ;
2017-03-16 00:56:58 +00:00
}
2017-06-13 22:13:03 +00:00
int
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( char * Dest , char * String )
{
2017-06-13 22:13:03 +00:00
int Length = 0 ;
2017-06-07 23:47:47 +00:00
while ( * String )
{
* Dest + + = * String + + ;
2017-06-13 22:13:03 +00:00
+ + Length ;
2017-06-07 23:47:47 +00:00
}
2017-06-15 22:10:36 +00:00
* Dest = ' \0 ' ;
2017-06-13 22:13:03 +00:00
return Length ;
2017-06-07 23:47:47 +00:00
}
2017-08-29 20:35:28 +00:00
// TODO(matt): Maybe do a version of this that takes a string as a Terminator
2017-08-18 22:46:58 +00:00
int
CopyStringNoFormatT ( char * Dest , char * String , char Terminator )
{
int Length = 0 ;
while ( * String ! = Terminator )
{
* Dest + + = * String + + ;
+ + Length ;
}
* Dest = ' \0 ' ;
return Length ;
}
2017-03-29 03:38:12 +00:00
__attribute__ ( ( format ( printf , 2 , 3 ) ) )
2017-03-16 00:56:58 +00:00
void
2017-03-23 00:34:59 +00:00
CopyStringToBuffer ( buffer * Dest , char * Format , . . . )
2017-03-16 00:56:58 +00:00
{
2017-03-23 00:34:59 +00:00
va_list Args ;
va_start ( Args , Format ) ;
2017-06-03 01:32:18 +00:00
int Length = vsnprintf ( Dest - > Ptr , Dest - > Size - ( Dest - > Ptr - Dest - > Location ) , Format , Args ) ;
2017-03-23 00:34:59 +00:00
va_end ( Args ) ;
2017-05-21 06:35:16 +00:00
// TODO(matt):
{
if ( Length + ( Dest - > Ptr - Dest - > Location ) > = Dest - > Size )
{
2017-06-21 23:16:09 +00:00
fprintf ( stderr , " CopyStringToBuffer: %s cannot accommodate %d-character string: \n "
" \n "
2017-06-23 14:47:48 +00:00
" %s \n " , Dest - > ID , Length , Format ) ;
2017-05-21 06:35:16 +00:00
__asm__ ( " int3 " ) ;
}
}
2017-03-23 00:34:59 +00:00
Dest - > Ptr + = Length ;
2017-03-16 00:56:58 +00:00
}
2017-06-07 23:47:47 +00:00
void
CopyStringToBufferHTMLSafe ( buffer * Dest , char * String )
{
while ( * String )
{
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
fprintf ( stderr , " CopyStringToBufferHTMLSafe: %s cannot accommodate %d-character string \n " , Dest - > ID , StringLength ( String ) ) ;
__asm__ ( " int3 " ) ;
}
switch ( * String )
{
case ' < ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( Dest , " < " ) ;
String + + ;
break ;
2017-06-07 23:47:47 +00:00
case ' > ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( Dest , " > " ) ;
String + + ;
break ;
2017-06-07 23:47:47 +00:00
case ' & ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( Dest , " & " ) ;
String + + ;
break ;
2017-06-07 23:47:47 +00:00
case ' \" ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( Dest , " " " ) ;
String + + ;
break ;
2017-06-07 23:47:47 +00:00
case ' \' ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( Dest , " ' " ) ;
String + + ;
break ;
2017-06-07 23:47:47 +00:00
default :
2017-06-25 18:05:58 +00:00
* Dest - > Ptr + + = * String + + ;
break ;
2017-06-07 23:47:47 +00:00
}
}
}
2017-03-17 01:45:16 +00:00
int
2017-06-13 22:13:03 +00:00
StringsDiffer ( char * A , char * B ) // NOTE(matt): Two null-terminated strings
2017-03-17 01:45:16 +00:00
{
2017-03-21 02:38:18 +00:00
while ( * A & & * B & & * A = = * B )
2017-03-17 01:45:16 +00:00
{
+ + A , + + B ;
}
return * A - * B ;
}
2017-04-23 00:30:37 +00:00
bool
2017-06-13 22:13:03 +00:00
StringsDifferT ( char * A , // NOTE(matt): Null-terminated string
char * B , // NOTE(matt): Not null-terminated string (e.g. one mid-buffer)
2017-06-16 07:55:59 +00:00
char Terminator // NOTE(matt): Caller definable terminator. Pass 0 to only match on the extent of A
2017-06-13 22:13:03 +00:00
)
2017-04-23 00:30:37 +00:00
{
2017-06-13 22:13:03 +00:00
int ALength = StringLength ( A ) ;
2017-04-23 00:30:37 +00:00
int i = 0 ;
2017-06-13 22:13:03 +00:00
while ( i < ALength & & A [ i ] & & A [ i ] = = B [ i ] )
2017-04-23 00:30:37 +00:00
{
+ + i ;
}
2017-06-13 22:13:03 +00:00
if ( ( ! Terminator & & ! A [ i ] & & ALength = = i ) | |
2017-06-25 18:05:58 +00:00
( ! A [ i ] & & ALength = = i & & ( B [ i ] = = Terminator ) ) )
2017-04-23 00:30:37 +00:00
{
return FALSE ;
}
else
{
return TRUE ;
}
}
2017-08-20 21:23:15 +00:00
int
MakeDir ( char * Path )
{
2017-09-07 21:41:08 +00:00
// TODO(matt): Correctly check for permissions
2017-08-20 21:23:15 +00:00
int i = StringLength ( Path ) ;
int Ancestors = 0 ;
while ( mkdir ( Path , 00755 ) = = - 1 )
{
2017-09-07 21:41:08 +00:00
if ( errno = = EACCES )
{
return RC_ERROR_DIRECTORY ;
}
2017-08-20 21:23:15 +00:00
while ( Path [ i ] ! = ' / ' & & i > 0 )
{
- - i ;
}
+ + Ancestors ;
Path [ i ] = ' \0 ' ;
2017-09-07 21:41:08 +00:00
if ( i = = 0 ) { return RC_ERROR_DIRECTORY ; }
2017-08-20 21:23:15 +00:00
}
while ( Ancestors > 0 )
{
while ( Path [ i ] ! = ' \0 ' )
{
+ + i ;
}
Path [ i ] = ' / ' ;
- - Ancestors ;
if ( ( mkdir ( Path , 00755 ) ) = = - 1 )
{
2017-09-07 21:41:08 +00:00
return RC_ERROR_DIRECTORY ;
2017-08-20 21:23:15 +00:00
}
}
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
}
void
LogUsage ( buffer Buffer , char * CacheDir )
{
2017-09-15 01:11:39 +00:00
# if DEBUG
2017-09-07 21:41:08 +00:00
char LogPath [ 255 ] ;
CopyString ( LogPath , " %s/%s " , CacheDir , " buffers.log " ) ;
FILE * LogFile ;
if ( ! ( LogFile = fopen ( LogPath , " a+ " ) ) )
{
MakeDir ( CacheDir ) ;
if ( ! ( LogFile = fopen ( LogPath , " a+ " ) ) )
{
perror ( " LogUsage " ) ;
return ;
}
}
fprintf ( LogFile , " %s,%ld,%d \n " ,
Buffer . ID ,
Buffer . Ptr - Buffer . Location ,
Buffer . Size ) ;
fclose ( LogFile ) ;
2017-09-15 01:11:39 +00:00
# endif
2017-09-07 21:41:08 +00:00
}
__attribute__ ( ( format ( printf , 3 , 4 ) ) )
void
LogError ( config Config , int LogLevel , char * Format , . . . )
{
if ( Config . LogLevel > = LogLevel )
{
char LogPath [ 255 ] ;
CopyString ( LogPath , " %s/%s " , Config . CacheDir , " errors.log " ) ;
FILE * LogFile ;
if ( ! ( LogFile = fopen ( LogPath , " a+ " ) ) )
{
MakeDir ( Config . CacheDir ) ;
if ( ! ( LogFile = fopen ( LogPath , " a+ " ) ) )
{
perror ( " LogUsage " ) ;
return ;
}
}
va_list Args ;
va_start ( Args , Format ) ;
vfprintf ( LogFile , Format , Args ) ;
va_end ( Args ) ;
// TODO(matt): Include the LogLevel "string" and the current wall time
fprintf ( LogFile , " \n " ) ;
fclose ( LogFile ) ;
}
2017-08-20 21:23:15 +00:00
}
void
FreeBuffer ( buffer * Buffer )
{
free ( Buffer - > Location ) ;
}
2017-08-29 20:35:28 +00:00
#if 0
# define ClaimBuffer(MemoryArena, Buffer, ID, Size) if(__ClaimBuffer(MemoryArena, Buffer, ID, Size))\
2017-08-20 21:23:15 +00:00
{ \
fprintf ( stderr , " %s:%d: MemoryArena cannot contain %s of size %d \n " , __FILE__ , __LINE__ , ID , Size ) ; \
hmml_free ( & HMML ) ; \
FreeBuffer ( MemoryArena ) ; \
return 1 ; \
} ;
2017-08-29 20:35:28 +00:00
# endif
2017-08-20 21:23:15 +00:00
int
2017-09-07 21:41:08 +00:00
ClaimBuffer ( arena * MemoryArena , buffer * Buffer , char * ID , int Size )
2017-08-20 21:23:15 +00:00
{
2017-08-29 20:35:28 +00:00
if ( MemoryArena - > Ptr - MemoryArena - > Location + Size > MemoryArena - > Size )
2017-08-20 21:23:15 +00:00
{
2017-09-07 21:41:08 +00:00
return RC_ARENA_FULL ;
2017-08-20 21:23:15 +00:00
}
2017-09-07 21:41:08 +00:00
Buffer - > Location = ( char * ) MemoryArena - > Ptr ;
2017-08-20 21:23:15 +00:00
Buffer - > Size = Size ;
Buffer - > ID = ID ;
2017-08-29 20:35:28 +00:00
MemoryArena - > Ptr + = Buffer - > Size ;
2017-08-20 21:23:15 +00:00
* Buffer - > Location = ' \0 ' ;
Buffer - > Ptr = Buffer - > Location ;
# if DEBUG
printf ( " Claimed: %s: %d \n "
2017-08-29 20:35:28 +00:00
" Total ClaimedMemory: %ld \n \n " , Buffer - > ID , Buffer - > Size , MemoryArena - > Ptr - MemoryArena - > Location ) ;
2017-08-20 21:23:15 +00:00
# endif
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
2017-08-20 21:23:15 +00:00
}
2017-08-29 20:35:28 +00:00
# define DeclaimBuffer(MemoryArena, Buffer) __DeclaimBuffer(MemoryArena, Buffer, Config)
2017-08-20 21:23:15 +00:00
void
2017-09-07 21:41:08 +00:00
__DeclaimBuffer ( arena * MemoryArena , buffer * Buffer , config Config )
2017-08-20 21:23:15 +00:00
{
* Buffer - > Location = ' \0 ' ;
2017-08-29 20:35:28 +00:00
MemoryArena - > Ptr - = Buffer - > Size ;
2017-08-20 21:23:15 +00:00
float PercentageUsed = ( float ) ( Buffer - > Ptr - Buffer - > Location ) / Buffer - > Size * 100 ;
# if DEBUG
printf ( " Declaimed: %s \n "
" Used: %ld / %d (%.2f%%) \n "
" \n "
2017-08-29 20:35:28 +00:00
" Total ClaimedMemory: %ld \n \n " ,
2017-08-20 21:23:15 +00:00
Buffer - > ID ,
Buffer - > Ptr - Buffer - > Location ,
Buffer - > Size ,
PercentageUsed ,
2017-08-29 20:35:28 +00:00
MemoryArena - > Ptr - MemoryArena - > Location ) ;
2017-08-20 21:23:15 +00:00
# endif
2017-08-29 20:35:28 +00:00
LogUsage ( * Buffer , Config . CacheDir ) ;
2017-08-20 21:23:15 +00:00
if ( PercentageUsed > = 80.0f )
{
// TODO(matt): Implement either dynamically growing buffers, or phoning home to matt@handmadedev.org
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " %s used %.2f%% of its allotted memory \n " , Buffer - > ID , PercentageUsed ) ;
2017-08-20 21:23:15 +00:00
fprintf ( stderr , " Warning: %s used %.2f%% of its allotted memory \n " , Buffer - > ID , PercentageUsed ) ;
}
}
2017-09-07 21:41:08 +00:00
int
ClaimTemplate ( arena * MemoryArena , template * * Template , char * ID )
{
if ( MemoryArena - > Ptr - MemoryArena - > Location + sizeof ( template ) > MemoryArena - > Size )
{
return RC_ARENA_FULL ;
}
* Template = ( template * ) MemoryArena - > Ptr ;
CopyString ( ( * Template ) - > Filename , ID ) ;
MemoryArena - > Ptr + = sizeof ( template ) ;
# if DEBUG
printf ( " Claimed: %s metadata: %ld \n "
" Total ClaimedMemory: %ld \n \n " , ( * Template ) - > Filename , sizeof ( template ) , MemoryArena - > Ptr - MemoryArena - > Location ) ;
# endif
return RC_SUCCESS ;
}
int
DeclaimTemplate ( arena * MemoryArena , template * * Template )
{
MemoryArena - > Ptr - = sizeof ( template ) ;
# if DEBUG
printf ( " Declaimed: %s metadata \n "
" Total ClaimedMemory: %ld \n \n " ,
( * Template ) - > Filename ,
MemoryArena - > Ptr - MemoryArena - > Location ) ;
# endif
return RC_SUCCESS ;
}
2017-08-20 21:23:15 +00:00
int
TimecodeToSeconds ( char * Timecode )
{
int HMS [ 3 ] = { 0 , 0 , 0 } ; // 0 == Seconds; 1 == Minutes; 2 == Hours
int Colons = 0 ;
while ( * Timecode )
{
//if((*Timecode < '0' || *Timecode > '9') && *Timecode != ':') { return FALSE; }
if ( * Timecode = = ' : ' )
{
+ + Colons ;
//if(Colons > 2) { return FALSE; }
for ( int i = 0 ; i < Colons ; + + i )
{
HMS [ Colons - i ] = HMS [ Colons - ( i + 1 ) ] ;
}
HMS [ 0 ] = 0 ;
}
else
{
HMS [ 0 ] = HMS [ 0 ] * 10 + * Timecode - ' 0 ' ;
}
+ + Timecode ;
}
//if(HMS[0] > 59 || HMS[1] > 59 || Timecode[-1] == ':') { return FALSE; }
return HMS [ 2 ] * 60 * 60 + HMS [ 1 ] * 60 + HMS [ 0 ] ;
}
2017-04-13 23:46:21 +00:00
typedef struct
{
2017-04-21 01:25:40 +00:00
unsigned int Hue : 16 ;
unsigned int Saturation : 8 ;
unsigned int Lightness : 8 ;
2017-04-13 23:46:21 +00:00
} hsl_colour ;
hsl_colour
2017-03-18 02:04:13 +00:00
CharToColour ( char Char )
{
2017-04-13 23:46:21 +00:00
hsl_colour Colour ;
2017-03-18 02:04:13 +00:00
if ( Char > = ' a ' & & Char < = ' z ' )
{
2017-04-19 01:10:45 +00:00
Colour . Hue = ( ( ( float ) Char - ' a ' ) / ( ' z ' - ' a ' ) * 360 ) ;
Colour . Saturation = ( ( ( float ) Char - ' a ' ) / ( ' z ' - ' a ' ) * 26 + 74 ) ;
2017-03-18 02:04:13 +00:00
}
else if ( Char > = ' A ' & & Char < = ' Z ' )
{
2017-04-19 01:10:45 +00:00
Colour . Hue = ( ( ( float ) Char - ' A ' ) / ( ' Z ' - ' A ' ) * 360 ) ;
Colour . Saturation = ( ( ( float ) Char - ' A ' ) / ( ' Z ' - ' A ' ) * 26 + 74 ) ;
2017-03-18 02:04:13 +00:00
}
else if ( Char > = ' 0 ' & & Char < = ' 9 ' )
{
2017-04-19 01:10:45 +00:00
Colour . Hue = ( ( ( float ) Char - ' 0 ' ) / ( ' 9 ' - ' 0 ' ) * 360 ) ;
Colour . Saturation = ( ( ( float ) Char - ' 0 ' ) / ( ' 9 ' - ' 0 ' ) * 26 + 74 ) ;
2017-03-18 02:04:13 +00:00
}
else
{
2017-04-13 23:46:21 +00:00
Colour . Hue = 180 ;
Colour . Saturation = 50 ;
2017-03-18 02:04:13 +00:00
}
2017-06-25 18:22:54 +00:00
2017-04-13 23:46:21 +00:00
return Colour ;
2017-03-18 02:04:13 +00:00
}
2017-06-09 22:04:07 +00:00
hsl_colour *
StringToColourHash ( hsl_colour * Colour , char * String )
2017-03-18 02:04:13 +00:00
{
2017-06-09 22:04:07 +00:00
Colour - > Hue = 0 ;
Colour - > Saturation = 0 ;
Colour - > Lightness = 26 ;
2017-06-25 18:22:54 +00:00
2017-03-18 02:04:13 +00:00
int i ;
for ( i = 0 ; String [ i ] ; + + i )
{
2017-06-09 22:04:07 +00:00
Colour - > Hue + = CharToColour ( String [ i ] ) . Hue ;
Colour - > Saturation + = CharToColour ( String [ i ] ) . Saturation ;
2017-03-18 02:04:13 +00:00
}
2017-06-25 18:22:54 +00:00
2017-06-09 22:04:07 +00:00
Colour - > Hue = Colour - > Hue % 360 ;
Colour - > Saturation = Colour - > Saturation % 26 + 74 ;
return ( Colour ) ;
2017-03-18 02:04:13 +00:00
}
2017-03-25 02:07:55 +00:00
char *
SanitisePunctuation ( char * String )
{
char * Ptr = String ;
while ( * Ptr )
{
if ( * Ptr = = ' ' )
{
* Ptr = ' _ ' ;
}
if ( ( * Ptr < ' 0 ' | | * Ptr > ' 9 ' ) & &
2017-06-25 18:05:58 +00:00
( * Ptr < ' a ' | | * Ptr > ' z ' ) & &
( * Ptr < ' A ' | | * Ptr > ' Z ' ) )
2017-03-25 02:07:55 +00:00
{
* Ptr = ' - ' ;
}
+ + Ptr ;
}
return String ;
}
2017-08-18 22:46:58 +00:00
enum
2017-05-25 20:28:52 +00:00
{
2017-08-19 21:41:51 +00:00
CreditsError_NoHost ,
CreditsError_NoAnnotator ,
CreditsError_NoCredentials
} ;
2017-06-25 18:22:54 +00:00
2017-08-18 22:46:58 +00:00
int
2017-08-29 20:35:28 +00:00
SearchCredentials ( config Config , buffer * CreditsMenu , bool * HasCreditsMenu , char * ImagesDir , char * Person , char * Role )
2017-08-18 22:46:58 +00:00
{
bool Found = FALSE ;
2017-05-25 20:28:52 +00:00
for ( int CredentialIndex = 0 ; CredentialIndex < ArrayCount ( Credentials ) ; + + CredentialIndex )
{
2017-08-18 22:46:58 +00:00
if ( ! StringsDiffer ( Person , Credentials [ CredentialIndex ] . Username ) )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
Found = TRUE ;
if ( * HasCreditsMenu = = FALSE )
{
CopyStringToBuffer ( CreditsMenu ,
" <div class= \" menu credits \" > \n "
" <div class= \" mouse_catcher \" ></div> \n "
" <span>Credits</span> \n "
" <div class= \" credits_container \" > \n " ) ;
* HasCreditsMenu = TRUE ;
}
CopyStringToBuffer ( CreditsMenu ,
" <span class= \" credit \" > \n " ) ;
if ( * Credentials [ CredentialIndex ] . HomepageURL )
{
CopyStringToBuffer ( CreditsMenu ,
" <a class= \" person \" href= \" %s \" target= \" _blank \" > \n "
" <div class= \" role \" >%s</div> \n "
" <div class= \" name \" >%s</div> \n "
" </a> \n " ,
Credentials [ CredentialIndex ] . HomepageURL ,
Role ,
Credentials [ CredentialIndex ] . CreditedName ) ;
2017-05-25 20:28:52 +00:00
}
else
{
2017-08-18 22:46:58 +00:00
CopyStringToBuffer ( CreditsMenu ,
" <div class= \" person \" > \n "
" <div class= \" role \" >%s</div> \n "
" <div class= \" name \" >%s</div> \n "
" </div> \n " ,
Role ,
Credentials [ CredentialIndex ] . CreditedName ) ;
2017-05-25 20:28:52 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-18 22:46:58 +00:00
if ( * Credentials [ CredentialIndex ] . SupportIcon & & * Credentials [ CredentialIndex ] . SupportURL )
2017-05-25 20:28:52 +00:00
{
2017-08-29 20:35:28 +00:00
if ( Config . Edition = = EDITION_PROJECT )
{
CopyStringToBuffer ( CreditsMenu ,
" <a class= \" support \" href= \" %s \" target= \" _blank \" ><img src= \" ../%s/%s \" ></a> \n " ,
Credentials [ CredentialIndex ] . SupportURL ,
ImagesDir ,
Credentials [ CredentialIndex ] . SupportIcon ) ;
}
else
{
CopyStringToBuffer ( CreditsMenu ,
" <a class= \" support \" href= \" %s \" target= \" _blank \" ><img src= \" %s/%s \" ></a> \n " ,
Credentials [ CredentialIndex ] . SupportURL ,
ImagesDir ,
Credentials [ CredentialIndex ] . SupportIcon ) ;
}
2017-05-25 20:28:52 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-18 22:46:58 +00:00
CopyStringToBuffer ( CreditsMenu ,
" </span> \n " ) ;
2017-05-25 20:28:52 +00:00
}
2017-08-18 22:46:58 +00:00
}
return Found ? 0 : CreditsError_NoCredentials ;
}
2017-06-25 18:22:54 +00:00
2017-08-18 22:46:58 +00:00
int
2017-08-29 20:35:28 +00:00
BuildCredits ( config Config , buffer * CreditsMenu , bool * HasCreditsMenu , char * ImagesDir , HMML_VideoMetaData Metadata )
2017-08-18 22:46:58 +00:00
// TODO(matt): Make this take the Credentials, once we are parsing them from a config
{
if ( Metadata . member )
{
2017-08-29 20:35:28 +00:00
if ( SearchCredentials ( Config , CreditsMenu , HasCreditsMenu , ImagesDir , Metadata . member , " Host " ) )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
printf ( " No credentials for %s. Please contact matt@handmadedev.org with their: \n "
" Full name \n "
" Homepage URL (optional) \n "
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata . member ) ;
}
}
else
{
if ( * HasCreditsMenu = = TRUE )
{
CopyStringToBuffer ( CreditsMenu ,
" </div> \n "
" </div> \n " ) ;
}
return CreditsError_NoHost ;
}
if ( Metadata . co_host_count > 0 )
{
for ( int i = 0 ; i < Metadata . co_host_count ; + + i )
{
2017-08-29 20:35:28 +00:00
if ( SearchCredentials ( Config , CreditsMenu , HasCreditsMenu , ImagesDir , Metadata . co_hosts [ i ] , " Co-host " ) )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
printf ( " No credentials for %s. Please contact matt@handmadedev.org with their: \n "
" Full name \n "
" Homepage URL (optional) \n "
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata . co_hosts [ i ] ) ;
2017-05-25 20:28:52 +00:00
}
2017-08-18 22:46:58 +00:00
}
}
2017-06-25 18:22:54 +00:00
2017-08-18 22:46:58 +00:00
if ( Metadata . guest_count > 0 )
{
for ( int i = 0 ; i < Metadata . guest_count ; + + i )
{
2017-08-29 20:35:28 +00:00
if ( SearchCredentials ( Config , CreditsMenu , HasCreditsMenu , ImagesDir , Metadata . guests [ i ] , " Guest " ) )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
printf ( " No credentials for %s. Please contact matt@handmadedev.org with their: \n "
" Full name \n "
" Homepage URL (optional) \n "
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata . guests [ i ] ) ;
2017-05-25 20:28:52 +00:00
}
}
}
2017-06-25 18:22:54 +00:00
2017-08-18 22:46:58 +00:00
if ( Metadata . annotator_count > 0 )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
for ( int i = 0 ; i < Metadata . annotator_count ; + + i )
2017-05-25 20:28:52 +00:00
{
2017-08-29 20:35:28 +00:00
if ( SearchCredentials ( Config , CreditsMenu , HasCreditsMenu , ImagesDir , Metadata . annotators [ i ] , " Annotator " ) )
2017-08-18 22:46:58 +00:00
{
printf ( " No credentials for %s. Please contact matt@handmadedev.org with their: \n "
" Full name \n "
" Homepage URL (optional) \n "
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata . annotators [ i ] ) ;
}
2017-05-25 20:28:52 +00:00
}
2017-08-18 22:46:58 +00:00
}
else
{
if ( * HasCreditsMenu = = TRUE )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
CopyStringToBuffer ( CreditsMenu ,
" </div> \n "
" </div> \n " ) ;
2017-05-25 20:28:52 +00:00
}
2017-08-18 22:46:58 +00:00
return CreditsError_NoAnnotator ;
2017-05-25 20:28:52 +00:00
}
2017-08-18 22:46:58 +00:00
if ( * HasCreditsMenu = = TRUE )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
CopyStringToBuffer ( CreditsMenu ,
" </div> \n "
" </div> \n " ) ;
2017-05-25 20:28:52 +00:00
}
2017-06-25 18:22:54 +00:00
2017-05-25 20:28:52 +00:00
return 0 ;
}
2017-04-21 01:25:40 +00:00
int
2017-04-19 01:10:45 +00:00
BuildReference ( ref_info * ReferencesArray , int RefIdentifier , int UniqueRefs , HMML_Reference Ref , HMML_Annotation Anno )
{
2017-07-29 23:01:39 +00:00
# define REF_SITE (1 << 0)
# define REF_PAGE (1 << 1)
# define REF_URL (1 << 2)
# define REF_TITLE (1 << 3)
# define REF_ARTICLE (1 << 4)
# define REF_AUTHOR (1 << 5)
# define REF_EDITOR (1 << 6)
# define REF_PUBLISHER (1 << 7)
# define REF_ISBN (1 << 8)
int Mask = 0 ;
if ( Ref . site ) { Mask | = REF_SITE ; }
if ( Ref . page ) { Mask | = REF_PAGE ; }
if ( Ref . url ) { Mask | = REF_URL ; }
if ( Ref . title ) { Mask | = REF_TITLE ; }
if ( Ref . article ) { Mask | = REF_ARTICLE ; }
if ( Ref . author ) { Mask | = REF_AUTHOR ; }
if ( Ref . editor ) { Mask | = REF_EDITOR ; }
if ( Ref . publisher ) { Mask | = REF_PUBLISHER ; }
if ( Ref . isbn ) { Mask | = REF_ISBN ; }
if ( ( REF_URL | REF_TITLE | REF_AUTHOR | REF_PUBLISHER | REF_ISBN ) = = Mask )
2017-04-21 01:25:40 +00:00
{
2017-04-23 00:30:37 +00:00
CopyString ( ReferencesArray [ UniqueRefs ] . ID , Ref . isbn ) ;
CopyString ( ReferencesArray [ UniqueRefs ] . Source , " %s (%s) " , Ref . author , Ref . publisher ) ;
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref . title ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref . url ) ;
2017-04-21 01:25:40 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_AUTHOR | REF_SITE | REF_PAGE | REF_URL ) = = Mask )
{
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref . url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , Ref . site ) ;
CopyString ( ReferencesArray [ UniqueRefs ] . RefTitle , " %s: \" %s \" " , Ref . author , Ref . page ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref . url ) ;
}
else if ( ( REF_PAGE | REF_URL | REF_TITLE ) = = Mask )
2017-04-21 01:25:40 +00:00
{
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref . url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , Ref . title ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref . page ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref . url ) ;
2017-04-21 01:25:40 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_SITE | REF_PAGE | REF_URL ) = = Mask )
2017-04-21 01:25:40 +00:00
{
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref . url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , Ref . site ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref . page ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref . url ) ;
2017-04-21 01:25:40 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_SITE | REF_URL | REF_TITLE ) = = Mask )
2017-04-21 01:25:40 +00:00
{
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref . url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , Ref . site ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref . title ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref . url ) ;
2017-04-21 01:25:40 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_TITLE | REF_AUTHOR | REF_ISBN ) = = Mask )
2017-04-19 01:10:45 +00:00
{
CopyString ( ReferencesArray [ UniqueRefs ] . ID , Ref . isbn ) ;
CopyString ( ReferencesArray [ UniqueRefs ] . Source , Ref . author ) ;
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref . title ) ;
2017-06-03 01:32:18 +00:00
CopyString ( ReferencesArray [ UniqueRefs ] . URL , " http://www.openisbn.com/isbn/%s " , Ref . isbn ) ;
2017-04-19 01:10:45 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_URL | REF_ARTICLE | REF_AUTHOR ) = = Mask )
2017-04-19 01:10:45 +00:00
{
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref . url ) ;
2017-04-21 01:25:40 +00:00
CopyString ( ReferencesArray [ UniqueRefs ] . Source , Ref . author ) ;
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref . article ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref . url ) ;
2017-04-19 01:10:45 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_URL | REF_TITLE | REF_AUTHOR ) = = Mask )
2017-04-21 01:25:40 +00:00
{
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref . url ) ;
2017-04-21 01:25:40 +00:00
CopyString ( ReferencesArray [ UniqueRefs ] . Source , Ref . author ) ;
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref . title ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref . url ) ;
2017-04-21 01:25:40 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_URL | REF_TITLE ) = = Mask )
2017-04-23 00:30:37 +00:00
{
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref . url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref . title ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref . url ) ;
2017-04-23 00:30:37 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_SITE | REF_URL ) = = Mask )
2017-04-23 00:30:37 +00:00
{
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref . url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref . site ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref . url ) ;
2017-04-23 00:30:37 +00:00
}
2017-04-21 01:25:40 +00:00
else
{
return 1 ;
}
2017-06-25 18:22:54 +00:00
2017-04-19 01:10:45 +00:00
CopyString ( ReferencesArray [ UniqueRefs ] . Identifier [ ReferencesArray [ UniqueRefs ] . IdentifierCount ] . Timecode , Anno . time ) ;
ReferencesArray [ UniqueRefs ] . Identifier [ ReferencesArray [ UniqueRefs ] . IdentifierCount ] . Identifier = RefIdentifier ;
2017-04-21 01:25:40 +00:00
return 0 ;
2017-04-19 01:10:45 +00:00
}
2017-05-21 06:35:16 +00:00
void
2017-08-19 22:35:07 +00:00
InsertCategory ( categories * Topics , categories * Media , char * Marker )
2017-05-21 06:35:16 +00:00
{
2017-05-24 21:56:36 +00:00
bool IsMedium = FALSE ;
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
int CategoryMediumIndex ;
for ( CategoryMediumIndex = 0 ; CategoryMediumIndex < ArrayCount ( CategoryMedium ) ; + + CategoryMediumIndex )
2017-05-21 06:35:16 +00:00
{
2017-08-19 21:41:51 +00:00
if ( ! StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . Medium , Marker ) )
2017-05-21 06:35:16 +00:00
{
2017-05-24 21:56:36 +00:00
IsMedium = TRUE ;
break ;
2017-05-21 06:35:16 +00:00
}
2017-05-24 21:56:36 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
int CategoryIndex ;
2017-05-24 21:56:36 +00:00
if ( IsMedium )
{
2017-08-19 21:41:51 +00:00
for ( CategoryIndex = 0 ; CategoryIndex < Media - > Count ; + + CategoryIndex )
2017-05-22 21:37:00 +00:00
{
2017-08-19 21:41:51 +00:00
if ( ! StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . Medium , Media - > Category [ CategoryIndex ] . Marker ) )
2017-05-22 21:37:00 +00:00
{
2017-05-24 21:56:36 +00:00
return ;
2017-05-22 21:37:00 +00:00
}
2017-08-19 21:41:51 +00:00
if ( ( StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . WrittenName , Media - > Category [ CategoryIndex ] . WrittenText ) ) < 0 )
2017-05-24 21:56:36 +00:00
{
2017-08-19 21:41:51 +00:00
int CategoryCount ;
for ( CategoryCount = Media - > Count ; CategoryCount > CategoryIndex ; - - CategoryCount )
2017-05-24 21:56:36 +00:00
{
2017-08-19 21:41:51 +00:00
CopyString ( Media - > Category [ CategoryCount ] . Marker , Media - > Category [ CategoryCount - 1 ] . Marker ) ;
CopyString ( Media - > Category [ CategoryCount ] . WrittenText , Media - > Category [ CategoryCount - 1 ] . WrittenText ) ;
2017-05-24 21:56:36 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
CopyString ( Media - > Category [ CategoryCount ] . Marker , CategoryMedium [ CategoryMediumIndex ] . Medium ) ;
CopyString ( Media - > Category [ CategoryCount ] . WrittenText , CategoryMedium [ CategoryMediumIndex ] . WrittenName ) ;
2017-05-24 21:56:36 +00:00
break ;
}
2017-05-22 21:37:00 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
if ( CategoryIndex = = Media - > Count )
2017-05-24 21:56:36 +00:00
{
2017-08-19 21:41:51 +00:00
CopyString ( Media - > Category [ CategoryIndex ] . Marker , CategoryMedium [ CategoryMediumIndex ] . Medium ) ;
CopyString ( Media - > Category [ CategoryIndex ] . WrittenText , CategoryMedium [ CategoryMediumIndex ] . WrittenName ) ;
2017-05-24 21:56:36 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
+ + Media - > Count ;
2017-05-21 06:35:16 +00:00
}
2017-05-24 21:56:36 +00:00
else
{
2017-08-19 21:41:51 +00:00
for ( CategoryIndex = 0 ; CategoryIndex < Topics - > Count ; + + CategoryIndex )
2017-05-24 21:56:36 +00:00
{
2017-08-19 21:41:51 +00:00
if ( ! StringsDiffer ( Marker , Topics - > Category [ CategoryIndex ] . Marker ) )
2017-05-24 21:56:36 +00:00
{
return ;
}
2017-08-19 21:41:51 +00:00
if ( ( StringsDiffer ( Marker , Topics - > Category [ CategoryIndex ] . Marker ) ) < 0 )
2017-05-24 21:56:36 +00:00
{
2017-08-19 21:41:51 +00:00
int CategoryCount ;
for ( CategoryCount = Topics - > Count ; CategoryCount > CategoryIndex ; - - CategoryCount )
2017-05-24 21:56:36 +00:00
{
2017-08-19 21:41:51 +00:00
CopyString ( Topics - > Category [ CategoryCount ] . Marker , Topics - > Category [ CategoryCount - 1 ] . Marker ) ;
2017-05-24 21:56:36 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
CopyString ( Topics - > Category [ CategoryCount ] . Marker , Marker ) ;
2017-05-24 21:56:36 +00:00
break ;
}
}
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
if ( CategoryIndex = = Topics - > Count )
2017-05-21 06:35:16 +00:00
{
2017-08-19 21:41:51 +00:00
CopyString ( Topics - > Category [ CategoryIndex ] . Marker , Marker ) ;
2017-05-21 06:35:16 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
+ + Topics - > Count ;
2017-05-21 06:35:16 +00:00
}
2017-08-19 21:41:51 +00:00
return ;
2017-05-21 06:35:16 +00:00
}
2017-03-25 03:10:15 +00:00
void
2017-08-19 21:41:51 +00:00
BuildCategories ( buffer * AnnotationClass , buffer * TopicDots , categories LocalTopics , categories LocalMedia , int * MarkerIndex )
2017-03-25 03:10:15 +00:00
{
2017-08-19 21:41:51 +00:00
if ( LocalTopics . Count > 0 )
2017-03-25 03:10:15 +00:00
{
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( TopicDots , " <span class= \" categories \" > " ) ;
for ( int i = 0 ; i < LocalTopics . Count ; + + i )
2017-03-25 03:10:15 +00:00
{
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( TopicDots , " <div title= \" %s \" class= \" category %s \" ></div> " ,
SanitisePunctuation ( LocalTopics . Category [ i ] . Marker ) ,
SanitisePunctuation ( LocalTopics . Category [ i ] . Marker ) ) ;
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( AnnotationClass , " cat_%s " ,
SanitisePunctuation ( LocalTopics . Category [ i ] . Marker ) ) ;
2017-06-11 22:49:04 +00:00
}
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( TopicDots , " </span> " ) ;
2017-06-11 22:49:04 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
for ( int i = 0 ; i < LocalMedia . Count ; + + i )
2017-06-11 22:49:04 +00:00
{
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( AnnotationClass , " %s " , SanitisePunctuation ( LocalMedia . Category [ i ] . Marker ) ) ;
2017-06-11 22:49:04 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( AnnotationClass , " \" " ) ;
2017-03-25 03:10:15 +00:00
}
2017-04-23 02:47:42 +00:00
int
StringToInt ( char * String )
{
int Result = 0 ;
while ( * String )
{
Result = Result * 10 + ( * String - ' 0 ' ) ;
+ + String ;
}
return Result ;
}
2017-08-18 22:46:58 +00:00
size_t
CurlIntoBuffer ( char * InPtr , size_t CharLength , size_t Chars , char * * OutputPtr )
{
int Length = CharLength * Chars ;
int i ;
for ( i = 0 ; InPtr [ i ] & & i < Length ; + + i )
{
* ( ( * OutputPtr ) + + ) = InPtr [ i ] ;
}
* * OutputPtr = ' \0 ' ;
return Length ;
} ;
2017-08-11 22:41:52 +00:00
void
CurlQuotes ( buffer * QuoteStaging , char * QuotesURL )
{
CURL * curl = curl_easy_init ( ) ;
if ( curl ) {
CURLcode res ;
curl_easy_setopt ( curl , CURLOPT_WRITEDATA , & QuoteStaging - > Ptr ) ;
curl_easy_setopt ( curl , CURLOPT_WRITEFUNCTION , CurlIntoBuffer ) ;
curl_easy_setopt ( curl , CURLOPT_URL , QuotesURL ) ;
if ( ( res = curl_easy_perform ( curl ) ) )
{
fprintf ( stderr , " %s " , curl_easy_strerror ( res ) ) ;
}
curl_easy_cleanup ( curl ) ;
}
}
2017-08-18 22:46:58 +00:00
int
SearchQuotes ( buffer QuoteStaging , int CacheSize , quote_info * Info , int ID )
{
QuoteStaging . Ptr = QuoteStaging . Location ;
while ( QuoteStaging . Ptr - QuoteStaging . Location < CacheSize )
{
char InID [ 4 ] = { 0 } ;
char InTime [ 16 ] = { 0 } ;
char * OutPtr = InID ;
QuoteStaging . Ptr + = CopyStringNoFormatT ( OutPtr , QuoteStaging . Ptr , ' , ' ) ;
if ( StringToInt ( InID ) = = ID )
{
QuoteStaging . Ptr + = 1 ;
OutPtr = InTime ;
QuoteStaging . Ptr + = CopyStringNoFormatT ( OutPtr , QuoteStaging . Ptr , ' , ' ) ;
long int Time = StringToInt ( InTime ) ;
char DayString [ 3 ] ;
strftime ( DayString , 3 , " %d " , gmtime ( & Time ) ) ;
int Day = StringToInt ( DayString ) ;
char DaySuffix [ 3 ] ; if ( DayString [ 1 ] = = ' 1 ' & & Day ! = 11 ) { CopyString ( DaySuffix , " st " ) ; }
else if ( DayString [ 1 ] = = ' 2 ' & & Day ! = 12 ) { CopyString ( DaySuffix , " nd " ) ; }
else if ( DayString [ 1 ] = = ' 3 ' & & Day ! = 13 ) { CopyString ( DaySuffix , " rd " ) ; }
else { CopyString ( DaySuffix , " th " ) ; }
char MonthYear [ 32 ] ;
strftime ( MonthYear , 32 , " %B, %Y " , gmtime ( & Time ) ) ;
CopyString ( Info - > Date , " %d%s %s " , Day , DaySuffix , MonthYear ) ;
QuoteStaging . Ptr + = 1 ;
OutPtr = Info - > Text ;
QuoteStaging . Ptr + = CopyStringNoFormatT ( OutPtr , QuoteStaging . Ptr , ' \n ' ) ;
FreeBuffer ( & QuoteStaging ) ;
2017-09-07 21:41:08 +00:00
return RC_FOUND ;
2017-08-18 22:46:58 +00:00
}
else
{
while ( * QuoteStaging . Ptr ! = ' \n ' )
{
+ + QuoteStaging . Ptr ;
}
+ + QuoteStaging . Ptr ;
}
}
2017-09-07 21:41:08 +00:00
return RC_UNFOUND ;
2017-08-18 22:46:58 +00:00
}
2017-08-10 01:05:41 +00:00
int
BuildQuote ( quote_info * Info , char * Speaker , int ID , char * CacheDir )
{
// TODO(matt): Rebuild cache option
char QuoteCacheDir [ 255 ] ;
CopyString ( QuoteCacheDir , " %s/quotes " , CacheDir ) ;
char QuoteCachePath [ 255 ] ;
CopyString ( QuoteCachePath , " %s/%s " , QuoteCacheDir , Speaker ) ;
FILE * QuoteCache ;
2017-08-11 22:41:52 +00:00
char QuotesURL [ 256 ] ;
2017-08-29 20:35:28 +00:00
// TODO(matt): Make the URL configurable
2017-08-11 22:41:52 +00:00
CopyString ( QuotesURL , " https://dev.abaines.me.uk/quotes/%s.raw " , Speaker ) ;
bool CacheAvailable = FALSE ;
2017-08-10 01:05:41 +00:00
2017-08-11 22:41:52 +00:00
if ( ! ( QuoteCache = fopen ( QuoteCachePath , " a+ " ) ) )
2017-08-10 01:05:41 +00:00
{
2017-09-07 21:41:08 +00:00
if ( MakeDir ( QuoteCacheDir ) = = RC_SUCCESS )
2017-08-10 01:05:41 +00:00
{
2017-08-11 22:41:52 +00:00
CacheAvailable = TRUE ;
} ;
if ( ! ( QuoteCache = fopen ( QuoteCachePath , " a+ " ) ) )
{
2017-09-07 21:41:08 +00:00
fprintf ( stderr , " Unable to open quote cache %s: %s \n " , QuoteCachePath , strerror ( errno ) ) ;
2017-08-11 22:41:52 +00:00
}
else
{
CacheAvailable = TRUE ;
2017-08-10 01:05:41 +00:00
}
}
2017-08-11 22:41:52 +00:00
else
{
CacheAvailable = TRUE ;
}
2017-08-10 01:05:41 +00:00
buffer QuoteStaging ;
QuoteStaging . ID = " QuoteStaging " ;
QuoteStaging . Size = Kilobytes ( 256 ) ;
if ( ! ( QuoteStaging . Location = malloc ( QuoteStaging . Size ) ) )
{
2017-09-07 21:41:08 +00:00
fclose ( QuoteCache ) ;
return RC_ERROR_MEMORY ;
2017-08-10 01:05:41 +00:00
}
QuoteStaging . Ptr = QuoteStaging . Location ;
2017-08-11 22:41:52 +00:00
if ( CacheAvailable )
2017-08-10 01:05:41 +00:00
{
2017-08-11 22:41:52 +00:00
fseek ( QuoteCache , 0 , SEEK_END ) ;
int FileSize = ftell ( QuoteCache ) ;
fseek ( QuoteCache , 0 , SEEK_SET ) ;
fread ( QuoteStaging . Location , FileSize , 1 , QuoteCache ) ;
fclose ( QuoteCache ) ;
2017-08-10 01:05:41 +00:00
2017-09-07 21:41:08 +00:00
if ( SearchQuotes ( QuoteStaging , FileSize , Info , ID ) = = RC_UNFOUND )
2017-08-11 22:41:52 +00:00
{
CurlQuotes ( & QuoteStaging , QuotesURL ) ;
if ( ! ( QuoteCache = fopen ( QuoteCachePath , " w " ) ) )
2017-08-10 01:05:41 +00:00
{
2017-08-11 22:41:52 +00:00
perror ( QuoteCachePath ) ;
2017-08-10 01:05:41 +00:00
}
2017-08-11 22:41:52 +00:00
fwrite ( QuoteStaging . Location , QuoteStaging . Ptr - QuoteStaging . Location , 1 , QuoteCache ) ;
fclose ( QuoteCache ) ;
2017-08-10 01:05:41 +00:00
2017-08-11 22:41:52 +00:00
int CacheSize = QuoteStaging . Ptr - QuoteStaging . Location ;
QuoteStaging . Ptr = QuoteStaging . Location ;
if ( SearchQuotes ( QuoteStaging , CacheSize , Info , ID ) = = 1 )
{
FreeBuffer ( & QuoteStaging ) ;
return 1 ;
}
2017-08-10 01:05:41 +00:00
}
2017-08-11 22:41:52 +00:00
}
else
{
CurlQuotes ( & QuoteStaging , QuotesURL ) ;
2017-08-10 01:05:41 +00:00
int CacheSize = QuoteStaging . Ptr - QuoteStaging . Location ;
QuoteStaging . Ptr = QuoteStaging . Location ;
if ( SearchQuotes ( QuoteStaging , CacheSize , Info , ID ) = = 1 )
{
FreeBuffer ( & QuoteStaging ) ;
return 1 ;
}
}
2017-08-11 22:41:52 +00:00
2017-08-10 01:05:41 +00:00
return 0 ;
}
2017-09-07 21:41:08 +00:00
int
2017-08-20 21:23:15 +00:00
GenerateTopicColours ( char * Topic , char * TopicsDir )
2017-03-31 00:56:50 +00:00
{
for ( int i = 0 ; i < ArrayCount ( CategoryMedium ) ; + + i )
{
2017-08-18 22:46:58 +00:00
if ( ! StringsDiffer ( Topic , CategoryMedium [ i ] . Medium ) )
2017-03-31 00:56:50 +00:00
{
2017-09-07 21:41:08 +00:00
return RC_NOOP ;
2017-03-31 00:56:50 +00:00
}
}
2017-06-25 18:22:54 +00:00
2017-03-31 00:56:50 +00:00
FILE * TopicsFile ;
char * TopicsBuffer ;
2017-05-21 06:35:16 +00:00
// TODO(matt): Consider (optionally) pulling this path from the config
2017-06-25 18:05:58 +00:00
char TopicsPath [ 255 ] ;
2017-09-07 22:32:57 +00:00
CopyString ( TopicsPath , " %s/cinera_topics.css " , TopicsDir ) ;
if ( ( TopicsFile = fopen ( TopicsPath , " a+ " ) ) )
2017-03-31 00:56:50 +00:00
{
fseek ( TopicsFile , 0 , SEEK_END ) ;
int TopicsLength = ftell ( TopicsFile ) ;
fseek ( TopicsFile , 0 , SEEK_SET ) ;
2017-06-25 18:22:54 +00:00
2017-04-13 00:21:04 +00:00
if ( ! ( TopicsBuffer = malloc ( TopicsLength ) ) )
2017-03-31 00:56:50 +00:00
{
2017-09-07 21:41:08 +00:00
return RC_ERROR_MEMORY ;
2017-03-31 00:56:50 +00:00
}
2017-06-25 18:22:54 +00:00
2017-03-31 00:56:50 +00:00
fread ( TopicsBuffer , TopicsLength , 1 , TopicsFile ) ;
2017-06-25 18:22:54 +00:00
2017-04-13 00:21:04 +00:00
char * TopicsPtr = TopicsBuffer ;
2017-06-25 18:22:54 +00:00
2017-03-31 00:56:50 +00:00
while ( TopicsPtr - TopicsBuffer < TopicsLength )
{
2017-05-15 01:11:11 +00:00
TopicsPtr + = StringLength ( " .category. " ) ;
2017-06-13 22:13:03 +00:00
if ( ! StringsDifferT ( SanitisePunctuation ( Topic ) , TopicsPtr , ' ' ) )
2017-03-31 00:56:50 +00:00
{
free ( TopicsBuffer ) ;
2017-04-21 01:25:40 +00:00
fclose ( TopicsFile ) ;
2017-09-07 21:41:08 +00:00
return RC_NOOP ;
2017-03-31 00:56:50 +00:00
}
while ( TopicsPtr - TopicsBuffer < TopicsLength & & * TopicsPtr ! = ' \n ' )
{
+ + TopicsPtr ;
}
+ + TopicsPtr ;
}
2017-06-25 18:22:54 +00:00
2017-06-09 22:04:07 +00:00
hsl_colour Colour ;
StringToColourHash ( & Colour , Topic ) ;
fprintf ( TopicsFile , " .category.%s { border: 1px solid hsl(%d, %d%%, %d%%); background: hsl(%d, %d%%, %d%%); } \n " ,
2017-06-25 18:05:58 +00:00
SanitisePunctuation ( Topic ) , Colour . Hue , Colour . Saturation , Colour . Lightness , Colour . Hue , Colour . Saturation , Colour . Lightness ) ;
2017-06-25 18:22:54 +00:00
2017-04-13 00:21:04 +00:00
fclose ( TopicsFile ) ;
free ( TopicsBuffer ) ;
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
2017-03-31 00:56:50 +00:00
}
else
{
2017-09-07 21:41:08 +00:00
return RC_ERROR_FILE ;
2017-03-31 00:56:50 +00:00
}
}
2017-08-29 20:35:28 +00:00
void
PrintUsage ( char * BinaryLocation , config DefaultConfig )
{
fprintf ( stderr , " Usage: %s [option(s)] filename(s) \n "
" \n "
" Options: \n "
" -b <base output directory> \n "
" Override default base output directory ( \" %s \" ) \n "
" -c <CSS directory path> \n "
" Override default CSS directory ( \" %s \" ) \n "
" -f \n "
" Force integration with an incomplete template \n "
" -i <images directory path> \n "
" Override default images directory ( \" %s \" ) \n "
" -j <JS directory path> \n "
" Override default JS directory ( \" %s \" ) \n "
2017-09-07 21:41:08 +00:00
" -l <n> \n "
" Override default log level (%d), where n is from 0 (terse) to 7 (verbose) \n "
2017-08-29 20:35:28 +00:00
" -m <default medium> \n "
" Override default default medium ( \" %s \" ) \n "
" -o <output location> \n "
" Override default output location ( \" %s \" ) \n "
" -p <project directory> \n "
" Override default project directory ( \" %s \" ) \n "
2017-09-07 21:41:08 +00:00
" -t <player template location> \n "
" Override default player template location ( \" %s \" ) \n "
" and automatically enable integration \n "
" -x <index template location> \n "
" Override default index template location ( \" %s \" ) \n "
2017-08-29 20:35:28 +00:00
" and automatically enable integration \n "
//" -c config location\n"
" -h \n "
" display this help \n "
" \n "
" Environment Variables: \n "
" CINERA_MODE \n "
" =INTEGRATE \n "
" Enable integration \n "
" \n "
" Template: \n "
" A complete template shall contain exactly one each of the following tags: \n "
" <!-- __CINERA_INCLUDES__ --> \n "
" <!-- __CINERA_MENUS__ --> \n "
" <!-- __CINERA_PLAYER__ --> \n "
" <!-- __CINERA_SCRIPT__ --> (must come after <!-- __CINERA_PLAYER__ -->) \n "
" Other available tags include: \n "
" <!-- __CINERA_TITLE__ --> \n "
" \n "
" HMML Specification: \n "
" https://git.handmade.network/Annotation-Pushers/Annotation-System/wikis/hmmlspec \n " ,
2017-09-07 21:41:08 +00:00
BinaryLocation , DefaultConfig . BaseDir , DefaultConfig . CSSDir , DefaultConfig . ImagesDir , DefaultConfig . JSDir , DefaultConfig . LogLevel , DefaultConfig . DefaultMedium , DefaultConfig . OutLocation , DefaultConfig . ProjectDir , DefaultConfig . TemplatePlayerLocation , DefaultConfig . TemplateIndexLocation ) ;
2017-08-29 20:35:28 +00:00
}
2017-09-07 21:41:08 +00:00
void
DepartComment ( buffer * Template )
2017-05-13 15:38:10 +00:00
{
2017-09-07 21:41:08 +00:00
while ( Template - > Ptr - Template - > Location < Template - > Size )
{
if ( ! StringsDifferT ( " --> " , Template - > Ptr , 0 ) )
{
Template - > Ptr + = StringLength ( " --> " ) ;
break ;
}
+ + Template - > Ptr ;
}
}
2017-05-13 15:38:10 +00:00
2017-09-07 21:41:08 +00:00
enum
2017-05-13 15:38:10 +00:00
{
2017-09-07 21:41:08 +00:00
PAGE_PLAYER = 1 < < 0 ,
PAGE_INDEX = 1 < < 1
} pages ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
int
2017-09-07 21:41:08 +00:00
ValidateTemplate ( buffer * Errors , template * TemplateMetadata , config Config , int PageType )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
Errors - > Ptr = Errors - > Location ;
2017-08-29 20:35:28 +00:00
bool HaveErrors = FALSE ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
FILE * TemplateFile ;
2017-09-07 21:41:08 +00:00
if ( ! ( TemplateFile = fopen ( TemplateMetadata - > Filename , " r " ) ) )
2017-05-13 15:38:10 +00:00
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " Unable to open template %s: %s " , TemplateMetadata - > Filename , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to open template %s: %s \n " , TemplateMetadata - > Filename , strerror ( errno ) ) ;
return RC_ERROR_FILE ;
2017-05-13 15:38:10 +00:00
}
2017-06-25 18:22:54 +00:00
2017-09-07 21:41:08 +00:00
buffer Template ;
Template . ID = TemplateMetadata - > Filename ;
2017-08-29 20:35:28 +00:00
fseek ( TemplateFile , 0 , SEEK_END ) ;
2017-09-07 21:41:08 +00:00
Template . Size = ftell ( TemplateFile ) ;
2017-08-29 20:35:28 +00:00
fseek ( TemplateFile , 0 , SEEK_SET ) ;
2017-09-07 21:41:08 +00:00
if ( ! ( Template . Location = malloc ( Template . Size ) ) )
2017-05-13 15:38:10 +00:00
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " ValidateTemplate(): %s " ,
strerror ( errno ) ) ;
return RC_ERROR_MEMORY ;
2017-05-13 15:38:10 +00:00
}
2017-09-07 21:41:08 +00:00
Template . Ptr = Template . Location ;
fread ( Template . Location , Template . Size , 1 , TemplateFile ) ;
2017-08-29 20:35:28 +00:00
fclose ( TemplateFile ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
bool FoundIncludes = FALSE ;
bool FoundMenus = FALSE ;
bool FoundPlayer = FALSE ;
bool FoundScript = FALSE ;
2017-09-07 21:41:08 +00:00
bool FoundIndex = FALSE ;
2017-06-25 18:22:54 +00:00
2017-09-07 21:41:08 +00:00
char * Previous = Template . Location ;
while ( Template . Ptr - Template . Location < Template . Size )
2017-05-13 15:38:10 +00:00
{
2017-09-07 21:41:08 +00:00
Here :
if ( * Template . Ptr = = ' ! ' & & ( Template . Ptr > Template . Location & & ! StringsDifferT ( " <!-- " , & Template . Ptr [ - 1 ] , 0 ) ) )
2017-05-13 15:38:10 +00:00
{
2017-09-07 21:41:08 +00:00
char * CommentStart = & Template . Ptr [ - 1 ] ;
while ( Template . Ptr - Template . Location < Template . Size & & StringsDifferT ( " --> " , Template . Ptr , 0 ) )
2017-06-25 18:05:58 +00:00
{
2017-09-07 21:41:08 +00:00
for ( int i = 0 ; i < ArrayCount ( Tags ) ; + + i )
2017-05-13 15:38:10 +00:00
{
2017-09-07 21:41:08 +00:00
if ( ! ( StringsDifferT ( Tags [ i ] . Tag , Template . Ptr , 0 ) ) )
2017-05-13 15:38:10 +00:00
{
2017-09-07 21:41:08 +00:00
// TODO(matt): Pack up this data for BuffersToHTML() to use
/*
* Potential ways to compress these cases
*
* bool Found [ TAG_COUNT ]
* - Asaf
*
* int * flags [ ] = { [ TAG_INCLUDES ] = & FoundIncludes , [ TAG_MENUS ] = & FoundMenus } flags [ Tags [ i ] . Code ] = true ;
* - insofaras
*
*/
//printf("Switching on the tags\n");
switch ( Tags [ i ] . Code )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
case TAG_INDEX :
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . Offset = CommentStart - Previous ;
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . TagCode = TAG_INDEX ;
TemplateMetadata - > TagCount + + ;
DepartComment ( & Template ) ;
Previous = Template . Ptr ;
FoundIndex = TRUE ;
goto Here ;
case TAG_INCLUDES :
if ( ! Config . ForceIntegration & & FoundIncludes = = TRUE )
{
CopyStringToBuffer ( Errors , " Template contains more than one <!-- %s --> tag \n " , Tags [ i ] . Tag ) ;
HaveErrors = TRUE ;
}
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . Offset = CommentStart - Previous ;
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . TagCode = TAG_INCLUDES ;
TemplateMetadata - > TagCount + + ;
DepartComment ( & Template ) ;
Previous = Template . Ptr ;
FoundIncludes = TRUE ;
goto Here ;
case TAG_MENUS :
if ( ! Config . ForceIntegration & & FoundMenus = = TRUE )
{
CopyStringToBuffer ( Errors , " Template contains more than one <!-- %s --> tag \n " , Tags [ i ] . Tag ) ;
HaveErrors = TRUE ;
}
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . Offset = CommentStart - Previous ;
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . TagCode = TAG_MENUS ;
TemplateMetadata - > TagCount + + ;
DepartComment ( & Template ) ;
Previous = Template . Ptr ;
FoundMenus = TRUE ;
goto Here ;
case TAG_PLAYER :
if ( ! Config . ForceIntegration & & FoundPlayer = = TRUE )
{
CopyStringToBuffer ( Errors , " Template contains more than one <!-- %s --> tag \n " , Tags [ i ] . Tag ) ;
HaveErrors = TRUE ;
}
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . Offset = CommentStart - Previous ;
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . TagCode = TAG_PLAYER ;
TemplateMetadata - > TagCount + + ;
DepartComment ( & Template ) ;
Previous = Template . Ptr ;
FoundPlayer = TRUE ;
goto Here ;
case TAG_SCRIPT :
if ( ! Config . ForceIntegration & & FoundPlayer = = FALSE )
{
CopyStringToBuffer ( Errors , " <!-- %s --> must come after <!-- __CINERA_PLAYER__ --> \n " , Tags [ i ] . Tag ) ;
HaveErrors = TRUE ;
}
if ( ! Config . ForceIntegration & & FoundScript = = TRUE )
{
CopyStringToBuffer ( Errors , " Template contains more than one <!-- %s --> tag \n " , Tags [ i ] . Tag ) ;
HaveErrors = TRUE ;
}
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . Offset = CommentStart - Previous ;
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . TagCode = TAG_SCRIPT ;
TemplateMetadata - > TagCount + + ;
DepartComment ( & Template ) ;
Previous = Template . Ptr ;
FoundScript = TRUE ;
goto Here ;
case TAG_TITLE :
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . Offset = CommentStart - Previous ;
TemplateMetadata - > Tag [ TemplateMetadata - > TagCount ] . TagCode = TAG_TITLE ;
TemplateMetadata - > TagCount + + ;
DepartComment ( & Template ) ;
Previous = Template . Ptr ;
goto Here ;
} ;
2017-05-13 15:38:10 +00:00
}
}
2017-09-07 21:41:08 +00:00
+ + Template . Ptr ;
2017-08-29 20:35:28 +00:00
}
}
else
{
2017-09-07 21:41:08 +00:00
+ + Template . Ptr ;
2017-08-29 20:35:28 +00:00
}
}
2017-09-07 21:41:08 +00:00
FreeBuffer ( & Template ) ;
if ( FoundIndex )
{
TemplateMetadata - > Validity | = PAGE_INDEX ;
}
2017-08-29 20:35:28 +00:00
if ( ! HaveErrors & & FoundIncludes & & FoundMenus & & FoundPlayer & & FoundScript )
{
2017-09-07 21:41:08 +00:00
TemplateMetadata - > Validity | = PAGE_PLAYER ;
2017-08-29 20:35:28 +00:00
}
2017-09-07 21:41:08 +00:00
if ( ! Config . ForceIntegration )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
if ( PageType = = PAGE_INDEX & & ! ( TemplateMetadata - > Validity & PAGE_INDEX ) )
{
CopyStringToBuffer ( Errors , " Index template %s must include one <!-- __CINERA_INDEX__ --> tag \n " , TemplateMetadata - > Filename ) ;
fprintf ( stderr , " %s " , Errors - > Location ) ;
return RC_INVALID_TEMPLATE ;
}
else if ( PageType = = PAGE_PLAYER & & ! ( TemplateMetadata - > Validity & PAGE_PLAYER ) )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
if ( ! FoundIncludes ) { CopyStringToBuffer ( Errors , " Player template %s must include one <!-- __CINERA_INCLUDES__ --> tag \n " , TemplateMetadata - > Filename ) ; } ;
if ( ! FoundMenus ) { CopyStringToBuffer ( Errors , " Player template %s must include one <!-- __CINERA_MENUS__ --> tag \n " , TemplateMetadata - > Filename ) ; } ;
if ( ! FoundPlayer ) { CopyStringToBuffer ( Errors , " Player template %s must include one <!-- __CINERA_PLAYER__ --> tag \n " , TemplateMetadata - > Filename ) ; } ;
if ( ! FoundScript ) { CopyStringToBuffer ( Errors , " Player template %s must include one <!-- __CINERA_SCRIPT__ --> tag \n " , TemplateMetadata - > Filename ) ; } ;
fprintf ( stderr , " %s " , Errors - > Location ) ;
return RC_INVALID_TEMPLATE ;
2017-08-29 20:35:28 +00:00
}
}
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
}
void RewindBuffer ( buffer * Buffer )
{
# if DEBUG
float PercentageUsed = ( float ) ( Buffer - > Ptr - Buffer - > Location ) / Buffer - > Size * 100 ;
printf ( " Rewinding %s \n "
" Used: %ld / %d (%.2f%%) \n \n " ,
Buffer - > ID ,
Buffer - > Ptr - Buffer - > Location ,
Buffer - > Size ,
PercentageUsed ) ;
# endif
Buffer - > Ptr = Buffer - > Location ;
2017-08-29 20:35:28 +00:00
}
int
2017-09-07 21:41:08 +00:00
HMMLToBuffers ( arena * MemoryArena , buffers * CollationBuffers , config Config , char * Filename )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
RewindBuffer ( & CollationBuffers - > IncludesPlayer ) ;
RewindBuffer ( & CollationBuffers - > Menus ) ;
RewindBuffer ( & CollationBuffers - > Player ) ;
RewindBuffer ( & CollationBuffers - > Script ) ;
RewindBuffer ( & CollationBuffers - > IncludesIndex ) ;
2017-08-29 20:35:28 +00:00
char Filepath [ 255 ] ;
if ( Config . Edition = = EDITION_PROJECT )
{
CopyString ( Filepath , " %s/%s " , Config . ProjectDir , Filename ) ;
}
else
{
CopyString ( Filepath , " %s " , Filename ) ;
}
FILE * InFile ;
if ( ! ( InFile = fopen ( Filepath , " r " ) ) )
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " Unable to open (annotations file) %s: %s " , Filename , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to open (annotations file) %s: %s \n " , Filename , strerror ( errno ) ) ;
return RC_ERROR_FILE ;
2017-08-29 20:35:28 +00:00
}
HMML_Output HMML = hmml_parse_file ( InFile ) ;
fclose ( InFile ) ;
if ( HMML . well_formed )
{
CopyString ( CollationBuffers - > Title , HMML . metadata . title ) ;
2017-09-07 21:41:08 +00:00
CopyString ( CollationBuffers - > Project , HMML . metadata . project ) ;
2017-08-29 20:35:28 +00:00
# if DEBUG
printf (
" ================================================================================ \n "
" %s \n "
" ================================================================================ \n " ,
Filename ) ;
# endif
// NOTE(matt): Tree structure of "global" buffer dependencies
// Master
// IncludesPlayer
// Menus
// QuoteMenu
// ReferenceMenu
// FilterMenu
// FilterTopics
// FilterMedia
// CreditsMenu
// Player
// Script
// FilterState
buffer QuoteMenu ;
buffer ReferenceMenu ;
buffer FilterMenu ;
buffer FilterTopics ;
buffer FilterMedia ;
buffer CreditsMenu ;
buffer Annotation ;
buffer AnnotationHeader ;
buffer AnnotationClass ;
buffer AnnotationData ;
buffer Text ;
buffer TopicDots ;
buffer FilterState ;
2017-09-07 21:41:08 +00:00
if ( ClaimBuffer ( MemoryArena , & QuoteMenu , " QuoteMenu " , Kilobytes ( 16 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( MemoryArena , & ReferenceMenu , " ReferenceMenu " , Kilobytes ( 16 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( MemoryArena , & FilterMenu , " FilterMenu " , Kilobytes ( 16 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( MemoryArena , & FilterTopics , " FilterTopics " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( MemoryArena , & FilterMedia , " FilterMedia " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( MemoryArena , & CreditsMenu , " CreditsMenu " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( MemoryArena , & FilterState , " FilterState " , Kilobytes ( 4 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
2017-08-29 20:35:28 +00:00
ref_info ReferencesArray [ 200 ] = { 0 } ;
categories Topics = { 0 } ;
categories Media = { 0 } ;
bool HasQuoteMenu = FALSE ;
bool HasReferenceMenu = FALSE ;
bool HasFilterMenu = FALSE ;
bool HasCreditsMenu = FALSE ;
int QuoteIdentifier = 0x3b1 ;
int RefIdentifier = 1 ;
int UniqueRefs = 0 ;
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <div class= \" title %s \" > \n "
" <span class= \" episode_name \" > " , HMML . metadata . project ) ;
CopyStringToBufferHTMLSafe ( & CollationBuffers - > Menus , HMML . metadata . title ) ;
CopyStringToBuffer ( & CollationBuffers - > Menus , " </span> \n "
" <span id= \" focus-warn \" >⚠ Click here to regain focus ⚠</span> \n " ) ;
CopyStringToBuffer ( & CollationBuffers - > Player ,
" <div class= \" player_container \" > \n "
" <div class= \" video_container \" data-videoId= \" %s \" ></div> \n "
" <div class= \" markers_container %s \" > \n " , HMML . metadata . id , HMML . metadata . project ) ;
int CreditsErrorCode = BuildCredits ( Config , & CreditsMenu , & HasCreditsMenu , Config . ImagesDir , HMML . metadata ) ;
if ( CreditsErrorCode )
{
switch ( CreditsErrorCode )
{
case CreditsError_NoHost :
fprintf ( stderr , " %s: Missing \" member \" in the [video] node. Skipping... \n " , Filename ) ;
goto Cleanup ;
break ;
case CreditsError_NoAnnotator :
fprintf ( stderr , " %s: Missing \" annotator \" in the [video] node. Skipping... \n " , Filename ) ;
goto Cleanup ;
break ;
default :
break ;
2017-06-25 18:05:58 +00:00
}
2017-08-29 20:35:28 +00:00
}
# if DEBUG
printf ( " \n \n --- Entering Annotations Loop --- \n \n \n \n " ) ;
2017-06-25 18:05:58 +00:00
# endif
2017-08-29 20:35:28 +00:00
for ( int AnnotationIndex = 0 ; AnnotationIndex < HMML . annotation_count ; + + AnnotationIndex )
{
# if DEBUG
printf ( " %d \n " , AnnotationIndex ) ;
# endif
HMML_Annotation * Anno = HMML . annotations + AnnotationIndex ;
categories LocalTopics = { 0 } ;
categories LocalMedia = { 0 } ;
bool HasQuote = FALSE ;
bool HasReference = FALSE ;
quote_info QuoteInfo = { 0 } ;
// NOTE(matt): Tree structure of "annotation local" buffer dependencies
// Annotation
// AnnotationHeader
// AnnotationClass
// AnnotationData
// Text
// TopicDots
2017-09-07 21:41:08 +00:00
if ( ClaimBuffer ( MemoryArena , & Annotation , " Annotation " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( MemoryArena , & AnnotationHeader , " AnnotationHeader " , 512 ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( MemoryArena , & AnnotationClass , " AnnotationClass " , 256 ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( MemoryArena , & AnnotationData , " AnnotationData " , 512 ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( MemoryArena , & Text , " Text " , Kilobytes ( 4 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( MemoryArena , & TopicDots , " TopicDots " , 512 ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & AnnotationHeader ,
" <div data-timestamp= \" %d \" " ,
TimecodeToSeconds ( Anno - > time ) ) ;
CopyStringToBuffer ( & AnnotationClass ,
" class= \" marker " ) ;
if ( Anno - > author )
{
if ( ! HasFilterMenu )
2017-06-25 18:05:58 +00:00
{
2017-08-29 20:35:28 +00:00
HasFilterMenu = TRUE ;
2017-06-25 18:05:58 +00:00
}
2017-08-29 20:35:28 +00:00
InsertCategory ( & Topics , & Media , " authored " ) ;
CopyStringToBuffer ( & AnnotationClass , " authored " ) ;
hsl_colour AuthorColour ;
StringToColourHash ( & AuthorColour , Anno - > author ) ;
if ( Config . Edition = = EDITION_NETWORK )
2017-06-25 18:05:58 +00:00
{
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d - TODO(matt): Implement author hoverbox \n " , __FILE__ , __LINE__ ) ;
// NOTE(matt): We should get instructions on how to get this info in the config
CopyStringToBuffer ( & Text ,
" <a class= \" author \" href= \" https://handmade.network/m/%s \" target= \" blank \" style= \" color: hsl(%d, %d%%, %d%%); text-decoration: none \" data-hue= \" %d \" data-saturation= \" %d%% \" >%s</a> " ,
Anno - > author ,
AuthorColour . Hue , AuthorColour . Saturation , AuthorColour . Lightness ,
AuthorColour . Hue , AuthorColour . Saturation ,
Anno - > author ) ;
2017-06-25 18:05:58 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Text ,
" <span class= \" author \" style= \" color: hsl(%d, %d%%, %d%%); \" data-hue= \" %d \" data-saturation= \" %d%% \" >%s</span> " ,
AuthorColour . Hue , AuthorColour . Saturation , AuthorColour . Lightness ,
AuthorColour . Hue , AuthorColour . Saturation ,
Anno - > author ) ;
2017-06-25 18:05:58 +00:00
}
2017-08-29 20:35:28 +00:00
2017-06-25 18:05:58 +00:00
}
2017-08-29 20:35:28 +00:00
char * InPtr = Anno - > text ;
int MarkerIndex = 0 , RefIndex = 0 ;
while ( * InPtr | | RefIndex < Anno - > reference_count )
2017-06-25 18:05:58 +00:00
{
2017-08-29 20:35:28 +00:00
if ( MarkerIndex < Anno - > marker_count & &
InPtr - Anno - > text = = Anno - > markers [ MarkerIndex ] . offset )
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
char * Readable = Anno - > markers [ MarkerIndex ] . parameter
? Anno - > markers [ MarkerIndex ] . parameter
: Anno - > markers [ MarkerIndex ] . marker ;
if ( Anno - > markers [ MarkerIndex ] . type = = HMML_MEMBER )
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
hsl_colour MemberColour ;
StringToColourHash ( & MemberColour , Anno - > markers [ MarkerIndex ] . marker ) ;
if ( Config . Edition = = EDITION_NETWORK )
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d - TODO(matt): Implement member hoverbox \n " , __FILE__ , __LINE__ ) ;
// NOTE(matt): We should get instructions on how to get this info in the config
CopyStringToBuffer ( & Text ,
" <a class= \" member \" href= \" https://handmade.network/m/%s \" target= \" blank \" style= \" color: hsl(%d, %d%%, %d%%); text-decoration: none \" data-hue= \" %d \" data-saturation= \" %d%% \" >%.*s</a> " ,
Anno - > markers [ MarkerIndex ] . marker ,
MemberColour . Hue , MemberColour . Saturation , MemberColour . Lightness ,
MemberColour . Hue , MemberColour . Saturation ,
StringLength ( Readable ) , InPtr ) ;
2017-05-13 15:38:10 +00:00
}
2017-08-29 20:35:28 +00:00
else
{
CopyStringToBuffer ( & Text ,
" <span class= \" member \" style= \" color: hsl(%d, %d%%, %d%%); \" data-hue= \" %d \" data-saturation= \" %d%% \" >%.*s</span> " ,
MemberColour . Hue , MemberColour . Saturation , MemberColour . Lightness ,
MemberColour . Hue , MemberColour . Saturation ,
StringLength ( Readable ) , InPtr ) ;
}
InPtr + = StringLength ( Readable ) ;
+ + MarkerIndex ;
}
else if ( Anno - > markers [ MarkerIndex ] . type = = HMML_PROJECT )
{
hsl_colour ProjectColour ;
StringToColourHash ( & ProjectColour , Anno - > markers [ MarkerIndex ] . marker ) ;
if ( Config . Edition = = EDITION_NETWORK )
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d - TODO(matt): Implement project hoverbox \n " , __FILE__ , __LINE__ ) ;
// NOTE(matt): We should get instructions on how to get this info in the config
CopyStringToBuffer ( & Text ,
" <a class= \" project \" href= \" https://%s.handmade.network/ \" target= \" blank \" style= \" color: hsl(%d, %d%%, %d%%); text-decoration: none \" data-hue= \" %d \" data-saturation= \" %d%% \" >%s</a> " ,
Anno - > markers [ MarkerIndex ] . marker ,
ProjectColour . Hue , ProjectColour . Saturation , ProjectColour . Lightness ,
ProjectColour . Hue , ProjectColour . Saturation ,
Readable ) ;
2017-05-13 15:38:10 +00:00
}
2017-08-29 20:35:28 +00:00
else
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Text ,
" <span class= \" project \" style= \" color: hsl(%d, %d%%, %d%%); \" data-hue= \" %d \" data-saturation= \" %d%% \" >%s</span> " ,
ProjectColour . Hue , ProjectColour . Saturation , ProjectColour . Lightness ,
ProjectColour . Hue , ProjectColour . Saturation ,
Readable ) ;
2017-05-13 15:38:10 +00:00
}
2017-08-29 20:35:28 +00:00
InPtr + = StringLength ( Readable ) ;
+ + MarkerIndex ;
}
else if ( Anno - > markers [ MarkerIndex ] . type = = HMML_CATEGORY )
{
2017-09-07 21:41:08 +00:00
switch ( GenerateTopicColours ( Anno - > markers [ MarkerIndex ] . marker , Config . CSSDir ) )
{
case RC_SUCCESS :
case RC_NOOP :
break ;
case RC_ERROR_FILE :
case RC_ERROR_MEMORY :
return RC_ERROR_FATAL ;
} ;
2017-08-29 20:35:28 +00:00
if ( ! HasFilterMenu )
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
HasFilterMenu = TRUE ;
2017-05-13 15:38:10 +00:00
}
2017-08-29 20:35:28 +00:00
InsertCategory ( & Topics , & Media , Anno - > markers [ MarkerIndex ] . marker ) ; // Global
InsertCategory ( & LocalTopics , & LocalMedia , Anno - > markers [ MarkerIndex ] . marker ) ; // Local
2017-05-13 15:38:10 +00:00
}
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
while ( RefIndex < Anno - > reference_count & &
InPtr - Anno - > text = = Anno - > references [ RefIndex ] . offset )
2017-06-25 18:05:58 +00:00
{
2017-08-29 20:35:28 +00:00
HMML_Reference * CurrentRef = Anno - > references + RefIndex ;
if ( ! HasReferenceMenu )
{
CopyStringToBuffer ( & ReferenceMenu ,
" <div class= \" menu references \" > \n "
" <span>References ▼</span> \n "
" <div class= \" mouse_catcher \" ></div> \n "
" <div class= \" refs references_container \" > \n " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( BuildReference ( ReferencesArray , RefIdentifier , UniqueRefs , * CurrentRef , * Anno ) = = 1 )
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " Reference combination processing failed: %s:%d " , Filename , Anno - > line ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Cannot process new combination of reference info \n "
" \n "
" Either tweak your annotation, or contact matt@handmadedev.org \n "
" mentioning the ref node you want to write and how you want it to \n "
" appear in the references menu \n " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_REFERENCE ;
2017-08-29 20:35:28 +00:00
}
+ + ReferencesArray [ RefIdentifier - 1 ] . IdentifierCount ;
+ + UniqueRefs ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
HasReferenceMenu = TRUE ;
}
else
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
for ( int i = 0 ; i < UniqueRefs ; + + i )
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
if ( ReferencesArray [ i ] . IdentifierCount = = REF_MAX_IDENTIFIER )
2017-08-08 19:26:54 +00:00
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_EMERGENCY , " REF_MAX_IDENTIFIER (%d) reached. Contact matt@handmadedev.org " , REF_MAX_IDENTIFIER ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Too many timecodes associated with one reference (increase REF_MAX_IDENTIFIER) \n " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_ERROR_MAX_REFS ;
2017-08-08 19:26:54 +00:00
}
2017-08-29 20:35:28 +00:00
if ( CurrentRef - > isbn )
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
if ( ! StringsDiffer ( CurrentRef - > isbn , ReferencesArray [ i ] . ID ) )
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
CopyString ( ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Timecode , Anno - > time ) ;
ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Identifier = RefIdentifier ;
+ + ReferencesArray [ i ] . IdentifierCount ;
goto AppendedIdentifier ;
2017-08-08 19:26:54 +00:00
}
}
2017-08-29 20:35:28 +00:00
else if ( CurrentRef - > url )
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
if ( ! StringsDiffer ( CurrentRef - > url , ReferencesArray [ i ] . ID ) )
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
CopyString ( ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Timecode , Anno - > time ) ;
ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Identifier = RefIdentifier ;
+ + ReferencesArray [ i ] . IdentifierCount ;
goto AppendedIdentifier ;
2017-08-08 19:26:54 +00:00
}
}
2017-08-29 20:35:28 +00:00
else
2017-08-08 19:26:54 +00:00
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " Reference missing ISBN or URL: %s:%d " , Filename , Anno - > line ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Reference must have an ISBN or URL \n " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_REFERENCE ;
2017-08-08 19:26:54 +00:00
}
}
2017-08-29 20:35:28 +00:00
if ( BuildReference ( ReferencesArray , RefIdentifier , UniqueRefs , * CurrentRef , * Anno ) = = 1 )
2017-08-08 19:26:54 +00:00
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " Reference combination processing failed: %s:%d " , Filename , Anno - > line ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Cannot process new combination of reference info \n "
" \n "
" Either tweak your annotation, or contact matt@handmadedev.org \n "
" mentioning the ref node you want to write and how you want it to \n "
" appear in the references menu \n " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_REFERENCE ;
2017-08-08 19:26:54 +00:00
}
2017-08-29 20:35:28 +00:00
+ + ReferencesArray [ UniqueRefs ] . IdentifierCount ;
+ + UniqueRefs ;
2017-08-08 19:26:54 +00:00
}
2017-08-29 20:35:28 +00:00
AppendedIdentifier :
if ( ! HasReference )
2017-03-25 02:07:55 +00:00
{
2017-08-29 20:35:28 +00:00
if ( CurrentRef - > isbn )
2017-03-25 02:07:55 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & AnnotationData , " data-ref= \" %s " , CurrentRef - > isbn ) ;
2017-03-25 02:07:55 +00:00
}
2017-08-29 20:35:28 +00:00
else if ( CurrentRef - > url )
2017-03-29 03:38:12 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & AnnotationData , " data-ref= \" %s " , CurrentRef - > url ) ;
2017-03-29 03:38:12 +00:00
}
2017-03-30 02:57:04 +00:00
else
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " Reference missing ISBN or URL: %s:%d " , Filename , Anno - > line ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Reference must have an ISBN or URL \n " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_REFERENCE ;
2017-03-30 02:57:04 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
HasReference = TRUE ;
}
else
{
if ( CurrentRef - > isbn )
2017-03-29 03:38:12 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & AnnotationData , " ,%s " , CurrentRef - > isbn ) ;
2017-03-29 03:38:12 +00:00
}
2017-08-29 20:35:28 +00:00
else if ( CurrentRef - > url )
2017-05-24 21:56:36 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & AnnotationData , " ,%s " , CurrentRef - > url ) ;
2017-05-24 21:56:36 +00:00
}
else
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " Reference missing ISBN or URL: %s:%d " , Filename , Anno - > line ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Reference must have an ISBN or URL " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_REFERENCE ;
2017-05-24 21:56:36 +00:00
}
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Anno - > references [ RefIndex ] . offset = = Anno - > references [ RefIndex - 1 ] . offset )
{
CopyStringToBuffer ( & Text , " <sup>,%d</sup> " , RefIdentifier ) ;
2017-03-25 02:07:55 +00:00
}
2017-08-29 20:35:28 +00:00
else
{
CopyStringToBuffer ( & Text , " <sup>%d</sup> " , RefIdentifier ) ;
}
+ + RefIndex ;
+ + RefIdentifier ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( * InPtr )
{
switch ( * InPtr )
2017-03-25 02:07:55 +00:00
{
2017-08-29 20:35:28 +00:00
case ' < ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " < " ) ;
InPtr + + ;
break ;
2017-08-29 20:35:28 +00:00
case ' > ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " > " ) ;
InPtr + + ;
break ;
2017-08-29 20:35:28 +00:00
case ' & ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " & " ) ;
InPtr + + ;
break ;
2017-08-29 20:35:28 +00:00
case ' \" ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " " " ) ;
InPtr + + ;
break ;
2017-08-29 20:35:28 +00:00
case ' \' ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " ' " ) ;
InPtr + + ;
break ;
2017-08-29 20:35:28 +00:00
default :
2017-06-25 18:05:58 +00:00
* Text . Ptr + + = * InPtr + + ;
2017-08-19 21:41:51 +00:00
* Text . Ptr = ' \0 ' ;
2017-06-25 18:05:58 +00:00
break ;
2017-03-25 02:07:55 +00:00
}
}
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Anno - > is_quote )
{
if ( ! HasQuoteMenu )
2017-03-29 03:38:12 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & QuoteMenu ,
" <div class= \" menu quotes \" > \n "
" <span>Quotes ▼</span> \n "
" <div class= \" mouse_catcher \" ></div> \n "
" <div class= \" refs quotes_container \" > \n " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
HasQuoteMenu = TRUE ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( ! HasReference )
{
CopyStringToBuffer ( & AnnotationData , " data-ref= \" &#%d; " , QuoteIdentifier ) ;
}
else
{
CopyStringToBuffer ( & AnnotationData , " ,&#%d; " , QuoteIdentifier ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
HasQuote = TRUE ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
char * Speaker = Anno - > quote . author ? Anno - > quote . author : HMML . metadata . stream_username ? HMML . metadata . stream_username : HMML . metadata . member ;
if ( BuildQuote ( & QuoteInfo ,
Speaker ,
Anno - > quote . id ,
Config . CacheDir ) = = 1 )
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " Quote #%s %d not found: %s:%d " , Speaker , Anno - > quote . id , Filename , Anno - > line ) ;
fprintf ( stderr , " %s:%d: Quote #%s %d not found. Skipping this file... \n " ,
2017-08-29 20:35:28 +00:00
Filename ,
Anno - > line ,
Speaker ,
Anno - > quote . id ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_ERROR_QUOTE ;
2017-03-29 03:38:12 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & QuoteMenu ,
" <a target= \" _blank \" class= \" ref \" href= \" https://dev.abaines.me.uk/quotes/%s/%d \" > \n "
" <span data-id= \" &#%d; \" > \n "
" <span class= \" ref_content \" > \n "
" <div class= \" source \" >Quote %d</div> \n "
" <div class= \" ref_title \" > " ,
Speaker ,
Anno - > quote . id ,
QuoteIdentifier ,
Anno - > quote . id ) ;
CopyStringToBufferHTMLSafe ( & QuoteMenu , QuoteInfo . Text ) ;
CopyStringToBuffer ( & QuoteMenu , " </div> \n "
" <div class= \" quote_byline \" >—%s, %s</div> \n "
" </span> \n "
" <div class= \" ref_indices \" > \n "
" <span data-timestamp= \" %d \" class= \" timecode \" ><span class= \" ref_index \" >[&#%d;]</span><span class= \" time \" >%s</span></span> \n "
" </div> \n "
" </span> \n "
" </a> \n " ,
Speaker ,
QuoteInfo . Date ,
TimecodeToSeconds ( Anno - > time ) ,
QuoteIdentifier ,
Anno - > time ) ;
if ( ! Anno - > text [ 0 ] )
2017-03-25 02:07:55 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Text , " “ " ) ;
CopyStringToBufferHTMLSafe ( & Text , QuoteInfo . Text ) ;
CopyStringToBuffer ( & Text , " ” " ) ;
2017-05-22 21:37:00 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Text , " <sup>&#%d;</sup> " , QuoteIdentifier ) ;
+ + QuoteIdentifier ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
while ( MarkerIndex < Anno - > marker_count )
{
2017-09-07 21:41:08 +00:00
switch ( GenerateTopicColours ( Anno - > markers [ MarkerIndex ] . marker , Config . CSSDir ) )
{
case RC_SUCCESS :
case RC_NOOP :
break ;
case RC_ERROR_FILE :
case RC_ERROR_MEMORY :
return RC_ERROR_FATAL ;
} ;
2017-08-29 20:35:28 +00:00
if ( ! HasFilterMenu )
2017-05-22 21:37:00 +00:00
{
2017-08-29 20:35:28 +00:00
HasFilterMenu = TRUE ;
2017-03-25 02:07:55 +00:00
}
2017-08-29 20:35:28 +00:00
if ( Anno - > markers [ MarkerIndex ] . marker )
2017-03-29 03:38:12 +00:00
{
2017-08-29 20:35:28 +00:00
InsertCategory ( & Topics , & Media , Anno - > markers [ MarkerIndex ] . marker ) ;
2017-03-29 03:38:12 +00:00
}
2017-08-29 20:35:28 +00:00
InsertCategory ( & LocalTopics , & LocalMedia , Anno - > markers [ MarkerIndex ] . marker ) ;
+ + MarkerIndex ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( LocalMedia . Count = = 0 )
{
InsertCategory ( & Topics , & Media , Config . DefaultMedium ) ;
InsertCategory ( & LocalTopics , & LocalMedia , Config . DefaultMedium ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
BuildCategories ( & AnnotationClass , & TopicDots , LocalTopics , LocalMedia , & MarkerIndex ) ;
CopyBuffer ( & AnnotationHeader , & AnnotationClass ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasQuote | | HasReference )
{
CopyStringToBuffer ( & AnnotationData , " \" " ) ;
CopyBuffer ( & AnnotationHeader , & AnnotationData ) ;
}
CopyStringToBuffer ( & AnnotationHeader , " > \n " ) ;
CopyBuffer ( & Annotation , & AnnotationHeader ) ;
CopyStringToBuffer ( & Annotation ,
" <div class= \" content \" ><span class= \" timecode \" >%s</span> " ,
Anno - > time ) ;
CopyBuffer ( & Annotation , & Text ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( LocalTopics . Count > 0 )
{
CopyBuffer ( & Annotation , & TopicDots ) ;
2017-03-22 02:18:31 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Annotation , " </div> \n "
" <div class= \" progress faded \" > \n "
" <div class= \" content \" ><span class= \" timecode \" >%s</span> " ,
Anno - > time ) ;
CopyBuffer ( & Annotation , & Text ) ;
CopyStringToBuffer ( & Annotation , " </div> \n "
" </div> \n "
" <div class= \" progress main \" > \n "
" <div class= \" content \" ><span class= \" timecode \" >%s</span> " ,
Anno - > time ) ;
CopyBuffer ( & Annotation , & Text ) ;
CopyStringToBuffer ( & Annotation , " </div> \n "
" </div> \n "
" </div> \n " ) ;
CopyBuffer ( & CollationBuffers - > Player , & Annotation ) ;
// NOTE(matt): Tree structure of "annotation local" buffer dependencies
// TopicDots
// Text
// AnnotationData
// AnnotationClass
// AnnotationHeader
// Annotation
DeclaimBuffer ( MemoryArena , & TopicDots ) ;
DeclaimBuffer ( MemoryArena , & Text ) ;
DeclaimBuffer ( MemoryArena , & AnnotationData ) ;
DeclaimBuffer ( MemoryArena , & AnnotationClass ) ;
DeclaimBuffer ( MemoryArena , & AnnotationHeader ) ;
DeclaimBuffer ( MemoryArena , & Annotation ) ;
}
2017-05-21 06:35:16 +00:00
# if DEBUG
2017-08-29 20:35:28 +00:00
printf ( " \n \n --- End of Annotations Loop --- \n \n \n \n " ) ;
2017-05-21 06:35:16 +00:00
# endif
2017-08-29 20:35:28 +00:00
if ( HasQuoteMenu )
{
CopyStringToBuffer ( & QuoteMenu ,
" </div> \n "
" </div> \n " ) ;
CopyBuffer ( & CollationBuffers - > Menus , & QuoteMenu ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasReferenceMenu )
{
for ( int i = 0 ; i < UniqueRefs ; + + i )
2017-03-29 03:38:12 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & ReferenceMenu ,
" <a data-id= \" %s \" href= \" %s \" target= \" _blank \" class= \" ref \" > \n "
" <span class= \" ref_content \" > \n " ,
ReferencesArray [ i ] . ID ,
ReferencesArray [ i ] . URL ) ;
if ( * ReferencesArray [ i ] . Source )
2017-03-30 02:57:04 +00:00
{
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & ReferenceMenu ,
2017-08-29 20:35:28 +00:00
" <div class= \" source \" > " ) ;
CopyStringToBufferHTMLSafe ( & ReferenceMenu , ReferencesArray [ i ] . Source ) ;
CopyStringToBuffer ( & ReferenceMenu , " </div> \n "
" <div class= \" ref_title \" > " ) ;
CopyStringToBufferHTMLSafe ( & ReferenceMenu , ReferencesArray [ i ] . RefTitle ) ;
CopyStringToBuffer ( & ReferenceMenu , " </div> \n " ) ;
}
else
{
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & ReferenceMenu ,
2017-08-29 20:35:28 +00:00
" <div class= \" ref_title \" > " ) ;
CopyStringToBufferHTMLSafe ( & ReferenceMenu , ReferencesArray [ i ] . RefTitle ) ;
CopyStringToBuffer ( & ReferenceMenu , " </div> \n " ) ;
}
CopyStringToBuffer ( & ReferenceMenu ,
" </span> \n " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
for ( int j = 0 ; j < ReferencesArray [ i ] . IdentifierCount ; )
{
CopyStringToBuffer ( & ReferenceMenu ,
" <div class= \" ref_indices \" > \n " ) ;
for ( int k = 0 ; k < 3 & & j < ReferencesArray [ i ] . IdentifierCount ; + + k , + + j )
2017-06-25 18:05:58 +00:00
{
CopyStringToBuffer ( & ReferenceMenu ,
2017-08-29 20:35:28 +00:00
" <span data-timestamp= \" %d \" class= \" timecode \" ><span class= \" ref_index \" >[%d]</span><span class= \" time \" >%s</span></span> " ,
TimecodeToSeconds ( ReferencesArray [ i ] . Identifier [ j ] . Timecode ) ,
ReferencesArray [ i ] . Identifier [ j ] . Identifier ,
ReferencesArray [ i ] . Identifier [ j ] . Timecode ) ;
2017-06-25 18:05:58 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & ReferenceMenu , " \n "
" </div> \n " ) ;
2017-03-30 02:57:04 +00:00
}
2017-06-25 18:22:54 +00:00
2017-03-29 03:38:12 +00:00
CopyStringToBuffer ( & ReferenceMenu ,
2017-08-29 20:35:28 +00:00
" </a> \n " ) ;
}
CopyStringToBuffer ( & ReferenceMenu ,
" </div> \n "
" </div> \n " ) ;
CopyBuffer ( & CollationBuffers - > Menus , & ReferenceMenu ) ;
}
if ( HasFilterMenu )
{
CopyStringToBuffer ( & FilterState ,
" var filterState = { \n " ) ;
for ( int i = 0 ; i < Topics . Count ; + + i )
{
CopyStringToBuffer ( & FilterState , " \" %s \" : \t { \" type \" : \" %s \" , \t \" off \" : false }, \n " ,
Topics . Category [ i ] . Marker , " topic " ) ;
2017-03-29 03:38:12 +00:00
}
2017-08-29 20:35:28 +00:00
for ( int i = 0 ; i < Media . Count ; + + i )
2017-05-21 06:35:16 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & FilterState , " \" %s \" : \t { \" type \" : \" %s \" , \t \" off \" : false }, \n " ,
Media . Category [ i ] . Marker , " medium " ) ;
}
CopyStringToBuffer ( & FilterState ,
" }; \n " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Config . Edition = = EDITION_PROJECT )
{
CopyStringToBuffer ( & FilterMenu ,
" <div class= \" menu filter \" > \n "
" <span><img src= \" ../%s/cinera_icon_filter.png \" ></span> \n "
" <div class= \" filter_container \" > \n "
" <div class= \" filter_mode inclusive \" >Filter mode: </div> \n "
" <div class= \" filters \" > \n " , Config . ImagesDir ) ;
}
else
{
2017-05-21 06:35:16 +00:00
CopyStringToBuffer ( & FilterMenu ,
2017-08-19 22:35:07 +00:00
" <div class= \" menu filter \" > \n "
" <span><img src= \" %s/cinera_icon_filter.png \" ></span> \n "
" <div class= \" filter_container \" > \n "
" <div class= \" filter_mode inclusive \" >Filter mode: </div> \n "
2017-08-29 20:35:28 +00:00
" <div class= \" filters \" > \n " , Config . ImagesDir ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Topics . Count > 0 )
{
CopyStringToBuffer ( & FilterMenu ,
" <div class= \" filter_topics \" > \n "
" <div class= \" filter_title \" >Topics</div> \n " ) ;
for ( int i = 0 ; i < Topics . Count ; + + i )
2017-08-19 22:35:07 +00:00
{
CopyStringToBuffer ( & FilterTopics ,
2017-08-29 20:35:28 +00:00
" <div class= \" filter_content %s \" > \n "
" <span class= \" icon category %s \" ></span><span class= \" text \" >%s</span> \n "
" </div> \n " ,
Topics . Category [ i ] . Marker ,
Topics . Category [ i ] . Marker ,
Topics . Category [ i ] . Marker ) ;
2017-08-19 22:35:07 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & FilterTopics ,
" </div> \n " ) ;
CopyBuffer ( & FilterMenu , & FilterTopics ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Media . Count > 0 )
{
CopyStringToBuffer ( & FilterMedia ,
" <div class= \" filter_media \" > \n "
" <div class= \" filter_title \" >Media</div> \n " ) ;
for ( int i = 0 ; i < Media . Count ; + + i )
2017-08-19 22:35:07 +00:00
{
2017-08-29 20:35:28 +00:00
int j ;
for ( j = 0 ; j < ArrayCount ( CategoryMedium ) ; + + j )
2017-05-24 21:56:36 +00:00
{
2017-08-29 20:35:28 +00:00
if ( ! StringsDiffer ( Media . Category [ i ] . Marker , CategoryMedium [ j ] . Medium ) )
2017-05-24 21:56:36 +00:00
{
2017-08-29 20:35:28 +00:00
break ;
2017-05-24 21:56:36 +00:00
}
2017-05-21 06:35:16 +00:00
}
2017-08-29 20:35:28 +00:00
2017-08-19 22:35:07 +00:00
CopyStringToBuffer ( & FilterMedia ,
2017-08-29 20:35:28 +00:00
" <div class= \" filter_content %s \" > \n "
" <span class= \" icon \" >%s</span><span class= \" text \" >%s</span> \n "
" </div> \n " ,
Media . Category [ i ] . Marker ,
CategoryMedium [ j ] . Icon ,
CategoryMedium [ j ] . WrittenName
) ;
2017-05-21 06:35:16 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & FilterMedia ,
" </div> \n " ) ;
CopyBuffer ( & FilterMenu , & FilterMedia ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & FilterMenu ,
" </div> \n "
" </div> \n "
" </div> \n " ) ;
2017-08-19 22:35:07 +00:00
2017-08-29 20:35:28 +00:00
CopyBuffer ( & CollationBuffers - > Menus , & FilterMenu ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasCreditsMenu )
{
CopyBuffer ( & CollationBuffers - > Menus , & CreditsMenu ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <div class= \" help \" > \n "
" <span>?</span> \n "
" <div class= \" help_container \" > \n "
" <span class= \" help_key \" >?</span><h1>Keyboard Navigation</h1> \n "
" \n "
" <h2>Global Keys</h2> \n "
" <span class= \" help_key \" >W</span>, <span class= \" help_key \" >A</span>, <span class= \" help_key \" >P</span> / <span class= \" help_key \" >S</span>, <span class= \" help_key \" >D</span>, <span class= \" help_key \" >N</span> <span class= \" help_text \" >Jump to previous / next marker</span><br> \n " ) ;
if ( HasFilterMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >z</span> <span class= \" help_text \" >Toggle filter mode</span> <span class= \" help_key \" >V</span> <span class= \" help_text \" >Revert filter to original state</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >z</span> <span class= \" help_text unavailable \" >Toggle filter mode</span> <span class= \" help_key unavailable \" >V</span> <span class= \" help_text unavailable \" >Revert filter to original state</span> \n " ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" \n "
" <h2>Menu toggling</h2> \n " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasQuoteMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >q</span> <span class= \" help_text \" >Quotes</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >q</span> <span class= \" help_text unavailable \" >Quotes</span> \n " ) ;
}
if ( HasReferenceMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >r</span> <span class= \" help_text \" >References</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >r</span> <span class= \" help_text unavailable \" >References</span> \n " ) ;
}
if ( HasFilterMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >f</span> <span class= \" help_text \" >Filter</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >f</span> <span class= \" help_text unavailable \" >Filter</span> \n " ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasCreditsMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >c</span> <span class= \" help_text \" >Credits</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >c</span> <span class= \" help_text unavailable \" >Credits</span> \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" \n "
" <h2>Movement</h2> \n "
" <div class= \" help_paragraph \" > \n "
" <div class= \" key_block \" > \n "
" <div class= \" key_column \" style= \" flex-grow: 1 \" > \n "
" <span class= \" help_key \" >a</span> \n "
" </div> \n "
" <div class= \" key_column \" style= \" flex-grow: 2 \" > \n "
" <span class= \" help_key \" >w</span><br> \n "
" <span class= \" help_key \" >s</span> \n "
" </div> \n "
" <div class= \" key_column \" style= \" flex-grow: 1 \" > \n "
" <span class= \" help_key \" >d</span> \n "
" </div> \n "
" </div> \n "
" <div class= \" key_block \" > \n "
" <span class= \" help_key \" >h</span> \n "
" <span class= \" help_key \" >j</span> \n "
" <span class= \" help_key \" >k</span> \n "
" <span class= \" help_key \" >l</span> \n "
" </div> \n "
" <div class= \" key_block \" > \n "
" <div style= \" flex-grow: 1 \" > \n "
" <span class= \" help_key \" >←</span> \n "
" </div> \n "
" <div style= \" flex-grow: 2 \" > \n "
" <span class= \" help_key \" >↑</span><br> \n "
" <span class= \" help_key \" >↓</span> \n "
" </div> \n "
" <div style= \" flex-grow: 1 \" > \n "
" <span class= \" help_key \" >→</span> \n "
" </div> \n "
" </div> \n "
" </div> \n "
" <br> \n " ) ;
if ( HasQuoteMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2>Quotes " ) ;
2017-06-03 01:32:18 +00:00
if ( HasReferenceMenu )
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " and References Menus</h2> \n " ) ;
2017-06-03 01:32:18 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " <span class= \" unavailable \" >and References</span> Menus</h2> \n " ) ;
2017-06-03 01:32:18 +00:00
}
2017-08-29 20:35:28 +00:00
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2><span class= \" unavailable \" >Quotes " ) ;
if ( HasReferenceMenu )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " and</span> References Menus</h2> \n " ) ;
2017-06-03 01:32:18 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " and References Menus</span></h2> \n " ) ;
2017-06-03 01:32:18 +00:00
}
2017-08-29 20:35:28 +00:00
}
if ( HasQuoteMenu | | HasReferenceMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span style= \" width: auto \" class= \" help_key \" >Enter</span> <span class= \" help_text \" >Jump to timecode</span><br> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span style= \" width: auto \" class= \" help_key unavailable \" >Enter</span> <span class= \" help_text unavailable \" >Jump to timecode</span><br> \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Menus , " \n " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasQuoteMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2>Quotes " ) ;
if ( HasReferenceMenu )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " , References " ) ;
if ( HasCreditsMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus , " and Credits Menus</h2> " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus , " <span class= \" unavailable \" >and Credits</span> Menus</h2> " ) ;
}
2017-06-03 01:32:18 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " <span class= \" unavailable \" >, References " ) ;
if ( HasCreditsMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus , " </span>and Credits Menus</h2> " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus , " and Credits</span> Menus</h2> " ) ;
}
2017-06-03 01:32:18 +00:00
}
2017-08-29 20:35:28 +00:00
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2><span class= \" unavailable \" >Quotes " ) ;
if ( HasReferenceMenu )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " , </span>References " ) ;
if ( HasCreditsMenu )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " and Credits Menus</h2> " ) ;
2017-06-03 01:32:18 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " <span class= \" unavailable \" >and Credits</span> Menus</h2> " ) ;
2017-06-03 01:32:18 +00:00
}
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " , References " ) ;
if ( HasCreditsMenu )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " </span>and Credits Menus</h2> " ) ;
2017-06-03 01:32:18 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " and Credits Menus</h2></span> " ) ;
2017-06-03 01:32:18 +00:00
}
}
2017-08-29 20:35:28 +00:00
}
CopyStringToBuffer ( & CollationBuffers - > Menus , " \n " ) ;
if ( HasQuoteMenu | | HasReferenceMenu | | HasCreditsMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >o</span> <span class= \" help_text \" >Open URL (in new tab)</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >o</span> <span class= \" help_text unavailable \" >Open URL (in new tab)</span> \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" \n " ) ;
if ( HasFilterMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2>Filter Menu</h2> \n "
" <span class= \" help_key \" >x</span>, <span style= \" width: auto \" class= \" help_key \" >Space</span> <span class= \" help_text \" >Toggle category and focus next</span><br> \n "
" <span class= \" help_key \" >X</span>, <span style= \" width: auto; margin-right: 0px \" class= \" help_key \" >Shift</span><span style= \" width: auto \" class= \" help_key \" >Space</span> <span class= \" help_text \" >Toggle category and focus previous</span><br> \n "
" <span class= \" help_key \" >v</span> <span class= \" help_text \" >Invert topics / media as per focus</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2><span class= \" unavailable \" >Filter Menu</span></h2> \n "
" <span class= \" help_key unavailable \" >x</span>, <span style= \" width: auto \" class= \" help_key unavailable \" >Space</span> <span class= \" help_text unavailable \" >Toggle category and focus next</span><br> \n "
" <span class= \" help_key unavailable \" >X</span>, <span style= \" width: auto; margin-right: 0px \" class= \" help_key unavailable \" >Shift</span><span style= \" width: auto \" class= \" help_key unavailable \" >Space</span> <span class= \" help_text unavailable \" >Toggle category and focus previous</span><br> \n "
" <span class= \" help_key unavailable \" >v</span> <span class= \" help_text unavailable \" >Invert topics / media as per focus</span> \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Menus , " \n " ) ;
if ( HasCreditsMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2>Credits Menu</h2> \n "
" <span style= \" width: auto \" class= \" help_key \" >Enter</span> <span class= \" help_text \" >Open URL (in new tab)</span><br> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2><span class= \" unavailable \" >Credits Menu</span></h2> \n "
" <span style= \" width: auto \" class= \" help_key unavailable \" >Enter</span> <span class= \" help_text unavailable \" >Open URL (in new tab)</span><br> \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" </div> \n "
" </div> \n "
" </div> " ) ;
CopyStringToBuffer ( & CollationBuffers - > Player ,
" </div> \n "
" </div> " ) ;
// TODO(matt): Maybe do something about indentation levels
// TODO(matt): We may need to do some actual logic here to figure out
// where the style paths are in relation to us, rather than assuming them
// to be one directory up the tree
if ( Config . Edition = = EDITION_PROJECT )
{
CopyStringToBuffer ( & CollationBuffers - > IncludesIndex ,
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s/cinera.css \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s/cinera__%s.css \" > \n "
2017-09-07 21:41:08 +00:00
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s/cinera_topics.css \" > \n "
" \n "
" <meta charset= \" UTF-8 \" > \n "
" <meta name= \" generator \" content= \" Cinera \" > \n " ,
2017-08-29 20:35:28 +00:00
Config . CSSDir ,
Config . CSSDir ,
HMML . metadata . project ,
Config . CSSDir ) ;
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" <link rel= \" stylesheet \" type= \" text/css \" href= \" ../%s/cinera.css \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" ../%s/cinera__%s.css \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" ../%s/cinera_topics.css \" > \n " ,
Config . CSSDir ,
Config . CSSDir ,
HMML . metadata . project ,
Config . CSSDir ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s/cinera.css \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s/cinera__%s.css \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s/cinera_topics.css \" > \n " ,
Config . CSSDir ,
Config . CSSDir ,
HMML . metadata . project ,
Config . CSSDir ) ;
}
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" \n "
" <meta charset= \" UTF-8 \" > \n "
" <meta name= \" generator \" content= \" Cinera \" > \n " ) ;
if ( Topics . Count | | Media . Count )
{
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" <meta name= \" keywords \" content= \" " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Topics . Count > 0 )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
for ( int i = 0 ; i < Topics . Count ; + + i )
{
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer , " %s, " , Topics . Category [ i ] . Marker ) ;
}
2017-06-03 01:32:18 +00:00
}
2017-08-29 20:35:28 +00:00
if ( Media . Count > 0 )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
for ( int i = 0 ; i < Media . Count ; + + i )
{
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer , " %s, " , Media . Category [ i ] . WrittenText ) ;
}
2017-06-03 01:32:18 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CollationBuffers - > IncludesPlayer . Ptr - = 2 ;
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer , " \" > \n \n " ) ;
}
if ( Config . Edition = = EDITION_PROJECT )
{
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" <script type= \" text/javascript \" src= \" ../%s/cinera.js \" ></script> \n " ,
Config . JSDir ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" <script type= \" text/javascript \" src= \" %s/cinera.js \" ></script> \n " ,
Config . JSDir ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Script ,
" <script type= \" text/javascript \" > \n "
" var menuState = []; \n " ) ;
CopyStringToBuffer ( & CollationBuffers - > Script ,
" var quotesMenu = document.querySelector( \" .quotes_container \" ); \n " ) ;
if ( HasQuoteMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Script ,
" if(quotesMenu) \n "
" { \n "
" menuState.push(quotesMenu); \n "
" var quoteItems = quotesMenu.querySelectorAll( \" .ref \" ); \n "
" for(var i = 0; i < quoteItems.length; ++i) \n "
" { \n "
" quoteItems[i].addEventListener( \" mouseenter \" , function(ev) { \n "
" mouseOverQuotes(this); \n "
" }) \n "
" }; \n "
" var lastFocusedQuote = null; \n "
" } \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Script ,
" var referencesMenu = document.querySelector( \" .references_container \" ); \n " ) ;
if ( HasReferenceMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Script ,
" if(referencesMenu) \n "
" { \n "
" menuState.push(referencesMenu); \n "
" var referenceItems = referencesMenu.querySelectorAll( \" .ref \" ); \n "
" for(var i = 0; i < referenceItems.length; ++i) \n "
" { \n "
" referenceItems[i].addEventListener( \" mouseenter \" , function(ev) { \n "
" mouseOverReferences(this); \n "
" }) \n "
" }; \n "
" var lastFocusedReference = null; \n "
" var lastFocusedIdentifier = null; \n "
" } \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Script ,
" var filterMenu = document.querySelector( \" .filter_container \" ); \n " ) ;
if ( HasFilterMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Script ,
" if(filterMenu) \n "
" { \n "
" menuState.push(filterMenu); \n "
" var lastFocusedCategory = null; \n "
" var lastFocusedTopic = null; \n "
" var lastFocusedMedium = null; \n "
" \n "
" var filter = filterMenu.parentNode; \n "
" \n "
" var filterModeElement = filter.querySelector( \" .filter_mode \" ); \n "
" filterModeElement.addEventListener( \" click \" , function(ev) { \n "
" toggleFilterMode(); \n "
" }); \n "
" \n "
" var filterMode = filterModeElement.classList[1]; \n "
" var filterItems = filter.querySelectorAll( \" .filter_content \" ); \n "
" for(var i = 0; i < filterItems.length; ++i) \n "
" { \n "
" filterItems[i].addEventListener( \" mouseenter \" , function(ev) { \n "
" navigateFilter(this); \n "
" }) \n "
" \n "
" filterItems[i].addEventListener( \" click \" , function(ev) { \n "
" filterItemToggle(this); \n "
" }); \n "
" \n "
" %s "
" } \n "
" } \n " , FilterState . Location ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Script ,
" var creditsMenu = document.querySelector( \" .credits_container \" ); \n " ) ;
if ( HasCreditsMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Script ,
" if(creditsMenu) \n "
" { \n "
" menuState.push(creditsMenu); \n "
" var lastFocusedCreditItem = null; \n "
" \n "
" var creditItems = creditsMenu.querySelectorAll( \" .person, .support \" ); \n "
" for(var i = 0; i < creditItems.length; ++i) \n "
" { \n "
" creditItems[i].addEventListener( \" mouseenter \" , function(ev) { \n "
" if(this != lastFocusedCreditItem) \n "
" { \n "
" lastFocusedCreditItem.classList.remove( \" focused \" ); \n "
" lastFocusedCreditItem = this; \n "
" focusedElement = lastFocusedCreditItem; \n "
" focusedElement.classList.add( \" focused \" ); \n "
" } \n "
" }) \n "
" } \n "
" } \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Script ,
" var sourceMenus = document.querySelectorAll( \" .menu \" ); \n "
" \n "
" var helpButton = document.querySelector( \" .help \" ); \n "
" var helpDocumentation = helpButton.querySelector( \" .help_container \" ); \n "
" helpButton.addEventListener( \" click \" , function(ev) { \n "
" handleMouseOverMenu(this, ev.type); \n "
" }) \n "
" \n "
" var focusedElement = null; \n "
" var focusedIdentifier = null; \n "
" \n "
" var playerContainer = document.querySelector( \" .player_container \" ) \n "
" var player = new Player(playerContainer, onRefChanged); \n "
" window.addEventListener( \" resize \" , function() { player.updateSize(); }); \n "
" document.addEventListener( \" keydown \" , function(ev) { \n "
" var key = ev.key; \n "
" if(ev.getModifierState( \" Shift \" ) && key == \" \" ) \n "
" { \n "
" key = \" capitalSpace \" ; \n "
" } \n "
" \n "
" if(handleKey(key) == true && focusedElement) \n "
" { \n "
" ev.preventDefault(); \n "
" } \n "
" }); \n "
" \n "
" for(var i = 0; i < sourceMenus.length; ++i) \n "
" { \n "
" sourceMenus[i].addEventListener( \" mouseenter \" , function(ev) { \n "
" handleMouseOverMenu(this, ev.type); \n "
" }) \n "
" sourceMenus[i].addEventListener( \" mouseleave \" , function(ev) { \n "
" handleMouseOverMenu(this, ev.type); \n "
" }) \n "
" }; \n "
" \n "
" var refTimecodes = document.querySelectorAll( \" .refs .ref .timecode \" ); \n "
" for (var i = 0; i < refTimecodes.length; ++i) { \n "
" refTimecodes[i].addEventListener( \" click \" , function(ev) { \n "
" if (player) { \n "
" var time = ev.currentTarget.getAttribute( \" data-timestamp \" ); \n "
" mouseSkipToTimecode(player, time, ev); \n "
" } \n "
" }); \n "
" } \n "
" \n "
" var refSources = document.querySelectorAll( \" .refs .ref \" ); // This is for both quotes and refs \n "
" for (var i = 0; i < refSources.length; ++i) { \n "
" refSources[i].addEventListener( \" click \" , function(ev) { \n "
" if (player) { \n "
" player.pause(); \n "
" } \n "
" }); \n "
" } \n "
" \n "
" var testMarkers = playerContainer.querySelectorAll( \" .marker \" ); \n "
" \n "
" window.addEventListener( \" blur \" , function(){ \n "
" document.getElementById( \" focus-warn \" ).style.display = \" block \" ; \n "
" }); \n "
" \n "
" window.addEventListener( \" focus \" , function(){ \n "
" document.getElementById( \" focus-warn \" ).style.display = \" none \" ; \n "
" }); \n "
" \n "
" var colouredItems = playerContainer.querySelectorAll( \" .author, .member, .project \" ); \n "
" for(i = 0; i < colouredItems.length; ++i) \n "
" { \n "
" setTextLightness(colouredItems[i]); \n "
" } \n "
" \n "
" var topicDots = document.querySelectorAll( \" .category \" ); \n "
" for(var i = 0; i < topicDots.length; ++i) \n "
" { \n "
" setDotLightness(topicDots[i]); \n "
" } \n "
" \n "
" if(location.hash) { \n "
" player.setTime(location.hash.startsWith('#') ? location.hash.substr(1) : location.hash); \n "
" } \n "
" </script> " ) ;
// NOTE(matt): Tree structure of "global" buffer dependencies
// FilterState
// CreditsMenu
// FilterMedia
// FilterTopics
// FilterMenu
// ReferenceMenu
// QuoteMenu
Cleanup :
DeclaimBuffer ( MemoryArena , & FilterState ) ;
DeclaimBuffer ( MemoryArena , & CreditsMenu ) ;
DeclaimBuffer ( MemoryArena , & FilterMedia ) ;
DeclaimBuffer ( MemoryArena , & FilterTopics ) ;
DeclaimBuffer ( MemoryArena , & FilterMenu ) ;
DeclaimBuffer ( MemoryArena , & ReferenceMenu ) ;
DeclaimBuffer ( MemoryArena , & QuoteMenu ) ;
}
else
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " %s:%d: %s " , Filename , HMML . error . line , HMML . error . message ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: %s \n " , Filename , HMML . error . line , HMML . error . message ) ;
2017-09-07 21:41:08 +00:00
hmml_free ( & HMML ) ;
return RC_ERROR_HMML ;
2017-08-29 20:35:28 +00:00
}
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
2017-08-29 20:35:28 +00:00
}
2017-09-07 21:41:08 +00:00
int
BuffersToHTML ( arena * MemoryArena , buffers CollationBuffers , template * TemplateMetadata , config Config , char * OutputPath , int PageType )
2017-08-29 20:35:28 +00:00
{
# if DEBUG
2017-09-07 21:41:08 +00:00
printf ( " \n \n --- Buffer Collation --- \n "
" %s \n \n \n " , OutputPath ) ;
2017-08-29 20:35:28 +00:00
# endif
if ( Config . Mode = = MODE_INTEGRATE )
{
2017-09-07 21:41:08 +00:00
if ( TemplateMetadata - > Validity & PageType )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
// TODO(matt): Maybe enable the Template(s) to stick around for the duration of RefreshProject()
buffer Template ;
Template . ID = TemplateMetadata - > Filename ;
FILE * TemplateFile ;
if ( ! ( TemplateFile = fopen ( TemplateMetadata - > Filename , " r " ) ) )
{
LogError ( Config , LOG_ERROR , " Unable to open template %s: %s " , TemplateMetadata - > Filename , strerror ( errno ) ) ;
return RC_ERROR_FILE ;
}
fseek ( TemplateFile , 0 , SEEK_END ) ;
Template . Size = ftell ( TemplateFile ) ;
fseek ( TemplateFile , 0 , SEEK_SET ) ;
if ( ! ( Template . Location = malloc ( Template . Size ) ) )
{
LogError ( Config , LOG_ERROR , " BuffersToHTML(): %s " ,
strerror ( errno ) ) ;
fclose ( TemplateFile ) ;
return RC_ERROR_MEMORY ;
}
Template . Ptr = Template . Location ;
fread ( Template . Location , Template . Size , 1 , TemplateFile ) ;
fclose ( TemplateFile ) ;
buffer Output ;
Output . Size = Template . Size + ( Kilobytes ( 512 ) ) ;
Output . ID = " Output " ;
if ( ! ( Output . Location = malloc ( Output . Size ) ) )
{
LogError ( Config , LOG_ERROR , " BuffersToHTML(): %s " ,
strerror ( errno ) ) ;
FreeBuffer ( & Template ) ;
return RC_ERROR_MEMORY ;
}
Output . Ptr = Output . Location ;
2017-08-29 20:35:28 +00:00
2017-09-07 21:41:08 +00:00
Template . Ptr = Template . Location ;
for ( int i = 0 ; i < TemplateMetadata - > TagCount ; + + i )
2017-06-03 01:32:18 +00:00
{
2017-09-07 21:41:08 +00:00
int j = 0 ;
while ( TemplateMetadata - > Tag [ i ] . Offset > j )
2017-06-03 01:32:18 +00:00
{
2017-09-07 21:41:08 +00:00
* Output . Ptr + + = * Template . Ptr + + ;
+ + j ;
}
2017-08-29 20:35:28 +00:00
2017-09-07 21:41:08 +00:00
switch ( TemplateMetadata - > Tag [ i ] . TagCode )
{
case TAG_TITLE :
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Output , CollationBuffers . Title ) ;
2017-09-07 21:41:08 +00:00
break ;
case TAG_INDEX :
CopyBuffer ( & Output , & CollationBuffers . Index ) ;
break ;
case TAG_INCLUDES :
if ( PageType = = PAGE_PLAYER )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
CopyBuffer ( & Output , & CollationBuffers . IncludesPlayer ) ;
2017-08-29 20:35:28 +00:00
}
2017-09-07 21:41:08 +00:00
else
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
CopyBuffer ( & Output , & CollationBuffers . IncludesIndex ) ;
2017-08-29 20:35:28 +00:00
}
break ;
2017-09-07 21:41:08 +00:00
case TAG_MENUS :
CopyBuffer ( & Output , & CollationBuffers . Menus ) ;
break ;
case TAG_PLAYER :
2017-08-29 20:35:28 +00:00
CopyBuffer ( & Output , & CollationBuffers . Player ) ;
break ;
2017-09-07 21:41:08 +00:00
case TAG_SCRIPT :
2017-08-29 20:35:28 +00:00
CopyBuffer ( & Output , & CollationBuffers . Script ) ;
break ;
2017-06-03 01:32:18 +00:00
}
2017-09-07 21:41:08 +00:00
DepartComment ( & Template ) ;
2017-06-03 01:32:18 +00:00
}
2017-09-07 21:41:08 +00:00
while ( Template . Ptr - Template . Location < Template . Size )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
* Output . Ptr + + = * Template . Ptr + + ;
2017-06-03 01:32:18 +00:00
}
2017-08-29 20:35:28 +00:00
2017-09-07 21:41:08 +00:00
FreeBuffer ( & Template ) ;
FILE * OutFile ;
if ( ! ( OutFile = fopen ( Config . Edition = = EDITION_PROJECT ? OutputPath : Config . OutIntegratedLocation , " w " ) ) )
{
LogError ( Config , LOG_ERROR , " Unable to open output file %s: %s " , Config . Edition = = EDITION_PROJECT ? OutputPath : Config . OutIntegratedLocation , strerror ( errno ) ) ;
free ( Output . Location ) ;
return RC_ERROR_FILE ;
}
fwrite ( Output . Location , Output . Ptr - Output . Location , 1 , OutFile ) ;
fclose ( OutFile ) ;
free ( Output . Location ) ;
return RC_SUCCESS ;
}
else
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
return RC_INVALID_TEMPLATE ;
2017-08-29 20:35:28 +00:00
}
}
else
{
2017-09-07 21:41:08 +00:00
buffer Master ;
if ( ClaimBuffer ( MemoryArena , & Master , " Master " , Kilobytes ( 512 ) ) = = RC_ARENA_FULL ) { return RC_ARENA_FULL ; } ;
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Master ,
" <html> \n "
" <head> \n " ) ;
2017-09-07 21:41:08 +00:00
CopyBuffer ( & Master , PageType = = PAGE_PLAYER ? & CollationBuffers . IncludesPlayer : & CollationBuffers . IncludesIndex ) ;
CopyStringToBuffer ( & Master , " \n " ) ;
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Master ,
" </head> \n "
" <body> \n " ) ;
2017-09-07 21:41:08 +00:00
if ( PageType = = PAGE_PLAYER )
{
CopyBuffer ( & Master , & CollationBuffers . Menus ) ;
CopyStringToBuffer ( & Master , " \n " ) ;
CopyBuffer ( & Master , & CollationBuffers . Player ) ;
CopyStringToBuffer ( & Master , " \n " ) ;
CopyBuffer ( & Master , & CollationBuffers . Script ) ;
CopyStringToBuffer ( & Master , " \n " ) ;
}
else
{
CopyBuffer ( & Master , & CollationBuffers . Index ) ;
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Master ,
" </body> \n "
" </html> \n " ) ;
FILE * OutFile ;
if ( ! ( OutFile = fopen ( Config . Edition = = EDITION_PROJECT ? OutputPath : Config . OutLocation , " w " ) ) )
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " Unable to open output file %s: %s " , Config . Edition = = EDITION_PROJECT ? OutputPath : Config . OutLocation , strerror ( errno ) ) ;
DeclaimBuffer ( MemoryArena , & Master ) ;
return RC_ERROR_FILE ;
2017-08-29 20:35:28 +00:00
}
fwrite ( Master . Location , Master . Ptr - Master . Location , 1 , OutFile ) ;
fclose ( OutFile ) ;
2017-09-07 21:41:08 +00:00
DeclaimBuffer ( MemoryArena , & Master ) ;
return RC_SUCCESS ;
2017-08-29 20:35:28 +00:00
}
}
int
2017-09-07 21:41:08 +00:00
RefreshIndex ( arena * MemoryArena , buffers * CollationBuffers , config Config , char * BaseFilename )
2017-08-29 20:35:28 +00:00
{
char IndexPath [ 255 ] ;
2017-09-07 21:41:08 +00:00
CopyString ( IndexPath , " %s/%s.index " , Config . CacheDir , CollationBuffers - > Project ) ;
2017-08-29 20:35:28 +00:00
FILE * IndexFile ;
if ( ! ( IndexFile = fopen ( IndexPath , " a+ " ) ) )
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " Unable to open index file %s: %s " , IndexPath , strerror ( errno ) ) ;
return RC_ERROR_FILE ;
2017-08-29 20:35:28 +00:00
}
buffer Index ;
2017-09-07 21:41:08 +00:00
Index . ID = " Index " ;
2017-08-29 20:35:28 +00:00
fseek ( IndexFile , 0 , SEEK_END ) ;
2017-09-07 21:41:08 +00:00
int IndexFileSize = ftell ( IndexFile ) ;
Index . Size = IndexFileSize + StringLength ( BaseFilename ) + StringLength ( CollationBuffers - > Title ) + 3 ;
2017-08-29 20:35:28 +00:00
fseek ( IndexFile , 0 , SEEK_SET ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( ! ( Index . Location = malloc ( Index . Size ) ) )
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_ERROR , " RefreshIndex(): %s " , strerror ( errno ) ) ;
return RC_ERROR_MEMORY ;
2017-08-29 20:35:28 +00:00
}
Index . Ptr = Index . Location ;
2017-09-07 21:41:08 +00:00
fread ( Index . Location , IndexFileSize , 1 , IndexFile ) ;
2017-08-29 20:35:28 +00:00
bool Found = FALSE ;
2017-09-07 21:41:08 +00:00
bool Inserted = FALSE ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
int EntryCount = 0 ;
2017-09-07 21:41:08 +00:00
while ( Index . Ptr - Index . Location < IndexFileSize )
2017-08-29 20:35:28 +00:00
{
char IndexedFile [ 32 ] ;
char * Ptr = IndexedFile ;
Index . Ptr + = CopyStringNoFormatT ( Ptr , Index . Ptr , ' , ' ) ;
if ( ! StringsDiffer ( IndexedFile , BaseFilename ) )
{
Found = TRUE ;
break ;
}
2017-09-07 21:41:08 +00:00
else if ( StringsDiffer ( IndexedFile , BaseFilename ) > 0 )
{
while ( Index . Ptr > Index . Location & & * Index . Ptr ! = ' \n ' )
{
- - Index . Ptr ;
}
if ( Index . Ptr > Index . Location )
{
+ + Index . Ptr ;
}
int Head = Index . Ptr - Index . Location ;
buffer Scratch ;
Scratch . Size = IndexFileSize ;
if ( ! ( Scratch . Location = malloc ( Scratch . Size + 1 ) ) )
{
LogError ( Config , LOG_ERROR , " RefreshIndex(): %s " , strerror ( errno ) ) ;
free ( Index . Location ) ;
2017-09-15 01:11:39 +00:00
fclose ( IndexFile ) ;
2017-09-07 21:41:08 +00:00
return RC_ERROR_MEMORY ;
}
Scratch . Ptr = Scratch . Location ;
while ( Index . Ptr - Index . Location < IndexFileSize )
{
* Scratch . Ptr + + = * Index . Ptr + + ;
}
* Scratch . Ptr = ' \0 ' ;
Index . Ptr = Index . Location + Head ;
2017-09-15 01:11:39 +00:00
CopyStringToBuffer ( & Index , " %s,%s \n "
" %s " ,
BaseFilename , CollationBuffers - > Title ,
Scratch . Location ) ;
2017-09-07 21:41:08 +00:00
free ( Scratch . Location ) ;
2017-09-15 01:11:39 +00:00
Index . Size = Index . Ptr - Index . Location ;
2017-09-07 21:41:08 +00:00
fclose ( IndexFile ) ;
IndexFile = fopen ( IndexPath , " w " ) ;
2017-09-15 01:11:39 +00:00
fwrite ( Index . Location , Index . Size , 1 , IndexFile ) ;
2017-09-07 21:41:08 +00:00
Inserted = TRUE ;
}
2017-08-29 20:35:28 +00:00
else
{
2017-09-07 21:41:08 +00:00
while ( Index . Ptr - Index . Location < IndexFileSize & & * Index . Ptr ! = ' \n ' )
2017-06-25 18:05:58 +00:00
{
2017-08-29 20:35:28 +00:00
+ + Index . Ptr ;
2017-06-25 18:05:58 +00:00
}
2017-08-29 20:35:28 +00:00
+ + Index . Ptr ;
}
2017-09-07 21:41:08 +00:00
+ + EntryCount ;
2017-08-29 20:35:28 +00:00
}
2017-09-15 01:11:39 +00:00
if ( ! Found )
2017-08-29 20:35:28 +00:00
{
+ + EntryCount ;
// TODO(matt): Write the EntryCount into the index
2017-09-15 01:11:39 +00:00
if ( ! Inserted )
2017-09-07 21:41:08 +00:00
{
2017-09-15 01:11:39 +00:00
CopyStringToBuffer ( & Index , " %s,%s \n " , BaseFilename , CollationBuffers - > Title ) ;
2017-09-07 21:41:08 +00:00
fprintf ( IndexFile , " %s,%s \n " , BaseFilename , CollationBuffers - > Title ) ;
}
RewindBuffer ( & CollationBuffers - > Index ) ;
2017-09-15 01:11:39 +00:00
Index . Size = Index . Ptr - Index . Location ;
RewindBuffer ( & Index ) ;
2017-09-07 21:41:08 +00:00
CopyStringToBuffer ( & CollationBuffers - > Index , " <dl> \n " ) ;
2017-09-15 01:11:39 +00:00
while ( Index . Ptr - Index . Location < Index . Size )
2017-09-07 21:41:08 +00:00
{
char IndexedFile [ 32 ] ;
char * Ptr = IndexedFile ;
Index . Ptr + = CopyStringNoFormatT ( Ptr , Index . Ptr , ' , ' ) + 1 ;
2017-08-29 20:35:28 +00:00
2017-09-07 21:41:08 +00:00
char Title [ 255 ] ;
Ptr = Title ;
Index . Ptr + = CopyStringNoFormatT ( Ptr , Index . Ptr , ' \n ' ) + 1 ;
2017-08-29 20:35:28 +00:00
2017-09-15 01:11:39 +00:00
// TODO(matt): Fully figure out why the Table of Contents doesn't always get all the stuff from the index
// Steps to reproduce:
// 1. mv riscy04{4..6}.hmml out of the way
// 2. Call the program
// 3. mv riscy046.hmml into the project directory
// 4. See that riscy046 is in the index file, has its own player page, but is not mentioned in the Table of Contents
2017-09-07 21:41:08 +00:00
CopyStringToBuffer ( & CollationBuffers - > Index ,
2017-08-29 20:35:28 +00:00
" <dt> \n "
" <a href= \" %s \" >%s</a> \n "
" </dt> \n " ,
2017-09-07 21:41:08 +00:00
IndexedFile , Title ) ;
2017-06-25 18:22:54 +00:00
2017-09-07 21:41:08 +00:00
}
CopyStringToBuffer ( & CollationBuffers - > Index , " </dl> " ) ;
}
2017-08-19 21:41:51 +00:00
2017-09-07 21:41:08 +00:00
fclose ( IndexFile ) ;
FreeBuffer ( & Index ) ;
return Found ? RC_NOOP : RC_REFRESHED ;
}
2017-06-25 18:22:54 +00:00
2017-09-07 21:41:08 +00:00
int
RefreshProject ( arena * MemoryArena , buffers * CollationBuffers ,
template * IndexTemplateMetadata , template * PlayerTemplateMetadata ,
config Config )
{
DIR * ProjectDirHandle ;
if ( ! ( ProjectDirHandle = opendir ( Config . ProjectDir ) ) )
{
LogError ( Config , LOG_ERROR , " Unable to scan project directory %s: %s " , Config . ProjectDir , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to scan project directory %s: %s \n " , Config . ProjectDir , strerror ( errno ) ) ;
return RC_ERROR_DIRECTORY ;
}
2017-08-29 20:35:28 +00:00
2017-09-07 21:41:08 +00:00
struct dirent * ProjectFiles ;
int FileIndex = 0 ;
2017-08-29 20:35:28 +00:00
2017-09-07 21:41:08 +00:00
NextFile :
while ( ( ProjectFiles = readdir ( ProjectDirHandle ) ) )
{
// TODO(matt): Loft out into a function, maybe?
char * Ptr ;
Ptr = ProjectFiles - > d_name ;
Ptr + = ( StringLength ( ProjectFiles - > d_name ) - StringLength ( " .hmml " ) ) ;
if ( ! ( StringsDiffer ( Ptr , " .hmml " ) ) )
{
* Ptr = ' \0 ' ;
char BaseFilename [ 255 ] ;
CopyString ( BaseFilename , ProjectFiles - > d_name ) ;
* Ptr = ' . ' ;
switch ( HMMLToBuffers ( MemoryArena , CollationBuffers , Config , ProjectFiles - > d_name ) )
{
// TODO(matt): Actually sort out the fatality of these cases, once we are always-on
case RC_ERROR_FILE :
case RC_ERROR_FATAL :
closedir ( ProjectDirHandle ) ;
return RC_ERROR_FATAL ;
case RC_ERROR_HMML :
case RC_ERROR_MAX_REFS :
case RC_ERROR_QUOTE :
case RC_INVALID_REFERENCE :
goto NextFile ;
case RC_SUCCESS :
break ;
} ;
switch ( RefreshIndex ( MemoryArena , CollationBuffers , Config , BaseFilename ) )
2017-08-19 21:41:51 +00:00
{
2017-09-07 21:41:08 +00:00
// TODO(matt): Actually sort out the fatality of these cases, once we are always-on
case RC_ERROR_FILE :
case RC_ERROR_MEMORY :
closedir ( ProjectDirHandle ) ;
return RC_ERROR_FATAL ;
case RC_NOOP :
break ;
case RC_REFRESHED :
{
char OutputDir [ 255 ] ;
CopyString ( OutputDir , " %s/%s " , Config . BaseDir , BaseFilename ) ;
char OutputPath [ 255 ] ;
CopyString ( OutputPath , " %s/index.html " , OutputDir ) ;
DIR * OutputDirectoryHandle ;
if ( ! ( OutputDirectoryHandle = opendir ( OutputDir ) ) )
{
if ( MakeDir ( OutputDir ) = = RC_ERROR_DIRECTORY )
{
LogError ( Config , LOG_ERROR , " Unable to create directory %s: %s " , OutputDir , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to create directory %s: %s \n " , OutputDir , strerror ( errno ) ) ;
closedir ( ProjectDirHandle ) ;
return RC_ERROR_DIRECTORY ;
} ;
}
closedir ( OutputDirectoryHandle ) ;
// TODO(matt): Implement checksumming?
char IndexPagePath [ 255 ] ;
CopyString ( IndexPagePath , " %s/index.html " , Config . BaseDir ) ;
switch ( BuffersToHTML ( MemoryArena , * CollationBuffers , IndexTemplateMetadata , Config , IndexPagePath , PAGE_INDEX ) )
{
// TODO(matt): Actually sort out the fatality of these cases, once we are always-on
case RC_INVALID_TEMPLATE :
LogError ( Config , LOG_ERROR , " Invalid index template: %s " , IndexTemplateMetadata - > Filename ) ;
fprintf ( stderr , " Invalid index template: %s \n " , IndexTemplateMetadata - > Filename ) ;
closedir ( ProjectDirHandle ) ;
case RC_ERROR_MEMORY :
case RC_ERROR_FILE :
case RC_ARENA_FULL :
return RC_ERROR_FATAL ;
case RC_SUCCESS :
break ;
}
switch ( BuffersToHTML ( MemoryArena , * CollationBuffers , PlayerTemplateMetadata , Config , OutputPath , PAGE_PLAYER ) )
{
// TODO(matt): Actually sort out the fatality of these cases, once we are always-on
case RC_INVALID_TEMPLATE :
LogError ( Config , LOG_ERROR , " Invalid player template: %s " , PlayerTemplateMetadata - > Filename ) ;
fprintf ( stderr , " Invalid player template: %s \n " , PlayerTemplateMetadata - > Filename ) ;
closedir ( ProjectDirHandle ) ;
case RC_ERROR_MEMORY :
case RC_ERROR_FILE :
case RC_ARENA_FULL :
return RC_ERROR_FATAL ;
case RC_SUCCESS :
break ;
}
}
break ;
2017-08-29 20:35:28 +00:00
}
2017-08-19 21:41:51 +00:00
2017-09-07 21:41:08 +00:00
+ + FileIndex ;
2017-08-29 20:35:28 +00:00
}
}
2017-09-07 21:41:08 +00:00
closedir ( ProjectDirHandle ) ;
return RC_SUCCESS ;
2017-08-29 20:35:28 +00:00
}
2017-08-19 21:41:51 +00:00
2017-09-15 01:11:39 +00:00
int
MonitorDirectory ( arena * MemoryArena , buffers * CollationBuffers , template * IndexTemplateMetadata , template * PlayerTemplateMetadata , config Config , int inotifyInstance , int WatchDescriptor )
{
buffer Events ;
if ( ClaimBuffer ( MemoryArena , & Events , " inotify Events " , Kilobytes ( 4 ) ) = = RC_ARENA_FULL ) { return RC_ARENA_FULL ; } ;
struct inotify_event * Event ;
int BytesRead ;
while ( ( BytesRead = read ( inotifyInstance , Events . Location , Events . Size ) ) ! = - 1 & & errno = = EAGAIN & & BytesRead > 0 )
{
for ( Events . Ptr = Events . Location ;
Events . Ptr < Events . Location + BytesRead ;
Events . Ptr + = sizeof ( struct inotify_event ) + Event - > len )
{
Event = ( struct inotify_event * ) Events . Ptr ;
switch ( RefreshProject ( MemoryArena , CollationBuffers , IndexTemplateMetadata , PlayerTemplateMetadata , Config ) )
{
case RC_ERROR_DIRECTORY :
case RC_ERROR_FATAL :
DeclaimBuffer ( MemoryArena , & Events ) ;
return RC_ERROR_FATAL ;
case RC_SUCCESS :
break ;
}
}
}
DeclaimBuffer ( MemoryArena , & Events ) ;
return RC_SUCCESS ;
}
2017-08-29 20:35:28 +00:00
int
main ( int ArgC , char * * Args )
{
// TODO(matt): Read all defaults from the config
config DefaultConfig = {
. BaseDir = " . " ,
. CSSDir = " . " ,
. Edition = EDITION_SINGLE ,
. ImagesDir = " . " ,
. JSDir = " . " ,
2017-09-07 21:41:08 +00:00
. LogLevel = LOG_DEBUG ,
2017-08-29 20:35:28 +00:00
. DefaultMedium = " programming " ,
. Mode = getenv ( " CINERA_MODE " ) ? MODE_INTEGRATE : MODE_BARE ,
. OutLocation = " out.html " ,
. OutIntegratedLocation = " out_integrated.html " ,
. ForceIntegration = FALSE ,
. ProjectDir = " . " ,
2017-09-07 21:41:08 +00:00
. TemplatePlayerLocation = " template_player.html " ,
. TemplateIndexLocation = " template_index.html "
2017-08-29 20:35:28 +00:00
} ;
2017-08-19 21:41:51 +00:00
2017-08-29 20:35:28 +00:00
if ( getenv ( " XDG_CACHE_HOME " ) )
{
CopyString ( DefaultConfig . CacheDir , " %s/cinera " , getenv ( " XDG_CACHE_HOME " ) ) ;
}
else
{
CopyString ( DefaultConfig . CacheDir , " %s/.cache/cinera " , getenv ( " HOME " ) ) ;
}
2017-08-19 21:41:51 +00:00
2017-08-29 20:35:28 +00:00
config Config = DefaultConfig ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( ArgC < 2 )
{
PrintUsage ( Args [ 0 ] , DefaultConfig ) ;
2017-09-07 21:41:08 +00:00
return RC_RIP ;
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
char CommandLineArg ;
2017-09-07 21:41:08 +00:00
while ( ( CommandLineArg = getopt ( ArgC , Args , " b:c:fi:j:l:m:o:p:q:t:x:h " ) ) ! = - 1 )
2017-08-29 20:35:28 +00:00
{
switch ( CommandLineArg )
{
case ' b ' :
Config . BaseDir = optarg ;
break ;
case ' c ' :
Config . CSSDir = optarg ;
break ;
case ' f ' :
Config . ForceIntegration = TRUE ;
break ;
case ' i ' :
Config . ImagesDir = optarg ;
break ;
case ' j ' :
Config . JSDir = optarg ;
break ;
2017-09-07 21:41:08 +00:00
case ' l ' :
// TODO(matt): Make this actually take a string, rather than requiring the LogLevel number
Config . LogLevel = StringToInt ( optarg ) ;
break ;
2017-08-29 20:35:28 +00:00
case ' m ' :
Config . DefaultMedium = optarg ;
break ;
case ' o ' :
Config . OutLocation = optarg ;
Config . OutIntegratedLocation = optarg ;
break ;
case ' p ' :
Config . ProjectDir = optarg ;
break ;
case ' t ' :
2017-09-07 21:41:08 +00:00
Config . TemplatePlayerLocation = optarg ;
Config . Mode = MODE_INTEGRATE ;
break ;
case ' x ' :
Config . TemplateIndexLocation = optarg ;
2017-08-29 20:35:28 +00:00
Config . Mode = MODE_INTEGRATE ;
break ;
//case 'c':
// Override config path, once we even have a default!
case ' h ' :
default :
PrintUsage ( Args [ 0 ] , DefaultConfig ) ;
return 1 ;
}
}
if ( StringsDiffer ( Config . BaseDir , " . " ) | | StringsDiffer ( Config . ProjectDir , " . " ) )
{
if ( StringsDiffer ( Config . BaseDir , " . " ) & & StringsDiffer ( Config . ProjectDir , " . " ) )
{
Config . Edition = EDITION_PROJECT ;
}
else
{
fprintf ( stderr , " %s: Both the Base Output Directory and the Project Directory must be set in order for us to enter Project Mode \n " , Args [ 0 ] ) ;
return 1 ;
}
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Config . CSSDir [ StringLength ( Config . CSSDir ) - 1 ] = = ' / ' )
{
Config . CSSDir [ StringLength ( Config . CSSDir ) - 1 ] = ' \0 ' ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
bool ValidDefaultMedium = FALSE ;
for ( int i = 0 ; i < ArrayCount ( CategoryMedium ) ; + + i )
{
if ( ! StringsDiffer ( Config . DefaultMedium , CategoryMedium [ i ] . Medium ) )
{
ValidDefaultMedium = TRUE ;
break ;
}
}
if ( ! ValidDefaultMedium )
{
fprintf ( stderr , " Specified default medium \" %s \" not available. Valid media are: \n " , Config . DefaultMedium ) ;
for ( int i = 0 ; i < ArrayCount ( CategoryMedium ) ; + + i )
{
fprintf ( stderr , " %s \n " , CategoryMedium [ i ] . Medium ) ;
}
fprintf ( stderr , " To have \" %s \" added to the list, contact matt@handmadedev.org \n " , Config . DefaultMedium ) ;
return 1 ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Config . ImagesDir [ StringLength ( Config . ImagesDir ) - 1 ] = = ' / ' )
{
Config . ImagesDir [ StringLength ( Config . ImagesDir ) - 1 ] = ' \0 ' ;
}
if ( Config . JSDir [ StringLength ( Config . JSDir ) - 1 ] = = ' / ' )
{
Config . JSDir [ StringLength ( Config . JSDir ) - 1 ] = ' \0 ' ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
// NOTE(matt): Templating
//
// Config will contain paths of multiple templates
// App is running all the time, and picking up changes to the config as we go
// If we find a new template, we first of all validate it
// In our case here, we just want to straight up validate a template if Config.Mode == MODE_INTEGRATE
// And, in that same state, we gotta keep a Template buffer around
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
// NOTE(matt): Init MemoryArena
2017-09-07 21:41:08 +00:00
arena MemoryArena ;
2017-08-29 20:35:28 +00:00
MemoryArena . Size = Megabytes ( 4 ) ;
if ( ! ( MemoryArena . Location = calloc ( MemoryArena . Size , 1 ) ) )
{
2017-09-07 21:41:08 +00:00
LogError ( Config , LOG_EMERGENCY , " %s: %s " , Args [ 0 ] , strerror ( errno ) ) ;
return RC_RIP ;
2017-08-29 20:35:28 +00:00
}
MemoryArena . Ptr = MemoryArena . Location ;
2017-06-25 18:22:54 +00:00
2017-09-07 21:41:08 +00:00
buffer Errors ;
if ( ClaimBuffer ( & MemoryArena , & Errors , " Errors " , Kilobytes ( 1 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2017-08-29 20:35:28 +00:00
// NOTE(matt): Setup buffers and ptrs
//char *InPtr;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
// NOTE(matt): Tree structure of buffer dependencies
// IncludesPlayer
// Menus
// Player
// Script
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
buffers CollationBuffers ;
2017-09-07 21:41:08 +00:00
if ( ClaimBuffer ( & MemoryArena , & CollationBuffers . IncludesPlayer , " IncludesPlayer " , Kilobytes ( 1 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
if ( ClaimBuffer ( & MemoryArena , & CollationBuffers . Menus , " Menus " , Kilobytes ( 24 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
if ( ClaimBuffer ( & MemoryArena , & CollationBuffers . Player , " Player " , Kilobytes ( 256 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
if ( ClaimBuffer ( & MemoryArena , & CollationBuffers . Script , " Script " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2017-06-25 18:22:54 +00:00
2017-09-07 21:41:08 +00:00
if ( ClaimBuffer ( & MemoryArena , & CollationBuffers . IncludesIndex , " IncludesIndex " , Kilobytes ( 1 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
if ( ClaimBuffer ( & MemoryArena , & CollationBuffers . Index , " Index " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2017-08-29 20:35:28 +00:00
* CollationBuffers . Title = ' \0 ' ;
2017-06-25 18:22:54 +00:00
2017-09-07 21:41:08 +00:00
template * PlayerTemplateMetadata ;
template * IndexTemplateMetadata ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Config . Mode = = MODE_INTEGRATE )
{
2017-09-07 21:41:08 +00:00
if ( ClaimTemplate ( & MemoryArena , & PlayerTemplateMetadata , Config . TemplatePlayerLocation ) = = RC_ARENA_FULL ) { goto RIP ; } ;
switch ( ValidateTemplate ( & Errors , PlayerTemplateMetadata , Config , PAGE_PLAYER ) )
{
case RC_INVALID_TEMPLATE : // Invalid template
case RC_ERROR_FILE : // Could not load template
case RC_ERROR_MEMORY : // Could not allocate memory for template
goto RIP ;
case RC_SUCCESS :
break ;
}
if ( Config . Edition = = EDITION_PROJECT )
{
if ( ClaimTemplate ( & MemoryArena , & IndexTemplateMetadata , Config . TemplateIndexLocation ) = = RC_ARENA_FULL ) { goto RIP ; } ;
switch ( ValidateTemplate ( & Errors , IndexTemplateMetadata , Config , PAGE_INDEX ) )
{
case RC_INVALID_TEMPLATE : // Invalid template
case RC_ERROR_MEMORY : // Could not allocate memory for template
case RC_ERROR_FILE : // Could not load template
goto RIP ;
case RC_SUCCESS :
break ;
}
}
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
// NOTE(matt)
//
// Single Edition == Loop over Args[FileIndex]
// Project Edition == Loop over Config.ProjectDir
//
// Integrating or not
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Config . Edition = = EDITION_PROJECT )
{
2017-09-07 21:41:08 +00:00
switch ( RefreshProject ( & MemoryArena , & CollationBuffers , IndexTemplateMetadata , PlayerTemplateMetadata , Config ) )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
case RC_ERROR_DIRECTORY :
case RC_ERROR_FATAL :
goto RIP ;
case RC_SUCCESS :
break ;
2017-08-29 20:35:28 +00:00
}
2017-09-15 01:11:39 +00:00
int inotifyInstance = inotify_init1 ( IN_NONBLOCK ) ;
int WatchDescriptor = inotify_add_watch ( inotifyInstance , Config . ProjectDir , IN_CLOSE_WRITE | IN_MOVED_TO ) ;
while ( MonitorDirectory ( & MemoryArena , & CollationBuffers , IndexTemplateMetadata , PlayerTemplateMetadata , Config , inotifyInstance , WatchDescriptor ) = = RC_SUCCESS )
{
// TODO(matt): Make this update frequency configurable
sleep ( 1 ) ;
}
2017-09-07 21:41:08 +00:00
}
else
{
if ( optind = = ArgC )
2017-08-29 20:35:28 +00:00
{
fprintf ( stderr , " %s: requires at least one input .hmml file \n " , Args [ 0 ] ) ;
PrintUsage ( Args [ 0 ] , DefaultConfig ) ;
2017-09-07 21:41:08 +00:00
goto RIP ;
2017-03-22 02:18:31 +00:00
}
2017-09-07 21:41:08 +00:00
NextFile :
2017-08-29 20:35:28 +00:00
for ( int FileIndex = optind ; FileIndex < ArgC ; + + FileIndex )
2017-03-22 02:18:31 +00:00
{
2017-09-07 21:41:08 +00:00
switch ( HMMLToBuffers ( & MemoryArena , & CollationBuffers , Config , Args [ FileIndex ] ) )
{
// TODO(matt): Actually sort out the fatality of these cases, once we are always-on
case RC_ERROR_FILE :
case RC_ERROR_FATAL :
goto RIP ;
case RC_ERROR_HMML :
case RC_ERROR_MAX_REFS :
case RC_ERROR_QUOTE :
case RC_INVALID_REFERENCE :
2017-09-15 01:11:39 +00:00
if ( FileIndex < ( ArgC - 1 ) ) { goto NextFile ; }
else { goto RIP ; }
2017-09-07 21:41:08 +00:00
case RC_SUCCESS :
break ;
} ;
switch ( BuffersToHTML ( & MemoryArena , CollationBuffers , PlayerTemplateMetadata , Config , 0 , PAGE_PLAYER ) )
{
// TODO(matt): Actually sort out the fatality of these cases, once we are always-on
case RC_INVALID_TEMPLATE :
LogError ( Config , LOG_ERROR , " Invalid player template: %s " , PlayerTemplateMetadata - > Filename ) ;
case RC_ERROR_MEMORY :
case RC_ERROR_FILE :
case RC_ARENA_FULL :
goto RIP ;
case RC_SUCCESS :
break ;
} ;
2017-03-22 02:18:31 +00:00
}
}
2017-08-29 20:35:28 +00:00
if ( Config . Mode = = MODE_INTEGRATE )
{
2017-09-07 21:41:08 +00:00
DeclaimTemplate ( & MemoryArena , & PlayerTemplateMetadata ) ;
if ( Config . Edition = = EDITION_PROJECT )
{
DeclaimTemplate ( & MemoryArena , & IndexTemplateMetadata ) ;
}
2017-08-29 20:35:28 +00:00
}
DeclaimBuffer ( & MemoryArena , & CollationBuffers . Index ) ;
DeclaimBuffer ( & MemoryArena , & CollationBuffers . IncludesIndex ) ;
DeclaimBuffer ( & MemoryArena , & CollationBuffers . Script ) ;
DeclaimBuffer ( & MemoryArena , & CollationBuffers . Player ) ;
DeclaimBuffer ( & MemoryArena , & CollationBuffers . Menus ) ;
DeclaimBuffer ( & MemoryArena , & CollationBuffers . IncludesPlayer ) ;
2017-09-07 21:41:08 +00:00
DeclaimBuffer ( & MemoryArena , & Errors ) ;
RIP :
2017-07-29 00:36:14 +00:00
free ( MemoryArena . Location ) ;
2017-03-10 14:19:25 +00:00
}