- Main Page
- Namespaces
- Classes
- Files
- Directories

- File List
- File Members

# VobSubFileRipper.cpp

    00001 /*
    00002  *      Copyright (C) 2003-2005 Gabest
    00003  *      http://www.gabest.org
    00004  *
    00005  *  This Program is free software; you can redistribute it and/or modify
    00006  *  it under the terms of the GNU General Public License as published by
    00007  *  the Free Software Foundation; either version 2, or (at your option)
    00008  *  any later version.
    00009  *
    00010  *  This Program is distributed in the hope that it will be useful,
    00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    00013  *  GNU General Public License for more details.
    00014  *
    00015  *  You should have received a copy of the GNU General Public License
    00016  *  along with GNU Make; see the file COPYING.  If not, write to
    00017  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
    00018  *  http://www.gnu.org/copyleft/gpl.html
    00019  *
    00020  */
    00021
    00022 #include "StdAfx.h"
    00023 #include "vobsubfileripper.h"
    00024 #include "..\decss\VobDec.h"
    00025 #include "..\subtitles\CCDecoder.h"
    00026
    00027 //
    00028 // CVobSubFileRipper
    00029 //
    00030
    00031 CVobSubFileRipper::CVobSubFileRipper()
    00032         : CVobSubFile(NULL)
    00033         , m_fThreadActive(false)
    00034         , m_fBreakThread(false)
    00035         , m_fIndexing(false)
    00036 {
    00037         m_rd.Reset();
    00038         CAMThread::Create();
    00039 }
    00040
    00041 CVobSubFileRipper::~CVobSubFileRipper()
    00042 {
    00043         CAMThread::CallWorker(CMD_EXIT);
    00044         CAMThread::Close();
    00045 }
    00046
    00047 STDMETHODIMP CVobSubFileRipper::NonDelegatingQueryInterface(REFIID riid, void** ppv)
    00048 {
    00049         return
    00050                 QI(IVSFRipper)
    00051                 __super::NonDelegatingQueryInterface(riid, ppv);
    00052 }
    00053
    00054 void CVobSubFileRipper::Log(log_t type, LPCTSTR lpszFormat, ...)
    00055 {
    00056         CAutoLock cAutoLock(&m_csCallback);
    00057         if(!m_pCallback) return;
    00058
    00059         TCHAR buff[1024];
    00060
    00061         va_list args;
    00062         va_start(args, lpszFormat);
    00063         _vstprintf(buff, lpszFormat, args);
    00064         va_end(args);
    00065
    00066         CString msg;
    00067         switch(type)
    00068         {
    00069         default:
    00070         case LOG_INFO: msg = _T(""); break;
    00071         case LOG_WARNING: msg = _T("WARNING: "); break;
    00072         case LOG_ERROR: msg = _T("ERROR: "); break;
    00073         }
    00074
    00075         msg += buff;
    00076
    00077         m_pCallback->OnMessage(msg);
    00078 }
    00079
    00080 void CVobSubFileRipper::Progress(double progress)
    00081 {
    00082         CAutoLock cAutoLock(&m_csCallback);
    00083         if(!m_pCallback) return;
    00084
    00085         m_pCallback->OnProgress(progress);
    00086 }
    00087
    00088 void CVobSubFileRipper::Finished(bool fSucceeded)
    00089 {
    00090         CAutoLock cAutoLock(&m_csCallback);
    00091         if(!m_pCallback) return;
    00092
    00093         m_pCallback->OnFinished(fSucceeded);
    00094 }
    00095
    00096 #define ReadBEb(var) \
    00097         f.Read(&((BYTE*)&var)[0], 1); \
    00098
    00099 #define ReadBEw(var) \
    00100         f.Read(&((BYTE*)&var)[1], 1); \
    00101         f.Read(&((BYTE*)&var)[0], 1); \
    00102
    00103 #define ReadBEdw(var) \
    00104     f.Read(&((BYTE*)&var)[3], 1); \
    00105         f.Read(&((BYTE*)&var)[2], 1); \
    00106         f.Read(&((BYTE*)&var)[1], 1); \
    00107         f.Read(&((BYTE*)&var)[0], 1); \
    00108
    00109 bool CVobSubFileRipper::LoadIfo(CString fn)
    00110 {
    00111         CString str;
    00112
    00113         CFileStatus status;
    00114         if(!CFileGetStatus(fn, status) || !status.m_size)
    00115         {
    00116                 Log(LOG_ERROR, _T("Invalid ifo"));
    00117                 return(false);
    00118         }
    00119
    00120         CFile f;
    00121         if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
    00122         {
    00123                 Log(LOG_ERROR, _T("Cannot open ifo"));
    00124                 return(false);
    00125         }
    00126
    00127         Log(LOG_INFO, _T("Opening ifo OK"));
    00128
    00129         char hdr[13];
    00130         f.Read(hdr, 12);
    00131         hdr[12] = 0;
    00132         if(strcmp(hdr, "DVDVIDEO-VTS"))
    00133         {
    00134                 Log(LOG_ERROR, _T("Not a Video Title Set IFO file!"));
    00135                 return(false);
    00136         }
    00137
    00138         // lang ids
    00139
    00140         f.Seek(0x254, CFile::begin);
    00141
    00142         WORD ids[32];
    00143         memset(ids, 0, sizeof(ids));
    00144
    00145         int len = 0;
    00146         ReadBEw(len);
    00147
    00148         for(int i = 0; i < len; i++)
    00149         {
    00150                 f.Seek(2, CFile::current); // 01 00 ?
    00151                 ReadBEw(ids[i]);
    00152                 if(ids[i] == 0) ids[i] = '--';
    00153                 f.Seek(2, CFile::current); // 00 00 ?
    00154         }
    00155
    00156         /* Video info */
    00157
    00158         f.Seek(0x200, CFile::begin);
    00159         f.Read(&m_rd.vidinfo, 2);
    00160
    00161         SIZE res[4][2] =
    00162         {
    00163                 {{720,480},{720,576}},
    00164                 {{704,480},{704,576}},
    00165                 {{352,480},{352,576}},
    00166                 {{352,240},{352,288}}
    00167         };
    00168
    00169         m_rd.vidsize = res[m_rd.vidinfo.source_res][m_rd.vidinfo.system&1];
    00170
    00171         double rate = (m_rd.vidinfo.system == 0) ? 30.0/29.97 : 1.0;
    00172
    00173         /* PGCs */
    00174
    00175         {
    00176                 DWORD offset;
    00177
    00178                 DWORD pgcpos;
    00179                 f.Seek(0xc0+0x0c, CFile::begin);
    00180                 ReadBEdw(pgcpos);
    00181                 pgcpos *= 0x800;
    00182
    00183                 WORD nPGC;
    00184                 f.Seek(pgcpos, CFile::begin);
    00185                 ReadBEw(nPGC);
    00186
    00187                 m_rd.pgcs.RemoveAll();
    00188                 m_rd.pgcs.SetSize(nPGC);
    00189
    00190                 for(int i = 0; i < nPGC; i++)
    00191                 {
    00192                         PGC& pgc = m_rd.pgcs[i];
    00193
    00194                         f.Seek(pgcpos + 8 + i*8 + 4, CFile::begin);
    00195                         ReadBEdw(offset);
    00196                         offset += pgcpos;
    00197
    00198                         BYTE nProgs, nCells;
    00199                         f.Seek(offset + 2, CFile::begin);
    00200                         ReadBEb(nProgs);
    00201                         ReadBEb(nCells);
    00202
    00203                         //
    00204
    00205                         memcpy(pgc.ids, ids, sizeof(ids));
    00206
    00207                         struct splanginfo {BYTE res1, id1, id2, res2;};
    00208                         splanginfo splinfo[32];
    00209
    00210                         f.Seek(offset + 0x1c, CFile::begin);
    00211                         f.Read(splinfo, 32*4);
    00212
    00213                         for(int j = 0; j < 32; j++)
    00214                         {
    00215                                 if(splinfo[j].id1 || splinfo[i].id2)
    00216                                 {
    00217                                         WORD tmpids[32];
    00218                                         memset(tmpids, 0, sizeof(tmpids));
    00219
    00220                                         for(j = 0; j < 32; j++)
    00221                                         {
    00222                                                 if(!(splinfo[j].res1 & 0x80)) break;
    00223
    00224                                                 pgc.ids[splinfo[j].id1] = ids[j];
    00225                                                 pgc.ids[splinfo[j].id2] = ids[j];
    00226                                         }
    00227
    00228                                         break;
    00229                                 }
    00230                         }
    00231
    00232                         //
    00233
    00234                         f.Seek(offset + 0xa4, CFile::begin);
    00235
    00236                         for(int j = 0; j < 16; j++)
    00237                         {
    00238                                 BYTE y, u, v, tmp;
    00239
    00240                                 f.Read(&tmp, 1);
    00241                                 f.Read(&y, 1);
    00242                                 f.Read(&u, 1);
    00243                                 f.Read(&v, 1);
    00244
    00245                                 y = (y-16)*255/219;
    00246
    00247                                 pgc.pal[j].rgbRed = (BYTE)min(max(1.0*y + 1.4022*(u-128), 0), 255);
    00248                                 pgc.pal[j].rgbGreen = (BYTE)min(max(1.0*y - 0.3456*(u-128) - 0.7145*(v-128), 0), 255);
    00249                                 pgc.pal[j].rgbBlue = (BYTE)min(max(1.0*y + 1.7710*(v-128), 0) , 255);
    00250                         }
    00251
    00252                         //
    00253
    00254                         WORD progoff, celladdroff, vobcelloff;
    00255                         f.Seek(offset + 0xe6, CFile::begin);
    00256                         ReadBEw(progoff);
    00257                         f.Seek(offset + 0xe8, CFile::begin);
    00258                         ReadBEw(celladdroff);
    00259                         f.Seek(offset + 0xea, CFile::begin);
    00260                         ReadBEw(vobcelloff);
    00261
    00262                         //
    00263
    00264             CByteArray progs;
    00265                         progs.SetSize(nProgs);
    00266                         f.Seek(offset + progoff, CFile::begin);
    00267                         f.Read(progs.GetData(), nProgs);
    00268
    00269                         //
    00270
    00271                         pgc.angles[0].SetSize(nCells);
    00272                         pgc.iSelAngle = 0;
    00273
    00274                         //
    00275
    00276                         f.Seek(offset + vobcelloff, CFile::begin);
    00277                         for(int j = 0; j < nCells; j++)
    00278                         {
    00279                                 ReadBEw(pgc.angles[0][j].vob);
    00280                                 ReadBEw(pgc.angles[0][j].cell);
    00281                         }
    00282
    00283                         //
    00284
    00285                         DWORD tOffset = 0, tTotal = 0;
    00286
    00287                         int iAngle = 0;
    00288
    00289                         pgc.nAngles = 0;
    00290
    00291                         f.Seek(offset + celladdroff, CFile::begin);
    00292                         for(int j = 0; j < nCells; j++)
    00293                         {
    00294                                 BYTE b;
    00295                                 ReadBEb(b);
    00296                                 switch(b>>6)
    00297                                 {
    00298                                 case 0: iAngle = 0; break; // normal
    00299                                 case 1: iAngle = 1; break; // first angle block
    00300                                 case 2: iAngle++; break; // middle angle block
    00301                                 case 3: iAngle++; break; // last angle block (no more should follow)
    00302                                 }
    00303                                 pgc.angles[0][j].iAngle = iAngle;
    00304                                 pgc.nAngles = max(pgc.nAngles, iAngle);
    00305
    00306                                 f.Seek(3, CFile::current);
    00307                                 ReadBEdw(pgc.angles[0][j].tTime);
    00308                                 ReadBEdw(pgc.angles[0][j].start);
    00309                                 f.Seek(8, CFile::current);
    00310                                 ReadBEdw(pgc.angles[0][j].end);
    00311
    00312                                 float fps;
    00313                                 switch((pgc.angles[0][j].tTime>>6)&0x3)
    00314                                 {
    00315                                 default:
    00316                                 case 3: fps = 30; break;
    00317                                 case 1: fps = 25; break;
    00318                                 }
    00319
    00320                                 int t = pgc.angles[0][j].tTime;
    00321                                 int hh = ((t>>28)&0xf)*10+((t>>24)&0xf);
    00322                                 int mm = ((t>>20)&0xf)*10+((t>>16)&0xf);
    00323                                 int ss = ((t>>12)&0xf)*10+((t>>8)&0xf);
    00324                                 int ms = (int)(1000.0 * (((t>>4)&0x3)*10+((t>>0)&0xf)) / fps);
    00325                                 pgc.angles[0][j].tTime = (DWORD)((((hh*60+mm)*60+ss)*1000+ms)*rate);
    00326
    00327                                 // time discontinuity
    00328                                 if(b&0x02) tOffset = tTotal;
    00329                                 pgc.angles[0][j].fDiscontinuity = !!(b&0x02);
    00330
    00331                                 pgc.angles[0][j].tTotal = tTotal;
    00332                                 pgc.angles[0][j].tOffset = tOffset;
    00333
    00334                                 tTotal += pgc.angles[0][j].tTime;
    00335                         }
    00336
    00337                         for(iAngle = 1; iAngle <= 9; iAngle++)
    00338                         {
    00339                                 tOffset = tTotal = 0;
    00340
    00341                                 for(int j = 0, k = 0; j < nCells; j++)
    00342                                 {
    00343                                         if(pgc.angles[0][j].iAngle != 0
    00344                                         && pgc.angles[0][j].iAngle != iAngle)
    00345                                                 continue;
    00346
    00347                                         pgc.angles[iAngle].Add(pgc.angles[0][j]);
    00348
    00349                                         if(pgc.angles[iAngle][k].fDiscontinuity) tOffset = tTotal;
    00350
    00351                                         pgc.angles[iAngle][k].tTotal = tTotal;
    00352                                         pgc.angles[iAngle][k].tOffset = tOffset;
    00353
    00354                                         tTotal += pgc.angles[iAngle][k].tTime;
    00355
    00356                                         k++;
    00357                                 }
    00358                         }
    00359                 }
    00360         }
    00361
    00362     Log(LOG_INFO, _T("Parsing ifo OK"));
    00363
    00364         return(true);
    00365 }
    00366
    00367 bool CVobSubFileRipper::LoadVob(CString fn)
    00368 {
    00369         Log(LOG_INFO, _T("Searching vobs..."));
    00370 /*
    00371         CList<CString> m_vobs;
    00372
    00373         fn = fn.Left(fn.ReverseFind('.')+1);
    00374         fn.TrimRight(_T(".0123456789"));
    00375         for(int i = 0; i < 100; i++)
    00376         {
    00377                 CString vob;
    00378                 vob.Format(_T("%s%d.vob"), fn, i);
    00379
    00380                 CFileStatus status;
    00381                 if(!(CFileGetStatus(vob, status) && status.m_size))
    00382                 {
    00383                         if(i > 0) break;
    00384                         else continue;
    00385                 }
    00386
    00387                 if(status.m_size&0x7ff)
    00388                 {
    00389                         Log(LOG_ERROR, _T("Length of %s is not n*2048!"), vob);
    00390                         m_vobs.RemoveAll();
    00391                         break;
    00392                 }
    00393
    00394                 CString str = _T("Found ") + vob;
    00395
    00396                 if(i == 0)
    00397                 {
    00398                         str += _T(" (skipping, if not a menu vob rename it)");
    00399                 }
    00400                 else
    00401                 {
    00402                         m_vobs.AddTail(vob);
    00403                 }
    00404
    00405                 Log(LOG_INFO, str);
    00406         }
    00407
    00408         if(m_vobs.GetCount() <= 0)
    00409         {
    00410                 Log(LOG_ERROR, _T("Nothing found! (%s*.vob)"), fn);
    00411                 return(false);
    00412         }
    00413 */
    00414         CList<CString> vobs;
    00415         if(!m_vob.Open(fn, vobs/*m_vobs*/))
    00416         {
    00417                 Log(LOG_ERROR, _T("Cannot open vob sequence"));
    00418                 return(false);
    00419         }
    00420
    00421         if(vobs.GetCount() <= 0)
    00422         {
    00423                 Log(LOG_ERROR, _T("Nothing found! (%s*.vob)"), fn);
    00424                 return(false);
    00425         }
    00426
    00427         POSITION pos = vobs.GetHeadPosition();
    00428         while(pos) Log(LOG_INFO, _T("Found ") + vobs.GetNext(pos));
    00429
    00430         if(m_vob.IsDVD())
    00431         {
    00432                 Log(LOG_INFO, _T("DVD detected..."));
    00433
    00434                 BYTE key[5];
    00435
    00436                 if(m_vob.HasDiscKey(key))
    00437                         Log(LOG_INFO, _T("Disc key: %02x%02x%02x%02x%02x"), key[0], key[1], key[2], key[3], key[4]);
    00438                 else
    00439                         Log(LOG_WARNING, _T("Couldn't get the disc key"));
    00440
    00441                 if(m_vob.HasTitleKey(key))
    00442                         Log(LOG_INFO, _T("Title key: %02x%02x%02x%02x%02x"), key[0], key[1], key[2], key[3], key[4]);
    00443                 else
    00444                         Log(LOG_WARNING, _T("Couldn't get the title key"));
    00445
    00446                 BYTE buff[2048];
    00447
    00448                 m_vob.Seek(0);
    00449                 if(!m_vob.Read(buff))
    00450                 {
    00451                         Log(LOG_ERROR, _T("Can't read vob, please unlock it with a software player!"));
    00452                         return(false);
    00453                 }
    00454                 m_vob.Seek(0);
    00455         }
    00456
    00457         return(true);
    00458 }
    00459
    00460 DWORD CVobSubFileRipper::ThreadProc()
    00461 {
    00462         SetThreadPriority(m_hThread, THREAD_PRIORITY_BELOW_NORMAL);
    00463
    00464     while(1)
    00465         {
    00466                 DWORD cmd = GetRequest();
    00467
    00468                 m_fThreadActive = true;
    00469
    00470                 switch(cmd)
    00471                 {
    00472                 case CMD_EXIT:
    00473                     Reply(S_OK);
    00474                         return 0;
    00475
    00476                 case CMD_INDEX:
    00477                         Reply(S_OK);
    00478                         {
    00479                         m_fIndexing = true;
    00480                         bool fSucceeded = Create();
    00481                         m_fIndexing = false;
    00482                         Finished(fSucceeded);
    00483                         }
    00484                         break;
    00485
    00486                 default:
    00487                     Reply(E_FAIL);
    00488                         return -1;
    00489                 }
    00490
    00491                 m_fBreakThread = false;
    00492                 m_fThreadActive = false;
    00493         }
    00494
    00495         return 1;
    00496 }
    00497
    00498 static int SubPosSortProc(const void* e1, const void* e2)
    00499 {
    00500         return((int)(((CVobSubFile::SubPos*)e1)->start - ((CVobSubFile::SubPos*)e2)->start));
    00501 }
    00502
    00503 bool CVobSubFileRipper::Create()
    00504 {
    00505         CAutoLock cAutoLock(&m_csAccessLock);
    00506
    00507         if(m_rd.iSelPGC < 0 || m_rd.iSelPGC >= m_rd.pgcs.GetCount())
    00508         {
    00509                 Log(LOG_ERROR, _T("Invalid program chain number (%d)!"), m_rd.iSelPGC);
    00510                 return(false);
    00511         }
    00512
    00513         PGC& pgc = m_rd.pgcs[m_rd.iSelPGC];
    00514
    00515         if(pgc.iSelAngle < 0 || pgc.iSelAngle > 9 || pgc.angles[pgc.iSelAngle].GetCount() == 0)
    00516         {
    00517                 Log(LOG_ERROR, _T("Invalid angle number (%d)!"), pgc.iSelAngle);
    00518                 return(false);
    00519         }
    00520
    00521         CArray<vc_t>& angle = pgc.angles[pgc.iSelAngle];
    00522
    00523         if(m_rd.selids.GetCount() == 0 && !m_rd.fClosedCaption)
    00524         {
    00525                 Log(LOG_ERROR, _T("No valid stream set to be extacted!"));
    00526                 return(false);
    00527         }
    00528
    00529         if(m_rd.selvcs.GetCount() == 0)
    00530         {
    00531                 Log(LOG_ERROR, _T("No valid vob/cell id set to be extacted!"));
    00532                 return(false);
    00533         }
    00534
    00535         Log(LOG_INFO, _T("Indexing..."));
    00536
    00537         // initalize CVobSubFile
    00538         CVobSubFile::Close();
    00539         InitSettings();
    00540         m_title = m_outfn;
    00541         m_size = m_rd.vidsize;
    00542         TrimExtension(m_title);
    00543         memcpy(m_orgpal, pgc.pal, sizeof(m_orgpal));
    00544         m_sub.SetLength(0);
    00545
    00546         CCDecoder ccdec(m_title + _T(".cc.srt"), m_title + _T(".cc.raw"));
    00547
    00548         CVobDec vd;
    00549
    00550         __int64 SCR, PTS, tOffset = 0, tPrevOffset = 0, tTotal = 0, tStart = 0;
    00551         int vob = 0, cell = 0;
    00552         bool fDiscontinuity = false, fDiscontinuityFixApplied = false, fNavpackFound = false;
    00553
    00554         int PTSframeoffset = 0, minPTSframeoffset = 0;
    00555
    00556         if(m_rd.fResetTime)
    00557         {
    00558                 for(int i = 0; i < angle.GetCount() && ((angle[i].vob<<16)|angle[i].cell) != m_rd.selvcs[0]; i++)
    00559                         tStart += angle[i].tTime;
    00560
    00561                 Log(LOG_INFO, _T("Counting timestamps from %I64dms (v%02dc%02d)"),
    00562                         tStart, m_rd.selvcs[0]>>16, m_rd.selvcs[0]&0xffff);
    00563         }
    00564
    00565         CMap<DWORD, DWORD, int, int> selvcmap;
    00566         selvcmap.RemoveAll();
    00567         for(int i = 0; i < m_rd.selvcs.GetCount(); i++)
    00568                 selvcmap[m_rd.selvcs[i]] = 90000;
    00569
    00570         CArray<vcchunk> chunks, foundchunks, loadedchunks;
    00571
    00572         if(m_vob.IsDVD())
    00573         {
    00574                 Log(LOG_INFO, _T("Indexing mode: DVD"));
    00575
    00576                 for(int i = 0; i < angle.GetCount(); i++)
    00577                 {
    00578                         UINT vc = (angle[i].vob<<16)|angle[i].cell;
    00579                         if(selvcmap.PLookup(vc) == NULL)
    00580                                 continue;
    00581
    00582                         vcchunk c = {2048i64*angle[i].start, 2048i64*angle[i].end+2048, vc};
    00583                         chunks.Add(c);
    00584
    00585                         Log(LOG_INFO, _T("Adding: 0x%x - 0x%x (lba) for vob %d cell %d"),
    00586                                 angle[i].start, angle[i].end, angle[i].vob, angle[i].cell);
    00587                 }
    00588         }
    00589         else if(LoadChunks(loadedchunks))
    00590         {
    00591                 Log(LOG_INFO, _T("Indexing mode: File"));
    00592
    00593                 for(int i = 0; i < loadedchunks.GetCount(); i++)
    00594                 {
    00595                         UINT vcid = loadedchunks[i].vc;
    00596                         if(selvcmap.PLookup(vcid) == NULL)
    00597                                 continue;
    00598
    00599                         chunks.Add(loadedchunks[i]);
    00600                 }
    00601
    00602                 Log(LOG_INFO, _T(".chunk file loaded"));
    00603         }
    00604         else
    00605         {
    00606                 Log(LOG_INFO, _T("Indexing mode: File"));
    00607
    00608                 chunks.RemoveAll();
    00609                 vcchunk c = {0, 2048i64*m_vob.GetLength(), 0};
    00610                 chunks.Add(c);
    00611         }
    00612
    00613         __int64 sizedone = 0, sizetotal = 0;
    00614         for(int i = 0; i < chunks.GetCount(); i++)
    00615                 sizetotal += chunks[i].end - chunks[i].start;
    00616
    00617         for(int i = 0; !m_fBreakThread && i < chunks.GetCount(); i++)
    00618         {
    00619                 __int64 curpos = chunks[i].start, endpos = chunks[i].end;
    00620
    00621                 vcchunk curchunk = {curpos, curpos, chunks[i].vc};
    00622
    00623                 for(m_vob.Seek((int)(curpos/2048)); !m_fBreakThread && curpos < endpos; curpos += 2048, sizedone += 2048)
    00624                 {
    00625                         if(!(curpos&0x7ffff))
    00626                                 Progress(1.0 * sizedone / sizetotal);
    00627
    00628                         static BYTE buff[2048];
    00629
    00630                         if(!m_vob.Read(buff))
    00631                         {
    00632                                 Log(LOG_ERROR, _T("Cannot read, either locked dvd or truncated/missing files!"));
    00633                                 return(false);
    00634                         }
    00635
    00636                         curchunk.end = curpos;
    00637
    00638                         if(buff[0x14] & 0x30)
    00639                         {
    00640                                 if(!vd.m_fFoundKey)
    00641                                 {
    00642                                         Log(LOG_INFO, _T("Encrypted sector found, searching key..."));
    00643
    00644                                         __int64 savepos = curpos;
    00645
    00646                                         m_vob.Seek(0);
    00647                                         for(__int64 pos = 0; !m_fBreakThread && pos < endpos; pos += 2048)
    00648                                         {
    00649                                                 if(!m_vob.Read(buff))
    00650                                                 {
    00651                                                         Log(LOG_ERROR, _T("Cannot read, either locked dvd or truncated/missing files!"));
    00652                                                         return(false);
    00653                                                 }
    00654
    00655                                                 if(vd.FindKey(buff))
    00656                                                         break;
    00657                                         }
    00658
    00659                                         if(m_fBreakThread)
    00660                                                 break;
    00661
    00662                                         if(!vd.m_fFoundKey)
    00663                                         {
    00664                                                 Log(LOG_ERROR, _T("Key not found, can't decrypt!"));
    00665                                                 return(false);
    00666                                         }
    00667
    00668                                         Log(LOG_INFO, _T("Key found, continuing extraction..."));
    00669
    00670                                         m_vob.Seek((int)((curpos = savepos)/2048));
    00671                                         m_vob.Read(buff);
    00672                                 }
    00673
    00674                                 vd.Decrypt(buff);
    00675                         }
    00676
    00677                         if(*((DWORD*)&buff[0]) != 0xba010000)
    00678                         {
    00679                                 Log(LOG_WARNING, _T("Bad sector header at block %08d!"), (int)(curpos/2048));
    00680
    00681                                 if(AfxMessageBox(_T("Bad packet header found, do you want to continue?"), MB_YESNO) == IDNO)
    00682                                 {
    00683                                         Log(LOG_ERROR, _T("Terminated!"));
    00684                                         return(false);
    00685                                 }
    00686                         }
    00687
    00688                         SCR = (__int64(buff[0x04] & 0x38) >> 3) << 30
    00689                                 | __int64(buff[0x04] & 0x03) << 28
    00690                                 | __int64(buff[0x05]) << 20
    00691                                 | (__int64(buff[0x06] & 0xf8) >> 3) << 15
    00692                                 | __int64(buff[0x06] & 0x03) << 13
    00693                                 | __int64(buff[0x07]) << 5
    00694                                 | (__int64(buff[0x08] & 0xf8) >> 3) << 0;
    00695
    00696                         bool hasPTS = false;
    00697
    00698                         if((*(DWORD*)&buff[0x0e] == 0xe0010000 || *(DWORD*)&buff[0x0e] == 0xbd010000)
    00699                                 && buff[0x15] & 0x80)
    00700                         {
    00701                                 PTS = (__int64)(buff[0x17] & 0x0e) << 29                // 32-30 (+marker)
    00702                                         | ((__int64)(buff[0x18]) << 22)                         // 29-22
    00703                                         | ((__int64)(buff[0x19] & 0xfe) << 14)          // 21-15 (+marker)
    00704                                         | ((__int64)(buff[0x1a]) << 7)                          // 14-07
    00705                                         | ((__int64)(buff[0x1b]) >> 1);                         // 06-00 (+marker)
    00706
    00707                                 hasPTS = true;
    00708                         }
    00709
    00710                         if(*((DWORD*)&buff[0x0e]) == 0xbb010000)
    00711                         {
    00712                                 fNavpackFound = true;
    00713
    00714                                 if(vob == buff[0x420] && cell == buff[0x422])
    00715                                         continue;
    00716
    00717                                 vob = buff[0x420];
    00718                                 cell = buff[0x422];
    00719
    00720                                 tOffset = tTotal = 0;
    00721
    00722                                 for(int i = 0; i < angle.GetSize(); i++)
    00723                                 {
    00724                                         if(angle[i].vob == vob && angle[i].cell == cell)
    00725                                         {
    00726                                                 tPrevOffset = tOffset;
    00727                                                 tOffset = (__int64)angle[i].tOffset;
    00728                                                 tTotal = (__int64)angle[i].tTotal;
    00729                                                 fDiscontinuity = angle[i].fDiscontinuity;
    00730                                                 fDiscontinuityFixApplied = false;
    00731                                                 break;
    00732                                         }
    00733                                 }
    00734
    00735                                 if(curchunk.vc != ((vob<<16)|cell))
    00736                                 {
    00737                                         if(curchunk.vc != 0) foundchunks.Add(curchunk);
    00738                                         curchunk.start = curchunk.end = curpos;
    00739                                         curchunk.vc = (vob<<16)|cell;
    00740                                 }
    00741
    00742                                 CString str, str2;
    00743                                 str.Format(_T("v%02d c%02d lba%08d"), vob, cell, (int)(curpos/2048));
    00744                                 UINT vcid = (vob<<16)|cell;
    00745                                 if(!selvcmap.Lookup(vcid, minPTSframeoffset)) str2 = _T(", skipping");
    00746                                 else str2.Format(_T(", total=%I64dms, off=%I64dms, corr=%I64dms, discont.:%d"),
    00747                                         tTotal, tOffset, -tStart, (int)fDiscontinuity);
    00748                                 Log(LOG_INFO, str + str2);
    00749                         }
    00750
    00751                         UINT vcid = (vob<<16)|cell;
    00752                         if(!selvcmap.Lookup(vcid, minPTSframeoffset))
    00753                                 continue;
    00754
    00755                         if(hasPTS && fDiscontinuity && !fDiscontinuityFixApplied)
    00756                         {
    00757                                 __int64 tDiff = tOffset - tPrevOffset;
    00758                                 if(tDiff > 0 && tDiff < (PTS/90+1000))
    00759                                 {
    00760                                         CString str;
    00761                                         str.Format(_T("False discontinuity detected, correcting time by %I64dms"), -tDiff);
    00762                                         Log(LOG_INFO, str);
    00763
    00764                                         tStart += tDiff;
    00765                                 }
    00766                                 fDiscontinuityFixApplied = true;
    00767                         }
    00768
    00769                         if(*(DWORD*)&buff[0x0e] == 0xe0010000)
    00770                         {
    00771                                 if(fDiscontinuity)
    00772                                 {
    00773                                         if(PTS < minPTSframeoffset)
    00774                                         {
    00775                                                 selvcmap[vcid] = PTSframeoffset = PTS;
    00776                                         }
    00777
    00778                                         fDiscontinuity = false;
    00779                                 }
    00780
    00781                                 if(m_rd.fClosedCaption)
    00782                                         ccdec.ExtractCC(buff, 2048, tOffset + ((PTS - PTSframeoffset) / 90) - tStart);
    00783                         }
    00784                         else if(*(DWORD*)&buff[0x0e] == 0xbd010000)
    00785                         {
    00786                                 BYTE id = buff[0x17 + buff[0x16]], iLang = id&0x1f;
    00787
    00788                                 if((id & 0xe0) == 0x20 && m_rd.selids.PLookup(iLang))
    00789                                 {
    00790                                         if(hasPTS)
    00791                                         {
    00792                                                 SubPos sb;
    00793                                                 sb.filepos = m_sub.GetPosition();
    00794                                                 sb.start = tOffset + ((PTS - PTSframeoffset) / 90) - tStart;
    00795                                                 sb.vobid = (char)vob;
    00796                                                 sb.cellid = (char)cell;
    00797                                                 sb.celltimestamp = tTotal;
    00798                                                 sb.fValid = true;
    00799                                                 m_langs[iLang].subpos.Add(sb);
    00800                                         }
    00801
    00802                                         m_sub.Write(buff, 2048);
    00803                                 }
    00804                         }
    00805                 }
    00806
    00807                 if(curchunk.vc != ((vob<<16)|cell))
    00808                 {
    00809                         if(curchunk.vc != 0) foundchunks.Add(curchunk);
    00810                         curchunk.start = curchunk.end = curpos;
    00811                         curchunk.vc = (vob<<16)|cell;
    00812                 }
    00813         }
    00814
    00815         if(sizedone < sizetotal)
    00816         {
    00817                 Log(LOG_ERROR, _T("Indexing terminated before reaching the end!"));
    00818                 Progress(0);
    00819                 return(false);
    00820         }
    00821
    00822         if(!fNavpackFound)
    00823         {
    00824                 Log(LOG_ERROR, _T("Could not find any system header start code! (0x000001bb)"));
    00825                 if(!m_vob.IsDVD()) Log(LOG_ERROR, _T("Make sure the ripper doesn't strip these packets."));
    00826                 Progress(0);
    00827                 return(false);
    00828         }
    00829
    00830         Log(LOG_INFO, _T("Indexing finished"));
    00831         Progress(1);
    00832
    00833         for(int i = 0; i < 32; i++)
    00834         {
    00835                 if(m_iLang == -1 && m_langs[i].subpos.GetSize() > 0) m_iLang = i;
    00836                 m_langs[i].id = pgc.ids[i];
    00837                 m_langs[i].name = m_langs[i].alt = FindLangFromId(m_langs[i].id);
    00838
    00839                 CArray<SubPos>& sp = m_langs[i].subpos;
    00840                 qsort(sp.GetData(), sp.GetSize(), sizeof(SubPos), SubPosSortProc);
    00841
    00842                 if(m_rd.fForcedOnly)
    00843                 {
    00844                         Log(LOG_INFO, _T("Searching for forced subs..."));
    00845                         Progress(0);
    00846
    00847                         for(int j = 0, len = sp.GetCount(); j < len; j++)
    00848                         {
    00849                                 Progress(1.0 * j / len);
    00850
    00851                                 sp[j].fValid = false;
    00852                                 int packetsize = 0, datasize = 0;
    00853                                 if(BYTE* buff = GetPacket(j, packetsize, datasize, i))
    00854                                 {
    00855                                         m_img.GetPacketInfo(buff, packetsize, datasize);
    00856                                         sp[j].fValid = m_img.fForced;
    00857                                         delete [] buff;
    00858                                 }
    00859                         }
    00860
    00861                         Progress(1);
    00862                 }
    00863         }
    00864
    00865         Log(LOG_INFO, _T("Saving files..."));
    00866
    00867         if(m_iLang != -1)
    00868         {
    00869                 if(!Save(m_title))
    00870                 {
    00871                         Log(LOG_ERROR, _T("Could not save output files!"));
    00872                         return(false);
    00873                 }
    00874         }
    00875
    00876         Log(LOG_INFO, _T("Subtitles saved"));
    00877
    00878         if(!m_vob.IsDVD() && loadedchunks.GetCount() == 0)
    00879         {
    00880                 if(SaveChunks(foundchunks))
    00881                 {
    00882                         Log(LOG_INFO, _T(".chunk file saved"));
    00883                 }
    00884         }
    00885
    00886         Log(LOG_INFO, _T("Done!"));
    00887
    00888         return(true);
    00889 }
    00890
    00891 static const DWORD s_version = 1;
    00892
    00893 bool CVobSubFileRipper::LoadChunks(CArray<vcchunk>& chunks)
    00894 {
    00895         CFile f;
    00896
    00897         CString fn = m_infn;
    00898         TrimExtension(fn);
    00899         fn += _T(".chunks");
    00900
    00901         DWORD chksum = 0, chunklen, version;
    00902         __int64 voblen;
    00903
    00904         if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
    00905                 return(false);
    00906         f.Read(&version, sizeof(version));
    00907         if(version == 1)
    00908         {
    00909                 f.Read(&chksum, sizeof(chksum));
    00910                 f.Read(&voblen, sizeof(voblen));
    00911                 f.Read(&chunklen, sizeof(chunklen));
    00912                 chunks.SetSize(chunklen);
    00913                 f.Read(chunks.GetData(), sizeof(vcchunk)*chunks.GetCount());
    00914         }
    00915         f.Close();
    00916
    00917         if(voblen != m_vob.GetLength())
    00918         {
    00919                 chunks.RemoveAll();
    00920                 return(false);
    00921         }
    00922
    00923         if(!f.Open(m_infn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
    00924                 return(false);
    00925         DWORD dw, chksum2 = 0;
    00926         while(f.Read(&dw, sizeof(dw)) == sizeof(dw)) chksum2 += dw;
    00927         f.Close();
    00928
    00929         if(chksum != chksum2)
    00930         {
    00931                 chunks.RemoveAll();
    00932                 return(false);
    00933         }
    00934
    00935         return(true);
    00936 }
    00937
    00938 bool CVobSubFileRipper::SaveChunks(CArray<vcchunk>& chunks)
    00939 {
    00940         CFile f;
    00941
    00942         CString fn = m_infn;
    00943         TrimExtension(fn);
    00944         fn += _T(".chunks");
    00945
    00946         DWORD chksum = 0, chunklen = chunks.GetCount();
    00947         __int64 voblen = m_vob.GetLength();
    00948
    00949         if(!f.Open(m_infn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
    00950                 return(false);
    00951         DWORD dw;
    00952         while(f.Read(&dw, sizeof(dw)) == sizeof(dw)) chksum += dw;
    00953         f.Close();
    00954
    00955         if(!f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
    00956                 return(false);
    00957         f.Write(&s_version, sizeof(s_version));
    00958         f.Write(&chksum, sizeof(chksum));
    00959         f.Write(&voblen, sizeof(voblen));
    00960         f.Write(&chunklen, sizeof(chunklen));
    00961         f.Write(chunks.GetData(), sizeof(vcchunk)*chunklen);
    00962         f.Close();
    00963
    00964         return(true);
    00965 }
    00966
    00967 // IVSFRipper
    00968
    00969 STDMETHODIMP CVobSubFileRipper::SetCallBack(IVSFRipperCallback* pCallback)
    00970 {
    00971         CAutoLock cAutoLock(&m_csCallback);
    00972         m_pCallback = pCallback;
    00973         return S_OK;
    00974 }
    00975
    00976 STDMETHODIMP CVobSubFileRipper::LoadParamFile(CString fn)
    00977 {
    00978         CAutoLock cAutoLock(&m_csAccessLock);
    00979
    00980         m_rd.Reset();
    00981
    00982         CStdioFile f;
    00983         if(!f.Open(fn, CFile::modeRead|CFile::typeText))
    00984                 return E_FAIL;
    00985
    00986         TCHAR langid[256];
    00987
    00988         enum {P_INPUT, P_OUTPUT, P_PGC, P_ANGLE, P_LANGS, P_OPTIONS};
    00989         int phase = P_INPUT;
    00990
    00991         CString line;
    00992         while(f.ReadString(line))
    00993         {
    00994                 if(line.Trim().IsEmpty() || line[0] == '#') continue;
    00995
    00996                 if(phase == P_INPUT)
    00997                 {
    00998                         if(S_OK != SetInput(line)) break;
    00999                         phase = P_OUTPUT;
    01000                 }
    01001                 else if(phase == P_OUTPUT)
    01002                 {
    01003                         if(S_OK != SetOutput(line)) break;
    01004                         phase = P_PGC;
    01005                 }
    01006                 else if(phase == P_PGC)
    01007                 {
    01008                         m_rd.iSelPGC = _tcstol(line, NULL, 10)-1;
    01009                         if(m_rd.iSelPGC < 0 || m_rd.iSelPGC >= m_rd.pgcs.GetCount()) break;
    01010                         phase = P_ANGLE;
    01011                 }
    01012                 else if(phase == 3)
    01013                 {
    01014                         PGC& pgc = m_rd.pgcs[m_rd.iSelPGC];
    01015
    01016                         pgc.iSelAngle = _tcstol(line, NULL, 10);
    01017                         if(pgc.iSelAngle < 0 || pgc.iSelAngle > max(1, pgc.nAngles) || pgc.iSelAngle > 9) break;
    01018
    01019                         CArray<vc_t>& angle = pgc.angles[pgc.iSelAngle];
    01020
    01021                         if(line.Find('v') >= 0)
    01022                         {
    01023                                 int vob = 0, cell = 0;
    01024
    01025                                 line += ' ';
    01026
    01027                                 TCHAR* s = (LPTSTR)(LPCTSTR)line;
    01028                                 TCHAR* e = s + line.GetLength();
    01029                                 while(s < e)
    01030                                 {
    01031                                         if(*s == 'v' || s == e-1)
    01032                                         {
    01033                                                 s++;
    01034                                                 if(vob != 0 && cell == 0)
    01035                                                 {
    01036                                                         for(int i = 0; i < angle.GetCount(); i++)
    01037                                                         {
    01038                                                                 if(angle[i].vob == vob)
    01039                                                                         m_rd.selvcs.Add((angle[i].vob<<16)|angle[i].cell);
    01040                                                         }
    01041                                                 }
    01042
    01043                                                 vob = _tcstol(s, &s, 10);
    01044                                                 cell = 0;
    01045                                         }
    01046                                         else if(*s == 'c' && vob > 0)
    01047                                         {
    01048                                                 s++;
    01049                                                 cell = _tcstol(s, &s, 10);
    01050
    01051                                                 for(int i = 0; i < angle.GetCount(); i++)
    01052                                                 {
    01053                                                         if(angle[i].vob == vob && angle[i].cell == cell)
    01054                                                         {
    01055                                                                 m_rd.selvcs.Add((vob<<16)|cell);
    01056                                                                 break;
    01057                                                         }
    01058                                                 }
    01059                                         }
    01060                                         else
    01061                                         {
    01062                                                 s++;
    01063                                         }
    01064                                 }
    01065                         }
    01066                         else
    01067                         {
    01068                                 for(int i = 0; i < angle.GetCount(); i++)
    01069                                         m_rd.selvcs.Add((angle[i].vob<<16)|angle[i].cell);
    01070                         }
    01071
    01072                         phase = P_LANGS;
    01073                 }
    01074                 else if(phase == 4)
    01075                 {
    01076                         if(!line.CompareNoCase(_T("ALL")))
    01077                         {
    01078                                 for(int i = 0; i < 32; i++) m_rd.selids[i] = true;
    01079                                 m_rd.fClosedCaption = true;
    01080                                 phase = P_OPTIONS;
    01081                         }
    01082                         else
    01083                         {
    01084                                 line += ' ';
    01085
    01086                                 while(line.GetLength() > 0)
    01087                                 {
    01088                                         int n = line.Find(_T(" "));
    01089
    01090                                         CString lang = line.Left(n);
    01091
    01092                                         line = line.Mid(n);
    01093                                         line.TrimLeft();
    01094
    01095                                         n = 0;
    01096
    01097                                         int langnum;
    01098
    01099                                         if(_istdigit(lang[0]))
    01100                                         {
    01101                                                 n = _stscanf(lang, _T("%d"), &langnum);
    01102                                                 if(n != 1) break;
    01103
    01104                                                 m_rd.selids[langnum] = true;
    01105                                         }
    01106                                         else if(_istalpha(lang[0]))
    01107                                         {
    01108                                                 n = _stscanf(lang, _T("%s"), langid);
    01109                                                 if(n != 1) break;
    01110
    01111                                                 int id = (langid[0] << 8) + langid[1];
    01112
    01113                                                 if(id == 'cc')
    01114                                                 {
    01115                                                         m_rd.fClosedCaption = true;
    01116                                                 }
    01117                                                 else
    01118                                                 {
    01119                                                         m_rd.selids[id] = true;
    01120                                                 }
    01121                                         }
    01122                                         else break;
    01123
    01124                                         if(n != 1) break;
    01125                                 }
    01126
    01127                                 if((m_rd.selids.GetSize() > 0 || m_rd.fClosedCaption) && line.IsEmpty())
    01128                                         phase = P_OPTIONS;
    01129                         }
    01130                 }
    01131                 else if(phase == 5 && !line.CompareNoCase(_T("CLOSE")))
    01132                         m_rd.fClose = true;
    01133                 else if(phase == 5 && !line.CompareNoCase(_T("BEEP")))
    01134                         m_rd.fBeep = true;
    01135                 else if(phase == 5 && !line.CompareNoCase(_T("RESETTIME")))
    01136                         m_rd.fResetTime = true;
    01137                 else if(phase == 5 && !line.CompareNoCase(_T("FORCEDONLY")))
    01138                         m_rd.fForcedOnly = true;
    01139                 else if(phase == 5 && !line.CompareNoCase(_T("CLOSEIGNOREERRORS")))
    01140                         m_rd.fCloseIgnoreError = true;
    01141
    01142         }
    01143
    01144         m_rd.fAuto = true;
    01145
    01146         return phase == P_OPTIONS ? S_OK : E_FAIL;
    01147 }
    01148
    01149 STDMETHODIMP CVobSubFileRipper::SetInput(CString infn)
    01150 {
    01151         CAutoLock cAutoLock(&m_csAccessLock);
    01152
    01153         m_rd.Reset();
    01154
    01155         if(!LoadIfo(infn) || !LoadVob(infn))
    01156                 return E_INVALIDARG;
    01157
    01158         m_infn = infn;
    01159
    01160         return S_OK;
    01161 }
    01162
    01163 STDMETHODIMP CVobSubFileRipper::SetOutput(CString outfn)
    01164 {
    01165         CAutoLock cAutoLock(&m_csAccessLock);
    01166         m_outfn = outfn;
    01167         return S_OK;
    01168 }
    01169
    01170 STDMETHODIMP CVobSubFileRipper::GetRipperData(VSFRipperData& rd)
    01171 {
    01172         CAutoLock cAutoLock(&m_csAccessLock);
    01173         rd.Copy(m_rd);
    01174         return S_OK;
    01175 }
    01176
    01177 STDMETHODIMP CVobSubFileRipper::UpdateRipperData(VSFRipperData& rd)
    01178 {
    01179         CAutoLock cAutoLock(&m_csAccessLock);
    01180         m_rd.Copy(rd);
    01181         return S_OK;
    01182 }
    01183
    01184 STDMETHODIMP CVobSubFileRipper::Index()
    01185 {
    01186         if(m_fIndexing) return E_FAIL;
    01187         CAMThread::CallWorker(CMD_INDEX);
    01188         return S_OK;
    01189 }
    01190
    01191
    01192 STDMETHODIMP CVobSubFileRipper::IsIndexing()
    01193 {
    01194         return m_fIndexing ? S_OK : S_FALSE;
    01195 }
    01196
    01197 STDMETHODIMP CVobSubFileRipper::Abort(bool fSavePartial)
    01198 {
    01199         m_fBreakThread = true;
    01200         return S_OK;
    01201 }
    01202
    01203 //
    01204
    01205 void VSFRipperData::Reset()
    01206 {
    01207         vidsize.SetSize(0,0);
    01208         memset(&vidinfo, 0, sizeof(vidinfo));
    01209         pgcs.RemoveAll();
    01210         iSelPGC = -1;
    01211         fResetTime = fClosedCaption = true;
    01212         fForcedOnly = false;
    01213         fClose = fBeep = fAuto = false;
    01214         fCloseIgnoreError = false;
    01215
    01216         selvcs.RemoveAll();
    01217         selids.RemoveAll();
    01218 }
    01219
    01220 void VSFRipperData::Copy(VSFRipperData& rd)
    01221 {
    01222         Reset();
    01223
    01224         vidsize = rd.vidsize;
    01225         vidinfo = rd.vidinfo;
    01226         if(int len = rd.pgcs.GetCount())
    01227         {
    01228                 pgcs.SetSize(len);
    01229                 for(int i = 0; i < len; i++)
    01230                 {
    01231                         PGC& src = rd.pgcs[i];
    01232                         PGC& dst = pgcs[i];
    01233                         dst.nAngles = src.nAngles;
    01234                         for(int i = 0; i < countof(dst.angles); i++)
    01235                                 dst.angles[i].Copy(src.angles[i]);
    01236                         dst.iSelAngle = src.iSelAngle;
    01237                         memcpy(dst.pal, src.pal, sizeof(src.pal));
    01238                         memcpy(dst.ids, src.ids, sizeof(src.ids));
    01239                 }
    01240         }
    01241         iSelPGC = rd.iSelPGC;
    01242         fResetTime = rd.fResetTime;
    01243         fClosedCaption = rd.fClosedCaption;
    01244         fForcedOnly = rd.fForcedOnly;
    01245         fClose = rd.fClose;
    01246         fBeep = rd.fBeep;
    01247         fAuto = rd.fAuto;
    01248         fCloseIgnoreError = rd.fCloseIgnoreError;
    01249         selvcs.Copy(rd.selvcs);
    01250         POSITION pos = rd.selids.GetStartPosition();
    01251         while(pos)
    01252         {
    01253                 BYTE key;
    01254                 bool val;
    01255                 rd.selids.GetNextAssoc(pos, key, val);
    01256                 selids[key] = val;
    01257         }
    01258 }
    01259