diff --git a/.gitattributes b/.gitattributes
index 1525bf90fe3..4cc8dc2c87f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -8971,5 +8971,7 @@ web/index.html svneol=native#text/html
web/info.html svneol=native#text/html
web/js/jquery.js svneol=native#text/javascript
web/js/jquery.mobile.js svneol=native#text/javascript
+web/loadstate.html svneol=native#text/plain
web/logs.html svneol=native#text/html
web/options.html svneol=native#text/html
+web/savestate.html svneol=native#text/plain
diff --git a/src/emu/webengine.c b/src/emu/webengine.c
index 3e5e7bc8355..3fcb71970cb 100644
--- a/src/emu/webengine.c
+++ b/src/emu/webengine.c
@@ -76,6 +76,40 @@ static void get_qsvar(const struct mg_request_info *request_info,
mg_get_var(qs, strlen(qs == NULL ? "" : qs), name, dst, dst_len);
}
+char* websanitize_statefilename ( char* unsanitized )
+{
+ // It's important that we remove any dangerous characters from any filename
+ // we receive from a web client. This can be a serious security hole.
+ // As MAME/MESS policy is lowercase filenames, also lowercase it.
+
+ char* sanitized = new char[64];
+ int insertpoint =0;
+ char charcompare;
+
+ while (*unsanitized != 0)
+ {
+ charcompare = *unsanitized;
+ // ASCII 48-57 are 0-9
+ // ASCII 97-122 are lowercase A-Z
+
+ if ((charcompare >= 48 && charcompare <= 57) || (charcompare >= 97 && charcompare <= 122))
+ {
+ sanitized[insertpoint] = charcompare;
+ insertpoint++;
+ sanitized[insertpoint] = '\0'; // Make sure we're null-terminated.
+ }
+ // ASCII 65-90 are uppercase A-Z. These need to be lowercased.
+ if (charcompare >= 65 && charcompare <= 90)
+ {
+ sanitized[insertpoint] = tolower(charcompare); // Lowercase it
+ insertpoint++;
+ sanitized[insertpoint] = '\0'; // Make sure we're null-terminated.
+ }
+ unsanitized++;
+ }
+ return (sanitized);
+}
+
int web_engine::json_game_handler(struct mg_connection *conn)
{
Json::Value data;
@@ -189,6 +223,25 @@ int web_engine::begin_request_handler(struct mg_connection *conn)
else
m_machine->pause();
}
+ else if(!strcmp(cmd_name,"savestate"))
+ {
+ char cmd_val[64];
+ get_qsvar(request_info, "val", cmd_val, sizeof(cmd_val));
+ char *filename = websanitize_statefilename(cmd_val);
+ m_machine->schedule_save(filename);
+ }
+ else if(!strcmp(cmd_name,"loadstate"))
+ {
+ char cmd_val[64];
+ get_qsvar(request_info, "val", cmd_val, sizeof(cmd_val));
+ char *filename = cmd_val;
+ m_machine->schedule_load(filename);
+ }
+ else if(!strcmp(cmd_name,"loadauto"))
+ {
+ // This is here to just load the autosave and only the autosave.
+ m_machine->schedule_load("auto");
+ }
// Send HTTP reply to the client
mg_printf(conn,
diff --git a/web/commands.html b/web/commands.html
index 53fb2a3f7fe..3fc3fb7822a 100644
--- a/web/commands.html
+++ b/web/commands.html
@@ -1,4 +1,6 @@
Toggle Pause
Soft reset
Hard reset
+Save State
+Load State
Exit
diff --git a/web/index.html b/web/index.html
index f315485b1ef..554f13c2ecb 100644
--- a/web/index.html
+++ b/web/index.html
@@ -170,3 +170,4 @@