1 /*
2  * Copyright 2003-2021 The Music Player Daemon Project
3  * http://www.musicpd.org
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 /* \file
21  *
22  * This file contains functions used by the DSF and DSDIFF decoders.
23  *
24  */
25 
26 #include "config.h"
27 #include "DsdLib.hxx"
28 #include "../DecoderAPI.hxx"
29 #include "input/InputStream.hxx"
30 #include "tag/Id3Scan.hxx"
31 
32 #ifdef ENABLE_ID3TAG
33 #include <id3tag.h>
34 #endif
35 
36 #include <string.h>
37 #include <stdlib.h>
38 
39 bool
Equals(const char * s) const40 DsdId::Equals(const char *s) const noexcept
41 {
42 	assert(s != nullptr);
43 	assert(strlen(s) == sizeof(value));
44 
45 	return memcmp(value, s, sizeof(value)) == 0;
46 }
47 
48 /**
49  * Skip the #InputStream to the specified offset.
50  */
51 bool
dsdlib_skip_to(DecoderClient * client,InputStream & is,offset_type offset)52 dsdlib_skip_to(DecoderClient *client, InputStream &is,
53 	       offset_type offset)
54 {
55 	if (is.IsSeekable()) {
56 		try {
57 			is.LockSeek(offset);
58 		} catch (...) {
59 			return false;
60 		}
61 	}
62 
63 	if (is.GetOffset() > offset)
64 		return false;
65 
66 	return dsdlib_skip(client, is, offset - is.GetOffset());
67 }
68 
69 /**
70  * Skip some bytes from the #InputStream.
71  */
72 bool
dsdlib_skip(DecoderClient * client,InputStream & is,offset_type delta)73 dsdlib_skip(DecoderClient *client, InputStream &is,
74 	    offset_type delta)
75 {
76 	if (delta == 0)
77 		return true;
78 
79 	if (is.IsSeekable()) {
80 		try {
81 			is.LockSeek(is.GetOffset() + delta);
82 		} catch (...) {
83 			return false;
84 		}
85 	}
86 
87 	if (delta > 1024 * 1024)
88 		/* don't skip more than one megabyte; it would be too
89 		   expensive */
90 		return false;
91 
92 	return decoder_skip(client, is, delta);
93 }
94 
95 bool
dsdlib_valid_freq(uint32_t samplefreq)96 dsdlib_valid_freq(uint32_t samplefreq) noexcept
97 {
98 	switch (samplefreq) {
99 	case 2822400: /* DSD64, 64xFs, Fs = 44.100kHz */
100 	case 3072000: /* DSD64 with Fs = 48.000 kHz */
101 	case 5644800:
102 	case 6144000:
103 	case 11289600:
104 	case 12288000:
105 	case 22579200:/* DSD512 */
106 	case 24576000:
107 		return true;
108 
109 	default:
110 		return false;
111 	}
112 }
113 
114 #ifdef ENABLE_ID3TAG
115 void
dsdlib_tag_id3(InputStream & is,TagHandler & handler,offset_type tagoffset)116 dsdlib_tag_id3(InputStream &is, TagHandler &handler,
117 	       offset_type tagoffset)
118 {
119 	if (tagoffset == 0 || !is.KnownSize())
120 		return;
121 
122 	/* Prevent broken files causing problems */
123 	const auto size = is.GetSize();
124 	if (tagoffset >= size)
125 		return;
126 
127 	const auto count64 = size - tagoffset;
128 	if (count64 < 10 || count64 > 4 * 1024 * 1024)
129 		return;
130 
131 	if (!dsdlib_skip_to(nullptr, is, tagoffset))
132 		return;
133 
134 	const id3_length_t count = count64;
135 
136 	auto *const id3_buf = new id3_byte_t[count];
137 	if (id3_buf == nullptr)
138 		return;
139 
140 	if (!decoder_read_full(nullptr, is, id3_buf, count)) {
141 		delete[] id3_buf;
142 		return;
143 	}
144 
145 	struct id3_tag *id3_tag = id3_tag_parse(id3_buf, count);
146 	delete[] id3_buf;
147 	if (id3_tag == nullptr)
148 		return;
149 
150 	scan_id3_tag(id3_tag, handler);
151 
152 	id3_tag_delete(id3_tag);
153 }
154 #endif
155