1 /*
2 mkvmerge -- utility for splicing together matroska files
3 from component media subtypes
4
5 Distributed under the GPL v2
6 see the file COPYING for details
7 or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
8
9 Blu-ray index file handling
10
11 Written by Moritz Bunkus <moritz@bunkus.org>.
12 */
13
14 #include "common/common_pch.h"
15
16 #include "common/bit_reader.h"
17 #include "common/bluray/index.h"
18 #include "common/bluray/index_p.h"
19 #include "common/list_utils.h"
20 #include "common/mm_file_io.h"
21
22 namespace mtx::bluray::index {
23
24 void
dump() const25 first_playback_t::dump()
26 const {
27 mxinfo(fmt::format(" first_playback:\n"
28 " object_type: {0} ({1})\n",
29 object_type,
30 object_type == 0b01 ? "movie object"
31 : object_type == 0b10 ? "BD-J object"
32 : "reserved"));
33
34 if (object_type == 0b01)
35 mxinfo(fmt::format(" hdmv_title_playback_type: {0} ({2})\n"
36 " mobj_id_ref: 0x{1:04x}\n",
37 hdmv_title_playback_type, mobj_id_ref,
38 hdmv_title_playback_type == 0b00 ? "movie title"
39 : hdmv_title_playback_type == 0b01 ? "interactive title"
40 : "reserved"));
41 else
42 mxinfo(fmt::format(" bd_j_title_playback_type: {0} ({2})\n"
43 " bjdo_file_name: {1}\n",
44 bd_j_title_playback_type, bdjo_file_name,
45 bd_j_title_playback_type == 0b10 ? "movie title"
46 : bd_j_title_playback_type == 0b11 ? "interactive title"
47 : "reserved"));
48 }
49
50 void
dump() const51 top_menu_t::dump()
52 const {
53 mxinfo(fmt::format(" top_menu:\n"
54 " object_type: {0} ({1})\n",
55 object_type,
56 object_type == 0b01 ? "movie object"
57 : object_type == 0b10 ? "BD-J object"
58 : "reserved"));
59
60 if (object_type == 0b01)
61 mxinfo(fmt::format(" mobj_id_ref: 0x{0:04x}\n", mobj_id_ref));
62
63 else
64 mxinfo(fmt::format(" bjdo_file_name: {0}\n", bdjo_file_name));
65 }
66
67 void
dump(unsigned int idx) const68 title_t::dump(unsigned int idx)
69 const {
70 mxinfo(fmt::format(" title {0}:\n"
71 " object_type: {1} ({3})\n"
72 " access_type: {2} ({4})\n",
73 idx, object_type, access_type,
74 object_type == 0b01 ? "movie object"
75 : object_type == 0b10 ? "BD-J object"
76 : "reserved",
77 access_type == 0b00 ? "title search permitted"
78 : access_type == 0b01 ? "title search prohibited, title_number display permitted"
79 : access_type == 0b11 ? "title search prohibited, title_number display prohibited"
80 : "reserved"));
81
82 if (object_type == 0b01)
83 mxinfo(fmt::format(" hdmv_title_playback_type: {0} ({2})\n"
84 " mobj_id_ref: 0x{1:04x}\n",
85 hdmv_title_playback_type, mobj_id_ref,
86 hdmv_title_playback_type == 0b00 ? "movie title"
87 : hdmv_title_playback_type == 0b01 ? "interactive title"
88 : "reserved"));
89 else
90 mxinfo(fmt::format(" bd_j_title_playback_type: {0} ({2})\n"
91 " bjdo_file_name: {1}\n",
92 bd_j_title_playback_type, bdjo_file_name,
93 bd_j_title_playback_type == 0b10 ? "movie title"
94 : bd_j_title_playback_type == 0b11 ? "interactive title"
95 : "reserved"));
96 }
97
98 void
dump() const99 index_t::dump()
100 const {
101 mxinfo(fmt::format("Index dump:\n"));
102
103 first_playback.dump();
104 top_menu.dump();
105
106 mxinfo(fmt::format(" num_titles: {0}\n", titles.size()));
107
108 auto idx = 0;
109 for (auto const &title : titles)
110 title.dump(idx++);
111 }
112
113 // ------------------------------------------------------------
114
parser_c(std::string file_name)115 parser_c::parser_c(std::string file_name)
116 : p_ptr{new parser_private_c{std::move(file_name)}}
117 {
118 }
119
parser_c(parser_private_c & p)120 parser_c::parser_c(parser_private_c &p)
121 : p_ptr{&p}
122 {
123 }
124
~parser_c()125 parser_c::~parser_c() { // NOLINT(modernize-use-equals-default) due to pimpl idiom requiring explicit dtor declaration somewhere
126 }
127
128 void
dump() const129 parser_c::dump()
130 const {
131 auto p = p_func();
132
133 mxinfo(fmt::format("Index parser dump:\n"
134 " ok: {0}\n"
135 " index_start: {1}\n",
136 p->m_ok, p->m_index_start));
137
138 p->m_index.dump();
139 }
140
141 bool
parse()142 parser_c::parse() {
143 auto p = p_func();
144
145 try {
146 auto content = mm_file_io_c::slurp(p->m_file_name);
147 if (!content)
148 throw false;
149
150 p->m_bc = std::make_shared<mtx::bits::reader_c>(content->get_buffer(), content->get_size());
151
152 parse_header();
153 parse_index();
154
155 if (p->m_debug)
156 dump();
157
158 p->m_ok = true;
159
160 } catch (...) {
161 mxdebug_if(p->m_debug, "Parsing NOT OK\n");
162 }
163
164 p->m_bc.reset();
165
166 return p->m_ok;
167 }
168
169 void
parse_header()170 parser_c::parse_header() {
171 auto p = p_func();
172
173 p->m_bc->set_bit_position(0);
174
175 auto magic = p->m_bc->get_bits(32);
176 mxdebug_if(p->m_debug, fmt::format("File magic 1: 0x{0:08x}\n", magic));
177
178 if (magic != mtx::calc_fourcc('I', 'N', 'D', 'X'))
179 throw false;
180
181 magic = p->m_bc->get_bits(32);
182 mxdebug_if(p->m_debug, fmt::format("File magic 2: 0x{0:08x}\n", magic));
183
184 if (!mtx::included_in(magic, mtx::calc_fourcc('0', '1', '0', '0'), mtx::calc_fourcc('0', '2', '0', '0'), mtx::calc_fourcc('0', '3', '0', '0')))
185 throw false;
186
187 p->m_index_start = p->m_bc->get_bits(32);
188 }
189
190 void
parse_first_playback()191 parser_c::parse_first_playback() {
192 auto p = p_func();
193 auto &fp = p->m_index.first_playback;
194 fp.object_type = p->m_bc->get_bits(2);
195
196 p->m_bc->skip_bits(30); // reserved
197
198 if (fp.object_type == 0b01) {
199 fp.hdmv_title_playback_type = p->m_bc->get_bits(2);
200 p->m_bc->skip_bits(14); // alignment
201 fp.mobj_id_ref = p->m_bc->get_bits(16);
202 p->m_bc->skip_bits(8 * 4); // reserved
203
204 } else if (fp.object_type == 0b10) {
205 fp.bd_j_title_playback_type = p->m_bc->get_bits(2);
206 p->m_bc->skip_bits(14); // alignment
207 fp.bdjo_file_name = p->m_bc->get_string(5);
208 p->m_bc->skip_bits(8); // alignment
209
210 } else
211 throw false;
212 }
213
214 void
parse_top_menu()215 parser_c::parse_top_menu() {
216 auto p = p_func();
217 auto &tm = p->m_index.top_menu;
218 tm.object_type = p->m_bc->get_bits(2);
219
220 p->m_bc->skip_bits(30); // reserved
221
222 if (tm.object_type == 0b01) {
223 p->m_bc->skip_bits(2 + 14); // 01, alignment
224 tm.mobj_id_ref = p->m_bc->get_bits(16);
225 p->m_bc->skip_bits(8 * 4); // reserved
226
227 } else if (tm.object_type == 0b10) {
228 p->m_bc->skip_bits(2 + 14); // 11, alignment
229 tm.bdjo_file_name = p->m_bc->get_string(5);
230 p->m_bc->skip_bits(8); // alignment
231
232 } else
233 throw false;
234 }
235
236 void
parse_title()237 parser_c::parse_title() {
238 auto p = p_func();
239
240 p->m_index.titles.emplace_back();
241
242 auto &t = p->m_index.titles.back();
243 t.object_type = p->m_bc->get_bits(2);
244 t.access_type = p->m_bc->get_bits(2);
245 p->m_bc->skip_bits(28); // reserved
246
247 if (t.object_type == 0b01) {
248 t.hdmv_title_playback_type = p->m_bc->get_bits(2);
249 p->m_bc->skip_bits(14); // alignment
250 t.mobj_id_ref = p->m_bc->get_bits(16);
251 p->m_bc->skip_bits(8 * 4); // reserved
252
253 } else if (t.object_type == 0b10) {
254 t.bd_j_title_playback_type = p->m_bc->get_bits(2);
255 p->m_bc->skip_bits(14); // alignment
256 t.bdjo_file_name = p->m_bc->get_string(5);
257 p->m_bc->skip_bits(8); // alignment
258
259 } else
260 throw false;
261 }
262
263 void
parse_index()264 parser_c::parse_index() {
265 auto p = p_func();
266
267 p->m_bc->set_bit_position(p->m_index_start * 8);
268
269 p->m_bc->skip_bits(32); // 32 bits length
270
271 parse_first_playback();
272 parse_top_menu();
273
274 auto number_of_titles = p->m_bc->get_bits(16);
275
276 for (auto title_idx = 0u; title_idx < number_of_titles; ++title_idx)
277 parse_title();
278 }
279
280 bool
is_ok() const281 parser_c::is_ok()
282 const {
283 return p_ptr->m_ok;
284 }
285
286 index_t const &
get_index() const287 parser_c::get_index()
288 const {
289 return p_ptr->m_index;
290 }
291
292 }
293