view src/patchdir.cc @ 109:f05330267c66

Use stdint types.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 06 Oct 2014 12:59:23 +0300
parents a68786b9c74b
children
line wrap: on
line source

/*
 *        patchdir.cc
 *        Patch_dir class
 *        AYM 1999-11-25
 */


/*
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 "patchdir.h"
#include "wadfile.h"
#include "wads.h"


/* This class has only one instance, global and shared by
   everyone. I don't think there would ever be a need for
   multiple instances. Unless you add the capability to edit
   levels from several different iwads simultaneously. But then,
   how do you decide which windows are affected by loading a
   pwad ? -- AYM 1999-11-27 */
Patch_dir patch_dir;


/*
 *        ctor
 */
Patch_dir::Patch_dir()
{
    pnames = 0;
    npnames = 0;
}


/*
 *        dtor
 */
Patch_dir::~Patch_dir()
{
    if (pnames != 0)
        FreeMemory(pnames);
    if (!patch_lumps.empty())
        patch_lumps.clear();
}


/*
 *        Patch_dir::refresh
 */
void Patch_dir::refresh(MDirPtr master_dir)
{
    /* refresh() can be called more than once on the same object.
       And usually is ! */
    if (pnames != 0)
    {
        FreeMemory(pnames);
        pnames = 0;
        npnames = 0;
    }
    if (!patch_lumps.empty())
        patch_lumps.clear();

    /* First load PNAMES so that we known in which order we should
       put the patches in the array. */
    {
        bool success = false;
        const char *lump_name = "PNAMES";
        do
        {
            MDirPtr dir;
            int32_t npatches_head = -42;
            int32_t npatches_body = -42;

            dir = FindMasterDir(master_dir, lump_name);
            if (dir == 0)
            {
                warn("No %s lump, won't be able to render textures\n",
                     lump_name);
                break;
            }
            int32_t pnames_body_size = dir->dir.size - 4;
            if (pnames_body_size < 0)
            {
                warn("Bad %s (negative size %ld), won't be able to render textures\n", lump_name, (long) dir->dir.size);
                break;
            }
            if (pnames_body_size % 8)
            {
                warn("%s lump has weird size %ld, discarding last %d bytes\n",
                     (long) dir->dir.size, (int) (pnames_body_size % 8));
            }
            npatches_body = pnames_body_size / 8;
            const Wad_file *wf = dir->wadfile;
            wf->seek(dir->dir.start);
            if (wf->error())
            {
                warn("%s: seek error\n", lump_name);
                break;
            }
            wf->read_i32(&npatches_head);
            if (wf->error())
            {
                warn("%s: error reading header\n", lump_name);
                break;
            }
            if (npatches_head > 0 && npatches_head < npatches_body)
                npnames = npatches_head;
            else
                npnames = npatches_body;
            if (npatches_head != npatches_body)
            {
                warn("%s: header says %ld patches, lump size suggests %ld,"
                     " going for %lu\n",
                     lump_name, long (npatches_head), long (npatches_body),
                     (unsigned long) npnames);
            }
            if (npnames > 32767)
            {
                warn("%s: too big (%lu patches), keeping only first 32767\n",
                     lump_name, (unsigned long) npnames);
                npnames = 32767;
            }
            pnames = (char *) GetMemory(npnames * WAD_PIC_NAME);
            wf->read_bytes(pnames, npnames * WAD_PIC_NAME);
            if (wf->error())
            {
                warn("%s: error reading names\n", lump_name);
                break;
            }
            success = true;
        }
        while (0);
        if (!success)
            warn("%s: errors found, won't be able to render textures\n",
                 lump_name);
    }

    /* Get list of patches in the master directory. Everything
       that is between P_START/P_END or PP_START/PP_END and that
       is not a label is supposed to be a patch. */
    {
        for (MDirPtr dir = master_dir;
             dir && (dir = FindMasterDir(dir, "P_START", "PP_START"));)
        {
            MDirPtr start_label = dir;
            const char *end_label = 0;
            if (!y_strnicmp(dir->dir.name, "P_START", WAD_NAME))
                end_label = "P_END";
            else if (!y_strnicmp(dir->dir.name, "PP_START", WAD_NAME))
                end_label = "PP_END";
            else
                fatal_error("Bad start label \"%.*s\"", (int) WAD_NAME,
                            dir->dir.name);
            //printf ("[%-8.8s ", dir->dir.name);  // DEBUG
            //fflush (stdout);
            dir = dir->next;
            for (;; dir = dir->next)
            {
                if (!dir)
                {
                    warn("%.128s: no matching %s for %.*s\n",
                         start_label->wadfile->pathname(),
                         end_label, (int) WAD_NAME, start_label->dir.name);
                    break;
                }
                if (!y_strnicmp(dir->dir.name, end_label, WAD_NAME))
                {
                    if (dir->dir.size != 0)
                        warn("%.128s: label %.*s has non-zero size %ld\n",
                             dir->wadfile->pathname(),
                             (int) WAD_NAME, dir->dir.name,
                             (long) dir->dir.size);
                    dir = dir->next;
                    //printf ("%-8.8s]\n", dir->dir.name);  // DEBUG
                    break;
                }
                if (dir->dir.start == 0 || dir->dir.size == 0)
                {
                    if (!(toupper(dir->dir.name[0]) == 'P'
                          && (dir->dir.name[1] == '1'
                              || dir->dir.name[1] == '2'
                              || dir->dir.name[1] == '3')
                          && dir->dir.name[2] == '_'
                          &&
                          (!y_strnicmp
                           (dir->dir.name + 3, "START", WAD_NAME - 3)
                           || !y_strnicmp(dir->dir.name + 3, "END",
                                          WAD_NAME - 3))))
                        warn("%.128s: unexpected label \"%.*s\" among patches.\n", dir->wadfile->pathname(), (int) WAD_NAME, dir->dir.name);
                    continue;
                }
                //printf ("%-9.8s ", dir->dir.name); fflush (stdout);  // DEBUG
                wad_flat_name_t name;
                memcpy(name, dir->dir.name, sizeof name);
                patch_lumps[name]
                    = Lump_loc(dir->wadfile, dir->dir.start, dir->dir.size);
            }
            if (dir)
                dir = dir->next;
        }
        //putchar ('\n');  // DEBUG
    }
#ifdef DEBUG
    for (Patch_lumps_map::const_iterator i = patch_lumps.begin();
         i != patch_lumps.end(); i++)
    {
        printf("%-8.8s %p %08lX %ld\n",
               i->first._name, i->second.wad, i->second.ofs, i->second.len);
    }
#endif
}


/*
 *        loc_by_name
 *        Return the (wad, offset, length) location of the lump
 *        that contains patch <name>.
 */
void Patch_dir::loc_by_name(const char *name, Lump_loc & loc)
{
    Patch_lumps_map::const_iterator i = patch_lumps.find(name);
    if (i == patch_lumps.end())
    {
        loc.wad = 0;
        return;
    }
    loc = i->second;
}


/*
 *        loc_by_num
 *        Return the (wad, offset, length) location of the lump
 *        that contains patch# <num>.
 */
void Patch_dir::loc_by_num(int16_t num, Lump_loc & loc)
{
    wad_pic_name_t *nm = name_for_num(num);
    if (nm == 0)
    {
        loc.wad = 0;
        return;
    }
    loc_by_name((const char *) nm, loc);
}


/*
 *        name_for_num
 *        Return a pointer on the name of the patch of number <num>
 *        or 0 if no such patch.
 */
wad_pic_name_t *Patch_dir::name_for_num(int16_t num)
{
    if (num < 0 || (size_t) num >= npnames)        // Cast to silence GCC warning
        return 0;
    return (wad_pic_name_t *) (pnames + WAD_PIC_NAME * num);
}


/*
 *        list
 *        Put a list of all existing patch lump, sorted by name
 *        and without duplicates, in <pl>.
 */
void Patch_dir::list(Patch_list & pl)
{
    pl.set(patch_lumps);
}


/*-------------------------- Patch_list --------------------------*/


Patch_list::Patch_list()
{
    array = 0;
    nelements = 0;
}


Patch_list::~Patch_list()
{
    clear();
}


void Patch_list::set(Patch_lumps_map & patch_lumps)
{
    clear();
    nelements = patch_lumps.size();
    array = new char *[nelements];

    Patch_lumps_map::const_iterator i = patch_lumps.begin();
    for (size_t n = 0; n < nelements; n++)
    {
        array[n] = new char[WAD_PIC_NAME + 1];
        *array[n] = '\0';
        strncat(array[n], i++->first._name, WAD_PIC_NAME);
    }
}


void Patch_list::clear()
{
    if (array != 0)
    {
        for (size_t n = 0; n < nelements; n++)
            delete[]array[n];
        delete[]array;
    }
}


const char **Patch_list::data()
{
    return (const char **) array;
}


size_t Patch_list::size()
{
    return nelements;
}


/*---------------------------- Pllik -----------------------------*/


Pllik::Pllik(const char *name)
{
    memcpy(_name, name, sizeof _name);
}


/*-------------------------- Pllik_less --------------------------*/


bool Pllik_less::operator () (const Pllik & p1, const Pllik & p2)
     const
     {
         return y_strnicmp((const char *) &p1, (const char *) &p2,
                           WAD_PIC_NAME) < 0;
     }