1 // $Id: field_string_ascii.cpp,v 1.29 2003/03/02 14:23:58 t1mpy Exp $
2
3 // id3lib: a C++ library for creating and manipulating id3v1/v2 tags
4 // Copyright 1999, 2000 Scott Thomas Haug
5
6 // This library is free software; you can redistribute it and/or modify it
7 // under the terms of the GNU Library General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or (at your
9 // option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful, but WITHOUT
12 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14 // License for more details.
15 //
16 // You should have received a copy of the GNU Library General Public License
17 // along with this library; if not, write to the Free Software Foundation,
18 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 // The id3lib authors encourage improvements and optimisations to be sent to
21 // the id3lib coordinator. Please see the README file for details on where to
22 // send such submissions. See the AUTHORS file for a list of people who have
23 // contributed to id3lib. See the ChangeLog file for a list of changes to
24 // id3lib. These files are distributed with id3lib at
25 // http://download.sourceforge.net/id3lib/
26
27 #include "field_impl.h"
28 #include "id3/utils.h" // has <config.h> "id3/id3lib_streams.h" "id3/globals.h" "id3/id3lib_strings.h"
29 #include "io_helpers.h"
30
31 using namespace dami;
32
33 /** \fn ID3_Field& ID3_Field::operator=(const char* data)
34 ** \brief Shortcut for the Set operator.
35 ** \param data The string to assign to this field
36 ** \sa Set(const char*)
37 **/
38
39 /** \brief Copies the supplied string to the field.
40 ** You may dispose of the source string after a call to this method.
41 ** \code
42 ** myFrame.GetField(ID3FN_TEXT)->Set("ID3Lib is very cool!");
43 ** \endcode
44 **/
Set(const char * data)45 size_t ID3_FieldImpl::Set(const char* data)
46 {
47 size_t len = 0;
48 if ((this->GetType() == ID3FTY_TEXTSTRING) && data)
49 {
50 String str(data);
51 len = this->SetText_i(str);
52 }
53 return len;
54 }
55
56 // the ::Get() function for ASCII
57
58 /** Copies the contents of the field into the supplied buffer, up to the
59 ** number of characters specified; for fields with multiple entries, the
60 ** optional third parameter indicates which of the fields to retrieve.
61 **
62 ** The third parameter is useful when using text lists (see Add(const char*)
63 ** for more details). The default value for this third parameter is 1,
64 ** which returns the entire string if the field contains only one item.
65 **
66 ** It returns the number of characters (not bytes necessarily, and not
67 ** including any NULL terminator) of the supplied buffer that are now used.
68 **
69 ** \code
70 ** char myBuffer[1024];
71 ** size_t charsUsed = myFrame.GetField(ID3FN_TEXT)->Get(buffer, 1024);
72 ** \endcode
73 **
74 ** It fills the buffer with as much data from the field as is present in the
75 ** field, or as large as the buffer, whichever is smaller.
76 **
77 ** \code
78 ** char myBuffer[1024];
79 ** size_t charsUsed = myFrame.GetField(ID3FN_TEXT)->Get(buffer, 1024, 3);
80 ** \endcode
81 **
82 ** This fills the buffer with up to the first 1024 characters from the third
83 ** element of the text list.
84 **
85 ** \sa Add(const char*)
86 **/
Get(char * buffer,size_t maxLength) const87 size_t ID3_FieldImpl::Get(char* buffer, size_t maxLength) const
88 {
89 size_t size = 0;
90 if (this->GetType() == ID3FTY_TEXTSTRING &&
91 this->GetEncoding() == ID3TE_ASCII &&
92 buffer != NULL && maxLength > 0)
93 {
94 String data = this->GetText();
95 size = dami::min(maxLength, data.size());
96 ::memcpy(buffer, data.data(), size);
97 if (size < maxLength)
98 {
99 buffer[size] = '\0';
100 }
101 }
102
103 return size;
104 }
105
Get(char * buf,size_t maxLen,size_t index) const106 size_t ID3_FieldImpl::Get(char* buf, size_t maxLen, size_t index) const
107 {
108 size_t size = 0;
109 if (this->GetType() == ID3FTY_TEXTSTRING &&
110 this->GetEncoding() == ID3TE_ASCII &&
111 buf != NULL && maxLen > 0)
112 {
113 String data = this->GetTextItem(index);
114 size = dami::min(maxLen, data.size());
115 ::memcpy(buf, data.data(), size);
116 if (size < maxLen)
117 {
118 buf[size] = '\0';
119 }
120 }
121 return size;
122 }
123
GetText() const124 String ID3_FieldImpl::GetText() const
125 {
126 String data;
127 if (this->GetType() == ID3FTY_TEXTSTRING)
128 {
129 data = _text;
130 }
131 return data;
132 }
133
GetTextItem(size_t index) const134 String ID3_FieldImpl::GetTextItem(size_t index) const
135 {
136 String data;
137 if (this->GetType() == ID3FTY_TEXTSTRING &&
138 this->GetEncoding() == ID3TE_ASCII)
139 {
140 const char* raw = this->GetRawTextItem(index);
141 if (raw != NULL)
142 {
143 data = raw;
144 }
145 }
146 return data;
147 }
148
149 namespace
150 {
getFixed(String data,size_t size)151 String getFixed(String data, size_t size)
152 {
153 String text(data, 0, size);
154 if (text.size() < size)
155 {
156 text.append(size - text.size(), '\0');
157 }
158 return text;
159 }
160 }
161
162
SetText_i(String data)163 size_t ID3_FieldImpl::SetText_i(String data)
164 {
165 this->Clear();
166 if (_fixed_size > 0)
167 {
168 _text = getFixed(data, _fixed_size);
169 }
170 else
171 {
172 _text = data;
173 }
174 ID3D_NOTICE( "SetText_i: text = \"" << _text << "\"" );
175 _changed = true;
176
177 if (_text.size() == 0)
178 {
179 _num_items = 0;
180 }
181 else
182 {
183 _num_items = 1;
184 }
185
186 return _text.size();
187 }
188
SetText(String data)189 size_t ID3_FieldImpl::SetText(String data)
190 {
191 size_t len = 0;
192 if (this->GetType() == ID3FTY_TEXTSTRING)
193 {
194 len = this->SetText_i(data);
195 }
196 return len;
197 }
198
199
200 /** For fields which support this feature, adds a string to the list of
201 ** strings currently in the field.
202 **
203 ** This is useful for using id3v2 frames such as the involved people list,
204 ** composer, and part of setp. You can use the GetNumTextItems() method to
205 ** find out how many such items are in a list.
206 **
207 ** \code
208 ** myFrame.GetField(ID3FN_TEXT)->Add("this is a test");
209 ** \endcode
210 **
211 ** \param string The string to add to the field
212 **/
AddText_i(String data)213 size_t ID3_FieldImpl::AddText_i(String data)
214 {
215 size_t len = 0; // how much of str we copied into this field (max is strLen)
216 ID3D_NOTICE ("ID3_FieldImpl::AddText_i: Adding \"" << data << "\"" );
217 if (this->GetNumTextItems() == 0)
218 {
219 // there aren't any text items in the field so just assign the string to
220 // the field
221 len = this->SetText_i(data);
222 }
223 else
224 {
225
226 // ASSERT(_fixed_size == 0)
227 _text += '\0';
228 if (this->GetEncoding() == ID3TE_UNICODE)
229 {
230 _text += '\0';
231 }
232 _text.append(data);
233 len = data.size();
234 _num_items++;
235 }
236
237 return len;
238 }
239
AddText(String data)240 size_t ID3_FieldImpl::AddText(String data)
241 {
242 size_t len = 0;
243 if (this->GetType() == ID3FTY_TEXTSTRING)
244 {
245 len = this->AddText_i(data);
246 }
247 return len;
248 }
249
Add(const char * data)250 size_t ID3_FieldImpl::Add(const char* data)
251 {
252 size_t len = 0;
253 if (this->GetType() == ID3FTY_TEXTSTRING)
254 {
255 String str(data);
256 len = this->AddText_i(str);
257 }
258 return len;
259 }
260
GetRawText() const261 const char* ID3_FieldImpl::GetRawText() const
262 {
263 const char* text = NULL;
264 if (this->GetType() == ID3FTY_TEXTSTRING &&
265 this->GetEncoding() == ID3TE_ASCII)
266 {
267 text = _text.c_str();
268 }
269 return text;
270 }
271
GetRawTextItem(size_t index) const272 const char* ID3_FieldImpl::GetRawTextItem(size_t index) const
273 {
274 const char* text = NULL;
275 if (this->GetType() == ID3FTY_TEXTSTRING &&
276 this->GetEncoding() == ID3TE_ASCII &&
277 index < this->GetNumTextItems())
278 {
279 text = _text.c_str();
280 for (size_t i = 0; i < index; ++i)
281 {
282 text += strlen(text) + 1;
283 }
284 }
285 return text;
286 }
287
288 namespace
289 {
readEncodedText(ID3_Reader & reader,size_t len,ID3_TextEnc enc)290 String readEncodedText(ID3_Reader& reader, size_t len, ID3_TextEnc enc)
291 {
292 if (enc == ID3TE_ASCII)
293 {
294 return io::readText(reader, len);
295 }
296 return io::readUnicodeText(reader, len);
297 }
298
readEncodedString(ID3_Reader & reader,ID3_TextEnc enc)299 String readEncodedString(ID3_Reader& reader, ID3_TextEnc enc)
300 {
301 if (enc == ID3TE_ASCII)
302 {
303 return io::readString(reader);
304 }
305 return io::readUnicodeString(reader);
306 }
307
writeEncodedText(ID3_Writer & writer,String data,ID3_TextEnc enc)308 size_t writeEncodedText(ID3_Writer& writer, String data, ID3_TextEnc enc)
309 {
310 if (enc == ID3TE_ASCII)
311 {
312 return io::writeText(writer, data);
313 }
314 return io::writeUnicodeText(writer, data);
315 }
316
writeEncodedString(ID3_Writer & writer,String data,ID3_TextEnc enc)317 size_t writeEncodedString(ID3_Writer& writer, String data, ID3_TextEnc enc)
318 {
319 if (enc == ID3TE_ASCII)
320 {
321 return io::writeString(writer, data);
322 }
323 return io::writeUnicodeString(writer, data);
324 }
325 }
326
ParseText(ID3_Reader & reader)327 bool ID3_FieldImpl::ParseText(ID3_Reader& reader)
328 {
329 ID3D_NOTICE( "ID3_Field::ParseText(): reader.getBeg() = " << reader.getBeg() );
330 ID3D_NOTICE( "ID3_Field::ParseText(): reader.getCur() = " << reader.getCur() );
331 ID3D_NOTICE( "ID3_Field::ParseText(): reader.getEnd() = " << reader.getEnd() );
332 this->Clear();
333
334 ID3_TextEnc enc = this->GetEncoding();
335 size_t fixed_size = this->Size();
336 if (fixed_size)
337 {
338 ID3D_NOTICE( "ID3_Field::ParseText(): fixed size string" );
339 // The string is of fixed length
340 String text = readEncodedText(reader, fixed_size, enc);
341 this->SetText(text);
342 ID3D_NOTICE( "ID3_Field::ParseText(): fixed size string = " << text );
343 }
344 else if (_flags & ID3FF_LIST)
345 {
346 ID3D_NOTICE( "ID3_Field::ParseText(): text list" );
347 // lists are always the last field in a frame. parse all remaining
348 // characters in the reader
349 while (!reader.atEnd())
350 {
351 String text = readEncodedString(reader, enc);
352 this->AddText(text);
353 ID3D_NOTICE( "ID3_Field::ParseText(): adding string = " << text );
354 }
355 }
356 else if (_flags & ID3FF_CSTR)
357 {
358 ID3D_NOTICE( "ID3_Field::ParseText(): null terminated string" );
359 String text = readEncodedString(reader, enc);
360 this->SetText(text);
361 ID3D_NOTICE( "ID3_Field::ParseText(): null terminated string = " << text );
362 }
363 else
364 {
365 ID3D_NOTICE( "ID3_Field::ParseText(): last field string" );
366 String text = readEncodedText(reader, reader.remainingBytes(), enc);
367 // not null terminated.
368 this->AddText(text);
369 ID3D_NOTICE( "ID3_Field::ParseText(): last field string = " << text );
370 }
371
372 _changed = false;
373 return true;
374 }
375
RenderText(ID3_Writer & writer) const376 void ID3_FieldImpl::RenderText(ID3_Writer& writer) const
377 {
378 ID3_TextEnc enc = this->GetEncoding();
379
380 if (_flags & ID3FF_CSTR)
381 {
382 writeEncodedString(writer, _text, enc);
383 }
384 else
385 {
386 writeEncodedText(writer, _text, enc);
387 }
388 _changed = false;
389 };
390
391 /** Returns the number of items in a text list.
392 **
393 ** \code
394 ** size_t numItems = myFrame.GetField(ID3FN_UNICODE)->GetNumItems();
395 ** \endcode
396 **
397 ** \return The number of items in a text list.
398 **/
GetNumTextItems() const399 size_t ID3_FieldImpl::GetNumTextItems() const
400 {
401 return _num_items;
402 }
403
404