├── Makefile ├── README.md ├── binaries ├── linux │ ├── qcndiff32 │ └── qcndiff64 └── windows │ ├── qcndiff32.exe │ └── qcndiff64.exe ├── main.cpp ├── nv.txt ├── qcn.cpp ├── qcn.hpp └── testfiles ├── 906k.txt ├── 906s.txt ├── G900F-bkp.txt ├── empty.txt ├── tst1.txt └── tst2.txt /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | RMOPTS=2> nul 3 | RM=@del 4 | INCLUDE=-ID:\boost_1_57_0 5 | LIBSUFFIX=-mgw49-mt-1_57 6 | LDOPTS=-static 7 | EXT=.exe 8 | ifeq ($(PROCESSOR_ARCHITECTURE),AMD64) 9 | ARCH=64 10 | endif 11 | ifeq ($(PROCESSOR_ARCHITECTURE),x86) 12 | ARCH=32 13 | endif 14 | ifeq ($(ARCH),64) 15 | LIBPATH=-LD:\boost_1_57_0\x64\lib 16 | endif 17 | ifeq ($(ARCH),32) 18 | LIBPATH=-LD:\boost_1_57_0\x86\lib 19 | endif 20 | else 21 | RMOPTS=2> /dev/null || true 22 | RM=@rm -f 23 | LIBSUFFIX= 24 | EXT= 25 | UNAME_S := $(shell uname -s) 26 | ifeq ($(UNAME_S),Linux) 27 | UNAME_P := $(shell uname -p) 28 | ifeq ($(UNAME_P),x86_64) 29 | ARCH=64 30 | endif 31 | ifneq ($(filter %86,$(UNAME_P)),) 32 | ARCH=32 33 | endif 34 | endif 35 | endif 36 | 37 | STANDARD=-std=c++11 38 | CPPFLAGS=-m$(ARCH) $(STANDARD) $(INCLUDE) -c 39 | LDFLAGS=-m$(ARCH) $(LIBPATH) $(LDOPTS) 40 | LIBS=-lboost_program_options$(LIBSUFFIX) -lboost_filesystem$(LIBSUFFIX) -lboost_system$(LIBSUFFIX) 41 | EXENAME=qcndiff 42 | CLEANFILES=*.o $(EXENAME)$(ARCH)$(EXE) 43 | 44 | $(EXENAME)$(ARCH): qcn$(ARCH).o main$(ARCH).o 45 | g++ $(LDFLAGS) -o $(EXENAME)$(ARCH) qcn$(ARCH).o main$(ARCH).o $(LIBS) 46 | # strip $(EXENAME)$(ARCH)$(EXT) 47 | 48 | qcn$(ARCH).o: qcn.cpp qcn.hpp 49 | g++ $(CPPFLAGS) -o qcn$(ARCH).o qcn.cpp 50 | 51 | main$(ARCH).o: qcn.cpp qcn.hpp main.cpp 52 | g++ $(CPPFLAGS) -o main$(ARCH).o main.cpp 53 | 54 | clean: 55 | $(RM) $(CLEANFILES) $(RMOPTS) 56 | 57 | target: 58 | echo Processor architecture is $(PROCESSOR_ARCHITECTURE) 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

QCNDIFF

2 | 3 | This is a utility that parses and displays differences between two text format .qcn files produced by Qualcomm's QPST (Qualcomm Product Support Tools). Examples for Linux usage and compilation are given for a RHEL / Centos / Fedora distribution. Adapt as necessary to your own distribution. 4 | 5 |

HOW TO USE

6 | 7 | There are pre-built binaries for 64 and 32 bit Windows as well as pre-built binaries for 32 and 64 bit Linux. The Linux binaries were built on Fedora 20 and the Windows binaries were built with MinGW and statically linked. For Linux you will need to install the boost shared libraries 8 | ```` 9 | yum install boost 10 | ```` 11 | 12 | Program usage is as follows 13 | 14 | ```` 15 | Usage: qcndiff64 [options] file file 16 | 17 | -h [ --help ] show help message 18 | 19 | -t [ --type ] arg (=p) show differences 20 | p for items present in both files 21 | m for items missing in either file 22 | b for both present and missing items 23 | 24 | -f [ --format ] arg (=i) output format 25 | i for interleaved output 26 | s for sequential output 27 | c to suppress item data and print only 28 | count 29 | 30 | -l [ --lookup ] arg (=nv.txt) nv item descriptions 31 | ```` 32 | 33 | Interleaved output shows the nvitem that is different for both files before displaying the next one. Sequential output displays all the differing items in the first file before proceeding to display the second file. 34 | 35 | If the file nv.txt exists (use -l to override the name) it will be used to look up text descriptions of the codes in order to render the output more friendly. 36 | 37 |

HOW TO COMPILE

38 | 39 |

Linux

40 | 41 | ```` 42 | yum install boost boost-devel 43 | make 44 | ```` 45 | 46 | If you wish to compile for a different architecture, for example you run x86_64 and you wish to compile a 32 bit variant, then first make sure you have the 32 bit libraries and then override the target with the ARCH commandline option 47 | 48 | ```` 49 | yum install boost.i686 boost-devel.i686 50 | make ARCH=32 51 | ```` 52 | 53 |

Windows

54 | 55 | You will need to install MinGW. You need separate toolchains for the 64 bit versions and the 32 bit versions. You will also need to compile the boost libraries. 56 | 57 | Once Boost is compiled, edit the Makefile to change the paths to the libraries and include files to conform to where you have them installed. Take note that if the default --layout==versioning option is used to compile Boost then you may also need to change the suffix option in the Makefile 58 | 59 |

CHANGELOG

60 | 61 | * 0.2 - add dictionary facility to allow the lookup of text descriptions 62 | * 0.1 - initial release 63 | 64 |

DOWNLOAD LINKS

65 | 66 | MinGW-32 binary distribution: http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-posix/dwarf/i686-4.9.2-release-posix-dwarf-rt_v3-rev0.7z/download 67 | 68 | MinGW-64 binary distribution: http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-posix/seh/x86_64-4.9.2-release-posix-seh-rt_v3-rev0.7z/download 69 | 70 | Boost 1.57 source: http://sourceforge.net/projects/boost/files/boost/1.57.0/ 71 | 72 | gmake for Windows: http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-posix/dwarf/i686-4.9.2-release-posix-dwarf-rt_v3-rev0.7z/download 73 | -------------------------------------------------------------------------------- /binaries/linux/qcndiff32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dl12345/qcn/d0119024f010d8e1923099d7087afa763f9db61e/binaries/linux/qcndiff32 -------------------------------------------------------------------------------- /binaries/linux/qcndiff64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dl12345/qcn/d0119024f010d8e1923099d7087afa763f9db61e/binaries/linux/qcndiff64 -------------------------------------------------------------------------------- /binaries/windows/qcndiff32.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dl12345/qcn/d0119024f010d8e1923099d7087afa763f9db61e/binaries/windows/qcndiff32.exe -------------------------------------------------------------------------------- /binaries/windows/qcndiff64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dl12345/qcn/d0119024f010d8e1923099d7087afa763f9db61e/binaries/windows/qcndiff64.exe -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with this program. If not, see . 14 | 15 | Copyright 2014 dl12345@xda-developers forum 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "qcn.hpp" 27 | 28 | using qcn::Qcn; 29 | namespace fs = boost::filesystem; 30 | typedef enum {interleave, sequential, count} printformat; 31 | 32 | bool const ProcessCommandLine( 33 | int ac, 34 | char *av[], 35 | Qcn::cmp& cmp, 36 | printformat& format, 37 | std::string& fileone, 38 | std::string& filetwo, 39 | std::string& filedict 40 | ) 41 | { 42 | namespace po = boost::program_options; 43 | typedef std::vector files_type; 44 | 45 | char t, pf; 46 | files_type f; 47 | 48 | std::string prog(fs::path(av[0]).filename().string()); 49 | std::string usage = "Usage: " + prog + " [options] file file"; 50 | 51 | po::options_description visible; 52 | visible.add_options() 53 | ("help,h", "show help message\n") 54 | ("type,t", po::value(&t)->default_value('p'), 55 | "show differences\n" 56 | " p for items present in both files\n" 57 | " m for items missing in either file\n" 58 | " b for both present and missing items\n") 59 | ("format,f", po::value(&pf)->default_value('i'), 60 | "output format\n" 61 | " i for interleaved output\n" 62 | " s for sequential output\n" 63 | " c to suppress item data and print only count\n") 64 | ("lookup,l", po::value(&filedict)->default_value("nv.txt"), 65 | "nv item descriptions") 66 | ; 67 | 68 | po::options_description hidden("hidden options"); 69 | hidden.add_options()("input,i", po::value(&f), "input file"); 70 | 71 | po::positional_options_description p; 72 | p.add("input", -1); 73 | 74 | po::options_description all; 75 | all.add(visible).add(hidden); 76 | 77 | po::variables_map vm; 78 | po::store( 79 | po::command_line_parser(ac, av).options(all).positional(p).run(), vm 80 | ); 81 | po::notify(vm); 82 | 83 | if (vm.count("help")) 84 | { 85 | std::cout << std::endl << usage << std::endl << std::endl; 86 | std::cout << visible; 87 | return false; 88 | } 89 | 90 | // process and set input files 91 | 92 | if (vm.count("input") && f.size() == 2) 93 | { 94 | fileone = f[0]; 95 | filetwo = f[1]; 96 | 97 | if (!fs::exists(fileone)) 98 | { 99 | std::cout << prog << ": " << fileone << " not found" << std::endl; 100 | return false; 101 | } 102 | 103 | if (!fs::exists(filetwo)) 104 | { 105 | std::cout << prog << ": " << filetwo << " not found" << std::endl; 106 | return false; 107 | } 108 | } 109 | else 110 | { 111 | std::cout << std::endl << usage << std::endl << std::endl; 112 | std::cout << visible; 113 | return false; 114 | } 115 | 116 | // process and set comparison type 117 | 118 | if (vm.count("type")) 119 | { 120 | switch(t) 121 | { 122 | case 'p': 123 | cmp = Qcn::cmp::present; 124 | break; 125 | case 'm': 126 | cmp = Qcn::cmp::missing; 127 | break; 128 | case 'b': 129 | cmp = Qcn::cmp::both; 130 | break; 131 | default: 132 | std::cout << std::endl << usage << std::endl << std::endl; 133 | std::cout << visible; 134 | return false; 135 | } 136 | } 137 | 138 | // process print output format 139 | 140 | if (vm.count("format")) 141 | { 142 | switch(pf) 143 | { 144 | case 'i': 145 | format = interleave; 146 | break; 147 | case 's': 148 | format = sequential; 149 | break; 150 | case 'c': 151 | format = count; 152 | break; 153 | default: 154 | std::cout << std::endl << usage << std::endl << std::endl; 155 | std::cout << visible; 156 | return false; 157 | } 158 | } 159 | 160 | return true; 161 | } 162 | 163 | void PrintOutput( 164 | qcn::diff_type const& d, 165 | std::string const& fileone, 166 | std::string const& filetwo, 167 | std::string const& filedict, 168 | printformat p = interleave 169 | ) 170 | { 171 | std::cout << std::endl; 172 | std::cout << "Found " << d.size() << " non matching items"; 173 | std::cout << std::endl << std::endl; 174 | 175 | qcn::Dictionary dict(filedict); 176 | bool printinfo = false; 177 | if (dict.Open()) printinfo = true; 178 | 179 | switch(p) 180 | { 181 | case interleave: 182 | 183 | for (auto i = d.begin(); i != d.end(); ++i) 184 | { 185 | auto p = *i; 186 | 187 | if (printinfo) 188 | { 189 | auto j = dict.Find(p.first.code); 190 | 191 | if (j != dict.end()) 192 | { 193 | auto q = *j; 194 | 195 | p.first.description = q.description; 196 | p.second.description = q.description; 197 | 198 | p.first.category = q.category; 199 | p.second.category = q.category; 200 | } 201 | } 202 | 203 | std::cout << '[' << fileone << "]: "; 204 | std::cout << p.first << std::endl; 205 | 206 | std::cout << '[' << filetwo << "]: "; 207 | std::cout << p.second << std::endl; 208 | } 209 | break; 210 | 211 | case sequential: 212 | 213 | std::cout << '[' << fileone << "]: " << std::endl << std::endl; 214 | for (auto i = d.begin(); i != d.end(); ++i) 215 | { 216 | auto p = *i; 217 | std::cout << p.first << std::endl; 218 | } 219 | 220 | std::cout << '[' << filetwo << "]: " << std::endl << std::endl; 221 | for (auto i = d.begin(); i != d.end(); ++i) 222 | { 223 | auto p = *i; 224 | std::cout << p.second << std::endl; 225 | } 226 | break; 227 | 228 | default: 229 | break; 230 | } 231 | } 232 | 233 | int main(int argc, char *argv[]) 234 | { 235 | Qcn::cmp cmp; 236 | std::string nameone, nametwo, nameinfo; 237 | printformat pf; 238 | 239 | if (ProcessCommandLine(argc, argv, cmp, pf, nameone, nametwo, nameinfo)) 240 | { 241 | qcn::Qcn fileone(nameone), filetwo(nametwo); 242 | auto n1 = fs::path(nameone).filename().string(); 243 | auto n2 = fs::path(nametwo).filename().string(); 244 | 245 | if (fileone.Open() && filetwo.Open()) 246 | { 247 | 248 | auto d = qcn::Compare(fileone, filetwo, cmp); 249 | PrintOutput(d, n1, n2, nameinfo, pf); 250 | } 251 | else 252 | { 253 | if (!fileone.IsOpen()) 254 | { 255 | std::cout << n1 << ": "; 256 | std::cout << fileone.ErrorMessage() << std::endl; 257 | } 258 | if (!filetwo.IsOpen()) 259 | { 260 | std::cout << n2 << ": "; 261 | std::cout << filetwo.ErrorMessage() << std::endl; 262 | } 263 | return 1; 264 | } 265 | } 266 | return 0; 267 | } 268 | 269 | -------------------------------------------------------------------------------- /qcn.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with this program. If not, see . 14 | 15 | Copyright 2014 dl12345@xda-developers forum 16 | 17 | */ 18 | 19 | #ifdef DEBUG 20 | #define BOOST_SPIRIT_DEBUG 21 | #endif 22 | 23 | #include 24 | #include "qcn.hpp" 25 | 26 | namespace qcn 27 | { 28 | diff_type const Compare( 29 | Qcn const& lhs, 30 | Qcn const& rhs, 31 | Qcn::cmp const cmp, 32 | bool const recurse 33 | ) 34 | { 35 | // option ::present will perform an inner join of non-matching items 36 | // option ::missing will perform an outer join of missing items 37 | // option ::both will perform an outer join of all non-matching items 38 | 39 | diff_type d; 40 | for (auto l = lhs.begin(); l != lhs.end(); ++l) 41 | { 42 | auto r = rhs.Find(l->code); 43 | if (r != rhs.end()) 44 | { 45 | if (cmp != Qcn::cmp::missing && *l != *r) // inner join 46 | { 47 | pair_type p(*l, *r); 48 | d.push_back(p); 49 | } 50 | } 51 | else if (cmp != Qcn::cmp::present) // left outer join missing items 52 | { 53 | Qcn::item empty; 54 | pair_type p(*l, empty); 55 | d.push_back(p); 56 | } 57 | } 58 | if (cmp != Qcn::cmp::present && recurse) // right outer join missing items 59 | { 60 | auto rhs_only = Compare(rhs, lhs, Qcn::cmp::missing, false); 61 | for (auto s = rhs_only.begin(); s != rhs_only.end(); ++s) 62 | { 63 | // swap the order so that we have rhs and lhs correctly ordered 64 | 65 | Qcn::item empty; 66 | pair_type p(empty, s->first); 67 | d.push_back(p); 68 | } 69 | } 70 | return d; 71 | } 72 | 73 | } 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /qcn.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with this program. If not, see . 14 | 15 | Copyright 2014 dl12345@xda-developers forum 16 | 17 | */ 18 | 19 | #ifndef QCN_H 20 | #define QCN_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | 34 | namespace qcn 35 | { 36 | namespace spirit = boost::spirit; 37 | namespace qi = boost::spirit::qi; 38 | namespace ascii = boost::spirit::ascii; 39 | namespace fusion = boost::fusion; 40 | namespace range = boost::range_detail; 41 | namespace phx = boost::phoenix; 42 | 43 | class Dictionary; 44 | class Qcn; 45 | 46 | struct qitem; 47 | struct dict_code; 48 | 49 | typedef unsigned int uint; 50 | 51 | // Skipper type for parser 52 | typedef ascii::space_type space_type; 53 | 54 | // item grammar types 55 | typedef struct qitem qcn_item_type; 56 | typedef std::vector qcn_items_type; 57 | typedef std::vector< uint > qcn_item_data_type; 58 | typedef boost::unordered_map qcn_map_type; 59 | 60 | // dictionary grammar types 61 | typedef struct dict_code dict_code_type; 62 | typedef std::vector dict_codes_type; 63 | typedef boost::unordered_map dict_map_type; 64 | 65 | // comparison type pairs 66 | typedef std::pair pair_type; 67 | typedef std::vector diff_type; 68 | 69 | 70 | struct qitem 71 | { 72 | uint code; 73 | std::string description; 74 | std::string category; 75 | std::string status; 76 | qcn_item_data_type data; 77 | 78 | qitem(): code(0) {} 79 | 80 | qitem(struct qitem const& c) 81 | : code(c.code), status(c.status), data(c.data) {} 82 | 83 | bool operator==(struct qitem const& rhs) const 84 | { 85 | if (code == 0 && status.empty()) // the empty object doesn't match 86 | { 87 | return false; 88 | } 89 | if (code == rhs.code && status == rhs.status && data == rhs.data) 90 | { 91 | return true; 92 | } 93 | return false; 94 | } 95 | 96 | bool operator!=(struct qitem const& rhs) const 97 | { 98 | if (code == 0 && status.empty()) 99 | { 100 | return true; 101 | } 102 | if (code != rhs.code || status != rhs.status || data != rhs.data) 103 | { 104 | return true; 105 | } 106 | return false; 107 | } 108 | 109 | friend std::ostream& operator<<(std::ostream& o, struct qitem const& q) 110 | { 111 | o << std::setw(4) << std::setfill('0'); 112 | o << std::dec << q.code; 113 | 114 | if (!q.description.empty()) 115 | { 116 | o << " (" << q.description << ", " << q.category; 117 | } 118 | else 119 | { 120 | o << " (0x"; 121 | o << std::setw(4) << std::setfill('0'); 122 | o << std::uppercase << std::hex << q.code; 123 | } 124 | o << ") - " << (q.status == "" ? "Missing" : q.status) << std::endl; 125 | 126 | for (auto i = 0; i < q.data.size(); i++) 127 | { 128 | if (i && i % 16 == 0) 129 | { 130 | std::cout << std::endl; 131 | } 132 | else if (i) o << " "; 133 | 134 | o << std::setw(2) << std::setfill('0'); 135 | o << std::uppercase << std::hex << q.data[i]; 136 | } 137 | if(q.data.size() > 0) o << std::endl; 138 | return o; 139 | } 140 | 141 | }; 142 | 143 | template < typename Iterator, typename Skipper = space_type > 144 | class qcnparser : public qi::grammar< Iterator, qcn_items_type(), Skipper > 145 | { 146 | public: 147 | 148 | // qcn grammar types 149 | typedef uint code_type; 150 | typedef std::vector< uint > leaf_type; 151 | typedef fusion::vector2< std::string, uint > size_type; 152 | typedef fusion::vector2< size_type, size_type > info_type; 153 | 154 | qcnparser() : qcnparser::base_type(qcndata), size_(0) 155 | { 156 | 157 | using qi::uint_; 158 | using qi::ulong_; 159 | using qi::hex; 160 | using qi::char_; 161 | using qi::string; 162 | using qi::lit; 163 | using qi::eol; 164 | using qi::uint_parser; 165 | using qi::repeat; 166 | using qi::_val; 167 | using qi::omit; 168 | using qi::_1; 169 | using qi::attr; 170 | using qi::eps; 171 | using phx::ref; 172 | 173 | qcndata = omit[+(header)] >> *(item); 174 | 175 | header = '[' >> description >> ']'; 176 | 177 | description = nvitemslabel | iteminfo; 178 | 179 | nvitemslabel = lit("NV items") ; 180 | 181 | iteminfo = itemnumber >> ',' >> itemsize; 182 | 183 | itemnumber = string("Complete items") >> '-' >> uint_; 184 | 185 | // In the presence of a semantic action the % operator is 186 | // necessary to ensure the propagation of the attribute if 187 | // _val is not assigned explicitly 188 | 189 | itemsize %= (string("Items size") >> '-' >> uint_[ref(size_)=_1]); 190 | 191 | item = itempresent | itemnotpresent; 192 | 193 | itempresent = itemcode >> statusok >> itemdata; 194 | 195 | itemnotpresent = itemcode 196 | >> statusother 197 | >> attr(qcn_item_data_type()); 198 | 199 | itemcode = ulong_ >> omit [ itemdiscard ]; 200 | 201 | itemdiscard = '(' >> lit("0x") >> hex >> ')' >> "-"; 202 | 203 | statusok = string("OK"); 204 | 205 | statusother = (string("Inactive item") 206 | | string("Parameter bad") 207 | | string("Access denied")); 208 | 209 | // Rules separated to allow for semantic action on data if desired 210 | 211 | // For example 212 | // omit[eps] >> itemleaf[_val=convert(_1, qcn_item_data_type())]; 213 | 214 | itemdata = omit[eps] >> itemleaf; 215 | itemleaf = repeat(ref(size_))[hex]; 216 | 217 | #ifdef BOOST_SPIRIT_DEBUG 218 | 219 | BOOST_SPIRIT_DEBUG_NODE(qcndata); 220 | BOOST_SPIRIT_DEBUG_NODE(header); 221 | BOOST_SPIRIT_DEBUG_NODE(item); 222 | 223 | #endif 224 | 225 | } 226 | private: 227 | qi::rule qcndata; 228 | 229 | qi::rule header; 230 | qi::rule description; 231 | 232 | qi::rule nvitemslabel; 233 | qi::rule iteminfo; 234 | 235 | qi::rule itemnumber; 236 | qi::rule itemsize; 237 | 238 | qi::rule item; 239 | qi::rule itempresent; 240 | qi::rule itemnotpresent; 241 | 242 | qi::rule statusok; 243 | qi::rule statusother; 244 | 245 | qi::rule itemcode; 246 | qi::rule itemdiscard; 247 | qi::rule itemdata; 248 | qi::rule itemleaf; 249 | 250 | unsigned int size_; 251 | 252 | }; 253 | 254 | struct dict_code 255 | { 256 | 257 | uint code; 258 | std::string description; 259 | std::string category; 260 | 261 | friend std::ostream& operator<<( 262 | std::ostream& o, 263 | struct dict_code const& c) 264 | { 265 | o << c.description << " - " << c.category; 266 | return o; 267 | } 268 | 269 | }; 270 | 271 | template < typename Iterator, typename Skipper = space_type > 272 | class codeparser : public qi::grammar 273 | { 274 | public: 275 | 276 | codeparser() : codeparser::base_type(codes) 277 | { 278 | 279 | using qi::uint_; 280 | using qi::char_; 281 | using qi::string; 282 | using qi::lit; 283 | using qi::eol; 284 | using qi::omit; 285 | using qi::no_skip; 286 | using qi::_a; 287 | 288 | codes = *(uint_ >> sep >> description >> sep >> category); 289 | 290 | sep = omit[ char_("^") ]; 291 | 292 | description = 293 | omit[char_("\"") ] 294 | >> no_skip[+(char_ - char_("\""))] 295 | >> omit[char_("\"")] 296 | ; 297 | 298 | category = 299 | omit[ char_("'\"") ] 300 | >> no_skip[+(char_ - char_("*\""))] 301 | >> no_skip[ omit[ *(char_ - eol) ] >> eol] 302 | ; 303 | 304 | #ifdef BOOST_SPIRIT_DEBUG 305 | 306 | BOOST_SPIRIT_DEBUG_NODE(codes); 307 | BOOST_SPIRIT_DEBUG_NODE(description); 308 | BOOST_SPIRIT_DEBUG_NODE(category); 309 | BOOST_SPIRIT_DEBUG_NODE(sep); 310 | 311 | #endif 312 | 313 | } 314 | private: 315 | qi::rule codes; 316 | qi::rule sep; 317 | qi::rule description; 318 | qi::rule category; 319 | }; 320 | 321 | template < 322 | typename T_data_type, 323 | typename T_item_type, 324 | typename T_map_type, 325 | typename T_parser_type 326 | > 327 | class DataFile 328 | { 329 | public: 330 | 331 | typedef typename T_data_type::iterator d_iterator; 332 | typedef typename T_map_type::iterator m_iterator; 333 | typedef typename T_map_type::const_iterator m_const_iterator; 334 | 335 | DataFile(std::string const& filename) 336 | : filename_(filename), 337 | err_(""), 338 | success_(false) 339 | { 340 | } 341 | 342 | DataFile(DataFile const& rhs) 343 | : filename_(rhs.filename_), 344 | err_(rhs.err_), 345 | success_(rhs.success_), 346 | map_(rhs.map_) 347 | { 348 | } 349 | 350 | ~DataFile() {} 351 | 352 | bool const Open() 353 | { 354 | using spirit::ascii::space; 355 | using qi::eoi; 356 | using qi::blank; 357 | 358 | std::ifstream in(filename_); 359 | if (!in.is_open()) 360 | { 361 | err_ = "Could not open input file"; 362 | success_ = false; 363 | return success_; 364 | } 365 | in.unsetf(std::ios::skipws); 366 | 367 | typedef spirit::istream_iterator iterator_type; 368 | 369 | iterator_type begin(in); 370 | iterator_type end; 371 | 372 | if (begin == end) 373 | { 374 | err_ = "Empty input file"; 375 | success_ = false; 376 | return success_; 377 | } 378 | 379 | T_parser_type p; 380 | bool r = phrase_parse(begin, end, p >> eoi, space, data_); 381 | 382 | if (r && begin == end) 383 | { 384 | MakeHashTable(); 385 | success_ = true; 386 | return success_; 387 | } 388 | else 389 | { 390 | err_ = "Invalid format input file"; 391 | success_ = false; 392 | return success_; 393 | } 394 | return false; 395 | } 396 | 397 | bool const IsOpen() const { return success_; } 398 | uint const Size() const { return map_.size(); } 399 | std::string const& ErrorMessage() { return err_; } 400 | 401 | public: 402 | 403 | class iterator 404 | : public boost::iterator_adaptor< 405 | iterator, 406 | m_iterator, 407 | T_item_type&, 408 | boost::forward_traversal_tag 409 | > 410 | { 411 | public: 412 | iterator(): iterator::iterator_adaptor_() {} 413 | 414 | iterator(const m_iterator& i) : iterator::iterator_adaptor_(i){} 415 | 416 | T_item_type& dereference() const 417 | { 418 | return this->base()->second; 419 | } 420 | 421 | T_item_type* reference() const 422 | { 423 | return &(this->base()->second); 424 | } 425 | 426 | private: 427 | friend class boost::iterator_core_access; 428 | }; 429 | 430 | class const_iterator 431 | : public boost::iterator_adaptor< 432 | const_iterator, 433 | m_const_iterator, 434 | T_item_type const&, 435 | boost::forward_traversal_tag 436 | > 437 | { 438 | public: 439 | 440 | const_iterator(): const_iterator::iterator_adaptor_() {} 441 | 442 | const_iterator(const m_const_iterator& i) 443 | : const_iterator::iterator_adaptor_(i) {} 444 | 445 | const_iterator(const iterator& i) 446 | : const_iterator::iterator_adaptor_(i.base()) {} 447 | 448 | T_item_type const& dereference() const 449 | { 450 | return (T_item_type const&) this->base()->second; 451 | } 452 | 453 | T_item_type const* reference() const 454 | { 455 | return (T_item_type const *) &(this->base()->second); 456 | } 457 | 458 | private: 459 | friend class boost::iterator_core_access; 460 | }; 461 | 462 | iterator begin() 463 | { 464 | return iterator(map_.begin()); 465 | } 466 | 467 | iterator end() 468 | { 469 | return iterator(map_.end()); 470 | } 471 | 472 | const_iterator begin() const 473 | { 474 | return const_iterator(map_.begin()); 475 | } 476 | 477 | const_iterator end() const 478 | { 479 | return const_iterator(map_.end()); 480 | } 481 | 482 | iterator Find(uint const& key) { return map_.find(key); } 483 | const_iterator Find(uint const& key) const { return map_.find(key); } 484 | 485 | 486 | private: 487 | 488 | virtual typename T_map_type::key_type const Key(d_iterator const& i)=0; 489 | 490 | void MakeHashTable() 491 | { 492 | for (auto i = data_.begin(); i != data_.end(); ++i) 493 | { 494 | map_[Key(i)] = *i; 495 | } 496 | data_.clear(); 497 | } 498 | 499 | std::string filename_; 500 | std::string err_; 501 | bool success_; 502 | T_data_type data_; 503 | T_map_type map_; 504 | }; 505 | 506 | class Dictionary: public DataFile < 507 | dict_codes_type, 508 | dict_code_type, 509 | dict_map_type, 510 | codeparser 511 | > 512 | { 513 | public: 514 | 515 | Dictionary(std::string const& filename) : DataFile(filename) {} 516 | Dictionary(Dictionary const& rhs) : DataFile(rhs) {} 517 | 518 | 519 | private: 520 | 521 | typename dict_map_type::key_type const Key(d_iterator const& i) 522 | { 523 | return i->code; 524 | } 525 | 526 | }; 527 | 528 | class Qcn: public DataFile < 529 | qcn_items_type, 530 | qcn_item_type, 531 | qcn_map_type, 532 | qcnparser< spirit::istream_iterator > 533 | > 534 | { 535 | public: 536 | 537 | typedef qcn_item_type item; 538 | typedef enum {present, missing, both} cmp; 539 | 540 | Qcn(std::string const& filename) : DataFile(filename) {} 541 | Qcn(Qcn const& rhs) : DataFile(rhs) {} 542 | 543 | private: 544 | 545 | typename qcn_map_type::key_type const Key(d_iterator const& i) 546 | { 547 | return i->code; 548 | } 549 | 550 | }; 551 | 552 | diff_type const Compare ( 553 | Qcn const& lhs, 554 | Qcn const& rhs, 555 | Qcn::cmp const cmp = Qcn::cmp::both, 556 | bool const recurse = true 557 | ); 558 | 559 | } 560 | 561 | BOOST_FUSION_ADAPT_STRUCT( 562 | qcn::qitem, 563 | (qcn::uint, code) 564 | (std::string, status) 565 | (std::vector, data) 566 | ) 567 | 568 | BOOST_FUSION_ADAPT_STRUCT( 569 | qcn::dict_code, 570 | (qcn::uint, code) 571 | (std::string, description) 572 | (std::string, category) 573 | ) 574 | 575 | #endif 576 | -------------------------------------------------------------------------------- /testfiles/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dl12345/qcn/d0119024f010d8e1923099d7087afa763f9db61e/testfiles/empty.txt -------------------------------------------------------------------------------- /testfiles/tst1.txt: -------------------------------------------------------------------------------- 1 | [NV items] 2 | [Complete items - 653, Items size - 128] 3 | 4 | 00004 (0x0004) - Inactive item 5 | 6 | 00005 (0x0005) - OK 7 | 3A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 8 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 9 | 00 00 00 00 FF 00 00 00 00 00 00 00 00 00 00 00 10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 13 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 15 | 16 | 00007 (0x0007) - OK 17 | 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 19 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 21 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 23 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 24 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 25 | 26 | 06828 (0x1AAC) - OK 27 | D5 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 28 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 31 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 34 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 35 | 36 | 06829 (0x1AAD) - Inactive item 37 | 38 | 06593 (0x19C1) - OK 39 | BE 0A EA 0A 16 0B 42 0B 6E 0B 9A 0B C6 0B F2 0B 40 | 1E 0C 4A 0C 76 0C A2 0C CE 0C FA 0C 26 0D 52 0D 41 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 42 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 43 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 44 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 45 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 46 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 47 | -------------------------------------------------------------------------------- /testfiles/tst2.txt: -------------------------------------------------------------------------------- 1 | [NV items] 2 | [Complete items - 653, Items size - 128] 3 | 4 | 00004 (0x0004) - Inactive item 5 | 6 | 00005 (0x0005) - OK 7 | 3A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 8 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 9 | 00 00 00 00 FF 00 00 00 00 00 00 00 00 00 00 00 10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 13 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 15 | 16 | 00007 (0x0007) - OK 17 | 64 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 18 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 19 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 21 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 23 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 24 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 25 | 26 | 06828 (0x1AAC) - OK 27 | D5 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 28 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 31 | 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 32 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 34 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 35 | 36 | 06829 (0x1AAD) - Parameter bad 37 | 38 | 00010 (0x000A) - OK 39 | 00 1F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 42 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 43 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 44 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 45 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 46 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 47 | --------------------------------------------------------------------------------