1 // $Id: tag_render.cpp,v 1.44 2002/07/31 13:20:49 t1mpy Exp $
2 
3 // id3lib: a C++ library for creating and manipulating id3v1/v2 tags
4 // Copyright 1999, 2000  Scott Thomas Haug
5 // Copyright 2002  Thijmen Klok (thijmen@id3lib.org)
6 
7 // This library is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU Library General Public License as published by
9 // the Free Software Foundation; either version 2 of the License, or (at your
10 // option) any later version.
11 //
12 // This library is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
15 // License for more details.
16 //
17 // You should have received a copy of the GNU Library General Public License
18 // along with this library; if not, write to the Free Software Foundation,
19 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 
21 // The id3lib authors encourage improvements and optimisations to be sent to
22 // the id3lib coordinator.  Please see the README file for details on where to
23 // send such submissions.  See the AUTHORS file for a list of people who have
24 // contributed to id3lib.  See the ChangeLog file for a list of changes to
25 // id3lib.  These files are distributed with id3lib at
26 // http://download.sourceforge.net/id3lib/
27 
28 #include <memory.h>
29 #include "tag_impl.h" //has <stdio.h> "tag.h" "header_tag.h" "frame.h" "field.h" "spec.h" "id3lib_strings.h" "utils.h"
30 #include "helpers.h"
31 #include "writers.h"
32 #include "id3/io_decorators.h" //has "readers.h" "io_helpers.h" "utils.h"
33 #include "io_helpers.h"
34 #include "io_strings.h"
35 
36 #if defined HAVE_SYS_PARAM_H
37 #include <sys/param.h>
38 #endif
39 
40 using namespace dami;
41 
render(ID3_Writer & writer,const ID3_TagImpl & tag)42 void id3::v1::render(ID3_Writer& writer, const ID3_TagImpl& tag)
43 {
44   writer.writeChars("TAG", 3);
45 
46   io::writeTrailingSpaces(writer, id3::v2::getTitle(tag),  ID3_V1_LEN_TITLE);
47   io::writeTrailingSpaces(writer, id3::v2::getArtist(tag), ID3_V1_LEN_ARTIST);
48   io::writeTrailingSpaces(writer, id3::v2::getAlbum(tag),  ID3_V1_LEN_ALBUM);
49   io::writeTrailingSpaces(writer, id3::v2::getYear(tag),   ID3_V1_LEN_YEAR);
50 
51   size_t track = id3::v2::getTrackNum(tag);
52   String comment = id3::v2::getV1Comment(tag);
53   if (track > 0)
54   {
55     io::writeTrailingSpaces(writer, comment, ID3_V1_LEN_COMMENT - 2);
56     writer.writeChar('\0');
57     writer.writeChar((char) track);
58   }
59   else
60   {
61     io::writeTrailingSpaces(writer, comment, ID3_V1_LEN_COMMENT);
62   }
63   writer.writeChar((char) id3::v2::getGenreNum(tag));
64 }
65 
66 namespace
67 {
renderFrames(ID3_Writer & writer,const ID3_TagImpl & tag)68   void renderFrames(ID3_Writer& writer, const ID3_TagImpl& tag)
69   {
70     for (ID3_TagImpl::const_iterator iter = tag.begin(); iter != tag.end(); ++iter)
71     {
72       const ID3_Frame* frame = *iter;
73       if (frame) frame->Render(writer);
74     }
75   }
76 }
77 
render(ID3_Writer & writer,const ID3_TagImpl & tag)78 void id3::v2::render(ID3_Writer& writer, const ID3_TagImpl& tag)
79 {
80   // There has to be at least one frame for there to be a tag...
81   if (tag.NumFrames() == 0)
82   {
83     ID3D_WARNING( "id3::v2::render(): no frames to render" );
84     return;
85   }
86 
87   ID3D_NOTICE( "id3::v2::render(): rendering" );
88   ID3_TagHeader hdr;
89   hdr.SetSpec(tag.GetSpec());
90   hdr.SetExtended(tag.GetExtended());
91   hdr.SetExperimental(tag.GetExperimental());
92   hdr.SetFooter(tag.GetFooter());
93 
94   // set up the encryption and grouping IDs
95 
96   // ...
97   String frms;
98   io::StringWriter frmWriter(frms);
99   if (!tag.GetUnsync())
100   {
101     ID3D_NOTICE( "id3::v2::render(): rendering frames" );
102     renderFrames(frmWriter, tag);
103     hdr.SetUnsync(false);
104   }
105   else
106   {
107     ID3D_NOTICE( "id3::v2::render(): rendering unsynced frames" );
108     io::UnsyncedWriter uw(frmWriter);
109     renderFrames(uw, tag);
110     uw.flush();
111     ID3D_NOTICE( "id3::v2::render(): numsyncs = " << uw.getNumSyncs() );
112     hdr.SetUnsync(uw.getNumSyncs() > 0);
113   }
114   size_t frmSize = frms.size();
115   if (frmSize == 0)
116   {
117     ID3D_WARNING( "id3::v2::render(): rendered frame size is 0 bytes" );
118     return;
119   }
120 
121   // zero the remainder of the buffer so that our padding bytes are zero
122   luint nPadding = tag.PaddingSize(frmSize);
123   ID3D_NOTICE( "id3::v2::render(): padding size = " << nPadding );
124 
125   hdr.SetDataSize(frmSize + tag.GetExtendedBytes() + nPadding);
126 
127   hdr.Render(writer);
128 
129   writer.writeChars(frms.data(), frms.size());
130 
131   for (size_t i = 0; i < nPadding; ++i)
132   {
133     if (writer.writeChar('\0') == ID3_Writer::END_OF_WRITER)
134     {
135       break;
136     }
137   }
138 }
139 
Size() const140 size_t ID3_TagImpl::Size() const
141 {
142   if (this->NumFrames() == 0)
143   {
144     return 0;
145   }
146   ID3_TagHeader hdr;
147 
148   hdr.SetSpec(this->GetSpec());
149   size_t bytesUsed = hdr.Size();
150 
151   size_t frameBytes = 0;
152   for (const_iterator cur = _frames.begin(); cur != _frames.end(); ++cur)
153   {
154     if (*cur)
155     {
156       (*cur)->SetSpec(this->GetSpec());
157       frameBytes += (*cur)->Size();
158     }
159   }
160 
161   if (!frameBytes)
162   {
163     return 0;
164   }
165 
166   bytesUsed += frameBytes;
167   // add 30% for sync
168   if (this->GetUnsync())
169   {
170     bytesUsed += bytesUsed / 3;
171   }
172 
173   bytesUsed += this->PaddingSize(bytesUsed);
174   return bytesUsed;
175 }
176 
177 
RenderExtHeader(uchar * buffer)178 void ID3_TagImpl::RenderExtHeader(uchar *buffer)
179 {
180   if (this->GetSpec() == ID3V2_3_0)
181   {
182   }
183 
184   return ;
185 }
186 
187 
188 #define ID3_PADMULTIPLE (2048)
189 #define ID3_PADMAX  (4096)
190 
191 
PaddingSize(size_t curSize) const192 size_t ID3_TagImpl::PaddingSize(size_t curSize) const
193 {
194   luint newSize = 0;
195 
196   // if padding is switched off
197   if (! _is_padded)
198   {
199     return 0;
200   }
201 
202   // if the old tag was large enough to hold the new tag, then we will simply
203   // pad out the difference - that way the new tag can be written without
204   // shuffling the rest of the song file around
205   if ((this->GetPrependedBytes()-ID3_TagHeader::SIZE > 0) &&
206       (this->GetPrependedBytes()-ID3_TagHeader::SIZE >= curSize) &&
207       (this->GetPrependedBytes()-ID3_TagHeader::SIZE - curSize) < ID3_PADMAX)
208   {
209     newSize = this->GetPrependedBytes()-ID3_TagHeader::SIZE;
210   }
211   else
212   {
213     luint tempSize = curSize + ID3_GetDataSize(*this) +
214                      this->GetAppendedBytes() + ID3_TagHeader::SIZE;
215 
216     // this method of automatic padding rounds the COMPLETE FILE up to the
217     // nearest 2K.  If the file will already be an even multiple of 2K (with
218     // the tag included) then we just add another 2K of padding
219     tempSize = ((tempSize / ID3_PADMULTIPLE) + 1) * ID3_PADMULTIPLE;
220 
221     // the size of the new tag is the new filesize minus the audio data
222     newSize = tempSize - ID3_GetDataSize(*this) - this->GetAppendedBytes () -
223               ID3_TagHeader::SIZE;
224   }
225 
226   return newSize - curSize;
227 }
228 
229