1 /*************************************************************************
2           RIFFChunk.cpp  -  information about a RIFF chunk
3                              -------------------
4     begin                : Tue Mar 20 2002
5     copyright            : (C) 2002 by Thomas Eschenbacher
6     email                : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "config.h"
19 
20 #include <QList>
21 #include <QString>
22 
23 #include "libkwave/String.h"
24 
25 #include "RIFFChunk.h"
26 
27 //***************************************************************************
RIFFChunk(RIFFChunk * parent,const QByteArray & name,const QByteArray & format,quint32 length,quint32 phys_offset,quint32 phys_length)28 Kwave::RIFFChunk::RIFFChunk(RIFFChunk *parent, const QByteArray &name,
29                             const QByteArray &format, quint32 length,
30                             quint32 phys_offset, quint32 phys_length)
31     :m_type(Sub), m_name(name), m_format(format), m_parent(parent),
32      m_chunk_length(length), m_phys_offset(phys_offset),
33      m_phys_length(phys_length), m_sub_chunks()
34 {
35 }
36 
37 //***************************************************************************
~RIFFChunk()38 Kwave::RIFFChunk::~RIFFChunk()
39 {
40     while (!m_sub_chunks.isEmpty()) {
41         Kwave::RIFFChunk *chunk = m_sub_chunks.takeLast();
42         if (chunk) delete chunk;
43     }
44 }
45 
46 //***************************************************************************
47 // #define CHECK(x) Q_ASSERT(!(x)); if (x) return false;
48 #define CHECK(x) if (x) return false;
isSane() const49 bool Kwave::RIFFChunk::isSane() const
50 {
51     CHECK(m_type == Empty)
52     CHECK(m_type == Garbage)
53     CHECK((m_type == Main) && m_sub_chunks.isEmpty())
54     CHECK((m_type == Root) && m_sub_chunks.isEmpty())
55 
56 #ifdef DEBUG
57     if (m_phys_length & 0x1) {
58 	// size is not an even number: no criterium for insanity
59 	// but worth a warning
60 	qWarning("%s: physical length is not an even number: %u",
61 	        path().data(), m_phys_length);
62     }
63 #endif /* DEBUG */
64 
65     unsigned int datalen = dataLength();
66     if (m_type == Main) datalen += 4;
67     if (((datalen + 1) < m_phys_length) || (datalen > m_phys_length)) {
68 	qWarning("%s: dataLength=%u, phys_length=%u",
69 	         path().data(), datalen, m_phys_length);
70 	return false;
71     }
72 
73     foreach (const Kwave::RIFFChunk *chunk, subChunks())
74         if (chunk && !chunk->isSane()) return false;
75     return true;
76 }
77 
78 //***************************************************************************
physEnd() const79 quint32 Kwave::RIFFChunk::physEnd() const
80 {
81     quint32 end = m_phys_offset + m_phys_length;
82     if (m_phys_length) --end;
83     if ((m_type != Root) && (m_type != Garbage)) end += 8;
84     return end;
85 }
86 
87 //***************************************************************************
path() const88 const QByteArray Kwave::RIFFChunk::path() const
89 {
90     QByteArray p = "";
91 
92     if (m_parent) p += m_parent->path() + '/';
93     p += m_name;
94     if (m_type == Main) p += ':' + m_format;
95 
96     if (m_parent) {
97 	QListIterator<Kwave::RIFFChunk *> it(m_parent->subChunks());
98 	unsigned int before = 0;
99 	unsigned int after  = 0;
100         const Kwave::RIFFChunk *chunk = Q_NULLPTR;
101 	while (it.hasNext()) {
102             chunk = it.next();
103             if (!chunk) continue;
104             if (chunk == this) break;
105 	    if (chunk->name() != m_name) continue;
106 	    if (chunk->type() != m_type) continue;
107 	    if ((m_type == Main) && (chunk->format() != m_format)) continue;
108 	    before++;
109 	}
110 	if ((chunk == this) && (it.hasNext())) chunk = it.next();
111 	while ((chunk != this) && (it.hasNext())) {
112             chunk = it.next();
113             if (!chunk) continue;
114 	    if (chunk->name() != m_name) continue;
115 	    if (chunk->type() != m_type) continue;
116 	    if ((m_type == Main) && (chunk->format() != m_format)) continue;
117 	    after++;
118 	}
119 	if (before + after != 0) {
120 	    QByteArray index;
121 	    index.setNum(before);
122 	    p += '(' + index + ')';
123 	}
124     }
125 
126     return p;
127 }
128 
129 //***************************************************************************
dataStart() const130 quint32 Kwave::RIFFChunk::dataStart() const
131 {
132     return m_phys_offset + ((m_type == Main) ? 12 : 8);
133 }
134 
135 //***************************************************************************
dataLength() const136 quint32 Kwave::RIFFChunk::dataLength() const
137 {
138     return m_chunk_length - ((m_type == Main) ? 4 : 0);
139 }
140 
141 //***************************************************************************
setLength(quint32 length)142 void Kwave::RIFFChunk::setLength(quint32 length)
143 {
144     m_chunk_length = length;
145     m_phys_length  = length;
146 }
147 
148 //***************************************************************************
isChildOf(Kwave::RIFFChunk * chunk)149 bool Kwave::RIFFChunk::isChildOf(Kwave::RIFFChunk *chunk)
150 {
151     if (!chunk) return (m_type == Root); // only root has null as parent
152     if (chunk == m_parent) return true;
153     if (m_parent) return m_parent->isChildOf(chunk);
154     return false;
155 }
156 
157 //***************************************************************************
fixSize()158 void Kwave::RIFFChunk::fixSize()
159 {
160     // pass one: fix sizes of sub chunks recursively
161     foreach (Kwave::RIFFChunk *chunk, subChunks())
162         if (chunk) chunk->fixSize();
163 
164     // pass two: sum up sub-chunks if type is main or root.
165     if ((m_type == Main) || (m_type == Root)) {
166 	quint32 old_length = m_phys_length;
167 	m_phys_length = 0;
168 	if (m_type == Main) m_phys_length += 4;
169 
170         foreach (Kwave::RIFFChunk *chunk, subChunks()) {
171             if (!chunk) continue;
172 	    quint32 len = chunk->physEnd() - chunk->physStart() + 1;
173 	    m_phys_length += len;
174 	}
175 	if (m_phys_length != old_length) {
176 	    qDebug("%s: setting size from %u to %u",
177 	        path().data(), old_length, m_phys_length);
178 	}
179 	// chunk length is always equal to physical length for
180 	// main and root !
181 	m_chunk_length = m_phys_length;
182     } else {
183 	// just round up if no main or root chunk
184 	if (m_phys_length & 0x1) {
185 	    m_phys_length++;
186 	    qDebug("%s: rounding up size to %u", path().data(), m_phys_length);
187 	}
188 
189 	// adjust chunk size to physical size if not long enough
190 	if ((m_chunk_length+1 != m_phys_length) &&
191 	    (m_chunk_length != m_phys_length))
192 	{
193 	    qDebug("%s: resizing chunk from %u to %u",
194 	        path().data(), m_chunk_length, m_phys_length);
195 	    m_chunk_length = m_phys_length;
196 	}
197 
198     }
199 
200 }
201 
202 //***************************************************************************
dumpStructure()203 void Kwave::RIFFChunk::dumpStructure()
204 {
205     // translate the type into a user-readable string
206     const char *t = "?unknown?";
207     switch (m_type) {
208         case Root:    t = "ROOT";    break;
209         case Main:    t = "MAIN";    break;
210         case Sub:     t = "SUB";     break;
211         case Garbage: t = "GARBAGE"; break;
212         case Empty:   t = "EMPTY";   break;
213     }
214 
215     // dump this chunk
216     qDebug("[0x%08X-0x%08X] (%10u/%10u) %7s, '%s'",
217           m_phys_offset, physEnd(), physLength(), length(),
218           t, path().data()
219     );
220 
221     // recursively dump all sub-chunks
222     foreach (Kwave::RIFFChunk *chunk, m_sub_chunks)
223         if (chunk) chunk->dumpStructure();
224 
225 }
226 
227 //***************************************************************************
228 //***************************************************************************
229