Remove web server
This commit is contained in:
parent
4a0fc7ae18
commit
04866122c5
|
@ -503,7 +503,6 @@ set(SRC_CORE
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/boards/vrc7p.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/yoko.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/inx007t.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/RNBW/mongoose.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/RNBW/pping.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/rainbow_esp.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/rainbow13.cpp
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -16,7 +16,8 @@
|
|||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
|
||||
//Note: do not include UDP networking, mongoose.h should have done it correctly taking care of defining portability macros
|
||||
// UDP networking
|
||||
#include <winsock2.h>
|
||||
|
||||
// Compatibility hacks
|
||||
typedef SSIZE_T ssize_t;
|
||||
|
@ -30,6 +31,7 @@ typedef SSIZE_T ssize_t;
|
|||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Compatibility hacks
|
||||
|
@ -65,31 +67,6 @@ uint64_t wall_clock_milli() {
|
|||
|
||||
namespace {
|
||||
|
||||
std::vector<uint8> readHostFile(std::string const& file_path) {
|
||||
// Open file
|
||||
std::ifstream ifs(file_path, std::ifstream::binary);
|
||||
if (!ifs) {
|
||||
throw std::runtime_error("unable to open file");
|
||||
}
|
||||
|
||||
// Get file length
|
||||
ifs.seekg(0, ifs.end);
|
||||
size_t const file_length = ifs.tellg();
|
||||
if (ifs.fail()) {
|
||||
throw std::runtime_error("unable to get file length");
|
||||
}
|
||||
ifs.seekg(0, ifs.beg);
|
||||
|
||||
// Read file
|
||||
std::vector<uint8> data(file_length);
|
||||
ifs.read(reinterpret_cast<char*>(data.data()), file_length);
|
||||
if (ifs.fail()) {
|
||||
throw std::runtime_error("error while reading file");
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::array<std::string, NUM_FILE_PATHS> dir_names = { "save", "roms", "user" };
|
||||
|
||||
}
|
||||
|
@ -109,25 +86,6 @@ BrokeStudioFirmware::BrokeStudioFirmware() {
|
|||
port_iss >> this->server_settings_port;
|
||||
this->default_server_settings_port = this->server_settings_port;
|
||||
|
||||
// Start web server
|
||||
this->httpd_run = true;
|
||||
char const* httpd_port = ::getenv("RAINBOW_WWW_PORT");
|
||||
if (httpd_port == nullptr) httpd_port = "8080";
|
||||
mg_mgr_init(&this->mgr, reinterpret_cast<void*>(this));
|
||||
UDBG("Starting web server on port %s\n", httpd_port);
|
||||
this->nc = mg_bind(&this->mgr, httpd_port, BrokeStudioFirmware::httpdEvent);
|
||||
if (this->nc == NULL) {
|
||||
printf("Failed to create web server\n");
|
||||
} else {
|
||||
mg_set_protocol_http_websocket(this->nc);
|
||||
this->httpd_thread = std::thread([this] {
|
||||
while (this->httpd_run) {
|
||||
mg_mgr_poll(&this->mgr, 1000);
|
||||
}
|
||||
mg_mgr_free(&this->mgr);
|
||||
});
|
||||
}
|
||||
|
||||
// Init fake registered networks
|
||||
this->networks = { {
|
||||
{"FCEUX_SSID", "FCEUX_PASS", true},
|
||||
|
@ -150,11 +108,6 @@ BrokeStudioFirmware::~BrokeStudioFirmware() {
|
|||
|
||||
this->closeConnection();
|
||||
|
||||
this->httpd_run = false;
|
||||
if (this->httpd_thread.joinable()) {
|
||||
this->httpd_thread.join();
|
||||
}
|
||||
|
||||
this->cleanupDownload();
|
||||
}
|
||||
|
||||
|
@ -1767,642 +1720,6 @@ std::pair<bool, sockaddr_in> BrokeStudioFirmware::resolve_server_address() {
|
|||
return std::make_pair(true, addr);
|
||||
}
|
||||
|
||||
void BrokeStudioFirmware::httpdEvent(mg_connection *nc, int ev, void *ev_data) {
|
||||
BrokeStudioFirmware* self = reinterpret_cast<BrokeStudioFirmware*>(nc->mgr->user_data);
|
||||
auto send_message = [&] (int status_code, char const * body, char const * mime) {
|
||||
std::string header = std::string("Content-Type: ") + mime + "\r\nConnection: close\r\n";
|
||||
mg_send_response_line(nc, status_code, header.c_str());
|
||||
mg_printf(nc, body);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
};
|
||||
auto send_generic_error = [&] {
|
||||
send_message(200, "{\"success\":\"false\"}\n", "application/json");
|
||||
};
|
||||
if (ev == MG_EV_HTTP_REQUEST) {
|
||||
UDBG("http request event \n");
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
UDBG(" uri: %.*s\n", static_cast<int>(std::max<size_t>(0, std::min<size_t>(INT_MAX, hm->uri.len))), hm->uri.p);
|
||||
if (std::string("/api/esp/status") == std::string(hm->uri.p, hm->uri.len)) {
|
||||
if (mg_vcasecmp(&hm->method, "GET") == 0) {
|
||||
mg_send_response_line(nc, 200, "Content-Type: application/json\r\nConnection: close\r\n");
|
||||
mg_printf(nc, "{\"sd\":{\"isPresent\":%s}}\n", self->isSdCardFilePresent ? "true" : "false");
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
}
|
||||
}
|
||||
else if (std::string("/api/config") == std::string(hm->uri.p, hm->uri.len)) {
|
||||
if (mg_vcasecmp(&hm->method, "GET") == 0) {
|
||||
mg_send_response_line(nc, 200, "Content-Type: application/json\r\nConnection: close\r\n");
|
||||
mg_printf(nc, "{\"server\":{\"host\":\"%s\", \"port\":\"%u\"}}\n", self->server_settings_address.c_str(), self->server_settings_port);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
} else if (mg_vcasecmp(&hm->method, "POST") == 0) {
|
||||
char var_name[100], file_name[100];
|
||||
const char *chunk;
|
||||
size_t chunk_len, n1, n2;
|
||||
n1 = n2 = 0;
|
||||
while ((n2 = mg_parse_multipart(hm->body.p + n1, hm->body.len - n1, var_name, sizeof(var_name), file_name, sizeof(file_name), &chunk, &chunk_len)) > 0) {
|
||||
if (strcmp(var_name, "server_host") == 0) {
|
||||
self->server_settings_address = std::string(chunk, (int)chunk_len);
|
||||
}
|
||||
if (strcmp(var_name, "server_port") == 0) {
|
||||
self->server_settings_port = std::atoi(chunk);
|
||||
}
|
||||
n1 += n2;
|
||||
}
|
||||
mg_send_response_line(nc, 200, "Content-Type: application/json\r\nConnection: close\r\n");
|
||||
mg_printf(nc, "{\"success\":\"true\", \"config\":{\"server\":{\"host\":\"%s\", \"port\":\"%u\"}}}\n", self->server_settings_address.c_str(), self->server_settings_port);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
} else {
|
||||
send_generic_error();
|
||||
}
|
||||
}
|
||||
else if (std::string("/api/esp/advancedconfig") == std::string(hm->uri.p, hm->uri.len)) {
|
||||
if (mg_vcasecmp(&hm->method, "GET") == 0) {
|
||||
mg_send_response_line(nc, 200, "Content-Type: application/json\r\nConnection: close\r\n");
|
||||
mg_printf(nc, "{\"debugConfig\":\"%u\"}", self->debug_config);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
} else if (mg_vcasecmp(&hm->method, "POST") == 0) {
|
||||
char var_name[100], file_name[100];
|
||||
const char *chunk;
|
||||
size_t chunk_len, n1, n2;
|
||||
n1 = n2 = 0;
|
||||
while ((n2 = mg_parse_multipart(hm->body.p + n1, hm->body.len - n1, var_name, sizeof(var_name), file_name, sizeof(file_name), &chunk, &chunk_len)) > 0) {
|
||||
if (strcmp(var_name, "debugConfig") == 0) {
|
||||
self->debug_config = *chunk - '0';
|
||||
send_message(200, "{\"success\":\"true\"}\n", "application/json");
|
||||
break;
|
||||
}
|
||||
n1 += n2;
|
||||
}
|
||||
}else {
|
||||
send_generic_error();
|
||||
}
|
||||
}
|
||||
else if (std::string("/api/file/list") == std::string(hm->uri.p, hm->uri.len)) {
|
||||
if (mg_vcasecmp(&hm->method, "GET") == 0) {
|
||||
char _drive[256];
|
||||
int const drive_len = mg_get_http_var(&hm->query_string, "drive", _drive, 256);
|
||||
if (drive_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
char _path[256];
|
||||
int const path_len = mg_get_http_var(&hm->query_string, "path", _path, 256);
|
||||
if (path_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
|
||||
mg_send_response_line(nc, 200, "Content-Type: application/json\r\nConnection: close\r\n");
|
||||
mg_printf(nc, "[");
|
||||
|
||||
int slash_pos;
|
||||
int id = 0;
|
||||
int drive = std::atoi(_drive);
|
||||
|
||||
std::vector<std::string> path_list;
|
||||
|
||||
std::string label = ""; // Dir: b | File: esp8266.txt
|
||||
std::string path = "/"; // Dir: /a | File: /a/b
|
||||
std::string filename = ""; // Dir: b | File: esp8266.txt
|
||||
std::string fullname = ""; // Dir: /a/b | File: /a/b/esp8266.txt
|
||||
|
||||
std::string full_path = "";
|
||||
std::string request_path = std::string(_path);
|
||||
|
||||
// Need to go up?
|
||||
if (request_path != "/")
|
||||
{
|
||||
request_path = request_path + "/";
|
||||
std::string parent_path = "/";
|
||||
slash_pos = request_path.find_last_of("/", request_path.size() - 2);
|
||||
if (slash_pos != 0)
|
||||
{
|
||||
parent_path = request_path.substr(0, slash_pos);
|
||||
}
|
||||
mg_printf(
|
||||
nc,
|
||||
"{\"id\":\"%d\",\"type\":\"dir\",\"label\":\"..\",\"path\":\"%s\",\"filename\":\"..\",\"fullname\":\"%s\",\"size\":-1}",
|
||||
id, parent_path.c_str(), parent_path.c_str()
|
||||
);
|
||||
id++;
|
||||
}
|
||||
|
||||
// Loop through files
|
||||
for (size_t i = 0; i < self->files.size(); ++i) {
|
||||
if (self->files.at(i).drive == drive) {
|
||||
|
||||
fullname = self->files.at(i).filename;
|
||||
|
||||
// In requested path?
|
||||
std::string tmp = fullname.substr(0, request_path.size());
|
||||
if (tmp != request_path)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// File or Dir?
|
||||
slash_pos = fullname.find("/", request_path.size() + 1);
|
||||
if (slash_pos == -1)
|
||||
{
|
||||
// File
|
||||
|
||||
// Split file path and file name
|
||||
slash_pos = fullname.find_last_of("/");
|
||||
path = fullname.substr(0, slash_pos + 1);
|
||||
filename = fullname.substr(slash_pos + 1);
|
||||
label = filename;
|
||||
|
||||
if (id != 0) {
|
||||
mg_printf(nc, ",");
|
||||
}
|
||||
|
||||
mg_printf(
|
||||
nc,
|
||||
"{\"id\":%d,\"type\":\"file\",\"label\":\"%s\",\"path\":\"%s\",\"filename\":\"%s\",\"fullname\":\"%s\",\"size\":%lu}",
|
||||
id, label.c_str(), path.c_str(), filename.c_str(), fullname.c_str(), self->files.at(i).data.size()
|
||||
);
|
||||
id++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dir
|
||||
|
||||
slash_pos = fullname.find_first_of("/", request_path.size());
|
||||
path = fullname.substr(0, slash_pos);
|
||||
bool found = false;
|
||||
for (auto &i : path_list) {
|
||||
if (i == path) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
path_list.push_back(path);
|
||||
|
||||
slash_pos = path.find_last_of("/");
|
||||
filename = path.substr(slash_pos + 1);
|
||||
label = filename;
|
||||
|
||||
full_path = path;
|
||||
|
||||
slash_pos = path.find_last_of("/");
|
||||
path = path.substr(0, slash_pos + 1);
|
||||
if (path == "") path = "/";
|
||||
|
||||
if (id != 0) {
|
||||
mg_printf(nc, ",");
|
||||
}
|
||||
|
||||
mg_printf(
|
||||
nc,
|
||||
"{\"id\":%d,\"type\":\"dir\",\"label\":\"%s\",\"path\":\"%s\",\"filename\":\"%s\",\"fullname\":\"%s\",\"size\":-1}",
|
||||
id, label.c_str(), path.c_str(), filename.c_str(), full_path.c_str()
|
||||
);
|
||||
id++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mg_printf(nc, "]");
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
} else {
|
||||
send_generic_error();
|
||||
}
|
||||
}
|
||||
else if (std::string("/api/file/free") == std::string(hm->uri.p, hm->uri.len)) {
|
||||
if (mg_vcasecmp(&hm->method, "GET") == 0) {
|
||||
char drive[256];
|
||||
int const drive_len = mg_get_http_var(&hm->query_string, "drive", drive, 256);
|
||||
if (drive_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
char path[256];
|
||||
int const path_len = mg_get_http_var(&hm->query_string, "path", path, 256);
|
||||
if (path_len <= 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
mg_send_response_line(nc, 200, "Content-Type: application/json\r\nConnection: close\r\n");
|
||||
mg_printf(nc, "[");
|
||||
int drive_index = std::atoi(drive);
|
||||
int path_index = std::atoi(path);
|
||||
bool found = false;
|
||||
for (uint8_t file_index = 0; file_index < NUM_FILES; ++file_index) {
|
||||
std::string filename = self->getAutoFilename(path_index, file_index);
|
||||
int i = self->findFile(drive_index, filename);
|
||||
if (i == -1) {
|
||||
if (found) {
|
||||
mg_printf(nc, ",");
|
||||
}
|
||||
mg_printf(nc, "{\"id\":\"%d\",\"name\":\"%d\"}", file_index, file_index);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
mg_printf(nc, "]");
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
} else {
|
||||
send_generic_error();
|
||||
}
|
||||
}else if (std::string("/api/file/delete") == std::string(hm->uri.p, hm->uri.len)) {
|
||||
if (mg_vcasecmp(&hm->method, "POST") == 0) {
|
||||
char _drive[256];
|
||||
int const drive_len = mg_get_http_var(&hm->query_string, "drive", _drive, 256);
|
||||
if (drive_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
char _filename[256];
|
||||
int const filename_len = mg_get_http_var(&hm->query_string, "filename", _filename, 256);
|
||||
if (filename_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
std::string filename = std::string(_filename);
|
||||
int drive = atoi(_drive);
|
||||
int i = self->findFile(drive, filename);
|
||||
|
||||
UDBG("RAINBOW Web(self=%p) deleting file %s\n", self, filename.c_str());
|
||||
self->files.erase(self->files.begin() + i);
|
||||
self->saveFiles();
|
||||
send_message(200, "{\"success\":\"true\"}\n", "application/json");
|
||||
} else {
|
||||
send_generic_error();
|
||||
}
|
||||
|
||||
}else if (std::string("/api/file/rename") == std::string(hm->uri.p, hm->uri.len)) {
|
||||
if (mg_vcasecmp(&hm->method, "POST") == 0) {
|
||||
char _drive[256];
|
||||
int const drive_len = mg_get_http_var(&hm->query_string, "drive", _drive, 256);
|
||||
if (drive_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
char _new_drive[256];
|
||||
int const new_drive_len = mg_get_http_var(&hm->query_string, "newDrive", _new_drive, 256);
|
||||
if (new_drive_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
char _filename[256];
|
||||
int const filename_len = mg_get_http_var(&hm->query_string, "filename", _filename, 256);
|
||||
if (filename_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
char _new_filename[256];
|
||||
int const new_filename_len = mg_get_http_var(&hm->query_string, "newFilename", _new_filename, 256);
|
||||
if (new_filename_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
std::string filename = std::string(_filename);
|
||||
std::string new_filename = std::string(_new_filename);
|
||||
int drive = atoi(_drive);
|
||||
int new_drive = atoi(_new_drive);
|
||||
|
||||
int i = self->findFile(new_drive, new_filename);
|
||||
if (i != -1)
|
||||
{
|
||||
send_message(200, "{\"success\":false,\"message\":\"Destination file already exists.\"}\n", "application/json");
|
||||
return;
|
||||
}
|
||||
|
||||
i = self->findFile(drive, filename);
|
||||
if (i == -1)
|
||||
{
|
||||
send_message(200, "{\"success\":false,\"message\":\"Source file does not exist.\"}\n", "application/json");
|
||||
return;
|
||||
}
|
||||
|
||||
self->files.at(i).filename = new_filename;
|
||||
self->files.at(i).drive = new_drive;
|
||||
self->saveFiles();
|
||||
|
||||
send_message(200, "{\"success\":\"true\"}\n", "application/json");
|
||||
} else {
|
||||
send_generic_error();
|
||||
}
|
||||
}else if (std::string("/api/file/download") == std::string(hm->uri.p, hm->uri.len)) {
|
||||
if (mg_vcasecmp(&hm->method, "GET") == 0) {
|
||||
char _drive[256];
|
||||
int const drive_len = mg_get_http_var(&hm->query_string, "drive", _drive, 256);
|
||||
if (drive_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
char _filename[256];
|
||||
int const filename_len = mg_get_http_var(&hm->query_string, "filename", _filename, 256);
|
||||
if (filename_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
std::string filename = std::string(_filename);
|
||||
int drive = atoi(_drive);
|
||||
int i = self->findFile(drive, filename);
|
||||
|
||||
mg_send_response_line(
|
||||
nc, 200,
|
||||
"Content-Type: application/octet-stream\r\n"
|
||||
"Connection: close\r\n"
|
||||
);
|
||||
mg_send(nc, self->files.at(i).data.data(), self->files.at(i).data.size());
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
} else {
|
||||
send_generic_error();
|
||||
}
|
||||
}else if (std::string("/api/upload") == std::string(hm->uri.p, hm->uri.len)) {
|
||||
if (mg_vcasecmp(&hm->method, "POST") == 0) {
|
||||
char _drive[256];
|
||||
int const drive_len = mg_get_http_var(&hm->query_string, "drive", _drive, 256);
|
||||
if (drive_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
uint8 drive = atoi(_drive);
|
||||
|
||||
// Get boundary for multipart form in HTTP headers
|
||||
std::string multipart_boundary;
|
||||
{
|
||||
mg_str const * content_type = mg_get_http_header(hm, "Content-Type");
|
||||
if (content_type == NULL) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
static std::regex const content_type_regex("multipart/form-data; boundary=(.*)");
|
||||
std::smatch match;
|
||||
std::string content_type_str(content_type->p, content_type->len);
|
||||
if (!std::regex_match(content_type_str, match, content_type_regex)) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
assert(match.size() == 2);
|
||||
multipart_boundary = match[1];
|
||||
}
|
||||
|
||||
// Parse form parts
|
||||
std::map<std::string, std::string> params;
|
||||
{
|
||||
std::string body_str(hm->body.p, hm->body.len);
|
||||
std::string::size_type pos = 0;
|
||||
while (pos != std::string::npos) {
|
||||
// Find the parameter name
|
||||
std::string::size_type found_pos = body_str.find("form-data; name=\"", pos);
|
||||
if (found_pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
pos = found_pos + 17;
|
||||
found_pos = body_str.find('"', pos);
|
||||
if (found_pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
std::string const param_name = body_str.substr(pos, found_pos - pos);
|
||||
pos = found_pos;
|
||||
|
||||
// Find the begining of the body
|
||||
found_pos = body_str.find("\r\n\r\n", pos);
|
||||
if (found_pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
pos = found_pos + 4;
|
||||
|
||||
// Find the begining of the next delimiter
|
||||
found_pos = body_str.find("\r\n--" + multipart_boundary, pos);
|
||||
if (found_pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
std::string const param_value = body_str.substr(pos, found_pos - pos);
|
||||
pos = found_pos;
|
||||
|
||||
// Store parsed parameter
|
||||
params[param_name] = param_value;
|
||||
}
|
||||
}
|
||||
|
||||
// Process request
|
||||
std::map<std::string, std::string>::const_iterator filename = params.find("filename");
|
||||
std::map<std::string, std::string>::const_iterator file_data = params.find("file");
|
||||
|
||||
if (file_data == params.end() || filename == params.end()) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
|
||||
self->files.push_back(FileStruct({ drive, filename->second , std::vector<uint8>(file_data->second.begin(), file_data->second.end()) }));
|
||||
self->saveFiles();
|
||||
|
||||
UDBG("RAINBOW Web(self=%p) sucessfuly uploaded file %s\n", self, filename->second.c_str());
|
||||
|
||||
// Return something webbrowser friendly
|
||||
send_message(200, "{\"success\":\"true\"}\n", "application/json");
|
||||
} else {
|
||||
send_generic_error();
|
||||
}
|
||||
}else if (std::string("/api/file/format") == std::string(hm->uri.p, hm->uri.len)) {
|
||||
if (mg_vcasecmp(&hm->method, "POST") == 0) {
|
||||
char _drive[256];
|
||||
int const drive_len = mg_get_http_var(&hm->query_string, "drive", _drive, 256);
|
||||
if (drive_len < 0) {
|
||||
send_generic_error();
|
||||
return;
|
||||
}
|
||||
uint8 drive = atoi(_drive);
|
||||
self->clearFiles(drive);
|
||||
std::string str_drive;
|
||||
if (drive == 0) str_drive = "ESP Flash";
|
||||
else if (drive == 1) str_drive = "SD Card";
|
||||
UDBG("RAINBOW Web(self=%p) sucessfuly formatted file system %s\n", self, str_drive.c_str());
|
||||
|
||||
// Return something webbrowser friendly
|
||||
send_message(200, "{\"success\":\"true\"}\n", "application/json");
|
||||
}
|
||||
else {
|
||||
send_generic_error();
|
||||
}
|
||||
}else {
|
||||
char const* www_root = ::getenv("RAINBOW_WWW_ROOT");
|
||||
if (www_root == NULL) {
|
||||
std::string upload_form = R"""(
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Rainbow + FCEUX</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<main class="container">
|
||||
<h1>Rainbow + FCEUX</h1>
|
||||
<article>
|
||||
<h2>About</h2>
|
||||
<p>
|
||||
The Rainbow mapper offers the possibility to store files on the ESP
|
||||
embedded flash memory and/or the SD Card if present.
|
||||
</p>
|
||||
<p>
|
||||
This behaviour can be replicated with FCEUX by setting up some
|
||||
environment variables and the Rainbow Webapp.
|
||||
</p>
|
||||
<p>Also, a webapp is provided to help you browse those files.</p>
|
||||
</article>
|
||||
<article>
|
||||
<h2>Web server</h2>
|
||||
<p>
|
||||
If you can read this, it means that the web server is already running
|
||||
in FCEUX.
|
||||
</p>
|
||||
<p>Here's how you can change the port used for the web server:</p>
|
||||
<ol>
|
||||
<li>
|
||||
Create a environment variable called <kbd>RAINBOW_WWW_PORT</kbd>
|
||||
</li>
|
||||
<li>Set its value to the port you want to use</li>
|
||||
<li>You may need to restart your computer and/or FCEUX</li>
|
||||
</ol>
|
||||
</article>
|
||||
<article>
|
||||
<h2>File system</h2>
|
||||
<p>
|
||||
If you want to set up default value for your game server, you can
|
||||
create two environment variables as follows.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<kbd>RAINBOW_ESP_FILESYSTEM_FILE</kbd> defines the file used to save
|
||||
ESP Flash content
|
||||
</li>
|
||||
<li>
|
||||
<kbd>RAINBOW_SD_FILESYSTEM_FILE</kbd> defines the file used to save
|
||||
SD card content
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article>
|
||||
<h2>Webapp</h2>
|
||||
<ol>
|
||||
<li>
|
||||
Download the Webapp zipfile
|
||||
<a href="fceux-rainbow-webapp.zip"><strong>here</strong></a>
|
||||
</li>
|
||||
<li>Unzip the file where you want</li>
|
||||
<li>
|
||||
Create a environment variable called <kbd>RAINBOW_WWW_ROOT</kbd>
|
||||
</li>
|
||||
<li>
|
||||
Set its value to the absolute path to the folder containing the
|
||||
files you unzipped
|
||||
</li>
|
||||
<li>You may need to restart your computer and/or FCEUX</li>
|
||||
</ol>
|
||||
</article>
|
||||
<article>
|
||||
<h2>Game server</h2>
|
||||
<p>
|
||||
If you want to set up default values for your game server, you can
|
||||
create two environment variables as follows.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<kbd>RAINBOW_SERVER_ADDR</kbd> defines the server host/IP address
|
||||
</li>
|
||||
<li><kbd>RAINBOW_SERVER_PORT</kbd> defines the server port</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article>
|
||||
<h2>Need help?</h2>
|
||||
<p>
|
||||
If you need help or if you found an issue or a bug, feel free to
|
||||
contact us:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Mail:
|
||||
<a href="mailto:contact@brokestudio.fr">contact@brokestudio.fr</a>
|
||||
</li>
|
||||
<li>
|
||||
Discord:
|
||||
<a href="http://discord.gg/FffVMAuhTX"
|
||||
>http://discord.gg/FffVMAuhTX</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
Twitter:
|
||||
<a href="https://twitter.com/Broke_Studio"
|
||||
>https://twitter.com/Broke_Studio</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
<small
|
||||
>© <a href="https://brokestudio.fr">Broke Studio</a> • Page
|
||||
built with <a href="https://picocss.com">Pico</a>
|
||||
</small>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
)""";
|
||||
send_message(200, upload_form.c_str(), "text/html");
|
||||
}else {
|
||||
// Translate url path to a file path on disk
|
||||
assert(hm->uri.len > 0); // Impossible as HTTP header are constructed in such a way that there is always at least one char in path (I think)
|
||||
std::string uri(hm->uri.p+1, hm->uri.len-1); // remove leading "/"
|
||||
if (uri.empty()) {
|
||||
uri = "index.html";
|
||||
}
|
||||
|
||||
// Try to serve requested file, if not found, then try to serve index.html
|
||||
std::string file_path = "";
|
||||
try {
|
||||
file_path = std::string(www_root) + uri;
|
||||
std::vector<uint8> contents = readHostFile(file_path);
|
||||
}catch (std::runtime_error const& e) {
|
||||
try {
|
||||
file_path = std::string(www_root) + "index.html";
|
||||
std::vector<uint8> contents = readHostFile(file_path);
|
||||
}catch (std::runtime_error const& e) {
|
||||
std::string message(
|
||||
"<html><body><h1>404</h1><p>" +
|
||||
file_path +
|
||||
"</p><p>" +
|
||||
e.what() +
|
||||
"</p></body></html>"
|
||||
);
|
||||
send_message(
|
||||
404,
|
||||
message.c_str(),
|
||||
"text/html"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Serve file
|
||||
std::vector<uint8> contents = readHostFile(file_path);
|
||||
// Basic / naive mime type handler
|
||||
std::string ext = file_path.substr(file_path.find_last_of(".") + 1);
|
||||
std::string mime_type = "";
|
||||
if (ext == "html") mime_type = "text/html";
|
||||
else if (ext == "css") mime_type = "text/css";
|
||||
else if (ext == "js") mime_type = "application/javascript";
|
||||
else mime_type = "application/octet-stream";
|
||||
std::string content_type(
|
||||
"Content-Type: " + mime_type + "\r\n" +
|
||||
"Connection: close\r\n"
|
||||
);
|
||||
mg_send_response_line(
|
||||
nc, 200, content_type.c_str()
|
||||
);
|
||||
mg_send(nc, contents.data(), contents.size());
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
size_t download_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) {
|
||||
std::vector<uint8>* data = reinterpret_cast<std::vector<uint8>*>(userdata);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "../types.h"
|
||||
|
||||
#include "RNBW/mongoose.h"
|
||||
#include "RNBW/bootrom_chr.h"
|
||||
|
||||
#define CURL_STATICLIB
|
||||
|
@ -16,6 +15,12 @@
|
|||
|
||||
#include "esp.h"
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////
|
||||
// BrokeStudio's ESP firmware implementation
|
||||
|
||||
|
@ -281,8 +286,6 @@ private:
|
|||
std::pair<bool, sockaddr_in> resolve_server_address();
|
||||
static std::deque<uint8> read_socket(int socket);
|
||||
|
||||
static void httpdEvent(mg_connection *nc, int ev, void *ev_data);
|
||||
|
||||
void initDownload();
|
||||
static std::pair<uint8, uint8> curle_to_net_error(CURLcode curle);
|
||||
void downloadFile(std::string const& url, uint8_t path, uint8_t file);
|
||||
|
@ -321,11 +324,6 @@ private:
|
|||
|
||||
int tcp_socket = -1;
|
||||
|
||||
mg_mgr mgr;
|
||||
mg_connection *nc = nullptr;
|
||||
std::atomic<bool> httpd_run;
|
||||
std::thread httpd_thread;
|
||||
|
||||
bool msg_first_byte = true;
|
||||
uint8 msg_length = 0;
|
||||
uint8 last_byte_read = 0;
|
||||
|
|
|
@ -508,7 +508,6 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)"</Command>
|
|||
<ClCompile Include="..\src\boards\rainbow2.cpp" />
|
||||
<ClCompile Include="..\src\boards\rainbow_esp.cpp" />
|
||||
<ClCompile Include="..\src\boards\rainbow13.cpp" />
|
||||
<ClCompile Include="..\src\boards\RNBW\mongoose.c" />
|
||||
<ClCompile Include="..\src\boards\RNBW\pping.c" />
|
||||
<ClCompile Include="..\src\boards\rt-01.cpp" />
|
||||
<ClCompile Include="..\src\boards\sa-9602b.cpp" />
|
||||
|
@ -1025,7 +1024,6 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)"</Command>
|
|||
<ItemGroup>
|
||||
<ClInclude Include="..\src\asm.h" />
|
||||
<ClInclude Include="..\src\boards\RNBW\esp.h" />
|
||||
<ClInclude Include="..\src\boards\RNBW\mongoose.h" />
|
||||
<ClInclude Include="..\src\boards\RNBW\pping.h" />
|
||||
<ClInclude Include="..\src\cart.h" />
|
||||
<ClInclude Include="..\src\cheat.h" />
|
||||
|
|
|
@ -1133,9 +1133,6 @@
|
|||
<ClCompile Include="..\src\boards\rainbow13.cpp">
|
||||
<Filter>boards</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\boards\RNBW\mongoose.c">
|
||||
<Filter>boards</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\boards\RNBW\pping.c">
|
||||
<Filter>boards</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1654,9 +1651,6 @@
|
|||
<ClInclude Include="..\src\boards\RNBW\esp.h">
|
||||
<Filter>boards</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\boards\RNBW\mongoose.h">
|
||||
<Filter>boards</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\boards\RNBW\pping.h">
|
||||
<Filter>boards</Filter>
|
||||
</ClInclude>
|
||||
|
|
Loading…
Reference in New Issue