From f906959a09d58c85d87b445fd1791d91bf278bfa Mon Sep 17 00:00:00 2001
From: Ben Longbons <b.r.longbons@gmail.com>
Date: Fri, 15 Nov 2013 17:51:29 -0800
Subject: Use new IO classes

---
 real.make                |   2 +-
 src/admin/ladmin.cpp     |  22 ++++----
 src/char/char.cpp        |  45 +++++++---------
 src/char/int_party.cpp   |  24 ++++-----
 src/char/int_storage.cpp |  21 ++++----
 src/char/inter.cpp       |  26 ++++-----
 src/common/cxxstdio.hpp  |  28 +++++-----
 src/common/md5calc.cpp   |   6 ++-
 src/common/md5calc.hpp   |   4 +-
 src/common/utils.cpp     |  14 ++---
 src/common/utils.hpp     |   4 +-
 src/common/utils2.hpp    |   8 +++
 src/login/login.cpp      | 136 ++++++++++++++++++++---------------------------
 src/map/atcommand.cpp    |  43 +++++++--------
 src/map/battle.cpp       |   9 ++--
 src/map/clif.cpp         |   9 ++--
 src/map/grfio.cpp        |   8 +--
 src/map/itemdb.cpp       |  11 ++--
 src/map/map.cpp          |  24 +++++----
 src/map/mob.cpp          |  16 +++---
 src/map/npc.cpp          |  35 ++++++------
 src/map/pc.cpp           |   9 ++--
 src/map/script.cpp       |  27 +++++-----
 src/map/skill.cpp        |  11 ++--
 src/monitor/main.cpp     |   9 ++--
 src/poison.hpp           |  14 +++++
 26 files changed, 272 insertions(+), 293 deletions(-)

diff --git a/real.make b/real.make
index 305f150..8d5cfb6 100644
--- a/real.make
+++ b/real.make
@@ -243,7 +243,7 @@ ifndef MAKE_RESTARTS
 obj/%.d: src/%.cpp
 	$(MKDIR_FIRST)
 	set -o pipefail; \
-	${CXX} ${CPPFLAGS} ${CXXFLAGS} -MG -MP -MM $< \
+	${CXX} ${CPPFLAGS} -DGENERATING_DEPENDENCIES ${CXXFLAGS} -MG -MP -MM $< \
 	    -MT '$(patsubst %.d,%.ii,$@) $(patsubst %.d,%.ll,$@) $(patsubst %.d,%.bc,$@) $(patsubst %.d,%.s,$@) $(patsubst %.d,%.o,$@) $@' \
 	    | sed -e ':again; s:/[^/ ]*/../:/:; t again' \
 	    -e 's: ${SRC_DIR}/: :g' \
diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp
index cf1169c..023aeae 100644
--- a/src/admin/ladmin.cpp
+++ b/src/admin/ladmin.cpp
@@ -5,19 +5,18 @@
 
 #include <cassert>
 
-#include <fstream>
-#include <iostream>
-
 #include "../strings/mstring.hpp"
 #include "../strings/fstring.hpp"
 #include "../strings/zstring.hpp"
 #include "../strings/xstring.hpp"
 #include "../strings/vstring.hpp"
 
+#include "../io/read.hpp"
+#include "../io/write.hpp"
+
 #include "../common/core.hpp"
 #include "../common/cxxstdio.hpp"
 #include "../common/human_time_diff.hpp"
-#include "../common/io.hpp"
 #include "../common/md5calc.hpp"
 #include "../common/mmo.hpp"
 #include "../common/socket.hpp"
@@ -257,11 +256,10 @@ void SessionDeleter::operator()(SessionData *)
 static
 void ladmin_log(XString line)
 {
-    FILE *logfp = fopen(ladmin_log_filename.c_str(), "a");
-    if (!logfp)
+    io::AppendFile logfp(ladmin_log_filename);
+    if (!logfp.is_open())
         return;
     log_with_timestamp(logfp, line);
-    fclose(logfp);
 }
 
 static
@@ -1779,13 +1777,15 @@ void prompt(void)
         fflush(stdout);
 
         // get command and parameter
+        // TODO figure out a better way to do stdio
+        static auto cin = make_unique<io::ReadFile>(dup(0));
         FString buf;
-        io::getline(std::cin, buf);
+        cin->getline(buf);
 
         Iprintf("\033[0m");
         fflush(stdout);
 
-        if (!std::cin)
+        if (!cin->is_open())
             exit(0);
 
         if (!buf.is_print())
@@ -2771,7 +2771,7 @@ int Connect_login_server(void)
 static
 int ladmin_config_read(ZString cfgName)
 {
-    std::ifstream in(cfgName.c_str());
+    io::ReadFile in(cfgName);
     if (!in.is_open())
     {
         PRINTF("\033[0mConfiguration file (%s) not found.\n", cfgName);
@@ -2781,7 +2781,7 @@ int ladmin_config_read(ZString cfgName)
     Iprintf("\033[0m---Start reading of Ladmin configuration file (%s)\n",
          cfgName);
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         XString w1;
         ZString w2;
diff --git a/src/char/char.cpp b/src/char/char.cpp
index 170e91a..a3b1a27 100644
--- a/src/char/char.cpp
+++ b/src/char/char.cpp
@@ -13,7 +13,6 @@
 #include <ctime>
 
 #include <bitset>
-#include <fstream>
 #include <set>
 
 #include "../strings/mstring.hpp"
@@ -21,13 +20,14 @@
 #include "../strings/zstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/lock.hpp"
+#include "../io/read.hpp"
+
 #include "../common/core.hpp"
 #include "../common/cxxstdio.hpp"
 #include "../common/db.hpp"
 #include "../common/extract.hpp"
 #include "../common/human_time_diff.hpp"
-#include "../common/io.hpp"
-#include "../common/lock.hpp"
 #include "../common/socket.hpp"
 #include "../common/timer.hpp"
 #include "../common/version.hpp"
@@ -162,11 +162,10 @@ pid_t pid = 0;                  // For forked DB writes
 //------------------------------
 void char_log(XString line)
 {
-    FILE *logfp = fopen(char_log_filename.c_str(), "a");
-    if (!logfp)
+    io::AppendFile logfp(char_log_filename, true);
+    if (!logfp.is_open())
         return;
     log_with_timestamp(logfp, line);
-    fclose(logfp);
 }
 
 //----------------------------------------------------------------------
@@ -436,7 +435,7 @@ int mmo_char_init(void)
     char_data.clear();
     online_chars.clear();
 
-    std::ifstream in(char_txt.c_str());
+    io::ReadFile in(char_txt);
     if (!in.is_open())
     {
         PRINTF("Characters file not found: %s.\n", char_txt);
@@ -448,7 +447,7 @@ int mmo_char_init(void)
 
     int line_count = 0;
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         line_count++;
 
@@ -494,9 +493,8 @@ int mmo_char_init(void)
 static
 void mmo_char_sync(void)
 {
-    int lock;
-    FILE *fp = lock_fopen(char_txt, &lock);
-    if (fp == NULL)
+    io::WriteLock fp(char_txt);
+    if (!fp.is_open())
     {
         PRINTF("WARNING: Server can't not save characters.\n");
         CHAR_LOG("WARNING: Server can't not save characters.\n");
@@ -507,11 +505,9 @@ void mmo_char_sync(void)
         for (mmo_charstatus& cd : char_data)
         {
             FString line = mmo_char_tostr(&cd);
-            fwrite(line.data(), 1, line.size(), fp);
-            fputc('\n', fp);
+            fp.put_line(line);
         }
         FPRINTF(fp, "%d\t%%newid%%\n", char_id_count);
-        lock_fclose(fp, char_txt, &lock);
     }
 }
 
@@ -725,14 +721,13 @@ static
 void create_online_files(void)
 {
     // write files
-    FILE *fp = fopen(online_txt_filename.c_str(), "w");
-    if (fp != NULL)
+    io::WriteFile fp(online_txt_filename);
+    if (fp.is_open())
     {
-        FILE *fp2 = fopen(online_html_filename.c_str(), "w");
-        if (fp2 != NULL)
+        io::WriteFile fp2(online_html_filename);
+        if (fp2.is_open())
         {
             // get time
-#warning "Need to convert/check the PHP code"
             timestamp_seconds_buffer timetemp;
             stamp_time(timetemp);
             // write heading
@@ -836,9 +831,7 @@ void create_online_files(void)
             }
             FPRINTF(fp2, "  </BODY>\n");
             FPRINTF(fp2, "</HTML>\n");
-            fclose(fp2);
         }
-        fclose(fp);
     }
 
     return;
@@ -1363,7 +1356,7 @@ void parse_tologin(int fd)
                 if (RFIFOREST(fd) < 6)
                     return;
                 // Deletion of all characters of the account
-#warning "This comment is a lie, but it's still true."
+//#warning "This comment is a lie, but it's still true."
                 // needs to use index because they may move during resize
                 for (int idx = 0; idx < char_data.size(); idx++)
                 {
@@ -2551,7 +2544,7 @@ int lan_config_read(ZString lancfgName)
     lan_map_ip = IP4_LOCALHOST;
     lan_subnet = IP4Mask(IP4_LOCALHOST, IP4_BROADCAST);
 
-    std::ifstream in(lancfgName.c_str());
+    io::ReadFile in(lancfgName);
 
     if (!in.is_open())
     {
@@ -2562,7 +2555,7 @@ int lan_config_read(ZString lancfgName)
     PRINTF("---start reading of Lan Support configuration...\n");
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         XString w1;
         ZString w2;
@@ -2626,7 +2619,7 @@ int char_config_read(ZString cfgName)
 {
     struct hostent *h = NULL;
 
-    std::ifstream in(cfgName.c_str());
+    io::ReadFile in(cfgName);
 
     if (!in.is_open())
     {
@@ -2635,7 +2628,7 @@ int char_config_read(ZString cfgName)
     }
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         XString w1;
         ZString w2;
diff --git a/src/char/int_party.cpp b/src/char/int_party.cpp
index 0597efd..41aa9c3 100644
--- a/src/char/int_party.cpp
+++ b/src/char/int_party.cpp
@@ -3,17 +3,16 @@
 #include <cstdlib>
 #include <cstring>
 
-#include <fstream>
-
 #include "../strings/mstring.hpp"
 #include "../strings/fstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/lock.hpp"
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
 #include "../common/db.hpp"
 #include "../common/extract.hpp"
-#include "../common/io.hpp"
-#include "../common/lock.hpp"
 #include "../common/mmo.hpp"
 #include "../common/socket.hpp"
 
@@ -105,14 +104,14 @@ bool extract(XString str, party *p)
 // パーティデータのロード
 int inter_party_init(void)
 {
-    std::ifstream in(party_txt.c_str());
+    io::ReadFile in(party_txt);
     if (!in.is_open())
         return 1;
 
     // TODO: convert to use char_id, and change to extract()
     FString line;
     int c = 0;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         int i, j = 0;
         if (SSCANF(line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0
@@ -144,19 +143,17 @@ int inter_party_init(void)
 
 // パーティーデータのセーブ用
 static
-void inter_party_save_sub(struct party *data, FILE *fp)
+void inter_party_save_sub(struct party *data, io::WriteFile& fp)
 {
     FString line = inter_party_tostr(data);
-    FPRINTF(fp, "%s\n", line);
+    fp.put_line(line);
 }
 
 // パーティーデータのセーブ
 int inter_party_save(void)
 {
-    FILE *fp;
-    int lock;
-
-    if ((fp = lock_fopen(party_txt, &lock)) == NULL)
+    io::WriteLock fp(party_txt);
+    if (!fp.is_open())
     {
         PRINTF("int_party: cant write [%s] !!! data is lost !!!\n",
                 party_txt);
@@ -164,9 +161,6 @@ int inter_party_save(void)
     }
     for (auto& pair : party_db)
         inter_party_save_sub(&pair.second, fp);
-//  FPRINTF(fp, "%d\t%%newid%%\n", party_newid);
-    lock_fclose(fp, party_txt, &lock);
-//  PRINTF("int_party: %s saved.\n", party_txt);
 
     return 0;
 }
diff --git a/src/char/int_storage.cpp b/src/char/int_storage.cpp
index 205b21e..304a792 100644
--- a/src/char/int_storage.cpp
+++ b/src/char/int_storage.cpp
@@ -4,17 +4,17 @@
 #include <cstring>
 
 #include <functional>
-#include <fstream>
 
 #include "../strings/mstring.hpp"
 #include "../strings/fstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/lock.hpp"
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
 #include "../common/db.hpp"
 #include "../common/extract.hpp"
-#include "../common/io.hpp"
-#include "../common/lock.hpp"
 #include "../common/mmo.hpp"
 #include "../common/socket.hpp"
 
@@ -104,7 +104,7 @@ int inter_storage_init(void)
 {
     int c = 0;
 
-    std::ifstream in(storage_txt.c_str());
+    io::ReadFile in(storage_txt);
     if (!in.is_open())
     {
         PRINTF("cant't read : %s\n", storage_txt);
@@ -112,7 +112,7 @@ int inter_storage_init(void)
     }
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         struct storage s {};
         if (extract(line, &s))
@@ -131,21 +131,20 @@ int inter_storage_init(void)
 }
 
 static
-void inter_storage_save_sub(struct storage *data, FILE *fp)
+void inter_storage_save_sub(struct storage *data, io::WriteFile& fp)
 {
     FString line = storage_tostr(data);
     if (line)
-        FPRINTF(fp, "%s\n", line);
+        fp.put_line(line);
 }
 
 //---------------------------------------------------------
 // 倉庫データを書き込む
 int inter_storage_save(void)
 {
-    FILE *fp;
-    int lock;
+    io::WriteLock fp(storage_txt);
 
-    if ((fp = lock_fopen(storage_txt, &lock)) == NULL)
+    if (!fp.is_open())
     {
         PRINTF("int_storage: cant write [%s] !!! data is lost !!!\n",
                 storage_txt);
@@ -153,8 +152,6 @@ int inter_storage_save(void)
     }
     for (auto& pair : storage_db)
         inter_storage_save_sub(&pair.second, fp);
-    lock_fclose(fp, storage_txt, &lock);
-//  PRINTF("int_storage: %s saved.\n",storage_txt);
     return 0;
 }
 
diff --git a/src/char/inter.cpp b/src/char/inter.cpp
index b951cc1..330ea66 100644
--- a/src/char/inter.cpp
+++ b/src/char/inter.cpp
@@ -4,7 +4,6 @@
 #include <cstdlib>
 #include <cstring>
 
-#include <fstream>
 #include <vector>
 
 #include "../strings/mstring.hpp"
@@ -12,11 +11,12 @@
 #include "../strings/zstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/lock.hpp"
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
 #include "../common/db.hpp"
 #include "../common/extract.hpp"
-#include "../common/io.hpp"
-#include "../common/lock.hpp"
 #include "../common/socket.hpp"
 #include "../common/timer.hpp"
 #include "../common/utils.hpp"
@@ -110,11 +110,11 @@ int inter_accreg_init(void)
 {
     int c = 0;
 
-    std::ifstream in(accreg_txt.c_str());
+    io::ReadFile in(accreg_txt);
     if (!in.is_open())
         return 1;
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         struct accreg reg {};
         if (extract(line, &reg))
@@ -134,13 +134,12 @@ int inter_accreg_init(void)
 
 // アカウント変数のセーブ用
 static
-void inter_accreg_save_sub(struct accreg *reg, FILE *fp)
+void inter_accreg_save_sub(struct accreg *reg, io::WriteFile& fp)
 {
     if (reg->reg_num > 0)
     {
         FString line = inter_accreg_tostr(reg);
-        fwrite(line.data(), 1, line.size(), fp);
-        fputc('\n', fp);
+        fp.put_line(line);
     }
 }
 
@@ -148,10 +147,8 @@ void inter_accreg_save_sub(struct accreg *reg, FILE *fp)
 static
 int inter_accreg_save(void)
 {
-    FILE *fp;
-    int lock;
-
-    if ((fp = lock_fopen(accreg_txt, &lock)) == NULL)
+    io::WriteLock fp(accreg_txt);
+    if (!fp.is_open())
     {
         PRINTF("int_accreg: cant write [%s] !!! data is lost !!!\n",
                 accreg_txt);
@@ -159,7 +156,6 @@ int inter_accreg_save(void)
     }
     for (auto& pair : accreg_db)
         inter_accreg_save_sub(&pair.second, fp);
-    lock_fclose(fp, accreg_txt, &lock);
 
     return 0;
 }
@@ -173,7 +169,7 @@ int inter_accreg_save(void)
 static
 int inter_config_read(ZString cfgName)
 {
-    std::ifstream in(cfgName.c_str());
+    io::ReadFile in(cfgName);
     if (!in.is_open())
     {
         PRINTF("file not found: %s\n", cfgName);
@@ -181,7 +177,7 @@ int inter_config_read(ZString cfgName)
     }
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         XString w1;
         ZString w2;
diff --git a/src/common/cxxstdio.hpp b/src/common/cxxstdio.hpp
index 89cc5de..1ae1be3 100644
--- a/src/common/cxxstdio.hpp
+++ b/src/common/cxxstdio.hpp
@@ -24,6 +24,8 @@
 #include <cstdarg>
 #include <cstdio>
 
+#include "../io/fwd.hpp"
+
 #include "const_array.hpp"
 #include "utils2.hpp"
 
@@ -211,28 +213,28 @@ namespace cxxstdio
     };
 
 #define XPRINTF(out, fmt, ...)                                      \
-    ([&]() -> int                                                   \
+    (/*[&]() -> int*/                                                   \
     {                                                               \
         struct format_impl                                          \
         {                                                           \
             constexpr static                                        \
             const char *print_format() { return fmt; }              \
         };                                                          \
-        return cxxstdio::PrintFormatter<format_impl>::print(out, ## __VA_ARGS__);   \
-    }())
+        /*return*/ cxxstdio::PrintFormatter<format_impl>::print(out, ## __VA_ARGS__);   \
+    }/*()*/)
 
 #define XSCANF(out, fmt, ...)                                   \
-    ([&]() -> int                                               \
+    (/*[&]() -> int*/                                               \
     {                                                           \
         struct format_impl                                      \
         {                                                       \
             constexpr static                                    \
             const char *scan_format() { return fmt; }           \
         };                                                      \
-        return cxxstdio::ScanFormatter<format_impl>::scan(out, ## __VA_ARGS__);     \
-    }())
+        /*return*/ cxxstdio::ScanFormatter<format_impl>::scan(out, ## __VA_ARGS__);     \
+    }/*()*/)
 
-#define FPRINTF(file, fmt, ...)     XPRINTF(no_cast<FILE *>(file), fmt, ## __VA_ARGS__)
+#define FPRINTF(file, fmt, ...)     XPRINTF(/*no_cast<FILE *>*/(file), fmt, ## __VA_ARGS__)
 #define FSCANF(file, fmt, ...)      XSCANF(no_cast<FILE *>(file), fmt, ## __VA_ARGS__)
 #define PRINTF(fmt, ...)            FPRINTF(stdout, fmt, ## __VA_ARGS__)
 #define SPRINTF(str, fmt, ...)      XPRINTF(base_cast<FString&>(str), fmt, ## __VA_ARGS__)
@@ -241,20 +243,20 @@ namespace cxxstdio
 #define SSCANF(str, fmt, ...)       XSCANF(/*ZString or compatible*/str, fmt, ## __VA_ARGS__)
 
 #define STRPRINTF(fmt, ...)                     \
-    ([&]() -> FString                           \
+    (/*[&]() -> FString*/                           \
     {                                           \
         FString _out_impl;                      \
         SPRINTF(_out_impl, fmt, ## __VA_ARGS__);\
-        return _out_impl;                       \
-    }())
+        /*return*/ _out_impl;                       \
+    }/*()*/)
 
 #define STRNPRINTF(n, fmt, ...)                     \
-    ([&]() -> VString<n - 1>                        \
+    (/*[&]() -> VString<n - 1>*/                        \
     {                                               \
         VString<n - 1> _out_impl;                   \
         SNPRINTF(_out_impl, n, fmt, ## __VA_ARGS__);\
-        return _out_impl;                           \
-    }())
+        /*return*/ _out_impl;                           \
+    }/*()*/)
 
 } // namespace cxxstdio
 
diff --git a/src/common/md5calc.cpp b/src/common/md5calc.cpp
index 1961fa6..70b0e1f 100644
--- a/src/common/md5calc.cpp
+++ b/src/common/md5calc.cpp
@@ -5,6 +5,8 @@
 #include "../strings/xstring.hpp"
 #include "../strings/vstring.hpp"
 
+#include "../io/read.hpp"
+
 #include "cxxstdio.hpp"
 #include "random.hpp"
 #include "utils.hpp"
@@ -234,7 +236,7 @@ MD5_state MD5_from_string(XString msg)
 
 // TODO - refactor MD5 into a stream, and merge the implementations
 // I once implemented an ostream that does it ...
-MD5_state MD5_from_FILE(FILE* in)
+MD5_state MD5_from_FILE(io::ReadFile& in)
 {
     uint64_t total_len = 0;
 
@@ -248,7 +250,7 @@ MD5_state MD5_from_FILE(FILE* in)
 
     while (true)
     {
-        size_t rv = fread(buf + block_len, 1, 0x40 - block_len, in);
+        size_t rv = in.get(sign_cast<char *>(buf + block_len), 0x40 - block_len);
         if (!rv)
             break;
         total_len += 8 * rv; // in bits
diff --git a/src/common/md5calc.hpp b/src/common/md5calc.hpp
index e44a7a7..205c21a 100644
--- a/src/common/md5calc.hpp
+++ b/src/common/md5calc.hpp
@@ -14,6 +14,8 @@
 #include "../strings/fwd.hpp"
 #include "../strings/vstring.hpp"
 
+#include "../io/fwd.hpp"
+
 #include "ip.hpp"
 #include "mmo.hpp"
 
@@ -43,7 +45,7 @@ void MD5_to_str(MD5_state state, md5_string& out);
 
 // Convenience
 MD5_state MD5_from_string(XString msg);
-MD5_state MD5_from_FILE(FILE* in);
+MD5_state MD5_from_FILE(io::ReadFile& in);
 
 
 // whoever wrote this fails basic understanding of
diff --git a/src/common/utils.cpp b/src/common/utils.cpp
index 383c711..0f8a0af 100644
--- a/src/common/utils.cpp
+++ b/src/common/utils.cpp
@@ -9,6 +9,8 @@
 #include "../strings/zstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/write.hpp"
+
 #include "cxxstdio.hpp"
 #include "extract.hpp"
 
@@ -106,18 +108,16 @@ void stamp_time(timestamp_milliseconds_buffer& out)
     out = stringish<timestamp_milliseconds_buffer>(const_(buf));
 }
 
-void log_with_timestamp(FILE *out, XString line)
+void log_with_timestamp(io::WriteFile& out, XString line)
 {
     if (!line)
     {
-        fputc('\n', out);
+        out.put('\n');
         return;
     }
     timestamp_milliseconds_buffer tmpstr;
     stamp_time(tmpstr);
-    fputs(tmpstr.c_str(), out);
-    fputs(": ", out);
-    fwrite(line.data(), 1, line.size(), out);
-    if (line.back() != '\n')
-        fputc('\n', out);
+    out.really_put(tmpstr.data(), tmpstr.size());
+    out.really_put(": ", 2);
+    out.put_line(line);
 }
diff --git a/src/common/utils.hpp b/src/common/utils.hpp
index b0ee2bc..4171759 100644
--- a/src/common/utils.hpp
+++ b/src/common/utils.hpp
@@ -11,6 +11,8 @@
 #include "../strings/fwd.hpp"
 #include "../strings/vstring.hpp"
 
+#include "../io/fwd.hpp"
+
 #include "const_array.hpp"
 #include "operators.hpp"
 #include "utils2.hpp"
@@ -117,7 +119,7 @@ struct timestamp_milliseconds_buffer : VString<23> {};
 void stamp_time(timestamp_seconds_buffer&, const TimeT *t=nullptr);
 void stamp_time(timestamp_milliseconds_buffer&);
 
-void log_with_timestamp(FILE *out, XString line);
+void log_with_timestamp(io::WriteFile& out, XString line);
 
 // TODO VString?
 #define TIMESTAMP_DUMMY "YYYY-MM-DD HH:MM:SS"
diff --git a/src/common/utils2.hpp b/src/common/utils2.hpp
index 978ae54..adcb465 100644
--- a/src/common/utils2.hpp
+++ b/src/common/utils2.hpp
@@ -284,4 +284,12 @@ T maybe_cast(U u)
     return u;
 }
 
+template<class T, class U>
+typename std::remove_pointer<T>::type *sign_cast(U *u)
+{
+    typedef typename std::remove_pointer<T>::type T_;
+    static_assert(sizeof(T_) == sizeof(U), "sign cast must be same size");
+    return reinterpret_cast<T_ *>(u);
+}
+
 #endif // UTILS2_HPP
diff --git a/src/login/login.cpp b/src/login/login.cpp
index 1460264..b93a698 100644
--- a/src/login/login.cpp
+++ b/src/login/login.cpp
@@ -11,7 +11,6 @@
 
 #include <algorithm>
 #include <array>
-#include <fstream>
 #include <set>
 #include <type_traits>
 
@@ -21,13 +20,14 @@
 #include "../strings/xstring.hpp"
 #include "../strings/vstring.hpp"
 
+#include "../io/lock.hpp"
+#include "../io/read.hpp"
+
 #include "../common/core.hpp"
 #include "../common/cxxstdio.hpp"
 #include "../common/db.hpp"
 #include "../common/extract.hpp"
 #include "../common/human_time_diff.hpp"
-#include "../common/io.hpp"
-#include "../common/lock.hpp"
 #include "../common/md5calc.hpp"
 #include "../common/mmo.hpp"
 #include "../common/random.hpp"
@@ -224,11 +224,10 @@ using e::VERSION_2;
 static
 void login_log(XString line)
 {
-    FILE *logfp = fopen(login_log_filename.c_str(), "a");
-    if (!logfp)
+    io::AppendFile logfp(login_log_filename);
+    if (!logfp.is_open())
         return;
     log_with_timestamp(logfp, line);
-    fclose(logfp);
 }
 
 //----------------------------------------------------------------------
@@ -250,8 +249,6 @@ uint8_t isGM(int account_id)
 static
 int read_gm_account(void)
 {
-    char line[512];
-    FILE *fp;
     int c = 0;
     int GM_level;
 
@@ -259,7 +256,8 @@ int read_gm_account(void)
 
     creation_time_GM_account_file = file_modified(gm_account_filename);
 
-    if ((fp = fopen(gm_account_filename.c_str(), "r")) == NULL)
+    io::ReadFile fp(gm_account_filename);
+    if (!fp.is_open())
     {
         PRINTF("read_gm_account: GM accounts file [%s] not found.\n",
                 gm_account_filename);
@@ -271,15 +269,17 @@ int read_gm_account(void)
     }
     // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???)
     // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows)
-    while (fgets(line, sizeof(line) - 1, fp) && c < 4000)
+    FString line;
+    while (fp.getline(line) && c < 4000)
     {
-        if ((line[0] == '/' && line[1] == '/') || line[0] == '\0' || line[0] == '\n')
+        if (!line)
+            continue;
+        if (line.startswith("//"))
             continue;
         GM_Account p {};
-        if (sscanf(line, "%d %hhu", &p.account_id, &p.level) != 2
-            && sscanf(line, "%d: %hhu", &p.account_id, &p.level) != 2)
-            PRINTF("read_gm_account: file [%s], invalid 'id_acount level' format.\n",
-                 gm_account_filename);
+        if (!extract(line, record<' '>(&p.account_id, &p.level)))
+            PRINTF("read_gm_account: file [%s], invalid 'id_acount level' format: '%s'\n",
+                 gm_account_filename, line);
         else if (p.level <= 0)
             PRINTF("read_gm_account: file [%s] %dth account (invalid level [0 or negative]: %d).\n",
                  gm_account_filename, c + 1, p.level);
@@ -316,7 +316,6 @@ int read_gm_account(void)
             }
         }
     }
-    fclose(fp);
 
     PRINTF("read_gm_account: file '%s' readed (%d GM accounts found).\n",
             gm_account_filename, c);
@@ -537,7 +536,7 @@ int mmo_auth_init(void)
 {
     int GM_count = 0;
 
-    std::ifstream in(account_filename.c_str());
+    io::ReadFile in(account_filename);
     if (!in.is_open())
     {
         // no account file -> no account -> no login, including char-server (ERROR)
@@ -547,7 +546,7 @@ int mmo_auth_init(void)
     }
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         if (line.startswith("//"))
             continue;
@@ -591,13 +590,13 @@ int mmo_auth_init(void)
 static
 void mmo_auth_sync(void)
 {
-    FILE *fp;
-    int lock;
+    io::WriteLock fp(account_filename);
 
-    // Data save
-    fp = lock_fopen(account_filename, &lock);
-    if (fp == NULL)
+    if (!fp.is_open())
+    {
+        PRINTF("uh-oh - unable to save accounts\n");
         return;
+    }
     FPRINTF(fp,
              "// Accounts file: here are saved all information about the accounts.\n");
     FPRINTF(fp,
@@ -625,13 +624,9 @@ void mmo_auth_sync(void)
             continue;
 
         FString line = mmo_auth_tostr(&ad);
-        FPRINTF(fp, "%s\n", line);
+        fp.put_line(line);
     }
     FPRINTF(fp, "%d\t%%newid%%\n", account_id_count);
-
-    lock_fclose(fp, account_filename, &lock);
-
-    return;
 }
 
 // We want to sync the DB to disk as little as possible as it's fairly
@@ -1113,7 +1108,6 @@ void parse_fromchar(int fd)
                 {
                     int acc;
                     unsigned char buf[10];
-                    FILE *fp;
                     acc = RFIFOL(fd, 4);
                     //PRINTF("parse_fromchar: Request to become a GM acount from %d account.\n", acc);
                     WBUFW(buf, 0) = 0x2721;
@@ -1131,7 +1125,8 @@ void parse_fromchar(int fd)
                             if (level_new_gm > 0)
                             {
                                 // if we can open the file to add the new GM
-                                if ((fp = fopen(gm_account_filename.c_str(), "a")) != NULL)
+                                io::AppendFile fp(gm_account_filename);
+                                if (fp.is_open())
                                 {
                                     timestamp_seconds_buffer tmpstr;
                                     stamp_time(tmpstr);
@@ -1139,7 +1134,10 @@ void parse_fromchar(int fd)
                                              "\n// %s: @GM command on account %d\n%d %d\n",
                                              tmpstr,
                                              acc, acc, level_new_gm);
-                                    fclose(fp);
+                                    if (!fp.close())
+                                    {
+                                        PRINTF("warning: didn't actually save GM file\n");
+                                    }
                                     WBUFL(buf, 6) = level_new_gm;
                                     read_gm_account();
                                     send_GM_accounts();
@@ -1517,8 +1515,8 @@ void parse_fromchar(int fd)
 
             default:
             {
-                FILE *logfp = fopen(login_log_unknown_packets_filename.c_str(), "a");
-                if (logfp)
+                io::AppendFile logfp(login_log_unknown_packets_filename);
+                if (logfp.is_open())
                 {
                     timestamp_milliseconds_buffer timestr;
                     stamp_time(timestr);
@@ -1561,7 +1559,6 @@ void parse_fromchar(int fd)
                         FPRINTF(logfp, " %s\n", tmpstr);
                     }
                     FPRINTF(logfp, "\n");
-                    fclose(logfp);
                 }
             }
                 PRINTF("parse_fromchar: Unknown packet 0x%x (from a char-server)! -> disconnection.\n",
@@ -1974,6 +1971,7 @@ void parse_admin(int fd)
                 WFIFOL(fd, 2) = -1;
                 AccountName account_name = stringish<AccountName>(RFIFO_STRING<24>(fd, 2).to_print());
                 WFIFO_STRING(fd, 6, account_name, 24);
+                bool reread = false;
                 {
                     char new_gm_level;
                     new_gm_level = RFIFOB(fd, 26);
@@ -1992,46 +1990,30 @@ void parse_admin(int fd)
                             if (isGM(acc) != new_gm_level)
                             {
                                 // modification of the file
-                                FILE *fp, *fp2;
-                                int lock;
-                                char line[512];
                                 int GM_account, GM_level;
                                 int modify_flag;
-                                if ((fp2 = lock_fopen(gm_account_filename, &lock)) != NULL)
+                                io::WriteLock fp2(gm_account_filename);
+                                if (fp2.is_open())
                                 {
-                                    if ((fp = fopen(gm_account_filename.c_str(), "r")) != NULL)
+                                    io::ReadFile fp(gm_account_filename);
+                                    if (fp.is_open())
                                     {
                                         timestamp_seconds_buffer tmpstr;
                                         stamp_time(tmpstr);
                                         modify_flag = 0;
                                         // read/write GM file
-                                        while (fgets(line, sizeof(line) - 1, fp))
+                                        FString line;
+                                        while (fp.getline(line))
                                         {
-                                            while (line[0] != '\0'
-                                                   && line[strlen(line) - 1] == '\n')
-                                                line[strlen(line) - 1] = '\0';
-                                            if ((line[0] == '/'
-                                                 && line[1] == '/')
-                                                || line[0] == '\0')
-                                                FPRINTF(fp2, "%s\n",
-                                                         line);
+                                            if (!line || line.startswith("//"))
+                                                fp2.put_line(line);
                                             else
                                             {
-                                                if (sscanf(line, "%d %d",
-                                                     &GM_account,
-                                                     &GM_level) != 2
-                                                    && sscanf(line, "%d: %d",
-                                                               &GM_account,
-                                                               &GM_level) !=
-                                                    2)
-                                                    FPRINTF(fp2,
-                                                             "%s\n",
-                                                             line);
+                                                if (!extract(line, record<' '>(&GM_account, &GM_level)))
+                                                    fp2.put_line(line);
                                                 else if (GM_account != acc)
-                                                    FPRINTF(fp2,
-                                                             "%s\n",
-                                                             line);
-                                                else if (new_gm_level < 1)
+                                                    fp2.put_line(line);
+                                                else if (new_gm_level == 0)
                                                 {
                                                     FPRINTF(fp2,
                                                              "// %s: 'ladmin' GM level removed on account %d '%s' (previous level: %d)\n//%d %d\n",
@@ -2061,7 +2043,6 @@ void parse_admin(int fd)
                                                      tmpstr, acc,
                                                      ad->userid, acc,
                                                      new_gm_level);
-                                        fclose(fp);
                                     }
                                     else
                                     {
@@ -2069,14 +2050,11 @@ void parse_admin(int fd)
                                              ad->userid, acc,
                                              new_gm_level, ip);
                                     }
-                                    lock_fclose(fp2, gm_account_filename, &lock);
                                     WFIFOL(fd, 2) = acc;
                                     LOGIN_LOG("'ladmin': Modification of a GM level (account: %s (%d), new GM level: %d, ip: %s)\n",
                                          ad->userid, acc,
                                          new_gm_level, ip);
-                                    // read and send new GM informations
-                                    read_gm_account();
-                                    send_GM_accounts();
+                                    reread = true;
                                 }
                                 else
                                 {
@@ -2100,6 +2078,12 @@ void parse_admin(int fd)
                         }
                     }
                 }
+                if (reread)
+                {
+                    // read and send new GM informations
+                    read_gm_account();
+                    send_GM_accounts();
+                }
                 WFIFOSET(fd, 30);
             }
                 RFIFOSKIP(fd, 27);
@@ -2646,8 +2630,8 @@ void parse_admin(int fd)
 
             default:
             {
-                FILE *logfp = fopen(login_log_unknown_packets_filename.c_str(), "a");
-                if (logfp)
+                io::AppendFile logfp(login_log_unknown_packets_filename);
+                if (logfp.is_open())
                 {
                     timestamp_milliseconds_buffer timestr;
                     stamp_time(timestr);
@@ -2690,7 +2674,6 @@ void parse_admin(int fd)
                         FPRINTF(logfp, " %s\n", tmpstr);
                     }
                     FPRINTF(logfp, "\n");
-                    fclose(logfp);
                 }
             }
                 LOGIN_LOG("'ladmin': End of connection, unknown packet (ip: %s)\n",
@@ -3094,8 +3077,8 @@ void parse_login(int fd)
             default:
                 if (save_unknown_packets)
                 {
-                    FILE *logfp = fopen(login_log_unknown_packets_filename.c_str(), "a");
-                    if (logfp)
+                    io::AppendFile logfp(login_log_unknown_packets_filename);
+                    if (logfp.is_open())
                     {
                         timestamp_milliseconds_buffer timestr;
                         stamp_time(timestr);
@@ -3141,7 +3124,6 @@ void parse_login(int fd)
                             FPRINTF(logfp, " %s\n", tmpstr);
                         }
                         FPRINTF(logfp, "\n");
-                        fclose(logfp);
                     }
                 }
                 LOGIN_LOG("End of connection, unknown packet (ip: %s)\n", ip);
@@ -3164,7 +3146,7 @@ int login_lan_config_read(ZString lancfgName)
     lan_char_ip = IP4_LOCALHOST;
     lan_subnet = IP4Mask(IP4_LOCALHOST, IP4_BROADCAST);
 
-    std::ifstream in(lancfgName.c_str());
+    io::ReadFile in(lancfgName);
 
     if (!in.is_open())
     {
@@ -3176,7 +3158,7 @@ int login_lan_config_read(ZString lancfgName)
     PRINTF("---Start reading Lan Support configuration file\n");
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         XString w1;
         ZString w2;
@@ -3248,7 +3230,7 @@ int login_lan_config_read(ZString lancfgName)
 static
 int login_config_read(ZString cfgName)
 {
-    std::ifstream in(cfgName.c_str());
+    io::ReadFile in(cfgName);
     if (!in.is_open())
     {
         PRINTF("Configuration file (%s) not found.\n", cfgName);
@@ -3258,7 +3240,7 @@ int login_config_read(ZString cfgName)
     PRINTF("---Start reading of Login Server configuration file (%s)\n",
             cfgName);
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         XString w1;
         ZString w2;
diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp
index c8826f7..55596b3 100644
--- a/src/map/atcommand.cpp
+++ b/src/map/atcommand.cpp
@@ -4,19 +4,19 @@
 #include <cstring>
 #include <ctime>
 
-#include <fstream>
-
 #include "../strings/mstring.hpp"
 #include "../strings/fstring.hpp"
 #include "../strings/zstring.hpp"
 #include "../strings/xstring.hpp"
 #include "../strings/vstring.hpp"
 
+#include "../io/read.hpp"
+#include "../io/write.hpp"
+
 #include "../common/core.hpp"
 #include "../common/cxxstdio.hpp"
 #include "../common/extract.hpp"
 #include "../common/human_time_diff.hpp"
-#include "../common/io.hpp"
 #include "../common/mmo.hpp"
 #include "../common/nullpo.hpp"
 #include "../common/random.hpp"
@@ -92,9 +92,9 @@ ATCE atcommand_charstreset(const int fd, dumb_ptr<map_session_data> sd,
 
 void atcommand_config_write(ZString cfgName)
 {
-    FILE *out = fopen(cfgName.c_str(), "w");
+    io::WriteFile out(cfgName);
 
-    if (!out)
+    if (!out.is_open())
     {
         FPRINTF(stderr, "Failed to write atcommand config: %s\n", cfgName);
         return;
@@ -117,8 +117,6 @@ void atcommand_config_write(ZString cfgName)
                 cmd, info.args,
                 cmd, info.level);
     }
-
-    fclose(out);
 }
 
 
@@ -164,11 +162,11 @@ bool asplit(ZString raw, F *first_arg, R *... rest_args)
 }
 
 static
-FILE *get_gm_log();
+io::AppendFile *get_gm_log();
 
 void log_atcommand(dumb_ptr<map_session_data> sd, ZString cmd)
 {
-    FILE *fp = get_gm_log();
+    io::AppendFile *fp = get_gm_log();
     if (!fp)
         return;
     timestamp_seconds_buffer tmpstr;
@@ -176,17 +174,16 @@ void log_atcommand(dumb_ptr<map_session_data> sd, ZString cmd)
     MapName map = (sd->bl_m
             ? sd->bl_m->name_
             : stringish<MapName>("undefined.gat"));
-    FPRINTF(fp, "[%s] %s(%d,%d) %s(%d) : %s\n",
+    FPRINTF(*fp, "[%s] %s(%d,%d) %s(%d) : %s\n",
             tmpstr,
             map, sd->bl_x, sd->bl_y,
             sd->status.name, sd->status.account_id,
             cmd);
-    fflush(fp);
 }
 
 FString gm_log;
 
-FILE *get_gm_log()
+io::AppendFile *get_gm_log()
 {
     if (!gm_log)
         return NULL;
@@ -197,26 +194,26 @@ FILE *get_gm_log()
     int month = ctime.tm_mon + 1;
     int logfile_nr = (year * 12) + month;
 
-    static FILE *gm_logfile = NULL;
+    static std::unique_ptr<io::AppendFile> gm_logfile;
     static int last_logfile_nr = 0;
     if (logfile_nr == last_logfile_nr)
-        return gm_logfile;
+        return gm_logfile.get();
     last_logfile_nr = logfile_nr;
 
     FString fullname = STRPRINTF("%s.%04d-%02d",
             gm_log, year, month);
 
     if (gm_logfile)
-        fclose(gm_logfile);
+        gm_logfile.reset();
 
-    gm_logfile = fopen(fullname.c_str(), "a");
+    gm_logfile = make_unique<io::AppendFile>(fullname, true);
 
     if (!gm_logfile)
     {
         perror("GM log file");
         gm_log = FString();
     }
-    return gm_logfile;
+    return gm_logfile.get();
 }
 
 bool is_atcommand(const int fd, dumb_ptr<map_session_data> sd,
@@ -322,7 +319,7 @@ AtCommandInfo *get_atcommandinfo_byname(XString name)
 
 int atcommand_config_read(ZString cfgName)
 {
-    std::ifstream in(cfgName.c_str());
+    io::ReadFile in(cfgName);
     if (!in.is_open())
     {
         PRINTF("At commands configuration file not found: %s\n", cfgName);
@@ -330,7 +327,7 @@ int atcommand_config_read(ZString cfgName)
     }
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         XString w1;
         ZString w2;
@@ -1277,7 +1274,7 @@ ATCE atcommand_item(const int fd, dumb_ptr<map_session_data> sd,
 {
     ItemName item_name;
     int number = 0, item_id;
-    struct item_data *item_data;
+    struct item_data *item_data = NULL;
     int get_count, i;
 
     if (!extract(message, record<' ', 1>(&item_name, &number)))
@@ -3847,7 +3844,7 @@ static
 ATCE atcommand_character_item_list(const int fd, dumb_ptr<map_session_data> sd,
         ZString message)
 {
-    struct item_data *item_data, *item_temp;
+    struct item_data *item_data = NULL, *item_temp;
     int i, j, count, counter, counter2;
     CharName character;
 
@@ -4001,7 +3998,7 @@ ATCE atcommand_character_storage_list(const int fd, dumb_ptr<map_session_data> s
         ZString message)
 {
     struct storage *stor;
-    struct item_data *item_data, *item_temp;
+    struct item_data *item_data = NULL, *item_temp;
     int i, j, count, counter, counter2;
     CharName character;
 
@@ -4121,7 +4118,7 @@ static
 ATCE atcommand_character_cart_list(const int fd, dumb_ptr<map_session_data> sd,
         ZString message)
 {
-    struct item_data *item_data, *item_temp;
+    struct item_data *item_data = NULL, *item_temp;
     int i, j, count, counter, counter2;
     CharName character;
 
diff --git a/src/map/battle.cpp b/src/map/battle.cpp
index c95ad61..457c37a 100644
--- a/src/map/battle.cpp
+++ b/src/map/battle.cpp
@@ -2,13 +2,12 @@
 
 #include <cstring>
 
-#include <fstream>
-
 #include "../strings/fstring.hpp"
 #include "../strings/zstring.hpp"
 
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
-#include "../common/io.hpp"
 #include "../common/nullpo.hpp"
 #include "../common/random.hpp"
 
@@ -2405,7 +2404,7 @@ int battle_config_read(ZString cfgName)
         battle_config.mob_splash_radius = -1;
     }
 
-    std::ifstream in(cfgName.c_str());
+    io::ReadFile in(cfgName);
     if (!in.is_open())
     {
         PRINTF("file not found: %s\n", cfgName);
@@ -2413,7 +2412,7 @@ int battle_config_read(ZString cfgName)
     }
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
 #define BATTLE_CONFIG_VAR(name) {{#name}, &battle_config.name}
         const struct
diff --git a/src/map/clif.cpp b/src/map/clif.cpp
index 0ac518e..1a38ebe 100644
--- a/src/map/clif.cpp
+++ b/src/map/clif.cpp
@@ -10,6 +10,8 @@
 #include "../strings/zstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/write.hpp"
+
 #include "../common/cxxstdio.hpp"
 #include "../common/md5calc.hpp"
 #include "../common/random.hpp"
@@ -5536,8 +5538,7 @@ void clif_parse(int fd)
 #ifdef DUMP_UNKNOWN_PACKET
             {
                 int i;
-                FILE *fp;
-                char packet_txt[256] = "save/packet.txt";
+                ZString packet_txt = "save/packet.txt";
                 PRINTF("---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F");
                 for (i = 0; i < packet_len; i++)
                 {
@@ -5554,7 +5555,8 @@ void clif_parse(int fd)
                 else if (sd)    // not authentified! (refused by char-server or disconnect before to be authentified)
                     PRINTF("\nAccount ID %d.\n", sd->bl_id);
 
-                if ((fp = fopen(packet_txt, "a")) == NULL)
+                io::AppendFile fp(packet_txt);
+                if (!fp.is_open())
                 {
                     PRINTF("clif.c: cant write [%s] !!! data is lost !!!\n",
                             packet_txt);
@@ -5586,7 +5588,6 @@ void clif_parse(int fd)
                         FPRINTF(fp, "%02X ", RFIFOB(fd, i));
                     }
                     FPRINTF(fp, "\n\n");
-                    fclose(fp);
                 }
             }
 #endif
diff --git a/src/map/grfio.cpp b/src/map/grfio.cpp
index 41a285f..c486cf4 100644
--- a/src/map/grfio.cpp
+++ b/src/map/grfio.cpp
@@ -10,22 +10,22 @@
 #include <cstdio>
 #include <cstring>
 
-#include <fstream>
 #include <map>
 
 #include "../strings/mstring.hpp"
 #include "../strings/fstring.hpp"
 
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
 #include "../common/extract.hpp"
-#include "../common/io.hpp"
 
 #include "../poison.hpp"
 
 static
 std::map<MapName, FString> load_resnametable()
 {
-    std::ifstream in("data/resnametable.txt");
+    io::ReadFile in("data/resnametable.txt");
     if (!in.is_open())
     {
         fprintf(stderr, "Missing data/resnametable.txt");
@@ -34,7 +34,7 @@ std::map<MapName, FString> load_resnametable()
     std::map<MapName, FString> out;
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         MapName key;
         FString value;
diff --git a/src/map/itemdb.cpp b/src/map/itemdb.cpp
index a8d8b55..5a11aa1 100644
--- a/src/map/itemdb.cpp
+++ b/src/map/itemdb.cpp
@@ -3,16 +3,15 @@
 #include <cstdlib>
 #include <cstring>
 
-#include <fstream>
-
 #include "../strings/fstring.hpp"
 #include "../strings/zstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
 #include "../common/db.hpp"
 #include "../common/extract.hpp"
-#include "../common/io.hpp"
 #include "../common/nullpo.hpp"
 #include "../common/random.hpp"
 #include "../common/socket.hpp"
@@ -157,10 +156,10 @@ static
 int itemdb_readdb(void)
 {
     int ln = 0, lines = 0;
-    const char *filename = "db/item_db.txt";
+    ZString filename = "db/item_db.txt";
 
     {
-        std::ifstream in(filename);
+        io::ReadFile in(filename);
 
         if (!in.is_open())
         {
@@ -171,7 +170,7 @@ int itemdb_readdb(void)
         lines = 0;
 
         FString line;
-        while (io::getline(in, line))
+        while (in.getline(line))
         {
             lines++;
             if (!line)
diff --git a/src/map/map.cpp b/src/map/map.cpp
index 8845b23..d259451 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -10,18 +10,18 @@
 #include <cstdlib>
 #include <cstring>
 
-#include <fstream>
-
 #include "../strings/fstring.hpp"
 #include "../strings/zstring.hpp"
 #include "../strings/xstring.hpp"
 #include "../strings/vstring.hpp"
 
+#include "../io/write.hpp"
+#include "../io/read.hpp"
+
 #include "../common/core.hpp"
 #include "../common/cxxstdio.hpp"
 #include "../common/db.hpp"
 #include "../common/extract.hpp"
-#include "../common/io.hpp"
 #include "../common/random2.hpp"
 #include "../common/nullpo.hpp"
 #include "../common/socket.hpp"
@@ -1343,7 +1343,7 @@ void map_delmap(MapName mapname)
 constexpr int LOGFILE_SECONDS_PER_CHUNK_SHIFT = 10;
 
 static
-FILE *map_logfile = NULL;
+std::unique_ptr<io::AppendFile> map_logfile;
 static
 FString map_logfile_name;
 static
@@ -1364,8 +1364,7 @@ void map_close_logfile(void)
         };
         char **argv = const_cast<char **>(args);
 
-        fclose(map_logfile);
-        map_logfile = NULL;
+        map_logfile.reset();
 
         if (!fork())
         {
@@ -1385,9 +1384,12 @@ void map_start_logfile(long index)
             "%s.%ld",
             map_logfile_name,
             map_logfile_index);
-    map_logfile = fopen(filename_buf.c_str(), "w+");
-    if (!map_logfile)
+    map_logfile = make_unique<io::AppendFile>(filename_buf);
+    if (!map_logfile->is_open())
+    {
+        map_logfile.reset();
         perror(map_logfile_name.c_str());
+    }
 }
 
 static
@@ -1417,7 +1419,7 @@ void map_log(XString line)
         map_start_logfile(i);
     }
 
-    log_with_timestamp(map_logfile, line);
+    log_with_timestamp(*map_logfile, line);
 }
 
 /*==========================================
@@ -1429,7 +1431,7 @@ int map_config_read(ZString cfgName)
 {
     struct hostent *h = NULL;
 
-    std::ifstream in(cfgName.c_str());
+    io::ReadFile in(cfgName);
     if (!in.is_open())
     {
         PRINTF("Map configuration file not found at: %s\n", cfgName);
@@ -1437,7 +1439,7 @@ int map_config_read(ZString cfgName)
     }
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         XString w1;
         ZString w2;
diff --git a/src/map/mob.cpp b/src/map/mob.cpp
index 1885fbd..34374a7 100644
--- a/src/map/mob.cpp
+++ b/src/map/mob.cpp
@@ -7,14 +7,14 @@
 #include <cstring>
 
 #include <algorithm>
-#include <fstream>
 
 #include "../strings/fstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
 #include "../common/extract.hpp"
-#include "../common/io.hpp"
 #include "../common/nullpo.hpp"
 #include "../common/random.hpp"
 #include "../common/socket.hpp"
@@ -3415,19 +3415,19 @@ bool extract(XString str, LevelElement *le)
 static
 int mob_readdb(void)
 {
-    const char *filename = "db/mob_db.txt";
+    ZString filename = "db/mob_db.txt";
 
     for (mob_db_& e : mob_db)
         e = mob_db_{};
 
     {
-        std::ifstream in(filename);
+        io::ReadFile in(filename);
         if (!in.is_open())
         {
             return -1;
         }
         FString line;
-        while (io::getline(in, line))
+        while (in.getline(line))
         {
             int mob_class;
 
@@ -3632,17 +3632,17 @@ bool extract<MobSkillTarget, void, void>(XString str, MobSkillTarget *mst)
 static
 int mob_readskilldb(void)
 {
-    const char *filename = "db/mob_skill_db.txt";
+    ZString filename = "db/mob_skill_db.txt";
 
     {
-        std::ifstream in(filename);
+        io::ReadFile in(filename);
         if (!in.is_open())
         {
             PRINTF("can't read %s\n", filename);
             return 0;
         }
         FString line;
-        while (io::getline(in, line))
+        while (in.getline(line))
         {
             int mob_id;
 
diff --git a/src/map/npc.cpp b/src/map/npc.cpp
index e3038a1..e6c7445 100644
--- a/src/map/npc.cpp
+++ b/src/map/npc.cpp
@@ -12,6 +12,8 @@
 #include "../strings/zstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
 #include "../common/db.hpp"
 #include "../common/extract.hpp"
@@ -1160,7 +1162,7 @@ void npc_convertlabel_db(ScriptLabel lname, int pos, dumb_ptr<npc_data_script> n
  */
 static
 int npc_parse_script(XString w1, XString w2, NpcName w3, ZString w4,
-        XString first_line, FILE *fp, int *lines)
+        XString first_line, io::ReadFile& fp, int *lines)
 {
     int x, y;
     DIR dir = DIR::S;
@@ -1206,14 +1208,11 @@ int npc_parse_script(XString w1, XString w2, NpcName w3, ZString w4,
             if (it != srcbuf.rend() && *it == '}')
                 break;
 
-            char line_[1024];
-            if (!fgets(line_, 1020, fp))
+            FString line;
+            if (!fp.getline(line))
                 // eof
                 break;
             (*lines)++;
-            if (feof(fp))
-                break;
-            ZString line(strings::really_construct_from_a_pointer, line_, nullptr);
             if (!srcbuf)
             {
                 // may be a no-op
@@ -1224,6 +1223,7 @@ int npc_parse_script(XString w1, XString w2, NpcName w3, ZString w4,
             }
             else
                 srcbuf += line;
+            srcbuf += '\n';
         }
         script = parse_script(FString(srcbuf), startline);
         if (script == NULL)
@@ -1391,7 +1391,7 @@ int npc_parse_script(XString w1, XString w2, NpcName w3, ZString w4,
  */
 static
 int npc_parse_function(XString, XString, XString w3, ZString,
-        XString first_line, FILE *fp, int *lines)
+        XString first_line, io::ReadFile& fp, int *lines)
 {
     MString srcbuf;
     srcbuf += first_line.xislice_t(std::find(first_line.begin(), first_line.end(), '{'));
@@ -1402,13 +1402,11 @@ int npc_parse_function(XString, XString, XString w3, ZString,
         auto it = std::find_if_not(srcbuf.rbegin(), srcbuf.rend(), [](char c){ return c == ' ' || c == '\n'; });
         if (it != srcbuf.rend() && *it == '}')
             break;
-        char line_[1024];
-        if (!fgets(line_, 1020, fp))
+
+        FString line;
+        if (!fp.getline(line))
             break;
         (*lines)++;
-        if (feof(fp))
-            break;
-        ZString line(strings::really_construct_from_a_pointer, line_, nullptr);
         if (!srcbuf)
         {
             srcbuf += line.xislice_t(std::find(line.begin(), line.end(), '{'));
@@ -1416,6 +1414,7 @@ int npc_parse_function(XString, XString, XString w3, ZString,
         }
         else
             srcbuf += line;
+        srcbuf += '\n';
     }
     std::unique_ptr<const ScriptBuffer> script = parse_script(FString(srcbuf), startline);
     if (script == NULL)
@@ -1722,8 +1721,8 @@ int do_init_npc(void)
     for (; !npc_srcs.empty(); npc_srcs.pop_front())
     {
         FString nsl = npc_srcs.front();
-        FILE *fp = fopen(nsl.c_str(), "r");
-        if (fp == NULL)
+        io::ReadFile fp(nsl);
+        if (!fp.is_open())
         {
             PRINTF("file not found : %s\n", nsl);
             exit(1);
@@ -1731,12 +1730,9 @@ int do_init_npc(void)
         PRINTF("\rLoading NPCs [%d]: %-54s", npc_id - START_NPC_NUM,
                 nsl);
         int lines = 0;
-        char line_[1024];
-        while (fgets(line_, 1020, fp))
+        FString zline;
+        while (fp.getline(zline))
         {
-            // because it's still fgets
-            line_[strlen(line_) - 1] = '\0';
-            ZString zline(strings::really_construct_from_a_pointer, line_, nullptr);
             XString w1, w2, w3, w4x;
             ZString w4z;
             lines++;
@@ -1807,7 +1803,6 @@ int do_init_npc(void)
                 PRINTF("odd script line: %s\n", zline);
             }
         }
-        fclose(fp);
         fflush(stdout);
     }
     PRINTF("\rNPCs Loaded: %d [Warps:%d Shops:%d Scripts:%d Mobs:%d] %20s\n",
diff --git a/src/map/pc.cpp b/src/map/pc.cpp
index a723555..618d491 100644
--- a/src/map/pc.cpp
+++ b/src/map/pc.cpp
@@ -4,13 +4,12 @@
 #include <cstdlib>
 #include <cstring>
 
-#include <fstream>
-
 #include "../strings/fstring.hpp"
 #include "../strings/zstring.hpp"
 
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
-#include "../common/io.hpp"
 #include "../common/nullpo.hpp"
 #include "../common/random.hpp"
 #include "../common/socket.hpp"
@@ -778,11 +777,11 @@ int pc_authok(int id, int login_id2, TimeT connect_until_time,
 void pc_show_motd(dumb_ptr<map_session_data> sd)
 {
     sd->state.seen_motd = true;
-    std::ifstream in(motd_txt.c_str());
+    io::ReadFile in(motd_txt);
     if (in.is_open())
     {
         FString buf;
-        while (io::getline(in, buf))
+        while (in.getline(buf))
         {
             clif_displaymessage(sd->fd, buf);
         }
diff --git a/src/map/script.cpp b/src/map/script.cpp
index 4b009b1..f7a87a5 100644
--- a/src/map/script.cpp
+++ b/src/map/script.cpp
@@ -7,19 +7,18 @@
 #include <cstring>
 #include <ctime>
 
-#include <fstream>
-
 #include "../strings/mstring.hpp"
 #include "../strings/fstring.hpp"
 #include "../strings/zstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/lock.hpp"
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
 #include "../common/db.hpp"
 #include "../common/extract.hpp"
 #include "../common/intern-pool.hpp"
-#include "../common/io.hpp"
-#include "../common/lock.hpp"
 #include "../common/random.hpp"
 #include "../common/socket.hpp"
 #include "../common/utils.hpp"
@@ -688,7 +687,7 @@ void add_builtin_functions(void)
 static
 void read_constdb(void)
 {
-    std::ifstream in("db/const.txt");
+    io::ReadFile in("db/const.txt");
     if (!in.is_open())
     {
         PRINTF("can't read db/const.txt\n");
@@ -696,7 +695,7 @@ void read_constdb(void)
     }
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         if (line.startswith("//"))
             continue;
@@ -704,6 +703,7 @@ void read_constdb(void)
         FString name;
         int val;
         int type = 0; // if not provided
+        // TODO get rid of SSCANF - this is the last serious use
         if (SSCANF(line, "%m[A-Za-z0-9_] %i %i", &name, &val, &type) < 2)
             continue;
         str_data_t *n = add_strp(name);
@@ -4898,13 +4898,13 @@ void mapreg_setregstr(SIR reg, XString str)
 static
 void script_load_mapreg(void)
 {
-    std::ifstream in(mapreg_txt.c_str());
+    io::ReadFile in(mapreg_txt);
 
     if (!in.is_open())
         return;
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         XString buf1, buf2;
         int index = 0;
@@ -4946,7 +4946,7 @@ void script_load_mapreg(void)
  *------------------------------------------
  */
 static
-void script_save_mapreg_intsub(SIR key, int data, FILE *fp)
+void script_save_mapreg_intsub(SIR key, int data, io::WriteFile& fp)
 {
     int num = key.base(), i = key.index();
     ZString name = variable_names.outtern(num);
@@ -4960,7 +4960,7 @@ void script_save_mapreg_intsub(SIR key, int data, FILE *fp)
 }
 
 static
-void script_save_mapreg_strsub(SIR key, ZString data, FILE *fp)
+void script_save_mapreg_strsub(SIR key, ZString data, io::WriteFile& fp)
 {
     int num = key.base(), i = key.index();
     ZString name = variable_names.outtern(num);
@@ -4976,16 +4976,13 @@ void script_save_mapreg_strsub(SIR key, ZString data, FILE *fp)
 static
 void script_save_mapreg(void)
 {
-    FILE *fp;
-    int lock;
-
-    if ((fp = lock_fopen(mapreg_txt, &lock)) == NULL)
+    io::WriteLock fp(mapreg_txt);
+    if (!fp.is_open())
         return;
     for (auto& pair : mapreg_db)
         script_save_mapreg_intsub(pair.first, pair.second, fp);
     for (auto& pair : mapregstr_db)
         script_save_mapreg_strsub(pair.first, pair.second, fp);
-    lock_fclose(fp, mapreg_txt, &lock);
     mapreg_dirty = 0;
 }
 
diff --git a/src/map/skill.cpp b/src/map/skill.cpp
index ab5f55b..9187335 100644
--- a/src/map/skill.cpp
+++ b/src/map/skill.cpp
@@ -5,15 +5,14 @@
 #include <cstring>
 #include <ctime>
 
-#include <fstream>
-
 #include "../strings/mstring.hpp"
 #include "../strings/fstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
 #include "../common/extract.hpp"
-#include "../common/io.hpp"
 #include "../common/nullpo.hpp"
 #include "../common/random.hpp"
 #include "../common/socket.hpp"
@@ -1186,15 +1185,15 @@ int skill_readdb(void)
     for (skill_db_& skdb : skill_db)
         skdb = skill_db_{};
 
-    std::ifstream in("db/skill_db.txt");
-    if (!in)
+    io::ReadFile in("db/skill_db.txt");
+    if (!in.is_open())
     {
         PRINTF("can't read db/skill_db.txt\n");
         return 1;
     }
 
     FString line_;
-    while (io::getline(in, line_))
+    while (in.getline(line_))
     {
         XString comment = "//";
         XString line = line_.xislice_h(std::search(line_.begin(), line_.end(), comment.begin(), comment.end())).rstrip();
diff --git a/src/monitor/main.cpp b/src/monitor/main.cpp
index e20e2e3..b398b97 100644
--- a/src/monitor/main.cpp
+++ b/src/monitor/main.cpp
@@ -14,15 +14,14 @@
 
 #include <csignal>
 
-#include <fstream>
-
 #include "../strings/mstring.hpp"
 #include "../strings/fstring.hpp"
 #include "../strings/zstring.hpp"
 #include "../strings/xstring.hpp"
 
+#include "../io/read.hpp"
+
 #include "../common/cxxstdio.hpp"
-#include "../common/io.hpp"
 #include "../common/utils.hpp"
 
 #include "../poison.hpp"
@@ -79,7 +78,7 @@ void parse_option(XString name, ZString value)
 static
 void read_config(ZString filename)
 {
-    std::ifstream in(filename.c_str());
+    io::ReadFile in(filename);
     if (!in.is_open())
     {
         FPRINTF(stderr, "Monitor config file not found: %s\n", filename);
@@ -87,7 +86,7 @@ void read_config(ZString filename)
     }
 
     FString line;
-    while (io::getline(in, line))
+    while (in.getline(line))
     {
         XString name;
         ZString value;
diff --git a/src/poison.hpp b/src/poison.hpp
index e7cc4cc..d4a6cd0 100644
--- a/src/poison.hpp
+++ b/src/poison.hpp
@@ -1,3 +1,5 @@
+#ifndef GENERATING_DEPENDENCIES
+
 // impossible(*) to use safely
 // removed in C11
 #pragma GCC poison gets
@@ -85,3 +87,15 @@
 #pragma GCC poison tolower
 #pragma GCC poison isupper
 #pragma GCC poison islower
+
+// in favor of io::ReadFile and io::WriteFile
+// note that stdout and stderr are NOT poisoned (yet)
+#pragma GCC poison FILE
+#pragma GCC poison istream
+#pragma GCC poison ostream
+#pragma GCC poison iostream
+#pragma GCC poison ifstream
+#pragma GCC poison ofstream
+#pragma GCC poison fstream
+
+#endif // GENERATING_DEPENDENCIES
-- 
cgit v1.2.3-70-g09d2