diff --git a/src/blua/liolib.c b/src/blua/liolib.c
index 6ebde08a364b37fc6c18138b8c6772e93294c155..dc425f3c09cdcedce7f8dcb2e976c89531b3608a 100644
--- a/src/blua/liolib.c
+++ b/src/blua/liolib.c
@@ -137,6 +137,14 @@ static int aux_close (lua_State *L) {
 }
 
 
+static int io_close (lua_State *L) {
+  if (lua_isnone(L, 1))
+    lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);
+  tofile(L);  /* make sure argument is a file */
+  return aux_close(L);
+}
+
+
 static int io_gc (lua_State *L) {
   FILE *f = *tofilep(L);
   /* ignore closed files */
@@ -172,14 +180,11 @@ void MakePathDirs(char *path)
 }
 
 
-static int io_open (lua_State *L) {
-	FILE **pf;
-	const char *filename = luaL_checkstring(L, 1);
+static int CheckFileName(lua_State *L, const char *filename)
+{
+	int length = strlen(filename);
 	boolean pass = false;
 	size_t i;
-	int length = strlen(filename);
-	const char *mode = luaL_optstring(L, 2, "r");
-	luafiletransfer_t *filetransfer;
 
 	if (strchr(filename, '\\'))
 	{
@@ -202,67 +207,60 @@ static int io_open (lua_State *L) {
 		return pushresult(L,0,filename);
 	}
 
-	luaL_checktype(L, 4, LUA_TFUNCTION);
+	return 0;
+}
 
-	if (lua_isnil(L, 3) && (strchr(mode, 'r') || strchr(mode, '+'))) // Synched reading
-	{
-		AddLuaFileTransfer(filename, mode);
+static int io_open (lua_State *L) {
+	const char *filename = luaL_checkstring(L, 1);
+	const char *mode = luaL_optstring(L, 2, "r");
+	int checkresult;
 
-		/*pf = newfile(L);
-		*pf = fopen(realfilename, mode);
-		return (*pf == NULL) ? pushresult(L, 0, filename) : 1;*/
-	}
-	else // Local I/O
-	{
-		char *realfilename = va("%s" PATHSEP "%s", luafiledir, filename);
-		player_t *player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER));
+	checkresult = CheckFileName(L, filename);
+	if (checkresult)
+		return checkresult;
 
-		if (!player)
-			return LUA_ErrInvalid(L, "player_t");
+	luaL_checktype(L, 3, LUA_TFUNCTION);
 
-		if (player != &players[consoleplayer])
-			return 0;
+	if (!(strchr(mode, 'r') || strchr(mode, '+')))
+		luaL_error(L, "open() is only for reading, use openlocal() for writing");
 
-		if (client && strnicmp(filename, "shared/", strlen("shared/")))
-			I_Error("Access denied to %s\n"
-					"Clients can only access files stored in luafiles/shared/\n",
-					filename);
+	AddLuaFileTransfer(filename, mode);
 
-		// Prevent access if the file is being downloaded
-		for (filetransfer = luafiletransfers; filetransfer; filetransfer = filetransfer->next)
-			if (!stricmp(filetransfer->filename, filename))
-				I_Error("Access denied to %s\n"
-						"Files can't be opened while being downloaded\n",
-						filename);
+	return 0;
+}
 
-		MakePathDirs(realfilename);
 
-		// The callback is the last argument, no need to push it again
+static int io_openlocal (lua_State *L) {
+	FILE **pf;
+	const char *filename = luaL_checkstring(L, 1);
+	const char *mode = luaL_optstring(L, 2, "r");
+	luafiletransfer_t *filetransfer;
+	int checkresult;
 
-		// Push the first argument (file handle) on the stack
-		pf = newfile(gL); // Create and push the file handle
-		*pf = fopen(realfilename, mode); // Open the file
-		if (!*pf)
-		{
-			lua_pop(gL, 1);
-			lua_pushnil(gL);
-		}
+	checkresult = CheckFileName(L, filename);
+	if (checkresult)
+		return checkresult;
 
-		// Push the second argument (file name) on the stack
-		lua_pushstring(gL, filename);
+	char *realfilename = va("%s" PATHSEP "%s", luafiledir, filename);
 
-		// Call the callback
-		LUA_Call(gL, 2);
+	if (client && strnicmp(filename, "shared/", strlen("shared/")))
+		I_Error("Access denied to %s\n"
+		        "Clients can only access files stored in luafiles/shared/\n",
+		        filename);
 
-		// Close the file
-		if (*pf)
-		{
-			fclose(*pf);
-			*pf = NULL;
-		}
-	}
+	// Prevent access if the file is being downloaded
+	for (filetransfer = luafiletransfers; filetransfer; filetransfer = filetransfer->next)
+		if (!stricmp(filetransfer->filename, filename))
+			I_Error("Access denied to %s\n"
+			        "Files can't be opened while being downloaded\n",
+			        filename);
 
-	return 0; // !!! Todo: error handling?
+	MakePathDirs(realfilename);
+
+	// Open and return the file
+	pf = newfile(L);
+	*pf = fopen(realfilename, mode);
+	return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
 }
 
 
@@ -335,7 +333,7 @@ void Got_LuaFile(UINT8 **cp, INT32 playernum)
 void StoreLuaFileCallback(INT32 id)
 {
 	lua_pushfstring(gL, FMT_FILECALLBACKID, id);
-	lua_pushvalue(gL, 4); // Parameter 4 is the callback
+	lua_pushvalue(gL, 3); // Parameter 3 is the callback
 	lua_settable(gL, LUA_REGISTRYINDEX); // registry[callbackid] = callback
 }
 
@@ -347,6 +345,7 @@ void RemoveLuaFileCallback(INT32 id)
 	lua_settable(gL, LUA_REGISTRYINDEX); // registry[callbackid] = nil
 }
 
+
 static int io_tmpfile (lua_State *L) {
   FILE **pf = newfile(L);
   *pf = tmpfile();
@@ -649,10 +648,12 @@ static int f_flush (lua_State *L) {
 
 
 static const luaL_Reg iolib[] = {
+  {"close", io_close},
   {"flush", io_flush},
   {"input", io_input},
   {"lines", io_lines},
   {"open", io_open},
+  {"openlocal", io_openlocal},
   {"output", io_output},
   {"read", io_read},
   {"tmpfile", io_tmpfile},
@@ -663,6 +664,7 @@ static const luaL_Reg iolib[] = {
 
 
 static const luaL_Reg flib[] = {
+  {"close", io_close},
   {"flush", f_flush},
   {"lines", f_lines},
   {"read", f_read},