mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 15:22:04 +00:00
Add support for segmented text files.
The idea comes from The OpenGL Shader Wrangler (http://prideout.net/blog/?p=11). Text files are broken up into chunks via lines beginning with -- (^-- in regex). The chunks are optionally named with tags of the form: [0-9A-Za-z._]+. Unnamed chunks cannot be found. Searching for chunks looks for the longest tag that matches the beginning of the search tag (eg, a chunk named "Vertex" will be found with a search tag of "Vertex.foo"). Note that '.' forms the units for the searc ("Vertex.foo" will not find "Vertex.f"). Unlike glsw, this implementation does not have the concept of effects keys as that will be separate. Also, this implementation takes strings rather than file names (thus is more generally useful).
This commit is contained in:
parent
b133e9a092
commit
d912970cd3
6 changed files with 346 additions and 5 deletions
|
@ -7,8 +7,8 @@ nobase_pkginclude_HEADERS = \
|
|||
link.h llist.h locs.h mathlib.h mdfour.h mersenne.h model.h modelgen.h \
|
||||
msg.h object.h pak.h pakfile.h pcx.h png.h plugin.h pr_comp.h pr_debug.h \
|
||||
pr_obj.h progs.h qargs.h qdefs.h qendian.h qfplist.h qtypes.h quakefs.h \
|
||||
quakeio.h render.h riff.h ruamoko.h set.h screen.h script.h sizebuf.h \
|
||||
skin.h sound.h spritegn.h sys.h teamplay.h tga.h uint32.h va.h \
|
||||
quakeio.h render.h riff.h ruamoko.h screen.h script.h segtext.h set.h \
|
||||
sizebuf.h skin.h sound.h spritegn.h sys.h teamplay.h tga.h uint32.h va.h \
|
||||
ver_check.h vid.h vrect.h view.h wad.h wadfile.h winding.h zone.h \
|
||||
\
|
||||
GL/ati.h GL/defines.h GL/extensions.h GL/funcs.h GL/qf_draw.h \
|
||||
|
|
58
include/QF/segtext.h
Normal file
58
include/QF/segtext.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
segtext.h
|
||||
|
||||
Segmented text file handling
|
||||
|
||||
Copyright (C) 2013 Bill Currie <bill@taniwha.org>
|
||||
|
||||
Author: Bill Currie <bill@taniwha.org>
|
||||
Date: 2013/02/26
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __QF_segtext_h
|
||||
#define __QF_segtext_h
|
||||
|
||||
/** \defgroup segtext Segmented text files.
|
||||
\ingroup utils
|
||||
|
||||
Based on The OpenGL Shader Wrangler: http://prideout.net/blog/?p=11
|
||||
*/
|
||||
|
||||
typedef struct segchunk_s {
|
||||
struct segchunk_s *next;
|
||||
const char *tag;
|
||||
const char *text;
|
||||
int start_line;
|
||||
} segchunk_t;
|
||||
|
||||
typedef struct segtext_s {
|
||||
struct segtext_s *next;
|
||||
segchunk_t *chunk_list;
|
||||
struct hashtab_s *tab;
|
||||
} segtext_t;
|
||||
|
||||
segtext_t *Segtext_new (const char *src);
|
||||
void Segtext_delete (segtext_t *st);
|
||||
const segchunk_t *Segtext_FindChunk (const segtext_t *st, const char *tag);
|
||||
const char *Segtext_Find (const segtext_t *st, const char *tag);
|
||||
|
||||
#endif//__QF_segtext_h
|
|
@ -52,8 +52,8 @@ libQFutil_la_SOURCES= \
|
|||
bspfile.c buildnum.c cbuf.c checksum.c cmd.c crc.c cvar.c dstring.c \
|
||||
fendian.c hash.c idparse.c info.c link.c llist.c \
|
||||
mathlib.c mdfour.c mersenne.c msg.c pakfile.c plugin.c qargs.c qendian.c \
|
||||
qfplist.c quakefs.c quakeio.c riff.c script.c set.c sizebuf.c string.c \
|
||||
sys.c va.c ver_check.c vrect.c wad.c wadfile.c zone.c \
|
||||
qfplist.c quakefs.c quakeio.c riff.c script.c segtext.c set.c sizebuf.c \
|
||||
string.c sys.c va.c ver_check.c vrect.c wad.c wadfile.c zone.c \
|
||||
$(dirent) $(fnmatch) $(getopt)
|
||||
|
||||
EXTRA_DIST= $(fnmatch_src) $(getopt_src)
|
||||
|
|
214
libs/util/segtext.c
Normal file
214
libs/util/segtext.c
Normal file
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
segtext.c
|
||||
|
||||
Segmented text file handling
|
||||
|
||||
Copyright (C) 2013 Bill Currie <bill@taniwha.org>
|
||||
|
||||
Author: Bill Currie <bill@taniwha.org>
|
||||
Date: 2013/02/26
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "QF/alloc.h"
|
||||
#include "QF/hash.h"
|
||||
#include "QF/qtypes.h"
|
||||
#include "QF/segtext.h"
|
||||
|
||||
static segchunk_t *free_chunks;
|
||||
static segtext_t *free_texts;
|
||||
|
||||
static segchunk_t *
|
||||
new_chunk (void)
|
||||
{
|
||||
segchunk_t *chunk;
|
||||
ALLOC (64, segchunk_t, chunks, chunk);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
static void
|
||||
delete_chunk (segchunk_t *chunk)
|
||||
{
|
||||
FREE (chunks, chunk);
|
||||
}
|
||||
|
||||
static segtext_t *
|
||||
new_text (void)
|
||||
{
|
||||
segtext_t *text;
|
||||
ALLOC (64, segtext_t, texts, text);
|
||||
return text;
|
||||
}
|
||||
|
||||
static void
|
||||
delete_text (segtext_t *text)
|
||||
{
|
||||
FREE (texts, text);
|
||||
}
|
||||
|
||||
static const char *
|
||||
segtext_getkey (const void *sc, void *unused)
|
||||
{
|
||||
segchunk_t *chunk = (segchunk_t *) sc;
|
||||
if (!chunk->tag)
|
||||
return "";
|
||||
return chunk->tag;
|
||||
}
|
||||
|
||||
static char *
|
||||
next_line (char *src, int *line)
|
||||
{
|
||||
while (*src && *src != '\n')
|
||||
src++;
|
||||
if (*src == '\n') {
|
||||
*line += 1;
|
||||
src++;
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
static char *
|
||||
next_chunk (char *src, int *line)
|
||||
{
|
||||
while (*src) {
|
||||
// chunks begin on a line that starts with --
|
||||
if (src[0] == '-' && src[1] == '-')
|
||||
return src;
|
||||
src = next_line (src, line);
|
||||
}
|
||||
// no next chunk
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
is_tag_char (char ch)
|
||||
{
|
||||
return isalnum ((byte) ch) || ch == '.' || ch == '_';
|
||||
}
|
||||
|
||||
static char *
|
||||
find_tag (const char *src)
|
||||
{
|
||||
const char *end;
|
||||
char *tag;
|
||||
|
||||
while (*src && *src != '\n' && !is_tag_char (*src))
|
||||
src++;
|
||||
|
||||
for (end = src; is_tag_char (*end); end++)
|
||||
;
|
||||
if (end == src)
|
||||
return 0;
|
||||
|
||||
tag = malloc (end - src + 1);
|
||||
strncpy (tag, src, end - src);
|
||||
tag[end - src] = 0;
|
||||
return tag;
|
||||
}
|
||||
|
||||
VISIBLE segtext_t *
|
||||
Segtext_new (const char *source_string)
|
||||
{
|
||||
segchunk_t **chunk;
|
||||
segtext_t *text;
|
||||
char *src;
|
||||
int line = 1;
|
||||
|
||||
if (!source_string)
|
||||
return 0;
|
||||
text = new_text ();
|
||||
text->tab = Hash_NewTable (61, segtext_getkey, 0, 0);
|
||||
|
||||
src = strdup (source_string);
|
||||
// The first chunk is special in that it holds the pointer to the copied
|
||||
// string data. The chunk itself has no name and may be empty.
|
||||
chunk = &text->chunk_list;
|
||||
*chunk = new_chunk ();
|
||||
(*chunk)->tag = 0;
|
||||
(*chunk)->text = src;
|
||||
(*chunk)->start_line = line;
|
||||
|
||||
chunk = &(*chunk)->next;
|
||||
|
||||
while ((src = next_chunk (src, &line))) {
|
||||
*src++ = 0; // terminate the previous chunk
|
||||
*chunk = new_chunk ();
|
||||
(*chunk)->tag = find_tag (src);
|
||||
(*chunk)->text = next_line (src, &line);
|
||||
(*chunk)->start_line = line;
|
||||
// If tags are duplicated, the first one takes precedence
|
||||
if ((*chunk)->tag && !Hash_Find (text->tab, (*chunk)->tag))
|
||||
Hash_Add (text->tab, *chunk);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
VISIBLE void
|
||||
Segtext_delete (segtext_t *st)
|
||||
{
|
||||
segchunk_t *chunk;
|
||||
|
||||
// the first chunk holds the pointer to the block holding all chunk data;
|
||||
free ((char *) st->chunk_list->text);
|
||||
while (st->chunk_list) {
|
||||
chunk = st->chunk_list;
|
||||
st->chunk_list = chunk->next;
|
||||
if (chunk->tag)
|
||||
free ((char *) chunk->tag);
|
||||
delete_chunk (chunk);
|
||||
}
|
||||
Hash_DelTable (st->tab);
|
||||
delete_text (st);
|
||||
}
|
||||
|
||||
const segchunk_t *
|
||||
Segtext_FindChunk (const segtext_t *st, const char *tag)
|
||||
{
|
||||
char *sub;
|
||||
char *dot;
|
||||
segchunk_t *chunk;
|
||||
|
||||
sub = alloca (strlen (tag) + 1);
|
||||
strcpy (sub, tag);
|
||||
do {
|
||||
chunk = Hash_Find (st->tab, sub);
|
||||
if (chunk)
|
||||
return chunk;
|
||||
dot = strrchr (sub, '.');
|
||||
if (dot)
|
||||
*dot = 0;
|
||||
} while (dot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *
|
||||
Segtext_Find (const segtext_t *st, const char *tag)
|
||||
{
|
||||
const segchunk_t *chunk = Segtext_FindChunk (st, tag);
|
||||
if (chunk)
|
||||
return chunk->text;
|
||||
return 0;
|
||||
}
|
|
@ -4,7 +4,7 @@ AM_CPPFLAGS= -I$(top_srcdir)/include
|
|||
|
||||
check_PROGRAMS= \
|
||||
test-bary test-cs test-dq test-half test-mat3 test-mat4 test-plist \
|
||||
test-qfs test-quat test-seb test-set test-vrect
|
||||
test-qfs test-quat test-seb test-seg test-set test-vrect
|
||||
|
||||
test_bary_SOURCES=test-bary.c
|
||||
test_bary_LDADD=$(top_builddir)/libs/util/libQFutil.la
|
||||
|
@ -46,6 +46,10 @@ test_seb_SOURCES=test-seb.c
|
|||
test_seb_LDADD=$(top_builddir)/libs/util/libQFutil.la
|
||||
test_seb_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la
|
||||
|
||||
test_seg_SOURCES=test-seg.c
|
||||
test_seg_LDADD=$(top_builddir)/libs/util/libQFutil.la
|
||||
test_seg_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la
|
||||
|
||||
test_set_SOURCES=test-set.c
|
||||
test_set_LDADD=$(top_builddir)/libs/util/libQFutil.la
|
||||
test_set_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la
|
||||
|
|
65
libs/util/test/test-seg.c
Normal file
65
libs/util/test/test-seg.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "QF/segtext.h"
|
||||
|
||||
static const char *test_string =
|
||||
"blah\n"
|
||||
"-- Vertex\n"
|
||||
"FOO\n"
|
||||
"-- Geometry\n"
|
||||
"BAR\n"
|
||||
"-- Fragment.Erosion\n"
|
||||
"BAZ\n"
|
||||
"-- Fragment.Grassfire\n"
|
||||
"QUX\n"
|
||||
"-- TessControl\n"
|
||||
"QUUX\n"
|
||||
"-- TessEvaluation\n"
|
||||
"QUUUX";
|
||||
|
||||
struct {
|
||||
const char *tag;
|
||||
const char *expect;
|
||||
} seg_tests[] = {
|
||||
{"", 0}, // empty tags not findable
|
||||
{"Vertex", "FOO\n"},
|
||||
{"Geometry", "BAR\n"},
|
||||
{"Fragment.Erosion", "BAZ\n"},
|
||||
{"Fragment.Grassfire", "QUX\n"},
|
||||
{"TessControl", "QUUX\n"},
|
||||
{"TessEvaluation", "QUUUX"}, // final chunk doesn't have \n
|
||||
{"Vertex.Erosion", "FOO\n"},
|
||||
{"Geometry.Grasfire", "BAR\n"},
|
||||
};
|
||||
#define num_seg_tests (sizeof (seg_tests) / sizeof (seg_tests[0]))
|
||||
|
||||
int
|
||||
main (int argc, const char **argv)
|
||||
{
|
||||
size_t i;
|
||||
int res = 0;
|
||||
segtext_t *st;
|
||||
|
||||
st = Segtext_new (test_string);
|
||||
for (i = 0; i < num_seg_tests; i++) {
|
||||
const char *text = Segtext_Find (st, seg_tests[i].tag);
|
||||
if ((!text != !seg_tests[i].expect)
|
||||
&& (!text || !seg_tests[i].expect
|
||||
|| strcmp (text, seg_tests[i].expect))) {
|
||||
fprintf (stderr, "FAIL: (%zd) \"%s\" -> \"%s\", got \"%s\"\n", i,
|
||||
seg_tests[i].tag, seg_tests[i].expect, text);
|
||||
res = 1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
Loading…
Reference in a new issue