/* * 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: cdutils.cpp,v 1.24 2003-12-26 20:36:07 pixel Exp $ */ #include #include #include #include #include "generic.h" #include "cdutils.h" #include "Output.h" const long sec_sizes[7] = {0, 2048, 2336, 2048, 2324, 2352, 2352}; const long sec_offsts[7] = {0, 16, 16, 24, 24, 0, 0}; const String sec_modes[7] = {"MODE 0 (empty)", "MODE 1", "MODE 2", "MODE 2 FORM 1", "MODE 2 FORM 2", "Raw", "Autodetect"}; cdutils::cdutils(Handle * r, Handle * w) : rootDir(0), f_iso_r(r), f_iso_w(w), ppf_file(0), pt1(-1), pt2(-1), snum(0), ptl(0), root(0) {} cdutils::~cdutils() { if (ppf_file) delete ppf_file; free(rootDir); } unsigned char cdutils::from_BCD(unsigned char x) { return ((x & 0xf) + ((x & 0xf0) >> 4) * 10); } unsigned char cdutils::to_BCD(unsigned char x) { return ((x / 10) << 4) | (x % 10); } bool cdutils::is_valid_BCD(unsigned char x) { return (((x & 15) < 10) && ((x >> 4) < 10)); } unsigned long cdutils::from_MSF(unsigned char m, unsigned char s, unsigned char f, unsigned long start) { return (from_BCD(m) * 60 + from_BCD(s)) * 75 + from_BCD(f) - start; } unsigned long cdutils::from_MSF(unsigned long msf, unsigned long start) { unsigned char f = msf & 0xff, s = (msf >> 8) & 0xff, m = (msf >> 16) & 0xff; return from_MSF(m, s, f, start); } Handle * cdutils::open_ppf(String ppf, String comment) throw (GeneralException) { int i, l; if (ppf_file) throw GeneralException("Tried to open_ppf() while already opened."); ppf_file = new Output(ppf); ppf_file->write("PPF20\001", 6); l = comment.strlen(); if (l >= 50) { ppf_file->write(comment.to_charp(), 50); } else { char * t = " "; ppf_file->write(comment.to_charp(), l); for (i = l; i < 50; i++) { ppf_file->write(t, 1); } } l = f_iso_r->GetSize(); ppf_file->write(&l, sizeof(l)); f_iso_r->seek(0x9320, SEEK_SET); // copy(f_iso_r, ppf_file, 1024); return ppf_file; } void cdutils::write_ppf(Byte * old_sec, Byte * new_sec, int sec_num) { int i, l = 0, o; for (i = 0; i < 2352; i++) { if (old_sec[i] == new_sec[i]) { if (l != 0) { o = 2352 * sec_num + i; ppf_file->write(&o, sizeof(o)); ppf_file->write(&l, 1); ppf_file->write(new_sec + i - l - 1, l); l = 0; } } else { l++; if (l == 255) { o = 2352 * sec_num + i; ppf_file->write(&o, 4); ppf_file->write(&l, 1); ppf_file->write(new_sec + i - 255, 255); l = 0; } } } } void cdutils::close_ppf() throw (GeneralException) { if (ppf_file) { delete ppf_file; ppf_file = 0; } else { throw GeneralException("Tried to close_ppf() without previous opening"); } } void cdutils::set_iso_w(Handle * w) { if (!f_iso_w) f_iso_w = w; } String cdutils::format_date(String input) { String output; output = input.extract(6, 7) + '/'; output += input.extract(4, 5) + '/'; output += input.extract(0, 3) + ' '; output += input.extract(8, 9) + ':'; output += input.extract(10, 11) + ':'; output += input.extract(12, 13) + '.'; output += input.extract(14, 15); output += String("").set("%+3.1f", ((float) (*((input.to_charp()) + 16))) / 4); return output; } Uint16 cdutils::swap_word(Uint16 i) { return (i >> 8) | (i << 8); } Uint32 cdutils::swap_dword(Uint32 i) { return (i >> 24) | ((i >> 8) & 0x0000ff00) | ((i << 8) & 0x00ff0000) | (i << 24); } int cdutils::guess_type(int number) { Byte header[24]; if (number >= 0) { sector_seek(number); } f_iso_r->read(header, 24); f_iso_r->seek(-24, SEEK_CUR); if (header[15] == 1) { return MODE_1; } else if (header[15] == 2) { if (*((unsigned long *) &(header[16])) == *((unsigned long *) &(header[20]))) { if ((header[16] == 0) && (header[17] == 0) && (header[19] == 0)) { if (header[18] & 0x20) { return MODE_2_FORM_2; } else { return MODE_2_FORM_1; } } } return MODE_2; } return MODE_0; } void cdutils::sector_seek(long sector) { f_iso_r->seek(2352 * sector); if (f_iso_w) f_iso_w->seek(2352 * sector); } long cdutils::read_sector(Byte * buffer, int type, int number) { if (number >= 0) { sector_seek(number); } if (type == GUESS) { type = guess_type(); } f_iso_r->seek(sec_offsts[type], SEEK_CUR); f_iso_r->read(buffer, sec_sizes[type]); f_iso_r->seek(2352 - sec_offsts[type] - sec_sizes[type], SEEK_CUR); return sec_sizes[type]; } void cdutils::read_datas(Byte * buffer, long size, int type, int number) { Byte sector[2352]; int i, n, reste; if (type == GUESS) { type = guess_type(number); } n = size / sec_sizes[type]; reste = size - n * sec_sizes[type]; sector_seek(number); for (i = 0; i < n; i++) { read_sector(buffer + i * sec_sizes[type], type); } if (reste) { read_sector(sector, type); bcopy((char *) sector, (char *) (buffer + n * sec_sizes[type]), reste); } } void cdutils::read_file(Handle * file, long size, int type, int number) { Byte sector[2352]; int i, n, reste; if (type == GUESS) { type = guess_type(number); } n = size / sec_sizes[type]; reste = size - n * sec_sizes[type]; for (i = 0; i < n; i++) { sector_seek(number + i); try { read_sector(sector, type); } catch(...) { memset(sector, 0, 2352); } file->write(sector, sec_sizes[type]); } sector_seek(number + n); if (reste) { try { read_sector(sector, type); } catch(...) { memset(sector, 0, 2352); } file->write(sector, reste); } } void cdutils::write_sector(Byte * buffer, int type, int number) { Byte old_sector[2352], new_sector[2352]; if (type == GUESS) { type = guess_type(number); } if (number >= 0) { sector_seek(number); } else { number = f_iso_r->tell() / 2352; } f_iso_r->read(old_sector, 2352); yazedc_o.minute = old_sector[12]; yazedc_o.second = old_sector[13]; yazedc_o.frame = old_sector[14]; bcopy((char *) old_sector, (char *) new_sector, 2532); bcopy((char *) buffer, (char *) new_sector + sec_offsts[type], sec_sizes[type]); yazedc_o.do_encode_L2(new_sector, type, 0); if (f_iso_w) { f_iso_w->write(new_sector, 2352); } else if (ppf_file) { write_ppf(old_sector, new_sector, number); } else { printm(M_ERROR, "No writing method for iso file"); } } void cdutils::write_datas(Byte * buffer, long size, int type, int number) { long nbsectors, i; unsigned char sector[2352]; if (type == GUESS) { type = guess_type(number); } if (number >= 0) { sector_seek(number); } nbsectors = size / sec_sizes[type]; for (i = 0; i < nbsectors; i++) { write_sector(buffer + i * sec_sizes[type], type, number + i); } if (size % sec_sizes[type]) { memset(sector, 0, 2352); bcopy((char *) (buffer + i * sec_sizes[type]), (char *) sector, size % sec_sizes[type]); write_sector(sector, type, number + i); } } void cdutils::write_file(Handle * file, long size, int type, int number) { long nbsectors, i; unsigned char buffer[2352]; if (type == GUESS) { type = guess_type(number); } if (number >= 0) { sector_seek(number); } if (size < 0) size = file->GetSize(); nbsectors = size / sec_sizes[type]; if (size % sec_sizes[type]) { nbsectors++; } for (i = 0; i < nbsectors; i++) { memset(buffer, 0, 2352); file->read(buffer, sec_sizes[type]); write_sector(buffer, type); } } void cdutils::show_head_entry(void) { printm(M_BARE, "Sector Size Date Time Flags Name XA flags\n"); } int cdutils::show_entry(struct DirEntry * dir) { char pbuf[200], pad; int s; if ((!dir) || (!dir->R)) { return 1; } strncpy(pbuf, dir->id, dir->N); pbuf[dir->N] = 0; if ((dir->N == 1) && (pbuf[0] == 0)) { strcpy(pbuf, "."); } if ((dir->N == 1) && (pbuf[0] == 1)) { strcpy(pbuf, ".."); } s = 33 + dir->N; if (s & 1) { s++; pad = 1; } else { pad = 0; } if (s != dir->R) { if ((s + 14) == dir->R) { Byte * p; p = (Byte *) dir->id + dir->N + pad; if ((p[6] == 'X') && (p[7] == 'A')) { sprintf(pbuf, "%-14s %c%c%c%c%c", pbuf, p[4] & 0x80 ? 'd' : '-', p[4] & 0x40 ? 'a' : '-', p[4] & 0x20 ? 's' : '-', p[4] & 0x10 ? 'x' : '-', p[4] & 0x08 ? 'f' : '-'); } } } if (dir->Year < 70) dir->Year += 100; printm(M_BARE, "%6i %9i %2i/%02i/%04i %2i:%02i:%02i%+03.1f %c%c%c%c%c%c%c%c %s\n", dir->Sector, dir->Size, dir->Day, dir->Month, dir->Year + 1900, dir->Hour, dir->Minute, dir->Second, ((float) dir->Offset) / 4, dir->Flags & 1 ? 'H' : '-', dir->Flags & 2 ? 'D' : '-', dir->Flags & 4 ? 'A' : '-', dir->Flags & 8 ? 'R' : '-', dir->Flags & 16 ? 'P' : '-', dir->Flags & 32 ? '1' : '-', dir->Flags & 64 ? '1' : '-', dir->Flags & 128 ? 'C' : '-', pbuf); return dir->R; } int cdutils::show_dir(struct DirEntry * dir) { unsigned int ptr; Byte * buffer; if (!(dir->Flags & 2)) { return 0; } buffer = (Byte *) malloc(dir->Size); read_datas(buffer, dir->Size, GUESS, dir->Sector); ptr = 0; while(ptr < dir->Size) { ptr += show_entry((struct DirEntry *) &(buffer[ptr])); } free(buffer); return 1; } struct cdutils::DirEntry cdutils::find_dir_entry(struct DirEntry * dir, String name) { unsigned int ptr, size; unsigned char * buffer; struct DirEntry r = {0, 0, 0, 0, 0}; if (!(dir->Flags & 2)) { return r; } buffer = (unsigned char *) malloc(size = dir->Size); read_datas(buffer, dir->Size, GUESS, dir->Sector); ptr = 0; while(ptr < size) { dir = (struct DirEntry *) &(buffer[ptr]); if (!dir->R) { ptr++; } else { if (!strncmp(name.to_charp(), (char *) &(dir->id), dir->N)) { r = *dir; } ptr += dir->R; } } free(buffer); return r; } struct cdutils::DirEntry * cdutils::find_dir_entry(Byte ** bufout, struct cdutils::DirEntry * dir, String name) { unsigned int ptr, size; Byte * buffer; struct DirEntry * rdir = 0; *bufout = 0; if (!(dir->Flags & 2)) { return 0; } buffer = (Byte *) malloc(size = dir->Size); read_datas(buffer, dir->Size, GUESS, dir->Sector); ptr = 0; while(ptr < size) { dir = (struct DirEntry *) &(buffer[ptr]); if (!dir->R) { ptr++; } else { if (!strncmp(name.to_charp(), (char *) &(dir->id), dir->N)) { rdir = dir; } ptr += dir->R; } } if (rdir && rdir->R) { free(*bufout); *bufout = buffer; } else { free(buffer); rdir = 0; } return rdir; } int cdutils::show_iso_infos() { char buffer[2048]; char pbuff[130]; short int s, nogood = 0; read_sector((Byte *) buffer, GUESS, 16); printm(M_BARE, "Sector guessed mode : " + sec_modes[guess_type(16)] + "\n"); printm(M_BARE, "offset-size-info : contents\n"); printm(M_BARE, " 0 - 1- 1 : %i\n", buffer[0]); memcpy(pbuff, buffer + 1, 5); pbuff[5] = 0; printm(M_BARE, " 1 - 5-`CD001' : %s\n", pbuff); printm(M_BARE, " 6 - 2- 1 : %i\n", s = *((short int *) &(buffer[6]))); printm(M_BARE, "(*this was the signature*)\n"); if (buffer[0] != 1) nogood = 1; if (strcmp(pbuff, "CD001")) nogood = 1; if (s != 1) nogood = 1; if (nogood) { printm(M_BARE, "Not a valid iso9660 file.\n"); return 0; } memcpy(pbuff, buffer + 8, 32); pbuff[32] = 0; printm(M_BARE, " 8 - 32- SYSID : %s\n", pbuff); memcpy(pbuff, buffer + 40, 32); pbuff[32] = 0; printm(M_BARE, " 40 - 32- VOLID : %s\n", pbuff); printm(M_BARE, " 80 - 8- SNum : %li\n", *((long int *) &(buffer[80]))); printm(M_BARE, " 120 - 4- VOLSiz : %i\n", *((short int *) &(buffer[120]))); printm(M_BARE, " 124 - 4- VOLNum : %i\n", *((short int *) &(buffer[124]))); printm(M_BARE, " 128 - 4- SSize : %i\n", *((short int *) &(buffer[128]))); printm(M_BARE, " 132 - 8- PSize : %li\n", *((long int *) &(buffer[132]))); printm(M_BARE, " 140 - 4- 1SLPath: %i\n", *((short int *) &(buffer[140]))); printm(M_BARE, " 144 - 4- 2SLPath: %i\n", *((short int *) &(buffer[144]))); printm(M_BARE, " 148 - 4- 1SBPath: %i\n", swap_word(*((short int *) &(buffer[150])))); printm(M_BARE, " 152 - 4- 2SBPath: %i\n", swap_word(*((short int *) &(buffer[154])))); memcpy(pbuff, buffer + 190, 128); pbuff[128] = 0; printm(M_BARE, " 190 - 128- VStId : %s\n", pbuff); memcpy(pbuff, buffer + 318, 128); pbuff[128] = 0; printm(M_BARE, " 318 - 128- PubId : %s\n", pbuff); memcpy(pbuff, buffer + 446, 128); pbuff[128] = 0; printm(M_BARE, " 446 - 128- DPrId : %s\n", pbuff); memcpy(pbuff, buffer + 574, 128); pbuff[128] = 0; printm(M_BARE, " 574 - 128- AppId : %s\n", pbuff); memcpy(pbuff, buffer + 702, 37); pbuff[37] = 0; printm(M_BARE, " 702 - 37- CpyFile: %s\n", pbuff); memcpy(pbuff, buffer + 739, 37); pbuff[37] = 0; printm(M_BARE, " 739 - 37- AbsFile: %s\n", pbuff); memcpy(pbuff, buffer + 776, 37); pbuff[37] = 0; printm(M_BARE, " 776 - 37- BibFile: %s\n", pbuff); printm(M_BARE, " 813 - 17- DTCreat: " + format_date(&buffer[813]) + "\n"); printm(M_BARE, " 830 - 17- DTModif: " + format_date(&buffer[830]) + "\n"); printm(M_BARE, " 847 - 17- DTExpir: " + format_date(&buffer[847]) + "\n"); printm(M_BARE, " 864 - 17- DTEffec: " + format_date(&buffer[864]) + "\n"); printm(M_BARE, "Root record:\n"); show_head_entry(); show_entry((DirEntry *) &(buffer[156])); return 1; } int cdutils::get_iso_infos() { Byte buffer[2048]; char pbuff[130]; short int s, nogood = 0; int rootsec; read_sector(buffer, GUESS, 16); memcpy(pbuff, buffer + 1, 5); pbuff[5] = 0; s = *((short int *) &(buffer[6])); if (buffer[0] != 1) nogood = 1; if (strcmp(pbuff, "CD001")) nogood = 1; if (s != 1) nogood = 1; if (nogood) { printm(M_ERROR, "Not a valid iso9660 file.\n"); return 0; } pt1 = *((short int *) &(buffer[140])); pt2 = *((short int *) &(buffer[144])); snum = *((int *) &(buffer[80])); ptl = *((long int *) &(buffer[132])); rootsec = ((struct DirEntry *) (&buffer[156]))->Sector; read_sector(buffer, GUESS, rootsec); rootDir = (struct DirEntry *) malloc(buffer[0]); memcpy(rootDir, buffer, buffer[0]); return 1; } int cdutils::get_pt_infos() { Byte * buffer; if ((pt1 <= 0) && (pt2 <= 0)) if (!get_iso_infos()) return 0; if ((!pt1) & (!pt2)) { printm(M_ERROR, "No path table defined.\n"); return 0; } buffer = (Byte *) malloc(ptl); read_datas(buffer, ptl, GUESS, !pt1 ? pt2 : pt1); if (buffer[0] == 1) if (buffer[1] == 0) if (buffer[6] == 1) if (buffer[7] == 0) if (buffer[8] == 0) if (buffer[9] == 0) root = *((unsigned long int *) &(buffer[2])); free(buffer); return root ? 1 : 0; } int cdutils::show_pt_infos() { Byte * buffer; char pbuf[100]; int i, ptr; if ((pt1 <= 0) && (pt2 <= 0)) if (!get_iso_infos()) return 0; if ((!pt1) & (!pt2)) { printm(M_ERROR, "No path table defined.\n"); return 0; } buffer = (Byte *) malloc(ptl); read_datas(buffer, ptl, GUESS, !pt1 ? pt2 : pt1); printm(M_BARE, "node^paren@sector : name\n"); for (ptr = 0, i = 1; buffer[ptr]; ptr += ptr & 1, i++) { strncpy(pbuf, (char *) &(buffer[8 + ptr]), buffer[ptr]); pbuf[buffer[ptr]] = 0; printm(M_BARE, "%3i ^ %3i @ %6i: %s\n", i, *((unsigned short *) &(buffer[6 + ptr])), *((unsigned long *) &(buffer[2 + ptr])), pbuf); ptr += 8 + buffer[ptr]; } free(buffer); return 1; } struct cdutils::DirEntry cdutils::find_path(String path) { char * newpath = path.strdup(); char ** pts = split(newpath, '/'); struct DirEntry dir = {0, 0, 0, 0, 0}; if ((pt1 <= 0) && (pt2 <= 0)) if (!get_iso_infos()) { free(newpath); return dir; } if ((!pt1) & (!pt2)) { printm(M_ERROR, "No path table defined.\n"); free(newpath); return dir; } if (!**pts) pts++; for (dir = *rootDir; *pts; pts++) { if (!strlen(*pts)) continue; dir = find_dir_entry(&dir, *pts); if (!dir.R) { free(newpath); return dir; } } free(newpath); return dir; } struct cdutils::DirEntry cdutils::find_parent(String path) { char * newpath = path.strdup(); char ** pts, * p; struct DirEntry dir = {0, 0, 0, 0, 0}; if ((p = strchr(newpath, '/'))) { *p = 0; } if (!*newpath) { free(newpath); return *rootDir; } pts = split(newpath, '/'); if ((pt1 <= 0) && (pt2 <= 0)) if (!get_iso_infos()) { free(newpath); return dir; } if ((!pt1) & (!pt2)) { printm(M_ERROR, "No path table defined.\n"); free(newpath); return dir; } if (!**pts) pts++; for (dir = *rootDir; *pts; pts++) { if (!strlen(*pts)) continue; dir = find_dir_entry(&dir, *pts); if (!dir.R) { free(newpath); return dir; } } free(newpath); return dir; } struct cdutils::DirEntry * cdutils::find_path(Byte ** bufout, String path) { char * newpath = path.strdup(); char ** pts = split(newpath, '/'); struct DirEntry * dir; *bufout = 0; if ((pt1 <= 0) && (pt2 <= 0)) if (!get_iso_infos()) { free(newpath); return 0; } if ((!pt1) & (!pt2)) { printm(M_ERROR, "No path table defined.\n"); free(newpath); return 0; } if (!**pts) pts++; for (dir = rootDir; *pts; pts++) { if (!strlen(*pts)) continue; dir = find_dir_entry(bufout, dir, *pts); if (!dir) { free(newpath); free(*bufout); return 0; } } if (!dir) { free(*bufout); } free(newpath); return dir; } struct cdutils::DirEntry * cdutils::find_parent(Byte ** bufout, String path) { char * newpath = path.strdup(); char ** pts, * p; struct DirEntry * dir; *bufout = 0; if ((p = strchr(newpath, '/'))) { *p = 0; } if (!*newpath) { free(newpath); return rootDir; } pts = split(newpath, '/'); if ((pt1 <= 0) && (pt2 <= 0)) if (!get_iso_infos()) { free(newpath); return 0; } if ((!pt1) & (!pt2)) { printm(M_ERROR, "No path table defined.\n"); free(newpath); return 0; } if (!**pts) pts++; for (dir = rootDir; *pts; pts++) { free(*bufout); if (!strlen(*pts)) continue; dir = find_dir_entry(bufout, dir, *pts); if (!dir) { free(newpath); return dir; } } if (!dir) { free(*bufout); } free(newpath); return dir; } cdfile::cdfile(cdutils * _cd, const cdutils::DirEntry * d, int _mode) : Handle(-1), cd(_cd), sector(d->Sector), mode(_mode), size(d->Size), name(d->id) { if (mode == GUESS) { mode = cd->guess_type(sector); } dir = (cdutils::DirEntry *) malloc(d->R); memcpy(dir, d, d->R); itell = 0; } cdfile::cdfile(cdutils * _cd, int _sector, ssize_t _size, int _mode) : Handle(-1), cd(_cd), sector(_sector), mode(_mode), size(_size), name("raw reading") { Byte datas[2352]; bool eof; if (mode == GUESS) { mode = cd->guess_type(sector); } if (_size == -1) { size = 0; if ((mode == MODE2_FORM1) || (mode == MODE2_FORM2)) { do { cd->read_sector(datas, MODE_RAW, sector + size++); eof = datas[18] & 0x80; } while (!eof); size *= sec_sizes[mode]; } } dir = 0; itell = 0; } cdfile::~cdfile() { free(dir); } ssize_t cdfile::read(void *buf, size_t count) throw (GeneralException) { Byte buffer[2352]; size_t startsec, startbyte, nstartbytes; count = MIN(count, (size_t) (size - itell)); if (!count) return 0; startsec = itell / sec_sizes[mode] + sector; startbyte = itell % sec_sizes[mode]; nstartbytes = sec_sizes[mode] - startbyte; nstartbytes = MIN(nstartbytes, count); count -= nstartbytes; cd->read_sector(buffer, mode, startsec); memcpy(buf, buffer + startbyte, nstartbytes); buf = (Byte *) buf + nstartbytes; if (count) { cd->read_datas((Byte *) buf, count, mode, startsec + 1); } itell += count + nstartbytes; return count + nstartbytes; } bool cdfile::CanRead() const { return true; } String cdfile::GetName() const { return String("cdfile: ") + name; } bool cdfile::CanWatch() const { return false; } ssize_t cdfile::GetSize() const { return size; } bool cdfile::CanSeek() const { return true; } off_t cdfile::seek(off_t off, int wheel) throw (GeneralException) { switch (wheel) { case SEEK_SET: itell = off; break; case SEEK_CUR: itell += off; break; case SEEK_END: itell = size + off; break; } return itell; }