2011-09-16 03:49:04 +03:00
|
|
|
#include "moviedata.hpp"
|
|
|
|
#include "command.hpp"
|
2011-09-16 06:13:33 +03:00
|
|
|
#include "lua.hpp"
|
2011-09-17 09:55:35 +03:00
|
|
|
#include "misc.hpp"
|
2011-09-16 06:13:33 +03:00
|
|
|
#include "framebuffer.hpp"
|
|
|
|
#include <iomanip>
|
|
|
|
#include "lsnes.hpp"
|
2011-09-17 09:55:35 +03:00
|
|
|
#include "window.hpp"
|
2011-09-16 06:13:33 +03:00
|
|
|
#include <snes/snes.hpp>
|
|
|
|
#include <ui-libsnes/libsnes.hpp>
|
|
|
|
#include "rrdata.hpp"
|
|
|
|
#include "settings.hpp"
|
|
|
|
#include "controller.hpp"
|
2011-09-16 03:49:04 +03:00
|
|
|
|
|
|
|
struct moviefile our_movie;
|
2011-09-16 06:13:33 +03:00
|
|
|
struct loaded_rom* our_rom;
|
|
|
|
bool system_corrupt;
|
|
|
|
movie_logic movb;
|
2011-09-16 03:49:04 +03:00
|
|
|
|
|
|
|
std::vector<char>& get_host_memory()
|
|
|
|
{
|
|
|
|
return our_movie.host_memory;
|
|
|
|
}
|
|
|
|
|
2011-09-16 06:13:33 +03:00
|
|
|
movie& get_movie()
|
|
|
|
{
|
|
|
|
return movb.get_movie();
|
|
|
|
}
|
2011-09-16 03:49:04 +03:00
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
2011-09-16 06:13:33 +03:00
|
|
|
numeric_setting savecompression("savecompression", 0, 9, 7);
|
|
|
|
|
2011-09-17 10:15:35 +03:00
|
|
|
|
2011-09-17 12:00:49 +03:00
|
|
|
function_ptr_command<> get_gamename("get-gamename", "Get the game name",
|
2011-09-17 10:15:35 +03:00
|
|
|
"Syntax: get-gamename\nPrints the game name\n",
|
2011-09-17 12:00:49 +03:00
|
|
|
[]() throw(std::bad_alloc, std::runtime_error) {
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Game name is '" << our_movie.gamename << "'" << std::endl;
|
2011-09-17 10:15:35 +03:00
|
|
|
});
|
2011-09-16 03:49:04 +03:00
|
|
|
|
2011-09-17 12:00:49 +03:00
|
|
|
function_ptr_command<const std::string&> set_gamename("set-gamename", "Set the game name",
|
2011-09-17 11:29:10 +03:00
|
|
|
"Syntax: set-gamename <name>\nSets the game name to <name>\n",
|
|
|
|
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
|
|
|
|
our_movie.gamename = args;
|
|
|
|
messages << "Game name changed to '" << our_movie.gamename << "'" << std::endl;
|
|
|
|
});
|
|
|
|
|
2011-09-17 12:00:49 +03:00
|
|
|
function_ptr_command<> show_authors("show-authors", "Show the run authors",
|
2011-09-17 10:15:35 +03:00
|
|
|
"Syntax: show-authors\nShows the run authors\n",
|
2011-09-17 12:00:49 +03:00
|
|
|
[]() throw(std::bad_alloc, std::runtime_error)
|
2011-09-16 03:49:04 +03:00
|
|
|
{
|
|
|
|
size_t idx = 0;
|
|
|
|
for(auto i = our_movie.authors.begin(); i != our_movie.authors.end(); i++) {
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << (idx++) << ": " << i->first << "|" << i->second << std::endl;
|
2011-09-16 03:49:04 +03:00
|
|
|
}
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "End of authors list" << std::endl;
|
2011-09-17 10:15:35 +03:00
|
|
|
});
|
2011-09-16 03:49:04 +03:00
|
|
|
|
2011-09-17 13:15:20 +03:00
|
|
|
function_ptr_command<tokensplitter&> add_author("add-author", "Add an author",
|
2011-09-17 10:15:35 +03:00
|
|
|
"Syntax: add-author <fullname>\nSyntax: add-author |<nickname>\n"
|
|
|
|
"Syntax: add-author <fullname>|<nickname>\nAdds a new author\n",
|
2011-09-17 13:15:20 +03:00
|
|
|
[](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
|
2011-09-17 10:15:35 +03:00
|
|
|
auto g = split_author(t.tail());
|
|
|
|
our_movie.authors.push_back(g);
|
|
|
|
messages << (our_movie.authors.size() - 1) << ": " << g.first << "|" << g.second << std::endl;
|
|
|
|
});
|
2011-09-16 03:49:04 +03:00
|
|
|
|
2011-09-17 13:15:20 +03:00
|
|
|
function_ptr_command<tokensplitter&> remove_author("remove-author", "Remove an author",
|
2011-09-17 10:15:35 +03:00
|
|
|
"Syntax: remove-author <id>\nRemoves author with ID <id>\n",
|
2011-09-17 13:15:20 +03:00
|
|
|
[](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
|
2011-09-16 03:49:04 +03:00
|
|
|
uint64_t index = parse_value<uint64_t>(t.tail());
|
|
|
|
if(index >= our_movie.authors.size())
|
|
|
|
throw std::runtime_error("No such author");
|
|
|
|
our_movie.authors.erase(our_movie.authors.begin() + index);
|
2011-09-17 10:15:35 +03:00
|
|
|
});
|
2011-09-16 03:49:04 +03:00
|
|
|
|
2011-09-17 13:15:20 +03:00
|
|
|
function_ptr_command<tokensplitter&> edit_author("edit-author", "Edit an author",
|
2011-09-17 10:15:35 +03:00
|
|
|
"Syntax: edit-author <authorid> <fullname>\nSyntax: edit-author <authorid> |<nickname>\n"
|
|
|
|
"Syntax: edit-author <authorid> <fullname>|<nickname>\nEdits author name\n",
|
2011-09-17 13:15:20 +03:00
|
|
|
[](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
|
2011-09-16 03:49:04 +03:00
|
|
|
uint64_t index = parse_value<uint64_t>(t);
|
|
|
|
if(index >= our_movie.authors.size())
|
|
|
|
throw std::runtime_error("No such author");
|
2011-09-17 10:15:35 +03:00
|
|
|
auto g = split_author(t.tail());
|
|
|
|
our_movie.authors[index] = g;
|
|
|
|
});
|
2011-09-16 06:13:33 +03:00
|
|
|
|
2011-09-17 01:05:41 +03:00
|
|
|
void warn_hash_mismatch(const std::string& mhash, const loaded_slot& slot,
|
2011-09-16 06:13:33 +03:00
|
|
|
const std::string& name)
|
|
|
|
{
|
|
|
|
if(mhash != slot.sha256) {
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "WARNING: " << name << " hash mismatch!" << std::endl
|
2011-09-16 06:13:33 +03:00
|
|
|
<< "\tMovie: " << mhash << std::endl
|
|
|
|
<< "\tOur ROM: " << slot.sha256 << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-17 10:15:35 +03:00
|
|
|
std::pair<std::string, std::string> split_author(const std::string& author) throw(std::bad_alloc,
|
|
|
|
std::runtime_error)
|
|
|
|
{
|
|
|
|
std::string _author = author;
|
|
|
|
std::string fullname;
|
|
|
|
std::string nickname;
|
|
|
|
size_t split = _author.find_first_of("|");
|
2011-09-19 21:44:05 +03:00
|
|
|
if(split >= _author.length()) {
|
2011-09-17 10:15:35 +03:00
|
|
|
fullname = _author;
|
|
|
|
} else {
|
|
|
|
fullname = _author.substr(0, split);
|
|
|
|
nickname = _author.substr(split + 1);
|
|
|
|
}
|
|
|
|
if(fullname == "" && nickname == "")
|
|
|
|
throw std::runtime_error("Bad author name");
|
|
|
|
return std::make_pair(fullname, nickname);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-16 06:13:33 +03:00
|
|
|
//Save state.
|
2011-09-17 01:05:41 +03:00
|
|
|
void do_save_state(const std::string& filename) throw(std::bad_alloc,
|
2011-09-16 06:13:33 +03:00
|
|
|
std::runtime_error)
|
|
|
|
{
|
2011-09-17 00:06:20 +03:00
|
|
|
lua_callback_pre_save(filename, true);
|
2011-09-16 06:13:33 +03:00
|
|
|
try {
|
|
|
|
uint64_t origtime = get_ticks_msec();
|
|
|
|
our_movie.is_savestate = true;
|
|
|
|
our_movie.sram = save_sram();
|
|
|
|
our_movie.savestate = save_core_state();
|
|
|
|
framebuffer.save(our_movie.screenshot);
|
|
|
|
auto s = movb.get_movie().save_state();
|
|
|
|
our_movie.movie_state.resize(s.size());
|
|
|
|
memcpy(&our_movie.movie_state[0], &s[0], s.size());
|
|
|
|
our_movie.input = movb.get_movie().save();
|
|
|
|
our_movie.save(filename, savecompression);
|
|
|
|
uint64_t took = get_ticks_msec() - origtime;
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Saved state '" << filename << "' in " << took << "ms." << std::endl;
|
2011-09-17 00:06:20 +03:00
|
|
|
lua_callback_post_save(filename, true);
|
2011-09-16 06:13:33 +03:00
|
|
|
} catch(std::bad_alloc& e) {
|
2011-09-17 00:06:20 +03:00
|
|
|
throw;
|
2011-09-16 06:13:33 +03:00
|
|
|
} catch(std::exception& e) {
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Save failed: " << e.what() << std::endl;
|
2011-09-17 00:06:20 +03:00
|
|
|
lua_callback_err_save(filename);
|
2011-09-16 06:13:33 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Save movie.
|
2011-09-17 01:05:41 +03:00
|
|
|
void do_save_movie(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
|
2011-09-16 06:13:33 +03:00
|
|
|
{
|
2011-09-17 00:06:20 +03:00
|
|
|
lua_callback_pre_save(filename, false);
|
2011-09-16 06:13:33 +03:00
|
|
|
try {
|
|
|
|
uint64_t origtime = get_ticks_msec();
|
|
|
|
our_movie.is_savestate = false;
|
|
|
|
our_movie.input = movb.get_movie().save();
|
|
|
|
our_movie.save(filename, savecompression);
|
|
|
|
uint64_t took = get_ticks_msec() - origtime;
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Saved movie '" << filename << "' in " << took << "ms." << std::endl;
|
2011-09-17 00:06:20 +03:00
|
|
|
lua_callback_post_save(filename, false);
|
2011-09-16 06:13:33 +03:00
|
|
|
} catch(std::bad_alloc& e) {
|
2011-09-17 01:05:41 +03:00
|
|
|
OOM_panic();
|
2011-09-16 06:13:33 +03:00
|
|
|
} catch(std::exception& e) {
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Save failed: " << e.what() << std::endl;
|
2011-09-17 00:06:20 +03:00
|
|
|
lua_callback_err_save(filename);
|
2011-09-16 06:13:33 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Load state from loaded movie file (does not catch errors).
|
2011-09-17 01:05:41 +03:00
|
|
|
void do_load_state(struct moviefile& _movie, int lmode)
|
2011-09-16 06:13:33 +03:00
|
|
|
{
|
2011-09-18 17:27:03 +03:00
|
|
|
if(_movie.force_corrupt)
|
|
|
|
throw std::runtime_error("Movie file invalid");
|
2011-09-16 06:13:33 +03:00
|
|
|
bool will_load_state = _movie.is_savestate && lmode != LOAD_STATE_MOVIE;
|
|
|
|
if(gtype::toromtype(_movie.gametype) != our_rom->rtype)
|
|
|
|
throw std::runtime_error("ROM types of movie and loaded ROM don't match");
|
|
|
|
if(gtype::toromregion(_movie.gametype) != our_rom->orig_region && our_rom->orig_region != REGION_AUTO)
|
|
|
|
throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
|
|
|
|
|
|
|
|
if(_movie.coreversion != bsnes_core_version) {
|
|
|
|
if(will_load_state) {
|
|
|
|
std::ostringstream x;
|
|
|
|
x << "ERROR: Emulator core version mismatch!" << std::endl
|
|
|
|
<< "\tThis version: " << bsnes_core_version << std::endl
|
|
|
|
<< "\tFile is from: " << _movie.coreversion << std::endl;
|
|
|
|
throw std::runtime_error(x.str());
|
|
|
|
} else
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "WARNING: Emulator core version mismatch!" << std::endl
|
2011-09-16 06:13:33 +03:00
|
|
|
<< "\tThis version: " << bsnes_core_version << std::endl
|
|
|
|
<< "\tFile is from: " << _movie.coreversion << std::endl;
|
|
|
|
}
|
2011-09-17 01:05:41 +03:00
|
|
|
warn_hash_mismatch(_movie.rom_sha256, our_rom->rom, "ROM #1");
|
|
|
|
warn_hash_mismatch(_movie.romxml_sha256, our_rom->rom_xml, "XML #1");
|
|
|
|
warn_hash_mismatch(_movie.slota_sha256, our_rom->slota, "ROM #2");
|
|
|
|
warn_hash_mismatch(_movie.slotaxml_sha256, our_rom->slota_xml, "XML #2");
|
|
|
|
warn_hash_mismatch(_movie.slotb_sha256, our_rom->slotb, "ROM #3");
|
|
|
|
warn_hash_mismatch(_movie.slotbxml_sha256, our_rom->slotb_xml, "XML #3");
|
2011-09-16 06:13:33 +03:00
|
|
|
|
|
|
|
SNES::config.random = false;
|
|
|
|
SNES::config.expansion_port = SNES::System::ExpansionPortDevice::None;
|
|
|
|
|
|
|
|
movie newmovie;
|
|
|
|
if(lmode == LOAD_STATE_PRESERVE)
|
|
|
|
newmovie = movb.get_movie();
|
|
|
|
else
|
|
|
|
newmovie.load(_movie.rerecords, _movie.projectid, _movie.input);
|
|
|
|
|
|
|
|
if(will_load_state) {
|
|
|
|
std::vector<unsigned char> tmp;
|
|
|
|
tmp.resize(_movie.movie_state.size());
|
|
|
|
memcpy(&tmp[0], &_movie.movie_state[0], tmp.size());
|
|
|
|
newmovie.restore_state(tmp, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
//Negative return.
|
|
|
|
rrdata::read_base(_movie.projectid);
|
|
|
|
rrdata::add_internal();
|
|
|
|
try {
|
|
|
|
our_rom->region = gtype::toromregion(_movie.gametype);
|
|
|
|
our_rom->load();
|
|
|
|
|
|
|
|
if(_movie.is_savestate && lmode != LOAD_STATE_MOVIE) {
|
|
|
|
//Load the savestate and movie state.
|
|
|
|
controller_set_port_type(0, _movie.port1);
|
|
|
|
controller_set_port_type(1, _movie.port2);
|
|
|
|
load_core_state(_movie.savestate);
|
|
|
|
framebuffer.load(_movie.screenshot);
|
|
|
|
} else {
|
2011-09-17 01:05:41 +03:00
|
|
|
load_sram(_movie.movie_sram);
|
2011-09-16 06:13:33 +03:00
|
|
|
controller_set_port_type(0, _movie.port1);
|
|
|
|
controller_set_port_type(1, _movie.port2);
|
|
|
|
framebuffer = screen_nosignal;
|
|
|
|
}
|
|
|
|
} catch(std::bad_alloc& e) {
|
2011-09-17 01:05:41 +03:00
|
|
|
OOM_panic();
|
2011-09-16 06:13:33 +03:00
|
|
|
} catch(std::exception& e) {
|
|
|
|
system_corrupt = true;
|
|
|
|
framebuffer = screen_corrupt;
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Okay, copy the movie data.
|
|
|
|
our_movie = _movie;
|
|
|
|
if(!our_movie.is_savestate || lmode == LOAD_STATE_MOVIE) {
|
|
|
|
our_movie.is_savestate = false;
|
|
|
|
our_movie.host_memory.clear();
|
|
|
|
}
|
|
|
|
movb.get_movie() = newmovie;
|
|
|
|
//Activate RW mode if needed.
|
|
|
|
if(lmode == LOAD_STATE_RW)
|
|
|
|
movb.get_movie().readonly_mode(false);
|
|
|
|
if(lmode == LOAD_STATE_DEFAULT && !(movb.get_movie().get_frame_count()))
|
|
|
|
movb.get_movie().readonly_mode(false);
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "ROM Type ";
|
2011-09-16 06:13:33 +03:00
|
|
|
switch(our_rom->rtype) {
|
|
|
|
case ROMTYPE_SNES:
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "SNES";
|
2011-09-16 06:13:33 +03:00
|
|
|
break;
|
|
|
|
case ROMTYPE_BSX:
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "BS-X";
|
2011-09-16 06:13:33 +03:00
|
|
|
break;
|
|
|
|
case ROMTYPE_BSXSLOTTED:
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "BS-X slotted";
|
2011-09-16 06:13:33 +03:00
|
|
|
break;
|
|
|
|
case ROMTYPE_SUFAMITURBO:
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Sufami Turbo";
|
2011-09-16 06:13:33 +03:00
|
|
|
break;
|
|
|
|
case ROMTYPE_SGB:
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Super Game Boy";
|
2011-09-16 06:13:33 +03:00
|
|
|
break;
|
|
|
|
default:
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Unknown";
|
2011-09-16 06:13:33 +03:00
|
|
|
break;
|
|
|
|
}
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << " region ";
|
2011-09-16 06:13:33 +03:00
|
|
|
switch(our_rom->region) {
|
|
|
|
case REGION_PAL:
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "PAL";
|
2011-09-16 06:13:33 +03:00
|
|
|
break;
|
|
|
|
case REGION_NTSC:
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "NTSC";
|
2011-09-16 06:13:33 +03:00
|
|
|
break;
|
|
|
|
default:
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Unknown";
|
2011-09-16 06:13:33 +03:00
|
|
|
break;
|
|
|
|
}
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << std::endl;
|
2011-09-16 06:13:33 +03:00
|
|
|
uint64_t mlength = _movie.get_movie_length();
|
|
|
|
{
|
|
|
|
mlength += 999999;
|
|
|
|
std::ostringstream x;
|
|
|
|
if(mlength > 3600000000000) {
|
|
|
|
x << mlength / 3600000000000 << ":";
|
|
|
|
mlength %= 3600000000000;
|
|
|
|
}
|
|
|
|
x << std::setfill('0') << std::setw(2) << mlength / 60000000000 << ":";
|
|
|
|
mlength %= 60000000000;
|
|
|
|
x << std::setfill('0') << std::setw(2) << mlength / 1000000000 << ".";
|
|
|
|
mlength %= 1000000000;
|
|
|
|
x << std::setfill('0') << std::setw(3) << mlength / 1000000;
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Rerecords " << _movie.rerecords << " length " << x.str() << " ("
|
2011-09-16 06:13:33 +03:00
|
|
|
<< _movie.get_frame_count() << " frames)" << std::endl;
|
|
|
|
}
|
|
|
|
if(_movie.gamename != "")
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Game Name: " << _movie.gamename << std::endl;
|
2011-09-16 06:13:33 +03:00
|
|
|
for(size_t i = 0; i < _movie.authors.size(); i++)
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Author: " << _movie.authors[i].first << "(" << _movie.authors[i].second << ")"
|
2011-09-16 06:13:33 +03:00
|
|
|
<< std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Load state
|
2011-09-18 10:06:05 +03:00
|
|
|
bool do_load_state(const std::string& filename, int lmode)
|
2011-09-16 06:13:33 +03:00
|
|
|
{
|
|
|
|
uint64_t origtime = get_ticks_msec();
|
2011-09-17 00:06:20 +03:00
|
|
|
lua_callback_pre_load(filename);
|
2011-09-16 06:13:33 +03:00
|
|
|
struct moviefile mfile;
|
|
|
|
try {
|
|
|
|
mfile = moviefile(filename);
|
|
|
|
} catch(std::bad_alloc& e) {
|
2011-09-17 01:05:41 +03:00
|
|
|
OOM_panic();
|
2011-09-16 06:13:33 +03:00
|
|
|
} catch(std::exception& e) {
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Can't read movie/savestate '" << filename << "': " << e.what() << std::endl;
|
2011-09-17 00:06:20 +03:00
|
|
|
lua_callback_err_load(filename);
|
2011-09-18 10:06:05 +03:00
|
|
|
return false;
|
2011-09-16 06:13:33 +03:00
|
|
|
}
|
|
|
|
try {
|
2011-09-17 01:05:41 +03:00
|
|
|
do_load_state(mfile, lmode);
|
2011-09-16 06:13:33 +03:00
|
|
|
uint64_t took = get_ticks_msec() - origtime;
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Loaded '" << filename << "' in " << took << "ms." << std::endl;
|
2011-09-17 00:06:20 +03:00
|
|
|
lua_callback_post_load(filename, our_movie.is_savestate);
|
2011-09-16 06:13:33 +03:00
|
|
|
} catch(std::bad_alloc& e) {
|
2011-09-17 01:05:41 +03:00
|
|
|
OOM_panic();
|
2011-09-16 06:13:33 +03:00
|
|
|
} catch(std::exception& e) {
|
2011-09-17 09:55:35 +03:00
|
|
|
messages << "Can't load movie/savestate '" << filename << "': " << e.what() << std::endl;
|
2011-09-17 00:06:20 +03:00
|
|
|
lua_callback_err_load(filename);
|
2011-09-18 10:06:05 +03:00
|
|
|
return false;
|
2011-09-16 06:13:33 +03:00
|
|
|
}
|
2011-09-18 10:06:05 +03:00
|
|
|
return true;
|
2011-09-16 06:13:33 +03:00
|
|
|
}
|