00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "StdAfx.h"
00023 #include "vobsubfileripper.h"
00024 #include "..\decss\VobDec.h"
00025 #include "..\subtitles\CCDecoder.h"
00026
00027
00028
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
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);
00151 ReadBEw(ids[i]);
00152 if(ids[i] == 0) ids[i] = '--';
00153 f.Seek(2, CFile::current);
00154 }
00155
00156
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
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;
00299 case 1: iAngle = 1; break;
00300 case 2: iAngle++; break;
00301 case 3: iAngle++; break;
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
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
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414 CList<CString> vobs;
00415 if(!m_vob.Open(fn, 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
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
00702 | ((__int64)(buff[0x18]) << 22)
00703 | ((__int64)(buff[0x19] & 0xfe) << 14)
00704 | ((__int64)(buff[0x1a]) << 7)
00705 | ((__int64)(buff[0x1b]) >> 1);
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
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