parsed only once (unless they're modified again). Crawl also keeps only map stubs in memory (name, place, orient, tags) and loads the map body only when it is actually selected by the dungeon builder.
This probably breaks the Windows build, will be fixed soonish.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1637 c06c8d41-db1a-0410-9941-cceddc491573
NCDWWDJQLAU5ORSAQGZKJJ5E22VTDGGPJMVVBWQFHQ2B3U3UFHDQC
W52PCSHX72WAMWKG6L4BPUBVMO6E72KYYBNKAA7554KNOTY6V7WQC
AUXHSGS4EFOPZ6TVZYWNVOUDO7NYKUKE3HBKGQQWTALSVFOE3HAAC
2RSY5J2ED2GM5R564UF5NEPDAEJDGLJJ6U3YO6UBQOX4KSLGNY7QC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
KXUQB3WNWC5IFL6VFWADEPQMMU3VV3NDI5FLA666PGOEWFYUHCLQC
34C4U6EQWERY75GZJKUCM5KVGU2OUICETS5LGZF6RMKMZT4R5SQAC
MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC
6SSW2WIX3LUPDP2QI6Z75YUE53G5H3CZBAPCQA7OEQEPOCIXT73AC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
KCHX2F3JFEWOZT3WMJVZAAQUU2QSZ5Q7RDCD7WUJ7VE65J52JFUQC
A3CO4KBFTFU3ZSHWRY2OPPX3MMTFV7OUCZGL7Q4Y2FU7JO4AP7MAC
JDM27QE4HR52AYFSQE763BFF57ANOTF5MXKMO377PP5EXMN7SAOAC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
QXQE7C5XDYNF4JSHGDAVMS7HYLOC6YEZFOVFJ3RD7RB6U4AJSFIQC
GSQ72ULBSL6WBJZUB3GJKAPQDXZIQV7B2TDBA5OP2WVGHVJMCQFQC
KR655YT3I3U5DMN5NS3FPUGMDNT4BI56K3SFF2FNJ77C45NFKL5AC
2EF3QUVPUQAKBTZKLKQ5B73Z26TXX2H2G2MKIMXD7B7BSDCYE7SAC
TR4NPGNO5QNNRJNVMNSUEO5QLT37HCXXDOBKXCB5XWXRQNAJ5SHAC
Y56C5OMUQ5XF2G6DKDV4R5MED44UOIUPTBBQVWQBUHYIXYA5MOZAC
#include <cstdio>
#include <string>
#include <vector>
#include "mapdef.h"
#include "maps.h"
extern map_def lc_map;
extern level_range lc_range;
extern std::string lc_desfile;
void reset_map_parser();
extern depth_ranges lc_default_depths;
#include "AppHdr.h"
#include "levcomp.h"
std::string lc_desfile;
map_def lc_map;
level_range lc_range;
extern int yylineno;
void reset_map_parser()
{
lc_map.init();
lc_range.reset();
yylineno = 1;
}
lc_default_depths.clear();
depth_ranges lc_default_depths;
#include <vector>
void run_map_preludes();
void reset_map_parser();
std::string get_descache_path(const std::string &file,
const std::string &ext);
extern dlua_chunk map_file_prelude;
extern std::string lc_desfile;
extern map_def lc_map;
extern level_range lc_range;
extern depth_ranges lc_default_depths;
dlua_chunk lc_file_prelude;
std::string lc_desfile;
map_def lc_map;
level_range lc_range;
depth_ranges lc_default_depths;
std::set<std::string> map_files_read;
extern int yylineno;
void reset_map_parser()
{
lc_map.init();
lc_range.reset();
lc_default_depths.clear();
lc_file_prelude.clear();
yylineno = 1;
}
////////////////////////////////////////////////////////////////////////////
static bool checked_des_index_dir = false;
#define DESCACHE_VER 1000
static void check_des_index_dir()
{
if (checked_des_index_dir)
return;
std::string desdir = get_savedir_path("des");
if (!check_dir("Data file cache", desdir, true))
end(1, true, "Can't create data file cache: %s", desdir.c_str());
checked_des_index_dir = true;
}
std::string get_descache_path(const std::string &file,
const std::string &ext)
{
const std::string basename =
change_file_extension(get_base_filename(file), ext);
return get_savedir_path("des/" + basename);
}
static bool verify_file_version(const std::string &file)
{
FILE *inf = fopen(file.c_str(), "rb");
if (!inf)
return (false);
const long ver = readLong(inf);
fclose(inf);
return (ver == DESCACHE_VER);
}
static bool verify_map_index(const std::string &base)
{
return verify_file_version(base + ".idx");
}
static bool verify_map_full(const std::string &base)
{
return verify_file_version(base + ".dsc");
}
static bool load_map_index(const std::string &base)
{
FILE *inf = fopen((base + ".idx").c_str(), "rb");
// Discard version (it's been checked by verify_map_index).
readLong(inf);
const int nmaps = readShort(inf);
const int nexist = vdefs.size();
vdefs.resize( nexist + nmaps, map_def() );
for (int i = 0; i < nmaps; ++i)
{
vdefs[nexist + i].read_index(inf);
vdefs[nexist + i].set_file(base);
}
fclose(inf);
return (true);
}
static bool load_map_cache(const std::string &filename)
{
check_des_index_dir();
const std::string descache_base = get_descache_path(filename, "");
file_lock deslock(descache_base + ".lk", "rb", false);
if (is_newer(filename, descache_base + ".idx")
|| is_newer(filename, descache_base + ".dsc"))
return (false);
if (!verify_map_index(descache_base) || !verify_map_full(descache_base))
return (false);
return load_map_index(descache_base);
}
static void write_map_prelude(const std::string &filebase)
{
const std::string luafile = filebase + ".lua";
if (lc_file_prelude.empty())
{
unlink(luafile.c_str());
return;
}
FILE *outf = fopen(luafile.c_str(), "w");
if (!outf)
end(1, true, "Unable to open %s for writing", luafile.c_str());
fprintf(outf, "%s", lc_file_prelude.lua_string().c_str());
fclose(outf);
}
static void write_map_full(const std::string &filebase, size_t vs, size_t ve)
{
const std::string cfile = filebase + ".dsc";
FILE *outf = fopen(cfile.c_str(), "wb");
if (!outf)
end(1, true, "Unable to open %s for writing", cfile.c_str());
writeLong(outf, DESCACHE_VER);
for (size_t i = vs; i < ve; ++i)
vdefs[i].write_full(outf);
fclose(outf);
}
static void write_map_index(const std::string &filebase, size_t vs, size_t ve)
{
const std::string cfile = filebase + ".idx";
FILE *outf = fopen(cfile.c_str(), "wb");
if (!outf)
end(1, true, "Unable to open %s for writing", cfile.c_str());
writeLong(outf, DESCACHE_VER);
writeShort(outf, ve > vs? ve - vs : 0);
for (size_t i = vs; i < ve; ++i)
vdefs[i].write_index(outf);
fclose(outf);
}
static void write_map_cache(const std::string &filename, size_t vs, size_t ve)
{
check_des_index_dir();
const std::string descache_base = get_descache_path(filename, "");
file_lock deslock(descache_base + ".lk", "wb");
write_map_prelude(descache_base);
write_map_full(descache_base, vs, ve);
write_map_index(descache_base, vs, ve);
}
void map_def::read_full(FILE *inf)
{
// There's a potential race-condition here:
// - If someone modifies a .des file while there are games in progress,
// - a new Crawl process will overwrite the .dsc.
// - older Crawl processes trying to reading the new .dsc will be hosed.
// We could try to recover from the condition (by locking and
// reloading the index), but it's easier to save the game at this
// point and let the player reload.
if (readShort(inf) != 0x1eaf || readString(inf) != name)
save_game(true,
make_stringf("Level file cache for %s is out-of-sync! "
"Please reload your game.",
file.c_str()).c_str());
prelude.set_chunk(readString(inf, LUA_CHUNK_MAX_SIZE));
main.set_chunk(readString(inf, LUA_CHUNK_MAX_SIZE));
}
void map_def::load()
{
if (!index_only)
return;
const std::string loadfile = get_descache_path(file, ".dsc");
FILE *inf = fopen(loadfile.c_str(), "r");
fseek(inf, cache_offset, SEEK_SET);
read_full(inf);
fclose(inf);
index_only = false;
}
void map_def::write_index(FILE *outf) const
{
if (!cache_offset)
end(1, false, "Map %s: can't write index - cache offset not set!",
name.c_str());
writeString(outf, name);
writeShort(outf, orient);
writeLong(outf, chance);
writeLong(outf, cache_offset);
writeString(outf, tags);
writeString(outf, place);
write_depth_ranges(outf);
writeString(outf, prelude.lua_string(), LUA_CHUNK_MAX_SIZE);
}
void map_def::read_index(FILE *inf)
{
name = readString(inf);
orient = static_cast<map_section_type>( readShort(inf) );
chance = readLong(inf);
cache_offset = readLong(inf);
tags = readString(inf);
place = readString(inf);
read_depth_ranges(inf);
prelude.set_chunk(readString(inf, LUA_CHUNK_MAX_SIZE));
index_only = true;
}
void map_def::write_depth_ranges(FILE *outf) const
{
writeShort(outf, depths.size());
for (int i = 0, size = depths.size(); i < size; ++i)
depths[i].write(outf);
}
void map_def::read_depth_ranges(FILE *inf)
{
depths.clear();
const int nranges = readShort(inf);
for (int i = 0; i < nranges; ++i)
depths[i].read(inf);
}
void writeString(FILE* file, const std::string &s, int cap = 200);
std::string readString(FILE *file);
void writeString(FILE* file, const std::string &s, int cap = STR_CAP);
std::string readString(FILE *file, int cap = STR_CAP);
}
std::string get_base_filename(const std::string &filename)
{
std::string::size_type pos = filename.rfind(FILE_SEPARATOR);
if (pos != std::string::npos)
return filename.substr(pos + 1);
#ifdef ALT_FILE_SEPARATOR
pos = filename.rfind(ALT_FILE_SEPARATOR);
if (pos != std::string::npos)
return filename.substr(pos + 1);
#endif
return (filename);
}
std::string change_file_extension(const std::string &filename,
const std::string &ext)
{
const std::string::size_type pos = filename.rfind('.');
return ((pos == std::string::npos? filename : filename.substr(0, pos))
+ ext);
}
time_t file_modtime(const std::string &file)
{
struct stat filestat;
if (stat(file.c_str(), &filestat))
return (0);
return (filestat.st_mtime);
}
// Returns true if file a is newer than file b.
bool is_newer(const std::string &a, const std::string &b)
{
return (file_modtime(a) > file_modtime(b));
}
void check_newer(const std::string &target,
const std::string &dependency,
void (*action)())
{
if (is_newer(dependency, target))
action();
fprintf(stderr, "%s \"%s\" does not exist "
"and I can't create it.\n",
whatdir.c_str(), dir.c_str());
if (!silent)
fprintf(stderr, "%s \"%s\" does not exist "
"and I can't create it.\n",
whatdir.c_str(), dir.c_str());
time_t file_modtime(const std::string &file)
{
struct stat filestat;
if (stat(file.c_str(), &filestat))
return (0);
return (filestat.st_mtime);
}
// Returns true if file a is newer than file b.
bool is_newer(const std::string &a, const std::string &b)
{
return (file_modtime(a) > file_modtime(b));
}
void check_newer(const std::string &target,
const std::string &dependency,
void (*action)())
{
if (is_newer(dependency, target))
action();
}