Annotation-System/hmml_to_youtube/hmml_to_youtube.c

305 lines
9.5 KiB
C

#if 0
ctime -begin ${0%.*}.ctm
gcc -g -fsanitize=address $0 -o ${0%.*} hmml.a
ctime -end ${0%.*}.ctm
exit
#endif
#include "hmmlib.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#ifndef bool
typedef unsigned int bool;
#endif
#define TRUE 1
#define FALSE 0
typedef struct
{
char *Location;
char *Ptr;
int Size;
} buffer;
int
StringLength(char *String)
{
int i = 0;
while(String[i])
{
++i;
}
return i;
}
char
ToLower(char Char)
{
if(Char >= 'A' && Char <= 'Z')
{
Char += 'a' - 'A';
}
return Char;
}
int
StringsDiffer(char *A, char *B)
{
while(*A && *B && ToLower(*A) == ToLower(*B))
{
++A, ++B;
}
return *A - *B;
}
__attribute__ ((format (printf, 2, 3)))
void
CopyStringToBuffer(buffer *Dest, char *Format, ...)
{
va_list Args;
va_start(Args, Format);
int Length = vsprintf(Dest->Ptr, Format, Args);
va_end(Args);
Dest->Ptr += Length;
}
int
main(int ArgC, char **Args)
{
if(ArgC < 2)
{
fprintf(stderr, "Usage: %s filename(s)\n", Args[0]);
return 1;
}
FILE *InFile;
for(int FileIndex = 1; FileIndex < ArgC; ++FileIndex)
{
if(!(InFile = fopen(Args[FileIndex], "r")))
{
perror(Args[0]);
return 1;
}
// Init MemoryArena
int ArenaSize = 1024 * 32;
char *MemoryArena;
if(!(MemoryArena = calloc(ArenaSize, 1)))
{
perror(Args[0]);
return 1;
}
int ClaimedMemory = 0;
// Buffers and Pointers
char *InPtr; // Associated buffer is allocated by hmml_parse_file()
buffer Out;
//printf("Reading %s\n", Args[FileIndex]);
HMML_Output HMML = hmml_parse_file(InFile);
fclose(InFile);
if(HMML.well_formed)
{
Out.Location = MemoryArena + ClaimedMemory;
Out.Size = 1024*16;
ClaimedMemory += Out.Size;
char *Member = HMML.metadata.twitch ?
HMML.metadata.twitch :
HMML.metadata.member;
bool WritingChat = TRUE;
for(int BuildPass = 0; BuildPass < 2; ++BuildPass)
{
if(WritingChat == FALSE)
{
printf("%s: %ld characters over budget. Shrinking...\n", Args[FileIndex], (Out.Ptr - Out.Location) - 5000);
}
Out.Ptr = Out.Location;
HMML_Annotation *Anno;
for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex)
{
Anno = HMML.annotations + AnnotationIndex;
if(Anno->author && !WritingChat)
{
goto skip;
}
CopyStringToBuffer(&Out, "%s ", Anno->time);
InPtr = Anno->text;
if(Anno->author)
{
CopyStringToBuffer(&Out, "Chat comment: \"");
}
int MarkerIndex = 0, RefIndex = 0, InOffset = 0;
while(*InPtr || RefIndex < Anno->reference_count)
{
if(MarkerIndex < Anno->marker_count &&
InPtr - Anno->text == Anno->markers[MarkerIndex].offset)
{
char *Readable = Anno->markers[MarkerIndex].parameter
? Anno->markers[MarkerIndex].parameter
: Anno->markers[MarkerIndex].marker;
if(Anno->markers[MarkerIndex].type == HMML_MEMBER && (!StringsDiffer(Anno->markers[MarkerIndex].marker, Member)))
{
InPtr += StringLength(Readable);
while(*InPtr &&
( *InPtr == ','
|| *InPtr == ':'
|| *InPtr == ' '
)
)
{
++InPtr;
}
if(InPtr - Anno->text > 0 && !InOffset)
{
InOffset = InPtr - Anno->text;
}
}
++MarkerIndex;
}
if(RefIndex < Anno->reference_count &&
InPtr - Anno->text == Anno->references[RefIndex].offset)
{
if(Anno->references[RefIndex].offset == InOffset)
{
if(Out.Ptr[-1] != '"' && Out.Ptr[-1] != ' ')
{
*Out.Ptr++ = ' ';
}
if(Anno->references[RefIndex].page)
{
CopyStringToBuffer(&Out, "%s", Anno->references[RefIndex].page);
}
else if(Anno->references[RefIndex].site)
{
CopyStringToBuffer(&Out, "%s", Anno->references[RefIndex].site);
}
else if(Anno->references[RefIndex].title)
{
CopyStringToBuffer(&Out, "%s", Anno->references[RefIndex].title);
}
}
if(Anno->references[RefIndex].url)
{
if(Anno->references[RefIndex].offset < StringLength(Anno->text) ||
RefIndex < Anno->reference_count-1)
{
if(Out.Ptr[-1] != ' ')
{
*Out.Ptr++ = ' ';
}
CopyStringToBuffer(&Out, "- %s -", Anno->references[RefIndex].url);
}
else
{
if(Out.Ptr[-1] == ' ')
{
--Out.Ptr;
}
if(InPtr[-3] != ':')
{
CopyStringToBuffer(&Out, ": ");
}
CopyStringToBuffer(&Out, "%s", Anno->references[RefIndex].url);
}
}
++RefIndex;
}
if(*InPtr)
{
switch(*InPtr)
{
case '<':
CopyStringToBuffer(&Out, "&lt;");
break;
case '>':
CopyStringToBuffer(&Out, "&gt;");
break;
case '#':
break;
default:
*Out.Ptr++ = *InPtr;
break;
}
++InPtr;
}
}
if(Anno->author &&
WritingChat == TRUE)
{
*Out.Ptr++ = '\"';
}
*Out.Ptr++ = '\n';
skip: {};
}
CopyStringToBuffer(&Out, "\nAnnotated by %s - https://handmade.network/m/%s\n",
HMML.metadata.annotator, HMML.metadata.annotator);
if(Out.Ptr - Out.Location > 5000)
{
WritingChat = FALSE;
}
else
{
break;
}
}
if(Out.Ptr - Out.Location > 5000)
{
fprintf(stderr, "%s: %ld characters over budget. Requires manual shrinking!\n", Args[FileIndex], (Out.Ptr - Out.Location) - 5000);
}
// NOTE(matt): At this point we should have filled a buffer with the stuff
hmml_free(&HMML);
// NOTE(matt): Open a file for writing out to
char Filename[StringLength(Args[FileIndex]) + 5];
sprintf(Filename, "%s.txt", Args[FileIndex]);
//printf("Writing to %s\n", Filename);
FILE *OutFile;
if(!(OutFile = fopen(Filename, "w")))
{
perror(Args[0]);
free(MemoryArena);
return 1;
}
fwrite(Out.Location, Out.Ptr - Out.Location, 1, OutFile);
fclose(OutFile);
}
else
{
fprintf(stderr, "%s:%d: %s\n", Args[FileIndex], HMML.error.line, HMML.error.message);
hmml_free(&HMML);
}
free(MemoryArena);
}
return 0;
}