foobar2000 SDK  2015-01-14
cue_parser_embedding.cpp
Go to the documentation of this file.
1 #include "stdafx.h"
2 
3 using namespace cue_parser;
4 using namespace file_info_record_helper;
5 static void build_cue_meta_name(const char * p_name,unsigned p_tracknumber,pfc::string_base & p_out) {
6  p_out.reset();
7  p_out << "cue_track" << pfc::format_uint(p_tracknumber % 100,2) << "_" << p_name;
8 }
9 
10 static bool is_reserved_meta_entry(const char * p_name) {
11  return file_info::field_name_comparator::compare(p_name,"cuesheet") == 0;
12 }
13 
14 static bool is_global_meta_entry(const char * p_name) {
15  static const char header[] = "cue_track";
16  return pfc::stricmp_ascii_ex(p_name,strlen(header),header,~0) != 0;
17 }
18 static bool is_allowed_field(const char * p_name) {
19  return !is_reserved_meta_entry(p_name) && is_global_meta_entry(p_name);
20 }
21 namespace {
22  class __get_tag_cue_track_list_builder {
23  public:
24  __get_tag_cue_track_list_builder(cue_creator::t_entry_list & p_entries) : m_entries(p_entries) {}
25  void operator() (unsigned p_trackno,const track_record & p_record) {
26  if (p_trackno > 0) {
27  cue_creator::t_entry_list::iterator iter = m_entries.insert_last();
28  iter->m_file = p_record.m_file;
29  iter->m_flags = p_record.m_flags;
30  iter->m_index_list = p_record.m_index_list;
31  iter->m_track_number = p_trackno;
32  p_record.m_info.to_info(iter->m_infos);
33  }
34  }
35  private:
36  cue_creator::t_entry_list & m_entries;
37  };
38 
40 
41  class __get_tag__enum_fields_enumerator {
42  public:
43  __get_tag__enum_fields_enumerator(field_name_list & p_out) : m_out(p_out) {}
44  void operator() (unsigned p_trackno,const track_record & p_record) {
45  if (p_trackno > 0) p_record.m_info.enumerate_meta(*this);
46  }
47  template<typename t_value> void operator() (const char * p_name,const t_value & p_value) {
48  m_out.add(p_name);
49  }
50  private:
51  field_name_list & m_out;
52  };
53 
54 
55  class __get_tag__is_field_global_check {
56  private:
57  typedef file_info_record::t_meta_value t_value;
58  public:
59  __get_tag__is_field_global_check(const char * p_field) : m_field(p_field), m_value(NULL), m_state(true) {}
60 
61  void operator() (unsigned p_trackno,const track_record & p_record) {
62  if (p_trackno > 0 && m_state) {
63  const t_value * val = p_record.m_info.meta_query_ptr(m_field);
64  if (val == NULL) {m_state = false; return;}
65  if (m_value == NULL) {
66  m_value = val;
67  } else {
69  m_state = false; return;
70  }
71  }
72  }
73  }
74  void finalize(file_info_record::t_meta_map & p_globals) {
75  if (m_state && m_value != NULL) {
76  p_globals.set(m_field,*m_value);
77  }
78  }
79  private:
80  const char * const m_field;
81  const t_value * m_value;
82  bool m_state;
83  };
84 
85  class __get_tag__filter_globals {
86  public:
87  __get_tag__filter_globals(track_record_list const & p_tracks,file_info_record::t_meta_map & p_globals) : m_tracks(p_tracks), m_globals(p_globals) {}
88 
89  void operator() (const char * p_field) {
90  if (is_allowed_field(p_field)) {
91  __get_tag__is_field_global_check wrapper(p_field);
92  m_tracks.enumerate(wrapper);
93  wrapper.finalize(m_globals);
94  }
95  }
96  private:
97  const track_record_list & m_tracks;
98  file_info_record::t_meta_map & m_globals;
99  };
100 
101  class __get_tag__local_field_filter {
102  public:
103  __get_tag__local_field_filter(const file_info_record::t_meta_map & p_globals,file_info_record::t_meta_map & p_output) : m_globals(p_globals), m_output(p_output), m_currenttrack(0) {}
104  void operator() (unsigned p_trackno,const track_record & p_track) {
105  if (p_trackno > 0) {
106  m_currenttrack = p_trackno;
107  p_track.m_info.enumerate_meta(*this);
108  }
109  }
110  void operator() (const char * p_name,const file_info_record::t_meta_value & p_value) {
111  PFC_ASSERT(m_currenttrack > 0);
112  if (!m_globals.have_item(p_name)) {
113  build_cue_meta_name(p_name,m_currenttrack,m_buffer);
114  m_output.set(m_buffer,p_value);
115  }
116  }
117  private:
118  unsigned m_currenttrack;
119  pfc::string8_fastalloc m_buffer;
120  const file_info_record::t_meta_map & m_globals;
121  file_info_record::t_meta_map & m_output;
122  };
123 };
124 
125 static void strip_redundant_track_meta(unsigned p_tracknumber,const file_info & p_cueinfo,file_info_record::t_meta_map & p_meta,const char * p_metaname) {
126  t_size metaindex = p_cueinfo.meta_find(p_metaname);
127  if (metaindex == ~0) return;
128  pfc::string_formatter namelocal;
129  build_cue_meta_name(p_metaname,p_tracknumber,namelocal);
130  {
131  const file_info_record::t_meta_value * val = p_meta.query_ptr(namelocal);
132  if (val == NULL) return;
134  for(t_size valwalk = 0, valcount = p_cueinfo.meta_enum_value_count(metaindex); valwalk < valcount; ++valwalk) {
135  if (iter.is_empty()) return;
136  if (strcmp(*iter,p_cueinfo.meta_enum_value(metaindex,valwalk)) != 0) return;
137  ++iter;
138  }
139  if (!iter.is_empty()) return;
140  }
141  //success
142  p_meta.remove(namelocal);
143 }
144 
146  if (!have_cuesheet()) {
147  m_content.query_ptr((unsigned)0)->m_info.to_info(p_info);
148  p_info.meta_remove_field("cuesheet");
149  } else {
151  {
152  __get_tag_cue_track_list_builder e(entries);
153  m_content.enumerate(e);
154  }
155  pfc::string_formatter cuesheet;
156  cue_creator::create(cuesheet,entries);
157  entries.remove_all();
158  //parse it back to see what info got stored in the cuesheet and what needs to be stored outside cuesheet in the tags
159  cue_parser::parse_full(cuesheet,entries);
161 
162 
163 
164 
165  {
167  //1. find global infos and forward them
168  {
169  field_name_list fields;
170  { __get_tag__enum_fields_enumerator e(fields); m_content.enumerate(e);}
171  { __get_tag__filter_globals e(m_content,globals); fields.enumerate(e); }
172  }
173 
174  output.overwrite_meta(globals);
175 
176  //2. find local infos
177  {__get_tag__local_field_filter e(globals,output.m_meta); m_content.enumerate(e);}
178  }
179 
180 
181  //strip redundant titles and tracknumbers that the cuesheet already contains
182  for(cue_creator::t_entry_list::const_iterator iter = entries.first(); iter.is_valid(); ++iter) {
183  strip_redundant_track_meta(iter->m_track_number,iter->m_infos,output.m_meta,"tracknumber");
184  strip_redundant_track_meta(iter->m_track_number,iter->m_infos,output.m_meta,"title");
185  }
186 
187 
188  //add tech infos etc
189 
190  {
191  const track_record * rec = m_content.query_ptr((unsigned)0);
192  if (rec != NULL) {
193  output.set_length(rec->m_info.get_length());
194  output.set_replaygain(rec->m_info.get_replaygain());
195  output.overwrite_info(rec->m_info.m_info);
196  }
197  }
198  output.meta_set("cuesheet",cuesheet);
199  output.to_info(p_info);
200  }
201 }
202 
203 static bool resolve_cue_meta_name(const char * p_name,pfc::string_base & p_outname,unsigned & p_tracknumber) {
204  //"cue_trackNN_fieldname"
205  static const char header[] = "cue_track";
206  if (pfc::stricmp_ascii_ex(p_name,strlen(header),header,~0) != 0) return false;
207  p_name += strlen(header);
208  if (!pfc::char_is_numeric(p_name[0]) || !pfc::char_is_numeric(p_name[1]) || p_name[2] != '_') return false;
209  unsigned tracknumber = pfc::atoui_ex(p_name,2);
210  if (tracknumber == 0) return false;
211  p_name += 3;
212  p_tracknumber = tracknumber;
213  p_outname = p_name;
214  return true;
215 }
216 
217 
218 namespace {
219  class __set_tag_global_field_relay {
220  public:
221  __set_tag_global_field_relay(const file_info & p_info,t_size p_index) : m_info(p_info), m_index(p_index) {}
222  void operator() (unsigned p_trackno,track_record & p_record) {
223  if (p_trackno > 0) {
224  p_record.m_info.transfer_meta_entry(m_info.meta_enum_name(m_index),m_info,m_index);
225  }
226  }
227  private:
228  const file_info & m_info;
229  const t_size m_index;
230  };
231 }
232 
234  m_content.remove_all();
235 
236  {
237  track_record & track0 = m_content.find_or_add((unsigned)0);
238  track0.m_info.from_info(p_info);
239  track0.m_info.m_info.set("cue_embedded","no");
240  }
241 
242 
243 
244  const char * cuesheet = p_info.meta_get("cuesheet",0);
245  if (cuesheet == NULL) {
246  return;
247  }
248 
249  //processing order
250  //1. cuesheet content
251  //2. overwrite with global metadata from the tag
252  //3. overwrite with local metadata from the tag
253 
254  {
256  try {
257  cue_parser::parse_full(cuesheet,entries);
258  } catch(exception_io_data const & e) {
259  console::complain("Attempting to embed an invalid cuesheet", e.what());
260  return;
261  }
262 
263  {
264  const double length = p_info.get_length();
265  for(cue_creator::t_entry_list::const_iterator iter = entries.first(); iter.is_valid(); ++iter ) {
266  if (iter->m_index_list.start() > length) {
267  console::info("Invalid cuesheet - index outside allowed range");
268  return;
269  }
270  }
271  }
272 
273  for(cue_creator::t_entry_list::const_iterator iter = entries.first(); iter.is_valid(); ) {
275  ++next;
276  track_record & entry = m_content.find_or_add(iter->m_track_number);
277  entry.m_file = iter->m_file;
278  entry.m_flags = iter->m_flags;
279  entry.m_index_list = iter->m_index_list;
280  entry.m_info.from_info(iter->m_infos);
281  entry.m_info.from_info_overwrite_info(p_info);
282  entry.m_info.m_info.set("cue_embedded","yes");
283  double begin = entry.m_index_list.start(), end = next.is_valid() ? next->m_index_list.start() : p_info.get_length();
284  if (end <= begin) throw exception_io_data();
285  entry.m_info.set_length(end - begin);
286  iter = next;
287  }
288  }
289 
290  for(t_size metawalk = 0, metacount = p_info.meta_get_count(); metawalk < metacount; ++metawalk) {
291  const char * name = p_info.meta_enum_name(metawalk);
292  const t_size valuecount = p_info.meta_enum_value_count(metawalk);
293  if (valuecount > 0 && !is_reserved_meta_entry(name) && is_global_meta_entry(name)) {
294  __set_tag_global_field_relay relay(p_info,metawalk);
295  m_content.enumerate(relay);
296  }
297  }
298 
299  {
300  pfc::string8_fastalloc namebuffer;
301  for(t_size metawalk = 0, metacount = p_info.meta_get_count(); metawalk < metacount; ++metawalk) {
302  const char * name = p_info.meta_enum_name(metawalk);
303  const t_size valuecount = p_info.meta_enum_value_count(metawalk);
304  unsigned trackno;
305  if (valuecount > 0 && !is_reserved_meta_entry(name) && resolve_cue_meta_name(name,namebuffer,trackno)) {
306  track_record * rec = m_content.query_ptr(trackno);
307  if (rec != NULL) {
308  rec->m_info.transfer_meta_entry(namebuffer,p_info,metawalk);
309  }
310  }
311  }
312  }
313 }
314 
315 void embeddedcue_metadata_manager::get_track_info(unsigned p_track,file_info & p_info) const {
316  const track_record * rec = m_content.query_ptr(p_track);
317  if (rec == NULL) throw exception_io_data();
318  rec->m_info.to_info(p_info);
319 }
320 
321 void embeddedcue_metadata_manager::set_track_info(unsigned p_track,file_info const & p_info) {
322  track_record * rec = m_content.query_ptr(p_track);
323  if (rec == NULL) throw exception_io_data();
324  rec->m_info.from_info_set_meta(p_info);
325  rec->m_info.set_replaygain(p_info.get_replaygain());
326 }
327 
328 void embeddedcue_metadata_manager::query_track_offsets(unsigned p_track,double & p_begin,double & p_length) const {
329  const track_record * rec = m_content.query_ptr(p_track);
330  if (rec == NULL) throw exception_io_data();
331  p_begin = rec->m_index_list.start();
332  p_length = rec->m_info.get_length();
333 }
334 
336  return m_content.get_count() > 1;
337 }
338 
339 namespace {
340  class __remap_trackno_enumerator {
341  public:
342  __remap_trackno_enumerator(unsigned p_index) : m_countdown(p_index), m_result(0) {}
343  template<typename t_blah> void operator() (unsigned p_trackno,const t_blah&) {
344  if (p_trackno > 0 && m_result == 0) {
345  if (m_countdown == 0) {
346  m_result = p_trackno;
347  } else {
348  --m_countdown;
349  }
350  }
351  }
352  unsigned result() const {return m_result;}
353  private:
354  unsigned m_countdown;
355  unsigned m_result;
356  };
357 };
358 
359 unsigned embeddedcue_metadata_manager::remap_trackno(unsigned p_index) const {
360  if (have_cuesheet()) {
361  __remap_trackno_enumerator wrapper(p_index);
362  m_content.enumerate(wrapper);
363  return wrapper.result();
364  } else {
365  return 0;
366  }
367 }
368 
370  return m_content.get_count() - 1;
371 }
replaygain_info get_replaygain() const
Definition: cue_parser.h:36
virtual double get_length() const =0
Retrieves audio duration, in seconds. Note that the reported duration should not be assumed to be th...
void meta_set(const char *p_name, const char *p_value)
Definition: cue_parser.h:97
t_cuesheet_index_list m_index_list
Definition: cue_parser.h:171
void from_info(const file_info &p_info)
Definition: cue_parser.h:111
static bool is_global_meta_entry(const char *p_name)
void set_replaygain(const replaygain_info &p_replaygain)
Definition: cue_parser.h:37
void set_track_info(unsigned p_track, file_info const &p_info)
void info(const char *p_message)
Definition: console.cpp:4
void get_track_info(unsigned p_track, file_info &p_info) const
virtual t_size meta_enum_value_count(t_size p_index) const =0
Retrieves count of values in metadata entry of specified index. The value is always equal to or great...
static int compare(T1 const &v1, T2 const &v2)
Definition: stringNew.h:180
Differences between chain_list_v2_t<> and old chain_list_t<>: Iterators pointing to removed items as...
Definition: chain_list_v2.h:26
void query_track_offsets(unsigned p_track, double &p_begin, double &p_length) const
int stricmp_ascii_ex(const char *const s1, t_size const len1, const char *const s2, t_size const len2)
file_info_record_helper::file_info_record m_info
Definition: cue_parser.h:169
virtual const char * meta_enum_value(t_size p_index, t_size p_value_number) const =0
Retrieves specified value from specified metadata entry. Return value is a null-terminated UTF-8 enco...
bool is_valid() const
Definition: iterators.h:24
const t_meta_value * meta_query_ptr(const char *p_name) const
Definition: cue_parser.h:101
void complain(const char *what, const char *msg)
Definition: console.cpp:21
void transfer_meta_entry(const char *p_name, const file_info &p_info, t_size p_index)
Definition: cue_parser.h:84
void overwrite_meta(const t_source &p_meta)
Definition: cue_parser.h:70
const char * meta_get(const char *p_name, t_size p_index) const
Definition: file_info.h:160
Main interface class for information about some playable object.
Definition: file_info.h:73
void overwrite_info(const t_source &p_info)
Definition: cue_parser.h:74
const t_storage_value * query_ptr(const _t_key &p_key) const
Definition: map.h:64
void meta_remove_field(const char *p_name)
Definition: file_info.h:156
size_t t_size
Definition: int_types.h:48
string8_fastalloc string_formatter
Definition: string_base.h:614
virtual t_size meta_get_count() const =0
Retrieves count of metadata entries.
bool remove(const _t_key &p_key)
Definition: map.h:120
static bool resolve_cue_meta_name(const char *p_name, pfc::string_base &p_outname, unsigned &p_tracknumber)
void enumerate_meta(t_callback &p_callback) const
Definition: cue_parser.h:133
unsigned remap_trackno(unsigned p_index) const
bool is_empty() const
Definition: iterators.h:23
unsigned atoui_ex(const char *p_string, t_size p_string_len)
void from_info_overwrite_info(const file_info &p_info)
Definition: cue_parser.h:47
void create(pfc::string_formatter &p_out, const t_entry_list &p_data)
Definition: cue_creator.cpp:44
void set(const _t_key &p_key, const _t_value &p_value)
Definition: map.h:22
static void build_cue_meta_name(const char *p_name, unsigned p_tracknumber, pfc::string_base &p_out)
static bool is_reserved_meta_entry(const char *p_name)
void parse_full(const char *p_cuesheet, cue_creator::t_entry_list &p_out)
Throws exception_bad_cuesheet on failure.
Definition: cue_parser.cpp:744
void from_info_set_meta(const file_info &p_info)
Definition: cue_parser.h:106
bool char_is_numeric(char_t p_char)
Definition: string_base.h:99
virtual replaygain_info get_replaygain() const =0
Retrieves ReplayGain information.
static bool is_allowed_field(const char *p_name)
string8_t< pfc::alloc_fast_aggressive > string8_fastalloc
Definition: string_base.h:435
t_size meta_find(const char *p_name) const
Definition: file_info.h:154
static void strip_redundant_track_meta(unsigned p_tracknumber, const file_info &p_cueinfo, file_info_record::t_meta_map &p_meta, const char *p_metaname)
Definition: output.h:95
void to_info(file_info &p_info) const
Definition: cue_parser.h:118
virtual const char * meta_enum_name(t_size p_index) const =0
Retrieves the name of metadata entry of specified index. Return value is a null-terminated UTF-8 enco...