Merge with MPC-HC 6d1472b2f18266d92e5bc068667de348c0cd3b3b.
[xy_vsfilter.git] / src / subtitles / VobSubFileRipper.cpp
blobfedac9ec0c7db1b8f73e745993afd709bc0ca6ca
1 /*
2 * Copyright (C) 2003-2006 Gabest
3 * http://www.gabest.org
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GNU Make; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "StdAfx.h"
23 #include "vobsubfileripper.h"
24 #include "..\decss\VobDec.h"
25 #include "..\subtitles\CCDecoder.h"
28 // CVobSubFileRipper
31 CVobSubFileRipper::CVobSubFileRipper()
32 : CVobSubFile(NULL)
33 , m_fThreadActive(false)
34 , m_fBreakThread(false)
35 , m_fIndexing(false)
37 m_rd.Reset();
38 CAMThread::Create();
41 CVobSubFileRipper::~CVobSubFileRipper()
43 CAMThread::CallWorker(CMD_EXIT);
44 CAMThread::Close();
47 STDMETHODIMP CVobSubFileRipper::NonDelegatingQueryInterface(REFIID riid, void** ppv)
49 return
50 QI(IVSFRipper)
51 __super::NonDelegatingQueryInterface(riid, ppv);
54 void CVobSubFileRipper::Log(log_t type, LPCTSTR lpszFormat, ...)
56 CAutoLock cAutoLock(&m_csCallback);
57 if(!m_pCallback) return;
59 TCHAR buff[1024];
61 va_list args;
62 va_start(args, lpszFormat);
63 _vstprintf(buff, lpszFormat, args);
64 va_end(args);
66 CString msg;
67 switch(type)
69 default:
70 case LOG_INFO: msg = _T(""); break;
71 case LOG_WARNING: msg = _T("WARNING: "); break;
72 case LOG_ERROR: msg = _T("ERROR: "); break;
75 msg += buff;
77 m_pCallback->OnMessage(msg);
80 void CVobSubFileRipper::Progress(double progress)
82 CAutoLock cAutoLock(&m_csCallback);
83 if(!m_pCallback) return;
85 m_pCallback->OnProgress(progress);
88 void CVobSubFileRipper::Finished(bool fSucceeded)
90 CAutoLock cAutoLock(&m_csCallback);
91 if(!m_pCallback) return;
93 m_pCallback->OnFinished(fSucceeded);
96 #define ReadBEb(var) \
97 f.Read(&((BYTE*)&var)[0], 1); \
99 #define ReadBEw(var) \
100 f.Read(&((BYTE*)&var)[1], 1); \
101 f.Read(&((BYTE*)&var)[0], 1); \
103 #define ReadBEdw(var) \
104 f.Read(&((BYTE*)&var)[3], 1); \
105 f.Read(&((BYTE*)&var)[2], 1); \
106 f.Read(&((BYTE*)&var)[1], 1); \
107 f.Read(&((BYTE*)&var)[0], 1); \
109 bool CVobSubFileRipper::LoadIfo(CString fn)
111 CString str;
113 CFileStatus status;
114 if(!CFileGetStatus(fn, status) || !status.m_size)
116 Log(LOG_ERROR, _T("Invalid ifo"));
117 return(false);
120 CFile f;
121 if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
123 Log(LOG_ERROR, _T("Cannot open ifo"));
124 return(false);
127 Log(LOG_INFO, _T("Opening ifo OK"));
129 char hdr[13];
130 f.Read(hdr, 12);
131 hdr[12] = 0;
132 if(strcmp(hdr, "DVDVIDEO-VTS"))
134 Log(LOG_ERROR, _T("Not a Video Title Set IFO file!"));
135 return(false);
138 // lang ids
140 f.Seek(0x254, CFile::begin);
142 WORD ids[32];
143 memset(ids, 0, sizeof(ids));
145 int len = 0;
146 ReadBEw(len);
148 for(int i = 0; i < len; i++)
150 f.Seek(2, CFile::current); // 01 00 ?
151 ReadBEw(ids[i]);
152 if(ids[i] == 0) ids[i] = '--';
153 f.Seek(2, CFile::current); // 00 00 ?
156 /* Video info */
158 f.Seek(0x200, CFile::begin);
159 f.Read(&m_rd.vidinfo, 2);
161 SIZE res[4][2] =
163 {{720,480},{720,576}},
164 {{704,480},{704,576}},
165 {{352,480},{352,576}},
166 {{352,240},{352,288}}
169 m_rd.vidsize = res[m_rd.vidinfo.source_res][m_rd.vidinfo.system&1];
171 double rate = (m_rd.vidinfo.system == 0) ? 30.0/29.97 : 1.0;
173 /* PGCs */
176 DWORD offset;
178 DWORD pgcpos;
179 f.Seek(0xc0+0x0c, CFile::begin);
180 ReadBEdw(pgcpos);
181 pgcpos *= 0x800;
183 WORD nPGC;
184 f.Seek(pgcpos, CFile::begin);
185 ReadBEw(nPGC);
187 m_rd.pgcs.RemoveAll();
188 m_rd.pgcs.SetCount(nPGC);
190 for(int i = 0; i < nPGC; i++)
192 PGC& pgc = m_rd.pgcs[i];
194 f.Seek(pgcpos + 8 + i*8 + 4, CFile::begin);
195 ReadBEdw(offset);
196 offset += pgcpos;
198 BYTE nProgs, nCells;
199 f.Seek(offset + 2, CFile::begin);
200 ReadBEb(nProgs);
201 ReadBEb(nCells);
205 memcpy(pgc.ids, ids, sizeof(ids));
207 struct splanginfo {BYTE res1, id1, id2, res2;};
208 splanginfo splinfo[32];
210 f.Seek(offset + 0x1c, CFile::begin);
211 f.Read(splinfo, 32*4);
213 for(int j = 0; j < 32; j++)
215 if(splinfo[j].id1 || splinfo[i].id2)
217 WORD tmpids[32];
218 memset(tmpids, 0, sizeof(tmpids));
220 for(j = 0; j < 32; j++)
222 if(!(splinfo[j].res1 & 0x80)) break;
224 pgc.ids[splinfo[j].id1] = ids[j];
225 pgc.ids[splinfo[j].id2] = ids[j];
228 break;
234 f.Seek(offset + 0xa4, CFile::begin);
236 for(int j = 0; j < 16; j++)
238 BYTE y, u, v, tmp;
240 f.Read(&tmp, 1);
241 f.Read(&y, 1);
242 f.Read(&u, 1);
243 f.Read(&v, 1);
245 y = (y-16)*255/219;
247 pgc.pal[j].rgbRed = (BYTE)min(max(1.0*y + 1.4022*(u-128), 0), 255);
248 pgc.pal[j].rgbGreen = (BYTE)min(max(1.0*y - 0.3456*(u-128) - 0.7145*(v-128), 0), 255);
249 pgc.pal[j].rgbBlue = (BYTE)min(max(1.0*y + 1.7710*(v-128), 0) , 255);
254 WORD progoff, celladdroff, vobcelloff;
255 f.Seek(offset + 0xe6, CFile::begin);
256 ReadBEw(progoff);
257 f.Seek(offset + 0xe8, CFile::begin);
258 ReadBEw(celladdroff);
259 f.Seek(offset + 0xea, CFile::begin);
260 ReadBEw(vobcelloff);
264 CAtlArray<BYTE> progs;
265 progs.SetCount(nProgs);
266 f.Seek(offset + progoff, CFile::begin);
267 f.Read(progs.GetData(), nProgs);
271 pgc.angles[0].SetCount(nCells);
272 pgc.iSelAngle = 0;
276 f.Seek(offset + vobcelloff, CFile::begin);
277 for(int j = 0; j < nCells; j++)
279 ReadBEw(pgc.angles[0][j].vob);
280 ReadBEw(pgc.angles[0][j].cell);
285 DWORD tOffset = 0, tTotal = 0;
287 int iAngle = 0;
289 pgc.nAngles = 0;
291 f.Seek(offset + celladdroff, CFile::begin);
292 for(int j = 0; j < nCells; j++)
294 BYTE b;
295 ReadBEb(b);
296 switch(b>>6)
298 case 0: iAngle = 0; break; // normal
299 case 1: iAngle = 1; break; // first angle block
300 case 2: iAngle++; break; // middle angle block
301 case 3: iAngle++; break; // last angle block (no more should follow)
303 pgc.angles[0][j].iAngle = iAngle;
304 pgc.nAngles = max(pgc.nAngles, iAngle);
306 f.Seek(3, CFile::current);
307 ReadBEdw(pgc.angles[0][j].tTime);
308 ReadBEdw(pgc.angles[0][j].start);
309 f.Seek(8, CFile::current);
310 ReadBEdw(pgc.angles[0][j].end);
312 float fps;
313 switch((pgc.angles[0][j].tTime>>6)&0x3)
315 default:
316 case 3: fps = 30; break;
317 case 1: fps = 25; break;
320 int t = pgc.angles[0][j].tTime;
321 int hh = ((t>>28)&0xf)*10+((t>>24)&0xf);
322 int mm = ((t>>20)&0xf)*10+((t>>16)&0xf);
323 int ss = ((t>>12)&0xf)*10+((t>>8)&0xf);
324 int ms = (int)(1000.0 * (((t>>4)&0x3)*10+((t>>0)&0xf)) / fps);
325 pgc.angles[0][j].tTime = (DWORD)((((hh*60+mm)*60+ss)*1000+ms)*rate);
327 // time discontinuity
328 if(b&0x02) tOffset = tTotal;
329 pgc.angles[0][j].fDiscontinuity = !!(b&0x02);
331 pgc.angles[0][j].tTotal = tTotal;
332 pgc.angles[0][j].tOffset = tOffset;
334 tTotal += pgc.angles[0][j].tTime;
337 for(iAngle = 1; iAngle <= 9; iAngle++)
339 tOffset = tTotal = 0;
341 for(int j = 0, k = 0; j < nCells; j++)
343 if(pgc.angles[0][j].iAngle != 0
344 && pgc.angles[0][j].iAngle != iAngle)
345 continue;
347 pgc.angles[iAngle].Add(pgc.angles[0][j]);
349 if(pgc.angles[iAngle][k].fDiscontinuity) tOffset = tTotal;
351 pgc.angles[iAngle][k].tTotal = tTotal;
352 pgc.angles[iAngle][k].tOffset = tOffset;
354 tTotal += pgc.angles[iAngle][k].tTime;
356 k++;
362 Log(LOG_INFO, _T("Parsing ifo OK"));
364 return(true);
367 bool CVobSubFileRipper::LoadVob(CString fn)
369 Log(LOG_INFO, _T("Searching vobs..."));
371 CAtlList<CString> m_vobs;
373 fn = fn.Left(fn.ReverseFind('.')+1);
374 fn.TrimRight(_T(".0123456789"));
375 for(int i = 0; i < 100; i++)
377 CString vob;
378 vob.Format(_T("%s%d.vob"), fn, i);
380 CFileStatus status;
381 if(!(CFileGetStatus(vob, status) && status.m_size))
383 if(i > 0) break;
384 else continue;
387 if(status.m_size&0x7ff)
389 Log(LOG_ERROR, _T("Length of %s is not n*2048!"), vob);
390 m_vobs.RemoveAll();
391 break;
394 CString str = _T("Found ") + vob;
396 if(i == 0)
398 str += _T(" (skipping, if not a menu vob rename it)");
400 else
402 m_vobs.AddTail(vob);
405 Log(LOG_INFO, str);
408 if(m_vobs.GetCount() <= 0)
410 Log(LOG_ERROR, _T("Nothing found! (%s*.vob)"), fn);
411 return(false);
414 CAtlList<CString> vobs;
415 if(!m_vob.Open(fn, vobs/*m_vobs*/))
417 Log(LOG_ERROR, _T("Cannot open vob sequence"));
418 return(false);
421 if(vobs.GetCount() <= 0)
423 Log(LOG_ERROR, _T("Nothing found! (%s*.vob)"), fn);
424 return(false);
427 POSITION pos = vobs.GetHeadPosition();
428 while(pos) Log(LOG_INFO, _T("Found ") + vobs.GetNext(pos));
430 if(m_vob.IsDVD())
432 Log(LOG_INFO, _T("DVD detected..."));
434 BYTE key[5];
436 if(m_vob.HasDiscKey(key))
437 Log(LOG_INFO, _T("Disc key: %02x%02x%02x%02x%02x"), key[0], key[1], key[2], key[3], key[4]);
438 else
439 Log(LOG_WARNING, _T("Couldn't get the disc key"));
441 if(m_vob.HasTitleKey(key))
442 Log(LOG_INFO, _T("Title key: %02x%02x%02x%02x%02x"), key[0], key[1], key[2], key[3], key[4]);
443 else
444 Log(LOG_WARNING, _T("Couldn't get the title key"));
446 BYTE buff[2048];
448 m_vob.Seek(0);
449 if(!m_vob.Read(buff))
451 Log(LOG_ERROR, _T("Can't read vob, please unlock it with a software player!"));
452 return(false);
454 m_vob.Seek(0);
457 return(true);
460 DWORD CVobSubFileRipper::ThreadProc()
462 SetThreadPriority(m_hThread, THREAD_PRIORITY_BELOW_NORMAL);
464 while(1)
466 DWORD cmd = GetRequest();
468 m_fThreadActive = true;
470 switch(cmd)
472 case CMD_EXIT:
473 Reply(S_OK);
474 return 0;
476 case CMD_INDEX:
477 Reply(S_OK);
479 m_fIndexing = true;
480 bool fSucceeded = Create();
481 m_fIndexing = false;
482 Finished(fSucceeded);
484 break;
486 default:
487 Reply(E_FAIL);
488 return -1;
491 m_fBreakThread = false;
492 m_fThreadActive = false;
495 return 1;
498 static int SubPosSortProc(const void* e1, const void* e2)
500 return((int)(((CVobSubFile::SubPos*)e1)->start - ((CVobSubFile::SubPos*)e2)->start));
503 bool CVobSubFileRipper::Create()
505 CAutoLock cAutoLock(&m_csAccessLock);
507 if(m_rd.iSelPGC < 0 || m_rd.iSelPGC >= m_rd.pgcs.GetCount())
509 Log(LOG_ERROR, _T("Invalid program chain number (%d)!"), m_rd.iSelPGC);
510 return(false);
513 PGC& pgc = m_rd.pgcs[m_rd.iSelPGC];
515 if(pgc.iSelAngle < 0 || pgc.iSelAngle > 9 || pgc.angles[pgc.iSelAngle].GetCount() == 0)
517 Log(LOG_ERROR, _T("Invalid angle number (%d)!"), pgc.iSelAngle);
518 return(false);
521 CAtlArray<vc_t>& angle = pgc.angles[pgc.iSelAngle];
523 if(m_rd.selids.GetCount() == 0 && !m_rd.fClosedCaption)
525 Log(LOG_ERROR, _T("No valid stream set to be extacted!"));
526 return(false);
529 if(m_rd.selvcs.GetCount() == 0)
531 Log(LOG_ERROR, _T("No valid vob/cell id set to be extacted!"));
532 return(false);
535 Log(LOG_INFO, _T("Indexing..."));
537 // initalize CVobSubFile
538 CVobSubFile::Close();
539 InitSettings();
540 m_title = m_outfn;
541 m_size = m_rd.vidsize;
542 TrimExtension(m_title);
543 memcpy(m_orgpal, pgc.pal, sizeof(m_orgpal));
544 m_sub.SetLength(0);
546 CCDecoder ccdec(m_title + _T(".cc.srt"), m_title + _T(".cc.raw"));
548 CVobDec vd;
550 __int64 SCR, PTS, tOffset = 0, tPrevOffset = 0, tTotal = 0, tStart = 0;
551 int vob = 0, cell = 0;
552 bool fDiscontinuity = false, fDiscontinuityFixApplied = false, fNavpackFound = false;
554 int PTSframeoffset = 0, minPTSframeoffset = 0;
556 if(m_rd.fResetTime)
558 for(size_t i = 0; i < angle.GetCount() && ((angle[i].vob<<16)|angle[i].cell) != m_rd.selvcs[0]; i++)
559 tStart += angle[i].tTime;
561 Log(LOG_INFO, _T("Counting timestamps from %I64dms (v%02dc%02d)"),
562 tStart, m_rd.selvcs[0]>>16, m_rd.selvcs[0]&0xffff);
565 CAtlMap<DWORD, int> selvcmap;
566 selvcmap.RemoveAll();
567 for(size_t i = 0; i < m_rd.selvcs.GetCount(); i++)
568 selvcmap[m_rd.selvcs[i]] = 90000;
570 CAtlArray<vcchunk> chunks, foundchunks, loadedchunks;
572 if(m_vob.IsDVD())
574 Log(LOG_INFO, _T("Indexing mode: DVD"));
576 for(size_t i = 0; i < angle.GetCount(); i++)
578 DWORD vc = (angle[i].vob<<16)|angle[i].cell;
579 if(!selvcmap.Lookup(vc))
580 continue;
582 vcchunk c = {2048i64*angle[i].start, 2048i64*angle[i].end+2048, vc};
583 chunks.Add(c);
585 Log(LOG_INFO, _T("Adding: 0x%x - 0x%x (lba) for vob %d cell %d"),
586 angle[i].start, angle[i].end, angle[i].vob, angle[i].cell);
589 else if(LoadChunks(loadedchunks))
591 Log(LOG_INFO, _T("Indexing mode: File"));
593 for(size_t i = 0; i < loadedchunks.GetCount(); i++)
595 DWORD vcid = loadedchunks[i].vc;
596 if(!selvcmap.Lookup(vcid))
597 continue;
599 chunks.Add(loadedchunks[i]);
602 Log(LOG_INFO, _T(".chunk file loaded"));
604 else
606 Log(LOG_INFO, _T("Indexing mode: File"));
608 chunks.RemoveAll();
609 vcchunk c = {0, 2048i64*m_vob.GetLength(), 0};
610 chunks.Add(c);
613 __int64 sizedone = 0, sizetotal = 0;
614 for(size_t i = 0; i < chunks.GetCount(); i++)
615 sizetotal += chunks[i].end - chunks[i].start;
617 for(size_t i = 0; !m_fBreakThread && i < chunks.GetCount(); i++)
619 __int64 curpos = chunks[i].start, endpos = chunks[i].end;
621 vcchunk curchunk = {curpos, curpos, chunks[i].vc};
623 for(m_vob.Seek((int)(curpos/2048)); !m_fBreakThread && curpos < endpos; curpos += 2048, sizedone += 2048)
625 if(!(curpos&0x7ffff))
626 Progress(1.0 * sizedone / sizetotal);
628 static BYTE buff[2048];
630 if(!m_vob.Read(buff))
632 Log(LOG_ERROR, _T("Cannot read, either locked dvd or truncated/missing files!"));
633 return(false);
636 curchunk.end = curpos;
638 if(buff[0x14] & 0x30)
640 if(!vd.m_fFoundKey)
642 Log(LOG_INFO, _T("Encrypted sector found, searching key..."));
644 __int64 savepos = curpos;
646 m_vob.Seek(0);
647 for(__int64 pos = 0; !m_fBreakThread && pos < endpos; pos += 2048)
649 if(!m_vob.Read(buff))
651 Log(LOG_ERROR, _T("Cannot read, either locked dvd or truncated/missing files!"));
652 return(false);
655 if(vd.FindKey(buff))
656 break;
659 if(m_fBreakThread)
660 break;
662 if(!vd.m_fFoundKey)
664 Log(LOG_ERROR, _T("Key not found, can't decrypt!"));
665 return(false);
668 Log(LOG_INFO, _T("Key found, continuing extraction..."));
670 m_vob.Seek((int)((curpos = savepos)/2048));
671 m_vob.Read(buff);
674 vd.Decrypt(buff);
677 if(*((DWORD*)&buff[0]) != 0xba010000)
679 Log(LOG_WARNING, _T("Bad sector header at block %08d!"), (int)(curpos/2048));
681 if(AfxMessageBox(_T("Bad packet header found, do you want to continue?"), MB_YESNO) == IDNO)
683 Log(LOG_ERROR, _T("Terminated!"));
684 return(false);
688 SCR = (__int64(buff[0x04] & 0x38) >> 3) << 30
689 | __int64(buff[0x04] & 0x03) << 28
690 | __int64(buff[0x05]) << 20
691 | (__int64(buff[0x06] & 0xf8) >> 3) << 15
692 | __int64(buff[0x06] & 0x03) << 13
693 | __int64(buff[0x07]) << 5
694 | (__int64(buff[0x08] & 0xf8) >> 3) << 0;
696 bool hasPTS = false;
698 if((*(DWORD*)&buff[0x0e] == 0xe0010000 || *(DWORD*)&buff[0x0e] == 0xbd010000)
699 && buff[0x15] & 0x80)
701 PTS = (__int64)(buff[0x17] & 0x0e) << 29 // 32-30 (+marker)
702 | ((__int64)(buff[0x18]) << 22) // 29-22
703 | ((__int64)(buff[0x19] & 0xfe) << 14) // 21-15 (+marker)
704 | ((__int64)(buff[0x1a]) << 7) // 14-07
705 | ((__int64)(buff[0x1b]) >> 1); // 06-00 (+marker)
707 hasPTS = true;
710 if(*((DWORD*)&buff[0x0e]) == 0xbb010000)
712 fNavpackFound = true;
714 if(vob == buff[0x420] && cell == buff[0x422])
715 continue;
717 vob = buff[0x420];
718 cell = buff[0x422];
720 tOffset = tTotal = 0;
722 for(size_t i = 0; i < angle.GetCount(); i++)
724 if(angle[i].vob == vob && angle[i].cell == cell)
726 tPrevOffset = tOffset;
727 tOffset = (__int64)angle[i].tOffset;
728 tTotal = (__int64)angle[i].tTotal;
729 fDiscontinuity = angle[i].fDiscontinuity;
730 fDiscontinuityFixApplied = false;
731 break;
735 if(curchunk.vc != ((vob<<16)|cell))
737 if(curchunk.vc != 0) foundchunks.Add(curchunk);
738 curchunk.start = curchunk.end = curpos;
739 curchunk.vc = (vob<<16)|cell;
742 CString str, str2;
743 str.Format(_T("v%02d c%02d lba%08d"), vob, cell, (int)(curpos/2048));
744 UINT vcid = (vob<<16)|cell;
745 if(!selvcmap.Lookup(vcid, minPTSframeoffset)) str2 = _T(", skipping");
746 else str2.Format(_T(", total=%I64dms, off=%I64dms, corr=%I64dms, discont.:%d"),
747 tTotal, tOffset, -tStart, (int)fDiscontinuity);
748 Log(LOG_INFO, str + str2);
751 DWORD vcid = (vob<<16)|cell;
752 if(!selvcmap.Lookup(vcid, minPTSframeoffset))
753 continue;
755 if(hasPTS && fDiscontinuity && !fDiscontinuityFixApplied)
757 __int64 tDiff = tOffset - tPrevOffset;
758 if(tDiff > 0 && tDiff < (PTS/90+1000))
760 CString str;
761 str.Format(_T("False discontinuity detected, correcting time by %I64dms"), -tDiff);
762 Log(LOG_INFO, str);
764 tStart += tDiff;
766 fDiscontinuityFixApplied = true;
769 if(*(DWORD*)&buff[0x0e] == 0xe0010000)
771 if(fDiscontinuity)
773 if(PTS < minPTSframeoffset)
775 selvcmap[vcid] = PTSframeoffset = PTS;
778 fDiscontinuity = false;
781 if(m_rd.fClosedCaption)
782 ccdec.ExtractCC(buff, 2048, tOffset + ((PTS - PTSframeoffset) / 90) - tStart);
784 else if(*(DWORD*)&buff[0x0e] == 0xbd010000)
786 BYTE id = buff[0x17 + buff[0x16]], iLang = id&0x1f;
788 if((id & 0xe0) == 0x20 && m_rd.selids.Lookup(iLang))
790 if(hasPTS)
792 SubPos sb;
793 sb.filepos = m_sub.GetPosition();
794 sb.start = tOffset + ((PTS - PTSframeoffset) / 90) - tStart;
795 sb.vobid = (char)vob;
796 sb.cellid = (char)cell;
797 sb.celltimestamp = tTotal;
798 sb.fValid = true;
799 m_langs[iLang].subpos.Add(sb);
802 m_sub.Write(buff, 2048);
807 if(curchunk.vc != ((vob<<16)|cell))
809 if(curchunk.vc != 0) foundchunks.Add(curchunk);
810 curchunk.start = curchunk.end = curpos;
811 curchunk.vc = (vob<<16)|cell;
815 if(sizedone < sizetotal)
817 Log(LOG_ERROR, _T("Indexing terminated before reaching the end!"));
818 Progress(0);
819 return(false);
822 if(!fNavpackFound)
824 Log(LOG_ERROR, _T("Could not find any system header start code! (0x000001bb)"));
825 if(!m_vob.IsDVD()) Log(LOG_ERROR, _T("Make sure the ripper doesn't strip these packets."));
826 Progress(0);
827 return(false);
830 Log(LOG_INFO, _T("Indexing finished"));
831 Progress(1);
833 for(int i = 0; i < 32; i++)
835 if(m_iLang == -1 && m_langs[i].subpos.GetCount() > 0) m_iLang = i;
836 m_langs[i].id = pgc.ids[i];
837 m_langs[i].name = m_langs[i].alt = FindLangFromId(m_langs[i].id);
839 CAtlArray<SubPos>& sp = m_langs[i].subpos;
840 qsort(sp.GetData(), sp.GetCount(), sizeof(SubPos), SubPosSortProc);
842 if(m_rd.fForcedOnly)
844 Log(LOG_INFO, _T("Searching for forced subs..."));
845 Progress(0);
847 for(int j = 0, len = sp.GetCount(); j < len; j++)
849 Progress(1.0 * j / len);
851 sp[j].fValid = false;
852 int packetsize = 0, datasize = 0;
853 if(BYTE* buff = GetPacket(j, packetsize, datasize, i))
855 m_img.GetPacketInfo(buff, packetsize, datasize);
856 sp[j].fValid = m_img.fForced;
857 delete [] buff;
861 Progress(1);
865 Log(LOG_INFO, _T("Saving files..."));
867 if(m_iLang != -1)
869 if(!Save(m_title))
871 Log(LOG_ERROR, _T("Could not save output files!"));
872 return(false);
876 Log(LOG_INFO, _T("Subtitles saved"));
878 if(!m_vob.IsDVD() && loadedchunks.GetCount() == 0)
880 if(SaveChunks(foundchunks))
882 Log(LOG_INFO, _T(".chunk file saved"));
886 Log(LOG_INFO, _T("Done!"));
888 return(true);
891 static const DWORD s_version = 1;
893 bool CVobSubFileRipper::LoadChunks(CAtlArray<vcchunk>& chunks)
895 CFile f;
897 CString fn = m_infn;
898 TrimExtension(fn);
899 fn += _T(".chunks");
901 DWORD chksum = 0, chunklen, version;
902 __int64 voblen;
904 if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
905 return(false);
906 f.Read(&version, sizeof(version));
907 if(version == 1)
909 f.Read(&chksum, sizeof(chksum));
910 f.Read(&voblen, sizeof(voblen));
911 f.Read(&chunklen, sizeof(chunklen));
912 chunks.SetCount(chunklen);
913 f.Read(chunks.GetData(), sizeof(vcchunk)*chunks.GetCount());
915 f.Close();
917 if(voblen != m_vob.GetLength())
919 chunks.RemoveAll();
920 return(false);
923 if(!f.Open(m_infn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
924 return(false);
925 DWORD dw, chksum2 = 0;
926 while(f.Read(&dw, sizeof(dw)) == sizeof(dw)) chksum2 += dw;
927 f.Close();
929 if(chksum != chksum2)
931 chunks.RemoveAll();
932 return(false);
935 return(true);
938 bool CVobSubFileRipper::SaveChunks(CAtlArray<vcchunk>& chunks)
940 CFile f;
942 CString fn = m_infn;
943 TrimExtension(fn);
944 fn += _T(".chunks");
946 DWORD chksum = 0, chunklen = chunks.GetCount();
947 __int64 voblen = m_vob.GetLength();
949 if(!f.Open(m_infn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
950 return(false);
951 DWORD dw;
952 while(f.Read(&dw, sizeof(dw)) == sizeof(dw)) chksum += dw;
953 f.Close();
955 if(!f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
956 return(false);
957 f.Write(&s_version, sizeof(s_version));
958 f.Write(&chksum, sizeof(chksum));
959 f.Write(&voblen, sizeof(voblen));
960 f.Write(&chunklen, sizeof(chunklen));
961 f.Write(chunks.GetData(), sizeof(vcchunk)*chunklen);
962 f.Close();
964 return(true);
967 // IVSFRipper
969 STDMETHODIMP CVobSubFileRipper::SetCallBack(IVSFRipperCallback* pCallback)
971 CAutoLock cAutoLock(&m_csCallback);
972 m_pCallback = pCallback;
973 return S_OK;
976 STDMETHODIMP CVobSubFileRipper::LoadParamFile(CString fn)
978 CAutoLock cAutoLock(&m_csAccessLock);
980 m_rd.Reset();
982 CStdioFile f;
983 if(!f.Open(fn, CFile::modeRead|CFile::typeText))
984 return E_FAIL;
986 TCHAR langid[256];
988 enum {P_INPUT, P_OUTPUT, P_PGC, P_ANGLE, P_LANGS, P_OPTIONS};
989 int phase = P_INPUT;
991 CString line;
992 while(f.ReadString(line))
994 if(line.Trim().IsEmpty() || line[0] == '#') continue;
996 if(phase == P_INPUT)
998 if(S_OK != SetInput(line)) break;
999 phase = P_OUTPUT;
1001 else if(phase == P_OUTPUT)
1003 if(S_OK != SetOutput(line)) break;
1004 phase = P_PGC;
1006 else if(phase == P_PGC)
1008 m_rd.iSelPGC = _tcstol(line, NULL, 10)-1;
1009 if(m_rd.iSelPGC < 0 || m_rd.iSelPGC >= m_rd.pgcs.GetCount()) break;
1010 phase = P_ANGLE;
1012 else if(phase == 3)
1014 PGC& pgc = m_rd.pgcs[m_rd.iSelPGC];
1016 pgc.iSelAngle = _tcstol(line, NULL, 10);
1017 if(pgc.iSelAngle < 0 || pgc.iSelAngle > max(1, pgc.nAngles) || pgc.iSelAngle > 9) break;
1019 CAtlArray<vc_t>& angle = pgc.angles[pgc.iSelAngle];
1021 if(line.Find('v') >= 0)
1023 int vob = 0, cell = 0;
1025 line += ' ';
1027 TCHAR* s = (LPTSTR)(LPCTSTR)line;
1028 TCHAR* e = s + line.GetLength();
1029 while(s < e)
1031 if(*s == 'v' || s == e-1)
1033 s++;
1034 if(vob != 0 && cell == 0)
1036 for(size_t i = 0; i < angle.GetCount(); i++)
1038 if(angle[i].vob == vob)
1039 m_rd.selvcs.Add((angle[i].vob<<16)|angle[i].cell);
1043 vob = _tcstol(s, &s, 10);
1044 cell = 0;
1046 else if(*s == 'c' && vob > 0)
1048 s++;
1049 cell = _tcstol(s, &s, 10);
1051 for(size_t i = 0; i < angle.GetCount(); i++)
1053 if(angle[i].vob == vob && angle[i].cell == cell)
1055 m_rd.selvcs.Add((vob<<16)|cell);
1056 break;
1060 else
1062 s++;
1066 else
1068 for(size_t i = 0; i < angle.GetCount(); i++)
1069 m_rd.selvcs.Add((angle[i].vob<<16)|angle[i].cell);
1072 phase = P_LANGS;
1074 else if(phase == 4)
1076 if(!line.CompareNoCase(_T("ALL")))
1078 for(int i = 0; i < 32; i++) m_rd.selids[i] = true;
1079 m_rd.fClosedCaption = true;
1080 phase = P_OPTIONS;
1082 else
1084 line += ' ';
1086 while(line.GetLength() > 0)
1088 int n = line.Find(_T(" "));
1090 CString lang = line.Left(n);
1092 line = line.Mid(n);
1093 line.TrimLeft();
1095 n = 0;
1097 int langnum;
1099 if(_istdigit(lang[0]))
1101 n = _stscanf(lang, _T("%d"), &langnum);
1102 if(n != 1) break;
1104 m_rd.selids[langnum] = true;
1106 else if(_istalpha(lang[0]))
1108 n = _stscanf(lang, _T("%s"), langid);
1109 if(n != 1) break;
1111 int id = (langid[0] << 8) + langid[1];
1113 if(id == 'cc')
1115 m_rd.fClosedCaption = true;
1117 else
1119 m_rd.selids[id] = true;
1122 else break;
1124 if(n != 1) break;
1127 if((m_rd.selids.GetCount() > 0 || m_rd.fClosedCaption) && line.IsEmpty())
1128 phase = P_OPTIONS;
1131 else if(phase == 5 && !line.CompareNoCase(_T("CLOSE")))
1132 m_rd.fClose = true;
1133 else if(phase == 5 && !line.CompareNoCase(_T("BEEP")))
1134 m_rd.fBeep = true;
1135 else if(phase == 5 && !line.CompareNoCase(_T("RESETTIME")))
1136 m_rd.fResetTime = true;
1137 else if(phase == 5 && !line.CompareNoCase(_T("FORCEDONLY")))
1138 m_rd.fForcedOnly = true;
1139 else if(phase == 5 && !line.CompareNoCase(_T("CLOSEIGNOREERRORS")))
1140 m_rd.fCloseIgnoreError = true;
1144 m_rd.fAuto = true;
1146 return phase == P_OPTIONS ? S_OK : E_FAIL;
1149 STDMETHODIMP CVobSubFileRipper::SetInput(CString infn)
1151 CAutoLock cAutoLock(&m_csAccessLock);
1153 m_rd.Reset();
1155 if(!LoadIfo(infn) || !LoadVob(infn))
1156 return E_INVALIDARG;
1158 m_infn = infn;
1160 return S_OK;
1163 STDMETHODIMP CVobSubFileRipper::SetOutput(CString outfn)
1165 CAutoLock cAutoLock(&m_csAccessLock);
1166 m_outfn = outfn;
1167 return S_OK;
1170 STDMETHODIMP CVobSubFileRipper::GetRipperData(VSFRipperData& rd)
1172 CAutoLock cAutoLock(&m_csAccessLock);
1173 rd.Copy(m_rd);
1174 return S_OK;
1177 STDMETHODIMP CVobSubFileRipper::UpdateRipperData(VSFRipperData& rd)
1179 CAutoLock cAutoLock(&m_csAccessLock);
1180 m_rd.Copy(rd);
1181 return S_OK;
1184 STDMETHODIMP CVobSubFileRipper::Index()
1186 if(m_fIndexing) return E_FAIL;
1187 CAMThread::CallWorker(CMD_INDEX);
1188 return S_OK;
1192 STDMETHODIMP CVobSubFileRipper::IsIndexing()
1194 return m_fIndexing ? S_OK : S_FALSE;
1197 STDMETHODIMP CVobSubFileRipper::Abort(bool fSavePartial)
1199 m_fBreakThread = true;
1200 return S_OK;
1205 void VSFRipperData::Reset()
1207 vidsize.SetSize(0,0);
1208 memset(&vidinfo, 0, sizeof(vidinfo));
1209 pgcs.RemoveAll();
1210 iSelPGC = -1;
1211 fResetTime = fClosedCaption = true;
1212 fForcedOnly = false;
1213 fClose = fBeep = fAuto = false;
1214 fCloseIgnoreError = false;
1216 selvcs.RemoveAll();
1217 selids.RemoveAll();
1220 void VSFRipperData::Copy(VSFRipperData& rd)
1222 Reset();
1224 vidsize = rd.vidsize;
1225 vidinfo = rd.vidinfo;
1226 if(int len = rd.pgcs.GetCount())
1228 pgcs.SetCount(len);
1229 for(int i = 0; i < len; i++)
1231 PGC& src = rd.pgcs[i];
1232 PGC& dst = pgcs[i];
1233 dst.nAngles = src.nAngles;
1234 for(int i = 0; i < countof(dst.angles); i++)
1235 dst.angles[i].Copy(src.angles[i]);
1236 dst.iSelAngle = src.iSelAngle;
1237 memcpy(dst.pal, src.pal, sizeof(src.pal));
1238 memcpy(dst.ids, src.ids, sizeof(src.ids));
1241 iSelPGC = rd.iSelPGC;
1242 fResetTime = rd.fResetTime;
1243 fClosedCaption = rd.fClosedCaption;
1244 fForcedOnly = rd.fForcedOnly;
1245 fClose = rd.fClose;
1246 fBeep = rd.fBeep;
1247 fAuto = rd.fAuto;
1248 fCloseIgnoreError = rd.fCloseIgnoreError;
1249 selvcs.Copy(rd.selvcs);
1250 POSITION pos = rd.selids.GetStartPosition();
1251 while(pos)
1253 BYTE key;
1254 bool val;
1255 rd.selids.GetNextAssoc(pos, key, val);
1256 selids[key] = val;