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)
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
23 #include "vobsubfileripper.h"
24 #include "..\decss\VobDec.h"
25 #include "..\subtitles\CCDecoder.h"
31 CVobSubFileRipper::CVobSubFileRipper()
33 , m_fThreadActive(false)
34 , m_fBreakThread(false)
41 CVobSubFileRipper::~CVobSubFileRipper()
43 CAMThread::CallWorker(CMD_EXIT
);
47 STDMETHODIMP
CVobSubFileRipper::NonDelegatingQueryInterface(REFIID riid
, void** ppv
)
51 __super::NonDelegatingQueryInterface(riid
, ppv
);
54 void CVobSubFileRipper::Log(log_t type
, LPCTSTR lpszFormat
, ...)
56 CAutoLock
cAutoLock(&m_csCallback
);
57 if(!m_pCallback
) return;
62 va_start(args
, lpszFormat
);
63 _vstprintf(buff
, lpszFormat
, args
);
70 case LOG_INFO
: msg
= _T(""); break;
71 case LOG_WARNING
: msg
= _T("WARNING: "); break;
72 case LOG_ERROR
: msg
= _T("ERROR: "); break;
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)
114 if(!CFileGetStatus(fn
, status
) || !status
.m_size
)
116 Log(LOG_ERROR
, _T("Invalid ifo"));
121 if(!f
.Open(fn
, CFile::modeRead
|CFile::typeBinary
|CFile::shareDenyWrite
))
123 Log(LOG_ERROR
, _T("Cannot open ifo"));
127 Log(LOG_INFO
, _T("Opening ifo OK"));
132 if(strcmp(hdr
, "DVDVIDEO-VTS"))
134 Log(LOG_ERROR
, _T("Not a Video Title Set IFO file!"));
140 f
.Seek(0x254, CFile::begin
);
143 memset(ids
, 0, sizeof(ids
));
148 for(int i
= 0; i
< len
; i
++)
150 f
.Seek(2, CFile::current
); // 01 00 ?
152 if(ids
[i
] == 0) ids
[i
] = '--';
153 f
.Seek(2, CFile::current
); // 00 00 ?
158 f
.Seek(0x200, CFile::begin
);
159 f
.Read(&m_rd
.vidinfo
, 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;
179 f
.Seek(0xc0+0x0c, CFile::begin
);
184 f
.Seek(pgcpos
, CFile::begin
);
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
);
199 f
.Seek(offset
+ 2, CFile::begin
);
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
)
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
];
234 f
.Seek(offset
+ 0xa4, CFile::begin
);
236 for(int j
= 0; j
< 16; j
++)
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
);
257 f
.Seek(offset
+ 0xe8, CFile::begin
);
258 ReadBEw(celladdroff
);
259 f
.Seek(offset
+ 0xea, CFile::begin
);
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
);
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;
291 f
.Seek(offset
+ celladdroff
, CFile::begin
);
292 for(int j
= 0; j
< nCells
; j
++)
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
);
313 switch((pgc
.angles
[0][j
].tTime
>>6)&0x3)
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
)
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
;
362 Log(LOG_INFO
, _T("Parsing ifo OK"));
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++)
378 vob.Format(_T("%s%d.vob"), fn, i);
381 if(!(CFileGetStatus(vob, status) && status.m_size))
387 if(status.m_size&0x7ff)
389 Log(LOG_ERROR, _T("Length of %s is not n*2048!"), vob);
394 CString str = _T("Found ") + vob;
398 str += _T(" (skipping, if not a menu vob rename it)");
408 if(m_vobs.GetCount() <= 0)
410 Log(LOG_ERROR, _T("Nothing found! (%s*.vob)"), fn);
414 CAtlList
<CString
> vobs
;
415 if(!m_vob
.Open(fn
, vobs
/*m_vobs*/))
417 Log(LOG_ERROR
, _T("Cannot open vob sequence"));
421 if(vobs
.GetCount() <= 0)
423 Log(LOG_ERROR
, _T("Nothing found! (%s*.vob)"), fn
);
427 POSITION pos
= vobs
.GetHeadPosition();
428 while(pos
) Log(LOG_INFO
, _T("Found ") + vobs
.GetNext(pos
));
432 Log(LOG_INFO
, _T("DVD detected..."));
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]);
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]);
444 Log(LOG_WARNING
, _T("Couldn't get the title key"));
449 if(!m_vob
.Read(buff
))
451 Log(LOG_ERROR
, _T("Can't read vob, please unlock it with a software player!"));
460 DWORD
CVobSubFileRipper::ThreadProc()
462 SetThreadPriority(m_hThread
, THREAD_PRIORITY_BELOW_NORMAL
);
466 DWORD cmd
= GetRequest();
468 m_fThreadActive
= true;
480 bool fSucceeded
= Create();
482 Finished(fSucceeded
);
491 m_fBreakThread
= false;
492 m_fThreadActive
= false;
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
);
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
);
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!"));
529 if(m_rd
.selvcs
.GetCount() == 0)
531 Log(LOG_ERROR
, _T("No valid vob/cell id set to be extacted!"));
535 Log(LOG_INFO
, _T("Indexing..."));
537 // initalize CVobSubFile
538 CVobSubFile::Close();
541 m_size
= m_rd
.vidsize
;
542 TrimExtension(m_title
);
543 memcpy(m_orgpal
, pgc
.pal
, sizeof(m_orgpal
));
546 CCDecoder
ccdec(m_title
+ _T(".cc.srt"), m_title
+ _T(".cc.raw"));
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;
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
;
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
))
582 vcchunk c
= {2048i64
*angle
[i
].start
, 2048i64
*angle
[i
].end
+2048, vc
};
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
))
599 chunks
.Add(loadedchunks
[i
]);
602 Log(LOG_INFO
, _T(".chunk file loaded"));
606 Log(LOG_INFO
, _T("Indexing mode: File"));
609 vcchunk c
= {0, 2048i64
*m_vob
.GetLength(), 0};
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!"));
636 curchunk
.end
= curpos
;
638 if(buff
[0x14] & 0x30)
642 Log(LOG_INFO
, _T("Encrypted sector found, searching key..."));
644 __int64 savepos
= curpos
;
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!"));
664 Log(LOG_ERROR
, _T("Key not found, can't decrypt!"));
668 Log(LOG_INFO
, _T("Key found, continuing extraction..."));
670 m_vob
.Seek((int)((curpos
= savepos
)/2048));
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!"));
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;
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)
710 if(*((DWORD
*)&buff
[0x0e]) == 0xbb010000)
712 fNavpackFound
= true;
714 if(vob
== buff
[0x420] && 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;
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
;
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
))
755 if(hasPTS
&& fDiscontinuity
&& !fDiscontinuityFixApplied
)
757 __int64 tDiff
= tOffset
- tPrevOffset
;
758 if(tDiff
> 0 && tDiff
< (PTS
/90+1000))
761 str
.Format(_T("False discontinuity detected, correcting time by %I64dms"), -tDiff
);
766 fDiscontinuityFixApplied
= true;
769 if(*(DWORD
*)&buff
[0x0e] == 0xe0010000)
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
))
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
;
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!"));
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."));
830 Log(LOG_INFO
, _T("Indexing finished"));
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
);
844 Log(LOG_INFO
, _T("Searching for forced subs..."));
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
;
865 Log(LOG_INFO
, _T("Saving files..."));
871 Log(LOG_ERROR
, _T("Could not save output files!"));
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!"));
891 static const DWORD s_version
= 1;
893 bool CVobSubFileRipper::LoadChunks(CAtlArray
<vcchunk
>& chunks
)
901 DWORD chksum
= 0, chunklen
, version
;
904 if(!f
.Open(fn
, CFile::modeRead
|CFile::typeBinary
|CFile::shareDenyWrite
))
906 f
.Read(&version
, sizeof(version
));
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());
917 if(voblen
!= m_vob
.GetLength())
923 if(!f
.Open(m_infn
, CFile::modeRead
|CFile::typeBinary
|CFile::shareDenyWrite
))
925 DWORD dw
, chksum2
= 0;
926 while(f
.Read(&dw
, sizeof(dw
)) == sizeof(dw
)) chksum2
+= dw
;
929 if(chksum
!= chksum2
)
938 bool CVobSubFileRipper::SaveChunks(CAtlArray
<vcchunk
>& 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
))
952 while(f
.Read(&dw
, sizeof(dw
)) == sizeof(dw
)) chksum
+= dw
;
955 if(!f
.Open(fn
, CFile::modeCreate
|CFile::modeWrite
|CFile::typeBinary
|CFile::shareDenyWrite
))
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
);
969 STDMETHODIMP
CVobSubFileRipper::SetCallBack(IVSFRipperCallback
* pCallback
)
971 CAutoLock
cAutoLock(&m_csCallback
);
972 m_pCallback
= pCallback
;
976 STDMETHODIMP
CVobSubFileRipper::LoadParamFile(CString fn
)
978 CAutoLock
cAutoLock(&m_csAccessLock
);
983 if(!f
.Open(fn
, CFile::modeRead
|CFile::typeText
))
988 enum {P_INPUT
, P_OUTPUT
, P_PGC
, P_ANGLE
, P_LANGS
, P_OPTIONS
};
992 while(f
.ReadString(line
))
994 if(line
.Trim().IsEmpty() || line
[0] == '#') continue;
998 if(S_OK
!= SetInput(line
)) break;
1001 else if(phase
== P_OUTPUT
)
1003 if(S_OK
!= SetOutput(line
)) break;
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;
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;
1027 TCHAR
* s
= (LPTSTR
)(LPCTSTR
)line
;
1028 TCHAR
* e
= s
+ line
.GetLength();
1031 if(*s
== 'v' || s
== e
-1)
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);
1046 else if(*s
== 'c' && vob
> 0)
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
);
1068 for(size_t i
= 0; i
< angle
.GetCount(); i
++)
1069 m_rd
.selvcs
.Add((angle
[i
].vob
<<16)|angle
[i
].cell
);
1076 if(!line
.CompareNoCase(_T("ALL")))
1078 for(int i
= 0; i
< 32; i
++) m_rd
.selids
[i
] = true;
1079 m_rd
.fClosedCaption
= true;
1086 while(line
.GetLength() > 0)
1088 int n
= line
.Find(_T(" "));
1090 CString lang
= line
.Left(n
);
1099 if(_istdigit(lang
[0]))
1101 n
= _stscanf(lang
, _T("%d"), &langnum
);
1104 m_rd
.selids
[langnum
] = true;
1106 else if(_istalpha(lang
[0]))
1108 n
= _stscanf(lang
, _T("%s"), langid
);
1111 int id
= (langid
[0] << 8) + langid
[1];
1115 m_rd
.fClosedCaption
= true;
1119 m_rd
.selids
[id
] = true;
1127 if((m_rd
.selids
.GetCount() > 0 || m_rd
.fClosedCaption
) && line
.IsEmpty())
1131 else if(phase
== 5 && !line
.CompareNoCase(_T("CLOSE")))
1133 else if(phase
== 5 && !line
.CompareNoCase(_T("BEEP")))
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;
1146 return phase
== P_OPTIONS
? S_OK
: E_FAIL
;
1149 STDMETHODIMP
CVobSubFileRipper::SetInput(CString infn
)
1151 CAutoLock
cAutoLock(&m_csAccessLock
);
1155 if(!LoadIfo(infn
) || !LoadVob(infn
))
1156 return E_INVALIDARG
;
1163 STDMETHODIMP
CVobSubFileRipper::SetOutput(CString outfn
)
1165 CAutoLock
cAutoLock(&m_csAccessLock
);
1170 STDMETHODIMP
CVobSubFileRipper::GetRipperData(VSFRipperData
& rd
)
1172 CAutoLock
cAutoLock(&m_csAccessLock
);
1177 STDMETHODIMP
CVobSubFileRipper::UpdateRipperData(VSFRipperData
& rd
)
1179 CAutoLock
cAutoLock(&m_csAccessLock
);
1184 STDMETHODIMP
CVobSubFileRipper::Index()
1186 if(m_fIndexing
) return E_FAIL
;
1187 CAMThread::CallWorker(CMD_INDEX
);
1192 STDMETHODIMP
CVobSubFileRipper::IsIndexing()
1194 return m_fIndexing
? S_OK
: S_FALSE
;
1197 STDMETHODIMP
CVobSubFileRipper::Abort(bool fSavePartial
)
1199 m_fBreakThread
= true;
1205 void VSFRipperData::Reset()
1207 vidsize
.SetSize(0,0);
1208 memset(&vidinfo
, 0, sizeof(vidinfo
));
1211 fResetTime
= fClosedCaption
= true;
1212 fForcedOnly
= false;
1213 fClose
= fBeep
= fAuto
= false;
1214 fCloseIgnoreError
= false;
1220 void VSFRipperData::Copy(VSFRipperData
& rd
)
1224 vidsize
= rd
.vidsize
;
1225 vidinfo
= rd
.vidinfo
;
1226 if(int len
= rd
.pgcs
.GetCount())
1229 for(int i
= 0; i
< len
; i
++)
1231 PGC
& src
= rd
.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
;
1248 fCloseIgnoreError
= rd
.fCloseIgnoreError
;
1249 selvcs
.Copy(rd
.selvcs
);
1250 POSITION pos
= rd
.selids
.GetStartPosition();
1255 rd
.selids
.GetNextAssoc(pos
, key
, val
);