duke3d/scriplib.c

1018 lines
19 KiB
C
Executable File

//-------------------------------------------------------------------------
/*
Copyright (C) 1996, 2003 - 3D Realms Entertainment
This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
Duke Nukem 3D 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 the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Original Source: 1996 - Todd Replogle
Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duke3d.h"
#include "scriplib.h"
//#define DEBUG_SCRIPLIB 1
#define MAX_SCRIPTS 5
typedef enum {
SCRIPTFLAG_UNKNOWN,
SCRIPTFLAG_CATEGORY,
SCRIPTFLAG_ONESTRING,
SCRIPTFLAG_TWOSTRING,
SCRIPTFLAG_HEX,
SCRIPTFLAG_DECIMAL,
SCRIPTFLAG_FLOAT
} scriptflag_t;
typedef struct scriptnode_s {
struct scriptnode_s *child;
struct scriptnode_s *sibling;
char *key;
scriptflag_t type;
union {
char *string[2];
int number;
float floatnumber;
} data;
} scriptnode_t;
static int script_nexthandle = 0;
static int script_numscriptsopen = 0;
static scriptnode_t *script_headnode[MAX_SCRIPTS];
/* Utility functions */
static scriptnode_t *SCRIPT_constructnode (void)
{
scriptnode_t *s;
s = (scriptnode_t *) malloc (sizeof (scriptnode_t));
if (s != NULL)
{
s->child = NULL;
s->sibling = NULL;
s->key = NULL;
s->data.string[0] = NULL;
s->data.string[1] = NULL;
s->type = SCRIPTFLAG_UNKNOWN;
}
return s;
}
static void SCRIPT_freenode (scriptnode_t *node)
{
assert (node != NULL);
if (node->type == SCRIPTFLAG_ONESTRING) {
free (node->data.string[0]);
} else if (node->type == SCRIPTFLAG_TWOSTRING) {
free (node->data.string[0]);
free (node->data.string[1]);
}
free (node->key);
free (node->sibling);
free (node->child);
free (node);
}
static void SCRIPT_writenode (scriptnode_t *node, FILE *fp)
{
switch (node->type)
{
case SCRIPTFLAG_UNKNOWN:
return;
break;
case SCRIPTFLAG_CATEGORY:
fprintf (fp, "\n[%s]\n", node->key);
break;
case SCRIPTFLAG_ONESTRING:
fprintf (fp, "%s = \"%s\"\n", node->key, node->data.string[0]);
break;
case SCRIPTFLAG_TWOSTRING:
fprintf (fp, "%s = \"%s\" \"%s\"\n", node->key, node->data.string[0], node->data.string[1]);
break;
case SCRIPTFLAG_HEX:
fprintf (fp, "%s = 0x%X\n", node->key, node->data.number);
break;
case SCRIPTFLAG_DECIMAL:
fprintf (fp, "%s = %d\n", node->key, (unsigned short)node->data.number);
break;
case SCRIPTFLAG_FLOAT:
fprintf (fp, "%s = %ff\n", node->key, node->data.floatnumber);
break;
}
}
static void SCRIPT_recursivewrite (scriptnode_t *node, FILE *fp)
{
if (node == NULL) return;
SCRIPT_writenode (node, fp);
/* Free dependant nodes first */
if (node->child) {
SCRIPT_recursivewrite (node->child, fp);
}
if (node->sibling) {
SCRIPT_recursivewrite (node->sibling, fp);
}
}
static void SCRIPT_recursivefree (scriptnode_t *node)
{
assert (node != NULL);
/* Free dependant nodes first */
if (node->child) {
SCRIPT_recursivefree (node->child);
node->child = NULL;
}
if (node->sibling) {
SCRIPT_recursivefree (node->sibling);
node->sibling = NULL;
}
SCRIPT_freenode (node);
node = NULL;
}
static void SCRIPT_addsibling (scriptnode_t *node, scriptnode_t *sibling)
{
assert (node != NULL);
assert (sibling != NULL);
/* printf ("addsib: %p, %p, %p\n", node, node->sibling, sibling); */
if (node->sibling == NULL) {
node->sibling = sibling;
} else {
SCRIPT_addsibling (node->sibling, sibling);
}
}
static void SCRIPT_addchild (scriptnode_t *parent, scriptnode_t *child)
{
assert (parent != NULL);
assert (child != NULL);
if (parent->child == NULL) {
parent->child = child;
} else {
SCRIPT_addsibling (parent->child, child);
}
}
static char *SCRIPT_copystring (char * s)
{
char *ret;
ret = (char *) malloc (strlen (s)+1);
if (ret != NULL)
{
strcpy (ret, s);
}
return ret;
}
static int SCRIPT_getnexttoken (char *buffer, char* token, int start)
{
int iterator = start;
if (buffer[start] == '\0') {
token[0] = '\0';
return start;
}
while (iterator < 128 && !isspace (buffer[iterator]))
{
token[iterator-start] = buffer[iterator];
iterator++;
}
token[iterator-start] = '\0';
/* Trim off any extra whitespace */
while (iterator < 127 && isspace(buffer[iterator+1]))
iterator++;
return ++iterator;
}
/* Fills in a scriptnode with the interpreted contents of a line */
static void SCRIPT_parseline (char *curline, scriptnode_t *node)
{
char token[128];
int i;
/* Needs to handle 5 cases: */
/* key = someint */
/* key = 0xsomehexnum */
/* key = ~ */
/* key = "onestring" */
/* key = "two" "strings" */
assert (node != NULL);
assert (curline != NULL);
i = SCRIPT_getnexttoken (curline, token, 0);
node->key = SCRIPT_copystring (token);
i = SCRIPT_getnexttoken (curline, token, i);
/* Sanity check... this token should be "=" */
if (strcmp (token, "=")) {
/* Error state, free the memory allocated */
SCRIPT_recursivefree (node);
return;
}
/* This is where the real fun begins... */
/* we can begin to determine which of the 5 */
/* possibilities the node is now */
i = SCRIPT_getnexttoken (curline, token, i);
if (!strncmp (token, "0x", 2)) {
/* Found a hex digit! */
node->type = SCRIPTFLAG_HEX;
node->data.number = strtol (token, NULL, 16);
} else if (isdigit (token[0]) || token[0] == '-') {
/* Found a number! */
node->type = SCRIPTFLAG_DECIMAL;
node->data.number = atoi (token);
} else if (token[strlen(token) - 1] == 'f') {
/* Found a float */
node->type = SCRIPTFLAG_FLOAT;
node->data.floatnumber = atof(token);
} else if (token[0] == '~') {
/* Found a ... who knows */
node->type = SCRIPTFLAG_DECIMAL;
node->data.number = -1;
} else if (token[0] == '"') {
char workbuf[128];
int r;
/* Found one of possibly two strings */
strcpy (workbuf, token);
while (token != NULL && workbuf[strlen(workbuf)-1] != '"')
{
i = SCRIPT_getnexttoken (curline, token, i);
strcat (workbuf, " ");
strcat (workbuf, token);
}
r = sscanf(workbuf, "\"%[^\"]\"", workbuf);
if (r == 0) workbuf[0] = '\0';
node->type = SCRIPTFLAG_ONESTRING;
node->data.string[0] = SCRIPT_copystring (workbuf);
/* Check for a second string */
i = SCRIPT_getnexttoken (curline, token, i);
if (token[0] == '"') {
strcpy (workbuf, token);
while (token != NULL && workbuf[strlen(workbuf)-1] != '"')
{
i = SCRIPT_getnexttoken (curline, token, i);
strcat (workbuf, " ");
strcat (workbuf, token);
}
r = sscanf(workbuf, "\"%[^\"]\"", workbuf);
if (r == 0) workbuf[0] = '\0';
node->type = SCRIPTFLAG_TWOSTRING;
node->data.string[1] = SCRIPT_copystring (workbuf);
}
} else {
/* Error state! */
SCRIPT_recursivefree (node);
}
}
static scriptnode_t *SCRIPT_findinchildren (scriptnode_t *parent, char *s)
{
scriptnode_t *cur = parent;
if (cur == NULL) return NULL;
cur = cur->child;
if (cur == NULL) return NULL;
while (cur != NULL)
{
if (!strcmp(cur->key, s))
break;
cur = cur->sibling;
}
return cur;
}
/*
==============
=
= SCRIPT_Init
=
==============
*/
int32 SCRIPT_Init( char * name )
{
STUBBED("Init");
return -1;
}
/*
==============
=
= SCRIPT_Free
=
==============
*/
void SCRIPT_Free( int32 scripthandle )
{
/* STUBBED("Free"); */
if (scripthandle == -1) return;
SCRIPT_recursivefree (script_headnode[scripthandle]);
script_numscriptsopen--;
}
/*
==============
=
= SCRIPT_Parse
=
==============
*/
int32 SCRIPT_Parse ( char *data, int32 length, char * name )
{
STUBBED("Parse");
return -1;
}
/*
==============
=
= SCRIPT_Load
=
==============
*/
int32 SCRIPT_Load ( char * filename )
{
FILE *fp;
char curline[128];
scriptnode_t *headnode = NULL;
scriptnode_t *cur_subsection = NULL;
if (script_numscriptsopen == MAX_SCRIPTS) return -1;
/* The main program does not check for any sort of */
/* error in loading, so each SCRIPT_ function needs */
/* to check if the handle is -1 before doing anything */
fp = fopen (filename, "r");
if (fp == NULL) return -1;
/* Start loading the script */
/* Loads and parse the entire file into a tree */
script_numscriptsopen++;
/* script_nexthandle is the current handle until we increment it */
script_headnode[script_nexthandle] = SCRIPT_constructnode ();
headnode = script_headnode[script_nexthandle];
memset (curline, 0, 128);
while (fgets (curline, 128, fp))
{
/* Skip comments */
if (curline[0] == ';') continue;
/* Parse line */
/* We have two options... it starts with a [, making it */
/* a new subsection (child of headnode) or it starts with */
/* a letter, making it a child of a subsection. */
if (curline[0] == '[')
{
scriptnode_t *node;
int i;
/* Remove [] manually */
for (i = 0; i < 127; i++)
curline[i] = curline[i+1];
for (i = 127; i >= 0; i--)
{
if (curline[i] == ']') {
curline[i] = '\0';
break;
} else {
curline[i] = '\0';
}
}
/* Insert into head */
node = SCRIPT_constructnode ();
node->type = SCRIPTFLAG_CATEGORY;
node->key = SCRIPT_copystring (curline);
SCRIPT_addchild (headnode, node);
cur_subsection = node;
/* printf ("Working in section \"%s\"\n", node->key); */
} else if (isalpha (curline[0])) {
scriptnode_t *node;
/* Ignore if not under a subsection */
if (cur_subsection == NULL)
continue;
node = SCRIPT_constructnode ();
/* TODO: Parse line here */
SCRIPT_parseline (curline, node);
if (node != NULL)
{
/* printf ("Adding node with key \"%s\"\n", node->key); */
SCRIPT_addchild (cur_subsection, node);
}
}
memset (curline, 0, 128);
}
fclose (fp);
return script_nexthandle++; /* postincrement is important here */
}
/*
==============
=
= SCRIPT_Save
=
==============
*/
void SCRIPT_Save (int32 scripthandle, char * filename)
{
FILE *fp;
scriptnode_t *head;
if(scripthandle >= MAX_SCRIPTS || scripthandle < 0)
return;
fp = fopen (filename, "w");
if (fp == NULL) return;
head = script_headnode[scripthandle];
SCRIPT_recursivewrite (head, fp);
fclose (fp);
}
/*
==============
=
= SCRIPT_NumberSections
=
==============
*/
int32 SCRIPT_NumberSections( int32 scripthandle )
{
STUBBED("NumberSections");
return -1;
}
/*
==============
=
= SCRIPT_Section
=
==============
*/
char * SCRIPT_Section( int32 scripthandle, int32 which )
{
STUBBED("Section");
return NULL;
}
/*
==============
=
= SCRIPT_NumberEntries
=
==============
*/
int32 SCRIPT_NumberEntries( int32 scripthandle, char * sectionname )
{
scriptnode_t *node = NULL;
int32 entries = 0;
if(scripthandle >= MAX_SCRIPTS || scripthandle < 0)
return 0;
node = script_headnode[scripthandle];
node = SCRIPT_findinchildren(node, sectionname);
if(!node) return 0;
for(node=node->child; node ; node=node->sibling)
{
++entries;
}
return entries;
}
/*
==============
=
= SCRIPT_Entry
=
==============
*/
char * SCRIPT_Entry( int32 scripthandle, char * sectionname, int32 which )
{
scriptnode_t *node = NULL;
int32 entrynum = 0;
char* val = NULL;
if(scripthandle >= MAX_SCRIPTS || scripthandle < 0)
return "";
node = script_headnode[scripthandle];
node = SCRIPT_findinchildren(node,sectionname);
if(!node) return "";
for(node=node->child; node ; node=node->sibling, ++entrynum)
{
if(entrynum == which)
{
val = node->key;
break;
}
}
return val;
}
/*
==============
=
= SCRIPT_GetRaw
=
==============
*/
char * SCRIPT_GetRaw(int32 scripthandle, char * sectionname, char * entryname)
{
STUBBED("GetRaw");
return NULL;
}
/*
==============
=
= SCRIPT_GetString
=
==============
*/
void SCRIPT_GetString
(
int32 scripthandle,
char * sectionname,
char * entryname,
char * dest
)
{
scriptnode_t *cur;
/* STUBBED("GetString"); */
if (scripthandle == -1) return;
cur = script_headnode[scripthandle];
cur = SCRIPT_findinchildren (cur, sectionname);
cur = SCRIPT_findinchildren (cur, entryname);
if (cur != NULL && cur->type == SCRIPTFLAG_ONESTRING)
{
strcpy (dest, cur->data.string[0]);
#ifdef DEBUG_SCRIPLIB
printf ("GetString: value for %s:%s is %s\n", sectionname, entryname, dest);
#endif
}
}
/*
==============
=
= SCRIPT_GetDoubleString
=
==============
*/
void SCRIPT_GetDoubleString
(
int32 scripthandle,
char * sectionname,
char * entryname,
char * dest1,
char * dest2
)
{
scriptnode_t *cur;
/* STUBBED("GetDoubleString"); */
if (scripthandle == -1) return;
cur = script_headnode[scripthandle];
cur = SCRIPT_findinchildren (cur, sectionname);
cur = SCRIPT_findinchildren (cur, entryname);
if (cur != NULL && cur->type == SCRIPTFLAG_TWOSTRING)
{
strcpy (dest1, cur->data.string[0]);
strcpy (dest2, cur->data.string[1]);
#ifdef DEBUG_SCRIPLIB
printf ("GetDoubleString: value for %s:%s is %s %s\n", sectionname, entryname, dest1, dest2);
#endif
}
}
/*
==============
=
= SCRIPT_GetNumber
=
==============
*/
boolean SCRIPT_GetNumber
(
int32 scripthandle,
char * sectionname,
char * entryname,
int32 * number
)
{
scriptnode_t *cur;
if (scripthandle == -1) return false;
cur = script_headnode[scripthandle];
cur = SCRIPT_findinchildren (cur, sectionname);
cur = SCRIPT_findinchildren (cur, entryname);
if (cur != NULL && cur->type == SCRIPTFLAG_DECIMAL)
{
*number = cur->data.number;
#ifdef DEBUG_SCRIPLIB
printf ("GetNumber: value for %s:%s is %ld\n", sectionname, entryname, *number);
#endif
}
return (cur != NULL) ? true : false;
}
/*
==============
=
= SCRIPT_GetBoolean
=
==============
*/
void SCRIPT_GetBoolean
(
int32 scripthandle,
char * sectionname,
char * entryname,
boolean * bool
)
{
STUBBED("GetBoolean");
}
/*
==============
=
= SCRIPT_GetFloat
=
==============
*/
boolean SCRIPT_GetFloat
(
int32 scripthandle,
char * sectionname,
char * entryname,
float * floatnumber
)
{
scriptnode_t *cur;
if (scripthandle == -1) return false;
cur = script_headnode[scripthandle];
cur = SCRIPT_findinchildren (cur, sectionname);
cur = SCRIPT_findinchildren (cur, entryname);
if (cur != NULL && cur->type == SCRIPTFLAG_FLOAT)
{
*floatnumber = cur->data.floatnumber;
#ifdef DEBUG_SCRIPLIB
printf ("GetFloat: value for %s:%s is %f\n", sectionname, entryname, *floatnumber);
#endif
}
return (cur != NULL) ? true : false;
}
/*
+==============
+=
= SCRIPT_GetDouble
=
==============
*/
void SCRIPT_GetDouble
(
int32 scripthandle,
char * sectionname,
char * entryname,
double * number
)
{
STUBBED("GetDouble");
}
/*
==============
=
= SCRIPT_PutComment
=
==============
*/
void SCRIPT_PutComment( int32 scripthandle, char * sectionname, char * comment )
{
STUBBED("PutComment");
}
/*
==============
=
= SCRIPT_PutEOL
=
==============
*/
void SCRIPT_PutEOL( int32 scripthandle, char * sectionname )
{
STUBBED("PutEOL");
}
/*
==============
=
= SCRIPT_PutMultiComment
=
==============
*/
void SCRIPT_PutMultiComment
(
int32 scripthandle,
char * sectionname,
char * comment,
...
)
{
STUBBED("PutMultiComment");
}
/*
==============
=
= SCRIPT_PutSection
=
==============
*/
void SCRIPT_PutSection( int32 scripthandle, char * sectionname )
{
STUBBED("PutSection");
}
/*
==============
=
= SCRIPT_PutRaw
=
==============
*/
void SCRIPT_PutRaw
(
int32 scripthandle,
char * sectionname,
char * entryname,
char * raw
)
{
STUBBED("PutRaw");
}
/*
==============
=
= SCRIPT_PutString
=
==============
*/
void SCRIPT_PutString
(
int32 scripthandle,
char * sectionname,
char * entryname,
char * string
)
{
scriptnode_t *head;
scriptnode_t *section;
scriptnode_t *node;
if(scripthandle >= MAX_SCRIPTS || scripthandle < 0)
return;
head = script_headnode[scripthandle];
/* We're screwed if there's no head */
if (head == NULL) return;
section = SCRIPT_findinchildren (head, sectionname);
if (section == NULL)
{
/* Add the section if it does not exist */
section = SCRIPT_constructnode ();
section->type = SCRIPTFLAG_CATEGORY;
section->key = SCRIPT_copystring (sectionname);
SCRIPT_addchild (head, section);
}
node = SCRIPT_findinchildren (section, entryname);
if (node == NULL)
{
/* Add the section if it does not exist */
node = SCRIPT_constructnode ();
node->type = SCRIPTFLAG_ONESTRING;
node->key = SCRIPT_copystring (entryname);
SCRIPT_addchild (section, node);
} else {
free (node->data.string[0]);
}
node->data.string[0] = SCRIPT_copystring (string);
}
/*
==============
=
= SCRIPT_PutDoubleString
=
==============
*/
void SCRIPT_PutDoubleString
(
int32 scripthandle,
char * sectionname,
char * entryname,
char * string1,
char * string2
)
{
STUBBED("PutDoubleString");
}
/*
==============
=
= SCRIPT_PutNumber
=
==============
*/
void SCRIPT_PutNumber
(
int32 scripthandle,
char * sectionname,
char * entryname,
int32 number,
boolean hexadecimal,
boolean defaultvalue
)
{
/* DDOI - I don't know what "defaultvalue" is for so it's ignored */
scriptnode_t *head;
scriptnode_t *section;
scriptnode_t *node;
if(scripthandle >= MAX_SCRIPTS || scripthandle < 0)
return;
head = script_headnode[scripthandle];
/* We're screwed if there's no head */
if (head == NULL) return;
section = SCRIPT_findinchildren (head, sectionname);
if (section == NULL)
{
/* Add the section if it does not exist */
section = SCRIPT_constructnode ();
section->type = SCRIPTFLAG_CATEGORY;
section->key = SCRIPT_copystring (sectionname);
SCRIPT_addchild (head, section);
}
node = SCRIPT_findinchildren (section, entryname);
if (node == NULL)
{
/* Add the section if it does not exist */
node = SCRIPT_constructnode ();
node->key = SCRIPT_copystring (entryname);
SCRIPT_addchild (section, node);
}
if (hexadecimal)
node->type = SCRIPTFLAG_HEX;
else
node->type = SCRIPTFLAG_DECIMAL;
node->data.number = number;
}
/*
==============
=
= SCRIPT_PutBoolean
=
==============
*/
void SCRIPT_PutBoolean
(
int32 scripthandle,
char * sectionname,
char * entryname,
boolean bool
)
{
STUBBED("PutBoolean");
}
/*
==============
=
= SCRIPT_PutDouble
=
==============
*/
void SCRIPT_PutDouble
(
int32 scripthandle,
char * sectionname,
char * entryname,
double number,
boolean defaultvalue
)
{
STUBBED("PutDouble");
}