Movie downloading

This commit is contained in:
Ilari Liusvaara 2013-11-11 01:49:06 +02:00
parent 2a14872ae7
commit d91b20d430
8 changed files with 295 additions and 0 deletions

View file

@ -0,0 +1,35 @@
#ifndef _filedownload__hpp__included__
#define _filedownload__hpp__included__
#include "library/threadtypes.hpp"
#include "library/httpreq.hpp"
#include <string>
#include <list>
#include <vector>
struct file_download
{
//Variables.
std::string url;
std::string target_slot;
//Ctor
file_download();
~file_download();
//Lauch.
void do_async();
void cancel();
//Status.
volatile bool finished; //This signals download finishing, call finish().
std::string errormsg;
http_async_request req;
std::string statusmsg();
cv_class cond;
mutex_class m;
//Internal.
void _do_async();
std::string tempname;
std::string tempname2;
};
#endif

View file

@ -122,6 +122,11 @@ std::string safe_filename(const std::string& str);
*/
std::string mangle_name(const std::string& orig);
/**
* Return a new temporary file. The file will be created.
*/
std::string get_temp_file();
/**
* Mix some entropy.
*/

View file

@ -213,6 +213,10 @@ struct moviefile
* returns: Length of the movie in nanoseconds.
*/
uint64_t get_movie_length() throw();
/**
* Return reference to memory slot.
*/
static moviefile& memref(const std::string& slot);
private:
void binary_io(std::ostream& stream) throw(std::bad_alloc, std::runtime_error);
void binary_io(std::istream& stream, struct core_type& romtype) throw(std::bad_alloc, std::runtime_error);

View file

@ -1,6 +1,7 @@
#ifndef _plat_wxwidgets__window_mainwindow__hpp__included__
#define _plat_wxwidgets__window_mainwindow__hpp__included__
#include "core/filedownload.hpp"
#include "core/window.hpp"
#include "platform/wxwidgets/window_status.hpp"
#include "platform/wxwidgets/menu_recent.hpp"
@ -55,6 +56,7 @@ public:
recent_menu<recentfile_path>* recent_movies;
recent_menu<recentfile_path>* recent_scripts;
loadrom_menu* loadroms;
file_download* download_in_progress;
private:
void do_load_rom_image(core_type* t);
void handle_menu_click_cancelable(wxCommandEvent& e);

170
src/core/filedownload.cpp Normal file
View file

@ -0,0 +1,170 @@
#include "core/filedownload.hpp"
#include "core/misc.hpp"
#include "library/string.hpp"
#include "library/zip.hpp"
#include "core/moviedata.hpp"
#include "core/rom.hpp"
#include "interface/romtype.hpp"
#include <fstream>
namespace
{
void file_download_thread_trampoline(file_download* d)
{
d->_do_async();
}
class file_download_handler : public http_request::output_handler
{
public:
file_download_handler(const std::string& filename)
{
fp.open(filename, std::ios::binary);
tsize = 0;
}
~file_download_handler()
{
}
void header(const std::string& name, const std::string& content)
{
//Ignore headers.
}
void write(const char* source, size_t srcsize)
{
fp.write(source, srcsize);
tsize += srcsize;
}
private:
std::ofstream fp;
size_t tsize;
};
}
file_download::file_download()
{
finished = false;
req.ohandler = NULL;
}
file_download::~file_download()
{
if(req.ohandler) delete req.ohandler;
}
void file_download::cancel()
{
req.cancel();
errormsg = "Canceled";
finished = true;
}
void file_download::do_async()
{
tempname = get_temp_file();
req.ihandler = NULL;
req.ohandler = new file_download_handler(tempname);
req.verb = "GET";
req.url = url;
try {
req.lauch_async();
(new thread_class(file_download_thread_trampoline, this))->detach();
} catch(std::exception& e) {
req.cancel();
umutex_class h(m);
errormsg = e.what();
finished = true;
cond.notify_all();
}
}
std::string file_download::statusmsg()
{
int64_t dn, dt, un, ut;
if(finished)
return (stringfmt() << "Downloading finished").str();
req.get_xfer_status(dn, dt, un, ut);
if(dn == 0)
return "Connecting...";
else if(dt == 0)
return (stringfmt() << "Downloading (" << dn << "/<unknown>)").str();
else if(dn < dt)
return (stringfmt() << "Downloading (" << (100 * dn / dt) << "%)").str();
else
return (stringfmt() << "Downloading finished").str();
}
void file_download::_do_async()
{
while(!req.finished) {
umutex_class h(req.m);
req.finished_cond.wait(h);
if(!req.finished)
continue;
if(req.errormsg != "") {
remove(tempname.c_str());
umutex_class h(m);
errormsg = req.errormsg;
finished = true;
cond.notify_all();
return;
}
}
delete req.ohandler;
req.ohandler = NULL;
if(req.http_code > 299) {
umutex_class h(m);
errormsg = (stringfmt() << "Got HTTP error " << req.http_code).str();
finished = true;
cond.notify_all();
}
//Okay, we got the file.
std::istream* s = NULL;
try {
zip_reader r(tempname);
unsigned count = 0;
for(auto i : r) {
count++;
}
if(count == 1) {
std::istream& s = r[*r.begin()];
std::ofstream out(tempname2 = get_temp_file(), std::ios::binary);
while(s) {
char buf[4096];
s.read(buf, sizeof(buf));
out.write(buf, s.gcount());
}
delete &s;
} else {
tempname2 = tempname;
}
} catch(...) {
if(s) delete s;
tempname2 = tempname;
}
if(tempname != tempname2) remove(tempname.c_str());
try {
core_type* gametype = NULL;
if(!our_rom.rtype->isnull())
gametype = our_rom.rtype;
else {
moviefile::brief_info info(tempname2);
auto sysregs = core_sysregion::find_matching(info.sysregion);
for(auto i : sysregs)
gametype = &i->get_type();
}
moviefile::memref(target_slot) = moviefile(tempname2, *gametype);
remove(tempname2.c_str());
} catch(std::exception& e) {
remove(tempname2.c_str());
umutex_class h(m);
errormsg = e.what();
finished = true;
cond.notify_all();
return;
}
//We are done!
umutex_class h(m);
finished = true;
cond.notify_all();
}

View file

@ -13,6 +13,7 @@
#include "library/serialization.hpp"
#include "library/arch-detect.hpp"
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <iomanip>
@ -24,6 +25,9 @@
#include <cstdlib>
#include <cstring>
#include <boost/filesystem.hpp>
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#endif
#ifdef USE_LIBGCRYPT_SHA256
#include <gcrypt.h>
@ -385,6 +389,27 @@ void highrandom_256(uint8_t* buf)
#endif
}
std::string get_temp_file()
{
#if !defined(_WIN32) && !defined(_WIN64)
char tname[512];
strcpy(tname, "/tmp/lsnestmp_XXXXXX");
int h = mkstemp(tname);
if(h < 0)
throw std::runtime_error("Failed to get new tempfile name");
close(h);
return tname;
#else
char tpath[512];
char tname[512];
if(!GetTempPathA(512, tpath))
throw std::runtime_error("Failed to get new tempfile name");
if(!GetTempFileNameA(tpath, "lsn", 0, tname))
throw std::runtime_error("Failed to get new tempfile name");
return tname;
#endif
}
function_ptr_command<const std::string&> macro_test(lsnes_cmd, "test-macro", "", "",
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
regex_results r = regex("([0-9]+)[ \t](.*)", args);

View file

@ -1035,3 +1035,8 @@ uint64_t moviefile::get_movie_length() throw()
t += frames * _magic[STEP_W] + (frames * _magic[STEP_N] / _magic[BLOCK_FRAMES]);
return t;
}
moviefile& moviefile::memref(const std::string& slot)
{
return memory_saves[slot];
}

View file

@ -129,6 +129,7 @@ enum
wxID_RLUA_LAST = wxID_RLUA_FIRST + 16,
wxID_UPLOAD_FIRST,
wxID_UPLOAD_LAST = wxID_UPLOAD_FIRST + 256,
wxID_DOWNLOAD,
};
@ -160,6 +161,37 @@ namespace
int64_t last_update = 0;
thread_class* emulation_thread;
class download_timer : public wxTimer
{
public:
download_timer(wxwin_mainwindow* main)
{
w = main;
Start(50);
}
void Notify()
{
if(w->download_in_progress->finished) {
w->update_statusbar(std::map<std::string, std::u32string>());
auto old = w->download_in_progress;
w->download_in_progress = NULL;
if(old->errormsg != "") {
show_message_ok(w, "Error downloading movie", old->errormsg,
wxICON_EXCLAMATION);
} else {
platform::queue("load-movie $MEMORY:wxwidgets_download_tmp");
}
delete old;
Stop();
delete this;
} else {
w->update_statusbar(std::map<std::string, std::u32string>());
}
}
private:
wxwin_mainwindow* w;
};
template<typename T> void runemufn_async(T fn)
{
platform::queue(functor_call_helper2<T>, new T(fn), false);
@ -931,6 +963,7 @@ wxwin_mainwindow::wxwin_mainwindow()
: wxFrame(NULL, wxID_ANY, getname(), wxDefaultPosition, wxSize(-1, -1),
wxMINIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxCLOSE_BOX)
{
download_in_progress = NULL;
Centre();
mwindow = NULL;
toplevel = new wxFlexGridSizer(1, 2, 0, 0);
@ -953,6 +986,7 @@ wxwin_mainwindow::wxwin_mainwindow()
menu_start_sub(wxT("Load"));
menu_entry(wxID_LOAD_STATE, wxT("State..."));
menu_entry(wxID_LOAD_MOVIE, wxT("Movie..."));
menu_entry(wxID_DOWNLOAD, wxT("Download movie..."));
if(loaded_library::call_library() != "") {
menu_separator();
menu_entry(wxID_LOAD_LIBRARY, towxstring(std::string("Load ") + loaded_library::call_library()));
@ -1133,6 +1167,10 @@ std::u32string read_variable_map(const std::map<std::string, std::u32string>& va
void wxwin_mainwindow::update_statusbar(const std::map<std::string, std::u32string>& vars)
{
if(download_in_progress) {
statusbar->SetStatusText(download_in_progress->statusmsg());
return;
}
if(hashing_in_progress) {
//TODO: Display this as a dialog.
std::ostringstream s;
@ -1614,6 +1652,17 @@ void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent& e)
d->Destroy();
chdir(path.c_str());
messages << "Changed working directory to '" << path << "'" << std::endl;
return;
}
case wxID_DOWNLOAD: {
if(download_in_progress) return;
filename = pick_text(this, "Download movie", "Enter URL to download");
download_in_progress = new file_download();
download_in_progress->url = filename;
download_in_progress->target_slot = "wxwidgets_download_tmp";
download_in_progress->do_async();
new download_timer(this);
return;
}
};
}