/* * 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; dvdmode = father->dvdmode; creation = father->creation; if (father->child) { for (p = father->child; p->brother; p = p->brother); p->brother = this; } else { father->child = this; } } void isobuilder::DirTree::setdvdmode() { dvdmode = true; } 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, n = 1; for (i = 1; i < maxlv; i++) { R = buildpath_r(&n, data, size, 1, i, bigendian); data += R; cumul += R; } return cumul; } int isobuilder::DirTree::buildpath_r(int * n, 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(n, data, size, current_level, level_to_dump, bigendian); } else { return 0; } } if (current_level == level_to_dump) { if (!father) { N = 1; pbuf[0] = 0; pn = 1; } else { N = name.strlen(); strcpy(pbuf, name.to_charp()); pn = father->node; } node = (*n)++; 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(n, data, size, current_level + 1, level_to_dump, bigendian); r += tr; size -= tr; data += tr; } if (brother) { tr = brother->buildpath_r(n, 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 == ".") { N = 1; pbuf[0] = 0; } else if (name == "..") { N = 1; pbuf[0] = 1; } else { strcpy(pbuf, name.to_charp()); N = name.strlen(); if ((!dir) && !dvdmode) { 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; } 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; }