Movie downloading
This commit is contained in:
parent
2a14872ae7
commit
d91b20d430
8 changed files with 295 additions and 0 deletions
35
include/core/filedownload.hpp
Normal file
35
include/core/filedownload.hpp
Normal 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
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
170
src/core/filedownload.cpp
Normal 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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue