2011-09-13 17:50:18 +03:00
|
|
|
#include "lsnes.hpp"
|
|
|
|
#include "memorymanip.hpp"
|
2011-09-17 01:05:41 +03:00
|
|
|
#include "window.hpp"
|
2011-09-13 17:50:18 +03:00
|
|
|
#include "misc.hpp"
|
|
|
|
#include "rom.hpp"
|
|
|
|
#include <sstream>
|
|
|
|
#include <iostream>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <fstream>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <ctime>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
|
|
|
#include <boost/filesystem.hpp>
|
|
|
|
|
2011-09-25 11:48:36 +03:00
|
|
|
#ifdef NO_TIME_INTERCEPT
|
|
|
|
time_t __real_time(time_t* t)
|
|
|
|
{
|
|
|
|
return time(t);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-09-13 17:50:18 +03:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
std::string rseed;
|
|
|
|
uint64_t rcounter = 0;
|
|
|
|
|
|
|
|
std::string get_random_hexstring_64(size_t index)
|
|
|
|
{
|
|
|
|
std::ostringstream str;
|
2011-09-25 02:13:50 +03:00
|
|
|
str << rseed << " " << __real_time(NULL) << " " << (rcounter++) << " " << index;
|
2011-09-13 17:50:18 +03:00
|
|
|
std::string s = str.str();
|
|
|
|
std::vector<char> x;
|
|
|
|
x.resize(s.length());
|
|
|
|
std::copy(s.begin(), s.end(), x.begin());
|
|
|
|
return sha256::hash(reinterpret_cast<uint8_t*>(&x[0]), x.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string collect_identifying_information()
|
|
|
|
{
|
|
|
|
//TODO: Collect as much identifying information as possible.
|
|
|
|
std::ostringstream str;
|
2011-09-25 02:13:50 +03:00
|
|
|
time_t told = __real_time(NULL);
|
2011-09-13 17:50:18 +03:00
|
|
|
time_t tnew;
|
|
|
|
uint64_t loops = 0;
|
|
|
|
uint64_t base = 0;
|
|
|
|
int cnt = 0;
|
|
|
|
while(cnt < 3) {
|
2011-09-25 02:13:50 +03:00
|
|
|
tnew = __real_time(NULL);
|
2011-09-13 17:50:18 +03:00
|
|
|
if(tnew > told) {
|
|
|
|
told = tnew;
|
|
|
|
cnt++;
|
|
|
|
str << (loops - base) << " ";
|
|
|
|
base = loops;
|
|
|
|
}
|
|
|
|
loops++;
|
|
|
|
}
|
|
|
|
return str.str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string get_random_hexstring(size_t length) throw(std::bad_alloc)
|
|
|
|
{
|
|
|
|
std::string out;
|
|
|
|
for(size_t i = 0; i < length; i += 64)
|
|
|
|
out = out + get_random_hexstring_64(i);
|
|
|
|
return out.substr(0, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_random_seed(const std::string& seed) throw(std::bad_alloc)
|
|
|
|
{
|
|
|
|
std::ostringstream str;
|
|
|
|
str << seed.length() << " " << seed;
|
|
|
|
rseed = str.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_random_seed() throw(std::bad_alloc)
|
|
|
|
{
|
|
|
|
//Try /dev/urandom first.
|
|
|
|
{
|
|
|
|
std::ifstream r("/dev/urandom", std::ios::binary);
|
|
|
|
if(r.is_open()) {
|
|
|
|
char buf[64];
|
|
|
|
r.read(buf, 64);
|
|
|
|
std::string s(buf, 64);
|
|
|
|
set_random_seed(s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//Fall back to time.
|
|
|
|
std::ostringstream str;
|
2011-09-25 02:13:50 +03:00
|
|
|
str << collect_identifying_information() << " " << __real_time(NULL);
|
2011-09-13 17:50:18 +03:00
|
|
|
set_random_seed(str.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
//VERY dirty hack.
|
|
|
|
namespace foobar
|
|
|
|
{
|
|
|
|
#include <nall/sha256.hpp>
|
|
|
|
}
|
|
|
|
using foobar::nall::sha256_ctx;
|
|
|
|
using foobar::nall::sha256_init;
|
|
|
|
using foobar::nall::sha256_final;
|
|
|
|
using foobar::nall::sha256_hash;
|
|
|
|
using foobar::nall::sha256_chunk;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Opaque internal state of SHA256
|
|
|
|
*/
|
|
|
|
struct sha256_opaque
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* \brief Opaque internal state structure of SHA256
|
|
|
|
*/
|
|
|
|
sha256_ctx shactx;
|
|
|
|
};
|
|
|
|
|
|
|
|
sha256::sha256() throw(std::bad_alloc)
|
|
|
|
{
|
|
|
|
opaque = new sha256_opaque();
|
|
|
|
finished = false;
|
|
|
|
sha256_init(&opaque->shactx);
|
|
|
|
}
|
|
|
|
|
|
|
|
sha256::~sha256() throw()
|
|
|
|
{
|
|
|
|
delete opaque;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sha256::write(const uint8_t* data, size_t datalen) throw()
|
|
|
|
{
|
|
|
|
sha256_chunk(&opaque->shactx, data, datalen);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sha256::read(uint8_t* hashout) throw()
|
|
|
|
{
|
|
|
|
if(!finished)
|
|
|
|
sha256_final(&opaque->shactx);
|
|
|
|
finished = true;
|
|
|
|
sha256_hash(&opaque->shactx, hashout);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string sha256::read() throw(std::bad_alloc)
|
|
|
|
{
|
|
|
|
uint8_t b[32];
|
|
|
|
read(b);
|
|
|
|
return sha256::tostring(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sha256::hash(uint8_t* hashout, const uint8_t* data, size_t datalen) throw()
|
|
|
|
{
|
|
|
|
sha256 s;
|
|
|
|
s.write(data, datalen);
|
|
|
|
s.read(hashout);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string sha256::tostring(const uint8_t* hashout) throw(std::bad_alloc)
|
|
|
|
{
|
|
|
|
std::ostringstream str;
|
|
|
|
for(unsigned i = 0; i < 32; i++)
|
|
|
|
str << std::hex << std::setw(2) << std::setfill('0') << (unsigned)hashout[i];
|
|
|
|
return str.str();
|
|
|
|
}
|
|
|
|
|
2011-09-17 01:05:41 +03:00
|
|
|
struct loaded_rom load_rom_from_commandline(std::vector<std::string> cmdline) throw(std::bad_alloc,
|
2011-09-13 17:50:18 +03:00
|
|
|
std::runtime_error)
|
|
|
|
{
|
|
|
|
struct rom_files f;
|
|
|
|
try {
|
2011-09-17 01:05:41 +03:00
|
|
|
f = rom_files(cmdline);
|
2011-09-13 17:50:18 +03:00
|
|
|
f.resolve_relative();
|
|
|
|
} catch(std::bad_alloc& e) {
|
2011-09-17 01:05:41 +03:00
|
|
|
OOM_panic();
|
2011-09-13 17:50:18 +03:00
|
|
|
} catch(std::exception& e) {
|
|
|
|
throw std::runtime_error(std::string("Can't resolve ROM files: ") + e.what());
|
|
|
|
}
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "ROM type: " << gtype::tostring(f.rtype, f.region) << std::endl;
|
|
|
|
if(f.rom != "") messages << name_subrom(f.rtype, 0) << " file: '" << f.rom << "'" << std::endl;
|
|
|
|
if(f.rom_xml != "") messages << name_subrom(f.rtype, 1) << " file: '" << f.rom_xml << "'"
|
2011-09-17 01:05:41 +03:00
|
|
|
<< std::endl;
|
2011-09-17 09:55:35 +03:00
|
|
|
if(f.slota != "") messages << name_subrom(f.rtype, 2) << " file: '" << f.slota << "'" << std::endl;
|
|
|
|
if(f.slota_xml != "") messages << name_subrom(f.rtype, 3) << " file: '" << f.slota_xml << "'"
|
2011-09-17 01:05:41 +03:00
|
|
|
<< std::endl;
|
2011-09-17 09:55:35 +03:00
|
|
|
if(f.slotb != "") messages << name_subrom(f.rtype, 4) << " file: '" << f.slotb << "'" << std::endl;
|
|
|
|
if(f.slotb_xml != "") messages << name_subrom(f.rtype, 5) << " file: '" << f.slotb_xml << "'"
|
2011-09-17 01:05:41 +03:00
|
|
|
<< std::endl;
|
2011-09-13 17:50:18 +03:00
|
|
|
|
|
|
|
struct loaded_rom r;
|
|
|
|
try {
|
2011-09-17 01:05:41 +03:00
|
|
|
r = loaded_rom(f);
|
|
|
|
r.do_patch(cmdline);
|
2011-09-13 17:50:18 +03:00
|
|
|
} catch(std::bad_alloc& e) {
|
2011-09-17 01:05:41 +03:00
|
|
|
OOM_panic();
|
2011-09-13 17:50:18 +03:00
|
|
|
} catch(std::exception& e) {
|
|
|
|
throw std::runtime_error(std::string("Can't load ROM: ") + e.what());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string not_present = "N/A";
|
2011-09-17 09:55:35 +03:00
|
|
|
if(r.rom.valid) messages << name_subrom(f.rtype, 0) << " hash: " << r.rom.sha256 << std::endl;
|
|
|
|
if(r.rom_xml.valid) messages << name_subrom(f.rtype, 1) << " hash: " << r.rom_xml.sha256 << std::endl;
|
|
|
|
if(r.slota.valid) messages << name_subrom(f.rtype, 2) << " hash: " << r.slota.sha256 << std::endl;
|
|
|
|
if(r.slota_xml.valid) messages << name_subrom(f.rtype, 3) << " hash: " << r.slota_xml.sha256
|
2011-09-17 01:05:41 +03:00
|
|
|
<< std::endl;
|
2011-09-17 09:55:35 +03:00
|
|
|
if(r.slotb.valid) messages << name_subrom(f.rtype, 4) << " hash: " << r.slotb.sha256 << std::endl;
|
|
|
|
if(r.slotb_xml.valid) messages << name_subrom(f.rtype, 5) << " hash: " << r.slotb_xml.sha256
|
2011-09-17 01:05:41 +03:00
|
|
|
<< std::endl;
|
2011-09-13 17:50:18 +03:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-09-17 01:05:41 +03:00
|
|
|
void dump_region_map() throw(std::bad_alloc)
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
|
|
|
std::vector<struct memory_region> regions = get_regions();
|
|
|
|
for(auto i = regions.begin(); i != regions.end(); ++i) {
|
|
|
|
char buf[256];
|
|
|
|
sprintf(buf, "Region: %08X-%08X %08X %s%c %s", i->baseaddr, i->lastaddr, i->size,
|
|
|
|
i->readonly ? "R-" : "RW", i->native_endian ? 'N' : 'B', i->region_name.c_str());
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << buf << std::endl;
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-17 01:05:41 +03:00
|
|
|
void fatal_error() throw()
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
2011-09-17 01:05:41 +03:00
|
|
|
window::fatal_error();
|
2011-09-13 17:50:18 +03:00
|
|
|
std::cout << "PANIC: Fatal error, can't continue." << std::endl;
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2011-09-17 01:05:41 +03:00
|
|
|
std::string get_config_path() throw(std::bad_alloc)
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
|
|
|
const char* tmp;
|
|
|
|
std::string basedir;
|
|
|
|
if((tmp = getenv("APPDATA"))) {
|
|
|
|
//If $APPDATA exists, it is the base directory
|
|
|
|
basedir = tmp;
|
|
|
|
} else if((tmp = getenv("XDG_CONFIG_HOME"))) {
|
|
|
|
//If $XDG_CONFIG_HOME exists, it is the base directory
|
|
|
|
basedir = tmp;
|
|
|
|
} else if((tmp = getenv("HOME"))) {
|
|
|
|
//If $HOME exists, the base directory is '.config' there.
|
|
|
|
basedir = std::string(tmp) + "/.config";
|
|
|
|
} else {
|
|
|
|
//Last chance: Return current directory.
|
|
|
|
return ".";
|
|
|
|
}
|
|
|
|
//Try to create 'lsnes'. If it exists (or is created) and is directory, great. Otherwise error out.
|
|
|
|
std::string lsnes_path = basedir + "/lsnes";
|
|
|
|
boost::filesystem::path p(lsnes_path);
|
|
|
|
if(!boost::filesystem::create_directories(p) && !boost::filesystem::is_directory(p)) {
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "FATAL: Can't create configuration directory '" << lsnes_path << "'" << std::endl;
|
2011-09-17 01:05:41 +03:00
|
|
|
fatal_error();
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
//Yes, this is racy, but portability is more important than being absolutely correct...
|
|
|
|
std::string tfile = lsnes_path + "/test";
|
|
|
|
remove(tfile.c_str());
|
|
|
|
FILE* x;
|
|
|
|
if(!(x = fopen(tfile.c_str(), "w+"))) {
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "FATAL: Configuration directory '" << lsnes_path << "' is not writable" << std::endl;
|
2011-09-17 01:05:41 +03:00
|
|
|
fatal_error();
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
fclose(x);
|
|
|
|
remove(tfile.c_str());
|
|
|
|
return lsnes_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern const char* lsnesrc_file;
|
|
|
|
|
2011-09-17 01:05:41 +03:00
|
|
|
void create_lsnesrc()
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
2011-09-17 01:05:41 +03:00
|
|
|
std::string rcfile = get_config_path() + "/lsnes.rc";
|
2011-09-13 17:50:18 +03:00
|
|
|
std::ifstream x(rcfile.c_str());
|
|
|
|
if(x) {
|
|
|
|
x.close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::ofstream y(rcfile.c_str());
|
|
|
|
if(!y) {
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "FATAL: lsnes.rc (" << rcfile << ") doesn't exist nor it can be created" << std::endl;
|
2011-09-17 01:05:41 +03:00
|
|
|
fatal_error();
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
y.write(lsnesrc_file, strlen(lsnesrc_file));
|
|
|
|
y.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-17 01:05:41 +03:00
|
|
|
void OOM_panic()
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "FATAL: Out of memory!" << std::endl;
|
2011-09-17 01:05:41 +03:00
|
|
|
fatal_error();
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
|
2011-09-17 09:55:35 +03:00
|
|
|
std::ostream& _messages()
|
|
|
|
{
|
|
|
|
return window::out();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-13 17:50:18 +03:00
|
|
|
std::string bsnes_core_version;
|
2011-10-01 13:05:03 +03:00
|
|
|
std::string lsnes_version = "0-β14";
|