Attached Files |
0001-Don-t-die-when-encountering-0-byte-ghost-files.patch [^] (4,953 bytes) 2014-05-18 17:06 [Show Content] [Hide Content]From 239439149e0354bb410a8ae7e9013904ebd62fae Mon Sep 17 00:00:00 2001
From: Nicholas Feinberg <pleasingfung@gmail.com>
Date: Sat, 17 May 2014 23:27:02 -0700
Subject: [PATCH] Don't die when encountering 0-byte ghost files
Conflicts:
crawl-ref/source/files.cc
---
crawl-ref/source/files.cc | 1 +
crawl-ref/source/tags.cc | 20 ++++++++++----------
crawl-ref/source/tags.h | 8 ++++++--
3 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc
index 5d220c9..c4f0c7f 100644
--- a/crawl-ref/source/files.cc
+++ b/crawl-ref/source/files.cc
@@ -1761,6 +1761,7 @@ bool load_ghost(bool creating_level)
return false; // no such ghost.
}
+ inf.set_safe_read(true); // don't die on 0-byte bones
if (_ghost_version_compatible(inf))
{
try
diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc
index 66abb8b..9415ea0 100644
--- a/crawl-ref/source/tags.cc
+++ b/crawl-ref/source/tags.cc
@@ -97,7 +97,7 @@ extern abyss_state abyssal_state;
reader::reader(const string &_read_filename, int minorVersion)
: _filename(_read_filename), _chunk(0), _pbuf(NULL), _read_offset(0),
- _minorVersion(minorVersion)
+ _minorVersion(minorVersion), _safe_read(false)
{
_file = fopen_u(_filename.c_str(), "rb");
opened_file = !!_file;
@@ -105,7 +105,7 @@ reader::reader(const string &_read_filename, int minorVersion)
reader::reader(package *save, const string &chunkname, int minorVersion)
: _file(0), _chunk(0), opened_file(false), _pbuf(0), _read_offset(0),
- _minorVersion(minorVersion)
+ _minorVersion(minorVersion), _safe_read(false)
{
ASSERT(save);
_chunk = new chunk_reader(save, chunkname);
@@ -143,9 +143,9 @@ bool reader::valid() const
(_pbuf && _read_offset < _pbuf->size());
}
-static NORETURN void _short_read()
+static NORETURN void _short_read(bool safe_read)
{
- if (!crawl_state.need_save)
+ if (!crawl_state.need_save || safe_read)
throw short_read_exception();
// Would be nice to name the save chunk here, but in interesting cases
// we're reading a copy from memory (why?).
@@ -159,20 +159,20 @@ unsigned char reader::readByte()
{
int b = fgetc(_file);
if (b == EOF)
- _short_read();
+ _short_read(_safe_read);
return b;
}
else if (_chunk)
{
unsigned char buf;
if (_chunk->read(&buf, 1) != 1)
- _short_read();
+ _short_read(_safe_read);
return buf;
}
else
{
if (_read_offset >= _pbuf->size())
- _short_read();
+ _short_read(_safe_read);
return (*_pbuf)[_read_offset++];
}
}
@@ -184,7 +184,7 @@ void reader::read(void *data, size_t size)
if (data)
{
if (fread(data, 1, size, _file) != size)
- _short_read();
+ _short_read(_safe_read);
}
else
fseek(_file, (long)size, SEEK_CUR);
@@ -192,12 +192,12 @@ void reader::read(void *data, size_t size)
else if (_chunk)
{
if (_chunk->read(data, size) != size)
- _short_read();
+ _short_read(_safe_read);
}
else
{
if (_read_offset+size > _pbuf->size())
- _short_read();
+ _short_read(_safe_read);
if (data && size)
memcpy(data, &(*_pbuf)[_read_offset], size);
diff --git a/crawl-ref/source/tags.h b/crawl-ref/source/tags.h
index 637852a..64eb097 100644
--- a/crawl-ref/source/tags.h
+++ b/crawl-ref/source/tags.h
@@ -99,11 +99,11 @@ public:
reader(const string &filename, int minorVersion = TAG_MINOR_INVALID);
reader(FILE* input, int minorVersion = TAG_MINOR_INVALID)
: _file(input), _chunk(0), opened_file(false), _pbuf(0),
- _read_offset(0), _minorVersion(minorVersion) {}
+ _read_offset(0), _minorVersion(minorVersion), _safe_read(false) {}
reader(const vector<unsigned char>& input,
int minorVersion = TAG_MINOR_INVALID)
: _file(0), _chunk(0), opened_file(false), _pbuf(&input),
- _read_offset(0), _minorVersion(minorVersion) {}
+ _read_offset(0), _minorVersion(minorVersion), _safe_read(false) {}
reader(package *save, const string &chunkname,
int minorVersion = TAG_MINOR_INVALID);
~reader();
@@ -119,6 +119,8 @@ public:
string filename() const { return _filename; }
+ void set_safe_read(bool setting) { _safe_read = setting; }
+
private:
string _filename;
FILE* _file;
@@ -127,6 +129,8 @@ private:
const vector<unsigned char>* _pbuf;
unsigned int _read_offset;
int _minorVersion;
+ // always throw an exception rather than dying when reading past EOF
+ bool _safe_read;
};
class short_read_exception : exception {};
--
1.8.5.2 (Apple Git-48)
0001-New-attempt-at-multi-ghost-file-support.patch [^] (8,285 bytes) 2014-05-18 17:07 [Show Content] [Hide Content]From a88b811fdc7938a9013dd2d92d02ff2be02850d3 Mon Sep 17 00:00:00 2001
From: Nicholas Feinberg <pleasingfung@gmail.com>
Date: Wed, 7 May 2014 20:46:47 -0700
Subject: [PATCH] New attempt at multi-ghost-file support
---
crawl-ref/source/files.cc | 184 +++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 164 insertions(+), 20 deletions(-)
diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc
index 5d220c9..398d7fd 100644
--- a/crawl-ref/source/files.cc
+++ b/crawl-ref/source/files.cc
@@ -111,6 +111,8 @@ static bool _read_char_chunk(package *save);
const short GHOST_SIGNATURE = short(0xDC55);
+const int GHOST_LIMIT = 27; // max number of ghost files per level
+
static void _redraw_all(void)
{
you.redraw_hit_points = true;
@@ -531,9 +533,30 @@ static string _get_savefile_directory()
return dir;
}
+
+/**
+ * Location of legacy ghost files. (The save directory.)
+ *
+ * @return The path to the directory for old ghost files.
+ */
+static string _get_old_bonefile_directory()
+{
+ string dir = catpath(Options.shared_dir, crawl_state.game_savedir_path());
+ check_mkdir("Bones directory", &dir, false);
+ if (dir.empty())
+ dir = ".";
+ return dir;
+}
+
+/**
+ * Location of ghost files.
+ *
+ * @return The path to the directory for ghost files.
+ */
static string _get_bonefile_directory()
{
string dir = catpath(Options.shared_dir, crawl_state.game_savedir_path());
+ dir = catpath(dir, "bones");
check_mkdir("Bones directory", &dir, false);
if (dir.empty())
dir = ".";
@@ -1718,12 +1741,63 @@ void save_game_state()
static string _make_ghost_filename()
{
- return _get_bonefile_directory() + "bones."
+ return "bones."
+ replace_all(level_id::current().describe(), ":", "-");
}
#define BONES_DIAGNOSTICS (defined(WIZARD) || defined(DEBUG_BONES) | defined(DEBUG_DIAGNOSTICS))
+/**
+ * Lists all bonefiles for the current level.
+ *
+ * @return A vector containing absolute paths to 0+ bonefiles.
+ */
+vector<string> _list_bones()
+{
+ string bonefile_dir = _get_bonefile_directory();
+ string base_filename = _make_ghost_filename();
+ string underscored_filename = base_filename + "_";
+
+ vector<string> filenames = get_dir_files(bonefile_dir);
+ vector<string> bonefiles;
+ for (std::vector<string>::iterator it = filenames.begin();
+ it != filenames.end(); ++it)
+ {
+ const string &filename = *it;
+
+ if (starts_with(filename, underscored_filename))
+ bonefiles.push_back(bonefile_dir + filename);
+ }
+
+ string old_bonefile = _get_old_bonefile_directory() + base_filename;
+ if (access(old_bonefile.c_str(), F_OK) == 0)
+ {
+ dprf("Found old bonefile %s", old_bonefile.c_str());
+ bonefiles.push_back(old_bonefile);
+ }
+
+ return bonefiles;
+}
+
+/**
+ * Attempts to find a file containing ghost(s) appropriate for the player.
+ *
+ * @return The filename of an appropriate bones file; may be "".
+ */
+string _find_ghost_file()
+{
+ vector<string> bonefiles = _list_bones();
+ if (bonefiles.empty())
+ return "";
+ return bonefiles[ui_random(bonefiles.size())];
+}
+
+/**
+ * Attempt to load one or more ghosts into the level.
+ *
+ * @param creating_level Whether a level is currently being generated.
+ * @return Whether ghosts were actually generated.
+ */
bool load_ghost(bool creating_level)
{
const bool wiz_cmd = (crawl_state.prev_cmd == CMD_WIZARD);
@@ -1752,15 +1826,28 @@ bool load_ghost(bool creating_level)
#endif // BONES_DIAGNOSTICS
- const string ghost_filename = _make_ghost_filename();
+ const string ghost_filename = _find_ghost_file();
+ if (ghost_filename.empty())
+ {
+ if (wiz_cmd && !creating_level)
+ mprf(MSGCH_PROMPT, "No ghost files for this level.");
+ return false; // no such ghost.
+ }
+
reader inf(ghost_filename);
if (!inf.valid())
{
if (wiz_cmd && !creating_level)
- mprf(MSGCH_PROMPT, "No ghost files for this level.");
- return false; // no such ghost.
+ mprf(MSGCH_PROMPT, "Ghost file invalidated before read.");
+ return false;
}
+ /**
+ * This will crash if the bones file is empty, which can happen if
+ * we open it while another process has just created but not yet write-
+ * -locked it. (A rare race condition.) Someone should probably fix this
+ * not to crash in those cases, though.
+ */
if (_ghost_version_compatible(inf))
{
try
@@ -2230,6 +2317,55 @@ static bool _ghost_version_compatible(reader &inf)
return true;
}
+/**
+ * Attempt to open a new bones file for saving ghosts.
+ *
+ * @param[out] return_gfilename The name of the file created, if any.
+ * @return A FILE object, or NULL.
+ **/
+FILE* _make_bones_file(string * return_gfilename)
+{
+
+ const string bone_dir = _get_bonefile_directory();
+ const string base_filename = _make_ghost_filename();
+ for (int i = 0; i < GHOST_LIMIT; i++)
+ {
+ const string g_file_name = make_stringf("%s%s_%d", bone_dir.c_str(),
+ base_filename.c_str(), i);
+ FILE *gfil = lk_open("ab", g_file_name);
+ // need to check file size, so can't open 'wb' - would truncate!
+
+ if (!gfil)
+ {
+ dprf("Could not open %s", g_file_name.c_str());
+ continue;
+ }
+
+ if (file_size(gfil) > 0) // bones file already exists
+ {
+ dprf("%s already exists", g_file_name.c_str());
+ lk_close(gfil, "ab", g_file_name);
+ continue;
+ }
+
+ dprf("found %s", g_file_name.c_str());
+
+ *return_gfilename = g_file_name;
+ return gfil;
+ }
+
+ return NULL;
+}
+
+/**
+ * Attempt to save all ghosts from the current level.
+ *
+ * Including the player, if they're not undead. Doesn't save ghosts from D:1-2
+ * or Temple.
+ *
+ * @param force Forces ghost generation even in otherwise-disallowed levels.
+ **/
+
void save_ghost(bool force)
{
#ifdef BONES_DIAGNOSTICS
@@ -2244,6 +2380,17 @@ void save_ghost(bool force)
#endif // BONES_DIAGNOSTICS
+ ghosts = ghost_demon::find_ghosts();
+
+ if (ghosts.empty())
+ {
+#ifdef BONES_DIAGNOSTICS
+ if (do_diagnostics)
+ mprf(MSGCH_DIAGNOSTICS, "Could not find any ghosts for this level.");
+#endif
+ return;
+ }
+
// No ghosts on D:1, D:2, or the Temple.
if (!force && (you.depth < 3 && player_in_branch(BRANCH_DUNGEON)
|| player_in_branch(BRANCH_TEMPLE)))
@@ -2251,40 +2398,37 @@ void save_ghost(bool force)
return;
}
- const string cha_fil = _make_ghost_filename();
- FILE *gfile = fopen_u(cha_fil.c_str(), "rb");
-
- // Don't overwrite existing bones!
- if (gfile != NULL)
+ if (_list_bones().size() >= GHOST_LIMIT)
{
#ifdef BONES_DIAGNOSTICS
if (do_diagnostics)
- mprf(MSGCH_DIAGNOSTICS, "Ghost file for this level already exists.");
+ mprf(MSGCH_DIAGNOSTICS, "Too many ghosts for this level already!");
#endif
- fclose(gfile);
return;
}
- ghosts = ghost_demon::find_ghosts();
-
- if (ghosts.empty())
+ string g_file_name = "";
+ FILE* ghost_file = _make_bones_file(&g_file_name);
+
+ if (!ghost_file)
{
#ifdef BONES_DIAGNOSTICS
if (do_diagnostics)
- mprf(MSGCH_DIAGNOSTICS, "Could not find any ghosts for this level.");
+ mprf(MSGCH_DIAGNOSTICS, "Could not open file to save ghosts.");
#endif
return;
}
-
- safe_file_writer sw(cha_fil, "wb", true);
- writer outw(cha_fil, sw.open());
-
+
+ writer outw(g_file_name, ghost_file);
+
_write_ghost_version(outw);
tag_write(TAG_GHOST, outw);
+ lk_close(ghost_file, "ab", g_file_name);
+
#ifdef BONES_DIAGNOSTICS
if (do_diagnostics)
- mprf(MSGCH_DIAGNOSTICS, "Saved ghost (%s).", cha_fil.c_str());
+ mprf(MSGCH_DIAGNOSTICS, "Saved ghosts (%s).", g_file_name.c_str());
#endif
}
--
1.8.5.2 (Apple Git-48)
|