/*
 *  PSX-Tools Bundle Pack
 *  Copyright (C) 2002-2003 Nicolas "Pixel" Noble
 *
 *  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
 */

/* $Id: isobuilder.cpp,v 1.20 2006-07-17 16:42:10 pixel Exp $ */

#include "isobuilder.h"

void isobuilder::Date::dump(Byte * data) {
    char pbuf[256];

    sprintf(pbuf, "%04i", year);
    memcpy(data +  0, pbuf, 4);
    sprintf(pbuf, "%02i", month);
    memcpy(data +  4, pbuf, 2);
    sprintf(pbuf, "%02i", day);
    memcpy(data +  6, pbuf, 2);
    sprintf(pbuf, "%02i", hour);
    memcpy(data +  8, pbuf, 2);
    sprintf(pbuf, "%02i", minute);
    memcpy(data + 10, pbuf, 2);
    sprintf(pbuf, "%02i", second);
    memcpy(data + 12, pbuf, 2);
    sprintf(pbuf, "%02i", hundredths);
    memcpy(data + 14, pbuf, 2);

    *((char *) (data + 16)) = offset;
}

isobuilder::Date::Date(int) {
    year = month = day = hour = minute = second = hundredths = offset = 0;
}

isobuilder::Date::Date(Byte * data) {
    char pbuf[256];
    char * cdata = (char *) data;

    memcpy(pbuf, cdata +  0, 4);
    pbuf[4] = 0;
    sscanf(pbuf, "%d", &year);

    memcpy(pbuf, cdata +  4, 2);
    pbuf[2] = 0;
    sscanf(pbuf, "%d", &month);

    memcpy(pbuf, cdata +  6, 2);
    pbuf[2] = 0;
    sscanf(pbuf, "%d", &day);

    memcpy(pbuf, cdata +  8, 2);
    pbuf[2] = 0;
    sscanf(pbuf, "%d", &hour);

    memcpy(pbuf, cdata + 10, 2);
    pbuf[2] = 0;
    sscanf(pbuf, "%d", &minute);

    memcpy(pbuf, cdata + 12, 2);
    pbuf[2] = 0;
    sscanf(pbuf, "%d", &second);

    memcpy(pbuf, cdata + 14, 2);
    pbuf[2] = 0;
    sscanf(pbuf, "%d", &hundredths);

    offset = *(cdata + 16);
}

isobuilder::DirTree::DirTree(isobuilder::DirTree * _father, bool _dir) : mode(-1), father(_father), dir(_dir) {
    DirTree * p;

    child = brother = 0;

    if (!father)
        return;

    creation = father->creation;

    if (father->child) {
        for (p = father->child; p->brother; p = p->brother);
        p->brother = this;
    } else {
        father->child = this;
    }
}

isobuilder::DirTree::~DirTree() {
    while (child) {
        delete child;
    }

    if (!father)
        return;

    if (father->child == this) {
        father->child = brother;
    } else {
        // Dirty, should not happen.
        DirTree * p;
        for (p = father->child; p->brother != this; p = p->brother);
        p->brother = brother;
    }
}

void isobuilder::DirTree::dumpdirs(isobuilder * builder) throw (GeneralException) {
    Byte * dir, * odir;
    int cursiz, cursectsize, R;
    String oldname;
    DirTree * p;

    odir = dir = (Byte *) malloc(cursiz = size);
    cursectsize = 2048;

    oldname = name;
    name = ".";
    R = buildentry(dir, cursectsize);
    name = oldname;
    cursectsize -= R;
    dir += R;

    if (father) {
        oldname = father->name;
        father->name = "..";
        R = father->buildentry(dir, cursectsize);
        father->name = oldname;
    } else {
        name = "..";
        R = buildentry(dir, cursectsize);
        name = ".";
    }

    cursectsize -= R;
    dir += R;

    for (p = child; p; p = p->brother) {
        if (p->dir) {
            p->dumpdirs(builder);
        }
        if (p->hardhide)
            continue;
        while (!(R = p->buildentry(dir, cursectsize))) {
            cursiz -= 2048;
            dir += cursectsize;
            cursectsize = 2048;
            if (!cursiz)
                throw GeneralException("Directory is too small! Entries don't fit.");
        }
        cursectsize -= R;
        dir += R;
    }

    builder->putdata(odir, size, mode, sector);

    free(odir);
}

int isobuilder::DirTree::maxlevel(int current_level) {
    int m_level_c = 0, m_level_b = 0;
    if (dir) {
        if (child) {
            m_level_c = child->maxlevel(current_level + 1);
        }
    }

    if (brother) {
        m_level_b = brother->maxlevel(current_level);
    }

    return MAX(current_level, MAX(m_level_c, m_level_b));
}

int isobuilder::DirTree::buildpath(Byte * data, int size, bool bigendian) {
    int maxlv = maxlevel(), i, R, cumul = 0;

    for (i = 1; i < maxlv; i++) {
        R = buildpath_r(data, size, 1, i, bigendian);
        data += R;
        cumul += R;
    }

    return cumul;
}

int isobuilder::DirTree::buildpath_r(Byte * data, int size, int current_level, int level_to_dump, bool bigendian) throw (GeneralException) {
    int N, r = 0, tr;
    Uint16 pn;
    char pbuf[256], pad;

    if (!dir) {
        if (brother) {
            return brother->buildpath_r(data, size, current_level, level_to_dump, bigendian);
        } else {
            return 0;
        }
    }

    if (current_level == level_to_dump) {
        if (!father) {
            numerate(1);
            N = 1;
            pbuf[0] = 0;
            pn = 1;
        } else {
            N = name.strlen();
            strcpy(pbuf, name.to_charp());
            pn = father->node;
        }

        pad = N & 1;
        size -= (r = N + pad + 8);

        if (size < 0)
            throw GeneralException("Path table too small.");

        data[0] = N;
        data[1] = 0;
        *((Uint32 *) (data + 2)) = bigendian ? cdutils::to_BE32(sector) : cdutils::to_LE32(sector);
        *((Uint16 *) (data + 6)) = bigendian ? cdutils::to_BE16(pn) : cdutils::to_LE16(pn);
        memcpy(data + 8, pbuf, N);
        if (pad)
            data[8 + N] = 0;

        data += r;
    }

    if (child) {
        tr = child->buildpath_r(data, size, current_level + 1, level_to_dump, bigendian);
        r += tr;
        size -= tr;
        data += tr;
    }

    if (brother) {
        tr = brother->buildpath_r(data, size, current_level, level_to_dump, bigendian);
        r += tr;
        size -= tr;
        data += tr;
    }

    return r;
}

int isobuilder::DirTree::buildentry(Byte * buffer, int spaceleft, bool put_xa) {
    int N, R;
    char pbuf[256], pad;
    Byte * p;
    cdutils::DirEntry * d = (cdutils::DirEntry *) buffer;

    put_xa = put_xa && have_xa;

    memset(pbuf, 0, 256);

    if (name == "BM2.ELF") {
        printm(M_INFO, "GRON");
    }

    if (name == ".") {
        N = 1;
        pbuf[0] = 0;
    } else if (name == "..") {
        N = 1;
        pbuf[0] = 1;
    } else {
        strcpy(pbuf, name.to_charp());
        N = name.strlen();
        if (!dir) {
            N += 2;
            strcat(pbuf, ";1");
        }
    }

    R = N + 33;

    if (R & 1) {
        R++;
        pad = 1;
    } else {
        pad = 0;
    }

    if (put_xa) {
        R += 14;
        p = (Byte *) pbuf + N + pad;
        p[4] = 0x05;
        p[5] = 0x55;
        p[6] = 'X';
        p[7] = 'A';

        p[4] |= xa_dir   ? 0x80 : 0;
        p[4] |= xa_audio ? 0x40 : 0;
        p[4] |= xa_str   ? 0x20 : 0;
        p[4] |= xa_xa    ? 0x10 : 0;
        p[4] |= xa_form1 ? 0x08 : 0;
    }

    if (R > spaceleft) {
        return 0;
    }

    memset(d, 0, R);

    d->R = R;
    d->N = N;
    memcpy(d->id, pbuf, N + pad + (put_xa ? 14 : 0));

    d->Sector = cdutils::to_LE32(sector);
    d->BESector = cdutils::to_BE32(sector);
    d->Size = cdutils::to_LE32(size);
    d->BESize = cdutils::to_BE32(size);
    if (creation.year >= 1000) {
	d->Year = creation.year - 1900;
    } else {
	d->Year = creation.year;
    }
    d->Month = creation.month;
    d->Day = creation.day;
    d->Hour = creation.hour;
    d->Minute = creation.minute;
    d->Second = creation.second;
    d->Offset = creation.offset;
    d->Flags |= hidden ? 1 : 0;
    d->Flags |= dir ? 2 : 0;

    return R;
}

void isobuilder::DirTree::fromdir(cdutils::DirEntry * d) {
    Date t;
    char pbuf[200], pad;
    int s;
    if ((!d) || (!d->R)) {
	return;
    }

    if (name == "") {
        if ((d->N == 1) && (d->id[0] == 0)) {
            name = ".";
        } else if ((d->N == 1) && (d->id[0] == 1)) {
            name = "..";
        } else {
            memcpy(pbuf, d->id, s = (d->N - ((d->Flags & 2) ? 0 : 2)));
            pbuf[s] = 0;
            name = pbuf;
        }
    }
    hidden = d->Flags & 1;
    if (d->Year < 70)
        d->Year += 100;
    t.year = d->Year;
    t.month = d->Month;
    t.day = d->Day;
    t.hour = d->Hour;
    t.second = d->Second;
    t.hundredths = 0;
    t.offset = d->Offset;
    creation = t;

    s = 33 + d->N;
    if (s & 1) {
        s++;
        pad = 1;
    } else {
        pad = 0;
    }
    if (s != d->R) {
        if ((s + 14) == d->R) {
            Byte * p;
            p = (Byte *) d->id + d->N + pad;
            if ((p[6] == 'X') && (p[7] == 'A')) {
                have_xa = true;
                xa_dir   = p[4] & 0x80;
                xa_audio = p[4] & 0x40;
                xa_str   = p[4] & 0x20;
                xa_xa    = p[4] & 0x10;
                xa_form1 = p[4] & 0x08;
            }
        }
    }
}

bool isobuilder::DirTree::isdir() {
    return dir;
}

void isobuilder::DirTree::setbasicsxa() {
    have_xa = true;
    if (dir)
        xa_dir = true;
    xa_form1 = true;
}

int isobuilder::DirTree::numerate(int n) {
    if (!dir) {
        if (brother) {
            return brother->numerate(n);
        } else {
            return n;
        }
    }

    node = n++;

    if (brother)
        n = brother->numerate(n);

    if (child)
        n = child->numerate(n);

    return n;
}

isobuilder::DirTree * isobuilder::DirTree::Father() {
    return father;
}

isobuilder::DirTree * isobuilder::DirTree::Brother() {
    return brother;
}

isobuilder::DirTree * isobuilder::DirTree::Child() {
    return child;
}

isobuilder::DirTree * isobuilder::DirTree::Find(const String & _name) {
    DirTree * p = 0;

    if (name == _name)
        return this;
    
    if (brother)
        p = brother->Find(_name);

    if (!p && child)
        return child->Find(_name);

    return p;
}

isobuilder::isobuilder(Handle * _w, int _mode) : w(_w), sector(0), nsectors(0), basics(false), dmode(_mode) {
    Byte sect[2352];
    memset(sect, 0, 2352);

    for (int i = 0; i < 16; i++) {
        createsector(sect, MODE2, i);
    }
}

isobuilder::~isobuilder() {
    if (!closed)
        close();
    if (root)
        delete root;
}

void isobuilder::foreword(cdutils * cd) {
    Byte sect[2352];
    for (int i = 0; i < 16; i++) {
        cd->read_sector(sect, MODE_RAW, i);
        createsector(sect, MODE_RAW, i);
    }
}

void isobuilder::foreword(Handle * forewords, int mode) {
    Byte sect[2352];
    for (int i = 0; i < 16; i++) {
        forewords->read(sect, sec_sizes[mode]);
        createsector(sect, mode, i);
    }
}

void isobuilder::foreword(Byte * forewords, int mode) {
    for (int i = 0; i < 16; i++) {
        createsector(forewords + i * sec_sizes[mode], mode, i);
    }
}

int isobuilder::getdispsect() {
    return lastdispsect;
}

int isobuilder::putfile(Handle * file, int mode, int n) {
    Byte data[2352];
    ssize_t filesize;
    int fsect;

    if (mode < 0)
        mode = dmode;

    if (n >= 0) {
        sector = n;
    } else {
        sector = lastdispsect;
    }

    fsect = sector;

    filesize = file->GetSize();

    while (filesize > 0) {
        memset(data, 0, 2352);
        filesize -= file->read(data, sec_sizes[mode]);
        if ((mode == MODE2_FORM1) || (mode == MODE2_FORM2)) {
            if (filesize) {
                clearEOF();
            } else {
                setEOF();
            }
        }
        createsector(data, mode);
    }

    return fsect;
}

int isobuilder::putdata(Byte * _data, size_t size, int smode, int n) {
    Byte data[2352];
    size_t eating;
    int dsect;
    if (n >= 0) {
        sector = n;
    } else {
        sector = lastdispsect;
    }

    dsect = sector;

    if (smode < 0)
        smode = dmode;

    while (size > 0) {
        memset(data, 0, 2352);
        eating = MIN(size, (size_t) sec_sizes[smode]);
        memcpy(data, _data, eating);
        size -= eating;
        _data += eating;
        if ((smode == MODE2_FORM1) || (smode == MODE2_FORM2)) {
            if (size) {
                clearEOF();
            } else {
                setEOF();
            }
        }
        createsector(data, smode);
    }

    return dsect;
}

int isobuilder::createsector(Byte * data, int smode, int n, int FN, int CN, int SM, int CI) {
    Byte dsector[2352];
    int rsector;
    if (n >= 0)
        sector = n;

    if (smode < 0)
        smode = dmode;

    rsector = sector;

    w->seek(2352 * sector, SEEK_SET);

    memcpy(dsector + sec_offsts[smode], data, sec_sizes[smode]);

    if ((smode == MODE2_FORM1) || (smode == MODE2_FORM2)) {
        // Mode 2 Form 2 would be odd, but well....
        dsector[16] = dsector[20] = FN < 0 ? 0 : FN;  // File Number
        dsector[17] = dsector[21] = CN < 0 ? 0 : CN;  // Channel Number
        dsector[18] = dsector[22] = SM < 0 ? (sub_EOR | sub_EOF | 8 |
                                              (smode == MODE2_FORM2 ? 32 : 0)) : SM;
        dsector[19] = dsector[23] = CI < 0 ? 0 : CI;  // Coding Info
    }

    if (smode != MODE_RAW) {
        sector += 150;
        yazedc_o.minute = cdutils::to_BCD(sector / 60 / 75);
        yazedc_o.second = cdutils::to_BCD((sector / 75) % 60);
        yazedc_o.frame = cdutils::to_BCD(sector % 75);
        sector -= 150;
        yazedc_o.do_encode_L2(dsector, smode, 0);
    }

    w->write(dsector, 2352);

    sector++;

    nsectors = MAX(nsectors, sector);
    lastdispsect = MAX(lastdispsect, sector);

    return rsector;
}

int isobuilder::createsector(Handle * file, int smode, int n, int FN, int CN, int SM, int CI) {
    Byte dsector[2352];

    if (smode < 0)
        smode = dmode;

    memset(dsector, 0, 2352);
    file->read(dsector, sec_sizes[smode]);

    return createsector(dsector, smode, n, FN, CN, SM, CI);
}

void isobuilder::setEOF() {
    sub_EOF = 128;
    sub_EOR = 1;
}

void isobuilder::clearEOF() {
    sub_EOF = sub_EOR = 0;
}

isobuilder::DirTree * isobuilder::setbasics(PVD _pvd, int _rootsize, int _ptsize, int _nvd, int _rootsect) throw (GeneralException) {
    if (basics) {
        throw GeneralException("Basic ISO structures already set");
    }
    basics = true;

    pvd = _pvd;
    rootsize = _rootsize;
    ptsize = _ptsize;
    nvd = _nvd;

    ptsect = 17 + nvd;
    rootsect = ptsect + ptsize * 4;
    if (_rootsect >= 0)
        rootsect = _rootsect;
    lastdispsect = rootsect + rootsize;

    root = new DirTree(0);
    root->name = ".";
    root->creation = pvd.volcreat;
    root->sector = rootsect;
    root->size = rootsize * 2048;
    return root;
}

isobuilder::DirTree * isobuilder::createdir(DirTree * p, const String & _name, int size, cdutils::DirEntry * d, int mode) throw (GeneralException) {
    DirTree * r;

    if (!p)
        throw GeneralException("Empty father");

    if (closed)
        throw GeneralException("ISO is closed");

    if (!basics)
        throw GeneralException("ISO basis not created (no root!)");

    r = new DirTree(p);

    r->creation = p->creation;
    if (d)
        r->fromdir(d);
    if (_name != "")
        r->name = _name;
    r->size = size * 2048;
    if (!r->size)
        r->size = cdutils::from_LE32(d->Size);
    r->sector = lastdispsect;
    if (mode >= 0)
        r->mode = mode;
    else
        r->mode = dmode;

    lastdispsect += size;

    return r;
}

isobuilder::DirTree * isobuilder::createfile(DirTree * p, Handle * file, const String & _name, cdutils::DirEntry * d, int mode) throw (GeneralException) {
    DirTree * r;

    if (!p)
        throw GeneralException("Empty father");

    if (closed)
        throw GeneralException("ISO is closed");

    if (!basics)
        throw GeneralException("ISO basis not created (no root!)");

    r = new DirTree(p, false);

    r->name = _name;
    r->creation = p->creation;
    r->fromdir(d);
    if (_name != "")
        r->name = _name;
    r->size = file->GetSize();
    r->sector = putfile(file, mode);
    if (mode >= 0)
        r->mode = mode;
    else
        r->mode = dmode;

    return r;
}

void isobuilder::copydir(isobuilder::DirTree * r, cdutils * cd, cdutils::DirEntry * d, int mode) {
    Byte data[2048];
    cdutils::DirEntry * p;
    int nsectors = cdutils::from_LE32(d->Size) / 2048, ssize, c = 0;
    int fsize, osize;

    if (mode < 0)
        mode = dmode;

    while (nsectors) {
        cd->read_sector(data, mode, cdutils::from_LE32(d->Sector) + c++);
        nsectors--;
        p = (cdutils::DirEntry *) data;
        ssize = 2048;
        while ((ssize) && (p->R)) {
            ssize -= p->R;
            char pbuf[256];
            memcpy(pbuf, p->id, p->N);
            pbuf[p->N] = 0;
            if (p->Flags & 2) {
                if (!((p->N == 1) && ((p->id[0] == 0) || (p->id[0] == 1))))
                    copydir(createdir(r, "", cdutils::from_LE32(p->Size) / 2048, p, mode), cd, p, mode);
            } else {
                printm(M_INFO, "Dupping %s\n", pbuf);
                int fmode;
                osize = fsize = cdutils::from_LE32(p->Size);
                if (mode == MODE1) {
                    fmode = mode;
                } else {
                    fmode = MODE2_FORM1;
                    int s, pad;
                    s = 33 + p->N;
                    if (s & 1) {
                        s++;
                        pad = 1;
                    } else {
                        pad = 0;
                    }
                    if ((s != p->R) && ((s + 14) == p->R)) {
                        if (!(p->id[p->N + pad + 4] & 8)) {
                            fmode = MODE2;
                            fsize = (cdutils::from_LE32(p->Size) / 2048) * 2336;
                        }
                    }
                }
                p->Size = cdutils::to_BE32(fsize);
		cdfile * tmp = new cdfile(cd, p, fmode);
                createfile(r, tmp, "", p, fmode)->size = osize;
		delete tmp;
                p->Size = cdutils::to_BE32(osize);
            }
            p = (cdutils::DirEntry *) (((Byte *) p) + p->R);
        }
    }
}

isobuilder::PVD isobuilder::createpvd(Handle * f) {
    Byte data[2048];
    f->read(data, 2048);
    return createpvd(data);
}

isobuilder::PVD isobuilder::createpvd(cdutils * cd) {
    Byte data[2048];
    cd->read_sector(data, GUESS, 16);
    return createpvd(data);
}

isobuilder::PVD isobuilder::createpvd(Byte * buffer) {
    PVD r;
    char pbuff[256];

    memcpy(pbuff, buffer + 8, 32);
    pbuff[32] = 0;
    r.sysid = pbuff;
    r.sysid.rtrim();
    memcpy(pbuff, buffer + 40, 32);
    pbuff[32] = 0;
    r.volid = pbuff;
    r.volid.rtrim();
    memcpy(pbuff, buffer + 190, 128);
    pbuff[128] = 0;
    r.volsetid = pbuff;
    r.volsetid.rtrim();
    memcpy(pbuff, buffer + 318, 128);
    pbuff[128] = 0;
    r.pubid = pbuff;
    r.pubid.rtrim();
    memcpy(pbuff, buffer + 446, 128);
    pbuff[128] = 0;
    r.prepid = pbuff;
    r.prepid.rtrim();
    memcpy(pbuff, buffer + 574, 128);
    pbuff[128] = 0;
    r.appid = pbuff;
    r.appid.rtrim();
    memcpy(pbuff, buffer + 702, 37);
    pbuff[37] = 0;
    r.copyright = pbuff;
    r.copyright.rtrim();
    memcpy(pbuff, buffer + 739, 37);
    pbuff[37] = 0;
    r.abstract = pbuff;
    r.abstract.rtrim();
    memcpy(pbuff, buffer + 776, 37);
    pbuff[37] = 0;
    r.biblio = pbuff;
    r.biblio.rtrim();

    r.volcreat = Date(buffer + 813);
    r.modif = Date(buffer + 830);
    r.volexp = Date(buffer + 847);
    r.voleff = Date(buffer + 864);

    memcpy(r.appdata, buffer + 883, 512);

    return r;
}

void isobuilder::close(Handle * cue, int mode, int nsects) throw (GeneralException) {
    Byte data[2048];
    Byte * pdata;
    char * cdata = (char *) data;
    int psize;

    if (!root) {
        throw GeneralException("No root directory, no point of closing the iso.");
    }
    
    if (nsects < 0)
	nsects = nsectors;

    memset(data, 0, 2048);

    pdata = (Byte *) malloc(ptsize * 2048);
    psize = root->buildpath(pdata, ptsize * 2048);
    putdata(pdata, ptsize * 2048, mode, ptsect);
    putdata(pdata, ptsize * 2048, mode, ptsect + ptsize);
    root->buildpath(pdata, ptsize * 2048, true);
    putdata(pdata, ptsize * 2048, mode, ptsect + ptsize * 2);
    putdata(pdata, ptsize * 2048, mode, ptsect + ptsize * 3);
    free(pdata);

    data[0] =  1;
    data[1] = 67;
    data[2] = 68;
    data[3] = 48;
    data[4] = 48;
    data[5] = 49;
    data[6] =  1;
    data[7] =  0;

    sprintf(cdata +   8, "%-32s", pvd.sysid.to_charp());
    sprintf(cdata +  40, "%-32s", pvd.volid.to_charp());
    *((Uint32 *) (data + 80)) = cdutils::to_LE32(nsects);
    *((Uint32 *) (data + 84)) = cdutils::to_BE32(nsects);

    data[120] = 1;
    data[121] = 0;
    data[122] = 0;
    data[123] = 1;
    data[124] = 1;
    data[125] = 0;
    data[126] = 0;
    data[127] = 1;
    data[128] = 0;
    data[129] = 8;
    data[130] = 8;
    data[131] = 0;
    *((Uint32 *) (data + 132)) = cdutils::to_LE32(psize);
    *((Uint32 *) (data + 136)) = cdutils::to_BE32(psize);
    *((Uint32 *) (data + 140)) = cdutils::to_LE32(ptsect);
    *((Uint32 *) (data + 144)) = cdutils::to_LE32(ptsect + ptsize);
    *((Uint32 *) (data + 148)) = cdutils::to_BE32(ptsect + ptsize * 2);
    *((Uint32 *) (data + 152)) = cdutils::to_BE32(ptsect + ptsize * 3);

    root->buildentry(data + 156, 34, false);

    sprintf(cdata + 190, "%-128s", pvd.volsetid.to_charp());
    sprintf(cdata + 318, "%-128s", pvd.pubid.to_charp());
    sprintf(cdata + 446, "%-128s", pvd.prepid.to_charp());
    sprintf(cdata + 574, "%-128s", pvd.appid.to_charp());
    sprintf(cdata + 702, "%-37s", pvd.copyright.to_charp());
    sprintf(cdata + 739, "%-37s", pvd.abstract.to_charp());
    sprintf(cdata + 776, "%-37s", pvd.biblio.to_charp());

    pvd.volcreat.dump(data + 813);
    pvd.modif.dump(data + 830);
    pvd.volexp.dump(data + 847);
    pvd.voleff.dump(data + 864);

    memcpy(data + 883, pvd.appdata, 512);

    clearEOF();
    sub_EOR = 1;
    createsector(data, mode, 16);

    memset(data, 0, 2048);
    data[0] =255;
    data[1] = 67;
    data[2] = 68;
    data[3] = 48;
    data[4] = 48;
    data[5] = 49;
    data[6] =  1;

    setEOF();
    createsector(data, mode);

    root->dumpdirs(this);

    closed = true;
}