Describe command changes from last commit
[lsnes.git] / misc.cpp
blob8156025dad99593c77e4d7948b6ea37cab8304ad
1 #include "lsnes.hpp"
2 #include "memorymanip.hpp"
3 #include "misc.hpp"
4 #include "rom.hpp"
5 #include <sstream>
6 #include <iostream>
7 #include <iomanip>
8 #include <fstream>
9 #include <string>
10 #include <vector>
11 #include <algorithm>
12 #include <ctime>
13 #include <cstdlib>
14 #include <cstring>
15 #include <boost/filesystem.hpp>
17 namespace
19 std::string rseed;
20 uint64_t rcounter = 0;
22 std::string get_random_hexstring_64(size_t index)
24 std::ostringstream str;
25 str << rseed << " " << time(NULL) << " " << (rcounter++) << " " << index;
26 std::string s = str.str();
27 std::vector<char> x;
28 x.resize(s.length());
29 std::copy(s.begin(), s.end(), x.begin());
30 return sha256::hash(reinterpret_cast<uint8_t*>(&x[0]), x.size());
33 std::string collect_identifying_information()
35 //TODO: Collect as much identifying information as possible.
36 std::ostringstream str;
37 time_t told = time(NULL);
38 time_t tnew;
39 uint64_t loops = 0;
40 uint64_t base = 0;
41 int cnt = 0;
42 while(cnt < 3) {
43 tnew = time(NULL);
44 if(tnew > told) {
45 told = tnew;
46 cnt++;
47 str << (loops - base) << " ";
48 base = loops;
50 loops++;
52 return str.str();
56 bool is_cmd_prefix(const std::string& haystack, const std::string& needle) throw()
58 size_t hlen = haystack.length();
59 size_t nlen = needle.length();
60 if(hlen < nlen)
61 return false;
62 for(size_t i = 0; i < nlen; i++)
63 if(haystack[i] != needle[i])
64 return false;
65 if(hlen == nlen)
66 return true;
67 return (haystack[nlen] == ' ' || haystack[nlen] == '\t');
70 std::string get_random_hexstring(size_t length) throw(std::bad_alloc)
72 std::string out;
73 for(size_t i = 0; i < length; i += 64)
74 out = out + get_random_hexstring_64(i);
75 return out.substr(0, length);
78 void set_random_seed(const std::string& seed) throw(std::bad_alloc)
80 std::ostringstream str;
81 str << seed.length() << " " << seed;
82 rseed = str.str();
85 void set_random_seed() throw(std::bad_alloc)
87 //Try /dev/urandom first.
89 std::ifstream r("/dev/urandom", std::ios::binary);
90 if(r.is_open()) {
91 char buf[64];
92 r.read(buf, 64);
93 std::string s(buf, 64);
94 set_random_seed(s);
95 return;
98 //Fall back to time.
99 std::ostringstream str;
100 str << collect_identifying_information() << " " << time(NULL);
101 set_random_seed(str.str());
104 //VERY dirty hack.
105 namespace foobar
107 #include <nall/sha256.hpp>
109 using foobar::nall::sha256_ctx;
110 using foobar::nall::sha256_init;
111 using foobar::nall::sha256_final;
112 using foobar::nall::sha256_hash;
113 using foobar::nall::sha256_chunk;
116 * \brief Opaque internal state of SHA256
118 struct sha256_opaque
121 * \brief Opaque internal state structure of SHA256
123 sha256_ctx shactx;
126 sha256::sha256() throw(std::bad_alloc)
128 opaque = new sha256_opaque();
129 finished = false;
130 sha256_init(&opaque->shactx);
133 sha256::~sha256() throw()
135 delete opaque;
138 void sha256::write(const uint8_t* data, size_t datalen) throw()
140 sha256_chunk(&opaque->shactx, data, datalen);
143 void sha256::read(uint8_t* hashout) throw()
145 if(!finished)
146 sha256_final(&opaque->shactx);
147 finished = true;
148 sha256_hash(&opaque->shactx, hashout);
151 std::string sha256::read() throw(std::bad_alloc)
153 uint8_t b[32];
154 read(b);
155 return sha256::tostring(b);
158 void sha256::hash(uint8_t* hashout, const uint8_t* data, size_t datalen) throw()
160 sha256 s;
161 s.write(data, datalen);
162 s.read(hashout);
165 std::string sha256::tostring(const uint8_t* hashout) throw(std::bad_alloc)
167 std::ostringstream str;
168 for(unsigned i = 0; i < 32; i++)
169 str << std::hex << std::setw(2) << std::setfill('0') << (unsigned)hashout[i];
170 return str.str();
173 struct loaded_rom load_rom_from_commandline(std::vector<std::string> cmdline, window* win) throw(std::bad_alloc,
174 std::runtime_error)
176 struct rom_files f;
177 try {
178 f = rom_files(cmdline, win);
179 f.resolve_relative();
180 } catch(std::bad_alloc& e) {
181 OOM_panic(win);
182 } catch(std::exception& e) {
183 throw std::runtime_error(std::string("Can't resolve ROM files: ") + e.what());
185 out(win) << "ROM type: " << gtype::tostring(f.rtype, f.region) << std::endl;
186 if(f.rom != "") out(win) << name_subrom(f.rtype, 0) << " file: '" << f.rom << "'" << std::endl;
187 if(f.rom_xml != "") out(win) << name_subrom(f.rtype, 1) << " file: '" << f.rom_xml << "'" << std::endl;
188 if(f.slota != "") out(win) << name_subrom(f.rtype, 2) << " file: '" << f.slota << "'" << std::endl;
189 if(f.slota_xml != "") out(win) << name_subrom(f.rtype, 3) << " file: '" << f.slota_xml << "'" << std::endl;
190 if(f.slotb != "") out(win) << name_subrom(f.rtype, 4) << " file: '" << f.slotb << "'" << std::endl;
191 if(f.slotb_xml != "") out(win) << name_subrom(f.rtype, 5) << " file: '" << f.slotb_xml << "'" << std::endl;
193 struct loaded_rom r;
194 try {
195 r = loaded_rom(f, win);
196 r.do_patch(cmdline, win);
197 } catch(std::bad_alloc& e) {
198 OOM_panic(win);
199 } catch(std::exception& e) {
200 throw std::runtime_error(std::string("Can't load ROM: ") + e.what());
203 std::string not_present = "N/A";
204 if(r.rom.valid) out(win) << name_subrom(f.rtype, 0) << " hash: " << r.rom.sha256 << std::endl;
205 if(r.rom_xml.valid) out(win) << name_subrom(f.rtype, 1) << " hash: " << r.rom_xml.sha256 << std::endl;
206 if(r.slota.valid) out(win) << name_subrom(f.rtype, 2) << " hash: " << r.slota.sha256 << std::endl;
207 if(r.slota_xml.valid) out(win) << name_subrom(f.rtype, 3) << " hash: " << r.slota_xml.sha256 << std::endl;
208 if(r.slotb.valid) out(win) << name_subrom(f.rtype, 4) << " hash: " << r.slotb.sha256 << std::endl;
209 if(r.slotb_xml.valid) out(win) << name_subrom(f.rtype, 5) << " hash: " << r.slotb_xml.sha256 << std::endl;
210 return r;
213 void dump_region_map(window* win) throw(std::bad_alloc)
215 std::vector<struct memory_region> regions = get_regions();
216 for(auto i = regions.begin(); i != regions.end(); ++i) {
217 char buf[256];
218 sprintf(buf, "Region: %08X-%08X %08X %s%c %s", i->baseaddr, i->lastaddr, i->size,
219 i->readonly ? "R-" : "RW", i->native_endian ? 'N' : 'B', i->region_name.c_str());
220 out(win) << buf << std::endl;
224 std::ostream& out(window* win) throw(std::bad_alloc)
226 if(!win)
227 return std::cout;
228 else
229 return win->out();
232 void fatal_error(window* win) throw()
234 if(win)
235 win->fatal_error();
236 std::cout << "PANIC: Fatal error, can't continue." << std::endl;
237 exit(1);
240 std::string get_config_path(window* win) throw(std::bad_alloc)
242 const char* tmp;
243 std::string basedir;
244 if((tmp = getenv("APPDATA"))) {
245 //If $APPDATA exists, it is the base directory
246 basedir = tmp;
247 } else if((tmp = getenv("XDG_CONFIG_HOME"))) {
248 //If $XDG_CONFIG_HOME exists, it is the base directory
249 basedir = tmp;
250 } else if((tmp = getenv("HOME"))) {
251 //If $HOME exists, the base directory is '.config' there.
252 basedir = std::string(tmp) + "/.config";
253 } else {
254 //Last chance: Return current directory.
255 return ".";
257 //Try to create 'lsnes'. If it exists (or is created) and is directory, great. Otherwise error out.
258 std::string lsnes_path = basedir + "/lsnes";
259 boost::filesystem::path p(lsnes_path);
260 if(!boost::filesystem::create_directories(p) && !boost::filesystem::is_directory(p)) {
261 out(win) << "FATAL: Can't create configuration directory '" << lsnes_path << "'" << std::endl;
262 fatal_error(win);
264 //Yes, this is racy, but portability is more important than being absolutely correct...
265 std::string tfile = lsnes_path + "/test";
266 remove(tfile.c_str());
267 FILE* x;
268 if(!(x = fopen(tfile.c_str(), "w+"))) {
269 out(win) << "FATAL: Configuration directory '" << lsnes_path << "' is not writable" << std::endl;
270 fatal_error(win);
272 fclose(x);
273 remove(tfile.c_str());
274 return lsnes_path;
277 extern const char* lsnesrc_file;
279 void create_lsnesrc(window* win)
281 std::string rcfile = get_config_path(win) + "/lsnes.rc";
282 std::ifstream x(rcfile.c_str());
283 if(x) {
284 x.close();
285 return;
287 std::ofstream y(rcfile.c_str());
288 if(!y) {
289 out(win) << "FATAL: lsnes.rc (" << rcfile << ") doesn't exist nor it can be created" << std::endl;
290 fatal_error(win);
292 y.write(lsnesrc_file, strlen(lsnesrc_file));
293 y.close();
297 void OOM_panic(window* win)
299 out(win) << "FATAL: Out of memory!" << std::endl;
300 fatal_error(win);
303 std::string bsnes_core_version;
304 std::string lsnes_version = "0-β1";