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