| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  | #include "AD1CCty.hpp"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <string>
 | 
					
						
							|  |  |  | #include <stdexcept>
 | 
					
						
							|  |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <boost/multi_index_container.hpp>
 | 
					
						
							|  |  |  | #include <boost/multi_index/hashed_index.hpp>
 | 
					
						
							|  |  |  | #include <boost/multi_index/ordered_index.hpp>
 | 
					
						
							|  |  |  | #include <boost/multi_index/key_extractors.hpp>
 | 
					
						
							|  |  |  | #include <boost/lambda/lambda.hpp>
 | 
					
						
							|  |  |  | #include <boost/lexical_cast.hpp>
 | 
					
						
							|  |  |  | #include <QString>
 | 
					
						
							|  |  |  | #include <QStandardPaths>
 | 
					
						
							|  |  |  | #include <QDir>
 | 
					
						
							|  |  |  | #include <QFile>
 | 
					
						
							|  |  |  | #include <QTextStream>
 | 
					
						
							|  |  |  | #include <QDebug>
 | 
					
						
							|  |  |  | #include <QDebugStateSaver>
 | 
					
						
							| 
									
										
										
										
											2023-03-16 19:13:59 -07:00
										 |  |  | #include <QRegularExpression>
 | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  | #include "Configuration.hpp"
 | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  | #include "Radio.hpp"
 | 
					
						
							|  |  |  | #include "pimpl_impl.hpp"
 | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  | #include "Logger.hpp"
 | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "moc_AD1CCty.cpp"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using namespace boost::multi_index; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   auto const file_name = "cty.dat"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct entity | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   using Continent = AD1CCty::Continent; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   explicit entity (int id | 
					
						
							|  |  |  |                    , QString const& name | 
					
						
							|  |  |  |                    , bool WAE_only | 
					
						
							|  |  |  |                    , int CQ_zone | 
					
						
							|  |  |  |                    , int ITU_zone | 
					
						
							|  |  |  |                    , Continent continent | 
					
						
							|  |  |  |                    , float latitude | 
					
						
							|  |  |  |                    , float longtitude | 
					
						
							|  |  |  |                    , int UTC_offset | 
					
						
							|  |  |  |                    , QString const& primary_prefix) | 
					
						
							|  |  |  |     : id_ {id} | 
					
						
							|  |  |  |     , name_ {name} | 
					
						
							|  |  |  |     , WAE_only_ {WAE_only} | 
					
						
							|  |  |  |     , CQ_zone_ {CQ_zone} | 
					
						
							|  |  |  |     , ITU_zone_ {ITU_zone} | 
					
						
							|  |  |  |     , continent_ {continent} | 
					
						
							|  |  |  |     , lat_ {latitude} | 
					
						
							|  |  |  |     , long_ {longtitude} | 
					
						
							|  |  |  |     , UTC_offset_ {UTC_offset} | 
					
						
							|  |  |  |     , primary_prefix_ {primary_prefix} | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   int id_; | 
					
						
							|  |  |  |   QString name_; | 
					
						
							|  |  |  |   bool WAE_only_;               // DARC WAE only, not valid for ARRL awards
 | 
					
						
							|  |  |  |   int CQ_zone_; | 
					
						
							|  |  |  |   int ITU_zone_; | 
					
						
							|  |  |  |   Continent continent_; | 
					
						
							|  |  |  |   float lat_;                   // degrees + is North
 | 
					
						
							|  |  |  |   float long_;                  // degrees + is West
 | 
					
						
							|  |  |  |   int UTC_offset_;              // seconds
 | 
					
						
							|  |  |  |   QString primary_prefix_; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if !defined (QT_NO_DEBUG_STREAM)
 | 
					
						
							|  |  |  | QDebug operator << (QDebug dbg, entity const& e) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   QDebugStateSaver saver {dbg}; | 
					
						
							|  |  |  |   dbg.nospace () << "entity(" | 
					
						
							|  |  |  |                  << e.id_ << ", " | 
					
						
							|  |  |  |                  << e.name_ << ", " | 
					
						
							|  |  |  |                  << e.WAE_only_ << ", " | 
					
						
							|  |  |  |                  << e.CQ_zone_ << ", " | 
					
						
							|  |  |  |                  << e.ITU_zone_ << ", " | 
					
						
							|  |  |  |                  << e.continent_ << ", " | 
					
						
							|  |  |  |                  << e.lat_ << ", " | 
					
						
							|  |  |  |                  << e.long_ << ", " | 
					
						
							|  |  |  |                  << (e.UTC_offset_ / (60. * 60.)) << ", " | 
					
						
							|  |  |  |                  << e.primary_prefix_ << ')'; | 
					
						
							|  |  |  |   return dbg; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // tags
 | 
					
						
							|  |  |  | struct id {}; | 
					
						
							|  |  |  | struct primary_prefix {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // hash operation for QString object instances
 | 
					
						
							|  |  |  | struct hash_QString | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   std::size_t operator () (QString const& qs) const | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     return qHash (qs); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // set with hashed unique index that allow for efficient lookup of
 | 
					
						
							|  |  |  | // entity by internal id
 | 
					
						
							|  |  |  | typedef multi_index_container< | 
					
						
							|  |  |  |   entity, | 
					
						
							|  |  |  |   indexed_by< | 
					
						
							|  |  |  |     hashed_unique<tag<id>, member<entity, int, &entity::id_> >, | 
					
						
							|  |  |  |     hashed_unique<tag<primary_prefix>, member<entity, QString, &entity::primary_prefix_>, hash_QString> > | 
					
						
							|  |  |  |   > entities_type; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct prefix | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   explicit prefix (QString const& prefix, bool exact_match_only, int entity_id) | 
					
						
							|  |  |  |     : prefix_ {prefix} | 
					
						
							|  |  |  |     , exact_ {exact_match_only} | 
					
						
							|  |  |  |     , entity_id_ {entity_id} | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // extract key which is the prefix ignoring the trailing override
 | 
					
						
							|  |  |  |   // information
 | 
					
						
							|  |  |  |   QString prefix_key () const | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     auto const& prefix = prefix_.toStdString (); | 
					
						
							|  |  |  |     return QString::fromStdString (prefix.substr (0, prefix.find_first_of ("({[<~"))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |   QString prefix_;              // call or prefix with optional
 | 
					
						
							|  |  |  |                                 // trailing override information
 | 
					
						
							|  |  |  |   bool exact_; | 
					
						
							|  |  |  |   int entity_id_; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if !defined (QT_NO_DEBUG_STREAM)
 | 
					
						
							|  |  |  | QDebug operator << (QDebug dbg, prefix const& p) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   QDebugStateSaver saver {dbg}; | 
					
						
							|  |  |  |   dbg.nospace () << "prefix(" | 
					
						
							|  |  |  |                  << p.prefix_ << ", " | 
					
						
							|  |  |  |                  << p.exact_ << ", " | 
					
						
							|  |  |  |                  << p.entity_id_ << ')'; | 
					
						
							|  |  |  |   return dbg; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // set with ordered unique index that allow for efficient
 | 
					
						
							|  |  |  | // determination of entity and entity overrides for a call or call
 | 
					
						
							|  |  |  | // prefix
 | 
					
						
							|  |  |  | typedef multi_index_container< | 
					
						
							|  |  |  |   prefix, | 
					
						
							|  |  |  |   indexed_by< | 
					
						
							|  |  |  |     ordered_unique<const_mem_fun<prefix, QString, &prefix::prefix_key> > > | 
					
						
							|  |  |  |   > prefixes_type; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class AD1CCty::impl final | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |   using entity_by_id = entities_type::index<id>::type; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   explicit impl (Configuration const * configuration) | 
					
						
							|  |  |  |     : configuration_ {configuration} | 
					
						
							| 
									
										
										
										
											2018-10-26 17:30:58 +01:00
										 |  |  |   { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  |   QString get_cty_path(const Configuration *configuration); | 
					
						
							|  |  |  |   void load_cty(QFile &file); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |   entity_by_id::iterator lookup_entity (QString call, prefix const& p) const | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2018-11-29 00:56:53 +00:00
										 |  |  |     call = call.toUpper (); | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |     entity_by_id::iterator e;   // iterator into entity set
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // deal with special rules that cty.dat does not cope with
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     if (call.startsWith ("KG4") && call.size () != 5 && call.size () != 3) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         // KG4 2x1 and 2x3 calls that map to Gitmo are mainland US not Gitmo
 | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |         return entities_.project<id> (entities_.get<primary_prefix> ().find ("K")); | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |       } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       { | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |         return entities_.get<id> ().find (p.entity_id_); | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Record fixup (prefix const& p, entity const& e) const | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |     Record result; | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |     result.continent = e.continent_; | 
					
						
							|  |  |  |     result.CQ_zone = e.CQ_zone_; | 
					
						
							|  |  |  |     result.ITU_zone = e.ITU_zone_; | 
					
						
							|  |  |  |     result.entity_name = e.name_; | 
					
						
							|  |  |  |     result.WAE_only = e.WAE_only_; | 
					
						
							|  |  |  |     result.latitude = e.lat_; | 
					
						
							|  |  |  |     result.longtitude = e.long_; | 
					
						
							|  |  |  |     result.UTC_offset = e.UTC_offset_; | 
					
						
							|  |  |  |     result.primary_prefix = e.primary_prefix_; | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // check for overrides
 | 
					
						
							|  |  |  |     bool ok1 {true}, ok2 {true}, ok3 {true}, ok4 {true}, ok5 {true}; | 
					
						
							|  |  |  |     QString value; | 
					
						
							|  |  |  |     if (override_value (p.prefix_, '(', ')', value)) result.CQ_zone = value.toInt (&ok1); | 
					
						
							|  |  |  |     if (override_value (p.prefix_, '[', ']', value)) result.ITU_zone = value.toInt (&ok2); | 
					
						
							|  |  |  |     if (override_value (p.prefix_, '<', '>', value)) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         auto const& fix = value.split ('/'); | 
					
						
							|  |  |  |         result.latitude = fix[0].toFloat (&ok3); | 
					
						
							|  |  |  |         result.longtitude = fix[1].toFloat (&ok4); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     if (override_value (p.prefix_, '{', '}', value)) result.continent = continent (value); | 
					
						
							|  |  |  |     if (override_value (p.prefix_, '~', '~', value)) result.UTC_offset = static_cast<int> (value.toFloat (&ok5) * 60 * 60); | 
					
						
							|  |  |  |     if (!(ok1 && ok2 && ok3 && ok4 && ok5)) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         throw std::domain_error {"Invalid number in cty.dat for override of " + p.prefix_.toStdString ()}; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     return result; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static bool override_value (QString const& s, QChar lb, QChar ub, QString& v) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     auto pos = s.indexOf (lb); | 
					
						
							|  |  |  |     if (pos >= 0) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         v = s.mid (pos + 1, s.indexOf (ub, pos + 1) - pos - 1); | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |   Configuration const * configuration_; | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |   QString path_; | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  |   QString cty_version_; | 
					
						
							| 
									
										
										
										
											2023-03-16 19:13:59 -07:00
										 |  |  |   QString cty_version_date_; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |   entities_type entities_; | 
					
						
							|  |  |  |   prefixes_type prefixes_; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AD1CCty::Record::Record () | 
					
						
							|  |  |  |   : continent {Continent::UN} | 
					
						
							|  |  |  |   , CQ_zone {0} | 
					
						
							|  |  |  |   , ITU_zone {0} | 
					
						
							|  |  |  |   , WAE_only {false} | 
					
						
							|  |  |  |   , latitude {NAN} | 
					
						
							|  |  |  |   , longtitude {NAN} | 
					
						
							|  |  |  |   , UTC_offset {0} | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if !defined (QT_NO_DEBUG_STREAM)
 | 
					
						
							|  |  |  |   QDebug operator << (QDebug dbg, AD1CCty::Record const& r) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     QDebugStateSaver saver {dbg}; | 
					
						
							|  |  |  |     dbg.nospace () << "AD1CCty::Record(" | 
					
						
							|  |  |  |                    << r.continent << ", " | 
					
						
							|  |  |  |                    << r.CQ_zone << ", " | 
					
						
							|  |  |  |                    << r.ITU_zone << ", " | 
					
						
							|  |  |  |                    << r.entity_name << ", " | 
					
						
							|  |  |  |                    << r.WAE_only << ", " | 
					
						
							|  |  |  |                    << r.latitude << ", " | 
					
						
							|  |  |  |                    << r.longtitude << ", " | 
					
						
							|  |  |  |                    << (r.UTC_offset / (60. * 60.)) << ", " | 
					
						
							|  |  |  |                    << r.primary_prefix << ')'; | 
					
						
							|  |  |  |     return dbg; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | auto AD1CCty::continent (QString const& continent_id) -> Continent | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   Continent continent; | 
					
						
							|  |  |  |   if ("AF" == continent_id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       continent = Continent::AF; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else if ("AN" == continent_id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       continent = Continent::AN; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else if ("AS" == continent_id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       continent = Continent::AS; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else if ("EU" == continent_id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       continent = Continent::EU; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else if ("NA" == continent_id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       continent = Continent::NA; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else if ("OC" == continent_id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       continent = Continent::OC; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else if ("SA" == continent_id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       continent = Continent::SA; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       throw std::domain_error {"Invalid continent id: " + continent_id.toStdString ()}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   return continent; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | char const * AD1CCty::continent (Continent c) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   switch (c) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     case Continent::AF: return "AF"; | 
					
						
							|  |  |  |     case Continent::AN: return "AN"; | 
					
						
							|  |  |  |     case Continent::AS: return "AS"; | 
					
						
							|  |  |  |     case Continent::EU: return "EU"; | 
					
						
							|  |  |  |     case Continent::NA: return "NA"; | 
					
						
							|  |  |  |     case Continent::OC: return "OC"; | 
					
						
							|  |  |  |     case Continent::SA: return "SA"; | 
					
						
							|  |  |  |     default: return "UN"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  | QString AD1CCty::impl::get_cty_path(Configuration const * configuration) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-03-17 12:02:41 +01:00
										 |  |  |   QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}; | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  |   auto path = dataPath.exists (file_name) | 
					
						
							|  |  |  |               ? dataPath.absoluteFilePath (file_name) // user override
 | 
					
						
							|  |  |  |               : configuration->data_dir ().absoluteFilePath (file_name); // or original
 | 
					
						
							|  |  |  |   return path; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AD1CCty::impl::load_cty(QFile &file) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-03-16 19:13:59 -07:00
										 |  |  |   QRegularExpression version_pattern{R"(VER\d{8})"}; | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  |   int entity_id = 0; | 
					
						
							|  |  |  |   int line_number{0}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   entities_.clear(); | 
					
						
							|  |  |  |   prefixes_.clear(); | 
					
						
							| 
									
										
										
										
											2023-03-16 19:13:59 -07:00
										 |  |  |   cty_version_ = QString{}; | 
					
						
							|  |  |  |   cty_version_date_ = QString{}; | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   QTextStream in{&file}; | 
					
						
							|  |  |  |   while (!in.atEnd()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     auto const &entity_line = in.readLine(); | 
					
						
							|  |  |  |     ++line_number; | 
					
						
							|  |  |  |     if (!in.atEnd()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       auto const &entity_parts = entity_line.split(':'); | 
					
						
							|  |  |  |       if (entity_parts.size() >= 8) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         auto primary_prefix = entity_parts[7].trimmed(); | 
					
						
							|  |  |  |         bool WAE_only{false}; | 
					
						
							|  |  |  |         if (primary_prefix.startsWith('*')) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           primary_prefix = primary_prefix.mid(1); | 
					
						
							|  |  |  |           WAE_only = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         bool ok1, ok2, ok3, ok4, ok5; | 
					
						
							|  |  |  |         entities_.emplace(++entity_id, entity_parts[0].trimmed(), WAE_only, entity_parts[1].trimmed().toInt(&ok1), | 
					
						
							|  |  |  |                           entity_parts[2].trimmed().toInt(&ok2), continent(entity_parts[3].trimmed()), | 
					
						
							|  |  |  |                           entity_parts[4].trimmed().toFloat(&ok3), entity_parts[5].trimmed().toFloat(&ok4), | 
					
						
							|  |  |  |                           static_cast<int> (entity_parts[6].trimmed().toFloat(&ok5) * 60 * 60), primary_prefix); | 
					
						
							|  |  |  |         if (!(ok1 && ok2 && ok3 && ok4 && ok5)) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           throw std::domain_error{"Invalid number in cty.dat line " + boost::lexical_cast<std::string>(line_number)}; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         QString line; | 
					
						
							|  |  |  |         QString detail; | 
					
						
							|  |  |  |         do | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           in.readLineInto(&line); | 
					
						
							|  |  |  |           ++line_number; | 
					
						
							|  |  |  |         } while (detail += line, !detail.endsWith(';')); | 
					
						
							|  |  |  |         for (auto prefix: detail.left(detail.size() - 1).split(',')) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           prefix = prefix.trimmed(); | 
					
						
							|  |  |  |           bool exact{false}; | 
					
						
							|  |  |  |           if (prefix.startsWith('=')) | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             prefix = prefix.mid(1); | 
					
						
							|  |  |  |             exact = true; | 
					
						
							| 
									
										
										
										
											2023-03-16 19:13:59 -07:00
										 |  |  |             // match version pattern to prefix
 | 
					
						
							|  |  |  |             if (version_pattern.match(prefix).hasMatch()) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               cty_version_date_ = prefix; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  |           } | 
					
						
							|  |  |  |           prefixes_.emplace(prefix, exact, entity_id); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  | AD1CCty::AD1CCty (Configuration const * configuration) | 
					
						
							|  |  |  |   : m_ {configuration} | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |   Q_ASSERT (configuration); | 
					
						
							|  |  |  |   // TODO: G4WJS - consider doing the following asynchronously to
 | 
					
						
							| 
									
										
										
										
											2020-08-14 14:47:07 +01:00
										 |  |  |   // speed up startup. Not urgent as it takes less than 0.5s on a Core
 | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |   // i7 reading BIG CTY.DAT.
 | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  |   AD1CCty::reload (configuration); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AD1CCty::reload(Configuration const * configuration) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->path_ = m_->impl::get_cty_path(configuration); | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |   QFile file {m_->path_}; | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   LOG_INFO(QString{"Loading CTY.DAT from %1"}.arg (m_->path_)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |   if (file.open (QFile::ReadOnly)) | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  |   { | 
					
						
							|  |  |  |     m_->impl::load_cty(file); | 
					
						
							|  |  |  |     m_->cty_version_ = AD1CCty::lookup("VERSION").entity_name; | 
					
						
							|  |  |  |     Q_EMIT cty_loaded(m_->cty_version_); | 
					
						
							| 
									
										
										
										
											2023-03-16 19:13:59 -07:00
										 |  |  |     LOG_INFO(QString{"Loaded CTY.DAT version %1, %2"}.arg (m_->cty_version_date_).arg (m_->cty_version_)); | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AD1CCty::~AD1CCty () | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | auto AD1CCty::lookup (QString const& call) const -> Record | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   auto const& exact_search = call.toUpper (); | 
					
						
							|  |  |  |   if (!(exact_search.endsWith ("/MM") || exact_search.endsWith ("/AM"))) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       auto search_prefix = Radio::effective_prefix (exact_search); | 
					
						
							|  |  |  |       if (search_prefix != exact_search) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           auto p = m_->prefixes_.find (exact_search); | 
					
						
							|  |  |  |           if (p != m_->prefixes_.end () && p->exact_) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |               return m_->fixup (*p, *m_->lookup_entity (call, *p)); | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       while (search_prefix.size ()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           auto p = m_->prefixes_.find (search_prefix); | 
					
						
							|  |  |  |           if (p != m_->prefixes_.end ()) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |               impl::entity_by_id::iterator e = m_->lookup_entity (call, *p); | 
					
						
							|  |  |  |               if ((m_->configuration_->include_WAE_entities () || !e->WAE_only_) | 
					
						
							|  |  |  |                   && (!p->exact_ || call.size () == search_prefix.size ())) | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2019-05-29 23:35:18 +01:00
										 |  |  |                   return m_->fixup (*p, *e); | 
					
						
							| 
									
										
										
										
											2018-10-26 03:24:36 +01:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           search_prefix = search_prefix.left (search_prefix.size () - 1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   return Record {}; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  | auto AD1CCty::version () const -> QString | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-03-16 19:13:59 -07:00
										 |  |  |   return m_->cty_version_date_; | 
					
						
							| 
									
										
										
										
											2023-03-15 20:42:03 -07:00
										 |  |  | } |