/* * 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.16 2004-12-17 11:48:41 pixel Exp $ */ #include "isobuilder.h" void isobuilder::Date::dump(Byte * datas) { char pbuf[256]; sprintf(pbuf, "%04i", year); memcpy(datas + 0, pbuf, 4); sprintf(pbuf, "%02i", month); memcpy(datas + 4, pbuf, 2); sprintf(pbuf, "%02i", day); memcpy(datas + 6, pbuf, 2); sprintf(pbuf, "%02i", hour); memcpy(datas + 8, pbuf, 2); sprintf(pbuf, "%02i", minute); memcpy(datas + 10, pbuf, 2); sprintf(pbuf, "%02i", second); memcpy(datas + 12, pbuf, 2); sprintf(pbuf, "%02i", hundredths); memcpy(datas + 14, pbuf, 2); *((char *) (datas + 16)) = offset; } isobuilder::Date::Date(int) { year = month = day = hour = minute = second = hundredths = offset = 0; } isobuilder::Date::Date(Byte * datas) { char pbuf[256]; char * cdatas = (char *) datas; memcpy(pbuf, cdatas + 0, 4); pbuf[4] = 0; sscanf(pbuf, "%d", &year); memcpy(pbuf, cdatas + 4, 2); pbuf[2] = 0; sscanf(pbuf, "%d", &month); memcpy(pbuf, cdatas + 6, 2); pbuf[2] = 0; sscanf(pbuf, "%d", &day); memcpy(pbuf, cdatas + 8, 2); pbuf[2] = 0; sscanf(pbuf, "%d", &hour); memcpy(pbuf, cdatas + 10, 2); pbuf[2] = 0; sscanf(pbuf, "%d", &minute); memcpy(pbuf, cdatas + 12, 2); pbuf[2] = 0; sscanf(pbuf, "%d", &second); memcpy(pbuf, cdatas + 14, 2); pbuf[2] = 0; sscanf(pbuf, "%d", &hundredths); offset = *(cdatas + 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->putdatas(odir, size, mode, sector); free(odir); } int isobuilder::DirTree::buildpath(Byte * datas, int size, bool bigendian) throw (GeneralException) { int N, r, tr; Uint16 pn; char pbuf[256], pad; if (!dir) { if (brother) { return brother->buildpath(datas, size, bigendian); } else { return 0; } } 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."); datas[0] = N; datas[1] = 0; *((Uint32 *) (datas + 2)) = bigendian ? cdutils::swap_dword(sector) : sector; *((Uint16 *) (datas + 6)) = bigendian ? cdutils::swap_word(pn) : pn; memcpy(datas + 8, pbuf, N); if (pad) datas[8 + N] = 0; datas += r; if (brother) { tr = brother->buildpath(datas, size, bigendian); r += tr; size -= tr; datas += tr; } if (child) { tr = child->buildpath(datas, size, bigendian); r += tr; size -= tr; datas += 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) { 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 = sector; d->BESector = cdutils::swap_dword(sector); d->Size = size; d->BESize = cdutils::swap_dword(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 ((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 datas[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(datas, 0, 2352); filesize -= file->read(datas, sec_sizes[mode]); if ((mode == MODE2_FORM1) || (mode == MODE2_FORM2)) { if (filesize) { clearEOF(); } else { setEOF(); } } createsector(datas, mode); } return fsect; } int isobuilder::putdatas(Byte * _datas, size_t size, int smode, int n) { Byte datas[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(datas, 0, 2352); eating = MIN(size, (size_t) sec_sizes[smode]); memcpy(datas, _datas, eating); size -= eating; _datas += eating; if ((smode == MODE2_FORM1) || (smode == MODE2_FORM2)) { if (size) { clearEOF(); } else { setEOF(); } } createsector(datas, smode); } return dsect; } int isobuilder::createsector(Byte * datas, 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], datas, 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 = 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 datas[2048]; cdutils::DirEntry * p; int nsectors = d->Size / 2048, ssize, c = 0; int fsize, osize; if (mode < 0) mode = dmode; while (nsectors) { cd->read_sector(datas, mode, d->Sector + c++); nsectors--; p = (cdutils::DirEntry *) datas; 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, "", p->Size / 2048, p, mode), cd, p, mode); } else { printm(M_INFO, "Dupping %s\n", pbuf); int fmode; osize = fsize = 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 = (p->Size / 2048) * 2336; } } } p->Size = fsize; cdfile * tmp = new cdfile(cd, p, fmode); createfile(r, tmp, "", p, fmode)->size = osize; delete tmp; p->Size = osize; } p = (cdutils::DirEntry *) (((Byte *) p) + p->R); } } } isobuilder::PVD isobuilder::createpvd(Handle * f) { Byte datas[2048]; f->read(datas, 2048); return createpvd(datas); } isobuilder::PVD isobuilder::createpvd(cdutils * cd) { Byte datas[2048]; cd->read_sector(datas, GUESS, 16); return createpvd(datas); } 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 datas[2048]; Byte * pdatas; char * cdatas = (char *) datas; int psize; if (nsects < 0) nsects = nsectors; memset(datas, 0, 2048); pdatas = (Byte *) malloc(ptsize * 2048); psize = root->buildpath(pdatas, ptsize * 2048); putdatas(pdatas, ptsize * 2048, mode, ptsect); putdatas(pdatas, ptsize * 2048, mode, ptsect + ptsize); root->buildpath(pdatas, ptsize * 2048, true); putdatas(pdatas, ptsize * 2048, mode, ptsect + ptsize * 2); putdatas(pdatas, ptsize * 2048, mode, ptsect + ptsize * 3); free(pdatas); datas[0] = 1; datas[1] = 67; datas[2] = 68; datas[3] = 48; datas[4] = 48; datas[5] = 49; datas[6] = 1; datas[7] = 0; sprintf(cdatas + 8, "%-32s", pvd.sysid.to_charp()); sprintf(cdatas + 40, "%-32s", pvd.volid.to_charp()); *((Uint32 *) (datas + 80)) = nsects; *((Uint32 *) (datas + 84)) = cdutils::swap_dword(nsects); datas[120] = 1; datas[121] = 0; datas[122] = 0; datas[123] = 1; datas[124] = 1; datas[125] = 0; datas[126] = 0; datas[127] = 1; datas[128] = 0; datas[129] = 8; datas[130] = 8; datas[131] = 0; *((Uint32 *) (datas + 132)) = psize; *((Uint32 *) (datas + 136)) = cdutils::swap_dword(psize); *((Uint32 *) (datas + 140)) = ptsect; *((Uint32 *) (datas + 144)) = ptsect + ptsize; *((Uint32 *) (datas + 148)) = cdutils::swap_dword(ptsect + ptsize * 2); *((Uint32 *) (datas + 152)) = cdutils::swap_dword(ptsect + ptsize * 3); root->buildentry(datas + 156, 34, false); sprintf(cdatas + 190, "%-128s", pvd.volsetid.to_charp()); sprintf(cdatas + 318, "%-128s", pvd.pubid.to_charp()); sprintf(cdatas + 446, "%-128s", pvd.prepid.to_charp()); sprintf(cdatas + 574, "%-128s", pvd.appid.to_charp()); sprintf(cdatas + 702, "%-37s", pvd.copyright.to_charp()); sprintf(cdatas + 739, "%-37s", pvd.abstract.to_charp()); sprintf(cdatas + 776, "%-37s", pvd.biblio.to_charp()); pvd.volcreat.dump(datas + 813); pvd.modif.dump(datas + 830); pvd.volexp.dump(datas + 847); pvd.voleff.dump(datas + 864); memcpy(datas + 883, pvd.appdata, 512); clearEOF(); sub_EOR = 1; createsector(datas, mode, 16); memset(datas, 0, 2048); datas[0] =255; datas[1] = 67; datas[2] = 68; datas[3] = 48; datas[4] = 48; datas[5] = 49; datas[6] = 1; setEOF(); createsector(datas, mode); root->dumpdirs(this); closed = true; }