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