10 #include "mpeg3private.h"
11 #include "mpeg3protos.h"
15 // Bytes relative to start of stream.
18 // Used in final table
20 // Used in cell play info
22 // Used in cell addresses
29 mpeg3ifo_cell_t
*cells
;
32 } mpeg3ifo_celltable_t
;
35 #define CADDR_HDR_LEN 8
38 u_short num
: 16; // Number of Video Objects
39 u_short unknown
: 16; // don't know
40 u_int len
: 32; // length of table
44 u_int foo
: 16; // ???
45 u_int num
: 16; // number of subchannels
48 #define AUDIO_HDR_LEN 4
51 u_short id
: 16; // Language
52 u_short
: 16; // don't know
53 u_int start
: 32; // Start of unit
56 #define PGCI_SUB_LEN 8
58 #define PGCI_COLOR_LEN 4
61 static u_int
get4bytes(u_char
*buf
)
63 return bswap_32 (*((u_int32_t
*)buf
));
66 static u_int
get2bytes(u_char
*buf
)
68 return bswap_16 (*((u_int16_t
*)buf
));
71 static int ifo_read(int fd
, long pos
, long count
, unsigned char *data
)
73 if((pos
= lseek(fd
, pos
, SEEK_SET
)) < 0)
79 return read(fd
, data
, count
);
82 #define OFF_PTT get4bytes (ifo->data[ID_MAT] + 0xC8)
83 #define OFF_TITLE_PGCI get4bytes (ifo->data[ID_MAT] + 0xCC)
84 #define OFF_MENU_PGCI get4bytes (ifo->data[ID_MAT] + 0xD0)
85 #define OFF_TMT get4bytes (ifo->data[ID_MAT] + 0xD4)
86 #define OFF_MENU_CELL_ADDR get4bytes (ifo->data[ID_MAT] + 0xD8)
87 #define OFF_MENU_VOBU_ADDR_MAP get4bytes (ifo->data[ID_MAT] + 0xDC)
88 #define OFF_TITLE_CELL_ADDR get4bytes (ifo->data[ID_MAT] + 0xE0)
89 #define OFF_TITLE_VOBU_ADDR_MAP get4bytes (ifo->data[ID_MAT] + 0xE4)
91 #define OFF_VMG_TSP get4bytes (ifo->data[ID_MAT] + 0xC4)
92 #define OFF_VMG_MENU_PGCI get4bytes (ifo->data[ID_MAT] + 0xC8)
93 #define OFF_VMG_TMT get4bytes (ifo->data[ID_MAT] + 0xD0)
96 static int ifo_vts(ifo_t
*ifo
)
98 if(!strncmp((char*)ifo
->data
[ID_MAT
], "DVDVIDEO-VTS", 12))
105 static int ifo_vmg(ifo_t
*ifo
)
107 if(!strncmp((char*)ifo
->data
[ID_MAT
], "DVDVIDEO-VMG", 12))
113 static int ifo_table(ifo_t
*ifo
, int64_t offset
, unsigned long tbl_id
)
120 if(!offset
) return -1;
122 data
= (u_char
*)calloc(1, DVD_VIDEO_LB_LEN
);
124 if(ifo_read(ifo
->fd
, ifo
->pos
+ offset
* DVD_VIDEO_LB_LEN
, DVD_VIDEO_LB_LEN
, data
) <= 0)
132 case ID_TITLE_VOBU_ADDR_MAP
:
133 case ID_MENU_VOBU_ADDR_MAP
:
134 len
= get4bytes(data
) + 1;
139 ifo_hdr_t
*hdr
= (ifo_hdr_t
*)data
;
140 len
= bswap_32(hdr
->len
) + 1;
144 if(len
> DVD_VIDEO_LB_LEN
)
146 data
= (u_char
*)realloc((void *)data
, len
);
148 ifo_read(ifo
->fd
, ifo
->pos
+ offset
* DVD_VIDEO_LB_LEN
, len
, data
);
151 ifo
->data
[tbl_id
] = data
;
152 ptr
= (u_int32_t
*)data
;
156 for (i
= 0; i
< len
; i
++)
157 ptr
[i
] = bswap_32(ptr
[i
]);
162 static ifo_t
* ifo_open(int fd
, long pos
)
166 ifo
= (ifo_t
*)calloc(sizeof(ifo_t
), 1);
168 ifo
->data
[ID_MAT
] = (unsigned char *)calloc(DVD_VIDEO_LB_LEN
, 1);
173 if(ifo_read(fd
, pos
, DVD_VIDEO_LB_LEN
, ifo
->data
[ID_MAT
]) < 0)
176 free(ifo
->data
[ID_MAT
]);
181 ifo
->num_menu_vobs
= get4bytes(ifo
->data
[ID_MAT
] + 0xC0);
182 ifo
->vob_start
= get4bytes(ifo
->data
[ID_MAT
] + 0xC4);
185 printf ("num of vobs: %x vob_start %x\n", ifo
->num_menu_vobs
, ifo
->vob_start
);
190 ifo_table(ifo
, OFF_PTT
, ID_PTT
);
191 ifo_table(ifo
, OFF_TITLE_PGCI
, ID_TITLE_PGCI
);
192 ifo_table(ifo
, OFF_MENU_PGCI
, ID_MENU_PGCI
);
193 ifo_table(ifo
, OFF_TMT
, ID_TMT
);
194 ifo_table(ifo
, OFF_MENU_CELL_ADDR
, ID_MENU_CELL_ADDR
);
195 ifo_table(ifo
, OFF_MENU_VOBU_ADDR_MAP
, ID_MENU_VOBU_ADDR_MAP
);
196 ifo_table(ifo
, OFF_TITLE_CELL_ADDR
, ID_TITLE_CELL_ADDR
);
197 ifo_table(ifo
, OFF_TITLE_VOBU_ADDR_MAP
, ID_TITLE_VOBU_ADDR_MAP
);
202 ifo_table(ifo
, OFF_VMG_TSP
, ID_TSP
);
203 ifo_table(ifo
, OFF_VMG_MENU_PGCI
, ID_MENU_PGCI
);
204 ifo_table(ifo
, OFF_VMG_TMT
, ID_TMT
);
205 ifo_table(ifo
, OFF_TITLE_CELL_ADDR
, ID_TITLE_CELL_ADDR
);
206 ifo_table(ifo
, OFF_TITLE_VOBU_ADDR_MAP
, ID_TITLE_VOBU_ADDR_MAP
);
213 static int ifo_close(ifo_t
*ifo
)
215 if(ifo
->data
[ID_MAT
]) free(ifo
->data
[ID_MAT
]);
216 if(ifo
->data
[ID_PTT
]) free(ifo
->data
[ID_PTT
]);
217 if(ifo
->data
[ID_TITLE_PGCI
]) free(ifo
->data
[ID_TITLE_PGCI
]);
218 if(ifo
->data
[ID_MENU_PGCI
]) free(ifo
->data
[ID_MENU_PGCI
]);
219 if(ifo
->data
[ID_TMT
]) free(ifo
->data
[ID_TMT
]);
220 if(ifo
->data
[ID_MENU_CELL_ADDR
]) free(ifo
->data
[ID_MENU_CELL_ADDR
]);
221 if(ifo
->data
[ID_MENU_VOBU_ADDR_MAP
]) free(ifo
->data
[ID_MENU_VOBU_ADDR_MAP
]);
222 if(ifo
->data
[ID_TITLE_CELL_ADDR
]) free(ifo
->data
[ID_TITLE_CELL_ADDR
]);
223 if(ifo
->data
[ID_TITLE_VOBU_ADDR_MAP
]) free(ifo
->data
[ID_TITLE_VOBU_ADDR_MAP
]);
230 static int ifo_audio(char *_hdr
, char **ptr
)
232 audio_hdr_t
*hdr
= (audio_hdr_t
*)_hdr
;
236 *ptr
= _hdr
+ AUDIO_HDR_LEN
;
238 return bswap_16(hdr
->num
);
242 static int pgci(ifo_hdr_t
*hdr
, int title
, char **ptr
)
244 pgci_sub_t
*pgci_sub
;
250 if(title
> hdr
->num
) return -1;
254 pgci_sub
= (pgci_sub_t
*)*ptr
+ title
;
256 *ptr
= (char *)hdr
+ bswap_32(pgci_sub
->start
);
261 static int program_map(mpeg3_t
*file
, char *pgc
, unsigned char **ptr
)
274 *ptr
+= 8 * 2; // AUDIO
275 *ptr
+= 32 * 4; // SUBPICTURE
277 // subtitle color palette
278 // *ptr += 16 * PGCI_COLOR_LEN;
280 if(!file
->have_palette
)
282 for(i
= 0; i
< 16; i
++)
284 int r
= (int)*(*ptr
)++;
285 int g
= (int)*(*ptr
)++;
286 int b
= (int)*(*ptr
)++;
289 int y
= (int)(0.29900 * r
+ 0.58700 * g
+ 0.11400 * b
);
290 int u
= (int)(-0.16874 * r
+ -0.33126 * g
+ 0.50000 * b
+ 0x80);
291 int v
= (int)(0.50000 * r
+ -0.41869 * g
+ -0.08131 * b
+ 0x80);
296 file
->palette
[i
* 4] = y
;
297 file
->palette
[i
* 4 + 1] = u
;
298 file
->palette
[i
* 4 + 2] = v
;
299 //printf("color %02d: 0x%02x 0x%02x 0x%02x\n", i, y, u, v);
302 file
->have_palette
= 1;
313 *ptr
= get2bytes((unsigned char*)*ptr
) + pgc
;
319 static u_int
get_cellplayinfo(u_char
*pgc
, u_char
**ptr
)
331 *ptr
+= 8 * 2; // AUDIO
332 *ptr
+= 32 * 4; // SUBPICTURE
334 *ptr
+= 16 * PGCI_COLOR_LEN
; // CLUT
337 *ptr
= get2bytes(*ptr
) + pgc
;
342 static void get_ifo_playlist(mpeg3_t
*file
, mpeg3_demuxer_t
*demuxer
)
345 char directory
[MPEG3_STRLEN
];
346 char filename
[MPEG3_STRLEN
];
347 char complete_path
[MPEG3_STRLEN
];
348 char title_path
[MPEG3_STRLEN
];
349 char vob_prefix
[MPEG3_STRLEN
];
350 struct dirent
*new_filename
;
352 int64_t total_bytes
= 0;
355 // Get titles matching ifo file
356 mpeg3io_complete_path(complete_path
, file
->fs
->path
);
357 mpeg3io_get_directory(directory
, complete_path
);
358 mpeg3io_get_filename(filename
, complete_path
);
359 strncpy(vob_prefix
, filename
, 6);
361 dirstream
= opendir(directory
);
362 while(new_filename
= readdir(dirstream
))
364 if(!strncasecmp(new_filename
->d_name
, vob_prefix
, 6))
366 ptr
= strrchr(new_filename
->d_name
, '.');
367 if(ptr
&& !strncasecmp(ptr
, ".vob", 4))
370 if(atol(&new_filename
->d_name
[7]) > 0)
372 mpeg3_title_t
*title
;
374 mpeg3io_joinpath(title_path
, directory
, new_filename
->d_name
);
375 title
= demuxer
->titles
[demuxer
->total_titles
++] =
376 mpeg3_new_title(file
, title_path
);
377 title
->total_bytes
= mpeg3io_path_total_bytes(title_path
);
378 title
->start_byte
= total_bytes
;
379 title
->end_byte
= total_bytes
+ title
->total_bytes
;
380 total_bytes
+= title
->total_bytes
;
382 mpeg3_new_cell(title
,
389 //printf("%s\n", title_path);
397 // Alphabetize titles. Only problematic for guys who rip entire DVD's
398 // to their hard drives while retaining the file structure.
402 for(i
= 0; i
< demuxer
->total_titles
- 1; i
++)
404 if(strcmp(demuxer
->titles
[i
]->fs
->path
, demuxer
->titles
[i
+ 1]->fs
->path
) > 0)
406 mpeg3_title_t
*temp
= demuxer
->titles
[i
];
407 demuxer
->titles
[i
] = demuxer
->titles
[i
+ 1];
408 demuxer
->titles
[i
+ 1] = temp
;
421 static void get_ifo_header(mpeg3_demuxer_t
*demuxer
, ifo_t
*ifo
)
425 demuxer
->vstream_table
[0] = 1;
432 // Doesn't detect number of tracks.
433 int atracks
= ifo_audio((char*)ifo
->data
[ID_MAT
] + IFO_OFFSET_AUDIO
, (char**)&audio
);
434 int atracks_empirical
= 0;
436 // Collect stream id's
437 #define TEST_START 0x1000000
438 #define TEST_LEN 0x1000000
439 mpeg3demux_open_title(demuxer
, 0);
440 mpeg3demux_seek_byte(demuxer
, TEST_START
);
442 !mpeg3demux_eof(demuxer
) &&
443 mpeg3demux_tell_byte(demuxer
) < TEST_START
+ TEST_LEN
)
445 result
= mpeg3_read_next_packet(demuxer
);
447 mpeg3demux_seek_byte(demuxer
, 0);
449 for(i
= 0; i
< MPEG3_MAX_STREAMS
; i
++)
451 if(demuxer
->astream_table
[i
]) atracks_empirical
++;
454 // Doesn't detect PCM audio or total number of tracks
456 * if(atracks && !atracks_empirical)
457 * for(i = 0; i < atracks; i++)
459 * int audio_mode = AUDIO_AC3;
460 * switch(audio->coding_mode)
462 * case 0: audio_mode = AUDIO_AC3; break;
463 * case 1: audio_mode = AUDIO_MPEG; break;
464 * case 2: audio_mode = AUDIO_MPEG; break;
465 * case 3: audio_mode = AUDIO_PCM; break;
467 * if(!demuxer->astream_table[i + 0x80]) demuxer->astream_table[i + 0x80] = audio_mode;
477 static mpeg3ifo_cell_t
* append_cell(mpeg3ifo_celltable_t
*table
)
479 if(!table
->cells
|| table
->total_cells
>= table
->cells_allocated
)
482 mpeg3ifo_cell_t
*new_cells
;
484 new_allocation
= table
->cells_allocated
? table
->cells_allocated
* 2 : 64;
485 new_cells
= calloc(1, sizeof(mpeg3ifo_cell_t
) * new_allocation
);
488 memcpy(new_cells
, table
->cells
, sizeof(mpeg3ifo_cell_t
) * table
->total_cells
);
491 table
->cells
= new_cells
;
492 table
->cells_allocated
= new_allocation
;
495 return &table
->cells
[table
->total_cells
++];
498 static void delete_celltable(mpeg3ifo_celltable_t
*table
)
500 if(table
->cells
) free(table
->cells
);
504 static void cellplayinfo(mpeg3_t
*file
, ifo_t
*ifo
, mpeg3ifo_celltable_t
*cells
)
507 char *cell_hdr
, *cell_hdr_start
, *cell_info
;
508 ifo_hdr_t
*hdr
= (ifo_hdr_t
*)ifo
->data
[ID_TITLE_PGCI
];
509 int program_chains
= bswap_16(hdr
->num
);
512 //printf("cellplayinfo\n");
513 for(j
= 0; j
< program_chains
; j
++)
516 // Program Chain Information
517 pgci(hdr
, j
, &cell_hdr
);
519 cell_hdr_start
= cell_hdr
;
529 for(i
= 0; i
< 8; i
++) cell_hdr
+= 2;
531 for(i
= 0; i
< 32; i
++) cell_hdr
+= 4;
533 for(i
= 0; i
< 8; i
++) cell_hdr
++;
537 if(program_map(file
, cell_hdr_start
, &cell_hdr
))
541 if(total_cells
= get_cellplayinfo((unsigned char*)cell_hdr_start
, (unsigned char**)&cell_hdr
))
543 //printf("cellplayinfo %d %d\n", j, total_cells);
544 cell_info
= cell_hdr
;
545 for(i
= 0; i
< total_cells
; i
++)
547 ifo_pgci_cell_addr_t
*cell_addr
= (ifo_pgci_cell_addr_t
*)cell_info
;
548 int64_t start_byte
= bswap_32(cell_addr
->vobu_start
);
549 int64_t end_byte
= bswap_32(cell_addr
->vobu_last_end
);
550 int cell_type
= cell_addr
->chain_info
;
552 if(!cells
->total_cells
&& start_byte
> 0)
555 if(!cells
->total_cells
||
556 end_byte
>= cells
->cells
[cells
->total_cells
- 1].end_byte
)
558 mpeg3ifo_cell_t
*cell
= append_cell(cells
);
560 cell
->start_byte
= start_byte
;
561 cell
->end_byte
= end_byte
;
562 cell
->cell_type
= cell_type
;
563 //printf("cellplayinfo start: %llx end: %llx type: %x\n",
564 // (int64_t)cell->start_byte * 0x800, (int64_t)cell->end_byte * 0x800, cell->cell_type);
566 cell_info
+= PGCI_CELL_ADDR_LEN
;
572 static void celladdresses(ifo_t
*ifo
, mpeg3ifo_celltable_t
*cell_addresses
)
575 char *ptr
= (char*)ifo
->data
[ID_TITLE_CELL_ADDR
];
577 cell_addr_hdr_t
*cell_addr_hdr
= (cell_addr_hdr_t
*)ptr
;
578 ifo_cell_addr_t
*cell_addr
= (ifo_cell_addr_t
*)(ptr
+ CADDR_HDR_LEN
);
580 //printf("celladdresses\n");
582 if(total_addresses
= bswap_32(cell_addr_hdr
->len
) / sizeof(ifo_cell_addr_t
))
584 for(i
= 0; i
< total_addresses
; i
++)
586 mpeg3ifo_cell_t
*cell
;
587 cell
= append_cell(cell_addresses
);
588 cell
->start_byte
= (int64_t)bswap_32(cell_addr
->start
);
589 cell
->end_byte
= (int64_t)bswap_32(cell_addr
->end
);
590 cell
->vob_id
= bswap_16(cell_addr
->vob_id
);
591 cell
->cell_id
= cell_addr
->cell_id
;
596 // Sort addresses by address instead of vob id
601 for(i
= 0; i
< total_addresses
- 1; i
++)
603 mpeg3ifo_cell_t
*cell1
, *cell2
;
604 cell1
= &cell_addresses
->cells
[i
];
605 cell2
= &cell_addresses
->cells
[i
+ 1];
607 if(cell1
->start_byte
> cell2
->start_byte
)
609 mpeg3ifo_cell_t temp
= *cell1
;
618 for(i
= 0; i
< total_addresses
; i
++)
620 mpeg3ifo_cell_t
*cell
= &cell_addresses
->cells
[i
];
624 static void finaltable(mpeg3ifo_celltable_t
*final_cells
,
625 mpeg3ifo_celltable_t
*cells
,
626 mpeg3ifo_celltable_t
*cell_addresses
)
628 int input_cell
= 0, current_address
= 0;
633 // Start and end bytes of programs
634 int64_t program_start_byte
[256], program_end_byte
[256];
636 final_cells
->total_cells
= 0;
637 final_cells
->cells_allocated
= cell_addresses
->total_cells
;
638 final_cells
->cells
= calloc(1, sizeof(mpeg3ifo_cell_t
) * final_cells
->cells_allocated
);
640 // Assign programs to cells
642 for(i
= cell_addresses
->total_cells
- 1; i
>= 0; i
--)
644 mpeg3ifo_cell_t
*input
= &cell_addresses
->cells
[i
];
645 mpeg3ifo_cell_t
*output
= &final_cells
->cells
[i
];
646 if(current_vobid
< 0) current_vobid
= input
->vob_id
;
649 // Reduce current vobid
650 if(input
->vob_id
< current_vobid
)
651 current_vobid
= input
->vob_id
;
653 // Get the current program number
654 if(input
->vob_id
> current_vobid
)
656 int current_program
= input
->vob_id
- current_vobid
;
657 output
->program
= current_program
;
659 // Get the last interleave by brute force
661 j
< cell_addresses
->total_cells
&& cell_addresses
->cells
[i
].cell_id
== cell_addresses
->cells
[j
].cell_id
;
664 int new_program
= final_cells
->cells
[j
].vob_id
- current_vobid
;
665 if(new_program
<= current_program
)
666 final_cells
->cells
[j
].program
= new_program
;
670 final_cells
->total_cells
++;
673 // Expand byte position and remove duplicates
674 for(i
= 0; i
< final_cells
->total_cells
; i
++)
676 if(i
< final_cells
->total_cells
- 1 &&
677 final_cells
->cells
[i
].start_byte
== final_cells
->cells
[i
+ 1].start_byte
)
679 for(j
= i
; j
< final_cells
->total_cells
- 1; j
++)
680 final_cells
->cells
[j
] = final_cells
->cells
[j
+ 1];
682 final_cells
->total_cells
--;
685 final_cells
->cells
[i
].start_byte
*= (int64_t)2048;
686 final_cells
->cells
[i
].end_byte
*= (int64_t)2048;
687 // End index seems to be inclusive
688 final_cells
->cells
[i
].end_byte
+= 2048;
693 printf("finaltable\n");
694 for(i
= 0; i
< final_cells
->total_cells
; i
++)
696 printf(" vob id: %x cell id: %x start: %llx end: %llx program: %x\n",
697 final_cells
->cells
[i
].vob_id
, final_cells
->cells
[i
].cell_id
, (int64_t)final_cells
->cells
[i
].start_byte
, (int64_t)final_cells
->cells
[i
].end_byte
, final_cells
->cells
[i
].program
);
704 /* Read the title information from an ifo */
705 int mpeg3_read_ifo(mpeg3_t
*file
,
708 int64_t last_ifo_byte
= 0, first_ifo_byte
= 0;
709 mpeg3ifo_celltable_t
*cells
, *cell_addresses
, *final_cells
;
710 mpeg3_demuxer_t
*demuxer
= file
->demuxer
;
711 int current_title
= 0, current_cell
= 0;
714 int fd
= mpeg3io_get_fd(file
->fs
);
715 int64_t title_start_byte
= 0;
718 if(!(ifo
= ifo_open(fd
, 0)))
720 fprintf(stderr
, "read_ifo: Error decoding ifo.\n");
724 demuxer
->read_all
= 1;
725 cells
= calloc(1, sizeof(mpeg3ifo_celltable_t
));
726 cell_addresses
= calloc(1, sizeof(mpeg3ifo_celltable_t
));
727 final_cells
= calloc(1, sizeof(mpeg3ifo_celltable_t
));
729 get_ifo_playlist(file
, demuxer
);
730 get_ifo_header(demuxer
, ifo
);
731 cellplayinfo(file
, ifo
, cells
);
732 celladdresses(ifo
, cell_addresses
);
733 finaltable(final_cells
,
737 // Get maximum program for program_bytes table
738 int total_programs
= 0;
741 for(i
= 0; i
< final_cells
->total_cells
; i
++)
743 mpeg3ifo_cell_t
*cell
= &final_cells
->cells
[i
];
744 if(cell
->program
> total_programs
- 1)
745 total_programs
= cell
->program
+ 1;
748 int64_t *program_bytes
= calloc(total_programs
, sizeof(int64_t));
751 // Clear out old cells
752 for(i
= 0; i
< demuxer
->total_titles
; i
++)
754 mpeg3_title_t
*title
= demuxer
->titles
[i
];
755 if(title
->cell_table
)
757 for(j
= 0; j
< title
->cell_table_size
; j
++)
759 free(title
->cell_table
);
760 title
->cell_table
= 0;
766 // Assign new cells to titles
767 while(final_cells
&& current_cell
< final_cells
->total_cells
)
769 mpeg3_title_t
*title
;
770 mpeg3ifo_cell_t
*cell
;
771 int64_t cell_start
, cell_end
;
774 title
= demuxer
->titles
[current_title
];
775 cell
= &final_cells
->cells
[current_cell
];
776 cell_start
= cell
->start_byte
;
777 cell_end
= cell
->end_byte
;
779 // Cell may be split by a title so handle in fragments.
780 while(cell_start
< cell_end
&& length
> 0)
782 length
= cell_end
- cell_start
;
784 // Clamp length to end of current title
785 if(cell_start
+ length
- title_start_byte
> title
->total_bytes
)
786 length
= title
->total_bytes
- cell_start
+ title_start_byte
;
788 //printf("%llx %llx %llx %llx\n", cell_end, cell_start, title_start_byte, length);
790 // Should never fail. If it does it means the length of the cells and the
791 // length of the titles don't match. The title lengths must match or else
792 // the cells won't line up.
795 int64_t program_start
= program_bytes
[cell
->program
];
796 int64_t program_end
= program_start
+ length
;
797 int64_t title_start
= cell_start
- title_start_byte
;
798 int64_t title_end
= title_start
+ length
;
799 mpeg3_new_cell(title
,
805 cell_start
+= length
;
806 program_bytes
[cell
->program
] += length
;
811 "read_ifo: cell length and title length don't match! title=%d cell=%d cell_start=%llx cell_end=%llx.\n",
814 cell_start
- title_start_byte
,
815 cell_end
- title_start_byte
);
817 // Try this out. It works for Contact where one VOB is 0x800 bytes longer than
818 // the cells in it but the next cell aligns perfectly with the next VOB.
819 if(current_title
< demuxer
->total_titles
- 1) current_cell
--;
823 if(cell_start
- title_start_byte
>= title
->total_bytes
&&
824 current_title
< demuxer
->total_titles
- 1)
826 title_start_byte
+= title
->total_bytes
;
827 title
= demuxer
->titles
[++current_title
];
834 delete_celltable(cells
);
835 delete_celltable(cell_addresses
);
836 delete_celltable(final_cells
);