Don't crash if recentfiles line fails to deserialize
[lsnes.git] / src / library / recentfiles.cpp
blobd89c40abc542e78421d047c8d174df4780c8a7f7
1 #include "recentfiles.hpp"
2 #include "zip.hpp"
3 #include "eatarg.hpp"
4 #include "json.hpp"
5 #include "string.hpp"
6 #include <fstream>
8 namespace recentfiles
10 path::path()
14 path::path(const std::string& p)
16 pth = p;
19 std::string path::serialize() const
21 return pth;
24 path path::deserialize(const std::string& s)
26 path p(s);
27 return p;
30 std::string path::get_path() const
32 return pth;
35 bool path::check() const
37 if(pth == "")
38 return false;
39 try {
40 return zip::file_exists(pth);
41 return true;
42 } catch(...) {
43 return false;
47 std::string path::display() const
49 return pth;
52 bool path::operator==(const path& p) const
54 return p.pth == pth;
57 multirom::multirom()
59 //Nothing to do.
62 std::string multirom::serialize() const
64 bool any = false;
65 JSON::node output(JSON::object);
66 if(packfile != "") {
67 output["pack"] = JSON::string(packfile);
68 } else if(singlefile != "") {
69 output["file"] = JSON::string(singlefile);
70 if(core != "") output["core"] = JSON::string(core);
71 if(system != "") output["system"] = JSON::string(system);
72 if(region != "") output["region"] = JSON::string(region);
73 } else {
74 output["files"] = JSON::array();
75 JSON::node& f = output["files"];
76 for(auto i : files) {
77 f.append(JSON::string(i));
78 if(i != "")
79 any = true;
81 if(core != "") output["core"] = JSON::string(core);
82 if(system != "") output["system"] = JSON::string(system);
83 if(region != "") output["region"] = JSON::string(region);
85 if(packfile != "" || singlefile != "" || core != "" || system != "" || region != "") any = true;
86 if(!any) return "";
87 return output.serialize();
90 multirom multirom::deserialize(const std::string& s)
92 multirom r;
93 if(s.length() > 0 && s[0] == '{') {
94 //JSON.
95 try {
96 JSON::node d(s);
97 if(d.field_exists("pack")) r.packfile = d["pack"].as_string8();
98 if(d.field_exists("file")) r.singlefile = d["file"].as_string8();
99 if(d.field_exists("core")) r.core = d["core"].as_string8();
100 if(d.field_exists("system")) r.system = d["system"].as_string8();
101 if(d.field_exists("region")) r.region= d["region"].as_string8();
102 if(d.field_exists("files")) {
103 size_t cnt = d["files"].index_count();
104 r.files.resize(cnt);
105 for(unsigned i = 0; i < cnt; i++)
106 r.files[i] = d["files"].index(i).as_string8();
108 } catch(...) {
109 r.packfile = "";
110 r.singlefile = "";
111 r.core = "";
112 r.system = "";
113 r.region = "";
114 r.files.clear();
116 } else {
117 //Legacy form.
118 r.packfile = s;
120 return r;
123 bool multirom::check() const
125 if(packfile == "" && singlefile == "" && core == "" && system == "" && region == "" && files.empty())
126 return false;
127 if(packfile != "" && !zip::file_exists(packfile))
128 return false;
129 if(singlefile != "" && !zip::file_exists(singlefile))
130 return false;
131 for(auto i : files)
132 if(i != "" && !zip::file_exists(i))
133 return false;
134 return true;
137 std::string multirom::display() const
139 if(packfile != "")
140 return packfile;
141 if(singlefile != "") {
142 return singlefile + " <" + core + "/" + system + ">";
143 } else {
144 if(files.size() > 1 && files[1] != "")
145 return (stringfmt() << files[1] << " (+" << files.size() << ")").str();
146 else if(files.size() > 0)
147 return (stringfmt() << files[0] << " (+" << files.size() << ")").str();
148 else
149 return "(blank) <" + core + "/" + system + ">";
153 bool multirom::operator==(const multirom& p) const
155 if(packfile != p.packfile)
156 return false;
157 if(singlefile != p.singlefile)
158 return false;
159 if(core != p.core)
160 return false;
161 if(system != p.system)
162 return false;
163 if(region != p.region)
164 return false;
165 if(files.size() != p.files.size())
166 return false;
167 for(unsigned i = 0; i < files.size(); i++)
168 if(files[i] != p.files[i])
169 return false;
170 return true;
173 template<class T> set<T>::set(const std::string& _cfgfile, size_t _maxcount)
175 cfgfile = _cfgfile;
176 maxcount = _maxcount;
179 template<class T> void set<T>::add(const T& file)
181 std::list<T> ents;
182 //Load the list.
184 std::ifstream in(cfgfile);
185 std::string f;
186 while(in) {
187 std::getline(in, f);
188 T g;
189 try {
190 g = T::deserialize(f);
191 } catch(...) {
192 continue;
194 if(g.check())
195 ents.push_back(g);
198 //Search for matches. If found, move to front, otherwise push to first.
199 auto itr = ents.begin();
200 for(; itr != ents.end(); itr++)
201 if(*itr == file)
202 break;
203 if(itr != ents.end())
204 ents.erase(itr);
205 ents.push_front(file);
206 //Write up to maxcount entries.
208 size_t i = 0;
209 std::ofstream out(cfgfile);
210 for(itr = ents.begin(); itr != ents.end() && i < maxcount; itr++, i++)
211 out << itr->serialize() << std::endl;
213 for(auto i : hooks)
214 (*i)();
217 template<class T> void set<T>::add_hook(hook& h)
219 hooks.push_back(&h);
222 template<class T> void set<T>::remove_hook(hook& h)
224 for(auto itr = hooks.begin(); itr != hooks.end(); itr++)
225 if(*itr == &h) {
226 hooks.erase(itr);
227 break;
231 template<class T> std::list<T> set<T>::get()
233 size_t c = 0;
234 std::list<T> ents;
235 //Load the list.
237 std::ifstream in(cfgfile);
238 std::string f;
239 while(in) {
240 std::getline(in, f);
241 T g;
242 try {
243 g = T::deserialize(f);
244 } catch(...) {
245 continue;
247 if(c < maxcount && g.check()) {
248 ents.push_back(g);
249 c++;
253 return ents;
256 hook::~hook()
260 void _dummy_63263632747434353545()
262 set<path> x("", 0);
263 eat_argument(&set<path>::add);
264 eat_argument(&set<path>::add_hook);
265 eat_argument(&set<path>::remove_hook);
266 eat_argument(&set<path>::get);
267 set<multirom> y("", 0);
268 eat_argument(&set<multirom>::add);
269 eat_argument(&set<multirom>::add_hook);
270 eat_argument(&set<multirom>::remove_hook);
271 eat_argument(&set<multirom>::get);