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    hacks :)
10 
11    Written by Moritz Bunkus <moritz@bunkus.org>.
12 */
13 
14 #include "common/common_pch.h"
15 
16 #include "common/base64.h"
17 #include "common/hacks.h"
18 #include "common/strings/editing.h"
19 #include "common/strings/formatting.h"
20 
21 namespace mtx::hacks {
22 
23 namespace {
24 std::vector<bool> s_engaged_hacks(MAX_IDX + 1, false);
25 }
26 
27 std::vector<hack_t>
get_list()28 get_list() {
29   using svec = std::vector<std::string>;
30 
31   std::vector<hack_t> hacks;
32 
33   hacks.emplace_back("space_after_chapters",          svec{ Y("Leave additional space (EbmlVoid) in the destination file after the chapters.") });
34   hacks.emplace_back("no_chapters_in_meta_seek",      svec{ Y("Do not add an entry for the chapters in the meta seek element.") });
35   hacks.emplace_back("no_meta_seek",                  svec{ Y("Do not write meta seek elements at all.") });
36   hacks.emplace_back("lacing_xiph",                   svec{ Y("Force Xiph style lacing.") });
37   hacks.emplace_back("lacing_ebml",                   svec{ Y("Force EBML style lacing.") });
38   hacks.emplace_back("native_mpeg4",                  svec{ Y("Analyze MPEG4 bitstreams, put each frame into one Matroska block, use proper timestamping (I P B B = 0 120 40 80), use V_MPEG4/ISO/... CodecIDs.") });
39   hacks.emplace_back("no_variable_data",              svec{ Y("Use fixed values for the elements that change with each file otherwise (multiplexing date, segment UID, track UIDs etc.)."),
40                                                             Y("Two files multiplexed with the same settings and this switch activated will be identical.") });
41   hacks.emplace_back("force_passthrough_packetizer",  svec{ Y("Forces the Matroska reader to use the generic passthrough packetizer even for known and supported track types.") });
42   hacks.emplace_back("write_headers_twice",           svec{ Y("Causes mkvmerge to write a second set of identical track headers near the end of the file (after all the clusters).") });
43   hacks.emplace_back("allow_avc_in_vfw_mode",         svec{ Y("Allows storing AVC/H.264 video in Video-for-Windows compatibility mode, e.g. when it is read from an AVI.") });
44   hacks.emplace_back("keep_bitstream_ar_info",        svec{ Y("This option does nothing and is only kept for backwards compatibility.") });
45   hacks.emplace_back("no_simpleblocks",               svec{ Y("Disable the use of SimpleBlocks instead of BlockGroups.") });
46   hacks.emplace_back("use_codec_state_only",          svec{ Y("Store changes in CodecPrivate data in CodecState elements instead of the frames."),
47                                                             Y("This is used for e.g. MPEG-1/-2 video tracks for storing the sequence headers.") });
48   hacks.emplace_back("enable_timestamp_warning",      svec{ Y("Enables warnings for certain conditions where timestamps aren't monotonous in situations where they should be which could indicate either a problem with "
49                                                               "the file or a programming error.") });
50   hacks.emplace_back("remove_bitstream_ar_info",      svec{ Y("Normally mkvmerge keeps aspect ratio information in MPEG4 video bitstreams and puts the information into the container."),
51                                                             Y("This option causes mkvmerge to remove the aspect ratio information from the bitstream.") });
52   hacks.emplace_back("vobsub_subpic_stop_cmds",       svec{ Y("Causes mkvmerge to add 'stop display' commands to VobSub subtitle packets that do not contain a duration field.") });
53   hacks.emplace_back("no_cue_duration",               svec{ Y("Causes mkvmerge not to write 'CueDuration' elements in the cues.") });
54   hacks.emplace_back("no_cue_relative_position",      svec{ Y("Causes mkvmerge not to write 'CueRelativePosition' elements in the cues.") });
55   hacks.emplace_back("no_delay_for_garbage_in_avi",   svec{ Y("Garbage at the start of audio tracks in AVI files is normally used for delaying that track."),
56                                                             Y("mkvmerge normally calculates the delay implied by its presence and offsets all of the track's timestamps by it."),
57                                                             Y("This option prevents that behavior.") });
58   hacks.emplace_back("keep_last_chapter_in_mpls",     svec{ Y("Blu-ray discs often contain a chapter entry very close to the end of the movie."),
59                                                             Y("mkvmerge normally removes that last entry if it's timestamp is within five seconds of the total duration."),
60                                                             Y("Enabling this option causes mkvmerge to keep that last entry.") });
61   hacks.emplace_back("keep_track_statistics_tags",    svec{ Y("Don't remove track statistics tags when reading Matroska files, no matter if new ones are created or not.") });
62   hacks.emplace_back("all_i_slices_are_key_frames",   svec{ Y("Some AVC/H.264 tracks contain I slices but lack real key frames."),
63                                                             Y("This option forces mkvmerge to treat all of those I slices as key frames.") });
64   hacks.emplace_back("append_and_split_flac",         svec{ Y("Enable appending and splitting FLAC tracks."),
65                                                             Y("The resulting tracks will be broken: the official FLAC tools will not be able to decode them and seeking will not work as expected.") });
66   hacks.emplace_back("dont_normalize_parameter_sets", svec{ Y("Normally the HEVC/H.265 code in mkvmerge and mkvextract normalizes parameter sets by prefixing all key frames with all currently active parameter sets and removes duplicates that might already be present."),
67                                                             Y("If this hack is enabled, the code will leave the parameter sets as they are.") });
68   hacks.emplace_back("cow",                           svec{ Y("No help available.") });
69 
70 
71   return hacks;
72 }
73 
74 bool
is_engaged(unsigned int id)75 is_engaged(unsigned int id) {
76   return (s_engaged_hacks.size() > id) && s_engaged_hacks[id];
77 }
78 
79 void
engage(unsigned int id)80 engage(unsigned int id) {
81   if (s_engaged_hacks.size() > id)
82     s_engaged_hacks[id] = true;
83 }
84 
85 void
engage(const std::string & hacks)86 engage(const std::string &hacks) {
87   auto engage_args   = mtx::string::split(hacks, ",");
88   auto list_of_hacks = get_list();
89 
90   if (std::find(engage_args.begin(), engage_args.end(), "list") != engage_args.end()) {
91     mxinfo(Y("Valid hacks are:\n"));
92 
93     for (auto const &hack : list_of_hacks)
94       mxinfo(fmt::format(u8"  • {0} — {1}\n", hack.name, mtx::string::join(hack.description, " ")));
95 
96     mxexit();
97   }
98 
99   if (std::find(engage_args.begin(), engage_args.end(), "cow") != engage_args.end()) {
100     auto const initial    = "ICAgICAgICAgIChfXykKICAgICAgICAgICgqKikgIE9oIGhvbmV5LCB0aGF0J3Mgc28gc3dlZXQhCiAgIC8tLS0tLS0tXC8gICBPZiBjb3Vyc2UgSSdsbCBtYXJyeSB5b3UhCiAgLyB8ICAgICB8fAogKiAgfHwtLS0tfHwKICAgIF5eICAgIF5eCg=="s;
101     auto const correction = mtx::base64::decode(initial);
102     mxinfo(correction->to_string());
103     mxexit();
104   }
105 
106   auto hacks_begin = list_of_hacks.begin();
107   auto hacks_end   = list_of_hacks.end();
108 
109   for (auto const &name : engage_args) {
110     auto itr = std::find_if(list_of_hacks.begin(), list_of_hacks.end(), [&name](auto const &hack) { return hack.name == name; });
111 
112     if (itr != hacks_end)
113       s_engaged_hacks[std::distance(hacks_begin, itr)] = true;
114     else
115       mxerror(fmt::format(Y("'{0}' is not a valid hack.\n"), name));
116   }
117 }
118 
119 void
init()120 init() {
121   std::vector<std::string> env_vars = { "MKVTOOLNIX_ENGAGE", "MTX_ENGAGE", balg::to_upper_copy(get_program_name()) + "_ENGAGE" };
122 
123   for (auto &name : env_vars) {
124     auto value = getenv(name.c_str());
125     if (value)
126       engage(value);
127   }
128 }
129 
130 }
131