view src/game.cc @ 79:0602d9bf474a

Various cleanups.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 26 Sep 2011 17:14:54 +0300
parents 0afa189be30d
children 76ad709ed3c6
line wrap: on
line source

/*
 *        game.cc
 *        Load .ygd file (Yadex Game Definitions)
 *        AYM 1998-01-04
 */


/*
This file is part of Yadex.

Yadex incorporates code from DEU 5.21 that was put in the public domain in
1994 by Raphaël Quinet and Brendon Wyber.

The rest of Yadex is Copyright © 1997-2003 André Majorel and others.

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 the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307, USA.
*/


#include "yadex.h"
#include "acolours.h"
#include "game.h"
#include "gamesky.h"
#include "locate.h"
#include "things.h"


const char ygd_file_magic[] = "# Yadex game definition file version 4";


/*
 *        InitGameDefs
 *        Create empty lists for game definitions
 */
void InitGameDefs(void)
{
    ldtdef = al_lcreate(sizeof(ldtdef_t));
    ldtgroup = al_lcreate(sizeof(ldtgroup_t));
    stdef = al_lcreate(sizeof(stdef_t));
    thingdef = al_lcreate(sizeof(thingdef_t));
    thinggroup = al_lcreate(sizeof(thinggroup_t));
}


/*
 *        LoadGameDefs
 *        Looks for file <game>.ygd in various directories and reads it.
 *        Builds list ThingsDefs.
 *        A totally boring piece of code.
 */
void LoadGameDefs(const char *game)
{
    FILE *ygdfile = 0;                /* YGD file descriptor */
#define YGD_BUF 200                /* max. line length + 2 */
    char readbuf[YGD_BUF];        /* buffer the line is read into */
#define MAX_TOKENS 10                /* tokens per line */
    int lineno;                        /* current line of file */
    char filename[1025];
    char basename[256];

    al_scps(basename, game, sizeof basename - 1);
    al_saps(basename, ".ygd", sizeof basename - 1);

/* Locate the game definition file. */
    {
        Locate locate(yadex_share_path, basename, false);
        const char *pathname = locate.get_next();
        if (pathname == NULL)
            fatal_error("Game definition file \"%s\" not found", basename);
        if (strlen(pathname) > sizeof filename - 1)
            fatal_error("%s: file name too long");
        strcpy(filename, pathname);
        printf("Reading game definition file \"%s\".\n", filename);
    }

    ygdfile = fopen(filename, "r");
    if (ygdfile == NULL)
        fatal_error("%s: %s", filename, strerror(errno));

/* The first line of the ygd file must
   contain exactly ygd_file_magic. */
    if (fgets(readbuf, sizeof readbuf, ygdfile) == NULL
        || memcmp(readbuf, ygd_file_magic, sizeof ygd_file_magic - 1)
        || readbuf[sizeof ygd_file_magic - 1] != '\n'
        || readbuf[sizeof ygd_file_magic] != '\0')
    {
        err("%s is not a valid Yadex game definition file", filename);
        fatal_error("Perhaps a leftover from a previous version of Yadex ?");
    }

/* Read the game definition
   file, line by line. */
    for (lineno = 2; fgets(readbuf, sizeof readbuf, ygdfile); lineno++)
    {
        int ntoks;
        char *token[MAX_TOKENS];
        int quoted;
        int in_token;
        const char *iptr;
        char *optr;
        char *buf;
        const char *const bad_arg_count =
            "%s(%d): directive \"%s\" takes %d parameters";

        /* duplicate the buffer */
        buf = (char *) malloc(strlen(readbuf) + 1);
        if (!buf)
            fatal_error("not enough memory");

        /* break the line into whitespace-separated tokens.
           whitespace can be enclosed in double quotes. */
        for (in_token = 0, quoted = 0, iptr = readbuf, optr = buf, ntoks = 0;;
             iptr++)
        {
            if (*iptr == '\n' || *iptr == '\0')
            {
                if (in_token)
                    *optr = '\0';
                break;
            }

            else if (*iptr == '"')
                quoted ^= 1;

            // "#" at the beginning of a token
            else if (!in_token && !quoted && *iptr == '#')
                break;

            // First character of token
            else if (!in_token && (quoted || !isspace(*iptr)))
            {
                if (ntoks >= (int) (sizeof token / sizeof *token))
                    fatal_error("%s(%d): more than %d tokens",
                                filename, lineno,
                                sizeof token / sizeof *token);
                token[ntoks] = optr;
                ntoks++;
                in_token = 1;
                *optr++ = *iptr;
            }

            // First space between two tokens
            else if (in_token && !quoted && isspace(*iptr))
            {
                *optr++ = '\0';
                in_token = 0;
            }

            // Character in the middle of a token
            else if (in_token)
                *optr++ = *iptr;
        }
        if (quoted)
            fatal_error("%s(%d): unmatched double quote", filename, lineno);

        /* process line */
        if (ntoks == 0)
        {
            free(buf);
            continue;
        }
        if (!strcmp(token[0], "ldt"))
        {
            ldtdef_t buf;

            if (ntoks != 5)
                fatal_error(bad_arg_count, filename, lineno, token[0], 4);
            buf.number = atoi(token[1]);
            buf.ldtgroup = *token[2];
            buf.shortdesc = token[3];        /* FIXME: trunc to 16 char. */
            buf.longdesc = token[4];        /* FIXME: trunc reasonably */
            if (al_lwrite(ldtdef, &buf))
                fatal_error("LGD1 (%s)", al_astrerror(al_aerrno));
        }
        else if (!strcmp(token[0], "ldtgroup"))
        {
            ldtgroup_t buf;

            if (ntoks != 3)
                fatal_error(bad_arg_count, filename, lineno, token[0], 2);
            buf.ldtgroup = *token[1];
            buf.desc = token[2];
            if (al_lwrite(ldtgroup, &buf))
                fatal_error("LGD2 (%s)", al_astrerror(al_aerrno));
        }
        else if (!strcmp(token[0], "level_format"))
        {
            if (ntoks != 2)
                fatal_error(bad_arg_count, filename, lineno, token[0], 1);
            if (!strcmp(token[1], "alpha"))
                yg_level_format = YGLF_ALPHA;
            else if (!strcmp(token[1], "doom"))
                yg_level_format = YGLF_DOOM;
            else if (!strcmp(token[1], "hexen"))
                yg_level_format = YGLF_HEXEN;
            else
                fatal_error
                    ("%s(%d): invalid argument \"%.32s\" (alpha|doom|hexen)",
                     filename, lineno, token[1]);
            free(buf);
        }
        else if (!strcmp(token[0], "level_name"))
        {
            if (ntoks != 2)
                fatal_error(bad_arg_count, filename, lineno, token[0], 1);
            if (!strcmp(token[1], "e1m1"))
                yg_level_name = YGLN_E1M1;
            else if (!strcmp(token[1], "e1m10"))
                yg_level_name = YGLN_E1M10;
            else if (!strcmp(token[1], "map01"))
                yg_level_name = YGLN_MAP01;
            else
                fatal_error
                    ("%s(%d): invalid argument \"%.32s\" (e1m1|e1m10|map01)",
                     filename, lineno, token[1]);
            free(buf);
        }
        else if (!strcmp(token[0], "picture_format"))
        {
            if (ntoks != 2)
                fatal_error(bad_arg_count, filename, lineno, token[0], 1);
            if (!strcmp(token[1], "alpha"))
                yg_picture_format = YGPF_ALPHA;
            else if (!strcmp(token[1], "pr"))
                yg_picture_format = YGPF_PR;
            else if (!strcmp(token[1], "normal"))
                yg_picture_format = YGPF_NORMAL;
            else
                fatal_error
                    ("%s(%d): invalid argument \"%.32s\" (alpha|pr|normal)",
                     filename, lineno, token[1]);
            free(buf);
        }
        else if (!strcmp(token[0], "sky_flat"))
        {
            if (ntoks != 2)
                fatal_error(bad_arg_count, filename, lineno, token[0], 1);
            sky_flat = token[1];
        }
        else if (!strcmp(token[0], "st"))
        {
            stdef_t buf;

            if (ntoks != 4)
                fatal_error(bad_arg_count, filename, lineno, token[0], 3);
            buf.number = atoi(token[1]);
            buf.shortdesc = token[2];        /* FIXME: trunc to 14 char. */
            buf.longdesc = token[3];        /* FIXME: trunc reasonably */
            if (al_lwrite(stdef, &buf))
                fatal_error("LGD3 (%s)", al_astrerror(al_aerrno));
        }
        else if (!strcmp(token[0], "texture_format"))
        {
            if (ntoks != 2)
                fatal_error(bad_arg_count, filename, lineno, token[0], 1);
            if (!strcmp(token[1], "nameless"))
                yg_texture_format = YGTF_NAMELESS;
            else if (!strcmp(token[1], "normal"))
                yg_texture_format = YGTF_NORMAL;
            else if (!strcmp(token[1], "strife11"))
                yg_texture_format = YGTF_STRIFE11;
            else
                fatal_error
                    ("%s(%d): invalid argument \"%.32s\" (normal|nameless|strife11)",
                     filename, lineno, token[1]);
            free(buf);
        }
        else if (!strcmp(token[0], "texture_lumps"))
        {
            if (ntoks != 2)
                fatal_error(bad_arg_count, filename, lineno, token[0], 1);
            if (!strcmp(token[1], "textures"))
                yg_texture_lumps = YGTL_TEXTURES;
            else if (!strcmp(token[1], "normal"))
                yg_texture_lumps = YGTL_NORMAL;
            else if (!strcmp(token[1], "none"))
                yg_texture_lumps = YGTL_NONE;
            else
                fatal_error
                    ("%s(%d): invalid argument \"%.32s\" (normal|textures|none)",
                     filename, lineno, token[1]);
            free(buf);
        }
        else if (!strcmp(token[0], "thing"))
        {
            thingdef_t buf;

            if (ntoks < 6 || ntoks > 7)
                fatal_error
                    ("%s(d%): directive \"%s\" takes between 5 and 6 parameters",
                     filename, lineno, token[0]);
            buf.number = atoi(token[1]);
            buf.thinggroup = *token[2];
            buf.flags = *token[3] == 's' ? THINGDEF_SPECTRAL : 0;        // FIXME!
            buf.radius = atoi(token[4]);
            buf.desc = token[5];
            buf.sprite = ntoks >= 7 ? token[6] : 0;
            if (al_lwrite(thingdef, &buf))
                fatal_error("LGD4 (%s)", al_astrerror(al_aerrno));
        }
        else if (!strcmp(token[0], "thinggroup"))
        {
            thinggroup_t buf;

            if (ntoks != 4)
                fatal_error(bad_arg_count, filename, lineno, token[0], 3);
            buf.thinggroup = *token[1];
            if (getcolour(token[2], &buf.rgb))
                fatal_error("%s(%d): bad colour spec \"%.32s\"",
                            filename, lineno, token[2]);
            buf.acn = add_app_colour(buf.rgb);
            buf.desc = token[3];
            if (al_lwrite(thinggroup, &buf))
                fatal_error("LGD5 (%s)", al_astrerror(al_aerrno));
        }
        else
        {
            free(buf);
            fatal_error("%s(%d): unknown directive \"%.32s\"",
                        filename, lineno, token[0]);
        }
    }

    fclose(ygdfile);

/* Verify that all the mandatory directives are present. */
    {
        bool abort = false;
        if (yg_level_format == YGLF__)
        {
            err("%s: Missing \"level_format\" directive.", filename);
            abort = true;
        }
        if (yg_level_name == YGLN__)
        {
            err("%s: Missing \"level_name\" directive.", filename);
            abort = true;
        }
// FIXME perhaps print a warning message if picture_format
// is missing ("assuming picture_format=normal").
// FIXME and same thing for texture_format and texture_lumps ?
        if (abort)
            exit(2);
    }

/*
 *        Second pass
 */

/* Speed optimization : build the table of things attributes
   that get_thing_*() use. */
    create_things_table();

/* KLUDGE: Add bogus ldtgroup LDT_FREE. InputLinedefType()
   knows that it means "let the user enter a number". */
    {
        ldtgroup_t buf;

        buf.ldtgroup = LDT_FREE;        /* that is '\0' */
        buf.desc = "Other (enter number)";
        al_lseek(ldtgroup, 0, SEEK_END);
        if (al_lwrite(ldtgroup, &buf))
            fatal_error("LGD90 (%s)", al_astrerror(al_aerrno));
    }

/* KLUDGE: Add bogus thinggroup THING_FREE.
   InputThingType() knows that it means "let the user enter a number". */
    {
        thinggroup_t buf;

        buf.thinggroup = THING_FREE;        /* that is '\0' */
        buf.desc = "Other (enter number)";
        al_lseek(thinggroup, 0, SEEK_END);
        if (al_lwrite(thinggroup, &buf))
            fatal_error("LGD91 (%s)", al_astrerror(al_aerrno));
    }

/* KLUDGE: Add bogus sector type at the end of stdef.
   SectorProperties() knows that it means "let the user enter a number". */
    {
        stdef_t buf;

        buf.number = 0;                /* not significant */
        buf.shortdesc = 0;        /* not significant */
        buf.longdesc = "Other (enter number)";
        al_lseek(stdef, 0, SEEK_END);
        if (al_lwrite(stdef, &buf))
            fatal_error("LGD92 (%s)", al_astrerror(al_aerrno));
    }

}


/*
 *        FreeGameDefs
 *        Free all memory allocated to game definitions
 */
void FreeGameDefs(void)
{
    delete_things_table();
    al_ldiscard(ldtdef);
    al_ldiscard(ldtgroup);
    al_ldiscard(stdef);
    al_ldiscard(thingdef);
    al_ldiscard(thinggroup);
}