vkdoom_m/src/common/rendering/stb_include.cpp
2025-04-23 18:49:23 -03:00

209 lines
5.1 KiB
C++

// stb_include.h - v0.02 - parse and process #include directives - public domain
//
// To build this, in one source file that includes this file do
// #define STB_INCLUDE_IMPLEMENTATION
//
// This program parses a string and replaces lines of the form
// #include "foo"
// with the contents of a file named "foo". It also embeds the
// appropriate #line directives. Note that all include files must
// reside in the location specified in the path passed to the API;
// it does not check multiple directories.
//
// If the string contains a line of the form
// #inject
// then it will be replaced with the contents of the string 'inject' passed to the API.
//
// Options:
//
// Define STB_INCLUDE_LINE_GLSL to get GLSL-style #line directives
// which use numbers instead of filenames.
//
// Define STB_INCLUDE_LINE_NONE to disable output of #line directives.
//
// Standard libraries:
//
// stdio.h FILE, fopen, fclose, fseek, ftell
// stdlib.h malloc, realloc, free
// string.h strcpy, strncmp, memcpy
//
// Credits:
//
// Written by Sean Barrett.
//
// Fixes:
// Michal Klos
#include "stb_include.h"
#include "filesystem.h"
#include "cmdlib.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
static bool stb_include_load_file(FString filename, FString &out)
{
int f = fileSystem.FindFile(filename.GetChars());
if(f < 0) return false;
out = GetStringFromLump(f, false);
return true;
}
struct include_info
{
int64_t offset;
int64_t end;
FString filename;
int64_t next_line_after;
};
static bool stb_include_isspace(int ch)
{
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
}
static void skip_whitespace(const char * &s)
{
while (*s == ' ' || *s == '\t')
{
++s;
}
}
static void skip_newline(const char * &s)
{
while (*s != '\r' && *s != '\n' && *s != 0)
{
++s;
}
if(s[0] == '\r' && s[1] == '\n')
{
s += 2;
}
else if (*s == '\r' || *s == '\n')
{
s++;
}
}
static void find_end_quote(const char * &t)
{
while (*t != '"' && *t != '\n' && *t != '\r' && *t != 0)
{
++t;
}
}
static void find_newline(const char * &s)
{
while (*s != '\r' && *s != '\n' && *s != 0)
{
++s;
}
}
// find location of all #include and #inject
static int64_t stb_include_find_includes(const char *text, TArray<include_info> &plist)
{
int64_t line_count = 1;
int64_t inc_count = 0;
const char *s = text;
const char *start;
TArray<include_info> list;
while (*s)
{
// parse is always at start of line when we reach here
start = s;
skip_whitespace(s);
if (*s == '#')
{
++s;
skip_whitespace(s);
if (0==strncmp(s, "include", 7) && stb_include_isspace(s[7]))
{
s += 7;
skip_whitespace(s);
if (*s == '"')
{
const char * include_filename = ++s;
find_end_quote(include_filename);
if (*include_filename == '"')
{ // ignore unterminated quotes
FString filename;
filename.AppendCStrPart(s, include_filename - s);
s = include_filename;
find_newline(s);
// s points to the newline, so s-start is everything except the newline
list.Push({start - text, s - text, filename, line_count + 1});
inc_count++;
}
}
}
}
skip_newline(s);
++line_count;
}
plist = std::move(list);
return inc_count;
}
FString stb_include_string(FString str, FString filename, TArray<FString> &filenames, FString &error)
{
error = "";
TArray<include_info> inc_list;
int64_t num = stb_include_find_includes(str.GetChars(), inc_list);
FString text = "";
size_t last = 0;
filenames.Push(filename);
size_t curIndex = filenames.Size();
text.AppendFormat("\n#line 1 %d // %s\n", curIndex, filename.GetChars());
for (int64_t i = 0; i < num; ++i)
{
text.AppendCStrPart(str.GetChars() + last, inc_list[i].offset - last);
FString inc = stb_include_file(inc_list[i].filename.GetChars(), filenames, error);
if (!error.IsEmpty())
{
return "";
}
text += inc;
text.AppendFormat("\n#line %d %d // %s\n", inc_list[i].next_line_after, curIndex, filename.GetChars());
// no newlines, because we kept the #include newlines, which will get appended next
last = inc_list[i].end;
}
text.AppendCStrPart(str.GetChars() + last, str.Len() - last);
return text;
}
FString stb_include_file(FString filename, TArray<FString> &filenames, FString &error)
{
FString text;
if (!stb_include_load_file(filename, text))
{
error += "Error: couldn't load '";
error += filename;
error += "'";
return "";
}
return stb_include_string(text, filename, filenames, error);
}