"Fossies" - the Fresh Open Source Software Archive

Member "MP3Diags-unstable-1.5.01/src/Notes.h" (3 Apr 2019, 37916 Bytes) of package /linux/privat/MP3Diags-unstable-1.5.01.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "Notes.h" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.3.04_vs_1.5.01.

    1 /***************************************************************************
    2  *   MP3 Diags - diagnosis, repairs and tag editing for MP3 files          *
    3  *                                                                         *
    4  *   Copyright (C) 2009 by Marian Ciobanu                                  *
    5  *   ciobi@inbox.com                                                       *
    6  *                                                                         *
    7  *   This program is free software; you can redistribute it and/or modify  *
    8  *   it under the terms of the GNU General Public License version 2 as     *
    9  *   published by the Free Software Foundation.                            *
   10  *                                                                         *
   11  *   This program is distributed in the hope that it will be useful,       *
   12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
   13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
   14  *   GNU General Public License for more details.                          *
   15  *                                                                         *
   16  *   You should have received a copy of the GNU General Public License     *
   17  *   along with this program; if not, write to the                         *
   18  *   Free Software Foundation, Inc.,                                       *
   19  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
   20  ***************************************************************************/
   21 
   22 
   23 #ifndef NotesH
   24 #define NotesH
   25 
   26 #include  <string>
   27 #include  <vector>
   28 #include  <set>
   29 #include  <iosfwd>
   30 #include  <stdexcept>
   31 
   32 #include  <QApplication> // for translation
   33 
   34 #include  "SerSupport.h"
   35 #include  <boost/serialization/split_member.hpp>
   36 #ifndef Q_MOC_RUN  // See: https://bugreports.qt-project.org/browse/QTBUG-22829
   37 #include  <boost/lexical_cast.hpp>
   38 #endif
   39 
   40 #include  "CbException.h"
   41 
   42 #ifdef ERR
   43 #undef ERR
   44 #endif
   45 
   46 /*
   47 Stores a message that is added while parsing a file. Multiple messages may be added, depending on the issues found.
   48 
   49 Notes are added to an Mp3Handler:
   50     1) while creating (or attempting to create) new streams
   51     2) after the streams were created, addressing the relations between streams (this may include non-existing streams that should have been present).
   52 
   53 Calling MP3_CHECK(), MP3_THROW() or MP3_NOTE() adds a note to an Mp3Handler.
   54 
   55 Notes have a "Severity":
   56     ERR: Something was seriously wrong and it should be reported to the user.
   57     WARNING: There may be some issues, in some cases.
   58     SUPPORT: A stream tries to use a feature that is not currently supported. The program needs an update.
   59     TRACE: Meant for debugging only. Most operations generate TRACE notes, so that it should be easy to follow the steps that led to an unexpected situation while processing a given file.
   60 
   61 A note may be attached to a stream. This happens implicitly, by using the "pos" value: if this is -1, the note is non-attached; otherwise it is attached to the stream that contains that address. As a result, it is possible for ERR and SUPPORT notes to be attached to apparently unrelated streams (usually they would be "Unknown" streams). This happens because they belong to streams that can't be constructed, because they are either broken or not currently supported. When the construction fails, "Unknown" streams are most likely to be constructed instead, and the note gets attached to them.
   62 
   63 Note that not all exceptions thrown on streams' constructors with MP3_CHECK() or MP3_THROW are ERR. Indeed, most are TRACE. This is because the algorithm for identifying the streams simply tries to construct all known streams (for a given position). If the actual stream doesn't match the constructor, a TRACE exception is thrown and the constructor for the next stream type is called. On the other hand, if the stream can be identified as matching by the constructor but has errors preventing the constructor to complete successfully, ERR is the severity that should be used by the note. (And similarly for not supported features, but depending on the particular case, it may be decided to continue loading with that support missing and partial functionality.)
   64 
   65 SUPPORT notes end up in the UI, so care should be taken to avoid false positives (e.g. if "MPEG 2.5 not supported" is thrown as SUPPORT, we should check that an MPEG 2.5 stream is present, not just a few bits at the beginning"; anyway, for this reason, "MPEG 2.5 not supported" is thrown as TRACE).
   66 
   67 Since SUPPORT notes are usually attached to "Unknown" streams, the original stream (which couldn't be constructed because of the unsupported feature) should be identifiable from the error message. The same is true for ERR exceptions thrown on the constructors, but it isn't necessary for the other notes (at least for those that are linked to streams).
   68 
   69 Reformulated: there are 2 situations for exceptions in constructors:
   70     1) obviously not the stream that was expected, no need to report it; use TRACE;
   71     2) the beginning was OK, several checks went OK, but then something went wrong; should be reported, and the type of the stream should be specified in the message; use ERR or SUPPORT;
   72 
   73 "Detail" is better avoided, because it takes space. It is supposed to provide context-dependent detail for "description", but most of the time the only detail that matters is the position, which is already available. The code that uses Note should call getDetail() first, and if that returns an empty string use getDescription() to present the user with all the information available.
   74 
   75 */
   76 struct Note
   77 {
   78     enum Severity { ERR, WARNING, SUPPORT, TRACE }; // !!! the reason "ERR" is used (and not "ERROR") is that "ERROR" is a macro in MSVC
   79     enum Category { AUDIO, XING, VBRI, ID3V2, APIC, ID3V230, ID3V240, ID3V1, BROKEN, TRUNCATED, UNKNOWN, LYRICS, APE, MISC, CUSTOM, CATEG_CNT }; // !!! CUSTOM must be just before CATEG_CNT (which is just a counter)
   80 
   81     static const std::string severityToString(Severity s);
   82 
   83     struct SharedData // the purpose of this is to make the Note class as small as possible, by storing shared info only once
   84     {
   85         Severity m_eSeverity;
   86         Category m_eCategory;
   87         int m_nLabelIndex; // position in a given category
   88         int m_nNoteId; // position in the global vector for non-trace notes; -1 for trace
   89         bool m_bAllowErase; // needed for the option to remove offending streams;
   90 
   91         std::string m_strDescription;
   92 
   93         SharedData(const std::string& strDescription, bool bAllowErase) : m_eSeverity(TRACE), m_eCategory(CUSTOM), m_nLabelIndex(-1), m_nNoteId(-1), m_bAllowErase(bAllowErase), m_strDescription(strDescription) {}
   94     private:
   95         friend struct Notes;
   96         SharedData(Severity eSeverity, Category eCategory, const std::string& strDescription, bool bAllowErase) : m_eSeverity(eSeverity), m_eCategory(eCategory), m_nLabelIndex(-1), m_nNoteId(-1), m_bAllowErase(bAllowErase), m_strDescription(strDescription) {}
   97 
   98     private:
   99         friend class boost::serialization::access;
  100         SharedData() {}
  101         template<class Archive>
  102         void serialize(Archive& ar, const unsigned int nVersion)
  103         {
  104             if (nVersion > 0) { CB_THROW1(CbRuntimeError, "invalid version of serialized file"); }
  105 
  106             ar & m_strDescription;
  107             // !!! don't care about other attributes, because after loading, a SharedData object is replaced with one from Notes::getNote(strDescr)
  108         }
  109     };
  110 
  111     Note(const Note&, std::streampos pos, const std::string& strDetail = "");
  112     Note(SharedData& sharedData, std::streampos pos, const std::string& strDetail = ""); // Severity is forced to TRACE
  113     ~Note();
  114 private:
  115     friend struct Notes;
  116     Note(SharedData& sharedData);
  117     SharedData* m_pSharedData; // !!! can't be const because Notes needs to set indexes when adding notes to vectors, and also because of serialization
  118 
  119 public:
  120 
  121     //id // to sort columns
  122     const char* getDescription() const { return m_pSharedData->m_strDescription.c_str(); }
  123     std::string getDetail() const { return m_strDetail; }
  124     std::streampos getPos() const { return m_pos; }
  125     std::string getPosHex() const; // returns an empty string for an invalid position (i.e. one initialized from -1)
  126     Severity getSeverity() const { return m_pSharedData->m_eSeverity; }
  127     Category getCategory() const { return m_pSharedData->m_eCategory; }
  128     int getLabelIndex() const { return m_pSharedData->m_nLabelIndex; } // index in a given category; used for displaying a label
  129     int getNoteId() const { return m_pSharedData->m_nNoteId; } // index across categories; used for sorting
  130     bool allowErase() const { return m_pSharedData->m_bAllowErase; }
  131 
  132     //bool operator<(const Note&) const; // error before warning, ... ; alpha by descr ; alpha by detail
  133     bool operator==(const Note& other) const;
  134 
  135 protected:
  136     std::streampos m_pos;
  137     std::string m_strDetail;
  138     //mutable char m_szPosBfr [20];
  139 
  140 private:
  141     friend class boost::serialization::access;
  142     Note();
  143     /*template<class Archive>
  144     void serialize(Archive& ar, const unsigned int / *nVersion* /)
  145     {
  146     }*/
  147 
  148     template<class Archive> void save(Archive& ar, const unsigned int nVersion) const;
  149     template<class Archive> void load(Archive& ar, const unsigned int nVersion);
  150 
  151     BOOST_SERIALIZATION_SPLIT_MEMBER()
  152 };
  153 
  154 
  155 
  156 /*
  157 
  158 The order of DECL_NOTE_INFO() in struct Notes is irrelevant. What matters is what happens in initVec(). The current approach is to group together related notes, regardless of their severity. So audio-related notes come first, then Xing, then ID3V2 ...
  159 
  160 The labels are used for convenience. They may change between releases, and are not used internally for anything (e.g. "ignored" notes are stored as full-text in the config file, so they remain ignored as long as their text doesn't change, even if their label changes.)
  161 
  162 For a given color / severity the labels should be ordered alphabetically, though (well, as long as there are enough letters; then it depends on what m_nLabelIndex gets mapped to)
  163 
  164 */
  165 
  166 // translation of a note
  167 #define noteTr(MSG_ID) QCoreApplication::translate("Notes", Notes::MSG_ID().getDescription())
  168 
  169 #define DECL_NOTE_INFO(NAME, SEV, CATEG, MSG, ALLOW_ERASE) static Note& NAME() { static Note::SharedData d (Note::SEV, Note::CATEG, MSG, ALLOW_ERASE); static Note n (d); return n; }
  170 
  171 struct Notes
  172 {
  173     Q_DECLARE_TR_FUNCTIONS(Notes)
  174 
  175 public:
  176     static const Note* getNote(const std::string& strDescr); // returns 0 if not found
  177     static const Note* getNote(int n); // returns 0 if n is out of range
  178     static const Note* getMaster(const Note* p); // asserts there is a master
  179 
  180     static const Note* getMissingNote(); // to be used by serialization: if a description is no longer found, the note gets replace with a default, "missing", one
  181     static void addToDestroyList(Note::SharedData* p) { s_spDestroyList.insert(p); } // while loading from disk, Notes replace the SharedData objects from the disk with instances from getNote(strDescr); after the loading is done, the list (actually a set) must be cleared;
  182     static void clearDestroyList(); // destoys the pointers added by addToDestroyList(); to be called after loading is complete;
  183 
  184     // a list with all known notes
  185     static const std::vector<const Note*>& getAllNotes();
  186 
  187     static const std::vector<int>& getDefaultIgnoredNoteIds();
  188 
  189     // audio // a
  190     DECL_NOTE_INFO(twoAudio, ERR, AUDIO, QT_TR_NOOP("Two MPEG audio streams found, but a file should have exactly one."), false) // a
  191     DECL_NOTE_INFO(lowQualAudio, WARNING, AUDIO, QT_TR_NOOP("Low quality MPEG audio stream. (What is considered \"low quality\" can be changed in the configuration dialog, under \"Quality thresholds\".)"), false) // b
  192     DECL_NOTE_INFO(noAudio, ERR, AUDIO, QT_TR_NOOP("No MPEG audio stream found."), false) // c
  193     DECL_NOTE_INFO(vbrUsedForNonMpg1L3, WARNING, AUDIO, QT_TR_NOOP("VBR with audio streams other than MPEG1 Layer III might work incorrectly."), false) // d
  194     DECL_NOTE_INFO(incompleteFrameInAudio, ERR, AUDIO, QT_TR_NOOP("Incomplete MPEG frame at the end of an MPEG stream."), false) // e
  195     DECL_NOTE_INFO(validFrameDiffVer, ERR, AUDIO, QT_TR_NOOP("Valid frame with a different version found after an MPEG stream."), false) // f
  196     DECL_NOTE_INFO(validFrameDiffLayer, ERR, AUDIO, QT_TR_NOOP("Valid frame with a different layer found after an MPEG stream."), false) // g
  197     DECL_NOTE_INFO(validFrameDiffMode, ERR, AUDIO, QT_TR_NOOP("Valid frame with a different channel mode found after an MPEG stream."), false) // h
  198     DECL_NOTE_INFO(validFrameDiffFreq, ERR, AUDIO, QT_TR_NOOP("Valid frame with a different frequency found after an MPEG stream."), false) // i
  199     DECL_NOTE_INFO(validFrameDiffCrc, ERR, AUDIO, QT_TR_NOOP("Valid frame with a different CRC policy found after an MPEG stream."), false) // j
  200     DECL_NOTE_INFO(audioTooShort, ERR, AUDIO, QT_TR_NOOP("Invalid MPEG stream. Stream has fewer than 10 frames."), false) // k
  201     DECL_NOTE_INFO(diffBitrateInFirstFrame, ERR, AUDIO, QT_TR_NOOP("Invalid MPEG stream. First frame has different bitrate than the rest."), false) // l
  202     DECL_NOTE_INFO(noMp3Gain, WARNING, AUDIO, QT_TR_NOOP("No normalization undo information found. The song is probably not normalized by MP3Gain or a similar program. As a result, it may sound too loud or too quiet when compared to songs from other albums."), false) // n
  203     DECL_NOTE_INFO(untestedEncoding, SUPPORT, AUDIO, QT_TR_NOOP("Found audio stream in an encoding other than \"MPEG-1 Layer 3\" or \"MPEG-2 Layer 3.\" While MP3 Diags understands such streams, very few tests were run on files containing them (because they are not supposed to be found inside files with the \".mp3\" extension), so there is a bigger chance of something going wrong while processing them."), false) // o
  204 
  205     // xing // b
  206     DECL_NOTE_INFO(twoLame, ERR, XING, QT_TR_NOOP("Two Lame headers found, but a file should have at most one of them."), true) // a
  207     DECL_NOTE_INFO(xingAddedByMp3Fixer, WARNING, XING, QT_TR_NOOP("Xing header seems added by Mp3Fixer, which makes the first frame unusable and causes a 16-byte unknown or null stream to be detected next."), false) // b
  208     DECL_NOTE_INFO(xingFrameCountMismatch, ERR, XING, QT_TR_NOOP("Frame count mismatch between the Xing header and the audio stream."), false) // c
  209     DECL_NOTE_INFO(twoXing, ERR, XING, QT_TR_NOOP("Two Xing headers found, but a file should have at most one of them."), false) // d
  210     DECL_NOTE_INFO(xingNotBeforeAudio, ERR, XING, QT_TR_NOOP("The Xing header should be located immediately before the MPEG audio stream."), false) // e
  211     DECL_NOTE_INFO(incompatXing, ERR, XING, QT_TR_NOOP("The Xing header should be compatible with the MPEG audio stream, meaning that their MPEG version, layer and frequency must be equal."), false) // f
  212     DECL_NOTE_INFO(missingXing, WARNING, XING, QT_TR_NOOP("The MPEG audio stream uses VBR but a Xing header wasn't found. This will confuse some players, which won't be able to display the song duration or to seek."), false) // g
  213     DECL_NOTE_INFO(xingFrameInCount, WARNING, XING, QT_TR_NOOP("Xing header included in audio frame count. This is probably best ignored, as most players are fine with it and the fix erases potentially important information, like gapless playing information or the table of contents."), false) // h
  214 
  215     // vbri // c
  216     DECL_NOTE_INFO(twoVbri, ERR, VBRI, QT_TR_NOOP("Two VBRI headers found, but a file should have at most one of them."), true) // a
  217     DECL_NOTE_INFO(vbriFound, WARNING, VBRI, QT_TR_NOOP("VBRI headers aren't well supported by some players. They should be replaced by Xing headers."), true) // b
  218     DECL_NOTE_INFO(foundVbriAndXing, WARNING, VBRI, QT_TR_NOOP("VBRI header found alongside Xing header. The VBRI header should probably be removed."), true) // c
  219 
  220     // id3 v2 // d
  221     DECL_NOTE_INFO(id3v2FrameTooShort, ERR, ID3V2, QT_TR_NOOP("Invalid ID3V2 frame. File too short."), false) // a
  222     DECL_NOTE_INFO(id3v2InvalidName, ERR, ID3V2, QT_TR_NOOP("Invalid frame name in ID3V2 tag."), true) // b
  223     DECL_NOTE_INFO(id3v2IncorrectFlg1, WARNING, ID3V2, QT_TR_NOOP("Flags in the first flag group that are supposed to always be 0 are set to 1. They will be ignored."), true) // c
  224     DECL_NOTE_INFO(id3v2IncorrectFlg2, WARNING, ID3V2, QT_TR_NOOP("Flags in the second flag group that are supposed to always be 0 are set to 1. They will be ignored."), true) // d
  225     DECL_NOTE_INFO(id3v2TextError, ERR, ID3V2, QT_TR_NOOP("Error decoding the value of a text frame while reading an Id3V2 Stream."), true) // e
  226     DECL_NOTE_INFO(id3v2HasLatin1NonAscii, WARNING, ID3V2, QT_TR_NOOP("ID3V2 tag has text frames using Latin-1 encoding that contain characters with a code above 127. While this is valid, those frames may have their content set or displayed incorrectly by software that uses the local code page instead of Latin-1. Conversion to Unicode (UTF16) is recommended."), true) // f
  227     DECL_NOTE_INFO(id3v2EmptyTcon, WARNING, ID3V2, QT_TR_NOOP("Empty genre frame (TCON) found."), true) // g
  228     DECL_NOTE_INFO(id3v2MultipleFramesWithSameName, WARNING, ID3V2, QT_TR_NOOP("Multiple frame instances found, but only the first one will be used."), true) // h
  229     DECL_NOTE_INFO(id3v2PaddingTooLarge, WARNING, ID3V2, QT_TR_NOOP("The padding in the ID3V2 tag is too large, wasting space. (Large padding improves the tag editor saving speed, if fast saving is enabled, so you may want to delay compacting the tag until after you're done with the tag editor.)"), false) // i
  230     DECL_NOTE_INFO(id3v2UnsuppVer, SUPPORT, ID3V2, QT_TR_NOOP("Unsupported ID3V2 version."), true) // j
  231     DECL_NOTE_INFO(id3v2UnsuppFlag, SUPPORT, ID3V2, QT_TR_NOOP("Unsupported ID3V2 tag. Unsupported flag."), true) // k
  232     DECL_NOTE_INFO(id3v2UnsuppFlags1, SUPPORT, ID3V2, QT_TR_NOOP("Unsupported value for Flags1 in ID3V2 frame. (This may also indicate that the file contains garbage where it was supposed to be zero.)"), true) // l
  233     DECL_NOTE_INFO(id3v2UnsuppFlags2, SUPPORT, ID3V2, QT_TR_NOOP("Unsupported value for Flags2 in ID3V2 frame. (This may also indicate that the file contains garbage where it was supposed to be zero.)"), true) // n
  234     DECL_NOTE_INFO(id3v2DuplicatePopm, SUPPORT, ID3V2, QT_TR_NOOP("Multiple instances of the POPM frame found in ID3V2 tag. The current version discards all the instances except the first when processing this tag."), true) // o
  235     DECL_NOTE_INFO(id3v2EmptyTag, WARNING, ID3V2, QT_TR_NOOP("ID3V2 tag contains no frames, which is invalid. This note will disappear once you add track information in the tag editor."), true) //p
  236     DECL_NOTE_INFO(id3v2EmptyTextFrame, WARNING, ID3V2, QT_TR_NOOP("ID3V2 tag contains an empty text frame, which is invalid."), true) //q
  237 
  238     // apic // e
  239     DECL_NOTE_INFO(id3v2NoApic, WARNING, APIC, QT_TR_NOOP("ID3V2 tag doesn't have an APIC frame (which is used to store images)."), true) // a
  240     DECL_NOTE_INFO(id3v2CouldntLoadPic, WARNING, APIC, QT_TR_NOOP("ID3V2 tag has an APIC frame (which is used to store images), but the image couldn't be loaded."), true) // b
  241     DECL_NOTE_INFO(id3v2NotCoverPicture, WARNING, APIC, QT_TR_NOOP("ID3V2 tag has at least one valid APIC frame (which is used to store images), but no frame has a type that is associated with an album cover."), true) // c
  242     DECL_NOTE_INFO(id3v2ErrorLoadingApic, WARNING, APIC, QT_TR_NOOP("Error loading image in APIC frame."), true) // d
  243     DECL_NOTE_INFO(id3v2ErrorLoadingApicTooShort, WARNING, APIC, QT_TR_NOOP("Error loading image in APIC frame. The frame is too short anyway to have space for an image."), true) // e
  244     DECL_NOTE_INFO(id3v2DuplicatePic, ERR, APIC, QT_TR_NOOP("ID3V2 tag has multiple APIC frames with the same picture type."), true) // f
  245     DECL_NOTE_INFO(id3v2MultipleApic, WARNING, APIC, QT_TR_NOOP("ID3V2 tag has multiple APIC frames. While this is valid, players usually use only one of them to display an image, discarding the others."), true) // g
  246     //DECL_NOTE_INFO(id3v2LinkNotSupported, SUPPORT, APIC, QT_TR_NOOP("ID3V2 tag has an APIC frame (which is used to store images), but it uses a link to an external file, which is not supported."), false)
  247     DECL_NOTE_INFO(id3v2UnsupApicTextEnc, SUPPORT, APIC, QT_TR_NOOP("Unsupported text encoding for APIC frame in ID3V2 tag."), true) // h
  248     DECL_NOTE_INFO(id3v2LinkInApic, SUPPORT, APIC, QT_TR_NOOP("APIC frame uses a link to a file as a MIME Type, which is not supported."), true) // i
  249     DECL_NOTE_INFO(id3v2PictDescrIgnored, SUPPORT, APIC, QT_TR_NOOP("Picture description is ignored in the current version."), true) // j
  250     DECL_NOTE_INFO(id3v2ProgressiveJpeg, WARNING, APIC, QT_TR_NOOP("Found image encoded as progressive JPEG. Some players might be incompatible with it."), true) // k
  251 
  252     // id3 v2.3.0 // f
  253     DECL_NOTE_INFO(noId3V230, WARNING, ID3V230, QT_TR_NOOP("No ID3V2.3.0 tag found, although this is the most popular tag for storing song information."), false) // a
  254     DECL_NOTE_INFO(twoId3V230, ERR, ID3V230, QT_TR_NOOP("Two ID3V2.3.0 tags found, but a file should have at most one of them."), true) // b
  255     DECL_NOTE_INFO(bothId3V230_V240, WARNING, ID3V230, QT_TR_NOOP("Both ID3V2.3.0 and ID3V2.4.0 tags found, but there should be only one of them."), true) // c
  256     DECL_NOTE_INFO(id3v230AfterAudio, ERR, ID3V230, QT_TR_NOOP("The ID3V2.3.0 tag should be the first tag in a file."), true) // d
  257     DECL_NOTE_INFO(id3v230UsesUtf8, WARNING, ID3V230, QT_TR_NOOP("ID3V2.3.0 tag contains a text frame encoded as UTF-8, which is valid in ID3V2.4.0 but not in ID3V2.3.0."), true) // e
  258     DECL_NOTE_INFO(id3v230UnsuppText, SUPPORT, ID3V230, QT_TR_NOOP("Unsupported value of text frame while reading an Id3V2 Stream."), true) // f
  259     DECL_NOTE_INFO(id3v230CantReadFrame, ERR, ID3V230, QT_TR_NOOP("Invalid ID3V2.3.0 frame. Incorrect frame size or file too short."), true) // g
  260 
  261     // id3 v2.4.0 // g
  262     DECL_NOTE_INFO(twoId3V240, ERR, ID3V240, QT_TR_NOOP("Two ID3V2.4.0 tags found, but a file should have at most one of them."), true) // a
  263     DECL_NOTE_INFO(id3v240CantReadFrame, ERR, ID3V240, QT_TR_NOOP("Invalid ID3V2.4.0 frame. Incorrect frame size or file too short."), true) // b
  264     DECL_NOTE_INFO(id3v240IncorrectSynch, WARNING, ID3V240, QT_TR_NOOP("Invalid ID3V2.4.0 frame. Frame size is supposed to be stored as a synchsafe integer, which uses only 7 bits in a byte, but the size uses all 8 bits, as in ID3V2.3.0. This will confuse some applications"), true) // c
  265     DECL_NOTE_INFO(id3v240DeprTyerAndTdrc, WARNING, ID3V240, QT_TR_NOOP("Deprecated TYER frame found in 2.4.0 tag alongside a TDRC frame."), true) // d
  266     DECL_NOTE_INFO(id3v240DeprTyer, WARNING, ID3V240, QT_TR_NOOP("Deprecated TYER frame found in 2.4.0 tag. It's supposed to be replaced by a TDRC frame."), true) // e
  267     DECL_NOTE_INFO(id3v240DeprTdatAndTdrc, WARNING, ID3V240, QT_TR_NOOP("Deprecated TDAT frame found in 2.4.0 tag alongside a TDRC frame."), true) // f
  268     DECL_NOTE_INFO(id3v240DeprTdat, WARNING, ID3V240, QT_TR_NOOP("Deprecated TDAT frame found in 2.4.0 tag. It's supposed to be replaced by a TDRC frame."), true) // g
  269     DECL_NOTE_INFO(id3v240IncorrectDli, WARNING, ID3V240, QT_TR_NOOP("Invalid ID3V2.4.0 frame. Mismatched Data length indicator. Frame value is probably incorrect"), true) // h
  270     DECL_NOTE_INFO(id3v240IncorrectFrameSynch, WARNING, ID3V240, QT_TR_NOOP("Invalid ID3V2.4.0 frame. Incorrect unsynchronization bit."), true) // i
  271     DECL_NOTE_INFO(id3v240UnsuppText, SUPPORT, ID3V240, QT_TR_NOOP("Unsupported value of text frame while reading an Id3V2.4.0 stream. It may be using an unsupported text encoding."), true) // j
  272 
  273     // id3 v1 // h
  274     DECL_NOTE_INFO(onlyId3V1, WARNING, ID3V1, QT_TR_NOOP("The only supported tag found that is capable of storing song information is ID3V1, which has pretty limited capabilities."), false) // a
  275     DECL_NOTE_INFO(id3v1BeforeAudio, ERR, ID3V1, QT_TR_NOOP("The ID3V1 tag should be located after the MPEG audio stream."), true) // b
  276     DECL_NOTE_INFO(id3v1TooShort, ERR, ID3V1, QT_TR_NOOP("Invalid ID3V1 tag. File too short."), false) // c
  277     DECL_NOTE_INFO(twoId3V1, ERR, ID3V1, QT_TR_NOOP("Two ID3V1 tags found, but a file should have at most one of them."), true) // d
  278     //DECL_NOTE_INFO(zeroInId3V1, WARNING, ID3V1, QT_TR_NOOP("ID3V1 tag contains characters with the code 0, although this is not allowed by the standard (yet used by some tools)."), false)
  279     DECL_NOTE_INFO(mixedPaddingInId3V1, WARNING, ID3V1, QT_TR_NOOP("ID3V1 tag contains fields padded with spaces alongside fields padded with zeroes. The standard only allows zeroes, but some tools use spaces. Even so, zero-padding and space-padding shouldn't be mixed."), true) // e
  280     DECL_NOTE_INFO(mixedFieldPaddingInId3V1, WARNING, ID3V1, QT_TR_NOOP("ID3V1 tag contains fields that are padded with spaces mixed with zeroes. The standard only allows zeroes, but some tools use spaces. Even so, one character should be used for padding for the whole tag."), true) // f
  281     DECL_NOTE_INFO(id3v1InvalidName, ERR, ID3V1, QT_TR_NOOP("Invalid ID3V1 tag. Invalid characters in Name field."), true) // g
  282     DECL_NOTE_INFO(id3v1InvalidArtist, ERR, ID3V1, QT_TR_NOOP("Invalid ID3V1 tag. Invalid characters in Artist field."), true) // h
  283     DECL_NOTE_INFO(id3v1InvalidAlbum, ERR, ID3V1, QT_TR_NOOP("Invalid ID3V1 tag. Invalid characters in Album field."), true) // i
  284     DECL_NOTE_INFO(id3v1InvalidYear, ERR, ID3V1, QT_TR_NOOP("Invalid ID3V1 tag. Invalid characters in Year field."), true) // j
  285     DECL_NOTE_INFO(id3v1InvalidComment, ERR, ID3V1, QT_TR_NOOP("Invalid ID3V1 tag. Invalid characters in Comment field."), true) // k
  286 
  287     // broken // i
  288     DECL_NOTE_INFO(brokenAtTheEnd, ERR, BROKEN, QT_TR_NOOP("Broken stream found."), true) // a
  289     DECL_NOTE_INFO(brokenInTheMiddle, ERR, BROKEN, QT_TR_NOOP("Broken stream found. Since other streams follow, it is possible that players and tools will have problems using the file. Removing the stream is recommended."), true) // b
  290 
  291     // trunc // j
  292     DECL_NOTE_INFO(truncAudioWithWholeFile, ERR, TRUNCATED, QT_TR_NOOP("Truncated MPEG stream found. The cause for this seems to be that the file was truncated."), false) // a
  293     DECL_NOTE_INFO(truncAudio, ERR, TRUNCATED, QT_TR_NOOP("Truncated MPEG stream found. Since other streams follow, it is possible that players and tools will have problems using the file. Removing the stream or padding it with 0 to reach its declared size is strongly recommended."), false) // b
  294 
  295     // unknown // k
  296     DECL_NOTE_INFO(unknTooShort, WARNING, UNKNOWN, QT_TR_NOOP("Not enough remaining bytes to create an UnknownDataStream."), false) // a
  297     DECL_NOTE_INFO(unknownAtTheEnd, ERR, UNKNOWN, QT_TR_NOOP("Unknown stream found."), true) // b
  298     DECL_NOTE_INFO(unknownInTheMiddle, ERR, UNKNOWN, QT_TR_NOOP("Unknown stream found. Since other streams follow, it is possible that players and tools will have problems using the file. Removing the stream is recommended."), true) // c
  299     DECL_NOTE_INFO(foundNull, WARNING, UNKNOWN, QT_TR_NOOP("File contains null streams."), true) // d
  300 
  301     // lyrics // l
  302     DECL_NOTE_INFO(lyrTooShort, ERR, LYRICS, QT_TR_NOOP("Invalid Lyrics stream tag. File too short."), false) // a
  303     DECL_NOTE_INFO(twoLyr, SUPPORT, LYRICS, QT_TR_NOOP("Two Lyrics tags found, but only one is supported."), true) // b // ttt2 see if this is error
  304     //DECL_NOTE_INFO(lyricsNotSupported, SUPPORT, LYRICS, QT_TR_NOOP("Lyrics tags cannot be processed in the current version. Some players don't understand them."), false)
  305     DECL_NOTE_INFO(invalidLyr, ERR, LYRICS, QT_TR_NOOP("Invalid Lyrics stream tag. Unexpected characters found."), false) // c
  306     DECL_NOTE_INFO(duplicateFields, SUPPORT, LYRICS, QT_TR_NOOP("Multiple fields with the same name were found in a Lyrics tag, but only one is supported."), true) // d
  307     //DECL_NOTE_INFO(imgInLyrics, SUPPORT, LYRICS, QT_TR_NOOP("Currently images referenced from Lyrics tags are ignored."), true) // (e)
  308     DECL_NOTE_INFO(infInLyrics, SUPPORT, LYRICS, QT_TR_NOOP("Currently INF fields in Lyrics tags are not fully supported."), true) // e
  309 
  310     // ape // n
  311     DECL_NOTE_INFO(apeItemTooShort, ERR, APE, QT_TR_NOOP("Invalid Ape Item. File too short."), false) // a
  312     DECL_NOTE_INFO(apeItemTooBig, ERR, APE, QT_TR_NOOP("Ape Item seems too big. Although the size may be any 32-bit integer, 256 bytes should be enough in practice. If this note is determined to be incorrect, it will be removed in the future."), false) // b
  313     DECL_NOTE_INFO(apeMissingTerminator, ERR, APE, QT_TR_NOOP("Invalid Ape Item. Terminator not found for item name."), false) // c
  314     DECL_NOTE_INFO(apeFoundFooter, ERR, APE, QT_TR_NOOP("Invalid Ape tag. Header expected but footer found."), false) // d
  315     DECL_NOTE_INFO(apeTooShort, ERR, APE, QT_TR_NOOP("Not an Ape tag. File too short."), false) // e
  316     DECL_NOTE_INFO(apeFoundHeader, ERR, APE, QT_TR_NOOP("Invalid Ape tag. Footer expected but header found."), false) // f
  317     DECL_NOTE_INFO(apeHdrFtMismatch, ERR, APE, QT_TR_NOOP("Invalid Ape tag. Mismatch between header and footer."), false) // g
  318     DECL_NOTE_INFO(twoApe, SUPPORT, APE, QT_TR_NOOP("Two Ape tags found, but only one is supported."), true) // h // ttt2 see if this is error
  319     DECL_NOTE_INFO(apeFlagsNotSupported, SUPPORT, APE, QT_TR_NOOP("Ape item flags not supported."), false) // i
  320     DECL_NOTE_INFO(apeUnsupported, SUPPORT, APE, QT_TR_NOOP("Unsupported Ape tag. Currently a missing header or footer are not supported."), false) // j
  321 
  322     // misc // o
  323     DECL_NOTE_INFO(fileWasChanged, WARNING, MISC, QT_TR_NOOP("The file seems to have been changed in the (short) time that passed between parsing it and the initial search for pictures. If you think that's not the case, report a bug."), false) // a
  324     DECL_NOTE_INFO(noInfoTag, WARNING, MISC, QT_TR_NOOP("No supported tag found that is capable of storing song information."), false) // b
  325     DECL_NOTE_INFO(tooManyTraceNotes, WARNING, MISC, QT_TR_NOOP("Too many TRACE notes added. The rest will be discarded."), false) // c
  326     DECL_NOTE_INFO(tooManyNotes, WARNING, MISC, QT_TR_NOOP("Too many notes added. The rest will be discarded."), false) // d
  327     DECL_NOTE_INFO(tooManyStreams, WARNING, MISC, QT_TR_NOOP("Too many streams found. Aborting processing."), false) // e
  328     DECL_NOTE_INFO(unsupportedFound, WARNING, MISC, QT_TR_NOOP("Unsupported stream found. It may be supported in the future if there's a real need for it."), false) // f
  329     DECL_NOTE_INFO(rescanningNeeded, WARNING, MISC, QT_TR_NOOP("The file was saved using the \"fast\" option. While this improves the saving speed, it may leave the notes in an inconsistent state, so you should rescan the file."), false) // g
  330     DECL_NOTE_INFO(failedToRead, ERR, MISC, QT_TR_NOOP("An error occurred while reading the file, so it wasn't fully processed. This usually happens when reading from external drives or USB sticks, in which case a workaround might be to copy the files to an internal drive."), false) // h
  331 
  332     struct CompNoteByName // needed for searching
  333     {
  334         bool operator()(const Note* p1, const Note* p2) const;
  335     };
  336     typedef std::set<Note*, CompNoteByName> NoteSet;
  337 
  338 private:
  339 
  340     //static std::vector<const Note*> s_vpErrNotes; // pointers are to static variables defined in DECL_NOTE_INFO
  341     //static std::vector<const Note*> s_vpWarnNotes; // pointers are to static variables defined in DECL_NOTE_INFO
  342     //static std::vector<const Note*> s_vpSuppNotes; // pointers are to static variables defined in DECL_NOTE_INFO
  343     static std::vector<std::vector<const Note*> > s_vpNotesByCateg;
  344 
  345     static std::vector<const Note*> s_vpAllNotes; // pointers are to static variables defined in DECL_NOTE_INFO
  346 
  347     static NoteSet s_spAllNotes; // pointers are to static variables defined in DECL_NOTE_INFO
  348 
  349     static void addNote(Note* p);
  350     static void initVec();
  351 
  352     static std::set<Note::SharedData*> s_spDestroyList; // !!! sorted by pointers
  353 };
  354 
  355 
  356 //============================================================================================================
  357 
  358 
  359 template<class Archive>
  360 inline void Note::save(Archive& ar, const unsigned int nVersion) const
  361 {
  362     if (nVersion > 0) { CB_THROW1(CbRuntimeError, "invalid version of serialized file"); }
  363 
  364     ar << m_pSharedData;
  365     ar << m_pos;
  366     ar << m_strDetail;
  367 }
  368 
  369 template<class Archive>
  370 inline void Note::load(Archive& ar, const unsigned int nVersion)
  371 {
  372     if (nVersion > 0) { CB_THROW1(CbRuntimeError, "invalid version of serialized file"); }
  373 
  374     SharedData* pSharedData;
  375     ar >> pSharedData;
  376     ar >> m_pos;
  377     ar >> m_strDetail;
  378 
  379     Notes::addToDestroyList(pSharedData);
  380     const Note* pNote (Notes::getNote(pSharedData->m_strDescription));
  381 
  382     m_pSharedData = 0 == pNote ? Notes::getMissingNote()->m_pSharedData : pNote->m_pSharedData; // 2012.02.03 - the test seems pointless: Notes::getNote(const std::string& strDescr) uses getMissingNote() if it cannot find the description
  383 }
  384 
  385 
  386 //============================================================================================================
  387 //============================================================================================================
  388 //============================================================================================================
  389 
  390 
  391 
  392 // notes associated with a Mp3Handler (but is used stand-alone too, to pass as a param to some functions and then discard the results)
  393 class NoteColl
  394 {
  395     std::vector<Note*> m_vpNotes; // owns the pointers
  396     int m_nCount;
  397     int m_nTraceCount;
  398     int m_nMaxTrace;
  399 public:
  400     NoteColl(int nMaxTrace) : m_nCount(0), m_nTraceCount(0), m_nMaxTrace(nMaxTrace) {}
  401     NoteColl() {} // serialization-only constructor
  402     ~NoteColl();
  403 
  404     void add(Note*);
  405     const std::vector<Note*>& getList() const { return m_vpNotes; }
  406     void resetCounter() { m_nCount = 0; } // normally only 200 notes are added and anything following is discarded; this allows to reset the counter
  407     void sort(); // sorts by CmpNotePtrById, which is needed for filtering
  408     void removeTraceNotes();
  409 
  410     bool hasFastSaveWarn() const;
  411     void addFastSaveWarn();
  412     void removeNotes(const std::streampos& posFrom, const std::streampos& posTo); // removes notes with addresses in the given range; posFrom is included, but posTo isn't
  413 
  414 private:
  415     friend class boost::serialization::access;
  416 
  417     template<class Archive>
  418     void serialize(Archive& ar, const unsigned int nVersion)
  419     {
  420         if (nVersion > 0) { CB_THROW1(CbRuntimeError, "invalid version of serialized file"); }
  421 
  422         ar & m_vpNotes;
  423 
  424         // these don't really matter, but they don't take a lot of space and can be useful at debugging
  425         ar & m_nCount;
  426         ar & m_nTraceCount;
  427         ar & m_nMaxTrace;
  428     }
  429 };
  430 
  431 
  432 
  433 /*struct CmpNotePtrBySevAndLabel
  434 {
  435     bool operator()(const Note* p1, const Note* p2) const
  436     {
  437         if (p1->getSeverity() < p2->getSeverity()) { return true; }
  438         if (p1->getSeverity() > p2->getSeverity()) { return false; }
  439         if (p1->getNoteId() < p2->getNoteId()) { return true; }
  440         return false;
  441     }
  442 
  443     static bool equals(const Note* p1, const Note* p2)
  444     {
  445         if (p1->getSeverity() != p2->getSeverity() || p1->getNoteId() != p2->getNoteId()) { return false; }
  446         return true;
  447     }
  448 };*/
  449 
  450 struct CmpNotePtrById // needed for unique notes, where equality doesn't include the position
  451 {
  452     bool operator()(const Note* p1, const Note* p2) const
  453     {
  454         if (p1->getNoteId() < p2->getNoteId()) { return true; }
  455         return false;
  456     }
  457 
  458     static bool equals(const Note* p1, const Note* p2)
  459     {
  460         if (p1->getNoteId() != p2->getNoteId()) { return false; }
  461         return true;
  462     }
  463 };
  464 
  465 
  466 
  467 /*
  468 struct CmpNotePtrBySevLabelAndPos
  469 {
  470     bool operator()(const Note* p1, const Note* p2) const
  471     {
  472         if (p1->getSeverity() < p2->getSeverity()) { return true; }
  473         if (p1->getSeverity() > p2->getSeverity()) { return false; }
  474         if (p1->getNoteId() < p2->getNoteId()) { return true; }
  475         if (p1->getNoteId() > p2->getNoteId()) { return false; }
  476         if (p1->getPos() < p2->getPos()) { return true; }
  477         return false;
  478     }
  479 };*/
  480 
  481 
  482 /*struct CmpNotePtrByPosSevAndLabel
  483 {
  484     bool operator()(const Note* p1, const Note* p2) const
  485     {
  486         if (p1->getPos() < p2->getPos()) { return true; }
  487         if (p1->getPos() > p2->getPos()) { return false; }
  488         if (p1->getSeverity() < p2->getSeverity()) { return true; }
  489         if (p1->getSeverity() > p2->getSeverity()) { return false; }
  490         if (p1->getNoteId() < p2->getNoteId()) { return true; }
  491         return false;
  492     }
  493 };*/
  494 
  495 /*
  496 struct CmpNotePtrByIdAndPos
  497 {
  498     bool operator()(const Note* p1, const Note* p2) const
  499     {
  500         if (p1->getNoteId() < p2->getNoteId()) { return true; }
  501         if (p1->getNoteId() > p2->getNoteId()) { return false; }
  502         if (p1->getPos() < p2->getPos()) { return true; }
  503         return false;
  504     }
  505 };*/
  506 
  507 
  508 struct CmpNotePtrByPosAndId
  509 {
  510     bool operator()(const Note* p1, const Note* p2) const
  511     {
  512         if (p1->getPos() < p2->getPos()) { return true; }
  513         if (p1->getPos() > p2->getPos()) { return false; }
  514         if (p1->getNoteId() < p2->getNoteId()) { return true; }
  515         return false;
  516     }
  517 };
  518 
  519 
  520 // allows strings to be shared (as pointers)
  521 struct StringWrp
  522 {
  523     std::string s;
  524     StringWrp(const std::string& s) : s(s) {}
  525 private:
  526     friend class boost::serialization::access;
  527     StringWrp() {}
  528 
  529     template<class Archive>
  530     void serialize(Archive& ar, const unsigned int nVersion)
  531     {
  532         if (nVersion > 0) { CB_THROW1(CbRuntimeError, "invalid version of serialized file"); }
  533 
  534         ar & s;
  535     }
  536 };
  537 
  538 //BOOST_CLASS_TRACKING(StringWrp, boost::serialization::track_always);
  539 //BOOST_CLASS_TRACKING(StringWrp, boost::serialization::track_never);
  540 
  541 
  542 #endif // #ifndef NotesH
  543 
  544