2017-03-10 14:19:25 +00:00
#if 0
ctime - begin $ { 0 % . * } . ctm
2018-02-21 21:50:23 +00:00
# gcc -g -fsanitize=address -Wall -std=c99 -pipe $0 -o ${0%.*} hmml.a -lcurl
gcc - O2 - Wall - std = c99 - pipe $ 0 - o $ { 0 % . * } hmml . a - lcurl
2018-04-22 20:57:31 +00:00
# clang -fsanitize=address -g -Wall -std=c99 -pipe $0 -o ${0%.*} hmml.a -lcurl
# clang -O2 -Wall -std=c99 -pipe $0 -o ${0%.*} hmml.a -lcurl
2017-03-10 14:19:25 +00:00
ctime - end $ { 0 % . * } . ctm
exit
# endif
2018-09-17 18:06:31 +00:00
# include <stdint.h>
2017-11-11 00:34:47 +00:00
typedef struct
{
2018-09-17 18:06:31 +00:00
uint32_t Major , Minor , Patch ;
2017-11-11 00:34:47 +00:00
} version ;
version CINERA_APP_VERSION = {
. Major = 0 ,
2018-09-17 18:06:31 +00:00
. Minor = 6 ,
2018-10-05 19:57:09 +00:00
. Patch = 4
2017-11-11 00:34:47 +00:00
} ;
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
2018-09-17 18:06:31 +00:00
# include <wordexp.h>
# define __USE_XOPEN2K8 // NOTE(matt): O_NOFOLLOW
# include <fcntl.h> // NOTE(matt): open()
# define __USE_XOPEN2K // NOTE(matt): readlink()
2017-09-15 01:11:39 +00:00
# include <unistd.h> // NOTE(matt): sleep()
2017-03-23 00:34:59 +00:00
2018-04-01 20:58:53 +00:00
typedef unsigned int bool ;
# define TRUE 1
# define FALSE 0
2018-09-17 18:06:31 +00:00
# define enum8(type) int8_t
# define enum16(type) int16_t
# define enum32(type) int32_t
2018-04-01 20:58:53 +00:00
# define DEBUG 0
# define DEBUG_MEM 0
bool PROFILING = 0 ;
2018-03-06 20:40:12 +00:00
clock_t TIMING_START ;
# define START_TIMING_BLOCK(...) if(PROFILING) { printf(__VA_ARGS__); TIMING_START = clock(); }
# define END_TIMING_BLOCK() if(PROFILING) { printf("\e[1;34m%ld\e[0m\n", clock() - TIMING_START);}
2017-05-24 21:56:36 +00:00
# define Kilobytes(Bytes) Bytes << 10
# define Megabytes(Bytes) Bytes << 20
2018-02-21 21:50:23 +00:00
# define MAX_PROJECT_ID_LENGTH 31
# define MAX_PROJECT_NAME_LENGTH 63
2018-09-17 18:06:31 +00:00
# define MAX_BASE_DIR_LENGTH 127
2018-02-21 21:50:23 +00:00
# define MAX_BASE_URL_LENGTH 127
# define MAX_RELATIVE_PAGE_LOCATION_LENGTH 31
# define MAX_PLAYER_URL_PREFIX_LENGTH 15
2018-09-17 18:06:31 +00:00
# define MAX_ROOT_DIR_LENGTH 127
# define MAX_ROOT_URL_LENGTH 127
# define MAX_RELATIVE_ASSET_LOCATION_LENGTH 31
2018-02-21 21:50:23 +00:00
# define MAX_BASE_FILENAME_LENGTH 31
2018-09-17 18:06:31 +00:00
# define MAX_TITLE_LENGTH 128 - (MAX_BASE_FILENAME_LENGTH + 1) - (int)sizeof(link_insertion_offsets) - (int)sizeof(unsigned short int) - 1 // NOTE(matt): We size this such that db_entry is 128 bytes total
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
# define MAX_ASSET_FILENAME_LENGTH 63
// TODO(matt): Stop distinguishing between short / long and lift the size limit once we're on the LUT
2018-02-23 23:36:42 +00:00
# define MAX_CUSTOM_SNIPPET_SHORT_LENGTH 255
# define MAX_CUSTOM_SNIPPET_LONG_LENGTH 1023
2018-09-17 18:06:31 +00:00
# define ArrayCount(A) sizeof(A) / sizeof(*(A))
# define Assert(Expression) if(!(Expression)) { printf("l.%d: \e[1;31mAssertion failure\e[0m\n", __LINE__); __asm__("int3"); }
# define FOURCC(String) ((uint32_t)(String[0] << 0) | (uint32_t)(String[1] << 8) | (uint32_t)(String[2] << 16) | (uint32_t)(String[3] << 24))
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
{
2018-04-01 20:58:53 +00:00
MODE_FORCEINTEGRATION = 1 < < 0 ,
MODE_ONESHOT = 1 < < 1 ,
MODE_EXAMINE = 1 < < 2 ,
MODE_NOCACHE = 1 < < 3 ,
MODE_NOPRIVACY = 1 < < 4 ,
2018-09-17 18:06:31 +00:00
MODE_SINGLETAB = 1 < < 5 ,
MODE_NOREVVEDRESOURCE = 1 < < 6
2017-08-29 20:35:28 +00:00
} 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 ,
2018-09-17 18:06:31 +00:00
RC_ERROR_PARSING ,
2017-11-11 00:34:47 +00:00
RC_ERROR_PROJECT ,
2017-09-07 21:41:08 +00:00
RC_ERROR_QUOTE ,
2017-11-11 00:34:47 +00:00
RC_ERROR_SEEK ,
2017-09-07 21:41:08 +00:00
RC_FOUND ,
RC_UNFOUND ,
RC_INVALID_REFERENCE ,
2017-11-11 00:34:47 +00:00
RC_INVALID_TEMPLATE ,
2018-04-01 20:58:53 +00:00
RC_PRIVATE_VIDEO ,
2017-09-07 21:41:08 +00:00
RC_NOOP ,
RC_RIP ,
RC_SUCCESS
} returns ;
typedef struct
{
void * Location ;
void * Ptr ;
char * ID ;
int Size ;
} arena ;
2018-02-28 01:04:06 +00:00
typedef struct
{
// Universal
2018-09-17 18:06:31 +00:00
char CacheDir [ 256 ] ;
enum8 ( editions ) Edition ;
enum8 ( log_levels ) LogLevel ;
enum8 ( modes ) Mode ;
int UpdateInterval ;
2018-02-28 01:04:06 +00:00
// Advisedly universal, although could be per-project
char * RootDir ; // Absolute
char * RootURL ;
char * CSSDir ; // Relative to Root{Dir,URL}
char * ImagesDir ; // Relative to Root{Dir,URL}
char * JSDir ; // Relative to Root{Dir,URL}
2018-09-17 18:06:31 +00:00
char * QueryString ;
2018-02-28 01:04:06 +00:00
// Per Project
char * ProjectID ;
char * Theme ;
char * DefaultMedium ;
// Per Project - Input
char * ProjectDir ; // Absolute
char * TemplatesDir ; // Absolute
2018-09-17 18:06:31 +00:00
char * TemplateSearchLocation ; // Relative to TemplatesDir ???
2018-02-28 01:04:06 +00:00
char * TemplatePlayerLocation ; // Relative to TemplatesDir ???
// Per Project - Output
char * BaseDir ; // Absolute
char * BaseURL ;
2018-09-17 18:06:31 +00:00
char * SearchLocation ; // Relative to Base{Dir,URL}
2018-02-28 01:04:06 +00:00
char * PlayerLocation ; // Relative to Base{Dir,URL}
char * PlayerURLPrefix ; /* NOTE(matt): This will become a full blown customisable output URL.
For now it simply replaces the ProjectID */
// Single Edition - Input
char SingleHMMLFilePath [ 256 ] ;
// Single Edition - Output
char * OutLocation ;
char * OutIntegratedLocation ;
} config ;
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-10-18 21:07:29 +00:00
typedef struct
{
buffer Buffer ;
FILE * Handle ;
2018-02-21 21:50:23 +00:00
char Path [ 256 ] ; // NOTE(matt): Could this just be a char *?
2017-10-18 21:07:29 +00:00
int FileSize ;
} file_buffer ;
2018-09-17 18:06:31 +00:00
char * AssetTypeNames [ ] =
{
" Generic " ,
" CSS " ,
" Image " ,
" JavaScript "
} ;
enum
{
ASSET_GENERIC ,
ASSET_CSS ,
ASSET_IMG ,
ASSET_JS ,
ASSET_TYPE_COUNT
} asset_types ;
typedef struct asset
{
int32_t Hash ;
enum8 ( asset_types ) Type ;
char Filename [ MAX_ASSET_FILENAME_LENGTH + 1 ] ;
int32_t FilenameAt : 29 ;
int32_t Known : 1 ;
int32_t OffsetLandmarks : 1 ;
int32_t DeferredUpdate : 1 ;
uint32_t SearchLandmarkCapacity ;
uint32_t SearchLandmarkCount ;
uint32_t * SearchLandmark ;
uint32_t PlayerLandmarkCapacity ;
uint32_t PlayerLandmarkCount ;
uint32_t * PlayerLandmark ;
} asset ;
asset BuiltinAssets [ ] =
{
{ 0 , ASSET_CSS , " cinera.css " } ,
{ 0 , ASSET_CSS } , // NOTE(matt): .Filename set by InitBuiltinAssets()
{ 0 , ASSET_CSS , " cinera_topics.css " } ,
{ 0 , ASSET_IMG , " cinera_icon_filter.png " } ,
{ 0 , ASSET_JS , " cinera_search.js " } ,
{ 0 , ASSET_JS , " cinera_player_pre.js " } ,
{ 0 , ASSET_JS , " cinera_player_post.js " } ,
} ;
enum
{
ASSET_CSS_CINERA ,
ASSET_CSS_THEME ,
ASSET_CSS_TOPICS ,
ASSET_IMG_FILTER ,
ASSET_JS_SEARCH ,
ASSET_JS_PLAYER_PRE ,
ASSET_JS_PLAYER_POST ,
BUILTIN_ASSETS_COUNT ,
} builtin_assets ;
typedef struct
{
int Count ;
int Capacity ;
asset * Asset ;
} assets ;
enum
{
WT_HMML ,
WT_ASSET
} watch_types ;
typedef struct
{
int Descriptor ;
enum8 ( watch_types ) Type ;
char Path [ MAX_ROOT_DIR_LENGTH + 1 + MAX_RELATIVE_ASSET_LOCATION_LENGTH ] ;
} watch_handle ;
typedef struct
{
int Count ;
int Capacity ;
watch_handle * Handle ;
} watch_handles ;
// DBVersion 1
typedef struct { unsigned int DBVersion ; version AppVersion ; version HMMLVersion ; unsigned int EntryCount ; } db_header1 ;
typedef struct { int Size ; char BaseFilename [ 32 ] ; } db_entry1 ;
typedef struct { file_buffer File ; file_buffer Metadata ; db_header1 Header ; db_entry1 Entry ; } database1 ;
//
// DBVersion 2
typedef struct { unsigned int DBVersion ; version AppVersion ; version HMMLVersion ; unsigned int EntryCount ; char SearchLocation [ 32 ] ; char PlayerLocation [ 32 ] ; } db_header2 ;
typedef db_entry1 db_entry2 ;
typedef struct { file_buffer File ; file_buffer Metadata ; db_header2 Header ; db_entry2 Entry ; } database2 ;
//
2018-02-21 21:50:23 +00:00
// TODO(matt): Increment CINERA_DB_VERSION!
typedef struct
{
2018-09-17 18:06:31 +00:00
unsigned int CurrentDBVersion ;
2018-02-21 21:50:23 +00:00
version CurrentAppVersion ;
version CurrentHMMLVersion ;
unsigned int InitialDBVersion ;
version InitialAppVersion ;
version InitialHMMLVersion ;
unsigned short int EntryCount ;
char ProjectID [ MAX_PROJECT_ID_LENGTH + 1 ] ;
char ProjectName [ MAX_PROJECT_NAME_LENGTH + 1 ] ;
char BaseURL [ MAX_BASE_URL_LENGTH + 1 ] ;
2018-09-17 18:06:31 +00:00
char SearchLocation [ MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 ] ;
2018-02-21 21:50:23 +00:00
char PlayerLocation [ MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 ] ;
2018-09-17 18:06:31 +00:00
char PlayerURLPrefix [ MAX_PLAYER_URL_PREFIX_LENGTH + 1 ] ;
} db_header3 ;
2018-02-21 21:50:23 +00:00
typedef struct
{
unsigned int PrevStart , NextStart ;
unsigned short int PrevEnd , NextEnd ;
2018-09-17 18:06:31 +00:00
} link_insertion_offsets ; // NOTE(matt): PrevStart is Absolute (or relative to start of file), the others are Relative to PrevStart
2018-02-21 21:50:23 +00:00
typedef struct
{
link_insertion_offsets LinkOffsets ;
unsigned short int Size ;
char BaseFilename [ MAX_BASE_FILENAME_LENGTH + 1 ] ;
char Title [ MAX_TITLE_LENGTH + 1 ] ;
2018-09-17 18:06:31 +00:00
} db_entry3 ;
typedef struct
{
file_buffer File ;
file_buffer Metadata ;
db_header3 Header ;
db_entry3 Entry ;
} database3 ;
# pragma pack(push, 1)
typedef struct
{
uint32_t HexSignature ; // 'CNRA'
uint32_t CurrentDBVersion ;
version CurrentAppVersion ;
version CurrentHMMLVersion ;
uint32_t InitialDBVersion ;
version InitialAppVersion ;
version InitialHMMLVersion ;
uint32_t BlockCount ;
} db_header4 ;
typedef struct
{
uint32_t BlockID ; // 'NTRY'
uint16_t Count ;
char ProjectID [ MAX_PROJECT_ID_LENGTH + 1 ] ;
char ProjectName [ MAX_PROJECT_NAME_LENGTH + 1 ] ;
char BaseURL [ MAX_BASE_URL_LENGTH + 1 ] ;
char SearchLocation [ MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 ] ;
char PlayerLocation [ MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 ] ;
char PlayerURLPrefix [ MAX_PLAYER_URL_PREFIX_LENGTH + 1 ] ; // TODO(matt): Replace this with the OutputPath, when we add that
} db_header_entries4 ;
typedef db_entry3 db_entry4 ;
typedef struct
{
uint32_t BlockID ; // 'ASET'
uint16_t Count ;
char RootDir [ MAX_ROOT_DIR_LENGTH + 1 ] ;
char RootURL [ MAX_ROOT_URL_LENGTH + 1 ] ;
char CSSDir [ MAX_RELATIVE_ASSET_LOCATION_LENGTH + 1 ] ;
char ImagesDir [ MAX_RELATIVE_ASSET_LOCATION_LENGTH + 1 ] ;
char JSDir [ MAX_RELATIVE_ASSET_LOCATION_LENGTH + 1 ] ;
} db_header_assets4 ;
typedef struct
{
int32_t Hash ;
uint32_t LandmarkCount ;
enum8 ( asset_types ) Type ;
char Filename [ MAX_ASSET_FILENAME_LENGTH + 1 ] ;
} db_asset4 ;
typedef struct
{
int32_t EntryIndex ;
uint32_t Position ;
} db_landmark4 ;
2018-02-21 21:50:23 +00:00
typedef struct
{
file_buffer File ;
file_buffer Metadata ;
2018-09-17 18:06:31 +00:00
db_header4 Header ;
db_header_entries4 EntriesHeader ;
db_entry4 Entry ;
db_header_assets4 AssetsHeader ;
db_asset4 Asset ;
db_landmark4 Landmark ;
} database4 ;
# pragma pack(pop)
# define CINERA_DB_VERSION 4
# define db_header db_header4
# define db_header_entries db_header_entries4
# define db_entry db_entry4
# define db_header_assets db_header_assets4
# define db_asset db_asset4
# define db_landmark db_landmark4
# define database database4
2018-02-21 21:50:23 +00:00
// TODO(matt): Increment CINERA_DB_VERSION!
2018-09-17 18:06:31 +00:00
// NOTE(matt): Globals
arena MemoryArena ;
config Config ;
assets Assets ;
int inotifyInstance ;
watch_handles WatchHandles ;
database DB ;
time_t LastPrivacyCheck ;
time_t LastQuoteFetch ;
//
enum
{
PAGE_TYPE_SEARCH = - 1 ,
} page_type_indices ;
2018-01-03 22:16:20 +00:00
typedef struct
{
2018-09-17 18:06:31 +00:00
buffer IncludesSearch ;
buffer SearchEntry ;
buffer Search ; // NOTE(matt): This buffer is malloc'd separately, rather than claimed from the memory_arena
2018-01-03 22:16:20 +00:00
buffer IncludesPlayer ;
buffer Menus ;
buffer Player ;
buffer ScriptPlayer ;
2018-02-23 23:36:42 +00:00
char Custom0 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom1 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom2 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom3 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom4 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom5 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom6 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom7 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom8 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom9 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom10 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom11 [ MAX_CUSTOM_SNIPPET_SHORT_LENGTH + 1 ] ;
char Custom12 [ MAX_CUSTOM_SNIPPET_LONG_LENGTH + 1 ] ;
char Custom13 [ MAX_CUSTOM_SNIPPET_LONG_LENGTH + 1 ] ;
char Custom14 [ MAX_CUSTOM_SNIPPET_LONG_LENGTH + 1 ] ;
char Custom15 [ MAX_CUSTOM_SNIPPET_LONG_LENGTH + 1 ] ;
2018-06-12 19:03:12 +00:00
char ProjectID [ MAX_PROJECT_ID_LENGTH + 1 ] ;
2018-02-21 21:50:23 +00:00
char ProjectName [ MAX_PROJECT_NAME_LENGTH + 1 ] ;
2018-06-12 19:03:12 +00:00
char Theme [ MAX_PROJECT_NAME_LENGTH + 1 ] ;
2018-02-21 21:50:23 +00:00
char Title [ MAX_TITLE_LENGTH + 1 ] ;
2018-09-17 18:06:31 +00:00
char URLSearch [ MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 ] ;
2018-02-21 21:50:23 +00:00
char URLPlayer [ MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 ] ;
2018-01-03 22:16:20 +00:00
char VideoID [ 16 ] ;
2018-09-19 19:50:21 +00:00
char VODPlatform [ 16 ] ;
2018-01-03 22:16:20 +00:00
} buffers ;
2017-09-07 21:41:08 +00:00
enum
{
2018-02-23 23:36:42 +00:00
// Contents and Player Pages Mandatory
TAG_INCLUDES ,
// Contents Page Mandatory
2018-09-17 18:06:31 +00:00
TAG_SEARCH ,
2017-09-07 21:41:08 +00:00
2018-02-23 23:36:42 +00:00
// Player Page Mandatory
2017-09-07 21:41:08 +00:00
TAG_MENUS ,
TAG_PLAYER ,
TAG_SCRIPT ,
2018-02-23 23:36:42 +00:00
// Player Page Optional
TAG_CUSTOM0 ,
TAG_CUSTOM1 ,
TAG_CUSTOM2 ,
TAG_CUSTOM3 ,
TAG_CUSTOM4 ,
TAG_CUSTOM5 ,
TAG_CUSTOM6 ,
TAG_CUSTOM7 ,
TAG_CUSTOM8 ,
TAG_CUSTOM9 ,
TAG_CUSTOM10 ,
TAG_CUSTOM11 ,
TAG_CUSTOM12 ,
TAG_CUSTOM13 ,
TAG_CUSTOM14 ,
TAG_CUSTOM15 ,
2017-12-12 23:24:10 +00:00
TAG_TITLE ,
TAG_VIDEO_ID ,
2018-09-19 19:50:21 +00:00
TAG_VOD_PLATFORM ,
2018-02-23 23:36:42 +00:00
// Anywhere Optional
2018-09-17 18:06:31 +00:00
TAG_ASSET ,
TAG_CSS ,
TAG_IMAGE ,
TAG_JS ,
2018-02-23 23:36:42 +00:00
TAG_PROJECT ,
2018-06-12 19:03:12 +00:00
TAG_PROJECT_ID ,
2018-09-19 19:50:21 +00:00
TAG_SEARCH_URL ,
2018-06-12 19:03:12 +00:00
TAG_THEME ,
2018-02-23 23:36:42 +00:00
TAG_URL ,
2018-09-17 18:06:31 +00:00
TEMPLATE_TAG_COUNT ,
} template_tag_codes ;
char * TemplateTags [ ] = {
" __CINERA_INCLUDES__ " ,
" __CINERA_SEARCH__ " ,
" __CINERA_MENUS__ " ,
" __CINERA_PLAYER__ " ,
" __CINERA_SCRIPT__ " ,
" __CINERA_CUSTOM0__ " ,
" __CINERA_CUSTOM1__ " ,
" __CINERA_CUSTOM2__ " ,
" __CINERA_CUSTOM3__ " ,
" __CINERA_CUSTOM4__ " ,
" __CINERA_CUSTOM5__ " ,
" __CINERA_CUSTOM6__ " ,
" __CINERA_CUSTOM7__ " ,
" __CINERA_CUSTOM8__ " ,
" __CINERA_CUSTOM9__ " ,
" __CINERA_CUSTOM10__ " ,
" __CINERA_CUSTOM11__ " ,
" __CINERA_CUSTOM12__ " ,
" __CINERA_CUSTOM13__ " ,
" __CINERA_CUSTOM14__ " ,
" __CINERA_CUSTOM15__ " ,
" __CINERA_TITLE__ " ,
" __CINERA_VIDEO_ID__ " ,
2018-09-19 19:50:21 +00:00
" __CINERA_VOD_PLATFORM__ " ,
2018-09-17 18:06:31 +00:00
" __CINERA_ASSET__ " ,
" __CINERA_CSS__ " ,
" __CINERA_IMAGE__ " ,
" __CINERA_JS__ " ,
" __CINERA_PROJECT__ " ,
" __CINERA_PROJECT_ID__ " ,
2018-09-19 19:50:21 +00:00
" __CINERA_SEARCH_URL__ " ,
2018-09-17 18:06:31 +00:00
" __CINERA_THEME__ " ,
" __CINERA_URL__ " ,
2017-09-07 21:41:08 +00:00
} ;
typedef struct
{
int Offset ;
2018-09-17 18:06:31 +00:00
uint32_t AssetIndex ;
2018-09-19 19:50:21 +00:00
enum8 ( template_tag_codes ) TagCode ;
2017-09-07 21:41:08 +00:00
} tag_offset ;
typedef struct
{
int Validity ; // NOTE(matt): Bitmask describing which page the template is valid for, i.e. contents and / or player page
2018-09-19 19:50:21 +00:00
int TagCapacity ;
2017-09-07 21:41:08 +00:00
int TagCount ;
2018-09-19 19:50:21 +00:00
tag_offset * Tags ;
2017-10-18 21:07:29 +00:00
} template_metadata ;
typedef struct
{
2018-09-19 19:50:21 +00:00
file_buffer File ;
2017-10-18 21:07:29 +00:00
template_metadata Metadata ;
2017-09-07 21:41:08 +00:00
} template ;
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 ;
2018-09-19 19:50:21 +00:00
# define MAX_REF_IDENTIFIER_COUNT 64
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 ] ;
2018-09-19 19:50:21 +00:00
identifier Identifier [ MAX_REF_IDENTIFIER_COUNT ] ;
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 ;
2018-09-17 18:06:31 +00:00
char * SupportIcons [ ] =
{
" cinera_sprite_patreon.png " ,
" cinera_sprite_sendowl.png " ,
} ;
typedef enum
{
ICON_PATREON = BUILTIN_ASSETS_COUNT ,
ICON_SENDOWL ,
SUPPORT_ICON_COUNT ,
} support_icons ;
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
{
2018-09-17 18:06:31 +00:00
char * Username ;
char * CreditedName ;
char * HomepageURL ;
enum8 ( support_icons ) SupportIconIndex ;
char * SupportURL ;
2017-08-18 22:46:58 +00:00
} credential_info ;
credential_info Credentials [ ] =
2017-05-25 20:28:52 +00:00
{
2018-09-17 18:06:31 +00:00
{ " a_waterman " , " Andrew Waterman " , " https://www.linkedin.com/in/andrew-waterman-76805788 " } ,
{ " y_lee " , " Yunsup Lee " , " https://www.linkedin.com/in/yunsup-lee-385b692b/ " } ,
{ " AndrewJDR " , " Andrew Johnson " } ,
{ " AsafGartner " , " Asaf Gartner " } ,
{ " BretHudson " , " Bret Hudson " , " http://www.brethudson.com/ " , ICON_PATREON , " https://www.patreon.com/indieFunction " } ,
{ " ChronalDragon " , " Andrew Chronister " , " http://chronal.net/ " } ,
{ " Kelimion " , " Jeroen van Rijn " , " https://handmade.network/home " } ,
{ " Mannilie " , " Emmanuel Vaccaro " , " http://emmanuelvaccaro.com/ " } ,
{ " Miblo " , " Matt Mascarenhas " , " https://miblodelcarpio.co.uk/ " , ICON_SENDOWL , " https://miblodelcarpio.co.uk/cinera#pledge " } ,
{ " Mr4thDimention " , " Allen Webster " , " http://www.4coder.net/ " } ,
{ " Pseudonym73 " , " Andrew Bromage " , " https://twitter.com/deguerre " } ,
{ " Quel_Solaar " , " Eskil Steenberg " , " http://quelsolaar.com/ " } ,
{ " ZedZull " , " Jay Waggle " } ,
{ " abnercoimbre " , " Abner Coimbre " , " https://handmade.network/m/abnercoimbre " } ,
{ " brianwill " , " Brian Will " , " http://brianwill.net/blog/ " } ,
{ " cbloom " , " Charles Bloom " , " http://cbloomrants.blogspot.co.uk/ " } ,
{ " cmuratori " , " Casey Muratori " , " https://handmadehero.org " , ICON_SENDOWL , " https://handmadehero.org/fund " } ,
{ " csnover " , " Colin Snover " , " https://zetafleet.com/ " } ,
{ " debiatan " , " Miguel Lechón " , " http://blog.debiatan.net/ " } ,
{ " dspecht " , " Dustin Specht " } ,
{ " effect0r " , " Cory Henderlite " } ,
{ " ffsjs " , " ffsjs " } ,
{ " fierydrake " , " Mike Tunnicliffe " } ,
{ " garlandobloom " , " Matthew VanDevander " , " https://lowtideproductions.com/ " , ICON_PATREON , " https://www.patreon.com/mv " } ,
{ " ikerms " , " Iker Murga " } ,
{ " insofaras " , " Alex Baines " , " https://abaines.me.uk/ " } ,
{ " jacebennett " , " Jace Bennett " } ,
{ " jon " , " Jonathan Blow " , " http://the-witness.net/news/ " } ,
{ " jpike " , " Jacob Pike " } ,
{ " martincohen " , " Martin Cohen " , " http://blog.coh.io/ " } ,
{ " miotatsu " , " Mio Iwakura " , " http://riscy.tv/ " , ICON_PATREON , " https://patreon.com/miotatsu " } ,
{ " nothings " , " Sean Barrett " , " https://nothings.org/ " } ,
{ " pervognsen " , " Per Vognsen " , " https://github.com/pervognsen/bitwise/ " } ,
{ " philipbuuck " , " Philip Buuck " , " http://philipbuuck.com/ " } ,
{ " powerc9000 " , " Clay Murray " , " http://claymurray.website/ " } ,
{ " rygorous " , " Fabian Giesen " , " https://fgiesen.wordpress.com/ " } ,
{ " schme " , " Kasper Sauramo " } ,
{ " sssmcgrath " , " Shawn McGrath " , " http://www.dyadgame.com/ " } ,
{ " thehappiecat " , " Anne " , " https://www.youtube.com/c/TheHappieCat " , ICON_PATREON , " https://www.patreon.com/thehappiecat " } ,
{ " theinternetftw " , " Ben Craddock " } ,
{ " wheatdog " , " Tim Liou " , " http://stringbulbs.com/ " } ,
{ " williamchyr " , " William Chyr " , " http://williamchyr.com/ " } ,
{ " wonchun " , " Won Chun " , " https://twitter.com/won3d " } ,
2017-08-18 22:46:58 +00:00
} ;
typedef struct
{
char * Medium ;
char * Icon ;
char * WrittenName ;
} category_medium ;
category_medium CategoryMedium [ ] =
{
// medium icon written name
2017-11-28 23:02:00 +00:00
{ " admin " , " 🗹 " , " Administrivia " } ,
2017-12-03 00:55:01 +00:00
{ " afk " , " … " , " Away from Keyboard " } ,
2018-09-17 18:06:31 +00:00
{ " authored " , " 🗪 " , " Chat Comment " } ,
2017-08-18 22:46:58 +00:00
{ " blackboard " , " 🖌 " , " Blackboard " } ,
2018-06-03 14:22:06 +00:00
{ " drawing " , " 🎨 " , " Drawing " } ,
2017-08-18 22:46:58 +00:00
{ " experience " , " 🍷 " , " Experience " } ,
2017-11-28 23:02:00 +00:00
{ " hat " , " 🎩 " , " Hat " } ,
{ " multimedia " , " 🎬 " , " Media Clip " } ,
2017-08-18 22:46:58 +00:00
{ " owl " , " 🦉 " , " Owl of Shame " } ,
2018-09-17 18:06:31 +00:00
{ " programming " , " 🖮 " , " Programming " } ,
2017-08-18 22:46:58 +00:00
{ " rant " , " 💢 " , " Rant " } ,
{ " research " , " 📖 " , " Research " } ,
2018-09-17 18:06:31 +00:00
{ " run " , " 🏃 " , " In-Game " } , // TODO(matt): Potentially make this written name configurable per project
2017-10-18 21:07:29 +00:00
{ " speech " , " 🗩 " , " Speech " } ,
2017-08-18 22:46:58 +00:00
{ " trivia " , " 🎲 " , " Trivia " } ,
2017-05-25 20:28:52 +00:00
} ;
2017-11-11 00:34:47 +00:00
enum
{
NS_CALENDRICAL ,
NS_LINEAR ,
NS_SEASONAL ,
} numbering_schemes ;
typedef struct
{
2018-09-17 18:06:31 +00:00
char * ProjectID ;
char * FullName ;
char * Unit ; // e.g. Day, Episode, Session
enum8 ( numbering_schemes ) NumberingScheme ; // numbering_schemes
char * Medium ;
char * AltURLPrefix ; // NOTE(matt): This currently just straight up replaces the ProjectID in the player
// pages' output directories
2017-11-11 00:34:47 +00:00
} project_info ;
project_info ProjectInfo [ ] =
{
2018-03-23 15:33:37 +00:00
{ " bitwise " , " Bitwise " , " Day " , NS_LINEAR , " programming " , " " } ,
2018-06-07 17:55:40 +00:00
{ " book " , " Book Club " , " Day " , NS_LINEAR , " research " , " " } ,
{ " coad " , " Computer Organization and Design " , " " , NS_LINEAR , " research " , " " } ,
{ " reader " , " RISC-V Reader " , " " , NS_LINEAR , " research " , " " } ,
{ " riscy " , " RISCY BUSINESS " , " Day " , NS_LINEAR , " programming " , " " } ,
{ " risc " , " RISCellaneous " , " " , NS_CALENDRICAL , " speech " , " " } ,
2017-12-07 01:15:13 +00:00
2017-12-07 21:07:36 +00:00
{ " chat " , " Handmade Chat " , " Day " , NS_LINEAR , " speech " , " " } ,
{ " code " , " Handmade Hero " , " Day " , NS_LINEAR , " programming " , " day " } ,
{ " intro-to-c " , " Intro to C on Windows " , " Day " , NS_LINEAR , " programming " , " day " } ,
{ " misc " , " Handmade Miscellany " , " " , NS_LINEAR , " admin " , " " } ,
{ " ray " , " Handmade Ray " , " Day " , NS_LINEAR , " programming " , " " } ,
2017-12-07 01:15:13 +00:00
2018-02-23 23:36:42 +00:00
{ " hmdshow " , " HandmadeDev Show " , " " , NS_SEASONAL , " speech " , " ep " } ,
{ " lecture " , " Abner Talks " , " " , NS_SEASONAL , " speech " , " " } ,
{ " stream " , " Abner Programs " , " " , NS_SEASONAL , " programming " , " " } ,
{ " special " , " Abner Show Special " , " " , NS_SEASONAL , " programming " , " " } ,
2017-12-07 01:15:13 +00:00
2017-12-07 21:07:36 +00:00
{ " obbg " , " Open Block Building Game " , " Episode " , NS_LINEAR , " programming " , " " } ,
2017-12-07 01:15:13 +00:00
2017-12-07 21:07:36 +00:00
{ " sysadmin " , " SysAdmin " , " Session " , NS_LINEAR , " admin " , " " } ,
2017-11-11 00:34:47 +00:00
} ;
2018-09-17 18:06:31 +00:00
char * ColourStrings [ ] =
{
" \ e[0m " ,
" \ e[0;33m " ,
" \ e[0;34m " ,
" \ e[0;35m " , " \ e[0;35m " ,
" \ e[1;30m " , " \ e[1;30m " ,
" \ e[1;31m " ,
" \ e[1;32m " , " \ e[1;32m " ,
" \ e[1;33m " ,
} ;
enum
{
CS_END ,
CS_WARNING ,
CS_PRIVATE ,
CS_ONGOING , CS_PURPLE ,
CS_COMMENT , CS_DELETION ,
CS_ERROR ,
CS_ADDITION , CS_SUCCESS ,
CS_REINSERTION ,
} colour_strings ;
typedef struct
{
char * Name ;
enum8 ( colour_strings ) Colour ;
} edit_type ;
enum
{
EDIT_INSERTION ,
EDIT_APPEND ,
EDIT_REINSERTION ,
EDIT_DELETION ,
EDIT_ADDITION ,
} edit_types ;
edit_type EditTypes [ ] =
{
{ " Inserted " , CS_ADDITION } ,
{ " Appended " , CS_ADDITION } ,
{ " Reinserted " , CS_REINSERTION } ,
{ " Deleted " , CS_DELETION } ,
{ " Added " , CS_ADDITION }
} ;
2017-03-25 02:07:55 +00:00
2018-04-22 20:57:31 +00:00
void
Clear ( char * String , int Size )
2017-05-21 06:35:16 +00:00
{
2018-04-22 20:57:31 +00:00
for ( int i = 0 ; i < Size ; + + i )
{
String [ i ] = 0 ;
}
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
}
2018-04-22 20:57:31 +00:00
# define CopyString(Dest, DestSize, Format, ...) CopyString_(__LINE__, (Dest), (DestSize), (Format), ##__VA_ARGS__)
__attribute__ ( ( format ( printf , 4 , 5 ) ) )
int
CopyString_ ( int LineNumber , char Dest [ ] , int DestSize , char * Format , . . . )
2018-02-21 21:50:23 +00:00
{
2018-04-22 20:57:31 +00:00
int Length = 0 ;
va_list Args ;
va_start ( Args , Format ) ;
Length = vsnprintf ( Dest , DestSize , Format , Args ) ;
if ( Length > = DestSize )
2018-02-21 21:50:23 +00:00
{
2018-04-22 20:57:31 +00:00
printf ( " CopyString() call on line %d has been passed a buffer too small (%d bytes) to contain null-terminated %d(+1)-character string \n " , LineNumber , DestSize , Length ) ;
__asm__ ( " int3 " ) ;
2018-02-21 21:50:23 +00:00
}
2018-04-22 20:57:31 +00:00
va_end ( Args ) ;
return Length ;
2018-02-21 21:50:23 +00:00
}
2018-04-22 20:57:31 +00:00
# define CopyStringNoFormat(Dest, DestSize, String) CopyStringNoFormat_(__LINE__, Dest, DestSize, String)
2017-06-13 22:13:03 +00:00
int
2018-04-22 20:57:31 +00:00
CopyStringNoFormat_ ( int LineNumber , char * Dest , int DestSize , char * String )
2017-06-07 23:47:47 +00:00
{
2017-06-13 22:13:03 +00:00
int Length = 0 ;
2018-04-22 20:57:31 +00:00
char * Start = String ;
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
}
2018-04-22 20:57:31 +00:00
if ( Length > = DestSize )
{
printf ( " CopyStringNoFormat() call on line %d has been passed a buffer too small (%d bytes) to contain null-terminated %d(+1)-character string: \n "
" %s \n " , LineNumber , DestSize , Length , Start ) ;
__asm__ ( " int3 " ) ;
}
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
}
2018-04-22 20:57:31 +00:00
# define ClearCopyStringNoFormat(Dest, DestSize, String) ClearCopyStringNoFormat_(__LINE__, Dest, DestSize, String)
2018-02-21 21:50:23 +00:00
int
2018-04-22 20:57:31 +00:00
ClearCopyStringNoFormat_ ( int LineNumber , char * Dest , int DestSize , char * String )
2018-02-21 21:50:23 +00:00
{
Clear ( Dest , DestSize ) ;
2018-04-22 20:57:31 +00:00
int Length = 0 ;
char * Start = String ;
while ( * String )
{
* Dest + + = * String + + ;
+ + Length ;
}
if ( Length > = DestSize )
{
printf ( " ClearCopyStringNoFormat() call on line %d has been passed a buffer too small (%d bytes) to contain null-terminated %d(+1)-character string: \n "
" %s \n " , LineNumber , DestSize , Length , Start ) ;
__asm__ ( " int3 " ) ;
}
* Dest = ' \0 ' ;
return Length ;
2018-02-21 21:50:23 +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
2018-04-22 20:57:31 +00:00
# define CopyStringNoFormatT(Dest, DestSize, String, Terminator) CopyStringNoFormatT_(__LINE__, Dest, DestSize, String, Terminator)
2017-08-18 22:46:58 +00:00
int
2018-04-22 20:57:31 +00:00
CopyStringNoFormatT_ ( int LineNumber , char * Dest , int DestSize , char * String , char Terminator )
2017-08-18 22:46:58 +00:00
{
int Length = 0 ;
2018-04-22 20:57:31 +00:00
char * Start = String ;
2017-08-18 22:46:58 +00:00
while ( * String ! = Terminator )
{
* Dest + + = * String + + ;
+ + Length ;
}
2018-04-22 20:57:31 +00:00
if ( Length > = DestSize )
{
printf ( " CopyStringNoFormatT() call on line %d has been passed a buffer too small (%d bytes) to contain %c-terminated %d(+1)-character string: \n "
" %.*s \n " , LineNumber , DestSize , Terminator = = 0 ? ' 0 ' : Terminator , Length , Length , Start ) ;
__asm__ ( " int3 " ) ;
}
2017-08-18 22:46:58 +00:00
* Dest = ' \0 ' ;
return Length ;
}
2018-04-22 20:57:31 +00:00
# define CopyStringToBuffer(Dest, Format, ...) CopyStringToBuffer_(__LINE__, Dest, Format, ##__VA_ARGS__)
__attribute__ ( ( format ( printf , 3 , 4 ) ) )
2017-03-16 00:56:58 +00:00
void
2018-04-22 20:57:31 +00:00
CopyStringToBuffer_ ( int LineNumber , 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 ) ;
2018-04-22 20:57:31 +00:00
if ( Length + ( Dest - > Ptr - Dest - > Location ) > = Dest - > Size )
2017-05-21 06:35:16 +00:00
{
2018-04-22 20:57:31 +00:00
fprintf ( stderr , " CopyStringToBuffer(%s) call on line %d cannot accommodate null-terminated %d(+1)-character string: \n "
" %s \n " , Dest - > ID , LineNumber , Length , Format ) ;
__asm__ ( " int3 " ) ;
2017-05-21 06:35:16 +00:00
}
2017-03-23 00:34:59 +00:00
Dest - > Ptr + = Length ;
2017-03-16 00:56:58 +00:00
}
2018-04-22 20:57:31 +00:00
# define CopyStringToBufferNoFormat(Dest, String) CopyStringToBufferNoFormat_(__LINE__, Dest, String)
2018-02-23 23:36:42 +00:00
void
2018-04-22 20:57:31 +00:00
CopyStringToBufferNoFormat_ ( int LineNumber , buffer * Dest , char * String )
2018-02-23 23:36:42 +00:00
{
2018-04-22 20:57:31 +00:00
char * Start = String ;
2018-02-23 23:36:42 +00:00
while ( * String )
{
* Dest - > Ptr + + = * String + + ;
}
2018-04-22 20:57:31 +00:00
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
fprintf ( stderr , " CopyStringToBufferNoFormat(%s) call on line %d cannot accommodate %d-character string: \n "
" %s \n " , Dest - > ID , LineNumber , StringLength ( Start ) , Start ) ;
__asm__ ( " int3 " ) ;
}
2018-02-23 23:36:42 +00:00
* Dest - > Ptr = ' \0 ' ;
}
2018-09-17 18:06:31 +00:00
# define CopyStringToBufferNoFormatL(Dest, Length, String) CopyStringToBufferNoFormatL_(__LINE__, Dest, Length, String)
void
CopyStringToBufferNoFormatL_ ( int LineNumber , buffer * Dest , int Length , char * String )
{
char * Start = String ;
for ( int i = 0 ; i < Length ; + + i )
{
* Dest - > Ptr + + = * String + + ;
}
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
fprintf ( stderr , " CopyStringToBufferNoFormat(%s) call on line %d cannot accommodate %d-character string: \n "
" %s \n " , Dest - > ID , LineNumber , StringLength ( Start ) , Start ) ;
__asm__ ( " int3 " ) ;
}
* Dest - > Ptr = ' \0 ' ;
}
2018-04-22 20:57:31 +00:00
# define CopyStringToBufferHTMLSafe(Dest, String) CopyStringToBufferHTMLSafe_(__LINE__, Dest, String)
2017-06-07 23:47:47 +00:00
void
2018-04-22 20:57:31 +00:00
CopyStringToBufferHTMLSafe_ ( int LineNumber , buffer * Dest , char * String )
2017-06-07 23:47:47 +00:00
{
2018-04-22 20:57:31 +00:00
char * Start = String ;
int Length = StringLength ( String ) ;
2017-06-07 23:47:47 +00:00
while ( * String )
{
switch ( * String )
{
2018-04-22 20:57:31 +00:00
case ' < ' : * Dest - > Ptr + + = ' & ' ; * Dest - > Ptr + + = ' l ' ; * Dest - > Ptr + + = ' t ' ; * Dest - > Ptr + + = ' ; ' ; Length + = 3 ; break ;
case ' > ' : * Dest - > Ptr + + = ' & ' ; * Dest - > Ptr + + = ' g ' ; * Dest - > Ptr + + = ' t ' ; * Dest - > Ptr + + = ' ; ' ; Length + = 3 ; break ;
case ' & ' : * Dest - > Ptr + + = ' & ' ; * Dest - > Ptr + + = ' a ' ; * Dest - > Ptr + + = ' m ' ; * Dest - > Ptr + + = ' p ' ; * Dest - > Ptr + + = ' ; ' ; Length + = 4 ; break ;
case ' \" ' : * Dest - > Ptr + + = ' & ' ; * Dest - > Ptr + + = ' q ' ; * Dest - > Ptr + + = ' u ' ; * Dest - > Ptr + + = ' o ' ; * Dest - > Ptr + + = ' t ' ; * Dest - > Ptr + + = ' ; ' ; Length + = 5 ; break ;
case ' \' ' : * Dest - > Ptr + + = ' & ' ; * Dest - > Ptr + + = ' # ' ; * Dest - > Ptr + + = ' 3 ' ; * Dest - > Ptr + + = ' 9 ' ; * Dest - > Ptr + + = ' ; ' ; Length + = 4 ; break ;
2018-04-17 23:05:14 +00:00
default : * Dest - > Ptr + + = * String ; break ;
}
+ + String ;
}
2018-04-22 20:57:31 +00:00
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
fprintf ( stderr , " CopyStringToBufferHTMLSafe(%s) call on line %d cannot accommodate %d(+1)-character HTML-sanitised string: \n "
" %s \n " , Dest - > ID , LineNumber , Length , Start ) ;
__asm__ ( " int3 " ) ;
}
* Dest - > Ptr = ' \0 ' ;
2018-04-17 23:05:14 +00:00
}
2018-04-22 20:57:31 +00:00
# define CopyStringToBufferHTMLSafeBreakingOnSlash(Dest, String) CopyStringToBufferHTMLSafeBreakingOnSlash_(__LINE__, Dest, String)
2018-04-17 23:05:14 +00:00
void
2018-04-22 20:57:31 +00:00
CopyStringToBufferHTMLSafeBreakingOnSlash_ ( int LineNumber , buffer * Dest , char * String )
2018-04-17 23:05:14 +00:00
{
2018-04-22 20:57:31 +00:00
char * Start = String ;
int Length = StringLength ( String ) ;
2018-04-17 23:05:14 +00:00
while ( * String )
{
switch ( * String )
{
2018-04-22 20:57:31 +00:00
case ' < ' : * Dest - > Ptr + + = ' & ' ; * Dest - > Ptr + + = ' l ' ; * Dest - > Ptr + + = ' t ' ; * Dest - > Ptr + + = ' ; ' ; Length + = 3 ; break ;
case ' > ' : * Dest - > Ptr + + = ' & ' ; * Dest - > Ptr + + = ' g ' ; * Dest - > Ptr + + = ' t ' ; * Dest - > Ptr + + = ' ; ' ; Length + = 3 ; break ;
case ' & ' : * Dest - > Ptr + + = ' & ' ; * Dest - > Ptr + + = ' a ' ; * Dest - > Ptr + + = ' m ' ; * Dest - > Ptr + + = ' p ' ; * Dest - > Ptr + + = ' ; ' ; Length + = 4 ; break ;
case ' \' ' : * Dest - > Ptr + + = ' & ' ; * Dest - > Ptr + + = ' # ' ; * Dest - > Ptr + + = ' 3 ' ; * Dest - > Ptr + + = ' 9 ' ; * Dest - > Ptr + + = ' ; ' ; Length + = 4 ; break ;
case ' \" ' : * Dest - > Ptr + + = ' & ' ; * Dest - > Ptr + + = ' q ' ; * Dest - > Ptr + + = ' u ' ; * Dest - > Ptr + + = ' o ' ; * Dest - > Ptr + + = ' t ' ; * Dest - > Ptr + + = ' ; ' ; Length + = 5 ; break ;
case ' / ' : * Dest - > Ptr + + = ' / ' ; * Dest - > Ptr + + = ' & ' ; * Dest - > Ptr + + = ' # ' ; * Dest - > Ptr + + = ' 8 ' ; * Dest - > Ptr + + = ' 2 ' ; * Dest - > Ptr + + = ' 0 ' ; * Dest - > Ptr + + = ' 3 ' ; * Dest - > Ptr + + = ' ; ' ; Length + = 7 ; break ;
2018-04-17 23:05:14 +00:00
default : * Dest - > Ptr + + = * String ; break ;
}
+ + String ;
2017-06-07 23:47:47 +00:00
}
2018-04-22 20:57:31 +00:00
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " CopyStringToBufferHTMLSafeBreakingOnSlash(%s) call on line %d cannot accommodate %d(+1)-character HTML-sanitised string: \n "
" %s \n " , Dest - > ID , LineNumber , Length , Start ) ;
__asm__ ( " int3 " ) ;
}
* Dest - > Ptr = ' \0 ' ;
}
# define CopyStringToBufferHTMLPercentEncoded(Dest, String) CopyStringToBufferHTMLPercentEncoded_(__LINE__, Dest, String)
void
CopyStringToBufferHTMLPercentEncoded_ ( int LineNumber , buffer * Dest , char * String )
{
char * Start = String ;
int Length = StringLength ( String ) ;
while ( * String )
{
switch ( * String )
{
case ' ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 2 ' ; * Dest - > Ptr + + = ' 0 ' ; Length + = 2 ; break ;
case ' \" ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 2 ' ; * Dest - > Ptr + + = ' 2 ' ; Length + = 2 ; break ;
case ' % ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 2 ' ; * Dest - > Ptr + + = ' 5 ' ; Length + = 2 ; break ;
case ' & ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 2 ' ; * Dest - > Ptr + + = ' 6 ' ; Length + = 2 ; break ;
case ' < ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 3 ' ; * Dest - > Ptr + + = ' C ' ; Length + = 2 ; break ;
case ' > ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 3 ' ; * Dest - > Ptr + + = ' E ' ; Length + = 2 ; break ;
case ' ? ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 3 ' ; * Dest - > Ptr + + = ' F ' ; Length + = 2 ; break ;
case ' \\ ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 5 ' ; * Dest - > Ptr + + = ' C ' ; Length + = 2 ; break ;
case ' ^ ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 5 ' ; * Dest - > Ptr + + = ' E ' ; Length + = 2 ; break ;
case ' ` ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 6 ' ; * Dest - > Ptr + + = ' 0 ' ; Length + = 2 ; break ;
case ' { ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 7 ' ; * Dest - > Ptr + + = ' B ' ; Length + = 2 ; break ;
case ' | ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 7 ' ; * Dest - > Ptr + + = ' C ' ; Length + = 2 ; break ;
case ' } ' : * Dest - > Ptr + + = ' % ' ; * Dest - > Ptr + + = ' 7 ' ; * Dest - > Ptr + + = ' D ' ; Length + = 2 ; break ;
default : * Dest - > Ptr + + = * String ; break ;
}
+ + String ;
}
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
fprintf ( stderr , " CopyStringToBufferHTMLPercentEncodedL(%s) call on line %d cannot accommodate %d(+1)-character percent-encoded string: \n "
2018-04-22 20:57:31 +00:00
" %s \n " , Dest - > ID , LineNumber , Length , Start ) ;
__asm__ ( " int3 " ) ;
}
2018-09-17 18:06:31 +00:00
* Dest - > Ptr = ' \0 ' ;
2017-06-07 23:47:47 +00:00
}
2018-05-01 21:05:44 +00:00
# define CopyBuffer(Dest, Src) CopyBuffer_(__LINE__, Dest, Src)
2017-11-11 00:34:47 +00:00
void
2018-05-01 21:05:44 +00:00
CopyBuffer_ ( int LineNumber , buffer * Dest , buffer * Src )
2017-11-11 00:34:47 +00:00
{
2018-05-01 21:05:44 +00:00
Src - > Ptr = Src - > Location ;
while ( * Src - > Ptr )
2017-11-11 00:34:47 +00:00
{
2018-05-01 21:05:44 +00:00
* Dest - > Ptr + + = * Src - > Ptr + + ;
2017-11-11 00:34:47 +00:00
}
2018-04-22 20:57:31 +00:00
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
2018-05-01 21:05:44 +00:00
fprintf ( stderr , " CopyBuffer(%s) call on line %d cannot accommodate %d(+1)-character %s \n " , Dest - > ID , LineNumber , StringLength ( Src - > Location ) , Src - > ID ) ;
2018-04-22 20:57:31 +00:00
__asm__ ( " int3 " ) ;
}
2018-05-01 21:05:44 +00:00
* Dest - > Ptr = ' \0 ' ;
2018-04-22 20:57:31 +00:00
}
2018-05-01 21:05:44 +00:00
# define CopyBufferSized(Dest, Src, Size) CopyBufferSized_(__LINE__, Dest, Src, Size)
2018-04-22 20:57:31 +00:00
void
2018-05-01 21:05:44 +00:00
CopyBufferSized_ ( int LineNumber , buffer * Dest , buffer * Src , int Size )
2018-04-22 20:57:31 +00:00
{
2018-09-17 18:06:31 +00:00
// NOTE(matt): Similar to CopyBuffer(), just without null-terminating
2018-04-22 20:57:31 +00:00
Src - > Ptr = Src - > Location ;
2018-05-01 21:05:44 +00:00
while ( Src - > Ptr - Src - > Location < Size )
2018-04-22 20:57:31 +00:00
{
* Dest - > Ptr + + = * Src - > Ptr + + ;
}
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
2018-05-01 21:05:44 +00:00
fprintf ( stderr , " CopyBufferNoNull(%s) call on line %d cannot accommodate %d(+1)-character %s \n " , Dest - > ID , LineNumber , StringLength ( Src - > Location ) , Src - > ID ) ;
2018-04-22 20:57:31 +00:00
__asm__ ( " int3 " ) ;
}
2017-11-11 00:34: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 ;
}
2018-04-04 22:39:38 +00:00
int
StringsDifferCaseInsensitive ( char * A , char * B ) // NOTE(matt): Two null-terminated strings
{
while ( * A & & * B & &
( ( * A > = ' A ' & & * A < = ' Z ' ) ? * A + ( ' a ' - ' A ' ) : * A ) = =
( ( * B > = ' A ' & & * B < = ' Z ' ) ? * B + ( ' a ' - ' A ' ) : * B ) )
{
+ + A , + + B ;
}
return * A - * B ;
}
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
{
2018-05-15 19:45:03 +00:00
// TODO(matt): Make sure this can't crash upon reaching the end of B's buffer
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 ;
}
}
2018-09-17 18:06:31 +00:00
enum
{
C_SEEK_FORWARDS ,
C_SEEK_BACKWARDS
} seek_directions ;
enum
{
C_SEEK_START , // First character of string
C_SEEK_BEFORE , // Character before first character
C_SEEK_END , // Last character of string
C_SEEK_AFTER // Character after last character
} seek_positions ;
int
SeekBufferForString ( buffer * Buffer , char * String ,
enum8 ( seek_directions ) Direction ,
enum8 ( seek_positions ) Position )
{
// TODO(matt): Optimise? Some means of analysing the String to increment
// the pointer in bigger strides
// Perhaps count up runs of consecutive chars and seek for the char with
// the longest run, in strides of that run-length
char * InitialLocation = Buffer - > Ptr ;
if ( Direction = = C_SEEK_FORWARDS )
{
while ( Buffer - > Ptr - Buffer - > Location < Buffer - > Size - StringLength ( String )
& & StringsDifferT ( String , Buffer - > Ptr , 0 ) )
{
+ + Buffer - > Ptr ;
}
}
else
{
while ( Buffer - > Ptr > Buffer - > Location
& & StringsDifferT ( String , Buffer - > Ptr , 0 ) )
{
- - Buffer - > Ptr ;
}
}
if ( StringsDifferT ( String , Buffer - > Ptr , 0 ) )
{
Buffer - > Ptr = InitialLocation ;
return RC_UNFOUND ;
}
switch ( Position )
{
case C_SEEK_START :
break ;
case C_SEEK_BEFORE :
if ( Buffer - > Ptr > Buffer - > Location )
{
- - Buffer - > Ptr ;
break ;
}
else
{
return RC_ERROR_SEEK ; // Ptr remains at string start
}
case C_SEEK_END :
Buffer - > Ptr + = StringLength ( String ) - 1 ;
break ;
case C_SEEK_AFTER :
if ( Buffer - > Size > = Buffer - > Ptr - Buffer - > Location + StringLength ( String ) )
{
Buffer - > Ptr + = StringLength ( String ) ;
break ;
}
else
{
return RC_ERROR_SEEK ; // Ptr remains at string start
// NOTE(matt): Should it, however, be left at the end of the string?
}
}
return RC_SUCCESS ;
}
int
StripComponentFromPath ( char * Path )
{
char * Ptr = Path + StringLength ( Path ) - 1 ;
if ( Ptr < Path ) { return RC_ERROR_DIRECTORY ; }
while ( Ptr > Path & & * Ptr ! = ' / ' )
{
- - Ptr ;
}
* Ptr = ' \0 ' ;
return RC_SUCCESS ;
}
int ClaimBuffer ( buffer * Buffer , char * ID , int Size ) ;
void DeclaimBuffer ( buffer * Buffer ) ;
int
ResolvePath ( char * Path )
{
buffer B ;
ClaimBuffer ( & B , " ResolvedPath " , StringLength ( Path ) + 1 ) ;
CopyStringToBufferNoFormat ( & B , Path ) ;
B . Ptr = B . Location ;
while ( SeekBufferForString ( & B , " /../ " , C_SEEK_FORWARDS , C_SEEK_END ) = = RC_SUCCESS )
{
char * NextComponentHead = B . Ptr ;
int RemainingChars = StringLength ( NextComponentHead ) ;
- - B . Ptr ;
SeekBufferForString ( & B , " / " , C_SEEK_BACKWARDS , C_SEEK_BEFORE ) ;
SeekBufferForString ( & B , " / " , C_SEEK_BACKWARDS , C_SEEK_START ) ;
CopyStringToBufferNoFormat ( & B , NextComponentHead ) ;
Clear ( B . Ptr , B . Size - ( B . Ptr - B . Location ) ) ;
B . Ptr - = RemainingChars ;
}
CopyStringNoFormat ( Path , StringLength ( Path ) + 1 , B . Location ) ;
DeclaimBuffer ( & B ) ;
return RC_SUCCESS ;
}
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 Ancestors = 0 ;
while ( mkdir ( Path , 00755 ) = = - 1 )
{
2017-09-07 21:41:08 +00:00
if ( errno = = EACCES )
{
return RC_ERROR_DIRECTORY ;
}
2018-09-17 18:06:31 +00:00
if ( StripComponentFromPath ( Path ) = = RC_ERROR_DIRECTORY ) { return RC_ERROR_DIRECTORY ; }
2017-08-20 21:23:15 +00:00
+ + Ancestors ;
}
2018-10-05 19:57:09 +00:00
int i = StringLength ( Path ) ;
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
2017-10-18 21:07:29 +00:00
LogUsage ( buffer * Buffer )
2017-09-07 21:41:08 +00:00
{
2017-09-15 01:11:39 +00:00
# if DEBUG
2017-11-11 00:34:47 +00:00
char LogPath [ 256 ] ;
2017-10-18 21:07:29 +00:00
CopyString ( LogPath , " %s/%s " , Config . CacheDir , " buffers.log " ) ;
2017-09-07 21:41:08 +00:00
FILE * LogFile ;
if ( ! ( LogFile = fopen ( LogPath , " a+ " ) ) )
{
2018-01-03 22:16:20 +00:00
MakeDir ( Config . CacheDir ) ;
2017-09-07 21:41:08 +00:00
if ( ! ( LogFile = fopen ( LogPath , " a+ " ) ) )
{
perror ( " LogUsage " ) ;
return ;
}
}
fprintf ( LogFile , " %s,%ld,%d \n " ,
2017-10-18 21:07:29 +00:00
Buffer - > ID ,
Buffer - > Ptr - Buffer - > Location ,
Buffer - > Size ) ;
2017-09-07 21:41:08 +00:00
fclose ( LogFile ) ;
2017-09-15 01:11:39 +00:00
# endif
2017-09-07 21:41:08 +00:00
}
2017-10-18 21:07:29 +00:00
__attribute__ ( ( format ( printf , 2 , 3 ) ) )
2017-09-07 21:41:08 +00:00
void
2017-10-18 21:07:29 +00:00
LogError ( int LogLevel , char * Format , . . . )
2017-09-07 21:41:08 +00:00
{
if ( Config . LogLevel > = LogLevel )
{
2017-11-11 00:34:47 +00:00
char LogPath [ 256 ] ;
2018-04-22 20:57:31 +00:00
CopyString ( LogPath , sizeof ( LogPath ) , " %s/%s " , Config . CacheDir , " errors.log " ) ;
2017-09-07 21:41:08 +00:00
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
}
2018-09-19 19:50:21 +00:00
int
ReadFileIntoBuffer ( file_buffer * File , int BufferPadding )
{
if ( ! ( File - > Handle = fopen ( File - > Path , " r " ) ) ) // TODO(matt): Fuller error handling
{
return RC_ERROR_FILE ;
}
fseek ( File - > Handle , 0 , SEEK_END ) ;
File - > FileSize = ftell ( File - > Handle ) ;
File - > Buffer . Size = File - > FileSize + 1 + BufferPadding ; // NOTE(matt): +1 to accommodate a NULL terminator
fseek ( File - > Handle , 0 , SEEK_SET ) ;
// TODO(matt): Consider using the MemoryArena? Maybe have separate ReadFileIntoMemory() and ReadFileIntoArena()
if ( ! ( File - > Buffer . Location = malloc ( File - > Buffer . Size ) ) )
{
fclose ( File - > Handle ) ;
return RC_ERROR_MEMORY ;
}
File - > Buffer . Ptr = File - > Buffer . Location ;
fread ( File - > Buffer . Location , File - > FileSize , 1 , File - > Handle ) ;
File - > Buffer . Location [ File - > FileSize ] = ' \0 ' ;
fclose ( File - > Handle ) ;
File - > Buffer . ID = File - > Path ;
return RC_SUCCESS ;
}
2017-08-20 21:23:15 +00:00
void
FreeBuffer ( buffer * Buffer )
{
free ( Buffer - > Location ) ;
2018-04-22 20:57:31 +00:00
Buffer - > Location = 0 ;
2018-09-19 19:50:21 +00:00
Buffer - > Ptr = 0 ;
Buffer - > Size = 0 ;
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Freed %s \n " , Buffer - > ID ) ;
fclose ( MemLog ) ;
printf ( " Freed %s \n " , Buffer - > ID ) ;
# endif
2018-09-19 19:50:21 +00:00
Buffer - > ID = 0 ;
2017-08-20 21:23:15 +00:00
}
int
2017-10-18 21:07:29 +00:00
ClaimBuffer ( buffer * Buffer , char * ID , int Size )
2017-08-20 21:23:15 +00:00
{
2017-10-18 21:07:29 +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-10-18 21:07:29 +00:00
Buffer - > Location = ( char * ) MemoryArena . Ptr ;
2017-08-20 21:23:15 +00:00
Buffer - > Size = Size ;
Buffer - > ID = ID ;
2017-10-18 21:07:29 +00:00
MemoryArena . Ptr + = Buffer - > Size ;
2017-08-20 21:23:15 +00:00
* Buffer - > Location = ' \0 ' ;
Buffer - > Ptr = Buffer - > Location ;
# if DEBUG
2018-01-03 22:16:20 +00:00
float PercentageUsed = ( float ) ( MemoryArena . Ptr - MemoryArena . Location ) / MemoryArena . Size * 100 ;
2017-10-18 21:07:29 +00:00
printf ( " ClaimBuffer(%s): %d \n "
2018-01-03 22:16:20 +00:00
" Total ClaimedMemory: %ld (%.2f%%, leaving %ld free) \n \n " , Buffer - > ID , Buffer - > Size , MemoryArena . Ptr - MemoryArena . Location , PercentageUsed , MemoryArena . 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
}
void
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( buffer * Buffer )
2017-08-20 21:23:15 +00:00
{
* Buffer - > Location = ' \0 ' ;
2017-10-18 21:07:29 +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
2017-10-18 21:07:29 +00:00
printf ( " DeclaimBuffer(%s) \n "
2017-08-20 21:23:15 +00:00
" 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-10-18 21:07:29 +00:00
MemoryArena . Ptr - MemoryArena . Location ) ;
2017-08-20 21:23:15 +00:00
# endif
2017-10-18 21:07:29 +00:00
LogUsage ( Buffer ) ;
2018-03-06 20:40:12 +00:00
if ( PercentageUsed > = 95.0f )
{
2018-09-17 18:06:31 +00:00
// TODO(matt): Implement either dynamically growing buffers, or phoning home to miblodelcarpio@gmail.com
2018-03-06 20:40:12 +00:00
LogError ( LOG_ERROR , " %s used %.2f%% of its allotted memory \n " , Buffer - > ID , PercentageUsed ) ;
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sWarning%s: %s used %.2f%% of its allotted memory \n " ,
ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] ,
Buffer - > ID , PercentageUsed ) ;
2018-03-06 20:40:12 +00:00
}
else if ( PercentageUsed > = 80.0f )
2017-08-20 21:23:15 +00:00
{
2018-09-17 18:06:31 +00:00
// TODO(matt): Implement either dynamically growing buffers, or phoning home to miblodelcarpio@gmail.com
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " %s used %.2f%% of its allotted memory \n " , Buffer - > ID , PercentageUsed ) ;
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sWarning%s: %s used %.2f%% of its allotted memory \n " ,
ColourStrings [ CS_WARNING ] , ColourStrings [ CS_END ] ,
Buffer - > ID , PercentageUsed ) ;
2017-08-20 21:23:15 +00:00
}
2017-11-11 00:34:47 +00:00
Buffer - > Size = 0 ;
2017-08-20 21:23:15 +00:00
}
2018-04-17 23:05:14 +00:00
void
RewindBuffer ( buffer * Buffer )
2017-10-18 21:07:29 +00:00
{
# 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 ;
}
2018-01-03 22:16:20 +00:00
enum
{
2018-09-17 18:06:31 +00:00
TEMPLATE_SEARCH ,
2018-01-03 22:16:20 +00:00
TEMPLATE_PLAYER ,
TEMPLATE_BESPOKE
2018-09-19 19:50:21 +00:00
} template_types ;
2018-01-03 22:16:20 +00:00
2018-01-04 20:55:51 +00:00
char *
GetDirectoryPath ( char * Filepath )
{
char * Ptr = Filepath + StringLength ( Filepath ) - 1 ;
while ( Ptr > Filepath & & * Ptr ! = ' / ' )
{
- - Ptr ;
}
if ( Ptr = = Filepath )
{
* Ptr + + = ' . ' ;
}
* Ptr = ' \0 ' ;
return Filepath ;
}
char *
GetBaseFilename ( char * Filepath ,
char * Extension // Including the "."
// Pass 0 to retain the whole file path, only without its parent directories
)
{
char * BaseFilename = Filepath + StringLength ( Filepath ) - 1 ;
while ( BaseFilename > Filepath & & * BaseFilename ! = ' / ' )
{
- - BaseFilename ;
}
if ( * BaseFilename = = ' / ' )
{
+ + BaseFilename ;
}
BaseFilename [ StringLength ( BaseFilename ) - StringLength ( Extension ) ] = ' \0 ' ;
return BaseFilename ;
}
2018-01-03 22:16:20 +00:00
void
2018-09-19 19:50:21 +00:00
ConstructTemplatePath ( template * Template , enum8 ( template_types ) Type )
2018-01-03 22:16:20 +00:00
{
2018-01-04 20:55:51 +00:00
// NOTE(matt): Bespoke template paths are set relative to:
// in Project Edition: ProjectDir
// in Single Edition: Parent directory of .hmml file
2018-09-19 19:50:21 +00:00
if ( Template - > File . Path [ 0 ] ! = ' / ' )
2018-01-03 22:16:20 +00:00
{
char Temp [ 256 ] ;
2018-09-19 19:50:21 +00:00
CopyString ( Temp , sizeof ( Temp ) , " %s " , Template - > File . Path ) ;
char * Ptr = Template - > File . Path ;
char * End = Template - > File . Path + sizeof ( Template - > File . Path ) ;
if ( Type = = TEMPLATE_BESPOKE )
2018-01-03 22:16:20 +00:00
{
2018-01-04 20:55:51 +00:00
if ( Config . Edition = = EDITION_SINGLE )
{
2018-04-22 20:57:31 +00:00
Ptr + = CopyString ( Ptr , End - Ptr , " %s/ " , GetDirectoryPath ( Config . SingleHMMLFilePath ) ) ;
2018-01-04 20:55:51 +00:00
}
else
{
2018-04-22 20:57:31 +00:00
Ptr + = CopyString ( Ptr , End - Ptr , " %s/ " , Config . ProjectDir ) ;
2018-01-04 20:55:51 +00:00
}
2018-01-03 22:16:20 +00:00
}
else
{
2018-04-22 20:57:31 +00:00
Ptr + = CopyString ( Ptr , End - Ptr , " %s/ " , Config . TemplatesDir ) ;
2018-01-03 22:16:20 +00:00
}
2018-04-22 20:57:31 +00:00
CopyString ( Ptr , End - Ptr , " %s " , Temp ) ;
2018-01-03 22:16:20 +00:00
}
}
2018-09-19 19:50:21 +00:00
void
FitTemplateTag ( template * Template )
2017-09-07 21:41:08 +00:00
{
2018-09-19 19:50:21 +00:00
int BlockSize = 16 ;
if ( Template - > Metadata . TagCount = = Template - > Metadata . TagCapacity )
2017-12-07 01:15:13 +00:00
{
2018-09-19 19:50:21 +00:00
Template - > Metadata . TagCapacity + = BlockSize ;
if ( Template - > Metadata . Tags )
{
Template - > Metadata . Tags = realloc ( Template - > Metadata . Tags , Template - > Metadata . TagCapacity * sizeof ( * Template - > Metadata . Tags ) ) ;
}
else
{
Template - > Metadata . Tags = calloc ( Template - > Metadata . TagCapacity , sizeof ( * Template - > Metadata . Tags ) ) ;
}
2017-12-07 01:15:13 +00:00
}
2018-01-03 22:16:20 +00:00
}
2018-09-19 19:50:21 +00:00
void
PushTemplateTag ( template * Template , int Offset , enum8 ( template_tag_types ) TagType , int AssetIndex )
2018-01-03 22:16:20 +00:00
{
2018-09-19 19:50:21 +00:00
FitTemplateTag ( Template ) ;
Template - > Metadata . Tags [ Template - > Metadata . TagCount ] . Offset = Offset ;
Template - > Metadata . Tags [ Template - > Metadata . TagCount ] . TagCode = TagType ;
Template - > Metadata . Tags [ Template - > Metadata . TagCount ] . AssetIndex = AssetIndex ;
+ + Template - > Metadata . TagCount ;
}
2017-10-18 21:07:29 +00:00
2018-09-19 19:50:21 +00:00
void
ClearTemplateMetadata ( template * Template )
{
Template - > Metadata . TagCapacity = 0 ;
Template - > Metadata . TagCount = 0 ;
Template - > Metadata . Validity = 0 ;
}
2018-01-03 22:16:20 +00:00
2018-09-19 19:50:21 +00:00
void
InitTemplate ( template * Template , char * Location , enum8 ( template_types ) Type )
{
CopyStringNoFormat ( Template - > File . Path , sizeof ( Template - > File . Path ) , Location ) ;
ConstructTemplatePath ( Template , Type ) ;
2018-09-19 23:43:47 +00:00
printf ( " %sPacking%s template: %s \n " , ColourStrings [ CS_ONGOING ] , ColourStrings [ CS_END ] , Template - > File . Path ) ;
2018-09-19 19:50:21 +00:00
ReadFileIntoBuffer ( & Template - > File , 0 ) ;
ClearTemplateMetadata ( Template ) ;
2017-09-07 21:41:08 +00:00
}
2018-09-19 19:50:21 +00:00
void
FreeTemplate ( template * Template )
2017-09-07 21:41:08 +00:00
{
2018-09-19 19:50:21 +00:00
FreeBuffer ( & Template - > File . Buffer ) ;
Clear ( Template - > File . Path , sizeof ( Template - > File . Path ) ) ;
Template - > File . FileSize = 0 ;
2018-01-03 22:16:20 +00:00
2018-09-19 19:50:21 +00:00
free ( Template - > Metadata . Tags ) ;
Template - > Metadata . Tags = 0 ;
ClearTemplateMetadata ( Template ) ;
2017-09-07 21:41:08 +00:00
}
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
}
2018-04-04 22:39:38 +00:00
void
2017-06-09 22:04:07 +00:00
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 ;
2017-11-11 00:34:47 +00:00
Colour - > Lightness = 74 ;
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 ;
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-12-07 21:07:36 +00:00
enum
{
PAGE_PLAYER = 1 < < 0 ,
2018-09-17 18:06:31 +00:00
PAGE_SEARCH = 1 < < 1
2017-12-07 21:07:36 +00:00
} pages ;
void
2018-09-17 18:06:31 +00:00
ConstructURLPrefix ( buffer * URLPrefix , enum8 ( asset_types ) AssetType , enum8 ( pages ) PageType )
2017-12-07 21:07:36 +00:00
{
2017-12-12 23:24:10 +00:00
RewindBuffer ( URLPrefix ) ;
2017-12-07 21:07:36 +00:00
if ( StringsDiffer ( Config . RootURL , " " ) )
{
2018-09-17 18:06:31 +00:00
CopyStringToBufferHTMLPercentEncoded ( URLPrefix , Config . RootURL ) ;
CopyStringToBuffer ( URLPrefix , " / " ) ;
2017-12-07 21:07:36 +00:00
}
else
{
if ( Config . Edition = = EDITION_PROJECT )
{
if ( PageType = = PAGE_PLAYER )
{
2018-04-22 20:57:31 +00:00
CopyStringToBuffer ( URLPrefix , " ../ " ) ;
2017-12-07 21:07:36 +00:00
}
2018-04-22 20:57:31 +00:00
CopyStringToBuffer ( URLPrefix , " ../ " ) ;
2017-12-07 21:07:36 +00:00
}
}
2018-09-17 18:06:31 +00:00
switch ( AssetType )
2017-12-07 21:07:36 +00:00
{
2018-09-17 18:06:31 +00:00
case ASSET_CSS :
2017-12-07 21:07:36 +00:00
if ( StringsDiffer ( Config . CSSDir , " " ) )
{
2018-09-17 18:06:31 +00:00
CopyStringToBufferHTMLPercentEncoded ( URLPrefix , Config . CSSDir ) ;
CopyStringToBuffer ( URLPrefix , " / " ) ;
2017-12-07 21:07:36 +00:00
}
break ;
2018-09-17 18:06:31 +00:00
case ASSET_IMG :
2017-12-07 21:07:36 +00:00
if ( StringsDiffer ( Config . ImagesDir , " " ) )
{
2018-09-17 18:06:31 +00:00
CopyStringToBufferHTMLPercentEncoded ( URLPrefix , Config . ImagesDir ) ;
CopyStringToBuffer ( URLPrefix , " / " ) ;
2017-12-07 21:07:36 +00:00
}
break ;
2018-09-17 18:06:31 +00:00
case ASSET_JS :
2017-12-07 21:07:36 +00:00
if ( StringsDiffer ( Config . JSDir , " " ) )
{
2018-09-17 18:06:31 +00:00
CopyStringToBufferHTMLPercentEncoded ( URLPrefix , Config . JSDir ) ;
CopyStringToBuffer ( URLPrefix , " / " ) ;
2017-12-07 21:07:36 +00:00
}
break ;
}
}
2018-04-04 22:39:38 +00:00
typedef struct
{
char Abbreviation [ 32 ] ;
hsl_colour Colour ;
credential_info * Credential ;
bool Seen ;
} speaker ;
typedef struct
{
speaker Speaker [ 16 ] ;
int Count ;
} speakers ;
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-11-11 00:34:47 +00:00
} credits_errors ;
2017-06-25 18:22:54 +00:00
2018-09-17 18:06:31 +00:00
size_t
StringToFletcher32 ( const char * Data , size_t Length )
{ // https://en.wikipedia.org/wiki/Fletcher%27s_checksum
size_t c0 , c1 ;
unsigned short int i ;
for ( c0 = c1 = 0 ; Length > = 360 ; Length - = 360 )
{
for ( i = 0 ; i < 360 ; + + i )
{
c0 + = * Data + + ;
c1 + = c0 ;
}
c0 % = 65535 ;
c1 % = 65535 ;
}
for ( i = 0 ; i < Length ; + + i )
{
c0 + = * Data + + ;
c1 + = c0 ;
}
c0 % = 65535 ;
c1 % = 65535 ;
return ( c1 < < 16 | c0 ) ;
}
void
ConstructAssetPath ( file_buffer * AssetFile , char * Filename , int Type )
{
buffer Path ;
ClaimBuffer ( & Path , " Path " , Kilobytes ( 4 ) ) ;
if ( StringsDiffer ( Config . RootDir , " " ) )
{
CopyStringToBuffer ( & Path , " %s " , Config . RootDir ) ;
}
char * AssetDir = 0 ;
switch ( Type )
{
case ASSET_CSS : AssetDir = Config . CSSDir ; break ;
case ASSET_IMG : AssetDir = Config . ImagesDir ; break ;
case ASSET_JS : AssetDir = Config . JSDir ; break ;
}
if ( AssetDir & & StringsDiffer ( AssetDir , " " ) ) { CopyStringToBuffer ( & Path , " /%s " , AssetDir ) ; }
if ( Filename ) { CopyStringToBuffer ( & Path , " /%s " , Filename ) ; }
CopyString ( AssetFile - > Path , sizeof ( AssetFile - > Path ) , Path . Location ) ;
DeclaimBuffer ( & Path ) ;
}
void
CycleFile ( file_buffer * File )
{
fclose ( File - > Handle ) ;
// TODO(matt): Rather than freeing the buffer, why not just realloc it to fit the new file? Couldn't that work easily?
// The reason we Free / Reread is to save us having to shuffle the buffer contents around, basically
// If we switch the whole database over to use linked lists - the database being the only file we actually cycle
// at the moment - then we may actually obviate the need to cycle at all
FreeBuffer ( & File - > Buffer ) ;
ReadFileIntoBuffer ( File , 0 ) ;
}
void
ConstructDirectoryPath ( buffer * DirectoryPath , int PageType , char * PageLocation , char * BaseFilename )
{
RewindBuffer ( DirectoryPath ) ;
CopyStringToBuffer ( DirectoryPath , " %s " , Config . BaseDir ) ;
switch ( PageType )
{
case PAGE_SEARCH :
if ( StringsDiffer ( PageLocation , " " ) )
{
CopyStringToBuffer ( DirectoryPath , " /%s " , PageLocation ) ;
}
break ;
case PAGE_PLAYER :
if ( StringsDiffer ( PageLocation , " " ) )
{
CopyStringToBuffer ( DirectoryPath , " /%s " , PageLocation ) ;
}
if ( BaseFilename )
{
if ( StringsDiffer ( Config . PlayerURLPrefix , " " ) )
{
char * Ptr = BaseFilename + StringLength ( Config . ProjectID ) ;
CopyStringToBuffer ( DirectoryPath , " /%s%s " , Config . PlayerURLPrefix , Ptr ) ;
}
else
{
CopyStringToBuffer ( DirectoryPath , " /%s " , BaseFilename ) ;
}
}
break ;
}
}
int
ReadSearchPageIntoBuffer ( file_buffer * File )
{
buffer SearchPagePath ;
ClaimBuffer ( & SearchPagePath , " SearchPagePath " , MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + 10 ) ;
ConstructDirectoryPath ( & SearchPagePath , PAGE_SEARCH , Config . SearchLocation , 0 ) ;
CopyString ( File - > Path , sizeof ( File - > Path ) , " %s/index.html " , SearchPagePath . Location ) ;
DeclaimBuffer ( & SearchPagePath ) ;
return ( ReadFileIntoBuffer ( File , 0 ) ) ;
}
int
ReadPlayerPageIntoBuffer ( file_buffer * File , db_entry * Entry )
{
buffer PlayerPagePath ;
ClaimBuffer ( & PlayerPagePath , " PlayerPagePath " , MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10 ) ;
ConstructDirectoryPath ( & PlayerPagePath , PAGE_PLAYER , Config . PlayerLocation , Entry - > BaseFilename ) ;
CopyString ( File - > Path , sizeof ( File - > Path ) , " %s/index.html " , PlayerPagePath . Location ) ;
DeclaimBuffer ( & PlayerPagePath ) ;
return ( ReadFileIntoBuffer ( File , 0 ) ) ;
}
void
ClearTerminalRow ( int Length )
{
fprintf ( stderr , " \r " ) ;
for ( int i = 0 ; i < Length ; + + i )
{
fprintf ( stderr , " " ) ;
}
fprintf ( stderr , " \r " ) ;
}
typedef struct
{
uint32_t First ;
uint32_t Length ;
} landmark_range ;
uint32_t
GetIndexRangeLength ( void * FirstLandmark , int EntryIndex , int LandmarkIndex , int LandmarkCount )
{
uint32_t Result = 1 ;
db_landmark Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( Landmark ) * LandmarkIndex ) ;
while ( EntryIndex = = Landmark . EntryIndex & & LandmarkIndex < LandmarkCount - 1 )
{
+ + LandmarkIndex ;
Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( Landmark ) * LandmarkIndex ) ;
if ( EntryIndex = = Landmark . EntryIndex )
{
+ + Result ;
}
}
return Result ;
}
landmark_range
GetIndexRange ( void * FirstLandmark , int EntryIndex , int LandmarkIndex , int LandmarkCount )
{
landmark_range Result = { } ;
db_landmark Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( Landmark ) * LandmarkIndex ) ;
while ( EntryIndex = = Landmark . EntryIndex & & LandmarkIndex > 0 )
{
- - LandmarkIndex ;
Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( Landmark ) * LandmarkIndex ) ;
}
if ( Landmark . EntryIndex ! = EntryIndex )
{
+ + LandmarkIndex ;
}
Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( Landmark ) * LandmarkIndex ) ;
Result . First = LandmarkIndex ;
Result . Length = GetIndexRangeLength ( FirstLandmark , EntryIndex , LandmarkIndex , LandmarkCount ) ;
return Result ;
}
landmark_range
BinarySearchForMetadataLandmark ( void * FirstLandmark , int EntryIndex , int LandmarkCount )
{
// NOTE(matt): Depends on FirstLandmark being positioned after an Asset "header"
landmark_range Result = { } ;
if ( LandmarkCount > 0 )
{
int Lower = 0 ;
db_landmark * LowerLandmark = ( db_landmark * ) ( FirstLandmark + sizeof ( LowerLandmark ) * Lower ) ;
if ( EntryIndex < LowerLandmark - > EntryIndex )
{
Result . First = 0 ;
Result . Length = 0 ;
return Result ;
}
int Upper = LandmarkCount - 1 ;
db_landmark * UpperLandmark ;
// TODO(matt): Is there a slicker way of doing this?
if ( Upper > = 0 )
{
UpperLandmark = ( db_landmark * ) ( FirstLandmark + sizeof ( db_landmark ) * Upper ) ;
if ( EntryIndex > UpperLandmark - > EntryIndex )
{
Result . First = LandmarkCount ;
Result . Length = 0 ;
return Result ;
}
}
int Pivot = Upper - ( ( Upper - Lower ) > > 1 ) ;
db_landmark * PivotLandmark ;
do {
LowerLandmark = ( db_landmark * ) ( FirstLandmark + sizeof ( db_landmark ) * Lower ) ;
PivotLandmark = ( db_landmark * ) ( FirstLandmark + sizeof ( db_landmark ) * Pivot ) ;
UpperLandmark = ( db_landmark * ) ( FirstLandmark + sizeof ( db_landmark ) * Upper ) ;
if ( EntryIndex = = LowerLandmark - > EntryIndex )
{
return GetIndexRange ( FirstLandmark , EntryIndex , Lower , LandmarkCount ) ;
}
if ( EntryIndex = = PivotLandmark - > EntryIndex )
{
return GetIndexRange ( FirstLandmark , EntryIndex , Pivot , LandmarkCount ) ;
}
if ( EntryIndex = = UpperLandmark - > EntryIndex )
{
return GetIndexRange ( FirstLandmark , EntryIndex , Upper , LandmarkCount ) ;
}
if ( EntryIndex < PivotLandmark - > EntryIndex ) { Upper = Pivot ; }
else { Lower = Pivot ; }
Pivot = Upper - ( ( Upper - Lower ) > > 1 ) ;
} while ( Upper > Pivot ) ;
Result . First = Upper ;
Result . Length = 0 ;
return Result ;
}
return Result ;
}
void
SnipeChecksumAndCloseFile ( file_buffer * File , void * FirstLandmark , int LandmarksInFile , buffer * Checksum , int * RunningLandmarkIndex )
{
for ( int j = 0 ; j < LandmarksInFile ; + + j , + + * RunningLandmarkIndex )
{
db_landmark Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( DB . Landmark ) * * RunningLandmarkIndex ) ;
File - > Buffer . Ptr = File - > Buffer . Location + Landmark . Position ;
CopyBufferSized ( & File - > Buffer , Checksum , Checksum - > Ptr - Checksum - > Location ) ;
}
File - > Handle = fopen ( File - > Path , " w " ) ;
fwrite ( File - > Buffer . Location , File - > FileSize , 1 , File - > Handle ) ;
fclose ( File - > Handle ) ;
FreeBuffer ( & File - > Buffer ) ;
}
void
SnipeChecksumIntoHTML ( void * FirstLandmark , buffer * Checksum )
{
landmark_range SearchRange = BinarySearchForMetadataLandmark ( FirstLandmark , PAGE_TYPE_SEARCH , DB . Asset . LandmarkCount ) ;
int RunningLandmarkIndex = 0 ;
if ( SearchRange . Length > 0 )
{
file_buffer HTML ;
ReadSearchPageIntoBuffer ( & HTML ) ;
SnipeChecksumAndCloseFile ( & HTML , FirstLandmark , SearchRange . Length , Checksum , & RunningLandmarkIndex ) ;
}
for ( ; RunningLandmarkIndex < DB . Asset . LandmarkCount ; )
{
db_landmark Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( Landmark ) * RunningLandmarkIndex ) ;
db_entry Entry = * ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * Landmark . EntryIndex ) ;
int Length = GetIndexRangeLength ( FirstLandmark , Landmark . EntryIndex , RunningLandmarkIndex , DB . Asset . LandmarkCount ) ;
file_buffer HTML ;
ReadPlayerPageIntoBuffer ( & HTML , & Entry ) ;
SnipeChecksumAndCloseFile ( & HTML , FirstLandmark , Length , Checksum , & RunningLandmarkIndex ) ;
}
}
void
PrintWatchHandles ( void )
{
printf ( " \n "
" PrintWatchHandles() \n " ) ;
for ( int i = 0 ; i < WatchHandles . Count ; + + i )
{
printf ( " %d • %s • %s \n " ,
WatchHandles . Handle [ i ] . Descriptor ,
WatchHandles . Handle [ i ] . Type = = WT_HMML ? " WT_HMML " : " WT_ASSET " ,
WatchHandles . Handle [ i ] . Path ) ;
}
}
bool
IsSymlink ( char * Filepath )
{
int File = open ( Filepath , O_RDONLY | O_NOFOLLOW ) ;
bool Result = ( errno = = ELOOP ) ;
close ( File ) ;
return Result ;
}
void
FitWatchHandle ( void )
{
int BlockSize = 8 ;
if ( WatchHandles . Count = = WatchHandles . Capacity )
{
WatchHandles . Capacity + = BlockSize ;
if ( WatchHandles . Handle )
{
WatchHandles . Handle = realloc ( WatchHandles . Handle , WatchHandles . Capacity * sizeof ( * WatchHandles . Handle ) ) ;
}
else
{
WatchHandles . Handle = calloc ( WatchHandles . Capacity , sizeof ( * WatchHandles . Handle ) ) ;
}
}
}
void
PushHMMLWatchHandle ( void )
{
FitWatchHandle ( ) ;
CopyString ( WatchHandles . Handle [ WatchHandles . Count ] . Path , sizeof ( WatchHandles . Handle [ 0 ] . Path ) , Config . ProjectDir ) ;
2018-10-05 19:57:09 +00:00
WatchHandles . Handle [ WatchHandles . Count ] . Descriptor = inotify_add_watch ( inotifyInstance , Config . ProjectDir , IN_MOVED_TO | IN_CLOSE_WRITE | IN_DELETE ) ;
2018-09-17 18:06:31 +00:00
WatchHandles . Handle [ WatchHandles . Count ] . Type = WT_HMML ;
+ + WatchHandles . Count ;
}
void
PushAssetWatchHandle ( file_buffer * AssetFile , uint32_t AssetIndex )
{
if ( IsSymlink ( AssetFile - > Path ) )
{
char ResolvedSymlinkPath [ 4096 ] = { } ;
readlink ( AssetFile - > Path , ResolvedSymlinkPath , 4096 ) ;
Clear ( AssetFile - > Path , sizeof ( AssetFile - > Path ) ) ;
CopyString ( AssetFile - > Path , sizeof ( AssetFile - > Path ) , ResolvedSymlinkPath ) ;
}
ResolvePath ( AssetFile - > Path ) ;
StripComponentFromPath ( AssetFile - > Path ) ;
for ( int i = 0 ; i < WatchHandles . Count ; + + i )
{
if ( ! StringsDiffer ( WatchHandles . Handle [ i ] . Path , AssetFile - > Path ) )
{
return ;
}
}
FitWatchHandle ( ) ;
WatchHandles . Handle [ WatchHandles . Count ] . Type = WT_ASSET ;
CopyString ( WatchHandles . Handle [ WatchHandles . Count ] . Path , sizeof ( WatchHandles . Handle [ 0 ] . Path ) , AssetFile - > Path ) ;
WatchHandles . Handle [ WatchHandles . Count ] . Descriptor = inotify_add_watch ( inotifyInstance , AssetFile - > Path , IN_CLOSE_WRITE ) ;
+ + WatchHandles . Count ;
}
void
UpdateAssetInDB ( int AssetIndex )
{
DB . Header = * ( db_header * ) DB . Metadata . Buffer . Location ;
if ( DB . Header . HexSignature ! = FOURCC ( " CNRA " ) )
{
printf ( " line %d: Malformed .metadata file. HexSignature not in expected location \n " , __LINE__ ) ;
exit ( 1 ) ;
}
DB . Metadata . Buffer . Ptr = DB . Metadata . Buffer . Location + sizeof ( DB . Header ) ;
DB . EntriesHeader = * ( db_header_entries * ) DB . Metadata . Buffer . Ptr ;
if ( DB . EntriesHeader . BlockID ! = FOURCC ( " NTRY " ) )
{
printf ( " line %d: Malformed .metadata file. Entries BlockID not in expected location \n " , __LINE__ ) ;
exit ( 1 ) ;
}
DB . Metadata . Buffer . Ptr + = sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * DB . EntriesHeader . Count ;
DB . AssetsHeader = * ( db_header_assets * ) DB . Metadata . Buffer . Ptr ;
if ( DB . AssetsHeader . BlockID ! = FOURCC ( " ASET " ) )
{
printf ( " line %d: Malformed .metadata file. Assets BlockID not in expected location \n " , __LINE__ ) ;
exit ( 1 ) ;
}
int AssetsHeaderLocation = DB . Metadata . Buffer . Ptr - DB . Metadata . Buffer . Location ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . AssetsHeader ) ;
bool Found = FALSE ;
for ( int i = 0 ; i < DB . AssetsHeader . Count ; + + i )
{
DB . Asset = * ( db_asset * ) DB . Metadata . Buffer . Ptr ;
if ( ! StringsDiffer ( DB . Asset . Filename , Assets . Asset [ AssetIndex ] . Filename ) & & DB . Asset . Type = = Assets . Asset [ AssetIndex ] . Type )
{
Found = TRUE ;
break ;
}
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Asset ) + sizeof ( DB . Landmark ) * DB . Asset . LandmarkCount ;
}
if ( Found )
{
if ( DB . Asset . Hash ! = Assets . Asset [ AssetIndex ] . Hash )
{
DB . Asset . Hash = Assets . Asset [ AssetIndex ] . Hash ;
* ( db_asset * ) DB . Metadata . Buffer . Ptr = DB . Asset ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Asset ) ;
buffer Checksum ;
ClaimBuffer ( & Checksum , " Checksum " , 16 ) ;
CopyStringToBuffer ( & Checksum , " %08x " , DB . Asset . Hash ) ;
file_buffer AssetFile ;
ConstructAssetPath ( & AssetFile , DB . Asset . Filename , DB . Asset . Type ) ;
ResolvePath ( AssetFile . Path ) ;
char Message [ 256 ] = { } ;
CopyString ( Message , sizeof ( Message ) , " %sUpdating%s checksum %s of %s in HTML files " , ColourStrings [ CS_ONGOING ] , ColourStrings [ CS_END ] , Checksum . Location , AssetFile . Path ) ;
fprintf ( stderr , Message ) ;
SnipeChecksumIntoHTML ( DB . Metadata . Buffer . Ptr , & Checksum ) ;
ClearTerminalRow ( StringLength ( Message ) ) ;
fprintf ( stderr , " %sUpdated%s checksum %s of %s \n " , ColourStrings [ CS_REINSERTION ] , ColourStrings [ CS_END ] , Checksum . Location , AssetFile . Path ) ;
DeclaimBuffer ( & Checksum ) ;
DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ;
fwrite ( DB . Metadata . Buffer . Location , DB . Metadata . FileSize , 1 , DB . Metadata . Handle ) ;
CycleFile ( & DB . Metadata ) ;
Assets . Asset [ AssetIndex ] . DeferredUpdate = FALSE ;
}
}
else
{
// Append new asset, not bothering to insertion sort because there likely won't be many
+ + DB . AssetsHeader . Count ;
DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ;
fwrite ( DB . Metadata . Buffer . Location , AssetsHeaderLocation , 1 , DB . Metadata . Handle ) ;
fwrite ( & DB . AssetsHeader , sizeof ( DB . AssetsHeader ) , 1 , DB . Metadata . Handle ) ;
fwrite ( DB . Metadata . Buffer . Location + AssetsHeaderLocation + sizeof ( DB . AssetsHeader ) ,
DB . Metadata . FileSize - AssetsHeaderLocation - sizeof ( DB . AssetsHeader ) ,
1 ,
DB . Metadata . Handle ) ;
db_asset Asset = { } ;
Asset . Hash = Assets . Asset [ AssetIndex ] . Hash ;
Asset . Type = Assets . Asset [ AssetIndex ] . Type ;
ClearCopyStringNoFormat ( Asset . Filename , sizeof ( Asset . Filename ) , Assets . Asset [ AssetIndex ] . Filename ) ;
fwrite ( & Asset , sizeof ( Asset ) , 1 , DB . Metadata . Handle ) ;
printf ( " %sAppended%s %s asset: %s [%08x] \n " , ColourStrings [ CS_ADDITION ] , ColourStrings [ CS_END ] , AssetTypeNames [ Asset . Type ] , Asset . Filename , Asset . Hash ) ;
CycleFile ( & DB . Metadata ) ;
}
if ( ! Assets . Asset [ AssetIndex ] . Known )
{
Assets . Asset [ AssetIndex ] . Known = TRUE ;
}
}
void
FitAssetLandmark ( enum8 ( builtin_assets + support_icons ) AssetIndex , int PageType )
{
int BlockSize = 2 ;
if ( PageType = = PAGE_PLAYER )
{
if ( Assets . Asset [ AssetIndex ] . PlayerLandmarkCount = = Assets . Asset [ AssetIndex ] . PlayerLandmarkCapacity )
{
Assets . Asset [ AssetIndex ] . PlayerLandmarkCapacity + = BlockSize ;
if ( Assets . Asset [ AssetIndex ] . PlayerLandmark )
{
Assets . Asset [ AssetIndex ] . PlayerLandmark =
realloc ( Assets . Asset [ AssetIndex ] . PlayerLandmark ,
Assets . Asset [ AssetIndex ] . PlayerLandmarkCapacity * sizeof ( * Assets . Asset [ AssetIndex ] . PlayerLandmark ) ) ;
}
else
{
Assets . Asset [ AssetIndex ] . PlayerLandmark =
calloc ( Assets . Asset [ AssetIndex ] . PlayerLandmarkCapacity , sizeof ( * Assets . Asset [ AssetIndex ] . PlayerLandmark ) ) ;
}
}
}
else
{
if ( Assets . Asset [ AssetIndex ] . SearchLandmarkCount = = Assets . Asset [ AssetIndex ] . SearchLandmarkCapacity )
{
Assets . Asset [ AssetIndex ] . SearchLandmarkCapacity + = BlockSize ;
if ( Assets . Asset [ AssetIndex ] . SearchLandmark )
{
Assets . Asset [ AssetIndex ] . SearchLandmark =
realloc ( Assets . Asset [ AssetIndex ] . SearchLandmark ,
Assets . Asset [ AssetIndex ] . SearchLandmarkCapacity * sizeof ( * Assets . Asset [ AssetIndex ] . SearchLandmark ) ) ;
}
else
{
Assets . Asset [ AssetIndex ] . SearchLandmark =
calloc ( Assets . Asset [ AssetIndex ] . SearchLandmarkCapacity , sizeof ( * Assets . Asset [ AssetIndex ] . SearchLandmark ) ) ;
}
}
}
}
void
PushAssetLandmark ( buffer * Dest , int AssetIndex , int PageType )
{
if ( ! ( Config . Mode & MODE_NOREVVEDRESOURCE ) )
{
FitAssetLandmark ( AssetIndex , PageType ) ;
CopyStringToBuffer ( Dest , " ?%s= " , Config . QueryString ) ;
if ( PageType = = PAGE_PLAYER )
{
Assets . Asset [ AssetIndex ] . PlayerLandmark [ Assets . Asset [ AssetIndex ] . PlayerLandmarkCount ] = Dest - > Ptr - Dest - > Location ;
+ + Assets . Asset [ AssetIndex ] . PlayerLandmarkCount ;
}
else
{
Assets . Asset [ AssetIndex ] . SearchLandmark [ Assets . Asset [ AssetIndex ] . SearchLandmarkCount ] = Dest - > Ptr - Dest - > Location ;
+ + Assets . Asset [ AssetIndex ] . SearchLandmarkCount ;
}
CopyStringToBuffer ( Dest , " %08x " , Assets . Asset [ AssetIndex ] . Hash ) ;
}
}
void
ResetAssetLandmarks ( void )
{
for ( int AssetIndex = 0 ; AssetIndex < Assets . Count ; + + AssetIndex )
{
for ( int LandmarkIndex = 0 ; LandmarkIndex < Assets . Asset [ AssetIndex ] . PlayerLandmarkCount ; + + LandmarkIndex )
{
Assets . Asset [ AssetIndex ] . PlayerLandmark [ LandmarkIndex ] = 0 ;
}
Assets . Asset [ AssetIndex ] . PlayerLandmarkCount = 0 ;
for ( int LandmarkIndex = 0 ; LandmarkIndex < Assets . Asset [ AssetIndex ] . SearchLandmarkCount ; + + LandmarkIndex )
{
Assets . Asset [ AssetIndex ] . SearchLandmark [ LandmarkIndex ] = 0 ;
}
Assets . Asset [ AssetIndex ] . SearchLandmarkCount = 0 ;
Assets . Asset [ AssetIndex ] . OffsetLandmarks = FALSE ;
}
}
void
FitAsset ( void )
{
int BlockSize = 16 ;
if ( Assets . Count = = Assets . Capacity )
{
Assets . Capacity + = BlockSize ;
if ( Assets . Asset )
{
Assets . Asset = realloc ( Assets . Asset , Assets . Capacity * sizeof ( * Assets . Asset ) ) ;
}
else
{
Assets . Asset = calloc ( Assets . Capacity , sizeof ( * Assets . Asset ) ) ;
}
}
}
void
UpdateAsset ( uint32_t AssetIndex , bool Defer )
{
file_buffer File ;
ConstructAssetPath ( & File , Assets . Asset [ AssetIndex ] . Filename , Assets . Asset [ AssetIndex ] . Type ) ;
if ( ReadFileIntoBuffer ( & File , 0 ) = = RC_SUCCESS )
{
Assets . Asset [ AssetIndex ] . Hash = StringToFletcher32 ( File . Buffer . Location , File . FileSize ) ;
if ( ! Defer )
{
UpdateAssetInDB ( AssetIndex ) ;
}
else
{
Assets . Asset [ AssetIndex ] . DeferredUpdate = TRUE ;
}
FreeBuffer ( & File . Buffer ) ;
}
}
int
FinalPathComponentPosition ( char * Path )
{
char * Ptr = Path + StringLength ( Path ) - 1 ;
while ( Ptr > Path & & * Ptr ! = ' / ' )
{
- - Ptr ;
}
if ( * Ptr = = ' / ' )
{
+ + Ptr ;
}
return Ptr - Path ;
}
int
PlaceAsset ( char * Filename , int Type , int Position )
{
FitAsset ( ) ;
Assets . Asset [ Position ] . Type = Type ;
CopyString ( Assets . Asset [ Position ] . Filename , sizeof ( Assets . Asset [ 0 ] . Filename ) , Filename ) ;
Assets . Asset [ Position ] . FilenameAt = FinalPathComponentPosition ( Filename ) ;
if ( Position = = Assets . Count & & ! Assets . Asset [ Position ] . Known ) { + + Assets . Count ; }
file_buffer File ;
ConstructAssetPath ( & File , Filename , Type ) ;
if ( ReadFileIntoBuffer ( & File , 0 ) = = RC_SUCCESS )
{
Assets . Asset [ Position ] . Hash = StringToFletcher32 ( File . Buffer . Location , File . FileSize ) ;
FreeBuffer ( & File . Buffer ) ;
PushAssetWatchHandle ( & File , Position ) ;
return RC_SUCCESS ;
}
else
{
ResolvePath ( File . Path ) ;
printf ( " %sNonexistent%s %s asset: %s \n " , ColourStrings [ CS_WARNING ] , ColourStrings [ CS_END ] , AssetTypeNames [ Type ] , File . Path ) ;
return RC_ERROR_FILE ;
}
}
int
PushAsset ( char * Filename , int Type , uint32_t * AssetIndexPtr )
{
for ( * AssetIndexPtr = 0 ; * AssetIndexPtr < Assets . Count ; + + * AssetIndexPtr )
{
if ( ! StringsDiffer ( Filename , Assets . Asset [ * AssetIndexPtr ] . Filename ) & & Type = = Assets . Asset [ * AssetIndexPtr ] . Type )
{
break ;
}
}
return PlaceAsset ( Filename , Type , * AssetIndexPtr ) ;
}
void
InitBuiltinAssets ( void )
{
Assert ( BUILTIN_ASSETS_COUNT = = ArrayCount ( BuiltinAssets ) ) ;
CopyString ( BuiltinAssets [ ASSET_CSS_THEME ] . Filename , sizeof ( BuiltinAssets [ 0 ] . Filename ) , " cinera__%s.css " , Config . Theme ) ;
for ( int AssetIndex = 0 ; AssetIndex < BUILTIN_ASSETS_COUNT ; + + AssetIndex )
{
if ( PlaceAsset ( BuiltinAssets [ AssetIndex ] . Filename , BuiltinAssets [ AssetIndex ] . Type , AssetIndex ) = = RC_ERROR_FILE & & AssetIndex = = ASSET_CSS_TOPICS )
{
printf ( " %s└───────┴────┴─── Don't worry about this one. We'll generate it if needed%s \n " , ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ) ;
}
}
Assets . Count = BUILTIN_ASSETS_COUNT ;
Assert ( SUPPORT_ICON_COUNT - BUILTIN_ASSETS_COUNT = = ArrayCount ( SupportIcons ) ) ;
for ( int SupportIconIndex = BUILTIN_ASSETS_COUNT ; SupportIconIndex < SUPPORT_ICON_COUNT ; + + SupportIconIndex )
{
PlaceAsset ( SupportIcons [ SupportIconIndex - BUILTIN_ASSETS_COUNT ] , ASSET_IMG , SupportIconIndex ) ;
}
}
void
SkipEntriesBlock ( void * EntriesHeaderLocation )
{
DB . EntriesHeader = * ( db_header_entries * ) EntriesHeaderLocation ;
DB . Metadata . Buffer . Ptr = EntriesHeaderLocation + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * DB . EntriesHeader . Count ;
}
void
LocateAssetsBlock ( void )
{
DB . Header = * ( db_header * ) DB . Metadata . Buffer . Location ;
DB . Metadata . Buffer . Ptr = DB . Metadata . Buffer . Location + sizeof ( DB . Header ) ;
for ( int BlockIndex = 0 ; BlockIndex < DB . Header . BlockCount ; + + BlockIndex )
{
uint32_t FirstInt = * ( uint32_t * ) DB . Metadata . Buffer . Ptr ;
if ( FirstInt = = FOURCC ( " NTRY " ) )
{
SkipEntriesBlock ( DB . Metadata . Buffer . Ptr ) ;
}
else if ( FirstInt = = FOURCC ( " ASET " ) )
{
return ;
}
}
}
void
InitAssets ( void )
{
InitBuiltinAssets ( ) ;
LocateAssetsBlock ( ) ;
DB . AssetsHeader = * ( db_header_assets * ) DB . Metadata . Buffer . Ptr ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . AssetsHeader ) ;
for ( int AssetIndex = 0 ; AssetIndex < DB . AssetsHeader . Count ; + + AssetIndex )
{
DB . Asset = * ( db_asset * ) DB . Metadata . Buffer . Ptr ;
uint32_t AI ;
PushAsset ( DB . Asset . Filename , DB . Asset . Type , & AI ) ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Asset ) + sizeof ( DB . Landmark ) * DB . Asset . LandmarkCount ;
}
}
void
ConstructResolvedAssetURL ( buffer * Buffer , uint32_t AssetIndex , enum8 ( pages ) PageType )
{
ClaimBuffer ( Buffer , " URL " , ( MAX_ROOT_URL_LENGTH + 1 + MAX_RELATIVE_ASSET_LOCATION_LENGTH + 1 ) * 2 ) ;
ConstructURLPrefix ( Buffer , Assets . Asset [ AssetIndex ] . Type , PageType ) ;
CopyStringToBufferHTMLPercentEncoded ( Buffer , Assets . Asset [ AssetIndex ] . Filename ) ;
ResolvePath ( Buffer - > Location ) ;
}
int
SearchCredentials ( buffer * CreditsMenu , bool * HasCreditsMenu , char * Person , char * Role , speakers * Speakers )
{
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
{
2018-04-04 22:39:38 +00:00
if ( Speakers )
{
Speakers - > Speaker [ Speakers - > Count ] . Credential = & Credentials [ CredentialIndex ] ;
+ + Speakers - > Count ;
}
2017-08-18 22:46:58 +00:00
Found = TRUE ;
if ( * HasCreditsMenu = = FALSE )
{
CopyStringToBuffer ( CreditsMenu ,
2017-11-11 00:34:47 +00:00
" <div class= \" menu credits \" > \n "
" <span>Credits</span> \n "
" <div class= \" credits_container \" > \n " ) ;
2017-08-18 22:46:58 +00:00
* HasCreditsMenu = TRUE ;
}
CopyStringToBuffer ( CreditsMenu ,
" <span class= \" credit \" > \n " ) ;
2018-09-17 18:06:31 +00:00
if ( Credentials [ CredentialIndex ] . HomepageURL )
2017-08-18 22:46:58 +00:00
{
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
2018-09-17 18:06:31 +00:00
if ( Credentials [ CredentialIndex ] . SupportURL )
2017-05-25 20:28:52 +00:00
{
2018-09-17 18:06:31 +00:00
buffer URL ;
ConstructResolvedAssetURL ( & URL , Credentials [ CredentialIndex ] . SupportIconIndex , PAGE_PLAYER ) ;
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( CreditsMenu ,
2018-09-17 18:06:31 +00:00
" <a class= \" support \" href= \" %s \" target= \" _blank \" ><div class= \" support_icon \" data-sprite= \" %s " ,
2017-11-11 00:34:47 +00:00
Credentials [ CredentialIndex ] . SupportURL ,
2018-09-17 18:06:31 +00:00
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
PushAssetLandmark ( CreditsMenu , Credentials [ CredentialIndex ] . SupportIconIndex , PAGE_PLAYER ) ;
CopyStringToBuffer ( CreditsMenu , " \" ></div></a> \n " ) ;
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
}
2018-04-04 22:39:38 +00:00
return Found ? RC_SUCCESS : CreditsError_NoCredentials ;
2017-08-18 22:46:58 +00:00
}
2017-06-25 18:22:54 +00:00
2018-04-04 22:39:38 +00:00
void
2018-04-22 20:57:31 +00:00
ClearNullTerminatedString ( char * String )
2018-04-04 22:39:38 +00:00
{
while ( * String )
{
* String + + = ' \0 ' ;
}
}
void
InitialString ( char * Dest , char * Src )
2017-08-18 22:46:58 +00:00
{
2018-04-22 20:57:31 +00:00
ClearNullTerminatedString ( Dest ) ;
2018-04-04 22:39:38 +00:00
* Dest + + = * Src + + ;
while ( * Src + + )
2017-08-18 22:46:58 +00:00
{
2018-04-04 22:39:38 +00:00
if ( * Src = = ' ' )
2017-05-25 20:28:52 +00:00
{
2018-04-04 22:39:38 +00:00
+ + Src ;
if ( * Src )
{
* Dest + + = * Src ;
}
2017-08-18 22:46:58 +00:00
}
}
2018-04-04 22:39:38 +00:00
}
void
GetFirstSubstring ( char * Dest , char * Src )
{
2018-04-22 20:57:31 +00:00
ClearNullTerminatedString ( Dest ) ;
2018-04-04 22:39:38 +00:00
while ( * Src & & * Src ! = ' ' )
2017-08-18 22:46:58 +00:00
{
2018-04-04 22:39:38 +00:00
* Dest + + = * Src + + ;
}
}
void
2018-04-22 20:57:31 +00:00
InitialAndGetFinalString ( char * Dest , int DestSize , char * Src )
2018-04-04 22:39:38 +00:00
{
2018-04-22 20:57:31 +00:00
ClearNullTerminatedString ( Dest ) ;
2018-04-04 22:39:38 +00:00
int SrcLength = StringLength ( Src ) ;
char * SrcPtr = Src + SrcLength - 1 ;
while ( SrcPtr > Src & & * SrcPtr ! = ' ' )
{
- - SrcPtr ;
}
if ( * SrcPtr = = ' ' & & SrcPtr - Src < SrcLength - 1 )
{
+ + SrcPtr ;
}
if ( Src < SrcPtr )
{
* Dest + + = * Src + + ;
* Dest + + = ' . ' ;
* Dest + + = ' ' ;
while ( Src < SrcPtr - 1 )
2017-08-18 22:46:58 +00:00
{
2018-04-04 22:39:38 +00:00
if ( * Src = = ' ' )
{
+ + Src ;
if ( * Src )
{
* Dest + + = * Src ;
* Dest + + = ' . ' ;
* Dest + + = ' ' ;
}
}
+ + Src ;
}
}
2018-04-22 20:57:31 +00:00
CopyString ( Dest , DestSize , " %s " , SrcPtr ) ;
2018-04-04 22:39:38 +00:00
}
bool
AbbreviationsClash ( speakers * Speakers )
{
for ( int i = 0 ; i < Speakers - > Count ; + + i )
{
for ( int j = i + 1 ; j < Speakers - > Count ; + + j )
{
if ( ! StringsDiffer ( Speakers - > Speaker [ i ] . Abbreviation , Speakers - > Speaker [ j ] . Abbreviation ) )
{
return TRUE ;
}
}
}
return FALSE ;
}
void
SortAndAbbreviateSpeakers ( speakers * Speakers )
{
for ( int i = 0 ; i < Speakers - > Count ; + + i )
{
for ( int j = i + 1 ; j < Speakers - > Count ; + + j )
{
if ( StringsDiffer ( Speakers - > Speaker [ i ] . Credential - > Username , Speakers - > Speaker [ j ] . Credential - > Username ) > 0 )
{
credential_info * Temp = Speakers - > Speaker [ j ] . Credential ;
Speakers - > Speaker [ j ] . Credential = Speakers - > Speaker [ i ] . Credential ;
Speakers - > Speaker [ i ] . Credential = Temp ;
break ;
}
}
}
for ( int i = 0 ; i < Speakers - > Count ; + + i )
{
StringToColourHash ( & Speakers - > Speaker [ i ] . Colour , Speakers - > Speaker [ i ] . Credential - > Username ) ;
InitialString ( Speakers - > Speaker [ i ] . Abbreviation , Speakers - > Speaker [ i ] . Credential - > CreditedName ) ;
}
int Attempt = 0 ;
while ( AbbreviationsClash ( Speakers ) )
{
for ( int i = 0 ; i < Speakers - > Count ; + + i )
{
switch ( Attempt )
{
case 0 : GetFirstSubstring ( Speakers - > Speaker [ i ] . Abbreviation , Speakers - > Speaker [ i ] . Credential - > CreditedName ) ; break ;
2018-04-22 20:57:31 +00:00
case 1 : InitialAndGetFinalString ( Speakers - > Speaker [ i ] . Abbreviation , sizeof ( Speakers - > Speaker [ i ] . Abbreviation ) , Speakers - > Speaker [ i ] . Credential - > CreditedName ) ; break ;
2018-04-04 22:39:38 +00:00
case 2 : ClearCopyStringNoFormat ( Speakers - > Speaker [ i ] . Abbreviation , sizeof ( Speakers - > Speaker [ i ] . Abbreviation ) , Speakers - > Speaker [ i ] . Credential - > Username ) ; break ;
}
2017-08-18 22:46:58 +00:00
}
2018-04-04 22:39:38 +00:00
+ + Attempt ;
}
}
int
BuildCredits ( buffer * CreditsMenu , bool * HasCreditsMenu , HMML_VideoMetaData * Metadata , speakers * Speakers )
// TODO(matt): Make this take the Credentials, once we are parsing them from a config
{
if ( SearchCredentials ( CreditsMenu , HasCreditsMenu , Metadata - > member , " Host " , Speakers ) = = CreditsError_NoCredentials )
{
2018-09-17 18:06:31 +00:00
printf ( " No credentials for member %s. Please contact miblodelcarpio@gmail.com with their: \n "
2018-04-04 22:39:38 +00:00
" Full name \n "
" Homepage URL (optional) \n "
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata - > member ) ;
return CreditsError_NoCredentials ;
2017-08-18 22:46:58 +00:00
}
2017-10-18 21:07:29 +00:00
if ( Metadata - > co_host_count > 0 )
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
for ( int i = 0 ; i < Metadata - > co_host_count ; + + i )
2017-08-18 22:46:58 +00:00
{
2018-04-04 22:39:38 +00:00
if ( SearchCredentials ( CreditsMenu , HasCreditsMenu , Metadata - > co_hosts [ i ] , " Co-host " , Speakers ) = = CreditsError_NoCredentials )
2017-05-25 20:28:52 +00:00
{
2018-09-17 18:06:31 +00:00
printf ( " No credentials for co-host %s. Please contact miblodelcarpio@gmail.com with their: \n "
2017-08-18 22:46:58 +00:00
" Full name \n "
" Homepage URL (optional) \n "
2017-10-18 21:07:29 +00:00
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata - > co_hosts [ i ] ) ;
2018-02-05 23:57:17 +00:00
return CreditsError_NoCredentials ;
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-10-18 21:07:29 +00:00
if ( Metadata - > guest_count > 0 )
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
for ( int i = 0 ; i < Metadata - > guest_count ; + + i )
2017-08-18 22:46:58 +00:00
{
2018-04-04 22:39:38 +00:00
if ( SearchCredentials ( CreditsMenu , HasCreditsMenu , Metadata - > guests [ i ] , " Guest " , Speakers ) = = CreditsError_NoCredentials )
2017-05-25 20:28:52 +00:00
{
2018-09-17 18:06:31 +00:00
printf ( " No credentials for guest %s. Please contact miblodelcarpio@gmail.com with their: \n "
2017-08-18 22:46:58 +00:00
" Full name \n "
" Homepage URL (optional) \n "
2017-10-18 21:07:29 +00:00
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata - > guests [ i ] ) ;
2018-02-05 23:57:17 +00:00
return CreditsError_NoCredentials ;
2017-05-25 20:28:52 +00:00
}
}
}
2017-06-25 18:22:54 +00:00
2018-04-04 22:39:38 +00:00
if ( Speakers - > Count > 1 )
{
SortAndAbbreviateSpeakers ( Speakers ) ;
}
2017-10-18 21:07:29 +00:00
if ( Metadata - > annotator_count > 0 )
2017-05-25 20:28:52 +00:00
{
2017-10-18 21:07:29 +00:00
for ( int i = 0 ; i < Metadata - > annotator_count ; + + i )
2017-05-25 20:28:52 +00:00
{
2018-04-04 22:39:38 +00:00
if ( SearchCredentials ( CreditsMenu , HasCreditsMenu , Metadata - > annotators [ i ] , " Annotator " , 0 ) = = CreditsError_NoCredentials )
2017-08-18 22:46:58 +00:00
{
2018-09-17 18:06:31 +00:00
printf ( " No credentials for annotator %s. Please contact miblodelcarpio@gmail.com with their: \n "
2017-08-18 22:46:58 +00:00
" Full name \n "
" Homepage URL (optional) \n "
2017-10-18 21:07:29 +00:00
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata - > annotators [ i ] ) ;
2018-02-05 23:57:17 +00:00
return CreditsError_NoCredentials ;
2017-08-18 22:46:58 +00:00
}
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
}
2018-02-05 23:57:17 +00:00
fprintf ( stderr , " Missing \" annotator \" in the [video] node \n " ) ;
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
2018-02-05 23:57:17 +00:00
return RC_SUCCESS ;
2017-05-25 20:28:52 +00:00
}
2018-04-05 21:25:23 +00:00
enum
{
2018-04-17 23:05:14 +00:00
REF_SITE = 1 < < 0 ,
REF_PAGE = 1 < < 1 ,
REF_URL = 1 < < 2 ,
REF_TITLE = 1 < < 3 ,
REF_ARTICLE = 1 < < 4 ,
REF_AUTHOR = 1 < < 5 ,
REF_EDITOR = 1 < < 6 ,
2018-04-05 21:25:23 +00:00
REF_PUBLISHER = 1 < < 7 ,
2018-04-17 23:05:14 +00:00
REF_ISBN = 1 < < 8 ,
2018-04-05 21:25:23 +00:00
} reference_fields ;
2017-04-21 01:25:40 +00:00
int
2017-10-18 21:07:29 +00:00
BuildReference ( ref_info * ReferencesArray , int RefIdentifier , int UniqueRefs , HMML_Reference * Ref , HMML_Annotation * Anno )
2017-04-19 01:10:45 +00:00
{
2018-04-17 23:05:14 +00:00
if ( Ref - > isbn )
{
2018-04-22 20:57:31 +00:00
CopyString ( ReferencesArray [ UniqueRefs ] . ID , sizeof ( ReferencesArray [ UniqueRefs ] . ID ) , " %s " , Ref - > isbn ) ;
if ( ! Ref - > url ) { CopyString ( ReferencesArray [ UniqueRefs ] . URL , sizeof ( ReferencesArray [ UniqueRefs ] . URL ) , " https://isbndb.com/book/%s " , Ref - > isbn ) ; }
else { CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , sizeof ( ReferencesArray [ UniqueRefs ] . URL ) , Ref - > url ) ; }
2018-04-17 23:05:14 +00:00
}
else if ( Ref - > url )
{
2018-04-22 20:57:31 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , sizeof ( ReferencesArray [ UniqueRefs ] . ID ) , Ref - > url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , sizeof ( ReferencesArray [ UniqueRefs ] . URL ) , Ref - > url ) ;
2018-04-17 23:05:14 +00:00
}
else { return RC_INVALID_REFERENCE ; }
2017-07-29 23:01:39 +00:00
2018-04-17 23:05:14 +00:00
int Mask = 0 ;
2017-10-18 21:07:29 +00:00
if ( Ref - > site ) { Mask | = REF_SITE ; }
if ( Ref - > page ) { Mask | = REF_PAGE ; }
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 ; }
2017-07-29 23:01:39 +00:00
2018-04-17 23:05:14 +00:00
// TODO(matt): Consider handling the various combinations more flexibly, unless we defer this stuff until we have the
// reference store, in which we could optionally customise the display of each reference entry
2018-04-05 21:25:23 +00:00
switch ( Mask )
2017-04-21 01:25:40 +00:00
{
2018-04-17 23:05:14 +00:00
case ( REF_TITLE | REF_AUTHOR | REF_PUBLISHER ) :
2018-04-05 21:25:23 +00:00
{
2018-04-22 20:57:31 +00:00
CopyString ( ReferencesArray [ UniqueRefs ] . Source , sizeof ( ReferencesArray [ UniqueRefs ] . Source ) , " %s (%s) " , Ref - > author , Ref - > publisher ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , sizeof ( ReferencesArray [ UniqueRefs ] . RefTitle ) , Ref - > title ) ;
2018-04-05 21:25:23 +00:00
} break ;
2018-04-17 23:05:14 +00:00
case ( REF_AUTHOR | REF_SITE | REF_PAGE ) :
2018-04-05 21:25:23 +00:00
{
2018-04-22 20:57:31 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , sizeof ( ReferencesArray [ UniqueRefs ] . Source ) , Ref - > site ) ;
CopyString ( ReferencesArray [ UniqueRefs ] . RefTitle , sizeof ( ReferencesArray [ UniqueRefs ] . RefTitle ) , " %s: \" %s \" " , Ref - > author , Ref - > page ) ;
2018-04-05 21:25:23 +00:00
} break ;
2018-04-17 23:05:14 +00:00
case ( REF_PAGE | REF_TITLE ) :
2018-04-05 21:25:23 +00:00
{
2018-04-22 20:57:31 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , sizeof ( ReferencesArray [ UniqueRefs ] . Source ) , Ref - > title ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , sizeof ( ReferencesArray [ UniqueRefs ] . RefTitle ) , Ref - > page ) ;
2018-04-05 21:25:23 +00:00
} break ;
2018-04-17 23:05:14 +00:00
case ( REF_SITE | REF_PAGE ) :
2018-04-05 21:25:23 +00:00
{
2018-04-22 20:57:31 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , sizeof ( ReferencesArray [ UniqueRefs ] . Source ) , Ref - > site ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , sizeof ( ReferencesArray [ UniqueRefs ] . RefTitle ) , Ref - > page ) ;
2018-04-05 21:25:23 +00:00
} break ;
2018-04-17 23:05:14 +00:00
case ( REF_SITE | REF_TITLE ) :
2018-04-05 21:25:23 +00:00
{
2018-04-22 20:57:31 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , sizeof ( ReferencesArray [ UniqueRefs ] . Source ) , Ref - > site ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , sizeof ( ReferencesArray [ UniqueRefs ] . RefTitle ) , Ref - > title ) ;
2018-04-05 21:25:23 +00:00
} break ;
2018-04-17 23:05:14 +00:00
case ( REF_TITLE | REF_AUTHOR ) :
2018-04-05 21:25:23 +00:00
{
2018-04-22 20:57:31 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , sizeof ( ReferencesArray [ UniqueRefs ] . Source ) , Ref - > author ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , sizeof ( ReferencesArray [ UniqueRefs ] . RefTitle ) , Ref - > title ) ;
2018-04-05 21:25:23 +00:00
} break ;
2018-04-17 23:05:14 +00:00
case ( REF_ARTICLE | REF_AUTHOR ) :
2018-04-05 21:25:23 +00:00
{
2018-04-22 20:57:31 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , sizeof ( ReferencesArray [ UniqueRefs ] . Source ) , Ref - > author ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , sizeof ( ReferencesArray [ UniqueRefs ] . RefTitle ) , Ref - > article ) ;
2018-04-05 21:25:23 +00:00
} break ;
2018-04-17 23:05:14 +00:00
case ( REF_TITLE | REF_PUBLISHER ) :
2018-04-05 21:25:23 +00:00
{
2018-04-22 20:57:31 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , sizeof ( ReferencesArray [ UniqueRefs ] . Source ) , Ref - > publisher ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , sizeof ( ReferencesArray [ UniqueRefs ] . RefTitle ) , Ref - > title ) ;
2018-04-05 21:25:23 +00:00
} break ;
2018-04-17 23:05:14 +00:00
case REF_TITLE :
2018-04-05 21:25:23 +00:00
{
2018-04-22 20:57:31 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , sizeof ( ReferencesArray [ UniqueRefs ] . RefTitle ) , Ref - > title ) ;
2018-04-05 21:25:23 +00:00
} break ;
2018-04-17 23:05:14 +00:00
case REF_SITE :
2018-04-05 21:25:23 +00:00
{
2018-04-22 20:57:31 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , sizeof ( ReferencesArray [ UniqueRefs ] . RefTitle ) , Ref - > site ) ;
2018-04-05 21:25:23 +00:00
} break ;
default : return RC_INVALID_REFERENCE ; break ;
2017-04-21 01:25:40 +00:00
}
2017-06-25 18:22:54 +00:00
2018-04-22 20:57:31 +00:00
CopyString ( ReferencesArray [ UniqueRefs ] . Identifier [ ReferencesArray [ UniqueRefs ] . IdentifierCount ] . Timecode , sizeof ( ReferencesArray [ UniqueRefs ] . Identifier [ ReferencesArray [ UniqueRefs ] . IdentifierCount ] . Timecode ) , " %s " , Anno - > time ) ;
2017-04-19 01:10:45 +00:00
ReferencesArray [ UniqueRefs ] . Identifier [ ReferencesArray [ UniqueRefs ] . IdentifierCount ] . Identifier = RefIdentifier ;
2018-04-05 21:25:23 +00:00
return RC_SUCCESS ;
2017-04-19 01:10:45 +00:00
}
2017-05-21 06:35:16 +00:00
void
2017-10-18 21:07:29 +00:00
InsertCategory ( categories * GlobalTopics , categories * LocalTopics , categories * GlobalMedia , categories * LocalMedia , 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-05-24 21:56:36 +00:00
if ( IsMedium )
{
2018-01-15 00:03:11 +00:00
int MediumIndex ;
for ( MediumIndex = 0 ; MediumIndex < LocalMedia - > Count ; + + MediumIndex )
2017-05-22 21:37:00 +00:00
{
2018-01-15 00:03:11 +00:00
if ( ! StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . Medium , LocalMedia - > Category [ MediumIndex ] . 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
}
2018-01-15 00:03:11 +00:00
if ( ( StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . WrittenName , LocalMedia - > Category [ MediumIndex ] . WrittenText ) ) < 0 )
2017-05-24 21:56:36 +00:00
{
2017-08-19 21:41:51 +00:00
int CategoryCount ;
2018-01-15 00:03:11 +00:00
for ( CategoryCount = LocalMedia - > Count ; CategoryCount > MediumIndex ; - - CategoryCount )
2017-05-24 21:56:36 +00:00
{
2018-04-22 20:57:31 +00:00
CopyString ( LocalMedia - > Category [ CategoryCount ] . Marker , sizeof ( LocalMedia - > Category [ CategoryCount ] . Marker ) , " %s " , LocalMedia - > Category [ CategoryCount - 1 ] . Marker ) ;
CopyString ( LocalMedia - > Category [ CategoryCount ] . WrittenText , sizeof ( LocalMedia - > Category [ CategoryCount ] . WrittenText ) , " %s " , LocalMedia - > Category [ CategoryCount - 1 ] . WrittenText ) ;
2017-05-24 21:56:36 +00:00
}
2017-06-25 18:22:54 +00:00
2018-04-22 20:57:31 +00:00
CopyString ( LocalMedia - > Category [ CategoryCount ] . Marker , sizeof ( LocalMedia - > Category [ CategoryCount ] . Marker ) , " %s " , CategoryMedium [ CategoryMediumIndex ] . Medium ) ;
CopyString ( LocalMedia - > Category [ CategoryCount ] . WrittenText , sizeof ( LocalMedia - > Category [ CategoryCount ] . WrittenText ) , " %s " , 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
2018-01-15 00:03:11 +00:00
if ( MediumIndex = = LocalMedia - > Count )
2017-05-24 21:56:36 +00:00
{
2018-04-22 20:57:31 +00:00
CopyString ( LocalMedia - > Category [ MediumIndex ] . Marker , sizeof ( LocalMedia - > Category [ MediumIndex ] . Marker ) , " %s " , CategoryMedium [ CategoryMediumIndex ] . Medium ) ;
CopyString ( LocalMedia - > Category [ MediumIndex ] . WrittenText , sizeof ( LocalMedia - > Category [ MediumIndex ] . WrittenText ) , " %s " , CategoryMedium [ CategoryMediumIndex ] . WrittenName ) ;
2017-05-24 21:56:36 +00:00
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
+ + LocalMedia - > Count ;
2018-01-15 00:03:11 +00:00
for ( MediumIndex = 0 ; MediumIndex < GlobalMedia - > Count ; + + MediumIndex )
2017-10-18 21:07:29 +00:00
{
2018-01-15 00:03:11 +00:00
if ( ! StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . Medium , GlobalMedia - > Category [ MediumIndex ] . Marker ) )
2017-10-18 21:07:29 +00:00
{
return ;
}
2018-01-15 00:03:11 +00:00
if ( ( StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . WrittenName , GlobalMedia - > Category [ MediumIndex ] . WrittenText ) ) < 0 )
2017-10-18 21:07:29 +00:00
{
int CategoryCount ;
2018-01-15 00:03:11 +00:00
for ( CategoryCount = GlobalMedia - > Count ; CategoryCount > MediumIndex ; - - CategoryCount )
2017-10-18 21:07:29 +00:00
{
2018-04-22 20:57:31 +00:00
CopyString ( GlobalMedia - > Category [ CategoryCount ] . Marker , sizeof ( GlobalMedia - > Category [ CategoryCount ] . Marker ) , " %s " , GlobalMedia - > Category [ CategoryCount - 1 ] . Marker ) ;
CopyString ( GlobalMedia - > Category [ CategoryCount ] . WrittenText , sizeof ( GlobalMedia - > Category [ CategoryCount ] . WrittenText ) , " %s " , GlobalMedia - > Category [ CategoryCount - 1 ] . WrittenText ) ;
2017-10-18 21:07:29 +00:00
}
2018-04-22 20:57:31 +00:00
CopyString ( GlobalMedia - > Category [ CategoryCount ] . Marker , sizeof ( GlobalMedia - > Category [ CategoryCount ] . Marker ) , " %s " , CategoryMedium [ CategoryMediumIndex ] . Medium ) ;
CopyString ( GlobalMedia - > Category [ CategoryCount ] . WrittenText , sizeof ( GlobalMedia - > Category [ CategoryCount ] . WrittenText ) , " %s " , CategoryMedium [ CategoryMediumIndex ] . WrittenName ) ;
2017-10-18 21:07:29 +00:00
break ;
}
}
2018-01-15 00:03:11 +00:00
if ( MediumIndex = = GlobalMedia - > Count )
2017-10-18 21:07:29 +00:00
{
2018-04-22 20:57:31 +00:00
CopyString ( GlobalMedia - > Category [ MediumIndex ] . Marker , sizeof ( GlobalMedia - > Category [ MediumIndex ] . Marker ) , " %s " , CategoryMedium [ CategoryMediumIndex ] . Medium ) ;
CopyString ( GlobalMedia - > Category [ MediumIndex ] . WrittenText , sizeof ( GlobalMedia - > Category [ MediumIndex ] . WrittenText ) , " %s " , CategoryMedium [ CategoryMediumIndex ] . WrittenName ) ;
2017-10-18 21:07:29 +00:00
}
+ + GlobalMedia - > Count ;
2017-05-21 06:35:16 +00:00
}
2017-05-24 21:56:36 +00:00
else
{
2018-01-15 00:03:11 +00:00
int TopicIndex ;
for ( TopicIndex = 0 ; TopicIndex < LocalTopics - > Count ; + + TopicIndex )
2017-05-24 21:56:36 +00:00
{
2018-01-15 00:03:11 +00:00
if ( ! StringsDiffer ( Marker , LocalTopics - > Category [ TopicIndex ] . Marker ) )
2017-05-24 21:56:36 +00:00
{
return ;
}
2018-01-15 00:03:11 +00:00
if ( ( StringsDiffer ( Marker , LocalTopics - > Category [ TopicIndex ] . Marker ) ) < 0 )
2017-05-24 21:56:36 +00:00
{
2017-08-19 21:41:51 +00:00
int CategoryCount ;
2018-01-15 00:03:11 +00:00
for ( CategoryCount = LocalTopics - > Count ; CategoryCount > TopicIndex ; - - CategoryCount )
2017-05-24 21:56:36 +00:00
{
2018-04-22 20:57:31 +00:00
CopyString ( LocalTopics - > Category [ CategoryCount ] . Marker , sizeof ( LocalTopics - > Category [ CategoryCount ] . Marker ) , " %s " , LocalTopics - > Category [ CategoryCount - 1 ] . Marker ) ;
2017-05-24 21:56:36 +00:00
}
2017-06-25 18:22:54 +00:00
2018-04-22 20:57:31 +00:00
CopyString ( LocalTopics - > Category [ CategoryCount ] . Marker , sizeof ( LocalTopics - > Category [ CategoryCount ] . Marker ) , " %s " , Marker ) ;
2017-05-24 21:56:36 +00:00
break ;
}
}
2017-06-25 18:22:54 +00:00
2018-01-15 00:03:11 +00:00
if ( TopicIndex = = LocalTopics - > Count )
2017-05-21 06:35:16 +00:00
{
2018-04-22 20:57:31 +00:00
CopyString ( LocalTopics - > Category [ TopicIndex ] . Marker , sizeof ( LocalTopics - > Category [ TopicIndex ] . Marker ) , " %s " , Marker ) ;
2017-05-21 06:35:16 +00:00
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
+ + LocalTopics - > Count ;
2018-01-15 00:03:11 +00:00
for ( TopicIndex = 0 ; TopicIndex < GlobalTopics - > Count ; + + TopicIndex )
2017-10-18 21:07:29 +00:00
{
2018-01-15 00:03:11 +00:00
if ( ! StringsDiffer ( Marker , GlobalTopics - > Category [ TopicIndex ] . Marker ) )
2017-10-18 21:07:29 +00:00
{
return ;
}
2018-01-15 00:03:11 +00:00
// NOTE(matt): This successfully sorts "nullTopic" at the end, but maybe figure out a more general way to force the
// order of stuff, perhaps blocks of dudes that should sort to the start / end
if ( ( ( StringsDiffer ( Marker , GlobalTopics - > Category [ TopicIndex ] . Marker ) ) < 0 | | ! StringsDiffer ( GlobalTopics - > Category [ TopicIndex ] . Marker , " nullTopic " ) ) )
2017-10-18 21:07:29 +00:00
{
2018-01-15 00:03:11 +00:00
if ( StringsDiffer ( Marker , " nullTopic " ) ) // NOTE(matt): This test (with the above || condition) forces nullTopic never to be inserted, only appended
2017-10-18 21:07:29 +00:00
{
2018-01-15 00:03:11 +00:00
int CategoryCount ;
for ( CategoryCount = GlobalTopics - > Count ; CategoryCount > TopicIndex ; - - CategoryCount )
{
2018-04-22 20:57:31 +00:00
CopyString ( GlobalTopics - > Category [ CategoryCount ] . Marker , sizeof ( GlobalTopics - > Category [ CategoryCount ] . Marker ) , " %s " , GlobalTopics - > Category [ CategoryCount - 1 ] . Marker ) ;
2018-01-15 00:03:11 +00:00
}
2017-10-18 21:07:29 +00:00
2018-04-22 20:57:31 +00:00
CopyString ( GlobalTopics - > Category [ CategoryCount ] . Marker , sizeof ( GlobalTopics - > Category [ CategoryCount ] . Marker ) , " %s " , Marker ) ;
2018-01-15 00:03:11 +00:00
break ;
}
2017-10-18 21:07:29 +00:00
}
}
2018-01-15 00:03:11 +00:00
if ( TopicIndex = = GlobalTopics - > Count )
2017-10-18 21:07:29 +00:00
{
2018-04-22 20:57:31 +00:00
CopyString ( GlobalTopics - > Category [ TopicIndex ] . Marker , sizeof ( GlobalTopics - > Category [ TopicIndex ] . Marker ) , " %s " , Marker ) ;
2017-10-18 21:07:29 +00:00
}
+ + GlobalTopics - > Count ;
2017-05-21 06:35:16 +00:00
}
}
2017-03-25 03:10:15 +00:00
void
2018-01-15 00:03:11 +00:00
BuildCategories ( buffer * AnnotationClass , buffer * CategoryIcons , categories * LocalTopics , categories * LocalMedia , int * MarkerIndex , char * DefaultMedium )
2017-03-25 03:10:15 +00:00
{
2018-01-15 00:03:11 +00:00
bool CategoriesSpan = FALSE ;
if ( ! ( LocalTopics - > Count = = 1 & & ! StringsDiffer ( LocalTopics - > Category [ 0 ] . Marker , " nullTopic " )
& & LocalMedia - > Count = = 1 & & ! StringsDiffer ( LocalMedia - > Category [ 0 ] . Marker , DefaultMedium ) ) )
{
CategoriesSpan = TRUE ;
CopyStringToBuffer ( CategoryIcons , " <span class= \" cineraCategories \" > " ) ;
}
if ( LocalTopics - > Count = = 1 & & ! StringsDiffer ( LocalTopics - > Category [ 0 ] . Marker , " nullTopic " ) )
{
2018-04-22 20:57:31 +00:00
char SanitisedMarker [ StringLength ( LocalTopics - > Category [ 0 ] . Marker ) + 1 ] ;
CopyString ( SanitisedMarker , sizeof ( SanitisedMarker ) , " %s " , LocalTopics - > Category [ 0 ] . Marker ) ;
2018-01-15 00:03:11 +00:00
SanitisePunctuation ( SanitisedMarker ) ;
CopyStringToBuffer ( AnnotationClass , " cat_%s " , SanitisedMarker ) ;
}
else
2017-03-25 03:10:15 +00:00
{
2017-10-18 21:07:29 +00:00
for ( int i = 0 ; i < LocalTopics - > Count ; + + i )
2017-03-25 03:10:15 +00:00
{
2018-04-22 20:57:31 +00:00
char SanitisedMarker [ StringLength ( LocalTopics - > Category [ i ] . Marker ) + 1 ] ;
CopyString ( SanitisedMarker , sizeof ( SanitisedMarker ) , " %s " , LocalTopics - > Category [ i ] . Marker ) ;
2018-01-15 00:03:11 +00:00
SanitisePunctuation ( SanitisedMarker ) ;
CopyStringToBuffer ( CategoryIcons , " <div title= \" %s \" class= \" category %s \" ></div> " ,
LocalTopics - > Category [ i ] . Marker ,
SanitisedMarker ) ;
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( AnnotationClass , " cat_%s " ,
2018-01-15 00:03:11 +00:00
SanitisedMarker ) ;
2017-06-11 22:49:04 +00:00
}
}
2017-06-25 18:22:54 +00:00
2018-01-15 00:03:11 +00:00
if ( LocalMedia - > Count = = 1 & & ! StringsDiffer ( LocalMedia - > Category [ 0 ] . Marker , DefaultMedium ) )
2017-06-11 22:49:04 +00:00
{
2018-04-22 20:57:31 +00:00
char SanitisedMarker [ StringLength ( LocalMedia - > Category [ 0 ] . Marker ) + 1 ] ;
CopyString ( SanitisedMarker , sizeof ( SanitisedMarker ) , " %s " , LocalMedia - > Category [ 0 ] . Marker ) ;
2018-01-15 00:03:11 +00:00
SanitisePunctuation ( SanitisedMarker ) ;
CopyStringToBuffer ( AnnotationClass , " %s " , SanitisedMarker ) ;
}
else
{
for ( int i = 0 ; i < LocalMedia - > Count ; + + i )
2017-11-11 00:34:47 +00:00
{
2018-04-22 20:57:31 +00:00
char SanitisedMarker [ StringLength ( LocalMedia - > Category [ i ] . Marker ) + 1 ] ;
CopyString ( SanitisedMarker , sizeof ( SanitisedMarker ) , " %s " , LocalMedia - > Category [ i ] . Marker ) ;
2018-01-15 00:03:11 +00:00
SanitisePunctuation ( SanitisedMarker ) ;
if ( ! StringsDiffer ( LocalMedia - > Category [ i ] . Marker , " afk " ) ) // TODO(matt): Initially hidden config
{
CopyStringToBuffer ( AnnotationClass , " off_%s skip " , SanitisedMarker ) ; // TODO(matt): Bulletproof this?
}
else
{
for ( int j = 0 ; j < ArrayCount ( CategoryMedium ) ; + + j )
{
if ( ! StringsDiffer ( LocalMedia - > Category [ i ] . Marker , CategoryMedium [ j ] . Medium ) )
{
CopyStringToBuffer ( CategoryIcons , " <div title= \" %s \" class= \" categoryMedium %s \" >%s</div> " ,
LocalMedia - > Category [ i ] . WrittenText ,
LocalMedia - > Category [ i ] . Marker ,
CategoryMedium [ j ] . Icon ) ;
CopyStringToBuffer ( AnnotationClass , " %s " , SanitisedMarker ) ;
}
}
}
2017-11-11 00:34:47 +00:00
}
2017-06-11 22:49:04 +00:00
}
2017-06-25 18:22:54 +00:00
2018-01-15 00:03:11 +00:00
if ( CategoriesSpan )
{
CopyStringToBuffer ( CategoryIcons , " </span> " ) ;
}
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 )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sFetching%s quotes: %s \n " , ColourStrings [ CS_ONGOING ] , ColourStrings [ CS_END ] , QuotesURL ) ;
2017-08-11 22:41:52 +00:00
CURL * curl = curl_easy_init ( ) ;
if ( curl ) {
2018-02-28 20:18:11 +00:00
CURLcode CurlReturnCode ;
2017-08-11 22:41:52 +00:00
curl_easy_setopt ( curl , CURLOPT_WRITEDATA , & QuoteStaging - > Ptr ) ;
curl_easy_setopt ( curl , CURLOPT_WRITEFUNCTION , CurlIntoBuffer ) ;
curl_easy_setopt ( curl , CURLOPT_URL , QuotesURL ) ;
2018-02-28 20:18:11 +00:00
if ( ( CurlReturnCode = curl_easy_perform ( curl ) ) )
2017-08-11 22:41:52 +00:00
{
2018-02-28 20:18:11 +00:00
fprintf ( stderr , " %s \n " , curl_easy_strerror ( CurlReturnCode ) ) ;
2017-08-11 22:41:52 +00:00
}
curl_easy_cleanup ( curl ) ;
}
}
2017-08-18 22:46:58 +00:00
int
2017-10-18 21:07:29 +00:00
SearchQuotes ( buffer * QuoteStaging , int CacheSize , quote_info * Info , int ID )
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
QuoteStaging - > Ptr = QuoteStaging - > Location ;
while ( QuoteStaging - > Ptr - QuoteStaging - > Location < CacheSize )
2017-08-18 22:46:58 +00:00
{
2018-04-22 20:57:31 +00:00
char InID [ 8 ] = { 0 } ;
2017-08-18 22:46:58 +00:00
char InTime [ 16 ] = { 0 } ;
2018-04-22 20:57:31 +00:00
QuoteStaging - > Ptr + = CopyStringNoFormatT ( InID , sizeof ( InID ) , QuoteStaging - > Ptr , ' , ' ) + 1 ; // Skip past the ','
2017-08-18 22:46:58 +00:00
if ( StringToInt ( InID ) = = ID )
{
2018-04-22 20:57:31 +00:00
QuoteStaging - > Ptr + = CopyStringNoFormatT ( InTime , sizeof ( InTime ) , QuoteStaging - > Ptr , ' , ' ) + 1 ; // Skip past the ','
2017-08-18 22:46:58 +00:00
long int Time = StringToInt ( InTime ) ;
2018-04-22 20:57:31 +00:00
char DayString [ 3 ] = { 0 } ;
2017-08-18 22:46:58 +00:00
strftime ( DayString , 3 , " %d " , gmtime ( & Time ) ) ;
int Day = StringToInt ( DayString ) ;
2018-04-22 20:57:31 +00:00
char DaySuffix [ 3 ] ; if ( DayString [ 1 ] = = ' 1 ' & & Day ! = 11 ) { CopyString ( DaySuffix , sizeof ( DaySuffix ) , " st " ) ; }
else if ( DayString [ 1 ] = = ' 2 ' & & Day ! = 12 ) { CopyString ( DaySuffix , sizeof ( DaySuffix ) , " nd " ) ; }
else if ( DayString [ 1 ] = = ' 3 ' & & Day ! = 13 ) { CopyString ( DaySuffix , sizeof ( DaySuffix ) , " rd " ) ; }
else { CopyString ( DaySuffix , sizeof ( DaySuffix ) , " th " ) ; }
2017-08-18 22:46:58 +00:00
char MonthYear [ 32 ] ;
strftime ( MonthYear , 32 , " %B, %Y " , gmtime ( & Time ) ) ;
2018-04-22 20:57:31 +00:00
CopyString ( Info - > Date , sizeof ( Info - > Date ) , " %d%s %s " , Day , DaySuffix , MonthYear ) ;
2017-08-18 22:46:58 +00:00
2018-04-22 20:57:31 +00:00
CopyStringNoFormatT ( Info - > Text , sizeof ( Info - > Text ) , QuoteStaging - > Ptr , ' \n ' ) ;
2017-08-18 22:46:58 +00:00
2017-10-18 21:07:29 +00:00
FreeBuffer ( QuoteStaging ) ;
2017-09-07 21:41:08 +00:00
return RC_FOUND ;
2017-08-18 22:46:58 +00:00
}
else
{
2017-10-18 21:07:29 +00:00
while ( * QuoteStaging - > Ptr ! = ' \n ' )
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
+ + QuoteStaging - > Ptr ;
2017-08-18 22:46:58 +00:00
}
2017-10-18 21:07:29 +00:00
+ + QuoteStaging - > Ptr ;
2017-08-18 22:46:58 +00:00
}
}
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
2018-02-28 01:04:06 +00:00
BuildQuote ( quote_info * Info , char * Speaker , int ID , bool ShouldFetchQuotes )
2017-08-10 01:05:41 +00:00
{
2017-11-11 00:34:47 +00:00
char QuoteCacheDir [ 256 ] ;
2018-04-22 20:57:31 +00:00
CopyString ( QuoteCacheDir , sizeof ( QuoteCacheDir ) , " %s/quotes " , Config . CacheDir ) ;
2017-11-11 00:34:47 +00:00
char QuoteCachePath [ 256 ] ;
2018-04-22 20:57:31 +00:00
CopyString ( QuoteCachePath , sizeof ( QuoteCachePath ) , " %s/%s " , QuoteCacheDir , Speaker ) ;
2018-02-28 01:04:06 +00:00
2017-08-10 01:05:41 +00:00
FILE * QuoteCache ;
2017-08-11 22:41:52 +00:00
char QuotesURL [ 256 ] ;
2018-02-28 20:18:11 +00:00
// TODO(matt): Make the URL configurable and also handle the case in which the .raw isn't available
2018-04-22 20:57:31 +00:00
CopyString ( QuotesURL , sizeof ( QuotesURL ) , " https://dev.abaines.me.uk/quotes/%s.raw " , Speaker ) ;
2017-08-11 22:41:52 +00:00
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 ;
2018-02-28 01:04:06 +00:00
}
2017-08-11 22:41:52 +00:00
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
}
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Allocated QuoteStaging (%d) \n " , QuoteStaging . Size ) ;
fclose ( MemLog ) ;
printf ( " Allocated QuoteStaging (%d) \n " , QuoteStaging . Size ) ;
# endif
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
2018-02-28 20:18:11 +00:00
if ( ShouldFetchQuotes | | SearchQuotes ( & QuoteStaging , FileSize , Info , ID ) = = RC_UNFOUND )
2017-08-11 22:41:52 +00:00
{
2018-09-17 18:06:31 +00:00
// TODO(matt): Error handling
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 ;
2018-01-04 20:55:51 +00:00
if ( SearchQuotes ( & QuoteStaging , CacheSize , Info , ID ) = = RC_UNFOUND )
2017-08-11 22:41:52 +00:00
{
FreeBuffer ( & QuoteStaging ) ;
2018-01-04 20:55:51 +00:00
return RC_UNFOUND ;
2017-08-11 22:41:52 +00:00
}
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 ;
2018-01-04 20:55:51 +00:00
if ( SearchQuotes ( & QuoteStaging , CacheSize , Info , ID ) = = RC_UNFOUND )
2017-08-10 01:05:41 +00:00
{
FreeBuffer ( & QuoteStaging ) ;
2018-01-04 20:55:51 +00:00
return RC_UNFOUND ;
2017-08-10 01:05:41 +00:00
}
}
2017-08-11 22:41:52 +00:00
2018-01-04 20:55:51 +00:00
return RC_SUCCESS ;
2017-08-10 01:05:41 +00:00
}
2017-09-07 21:41:08 +00:00
int
2017-10-18 21:07:29 +00:00
GenerateTopicColours ( char * Topic )
2017-03-31 00:56:50 +00:00
{
2018-04-22 20:57:31 +00:00
char SanitisedTopic [ StringLength ( Topic ) + 1 ] ;
CopyString ( SanitisedTopic , sizeof ( SanitisedTopic ) , " %s " , Topic ) ;
2018-01-15 00:03:11 +00:00
SanitisePunctuation ( SanitisedTopic ) ;
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-10-18 21:07:29 +00:00
file_buffer Topics ;
Topics . Buffer . ID = " Topics " ;
2017-11-11 00:34:47 +00:00
if ( StringsDiffer ( Config . CSSDir , " " ) )
{
2018-09-17 18:06:31 +00:00
CopyString ( Topics . Path , sizeof ( Topics . Path ) , " %s/%s/%s " , Config . RootDir , Config . CSSDir , BuiltinAssets [ ASSET_CSS_TOPICS ] . Filename ) ;
2017-11-11 00:34:47 +00:00
}
else
{
2018-09-17 18:06:31 +00:00
CopyString ( Topics . Path , sizeof ( Topics . Path ) , " %s/%s " , Config . RootDir , BuiltinAssets [ ASSET_CSS_TOPICS ] . Filename ) ;
2017-11-11 00:34:47 +00:00
}
2017-10-18 21:07:29 +00:00
2017-12-24 01:57:11 +00:00
char * Ptr = Topics . Path + StringLength ( Topics . Path ) - 1 ;
while ( * Ptr ! = ' / ' )
{
- - Ptr ;
}
* Ptr = ' \0 ' ;
DIR * CSSDirHandle ; // TODO(matt): open()
if ( ! ( CSSDirHandle = opendir ( Topics . Path ) ) )
{
if ( MakeDir ( Topics . Path ) = = RC_ERROR_DIRECTORY )
{
LogError ( LOG_ERROR , " Unable to create directory %s: %s " , Topics . Path , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to create directory %s: %s \n " , Topics . Path , strerror ( errno ) ) ;
return RC_ERROR_DIRECTORY ;
} ;
}
closedir ( CSSDirHandle ) ;
* Ptr = ' / ' ;
2017-10-18 21:07:29 +00:00
if ( ( Topics . Handle = fopen ( Topics . Path , " a+ " ) ) )
2017-03-31 00:56:50 +00:00
{
2017-10-18 21:07:29 +00:00
fseek ( Topics . Handle , 0 , SEEK_END ) ;
Topics . FileSize = ftell ( Topics . Handle ) ;
Topics . Buffer . Size = Topics . FileSize ;
fseek ( Topics . Handle , 0 , SEEK_SET ) ;
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
if ( ! ( Topics . Buffer . Location = malloc ( Topics . Buffer . Size ) ) )
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-10-18 21:07:29 +00:00
# if DEBUG_MEM
2017-11-11 00:34:47 +00:00
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
2018-02-23 23:36:42 +00:00
fprintf ( MemLog , " Allocated Topics (%d) \n " , Topics . Buffer . Size ) ;
2017-11-11 00:34:47 +00:00
fclose ( MemLog ) ;
2018-02-23 23:36:42 +00:00
printf ( " Allocated Topics (%d) \n " , Topics . Buffer . Size ) ;
2017-10-18 21:07:29 +00:00
# endif
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
Topics . Buffer . Ptr = Topics . Buffer . Location ;
fread ( Topics . Buffer . Location , Topics . Buffer . Size , 1 , Topics . Handle ) ;
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
while ( Topics . Buffer . Ptr - Topics . Buffer . Location < Topics . Buffer . Size )
2017-03-31 00:56:50 +00:00
{
2017-10-18 21:07:29 +00:00
Topics . Buffer . Ptr + = StringLength ( " .category. " ) ;
2018-01-15 00:03:11 +00:00
if ( ! StringsDifferT ( SanitisedTopic , Topics . Buffer . Ptr , ' ' ) )
2017-03-31 00:56:50 +00:00
{
2017-10-18 21:07:29 +00:00
FreeBuffer ( & Topics . Buffer ) ;
fclose ( Topics . Handle ) ;
2017-09-07 21:41:08 +00:00
return RC_NOOP ;
2017-03-31 00:56:50 +00:00
}
2017-10-18 21:07:29 +00:00
while ( Topics . Buffer . Ptr - Topics . Buffer . Location < Topics . Buffer . Size & & * Topics . Buffer . Ptr ! = ' \n ' )
2017-03-31 00:56:50 +00:00
{
2017-10-18 21:07:29 +00:00
+ + Topics . Buffer . Ptr ;
2017-03-31 00:56:50 +00:00
}
2017-10-18 21:07:29 +00:00
+ + Topics . Buffer . Ptr ;
2017-03-31 00:56:50 +00:00
}
2017-06-25 18:22:54 +00:00
2018-01-15 00:03:11 +00:00
if ( ! StringsDiffer ( Topic , " nullTopic " ) )
{
fprintf ( Topics . Handle , " .category.%s { border: 1px solid transparent; background: transparent; } \n " ,
SanitisedTopic ) ;
}
else
{
hsl_colour Colour ;
StringToColourHash ( & Colour , Topic ) ;
fprintf ( Topics . Handle , " .category.%s { border: 1px solid hsl(%d, %d%%, %d%%); background: hsl(%d, %d%%, %d%%); } \n " ,
SanitisedTopic , Colour . Hue , Colour . Saturation , Colour . Lightness , Colour . Hue , Colour . Saturation , Colour . Lightness ) ;
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
fclose ( Topics . Handle ) ;
FreeBuffer ( & Topics . Buffer ) ;
2018-09-17 18:06:31 +00:00
if ( Assets . Asset [ ASSET_CSS_TOPICS ] . Known )
{
UpdateAsset ( ASSET_CSS_TOPICS , TRUE ) ;
}
else
{
PlaceAsset ( BuiltinAssets [ ASSET_CSS_TOPICS ] . Filename , ASSET_CSS , ASSET_CSS_TOPICS ) ;
}
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
2017-03-31 00:56:50 +00:00
}
else
{
2018-09-17 18:06:31 +00:00
// NOTE(matt): Maybe it shouldn't be possible to hit this case now that we MakeDir the actual dir...
2017-12-24 01:57:11 +00:00
perror ( Topics . Path ) ;
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
2017-10-18 21:07:29 +00:00
PrintUsage ( char * BinaryLocation , config * DefaultConfig )
2017-08-29 20:35:28 +00:00
{
2018-01-03 22:16:20 +00:00
fprintf ( stderr ,
" Usage: %s [option(s)] filename(s) \n "
2017-08-29 20:35:28 +00:00
" \n "
" Options: \n "
2018-09-17 18:06:31 +00:00
" Paths: %s(advisedly universal, but may be set per-(sub)project as required)%s \n "
" -r <assets root directory> \n "
" Override default assets root directory ( \" %s \" ) \n "
" -R <assets root URL> \n "
" Override default assets root URL ( \" %s \" ) \n "
" %sIMPORTANT%s: -r and -R must correspond to the same location \n "
" %sUNSUPPORTED: If you move files from RootDir, the RootURL should \n "
" correspond to the resulting location%s \n "
2018-01-03 22:16:20 +00:00
" \n "
" -c <CSS directory path> \n "
" Override default CSS directory ( \" %s \" ), relative to root \n "
" -i <images directory path> \n "
" Override default images directory ( \" %s \" ), relative to root \n "
" -j <JS directory path> \n "
" Override default JS directory ( \" %s \" ), relative to root \n "
2018-09-17 18:06:31 +00:00
" -Q <revved resources query string> \n "
" Override default query string ( \" %s \" ) \n "
" To disable revved resources, set an empty string with -Q \" \" \n "
2018-01-03 22:16:20 +00:00
" \n "
" Project Settings: \n "
" -p <project ID> \n "
2018-09-17 18:06:31 +00:00
" Set the project ID, triggering PROJECT EDITION \n "
2018-01-03 22:16:20 +00:00
" -m <default medium> \n "
" Override default default medium ( \" %s \" ) \n "
2018-09-17 18:06:31 +00:00
" %sKnown project defaults: \n " ,
2018-01-03 22:16:20 +00:00
BinaryLocation ,
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , DefaultConfig - > RootDir ,
2018-01-03 22:16:20 +00:00
DefaultConfig - > RootURL ,
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ,
2018-01-03 22:16:20 +00:00
DefaultConfig - > CSSDir ,
DefaultConfig - > ImagesDir ,
DefaultConfig - > JSDir ,
2018-09-17 18:06:31 +00:00
DefaultConfig - > QueryString ,
2018-01-03 22:16:20 +00:00
2018-09-17 18:06:31 +00:00
DefaultConfig - > DefaultMedium ,
ColourStrings [ CS_COMMENT ] ) ;
2018-01-03 22:16:20 +00:00
for ( int ProjectIndex = 0 ; ProjectIndex < ArrayCount ( ProjectInfo ) ; + + ProjectIndex )
{
fprintf ( stderr , " %s: " ,
ProjectInfo [ ProjectIndex ] . ProjectID ) ;
// NOTE(matt): This kind of thing really needs to loop over the dudes once to find the longest one
for ( int i = 11 ; i > StringLength ( ProjectInfo [ ProjectIndex ] . ProjectID ) ; i - = 4 )
{
fprintf ( stderr , " \t " ) ;
}
fprintf ( stderr , " %s \n " ,
ProjectInfo [ ProjectIndex ] . Medium ) ;
}
fprintf ( stderr ,
2018-09-17 18:06:31 +00:00
" %s -s <style> \n "
2018-01-03 22:16:20 +00:00
" Set the style / theme, corresponding to a cinera__*.css file \n "
" This is equal to the \" project \" field in the HMML files by default \n "
" \n "
" Project Input Paths \n "
" -d <annotations directory> \n "
" Override default annotations directory ( \" %s \" ) \n "
" -t <templates directory> \n "
" Override default templates directory ( \" %s \" ) \n "
2017-12-12 23:24:10 +00:00
" \n "
2018-09-17 18:06:31 +00:00
" -x <search template location> \n "
" Set search template file path, either absolute or relative to \n "
2018-01-03 22:16:20 +00:00
" template directory, and enable integration \n "
" -y <player template location> \n "
" Set player template file path, either absolute or relative \n "
" to template directory, and enable integration \n "
" \n "
" Project Output Paths \n "
2017-12-12 23:24:10 +00:00
" -b <base output directory> \n "
" Override project's default base output directory ( \" %s \" ) \n "
" -B <base URL> \n "
" Override default base URL ( \" %s \" ) \n "
2018-01-03 22:16:20 +00:00
" NOTE: This must be set, if -n or -a are to be used \n "
2017-12-12 23:24:10 +00:00
" \n "
2018-09-17 18:06:31 +00:00
" -n <search location> \n "
" Override default search location ( \" %s \" ), relative to base \n "
2018-01-03 22:16:20 +00:00
" -a <player location> \n "
" Override default player location ( \" %s \" ), relative to base \n "
" NOTE: The PlayerURLPrefix is currently hardcoded in cinera.c but \n "
" will be configurable in the full configuration system \n "
2017-10-18 21:07:29 +00:00
" \n "
2018-01-03 22:16:20 +00:00
" Single Edition Output Path \n "
2017-12-12 23:24:10 +00:00
" -o <output location> \n "
2018-01-03 22:16:20 +00:00
" Override default output player location ( \" %s \" ) \n "
2017-10-18 21:07:29 +00:00
" \n "
2018-07-03 14:26:17 +00:00
" -1 \n "
2018-07-05 19:13:25 +00:00
" Open search result links in the same browser tab \n "
2018-07-03 14:26:17 +00:00
" NOTE: Ideal for a guide embedded in an iframe \n "
2017-12-12 23:24:10 +00:00
" -f \n "
" Force integration with an incomplete template \n "
2018-04-01 20:58:53 +00:00
" -g \n "
" Ignore video privacy status \n "
" NOTE: For use with projects whose videos are known to all be public, \n "
" to save us having to check their privacy status \n "
2018-07-03 14:26:17 +00:00
" -q \n "
" Quit after syncing with annotation files in project input directory \n "
2018-09-17 18:06:31 +00:00
" %sUNSUPPORTED: This is likely to be removed in the future%s \n "
2018-02-28 01:04:06 +00:00
" -w \n "
2018-09-17 18:06:31 +00:00
" Force quote cache rebuild %s(memory aid: \" wget \" )%s \n "
2017-12-12 23:24:10 +00:00
" \n "
" -l <n> \n "
" Override default log level (%d), where n is from 0 (terse) to 7 (verbose) \n "
2018-02-21 21:50:23 +00:00
" -u <seconds> \n "
2018-01-03 22:16:20 +00:00
" Override default update interval (%d) \n "
2017-08-29 20:35:28 +00:00
//" -c config location\n"
2018-07-03 14:26:17 +00:00
" \n "
" -e \n "
2018-09-17 18:06:31 +00:00
" Display (examine) database and exit \n "
2017-11-18 00:27:33 +00:00
" -v \n "
2018-02-21 21:50:23 +00:00
" Display version and exit \n "
2017-08-29 20:35:28 +00:00
" -h \n "
2018-02-21 21:50:23 +00:00
" Display this help \n "
2017-08-29 20:35:28 +00:00
" \n "
" Template: \n "
2018-09-17 18:06:31 +00:00
" A valid Search Template shall contain exactly one each of the following tags: \n "
2017-12-12 23:24:10 +00:00
" <!-- __CINERA_INCLUDES__ --> \n "
" to put inside your own <head></head> \n "
2018-09-17 18:06:31 +00:00
" <!-- __CINERA_SEARCH__ --> \n "
2017-12-07 01:15:13 +00:00
" \n "
2018-09-17 18:06:31 +00:00
" A valid Player Template shall contain exactly one each of the following tags: \n "
2017-12-12 23:24:10 +00:00
" <!-- __CINERA_INCLUDES__ --> \n "
" to put inside your own <head></head> \n "
2017-08-29 20:35:28 +00:00
" <!-- __CINERA_MENUS__ --> \n "
" <!-- __CINERA_PLAYER__ --> \n "
2017-12-12 23:24:10 +00:00
" <!-- __CINERA_SCRIPT__ --> \n "
" must come after <!-- __CINERA_MENUS__ --> and <!-- __CINERA_PLAYER__ --> \n "
2017-12-07 01:15:13 +00:00
" \n "
2018-02-28 01:04:06 +00:00
" Optional tags available for use in your Player Template: \n "
2018-02-21 21:50:23 +00:00
" <!-- __CINERA_TITLE__ --> \n "
" <!-- __CINERA_VIDEO_ID__ --> \n "
2018-09-19 19:50:21 +00:00
" <!-- __CINERA_VOD_PLATFORM__ --> \n "
2018-02-21 21:50:23 +00:00
" \n "
2018-09-17 18:06:31 +00:00
" Other tags available for use in either template: \n "
" Asset tags: \n "
" <!-- __CINERA_ASSET__ path.ext --> \n "
" General purpose tag that outputs the URL of the specified asset \n "
" relative to the Asset Root URL %s(-R)%s \n "
" <!-- __CINERA_IMAGE__ path.ext --> \n "
" General purpose tag that outputs the URL of the specified asset \n "
" relative to the Images Directory %s(-i)%s \n "
" <!-- __CINERA_CSS__ path.ext --> \n "
" Convenience tag that outputs a <link rel= \" stylesheet \" ...> node \n "
" for the specified asset relative to the CSS Directory %s(-c)%s, for \n "
" use inside your <head> block \n "
" <!-- __CINERA_JS__ path.ext --> \n "
" Convenience tag that outputs a <script type= \" text/javascript \" ...> \n "
" node for the specified asset relative to the JS Directory %s(-j)%s, \n "
" for use wherever a <script> node is valid \n "
" The path.ext in these tags supports parent directories to locate the \n "
" asset file relative to its specified type directory (generic, CSS, image \n "
" or JS), including the \" ../ \" directory, and paths containing spaces must \n "
" be surrounded with double-quotes ( \\ -escapable if the quoted path itself \n "
" contains double-quotes). \n "
" \n "
" All these asset tags additionally enable revving, appending a query \n "
" string %s(-Q)%s and the file's checksum to the URL. Changes to a file \n "
" trigger a rehash and edit of all HTML pages citing this asset. \n "
" \n "
2017-12-07 01:15:13 +00:00
" <!-- __CINERA_PROJECT__ --> \n "
2018-06-12 19:03:12 +00:00
" <!-- __CINERA_PROJECT_ID__ --> \n "
2018-09-19 19:50:21 +00:00
" <!-- __CINERA_SEARCH_URL__ --> \n "
2018-06-12 19:03:12 +00:00
" <!-- __CINERA_THEME__ --> \n "
2017-12-12 23:24:10 +00:00
" <!-- __CINERA_URL__ --> \n "
2018-09-17 18:06:31 +00:00
" Only really usable if BaseURL is set %s(-B)%s \n "
2018-02-23 23:36:42 +00:00
" <!-- __CINERA_CUSTOM0__ --> \n "
" <!-- __CINERA_CUSTOM1__ --> \n "
" <!-- __CINERA_CUSTOM2__ --> \n "
" ⋮ \n "
" <!-- __CINERA_CUSTOM15__ --> \n "
" Freeform buffers for small snippets of localised information, e.g. a \n "
" single <a> element or perhaps a <!-- comment --> \n "
" They correspond to the custom0 to custom15 attributes in the [video] \n "
" node in your .hmml files \n "
" 0 to 11 may hold up to 255 characters \n "
" 12 to 15 may hold up to 1023 characters \n "
2017-08-29 20:35:28 +00:00
" \n "
" HMML Specification: \n "
" https://git.handmade.network/Annotation-Pushers/Annotation-System/wikis/hmmlspec \n " ,
2018-01-03 22:16:20 +00:00
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_END ] ,
2018-01-03 22:16:20 +00:00
DefaultConfig - > ProjectDir ,
DefaultConfig - > TemplatesDir ,
DefaultConfig - > BaseDir ,
DefaultConfig - > BaseURL ,
2018-09-17 18:06:31 +00:00
DefaultConfig - > SearchLocation ,
2018-01-03 22:16:20 +00:00
DefaultConfig - > PlayerLocation ,
DefaultConfig - > OutLocation ,
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ,
2018-01-03 22:16:20 +00:00
DefaultConfig - > LogLevel ,
2018-09-17 18:06:31 +00:00
DefaultConfig - > UpdateInterval ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ) ;
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
2018-09-17 18:06:31 +00:00
char *
StripTrailingSlash ( char * String ) // NOTE(matt): For absolute paths
{
int Length = StringLength ( String ) ;
while ( Length > 0 & & String [ Length - 1 ] = = ' / ' )
{
String [ Length - 1 ] = ' \0 ' ;
- - Length ;
}
return String ;
}
char *
StripSurroundingSlashes ( char * String ) // NOTE(matt): For relative paths
{
char * Ptr = String ;
int Length = StringLength ( Ptr ) ;
if ( Length > 0 )
{
while ( ( Ptr [ 0 ] ) = = ' / ' )
{
+ + Ptr ;
- - Length ;
}
while ( Ptr [ Length - 1 ] = = ' / ' )
{
Ptr [ Length - 1 ] = ' \0 ' ;
- - Length ;
}
}
return Ptr ;
}
void
ConsumeWhitespace ( buffer * Buffer , char * * Ptr )
{
while ( * Ptr - Buffer - > Location < Buffer - > Size )
{
switch ( * * Ptr )
{
case ' ' :
case ' \t ' :
case ' \n ' :
+ + * Ptr ; break ;
default : return ;
}
}
}
int
ParseQuotedString ( buffer * Dest , buffer * Src , char * * Ptr , enum8 ( template_tag_codes ) TagIndex )
{
char * End ;
bool MissingPath = FALSE ;
bool MissingClosingQuote = FALSE ;
if ( * * Ptr = = ' \" ' )
{
+ + * Ptr ;
End = * Ptr ;
while ( End - Src - > Location < Src - > Size & & End - * Ptr < Dest - > Size )
{
switch ( * End )
{
case ' - ' :
{
if ( ( End + 2 ) - Src - > Location < Src - > Size & & End [ 1 ] = = ' - ' & & End [ 2 ] = = ' > ' )
{
MissingClosingQuote = TRUE ;
goto Finalise ;
}
}
case ' \" ' : goto Finalise ;
case ' \\ ' : + + End ;
default : * Dest - > Ptr + + = * End + + ; break ;
}
}
}
else
{
End = * Ptr ;
if ( ( End + 3 ) - Src - > Location < Src - > Size & & End [ 0 ] = = ' - ' & & End [ 1 ] = = ' - ' & & End [ 2 ] = = ' > ' )
{
MissingPath = TRUE ;
goto Finalise ;
}
while ( End - Src - > Location < Src - > Size & & * End ! = ' ' & & Dest - > Ptr - Dest - > Location < Dest - > Size )
{
* Dest - > Ptr + + = * End + + ;
}
}
* Dest - > Ptr = ' \0 ' ;
Finalise :
if ( MissingClosingQuote )
{
printf ( " %sQuoted string%s seems to be missing its closing quote mark: \n "
" %.*s \n " , ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] , ( int ) ( End - * Ptr ) , * Ptr ) ;
return RC_ERROR_PARSING ;
}
if ( MissingPath | | StringLength ( Dest - > Location ) = = 0 )
{
printf ( " %s%s tag%s seems to be missing a file path \n "
" %.*s \n " , ColourStrings [ CS_ERROR ] , TemplateTags [ TagIndex ] , ColourStrings [ CS_END ] , ( int ) ( End - * Ptr ) , * Ptr ) ;
return RC_ERROR_PARSING ;
}
if ( End - * Ptr = = Dest - > Size )
{
printf ( " %sAsset file path%s is too long (we support paths up to %d characters): \n "
" %.*s... \n " , ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] , MAX_ASSET_FILENAME_LENGTH , ( int ) ( End - * Ptr ) , * Ptr ) ;
return RC_ERROR_PARSING ;
}
return RC_SUCCESS ;
}
void
StripPWDIndicators ( char * Path )
{
buffer B ;
ClaimBuffer ( & B , " PWDStrippedPtr " , ( StringLength ( Path ) + 1 ) * 2 ) ;
CopyStringToBufferNoFormat ( & B , Path ) ;
B . Ptr = B . Location ;
if ( B . Size > = 2 & & B . Ptr [ 0 ] = = ' . ' & & B . Ptr [ 1 ] = = ' / ' )
{
char * NextComponentHead = B . Ptr + 2 ;
int RemainingChars = StringLength ( NextComponentHead ) ;
CopyStringToBufferNoFormat ( & B , NextComponentHead ) ;
Clear ( B . Ptr , B . Size - ( B . Ptr - B . Location ) ) ;
B . Ptr - = RemainingChars ;
}
while ( SeekBufferForString ( & B , " /./ " , C_SEEK_FORWARDS , C_SEEK_END ) = = RC_SUCCESS )
{
char * NextComponentHead = B . Ptr ;
int RemainingChars = StringLength ( NextComponentHead ) ;
- - B . Ptr ;
SeekBufferForString ( & B , " / " , C_SEEK_BACKWARDS , C_SEEK_START ) ;
CopyStringToBufferNoFormat ( & B , NextComponentHead ) ;
Clear ( B . Ptr , B . Size - ( B . Ptr - B . Location ) ) ;
B . Ptr - = RemainingChars ;
}
CopyStringNoFormat ( Path , StringLength ( Path ) + 1 , B . Location ) ;
DeclaimBuffer ( & B ) ;
}
int
2018-09-19 19:50:21 +00:00
ParseAssetString ( template * Template , enum8 ( template_tag_codes ) TagIndex , uint32_t * AssetIndexPtr )
2018-09-17 18:06:31 +00:00
{
2018-09-19 19:50:21 +00:00
char * Ptr = Template - > File . Buffer . Ptr + StringLength ( TemplateTags [ TagIndex ] ) ;
ConsumeWhitespace ( & Template - > File . Buffer , & Ptr ) ;
2018-09-17 18:06:31 +00:00
buffer AssetString ;
ClaimBuffer ( & AssetString , " AssetString " , MAX_ASSET_FILENAME_LENGTH ) ;
2018-09-19 19:50:21 +00:00
if ( ParseQuotedString ( & AssetString , & Template - > File . Buffer , & Ptr , TagIndex ) = = RC_ERROR_PARSING )
2018-09-17 18:06:31 +00:00
{
DeclaimBuffer ( & AssetString ) ;
return RC_ERROR_PARSING ;
}
int AssetType = 0 ;
switch ( TagIndex )
{
case TAG_ASSET : AssetType = ASSET_GENERIC ; break ;
case TAG_CSS : AssetType = ASSET_CSS ; break ;
case TAG_IMAGE : AssetType = ASSET_IMG ; break ;
case TAG_JS : AssetType = ASSET_JS ; break ;
}
AssetString . Ptr = StripSurroundingSlashes ( AssetString . Location ) ;
StripPWDIndicators ( AssetString . Ptr ) ;
PushAsset ( AssetString . Ptr , AssetType , AssetIndexPtr ) ;
DeclaimBuffer ( & AssetString ) ;
return RC_SUCCESS ;
}
2017-08-29 20:35:28 +00:00
int
2018-09-19 19:50:21 +00:00
PackTemplate ( template * Template , char * Location , enum8 ( template_types ) Type )
2017-08-29 20:35:28 +00:00
{
2018-06-12 19:03:12 +00:00
// TODO(matt): Record line numbers and contextual information:
// <? ?>
// <!-- -->
// < >
// <script </script>
2018-09-19 19:50:21 +00:00
InitTemplate ( Template , Location , Type ) ;
2018-01-03 22:16:20 +00:00
buffer Errors ;
2018-09-19 19:50:21 +00:00
if ( ClaimBuffer ( & Errors , " Errors " , Kilobytes ( 1 ) ) = = RC_ARENA_FULL ) { FreeTemplate ( Template ) ; return RC_ARENA_FULL ; } ;
2017-09-07 21:41:08 +00:00
2017-10-18 21:07:29 +00:00
bool HaveErrors = FALSE ;
2018-09-17 18:06:31 +00:00
bool HaveAssetParsingErrors = FALSE ;
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 ;
2018-09-17 18:06:31 +00:00
bool FoundSearch = FALSE ;
2017-06-25 18:22:54 +00:00
2018-09-19 19:50:21 +00:00
char * Previous = Template - > File . Buffer . Location ;
2017-09-07 21:41:08 +00:00
2018-09-19 19:50:21 +00:00
while ( Template - > File . Buffer . Ptr - Template - > File . Buffer . Location < Template - > File . Buffer . Size )
2017-05-13 15:38:10 +00:00
{
2018-02-23 23:36:42 +00:00
NextTagSearch :
2018-09-19 19:50:21 +00:00
if ( * Template - > File . Buffer . Ptr = = ' ! ' & & ( Template - > File . Buffer . Ptr > Template - > File . Buffer . Location & & ! StringsDifferT ( " <!-- " , & Template - > File . Buffer . Ptr [ - 1 ] , 0 ) ) )
2017-05-13 15:38:10 +00:00
{
2018-09-19 19:50:21 +00:00
char * CommentStart = & Template - > File . Buffer . Ptr [ - 1 ] ;
Template - > File . Buffer . Ptr + = StringLength ( " !-- " ) ;
while ( Template - > File . Buffer . Ptr - Template - > File . Buffer . Location < Template - > File . Buffer . Size & & StringsDifferT ( " --> " , Template - > File . Buffer . Ptr , 0 ) )
2017-06-25 18:05:58 +00:00
{
2018-09-17 18:06:31 +00:00
for ( int TagIndex = 0 ; TagIndex < TEMPLATE_TAG_COUNT ; + + TagIndex )
2017-05-13 15:38:10 +00:00
{
2018-09-19 19:50:21 +00:00
if ( ! ( StringsDifferT ( TemplateTags [ TagIndex ] , Template - > File . Buffer . 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
*
*/
2018-02-23 23:36:42 +00:00
2018-09-19 19:50:21 +00:00
uint32_t AssetIndex = 0 ;
2018-09-17 18:06:31 +00:00
switch ( TagIndex )
2017-08-29 20:35:28 +00:00
{
2018-09-17 18:06:31 +00:00
case TAG_SEARCH :
FoundSearch = TRUE ;
2018-02-23 23:36:42 +00:00
goto RecordTag ;
2017-09-07 21:41:08 +00:00
case TAG_INCLUDES :
2018-04-01 20:58:53 +00:00
if ( ! ( Config . Mode & MODE_FORCEINTEGRATION ) & & FoundIncludes = = TRUE )
2017-09-07 21:41:08 +00:00
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & Errors , " Template contains more than one <!-- %s --> tag \n " , TemplateTags [ TagIndex ] ) ;
2017-09-07 21:41:08 +00:00
HaveErrors = TRUE ;
}
FoundIncludes = TRUE ;
2018-02-23 23:36:42 +00:00
goto RecordTag ;
2017-09-07 21:41:08 +00:00
case TAG_MENUS :
2018-04-01 20:58:53 +00:00
if ( ! ( Config . Mode & MODE_FORCEINTEGRATION ) & & FoundMenus = = TRUE )
2017-09-07 21:41:08 +00:00
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & Errors , " Template contains more than one <!-- %s --> tag \n " , TemplateTags [ TagIndex ] ) ;
2017-09-07 21:41:08 +00:00
HaveErrors = TRUE ;
}
FoundMenus = TRUE ;
2018-02-23 23:36:42 +00:00
goto RecordTag ;
2017-09-07 21:41:08 +00:00
case TAG_PLAYER :
2018-04-01 20:58:53 +00:00
if ( ! ( Config . Mode & MODE_FORCEINTEGRATION ) & & FoundPlayer = = TRUE )
2017-09-07 21:41:08 +00:00
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & Errors , " Template contains more than one <!-- %s --> tag \n " , TemplateTags [ TagIndex ] ) ;
2017-09-07 21:41:08 +00:00
HaveErrors = TRUE ;
}
FoundPlayer = TRUE ;
2018-02-23 23:36:42 +00:00
goto RecordTag ;
2017-09-07 21:41:08 +00:00
case TAG_SCRIPT :
2018-04-01 20:58:53 +00:00
if ( ! ( Config . Mode & MODE_FORCEINTEGRATION ) & & ( FoundMenus = = FALSE | | FoundPlayer = = FALSE ) )
2017-09-07 21:41:08 +00:00
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & Errors , " <!-- %s --> must come after <!-- __CINERA_MENUS__ --> and <!-- __CINERA_PLAYER__ --> \n " , TemplateTags [ TagIndex ] ) ;
2017-09-07 21:41:08 +00:00
HaveErrors = TRUE ;
}
2018-04-01 20:58:53 +00:00
if ( ! ( Config . Mode & MODE_FORCEINTEGRATION ) & & FoundScript = = TRUE )
2017-09-07 21:41:08 +00:00
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & Errors , " Template contains more than one <!-- %s --> tag \n " , TemplateTags [ TagIndex ] ) ;
2017-09-07 21:41:08 +00:00
HaveErrors = TRUE ;
}
FoundScript = TRUE ;
2018-02-23 23:36:42 +00:00
goto RecordTag ;
2018-09-17 18:06:31 +00:00
case TAG_ASSET :
case TAG_CSS :
case TAG_IMAGE :
case TAG_JS :
{
2018-09-19 19:50:21 +00:00
if ( ParseAssetString ( Template , TagIndex , & AssetIndex ) = = RC_ERROR_PARSING )
2018-09-17 18:06:31 +00:00
{
HaveErrors = TRUE ;
HaveAssetParsingErrors = TRUE ;
}
} goto RecordTag ;
2018-02-23 23:36:42 +00:00
default : // NOTE(matt): All freely usable tags should hit this case
RecordTag :
2018-09-19 19:50:21 +00:00
{
int Offset = CommentStart - Previous ;
PushTemplateTag ( Template , Offset , TagIndex , AssetIndex ) ;
DepartComment ( & Template - > File . Buffer ) ;
Previous = Template - > File . Buffer . Ptr ;
goto NextTagSearch ;
}
2017-09-07 21:41:08 +00:00
} ;
2017-05-13 15:38:10 +00:00
}
}
2018-09-19 19:50:21 +00:00
+ + Template - > File . Buffer . Ptr ;
2017-08-29 20:35:28 +00:00
}
}
else
{
2018-09-19 19:50:21 +00:00
+ + Template - > File . Buffer . Ptr ;
2017-08-29 20:35:28 +00:00
}
}
2018-09-17 18:06:31 +00:00
if ( HaveAssetParsingErrors )
{
DeclaimBuffer ( & Errors ) ;
2018-09-19 19:50:21 +00:00
FreeTemplate ( Template ) ;
2018-09-17 18:06:31 +00:00
return RC_INVALID_TEMPLATE ;
}
2018-09-20 00:13:41 +00:00
if ( ! HaveErrors & & FoundIncludes & & FoundSearch )
2017-09-07 21:41:08 +00:00
{
2018-09-19 19:50:21 +00:00
Template - > Metadata . Validity | = PAGE_SEARCH ;
2017-09-07 21:41:08 +00:00
}
2017-08-29 20:35:28 +00:00
if ( ! HaveErrors & & FoundIncludes & & FoundMenus & & FoundPlayer & & FoundScript )
{
2018-09-19 19:50:21 +00:00
Template - > Metadata . Validity | = PAGE_PLAYER ;
2017-08-29 20:35:28 +00:00
}
2017-09-07 21:41:08 +00:00
2018-04-01 20:58:53 +00:00
if ( ! ( Config . Mode & MODE_FORCEINTEGRATION ) )
2017-08-29 20:35:28 +00:00
{
2018-09-19 19:50:21 +00:00
if ( Type = = TEMPLATE_SEARCH & & ! ( Template - > Metadata . Validity & PAGE_SEARCH ) )
2017-09-07 21:41:08 +00:00
{
2018-09-20 00:13:41 +00:00
if ( ! FoundIncludes ) { CopyStringToBuffer ( & Errors , " %sSearch template%s must include one <!-- __CINERA_INCLUDES__ --> tag \n " , ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] ) ; } ;
if ( ! FoundSearch ) { CopyStringToBuffer ( & Errors , " %sSearch template%s must include one <!-- __CINERA_SEARCH__ --> tag \n " , ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] ) ; } ;
2018-01-03 22:16:20 +00:00
fprintf ( stderr , " %s " , Errors . Location ) ;
DeclaimBuffer ( & Errors ) ;
2018-09-19 19:50:21 +00:00
FreeTemplate ( Template ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_TEMPLATE ;
}
2018-09-19 19:50:21 +00:00
else if ( ( Type = = TEMPLATE_PLAYER | | Type = = TEMPLATE_BESPOKE ) & & ! ( Template - > Metadata . Validity & PAGE_PLAYER ) )
2017-08-29 20:35:28 +00:00
{
2018-09-20 00:13:41 +00:00
if ( ! FoundIncludes ) { CopyStringToBuffer ( & Errors , " %s%slayer template%s must include one <!-- __CINERA_INCLUDES__ --> tag \n " , ColourStrings [ CS_ERROR ] , Type = = TEMPLATE_BESPOKE ? " Bespoke p " : " P " , ColourStrings [ CS_END ] ) ; } ;
if ( ! FoundMenus ) { CopyStringToBuffer ( & Errors , " %s%slayer template%s must include one <!-- __CINERA_MENUS__ --> tag \n " , ColourStrings [ CS_ERROR ] , Type = = TEMPLATE_BESPOKE ? " Bespoke p " : " P " , ColourStrings [ CS_END ] ) ; } ;
if ( ! FoundPlayer ) { CopyStringToBuffer ( & Errors , " %s%slayer template%s must include one <!-- __CINERA_PLAYER__ --> tag \n " , ColourStrings [ CS_ERROR ] , Type = = TEMPLATE_BESPOKE ? " Bespoke p " : " P " , ColourStrings [ CS_END ] ) ; } ;
if ( ! FoundScript ) { CopyStringToBuffer ( & Errors , " %s%slayer template%s must include one <!-- __CINERA_SCRIPT__ --> tag \n " , ColourStrings [ CS_ERROR ] , Type = = TEMPLATE_BESPOKE ? " Bespoke p " : " P " , ColourStrings [ CS_END ] ) ; } ;
2018-01-03 22:16:20 +00:00
fprintf ( stderr , " %s " , Errors . Location ) ;
DeclaimBuffer ( & Errors ) ;
2018-09-19 19:50:21 +00:00
FreeTemplate ( Template ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_TEMPLATE ;
2017-08-29 20:35:28 +00:00
}
}
2018-01-03 22:16:20 +00:00
DeclaimBuffer ( & Errors ) ;
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
}
2017-12-12 23:24:10 +00:00
void
2018-09-17 18:06:31 +00:00
ConstructSearchURL ( buffer * SearchURL )
2017-12-12 23:24:10 +00:00
{
2018-09-17 18:06:31 +00:00
RewindBuffer ( SearchURL ) ;
2017-12-12 23:24:10 +00:00
if ( StringsDiffer ( Config . BaseURL , " " ) )
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( SearchURL , " %s/ " , Config . BaseURL ) ;
if ( StringsDiffer ( Config . SearchLocation , " " ) )
2017-12-12 23:24:10 +00:00
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( SearchURL , " %s/ " , Config . SearchLocation ) ;
2017-12-12 23:24:10 +00:00
}
}
}
void
ConstructPlayerURL ( buffer * PlayerURL , char * BaseFilename )
{
RewindBuffer ( PlayerURL ) ;
if ( StringsDiffer ( Config . BaseURL , " " ) )
{
CopyStringToBuffer ( PlayerURL , " %s/ " , Config . BaseURL ) ;
if ( StringsDiffer ( Config . PlayerLocation , " " ) )
{
CopyStringToBuffer ( PlayerURL , " %s/ " , Config . PlayerLocation ) ;
}
}
if ( StringsDiffer ( BaseFilename , " " ) )
{
if ( StringsDiffer ( Config . PlayerURLPrefix , " " ) )
{
char * Ptr = BaseFilename + StringLength ( Config . ProjectID ) ;
2018-01-03 22:16:20 +00:00
CopyStringToBuffer ( PlayerURL , " %s%s/ " , Config . PlayerURLPrefix , Ptr ) ;
2017-12-12 23:24:10 +00:00
}
else
{
2018-01-03 22:16:20 +00:00
CopyStringToBuffer ( PlayerURL , " %s/ " , BaseFilename ) ;
2017-12-12 23:24:10 +00:00
}
}
}
2018-01-03 22:16:20 +00:00
bool
MediumExists ( char * Medium )
{
for ( int i = 0 ; i < ArrayCount ( CategoryMedium ) ; + + i )
{
if ( ! StringsDiffer ( Medium , CategoryMedium [ i ] . Medium ) )
{
return TRUE ;
}
}
fprintf ( stderr , " Specified default medium \" %s \" not available. Valid media are: \n " , Medium ) ;
for ( int i = 0 ; i < ArrayCount ( CategoryMedium ) ; + + i )
{
fprintf ( stderr , " %s \n " , CategoryMedium [ i ] . Medium ) ;
}
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " To have \" %s \" added to the list, contact miblodelcarpio@gmail.com \n " , Medium ) ;
2018-01-03 22:16:20 +00:00
return FALSE ;
}
2018-02-21 21:50:23 +00:00
typedef struct
{
char * BaseFilename ;
char * Title ;
} neighbour ;
typedef struct
{
neighbour Prev ;
neighbour Next ;
} neighbours ;
2018-09-17 18:06:31 +00:00
void
ExamineDB1 ( file_buffer File )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
database1 LocalDB ;
LocalDB . Header = * ( db_header1 * ) File . Buffer . Location ;
printf ( " Current: \n "
" \t DBVersion: %d \n "
" \t AppVersion: %d.%d.%d \n "
" \t HMMLVersion: %d.%d.%d \n "
" \n "
" Entries: %d \n " ,
LocalDB . Header . DBVersion ,
LocalDB . Header . AppVersion . Major , LocalDB . Header . AppVersion . Minor , LocalDB . Header . AppVersion . Patch ,
LocalDB . Header . HMMLVersion . Major , LocalDB . Header . HMMLVersion . Minor , LocalDB . Header . HMMLVersion . Patch ,
LocalDB . Header . EntryCount ) ;
File . Buffer . Ptr = File . Buffer . Location + sizeof ( LocalDB . Header ) ;
for ( int EntryIndex = 0 ; EntryIndex < LocalDB . Header . EntryCount ; + + EntryIndex )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
LocalDB . Entry = * ( db_entry1 * ) File . Buffer . Ptr ;
printf ( " %3d \t %s%sSize: %4d \n " ,
EntryIndex + 1 , LocalDB . Entry . BaseFilename ,
StringLength ( LocalDB . Entry . BaseFilename ) > 8 ? " \t " : " \t \t " , // NOTE(matt): Janktasm
LocalDB . Entry . Size ) ;
File . Buffer . Ptr + = sizeof ( LocalDB . Entry ) ;
}
}
void
ExamineDB2 ( file_buffer File )
{
database2 LocalDB ;
LocalDB . Header = * ( db_header2 * ) File . Buffer . Location ;
printf ( " Current: \n "
" \t DBVersion: %d \n "
" \t AppVersion: %d.%d.%d \n "
" \t HMMLVersion: %d.%d.%d \n "
" \n "
" Relative Search Page Location: %s \n "
" Relative Player Page Location: %s \n "
" \n "
" Entries: %d \n " ,
LocalDB . Header . DBVersion ,
LocalDB . Header . AppVersion . Major , LocalDB . Header . AppVersion . Minor , LocalDB . Header . AppVersion . Patch ,
LocalDB . Header . HMMLVersion . Major , LocalDB . Header . HMMLVersion . Minor , LocalDB . Header . HMMLVersion . Patch ,
LocalDB . Header . SearchLocation [ 0 ] ! = ' \0 ' ? LocalDB . Header . SearchLocation : " (same as Base URL) " ,
LocalDB . Header . PlayerLocation [ 0 ] ! = ' \0 ' ? LocalDB . Header . PlayerLocation : " (directly descended from Base URL) " ,
LocalDB . Header . EntryCount ) ;
File . Buffer . Ptr = File . Buffer . Location + sizeof ( LocalDB . Header ) ;
for ( int EntryIndex = 0 ; EntryIndex < LocalDB . Header . EntryCount ; + + EntryIndex )
{
LocalDB . Entry = * ( db_entry2 * ) File . Buffer . Ptr ;
printf ( " %3d \t %s%sSize: %4d \n " ,
EntryIndex + 1 , LocalDB . Entry . BaseFilename ,
StringLength ( LocalDB . Entry . BaseFilename ) > 8 ? " \t " : " \t \t " , // NOTE(matt): Janktasm
LocalDB . Entry . Size ) ;
File . Buffer . Ptr + = sizeof ( LocalDB . Entry ) ;
2018-02-21 21:50:23 +00:00
}
2018-09-17 18:06:31 +00:00
}
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
void
ExamineDB3 ( file_buffer File )
{
database3 LocalDB ;
LocalDB . Header = * ( db_header3 * ) File . Buffer . Location ;
2018-02-21 21:50:23 +00:00
printf ( " Current: \n "
" \t DBVersion: %d \n "
" \t AppVersion: %d.%d.%d \n "
" \t HMMLVersion: %d.%d.%d \n "
" \n "
" Initial: \n "
" \t DBVersion: %d \n "
" \t AppVersion: %d.%d.%d \n "
" \t HMMLVersion: %d.%d.%d \n "
" \n "
" Project ID: %s \n "
" Project Full Name: %s \n "
" \n "
" Base URL: %s \n "
2018-09-17 18:06:31 +00:00
" Relative Search Page Location: %s \n "
2018-02-21 21:50:23 +00:00
" Relative Player Page Location: %s \n "
" Player Page URL Prefix: %s \n "
" \n "
" Entries: %d \n " ,
2018-09-17 18:06:31 +00:00
LocalDB . Header . CurrentDBVersion ,
LocalDB . Header . CurrentAppVersion . Major , LocalDB . Header . CurrentAppVersion . Minor , LocalDB . Header . CurrentAppVersion . Patch ,
LocalDB . Header . CurrentHMMLVersion . Major , LocalDB . Header . CurrentHMMLVersion . Minor , LocalDB . Header . CurrentHMMLVersion . Patch ,
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
LocalDB . Header . InitialDBVersion ,
LocalDB . Header . InitialAppVersion . Major , LocalDB . Header . InitialAppVersion . Minor , LocalDB . Header . InitialAppVersion . Patch ,
LocalDB . Header . InitialHMMLVersion . Major , LocalDB . Header . InitialHMMLVersion . Minor , LocalDB . Header . InitialHMMLVersion . Patch ,
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
LocalDB . Header . ProjectID ,
LocalDB . Header . ProjectName ,
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
LocalDB . Header . BaseURL ,
LocalDB . Header . SearchLocation [ 0 ] ! = ' \0 ' ? LocalDB . Header . SearchLocation : " (same as Base URL) " ,
LocalDB . Header . PlayerLocation [ 0 ] ! = ' \0 ' ? LocalDB . Header . PlayerLocation : " (directly descended from Base URL) " ,
LocalDB . Header . PlayerURLPrefix [ 0 ] ! = ' \0 ' ? LocalDB . Header . PlayerURLPrefix : " (no special prefix, the player page URLs equal their entry's base filename) " ,
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
LocalDB . Header . EntryCount ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
File . Buffer . Ptr = File . Buffer . Location + sizeof ( LocalDB . Header ) ;
for ( int EntryIndex = 0 ; EntryIndex < LocalDB . Header . EntryCount ; + + EntryIndex )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
LocalDB . Entry = * ( db_entry3 * ) File . Buffer . Ptr ;
2018-02-21 21:50:23 +00:00
printf ( " %3d \t %s%sSize: %4d \t %d \t %d \t %d \t %d \n "
" \t %s \n " ,
2018-09-17 18:06:31 +00:00
EntryIndex + 1 , LocalDB . Entry . BaseFilename ,
StringLength ( LocalDB . Entry . BaseFilename ) > 8 ? " \t " : " \t \t " , // NOTE(matt): Janktasm
LocalDB . Entry . Size ,
LocalDB . Entry . LinkOffsets . PrevStart ,
LocalDB . Entry . LinkOffsets . PrevEnd ,
LocalDB . Entry . LinkOffsets . NextStart ,
LocalDB . Entry . LinkOffsets . NextEnd ,
LocalDB . Entry . Title ) ;
File . Buffer . Ptr + = sizeof ( LocalDB . Entry ) ;
}
2018-02-21 21:50:23 +00:00
}
2018-09-17 18:06:31 +00:00
void
ExamineDB4 ( file_buffer File )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
database4 LocalDB ;
LocalDB . Header = * ( db_header4 * ) File . Buffer . Location ;
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
LocalDB . EntriesHeader = * ( db_header_entries4 * ) ( File . Buffer . Location + sizeof ( LocalDB . Header ) ) ;
printf ( " Current: \n "
" \t DBVersion: %d \n "
" \t AppVersion: %d.%d.%d \n "
" \t HMMLVersion: %d.%d.%d \n "
" \n "
" Initial: \n "
" \t DBVersion: %d \n "
" \t AppVersion: %d.%d.%d \n "
" \t HMMLVersion: %d.%d.%d \n "
" \n "
" Database File Location: %s/%s.metadata \n "
" Search File Location: %s/%s.index \n "
" \n "
" Project ID: %s \n "
" Project Full Name: %s \n "
" \n "
" Base URL: %s \n "
" Relative Search Page Location: %s \n "
" Relative Player Page Location: %s \n "
" Player Page URL Prefix: %s \n "
" \n "
" Entries: %d \n " ,
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
LocalDB . Header . CurrentDBVersion ,
LocalDB . Header . CurrentAppVersion . Major , LocalDB . Header . CurrentAppVersion . Minor , LocalDB . Header . CurrentAppVersion . Patch ,
LocalDB . Header . CurrentHMMLVersion . Major , LocalDB . Header . CurrentHMMLVersion . Minor , LocalDB . Header . CurrentHMMLVersion . Patch ,
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
LocalDB . Header . InitialDBVersion ,
LocalDB . Header . InitialAppVersion . Major , LocalDB . Header . InitialAppVersion . Minor , LocalDB . Header . InitialAppVersion . Patch ,
LocalDB . Header . InitialHMMLVersion . Major , LocalDB . Header . InitialHMMLVersion . Minor , LocalDB . Header . InitialHMMLVersion . Patch ,
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
Config . BaseDir , Config . ProjectID ,
Config . BaseDir , Config . ProjectID ,
LocalDB . EntriesHeader . ProjectID ,
LocalDB . EntriesHeader . ProjectName ,
LocalDB . EntriesHeader . BaseURL ,
LocalDB . EntriesHeader . SearchLocation [ 0 ] ! = ' \0 ' ? LocalDB . EntriesHeader . SearchLocation : " (same as Base URL) " ,
LocalDB . EntriesHeader . PlayerLocation [ 0 ] ! = ' \0 ' ? LocalDB . EntriesHeader . PlayerLocation : " (directly descended from Base URL) " ,
LocalDB . EntriesHeader . PlayerURLPrefix [ 0 ] ! = ' \0 ' ? LocalDB . EntriesHeader . PlayerURLPrefix : " (no special prefix, the player page URLs equal their entry's base filename) " ,
LocalDB . EntriesHeader . Count ) ;
File . Buffer . Ptr = File . Buffer . Location + sizeof ( LocalDB . Header ) + sizeof ( LocalDB . EntriesHeader ) ;
for ( int EntryIndex = 0 ; EntryIndex < LocalDB . EntriesHeader . Count ; + + EntryIndex )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
LocalDB . Entry = * ( db_entry4 * ) File . Buffer . Ptr ;
printf ( " %3d \t %s%sSize: %4d \t %d \t %d \t %d \t %d \n "
" \t %s \n " ,
EntryIndex , LocalDB . Entry . BaseFilename ,
StringLength ( LocalDB . Entry . BaseFilename ) > 8 ? " \t " : " \t \t " , // NOTE(matt): Janktasm
LocalDB . Entry . Size ,
LocalDB . Entry . LinkOffsets . PrevStart ,
LocalDB . Entry . LinkOffsets . PrevEnd ,
LocalDB . Entry . LinkOffsets . NextStart ,
LocalDB . Entry . LinkOffsets . NextEnd ,
LocalDB . Entry . Title ) ;
File . Buffer . Ptr + = sizeof ( LocalDB . Entry ) ;
}
LocalDB . AssetsHeader = * ( db_header_assets4 * ) File . Buffer . Ptr ;
File . Buffer . Ptr + = sizeof ( LocalDB . AssetsHeader ) ;
printf ( " \n "
" Asset Root Directory: %s \n "
" Asset Root URL: %s \n "
" CSS Directory: %s \n "
" Images Directory: %s \n "
" JavaScript Directory: %s \n "
" Assets: %d \n "
" \n "
" %sLandmarks are displayed%s i•%sp%s %swhere i is the Entry Index and p is the Position \n "
" in bytes into the HTML file. Entry Index%s -1 %scorresponds to the Search Page%s \n " ,
LocalDB . AssetsHeader . RootDir ,
LocalDB . AssetsHeader . RootURL ,
StringsDiffer ( LocalDB . AssetsHeader . CSSDir , " " ) ? LocalDB . AssetsHeader . CSSDir : " (same as root) " ,
StringsDiffer ( LocalDB . AssetsHeader . ImagesDir , " " ) ? LocalDB . AssetsHeader . ImagesDir : " (same as root) " ,
StringsDiffer ( LocalDB . AssetsHeader . JSDir , " " ) ? LocalDB . AssetsHeader . JSDir : " (same as root) " ,
LocalDB . AssetsHeader . Count ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ,
ColourStrings [ CS_PURPLE ] , ColourStrings [ CS_END ] ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ) ;
for ( int AssetIndex = 0 ; AssetIndex < LocalDB . AssetsHeader . Count ; + + AssetIndex )
{
LocalDB . Asset = * ( db_asset4 * ) File . Buffer . Ptr ;
printf ( " \n "
" %s \n "
" Type: %s \n "
" Checksum: %08x \n "
" Landmarks: %d \n " ,
LocalDB . Asset . Filename ,
AssetTypeNames [ LocalDB . Asset . Type ] ,
LocalDB . Asset . Hash ,
LocalDB . Asset . LandmarkCount ) ;
File . Buffer . Ptr + = sizeof ( LocalDB . Asset ) ;
for ( int LandmarkIndex = 0 ; LandmarkIndex < LocalDB . Asset . LandmarkCount ; + + LandmarkIndex )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
LocalDB . Landmark = * ( db_landmark * ) File . Buffer . Ptr ;
File . Buffer . Ptr + = sizeof ( LocalDB . Landmark ) ;
printf ( " %d•%s%d%s " , LocalDB . Landmark . EntryIndex , ColourStrings [ CS_PURPLE ] , LocalDB . Landmark . Position , ColourStrings [ CS_END ] ) ;
2018-04-01 20:58:53 +00:00
}
2018-09-17 18:06:31 +00:00
printf ( " \n " ) ;
2018-04-01 20:58:53 +00:00
}
2018-09-17 18:06:31 +00:00
}
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
int
ExamineDB ( void )
{
file_buffer DBFile ;
DBFile . Buffer . ID = " DBFile " ;
CopyString ( DBFile . Path , sizeof ( DBFile . Path ) , " %s/%s.metadata " , Config . BaseDir , Config . ProjectID ) ;
int DBFileReadCode = ReadFileIntoBuffer ( & DBFile , 0 ) ;
switch ( DBFileReadCode )
{
case RC_ERROR_MEMORY :
return RC_ERROR_MEMORY ;
case RC_ERROR_FILE :
fprintf ( stderr , " Unable to open metadata file %s: %s \n " , DBFile . Path , strerror ( errno ) ) ;
return RC_ERROR_FILE ;
case RC_SUCCESS :
2018-04-01 20:58:53 +00:00
break ;
}
2018-09-17 18:06:31 +00:00
uint32_t FirstInt = * ( uint32_t * ) DBFile . Buffer . Location ;
if ( FirstInt ! = FOURCC ( " CNRA " ) )
{
switch ( FirstInt )
{
case 1 : ExamineDB1 ( DBFile ) ; break ;
case 2 : ExamineDB2 ( DBFile ) ; break ;
case 3 : ExamineDB3 ( DBFile ) ; break ;
default : printf ( " Invalid metadata file: %s \n " , DBFile . Path ) ; break ;
}
}
else
{
uint32_t SecondInt = * ( uint32_t * ) ( DBFile . Buffer . Location + sizeof ( uint32_t ) ) ;
switch ( SecondInt )
{
case 4 : ExamineDB4 ( DBFile ) ; break ;
default : printf ( " Invalid metadata file: %s \n " , DBFile . Path ) ; break ;
}
}
FreeBuffer ( & DBFile . Buffer ) ;
2018-04-01 20:58:53 +00:00
return RC_SUCCESS ;
}
2018-02-05 23:57:17 +00:00
# define HMMLCleanup() \
DeclaimBuffer ( & CreditsMenu ) ; \
DeclaimBuffer ( & FilterMedia ) ; \
DeclaimBuffer ( & FilterTopics ) ; \
DeclaimBuffer ( & FilterMenu ) ; \
DeclaimBuffer ( & ReferenceMenu ) ; \
DeclaimBuffer ( & QuoteMenu ) ; \
hmml_free ( & HMML ) ;
2018-04-01 20:58:53 +00:00
bool
VideoIsPrivate ( char * VideoID )
{
// NOTE(matt): Currently only supports YouTube
char Message [ 128 ] ;
2018-09-17 18:06:31 +00:00
CopyString ( Message , sizeof ( Message ) , " %sChecking%s privacy status of: https://youtube.com/watch?v=%s " , ColourStrings [ CS_ONGOING ] , ColourStrings [ CS_END ] , VideoID ) ;
2018-04-22 20:57:31 +00:00
fprintf ( stderr , " %s " , Message ) ;
2018-04-01 20:58:53 +00:00
int MessageLength = StringLength ( Message ) ;
buffer VideoAPIResponse ;
ClaimBuffer ( & VideoAPIResponse , " VideoAPIResponse " , Kilobytes ( 1 ) ) ;
CURL * curl = curl_easy_init ( ) ;
if ( curl ) {
LastPrivacyCheck = time ( 0 ) ;
# define APIKey "AIzaSyAdV2U8ivPk8PHMaPMId0gynksw_gdzr9k"
char URL [ 1024 ] = { 0 } ;
2018-04-22 20:57:31 +00:00
CopyString ( URL , sizeof ( URL ) , " https://www.googleapis.com/youtube/v3/videos?key=%s&part=status&id=%s " , APIKey , VideoID ) ;
2018-04-01 20:58:53 +00:00
CURLcode CurlReturnCode ;
curl_easy_setopt ( curl , CURLOPT_WRITEDATA , & VideoAPIResponse . Ptr ) ;
curl_easy_setopt ( curl , CURLOPT_WRITEFUNCTION , CurlIntoBuffer ) ;
curl_easy_setopt ( curl , CURLOPT_URL , URL ) ;
if ( ( CurlReturnCode = curl_easy_perform ( curl ) ) )
{
fprintf ( stderr , " %s \n " , curl_easy_strerror ( CurlReturnCode ) ) ;
}
curl_easy_cleanup ( curl ) ;
VideoAPIResponse . Ptr = VideoAPIResponse . Location ;
SeekBufferForString ( & VideoAPIResponse , " { " , C_SEEK_FORWARDS , C_SEEK_AFTER ) ;
SeekBufferForString ( & VideoAPIResponse , " \" totalResults \" : " , C_SEEK_FORWARDS , C_SEEK_AFTER ) ;
if ( * VideoAPIResponse . Ptr = = ' 0 ' )
{
DeclaimBuffer ( & VideoAPIResponse ) ;
// printf("Private video: https://youtube.com/watch?v=%s\n", VideoID);
ClearTerminalRow ( MessageLength ) ;
return TRUE ;
}
SeekBufferForString ( & VideoAPIResponse , " { " , C_SEEK_FORWARDS , C_SEEK_AFTER ) ;
SeekBufferForString ( & VideoAPIResponse , " \" privacyStatus \" : \" " , C_SEEK_FORWARDS , C_SEEK_AFTER ) ;
char Status [ 16 ] ;
2018-04-22 20:57:31 +00:00
CopyStringNoFormatT ( Status , sizeof ( Status ) , VideoAPIResponse . Ptr , ' \" ' ) ;
2018-04-01 20:58:53 +00:00
if ( ! StringsDiffer ( Status , " public " ) )
{
DeclaimBuffer ( & VideoAPIResponse ) ;
ClearTerminalRow ( MessageLength ) ;
return FALSE ;
}
}
DeclaimBuffer ( & VideoAPIResponse ) ;
// printf("Unlisted video: https://youtube.com/watch?v=%s\n", VideoID);
ClearTerminalRow ( MessageLength ) ;
return TRUE ;
}
typedef struct
{
2018-09-17 18:06:31 +00:00
db_entry Prev , This , Next ;
uint32_t PreLinkPrevOffsetTotal , PreLinkThisOffsetTotal , PreLinkNextOffsetTotal ;
uint32_t PrevOffsetModifier , ThisOffsetModifier , NextOffsetModifier ;
bool FormerIsFirst , LatterIsFinal ;
bool DeletedEntryWasFirst , DeletedEntryWasFinal ;
short int PrevIndex , PreDeletionThisIndex , ThisIndex , NextIndex ;
2018-04-01 20:58:53 +00:00
} neighbourhood ;
2018-04-04 22:39:38 +00:00
int
LinearSearchForSpeaker ( speakers Speakers , char * Username )
{
for ( int i = 0 ; i < Speakers . Count ; + + i )
{
if ( ! StringsDifferCaseInsensitive ( Speakers . Speaker [ i ] . Credential - > Username , Username ) )
{
return i ;
}
}
return - 1 ;
}
bool
IsCategorisedAFK ( HMML_Annotation Anno )
{
for ( int i = 0 ; i < Anno . marker_count ; + + i )
{
if ( ! StringsDiffer ( Anno . markers [ i ] . marker , " afk " ) )
{
return TRUE ;
}
}
return FALSE ;
}
2018-09-17 18:06:31 +00:00
// NOTE(matt): Perhaps these OffsetLandmarks* could be made redundant / generalised once we're on the LUT
void
OffsetLandmarks ( buffer * Src , int AssetIndex , int PageType )
{
if ( ! ( Config . Mode & MODE_NOREVVEDRESOURCE ) )
{
if ( PageType = = PAGE_PLAYER )
{
for ( int LandmarkIndex = 0 ; LandmarkIndex < Assets . Asset [ AssetIndex ] . PlayerLandmarkCount ; + + LandmarkIndex )
{
Assets . Asset [ AssetIndex ] . PlayerLandmark [ LandmarkIndex ] + = Src - > Ptr - Src - > Location ;
}
}
else
{
for ( int LandmarkIndex = 0 ; LandmarkIndex < Assets . Asset [ AssetIndex ] . SearchLandmarkCount ; + + LandmarkIndex )
{
Assets . Asset [ AssetIndex ] . SearchLandmark [ LandmarkIndex ] + = Src - > Ptr - Src - > Location ;
}
}
}
}
void
OffsetLandmarksIncludes ( buffer * Src , int PageType )
{
OffsetLandmarks ( Src , ASSET_CSS_CINERA , PageType ) ;
OffsetLandmarks ( Src , ASSET_CSS_THEME , PageType ) ;
OffsetLandmarks ( Src , ASSET_CSS_TOPICS , PageType ) ;
if ( PageType = = PAGE_PLAYER )
{
OffsetLandmarks ( Src , ASSET_JS_PLAYER_PRE , PageType ) ;
}
}
void
OffsetLandmarksCredits ( buffer * Src )
{
OffsetLandmarks ( Src , ICON_SENDOWL , PAGE_PLAYER ) ;
OffsetLandmarks ( Src , ICON_PATREON , PAGE_PLAYER ) ;
}
void
OffsetLandmarksMenus ( buffer * Src )
{
OffsetLandmarks ( Src , ASSET_IMG_FILTER , PAGE_PLAYER ) ;
OffsetLandmarksCredits ( Src ) ;
}
//
2017-08-29 20:35:28 +00:00
int
2018-09-19 19:50:21 +00:00
HMMLToBuffers ( buffers * CollationBuffers , template * BespokeTemplate , char * Filename , neighbourhood * N )
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 ) ;
2017-11-11 00:34:47 +00:00
RewindBuffer ( & CollationBuffers - > ScriptPlayer ) ;
2018-09-17 18:06:31 +00:00
RewindBuffer ( & CollationBuffers - > SearchEntry ) ;
2018-02-23 23:36:42 +00:00
* CollationBuffers - > Custom0 = ' \0 ' ;
* CollationBuffers - > Custom1 = ' \0 ' ;
* CollationBuffers - > Custom2 = ' \0 ' ;
* CollationBuffers - > Custom3 = ' \0 ' ;
* CollationBuffers - > Custom4 = ' \0 ' ;
* CollationBuffers - > Custom5 = ' \0 ' ;
* CollationBuffers - > Custom6 = ' \0 ' ;
* CollationBuffers - > Custom7 = ' \0 ' ;
* CollationBuffers - > Custom8 = ' \0 ' ;
* CollationBuffers - > Custom9 = ' \0 ' ;
* CollationBuffers - > Custom10 = ' \0 ' ;
* CollationBuffers - > Custom11 = ' \0 ' ;
* CollationBuffers - > Custom12 = ' \0 ' ;
* CollationBuffers - > Custom13 = ' \0 ' ;
* CollationBuffers - > Custom14 = ' \0 ' ;
* CollationBuffers - > Custom15 = ' \0 ' ;
2018-01-04 20:55:51 +00:00
* CollationBuffers - > Title = ' \0 ' ;
2018-09-19 19:50:21 +00:00
* CollationBuffers - > URLPlayer = ' \0 ' ;
* CollationBuffers - > URLSearch = ' \0 ' ;
* CollationBuffers - > VODPlatform = ' \0 ' ;
2017-08-29 20:35:28 +00:00
2017-11-11 00:34:47 +00:00
char Filepath [ 256 ] ;
2017-08-29 20:35:28 +00:00
if ( Config . Edition = = EDITION_PROJECT )
{
2018-04-22 20:57:31 +00:00
CopyString ( Filepath , sizeof ( Filepath ) , " %s/%s " , Config . ProjectDir , Filename ) ;
2017-08-29 20:35:28 +00:00
}
else
{
2018-04-22 20:57:31 +00:00
CopyString ( Filepath , sizeof ( Filepath ) , " %s " , Filename ) ;
2017-08-29 20:35:28 +00:00
}
FILE * InFile ;
if ( ! ( InFile = fopen ( Filepath , " r " ) ) )
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Unable to open (annotations file) %s: %s " , Filename , strerror ( errno ) ) ;
2017-09-07 21:41:08 +00:00
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 ) ;
2018-09-17 18:06:31 +00:00
char * BaseFilename = GetBaseFilename ( Filename , " .hmml " ) ;
2017-08-29 20:35:28 +00:00
if ( HMML . well_formed )
{
2018-02-21 21:50:23 +00:00
bool HaveErrors = FALSE ;
if ( StringLength ( BaseFilename ) > MAX_BASE_FILENAME_LENGTH )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sBase filename \" %s \" is too long (%d/%d characters)%s \n " , ColourStrings [ CS_ERROR ] , BaseFilename , StringLength ( BaseFilename ) , MAX_BASE_FILENAME_LENGTH , ColourStrings [ CS_END ] ) ;
2018-02-21 21:50:23 +00:00
HaveErrors = TRUE ;
}
2018-01-04 20:55:51 +00:00
if ( ! HMML . metadata . title )
{
fprintf ( stderr , " Please set the title attribute in the [video] node of your .hmml file \n " ) ;
2018-02-21 21:50:23 +00:00
HaveErrors = TRUE ;
}
else if ( StringLength ( HMML . metadata . title ) > MAX_TITLE_LENGTH )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sVideo title \" %s \" is too long (%d/%d characters)%s \n " , ColourStrings [ CS_ERROR ] , HMML . metadata . title , StringLength ( HMML . metadata . title ) , MAX_TITLE_LENGTH , ColourStrings [ CS_END ] ) ;
2018-02-21 21:50:23 +00:00
HaveErrors = TRUE ;
}
else
{
2018-04-22 20:57:31 +00:00
CopyString ( CollationBuffers - > Title , sizeof ( CollationBuffers - > Title ) , " %s " , HMML . metadata . title ) ;
2018-01-04 20:55:51 +00:00
}
if ( ! HMML . metadata . member )
{
fprintf ( stderr , " Please set the member attribute in the [video] node of your .hmml file \n " ) ;
2018-02-21 21:50:23 +00:00
HaveErrors = TRUE ;
2018-01-04 20:55:51 +00:00
}
2018-06-13 15:05:21 +00:00
if ( ! StringsDiffer ( CollationBuffers - > Theme , " " ) )
2018-01-04 20:55:51 +00:00
{
2018-06-13 15:05:21 +00:00
if ( HMML . metadata . project )
{
CopyStringNoFormat ( CollationBuffers - > Theme , sizeof ( CollationBuffers - > Theme ) , HMML . metadata . project ) ;
}
else
{
fprintf ( stderr , " Unable to determine which theme to apply to the HTML \n "
" Please set at least one of: \n "
" \t 1. project attribute in the [video] node of your .hmml file \n "
" \t 2. ProjectID on the command line with -p \n "
" \t 3. Style on the command line with -s \n "
) ;
HaveErrors = TRUE ;
}
2018-01-04 20:55:51 +00:00
}
if ( ! HMML . metadata . id )
{
fprintf ( stderr , " Please set the id attribute in the [video] node of your .hmml file \n " ) ;
2018-02-21 21:50:23 +00:00
HaveErrors = TRUE ;
2018-01-04 20:55:51 +00:00
}
2018-09-19 19:50:21 +00:00
else
{
CopyString ( CollationBuffers - > VideoID , sizeof ( CollationBuffers - > VideoID ) , " %s " , HMML . metadata . id ) ;
}
if ( ! HMML . metadata . vod_platform )
{
fprintf ( stderr , " Please set the vod_platform attribute in the [video] node of your .hmml file \n " ) ;
HaveErrors = TRUE ;
}
else
{
CopyString ( CollationBuffers - > VODPlatform , sizeof ( CollationBuffers - > VODPlatform ) , " %s " , HMML . metadata . vod_platform ) ;
}
2018-01-04 20:55:51 +00:00
buffer URLPlayer ;
2018-02-21 21:50:23 +00:00
ClaimBuffer ( & URLPlayer , " URLPlayer " , MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH ) ;
2018-01-04 20:55:51 +00:00
ConstructPlayerURL ( & URLPlayer , BaseFilename ) ;
2018-04-22 20:57:31 +00:00
CopyString ( CollationBuffers - > URLPlayer , sizeof ( CollationBuffers - > URLPlayer ) , " %s " , URLPlayer . Location ) ;
2018-01-04 20:55:51 +00:00
DeclaimBuffer ( & URLPlayer ) ;
2018-06-12 19:03:12 +00:00
if ( HMML . metadata . project & & ( ! StringsDiffer ( CollationBuffers - > ProjectID , " " ) | | ! StringsDiffer ( CollationBuffers - > ProjectName , " " ) ) )
2017-12-07 01:15:13 +00:00
{
2018-06-12 19:03:12 +00:00
for ( int ProjectIndex = 0 ; ProjectIndex < ArrayCount ( ProjectInfo ) ; + + ProjectIndex )
2017-12-07 01:15:13 +00:00
{
2018-06-12 19:03:12 +00:00
if ( ! StringsDiffer ( ProjectInfo [ ProjectIndex ] . ProjectID ,
HMML . metadata . project ) )
{
2018-06-12 19:32:56 +00:00
CopyStringNoFormat ( CollationBuffers - > ProjectID , sizeof ( CollationBuffers - > ProjectID ) , HMML . metadata . project ) ;
CopyStringNoFormat ( CollationBuffers - > ProjectName , sizeof ( CollationBuffers - > ProjectName ) , ProjectInfo [ ProjectIndex ] . FullName ) ;
2018-06-12 19:03:12 +00:00
break ;
}
2017-12-07 01:15:13 +00:00
}
}
2018-01-03 22:16:20 +00:00
char * DefaultMedium = Config . DefaultMedium ;
if ( HMML . metadata . medium )
{
if ( MediumExists ( HMML . metadata . medium ) )
{
DefaultMedium = HMML . metadata . medium ;
}
2018-02-21 21:50:23 +00:00
else { HaveErrors = TRUE ; }
2018-01-03 22:16:20 +00:00
}
2018-02-23 23:36:42 +00:00
// TODO(matt): Consider simply making these as buffers and claiming the necessary amount for them
// The nice thing about doing it this way, though, is that it encourages bespoke template use, which should
// usually be the more convenient way for people to write greater amounts of localised information
for ( int CustomIndex = 0 ; CustomIndex < HMML_CUSTOM_ATTR_COUNT ; + + CustomIndex )
{
if ( HMML . metadata . custom [ CustomIndex ] )
{
int LengthOfString = StringLength ( HMML . metadata . custom [ CustomIndex ] ) ;
if ( LengthOfString > ( CustomIndex < 12 ? MAX_CUSTOM_SNIPPET_SHORT_LENGTH : MAX_CUSTOM_SNIPPET_LONG_LENGTH ) )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sCustom string %d \" %s%s%s \" is too long (%d/%d characters)%s \n " ,
ColourStrings [ CS_ERROR ] , CustomIndex , ColourStrings [ CS_END ] , HMML . metadata . custom [ CustomIndex ] , ColourStrings [ CS_ERROR ] ,
LengthOfString , CustomIndex < 12 ? MAX_CUSTOM_SNIPPET_SHORT_LENGTH : MAX_CUSTOM_SNIPPET_LONG_LENGTH , ColourStrings [ CS_END ] ) ;
2018-02-23 23:36:42 +00:00
if ( LengthOfString < MAX_CUSTOM_SNIPPET_LONG_LENGTH )
{
fprintf ( stderr , " Consider using custom12 to custom15, which can hold %d characters \n " , MAX_CUSTOM_SNIPPET_LONG_LENGTH ) ;
}
else
{
fprintf ( stderr , " Consider using a bespoke template for longer amounts of localised information \n " ) ;
}
HaveErrors = TRUE ;
}
else
{
switch ( CustomIndex )
{
2018-04-22 20:57:31 +00:00
case 0 : CopyStringNoFormat ( CollationBuffers - > Custom0 , sizeof ( CollationBuffers - > Custom0 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 1 : CopyStringNoFormat ( CollationBuffers - > Custom1 , sizeof ( CollationBuffers - > Custom1 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 2 : CopyStringNoFormat ( CollationBuffers - > Custom2 , sizeof ( CollationBuffers - > Custom2 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 3 : CopyStringNoFormat ( CollationBuffers - > Custom3 , sizeof ( CollationBuffers - > Custom3 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 4 : CopyStringNoFormat ( CollationBuffers - > Custom4 , sizeof ( CollationBuffers - > Custom4 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 5 : CopyStringNoFormat ( CollationBuffers - > Custom5 , sizeof ( CollationBuffers - > Custom5 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 6 : CopyStringNoFormat ( CollationBuffers - > Custom6 , sizeof ( CollationBuffers - > Custom6 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 7 : CopyStringNoFormat ( CollationBuffers - > Custom7 , sizeof ( CollationBuffers - > Custom7 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 8 : CopyStringNoFormat ( CollationBuffers - > Custom8 , sizeof ( CollationBuffers - > Custom8 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 9 : CopyStringNoFormat ( CollationBuffers - > Custom9 , sizeof ( CollationBuffers - > Custom9 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 10 : CopyStringNoFormat ( CollationBuffers - > Custom10 , sizeof ( CollationBuffers - > Custom10 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 11 : CopyStringNoFormat ( CollationBuffers - > Custom11 , sizeof ( CollationBuffers - > Custom11 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 12 : CopyStringNoFormat ( CollationBuffers - > Custom12 , sizeof ( CollationBuffers - > Custom12 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 13 : CopyStringNoFormat ( CollationBuffers - > Custom13 , sizeof ( CollationBuffers - > Custom13 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 14 : CopyStringNoFormat ( CollationBuffers - > Custom14 , sizeof ( CollationBuffers - > Custom14 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
case 15 : CopyStringNoFormat ( CollationBuffers - > Custom15 , sizeof ( CollationBuffers - > Custom15 ) , HMML . metadata . custom [ CustomIndex ] ) ; break ;
2018-02-23 23:36:42 +00:00
}
}
}
}
2018-02-21 21:50:23 +00:00
if ( ! HaveErrors )
2018-01-03 22:16:20 +00:00
{
2018-02-21 21:50:23 +00:00
if ( HMML . metadata . template )
2018-01-03 22:16:20 +00:00
{
2018-09-19 19:50:21 +00:00
switch ( PackTemplate ( BespokeTemplate , HMML . metadata . template , TEMPLATE_BESPOKE ) )
2018-02-21 21:50:23 +00:00
{
case RC_ARENA_FULL :
case RC_INVALID_TEMPLATE : // Invalid template
case RC_ERROR_FILE : // Could not load template
case RC_ERROR_MEMORY : // Could not allocate memory for template
HaveErrors = TRUE ;
case RC_SUCCESS :
break ;
}
2018-01-03 22:16:20 +00:00
}
}
2018-02-21 21:50:23 +00:00
if ( HaveErrors )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sSkipping%s %s " , ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] , BaseFilename ) ;
2018-02-21 21:50:23 +00:00
if ( HMML . metadata . title ) { fprintf ( stderr , " - %s " , HMML . metadata . title ) ; }
fprintf ( stderr , " \n " ) ;
hmml_free ( & HMML ) ;
return RC_ERROR_HMML ;
}
2018-04-01 20:58:53 +00:00
if ( N )
2018-02-21 21:50:23 +00:00
{
2018-04-01 20:58:53 +00:00
N - > This . LinkOffsets . PrevStart = 0 ;
N - > This . LinkOffsets . PrevEnd = 0 ;
N - > This . LinkOffsets . NextStart = 0 ;
N - > This . LinkOffsets . NextEnd = 0 ;
2018-02-21 21:50:23 +00:00
}
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
buffer QuoteMenu ;
buffer ReferenceMenu ;
buffer FilterMenu ;
buffer FilterTopics ;
buffer FilterMedia ;
buffer CreditsMenu ;
buffer Annotation ;
buffer AnnotationHeader ;
buffer AnnotationClass ;
buffer AnnotationData ;
buffer Text ;
2018-01-15 00:03:11 +00:00
buffer CategoryIcons ;
2017-08-29 20:35:28 +00:00
2017-12-07 01:15:13 +00:00
if ( ClaimBuffer ( & QuoteMenu , " QuoteMenu " , Kilobytes ( 32 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & ReferenceMenu , " ReferenceMenu " , Kilobytes ( 32 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
2017-10-18 21:07:29 +00:00
if ( ClaimBuffer ( & FilterMenu , " FilterMenu " , Kilobytes ( 16 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & FilterTopics , " FilterTopics " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & FilterMedia , " FilterMedia " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & CreditsMenu , " CreditsMenu " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
2017-08-29 20:35:28 +00:00
2018-04-22 20:57:31 +00:00
ref_info ReferencesArray [ 200 ] = { } ;
categories Topics = { } ;
categories Media = { } ;
2017-08-29 20:35:28 +00:00
bool HasQuoteMenu = FALSE ;
bool HasReferenceMenu = FALSE ;
bool HasFilterMenu = FALSE ;
bool HasCreditsMenu = FALSE ;
int QuoteIdentifier = 0x3b1 ;
int RefIdentifier = 1 ;
int UniqueRefs = 0 ;
CopyStringToBuffer ( & CollationBuffers - > Menus ,
2018-01-08 22:59:36 +00:00
" <div class= \" cineraMenus %s \" > \n "
2017-12-07 01:15:13 +00:00
" <span class= \" episode_name \" > " , StringsDiffer ( Config . Theme , " " ) ? Config . Theme : HMML . metadata . project ) ;
2017-08-29 20:35:28 +00:00
CopyStringToBufferHTMLSafe ( & CollationBuffers - > Menus , HMML . metadata . title ) ;
2018-04-17 23:05:14 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " </span> \n " ) ;
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Player ,
2018-01-08 22:59:36 +00:00
" <div class= \" cineraPlayerContainer \" > \n "
2017-08-29 20:35:28 +00:00
" <div class= \" video_container \" data-videoId= \" %s \" ></div> \n "
2017-12-07 01:15:13 +00:00
" <div class= \" markers_container %s \" > \n " , HMML . metadata . id , StringsDiffer ( Config . Theme , " " ) ? Config . Theme : HMML . metadata . project ) ;
2017-08-29 20:35:28 +00:00
2018-04-01 20:58:53 +00:00
if ( N )
2018-02-21 21:50:23 +00:00
{
2018-04-01 20:58:53 +00:00
N - > This . LinkOffsets . PrevStart = ( CollationBuffers - > Player . Ptr - CollationBuffers - > Player . Location ) ;
if ( N - > Prev . Size | | N - > Next . Size )
2018-02-21 21:50:23 +00:00
{
2018-04-01 20:58:53 +00:00
if ( N - > Prev . Size )
2018-02-21 21:50:23 +00:00
{
// TODO(matt): Once we have a more rigorous notion of "Day Numbers", perhaps also use them here
buffer PreviousPlayerURL ;
ClaimBuffer ( & PreviousPlayerURL , " PreviousPlayerURL " , MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH ) ;
2018-04-01 20:58:53 +00:00
ConstructPlayerURL ( & PreviousPlayerURL , N - > Prev . BaseFilename ) ;
2018-02-21 21:50:23 +00:00
CopyStringToBuffer ( & CollationBuffers - > Player ,
" <a class= \" episodeMarker prev \" href= \" %s \" ><div>⏫</div><div>Previous: '%s'</div><div>⏫</div></a> \n " ,
PreviousPlayerURL . Location ,
2018-04-01 20:58:53 +00:00
N - > Prev . Title ) ;
2018-02-21 21:50:23 +00:00
DeclaimBuffer ( & PreviousPlayerURL ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Player ,
" <div class= \" episodeMarker first \" ><div>•</div><div>Welcome to <cite>%s</cite></div><div>•</div></div> \n " , CollationBuffers - > ProjectName ) ;
}
}
2018-04-01 20:58:53 +00:00
N - > This . LinkOffsets . PrevEnd = ( CollationBuffers - > Player . Ptr - CollationBuffers - > Player . Location - N - > This . LinkOffsets . PrevStart ) ;
2018-02-21 21:50:23 +00:00
}
CopyStringToBuffer ( & CollationBuffers - > Player ,
" <div class= \" markers \" > \n " ) ;
2018-04-22 20:57:31 +00:00
speakers Speakers = { } ;
2018-04-04 22:39:38 +00:00
switch ( BuildCredits ( & CreditsMenu , & HasCreditsMenu , & HMML . metadata , & Speakers ) )
2017-08-29 20:35:28 +00:00
{
2018-02-05 23:57:17 +00:00
case CreditsError_NoHost :
case CreditsError_NoAnnotator :
case CreditsError_NoCredentials :
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sSkipping%s %s - %s \n " , ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] , BaseFilename , HMML . metadata . title ) ;
2018-02-05 23:57:17 +00:00
HMMLCleanup ( ) ;
return RC_ERROR_HMML ;
2017-08-29 20:35:28 +00:00
}
2018-04-01 20:58:53 +00:00
bool PrivateVideo = FALSE ;
if ( Config . Edition ! = EDITION_SINGLE & & N - > This . Size = = 0 & & ! ( Config . Mode & MODE_NOPRIVACY ) )
{
if ( VideoIsPrivate ( HMML . metadata . id ) )
{
// TODO(matt): Actually generate these guys, just putting them in a secret location
N - > This . LinkOffsets . PrevStart = 0 ;
N - > This . LinkOffsets . PrevEnd = 0 ;
PrivateVideo = TRUE ;
HMMLCleanup ( ) ;
return RC_PRIVATE_VIDEO ;
}
}
if ( Config . Edition ! = EDITION_SINGLE & & ! PrivateVideo )
2018-01-04 20:55:51 +00:00
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > SearchEntry , " name: \" " ) ;
2018-04-22 20:57:31 +00:00
if ( StringsDiffer ( Config . PlayerURLPrefix , " " ) )
{
char * Ptr = BaseFilename + StringLength ( Config . ProjectID ) ;
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > SearchEntry , " %s%s " , Config . PlayerURLPrefix , Ptr ) ;
2018-04-22 20:57:31 +00:00
}
else
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > SearchEntry , " %s " , BaseFilename ) ;
2018-04-22 20:57:31 +00:00
}
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > SearchEntry , " \" \n "
2018-04-22 20:57:31 +00:00
" title: \" " ) ;
2018-09-17 18:06:31 +00:00
CopyStringToBufferNoFormat ( & CollationBuffers - > SearchEntry , HMML . metadata . title ) ;
CopyStringToBuffer ( & CollationBuffers - > SearchEntry , " \" \n "
2018-01-04 20:55:51 +00:00
" markers: \n " ) ;
}
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-11-11 00:34:47 +00:00
2018-02-05 23:57:17 +00:00
int PreviousTimecode = 0 ;
2017-08-29 20:35:28 +00:00
for ( int AnnotationIndex = 0 ; AnnotationIndex < HMML . annotation_count ; + + AnnotationIndex )
{
# if DEBUG
printf ( " %d \n " , AnnotationIndex ) ;
# endif
2017-11-11 00:34:47 +00:00
2017-08-29 20:35:28 +00:00
HMML_Annotation * Anno = HMML . annotations + AnnotationIndex ;
2017-11-11 00:34:47 +00:00
2018-02-05 23:57:17 +00:00
if ( TimecodeToSeconds ( Anno - > time ) < PreviousTimecode )
{
fprintf ( stderr , " %s:%d: Timecode %s is chronologically before previous timecode \n "
2018-09-17 18:06:31 +00:00
" %sSkipping%s %s - %s \n " ,
2018-02-05 23:57:17 +00:00
Filename , Anno - > line , Anno - > time ,
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] , BaseFilename , HMML . metadata . title ) ;
2018-02-05 23:57:17 +00:00
HMMLCleanup ( ) ;
return RC_ERROR_HMML ;
}
PreviousTimecode = TimecodeToSeconds ( Anno - > time ) ;
2018-04-22 20:57:31 +00:00
categories LocalTopics = { } ;
categories LocalMedia = { } ;
2017-08-29 20:35:28 +00:00
bool HasQuote = FALSE ;
bool HasReference = FALSE ;
2018-04-22 20:57:31 +00:00
quote_info QuoteInfo = { } ;
2017-08-29 20:35:28 +00:00
// NOTE(matt): Tree structure of "annotation local" buffer dependencies
// Annotation
// AnnotationHeader
// AnnotationClass
// AnnotationData
// Text
2018-01-15 00:03:11 +00:00
// CategoryIcons
2017-08-29 20:35:28 +00:00
2018-02-05 23:57:17 +00:00
// TODO(matt): Shouldn't all of these guys DeclaimBuffer() before returning?
2017-10-18 21:07:29 +00:00
if ( ClaimBuffer ( & Annotation , " Annotation " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & AnnotationHeader , " AnnotationHeader " , 512 ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & AnnotationClass , " AnnotationClass " , 256 ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & AnnotationData , " AnnotationData " , 512 ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & Text , " Text " , Kilobytes ( 4 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
2018-03-06 20:40:12 +00:00
if ( ClaimBuffer ( & CategoryIcons , " CategoryIcons " , Kilobytes ( 1 ) ) = = 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 " ) ;
2018-04-04 22:39:38 +00:00
if ( ( Anno - > author | | Speakers . Count > 1 ) & & ! IsCategorisedAFK ( * Anno ) )
2017-08-29 20:35:28 +00:00
{
2018-04-04 22:39:38 +00:00
int SpeakerIndex ;
if ( Anno - > author & & ( SpeakerIndex = LinearSearchForSpeaker ( Speakers , Anno - > author ) ) = = - 1 )
2017-06-25 18:05:58 +00:00
{
2018-04-04 22:39:38 +00:00
if ( ! HasFilterMenu )
{
HasFilterMenu = TRUE ;
}
InsertCategory ( & Topics , & LocalTopics , & Media , & LocalMedia , " authored " ) ;
hsl_colour AuthorColour ;
StringToColourHash ( & AuthorColour , Anno - > author ) ;
// TODO(matt): That EDITION_NETWORK site database API-polling stuff
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Text ,
2018-04-22 20:57:31 +00:00
" <span class= \" author \" data-hue= \" %d \" data-saturation= \" %d%% \" >%s</span> " ,
2017-08-29 20:35:28 +00:00
AuthorColour . Hue , AuthorColour . Saturation ,
Anno - > author ) ;
2017-06-25 18:05:58 +00:00
}
else
{
2018-04-04 22:39:38 +00:00
if ( ! Anno - > author )
{
SpeakerIndex = LinearSearchForSpeaker ( Speakers , HMML . metadata . member ) ;
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Text ,
2018-04-22 20:57:31 +00:00
" <span class= \" author \" data-hue= \" %d \" data-saturation= \" %d%% \" >%s</span>: " ,
2018-04-04 22:39:38 +00:00
Speakers . Speaker [ SpeakerIndex ] . Colour . Hue ,
Speakers . Speaker [ SpeakerIndex ] . Colour . Saturation ,
2017-08-29 20:35:28 +00:00
2018-04-04 22:39:38 +00:00
Speakers . Speaker [ SpeakerIndex ] . Seen = = FALSE ? Speakers . Speaker [ SpeakerIndex ] . Credential - > CreditedName : Speakers . Speaker [ SpeakerIndex ] . Abbreviation ) ;
Speakers . Speaker [ SpeakerIndex ] . Seen = TRUE ;
}
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 ;
2018-04-04 22:39:38 +00:00
switch ( Anno - > markers [ MarkerIndex ] . type )
2017-08-29 20:35:28 +00:00
{
2018-04-04 22:39:38 +00:00
case HMML_MEMBER :
case HMML_PROJECT :
{
// TODO(matt): That EDITION_NETWORK site database API-polling stuff
hsl_colour Colour ;
StringToColourHash ( & Colour , Anno - > markers [ MarkerIndex ] . marker ) ;
CopyStringToBuffer ( & Text ,
2018-04-22 20:57:31 +00:00
" <span class= \" %s \" data-hue= \" %d \" data-saturation= \" %d%% \" >%.*s</span> " ,
2018-04-04 22:39:38 +00:00
Anno - > markers [ MarkerIndex ] . type = = HMML_MEMBER ? " member " : " project " ,
Colour . Hue , Colour . Saturation ,
2018-04-07 05:30:28 +00:00
StringLength ( Readable ) , InPtr ) ;
2018-04-04 22:39:38 +00:00
} break ;
case HMML_CATEGORY :
2017-05-13 15:38:10 +00:00
{
2018-04-04 22:39:38 +00:00
switch ( GenerateTopicColours ( Anno - > markers [ MarkerIndex ] . marker ) )
{
case RC_SUCCESS :
case RC_NOOP :
break ;
case RC_ERROR_FILE :
case RC_ERROR_MEMORY :
hmml_free ( & HMML ) ;
return RC_ERROR_FATAL ;
} ;
if ( ! HasFilterMenu )
{
HasFilterMenu = TRUE ;
}
InsertCategory ( & Topics , & LocalTopics , & Media , & LocalMedia , Anno - > markers [ MarkerIndex ] . marker ) ;
2018-04-07 05:30:28 +00:00
CopyStringToBuffer ( & Text , " %.*s " , StringLength ( Readable ) , InPtr ) ;
2018-04-04 22:39:38 +00:00
} break ;
case HMML_MARKER_COUNT : break ;
2017-05-13 15:38:10 +00:00
}
2018-04-04 22:39:38 +00:00
InPtr + = StringLength ( Readable ) ;
+ + MarkerIndex ;
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= \" refs references_container \" > \n " ) ;
2017-06-25 18:22:54 +00:00
2018-04-05 21:25:23 +00:00
if ( BuildReference ( ReferencesArray , RefIdentifier , UniqueRefs , CurrentRef , Anno ) = = RC_INVALID_REFERENCE )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
LogError ( 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 "
2018-09-17 18:06:31 +00:00
" Either tweak your annotation, or contact miblodelcarpio@gmail.com \n "
2017-08-29 20:35:28 +00:00
" 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
{
2018-09-19 19:50:21 +00:00
if ( ReferencesArray [ i ] . IdentifierCount = = MAX_REF_IDENTIFIER_COUNT )
2017-08-08 19:26:54 +00:00
{
2018-09-19 19:50:21 +00:00
LogError ( LOG_EMERGENCY , " REF_MAX_IDENTIFIER (%d) reached. Contact miblodelcarpio@gmail.com " , MAX_REF_IDENTIFIER_COUNT ) ;
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
{
2018-04-22 20:57:31 +00:00
CopyString ( ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Timecode , sizeof ( ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Timecode ) , " %s " , Anno - > time ) ;
2017-08-29 20:35:28 +00:00
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
{
2018-04-22 20:57:31 +00:00
CopyString ( ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Timecode , sizeof ( ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Timecode ) , " %s " , Anno - > time ) ;
2017-08-29 20:35:28 +00:00
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-10-18 21:07:29 +00:00
LogError ( 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
2018-04-05 21:25:23 +00:00
if ( BuildReference ( ReferencesArray , RefIdentifier , UniqueRefs , CurrentRef , Anno ) = = RC_INVALID_REFERENCE )
2017-08-08 19:26:54 +00:00
{
2017-10-18 21:07:29 +00:00
LogError ( 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 "
2018-09-17 18:06:31 +00:00
" Either tweak your annotation, or contact miblodelcarpio@gmail.com \n "
2017-08-29 20:35:28 +00:00
" 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-10-18 21:07:29 +00:00
LogError ( 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-10-18 21:07:29 +00:00
LogError ( 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
2018-04-22 20:57:31 +00:00
CopyStringToBuffer ( & Text , " <sup>%s%d</sup> " ,
2018-04-04 22:39:38 +00:00
Anno - > references [ RefIndex ] . offset = = Anno - > references [ RefIndex - 1 ] . offset ? " , " : " " ,
RefIdentifier ) ;
2017-08-29 20:35:28 +00:00
+ + 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 , " < " ) ;
break ;
2017-08-29 20:35:28 +00:00
case ' > ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " > " ) ;
break ;
2017-08-29 20:35:28 +00:00
case ' & ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " & " ) ;
break ;
2017-08-29 20:35:28 +00:00
case ' \" ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " " " ) ;
break ;
2017-08-29 20:35:28 +00:00
case ' \' ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " ' " ) ;
break ;
2017-08-29 20:35:28 +00:00
default :
2018-04-04 22:39:38 +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
}
2018-04-04 22:39:38 +00:00
+ + InPtr ;
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= \" 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 ;
2018-02-28 01:04:06 +00:00
bool ShouldFetchQuotes = FALSE ;
if ( Config . Mode & MODE_NOCACHE | | ( Config . Edition ! = EDITION_SINGLE & & time ( 0 ) - LastQuoteFetch > 60 * 60 ) )
{
ShouldFetchQuotes = TRUE ;
LastQuoteFetch = time ( 0 ) ;
}
2017-08-29 20:35:28 +00:00
if ( BuildQuote ( & QuoteInfo ,
Speaker ,
2018-02-28 01:04:06 +00:00
Anno - > quote . id , ShouldFetchQuotes ) = = RC_UNFOUND )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Quote #%s %d not found: %s:%d " , Speaker , Anno - > quote . id , Filename , Anno - > line ) ;
2018-01-04 20:55:51 +00:00
Filename [ StringLength ( Filename ) - StringLength ( " .hmml " ) ] = ' \0 ' ;
fprintf ( stderr , " Quote #%s %d not found \n "
2018-09-17 18:06:31 +00:00
" %sSkipping%s %s - %s \n " ,
Speaker , Anno - > quote . id ,
ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] , BaseFilename , HMML . metadata . title ) ;
2017-08-29 20:35:28 +00:00
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
}
2018-04-22 20:57:31 +00:00
CopyStringToBuffer ( & Text , " <sup>&#%d;</sup> " , QuoteIdentifier ) ;
2017-08-29 20:35:28 +00:00
+ + QuoteIdentifier ;
}
2017-06-25 18:22:54 +00:00
2018-04-22 20:57:31 +00:00
if ( Config . Edition ! = EDITION_SINGLE & & ! PrivateVideo )
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > SearchEntry , " \" %d \" : \" " , TimecodeToSeconds ( Anno - > time ) ) ;
2018-04-22 20:57:31 +00:00
if ( Anno - > is_quote & & ! Anno - > text [ 0 ] )
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > SearchEntry , " \u201C " ) ;
CopyStringToBufferNoFormat ( & CollationBuffers - > SearchEntry , QuoteInfo . Text ) ;
CopyStringToBuffer ( & CollationBuffers - > SearchEntry , " \u201D " ) ;
2018-04-22 20:57:31 +00:00
}
else
{
2018-09-17 18:06:31 +00:00
CopyStringToBufferNoFormat ( & CollationBuffers - > SearchEntry , Anno - > text ) ;
2018-04-22 20:57:31 +00:00
}
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > SearchEntry , " \" \n " ) ;
2018-04-22 20:57:31 +00:00
}
2017-08-29 20:35:28 +00:00
while ( MarkerIndex < Anno - > marker_count )
{
2018-01-06 00:00:05 +00:00
switch ( GenerateTopicColours ( Anno - > markers [ MarkerIndex ] . marker ) )
2017-05-22 21:37:00 +00:00
{
2018-01-06 00:00:05 +00:00
case RC_SUCCESS :
case RC_NOOP :
break ;
case RC_ERROR_FILE :
case RC_ERROR_MEMORY :
hmml_free ( & HMML ) ;
return RC_ERROR_FATAL ;
2018-01-15 00:03:11 +00:00
}
2018-01-06 00:00:05 +00:00
if ( ! HasFilterMenu )
{
HasFilterMenu = TRUE ;
2017-03-25 02:07:55 +00:00
}
2018-01-06 00:00:05 +00:00
InsertCategory ( & Topics , & LocalTopics , & Media , & LocalMedia , Anno - > markers [ MarkerIndex ] . marker ) ;
2017-08-29 20:35:28 +00:00
+ + MarkerIndex ;
}
2017-06-25 18:22:54 +00:00
2018-01-15 00:03:11 +00:00
if ( LocalTopics . Count = = 0 )
{
switch ( GenerateTopicColours ( " nullTopic " ) )
{
case RC_SUCCESS :
case RC_NOOP :
break ;
case RC_ERROR_FILE :
case RC_ERROR_MEMORY :
hmml_free ( & HMML ) ;
return RC_ERROR_FATAL ;
} ;
InsertCategory ( & Topics , & LocalTopics , & Media , & LocalMedia , " nullTopic " ) ;
}
2017-08-29 20:35:28 +00:00
if ( LocalMedia . Count = = 0 )
{
2018-01-03 22:16:20 +00:00
InsertCategory ( & Topics , & LocalTopics , & Media , & LocalMedia , DefaultMedium ) ;
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2018-01-15 00:03:11 +00:00
BuildCategories ( & AnnotationClass , & CategoryIcons , & LocalTopics , & LocalMedia , & MarkerIndex , DefaultMedium ) ;
2017-08-29 20:35:28 +00:00
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 ,
2017-12-10 17:05:46 +00:00
" <div class= \" cineraContent \" ><span class= \" timecode \" >%s</span> " ,
2017-08-29 20:35:28 +00:00
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 )
{
2018-01-15 00:03:11 +00:00
CopyBuffer ( & Annotation , & CategoryIcons ) ;
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 "
2017-12-10 17:05:46 +00:00
" <div class= \" cineraContent \" ><span class= \" timecode \" >%s</span> " ,
2017-08-29 20:35:28 +00:00
Anno - > time ) ;
CopyBuffer ( & Annotation , & Text ) ;
2017-11-11 00:34:47 +00:00
if ( LocalTopics . Count > 0 )
{
2018-01-15 00:03:11 +00:00
CopyBuffer ( & Annotation , & CategoryIcons ) ;
2017-11-11 00:34:47 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Annotation , " </div> \n "
" </div> \n "
" <div class= \" progress main \" > \n "
2017-12-10 17:05:46 +00:00
" <div class= \" cineraContent \" ><span class= \" timecode \" >%s</span> " ,
2017-08-29 20:35:28 +00:00
Anno - > time ) ;
CopyBuffer ( & Annotation , & Text ) ;
2017-11-11 00:34:47 +00:00
if ( LocalTopics . Count > 0 )
{
2018-01-15 00:03:11 +00:00
CopyBuffer ( & Annotation , & CategoryIcons ) ;
2017-11-11 00:34:47 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Annotation , " </div> \n "
" </div> \n "
" </div> \n " ) ;
CopyBuffer ( & CollationBuffers - > Player , & Annotation ) ;
// NOTE(matt): Tree structure of "annotation local" buffer dependencies
2018-01-15 00:03:11 +00:00
// CategoryIcons
2017-08-29 20:35:28 +00:00
// Text
// AnnotationData
// AnnotationClass
// AnnotationHeader
// Annotation
2018-01-15 00:03:11 +00:00
DeclaimBuffer ( & CategoryIcons ) ;
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & Text ) ;
DeclaimBuffer ( & AnnotationData ) ;
DeclaimBuffer ( & AnnotationClass ) ;
DeclaimBuffer ( & AnnotationHeader ) ;
DeclaimBuffer ( & Annotation ) ;
2017-08-29 20:35:28 +00:00
}
2018-01-05 20:03:02 +00:00
2018-04-01 20:58:53 +00:00
if ( Config . Edition ! = EDITION_SINGLE & & ! PrivateVideo )
2018-01-05 20:03:02 +00:00
{
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > SearchEntry , " --- \n " ) ;
N - > This . Size = CollationBuffers - > SearchEntry . Ptr - CollationBuffers - > SearchEntry . Location ;
2018-01-05 20:03:02 +00:00
}
2017-08-29 20:35:28 +00:00
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 \" > " ) ;
2018-04-17 23:05:14 +00:00
CopyStringToBufferHTMLSafeBreakingOnSlash ( & ReferenceMenu , ReferencesArray [ i ] . Source ) ;
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & ReferenceMenu , " </div> \n "
" <div class= \" ref_title \" > " ) ;
2018-04-17 23:05:14 +00:00
CopyStringToBufferHTMLSafeBreakingOnSlash ( & ReferenceMenu , ReferencesArray [ i ] . RefTitle ) ;
2017-08-29 20:35:28 +00:00
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 \" > " ) ;
2018-04-17 23:05:14 +00:00
CopyStringToBufferHTMLSafeBreakingOnSlash ( & ReferenceMenu , ReferencesArray [ i ] . RefTitle ) ;
2017-08-29 20:35:28 +00:00
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 )
{
2018-09-17 18:06:31 +00:00
buffer URL ;
ConstructResolvedAssetURL ( & URL , ASSET_IMG_FILTER , PAGE_PLAYER ) ;
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( & FilterMenu ,
" <div class= \" menu filter \" > \n "
2018-09-17 18:06:31 +00:00
" <span><img src= \" %s " ,
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
PushAssetLandmark ( & FilterMenu , ASSET_IMG_FILTER , PAGE_PLAYER ) ;
CopyStringToBuffer ( & FilterMenu ,
" \" ></span> \n "
2017-11-11 00:34:47 +00:00
" <div class= \" filter_container \" > \n "
2018-01-15 00:03:11 +00:00
" <div class= \" filter_mode exclusive \" >Filter mode: </div> \n "
2018-09-17 18:06:31 +00:00
" <div class= \" filters \" > \n " ) ;
2017-11-11 00:34:47 +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
{
2018-04-22 20:57:31 +00:00
char SanitisedMarker [ StringLength ( Topics . Category [ i ] . Marker ) + 1 ] ;
CopyString ( SanitisedMarker , sizeof ( SanitisedMarker ) , " %s " , Topics . Category [ i ] . Marker ) ;
2018-01-15 00:03:11 +00:00
SanitisePunctuation ( SanitisedMarker ) ;
bool NullTopic = ! StringsDiffer ( Topics . Category [ i ] . Marker , " nullTopic " ) ;
2017-08-19 22:35:07 +00:00
CopyStringToBuffer ( & FilterTopics ,
2018-01-15 00:03:11 +00:00
" <div %sclass= \" filter_content %s \" > \n "
2017-12-29 16:00:44 +00:00
" <span class= \" icon category %s \" ></span><span class= \" cineraText \" >%s</span> \n "
2017-08-29 20:35:28 +00:00
" </div> \n " ,
2018-01-15 00:03:11 +00:00
NullTopic ? " title= \" Annotations that don't fit into the above topic(s) may be filtered using this pseudo-topic \" " : " " ,
SanitisedMarker ,
SanitisedMarker ,
NullTopic ? " (null topic) " : 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
{
2018-04-22 20:57:31 +00:00
char SanitisedMarker [ StringLength ( Media . Category [ i ] . Marker ) + 1 ] ;
CopyString ( SanitisedMarker , sizeof ( SanitisedMarker ) , " %s " , Media . Category [ i ] . Marker ) ;
2018-01-15 00:03:11 +00:00
SanitisePunctuation ( SanitisedMarker ) ;
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-11-11 00:34:47 +00:00
if ( ! StringsDiffer ( Media . Category [ i ] . Marker , " afk " ) ) // TODO(matt): Initially hidden config
2018-01-15 00:03:11 +00:00
// When we do this for real, we'll probably need to loop
// over the configured media to see who should be hidden
2017-11-11 00:34:47 +00:00
{
CopyStringToBuffer ( & FilterMedia ,
" <div class= \" filter_content %s off \" > \n "
2017-12-29 16:00:44 +00:00
" <span class= \" icon \" >%s</span><span class= \" cineraText \" >%s</span> \n "
2017-11-11 00:34:47 +00:00
" </div> \n " ,
2018-01-15 00:03:11 +00:00
SanitisedMarker ,
2017-11-11 00:34:47 +00:00
CategoryMedium [ j ] . Icon ,
2018-01-15 00:03:11 +00:00
CategoryMedium [ j ] . WrittenName ) ;
2017-11-11 00:34:47 +00:00
}
else
{
CopyStringToBuffer ( & FilterMedia ,
" <div class= \" filter_content %s \" > \n "
2018-01-15 00:03:11 +00:00
" <span class= \" icon \" >%s</span><span class= \" cineraText \" >%s%s</span> \n "
2017-11-11 00:34:47 +00:00
" </div> \n " ,
2018-01-15 00:03:11 +00:00
SanitisedMarker ,
2017-11-11 00:34:47 +00:00
CategoryMedium [ j ] . Icon ,
2018-01-15 00:03:11 +00:00
CategoryMedium [ j ] . WrittenName ,
! StringsDiffer ( Media . Category [ i ] . Marker , DefaultMedium ) ? " </span><span class= \" cineraDefaultMediumIndicator \" title= \" Default medium \n "
" Annotations lacking a media icon are in this medium \" >🟉 " : " " ) ;
2017-11-11 00:34:47 +00:00
}
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
2018-09-17 18:06:31 +00:00
OffsetLandmarks ( & CollationBuffers - > Menus , ASSET_IMG_FILTER , PAGE_PLAYER ) ;
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
2018-01-17 20:15:00 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <div class= \" menu views \" > \n "
" <div class= \" view \" data-id= \" theatre \" title= \" Theatre mode \" >🎭</div> \n "
" <div class= \" views_container \" > \n "
" <div class= \" view \" data-id= \" super \" title= \" SUPERtheatre mode \" >🏟</div> \n "
" </div> \n "
2018-05-22 21:43:59 +00:00
" </div> \n "
" <div class= \" menu link \" > \n "
" <span>🔗</span> \n "
" <div class= \" link_container \" > \n "
" <div id= \" cineraLinkMode \" >Link to current annotation</div> \n "
" <textarea title= \" Click to copy to clipboard \" id= \" cineraLink \" readonly spellcheck= \" false \" ></textarea> \n "
" </div> \n "
" </div> \n " ) ;
2018-01-17 20:15:00 +00:00
2017-08-29 20:35:28 +00:00
if ( HasCreditsMenu )
{
2018-09-17 18:06:31 +00:00
OffsetLandmarksCredits ( & CollationBuffers - > Menus ) ;
2017-08-29 20:35:28 +00:00
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 "
2018-02-28 01:04:06 +00:00
" <span class= \" help_key \" >[</span>, <span class= \" help_key \" ><</span> / <span class= \" help_key \" >]</span>, <span class= \" help_key \" >></span> <span class= \" help_text \" >Jump to previous / next episode</span><br> \n "
" <span class= \" help_key \" >W</span>, <span class= \" help_key \" >K</span>, <span class= \" help_key \" >P</span> / <span class= \" help_key \" >S</span>, <span class= \" help_key \" >J</span>, <span class= \" help_key \" >N</span> <span class= \" help_text \" >Jump to previous / next marker</span><br> \n "
2018-01-15 21:52:24 +00:00
" <span class= \" help_key \" >t</span> / <span class= \" help_key \" >T</span> <span class= \" help_text \" >Toggle theatre / SUPERtheatre mode</span><br> \n "
2018-09-17 18:06:31 +00:00
" <span class= \" help_key%s \" >V</span> <span class= \" help_text%s \" >Revert filter to original state</span> <span class= \" help_key \" >Y</span> <span class= \" help_text \" >Select link (requires manual Ctrl-c)</span> \n " ,
2017-08-29 20:35:28 +00:00
2018-09-17 18:06:31 +00:00
HasFilterMenu ? " " : " unavailable " , HasFilterMenu ? " " : " unavailable " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" \n "
2018-09-17 18:06:31 +00:00
" <h2>Menu toggling</h2> \n "
" <span class= \" help_key%s \" >q</span> <span class= \" help_text%s \" >Quotes</span> \n "
" <span class= \" help_key%s \" >r</span> <span class= \" help_text%s \" >References</span> \n "
" <span class= \" help_key%s \" >f</span> <span class= \" help_text%s \" >Filter</span> \n "
" <span class= \" help_key \" >y</span> <span class= \" help_text \" >Link</span> \n "
" <span class= \" help_key%s \" >c</span> <span class= \" help_text%s \" >Credits</span> \n " ,
HasQuoteMenu ? " " : " unavailable " , HasQuoteMenu ? " " : " unavailable " ,
HasReferenceMenu ? " " : " unavailable " , HasReferenceMenu ? " " : " unavailable " ,
HasFilterMenu ? " " : " unavailable " , HasFilterMenu ? " " : " unavailable " ,
HasCreditsMenu ? " " : " unavailable " , HasCreditsMenu ? " " : " unavailable " ) ;
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" \n "
2018-02-28 01:04:06 +00:00
" <h2>In-Menu Movement</h2> \n "
2017-08-29 20:35:28 +00:00
" <div class= \" help_paragraph \" > \n "
" <div class= \" key_block \" > \n "
2018-04-22 20:57:31 +00:00
" <div> \n "
2017-08-29 20:35:28 +00:00
" <span class= \" help_key \" >a</span> \n "
" </div> \n "
2018-04-22 20:57:31 +00:00
" <div> \n "
2017-08-29 20:35:28 +00:00
" <span class= \" help_key \" >w</span><br> \n "
" <span class= \" help_key \" >s</span> \n "
" </div> \n "
2018-04-22 20:57:31 +00:00
" <div> \n "
2017-08-29 20:35:28 +00:00
" <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 "
2018-04-22 20:57:31 +00:00
" <div> \n "
2017-08-29 20:35:28 +00:00
" <span class= \" help_key \" >←</span> \n "
" </div> \n "
2018-04-22 20:57:31 +00:00
" <div> \n "
2017-08-29 20:35:28 +00:00
" <span class= \" help_key \" >↑</span><br> \n "
" <span class= \" help_key \" >↓</span> \n "
" </div> \n "
2018-04-22 20:57:31 +00:00
" <div> \n "
2017-08-29 20:35:28 +00:00
" <span class= \" help_key \" >→</span> \n "
" </div> \n "
" </div> \n "
" </div> \n "
" <br> \n " ) ;
CopyStringToBuffer ( & CollationBuffers - > Menus ,
2018-09-17 18:06:31 +00:00
" <h2>%sQuotes %sand%s References%s Menus%s</h2> \n "
" <span class= \" help_key word%s \" >Enter</span> <span class= \" help_text%s \" >Jump to timecode</span><br> \n " ,
// Q R
//
// 0 0 <h2><span off>Quotes and References Menus</span></h2>
// 0 1 <h2><span off>Quotes and</span> References Menus</h2>
// 1 0 <h2>Quotes <span off>and References</span> Menus</h2>
// 1 1 <h2>Quotes and References Menus</h2>
HasQuoteMenu ? " " : " <span class= \" unavailable \" > " ,
HasQuoteMenu & & ! HasReferenceMenu ? " <span class= \" unavailable \" > " : " " ,
! HasQuoteMenu & & HasReferenceMenu ? " </span> " : " " ,
HasQuoteMenu & & ! HasReferenceMenu ? " </span> " : " " ,
! HasQuoteMenu & & ! HasReferenceMenu ? " </span> " : " " ,
HasQuoteMenu | | HasReferenceMenu ? " " : " unavailable " , HasQuoteMenu | | HasReferenceMenu ? " " : " unavailable " ) ;
2017-08-29 20:35:28 +00:00
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" \n "
" <h2>%sQuotes%s,%s References %sand%s Credits%s Menus%s</h2> "
" <span class= \" help_key%s \" >o</span> <span class= \" help_text%s \" >Open URL (in new tab)</span> \n " ,
// Q R C
//
// 0 0 0 <h2><span off>Quotes, References and Credits Menus</span></h2>
// 0 0 1 <h2><span off>Quotes, References and</span> Credits Menus</h2>
// 0 1 0 <h2><span off>Quotes,</span> References <span off>and Credits</span> Menus</h2>
// 0 1 1 <h2><span off>Quotes,</span> References and Credits Menus</h2>
// 1 0 0 <h2>Quotes<span off>, References and Credits</span> Menus</h2>
// 1 0 1 <h2>Quotes<span off>, References </span>and Credits Menus</h2>
// 1 1 0 <h2>Quotes, References <span off>and Credits</span> Menus</h2>
// 1 1 1 <h2>Quotes, References and Credits Menus</h2>
/* 0 */ HasQuoteMenu ? " " : " <span class= \" unavailable \" > " ,
/* 1 */ HasQuoteMenu & & ! HasReferenceMenu ? " <span class= \" unavailable \" > " : " " ,
/* 2 */ ! HasQuoteMenu & & HasReferenceMenu ? " </span> " : " " ,
/* 3 */ HasReferenceMenu & & ! HasCreditsMenu ? " <span class= \" unavailable \" > " : HasQuoteMenu & & ! HasReferenceMenu & & HasCreditsMenu ? " </span> " : " " ,
/* 4 */ ! HasQuoteMenu & & ! HasReferenceMenu & & HasCreditsMenu ? " </span> " : " " ,
/* 5 */ ! HasCreditsMenu & & ( HasQuoteMenu | | HasReferenceMenu ) ? " </span> " : " " ,
/* 6 */ ! HasQuoteMenu & & ! HasReferenceMenu & & ! HasCreditsMenu ? " </span> " : " " ,
HasQuoteMenu | | HasReferenceMenu | | HasCreditsMenu ? " " : " unavailable " ,
HasQuoteMenu | | HasReferenceMenu | | HasCreditsMenu ? " " : " unavailable " ) ;
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus ,
2018-09-17 18:06:31 +00:00
" \n "
" <h2>%sFilter Menu%s</h2> \n "
" <span class= \" help_key%s \" >x</span>, <span class= \" help_key word%s \" >Space</span> <span class= \" help_text%s \" >Toggle category and focus next</span><br> \n "
" <span class= \" help_key%s \" >X</span>, <span class= \" help_key word modifier%s \" >Shift</span><span class= \" help_key word%s \" >Space</span> <span class= \" help_text%s \" >Toggle category and focus previous</span><br> \n "
" <span class= \" help_key%s \" >v</span> <span class= \" help_text%s \" >Invert topics / media as per focus</span> \n "
" \n "
" <h2>%sFilter and%s Link Menus</h2> \n "
" <span class= \" help_key \" >z</span> <span class= \" help_text \" >Toggle %sfilter /%s linking mode</span> \n " ,
2017-08-29 20:35:28 +00:00
2018-09-17 18:06:31 +00:00
HasFilterMenu ? " " : " <span class= \" unavailable \" > " , HasFilterMenu ? " " : " </span> " ,
HasFilterMenu ? " " : " unavailable " , HasFilterMenu ? " " : " unavailable " , HasFilterMenu ? " " : " unavailable " ,
HasFilterMenu ? " " : " unavailable " , HasFilterMenu ? " " : " unavailable " , HasFilterMenu ? " " : " unavailable " , HasFilterMenu ? " " : " unavailable " ,
HasFilterMenu ? " " : " unavailable " , HasFilterMenu ? " " : " unavailable " ,
HasFilterMenu ? " " : " <span class= \" unavailable \" > " , HasFilterMenu ? " " : " </span> " ,
HasFilterMenu ? " " : " <span class= \" help_text unavailable \" > " , HasFilterMenu ? " " : " </span> " ) ;
2017-08-29 20:35:28 +00:00
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" \n "
" <h2>%sCredits Menu%s</h2> \n "
" <span class= \" help_key word%s \" >Enter</span> <span class= \" help_text%s \" >Open URL (in new tab)</span><br> \n " ,
2017-08-29 20:35:28 +00:00
2018-09-17 18:06:31 +00:00
HasCreditsMenu ? " " : " <span class= \" unavailable \" > " , HasCreditsMenu ? " " : " </span> " ,
HasCreditsMenu ? " " : " unavailable " , HasCreditsMenu ? " " : " unavailable " ) ;
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" </div> \n "
" </div> \n "
" </div> " ) ;
2018-02-21 21:50:23 +00:00
CopyStringToBuffer ( & CollationBuffers - > Player ,
" </div> \n " ) ;
2018-04-01 20:58:53 +00:00
if ( N )
2018-02-21 21:50:23 +00:00
{
2018-04-01 20:58:53 +00:00
N - > This . LinkOffsets . NextStart = ( CollationBuffers - > Player . Ptr - CollationBuffers - > Player . Location - ( N - > This . LinkOffsets . PrevStart + N - > This . LinkOffsets . PrevEnd ) ) ;
if ( N - > Prev . Size | | N - > Next . Size )
2018-02-21 21:50:23 +00:00
{
2018-04-01 20:58:53 +00:00
if ( N - > Next . Size > 0 )
2018-02-21 21:50:23 +00:00
{
// TODO(matt): Once we have a more rigorous notion of "Day Numbers", perhaps also use them here
buffer NextPlayerURL ;
ClaimBuffer ( & NextPlayerURL , " NextPlayerURL " , MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH ) ;
2018-04-01 20:58:53 +00:00
ConstructPlayerURL ( & NextPlayerURL , N - > Next . BaseFilename ) ;
2018-02-21 21:50:23 +00:00
CopyStringToBuffer ( & CollationBuffers - > Player ,
" <a class= \" episodeMarker next \" href= \" %s \" ><div>⏬</div><div>Next: '%s'</div><div>⏬</div></a> \n " ,
NextPlayerURL . Location ,
2018-04-01 20:58:53 +00:00
N - > Next . Title ) ;
2018-02-21 21:50:23 +00:00
DeclaimBuffer ( & NextPlayerURL ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Player ,
" <div class= \" episodeMarker last \" ><div>•</div><div>You have arrived at the (current) end of <cite>%s</cite></div><div>•</div></div> \n " , CollationBuffers - > ProjectName ) ;
}
}
2018-04-01 20:58:53 +00:00
N - > This . LinkOffsets . NextEnd = ( CollationBuffers - > Player . Ptr - CollationBuffers - > Player . Location - ( N - > This . LinkOffsets . PrevStart + N - > This . LinkOffsets . PrevEnd + N - > This . LinkOffsets . NextStart ) ) ;
2018-02-21 21:50:23 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Player ,
" </div> \n "
" </div> " ) ;
2018-09-17 18:06:31 +00:00
buffer URLSearch ;
ClaimBuffer ( & URLSearch , " URLSearch " , MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 ) ;
ConstructSearchURL ( & URLSearch ) ;
CopyString ( CollationBuffers - > URLSearch , sizeof ( CollationBuffers - > URLSearch ) , " %s " , URLSearch . Location ) ;
DeclaimBuffer ( & URLSearch ) ;
2017-11-11 00:34:47 +00:00
2018-04-22 20:57:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" <meta charset= \" UTF-8 \" > \n "
" <meta name= \" generator \" content= \" Cinera %d.%d.%d \" > \n " ,
2017-11-11 00:34:47 +00:00
CINERA_APP_VERSION . Major ,
CINERA_APP_VERSION . Minor ,
CINERA_APP_VERSION . Patch ) ;
2017-08-29 20:35:28 +00:00
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 )
{
2018-01-15 00:03:11 +00:00
if ( StringsDiffer ( Topics . Category [ i ] . Marker , " nullTopic " ) )
{
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer , " %s, " , Topics . Category [ i ] . Marker ) ;
}
2017-08-29 20:35:28 +00:00
}
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 ;
2018-05-25 19:05:06 +00:00
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer , " \" > \n " ) ;
2017-08-29 20:35:28 +00:00
}
2018-09-17 18:06:31 +00:00
buffer URL ;
ConstructResolvedAssetURL ( & URL , ASSET_CSS_CINERA , PAGE_PLAYER ) ;
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s " ,
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
PushAssetLandmark ( & CollationBuffers - > IncludesPlayer , ASSET_CSS_CINERA , PAGE_PLAYER ) ;
ConstructResolvedAssetURL ( & URL , ASSET_CSS_THEME , PAGE_PLAYER ) ;
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s " ,
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
PushAssetLandmark ( & CollationBuffers - > IncludesPlayer , ASSET_CSS_THEME , PAGE_PLAYER ) ;
ConstructResolvedAssetURL ( & URL , ASSET_CSS_TOPICS , PAGE_PLAYER ) ;
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s " ,
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
PushAssetLandmark ( & CollationBuffers - > IncludesPlayer , ASSET_CSS_TOPICS , PAGE_PLAYER ) ;
2018-04-22 20:57:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
2018-09-17 18:06:31 +00:00
" \" > \n " ) ;
ConstructResolvedAssetURL ( & URL , ASSET_JS_PLAYER_PRE , PAGE_PLAYER ) ;
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" <script type= \" text/javascript \" src= \" %s " ,
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
PushAssetLandmark ( & CollationBuffers - > IncludesPlayer , ASSET_JS_PLAYER_PRE , PAGE_PLAYER ) ;
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
2018-09-17 18:06:31 +00:00
" \" ></script> " ) ;
ConstructResolvedAssetURL ( & URL , ASSET_JS_PLAYER_POST , PAGE_PLAYER ) ;
CopyStringToBuffer ( & CollationBuffers - > ScriptPlayer ,
" <script type= \" text/javascript \" src= \" %s " ,
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
PushAssetLandmark ( & CollationBuffers - > ScriptPlayer , ASSET_JS_PLAYER_POST , PAGE_PLAYER ) ;
2017-08-29 20:35:28 +00:00
2018-02-23 23:36:42 +00:00
CopyStringToBuffer ( & CollationBuffers - > ScriptPlayer ,
2018-09-17 18:06:31 +00:00
" \" ></script> " ) ;
2018-02-23 23:36:42 +00:00
2017-08-29 20:35:28 +00:00
// NOTE(matt): Tree structure of "global" buffer dependencies
// CreditsMenu
// FilterMedia
// FilterTopics
// FilterMenu
// ReferenceMenu
// QuoteMenu
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & CreditsMenu ) ;
DeclaimBuffer ( & FilterMedia ) ;
DeclaimBuffer ( & FilterTopics ) ;
DeclaimBuffer ( & FilterMenu ) ;
DeclaimBuffer ( & ReferenceMenu ) ;
DeclaimBuffer ( & QuoteMenu ) ;
2017-08-29 20:35:28 +00:00
}
else
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " %s:%d: %s " , Filename , HMML . error . line , HMML . error . message ) ;
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sSkipping%s %s:%d: %s \n " , ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] , 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
2018-04-01 20:58:53 +00:00
BuffersToHTML ( buffers * CollationBuffers , template * Template , char * OutputPath , int PageType , unsigned int * PlayerOffset )
2017-08-29 20:35:28 +00:00
{
# if DEBUG
2017-09-07 21:41:08 +00:00
printf ( " \n \n --- Buffer Collation --- \n "
2018-01-03 22:16:20 +00:00
" %s \n \n \n " , OutputPath ? OutputPath : Config . OutLocation ) ;
2017-10-18 21:07:29 +00:00
# endif
# if DEBUG_MEM
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
2018-02-23 23:36:42 +00:00
fprintf ( MemLog , " \n Entered BuffersToHTML(%s) \n " , OutputPath ? OutputPath : Config . OutLocation ) ;
2017-10-18 21:07:29 +00:00
fclose ( MemLog ) ;
2017-08-29 20:35:28 +00:00
# endif
2018-09-19 19:50:21 +00:00
if ( Template - > File . Buffer . Location )
2017-08-29 20:35:28 +00:00
{
2018-09-19 19:50:21 +00:00
if ( ( Template - > Metadata . Validity & PageType ) | | Config . Mode & MODE_FORCEINTEGRATION )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
buffer Output ;
2018-09-19 19:50:21 +00:00
Output . Size = Template - > File . Buffer . Size + ( Kilobytes ( 512 ) ) ;
2017-09-07 21:41:08 +00:00
Output . ID = " Output " ;
if ( ! ( Output . Location = malloc ( Output . Size ) ) )
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " BuffersToHTML(): %s " ,
2017-09-07 21:41:08 +00:00
strerror ( errno ) ) ;
return RC_ERROR_MEMORY ;
}
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
2017-11-11 00:34:47 +00:00
MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Allocated Output (%d) \n " , Output . Size ) ;
fclose ( MemLog ) ;
printf ( " Allocated Output (%d) \n " , Output . Size ) ;
2017-10-18 21:07:29 +00:00
# endif
2017-09-07 21:41:08 +00:00
Output . Ptr = Output . Location ;
2017-08-29 20:35:28 +00:00
2018-02-21 21:50:23 +00:00
bool NeedPlayerOffset = PlayerOffset ? TRUE : FALSE ;
2018-09-19 19:50:21 +00:00
Template - > File . Buffer . Ptr = Template - > File . Buffer . Location ;
2017-10-18 21:07:29 +00:00
for ( int i = 0 ; i < Template - > Metadata . TagCount ; + + i )
2017-06-03 01:32:18 +00:00
{
2017-09-07 21:41:08 +00:00
int j = 0 ;
2018-09-19 19:50:21 +00:00
while ( Template - > Metadata . Tags [ i ] . Offset > j )
2017-06-03 01:32:18 +00:00
{
2018-09-19 19:50:21 +00:00
* Output . Ptr + + = * Template - > File . Buffer . Ptr + + ;
2017-09-07 21:41:08 +00:00
+ + j ;
}
2017-08-29 20:35:28 +00:00
2018-06-12 19:03:12 +00:00
// TODO(matt): Make this whole template stuff context-aware, so it can determine whether or not to HTML-encode
// or sanitise punctuation for CSS-safety
2018-09-19 19:50:21 +00:00
switch ( Template - > Metadata . Tags [ i ] . TagCode )
2017-09-07 21:41:08 +00:00
{
2018-06-12 19:03:12 +00:00
case TAG_PROJECT_ID :
if ( CollationBuffers - > ProjectID [ 0 ] = = ' \0 ' )
{
2018-06-12 19:32:56 +00:00
fprintf ( stderr , " Template contains a <!-- __CINERA_PROJECT_ID__ --> tag \n "
" Skipping just this tag, because no project ID is set \n " ) ;
2018-06-12 19:03:12 +00:00
}
else
{
2018-06-12 19:32:56 +00:00
CopyStringToBufferNoFormat ( & Output , CollationBuffers - > ProjectID ) ; // NOTE(matt): Not HTML-safe
2018-06-12 19:03:12 +00:00
}
break ;
2017-12-12 23:24:10 +00:00
case TAG_PROJECT :
2018-01-04 20:55:51 +00:00
if ( CollationBuffers - > ProjectName [ 0 ] = = ' \0 ' )
{
2018-06-12 19:03:12 +00:00
fprintf ( stderr , " Template contains a <!-- __CINERA_PROJECT__ --> tag \n "
2018-01-04 20:55:51 +00:00
" Skipping just this tag, because we do not know the project's full name \n " ) ;
}
else
{
2018-05-25 19:05:06 +00:00
CopyStringToBufferHTMLSafe ( & Output , CollationBuffers - > ProjectName ) ;
2018-01-04 20:55:51 +00:00
}
2017-12-12 23:24:10 +00:00
break ;
2018-09-19 19:50:21 +00:00
case TAG_SEARCH_URL : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > URLSearch ) ; break ; // NOTE(matt): Not HTML-safe
case TAG_THEME : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Theme ) ; break ; // NOTE(matt): Not HTML-safe
case TAG_TITLE : CopyStringToBufferHTMLSafe ( & Output , CollationBuffers - > Title ) ; break ;
2017-12-12 23:24:10 +00:00
case TAG_URL :
2018-02-21 21:50:23 +00:00
if ( PageType = = PAGE_PLAYER )
{
2018-02-23 23:36:42 +00:00
CopyStringToBufferNoFormat ( & Output , CollationBuffers - > URLPlayer ) ;
2018-02-21 21:50:23 +00:00
}
else
{
2018-09-17 18:06:31 +00:00
CopyStringToBufferNoFormat ( & Output , CollationBuffers - > URLSearch ) ;
2018-02-21 21:50:23 +00:00
}
2017-12-12 23:24:10 +00:00
break ;
2018-09-19 19:50:21 +00:00
case TAG_VIDEO_ID : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > VideoID ) ; break ;
case TAG_VOD_PLATFORM : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > VODPlatform ) ; break ;
2018-09-17 18:06:31 +00:00
case TAG_SEARCH :
2018-01-04 20:55:51 +00:00
if ( Config . Edition = = EDITION_SINGLE )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " Template contains a <!-- __CINERA_SEARCH__ --> tag \n "
" Skipping just this tag, because a search cannot be generated for inclusion in a \n "
2018-01-04 20:55:51 +00:00
" bespoke template in Single Edition \n " ) ;
}
else
{
2018-09-17 18:06:31 +00:00
OffsetLandmarks ( & Output , ASSET_JS_SEARCH , PAGE_SEARCH ) ;
CopyBuffer ( & Output , & CollationBuffers - > Search ) ;
2018-01-04 20:55:51 +00:00
}
2017-09-07 21:41:08 +00:00
break ;
case TAG_INCLUDES :
2018-02-21 21:50:23 +00:00
if ( PageType = = PAGE_PLAYER )
{
2018-09-17 18:06:31 +00:00
OffsetLandmarksIncludes ( & Output , PageType ) ;
2018-02-21 21:50:23 +00:00
CopyBuffer ( & Output , & CollationBuffers - > IncludesPlayer ) ;
}
else
{
2018-09-17 18:06:31 +00:00
OffsetLandmarksIncludes ( & Output , PageType ) ;
CopyBuffer ( & Output , & CollationBuffers - > IncludesSearch ) ;
2018-02-21 21:50:23 +00:00
}
2017-08-29 20:35:28 +00:00
break ;
2017-09-07 21:41:08 +00:00
case TAG_MENUS :
2018-09-17 18:06:31 +00:00
OffsetLandmarksMenus ( & Output ) ;
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Output , & CollationBuffers - > Menus ) ;
2017-09-07 21:41:08 +00:00
break ;
case TAG_PLAYER :
2018-04-01 20:58:53 +00:00
if ( NeedPlayerOffset ) { * PlayerOffset + = ( Output . Ptr - Output . Location ) ; NeedPlayerOffset = ! NeedPlayerOffset ; }
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Output , & CollationBuffers - > Player ) ;
2017-08-29 20:35:28 +00:00
break ;
2017-09-07 21:41:08 +00:00
case TAG_SCRIPT :
2018-09-17 18:06:31 +00:00
OffsetLandmarks ( & Output , ASSET_JS_PLAYER_POST , PAGE_PLAYER ) ;
2017-11-11 00:34:47 +00:00
CopyBuffer ( & Output , & CollationBuffers - > ScriptPlayer ) ;
2017-08-29 20:35:28 +00:00
break ;
2018-09-17 18:06:31 +00:00
case TAG_ASSET :
{
buffer URL ;
2018-09-19 19:50:21 +00:00
ConstructResolvedAssetURL ( & URL , Template - > Metadata . Tags [ i ] . AssetIndex , PageType ) ;
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & Output , " %s " , URL . Location ) ;
DeclaimBuffer ( & URL ) ;
2018-09-19 19:50:21 +00:00
PushAssetLandmark ( & Output , Template - > Metadata . Tags [ i ] . AssetIndex , PageType ) ;
2018-09-17 18:06:31 +00:00
} break ;
case TAG_CSS :
{
buffer URL ;
2018-09-19 19:50:21 +00:00
ConstructResolvedAssetURL ( & URL , Template - > Metadata . Tags [ i ] . AssetIndex , PageType ) ;
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & Output ,
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s " ,
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
2018-09-19 19:50:21 +00:00
PushAssetLandmark ( & Output , Template - > Metadata . Tags [ i ] . AssetIndex , PageType ) ;
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & Output , " \" > " ) ;
} break ;
case TAG_IMAGE :
{
buffer URL ;
2018-09-19 19:50:21 +00:00
ConstructResolvedAssetURL ( & URL , Template - > Metadata . Tags [ i ] . AssetIndex , PageType ) ;
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & Output , " %s " , URL . Location ) ;
DeclaimBuffer ( & URL ) ;
2018-09-19 19:50:21 +00:00
PushAssetLandmark ( & Output , Template - > Metadata . Tags [ i ] . AssetIndex , PageType ) ;
2018-09-17 18:06:31 +00:00
} break ;
case TAG_JS :
{
buffer URL ;
2018-09-19 19:50:21 +00:00
ConstructResolvedAssetURL ( & URL , Template - > Metadata . Tags [ i ] . AssetIndex , PageType ) ;
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & Output ,
" <script type= \" text/javascript \" src= \" %s " ,
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
2018-09-19 19:50:21 +00:00
PushAssetLandmark ( & Output , Template - > Metadata . Tags [ i ] . AssetIndex , PageType ) ;
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & Output , " \" ></script> " ) ;
} break ;
2018-02-23 23:36:42 +00:00
case TAG_CUSTOM0 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom0 ) ; break ;
case TAG_CUSTOM1 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom1 ) ; break ;
case TAG_CUSTOM2 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom2 ) ; break ;
case TAG_CUSTOM3 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom3 ) ; break ;
case TAG_CUSTOM4 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom4 ) ; break ;
case TAG_CUSTOM5 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom5 ) ; break ;
case TAG_CUSTOM6 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom6 ) ; break ;
case TAG_CUSTOM7 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom7 ) ; break ;
case TAG_CUSTOM8 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom8 ) ; break ;
case TAG_CUSTOM9 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom9 ) ; break ;
case TAG_CUSTOM10 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom10 ) ; break ;
case TAG_CUSTOM11 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom11 ) ; break ;
case TAG_CUSTOM12 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom12 ) ; break ;
case TAG_CUSTOM13 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom13 ) ; break ;
case TAG_CUSTOM14 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom14 ) ; break ;
case TAG_CUSTOM15 : CopyStringToBufferNoFormat ( & Output , CollationBuffers - > Custom15 ) ; break ;
2017-06-03 01:32:18 +00:00
}
2018-02-21 21:50:23 +00:00
2018-09-19 19:50:21 +00:00
DepartComment ( & Template - > File . Buffer ) ;
2017-06-03 01:32:18 +00:00
}
2018-09-19 19:50:21 +00:00
while ( Template - > File . Buffer . Ptr - Template - > File . Buffer . Location < Template - > File . Buffer . Size )
2017-06-03 01:32:18 +00:00
{
2018-09-19 19:50:21 +00:00
* Output . Ptr + + = * Template - > File . Buffer . 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
FILE * OutFile ;
if ( ! ( OutFile = fopen ( Config . Edition = = EDITION_PROJECT ? OutputPath : Config . OutIntegratedLocation , " w " ) ) )
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Unable to open output file %s: %s " , Config . Edition = = EDITION_PROJECT ? OutputPath : Config . OutIntegratedLocation , strerror ( errno ) ) ;
2018-09-19 19:50:21 +00:00
FreeBuffer ( & Output ) ;
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
2017-11-11 00:34:47 +00:00
MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Freed Output \n " ) ;
fclose ( MemLog ) ;
printf ( " Freed Output \n " ) ;
2017-10-18 21:07:29 +00:00
# endif
2017-09-07 21:41:08 +00:00
return RC_ERROR_FILE ;
}
fwrite ( Output . Location , Output . Ptr - Output . Location , 1 , OutFile ) ;
fclose ( OutFile ) ;
2018-09-19 19:50:21 +00:00
FreeBuffer ( & Output ) ;
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
2017-11-11 00:34:47 +00:00
MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Freed Output \n " ) ;
fclose ( MemLog ) ;
printf ( " Freed Output \n " ) ;
2017-10-18 21:07:29 +00:00
# endif
2017-09-07 21:41:08 +00:00
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 ;
2017-10-18 21:07:29 +00:00
if ( ClaimBuffer ( & Master , " Master " , Kilobytes ( 512 ) ) = = RC_ARENA_FULL ) { return RC_ARENA_FULL ; } ;
2017-09-07 21:41:08 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Master ,
" <html> \n "
2018-01-08 22:59:36 +00:00
" <head> \n "
" " ) ;
2017-08-29 20:35:28 +00:00
2018-09-17 18:06:31 +00:00
OffsetLandmarksIncludes ( & Master , PageType ) ;
CopyBuffer ( & Master , PageType = = PAGE_PLAYER ? & CollationBuffers - > IncludesPlayer : & CollationBuffers - > IncludesSearch ) ;
2017-09-07 21:41:08 +00:00
CopyStringToBuffer ( & Master , " \n " ) ;
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Master ,
" </head> \n "
2018-01-08 22:59:36 +00:00
" <body> \n "
" " ) ;
2017-09-07 21:41:08 +00:00
if ( PageType = = PAGE_PLAYER )
{
2018-01-17 20:15:00 +00:00
CopyStringToBuffer ( & Master , " <div> \n "
2018-01-15 21:52:24 +00:00
" " ) ;
2018-09-17 18:06:31 +00:00
OffsetLandmarksMenus ( & Master ) ;
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Master , & CollationBuffers - > Menus ) ;
2018-01-08 22:59:36 +00:00
CopyStringToBuffer ( & Master , " \n "
2018-01-15 21:52:24 +00:00
" " ) ;
2018-02-21 21:50:23 +00:00
2018-04-01 20:58:53 +00:00
if ( PlayerOffset ) { * PlayerOffset + = Master . Ptr - Master . Location ; }
2018-02-21 21:50:23 +00:00
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Master , & CollationBuffers - > Player ) ;
2018-01-08 22:59:36 +00:00
CopyStringToBuffer ( & Master , " \n "
2018-01-15 21:52:24 +00:00
" " ) ;
CopyStringToBuffer ( & Master , " </div> \n "
2018-01-08 22:59:36 +00:00
" " ) ;
2018-09-17 18:06:31 +00:00
OffsetLandmarks ( & Master , ASSET_JS_PLAYER_POST , PAGE_PLAYER ) ;
2017-11-11 00:34:47 +00:00
CopyBuffer ( & Master , & CollationBuffers - > ScriptPlayer ) ;
2017-09-07 21:41:08 +00:00
CopyStringToBuffer ( & Master , " \n " ) ;
}
else
{
2018-09-17 18:06:31 +00:00
OffsetLandmarks ( & Master , ASSET_JS_SEARCH , PAGE_SEARCH ) ;
CopyBuffer ( & Master , & CollationBuffers - > Search ) ;
2017-09-07 21:41:08 +00:00
}
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-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Unable to open output file %s: %s " , Config . Edition = = EDITION_PROJECT ? OutputPath : Config . OutLocation , strerror ( errno ) ) ;
DeclaimBuffer ( & Master ) ;
2017-09-07 21:41:08 +00:00
return RC_ERROR_FILE ;
2017-08-29 20:35:28 +00:00
}
fwrite ( Master . Location , Master . Ptr - Master . Location , 1 , OutFile ) ;
fclose ( OutFile ) ;
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & Master ) ;
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
2017-08-29 20:35:28 +00:00
}
}
2018-02-21 21:50:23 +00:00
int
2018-09-17 18:06:31 +00:00
BinarySearchForMetadataEntry ( db_entry * * Entry , char * SearchTerm )
2018-03-06 20:40:12 +00:00
{
2018-09-17 18:06:31 +00:00
DB . Metadata . Buffer . Ptr = DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) ;
2018-03-06 20:40:12 +00:00
int Lower = 0 ;
2018-09-17 18:06:31 +00:00
db_entry * LowerEntry = ( db_entry * ) ( DB . Metadata . Buffer . Ptr + sizeof ( DB . Entry ) * Lower ) ;
2018-04-01 20:58:53 +00:00
if ( StringsDiffer ( SearchTerm , LowerEntry - > BaseFilename ) < 0 ) { return - 1 ; }
2018-03-06 20:40:12 +00:00
2018-09-17 18:06:31 +00:00
int Upper = DB . EntriesHeader . Count - 1 ;
2018-03-06 20:40:12 +00:00
int Pivot = Upper - ( ( Upper - Lower ) > > 1 ) ;
2018-09-17 18:06:31 +00:00
db_entry * UpperEntry ;
db_entry * PivotEntry ;
2018-03-06 20:40:12 +00:00
do {
2018-09-17 18:06:31 +00:00
LowerEntry = ( db_entry * ) ( DB . Metadata . Buffer . Ptr + sizeof ( DB . Entry ) * Lower ) ;
PivotEntry = ( db_entry * ) ( DB . Metadata . Buffer . Ptr + sizeof ( DB . Entry ) * Pivot ) ;
UpperEntry = ( db_entry * ) ( DB . Metadata . Buffer . Ptr + sizeof ( DB . Entry ) * Upper ) ;
2018-03-06 20:40:12 +00:00
2018-04-01 20:58:53 +00:00
if ( ! StringsDiffer ( SearchTerm , LowerEntry - > BaseFilename ) ) { * Entry = LowerEntry ; return Lower ; }
if ( ! StringsDiffer ( SearchTerm , PivotEntry - > BaseFilename ) ) { * Entry = PivotEntry ; return Pivot ; }
if ( ! StringsDiffer ( SearchTerm , UpperEntry - > BaseFilename ) ) { * Entry = UpperEntry ; return Upper ; }
2018-03-06 20:40:12 +00:00
if ( ( StringsDiffer ( SearchTerm , PivotEntry - > BaseFilename ) < 0 ) ) { Upper = Pivot ; }
else { Lower = Pivot ; }
Pivot = Upper - ( ( Upper - Lower ) > > 1 ) ;
} while ( Upper > Pivot ) ;
return Upper ;
}
int
2018-09-17 18:06:31 +00:00
AccumulateDBEntryInsertionOffset ( int EntryIndex )
2018-03-06 20:40:12 +00:00
{
int Result = 0 ;
2018-09-17 18:06:31 +00:00
db_entry * AccEntry = { 0 } ;
if ( EntryIndex < DB . EntriesHeader . Count > > 1 )
2018-03-06 20:40:12 +00:00
{
for ( ; EntryIndex > 0 ; - - EntryIndex )
{
2018-09-17 18:06:31 +00:00
AccEntry = ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * ( EntryIndex - 1 ) ) ;
2018-03-06 20:40:12 +00:00
Result + = AccEntry - > Size ;
}
Result + = StringLength ( " --- \n " ) ;
}
else
{
2018-09-17 18:06:31 +00:00
for ( ; EntryIndex < DB . EntriesHeader . Count ; + + EntryIndex )
2018-03-06 20:40:12 +00:00
{
2018-09-17 18:06:31 +00:00
AccEntry = ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * EntryIndex ) ;
2018-03-06 20:40:12 +00:00
Result + = AccEntry - > Size ;
}
2018-09-17 18:06:31 +00:00
Result = DB . File . FileSize - Result ;
2018-03-06 20:40:12 +00:00
}
return Result ;
}
2018-04-01 20:58:53 +00:00
void
2018-09-17 18:06:31 +00:00
ClearEntry ( db_entry * Entry )
2017-11-11 00:34:47 +00:00
{
2018-04-01 20:58:53 +00:00
Entry - > LinkOffsets . PrevStart = 0 ;
Entry - > LinkOffsets . NextStart = 0 ;
Entry - > LinkOffsets . PrevEnd = 0 ;
Entry - > LinkOffsets . NextEnd = 0 ;
Entry - > Size = 0 ;
Clear ( Entry - > BaseFilename , sizeof ( Entry - > BaseFilename ) ) ;
Clear ( Entry - > Title , sizeof ( Entry - > Title ) ) ;
}
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
void
InitNeighbourhood ( neighbourhood * N )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
N - > PrevIndex = - 1 ;
N - > PreDeletionThisIndex = N - > ThisIndex = 0 ;
N - > NextIndex = - 1 ;
N - > PreLinkPrevOffsetTotal = N - > PreLinkThisOffsetTotal = N - > PreLinkNextOffsetTotal = 0 ;
N - > PrevOffsetModifier = N - > ThisOffsetModifier = N - > NextOffsetModifier = 0 ;
N - > FormerIsFirst = N - > LatterIsFinal = FALSE ;
N - > DeletedEntryWasFirst = N - > DeletedEntryWasFinal = FALSE ;
N - > Prev . LinkOffsets . PrevStart = N - > Prev . LinkOffsets . PrevEnd = N - > Prev . LinkOffsets . NextStart = N - > Prev . LinkOffsets . NextEnd = 0 ;
N - > This . LinkOffsets . PrevStart = N - > This . LinkOffsets . PrevEnd = N - > This . LinkOffsets . NextStart = N - > This . LinkOffsets . NextEnd = 0 ;
N - > Next . LinkOffsets . PrevStart = N - > Next . LinkOffsets . PrevEnd = N - > Next . LinkOffsets . NextStart = N - > Next . LinkOffsets . NextEnd = 0 ;
N - > Prev . Size = N - > This . Size = N - > Next . Size = 0 ;
Clear ( N - > Prev . BaseFilename , sizeof ( N - > Prev . BaseFilename ) ) ;
Clear ( N - > This . BaseFilename , sizeof ( N - > This . BaseFilename ) ) ;
Clear ( N - > Next . BaseFilename , sizeof ( N - > Next . BaseFilename ) ) ;
Clear ( N - > Prev . Title , sizeof ( N - > Prev . Title ) ) ;
Clear ( N - > This . Title , sizeof ( N - > This . Title ) ) ;
Clear ( N - > Next . Title , sizeof ( N - > Next . Title ) ) ;
}
2017-10-18 21:07:29 +00:00
2018-09-17 18:06:31 +00:00
# define PrintNeighbourhood(N) PrintNeighbourhood_(N, __LINE__)
2018-04-01 20:58:53 +00:00
void
2018-09-17 18:06:31 +00:00
PrintNeighbourhood_ ( neighbourhood * N , int Line )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
printf ( " \n "
" Neighbourhood (line %d): \n " , Line ) ;
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
if ( N - > PrevIndex > = 0 )
2017-11-11 00:34:47 +00:00
{
2018-09-17 18:06:31 +00:00
printf (
" Prev [%d]: %s: %6d %6d %6d %6d \n " ,
N - > PrevIndex , N - > Prev . BaseFilename ,
N - > Prev . LinkOffsets . PrevStart ,
N - > Prev . LinkOffsets . PrevEnd ,
N - > Prev . LinkOffsets . NextStart ,
N - > Prev . LinkOffsets . NextEnd ) ;
2018-04-01 20:58:53 +00:00
}
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
if ( N - > ThisIndex > = 0 )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
printf (
" This [%d (pre-deletion %d)]: %s: %6d %6d %6d %6d \n " ,
N - > ThisIndex , N - > PreDeletionThisIndex , N - > This . BaseFilename ,
N - > This . LinkOffsets . PrevStart ,
N - > This . LinkOffsets . PrevEnd ,
N - > This . LinkOffsets . NextStart ,
N - > This . LinkOffsets . NextEnd ) ;
2018-04-01 20:58:53 +00:00
}
2018-09-17 18:06:31 +00:00
if ( N - > NextIndex > = 0 )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
printf (
" Next [%d]: %s: %6d %6d %6d %6d \n " ,
N - > NextIndex , N - > Next . BaseFilename ,
N - > Next . LinkOffsets . PrevStart ,
N - > Next . LinkOffsets . PrevEnd ,
N - > Next . LinkOffsets . NextStart ,
N - > Next . LinkOffsets . NextEnd ) ;
}
printf (
" OffsetModifiers: Prev %6d • This %6d • Next %6d \n "
" PreLinkOffsetTotals: Prev %6d • This %6d • Next %6d \n "
" DeletedEntryWasFirst: %s \n "
" DeletedEntryWasFinal: %s \n "
" FormerIsFirst: %s \n "
" LatterIsFinal: %s \n "
" \n " ,
N - > PrevOffsetModifier , N - > ThisOffsetModifier , N - > NextOffsetModifier ,
N - > PreLinkPrevOffsetTotal , N - > PreLinkThisOffsetTotal , N - > PreLinkNextOffsetTotal ,
N - > DeletedEntryWasFirst ? " yes " : " no " , N - > DeletedEntryWasFinal ? " yes " : " no " ,
N - > FormerIsFirst ? " yes " : " no " , N - > LatterIsFinal ? " yes " : " no " ) ;
}
void
SetNeighbour ( db_entry * Dest , db_entry * Src )
{
Dest - > Size = Src - > Size ;
Dest - > LinkOffsets . PrevStart = Src - > LinkOffsets . PrevStart ;
Dest - > LinkOffsets . PrevEnd = Src - > LinkOffsets . PrevEnd ;
Dest - > LinkOffsets . NextStart = Src - > LinkOffsets . NextStart ;
Dest - > LinkOffsets . NextEnd = Src - > LinkOffsets . NextEnd ;
CopyString ( Dest - > BaseFilename , sizeof ( Dest - > BaseFilename ) , " %s " , Src - > BaseFilename ) ;
CopyString ( Dest - > Title , sizeof ( Dest - > Title ) , " %s " , Src - > Title ) ;
}
void
GetNeighbourhoodForAddition ( neighbourhood * N , enum8 ( edit_types ) EditType )
{
db_entry Entry = { } ;
int EntryIndex ;
2018-04-01 20:58:53 +00:00
bool FoundPrev = FALSE ;
2018-09-17 18:06:31 +00:00
bool FoundNext = FALSE ;
N - > FormerIsFirst = TRUE ;
EntryIndex = N - > ThisIndex - 1 ;
2018-04-01 20:58:53 +00:00
for ( ; EntryIndex > = 0 ; - - EntryIndex )
{
2018-09-17 18:06:31 +00:00
Entry = * ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * EntryIndex ) ;
2018-04-01 20:58:53 +00:00
if ( Entry . Size > 0 )
2018-03-06 20:40:12 +00:00
{
2018-04-01 20:58:53 +00:00
if ( ! FoundPrev )
2018-03-06 20:40:12 +00:00
{
2018-04-01 20:58:53 +00:00
N - > PrevIndex = EntryIndex ;
2018-09-17 18:06:31 +00:00
SetNeighbour ( & N - > Prev , & Entry ) ;
2018-04-01 20:58:53 +00:00
FoundPrev = TRUE ;
2018-03-06 20:40:12 +00:00
}
2018-04-01 20:58:53 +00:00
else
2018-03-06 20:40:12 +00:00
{
2018-09-17 18:06:31 +00:00
N - > FormerIsFirst = FALSE ;
2018-04-01 20:58:53 +00:00
break ;
2018-03-06 20:40:12 +00:00
}
}
2018-04-01 20:58:53 +00:00
}
2018-09-17 18:06:31 +00:00
switch ( EditType )
2018-04-01 20:58:53 +00:00
{
case EDIT_REINSERTION :
EntryIndex = N - > ThisIndex + 1 ;
break ;
case EDIT_ADDITION :
EntryIndex = N - > ThisIndex ;
break ;
}
2018-09-17 18:06:31 +00:00
N - > LatterIsFinal = TRUE ;
for ( ; EntryIndex < DB . EntriesHeader . Count ;
2018-04-01 20:58:53 +00:00
+ + EntryIndex )
{
2018-09-17 18:06:31 +00:00
Entry = * ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * EntryIndex ) ;
2018-04-01 20:58:53 +00:00
if ( Entry . Size > 0 )
2018-03-06 20:40:12 +00:00
{
2018-04-01 20:58:53 +00:00
if ( ! FoundNext )
2018-03-06 20:40:12 +00:00
{
2018-04-01 20:58:53 +00:00
N - > NextIndex = EntryIndex ;
2018-09-17 18:06:31 +00:00
SetNeighbour ( & N - > Next , & Entry ) ;
2018-04-01 20:58:53 +00:00
FoundNext = TRUE ;
2018-03-06 20:40:12 +00:00
}
else
{
2018-09-17 18:06:31 +00:00
N - > LatterIsFinal = FALSE ;
2018-04-01 20:58:53 +00:00
break ;
2018-03-06 20:40:12 +00:00
}
}
2018-04-01 20:58:53 +00:00
}
2018-09-17 18:06:31 +00:00
if ( EditType = = EDIT_ADDITION & & FoundNext )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
+ + N - > NextIndex ;
2018-04-01 20:58:53 +00:00
}
}
2018-09-17 18:06:31 +00:00
void
GetNeighbourhoodForDeletion ( neighbourhood * N )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
db_entry Entry = { } ;
Entry = * ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * N - > ThisIndex ) ;
N - > PreDeletionThisIndex = N - > ThisIndex ;
SetNeighbour ( & N - > This , & Entry ) ;
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
int EntryIndex ;
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
N - > DeletedEntryWasFirst = TRUE ;
N - > FormerIsFirst = TRUE ;
bool FoundPrev = FALSE ;
for ( EntryIndex = N - > PreDeletionThisIndex - 1 ; EntryIndex > = 0 ; - - EntryIndex )
{
Entry = * ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * EntryIndex ) ;
if ( Entry . Size > 0 )
2017-09-07 21:41:08 +00:00
{
2018-09-17 18:06:31 +00:00
if ( ! FoundPrev )
{
FoundPrev = TRUE ;
N - > DeletedEntryWasFirst = FALSE ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
N - > PrevIndex = EntryIndex ;
SetNeighbour ( & N - > Prev , & Entry ) ;
}
else
{
N - > FormerIsFirst = FALSE ;
break ;
}
2018-04-01 20:58:53 +00:00
}
2018-09-17 18:06:31 +00:00
}
N - > DeletedEntryWasFinal = TRUE ;
N - > LatterIsFinal = TRUE ;
bool FoundNext = FALSE ;
for ( EntryIndex = N - > PreDeletionThisIndex + 1 ; EntryIndex < DB . EntriesHeader . Count ; + + EntryIndex )
{
Entry = * ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * EntryIndex ) ;
if ( Entry . Size > 0 )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
if ( ! FoundNext )
2017-09-07 21:41:08 +00:00
{
2018-09-17 18:06:31 +00:00
FoundNext = TRUE ;
N - > DeletedEntryWasFinal = FALSE ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
N - > NextIndex = EntryIndex - 1 ;
SetNeighbour ( & N - > Next , & Entry ) ;
2017-09-07 21:41:08 +00:00
}
2017-10-18 21:07:29 +00:00
else
2017-09-07 21:41:08 +00:00
{
2018-09-17 18:06:31 +00:00
N - > LatterIsFinal = FALSE ;
break ;
2017-09-07 21:41:08 +00:00
}
2017-10-18 21:07:29 +00:00
}
2018-09-17 18:06:31 +00:00
}
}
void
GetNeighbourhood ( neighbourhood * N , enum8 ( edit_types ) EditType )
{
if ( EditType = = EDIT_DELETION )
{
GetNeighbourhoodForDeletion ( N ) ;
2017-10-18 21:07:29 +00:00
}
2017-11-11 00:34:47 +00:00
else
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
GetNeighbourhoodForAddition ( N , EditType ) ;
}
}
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
void
SnipeEntryIntoMetadataBuffer ( db_entry * Entry , int EntryIndex )
{
* ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * EntryIndex ) = * Entry ;
}
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
int
2018-09-19 19:50:21 +00:00
InsertIntoDB ( neighbourhood * N , buffers * CollationBuffers , template * BespokeTemplate , char * BaseFilename , bool RecheckingPrivacy , bool * Reinserting )
2018-09-17 18:06:31 +00:00
{
enum8 ( edit_types ) EditType = EDIT_APPEND ;
int EntryInsertionStart = StringLength ( " --- \n " ) ;
int EntryInsertionEnd ;
if ( DB . EntriesHeader . Count > 0 )
{
db_entry * Entry = { 0 } ;
N - > ThisIndex = BinarySearchForMetadataEntry ( & Entry , BaseFilename ) ;
if ( Entry )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
// Reinsert
* Reinserting = TRUE ;
EntryInsertionStart = AccumulateDBEntryInsertionOffset ( N - > ThisIndex ) ;
EntryInsertionEnd = EntryInsertionStart + Entry - > Size ;
EditType = EDIT_REINSERTION ;
2018-02-21 21:50:23 +00:00
}
2018-09-17 18:06:31 +00:00
else
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
if ( N - > ThisIndex = = - 1 ) { + + N - > ThisIndex ; } // NOTE(matt): BinarySearchForMetadataEntry returns -1 if search term precedes the set
Entry = ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * N - > ThisIndex ) ;
if ( StringsDiffer ( BaseFilename , Entry - > BaseFilename ) < 0 )
2017-11-11 00:34:47 +00:00
{
2018-09-17 18:06:31 +00:00
// Insert
EditType = EDIT_INSERTION ;
}
else
{
// Append
+ + N - > ThisIndex ;
EditType = EDIT_APPEND ;
}
EntryInsertionStart = AccumulateDBEntryInsertionOffset ( N - > ThisIndex ) ;
2017-10-18 21:07:29 +00:00
}
2018-09-17 18:06:31 +00:00
GetNeighbourhood ( N , * Reinserting ? EDIT_REINSERTION : EDIT_ADDITION ) ;
2017-10-18 21:07:29 +00:00
}
2017-09-07 21:41:08 +00:00
2018-04-22 20:57:31 +00:00
char InputFile [ StringLength ( BaseFilename ) + StringLength ( " .hmml " ) + 1 ] ;
CopyString ( InputFile , sizeof ( InputFile ) , " %s.hmml " , BaseFilename ) ;
2018-04-01 20:58:53 +00:00
bool VideoIsPrivate = FALSE ;
switch ( HMMLToBuffers ( CollationBuffers , BespokeTemplate , InputFile , N ) )
2018-02-21 21:50:23 +00:00
{
2018-05-01 21:05:44 +00:00
// TODO(matt): Actually sort out the fatality of these cases
2018-02-21 21:50:23 +00:00
case RC_ERROR_FILE :
case RC_ERROR_FATAL :
return RC_ERROR_FATAL ;
case RC_ERROR_HMML :
case RC_ERROR_MAX_REFS :
case RC_ERROR_QUOTE :
case RC_INVALID_REFERENCE :
return RC_ERROR_HMML ;
2018-04-01 20:58:53 +00:00
case RC_PRIVATE_VIDEO :
VideoIsPrivate = TRUE ;
2018-02-21 21:50:23 +00:00
case RC_SUCCESS :
break ;
2018-04-01 20:58:53 +00:00
}
2017-09-07 21:41:08 +00:00
2018-05-01 21:05:44 +00:00
ClearCopyStringNoFormat ( N - > This . BaseFilename , sizeof ( N - > This . BaseFilename ) , BaseFilename ) ;
2018-09-17 18:06:31 +00:00
if ( ! VideoIsPrivate ) { ClearCopyStringNoFormat ( N - > This . Title , sizeof ( N - > This . Title ) , CollationBuffers - > Title ) ; }
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
if ( EditType = = EDIT_REINSERTION )
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
// NOTE(matt): To save opening the DB.Metadata file, we defer sniping N->This in until InsertNeighbourLink()
2018-05-01 21:05:44 +00:00
if ( ! VideoIsPrivate )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
if ( ! ( DB . File . Handle = fopen ( DB . File . Path , " w " ) ) ) { return RC_ERROR_FILE ; }
fwrite ( DB . File . Buffer . Location , EntryInsertionStart , 1 , DB . File . Handle ) ;
fwrite ( CollationBuffers - > SearchEntry . Location , N - > This . Size , 1 , DB . File . Handle ) ;
fwrite ( DB . File . Buffer . Location + EntryInsertionEnd , DB . File . FileSize - EntryInsertionEnd , 1 , DB . File . Handle ) ;
fclose ( DB . File . Handle ) ;
2018-05-01 21:05:44 +00:00
2018-09-17 18:06:31 +00:00
if ( N - > This . Size = = EntryInsertionEnd - EntryInsertionStart )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
DB . File . Buffer . Ptr = DB . File . Buffer . Location + EntryInsertionStart ;
CopyBufferSized ( & DB . File . Buffer , & CollationBuffers - > SearchEntry , N - > This . Size ) ;
2018-05-01 21:05:44 +00:00
}
else
{
2018-09-17 18:06:31 +00:00
FreeBuffer ( & DB . File . Buffer ) ;
ReadFileIntoBuffer ( & DB . File , 0 ) ;
2018-04-01 20:58:53 +00:00
}
2018-05-01 21:05:44 +00:00
}
2017-11-11 00:34:47 +00:00
}
2018-05-01 21:05:44 +00:00
else
2017-11-11 00:34:47 +00:00
{
2018-09-17 18:06:31 +00:00
int ExistingEntryCount = DB . EntriesHeader . Count ;
+ + DB . EntriesHeader . Count ;
if ( ! ( DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ) ) { return RC_ERROR_FILE ; }
fwrite ( & DB . Header , sizeof ( DB . Header ) , 1 , DB . Metadata . Handle ) ;
fwrite ( & DB . EntriesHeader , sizeof ( DB . EntriesHeader ) , 1 , DB . Metadata . Handle ) ;
DB . Metadata . Buffer . Ptr = DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) ;
fwrite ( DB . Metadata . Buffer . Ptr ,
sizeof ( DB . Entry ) ,
N - > ThisIndex ,
DB . Metadata . Handle ) ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Entry ) * N - > ThisIndex ;
fwrite ( & N - > This , sizeof ( DB . Entry ) , 1 , DB . Metadata . Handle ) ;
fwrite ( DB . Metadata . Buffer . Ptr ,
sizeof ( DB . Entry ) ,
ExistingEntryCount - N - > ThisIndex ,
DB . Metadata . Handle ) ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Entry ) * ( ExistingEntryCount - N - > ThisIndex ) ;
fwrite ( DB . Metadata . Buffer . Ptr ,
DB . Metadata . FileSize - ( DB . Metadata . Buffer . Ptr - DB . Metadata . Buffer . Location ) ,
1 ,
DB . Metadata . Handle ) ;
CycleFile ( & DB . Metadata ) ;
if ( ! ( DB . File . Handle = fopen ( DB . File . Path , " w " ) ) ) { return RC_ERROR_FILE ; }
fwrite ( DB . File . Buffer . Location ,
DB . File . FileSize - ( DB . File . FileSize - EntryInsertionStart ) ,
1 , DB . File . Handle ) ;
fwrite ( CollationBuffers - > SearchEntry . Location , N - > This . Size , 1 , DB . File . Handle ) ;
fwrite ( DB . File . Buffer . Location + EntryInsertionStart , DB . File . FileSize - EntryInsertionStart , 1 , DB . File . Handle ) ;
CycleFile ( & DB . File ) ;
}
if ( ! VideoIsPrivate )
{
LogError ( LOG_NOTICE , " %s %s - %s " , EditTypes [ EditType ] . Name , BaseFilename , CollationBuffers - > Title ) ;
fprintf ( stderr , " %s%s%s %s - %s \n " , ColourStrings [ EditTypes [ EditType ] . Colour ] , EditTypes [ EditType ] . Name , ColourStrings [ CS_END ] , BaseFilename , CollationBuffers - > Title ) ;
}
else if ( ! RecheckingPrivacy )
{
LogError ( LOG_NOTICE , " Privately %s %s " , EditTypes [ EditType ] . Name , BaseFilename ) ;
fprintf ( stderr , " %sPrivately %s%s %s \n " , ColourStrings [ CS_PRIVATE ] , EditTypes [ EditType ] . Name , ColourStrings [ CS_END ] , BaseFilename ) ;
}
// TODO(matt): Remove VideoIsPrivate in favour of generating a player page in a random location
return VideoIsPrivate ? RC_PRIVATE_VIDEO : RC_SUCCESS ;
}
void
WritePastAssetsHeader ( void )
{
DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ;
fwrite ( DB . Metadata . Buffer . Location ,
sizeof ( DB . Header )
+ sizeof ( DB . EntriesHeader )
+ sizeof ( DB . Entry ) * DB . EntriesHeader . Count
+ sizeof ( DB . AssetsHeader ) ,
1 ,
DB . Metadata . Handle ) ;
DB . Metadata . Buffer . Ptr = DB . Metadata . Buffer . Location
+ sizeof ( DB . Header )
+ sizeof ( DB . EntriesHeader )
+ sizeof ( DB . Entry ) * DB . EntriesHeader . Count ;
DB . AssetsHeader = * ( db_header_assets * ) DB . Metadata . Buffer . Ptr ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . AssetsHeader ) ;
}
2018-05-01 21:05:44 +00:00
2018-09-17 18:06:31 +00:00
void
PrintLandmarks ( void * FirstLandmark , int LandmarkCount )
{
printf ( " PrintLandmarks() \n " ) ;
for ( int i = 0 ; i < LandmarkCount ; + + i )
{
db_landmark Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( Landmark ) * i ) ;
printf ( " %4d %d \n " , Landmark . EntryIndex , Landmark . Position ) ;
}
}
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
void
2018-09-19 19:50:21 +00:00
ProcessPrevLandmarks ( neighbourhood * N , void * FirstLandmark , int ExistingLandmarkCount , landmark_range * CurrentTarget , int * RunningIndex )
2018-09-17 18:06:31 +00:00
{
if ( N - > PrevIndex > = 0 )
{
landmark_range FormerTarget = BinarySearchForMetadataLandmark ( FirstLandmark , N - > PrevIndex , ExistingLandmarkCount ) ;
fwrite ( FirstLandmark , sizeof ( db_landmark ) , FormerTarget . First , DB . Metadata . Handle ) ;
* RunningIndex + = FormerTarget . First ;
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
for ( int j = 0 ; j < FormerTarget . Length ; + + j , + + * RunningIndex )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
db_landmark Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( Landmark ) * * RunningIndex ) ;
2018-09-19 19:50:21 +00:00
if ( Landmark . Position > = N - > PreLinkPrevOffsetTotal )
2018-09-17 18:06:31 +00:00
{
Landmark . Position + = N - > PrevOffsetModifier ;
}
fwrite ( & Landmark , sizeof ( Landmark ) , 1 , DB . Metadata . Handle ) ;
}
}
else
{
fwrite ( FirstLandmark + sizeof ( db_landmark ) * * RunningIndex , sizeof ( db_landmark ) , CurrentTarget - > First , DB . Metadata . Handle ) ;
* RunningIndex + = CurrentTarget - > First ;
}
}
2018-05-01 21:05:44 +00:00
2018-09-17 18:06:31 +00:00
void
2018-09-19 19:50:21 +00:00
ProcessNextLandmarks ( neighbourhood * N , void * FirstLandmark , int ExistingLandmarkCount , int * RunningIndex , enum8 ( edit_types ) EditType )
2018-09-17 18:06:31 +00:00
{
if ( N - > NextIndex > = 0 & & * RunningIndex < ExistingLandmarkCount )
{
db_landmark Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( Landmark ) * * RunningIndex ) ;
landmark_range LatterTarget = BinarySearchForMetadataLandmark ( FirstLandmark , Landmark . EntryIndex , ExistingLandmarkCount ) ;
2018-05-01 21:05:44 +00:00
2018-09-17 18:06:31 +00:00
for ( int j = 0 ; j < LatterTarget . Length ; + + j , + + * RunningIndex )
{
Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( Landmark ) * * RunningIndex ) ;
2018-09-19 19:50:21 +00:00
if ( Landmark . Position > = N - > PreLinkNextOffsetTotal )
2018-05-01 21:05:44 +00:00
{
2018-09-17 18:06:31 +00:00
Landmark . Position + = N - > NextOffsetModifier ;
2018-05-01 21:05:44 +00:00
}
2018-09-17 18:06:31 +00:00
switch ( EditType )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
case EDIT_DELETION : - - Landmark . EntryIndex ; break ;
case EDIT_ADDITION : + + Landmark . EntryIndex ; break ;
2018-04-01 20:58:53 +00:00
}
2018-09-17 18:06:31 +00:00
fwrite ( & Landmark , sizeof ( Landmark ) , 1 , DB . Metadata . Handle ) ;
2018-04-01 20:58:53 +00:00
}
2018-09-17 18:06:31 +00:00
for ( ; * RunningIndex < ExistingLandmarkCount ; + + * RunningIndex )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
Landmark = * ( db_landmark * ) ( FirstLandmark + sizeof ( Landmark ) * * RunningIndex ) ;
switch ( EditType )
2018-05-01 21:05:44 +00:00
{
2018-09-17 18:06:31 +00:00
case EDIT_DELETION : - - Landmark . EntryIndex ; break ;
case EDIT_ADDITION : + + Landmark . EntryIndex ; break ;
2018-05-01 21:05:44 +00:00
}
2018-09-17 18:06:31 +00:00
fwrite ( & Landmark , sizeof ( Landmark ) , 1 , DB . Metadata . Handle ) ;
}
}
}
void
DeleteStaleAssets ( void )
{
LocateAssetsBlock ( ) ;
char * AssetsHeaderLocation = DB . Metadata . Buffer . Ptr ;
DB . AssetsHeader = * ( db_header_assets * ) AssetsHeaderLocation ;
int AssetDeletionCount = 0 ;
int AssetDeletionLocations [ DB . AssetsHeader . Count ] ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . AssetsHeader ) ;
for ( int AssetIndex = 0 ; AssetIndex < DB . AssetsHeader . Count ; + + AssetIndex )
{
DB . Asset = * ( db_asset * ) DB . Metadata . Buffer . Ptr ;
if ( DB . Asset . LandmarkCount = = 0 )
{
AssetDeletionLocations [ AssetDeletionCount ] = DB . Metadata . Buffer . Ptr - DB . Metadata . Buffer . Location ;
+ + AssetDeletionCount ;
- - DB . AssetsHeader . Count ;
}
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Asset ) + sizeof ( DB . Landmark ) * DB . Asset . LandmarkCount ;
}
if ( AssetDeletionCount > 0 )
{
DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ;
fwrite ( DB . Metadata . Buffer . Location , AssetsHeaderLocation - DB . Metadata . Buffer . Location , 1 , DB . Metadata . Handle ) ;
fwrite ( & DB . AssetsHeader , sizeof ( DB . AssetsHeader ) , 1 , DB . Metadata . Handle ) ;
int WrittenBytes = ( AssetsHeaderLocation - DB . Metadata . Buffer . Location ) + sizeof ( DB . AssetsHeader ) ;
for ( int DeletionIndex = 0 ; DeletionIndex < AssetDeletionCount ; + + DeletionIndex )
{
DB . Metadata . Buffer . Ptr = DB . Metadata . Buffer . Location + AssetDeletionLocations [ DeletionIndex ] ;
fwrite ( DB . Metadata . Buffer . Location + WrittenBytes ,
( DB . Metadata . Buffer . Ptr - DB . Metadata . Buffer . Location ) - WrittenBytes ,
1 ,
DB . Metadata . Handle ) ;
WrittenBytes + = ( DB . Metadata . Buffer . Ptr - DB . Metadata . Buffer . Location ) - WrittenBytes + sizeof ( DB . Asset ) ;
}
fwrite ( DB . Metadata . Buffer . Location + WrittenBytes , DB . Metadata . FileSize - WrittenBytes , 1 , DB . Metadata . Handle ) ;
CycleFile ( & DB . Metadata ) ;
}
}
void
DeleteStaleLandmarks ( void )
{
WritePastAssetsHeader ( ) ;
for ( int AssetIndex = 0 ; AssetIndex < DB . AssetsHeader . Count ; + + AssetIndex )
{
DB . Asset = * ( db_asset * ) DB . Metadata . Buffer . Ptr ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Asset ) + sizeof ( DB . Landmark ) * DB . Asset . LandmarkCount ;
DB . Asset . LandmarkCount = 0 ;
fwrite ( & DB . Asset , sizeof ( DB . Asset ) , 1 , DB . Metadata . Handle ) ;
}
CycleFile ( & DB . Metadata ) ;
}
void
DeleteLandmarks ( neighbourhood * N )
{
for ( int AssetIndex = 0 ; AssetIndex < DB . AssetsHeader . Count ; + + AssetIndex )
{
DB . Asset = * ( db_asset * ) DB . Metadata . Buffer . Ptr ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Asset ) ;
db_landmark * FirstLandmark = ( db_landmark * ) DB . Metadata . Buffer . Ptr ;
int ExistingLandmarkCount = DB . Asset . LandmarkCount ;
int RunningIndex = 0 ;
landmark_range DeletionTarget = BinarySearchForMetadataLandmark ( FirstLandmark , N - > PreDeletionThisIndex , ExistingLandmarkCount ) ;
DB . Asset . LandmarkCount - = DeletionTarget . Length ;
fwrite ( & DB . Asset , sizeof ( DB . Asset ) , 1 , DB . Metadata . Handle ) ;
2018-09-19 19:50:21 +00:00
ProcessPrevLandmarks ( N , FirstLandmark , ExistingLandmarkCount , & DeletionTarget , & RunningIndex ) ;
2018-09-17 18:06:31 +00:00
RunningIndex + = DeletionTarget . Length ;
2018-09-19 19:50:21 +00:00
ProcessNextLandmarks ( N , FirstLandmark , ExistingLandmarkCount , & RunningIndex , EDIT_DELETION ) ;
2018-09-17 18:06:31 +00:00
DB . Metadata . Buffer . Ptr + = sizeof ( db_landmark ) * ExistingLandmarkCount ;
}
CycleFile ( & DB . Metadata ) ;
}
void UpdateLandmarksForNeighbourhood ( neighbourhood * N , enum8 ( edit_types ) EditType ) ;
void
AddLandmarks ( neighbourhood * N , enum8 ( edit_types ) EditType )
{
for ( int i = 0 ; i < Assets . Count ; + + i )
{
Assets . Asset [ i ] . Known = FALSE ;
}
for ( int StoredAssetIndex = 0 ; StoredAssetIndex < DB . AssetsHeader . Count ; + + StoredAssetIndex )
{
DB . Asset = * ( db_asset * ) DB . Metadata . Buffer . Ptr ;
int ExistingLandmarkCount = DB . Asset . LandmarkCount ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Asset ) ;
void * FirstLandmark ;
if ( ExistingLandmarkCount > 0 )
{
FirstLandmark = DB . Metadata . Buffer . Ptr ;
}
int RunningIndex = 0 ;
for ( int i = 0 ; i < Assets . Count ; + + i )
{
if ( ! StringsDiffer ( DB . Asset . Filename , Assets . Asset [ i ] . Filename ) & & DB . Asset . Type = = Assets . Asset [ i ] . Type )
2018-05-01 21:05:44 +00:00
{
2018-09-17 18:06:31 +00:00
Assets . Asset [ i ] . Known = TRUE ;
2018-09-19 19:50:21 +00:00
if ( ! Assets . Asset [ i ] . OffsetLandmarks )
2018-09-17 18:06:31 +00:00
{
2018-09-19 19:50:21 +00:00
DB . Asset . LandmarkCount + = Assets . Asset [ i ] . PlayerLandmarkCount ;
landmark_range ThisTarget ;
if ( ExistingLandmarkCount > 0 )
{
ThisTarget = BinarySearchForMetadataLandmark ( FirstLandmark , N - > ThisIndex , ExistingLandmarkCount ) ;
2018-09-17 18:06:31 +00:00
2018-09-19 19:50:21 +00:00
if ( EditType = = EDIT_REINSERTION ) { DB . Asset . LandmarkCount - = ThisTarget . Length ; }
}
2018-09-17 18:06:31 +00:00
2018-09-19 19:50:21 +00:00
fwrite ( & DB . Asset , sizeof ( DB . Asset ) , 1 , DB . Metadata . Handle ) ;
2018-09-17 18:06:31 +00:00
2018-09-19 19:50:21 +00:00
if ( ExistingLandmarkCount > 0 )
{
ProcessPrevLandmarks ( N , FirstLandmark , ExistingLandmarkCount , & ThisTarget , & RunningIndex ) ;
}
2018-09-17 18:06:31 +00:00
2018-09-19 19:50:21 +00:00
for ( int j = 0 ; j < Assets . Asset [ i ] . PlayerLandmarkCount ; + + j )
{
db_landmark Landmark ;
Landmark . EntryIndex = N - > ThisIndex ;
Landmark . Position = Assets . Asset [ i ] . PlayerLandmark [ j ] ;
fwrite ( & Landmark , sizeof ( Landmark ) , 1 , DB . Metadata . Handle ) ;
}
2018-09-17 18:06:31 +00:00
2018-09-19 19:50:21 +00:00
if ( ExistingLandmarkCount > 0 )
{
if ( EditType = = EDIT_REINSERTION ) { RunningIndex + = ThisTarget . Length ; }
2018-09-17 18:06:31 +00:00
2018-09-19 19:50:21 +00:00
ProcessNextLandmarks ( N , FirstLandmark , ExistingLandmarkCount , & RunningIndex , EditType ) ;
}
Assets . Asset [ i ] . OffsetLandmarks = TRUE ;
}
else
{
fwrite ( & DB . Asset , sizeof ( DB . Asset ) , 1 , DB . Metadata . Handle ) ;
fwrite ( DB . Metadata . Buffer . Ptr , sizeof ( db_landmark ) * ExistingLandmarkCount , 1 , DB . Metadata . Handle ) ;
2018-09-17 18:06:31 +00:00
}
break ;
2018-05-01 21:05:44 +00:00
}
2018-09-17 18:06:31 +00:00
}
DB . Metadata . Buffer . Ptr + = sizeof ( db_landmark ) * ExistingLandmarkCount ;
}
CycleFile ( & DB . Metadata ) ;
bool NewAsset = FALSE ;
for ( int i = 0 ; i < Assets . Count ; + + i )
{
if ( ! Assets . Asset [ i ] . Known & & Assets . Asset [ i ] . PlayerLandmarkCount > 0 )
{
UpdateAssetInDB ( i ) ;
NewAsset = TRUE ;
}
}
if ( NewAsset ) {
UpdateLandmarksForNeighbourhood ( N , EDIT_REINSERTION ) ; // NOTE(matt): EDIT_REINSERTION this time because all existing
// assets will have been updated in the first pass
}
}
void
UpdateLandmarksForNeighbourhood ( neighbourhood * N , enum8 ( edit_types ) EditType )
{
if ( ! ( Config . Mode & MODE_NOREVVEDRESOURCE ) )
{
WritePastAssetsHeader ( ) ;
switch ( EditType )
{
case EDIT_DELETION : DeleteLandmarks ( N ) ; break ;
case EDIT_ADDITION : case EDIT_REINSERTION : { AddLandmarks ( N , EditType ) ; break ; }
}
}
}
void
DeleteLandmarksForSearch ( void )
{
if ( ! ( Config . Mode & MODE_NOREVVEDRESOURCE ) )
{
WritePastAssetsHeader ( ) ;
for ( int AssetIndex = 0 ; AssetIndex < DB . AssetsHeader . Count ; + + AssetIndex )
{
DB . Asset = * ( db_asset * ) DB . Metadata . Buffer . Ptr ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Asset ) ;
db_landmark * FirstLandmark = ( db_landmark * ) DB . Metadata . Buffer . Ptr ;
int ExistingLandmarkCount = DB . Asset . LandmarkCount ;
2018-05-01 21:05:44 +00:00
2018-09-17 18:06:31 +00:00
landmark_range DeletionTarget = BinarySearchForMetadataLandmark ( FirstLandmark , PAGE_TYPE_SEARCH , ExistingLandmarkCount ) ;
DB . Asset . LandmarkCount - = DeletionTarget . Length ;
fwrite ( & DB . Asset , sizeof ( DB . Asset ) , 1 , DB . Metadata . Handle ) ;
2017-10-18 21:07:29 +00:00
2018-09-17 18:06:31 +00:00
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Landmark ) * DeletionTarget . Length ;
fwrite ( DB . Metadata . Buffer . Ptr , sizeof ( DB . Landmark ) , ExistingLandmarkCount - DeletionTarget . Length , DB . Metadata . Handle ) ;
2018-05-01 21:05:44 +00:00
2018-09-17 18:06:31 +00:00
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Landmark ) * ExistingLandmarkCount - DeletionTarget . Length ;
}
CycleFile ( & DB . Metadata ) ;
2018-05-01 21:05:44 +00:00
}
2017-10-18 21:07:29 +00:00
}
2017-12-12 23:24:10 +00:00
void
2018-09-17 18:06:31 +00:00
UpdateLandmarksForSearch ( void )
2017-12-12 23:24:10 +00:00
{
2018-09-17 18:06:31 +00:00
if ( ! ( Config . Mode & MODE_NOREVVEDRESOURCE ) )
2017-12-12 23:24:10 +00:00
{
2018-09-17 18:06:31 +00:00
for ( int i = 0 ; i < Assets . Count ; + + i )
{
Assets . Asset [ i ] . Known = FALSE ;
}
WritePastAssetsHeader ( ) ;
for ( int AssetIndex = 0 ; AssetIndex < DB . AssetsHeader . Count ; + + AssetIndex )
{
DB . Asset = * ( db_asset * ) DB . Metadata . Buffer . Ptr ;
int ExistingLandmarkCount = DB . Asset . LandmarkCount ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Asset ) ;
void * FirstLandmark = DB . Metadata . Buffer . Ptr ;
for ( int i = 0 ; i < Assets . Count ; + + i )
2017-12-12 23:24:10 +00:00
{
2018-09-17 18:06:31 +00:00
if ( ! StringsDiffer ( DB . Asset . Filename , Assets . Asset [ i ] . Filename ) & & DB . Asset . Type = = Assets . Asset [ i ] . Type )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
Assets . Asset [ i ] . Known = TRUE ;
landmark_range Target = BinarySearchForMetadataLandmark ( FirstLandmark , PAGE_TYPE_SEARCH , DB . Asset . LandmarkCount ) ;
DB . Asset . LandmarkCount + = Assets . Asset [ i ] . SearchLandmarkCount - Target . Length ;
fwrite ( & DB . Asset , sizeof ( DB . Asset ) , 1 , DB . Metadata . Handle ) ;
for ( int j = 0 ; j < Assets . Asset [ i ] . SearchLandmarkCount ; + + j )
{
DB . Landmark . EntryIndex = PAGE_TYPE_SEARCH ;
DB . Landmark . Position = Assets . Asset [ i ] . SearchLandmark [ j ] ;
fwrite ( & DB . Landmark , sizeof ( DB . Landmark ) , 1 , DB . Metadata . Handle ) ;
}
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Landmark ) * ( Target . First + Target . Length ) ;
fwrite ( DB . Metadata . Buffer . Ptr , sizeof ( DB . Landmark ) , ExistingLandmarkCount - ( Target . First + Target . Length ) , DB . Metadata . Handle ) ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Landmark ) * ( ExistingLandmarkCount - ( Target . First + Target . Length ) ) ;
break ;
2018-04-01 20:58:53 +00:00
}
2017-12-12 23:24:10 +00:00
}
2018-09-17 18:06:31 +00:00
}
CycleFile ( & DB . Metadata ) ;
bool NewAsset = FALSE ;
for ( int InternalAssetIndex = 0 ; InternalAssetIndex < Assets . Count ; + + InternalAssetIndex )
{
if ( ! Assets . Asset [ InternalAssetIndex ] . Known & & Assets . Asset [ InternalAssetIndex ] . SearchLandmarkCount > 0 )
{
NewAsset = TRUE ;
UpdateAssetInDB ( InternalAssetIndex ) ;
}
}
if ( NewAsset ) { UpdateLandmarksForSearch ( ) ; }
2017-12-12 23:24:10 +00:00
}
}
2018-02-21 21:50:23 +00:00
enum
{
LINK_INCLUDE ,
LINK_EXCLUDE
} link_types ;
enum
{
2018-09-17 18:06:31 +00:00
LINK_FORWARDS ,
LINK_BACKWARDS
2018-02-21 21:50:23 +00:00
} link_directions ;
2018-04-17 23:05:14 +00:00
int
2018-09-17 18:06:31 +00:00
InsertNeighbourLink ( db_entry * From , int FromIndex , db_entry * To , enum8 ( link_directions ) LinkDirection , bool FromHasOneNeighbour )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
file_buffer HTML ;
if ( ReadPlayerPageIntoBuffer ( & HTML , From ) = = RC_SUCCESS )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
if ( ! ( HTML . Handle = fopen ( HTML . Path , " w " ) ) ) { FreeBuffer ( & HTML . Buffer ) ; return RC_ERROR_FILE ; } ;
2018-02-21 21:50:23 +00:00
buffer Link ;
2018-09-17 18:06:31 +00:00
ClaimBuffer ( & Link , " Link " , Kilobytes ( 4 ) ) ;
2018-02-21 21:50:23 +00:00
buffer ToPlayerURL ;
if ( To )
{
ClaimBuffer ( & ToPlayerURL , " ToPlayerURL " , MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH ) ;
ConstructPlayerURL ( & ToPlayerURL , To - > BaseFilename ) ;
}
2018-09-17 18:06:31 +00:00
int NewPrevEnd = 0 ;
int NewNextEnd = 0 ;
2018-02-21 21:50:23 +00:00
switch ( LinkDirection )
{
2018-09-17 18:06:31 +00:00
case LINK_BACKWARDS :
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
fwrite ( HTML . Buffer . Location , From - > LinkOffsets . PrevStart , 1 , HTML . Handle ) ;
2018-02-21 21:50:23 +00:00
if ( To )
{
CopyStringToBuffer ( & Link ,
" <a class= \" episodeMarker prev \" href= \" %s \" ><div>⏫</div><div>Previous: '%s'</div><div>⏫</div></a> \n " ,
ToPlayerURL . Location ,
To - > Title ) ;
}
else
{
CopyStringToBuffer ( & Link ,
2018-09-17 18:06:31 +00:00
" <div class= \" episodeMarker first \" ><div>•</div><div>Welcome to <cite>%s</cite></div><div>•</div></div> \n " , DB . EntriesHeader . ProjectName ) ;
2018-02-21 21:50:23 +00:00
}
NewPrevEnd = Link . Ptr - Link . Location ;
2018-09-17 18:06:31 +00:00
fwrite ( Link . Location , ( Link . Ptr - Link . Location ) , 1 , HTML . Handle ) ;
2018-02-21 21:50:23 +00:00
if ( FromHasOneNeighbour )
{
2018-09-17 18:06:31 +00:00
fwrite ( HTML . Buffer . Location + From - > LinkOffsets . PrevStart + From - > LinkOffsets . PrevEnd , From - > LinkOffsets . NextStart , 1 , HTML . Handle ) ;
2018-02-21 21:50:23 +00:00
RewindBuffer ( & Link ) ;
CopyStringToBuffer ( & Link ,
2018-09-17 18:06:31 +00:00
" <div class= \" episodeMarker last \" ><div>•</div><div>You have arrived at the (current) end of <cite>%s</cite></div><div>•</div></div> \n " , DB . EntriesHeader . ProjectName ) ;
2018-02-21 21:50:23 +00:00
NewNextEnd = Link . Ptr - Link . Location ;
2018-09-17 18:06:31 +00:00
fwrite ( Link . Location , NewNextEnd , 1 , HTML . Handle ) ;
fwrite ( HTML . Buffer . Location + From - > LinkOffsets . PrevStart + From - > LinkOffsets . PrevEnd + From - > LinkOffsets . NextStart + From - > LinkOffsets . NextEnd ,
HTML . FileSize - ( From - > LinkOffsets . PrevStart + From - > LinkOffsets . PrevEnd + From - > LinkOffsets . NextStart + From - > LinkOffsets . NextEnd ) ,
2018-02-21 21:50:23 +00:00
1 ,
2018-09-17 18:06:31 +00:00
HTML . Handle ) ;
2018-02-21 21:50:23 +00:00
}
else
{
2018-09-17 18:06:31 +00:00
fwrite ( HTML . Buffer . Location + From - > LinkOffsets . PrevStart + From - > LinkOffsets . PrevEnd ,
HTML . FileSize - ( From - > LinkOffsets . PrevStart + From - > LinkOffsets . PrevEnd ) ,
2018-02-21 21:50:23 +00:00
1 ,
2018-09-17 18:06:31 +00:00
HTML . Handle ) ;
2018-02-21 21:50:23 +00:00
}
From - > LinkOffsets . PrevEnd = NewPrevEnd ;
if ( FromHasOneNeighbour ) { From - > LinkOffsets . NextEnd = NewNextEnd ; }
} break ;
2018-09-17 18:06:31 +00:00
case LINK_FORWARDS :
2018-02-21 21:50:23 +00:00
{
if ( FromHasOneNeighbour )
{
2018-09-17 18:06:31 +00:00
fwrite ( HTML . Buffer . Location , From - > LinkOffsets . PrevStart , 1 , HTML . Handle ) ;
2018-02-21 21:50:23 +00:00
CopyStringToBuffer ( & Link ,
2018-09-17 18:06:31 +00:00
" <div class= \" episodeMarker first \" ><div>•</div><div>Welcome to <cite>%s</cite></div><div>•</div></div> \n " , DB . EntriesHeader . ProjectName ) ;
2018-02-21 21:50:23 +00:00
NewPrevEnd = Link . Ptr - Link . Location ;
2018-09-17 18:06:31 +00:00
fwrite ( Link . Location , NewPrevEnd , 1 , HTML . Handle ) ;
2018-02-21 21:50:23 +00:00
RewindBuffer ( & Link ) ;
2018-09-17 18:06:31 +00:00
fwrite ( HTML . Buffer . Location + From - > LinkOffsets . PrevStart + From - > LinkOffsets . PrevEnd ,
From - > LinkOffsets . NextStart , 1 , HTML . Handle ) ;
2018-02-21 21:50:23 +00:00
}
else
{
2018-09-17 18:06:31 +00:00
fwrite ( HTML . Buffer . Location , From - > LinkOffsets . PrevStart + From - > LinkOffsets . PrevEnd + From - > LinkOffsets . NextStart , 1 , HTML . Handle ) ;
2018-02-21 21:50:23 +00:00
}
if ( To )
{
CopyStringToBuffer ( & Link ,
" <a class= \" episodeMarker next \" href= \" %s \" ><div>⏬</div><div>Next: '%s'</div><div>⏬</div></a> \n " ,
ToPlayerURL . Location ,
To - > Title ) ;
}
else
{
CopyStringToBuffer ( & Link ,
2018-09-17 18:06:31 +00:00
" <div class= \" episodeMarker last \" ><div>•</div><div>You have arrived at the (current) end of <cite>%s</cite></div><div>•</div></div> \n " , DB . EntriesHeader . ProjectName ) ;
2018-02-21 21:50:23 +00:00
}
NewNextEnd = Link . Ptr - Link . Location ;
2018-09-17 18:06:31 +00:00
fwrite ( Link . Location , ( Link . Ptr - Link . Location ) , 1 , HTML . Handle ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
fwrite ( HTML . Buffer . Location + From - > LinkOffsets . PrevStart + From - > LinkOffsets . PrevEnd + From - > LinkOffsets . NextStart + From - > LinkOffsets . NextEnd ,
HTML . FileSize - ( From - > LinkOffsets . PrevStart + From - > LinkOffsets . PrevEnd + From - > LinkOffsets . NextStart + From - > LinkOffsets . NextEnd ) ,
2018-02-21 21:50:23 +00:00
1 ,
2018-09-17 18:06:31 +00:00
HTML . Handle ) ;
2018-02-21 21:50:23 +00:00
if ( FromHasOneNeighbour ) { From - > LinkOffsets . PrevEnd = NewPrevEnd ; }
From - > LinkOffsets . NextEnd = NewNextEnd ;
} break ;
}
if ( To ) { DeclaimBuffer ( & ToPlayerURL ) ; }
DeclaimBuffer ( & Link ) ;
2018-09-17 18:06:31 +00:00
fclose ( HTML . Handle ) ;
FreeBuffer ( & HTML . Buffer ) ;
SnipeEntryIntoMetadataBuffer ( From , FromIndex ) ;
2018-02-21 21:50:23 +00:00
return RC_SUCCESS ;
}
else
{
return RC_ERROR_FILE ;
}
}
2018-04-17 23:05:14 +00:00
int
2018-09-17 18:06:31 +00:00
DeleteNeighbourLinks ( neighbourhood * N )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
db_entry * Entry ;
int EntryIndex ;
if ( N - > PrevIndex > = 0 )
{
Entry = & N - > Prev ;
EntryIndex = N - > PrevIndex ;
N - > PreLinkPrevOffsetTotal = N - > Prev . LinkOffsets . PrevEnd
+ N - > Prev . LinkOffsets . NextStart
+ N - > Prev . LinkOffsets . NextEnd ;
}
else
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
Entry = & N - > Next ;
EntryIndex = N - > NextIndex ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
N - > PreLinkNextOffsetTotal = N - > Next . LinkOffsets . PrevEnd
+ N - > Next . LinkOffsets . NextStart
+ N - > Next . LinkOffsets . NextEnd ;
}
file_buffer HTML ;
if ( ReadPlayerPageIntoBuffer ( & HTML , Entry ) = = RC_SUCCESS )
{
if ( ! ( HTML . Handle = fopen ( HTML . Path , " w " ) ) ) { FreeBuffer ( & HTML . Buffer ) ; return RC_ERROR_FILE ; } ;
fwrite ( HTML . Buffer . Location , Entry - > LinkOffsets . PrevStart , 1 , HTML . Handle ) ;
fwrite ( HTML . Buffer . Location + Entry - > LinkOffsets . PrevStart + Entry - > LinkOffsets . PrevEnd , Entry - > LinkOffsets . NextStart , 1 , HTML . Handle ) ;
fwrite ( HTML . Buffer . Location + Entry - > LinkOffsets . PrevStart + Entry - > LinkOffsets . PrevEnd + Entry - > LinkOffsets . NextStart + Entry - > LinkOffsets . NextEnd ,
HTML . FileSize - ( Entry - > LinkOffsets . PrevStart + Entry - > LinkOffsets . PrevEnd + Entry - > LinkOffsets . NextStart + Entry - > LinkOffsets . NextEnd ) ,
2018-02-21 21:50:23 +00:00
1 ,
2018-09-17 18:06:31 +00:00
HTML . Handle ) ;
fclose ( HTML . Handle ) ;
Entry - > LinkOffsets . PrevEnd = 0 ;
Entry - > LinkOffsets . NextEnd = 0 ;
if ( N - > PrevIndex > = 0 )
{
N - > PrevOffsetModifier = N - > Prev . LinkOffsets . PrevEnd
+ N - > Prev . LinkOffsets . NextStart
+ N - > Prev . LinkOffsets . NextEnd
- N - > PreLinkPrevOffsetTotal ;
}
else
{
N - > NextOffsetModifier = N - > Next . LinkOffsets . PrevEnd
+ N - > Next . LinkOffsets . NextStart
+ N - > Next . LinkOffsets . NextEnd
- N - > PreLinkNextOffsetTotal ;
}
FreeBuffer ( & HTML . Buffer ) ;
SnipeEntryIntoMetadataBuffer ( Entry , EntryIndex ) ;
2018-02-21 21:50:23 +00:00
}
2018-09-17 18:06:31 +00:00
return RC_SUCCESS ;
2018-02-21 21:50:23 +00:00
}
2018-09-17 18:06:31 +00:00
void
LinkToNewEntry ( neighbourhood * N )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
N - > PreLinkThisOffsetTotal = N - > This . LinkOffsets . PrevEnd
+ N - > This . LinkOffsets . NextStart
+ N - > This . LinkOffsets . NextEnd ;
if ( N - > PrevIndex > = 0 )
2018-03-06 20:40:12 +00:00
{
2018-09-17 18:06:31 +00:00
N - > PreLinkPrevOffsetTotal = N - > Prev . LinkOffsets . PrevEnd
+ N - > Prev . LinkOffsets . NextStart
+ N - > Prev . LinkOffsets . NextEnd ;
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
InsertNeighbourLink ( & N - > Prev , N - > PrevIndex , & N - > This , LINK_FORWARDS , N - > FormerIsFirst ) ;
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
N - > PrevOffsetModifier = N - > Prev . LinkOffsets . PrevEnd
+ N - > Prev . LinkOffsets . NextStart
+ N - > Prev . LinkOffsets . NextEnd
- N - > PreLinkPrevOffsetTotal ;
2018-03-06 20:40:12 +00:00
}
2018-09-17 18:06:31 +00:00
if ( N - > NextIndex > = 0 )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
N - > PreLinkNextOffsetTotal = N - > Next . LinkOffsets . PrevEnd
+ N - > Next . LinkOffsets . NextStart
+ N - > Next . LinkOffsets . NextEnd ;
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
InsertNeighbourLink ( & N - > Next , N - > NextIndex , & N - > This , LINK_BACKWARDS , N - > LatterIsFinal ) ;
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
N - > NextOffsetModifier = N - > Next . LinkOffsets . PrevEnd
+ N - > Next . LinkOffsets . NextStart
+ N - > Next . LinkOffsets . NextEnd
- N - > PreLinkNextOffsetTotal ;
}
}
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
void
MarkNextAsFirst ( neighbourhood * N )
{
file_buffer HTML ;
ReadPlayerPageIntoBuffer ( & HTML , & N - > Next ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
buffer Link ;
ClaimBuffer ( & Link , " Link " , Kilobytes ( 4 ) ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
HTML . Handle = fopen ( HTML . Path , " w " ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
fwrite ( HTML . Buffer . Location , N - > Next . LinkOffsets . PrevStart , 1 , HTML . Handle ) ;
CopyStringToBuffer ( & Link ,
" <div class= \" episodeMarker first \" ><div>•</div><div>Welcome to <cite>%s</cite></div><div>•</div></div> \n " , DB . EntriesHeader . ProjectName ) ;
fwrite ( Link . Location , ( Link . Ptr - Link . Location ) , 1 , HTML . Handle ) ;
fwrite ( HTML . Buffer . Location + N - > Next . LinkOffsets . PrevStart + N - > Next . LinkOffsets . PrevEnd ,
HTML . FileSize - ( N - > Next . LinkOffsets . PrevStart + N - > Next . LinkOffsets . PrevEnd ) ,
1 ,
HTML . Handle ) ;
N - > Next . LinkOffsets . PrevEnd = Link . Ptr - Link . Location ;
DeclaimBuffer ( & Link ) ;
fclose ( HTML . Handle ) ;
FreeBuffer ( & HTML . Buffer ) ;
SnipeEntryIntoMetadataBuffer ( & N - > Next , N - > NextIndex ) ;
}
void
MarkPrevAsFinal ( neighbourhood * N )
{
file_buffer File ;
ReadPlayerPageIntoBuffer ( & File , & N - > Prev ) ;
File . Handle = fopen ( File . Path , " w " ) ;
buffer Link ;
ClaimBuffer ( & Link , " Link " , Kilobytes ( 4 ) ) ;
fwrite ( File . Buffer . Location , N - > Prev . LinkOffsets . PrevStart + N - > Prev . LinkOffsets . PrevEnd + N - > Prev . LinkOffsets . NextStart , 1 , File . Handle ) ;
CopyStringToBuffer ( & Link ,
" <div class= \" episodeMarker last \" ><div>•</div><div>You have arrived at the (current) end of <cite>%s</cite></div><div>•</div></div> \n " , DB . EntriesHeader . ProjectName ) ;
fwrite ( Link . Location , ( Link . Ptr - Link . Location ) , 1 , File . Handle ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
fwrite ( File . Buffer . Location + N - > Prev . LinkOffsets . PrevStart + N - > Prev . LinkOffsets . PrevEnd + N - > Prev . LinkOffsets . NextStart + N - > Prev . LinkOffsets . NextEnd ,
File . FileSize - ( N - > Prev . LinkOffsets . PrevStart + N - > Prev . LinkOffsets . PrevEnd + N - > Prev . LinkOffsets . NextStart + N - > Prev . LinkOffsets . NextEnd ) ,
1 ,
File . Handle ) ;
N - > Prev . LinkOffsets . NextEnd = Link . Ptr - Link . Location ;
DeclaimBuffer ( & Link ) ;
fclose ( File . Handle ) ;
FreeBuffer ( & File . Buffer ) ;
SnipeEntryIntoMetadataBuffer ( & N - > Prev , N - > PrevIndex ) ;
}
void
LinkOverDeletedEntry ( neighbourhood * N )
{
if ( DB . EntriesHeader . Count = = 1 )
{
DeleteNeighbourLinks ( N ) ;
}
else
{
if ( N - > DeletedEntryWasFirst )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
N - > PreLinkNextOffsetTotal = N - > Next . LinkOffsets . PrevEnd
+ N - > Next . LinkOffsets . NextStart
+ N - > Next . LinkOffsets . NextEnd ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
MarkNextAsFirst ( N ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
N - > NextOffsetModifier = N - > Next . LinkOffsets . PrevEnd
+ N - > Next . LinkOffsets . NextStart
+ N - > Next . LinkOffsets . NextEnd
- N - > PreLinkNextOffsetTotal ;
return ;
}
else if ( N - > DeletedEntryWasFinal )
{
N - > PreLinkPrevOffsetTotal = N - > Prev . LinkOffsets . PrevEnd
+ N - > Prev . LinkOffsets . NextStart
+ N - > Prev . LinkOffsets . NextEnd ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
MarkPrevAsFinal ( N ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
N - > PrevOffsetModifier = N - > Prev . LinkOffsets . PrevEnd
+ N - > Prev . LinkOffsets . NextStart
+ N - > Prev . LinkOffsets . NextEnd
- N - > PreLinkPrevOffsetTotal ;
return ;
}
else
{
// Assert(N->PrevIndex >= 0 && N->NextIndex >= 0)
N - > PreLinkPrevOffsetTotal = N - > Prev . LinkOffsets . PrevEnd
+ N - > Prev . LinkOffsets . NextStart
+ N - > Prev . LinkOffsets . NextEnd ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
N - > PreLinkNextOffsetTotal = N - > Next . LinkOffsets . PrevEnd
+ N - > Next . LinkOffsets . NextStart
+ N - > Next . LinkOffsets . NextEnd ;
2018-02-21 21:50:23 +00:00
}
2018-09-17 18:06:31 +00:00
InsertNeighbourLink ( & N - > Prev , N - > PrevIndex , & N - > Next , LINK_FORWARDS , N - > FormerIsFirst ) ;
InsertNeighbourLink ( & N - > Next , N - > NextIndex , & N - > Prev , LINK_BACKWARDS , N - > LatterIsFinal ) ;
N - > PrevOffsetModifier = N - > Prev . LinkOffsets . PrevEnd
+ N - > Prev . LinkOffsets . NextStart
+ N - > Prev . LinkOffsets . NextEnd
- N - > PreLinkPrevOffsetTotal ;
N - > NextOffsetModifier = N - > Next . LinkOffsets . PrevEnd
+ N - > Next . LinkOffsets . NextStart
+ N - > Next . LinkOffsets . NextEnd
- N - > PreLinkNextOffsetTotal ;
2018-04-01 20:58:53 +00:00
}
}
2018-02-21 21:50:23 +00:00
2018-04-01 20:58:53 +00:00
void
2018-09-17 18:06:31 +00:00
LinkNeighbours ( neighbourhood * N , enum8 ( link_types ) LinkType )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
if ( LinkType = = LINK_INCLUDE )
{
LinkToNewEntry ( N ) ;
}
else
{
LinkOverDeletedEntry ( N ) ;
}
}
void
DeleteSearchPageFromFilesystem ( ) // NOTE(matt): Do we need to handle relocating, like the PlayerPage function?
{
buffer SearchDirectory ;
ClaimBuffer ( & SearchDirectory , " SearchDirectory " , MAX_BASE_DIR_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + 10 ) ;
ConstructDirectoryPath ( & SearchDirectory , PAGE_SEARCH , Config . SearchLocation , " " ) ;
char SearchPagePath [ 1024 ] ;
CopyString ( SearchPagePath , sizeof ( SearchPagePath ) , " %s/index.html " , SearchDirectory . Location ) ;
remove ( SearchPagePath ) ;
remove ( SearchDirectory . Location ) ;
DeclaimBuffer ( & SearchDirectory ) ;
2018-04-01 20:58:53 +00:00
}
2018-02-21 21:50:23 +00:00
2018-04-01 20:58:53 +00:00
int
DeletePlayerPageFromFilesystem ( char * BaseFilename , char * PlayerLocation , bool Relocating )
{
// NOTE(matt): Once we have the notion of an output filename format, we'll need to use that here
buffer OutputDirectoryPath ;
2018-09-17 18:06:31 +00:00
ClaimBuffer ( & OutputDirectoryPath , " OutputDirectoryPath " , MAX_BASE_DIR_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10 ) ;
2018-04-01 20:58:53 +00:00
ConstructDirectoryPath ( & OutputDirectoryPath , PAGE_PLAYER , PlayerLocation , BaseFilename ) ;
DIR * PlayerDir ;
2018-02-21 21:50:23 +00:00
2018-04-01 20:58:53 +00:00
if ( ( PlayerDir = opendir ( OutputDirectoryPath . Location ) ) ) // There is a directory for the Player, which there probably should be if not for manual intervention
{
char PlayerPagePath [ 256 ] ;
2018-04-22 20:57:31 +00:00
CopyString ( PlayerPagePath , sizeof ( PlayerPagePath ) , " %s/index.html " , OutputDirectoryPath . Location ) ;
2018-04-01 20:58:53 +00:00
FILE * PlayerPage ;
if ( ( PlayerPage = fopen ( PlayerPagePath , " r " ) ) )
{
fclose ( PlayerPage ) ;
remove ( PlayerPagePath ) ;
}
2018-02-21 21:50:23 +00:00
2018-04-01 20:58:53 +00:00
closedir ( PlayerDir ) ;
if ( ( remove ( OutputDirectoryPath . Location ) = = - 1 ) )
{
LogError ( LOG_NOTICE , " Mostly deleted %s. Unable to remove directory %s: %s " , BaseFilename , OutputDirectoryPath . Location , strerror ( errno ) ) ;
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sMostly deleted%s %s. %sUnable to remove directory%s %s: %s " , ColourStrings [ EditTypes [ EDIT_DELETION ] . Colour ] , ColourStrings [ CS_END ] , BaseFilename , ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] , OutputDirectoryPath . Location , strerror ( errno ) ) ;
2018-04-01 20:58:53 +00:00
}
else
{
if ( ! Relocating )
{
LogError ( LOG_INFORMATIONAL , " Deleted %s " , BaseFilename ) ;
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sDeleted%s %s \n " , ColourStrings [ EditTypes [ EDIT_DELETION ] . Colour ] , ColourStrings [ CS_END ] , BaseFilename ) ;
2018-04-01 20:58:53 +00:00
}
2018-02-21 21:50:23 +00:00
2018-04-01 20:58:53 +00:00
}
2018-02-21 21:50:23 +00:00
}
2018-04-01 20:58:53 +00:00
DeclaimBuffer ( & OutputDirectoryPath ) ;
2018-02-21 21:50:23 +00:00
return RC_SUCCESS ;
}
2017-10-18 21:07:29 +00:00
int
2018-09-17 18:06:31 +00:00
DeleteFromDB ( neighbourhood * N , char * BaseFilename )
2017-10-18 21:07:29 +00:00
{
// TODO(matt): LogError()
2018-09-17 18:06:31 +00:00
DB . Header = * ( db_header * ) DB . Metadata . Buffer . Location ;
DB . EntriesHeader = * ( db_header_entries * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) ) ;
2017-10-18 21:07:29 +00:00
2018-09-17 18:06:31 +00:00
db_entry * Entry = { 0 } ;
int EntryIndex = BinarySearchForMetadataEntry ( & Entry , BaseFilename ) ;
2018-04-01 20:58:53 +00:00
if ( Entry )
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
int DeleteFileFrom = AccumulateDBEntryInsertionOffset ( EntryIndex ) ;
2018-04-01 20:58:53 +00:00
int DeleteFileTo = DeleteFileFrom + Entry - > Size ;
N - > ThisIndex = EntryIndex ;
2018-09-17 18:06:31 +00:00
GetNeighbourhood ( N , EDIT_DELETION ) ;
- - DB . EntriesHeader . Count ;
2017-08-29 20:35:28 +00:00
2018-09-17 18:06:31 +00:00
if ( DB . EntriesHeader . Count = = 0 )
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
// TODO(matt): Handle this differently, allowing 0 entries but > 0 assets?
DeleteSearchPageFromFilesystem ( ) ;
2018-05-01 21:05:44 +00:00
2018-09-17 18:06:31 +00:00
remove ( DB . Metadata . Path ) ;
DB . Metadata . FileSize = 0 ;
FreeBuffer ( & DB . Metadata . Buffer ) ;
2018-05-01 21:05:44 +00:00
2018-09-17 18:06:31 +00:00
remove ( DB . File . Path ) ;
DB . File . FileSize = 0 ;
FreeBuffer ( & DB . File . Buffer ) ;
2017-10-18 21:07:29 +00:00
}
else
2017-09-07 21:41:08 +00:00
{
2018-09-17 18:06:31 +00:00
if ( ! ( DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ) ) { FreeBuffer ( & DB . Metadata . Buffer ) ; return RC_ERROR_FILE ; }
if ( ! ( DB . File . Handle = fopen ( DB . File . Path , " w " ) ) ) { FreeBuffer ( & DB . File . Buffer ) ; return RC_ERROR_FILE ; }
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
fwrite ( & DB . Header , sizeof ( DB . Header ) , 1 , DB . Metadata . Handle ) ;
fwrite ( & DB . EntriesHeader , sizeof ( DB . EntriesHeader ) , 1 , DB . Metadata . Handle ) ;
fwrite ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) , sizeof ( DB . Entry ) * EntryIndex , 1 , DB . Metadata . Handle ) ;
fwrite ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * ( EntryIndex + 1 ) ,
DB . Metadata . FileSize - sizeof ( DB . Header ) - sizeof ( DB . EntriesHeader ) - sizeof ( DB . Entry ) * ( EntryIndex + 1 ) ,
1 , DB . Metadata . Handle ) ;
CycleFile ( & DB . Metadata ) ;
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
fwrite ( DB . File . Buffer . Location , DeleteFileFrom , 1 , DB . File . Handle ) ;
fwrite ( DB . File . Buffer . Location + DeleteFileTo , DB . File . FileSize - DeleteFileTo , 1 , DB . File . Handle ) ;
CycleFile ( & DB . File ) ;
2017-09-07 21:41:08 +00:00
}
2017-10-18 21:07:29 +00:00
}
2018-04-01 20:58:53 +00:00
return Entry ? RC_SUCCESS : RC_NOOP ;
2017-10-18 21:07:29 +00:00
}
int
2018-09-17 18:06:31 +00:00
SearchToBuffer ( buffers * CollationBuffers ) // NOTE(matt): This guy malloc's CollationBuffers->Search
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
if ( DB . Metadata . Buffer . Location )
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
DB . Header = * ( db_header * ) DB . Metadata . Buffer . Location ;
DB . EntriesHeader = * ( db_header_entries * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) ) ;
if ( DB . EntriesHeader . Count > 0 )
2017-12-07 01:15:13 +00:00
{
2018-09-17 18:06:31 +00:00
RewindBuffer ( & CollationBuffers - > IncludesSearch ) ;
buffer URL ;
ConstructResolvedAssetURL ( & URL , ASSET_CSS_CINERA , PAGE_SEARCH ) ;
CopyStringToBuffer ( & CollationBuffers - > IncludesSearch ,
" <meta charset= \" UTF-8 \" > \n "
" <meta name= \" generator \" content= \" Cinera %d.%d.%d \" > \n "
" \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s " ,
CINERA_APP_VERSION . Major ,
CINERA_APP_VERSION . Minor ,
CINERA_APP_VERSION . Patch ,
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
PushAssetLandmark ( & CollationBuffers - > IncludesSearch , ASSET_CSS_CINERA , PAGE_SEARCH ) ;
ConstructResolvedAssetURL ( & URL , ASSET_CSS_THEME , PAGE_SEARCH ) ;
CopyStringToBuffer ( & CollationBuffers - > IncludesSearch ,
" \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s " ,
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
PushAssetLandmark ( & CollationBuffers - > IncludesSearch , ASSET_CSS_THEME , PAGE_SEARCH ) ;
ConstructResolvedAssetURL ( & URL , ASSET_CSS_TOPICS , PAGE_SEARCH ) ;
CopyStringToBuffer ( & CollationBuffers - > IncludesSearch ,
" \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %s " ,
URL . Location ) ;
DeclaimBuffer ( & URL ) ;
PushAssetLandmark ( & CollationBuffers - > IncludesSearch , ASSET_CSS_TOPICS , PAGE_SEARCH ) ;
CopyStringToBuffer ( & CollationBuffers - > IncludesSearch ,
" \" > \n " ) ;
int ProjectIndex ;
for ( ProjectIndex = 0 ; ProjectIndex < ArrayCount ( ProjectInfo ) ; + + ProjectIndex )
2017-12-07 01:15:13 +00:00
{
2018-09-17 18:06:31 +00:00
if ( ! StringsDiffer ( ProjectInfo [ ProjectIndex ] . ProjectID , Config . ProjectID ) )
{
break ;
}
2017-12-07 01:15:13 +00:00
}
2018-09-17 18:06:31 +00:00
char * Theme = StringsDiffer ( Config . Theme , " " ) ? Config . Theme : Config . ProjectID ;
int Allowance = StringLength ( Theme ) * 2 + StringLength ( Config . ProjectID ) + StringLength ( Config . BaseURL ) + StringLength ( Config . PlayerLocation ) ;
char queryContainer [ 678 + Allowance ] ; // NOTE(matt): Update the size if changing the string
CopyString ( queryContainer , sizeof ( queryContainer ) ,
" <div class= \" cineraQueryContainer %s \" > \n "
" <label for= \" query \" >Query:</label> \n "
" <div class= \" inputContainer \" > \n "
" <input type= \" text \" autocomplete= \" off \" id= \" query \" > \n "
" <div class= \" spinner \" > \n "
" Downloading data... \n "
" </div> \n "
" </div> \n "
" </div> \n "
" <div id= \" cineraResultsSummary \" >Found: 0 episodes, 0 markers, 0h 0m 0s total.</div> \n "
" <div id= \" cineraResults \" data-single= \" %d \" ></div> \n "
" \n "
" <div id= \" cineraIndex \" class= \" %s \" data-project= \" %s \" data-baseURL= \" %s \" data-playerLocation= \" %s \" > \n "
" <div id= \" cineraIndexSort \" >Sort: Old to New ⏶</div> \n "
" <div id= \" cineraIndexEntries \" > \n " ,
Theme ,
Config . Mode & MODE_SINGLETAB ? 1 : 0 ,
Theme ,
Config . ProjectID ,
Config . BaseURL ,
Config . PlayerLocation ) ;
ConstructResolvedAssetURL ( & URL , ASSET_JS_SEARCH , PAGE_SEARCH ) ;
buffer Script ;
ClaimBuffer ( & Script , " Script " , ( 117 + URL . Ptr - URL . Location ) * 2 ) ; // NOTE(matt): Update the size if changing the string
CopyStringToBuffer ( & Script ,
" </div> \n "
" </div> \n "
" <script type= \" text/javascript \" src= \" %s " , URL . Location ) ;
// NOTE(matt): DeclaimBuffer(&URL) happens later, after Script is declaimed because Script was only claimed here
PushAssetLandmark ( & Script , ASSET_JS_SEARCH , PAGE_SEARCH ) ;
CopyStringToBuffer ( & Script ,
" \" ></script> " ) ;
buffer PlayerURL ;
ClaimBuffer ( & PlayerURL , " PlayerURL " , MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH ) ;
ConstructPlayerURL ( & PlayerURL , " " ) ;
char * ProjectUnit = 0 ;
if ( StringsDiffer ( ProjectInfo [ ProjectIndex ] . Unit , " " ) )
{
ProjectUnit = ProjectInfo [ ProjectIndex ] . Unit ;
}
char Number [ 16 ] ;
db_entry * This ;
char Text [ ( ProjectUnit ? StringLength ( ProjectUnit ) : 0 ) + sizeof ( Number ) + sizeof ( This - > Title ) + 3 ] ;
2017-12-07 01:15:13 +00:00
2018-09-17 18:06:31 +00:00
int EntryLength = StringLength ( PlayerURL . Location ) + sizeof ( Text ) + 82 ;
CollationBuffers - > Search . Size = StringLength ( queryContainer ) + ( DB . EntriesHeader . Count * EntryLength ) + Script . Ptr - Script . Location ;
2018-01-03 22:16:20 +00:00
2018-09-17 18:06:31 +00:00
if ( ! ( CollationBuffers - > Search . Location = malloc ( CollationBuffers - > Search . Size ) ) ) { return RC_ERROR_MEMORY ; }
2018-05-01 21:05:44 +00:00
2018-09-17 18:06:31 +00:00
CollationBuffers - > Search . ID = " Search " ;
CollationBuffers - > Search . Ptr = CollationBuffers - > Search . Location ;
2017-12-07 01:15:13 +00:00
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > Search , " %s " , queryContainer ) ;
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
int ProjectIDLength = StringLength ( Config . ProjectID ) ;
bool SearchRequired = FALSE ;
2018-03-06 20:40:12 +00:00
2018-09-17 18:06:31 +00:00
DB . Metadata . Buffer . Ptr = DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) ;
for ( int EntryIndex = 0 ; EntryIndex < DB . EntriesHeader . Count ; + + EntryIndex , DB . Metadata . Buffer . Ptr + = sizeof ( DB . Entry ) )
2017-11-11 00:34:47 +00:00
{
2018-09-17 18:06:31 +00:00
This = ( db_entry * ) DB . Metadata . Buffer . Ptr ;
if ( This - > Size > 0 )
2017-11-11 00:34:47 +00:00
{
2018-09-17 18:06:31 +00:00
SearchRequired = TRUE ;
CopyString ( Number , sizeof ( Number ) , " %s " , This - > BaseFilename + ProjectIDLength ) ;
if ( ProjectInfo [ ProjectIndex ] . NumberingScheme = = NS_LINEAR )
2017-11-11 00:34:47 +00:00
{
2018-09-17 18:06:31 +00:00
for ( int i = 0 ; Number [ i ] ; + + i )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
if ( Number [ i ] = = ' _ ' )
{
Number [ i ] = ' . ' ;
}
2018-04-01 20:58:53 +00:00
}
2017-11-11 00:34:47 +00:00
}
2018-09-17 18:06:31 +00:00
ConstructPlayerURL ( & PlayerURL , This - > BaseFilename ) ;
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
if ( ProjectUnit )
{
CopyStringToBuffer ( & CollationBuffers - > Search ,
" <div> \n "
" <a href= \" %s \" > " , PlayerURL . Location ) ;
2017-12-07 21:07:36 +00:00
2018-09-17 18:06:31 +00:00
CopyString ( Text , sizeof ( Text ) , " %s %s: %s " ,
ProjectUnit , // TODO(matt): Do we need to special-case the various numbering schemes?
Number ,
This - > Title ) ;
2017-12-07 21:07:36 +00:00
2018-09-17 18:06:31 +00:00
CopyStringToBufferHTMLSafe ( & CollationBuffers - > Search , Text ) ;
2017-12-07 21:07:36 +00:00
2018-09-17 18:06:31 +00:00
CopyStringToBuffer ( & CollationBuffers - > Search ,
" </a> \n "
" </div> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Search ,
" <div> \n "
" <a href= \" %s \" > " , PlayerURL . Location ) ;
CopyStringToBufferHTMLSafe ( & CollationBuffers - > Search , This - > Title ) ;
CopyStringToBuffer ( & CollationBuffers - > Search ,
" </a> \n "
" </div> \n " ) ;
}
2018-04-01 20:58:53 +00:00
}
2018-03-06 20:40:12 +00:00
}
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
OffsetLandmarks ( & CollationBuffers - > Search , ASSET_JS_SEARCH , PAGE_SEARCH ) ;
CopyBuffer ( & CollationBuffers - > Search , & Script ) ;
2017-12-12 23:24:10 +00:00
2018-09-17 18:06:31 +00:00
DeclaimBuffer ( & PlayerURL ) ;
DeclaimBuffer ( & Script ) ;
DeclaimBuffer ( & URL ) ;
2017-12-12 23:24:10 +00:00
2018-09-17 18:06:31 +00:00
if ( ! SearchRequired ) { return RC_NOOP ; }
else { return RC_SUCCESS ; }
2017-12-12 23:24:10 +00:00
}
2017-12-07 21:07:36 +00:00
}
2018-09-17 18:06:31 +00:00
return RC_NOOP ;
2017-12-12 23:24:10 +00:00
}
int
2018-09-17 18:06:31 +00:00
GeneratePlayerPage ( neighbourhood * N , buffers * CollationBuffers , template * PlayerTemplate , char * BaseFilename , bool Reinserting )
2017-12-12 23:24:10 +00:00
{
buffer OutputDirectoryPath ;
2018-09-17 18:06:31 +00:00
ClaimBuffer ( & OutputDirectoryPath , " OutputDirectoryPath " , MAX_BASE_DIR_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10 ) ;
2017-12-12 23:24:10 +00:00
ConstructDirectoryPath ( & OutputDirectoryPath , PAGE_PLAYER , Config . PlayerLocation , BaseFilename ) ;
2017-09-15 01:11:39 +00:00
2017-10-18 21:07:29 +00:00
DIR * OutputDirectoryHandle ;
2017-12-12 23:24:10 +00:00
if ( ! ( OutputDirectoryHandle = opendir ( OutputDirectoryPath . Location ) ) ) // TODO(matt): open()
2017-10-18 21:07:29 +00:00
{
2017-12-12 23:24:10 +00:00
if ( MakeDir ( OutputDirectoryPath . Location ) = = RC_ERROR_DIRECTORY )
2017-09-07 21:41:08 +00:00
{
2017-12-12 23:24:10 +00:00
LogError ( LOG_ERROR , " Unable to create directory %s: %s " , OutputDirectoryPath . Location , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to create directory %s: %s \n " , OutputDirectoryPath . Location , strerror ( errno ) ) ;
2017-10-18 21:07:29 +00:00
return RC_ERROR_DIRECTORY ;
} ;
}
closedir ( OutputDirectoryHandle ) ;
2017-12-12 23:24:10 +00:00
char PlayerPagePath [ 1024 ] ;
2018-04-22 20:57:31 +00:00
CopyString ( PlayerPagePath , sizeof ( PlayerPagePath ) , " %s/index.html " , OutputDirectoryPath . Location ) ;
2017-12-12 23:24:10 +00:00
DeclaimBuffer ( & OutputDirectoryPath ) ;
2018-01-03 22:16:20 +00:00
2018-09-17 18:06:31 +00:00
bool SearchInTemplate = FALSE ;
2018-01-03 22:16:20 +00:00
for ( int TagIndex = 0 ; TagIndex < PlayerTemplate - > Metadata . TagCount ; + + TagIndex )
{
2018-09-19 19:50:21 +00:00
if ( PlayerTemplate - > Metadata . Tags [ TagIndex ] . TagCode = = TAG_SEARCH )
2018-01-03 22:16:20 +00:00
{
2018-09-17 18:06:31 +00:00
SearchInTemplate = TRUE ;
SearchToBuffer ( CollationBuffers ) ;
2018-01-03 22:16:20 +00:00
break ;
}
}
2018-05-01 21:05:44 +00:00
2018-04-01 20:58:53 +00:00
BuffersToHTML ( CollationBuffers , PlayerTemplate , PlayerPagePath , PAGE_PLAYER , & N - > This . LinkOffsets . PrevStart ) ;
2018-09-17 18:06:31 +00:00
// NOTE(matt): A previous InsertNeighbourLink() call will have done SnipeEntryIntoMetadataBuffer(), but we must do it here now
// that PrevStart has been adjusted by BuffersToHTML()
SnipeEntryIntoMetadataBuffer ( & N - > This , N - > ThisIndex ) ;
UpdateLandmarksForNeighbourhood ( N , Reinserting ? EDIT_REINSERTION : EDIT_ADDITION ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
if ( SearchInTemplate )
2018-01-03 22:16:20 +00:00
{
2018-09-17 18:06:31 +00:00
FreeBuffer ( & CollationBuffers - > Search ) ;
2018-01-03 22:16:20 +00:00
}
2017-10-18 21:07:29 +00:00
return RC_SUCCESS ;
}
int
2018-09-17 18:06:31 +00:00
GenerateSearchPage ( buffers * CollationBuffers , template * SearchTemplate )
2017-10-18 21:07:29 +00:00
{
2017-12-12 23:24:10 +00:00
buffer OutputDirectoryPath ;
2018-09-17 18:06:31 +00:00
ClaimBuffer ( & OutputDirectoryPath , " OutputDirectoryPath " , MAX_BASE_DIR_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + 10 ) ;
ConstructDirectoryPath ( & OutputDirectoryPath , PAGE_SEARCH , Config . SearchLocation , 0 ) ;
2017-12-12 23:24:10 +00:00
DIR * OutputDirectoryHandle ;
if ( ! ( OutputDirectoryHandle = opendir ( OutputDirectoryPath . Location ) ) ) // TODO(matt): open()
{
if ( MakeDir ( OutputDirectoryPath . Location ) = = RC_ERROR_DIRECTORY )
{
LogError ( LOG_ERROR , " Unable to create directory %s: %s " , OutputDirectoryPath . Location , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to create directory %s: %s \n " , OutputDirectoryPath . Location , strerror ( errno ) ) ;
return RC_ERROR_DIRECTORY ;
} ;
}
closedir ( OutputDirectoryHandle ) ;
2018-09-17 18:06:31 +00:00
char SearchPagePath [ 1024 ] ;
CopyString ( SearchPagePath , sizeof ( SearchPagePath ) , " %s/index.html " , OutputDirectoryPath . Location ) ;
2017-12-12 23:24:10 +00:00
DeclaimBuffer ( & OutputDirectoryPath ) ;
2018-09-17 18:06:31 +00:00
switch ( SearchToBuffer ( CollationBuffers ) )
2018-04-01 20:58:53 +00:00
{
case RC_SUCCESS :
{
2018-09-17 18:06:31 +00:00
BuffersToHTML ( CollationBuffers , SearchTemplate , SearchPagePath , PAGE_SEARCH , 0 ) ;
UpdateLandmarksForSearch ( ) ;
2018-04-01 20:58:53 +00:00
break ;
}
case RC_NOOP :
{
2018-09-17 18:06:31 +00:00
DeleteSearchPageFromFilesystem ( ) ;
DeleteLandmarksForSearch ( ) ;
2018-04-01 20:58:53 +00:00
break ;
}
}
2018-09-17 18:06:31 +00:00
FreeBuffer ( & CollationBuffers - > Search ) ;
2017-10-18 21:07:29 +00:00
return RC_SUCCESS ;
}
int
2018-09-17 18:06:31 +00:00
DeleteEntry ( neighbourhood * Neighbourhood , char * BaseFilename )
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
if ( DeleteFromDB ( Neighbourhood , BaseFilename ) = = RC_SUCCESS )
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
LinkNeighbours ( Neighbourhood , LINK_EXCLUDE ) ;
2018-04-01 20:58:53 +00:00
DeletePlayerPageFromFilesystem ( BaseFilename , Config . PlayerLocation , FALSE ) ;
2018-09-17 18:06:31 +00:00
UpdateLandmarksForNeighbourhood ( Neighbourhood , EDIT_DELETION ) ;
2018-04-01 20:58:53 +00:00
return RC_SUCCESS ;
}
return RC_NOOP ;
}
2017-10-18 21:07:29 +00:00
2018-05-01 21:05:44 +00:00
int
2018-09-17 18:06:31 +00:00
InsertEntry ( neighbourhood * Neighbourhood , buffers * CollationBuffers , template * PlayerTemplate , template * BespokeTemplate , char * BaseFilename , bool RecheckingPrivacy )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
bool Reinserting = FALSE ;
2018-09-19 19:50:21 +00:00
if ( InsertIntoDB ( Neighbourhood , CollationBuffers , BespokeTemplate , BaseFilename , RecheckingPrivacy , & Reinserting ) = = RC_SUCCESS )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
LinkNeighbours ( Neighbourhood , LINK_INCLUDE ) ;
2018-09-19 19:50:21 +00:00
if ( BespokeTemplate - > File . Buffer . Location )
2018-05-01 21:05:44 +00:00
{
2018-09-17 18:06:31 +00:00
GeneratePlayerPage ( Neighbourhood , CollationBuffers , BespokeTemplate , BaseFilename , Reinserting ) ;
2018-09-19 19:50:21 +00:00
FreeTemplate ( BespokeTemplate ) ;
2018-05-01 21:05:44 +00:00
}
else
{
2018-09-17 18:06:31 +00:00
GeneratePlayerPage ( Neighbourhood , CollationBuffers , PlayerTemplate , BaseFilename , Reinserting ) ;
2018-05-01 21:05:44 +00:00
}
return RC_SUCCESS ;
2017-10-18 21:07:29 +00:00
}
2018-05-01 21:05:44 +00:00
return RC_NOOP ;
2017-10-18 21:07:29 +00:00
}
2017-08-29 20:35:28 +00:00
2018-03-06 20:40:12 +00:00
int
2018-09-17 18:06:31 +00:00
RecheckPrivacy ( buffers * CollationBuffers , template * SearchTemplate , template * PlayerTemplate , template * BespokeTemplate )
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
if ( DB . Metadata . FileSize > 0 )
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
DB . Header = * ( db_header * ) DB . Metadata . Buffer . Location ;
DB . EntriesHeader = * ( db_header_entries * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) ) ;
db_entry Entry = { } ;
2018-04-01 20:58:53 +00:00
int PrivateEntryIndex = 0 ;
2018-09-17 18:06:31 +00:00
db_entry PrivateEntries [ DB . EntriesHeader . Count ] ;
2018-04-01 20:58:53 +00:00
bool Inserted = FALSE ;
2018-09-17 18:06:31 +00:00
for ( int IndexEntry = 0 ; IndexEntry < DB . EntriesHeader . Count ; + + IndexEntry )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
Entry = * ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * IndexEntry ) ;
2018-04-01 20:58:53 +00:00
if ( Entry . Size = = 0 )
{
PrivateEntries [ PrivateEntryIndex ] = Entry ;
+ + PrivateEntryIndex ;
}
}
2018-09-17 18:06:31 +00:00
for ( int i = 0 ; i < PrivateEntryIndex ; + + i )
{
neighbourhood Neighbourhood = { } ;
InitNeighbourhood ( & Neighbourhood ) ;
ResetAssetLandmarks ( ) ;
Inserted = ( InsertEntry ( & Neighbourhood , CollationBuffers , PlayerTemplate , BespokeTemplate , PrivateEntries [ i ] . BaseFilename , TRUE ) = = RC_SUCCESS ) ;
}
if ( Inserted )
{
GenerateSearchPage ( CollationBuffers , SearchTemplate ) ;
DeleteStaleAssets ( ) ;
}
LastPrivacyCheck = time ( 0 ) ;
}
return RC_SUCCESS ;
}
# define DEBUG_LANDMARKS 0
# if DEBUG_LANDMARKS
void
VerifyLandmarks ( void )
{
printf ( " \n "
" VerifyLandmarks() \n " ) ;
DB . Metadata . Buffer . Ptr = DB . Metadata . Buffer . Location ;
DB . Header = * ( db_header * ) DB . Metadata . Buffer . Ptr ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Header ) ;
DB . EntriesHeader = * ( db_header_entries * ) DB . Metadata . Buffer . Ptr ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . EntriesHeader ) ;
char * FirstEntry = DB . Metadata . Buffer . Ptr ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Entry ) * DB . EntriesHeader . Count ;
DB . AssetsHeader = * ( db_header_assets * ) DB . Metadata . Buffer . Ptr ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . AssetsHeader ) ;
char * FirstAsset = DB . Metadata . Buffer . Ptr ;
buffer OutputDirectoryPath ;
ClaimBuffer ( & OutputDirectoryPath , " OutputDirectoryPath " , MAX_BASE_DIR_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10 ) ;
bool UniversalSuccess = TRUE ;
// Search
{
file_buffer HTML ;
ReadSearchPageIntoBuffer ( & HTML ) ;
char * Cursor = FirstAsset ;
for ( int j = 0 ; j < DB . AssetsHeader . Count ; + + j )
{
DB . Asset = * ( db_asset * ) Cursor ;
Cursor + = sizeof ( DB . Asset ) ;
landmark_range Target = BinarySearchForMetadataLandmark ( Cursor , PAGE_TYPE_SEARCH , DB . Asset . LandmarkCount ) ;
for ( int k = 0 ; k < Target . Length ; + + k )
{
DB . Landmark = * ( db_landmark * ) ( Cursor + sizeof ( db_landmark ) * ( Target . First + k ) ) ;
bool Found = FALSE ;
for ( int l = 0 ; l < Assets . Count ; + + l )
{
// TODO(matt): StringToInt (base16)
char HashString [ 9 ] ;
sprintf ( HashString , " %08x " , Assets . Asset [ l ] . Hash ) ;
if ( HTML . Buffer . Size > = DB . Landmark . Position + 8 & & ! StringsDifferT ( HashString , HTML . Buffer . Location + DB . Landmark . Position , 0 ) )
{
Found = TRUE ;
break ;
}
}
if ( ! Found )
{
printf ( " Failure!!! \n " ) ;
printf ( " %s \n " , HTML . Path ) ;
printf ( " %.*s [at byte %d] is not a known asset hash \n " , 8 , HTML . Buffer . Location + DB . Landmark . Position , DB . Landmark . Position ) ;
UniversalSuccess = FALSE ;
}
}
Cursor + = sizeof ( DB . Landmark ) * DB . Asset . LandmarkCount ;
}
FreeBuffer ( & HTML . Buffer ) ;
}
for ( int i = 0 ; i < DB . EntriesHeader . Count ; + + i )
{
DB . Entry = * ( db_entry * ) ( FirstEntry + sizeof ( DB . Entry ) * i ) ;
ConstructDirectoryPath ( & OutputDirectoryPath , PAGE_PLAYER , Config . PlayerLocation , DB . Entry . BaseFilename ) ;
file_buffer HTML ;
CopyString ( HTML . Path , sizeof ( HTML . Path ) , " %s/index.html " , OutputDirectoryPath . Location ) ;
if ( ReadFileIntoBuffer ( & HTML , 0 ) = = RC_SUCCESS )
{
char * Cursor = FirstAsset ;
for ( int j = 0 ; j < DB . AssetsHeader . Count ; + + j )
{
DB . Asset = * ( db_asset * ) Cursor ;
Cursor + = sizeof ( DB . Asset ) ;
landmark_range Target = BinarySearchForMetadataLandmark ( Cursor , i , DB . Asset . LandmarkCount ) ;
for ( int k = 0 ; k < Target . Length ; + + k )
{
DB . Landmark = * ( db_landmark * ) ( Cursor + sizeof ( db_landmark ) * ( Target . First + k ) ) ;
bool Found = FALSE ;
for ( int l = 0 ; l < Assets . Count ; + + l )
{
// TODO(matt): StringToInt (base16)
char HashString [ 9 ] ;
sprintf ( HashString , " %08x " , Assets . Asset [ l ] . Hash ) ;
if ( ( HTML . Buffer . Size > = DB . Landmark . Position + 8 ) & & ! StringsDifferT ( HashString , HTML . Buffer . Location + DB . Landmark . Position , 0 ) )
{
Found = TRUE ;
break ;
}
}
if ( ! Found )
{
printf ( " %sFailure ↓%s \n " , ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] ) ;
printf ( " %s \n " , HTML . Path ) ;
printf ( " %.*s [at byte %d] is not a known asset hash \n " , 8 , HTML . Buffer . Location + DB . Landmark . Position , DB . Landmark . Position ) ;
UniversalSuccess = FALSE ;
}
}
Cursor + = sizeof ( DB . Landmark ) * DB . Asset . LandmarkCount ;
}
FreeBuffer ( & HTML . Buffer ) ;
}
else
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
printf ( " %sFailed to open%s %s \n " , ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] , HTML . Path ) ;
2018-04-01 20:58:53 +00:00
}
2018-09-17 18:06:31 +00:00
}
Assert ( UniversalSuccess ) ;
printf ( " Success! All landmarks correspond to asset hashes \n " ) ;
DeclaimBuffer ( & OutputDirectoryPath ) ;
}
# endif
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
void
UpdateDeferredAssetChecksums ( void )
{
for ( int i = 0 ; i < Assets . Count ; + + i )
{
if ( Assets . Asset [ i ] . DeferredUpdate )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
UpdateAssetInDB ( i ) ;
2018-04-01 20:58:53 +00:00
}
2017-10-18 21:07:29 +00:00
}
}
2017-08-29 20:35:28 +00:00
2018-09-17 18:06:31 +00:00
# define DEBUG_EVENTS 0
# if DEBUG_EVENTS
void
PrintEvent ( struct inotify_event * Event , int EventIndex )
{
printf ( " \n Event[%d] \n "
" wd: %d \n "
" mask: %d \n " ,
EventIndex ,
Event - > wd ,
Event - > mask ) ;
if ( Event - > mask & IN_ACCESS ) { printf ( " IN_ACCESS \n " ) ; }
if ( Event - > mask & IN_ATTRIB ) { printf ( " IN_ATTRIB \n " ) ; }
if ( Event - > mask & IN_CLOSE_WRITE ) { printf ( " IN_CLOSE_WRITE \n " ) ; }
if ( Event - > mask & IN_CLOSE_NOWRITE ) { printf ( " IN_CLOSE_NOWRITE \n " ) ; }
if ( Event - > mask & IN_CREATE ) { printf ( " IN_CREATE \n " ) ; }
if ( Event - > mask & IN_DELETE ) { printf ( " IN_DELETE \n " ) ; }
if ( Event - > mask & IN_DELETE_SELF ) { printf ( " IN_DELETE_SELF \n " ) ; }
if ( Event - > mask & IN_MODIFY ) { printf ( " IN_MODIFY \n " ) ; }
if ( Event - > mask & IN_MOVE_SELF ) { printf ( " IN_MOVE_SELF \n " ) ; }
if ( Event - > mask & IN_MOVED_FROM ) { printf ( " IN_MOVED_FROM \n " ) ; }
if ( Event - > mask & IN_MOVED_TO ) { printf ( " IN_MOVED_TO \n " ) ; }
if ( Event - > mask & IN_OPEN ) { printf ( " IN_OPEN \n " ) ; }
if ( Event - > mask & IN_MOVE ) { printf ( " IN_MOVE \n " ) ; }
if ( Event - > mask & IN_CLOSE ) { printf ( " IN_CLOSE \n " ) ; }
if ( Event - > mask & IN_DONT_FOLLOW ) { printf ( " IN_DONT_FOLLOW \n " ) ; }
if ( Event - > mask & IN_EXCL_UNLINK ) { printf ( " IN_EXCL_UNLINK \n " ) ; }
if ( Event - > mask & IN_MASK_ADD ) { printf ( " IN_MASK_ADD \n " ) ; }
if ( Event - > mask & IN_ONESHOT ) { printf ( " IN_ONESHOT \n " ) ; }
if ( Event - > mask & IN_ONLYDIR ) { printf ( " IN_ONLYDIR \n " ) ; }
if ( Event - > mask & IN_IGNORED ) { printf ( " IN_IGNORED \n " ) ; }
if ( Event - > mask & IN_ISDIR ) { printf ( " IN_ISDIR \n " ) ; }
if ( Event - > mask & IN_Q_OVERFLOW ) { printf ( " IN_Q_OVERFLOW \n " ) ; }
if ( Event - > mask & IN_UNMOUNT ) { printf ( " IN_UNMOUNT \n " ) ; }
printf ( " cookie: %d \n "
" len: %d \n "
" name: %s \n " ,
Event - > cookie ,
Event - > len ,
Event - > name ) ;
}
2017-10-18 21:07:29 +00:00
# endif
2018-09-17 18:06:31 +00:00
int
MonitorFilesystem ( buffers * CollationBuffers , template * SearchTemplate , template * PlayerTemplate , template * BespokeTemplate )
{
2017-10-18 21:07:29 +00:00
buffer Events ;
2018-04-01 20:58:53 +00:00
if ( ClaimBuffer ( & Events , " inotify Events " , Kilobytes ( 4 ) ) = = RC_ARENA_FULL ) { return RC_ARENA_FULL ; } ;
2018-09-17 18:06:31 +00:00
Clear ( Events . Location , Events . Size ) ;
2017-10-18 21:07:29 +00:00
struct inotify_event * Event ;
2018-04-01 20:58:53 +00:00
int BytesRead = read ( inotifyInstance , Events . Location , Events . Size ) ; // TODO(matt): Handle error EINVAL
2018-09-17 18:06:31 +00:00
if ( inotifyInstance < 0 ) { perror ( " MonitorFilesystem() " ) ; }
2018-04-01 20:58:53 +00:00
2018-05-01 21:05:44 +00:00
// TODO(matt): Test this with longer update intervals, and combinations of events...
2018-09-17 18:06:31 +00:00
# if DEBUG_EVENTS
int i = 0 ;
# endif
bool Deleted = FALSE ;
bool Inserted = FALSE ;
bool UpdatedAsset = FALSE ;
2018-02-21 21:50:23 +00:00
for ( Events . Ptr = Events . Location ;
2018-09-17 18:06:31 +00:00
Events . Ptr - Events . Location < BytesRead & & Events . Ptr - Events . Location < Events . Size ;
Events . Ptr + = sizeof ( struct inotify_event ) + Event - > len
# if DEBUG_EVENTS
, + + i
# endif
)
2017-10-18 21:07:29 +00:00
{
2018-02-21 21:50:23 +00:00
Event = ( struct inotify_event * ) Events . Ptr ;
2018-09-17 18:06:31 +00:00
# if DEBUG_EVENTS
PrintEvent ( Event , i ) ;
# endif
//PrintWatchHandles();
for ( int WatchHandleIndex = 0 ; WatchHandleIndex < WatchHandles . Count ; + + WatchHandleIndex )
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
if ( Event - > wd = = WatchHandles . Handle [ WatchHandleIndex ] . Descriptor )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
switch ( WatchHandles . Handle [ WatchHandleIndex ] . Type )
2018-03-06 20:40:12 +00:00
{
2018-09-17 18:06:31 +00:00
case WT_HMML :
{
char * Ptr = Event - > name ;
Ptr + = ( StringLength ( Event - > name ) - StringLength ( " .hmml " ) ) ;
if ( ! ( StringsDiffer ( Ptr , " .hmml " ) ) )
{
* Ptr = ' \0 ' ;
neighbourhood Neighbourhood = { } ;
InitNeighbourhood ( & Neighbourhood ) ;
ResetAssetLandmarks ( ) ;
if ( Event - > mask & IN_DELETE )
{
Deleted | = ( DeleteEntry ( & Neighbourhood , Event - > name ) = = RC_SUCCESS ) ;
}
2018-10-05 19:57:09 +00:00
else if ( Event - > mask & IN_CLOSE_WRITE | | Event - > mask & IN_MOVED_TO )
2018-09-17 18:06:31 +00:00
{
Inserted | = ( InsertEntry ( & Neighbourhood , CollationBuffers , PlayerTemplate , BespokeTemplate , Event - > name , 0 ) = = RC_SUCCESS ) ;
}
}
} break ;
case WT_ASSET :
{
for ( int i = 0 ; i < Assets . Count ; + + i )
{
if ( ! StringsDiffer ( Event - > name , Assets . Asset [ i ] . Filename + Assets . Asset [ i ] . FilenameAt ) )
{
UpdateAsset ( i , 0 ) ;
UpdatedAsset = TRUE ;
break ;
}
}
} break ;
2018-03-06 20:40:12 +00:00
}
2018-09-17 18:06:31 +00:00
break ;
2018-02-21 21:50:23 +00:00
}
2018-05-01 21:05:44 +00:00
}
}
2018-09-17 18:06:31 +00:00
if ( Deleted | | Inserted )
2018-05-01 21:05:44 +00:00
{
2018-09-17 18:06:31 +00:00
UpdateDeferredAssetChecksums ( ) ;
GenerateSearchPage ( CollationBuffers , SearchTemplate ) ;
DeleteStaleAssets ( ) ;
# if DEBUG_LANDMARKS
VerifyLandmarks ( ) ;
# endif
2017-09-07 21:41:08 +00:00
}
2017-08-19 21:41:51 +00:00
2018-09-17 18:06:31 +00:00
if ( UpdatedAsset )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
# if DEBUG_LANDMARKS
VerifyLandmarks ( ) ;
# endif
2018-04-01 20:58:53 +00:00
}
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & Events ) ;
2018-04-01 20:58:53 +00:00
return RC_SUCCESS ;
2017-09-07 21:41:08 +00:00
}
2017-06-25 18:22:54 +00:00
2017-12-12 23:24:10 +00:00
int
RemoveDirectory ( char * Path )
{
if ( ( remove ( Path ) = = - 1 ) )
{
LogError ( LOG_NOTICE , " Unable to remove directory %s: %s " , Path , strerror ( errno ) ) ;
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sUnable to remove directory%s %s: %s \n " , ColourStrings [ CS_WARNING ] , ColourStrings [ CS_END ] , Path , strerror ( errno ) ) ;
2017-12-12 23:24:10 +00:00
return RC_ERROR_DIRECTORY ;
}
else
{
LogError ( LOG_INFORMATIONAL , " Deleted %s " , Path ) ;
2018-09-17 18:06:31 +00:00
//fprintf(stderr, "%sDeleted%s %s\n", ColourStrings[CS_DELETION], ColourStrings[CS_END], Path);
2017-12-12 23:24:10 +00:00
return RC_SUCCESS ;
}
}
int
RemoveDirectoryRecursively ( char * Path )
{
if ( RemoveDirectory ( Path ) = = RC_ERROR_DIRECTORY ) { return RC_ERROR_DIRECTORY ; }
char * Ptr = Path + StringLength ( Path ) - 1 ;
while ( Ptr > Path )
{
if ( * Ptr = = ' / ' )
{
* Ptr = ' \0 ' ;
if ( RemoveDirectory ( Path ) = = RC_ERROR_DIRECTORY ) { return RC_ERROR_DIRECTORY ; }
}
- - Ptr ;
}
return RC_SUCCESS ;
}
int
2018-09-17 18:06:31 +00:00
UpgradeDB ( int OriginalDBVersion )
2017-12-12 23:24:10 +00:00
{
2018-02-21 21:50:23 +00:00
// NOTE(matt): For each new DB version, we must declare and initialise one instance of each preceding version, only cast the
2018-09-17 18:06:31 +00:00
// incoming DB to the type of the OriginalDBVersion, and move into the final case all operations on that incoming DB
2018-02-21 21:50:23 +00:00
bool OnlyHeaderChanged = TRUE ;
int OriginalHeaderSize = 0 ;
2018-09-17 18:06:31 +00:00
database1 DB1 = { } ;
database2 DB2 = { } ;
database3 DB3 = { } ;
2018-02-21 21:50:23 +00:00
switch ( OriginalDBVersion )
2017-12-12 23:24:10 +00:00
{
case 1 :
{
2018-09-17 18:06:31 +00:00
OriginalHeaderSize = sizeof ( db_header1 ) ;
DB1 . Header = * ( db_header1 * ) DB . Metadata . Buffer . Location ;
DB2 . Header . DBVersion = CINERA_DB_VERSION ;
DB2 . Header . AppVersion = CINERA_APP_VERSION ;
DB2 . Header . HMMLVersion . Major = hmml_version . Major ;
DB2 . Header . HMMLVersion . Minor = hmml_version . Minor ;
DB2 . Header . HMMLVersion . Patch = hmml_version . Patch ;
DB2 . Header . EntryCount = DB1 . Header . EntryCount ;
Clear ( DB2 . Header . SearchLocation , sizeof ( DB2 . Header . SearchLocation ) ) ;
Clear ( DB2 . Header . PlayerLocation , sizeof ( DB2 . Header . PlayerLocation ) ) ;
2018-02-21 21:50:23 +00:00
}
case 2 :
{
if ( OriginalDBVersion = = 2 )
2017-12-12 23:24:10 +00:00
{
2018-09-17 18:06:31 +00:00
OriginalHeaderSize = sizeof ( db_header2 ) ;
DB2 . Header = * ( db_header2 * ) DB . Metadata . Buffer . Location ;
2017-12-12 23:24:10 +00:00
2018-09-17 18:06:31 +00:00
DB . Header . InitialDBVersion = DB2 . Header . DBVersion ;
DB . Header . InitialAppVersion = DB2 . Header . AppVersion ;
DB . Header . InitialHMMLVersion . Major = DB2 . Header . HMMLVersion . Major ;
DB . Header . InitialHMMLVersion . Minor = DB2 . Header . HMMLVersion . Minor ;
DB . Header . InitialHMMLVersion . Patch = DB2 . Header . HMMLVersion . Patch ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
}
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
DB . EntriesHeader . Count = DB2 . Header . EntryCount ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
ClearCopyStringNoFormat ( DB . EntriesHeader . ProjectID , sizeof ( DB . EntriesHeader . ProjectID ) , Config . ProjectID ) ;
2017-12-12 23:24:10 +00:00
2018-09-17 18:06:31 +00:00
Clear ( DB . EntriesHeader . ProjectName , sizeof ( DB . EntriesHeader . ProjectName ) ) ;
2018-02-21 21:50:23 +00:00
for ( int ProjectIndex = 0 ; ProjectIndex < ArrayCount ( ProjectInfo ) ; + + ProjectIndex )
2017-12-12 23:24:10 +00:00
{
2018-02-21 21:50:23 +00:00
if ( ! StringsDiffer ( ProjectInfo [ ProjectIndex ] . ProjectID , Config . ProjectID ) )
{
2018-09-17 18:06:31 +00:00
CopyString ( DB . EntriesHeader . ProjectName , sizeof ( DB . EntriesHeader . ProjectName ) , " %s " , ProjectInfo [ ProjectIndex ] . FullName ) ;
2018-02-21 21:50:23 +00:00
break ;
}
}
2018-09-17 18:06:31 +00:00
ClearCopyStringNoFormat ( DB . EntriesHeader . BaseURL , sizeof ( DB . EntriesHeader . BaseURL ) , Config . BaseURL ) ;
ClearCopyStringNoFormat ( DB . EntriesHeader . SearchLocation , sizeof ( DB . EntriesHeader . SearchLocation ) , DB2 . Header . SearchLocation ) ;
ClearCopyStringNoFormat ( DB . EntriesHeader . PlayerLocation , sizeof ( DB . EntriesHeader . PlayerLocation ) , DB2 . Header . PlayerLocation ) ;
ClearCopyStringNoFormat ( DB . EntriesHeader . PlayerURLPrefix , sizeof ( DB . EntriesHeader . PlayerURLPrefix ) , Config . PlayerURLPrefix ) ;
2018-02-21 21:50:23 +00:00
OnlyHeaderChanged = FALSE ;
2017-12-12 23:24:10 +00:00
2018-09-17 18:06:31 +00:00
if ( ! ( DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ) ) { FreeBuffer ( & DB . Metadata . Buffer ) ; return RC_ERROR_FILE ; }
fwrite ( & DB . Header , sizeof ( DB . Header ) , 1 , DB . Metadata . Handle ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
DB . Metadata . Buffer . Ptr = DB . Metadata . Buffer . Location + OriginalHeaderSize ;
DB . File . Buffer . Ptr + = StringLength ( " --- \n " ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
for ( int EntryIndex = 0 ; EntryIndex < DB . EntriesHeader . Count ; + + EntryIndex )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
// NOTE(matt): We can use either db_entry1 or 2 here because they are the same
db_entry2 This = * ( db_entry2 * ) DB . Metadata . Buffer . Ptr ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
DB . Entry . LinkOffsets . PrevStart = 0 ;
DB . Entry . LinkOffsets . NextStart = 0 ;
DB . Entry . LinkOffsets . PrevEnd = 0 ;
DB . Entry . LinkOffsets . NextEnd = 0 ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
DB . Entry . Size = This . Size ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
ClearCopyStringNoFormat ( DB . Entry . BaseFilename , sizeof ( DB . Entry . BaseFilename ) , This . BaseFilename ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
char * EntryStart = DB . File . Buffer . Ptr ;
SeekBufferForString ( & DB . File . Buffer , " title: \" " , C_SEEK_FORWARDS , C_SEEK_AFTER ) ;
Clear ( DB . Entry . Title , sizeof ( DB . Entry . Title ) ) ;
CopyStringNoFormatT ( DB . Entry . Title , sizeof ( DB . Entry . Title ) , DB . File . Buffer . Ptr , ' \n ' ) ;
DB . Entry . Title [ StringLength ( DB . Entry . Title ) - 1 ] = ' \0 ' ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
fwrite ( & DB . Entry , sizeof ( DB . Entry ) , 1 , DB . Metadata . Handle ) ;
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
DB . Metadata . Buffer . Ptr + = sizeof ( This ) ;
EntryStart + = This . Size ;
DB . File . Buffer . Ptr = EntryStart ;
2018-02-21 21:50:23 +00:00
}
2018-09-17 18:06:31 +00:00
CycleFile ( & DB . Metadata ) ;
}
case 3 :
{
OriginalHeaderSize = sizeof ( db_header3 ) ;
DB3 . Header = * ( db_header3 * ) DB . Metadata . Buffer . Location ;
DB . Header . HexSignature = FOURCC ( " CNRA " ) ;
DB . Header . CurrentDBVersion = CINERA_DB_VERSION ;
DB . Header . CurrentAppVersion = CINERA_APP_VERSION ;
DB . Header . CurrentHMMLVersion . Major = hmml_version . Major ;
DB . Header . CurrentHMMLVersion . Minor = hmml_version . Minor ;
DB . Header . CurrentHMMLVersion . Patch = hmml_version . Patch ;
DB . Header . InitialDBVersion = DB3 . Header . InitialDBVersion ;
DB . Header . InitialAppVersion = DB3 . Header . InitialAppVersion ;
DB . Header . InitialHMMLVersion . Major = DB3 . Header . InitialHMMLVersion . Major ;
DB . Header . InitialHMMLVersion . Minor = DB3 . Header . InitialHMMLVersion . Minor ;
DB . Header . InitialHMMLVersion . Patch = DB3 . Header . InitialHMMLVersion . Patch ;
DB . Header . BlockCount = 0 ;
// TODO(matt): Consider allowing zero NTRY or ASET blocks in a future version
DB . EntriesHeader . BlockID = FOURCC ( " NTRY " ) ;
DB . EntriesHeader . Count = DB3 . Header . EntryCount ;
ClearCopyStringNoFormat ( DB . EntriesHeader . ProjectID , sizeof ( DB . EntriesHeader . ProjectID ) , DB3 . Header . ProjectID ) ;
ClearCopyStringNoFormat ( DB . EntriesHeader . ProjectName , sizeof ( DB . EntriesHeader . ProjectName ) , DB3 . Header . ProjectName ) ;
ClearCopyStringNoFormat ( DB . EntriesHeader . BaseURL , sizeof ( DB . EntriesHeader . BaseURL ) , DB3 . Header . BaseURL ) ;
ClearCopyStringNoFormat ( DB . EntriesHeader . SearchLocation , sizeof ( DB . EntriesHeader . SearchLocation ) , DB3 . Header . SearchLocation ) ;
ClearCopyStringNoFormat ( DB . EntriesHeader . PlayerLocation , sizeof ( DB . EntriesHeader . PlayerLocation ) , DB3 . Header . PlayerLocation ) ;
ClearCopyStringNoFormat ( DB . EntriesHeader . PlayerURLPrefix , sizeof ( DB . EntriesHeader . PlayerURLPrefix ) , DB3 . Header . PlayerURLPrefix ) ;
+ + DB . Header . BlockCount ;
DB . AssetsHeader . BlockID = FOURCC ( " ASET " ) ;
DB . AssetsHeader . Count = 0 ;
ClearCopyStringNoFormat ( DB . AssetsHeader . RootDir , sizeof ( DB . AssetsHeader . RootDir ) , Config . RootDir ) ;
ClearCopyStringNoFormat ( DB . AssetsHeader . RootURL , sizeof ( DB . AssetsHeader . RootURL ) , Config . RootURL ) ;
ClearCopyStringNoFormat ( DB . AssetsHeader . CSSDir , sizeof ( DB . AssetsHeader . CSSDir ) , Config . CSSDir ) ;
ClearCopyStringNoFormat ( DB . AssetsHeader . ImagesDir , sizeof ( DB . AssetsHeader . ImagesDir ) , Config . ImagesDir ) ;
ClearCopyStringNoFormat ( DB . AssetsHeader . JSDir , sizeof ( DB . AssetsHeader . JSDir ) , Config . JSDir ) ;
+ + DB . Header . BlockCount ;
OnlyHeaderChanged = FALSE ;
if ( ! ( DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ) ) { FreeBuffer ( & DB . Metadata . Buffer ) ; return RC_ERROR_FILE ; }
fwrite ( & DB . Header , sizeof ( DB . Header ) , 1 , DB . Metadata . Handle ) ;
fwrite ( & DB . EntriesHeader , sizeof ( DB . EntriesHeader ) , 1 , DB . Metadata . Handle ) ;
fwrite ( DB . Metadata . Buffer . Location + OriginalHeaderSize ,
sizeof ( DB . Entry ) ,
DB . EntriesHeader . Count ,
DB . Metadata . Handle ) ;
fwrite ( & DB . AssetsHeader , sizeof ( DB . AssetsHeader ) , 1 , DB . Metadata . Handle ) ;
CycleFile ( & DB . Metadata ) ;
2017-12-12 23:24:10 +00:00
}
2018-09-17 18:06:31 +00:00
// TODO(matt); DBVersion 4 is the first that uses HexSignatures
// We should try and deprecate earlier versions and just enforce a clean rebuild for invalid DB files
// Perhaps do this either the next time we have to bump the version, or when we scrap Single Edition
2017-12-12 23:24:10 +00:00
}
2018-02-21 21:50:23 +00:00
if ( OnlyHeaderChanged )
{
2018-09-17 18:06:31 +00:00
if ( ! ( DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ) ) { FreeBuffer ( & DB . Metadata . Buffer ) ; return RC_ERROR_FILE ; }
fwrite ( & DB . Header , sizeof ( DB . Header ) , 1 , DB . Metadata . Handle ) ;
fwrite ( DB . Metadata . Buffer . Location + OriginalHeaderSize , DB . Metadata . FileSize - OriginalHeaderSize , 1 , DB . Metadata . Handle ) ;
CycleFile ( & DB . Metadata ) ;
2018-02-21 21:50:23 +00:00
}
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " \n %sUpgraded Cinera DB from %d to %d!%s \n \n " , ColourStrings [ CS_SUCCESS ] , OriginalDBVersion , DB . Header . CurrentDBVersion , ColourStrings [ CS_END ] ) ;
2017-12-12 23:24:10 +00:00
return RC_SUCCESS ;
}
2017-10-18 21:07:29 +00:00
typedef struct
{
bool Present ;
char ID [ 32 ] ;
2018-09-17 18:06:31 +00:00
} entry_presence_id ; // Metadata, unless we actually want to bolster this?
2017-10-18 21:07:29 +00:00
2018-04-01 20:58:53 +00:00
void
RemoveChildDirectories ( buffer FullPath , char * ParentDirectory )
{
char * Ptr = FullPath . Location + StringLength ( ParentDirectory ) ;
RemoveDirectory ( FullPath . Location ) ;
while ( FullPath . Ptr > Ptr )
{
if ( * FullPath . Ptr = = ' / ' )
{
* FullPath . Ptr = ' \0 ' ;
RemoveDirectory ( FullPath . Location ) ;
}
- - FullPath . Ptr ;
}
}
2017-09-07 21:41:08 +00:00
int
2018-09-17 18:06:31 +00:00
DeleteDeadDBEntries ( void )
2017-09-07 21:41:08 +00:00
{
2018-04-01 20:58:53 +00:00
bool NewPlayerLocation = FALSE ;
2018-09-17 18:06:31 +00:00
bool NewSearchLocation = FALSE ;
if ( StringsDiffer ( DB . EntriesHeader . PlayerLocation , Config . PlayerLocation ) )
2017-12-12 23:24:10 +00:00
{
2018-04-01 20:58:53 +00:00
buffer OldPlayerDirectory ;
2018-09-17 18:06:31 +00:00
ClaimBuffer ( & OldPlayerDirectory , " OldPlayerDirectory " ,
MAX_BASE_DIR_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 ) ;
ConstructDirectoryPath ( & OldPlayerDirectory , PAGE_PLAYER , DB . EntriesHeader . PlayerLocation , 0 ) ;
2018-04-01 20:58:53 +00:00
buffer NewPlayerDirectory ;
2018-09-17 18:06:31 +00:00
ClaimBuffer ( & NewPlayerDirectory , " NewPlayerDirectory " ,
MAX_BASE_DIR_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 ) ;
2018-04-01 20:58:53 +00:00
ConstructDirectoryPath ( & NewPlayerDirectory , PAGE_PLAYER , Config . PlayerLocation , 0 ) ;
2018-09-17 18:06:31 +00:00
printf ( " %sRelocating Player Page%s from %s to %s%s \n " ,
ColourStrings [ CS_REINSERTION ] , DB . EntriesHeader . Count > 1 ? " s " : " " ,
OldPlayerDirectory . Location , NewPlayerDirectory . Location , ColourStrings [ CS_END ] ) ;
2018-04-01 20:58:53 +00:00
DeclaimBuffer ( & NewPlayerDirectory ) ;
2018-09-17 18:06:31 +00:00
for ( int EntryIndex = 0 ; EntryIndex < DB . EntriesHeader . Count ; + + EntryIndex )
2017-12-12 23:24:10 +00:00
{
2018-09-17 18:06:31 +00:00
db_entry This = * ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * EntryIndex ) ;
ConstructDirectoryPath ( & OldPlayerDirectory , PAGE_PLAYER , DB . EntriesHeader . PlayerLocation , This . BaseFilename ) ;
DeletePlayerPageFromFilesystem ( This . BaseFilename , DB . EntriesHeader . PlayerLocation , TRUE ) ;
2017-12-12 23:24:10 +00:00
}
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
ConstructDirectoryPath ( & OldPlayerDirectory , PAGE_PLAYER , DB . EntriesHeader . PlayerLocation , 0 ) ;
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
if ( StringLength ( DB . EntriesHeader . PlayerLocation ) > 0 )
2018-04-01 20:58:53 +00:00
{
RemoveChildDirectories ( OldPlayerDirectory , Config . BaseDir ) ;
}
DeclaimBuffer ( & OldPlayerDirectory ) ;
2018-09-17 18:06:31 +00:00
ClearCopyStringNoFormat ( DB . EntriesHeader . PlayerLocation , sizeof ( DB . EntriesHeader . PlayerLocation ) , Config . PlayerLocation ) ;
* ( db_header * ) DB . Metadata . Buffer . Location = DB . Header ;
2018-04-01 20:58:53 +00:00
NewPlayerLocation = TRUE ;
2018-02-21 21:50:23 +00:00
}
2018-09-17 18:06:31 +00:00
if ( StringsDiffer ( DB . EntriesHeader . SearchLocation , Config . SearchLocation ) )
2017-12-12 23:24:10 +00:00
{
2018-09-17 18:06:31 +00:00
buffer OldSearchDirectory ;
ClaimBuffer ( & OldSearchDirectory , " OldSearchDirectory " , MAX_BASE_DIR_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 ) ;
ConstructDirectoryPath ( & OldSearchDirectory , PAGE_SEARCH , DB . EntriesHeader . SearchLocation , 0 ) ;
buffer NewSearchDirectory ;
ClaimBuffer ( & NewSearchDirectory , " NewSearchDirectory " , MAX_BASE_DIR_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 ) ;
ConstructDirectoryPath ( & NewSearchDirectory , PAGE_SEARCH , Config . SearchLocation , 0 ) ;
printf ( " %sRelocating Search Page from %s to %s%s \n " ,
ColourStrings [ CS_REINSERTION ] , OldSearchDirectory . Location , NewSearchDirectory . Location , ColourStrings [ CS_END ] ) ;
DeclaimBuffer ( & NewSearchDirectory ) ;
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
char SearchPagePath [ 2048 ] = { 0 } ;
CopyString ( SearchPagePath , sizeof ( SearchPagePath ) , " %s/index.html " , OldSearchDirectory . Location ) ;
remove ( SearchPagePath ) ;
if ( StringLength ( DB . EntriesHeader . SearchLocation ) > 0 )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
RemoveChildDirectories ( OldSearchDirectory , Config . BaseDir ) ;
2018-04-01 20:58:53 +00:00
}
2018-09-17 18:06:31 +00:00
DeclaimBuffer ( & OldSearchDirectory ) ;
2017-12-12 23:24:10 +00:00
2018-09-17 18:06:31 +00:00
ClearCopyStringNoFormat ( DB . EntriesHeader . SearchLocation , sizeof ( DB . EntriesHeader . SearchLocation ) , Config . SearchLocation ) ;
* ( db_header * ) DB . Metadata . Buffer . Location = DB . Header ;
NewSearchLocation = TRUE ;
2018-04-01 20:58:53 +00:00
}
2018-09-17 18:06:31 +00:00
if ( NewPlayerLocation | | NewSearchLocation )
2018-04-01 20:58:53 +00:00
{
2018-09-17 18:06:31 +00:00
if ( ! ( DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ) ) { FreeBuffer ( & DB . Metadata . Buffer ) ; return RC_ERROR_FILE ; }
fwrite ( DB . Metadata . Buffer . Location , DB . Metadata . FileSize , 1 , DB . Metadata . Handle ) ;
CycleFile ( & DB . Metadata ) ;
2017-12-12 23:24:10 +00:00
}
2018-09-17 18:06:31 +00:00
entry_presence_id Entries [ DB . EntriesHeader . Count ] ;
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
for ( int EntryIndex = 0 ; EntryIndex < DB . EntriesHeader . Count ; + + EntryIndex )
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
db_entry This = * ( db_entry * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header ) + sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * EntryIndex ) ;
2018-04-22 20:57:31 +00:00
CopyStringNoFormat ( Entries [ EntryIndex ] . ID , sizeof ( Entries [ EntryIndex ] . ID ) , This . BaseFilename ) ;
2017-11-11 00:34:47 +00:00
Entries [ EntryIndex ] . Present = FALSE ;
2017-10-18 21:07:29 +00:00
}
2017-09-07 21:41:08 +00:00
DIR * ProjectDirHandle ;
if ( ! ( ProjectDirHandle = opendir ( Config . ProjectDir ) ) )
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Unable to scan project directory %s: %s " , Config . ProjectDir , strerror ( errno ) ) ;
2017-09-07 21:41:08 +00:00
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 ;
2017-08-29 20:35:28 +00:00
2017-09-07 21:41:08 +00:00
while ( ( ProjectFiles = readdir ( ProjectDirHandle ) ) )
{
char * Ptr ;
Ptr = ProjectFiles - > d_name ;
Ptr + = ( StringLength ( ProjectFiles - > d_name ) - StringLength ( " .hmml " ) ) ;
if ( ! ( StringsDiffer ( Ptr , " .hmml " ) ) )
{
* Ptr = ' \0 ' ;
2018-09-17 18:06:31 +00:00
for ( int i = 0 ; i < DB . EntriesHeader . Count ; + + i )
2017-08-19 21:41:51 +00:00
{
2017-10-18 21:07:29 +00:00
if ( ! StringsDiffer ( Entries [ i ] . ID , ProjectFiles - > d_name ) )
{
Entries [ i ] . Present = TRUE ;
2017-09-07 21:41:08 +00:00
break ;
2017-10-18 21:07:29 +00:00
}
2017-08-29 20:35:28 +00:00
}
}
}
2017-09-07 21:41:08 +00:00
closedir ( ProjectDirHandle ) ;
2017-10-18 21:07:29 +00:00
bool Deleted = FALSE ;
2018-09-17 18:06:31 +00:00
for ( int i = 0 ; i < DB . EntriesHeader . Count ; + + i )
2017-10-18 21:07:29 +00:00
{
if ( Entries [ i ] . Present = = FALSE )
{
Deleted = TRUE ;
2018-04-22 20:57:31 +00:00
neighbourhood Neighbourhood = { } ;
2018-09-17 18:06:31 +00:00
InitNeighbourhood ( & Neighbourhood ) ;
ResetAssetLandmarks ( ) ;
DeleteEntry ( & Neighbourhood , Entries [ i ] . ID ) ;
2017-10-18 21:07:29 +00:00
}
}
return Deleted ? RC_SUCCESS : RC_NOOP ;
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
2018-09-17 18:06:31 +00:00
SyncDBWithInput ( buffers * CollationBuffers , template * SearchTemplate , template * PlayerTemplate , template * BespokeTemplate )
2017-09-15 01:11:39 +00:00
{
2018-09-17 18:06:31 +00:00
if ( DB . Metadata . FileSize > 0 & & Config . Mode & MODE_NOREVVEDRESOURCE )
{
DeleteStaleLandmarks ( ) ;
}
2017-11-11 00:34:47 +00:00
bool Deleted = FALSE ;
2018-09-17 18:06:31 +00:00
Deleted = ( DB . Metadata . FileSize > 0 & & DB . File . FileSize > 0 & & DeleteDeadDBEntries ( ) = = RC_SUCCESS ) ;
2017-09-15 01:11:39 +00:00
2017-10-18 21:07:29 +00:00
DIR * ProjectDirHandle ;
if ( ! ( ProjectDirHandle = opendir ( Config . ProjectDir ) ) )
{
LogError ( 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-09-15 01:11:39 +00:00
2017-10-18 21:07:29 +00:00
struct dirent * ProjectFiles ;
bool Inserted = FALSE ;
while ( ( ProjectFiles = readdir ( ProjectDirHandle ) ) )
2017-09-15 01:11:39 +00:00
{
2017-10-18 21:07:29 +00:00
char * Ptr = ProjectFiles - > d_name ;
Ptr + = ( StringLength ( ProjectFiles - > d_name ) - StringLength ( " .hmml " ) ) ;
if ( ! ( StringsDiffer ( Ptr , " .hmml " ) ) )
2017-09-15 01:11:39 +00:00
{
2017-10-18 21:07:29 +00:00
* Ptr = ' \0 ' ;
2018-04-22 20:57:31 +00:00
neighbourhood Neighbourhood = { } ;
2018-09-17 18:06:31 +00:00
InitNeighbourhood ( & Neighbourhood ) ;
ResetAssetLandmarks ( ) ;
Inserted | = ( InsertEntry ( & Neighbourhood , CollationBuffers , PlayerTemplate , BespokeTemplate , ProjectFiles - > d_name , 0 ) = = RC_SUCCESS ) ;
2017-09-15 01:11:39 +00:00
}
}
2017-10-18 21:07:29 +00:00
closedir ( ProjectDirHandle ) ;
2017-09-15 01:11:39 +00:00
2018-09-17 18:06:31 +00:00
UpdateDeferredAssetChecksums ( ) ;
2017-11-11 00:34:47 +00:00
if ( Deleted | | Inserted )
2017-10-18 21:07:29 +00:00
{
2018-09-17 18:06:31 +00:00
GenerateSearchPage ( CollationBuffers , SearchTemplate ) ;
DeleteStaleAssets ( ) ;
# if DEBUG_LANDMARKS
VerifyLandmarks ( ) ;
# endif
2017-10-18 21:07:29 +00:00
}
2017-09-15 01:11:39 +00:00
return RC_SUCCESS ;
}
2017-11-18 00:27:33 +00:00
void
PrintVersions ( )
{
curl_version_info_data * CurlVersion = curl_version_info ( CURLVERSION_NOW ) ;
printf ( " Cinera: %d.%d.%d \n "
" Cinera DB: %d \n "
" hmmlib: %d.%d.%d \n "
" libcurl: %s \n " ,
CINERA_APP_VERSION . Major , CINERA_APP_VERSION . Minor , CINERA_APP_VERSION . Patch ,
CINERA_DB_VERSION ,
hmml_version . Major , hmml_version . Minor , hmml_version . Patch ,
CurlVersion - > version ) ;
}
2018-09-17 18:06:31 +00:00
int
InitDB ( void )
{
DB . Metadata . Buffer . ID = " DBMetadata " ;
CopyString ( DB . Metadata . Path , sizeof ( DB . Metadata . Path ) , " %s/%s.metadata " , Config . BaseDir , Config . ProjectID ) ;
ReadFileIntoBuffer ( & DB . Metadata , 0 ) ; // NOTE(matt): Could we actually catch errors (permissions?) here and bail?
DB . File . Buffer . ID = " DBFile " ;
CopyString ( DB . File . Path , sizeof ( DB . File . Path ) , " %s/%s.index " , Config . BaseDir , Config . ProjectID ) ;
ReadFileIntoBuffer ( & DB . File , 0 ) ; // NOTE(matt): Could we actually catch errors (permissions?) here and bail?
if ( DB . Metadata . Buffer . Location )
{
// TODO(matt): Handle this gracefully (it'll be an invalid file)
Assert ( DB . Metadata . FileSize > = sizeof ( DB . Header ) ) ;
uint32_t OriginalDBVersion = 0 ;
uint32_t FirstInt = * ( uint32_t * ) DB . Metadata . Buffer . Location ;
if ( FirstInt ! = FOURCC ( " CNRA " ) )
{
// TODO(matt): More checking, somehow? Ideally this should be able to report "Invalid .metadata file" rather than
// trying to upgrade a file that isn't even valid
// TODO(matt): We should try and deprecate < CINERA_DB_VERSION 4 and enforce a clean rebuild for invalid DB files
// Perhaps do this either the next time we have to bump the version, or when we scrap Single Edition
OriginalDBVersion = FirstInt ;
}
else
{
OriginalDBVersion = * ( uint32_t * ) ( DB . Metadata . Buffer . Location + sizeof ( DB . Header . HexSignature ) ) ;
}
if ( OriginalDBVersion < CINERA_DB_VERSION )
{
if ( CINERA_DB_VERSION = = 5 )
{
fprintf ( stderr , " \n %sHandle conversion from CINERA_DB_VERSION %d to %d!%s \n \n " , ColourStrings [ CS_ERROR ] , OriginalDBVersion , CINERA_DB_VERSION , ColourStrings [ CS_END ] ) ;
exit ( RC_ERROR_FATAL ) ;
}
if ( UpgradeDB ( OriginalDBVersion ) = = RC_ERROR_FILE ) { return RC_NOOP ; }
}
else if ( OriginalDBVersion > CINERA_DB_VERSION )
{
fprintf ( stderr , " %sUnsupported DB Version (%d). Please upgrade Cinera%s \n " , ColourStrings [ CS_ERROR ] , OriginalDBVersion , ColourStrings [ CS_END ] ) ;
exit ( RC_ERROR_FATAL ) ;
}
DB . Header = * ( db_header * ) DB . Metadata . Buffer . Location ;
DB . Header . CurrentAppVersion = CINERA_APP_VERSION ;
DB . Header . CurrentHMMLVersion . Major = hmml_version . Major ;
DB . Header . CurrentHMMLVersion . Minor = hmml_version . Minor ;
DB . Header . CurrentHMMLVersion . Patch = hmml_version . Patch ;
DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ;
fwrite ( & DB . Header , sizeof ( DB . Header ) , 1 , DB . Metadata . Handle ) ;
DB . Metadata . Buffer . Ptr = DB . Metadata . Buffer . Location + sizeof ( DB . Header ) ;
// NOTE(matt): In the future we may want to support multiple occurrences of a block type, at which point this code
// should continue to work
for ( int BlockIndex = 0 ; BlockIndex < DB . Header . BlockCount ; + + BlockIndex )
{
uint32_t BlockID = * ( uint32_t * ) DB . Metadata . Buffer . Ptr ;
if ( BlockID = = FOURCC ( " NTRY " ) )
{
DB . EntriesHeader = * ( db_header_entries * ) DB . Metadata . Buffer . Ptr ;
fwrite ( DB . Metadata . Buffer . Ptr ,
sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * DB . EntriesHeader . Count ,
1 ,
DB . Metadata . Handle ) ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . EntriesHeader ) + sizeof ( DB . Entry ) * DB . EntriesHeader . Count ;
}
else if ( BlockID = = FOURCC ( " ASET " ) )
{
DB . AssetsHeader = * ( db_header_assets * ) DB . Metadata . Buffer . Ptr ;
ClearCopyStringNoFormat ( DB . AssetsHeader . RootDir , sizeof ( DB . AssetsHeader . RootDir ) , Config . RootDir ) ;
ClearCopyStringNoFormat ( DB . AssetsHeader . RootURL , sizeof ( DB . AssetsHeader . RootURL ) , Config . RootURL ) ;
ClearCopyStringNoFormat ( DB . AssetsHeader . CSSDir , sizeof ( DB . AssetsHeader . CSSDir ) , Config . CSSDir ) ;
ClearCopyStringNoFormat ( DB . AssetsHeader . ImagesDir , sizeof ( DB . AssetsHeader . ImagesDir ) , Config . ImagesDir ) ;
ClearCopyStringNoFormat ( DB . AssetsHeader . JSDir , sizeof ( DB . AssetsHeader . JSDir ) , Config . JSDir ) ;
fwrite ( & DB . AssetsHeader , sizeof ( DB . AssetsHeader ) , 1 , DB . Metadata . Handle ) ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . AssetsHeader ) ;
for ( int AssetIndex = 0 ; AssetIndex < DB . AssetsHeader . Count ; + + AssetIndex )
{
DB . Asset = * ( db_asset * ) DB . Metadata . Buffer . Ptr ;
fwrite ( DB . Metadata . Buffer . Ptr ,
sizeof ( DB . Asset ) + sizeof ( DB . Landmark ) * DB . Asset . LandmarkCount ,
1 ,
DB . Metadata . Handle ) ;
DB . Metadata . Buffer . Ptr + = sizeof ( DB . Asset ) + sizeof ( DB . Landmark ) * DB . Asset . LandmarkCount ;
}
}
else
{
printf ( " %sMalformed metadata file%s: %s \n " , ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] , DB . Metadata . Path ) ;
}
}
CycleFile ( & DB . Metadata ) ;
}
else
{
// NOTE(matt): Initialising new db_header
DB . Header . HexSignature = FOURCC ( " CNRA " ) ;
DB . Header . InitialDBVersion = DB . Header . CurrentDBVersion = CINERA_DB_VERSION ;
DB . Header . InitialAppVersion = DB . Header . CurrentAppVersion = CINERA_APP_VERSION ;
DB . Header . InitialHMMLVersion . Major = DB . Header . CurrentHMMLVersion . Major = hmml_version . Major ;
DB . Header . InitialHMMLVersion . Minor = DB . Header . CurrentHMMLVersion . Minor = hmml_version . Minor ;
DB . Header . InitialHMMLVersion . Patch = DB . Header . CurrentHMMLVersion . Patch = hmml_version . Patch ;
DB . Header . BlockCount = 0 ;
CopyStringNoFormat ( DB . EntriesHeader . ProjectID , sizeof ( DB . EntriesHeader . ProjectID ) , Config . ProjectID ) ;
for ( int ProjectIndex = 0 ; ProjectIndex < ArrayCount ( ProjectInfo ) ; + + ProjectIndex )
{
if ( ! StringsDiffer ( ProjectInfo [ ProjectIndex ] . ProjectID , Config . ProjectID ) )
{
CopyStringNoFormat ( DB . EntriesHeader . ProjectName , sizeof ( DB . EntriesHeader . ProjectName ) , ProjectInfo [ ProjectIndex ] . FullName ) ;
break ;
}
}
// TODO(matt): Consider allowing zero NTRY or ASET blocks in a future version
DB . EntriesHeader . BlockID = FOURCC ( " NTRY " ) ;
DB . EntriesHeader . Count = 0 ;
CopyStringNoFormat ( DB . EntriesHeader . BaseURL , sizeof ( DB . EntriesHeader . BaseURL ) , Config . BaseURL ) ;
CopyStringNoFormat ( DB . EntriesHeader . SearchLocation , sizeof ( DB . EntriesHeader . SearchLocation ) , Config . SearchLocation ) ;
CopyStringNoFormat ( DB . EntriesHeader . PlayerLocation , sizeof ( DB . EntriesHeader . PlayerLocation ) , Config . PlayerLocation ) ;
CopyStringNoFormat ( DB . EntriesHeader . PlayerURLPrefix , sizeof ( DB . EntriesHeader . PlayerURLPrefix ) , Config . PlayerURLPrefix ) ;
+ + DB . Header . BlockCount ;
DB . AssetsHeader . BlockID = FOURCC ( " ASET " ) ;
DB . AssetsHeader . Count = 0 ;
CopyStringNoFormat ( DB . AssetsHeader . RootDir , sizeof ( DB . AssetsHeader . RootDir ) , Config . RootDir ) ;
CopyStringNoFormat ( DB . AssetsHeader . RootURL , sizeof ( DB . AssetsHeader . RootURL ) , Config . RootURL ) ;
CopyStringNoFormat ( DB . AssetsHeader . CSSDir , sizeof ( DB . AssetsHeader . CSSDir ) , Config . CSSDir ) ;
CopyStringNoFormat ( DB . AssetsHeader . ImagesDir , sizeof ( DB . AssetsHeader . ImagesDir ) , Config . ImagesDir ) ;
CopyStringNoFormat ( DB . AssetsHeader . JSDir , sizeof ( DB . AssetsHeader . JSDir ) , Config . JSDir ) ;
+ + DB . Header . BlockCount ;
DIR * OutputDirectoryHandle ;
if ( ! ( OutputDirectoryHandle = opendir ( Config . BaseDir ) ) )
{
if ( MakeDir ( Config . BaseDir ) = = RC_ERROR_DIRECTORY )
{
LogError ( LOG_ERROR , " Unable to create directory %s: %s " , Config . BaseDir , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to create directory %s: %s \n " , Config . BaseDir , strerror ( errno ) ) ;
return RC_ERROR_DIRECTORY ;
} ;
}
closedir ( OutputDirectoryHandle ) ;
DB . Metadata . Handle = fopen ( DB . Metadata . Path , " w " ) ;
fwrite ( & DB . Header , sizeof ( DB . Header ) , 1 , DB . Metadata . Handle ) ;
fwrite ( & DB . EntriesHeader , sizeof ( DB . EntriesHeader ) , 1 , DB . Metadata . Handle ) ;
fwrite ( & DB . AssetsHeader , sizeof ( DB . AssetsHeader ) , 1 , DB . Metadata . Handle ) ;
fclose ( DB . Metadata . Handle ) ;
ReadFileIntoBuffer ( & DB . Metadata , 0 ) ;
DB . File . Handle = fopen ( DB . File . Path , " w " ) ;
fprintf ( DB . File . Handle , " --- \n " ) ;
fclose ( DB . File . Handle ) ;
ReadFileIntoBuffer ( & DB . File , 0 ) ;
}
return RC_SUCCESS ;
}
void
SetCacheDirectory ( config * DefaultConfig )
{
int Flags = WRDE_NOCMD | WRDE_UNDEF | WRDE_APPEND ;
wordexp_t Expansions = { } ;
wordexp ( " $XDG_CACHE_HOME/cinera " , & Expansions , Flags ) ;
wordexp ( " $HOME/.cache/cinera " , & Expansions , Flags ) ;
if ( Expansions . we_wordc > 0 ) { CopyString ( DefaultConfig - > CacheDir , sizeof ( DefaultConfig - > CacheDir ) , Expansions . we_wordv [ 0 ] ) ; }
wordfree ( & Expansions ) ;
}
void
InitMemoryArena ( arena * Arena , int Size )
{
Arena - > Size = Size ;
if ( ! ( Arena - > Location = calloc ( 1 , Arena - > Size ) ) )
{
exit ( RC_ERROR_MEMORY ) ;
}
Arena - > Ptr = Arena - > Location ;
}
2017-08-29 20:35:28 +00:00
int
main ( int ArgC , char * * Args )
{
// TODO(matt): Read all defaults from the config
config DefaultConfig = {
2017-12-12 23:24:10 +00:00
. RootDir = " . " ,
. RootURL = " " ,
2017-11-11 00:34:47 +00:00
. CSSDir = " " ,
. ImagesDir = " " ,
. JSDir = " " ,
2018-09-17 18:06:31 +00:00
. QueryString = " r " ,
2018-01-03 22:16:20 +00:00
. TemplatesDir = " . " ,
2018-09-17 18:06:31 +00:00
. TemplateSearchLocation = " " ,
2018-01-03 22:16:20 +00:00
. TemplatePlayerLocation = " " ,
2017-12-12 23:24:10 +00:00
. BaseDir = " . " ,
. BaseURL = " " ,
2018-09-17 18:06:31 +00:00
. SearchLocation = " " ,
2017-12-12 23:24:10 +00:00
. PlayerLocation = " " , // Should default to the ProjectID
. Edition = EDITION_SINGLE ,
2017-10-18 21:07:29 +00:00
. LogLevel = LOG_EMERGENCY ,
2017-08-29 20:35:28 +00:00
. DefaultMedium = " programming " ,
2018-01-03 22:16:20 +00:00
. Mode = 0 ,
2017-08-29 20:35:28 +00:00
. OutLocation = " out.html " ,
. OutIntegratedLocation = " out_integrated.html " ,
. ProjectDir = " . " ,
2017-10-18 21:07:29 +00:00
. ProjectID = " " ,
2017-12-07 01:15:13 +00:00
. Theme = " " ,
2017-12-07 21:07:36 +00:00
. UpdateInterval = 4 ,
2017-12-12 23:24:10 +00:00
. PlayerURLPrefix = " "
2017-08-29 20:35:28 +00:00
} ;
2017-08-19 21:41:51 +00:00
2018-09-17 18:06:31 +00:00
SetCacheDirectory ( & DefaultConfig ) ;
2017-08-19 21:41:51 +00:00
2017-10-18 21:07:29 +00:00
Config = DefaultConfig ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( ArgC < 2 )
{
2017-10-18 21:07:29 +00:00
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 ;
2018-09-17 18:06:31 +00:00
while ( ( CommandLineArg = getopt ( ArgC , Args , " 1a:b:B:c:d:efghi:j:l:m:n:o:p:qQ:r:R:s:t:u:vwx:y: " ) ) ! = - 1 )
2017-08-29 20:35:28 +00:00
{
switch ( CommandLineArg )
{
2018-07-03 14:26:17 +00:00
case ' 1 ' :
Config . Mode | = MODE_SINGLETAB ;
break ;
2017-12-12 23:24:10 +00:00
case ' a ' :
Config . PlayerLocation = StripSurroundingSlashes ( optarg ) ;
break ;
2017-08-29 20:35:28 +00:00
case ' b ' :
2017-11-11 00:34:47 +00:00
Config . BaseDir = StripTrailingSlash ( optarg ) ;
2017-08-29 20:35:28 +00:00
break ;
2017-12-12 23:24:10 +00:00
case ' B ' :
Config . BaseURL = StripTrailingSlash ( optarg ) ;
break ;
2017-08-29 20:35:28 +00:00
case ' c ' :
2017-12-12 23:24:10 +00:00
Config . CSSDir = StripSurroundingSlashes ( optarg ) ;
2017-11-11 00:34:47 +00:00
break ;
case ' d ' :
Config . ProjectDir = StripTrailingSlash ( optarg ) ;
2017-08-29 20:35:28 +00:00
break ;
2018-02-21 21:50:23 +00:00
case ' e ' :
Config . Mode | = MODE_EXAMINE ;
break ;
2017-08-29 20:35:28 +00:00
case ' f ' :
2018-04-01 20:58:53 +00:00
Config . Mode | = MODE_FORCEINTEGRATION ;
break ;
case ' g ' :
Config . Mode | = MODE_NOPRIVACY ;
2017-08-29 20:35:28 +00:00
break ;
case ' i ' :
2017-12-12 23:24:10 +00:00
Config . ImagesDir = StripSurroundingSlashes ( optarg ) ;
2017-10-18 21:07:29 +00:00
break ;
2017-08-29 20:35:28 +00:00
case ' j ' :
2017-12-12 23:24:10 +00:00
Config . JSDir = StripSurroundingSlashes ( optarg ) ;
2017-08-29 20:35:28 +00:00
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 ;
2017-12-12 23:24:10 +00:00
case ' n ' :
2018-09-17 18:06:31 +00:00
Config . SearchLocation = StripSurroundingSlashes ( optarg ) ;
2017-12-12 23:24:10 +00:00
break ;
2017-08-29 20:35:28 +00:00
case ' o ' :
Config . OutLocation = optarg ;
Config . OutIntegratedLocation = optarg ;
break ;
case ' p ' :
2017-11-11 00:34:47 +00:00
Config . ProjectID = optarg ;
2017-08-29 20:35:28 +00:00
break ;
2017-12-18 14:31:05 +00:00
case ' q ' :
2017-12-18 15:10:02 +00:00
Config . Mode | = MODE_ONESHOT ;
2017-12-18 14:31:05 +00:00
break ;
2018-09-17 18:06:31 +00:00
case ' Q ' :
Config . QueryString = optarg ;
if ( ! StringsDiffer ( Config . QueryString , " " ) )
{
Config . Mode | = MODE_NOREVVEDRESOURCE ;
}
break ;
2017-10-18 21:07:29 +00:00
case ' r ' :
2017-11-11 00:34:47 +00:00
Config . RootDir = StripTrailingSlash ( optarg ) ;
2017-10-18 21:07:29 +00:00
break ;
2017-12-12 23:24:10 +00:00
case ' R ' :
Config . RootURL = StripTrailingSlash ( optarg ) ;
break ;
2017-12-07 01:15:13 +00:00
case ' s ' :
Config . Theme = optarg ;
break ;
2017-08-29 20:35:28 +00:00
case ' t ' :
2018-01-03 22:16:20 +00:00
Config . TemplatesDir = StripTrailingSlash ( optarg ) ;
2017-09-07 21:41:08 +00:00
break ;
2018-02-21 21:50:23 +00:00
case ' u ' :
2017-10-18 21:07:29 +00:00
Config . UpdateInterval = StringToInt ( optarg ) ;
break ;
2017-11-18 00:27:33 +00:00
case ' v ' :
PrintVersions ( ) ;
return RC_SUCCESS ;
2018-02-28 01:04:06 +00:00
case ' w ' :
Config . Mode | = MODE_NOCACHE ;
break ;
2017-09-07 21:41:08 +00:00
case ' x ' :
2018-09-17 18:06:31 +00:00
Config . TemplateSearchLocation = optarg ;
2018-01-03 22:16:20 +00:00
break ;
case ' y ' :
Config . TemplatePlayerLocation = optarg ;
2017-08-29 20:35:28 +00:00
break ;
case ' h ' :
default :
2017-10-18 21:07:29 +00:00
PrintUsage ( Args [ 0 ] , & DefaultConfig ) ;
2017-11-18 00:27:33 +00:00
return RC_SUCCESS ;
2017-08-29 20:35:28 +00:00
}
}
2018-02-21 21:50:23 +00:00
if ( Config . Mode & MODE_EXAMINE )
{
// TODO(matt): Allow optionally passing a .metadata file as an argument?
2018-09-17 18:06:31 +00:00
ExamineDB ( ) ;
2018-02-21 21:50:23 +00:00
exit ( RC_SUCCESS ) ;
}
2018-09-17 18:06:31 +00:00
// NOTE(matt): Init MemoryArenas (they are global)
InitMemoryArena ( & MemoryArena , Megabytes ( 4 ) ) ;
2018-06-12 19:03:12 +00:00
# if DEBUG_MEM
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Allocated MemoryArena (%d) \n " , MemoryArena . Size ) ;
fclose ( MemLog ) ;
printf ( " Allocated MemoryArena (%d) \n " , MemoryArena . Size ) ;
# endif
# if DEBUG
printf ( " Allocated MemoryArena: %d \n \n " , MemoryArena . Size ) ;
# endif
// NOTE(matt): Tree structure of buffer dependencies
// IncludesPlayer
// Menus
// Player
// ScriptPlayer
//
2018-09-17 18:06:31 +00:00
// IncludesSearch
// Search
2018-06-12 19:03:12 +00:00
buffers CollationBuffers = { } ;
if ( ClaimBuffer ( & CollationBuffers . IncludesPlayer , " IncludesPlayer " , Kilobytes ( 1 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
if ( ClaimBuffer ( & CollationBuffers . Menus , " Menus " , Kilobytes ( 32 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
if ( ClaimBuffer ( & CollationBuffers . Player , " Player " , Kilobytes ( 256 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
if ( ClaimBuffer ( & CollationBuffers . ScriptPlayer , " ScriptPlayer " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2018-09-17 18:06:31 +00:00
if ( ClaimBuffer ( & CollationBuffers . IncludesSearch , " IncludesSearch " , Kilobytes ( 1 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
if ( ClaimBuffer ( & CollationBuffers . SearchEntry , " Search " , Kilobytes ( 32 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2018-06-12 19:03:12 +00:00
2018-02-21 21:50:23 +00:00
bool HaveConfigErrors = FALSE ;
2017-11-11 00:34:47 +00:00
if ( StringsDiffer ( Config . ProjectID , " " ) )
2017-08-29 20:35:28 +00:00
{
2018-02-21 21:50:23 +00:00
if ( StringLength ( Config . ProjectID ) > MAX_PROJECT_ID_LENGTH )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sProjectID \" %s \" is too long (%d/%d characters)%s \n " , ColourStrings [ CS_ERROR ] , Config . ProjectID , StringLength ( Config . ProjectID ) , MAX_PROJECT_ID_LENGTH , ColourStrings [ CS_END ] ) ;
2018-02-21 21:50:23 +00:00
HaveConfigErrors = TRUE ;
}
2017-11-11 00:34:47 +00:00
Config . Edition = EDITION_PROJECT ;
2018-05-25 19:05:06 +00:00
bool KnownProject = FALSE ;
2017-12-07 21:07:36 +00:00
for ( int ProjectInfoIndex = 0 ; ProjectInfoIndex < ArrayCount ( ProjectInfo ) ; + + ProjectInfoIndex )
{
if ( ! StringsDiffer ( Config . ProjectID , ProjectInfo [ ProjectInfoIndex ] . ProjectID ) )
{
2018-05-25 19:05:06 +00:00
KnownProject = TRUE ;
2018-06-12 19:03:12 +00:00
CopyStringNoFormat ( CollationBuffers . ProjectID , sizeof ( CollationBuffers . ProjectID ) , Config . ProjectID ) ;
if ( ! StringsDiffer ( Config . Theme , " " ) )
{
2018-06-13 15:05:21 +00:00
Config . Theme = Config . ProjectID ;
2018-06-12 19:03:12 +00:00
}
2018-06-12 19:21:58 +00:00
CopyStringNoFormat ( CollationBuffers . Theme , sizeof ( CollationBuffers . Theme ) , Config . Theme ) ;
2018-06-12 19:03:12 +00:00
if ( StringsDiffer ( ProjectInfo [ ProjectInfoIndex ] . FullName , " " ) )
{
CopyStringNoFormat ( CollationBuffers . ProjectName , sizeof ( CollationBuffers . ProjectName ) , ProjectInfo [ ProjectInfoIndex ] . FullName ) ;
}
2017-12-07 21:07:36 +00:00
if ( StringsDiffer ( ProjectInfo [ ProjectInfoIndex ] . Medium , " " ) )
{
Config . DefaultMedium = ProjectInfo [ ProjectInfoIndex ] . Medium ;
}
2018-02-21 21:50:23 +00:00
2017-12-07 21:07:36 +00:00
if ( StringsDiffer ( ProjectInfo [ ProjectInfoIndex ] . AltURLPrefix , " " ) )
{
2018-02-21 21:50:23 +00:00
if ( StringLength ( ProjectInfo [ ProjectInfoIndex ] . AltURLPrefix ) > MAX_PLAYER_URL_PREFIX_LENGTH )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sPlayer URL Prefix \" %s \" is too long (%d/%d characters)%s \n " , ColourStrings [ CS_ERROR ] , ProjectInfo [ ProjectInfoIndex ] . AltURLPrefix , StringLength ( ProjectInfo [ ProjectInfoIndex ] . AltURLPrefix ) , MAX_PLAYER_URL_PREFIX_LENGTH , ColourStrings [ CS_END ] ) ;
2018-02-21 21:50:23 +00:00
HaveConfigErrors = TRUE ;
}
else
{
Config . PlayerURLPrefix = ProjectInfo [ ProjectInfoIndex ] . AltURLPrefix ;
}
}
if ( StringLength ( ProjectInfo [ ProjectInfoIndex ] . FullName ) > MAX_PROJECT_NAME_LENGTH )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sProject Name \" %s \" is too long (%d/%d characters)%s \n " , ColourStrings [ CS_ERROR ] , ProjectInfo [ ProjectInfoIndex ] . FullName , StringLength ( ProjectInfo [ ProjectInfoIndex ] . FullName ) , MAX_PROJECT_NAME_LENGTH , ColourStrings [ CS_END ] ) ;
2018-02-21 21:50:23 +00:00
HaveConfigErrors = TRUE ;
2017-12-07 21:07:36 +00:00
}
2018-02-21 21:50:23 +00:00
2017-12-07 21:07:36 +00:00
break ;
}
}
2018-05-25 19:05:06 +00:00
if ( ! KnownProject )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sMissing Project Info for %s%s %s(-p)%s \n " , ColourStrings [ CS_ERROR ] , Config . ProjectID , ColourStrings [ CS_END ] , ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] ) ;
2018-05-25 19:05:06 +00:00
HaveConfigErrors = TRUE ;
}
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2018-02-21 21:50:23 +00:00
if ( StringsDiffer ( Config . BaseURL , " " ) & & StringLength ( Config . BaseURL ) > MAX_BASE_URL_LENGTH )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sBase URL \" %s \" is too long (%d/%d characters)%s \n " , ColourStrings [ CS_ERROR ] , Config . BaseURL , StringLength ( Config . BaseURL ) , MAX_BASE_URL_LENGTH , ColourStrings [ CS_END ] ) ;
2018-02-21 21:50:23 +00:00
HaveConfigErrors = TRUE ;
}
2018-09-17 18:06:31 +00:00
if ( StringsDiffer ( Config . SearchLocation , " " ) & & StringLength ( Config . SearchLocation ) > MAX_RELATIVE_PAGE_LOCATION_LENGTH )
2018-02-21 21:50:23 +00:00
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sRelative Search Page Location \" %s \" is too long (%d/%d characters)%s \n " , ColourStrings [ CS_ERROR ] , Config . SearchLocation , StringLength ( Config . SearchLocation ) , MAX_RELATIVE_PAGE_LOCATION_LENGTH , ColourStrings [ CS_END ] ) ;
2018-02-21 21:50:23 +00:00
HaveConfigErrors = TRUE ;
}
if ( StringsDiffer ( Config . PlayerLocation , " " ) & & StringLength ( Config . PlayerLocation ) > MAX_RELATIVE_PAGE_LOCATION_LENGTH )
{
2018-09-17 18:06:31 +00:00
fprintf ( stderr , " %sRelative Player Page Location \" %s \" is too long (%d/%d characters)%s \n " , ColourStrings [ CS_ERROR ] , Config . PlayerLocation , StringLength ( Config . PlayerLocation ) , MAX_RELATIVE_PAGE_LOCATION_LENGTH , ColourStrings [ CS_END ] ) ;
2018-02-21 21:50:23 +00:00
HaveConfigErrors = TRUE ;
}
2018-01-03 22:16:20 +00:00
if ( ! MediumExists ( Config . DefaultMedium ) )
2017-08-29 20:35:28 +00:00
{
2018-01-03 22:16:20 +00:00
// TODO(matt): We'll want to stick around when we have multiple projects configured
2018-02-21 21:50:23 +00:00
HaveConfigErrors = TRUE ;
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2018-02-21 21:50:23 +00:00
if ( HaveConfigErrors ) { exit ( RC_RIP ) ; }
2017-11-18 00:27:33 +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
2018-01-03 22:16:20 +00:00
// If we find a new template, we first of all Init and Validate it
// In our case here, we just want to straight up Init our three possible templates
2018-09-17 18:06:31 +00:00
// and Validate the Search and Player templates if their locations are set
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Config . Edition = = EDITION_PROJECT )
{
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " w+ " ) ;
fprintf ( MemLog , " Entered Project Edition \n " ) ;
fclose ( MemLog ) ;
# endif
2017-11-11 00:34:47 +00:00
// TODO(matt): Also log these startup messages?
2017-11-18 00:27:33 +00:00
PrintVersions ( ) ;
printf ( " \n "
2018-01-03 22:16:20 +00:00
" Universal \n "
2018-09-17 18:06:31 +00:00
" Cache Directory: %s(XDG_CACHE_HOME)%s \t %s \n "
" Update Interval: %s(-u)%s \t \t %d second%s \n "
2017-10-18 21:07:29 +00:00
" \n "
2018-09-17 18:06:31 +00:00
" Assets Root \n "
" Directory: %s(-r)%s \t \t \t %s \n "
" URL: %s(-R)%s \t \t \t %s \n "
2017-11-11 00:34:47 +00:00
" Paths relative to root \n "
2018-09-17 18:06:31 +00:00
" CSS: %s(-c)%s \t \t \t %s \n "
" Images: %s(-i)%s \t \t \t %s \n "
" JS: %s(-j)%s \t \t \t %s \n "
" Revved resources query string: %s(-Q)%s \t %s \n "
2018-01-03 22:16:20 +00:00
" \n "
2017-11-11 00:34:47 +00:00
" Project \n "
2018-09-17 18:06:31 +00:00
" ID: %s(-p)%s \t \t \t \t %s \n "
" Default Medium: %s(-m)%s \t \t %s \n "
" Style / Theme: %s(-s)%s \t \t \t %s \n "
2018-01-03 22:16:20 +00:00
" \n "
" Input Paths \n "
2018-09-17 18:06:31 +00:00
" Annotations Directory: %s(-d)%s \t \t %s \n "
" Templates Directory: %s(-t)%s \t \t %s \n "
" Search Template: %s(-x)%s \t \t %s \n "
" Player Template: %s(-y)%s \t \t %s \n "
2018-01-03 22:16:20 +00:00
" \n "
" Output Paths \n "
2017-12-12 23:24:10 +00:00
" Base \n "
2018-09-17 18:06:31 +00:00
" Directory: %s(-b)%s \t \t \t %s \n "
" URL: %s(-B)%s \t \t \t %s \n "
2017-12-12 23:24:10 +00:00
" Paths relative to base \n "
2018-09-17 18:06:31 +00:00
" Search Page: %s(-n)%s \t \t %s \n "
2018-01-03 22:16:20 +00:00
/* NOTE(matt): Here, I think, is where we'll split into sub-projects (...really?...) */
2018-09-17 18:06:31 +00:00
" Player Page(s): %s(-a)%s \t \t %s \n "
" Player Page Prefix: %s(hardcoded)%s \t %s \n "
2018-04-01 20:58:53 +00:00
" \n "
" Modes \n "
2018-09-17 18:06:31 +00:00
" Single browser tab: %s(-1)%s \t \t %s \n "
" Force template integration: %s(-f)%s \t %s \n "
" Ignore video privacy status: %s(-g)%s \t %s \n "
" Quit after sync: %s(-q)%s \t \t %s \n "
" Force quote cache rebuild: %s(-w)%s \t %s \n "
2017-12-12 23:24:10 +00:00
" \n " ,
2017-11-11 00:34:47 +00:00
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . CacheDir ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . UpdateInterval ,
Config . UpdateInterval = = 1 ? " " : " s " ,
2018-01-03 22:16:20 +00:00
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . RootDir ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , StringsDiffer ( Config . RootURL , " " ) ? Config . RootURL : " [empty] " ,
2018-01-03 22:16:20 +00:00
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , StringsDiffer ( Config . CSSDir , " " ) ? Config . CSSDir : " (same as root) " ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , StringsDiffer ( Config . ImagesDir , " " ) ? Config . ImagesDir : " (same as root) " ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , StringsDiffer ( Config . JSDir , " " ) ? Config . JSDir : " (same as root) " ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . Mode & MODE_NOREVVEDRESOURCE ? " [disabled] " : Config . QueryString ,
2018-01-03 22:16:20 +00:00
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . ProjectID ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . DefaultMedium ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . Theme ,
2018-01-03 22:16:20 +00:00
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . ProjectDir ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . TemplatesDir ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , StringsDiffer ( Config . TemplateSearchLocation , " " ) ? Config . TemplateSearchLocation : " [none set] " ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , StringsDiffer ( Config . TemplatePlayerLocation , " " ) ? Config . TemplatePlayerLocation : " [none set] " ,
2018-01-03 22:16:20 +00:00
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . BaseDir ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , StringsDiffer ( Config . BaseURL , " " ) ? Config . BaseURL : " [empty] " ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , StringsDiffer ( Config . SearchLocation , " " ) ? Config . SearchLocation : " (same as base) " ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , StringsDiffer ( Config . PlayerLocation , " " ) ? Config . PlayerLocation : " (directly descended from base) " ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , StringsDiffer ( Config . PlayerURLPrefix , " " ) ? Config . PlayerURLPrefix : Config . ProjectID ,
2018-04-01 20:58:53 +00:00
2018-09-17 18:06:31 +00:00
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . Mode & MODE_SINGLETAB ? " on " : " off " ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . Mode & MODE_FORCEINTEGRATION ? " on " : " off " ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . Mode & MODE_NOPRIVACY ? " on " : " off " ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . Mode & MODE_ONESHOT ? " on " : " off " ,
ColourStrings [ CS_COMMENT ] , ColourStrings [ CS_END ] , Config . Mode & MODE_NOCACHE ? " on " : " off " ) ;
2017-10-18 21:07:29 +00:00
2018-09-17 18:06:31 +00:00
if ( ( StringsDiffer ( Config . SearchLocation , " " ) | | StringsDiffer ( Config . PlayerLocation , " " ) )
2017-12-12 23:24:10 +00:00
& & StringLength ( Config . BaseURL ) = = 0 )
{
2018-09-17 18:06:31 +00:00
printf ( " %sPlease set a Project Base URL (-B) so we can output the Search / Player pages to \n "
" locations other than the defaults%s \n " ,
ColourStrings [ CS_ERROR ] , ColourStrings [ CS_END ] ) ;
2017-12-12 23:24:10 +00:00
return ( RC_SUCCESS ) ;
}
2018-09-17 18:06:31 +00:00
#if 0
for ( int i = 0 ; i < Assets . Count ; + + i )
{
printf ( " %08x - %s \n " , Assets . Asset [ i ] . Hash , Assets . Asset [ i ] . Filename ) ;
}
# endif
InitDB ( ) ;
inotifyInstance = inotify_init1 ( IN_NONBLOCK ) ;
printf ( " ┌╼ Hashing assets ╾┐ \n " ) ;
2018-09-19 19:50:21 +00:00
// NOTE(matt): This had to happen before PackTemplate() because those guys may need to do PushAsset() and we must
2018-09-17 18:06:31 +00:00
// ensure that the builtin assets get placed correctly
InitAssets ( ) ;
}
2017-12-12 23:24:10 +00:00
2018-09-17 18:06:31 +00:00
printf ( " ┌╼ Packing templates ╾┐ \n " ) ;
2018-09-19 19:50:21 +00:00
template SearchTemplate ;
template PlayerTemplate ;
template BespokeTemplate ;
2018-05-01 21:05:44 +00:00
2018-09-17 18:06:31 +00:00
if ( StringsDiffer ( Config . TemplatePlayerLocation , " " ) )
{
2018-09-19 19:50:21 +00:00
switch ( PackTemplate ( & PlayerTemplate , Config . TemplatePlayerLocation , TEMPLATE_PLAYER ) )
2018-09-17 18:06:31 +00:00
{
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 ;
}
}
2018-05-01 21:05:44 +00:00
2018-09-17 18:06:31 +00:00
if ( Config . Edition = = EDITION_PROJECT & & StringsDiffer ( Config . TemplateSearchLocation , " " ) )
{
2018-09-19 19:50:21 +00:00
switch ( PackTemplate ( & SearchTemplate , Config . TemplateSearchLocation , TEMPLATE_SEARCH ) )
2018-09-17 18:06:31 +00:00
{
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 ;
}
}
2018-02-21 21:50:23 +00:00
2018-09-17 18:06:31 +00:00
if ( Config . Edition = = EDITION_PROJECT )
{
printf ( " \n ┌╼ Synchronising with Annotations Directory ╾┐ \n " ) ;
2018-09-19 19:50:21 +00:00
SyncDBWithInput ( & CollationBuffers , & SearchTemplate , & PlayerTemplate , & BespokeTemplate ) ;
2017-12-18 15:10:02 +00:00
if ( Config . Mode & MODE_ONESHOT )
2017-12-18 14:31:05 +00:00
{
goto RIP ;
}
2017-10-18 21:07:29 +00:00
2018-10-05 19:57:09 +00:00
printf ( " \n ┌╼ Monitoring file system for %snew%s, %sedited%s and %sdeleted%s .hmml and asset files ╾┐ \n " ,
2018-09-17 18:06:31 +00:00
ColourStrings [ EditTypes [ EDIT_ADDITION ] . Colour ] , ColourStrings [ CS_END ] ,
ColourStrings [ EditTypes [ EDIT_REINSERTION ] . Colour ] , ColourStrings [ CS_END ] ,
ColourStrings [ EditTypes [ EDIT_DELETION ] . Colour ] , ColourStrings [ CS_END ] ) ;
2017-10-18 21:07:29 +00:00
// NOTE(matt): Do we want to also watch IN_DELETE_SELF events?
2018-09-17 18:06:31 +00:00
PushHMMLWatchHandle ( ) ;
2017-09-15 01:11:39 +00:00
2018-09-19 19:50:21 +00:00
while ( MonitorFilesystem ( & CollationBuffers , & SearchTemplate , & PlayerTemplate , & BespokeTemplate ) ! = RC_ARENA_FULL )
2017-09-15 01:11:39 +00:00
{
2018-02-28 01:04:06 +00:00
// TODO(matt): Refetch the quotes and rebuild player pages if needed
//
2018-09-17 18:06:31 +00:00
// Every sixty mins, redownload the quotes and, I suppose, SyncDBWithInput(). But here we still don't even know
2018-02-28 01:04:06 +00:00
// who the speaker is. To know, we'll probably have to store all quoted speakers in the project's .metadata. Maybe
// postpone this for now, but we will certainly need this to happen
//
// The most ideal solution is possibly that we store quote numbers in the Metadata->Entry, listen for and handle a
// REST PUT request from insobot when a quote changes (unless we're supposed to poll insobot for them?), and rebuild
// the player page(s) accordingly.
2018-04-01 20:58:53 +00:00
//
if ( ! ( Config . Mode & MODE_NOPRIVACY ) & & time ( 0 ) - LastPrivacyCheck > 60 * 60 * 4 )
{
2018-09-19 19:50:21 +00:00
RecheckPrivacy ( & CollationBuffers , & SearchTemplate , & PlayerTemplate , & BespokeTemplate ) ;
2018-04-01 20:58:53 +00:00
}
2017-10-18 21:07:29 +00:00
sleep ( Config . UpdateInterval ) ;
2017-09-15 01:11:39 +00:00
}
2017-09-07 21:41:08 +00:00
}
else
{
2018-09-17 18:06:31 +00:00
// TODO(matt): Get rid of Single Edition once and for all, probably for v1.0.0
2017-09-07 21:41:08 +00:00
if ( optind = = ArgC )
2017-08-29 20:35:28 +00:00
{
fprintf ( stderr , " %s: requires at least one input .hmml file \n " , Args [ 0 ] ) ;
2017-10-18 21:07:29 +00:00
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
2018-09-17 18:06:31 +00:00
inotifyInstance = inotify_init1 ( IN_NONBLOCK ) ;
printf ( " ┌╼ Hashing assets ╾┐ \n " ) ;
InitBuiltinAssets ( ) ;
2017-09-07 21:41:08 +00:00
NextFile :
2017-12-24 01:57:11 +00:00
// TODO(matt): Just change the default output location so all these guys won't overwrite each other
2017-08-29 20:35:28 +00:00
for ( int FileIndex = optind ; FileIndex < ArgC ; + + FileIndex )
2017-03-22 02:18:31 +00:00
{
2018-01-04 20:55:51 +00:00
bool HasBespokeTemplate = FALSE ;
char * Ptr = Args [ FileIndex ] ;
Ptr + = ( StringLength ( Args [ FileIndex ] ) - StringLength ( " .hmml " ) ) ;
if ( ! ( StringsDiffer ( Ptr , " .hmml " ) ) )
2017-09-07 21:41:08 +00:00
{
2018-04-22 20:57:31 +00:00
CopyString ( Config . SingleHMMLFilePath , sizeof ( Config . SingleHMMLFilePath ) , " %s " , Args [ FileIndex ] ) ;
2018-04-01 20:58:53 +00:00
switch ( HMMLToBuffers ( & CollationBuffers , & BespokeTemplate , Args [ FileIndex ] , 0 ) )
2018-01-04 20:55:51 +00:00
{
2018-05-01 21:05:44 +00:00
// TODO(matt): Actually sort out the fatality of these cases
2018-01-04 20:55:51 +00:00
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 :
if ( FileIndex < ( ArgC - 1 ) ) { goto NextFile ; }
else { goto RIP ; }
case RC_SUCCESS :
break ;
} ;
2018-09-19 19:50:21 +00:00
HasBespokeTemplate = BespokeTemplate . File . Buffer . Location ! = NULL ;
2018-01-04 20:55:51 +00:00
switch ( BuffersToHTML ( & CollationBuffers ,
2018-09-19 19:50:21 +00:00
HasBespokeTemplate ? & BespokeTemplate : & PlayerTemplate ,
2018-01-04 20:55:51 +00:00
0 ,
2018-02-21 21:50:23 +00:00
PAGE_PLAYER , 0 ) )
2018-01-04 20:55:51 +00:00
{
2018-05-01 21:05:44 +00:00
// TODO(matt): Actually sort out the fatality of these cases
2018-01-04 20:55:51 +00:00
case RC_INVALID_TEMPLATE :
2018-09-19 19:50:21 +00:00
if ( HasBespokeTemplate ) { FreeTemplate ( & BespokeTemplate ) ; }
2018-01-04 20:55:51 +00:00
if ( FileIndex < ( ArgC - 1 ) ) { goto NextFile ; }
case RC_ERROR_MEMORY :
case RC_ERROR_FILE :
case RC_ARENA_FULL :
goto RIP ;
case RC_SUCCESS :
#if 0
2018-09-17 18:06:31 +00:00
fprintf ( stdout , " %sWritten%s %s \n " , HasBespokeTemplate ? Config . OutIntegratedLocation : Config . OutLocation ) ;
2018-01-04 20:55:51 +00:00
# endif
2018-09-19 19:50:21 +00:00
if ( HasBespokeTemplate ) { FreeTemplate ( & BespokeTemplate ) ; }
2018-01-04 20:55:51 +00:00
break ;
} ;
}
2017-03-22 02:18:31 +00:00
}
}
2017-08-29 20:35:28 +00:00
2018-09-19 19:50:21 +00:00
if ( PlayerTemplate . File . Buffer . Location )
2017-08-29 20:35:28 +00:00
{
2018-09-19 19:50:21 +00:00
FreeTemplate ( & PlayerTemplate ) ;
2018-01-03 22:16:20 +00:00
}
2018-09-19 19:50:21 +00:00
if ( Config . Edition = = EDITION_PROJECT & & SearchTemplate . File . Buffer . Location )
2018-01-03 22:16:20 +00:00
{
2018-09-19 19:50:21 +00:00
FreeTemplate ( & SearchTemplate ) ;
2017-08-29 20:35:28 +00:00
}
2018-09-17 18:06:31 +00:00
DeclaimBuffer ( & CollationBuffers . SearchEntry ) ;
DeclaimBuffer ( & CollationBuffers . IncludesSearch ) ;
2017-11-11 00:34:47 +00:00
DeclaimBuffer ( & CollationBuffers . ScriptPlayer ) ;
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & CollationBuffers . Player ) ;
DeclaimBuffer ( & CollationBuffers . Menus ) ;
DeclaimBuffer ( & CollationBuffers . IncludesPlayer ) ;
2017-09-07 21:41:08 +00:00
RIP :
2017-07-29 00:36:14 +00:00
free ( MemoryArena . Location ) ;
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Freed MemoryArena \n " ) ;
fclose ( MemLog ) ;
printf ( " Freed MemoryArena \n " ) ;
# endif
2017-03-10 14:19:25 +00:00
}