26 #include <QLatin1String> 28 #include <QMutableListIterator> 30 #include <QStringList> 34 #include <KLocalizedString> 41 #if Q_BYTE_ORDER == Q_BIG_ENDIAN 42 #define SYSTEM_ENDIANNES Kwave::BigEndian 44 #define SYSTEM_ENDIANNES Kwave::LittleEndian 53 const quint64 max = std::numeric_limits<qint32>::max();
54 return static_cast<quint32
>(qMin(x, max));
59 const QStringList &main_chunks,
60 const QStringList &known_subchunks)
62 m_root(Q_NULLPTR,
"",
"",
toUint32(device.size()), 0,
64 m_main_chunk_names(main_chunks), m_sub_chunk_names(known_subchunks),
79 for (i=0; i < 4; ++i) {
81 if ((c >=
'a') && (c <=
'z'))
continue;
82 if ((c >=
'A') && (c <=
'Z'))
continue;
83 if ((c >=
'0') && (c <=
'9'))
continue;
84 if ((c ==
' ') || (c ==
'(') || (c ==
')'))
continue;
111 if (sane_name ==
_(
"RIFF")) {
115 if (sane_name ==
_(
"RIFX")) {
121 emit
action(i18n(
"Detecting endianness (standard search)..."));
133 if (riff_offsets.count() && !rifx_offsets.count()) {
134 qDebug(
"detected little endian format");
141 if (rifx_offsets.count() && !riff_offsets.count()) {
142 qDebug(
"detected big endian format");
150 emit
action(i18n(
"Detecting endianness (statistic search)..."));
151 qDebug(
"doing statistic search to determine endianness...");
152 unsigned int le_matches = 0;
153 unsigned int be_matches = 0;
159 double half = (
m_dev.size() >> 1);
162 int count = names.count();
164 foreach (QString chunk_name, names) {
166 QByteArray
name = chunk_name.toLatin1();
173 foreach (quint32 ofs, offsets) {
178 m_dev.read(reinterpret_cast<char *>(&len), 4);
179 double dist_le = fabs(half - qFromLittleEndian<quint32>(len));
180 double dist_be = fabs(half - qFromBigEndian<quint32>(len));
183 if (dist_be > dist_le) ++le_matches;
184 if (dist_le > dist_be) ++be_matches;
187 emit
progress(100 * (++index) / count);
189 qDebug(
"big endian matches: %u", be_matches);
190 qDebug(
"little endian matches: %u", le_matches);
192 if (le_matches > be_matches) {
193 qDebug(
"assuming little endian");
195 }
else if (be_matches > le_matches) {
196 qDebug(
"assuming big endian");
200 qDebug(
"unable to determine endianness");
210 char s[5] = {0, 0, 0, 0, 0};
213 m_dev.read(&s[0], 4);
215 return QByteArray(s);
226 qWarning(
"unable to detect endianness -> giving up!");
237 const QByteArray &format, quint32 length,
238 quint32 phys_offset, quint32 phys_length,
243 parent = parent->
parent();
254 parent, name, format, length, phys_offset, phys_length);
256 if (!chunk)
return Q_NULLPTR;
265 if (pos > phys_offset) {
271 int index = (before) ? chunks.indexOf(before) : chunks.size();
272 chunks.insert(index, chunk);
282 qDebug(
"adding garbage chunk at 0x%08X, length=%u",offset,length);
285 QByteArray
name(16, 0);
286 qsnprintf(name.data(), name.size(),
"[0x%08X]", offset);
294 const QByteArray &
name,
305 quint32 offset, quint32 length)
311 if (
m_dev.isSequential())
return false;
312 if (!parent)
return false;
315 if (length & 1) length++;
322 if (offset >=
m_dev.size()) {
337 qWarning(
"chunk with less than 4 bytes at offset 0x%08X, "\
338 "length=%u bytes!", offset, length);
352 qWarning(
"invalid chunk name at offset 0x%08X", offset);
354 qDebug(
"addGarbageChunk(offset=0x%08X, length=0x%08X)",
365 m_dev.read(reinterpret_cast<char *>(&len), 4);
371 qDebug(
"empty chunk '%s' at 0x%08X", name.data(), offset);
387 quint32 phys_len = (length - 8 < len) ? (length - 8) : len;
388 if (phys_len & 1) phys_len++;
398 found_chunks.append(chunk);
408 if (!chunk)
continue;
414 QByteArray path = (parent ? parent->
path() : QByteArray(
"")) +
449 if (!chunk)
continue;
450 if (path.contains(
"/")) {
452 if (chunk->
path() == path)
return chunk;
455 if (chunk->
name() == path)
return chunk;
465 unsigned int count = 0;
470 if (!chunk)
continue;
471 if (path.contains(
"/")) {
473 if (chunk->
path() == path) ++count;
476 if (chunk->
name() == path) ++count;
485 quint32 offset, quint32 length,
486 int progress_start,
int progress_count)
488 QList<quint32> matches;
489 if (length < 4)
return matches;
490 quint32 end = offset + ((length > 4) ? (length - 4) : 0);
492 memset(buffer, 0x00,
sizeof(buffer));
495 m_dev.read(&buffer[0], 4);
497 qDebug(
"scannig for '%s' at [0x%08X...0x%08X] ...", name.data(),
501 for (pos = offset; (pos <= end) && !
m_cancel; ++pos) {
502 if (name == buffer) {
507 buffer[0] = buffer[1];
508 buffer[1] = buffer[2];
509 buffer[2] = buffer[3];
510 m_dev.getChar(&(buffer[3]));
513 if (!--next && progress_count && (end > offset)) {
514 int percent = (((100*progress_start + (100*(pos-offset)) /
515 (end-offset))) / progress_count);
517 next = (end-offset)/100;
528 list.append(&parent);
539 if (chunk && chunk->
physStart() == offset)
return chunk;
546 emit
action(i18n(
"Searching for missing chunk '%1'...",
547 QLatin1String(name)));
550 bool found_something =
false;
557 int count = all_chunks.count();
560 if (!chunk)
continue;
563 qDebug(
"searching in garbage at 0x%08X", chunk->
physStart());
567 if (offsets.count()) found_something =
true;
570 quint32 end = chunk->
physEnd();
571 foreach (quint32 pos, offsets) {
573 quint32 len = end - pos + 1;
574 qDebug(
"found at [0x%08X...0x%08X] len=%u", pos, end, len);
575 parse(chunk, pos, len);
576 qDebug(
"-------------------------------");
583 if (!found_something && !
m_cancel) {
584 qDebug(
"brute-force search from 0x%08X to 0x%08X",
590 foreach (quint32 pos, offsets) {
592 quint32 len = end - pos + 1;
593 qDebug(
"found at [0x%08X...0x%08X] len=%u", pos, end, len);
595 qDebug(
"-------------------------------");
605 bool one_more_pass =
true;
607 while (one_more_pass && !
m_cancel) {
623 qDebug(
"discarding garbage...");
627 one_more_pass =
false;
643 if (!chunk)
continue;
650 bool contains_only_garbage =
true;
654 contains_only_garbage =
false;
659 if (subchunks.count() && contains_only_garbage) {
661 quint32 end = chunk->
physEnd();
663 qDebug(
"chunk at 0x%08X contains only garbage!", start);
667 while (!subchunks.isEmpty()) {
684 qDebug(
"fixing ends of garbage chunks...");
688 QListIterator<Kwave::RIFFChunk *> it1(chunks);
689 QListIterator<Kwave::RIFFChunk *> it2(chunks);
692 if (it1.hasNext()) it1.next();
693 while (it1.hasNext() && !
m_cancel) {
696 if (it2.hasNext()) it2.next();
697 while (it2.hasNext() && !
m_cancel) {
710 if ((s2 <= e1) && (e2 >= s1)) {
711 qDebug(
"overlap detected:");
712 qDebug(
" at 0x%08X...0x%08X '%s'",
713 s1, e1, c1->
name().data());
714 qDebug(
" at 0x%08X...0x%08X '%s'",
715 s2, e2, c2->
name().data());
720 quint32 len = e1 - s1 + 1;
721 qDebug(
"shortening garbage to %u bytes", len);
733 qDebug(
"joining garbage to empty chunks (and to garbage)...");
737 QMutableListIterator<Kwave::RIFFChunk *> it1(chunks);
738 QMutableListIterator<Kwave::RIFFChunk *> it2(chunks);
741 if (it2.hasNext()) it2.next();
742 while (it2.hasNext() && it1.hasNext() && !
m_cancel) {
745 if (!chunk || !next)
continue;
769 qDebug(
"joining garbage to empty chunk '%s' at 0x%08X, %u bytes",
798 QMutableListIterator<Kwave::RIFFChunk *> it(chunk.
subChunks());
799 while (it.hasNext()) {
818 qDebug(
"RIFFParser: --- cancel ---");
QStringList m_sub_chunk_names
QByteArray read4ByteString(qint64 offset)
void listAllChunks(Kwave::RIFFChunk &parent, Kwave::RIFFChunkList &list)
RIFFParser(QIODevice &device, const QStringList &main_chunks, const QStringList &known_subchunks)
bool isValidName(const char *name)
void action(const QString &name)
Kwave::RIFFChunk * findMissingChunk(const QByteArray &name)
quint32 physStart() const
quint32 physLength() const
Kwave::RIFFChunk * parent() const
bool isKnownName(const QByteArray &name)
bool addGarbageChunk(Kwave::RIFFChunk *parent, quint32 offset, quint32 length)
bool joinGarbageToEmpty()
const QByteArray path() const
Kwave::RIFFChunk * chunkAt(quint32 offset)
QStringList m_main_chunk_names
quint32 dataStart() const
void setType(ChunkType type)
static quint32 toUint32(quint64 x)
unsigned int chunkCount(const QByteArray &path)
QList< Kwave::RIFFChunk * > RIFFChunkList
quint32 dataLength() const
Kwave::RIFFChunkList & subChunks()
void progress(int percent)
bool isChildOf(Kwave::RIFFChunk *chunk)
Kwave::RIFFChunk * findChunk(const QByteArray &path)
bool addEmptyChunk(Kwave::RIFFChunk *parent, const QByteArray &name, quint32 offset)
Kwave::RIFFChunk::ChunkType guessType(const QByteArray &name)
const QByteArray & name() const
void discardGarbage(Kwave::RIFFChunk &chunk)
Kwave::RIFFChunk * addChunk(Kwave::RIFFChunk *parent, const QByteArray &name, const QByteArray &format, quint32 length, quint32 phys_offset, quint32 phys_length, Kwave::RIFFChunk::ChunkType type)
void setLength(quint32 length)
Kwave::byte_order_t m_endianness
void setFormat(const QByteArray &format)
QList< quint32 > scanForName(const QByteArray &name, quint32 offset, quint32 length, int progress_start=0, int progress_count=1)