cinera.c: Binary search the .metadata
Also optimise out superfluous searches, and relieve IndexToBuffer() of the need to string-search the .index Rewrite the table of contents page after deleting an entry This commit also retains profiling, as a reminder to me how I used it, and the old linear search code. The timing blocks and the old code may be deleted in a future commit
This commit is contained in:
parent
ab598e37e6
commit
de2c632806
314
cinera/cinera.c
314
cinera/cinera.c
|
@ -14,20 +14,21 @@ typedef struct
|
||||||
version CINERA_APP_VERSION = {
|
version CINERA_APP_VERSION = {
|
||||||
.Major = 0,
|
.Major = 0,
|
||||||
.Minor = 5,
|
.Minor = 5,
|
||||||
.Patch = 37
|
.Patch = 38
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(matt): Copy in the DB 3 stuff from cinera_working.c
|
// TODO(matt): Copy in the DB 3 stuff from cinera_working.c
|
||||||
#define CINERA_DB_VERSION 3
|
#define CINERA_DB_VERSION 3
|
||||||
|
|
||||||
#define DEBUG 0
|
|
||||||
#define DEBUG_MEM 0
|
|
||||||
|
|
||||||
typedef unsigned int bool;
|
typedef unsigned int bool;
|
||||||
|
|
||||||
#define TRUE 1
|
#define TRUE 1
|
||||||
#define FALSE 0
|
#define FALSE 0
|
||||||
|
|
||||||
|
#define DEBUG 0
|
||||||
|
#define DEBUG_MEM 0
|
||||||
|
#define BINARY_SEARCH 1
|
||||||
|
bool PROFILING = 0;
|
||||||
|
|
||||||
#include <stdarg.h> // NOTE(matt): varargs
|
#include <stdarg.h> // NOTE(matt): varargs
|
||||||
#include <stdio.h> // NOTE(matt): printf, sprintf, vsprintf, fprintf, perror
|
#include <stdio.h> // NOTE(matt): printf, sprintf, vsprintf, fprintf, perror
|
||||||
#include <stdlib.h> // NOTE(matt): calloc, malloc, free
|
#include <stdlib.h> // NOTE(matt): calloc, malloc, free
|
||||||
|
@ -44,6 +45,10 @@ typedef unsigned int bool;
|
||||||
#include <sys/inotify.h> // NOTE(matt): inotify
|
#include <sys/inotify.h> // NOTE(matt): inotify
|
||||||
#include <unistd.h> // NOTE(matt): sleep()
|
#include <unistd.h> // NOTE(matt): sleep()
|
||||||
|
|
||||||
|
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);}
|
||||||
|
|
||||||
#define Kilobytes(Bytes) Bytes << 10
|
#define Kilobytes(Bytes) Bytes << 10
|
||||||
#define Megabytes(Bytes) Bytes << 20
|
#define Megabytes(Bytes) Bytes << 20
|
||||||
|
|
||||||
|
@ -880,11 +885,17 @@ DeclaimBuffer(buffer *Buffer)
|
||||||
MemoryArena.Ptr - MemoryArena.Location);
|
MemoryArena.Ptr - MemoryArena.Location);
|
||||||
#endif
|
#endif
|
||||||
LogUsage(Buffer);
|
LogUsage(Buffer);
|
||||||
if(PercentageUsed >= 80.0f)
|
if(PercentageUsed >= 95.0f)
|
||||||
{
|
{
|
||||||
// TODO(matt): Implement either dynamically growing buffers, or phoning home to matt@handmadedev.org
|
// TODO(matt): Implement either dynamically growing buffers, or phoning home to matt@handmadedev.org
|
||||||
LogError(LOG_ERROR, "%s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed);
|
LogError(LOG_ERROR, "%s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed);
|
||||||
fprintf(stderr, "Warning: %s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed);
|
fprintf(stderr, "\e[1;31mWarning\e[0m: %s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed);
|
||||||
|
}
|
||||||
|
else if(PercentageUsed >= 80.0f)
|
||||||
|
{
|
||||||
|
// TODO(matt): Implement either dynamically growing buffers, or phoning home to matt@handmadedev.org
|
||||||
|
LogError(LOG_ERROR, "%s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed);
|
||||||
|
fprintf(stderr, "\e[0;33mWarning\e[0m: %s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed);
|
||||||
}
|
}
|
||||||
Buffer->Size = 0;
|
Buffer->Size = 0;
|
||||||
}
|
}
|
||||||
|
@ -2916,7 +2927,7 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen
|
||||||
if(ClaimBuffer(&AnnotationClass, "AnnotationClass", 256) == 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(&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; };
|
if(ClaimBuffer(&Text, "Text", Kilobytes(4)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; };
|
||||||
if(ClaimBuffer(&CategoryIcons, "CategoryIcons", 512) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; };
|
if(ClaimBuffer(&CategoryIcons, "CategoryIcons", Kilobytes(1)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; };
|
||||||
|
|
||||||
CopyStringToBuffer(&AnnotationHeader,
|
CopyStringToBuffer(&AnnotationHeader,
|
||||||
" <div data-timestamp=\"%d\"",
|
" <div data-timestamp=\"%d\"",
|
||||||
|
@ -4373,7 +4384,61 @@ SeekBufferForString(buffer *Buffer, char *String,
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTemplate, char *BaseFilename)
|
BinarySearchForMetadataEntry(index *Index, index_metadata **Test, char *SearchTerm)
|
||||||
|
{
|
||||||
|
int Lower = 0;
|
||||||
|
index_metadata *LowerEntry = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * Lower);
|
||||||
|
if(StringsDiffer(SearchTerm, LowerEntry->BaseFilename) < 0 ) { return Lower; }
|
||||||
|
|
||||||
|
int Upper = Index->Header.EntryCount - 1;
|
||||||
|
int Pivot = Upper - ((Upper - Lower) >> 1);
|
||||||
|
index_metadata *UpperEntry;
|
||||||
|
index_metadata *PivotEntry;
|
||||||
|
|
||||||
|
do {
|
||||||
|
LowerEntry = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * Lower);
|
||||||
|
PivotEntry = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * Pivot);
|
||||||
|
UpperEntry = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * Upper);
|
||||||
|
|
||||||
|
if(!StringsDiffer(SearchTerm, LowerEntry->BaseFilename)) { *Test = LowerEntry; return Lower; }
|
||||||
|
if(!StringsDiffer(SearchTerm, PivotEntry->BaseFilename)) { *Test = PivotEntry; return Pivot; }
|
||||||
|
if(!StringsDiffer(SearchTerm, UpperEntry->BaseFilename)) { *Test = UpperEntry; return Upper; }
|
||||||
|
|
||||||
|
if((StringsDiffer(SearchTerm, PivotEntry->BaseFilename) < 0)) { Upper = Pivot; }
|
||||||
|
else { Lower = Pivot; }
|
||||||
|
Pivot = Upper - ((Upper - Lower) >> 1);
|
||||||
|
} while(Upper > Pivot);
|
||||||
|
return Upper;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
AccumulateIndexEntryInsertionOffset(index *Index, int EntryIndex)
|
||||||
|
{
|
||||||
|
int Result = 0;
|
||||||
|
index_metadata *AccEntry = { 0 };
|
||||||
|
if(EntryIndex < Index->Header.EntryCount >> 1)
|
||||||
|
{
|
||||||
|
for(; EntryIndex > 0; --EntryIndex)
|
||||||
|
{
|
||||||
|
AccEntry = (index_metadata*)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * (EntryIndex - 1));
|
||||||
|
Result += AccEntry->Size;
|
||||||
|
}
|
||||||
|
Result += StringLength("---\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(; EntryIndex < Index->Header.EntryCount; ++EntryIndex)
|
||||||
|
{
|
||||||
|
AccEntry = (index_metadata*)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * EntryIndex);
|
||||||
|
Result += AccEntry->Size;
|
||||||
|
}
|
||||||
|
Result = Index->File.FileSize - Result;
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
InsertIntoIndex(index *Index, int *EntryIndex, buffers *CollationBuffers, template **BespokeTemplate, char *BaseFilename)
|
||||||
{
|
{
|
||||||
int IndexMetadataFileReadCode = ReadFileIntoBuffer(&Index->Metadata, 0);
|
int IndexMetadataFileReadCode = ReadFileIntoBuffer(&Index->Metadata, 0);
|
||||||
switch(IndexMetadataFileReadCode)
|
switch(IndexMetadataFileReadCode)
|
||||||
|
@ -4399,12 +4464,11 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
|
||||||
int IndexEntryInsertionStart = -1;
|
int IndexEntryInsertionStart = -1;
|
||||||
int IndexEntryInsertionEnd = -1;
|
int IndexEntryInsertionEnd = -1;
|
||||||
Index->Header.EntryCount = 0;
|
Index->Header.EntryCount = 0;
|
||||||
char *IndexEntryStart;
|
|
||||||
bool Found = FALSE;
|
bool Found = FALSE;
|
||||||
|
|
||||||
int EntryIndex;
|
|
||||||
neighbours Neighbours = { 0 };
|
neighbours Neighbours = { 0 };
|
||||||
index_metadata *This, *Next;
|
index_metadata *This = { 0 };
|
||||||
|
index_metadata *Next = { 0 };
|
||||||
if(IndexMetadataFileReadCode == RC_SUCCESS && IndexFileReadCode == RC_SUCCESS)
|
if(IndexMetadataFileReadCode == RC_SUCCESS && IndexFileReadCode == RC_SUCCESS)
|
||||||
{
|
{
|
||||||
// TODO(matt): Index validation?
|
// TODO(matt): Index validation?
|
||||||
|
@ -4421,15 +4485,70 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
|
||||||
|
|
||||||
Index->Metadata.Buffer.Ptr += sizeof(Index->Header);
|
Index->Metadata.Buffer.Ptr += sizeof(Index->Header);
|
||||||
Index->File.Buffer.Ptr += StringLength("---\n");
|
Index->File.Buffer.Ptr += StringLength("---\n");
|
||||||
IndexEntryStart = Index->File.Buffer.Ptr;
|
|
||||||
|
|
||||||
for(EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex)
|
#if BINARY_SEARCH
|
||||||
|
START_TIMING_BLOCK(" InsertIntoIndex(%s) BinarySearch: ", BaseFilename);
|
||||||
|
index_metadata *Prev = { 0 };
|
||||||
|
*EntryIndex = BinarySearchForMetadataEntry(Index, &This, BaseFilename);
|
||||||
|
if(This)
|
||||||
|
{
|
||||||
|
// Reinsert
|
||||||
|
Found = TRUE;
|
||||||
|
MetadataInsertionOffset = sizeof(index_header) + sizeof(index_metadata) * *EntryIndex;
|
||||||
|
IndexEntryInsertionStart = AccumulateIndexEntryInsertionOffset(Index, *EntryIndex);
|
||||||
|
IndexEntryInsertionEnd = IndexEntryInsertionStart + This->Size;
|
||||||
|
if(*EntryIndex > 0)
|
||||||
|
{
|
||||||
|
Prev = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (*EntryIndex - 1));
|
||||||
|
Neighbours.Prev.BaseFilename = Prev->BaseFilename;
|
||||||
|
Neighbours.Prev.Title = Prev->Title;
|
||||||
|
}
|
||||||
|
if(*EntryIndex < Index->Header.EntryCount - 1)
|
||||||
|
{
|
||||||
|
Next = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (*EntryIndex + 1));
|
||||||
|
Neighbours.Next.BaseFilename = Next->BaseFilename;
|
||||||
|
Neighbours.Next.Title = Next->Title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
This = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * *EntryIndex);
|
||||||
|
if(StringsDiffer(BaseFilename, This->BaseFilename) < 0)
|
||||||
|
{
|
||||||
|
// Insert
|
||||||
|
MetadataInsertionOffset = sizeof(index_header) + sizeof(index_metadata) * *EntryIndex;
|
||||||
|
IndexEntryInsertionStart = AccumulateIndexEntryInsertionOffset(Index, *EntryIndex);
|
||||||
|
if(*EntryIndex > 0)
|
||||||
|
{
|
||||||
|
Prev = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (*EntryIndex - 1));
|
||||||
|
Neighbours.Prev.BaseFilename = Prev->BaseFilename;
|
||||||
|
Neighbours.Prev.Title = Prev->Title;
|
||||||
|
}
|
||||||
|
Neighbours.Next.BaseFilename = This->BaseFilename;
|
||||||
|
Neighbours.Next.Title = This->Title;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Append
|
||||||
|
if(*EntryIndex > 0)
|
||||||
|
{
|
||||||
|
Neighbours.Prev.BaseFilename = This->BaseFilename;
|
||||||
|
Neighbours.Prev.Title = This->Title;
|
||||||
|
}
|
||||||
|
++*EntryIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
START_TIMING_BLOCK(" InsertIntoIndex(%s) LinearSearch: ", BaseFilename);
|
||||||
|
char *IndexEntryStart;
|
||||||
|
IndexEntryStart = Index->File.Buffer.Ptr;
|
||||||
|
for(*EntryIndex = 0; *EntryIndex < Index->Header.EntryCount; ++*EntryIndex)
|
||||||
{
|
{
|
||||||
This = (index_metadata *)Index->Metadata.Buffer.Ptr;
|
This = (index_metadata *)Index->Metadata.Buffer.Ptr;
|
||||||
if(!StringsDiffer(This->BaseFilename, BaseFilename))
|
if(!StringsDiffer(This->BaseFilename, BaseFilename))
|
||||||
{
|
{
|
||||||
// Reinsert
|
// Reinsert
|
||||||
if(EntryIndex < (Index->Header.EntryCount - 1))
|
if(*EntryIndex < (Index->Header.EntryCount - 1))
|
||||||
{
|
{
|
||||||
Next = (index_metadata *)(Index->Metadata.Buffer.Ptr + sizeof(Index->Entry));
|
Next = (index_metadata *)(Index->Metadata.Buffer.Ptr + sizeof(Index->Entry));
|
||||||
Neighbours.Next.BaseFilename = Next->BaseFilename;
|
Neighbours.Next.BaseFilename = Next->BaseFilename;
|
||||||
|
@ -4463,9 +4582,11 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
|
||||||
Neighbours.Prev.Title = This->Title;
|
Neighbours.Prev.Title = This->Title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
START_TIMING_BLOCK(" InsertIntoIndex(%s) NoSearch: ", BaseFilename);
|
||||||
// NOTE(matt): Initialising new index_header
|
// NOTE(matt): Initialising new index_header
|
||||||
Index->Header.CurrentDBVersion = CINERA_DB_VERSION;
|
Index->Header.CurrentDBVersion = CINERA_DB_VERSION;
|
||||||
Index->Header.CurrentAppVersion = CINERA_APP_VERSION;
|
Index->Header.CurrentAppVersion = CINERA_APP_VERSION;
|
||||||
|
@ -4507,6 +4628,7 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
|
||||||
}
|
}
|
||||||
closedir(OutputDirectoryHandle);
|
closedir(OutputDirectoryHandle);
|
||||||
}
|
}
|
||||||
|
END_TIMING_BLOCK();
|
||||||
|
|
||||||
char InputFile[StringLength(BaseFilename) + StringLength(".hmml")];
|
char InputFile[StringLength(BaseFilename) + StringLength(".hmml")];
|
||||||
CopyString(InputFile, "%s.hmml", BaseFilename);
|
CopyString(InputFile, "%s.hmml", BaseFilename);
|
||||||
|
@ -4783,7 +4905,7 @@ int DeleteNeighbourLinks(file_buffer *File, index_metadata *Metadata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
|
int LinkNeighbours(index *Index, int EntryIndex, char *BaseFilename, int LinkType)
|
||||||
{
|
{
|
||||||
switch(ReadFileIntoBuffer(&Index->Metadata, 0))
|
switch(ReadFileIntoBuffer(&Index->Metadata, 0))
|
||||||
{
|
{
|
||||||
|
@ -4801,8 +4923,24 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
|
||||||
index_metadata *Prev = { 0 };
|
index_metadata *Prev = { 0 };
|
||||||
index_metadata *This = { 0 };
|
index_metadata *This = { 0 };
|
||||||
index_metadata *Next = { 0 };
|
index_metadata *Next = { 0 };
|
||||||
int EntryIndex = 0;
|
|
||||||
|
|
||||||
|
#if BINARY_SEARCH
|
||||||
|
START_TIMING_BLOCK(" LinkNeighbours(%s) [passing in EntryIndex]: ", BaseFilename);
|
||||||
|
This = (index_metadata *)Index->Metadata.Buffer.Ptr;
|
||||||
|
if(Index->Header.EntryCount != 1)
|
||||||
|
{
|
||||||
|
This = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * EntryIndex);
|
||||||
|
if(EntryIndex > 0)
|
||||||
|
{
|
||||||
|
Prev = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (EntryIndex - 1));
|
||||||
|
}
|
||||||
|
if(EntryIndex < Index->Header.EntryCount - 1)
|
||||||
|
{
|
||||||
|
Next = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (EntryIndex + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
START_TIMING_BLOCK(" LinkNeighbours(%s) LinearSearch: ", BaseFilename);
|
||||||
switch(LinkType)
|
switch(LinkType)
|
||||||
{
|
{
|
||||||
case LINK_INCLUDE:
|
case LINK_INCLUDE:
|
||||||
|
@ -4848,6 +4986,8 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
END_TIMING_BLOCK();
|
||||||
|
|
||||||
if(!Prev && !Next)
|
if(!Prev && !Next)
|
||||||
{
|
{
|
||||||
|
@ -4953,7 +5093,7 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
DeleteFromIndex(index *Index, char *BaseFilename)
|
DeleteFromIndex(index *Index, int *EntryIndex, char *BaseFilename)
|
||||||
{
|
{
|
||||||
// TODO(matt): LogError()
|
// TODO(matt): LogError()
|
||||||
switch(ReadFileIntoBuffer(&Index->Metadata, 0))
|
switch(ReadFileIntoBuffer(&Index->Metadata, 0))
|
||||||
|
@ -4985,9 +5125,23 @@ DeleteFromIndex(index *Index, char *BaseFilename)
|
||||||
int DeleteMetadataFrom = -1;
|
int DeleteMetadataFrom = -1;
|
||||||
int DeleteFileFrom = -1;
|
int DeleteFileFrom = -1;
|
||||||
int DeleteFileTo = -1;
|
int DeleteFileTo = -1;
|
||||||
int SizeAcc = 0;
|
|
||||||
|
|
||||||
for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex, Index->Metadata.Buffer.Ptr += sizeof(index_metadata))
|
#if BINARY_SEARCH
|
||||||
|
START_TIMING_BLOCK(" DeleteFromIndex(%s) BinarySearch: ", BaseFilename);
|
||||||
|
index_metadata *This = { 0 };
|
||||||
|
*EntryIndex = BinarySearchForMetadataEntry(Index, &This, BaseFilename);
|
||||||
|
if(This)
|
||||||
|
{
|
||||||
|
Found = TRUE;
|
||||||
|
DeleteMetadataFrom = (char *)This - Index->Metadata.Buffer.Location;
|
||||||
|
DeleteFileFrom = AccumulateIndexEntryInsertionOffset(Index, *EntryIndex);
|
||||||
|
DeleteFileTo = DeleteFileFrom + This->Size;
|
||||||
|
--Index->Header.EntryCount;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
START_TIMING_BLOCK(" DeleteFromIndex(%s) LinearSearch: ", BaseFilename);
|
||||||
|
int SizeAcc = 0;
|
||||||
|
for(*EntryIndex = 0; *EntryIndex < Index->Header.EntryCount; ++*EntryIndex, Index->Metadata.Buffer.Ptr += sizeof(index_metadata))
|
||||||
{
|
{
|
||||||
index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr;
|
index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr;
|
||||||
if(!StringsDiffer(This.BaseFilename, BaseFilename))
|
if(!StringsDiffer(This.BaseFilename, BaseFilename))
|
||||||
|
@ -5001,6 +5155,9 @@ DeleteFromIndex(index *Index, char *BaseFilename)
|
||||||
}
|
}
|
||||||
SizeAcc += This.Size;
|
SizeAcc += This.Size;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
END_TIMING_BLOCK();
|
||||||
|
|
||||||
|
|
||||||
if(Found)
|
if(Found)
|
||||||
{
|
{
|
||||||
|
@ -5019,6 +5176,11 @@ DeleteFromIndex(index *Index, char *BaseFilename)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if(*EntryIndex == Index->Header.EntryCount) // NOTE(matt): LinkNeighbours() requires this
|
||||||
|
{
|
||||||
|
--*EntryIndex;
|
||||||
|
}
|
||||||
|
|
||||||
if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; }
|
if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; }
|
||||||
if(!(Index->File.Handle = fopen(Index->File.Path, "w"))) { FreeBuffer(&Index->File.Buffer); return RC_ERROR_FILE; }
|
if(!(Index->File.Handle = fopen(Index->File.Path, "w"))) { FreeBuffer(&Index->File.Buffer); return RC_ERROR_FILE; }
|
||||||
|
|
||||||
|
@ -5043,18 +5205,10 @@ DeleteFromIndex(index *Index, char *BaseFilename)
|
||||||
int
|
int
|
||||||
IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy malloc's CollationBuffers->Index
|
IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy malloc's CollationBuffers->Index
|
||||||
{
|
{
|
||||||
// TODO(matt): Consider parsing the index into a linked / skip list, or do something to save us having to iterate through
|
if(ReadFileIntoBuffer(&Index->Metadata, 0) == RC_SUCCESS)
|
||||||
// the index file multiple times
|
|
||||||
|
|
||||||
int IndexMetadataFileReadCode = ReadFileIntoBuffer(&Index->Metadata, 0);
|
|
||||||
int IndexFileReadCode = ReadFileIntoBuffer(&Index->File, 0);
|
|
||||||
|
|
||||||
if(IndexMetadataFileReadCode == RC_SUCCESS && IndexFileReadCode == RC_SUCCESS)
|
|
||||||
{
|
{
|
||||||
Index->Header = *(index_header*)Index->Metadata.Buffer.Ptr;
|
Index->Header = *(index_header*)Index->Metadata.Buffer.Ptr;
|
||||||
Index->Metadata.Buffer.Ptr += sizeof(Index->Header);
|
Index->Metadata.Buffer.Ptr += sizeof(Index->Header);
|
||||||
Index->File.Buffer.Ptr += StringLength("---\n");
|
|
||||||
char *IndexEntryStart = Index->File.Buffer.Ptr;
|
|
||||||
|
|
||||||
bool ProjectFound = FALSE;
|
bool ProjectFound = FALSE;
|
||||||
int ProjectIndex;
|
int ProjectIndex;
|
||||||
|
@ -5071,12 +5225,12 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Missing Project Info for %s\n", Config.ProjectID);
|
fprintf(stderr, "Missing Project Info for %s\n", Config.ProjectID);
|
||||||
FreeBuffer(&Index->Metadata.Buffer);
|
FreeBuffer(&Index->Metadata.Buffer);
|
||||||
FreeBuffer(&Index->File.Buffer);
|
|
||||||
return RC_ERROR_PROJECT;
|
return RC_ERROR_PROJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ThemeStringLength = StringsDiffer(Config.Theme, "") ? (StringLength(Config.Theme) * 2) : (StringLength(Config.ProjectID) * 2);
|
char *Theme = StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID;
|
||||||
char queryContainer[680 + ThemeStringLength];
|
int ThemeStringAllowance = StringLength(Theme) * 2;
|
||||||
|
char queryContainer[680 + ThemeStringAllowance];
|
||||||
CopyString(queryContainer,
|
CopyString(queryContainer,
|
||||||
"<div class=\"cineraQueryContainer %s\">\n"
|
"<div class=\"cineraQueryContainer %s\">\n"
|
||||||
" <label for=\"query\">Query:</label>\n"
|
" <label for=\"query\">Query:</label>\n"
|
||||||
|
@ -5093,8 +5247,8 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
|
||||||
" <div id=\"cineraIndex\" class=\"%s\">\n"
|
" <div id=\"cineraIndex\" class=\"%s\">\n"
|
||||||
" <div id=\"cineraIndexSort\">Sort: Old to New ⏶</div>\n"
|
" <div id=\"cineraIndexSort\">Sort: Old to New ⏶</div>\n"
|
||||||
" <div id=\"cineraIndexEntries\">\n",
|
" <div id=\"cineraIndexEntries\">\n",
|
||||||
StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID,
|
Theme,
|
||||||
StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID);
|
Theme);
|
||||||
|
|
||||||
buffer URLPrefix;
|
buffer URLPrefix;
|
||||||
ClaimBuffer(&URLPrefix, "URLPrefix", 1024);
|
ClaimBuffer(&URLPrefix, "URLPrefix", 1024);
|
||||||
|
@ -5118,7 +5272,7 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
|
||||||
" </script>\n"
|
" </script>\n"
|
||||||
" <script type=\"text/javascript\" src=\"%scinera_search.js\"></script>\n",
|
" <script type=\"text/javascript\" src=\"%scinera_search.js\"></script>\n",
|
||||||
Config.ProjectID,
|
Config.ProjectID,
|
||||||
StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID,
|
Theme,
|
||||||
Config.BaseURL,
|
Config.BaseURL,
|
||||||
Config.PlayerLocation,
|
Config.PlayerLocation,
|
||||||
StringsDiffer(Config.PlayerURLPrefix, "") ? Config.PlayerURLPrefix : Config.ProjectID,
|
StringsDiffer(Config.PlayerURLPrefix, "") ? Config.PlayerURLPrefix : Config.ProjectID,
|
||||||
|
@ -5132,18 +5286,26 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
|
||||||
if(!(CollationBuffers->Index.Location = malloc(CollationBuffers->Index.Size)))
|
if(!(CollationBuffers->Index.Location = malloc(CollationBuffers->Index.Size)))
|
||||||
{
|
{
|
||||||
FreeBuffer(&Index->Metadata.Buffer);
|
FreeBuffer(&Index->Metadata.Buffer);
|
||||||
FreeBuffer(&Index->File.Buffer);
|
|
||||||
return(RC_ERROR_MEMORY);
|
return(RC_ERROR_MEMORY);
|
||||||
}
|
}
|
||||||
CollationBuffers->Index.Ptr = CollationBuffers->Index.Location;
|
CollationBuffers->Index.Ptr = CollationBuffers->Index.Location;
|
||||||
|
|
||||||
CopyStringToBuffer(&CollationBuffers->Index, queryContainer);
|
CopyStringToBuffer(&CollationBuffers->Index, queryContainer);
|
||||||
|
|
||||||
for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex)
|
char *ProjectUnit = 0;
|
||||||
|
if(StringsDiffer(ProjectInfo[ProjectIndex].Unit, ""))
|
||||||
{
|
{
|
||||||
index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr;
|
ProjectUnit = ProjectInfo[ProjectIndex].Unit;
|
||||||
|
}
|
||||||
|
int ProjectIDLength = StringLength(Config.ProjectID);
|
||||||
char Number[16];
|
char Number[16];
|
||||||
CopyString(Number, This.BaseFilename + StringLength(Config.ProjectID));
|
char Text[1024]; // NOTE(matt): Surely this will be big enough
|
||||||
|
index_metadata *This;
|
||||||
|
|
||||||
|
for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex, Index->Metadata.Buffer.Ptr += sizeof(index_metadata))
|
||||||
|
{
|
||||||
|
This = (index_metadata *)Index->Metadata.Buffer.Ptr;
|
||||||
|
CopyString(Number, This->BaseFilename + ProjectIDLength);
|
||||||
if(ProjectInfo[ProjectIndex].NumberingScheme == NS_LINEAR)
|
if(ProjectInfo[ProjectIndex].NumberingScheme == NS_LINEAR)
|
||||||
{
|
{
|
||||||
for(int i = 0; Number[i]; ++i)
|
for(int i = 0; Number[i]; ++i)
|
||||||
|
@ -5155,24 +5317,18 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeekBufferForString(&Index->File.Buffer, "title: \"", C_SEEK_FORWARDS, C_SEEK_AFTER);
|
ConstructPlayerURL(&PlayerURL, This->BaseFilename);
|
||||||
char Title[256];
|
|
||||||
CopyStringNoFormatT(Title, Index->File.Buffer.Ptr, '\n');
|
|
||||||
Title[StringLength(Title) - 1] = '\0';
|
|
||||||
|
|
||||||
ConstructPlayerURL(&PlayerURL, This.BaseFilename);
|
if(ProjectUnit)
|
||||||
|
|
||||||
if(StringsDiffer(ProjectInfo[ProjectIndex].Unit, ""))
|
|
||||||
{
|
{
|
||||||
CopyStringToBuffer(&CollationBuffers->Index,
|
CopyStringToBuffer(&CollationBuffers->Index,
|
||||||
" <div>\n"
|
" <div>\n"
|
||||||
" <a href=\"%s\">", PlayerURL.Location);
|
" <a href=\"%s\">", PlayerURL.Location);
|
||||||
|
|
||||||
char Text[1024]; // NOTE(matt): Surely this will be big enough
|
|
||||||
CopyString(Text, "%s %s: %s",
|
CopyString(Text, "%s %s: %s",
|
||||||
ProjectInfo[ProjectIndex].Unit, // TODO(matt): Do we need to special-case the various numbering schemes?
|
ProjectUnit, // TODO(matt): Do we need to special-case the various numbering schemes?
|
||||||
Number,
|
Number,
|
||||||
Title);
|
This->Title);
|
||||||
|
|
||||||
CopyStringToBufferHTMLSafe(&CollationBuffers->Index, Text);
|
CopyStringToBufferHTMLSafe(&CollationBuffers->Index, Text);
|
||||||
|
|
||||||
|
@ -5184,15 +5340,13 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
|
||||||
{
|
{
|
||||||
CopyStringToBuffer(&CollationBuffers->Index,
|
CopyStringToBuffer(&CollationBuffers->Index,
|
||||||
" <div>\n"
|
" <div>\n"
|
||||||
" <a href=\"%s\">%s</a>\n"
|
" <a href=\"%s\">", PlayerURL.Location);
|
||||||
" </div>\n",
|
|
||||||
PlayerURL.Location,
|
|
||||||
Title);
|
|
||||||
}
|
|
||||||
|
|
||||||
Index->Metadata.Buffer.Ptr += sizeof(Index->Entry);
|
CopyStringToBufferHTMLSafe(&CollationBuffers->Index, This->Title);
|
||||||
IndexEntryStart += This.Size;
|
CopyStringToBuffer(&CollationBuffers->Index,
|
||||||
Index->File.Buffer.Ptr = IndexEntryStart;
|
"</a>\n"
|
||||||
|
" </div>\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclaimBuffer(&PlayerURL);
|
DeclaimBuffer(&PlayerURL);
|
||||||
|
@ -5200,7 +5354,6 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
|
||||||
CopyStringToBuffer(&CollationBuffers->Index, Script);
|
CopyStringToBuffer(&CollationBuffers->Index, Script);
|
||||||
|
|
||||||
FreeBuffer(&Index->Metadata.Buffer);
|
FreeBuffer(&Index->Metadata.Buffer);
|
||||||
FreeBuffer(&Index->File.Buffer);
|
|
||||||
return RC_SUCCESS;
|
return RC_SUCCESS;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -5242,7 +5395,7 @@ StripSurroundingSlashes(char *String) // NOTE(matt): For relative paths
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
GeneratePlayerPage(index *Index, buffers *CollationBuffers, template *PlayerTemplate, char *BaseFilename)
|
GeneratePlayerPage(index *Index, int EntryIndex, buffers *CollationBuffers, template *PlayerTemplate, char *BaseFilename)
|
||||||
{
|
{
|
||||||
buffer OutputDirectoryPath;
|
buffer OutputDirectoryPath;
|
||||||
ClaimBuffer(&OutputDirectoryPath, "OutputDirectoryPath", 1024);
|
ClaimBuffer(&OutputDirectoryPath, "OutputDirectoryPath", 1024);
|
||||||
|
@ -5281,7 +5434,12 @@ GeneratePlayerPage(index *Index, buffers *CollationBuffers, template *PlayerTemp
|
||||||
ReadFileIntoBuffer(&Index->Metadata, 0);
|
ReadFileIntoBuffer(&Index->Metadata, 0);
|
||||||
Index->Metadata.Buffer.Ptr += sizeof(index_header);
|
Index->Metadata.Buffer.Ptr += sizeof(index_header);
|
||||||
int MetadataInsertionOffset = 0;
|
int MetadataInsertionOffset = 0;
|
||||||
for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex)
|
#if BINARY_SEARCH
|
||||||
|
START_TIMING_BLOCK(" GeneratePlayerPage(%s) [passing in EntryIndex]: ", BaseFilename);
|
||||||
|
MetadataInsertionOffset = sizeof(index_header) + sizeof(index_metadata) * EntryIndex;
|
||||||
|
#else
|
||||||
|
START_TIMING_BLOCK(" GeneratePlayerPage(%s) LinearSearch: ", BaseFilename);
|
||||||
|
for(EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex)
|
||||||
{
|
{
|
||||||
index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr;
|
index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr;
|
||||||
if(!StringsDiffer(This.BaseFilename, Index->Entry.BaseFilename))
|
if(!StringsDiffer(This.BaseFilename, Index->Entry.BaseFilename))
|
||||||
|
@ -5291,11 +5449,13 @@ GeneratePlayerPage(index *Index, buffers *CollationBuffers, template *PlayerTemp
|
||||||
}
|
}
|
||||||
Index->Metadata.Buffer.Ptr += sizeof(index_metadata);
|
Index->Metadata.Buffer.Ptr += sizeof(index_metadata);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
END_TIMING_BLOCK();
|
||||||
|
|
||||||
if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; }
|
if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; }
|
||||||
fwrite(Index->Metadata.Buffer.Location, MetadataInsertionOffset, 1, Index->Metadata.Handle);
|
fwrite(Index->Metadata.Buffer.Location, MetadataInsertionOffset, 1, Index->Metadata.Handle);
|
||||||
fwrite(&Index->Entry, sizeof(Index->Entry), 1, Index->Metadata.Handle);
|
fwrite(&Index->Entry, sizeof(Index->Entry), 1, Index->Metadata.Handle);
|
||||||
fwrite(Index->Metadata.Buffer.Ptr + sizeof(Index->Entry), Index->Metadata.FileSize - MetadataInsertionOffset - sizeof(Index->Entry), 1, Index->Metadata.Handle);
|
fwrite(Index->Metadata.Buffer.Location + MetadataInsertionOffset + sizeof(Index->Entry), Index->Metadata.FileSize - MetadataInsertionOffset - sizeof(Index->Entry), 1, Index->Metadata.Handle);
|
||||||
fclose(Index->Metadata.Handle);
|
fclose(Index->Metadata.Handle);
|
||||||
FreeBuffer(&Index->Metadata.Buffer);
|
FreeBuffer(&Index->Metadata.Buffer);
|
||||||
|
|
||||||
|
@ -5374,14 +5534,17 @@ DeletePlayerPageFromFilesystem(char *BaseFilename, char *PlayerLocation, bool Re
|
||||||
return RC_SUCCESS;
|
return RC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
int
|
||||||
DeleteEntry(index *Index, char *BaseFilename)
|
DeleteEntry(index *Index, char *BaseFilename)
|
||||||
{
|
{
|
||||||
if(DeleteFromIndex(Index, BaseFilename) == RC_SUCCESS)
|
int EntryIndex = 0;
|
||||||
|
if(DeleteFromIndex(Index, &EntryIndex, BaseFilename) == RC_SUCCESS)
|
||||||
{
|
{
|
||||||
LinkNeighbours(Index, BaseFilename, LINK_EXCLUDE);
|
LinkNeighbours(Index, EntryIndex, BaseFilename, LINK_EXCLUDE);
|
||||||
DeletePlayerPageFromFilesystem(BaseFilename, Config.PlayerLocation, FALSE);
|
DeletePlayerPageFromFilesystem(BaseFilename, Config.PlayerLocation, FALSE);
|
||||||
|
return RC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
return RC_NOOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -5419,24 +5582,28 @@ MonitorDirectory(index *Index, buffers *CollationBuffers, template *IndexTemplat
|
||||||
// TODO(matt): Maybe handle IN_ALL_EVENTS
|
// TODO(matt): Maybe handle IN_ALL_EVENTS
|
||||||
if(Event->mask & IN_DELETE || Event->mask & IN_MOVED_FROM)
|
if(Event->mask & IN_DELETE || Event->mask & IN_MOVED_FROM)
|
||||||
{
|
{
|
||||||
DeleteEntry(Index, BaseFilename);
|
if(DeleteEntry(Index, BaseFilename) == RC_SUCCESS)
|
||||||
|
{
|
||||||
|
GenerateIndexPage(Index, CollationBuffers, IndexTemplate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch(InsertIntoIndex(Index, CollationBuffers, &BespokeTemplate, BaseFilename))
|
int EntryIndex = 0;
|
||||||
|
switch(InsertIntoIndex(Index, &EntryIndex, CollationBuffers, &BespokeTemplate, BaseFilename))
|
||||||
{
|
{
|
||||||
case RC_SUCCESS:
|
case RC_SUCCESS:
|
||||||
case RC_UNFOUND:
|
case RC_UNFOUND:
|
||||||
LinkNeighbours(Index, BaseFilename, LINK_INCLUDE);
|
LinkNeighbours(Index, EntryIndex, BaseFilename, LINK_INCLUDE);
|
||||||
{
|
{
|
||||||
if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, ""))
|
if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, ""))
|
||||||
{
|
{
|
||||||
GeneratePlayerPage(Index, CollationBuffers, BespokeTemplate, BaseFilename);
|
GeneratePlayerPage(Index, EntryIndex, CollationBuffers, BespokeTemplate, BaseFilename);
|
||||||
DeclaimTemplate(BespokeTemplate);
|
DeclaimTemplate(BespokeTemplate);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GeneratePlayerPage(Index, CollationBuffers, PlayerTemplate, BaseFilename);
|
GeneratePlayerPage(Index, EntryIndex, CollationBuffers, PlayerTemplate, BaseFilename);
|
||||||
}
|
}
|
||||||
GenerateIndexPage(Index, CollationBuffers, IndexTemplate);
|
GenerateIndexPage(Index, CollationBuffers, IndexTemplate);
|
||||||
} break;
|
} break;
|
||||||
|
@ -5804,20 +5971,21 @@ SyncIndexWithInput(index *Index, buffers *CollationBuffers, template *IndexTempl
|
||||||
if(!(StringsDiffer(Ptr, ".hmml")))
|
if(!(StringsDiffer(Ptr, ".hmml")))
|
||||||
{
|
{
|
||||||
*Ptr = '\0';
|
*Ptr = '\0';
|
||||||
switch(InsertIntoIndex(Index, CollationBuffers, &BespokeTemplate, ProjectFiles->d_name))
|
int EntryIndex = 0;
|
||||||
|
switch(InsertIntoIndex(Index, &EntryIndex, CollationBuffers, &BespokeTemplate, ProjectFiles->d_name))
|
||||||
{
|
{
|
||||||
case RC_UNFOUND:
|
case RC_UNFOUND:
|
||||||
LinkNeighbours(Index, ProjectFiles->d_name, LINK_INCLUDE);
|
LinkNeighbours(Index, EntryIndex, ProjectFiles->d_name, LINK_INCLUDE);
|
||||||
case RC_SUCCESS:
|
case RC_SUCCESS:
|
||||||
{
|
{
|
||||||
if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, ""))
|
if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, ""))
|
||||||
{
|
{
|
||||||
GeneratePlayerPage(Index, CollationBuffers, BespokeTemplate, ProjectFiles->d_name);
|
GeneratePlayerPage(Index, EntryIndex, CollationBuffers, BespokeTemplate, ProjectFiles->d_name);
|
||||||
DeclaimTemplate(BespokeTemplate);
|
DeclaimTemplate(BespokeTemplate);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GeneratePlayerPage(Index, CollationBuffers, PlayerTemplate, ProjectFiles->d_name);
|
GeneratePlayerPage(Index, EntryIndex, CollationBuffers, PlayerTemplate, ProjectFiles->d_name);
|
||||||
}
|
}
|
||||||
Inserted = TRUE;
|
Inserted = TRUE;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue