diff --git a/src/makernote.cpp b/src/makernote.cpp index acadd35b..0d478b18 100644 --- a/src/makernote.cpp +++ b/src/makernote.cpp @@ -20,16 +20,17 @@ */ /* File: makernote.cpp - Version: $Name: $ $Revision: 1.18 $ + Version: $Name: $ $Revision: 1.19 $ Author(s): Andreas Huggel (ahu) History: 18-Feb-04, ahu: created */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.18 $ $RCSfile: makernote.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.19 $ $RCSfile: makernote.cpp,v $") -// Define DEBUG_MAKERNOTE to output debug information to std::cerr +// Define DEBUG_* to output debug information to std::cerr #undef DEBUG_MAKERNOTE +#define DEBUG_REGISTRY // ***************************************************************************** // included header files @@ -42,7 +43,7 @@ EXIV2_RCSID("@(#) $Name: $ $Revision: 1.18 $ $RCSfile: makernote.cpp,v $") #include #include -#ifdef DEBUG_MAKERNOTE +#if defined DEBUG_MAKERNOTE || defined DEBUG_REGISTRY # include #endif @@ -50,6 +51,12 @@ EXIV2_RCSID("@(#) $Name: $ $Revision: 1.18 $ $RCSfile: makernote.cpp,v $") // class member definitions namespace Exiv2 { + MakerNote::MakerNote(const MnTagInfo* pMnTagInfo, bool alloc) + : pMnTagInfo_(pMnTagInfo), alloc_(alloc), + offset_(0), byteOrder_(invalidByteOrder) + { + } + std::string MakerNote::makeKey(uint16 tag) const { return std::string(ExifTags::ifdItem(makerIfd)) @@ -150,6 +157,14 @@ namespace Exiv2 { << tagDesc(tag); } // MakerNote::writeMnTagInfo + + IfdMakerNote::IfdMakerNote(const MakerNote::MnTagInfo* pMnTagInfo, + bool alloc) + : MakerNote(pMnTagInfo, alloc), + absOffset_(true), adjOffset_(0), ifd_(makerIfd, 0, alloc) + { + } + int IfdMakerNote::read(const char* buf, long len, ByteOrder byteOrder, @@ -159,24 +174,22 @@ namespace Exiv2 { offset_ = offset; // Set byte order if none is set yet if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder; - int rc = 0; - if (!prefix_.empty()) { - // Check if makernote is long enough and starts with prefix - if ( len < static_cast(prefix_.size()) - || prefix_ != std::string(buf, prefix_.size())) rc = 2; - } - if (!absOffset_) { - // Use offsets relative to the start of the Makernote field - offset = 0; + // Read and check the header (and set offset adjustment) + int rc = readHeader(buf, len, byteOrder); + if (rc == 0) { + rc = checkHeader(); } + // Adjust the offset + offset = absOffset_ ? offset + adjOffset_ : adjOffset_; + // Read the makernote IFD if (rc == 0) { - rc = ifd_.read(buf + prefix_.size(), - len - prefix_.size(), - byteOrder_, - offset + prefix_.size()); + rc = ifd_.read(buf + headerSize(), + len - headerSize(), + byteOrder_, + offset); } if (rc == 0) { - // IfdMakerNote does not support multiple IFDs + // IfdMakerNote currently does not support multiple IFDs if (ifd_.next() != 0) rc = 3; } if (rc == 0) { @@ -188,8 +201,8 @@ namespace Exiv2 { #ifdef DEBUG_MAKERNOTE hexdump(std::cerr, buf, len, offset); if (rc == 0) ifd_.print(std::cerr); - else std::cerr << "IfdMakerNote::read() failed, rc = " << rc << "\n"; #endif + return rc; } // IfdMakerNote::read @@ -199,20 +212,39 @@ namespace Exiv2 { offset_ = offset; // Set byte order if none is set yet if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder; + long len = 0; - if (!prefix_.empty()) { - // Write the prefix string to the Makernote buffer - memcpy(buf, prefix_.data(), prefix_.size()); - len += prefix_.size(); - } - if (!absOffset_) { - // Use offsets relative to the start of the Makernote field - offset = 0; - } - len += ifd_.copy(buf + len, byteOrder_, offset + len); + len += copyHeader(buf); + len += ifd_.copy(buf + len, byteOrder_, offset_); + return len; } // IfdMakerNote::copy + int IfdMakerNote::readHeader(const char* buf, + long len, + ByteOrder byteOrder) + { + // Default implementation does nothing, assuming there is no header + return 0; + } + + int IfdMakerNote::checkHeader() const + { + // Default implementation does nothing, assuming there is no header + return 0; + } + + long IfdMakerNote::copyHeader(char* buf) const + { + if (header_.size_ != 0) memcpy(buf, header_.pData_, header_.size_); + return header_.size_; + } + + long IfdMakerNote::headerSize() const + { + return header_.size_; + } + Entries::const_iterator IfdMakerNote::findIdx(int idx) const { return ifd_.findIdx(idx); @@ -220,7 +252,7 @@ namespace Exiv2 { long IfdMakerNote::size() const { - return prefix_.size() + ifd_.size() + ifd_.dataSize(); + return headerSize() + ifd_.size() + ifd_.dataSize(); } MakerNoteFactory* MakerNoteFactory::pInstance_ = 0; @@ -237,7 +269,7 @@ namespace Exiv2 { const std::string& model, CreateFct createMakerNote) { -#ifdef DEBUG_MAKERNOTE +#ifdef DEBUG_REGISTRY std::cerr << "Registering MakerNote create function for \"" << make << "\" and \"" << model << "\".\n"; #endif @@ -276,42 +308,80 @@ namespace Exiv2 { const std::string& model, bool alloc) const { +#ifdef DEBUG_REGISTRY + std::cerr << "Entering MakerNoteFactory::create(\"" + << make << "\", \"" << model << "\", " + << (alloc == true ? "true" : "false") << ")\n"; +#endif // loop through each make of the registry to find the best matching make - int matchCount = -1; + int score = 0; ModelRegistry* modelRegistry = 0; +#ifdef DEBUG_REGISTRY + std::string makeMatch; + std::cerr << "Searching make registry...\n"; +#endif Registry::const_iterator end1 = registry_.end(); Registry::const_iterator pos1; for (pos1 = registry_.begin(); pos1 != end1; ++pos1) { - std::pair rc = match(pos1->first, make); - if (rc.first && rc.second > matchCount) { - matchCount = rc.second; + int rc = match(pos1->first, make); + if (rc > score) { + score = rc; +#ifdef DEBUG_REGISTRY + makeMatch = pos1->first; +#endif modelRegistry = pos1->second; } } if (modelRegistry == 0) return 0; +#ifdef DEBUG_REGISTRY + std::cerr << "Best match is \"" << makeMatch << "\".\n"; +#endif // loop through each model of the model registry to find the best match - matchCount = -1; + score = 0; CreateFct createMakerNote = 0; +#ifdef DEBUG_REGISTRY + std::string modelMatch; + std::cerr << "Searching model registry...\n"; +#endif ModelRegistry::const_iterator end2 = modelRegistry->end(); ModelRegistry::const_iterator pos2; for (pos2 = modelRegistry->begin(); pos2 != end2; ++pos2) { - std::pair rc = match(pos2->first, model); - if (rc.first && rc.second > matchCount) { - matchCount = rc.second; + int rc = match(pos2->first, model); + if (rc > score) { + score = rc; +#ifdef DEBUG_REGISTRY + modelMatch = pos2->first; +#endif createMakerNote = pos2->second; } } if (createMakerNote == 0) return 0; +#ifdef DEBUG_REGISTRY + std::cerr << "Best match is \"" << modelMatch << "\".\n"; +#endif return createMakerNote(alloc); } // MakerNoteFactory::create - std::pair MakerNoteFactory::match(const std::string& regEntry, - const std::string& key) + int MakerNoteFactory::match(const std::string& regEntry, + const std::string& key) { +#ifdef DEBUG_REGISTRY + std::cerr << " Matching registry entry \"" << regEntry << "\" (" + << regEntry.size() << ") with key \"" << key << "\" (" + << key.size() << "): "; +#endif // Todo: make the comparisons case insensitive + // Handle exact match (this is only necessary because of the different + // return value - the following algorithm also finds exact matches) + if (regEntry == key) { +#ifdef DEBUG_REGISTRY + std::cerr << "Exact match (score: " << key.size() + 2 << ")\n"; +#endif + return key.size() + 2; + } std::string uKey = key; std::string uReg = regEntry; @@ -327,7 +397,10 @@ namespace Exiv2 { uReg.substr(ei) : uReg.substr(ei, pos - ei); if (ki == std::string::npos) { - return std::make_pair(false, 0); +#ifdef DEBUG_REGISTRY + std::cerr << "Not a match.\n"; +#endif + return 0; } bool found = false; @@ -372,15 +445,21 @@ namespace Exiv2 { count += ss.size(); } else { - return std::make_pair(false, 0); +#ifdef DEBUG_REGISTRY + std::cerr << "Not a match.\n"; +#endif + return 0; } } // if the substr is not empty ei = pos == std::string::npos ? std::string::npos : pos + 1; } // while ei doesn't point to the end of the registry entry - - return std::make_pair(true, count); + +#ifdef DEBUG_REGISTRY + std::cerr << "Match (score: " << count + 1 << ")\n"; +#endif + return count + 1; } // MakerNoteFactory::match diff --git a/src/makernote.hpp b/src/makernote.hpp index 1b7edb5b..a9da013d 100644 --- a/src/makernote.hpp +++ b/src/makernote.hpp @@ -22,7 +22,7 @@ @file makernote.hpp @brief Contains the Exif %MakerNote interface, IFD %MakerNote and a MakerNote factory - @version $Name: $ $Revision: 1.17 $ + @version $Name: $ $Revision: 1.18 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 18-Feb-04, ahu: created @@ -85,11 +85,17 @@ namespace Exiv2 { To implement a new IFD makernote, all that you need to do is - subclass %IfdMakerNote, - - implement the create function, + - implement methods to read and check the header (if any) as well as + clone and create functions, - add a list of tag descriptions and appropriate print functions and - register the camera make/model and create function in the makernote factory. . - See CanonMakerNote for an example. + See existing makernote implementations for examples, e.g., CanonMakerNote + or FujiMakerNote. + + Finally, the implementation files should be named *mn.[ch]pp, so that the + magic, which ensures that the makernote is automatically registered + in the factory, will pick it up (see mn.sh for details). */ class MakerNote { public: @@ -112,21 +118,18 @@ namespace Exiv2 { allows to choose whether or not memory management is required for the Entries. */ - MakerNote(const MnTagInfo* pMnTagInfo =0, bool alloc =true) - : pMnTagInfo_(pMnTagInfo), alloc_(alloc), - byteOrder_(invalidByteOrder), offset_(0) {} + explicit MakerNote(const MnTagInfo* pMnTagInfo =0, bool alloc =true); //! Virtual destructor. virtual ~MakerNote() {} //@} //! @name Manipulators //@{ - //! Set the byte order (little or big endian). - void setByteOrder(ByteOrder byteOrder) { byteOrder_ = byteOrder; } /*! - @brief Read the makernote from character buffer buf of length len at - position offset (from the start of the TIFF header) and encoded - in byte order byteOrder. + @brief Read the makernote, including the makernote header, from + character buffer buf of length len at position offset (from the + start of the TIFF header) and encoded in byte order byteOrder. + Return 0 if successful. */ virtual int read(const char* buf, long len, @@ -231,13 +234,16 @@ namespace Exiv2 { False: no memory management needed. */ const bool alloc_; - /*! + /*! + @brief Offset of the makernote from the start of the TIFF header + (for offset()). + */ + long offset_; + /*! @brief Alternative byte order to use, invalid if the byte order of the Exif block can be used */ ByteOrder byteOrder_; - //! Offset of the makernote from the start of the TIFF header - long offset_; }; // class MakerNote @@ -253,9 +259,8 @@ namespace Exiv2 { allows to choose whether or not memory management is required for the Entries. */ - IfdMakerNote(const MakerNote::MnTagInfo* pMnTagInfo =0, bool alloc =true) - : MakerNote(pMnTagInfo, alloc), - absOffset_(true), ifd_(makerIfd, 0, alloc) {} + explicit IfdMakerNote(const MakerNote::MnTagInfo* pMnTagInfo =0, + bool alloc =true); //! Virtual destructor virtual ~IfdMakerNote() {} //@} @@ -266,6 +271,17 @@ namespace Exiv2 { long len, ByteOrder byteOrder, long offset); + /*! + @brief Read the makernote header from the makernote databuffer. This + method must set the offset adjustment (adjOffset_), if needed + (assuming that the required information is in the header). + Return 0 if successful. + @note The default implementation does nothing, assuming there is no + header + */ + virtual int readHeader(const char* buf, + long len, + ByteOrder byteOrder); virtual long copy(char* buf, ByteOrder byteOrder, long offset); void add(const Entry& entry) { ifd_.add(entry); } Entries::iterator begin() { return ifd_.begin(); } @@ -278,7 +294,27 @@ namespace Exiv2 { Entries::const_iterator end() const { return ifd_.end(); } Entries::const_iterator findIdx(int idx) const; long size() const; - virtual MakerNote* clone(bool alloc =true) const =0; + /*! + @brief Check the makernote header. This will typically check if a + required prefix string is present in the header. Return 0 if + successful. + @note The default implementation does nothing, assuming there is no + header + */ + virtual int checkHeader() const; + /*! + @brief Write the makernote header to a character buffer, return the + number of characters written. + @note The default implementation copies the header_ buffer. + */ + virtual long copyHeader(char* buf) const; + /*! + @brief Return the size of the makernote header in bytes. + @note The default implementation returns the size of the header_ + buffer. + */ + virtual long headerSize() const; + virtual IfdMakerNote* clone(bool alloc =true) const =0; virtual std::string sectionName(uint16 tag) const =0; virtual std::ostream& printTag(std::ostream& os, uint16 tag, @@ -287,14 +323,23 @@ namespace Exiv2 { protected: // DATA - //! String prefix at the beginning of the makernote, before the IFD - std::string prefix_; /*! - @brief True: Offsets are from start of the TIFF header, - False: Offsets are from start of the makernote + @brief True: Adjustment of the IFD offsets is to be added to the + offset from the start of the TIFF header, + False: Adjustment of the IFD offsets is a suitable absolute + value. Ignore the offset from the start of the TIFF + header. + */ + bool absOffset_; + /*! + @brief Adjustment of the IFD offsets relative to the start of the + TIFF header or to the start of the makernote, depending on + the setting of absOffset_. */ - bool absOffset_; - //! MakerNote IFD + long adjOffset_; + //! Data buffer for the makernote header + DataBuf header_; + //! The makernote IFD Ifd ifd_; }; // class IfdMakerNote @@ -383,15 +428,20 @@ namespace Exiv2 { @brief Match a registry entry with a key (used for make and model). The matching algorithm is case insensitive and wildcards ('*') in the - registry entry are supported. The best match is the match with the - most matching characters. - - @return A pair of which the first component indicates whether or not - the key matches and the second component contains the number - of matching characters. + registry entry are supported. The best match is an exact match, then + a match is rated according to the number of matching characters. + + @return A score value indicating how good the key and registry entry + match. 0 means no match, values greater than 0 indicate a + match, larger values are better matches:
+ 0: key and registry entry do not match
+ 1: a pure wildcard match, i.e., the registry entry is just + a wildcard.
+ Score values greater than 1 are computed by adding 1 to the + number of matching characters, except for an exact match, + which scores 2 plus the number of matching characters. */ - static std::pair match(const std::string& regEntry, - const std::string& key); + static int match(const std::string& regEntry, const std::string& key); private: //! @name Creators