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