From 1310516c14057f139a683272c55ca213ce2c5883 Mon Sep 17 00:00:00 2001 From: ac-stef Date: Tue, 30 Aug 2016 20:56:58 +0200 Subject: [PATCH] keep track of map load errors Collect warnings during load_world() and issue a warning at the end. Most, but not all warnings already printed a message - which usually scrolls out of the console quite fast. The collected value is also given back by the loadmap command, which allows batch editing scripts for maps stop at problematic maps. Mapsound errors are not detected, if soundvol is zero. Missing textures are not flagged, if notexture is set. The error code is ored together from those elements: 0x0000001 entity attribute overflow One or more map entities had attribute values, that are no longer supported with map format 10. Rework manually. 0x0000010 map decoder error Indicates a damaged map file. 0x0000100 world error -1 Map is completely solid. Very funny. 0x0000200 world error -2 Map border is not solid. Fix manually. 0x0001000 missing skymap texture files 0x0002000 missing world texture files (only if no notexture is set) 0x0004000 missing mapmodel files 0x0008000 missing mapsound files 0x0010000 bad mapsound command in map config 0x0020000 bad mapmodel command in map config 0x0040000 bad texture command in map config 0x0080000 bad loadnotexture command in map config 0x0100000 error during loading of mapmodels (see earlier console output) 0x1000000 out of range integer value in map config (for example, "fog 0") 0x2000000 out of range float value in map config 0x4000000 generic cubescript error in map config The command loadmap is now also available for non-debug builds. DOCREF loadmap --- source/src/audiomanager.cpp | 25 ++++++++++++----- source/src/clients2c.cpp | 4 +-- source/src/command.cpp | 4 ++- source/src/md2.h | 16 +++++------ source/src/md3.h | 22 ++++++++------- source/src/protos.h | 10 ++++--- source/src/rendermodel.cpp | 22 ++++++++++----- source/src/sound.h | 4 +-- source/src/texture.cpp | 14 +++++++--- source/src/worldio.cpp | 53 ++++++++++++++++++++++--------------- 10 files changed, 109 insertions(+), 65 deletions(-) diff --git a/source/src/audiomanager.cpp b/source/src/audiomanager.cpp index 7fe21a14..8b46eb00 100644 --- a/source/src/audiomanager.cpp +++ b/source/src/audiomanager.cpp @@ -233,20 +233,23 @@ int audiomanager::addsound(char *name, int vol, int maxuses, bool loop, vectorload(trydl) && !trydl) conoutf("\f3failed to load sample %s", buf->name); + if(!buf->load(trydl) && !trydl) { conoutf("\f3failed to load sample %s", buf->name); return false; } + return true; } -void audiomanager::preloadmapsounds(bool trydl) +bool audiomanager::preloadmapsounds(bool trydl) { + int missing = 0; loopv(ents) { entity &e = ents[i]; - if(e.type==SOUND) preloadmapsound(e, trydl); + if(e.type == SOUND && !preloadmapsound(e, trydl)) missing++; } + return !missing; } void audiomanager::applymapsoundchanges() // during map editing, drop all mapsounds so they can be re-added @@ -820,7 +823,11 @@ COMMANDF(mapsound, "si", (char *name, int *maxuses) filtertext(name, name, FTXT__MEDIAFILEPATH); defformatstring(stripped)("%s%s", mapsoundbasepath, name); unixpath(path(stripped)); - if(mapconfigdata.mapsoundlines.length() > 254) conoutf("\f3error: too many mapsounds"); + if(mapconfigdata.mapsoundlines.length() > 254) + { + conoutf("\f3error: too many mapsounds"); + flagmapconfigerror(LWW_CONFIGERR); + } else if(!strncmp(stripped, mapsoundbasepath, mapsoundbasepath_n)) { name = stripped + mapsoundbasepath_n; @@ -832,7 +839,11 @@ COMMANDF(mapsound, "si", (char *name, int *maxuses) mapsoundchanged = 1; flagmapconfigchange(); } - else conoutf("\f3error: mapsound \"%s\" outside packages/audio/", stripped); + else + { + conoutf("\f3error: mapsound \"%s\" outside packages/audio/", stripped); + flagmapconfigerror(LWW_CONFIGERR); + } }); COMMANDF(registermusic, "s", (char *name) diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 3f40db1d..ee5bad28 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -37,11 +37,11 @@ bool changemapserv(char *name, int mode, int download, int revision) // f if(m_demo) return true; if(m_coop) { - if(!name[0] || !load_world(name)) empty_world(0, true); + if(!name[0] || load_world(name) < 0) empty_world(0, true); return true; } else if(player1->state==CS_EDITING) { /*conoutf("SANITY drop from EDITING");*/ toggleedit(true); } // fix stuck-in-editmode bug - bool loaded = load_world(name); + bool loaded = load_world(name) >= 0; if(download > 0) { bool revmatch = hdr.maprevision == revision || revision == 0; diff --git a/source/src/command.cpp b/source/src/command.cpp index 3c8f17cc..d48da5f4 100644 --- a/source/src/command.cpp +++ b/source/src/command.cpp @@ -691,6 +691,7 @@ char *executeret(const char *p) // all evaluation hap { i1 = i1minval ? id->minval : id->maxval; // clamp to valid range conoutf("valid range for %s is %d..%d", id->name, id->minval, id->maxval); + flagmapconfigerror(LWW_SCRIPTERR); } *id->storage.i = i1; if(id->fun) ((void (__cdecl *)())id->fun)(); // call trigger function if available @@ -707,7 +708,7 @@ char *executeret(const char *p) // all evaluation hap { f1 = f1minvalf ? id->minvalf : id->maxvalf; // clamp to valid range conoutf("valid range for %s is %s..%s", id->name, floatstr(id->minvalf), floatstr(id->maxvalf)); - //scripterr(); // Why throw this error here when it's not done for ID_VAR above? Only difference is datatype, both are "valid range errors". // Bukz 2011june04 + flagmapconfigerror(LWW_SCRIPTERR * 2); } *id->storage.f = f1; if(id->fun) ((void (__cdecl *)())id->fun)(); // call trigger function if available @@ -1023,6 +1024,7 @@ void scripterr() { if(curcontext) conoutf("(%s: %s)", curcontext, curinfo); else conoutf("(from console or builtin)"); + flagmapconfigerror(LWW_SCRIPTERR * 4); } void setcontext(const char *context, const char *info) diff --git a/source/src/md2.h b/source/src/md2.h index b8d2303d..52619301 100644 --- a/source/src/md2.h +++ b/source/src/md2.h @@ -257,7 +257,7 @@ struct md2 : vertmodel Texture *skin; loadskin(loadname, pname, skin); loopv(mdl.meshes) mdl.meshes[i]->skin = skin; - if(skin==notexture) conoutf("could not load model skin for %s", name1); + if(skin==notexture) { conoutf("could not load model skin for %s", name1); flagmapconfigerror(LWW_MODELERR); } loadingmd2 = this; defformatstring(name2)("packages/models/%s/md2.cfg", loadname); per_idents = false; @@ -280,15 +280,15 @@ struct md2 : vertmodel void md2anim(char *anim, int *frame, int *range, float *speed) { - if(!loadingmd2 || loadingmd2->parts.empty()) { conoutf("not loading an md2"); return; } + if(!loadingmd2 || loadingmd2->parts.empty()) { conoutf("not loading an md2"); flagmapconfigerror(LWW_MODELERR); return; } int num = findanim(anim); - if(num<0) { conoutf("could not find animation %s", anim); return; } + if(num<0) { conoutf("could not find animation %s", anim); flagmapconfigerror(LWW_MODELERR); return; } loadingmd2->parts.last()->setanim(num, *frame, *range, *speed); } void md2tag(char *name, char *vert1, char *vert2, char *vert3, char *vert4) { - if(!loadingmd2 || loadingmd2->parts.empty()) { conoutf("not loading an md2"); return; } + if(!loadingmd2 || loadingmd2->parts.empty()) { conoutf("not loading an md2"); flagmapconfigerror(LWW_MODELERR); return; } md2::part &mdl = *loadingmd2->parts.last(); int indexes[4] = { -1, -1, -1, -1 }, numverts = 0; loopi(4) @@ -319,17 +319,17 @@ void md2tag(char *name, char *vert1, char *vert2, char *vert3, char *vert4) } if(!mdl.meshes.empty()) indexes[i] = mdl.meshes[0]->findvert(axis, dir); } - if(indexes[i] < 0) { conoutf("could not find vertex %s", vert); return; } + if(indexes[i] < 0) { conoutf("could not find vertex %s", vert); flagmapconfigerror(LWW_MODELERR); return; } numverts = i + 1; } - if(!mdl.gentag(name, indexes, numverts)) { conoutf("could not generate tag %s", name); return; } + if(!mdl.gentag(name, indexes, numverts)) { conoutf("could not generate tag %s", name); flagmapconfigerror(LWW_MODELERR); return; } } void md2emit(char *tag, int *type, int *arg1, int *arg2) { - if(!loadingmd2 || loadingmd2->parts.empty()) { conoutf("not loading an md2"); return; }; + if(!loadingmd2 || loadingmd2->parts.empty()) { conoutf("not loading an md2"); flagmapconfigerror(LWW_MODELERR); return; }; md2::part &mdl = *loadingmd2->parts.last(); - if(!mdl.addemitter(tag, *type, *arg1, *arg2)) { conoutf("could not find tag %s", tag); return; } + if(!mdl.addemitter(tag, *type, *arg1, *arg2)) { conoutf("could not find tag %s", tag); flagmapconfigerror(LWW_MODELERR); return; } } COMMAND(md2anim, "siif"); diff --git a/source/src/md3.h b/source/src/md3.h index d1f4c597..bf5cdcca 100644 --- a/source/src/md3.h +++ b/source/src/md3.h @@ -63,6 +63,7 @@ struct md3 : vertmodel { delete f; conoutf("md3: corrupted header"); + flagmapconfigerror(LWW_MODELERR); return false; } @@ -244,7 +245,7 @@ struct md3 : vertmodel Texture *skin; loadskin(loadname, pname, skin); loopv(mdl.meshes) mdl.meshes[i]->skin = skin; - if(skin==notexture) conoutf("could not load model skin for %s", name1); + if(skin==notexture) { conoutf("could not load model skin for %s", name1); flagmapconfigerror(LWW_MODELERR); } } loopv(parts) parts[i]->scaleverts(scale/16.0f, vec(translate.x, -translate.y, translate.z)); radius = calcradius(zradius); @@ -256,20 +257,20 @@ struct md3 : vertmodel void md3load(char *model) { - if(!loadingmd3) { conoutf("not loading an md3"); return; }; + if(!loadingmd3) { conoutf("not loading an md3"); flagmapconfigerror(LWW_MODELERR); return; }; filtertext(model, model, FTXT__MEDIAFILEPATH); defformatstring(filename)("%s/%s", md3dir, model); md3::md3part &mdl = *new md3::md3part; loadingmd3->parts.add(&mdl); mdl.model = loadingmd3; mdl.index = loadingmd3->parts.length()-1; - if(!mdl.load(path(filename))) conoutf("could not load %s", filename); // ignore failure + if(!mdl.load(path(filename))) { conoutf("could not load %s", filename); flagmapconfigerror(LWW_MODELERR); } // ignore failure } void md3skin(char *objname, char *skin) { filtertext(skin, skin, FTXT__MEDIAFILEPATH); - if(!loadingmd3 || loadingmd3->parts.empty()) { conoutf("not loading an md3"); return; }; + if(!loadingmd3 || loadingmd3->parts.empty()) { conoutf("not loading an md3"); flagmapconfigerror(LWW_MODELERR); return; }; md3::part &mdl = *loadingmd3->parts.last(); bool used = false; loopv(mdl.meshes) @@ -287,29 +288,30 @@ void md3skin(char *objname, char *skin) defformatstring(s)(", possibilities are: *"); loopv(mdl.meshes) concatformatstring(s, "|%s", mdl.meshes[i]->name); conoutf("mesh \"%s\" not found in model %s, skin %s not loaded%s", objname, loadingmd3->loadname, skin, mdl.meshes.length() ? s : ""); + flagmapconfigerror(LWW_MODELERR); } } void md3anim(char *anim, int *startframe, int *range, float *speed) { - if(!loadingmd3 || loadingmd3->parts.empty()) { conoutf("not loading an md3"); return; }; + if(!loadingmd3 || loadingmd3->parts.empty()) { conoutf("not loading an md3"); flagmapconfigerror(LWW_MODELERR); return; }; int num = findanim(anim); - if(num<0) { conoutf("could not find animation %s", anim); return; }; + if(num<0) { conoutf("could not find animation %s", anim); flagmapconfigerror(LWW_MODELERR); return; }; loadingmd3->parts.last()->setanim(num, *startframe, *range, *speed); } void md3link(int *parent, int *child, char *tagname) { if(!loadingmd3) { conoutf("not loading an md3"); return; }; - if(!loadingmd3->parts.inrange(*parent) || !loadingmd3->parts.inrange(*child)) { conoutf("no models loaded to link"); return; } - if(!loadingmd3->parts[*parent]->link(loadingmd3->parts[*child], tagname)) conoutf("could not link model %s", loadingmd3->loadname); + if(!loadingmd3->parts.inrange(*parent) || !loadingmd3->parts.inrange(*child)) { conoutf("no models loaded to link"); flagmapconfigerror(LWW_MODELERR); return; } + if(!loadingmd3->parts[*parent]->link(loadingmd3->parts[*child], tagname)) { conoutf("could not link model %s", loadingmd3->loadname); flagmapconfigerror(LWW_MODELERR); } } void md3emit(char *tag, int *type, int *arg1, int *arg2) { - if(!loadingmd3 || loadingmd3->parts.empty()) { conoutf("not loading an md3"); return; }; + if(!loadingmd3 || loadingmd3->parts.empty()) { conoutf("not loading an md3"); flagmapconfigerror(LWW_MODELERR); return; }; md3::part &mdl = *loadingmd3->parts.last(); - if(!mdl.addemitter(tag, *type, *arg1, *arg2)) { conoutf("could not find tag %s", tag); return; } + if(!mdl.addemitter(tag, *type, *arg1, *arg2)) { conoutf("could not find tag %s", tag); flagmapconfigerror(LWW_MODELERR); return; } } COMMAND(md3load, "s"); diff --git a/source/src/protos.h b/source/src/protos.h index 364940af..b14edb4d 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -429,7 +429,7 @@ extern const char *gettextureslot(int i); extern bool reloadtexture(Texture &t); extern void reloadtextures(); extern void blitsurface(SDL_Surface *dst, SDL_Surface *src, int x, int y); -extern void loadskymap(bool reload); +extern bool loadskymap(bool reload); extern void *texconfig_copy(); extern void texconfig_delete(void *s); extern uchar *texconfig_paste(void *_s, uchar *usedslots); @@ -813,18 +813,20 @@ extern bool addscorchmark(vec &o, float radius = 7); extern void render_particles(int time, int typemask = ~0); // worldio +enum { LWW_ENTATTROVERFLOW = 0x1, LWW_DECODEERR = 0x10, LWW_WORLDERROR = 0x100, LWW_MISSINGMEDIA = 0x1000, LWW_CONFIGERR = 0x10000, LWW_MODELERR = 0x100000, LWW_SCRIPTERR = 0x1000000 }; extern const char *setnames(const char *name); extern void save_world(char *mname, bool skipoptimise = false, bool addcomfort = false); -extern bool load_world(char *mname); +extern int load_world(char *mname); extern void writemap(char *name, int size, uchar *data); extern void writecfggz(char *name, int size, int sizegz, uchar *data); extern uchar *readmap(char *name, int *size, int *revision); extern uchar *readmcfggz(char *name, int *size, int *sizegz); extern void rlencodecubes(vector &f, sqr *s, int len, bool preservesolids); -extern void rldecodecubes(ucharbuf &f, sqr *s, int len, int version, bool silent); +extern bool rldecodecubes(ucharbuf &f, sqr *s, int len, int version, bool silent); extern void clearheaderextras(); extern void automapconfig(); extern void flagmapconfigchange(); +extern void flagmapconfigerror(int what); extern void getcurrentmapconfig(vector &f, bool onlysounds); extern char *mapinfo_license, *mapinfo_comment; extern void setmapinfo(const char *newlicense = NULL, const char *newcomment = NULL); @@ -879,7 +881,7 @@ extern void resetmdlnotfound(); extern void preload_playermodels(); extern void preload_entmodels(); extern void preload_hudguns(); -extern void preload_mapmodels(bool trydl = false); +extern bool preload_mapmodels(bool trydl = false); extern void renderclients(); extern void renderclient(playerent *d); extern void renderclient(playerent *d, const char *mdlname, const char *vwepname, int tex = 0); diff --git a/source/src/rendermodel.cpp b/source/src/rendermodel.cpp index 9501777f..1ff253c5 100644 --- a/source/src/rendermodel.cpp +++ b/source/src/rendermodel.cpp @@ -11,7 +11,7 @@ mapmodelattributes loadingattributes; #include "md2.h" #include "md3.h" -#define checkmdl if(!loadingmodel) { conoutf("not loading a model"); return; } +#define checkmdl if(!loadingmodel) { conoutf("not loading a model"); flagmapconfigerror(LWW_MODELERR); return; } void mdlcullface(int *cullface) { @@ -119,7 +119,11 @@ void mapmodel(int *rad, int *h, int *zoff, char *scale, char *name) mmi.h = *h; mmi.zoff = *zoff; mmi.scale = atof(scale); - if(mmi.scale < 0.25f || mmi.scale > 4.0f) mmi.scale = 1.0f; + if(mmi.scale < 0.25f || mmi.scale > 4.0f) + { + mmi.scale = 1.0f; + if(strcmp(scale, "0")) flagmapconfigerror(LWW_CONFIGERR * 2); + } mmi.m = NULL; filtertext(name, name, FTXT__MEDIAFILEPATH); @@ -382,7 +386,11 @@ model *loadmodel(const char *name, int i, bool trydl) // load model by name { mdllookup.access(m->name(), m); setmodelattributes(m->name(), loadingattributes); - if(m->shadowdist && !m->cullface) conoutf("\f3mapmodel config error: disabling face culling in combination with shadows will cause visual errors (%s)", m->name()); + if(m->shadowdist && !m->cullface) + { + conoutf("\f3mapmodel config error: disabling face culling in combination with shadows will cause visual errors (%s)", m->name()); + flagmapconfigerror(LWW_MODELERR); + } } loadingmodel = NULL; } @@ -896,15 +904,17 @@ void preload_entmodels() } } -void preload_mapmodels(bool trydl) +bool preload_mapmodels(bool trydl) { + int missing = 0; loopv(ents) { entity &e = ents[i]; if(e.type!=MAPMODEL || !mapmodels.inrange(e.attr2)) continue; - loadmodel(NULL, e.attr2, trydl); - if(e.attr4) lookuptexture(e.attr4, notexture, trydl); + if(!loadmodel(NULL, e.attr2, trydl)) missing++; + if(e.attr4 && lookuptexture(e.attr4, notexture, trydl) == notexture) missing++; } + return !missing; } inline void renderhboxpart(playerent *d, vec top, vec bottom, vec up) diff --git a/source/src/sound.h b/source/src/sound.h index b0dc8fff..1d7e2511 100644 --- a/source/src/sound.h +++ b/source/src/sound.h @@ -390,8 +390,8 @@ public: // init & setup void initsound(); - void preloadmapsound(entity &e, bool trydl = false); - void preloadmapsounds(bool trydl = false); + bool preloadmapsound(entity &e, bool trydl = false); + bool preloadmapsounds(bool trydl = false); void applymapsoundchanges(); // configuration diff --git a/source/src/texture.cpp b/source/src/texture.cpp index 527275ef..f80554aa 100644 --- a/source/src/texture.cpp +++ b/source/src/texture.cpp @@ -501,7 +501,11 @@ COMMANDF(texture, "fs", (float *scale, char *name) intret(slots.length()); Slot &s = slots.add(); _texture(s, scale, name); - if(*scale < 0.0f || *scale > 4.0f) conoutf("\f3texture slot #%d \"%s\" error: scale factor %.7g out of range 0..4", slots.length() - 1, name, *scale); + if(*scale < 0.0f || *scale > 4.0f) + { + conoutf("\f3texture slot #%d \"%s\" error: scale factor %.7g out of range 0..4", slots.length() - 1, name, *scale); + flagmapconfigerror(LWW_CONFIGERR * 4); + } flagmapconfigchange(); }); @@ -566,7 +570,7 @@ Texture *sky[6] = {NULL, NULL, NULL, NULL, NULL, NULL}; SVARF(loadsky, "", { loadskymap(false); }); -void loadskymap(bool reload) +bool loadskymap(bool reload) { const char *side[] = { "lf", "rt", "ft", "bk", "dn", "up" }; const char *legacyprefix = "textures/skymaps/"; @@ -582,7 +586,7 @@ void loadskymap(bool reload) } flagmapconfigchange(); } - bool old_silent_texture_load = silent_texture_load; + bool old_silent_texture_load = silent_texture_load, success = true; if(autodownload && execcontext == IEXC_MAPCFG) silent_texture_load = true; loopi(6) { @@ -593,8 +597,10 @@ void loadskymap(bool reload) requirepackage(PCK_SKYBOX, loadsky); break; } + if(sky[i] == notexture && reload) success = false; } silent_texture_load = old_silent_texture_load; + return success; } void loadnotexture(char *c) @@ -606,7 +612,7 @@ void loadnotexture(char *c) filtertext(mapconfigdata.notexturename, c, FTXT__MEDIAFILEPATH); defformatstring(p)("packages/textures/%s", mapconfigdata.notexturename); noworldtexture = textureload(p); - if(noworldtexture==notexture) conoutf("could not load alternative texture '%s'.", p); + if(noworldtexture==notexture) { conoutf("could not load alternative texture '%s'.", p); flagmapconfigerror(LWW_CONFIGERR * 8); } } flagmapconfigchange(); } diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index fb0a66b9..ab9cdecc 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -168,7 +168,7 @@ void rlencodecubes(vector &f, sqr *s, int len, bool preservesolids) // ru spurge; } -void rldecodecubes(ucharbuf &f, sqr *s, int len, int version, bool silent) // run-length decoding of a series of cubes (version is only relevant, if < 6) +bool rldecodecubes(ucharbuf &f, sqr *s, int len, int version, bool silent) // run-length decoding of a series of cubes (version is only relevant, if < 6) { sqr *t = NULL, *e = s + len; while(s < e) @@ -215,7 +215,7 @@ void rldecodecubes(ucharbuf &f, sqr *s, int len, int version, bool silent) // ru if(type<0 || type>=MAXTYPE) { if(!silent) conoutf("while reading map at %d: type %d out of range", int(cubicsize - (e - s)), type); - f.overread(); + f.forceoverread(); continue; } sqrdefault(s); @@ -236,6 +236,7 @@ void rldecodecubes(ucharbuf &f, sqr *s, int len, int version, bool silent) // ru t = s; s++; } + return !f.overread(); // true: no problem } // headerextra stores additional data in a map file (support since format 10) @@ -282,7 +283,7 @@ void unpackheaderextra(uchar *buf, int len) // break the extra data from the ma { int len = getuint(p), flags = getuint(p), type = flags & HX_TYPEMASK; if(p.overread() || len > p.remaining()) break; - clientlogf(" found headerextra \"%s\", %d bytes%s", hx_name(type), len, flags & HX_FLAG_PERSIST ? "persistent" : ""); // debug info + clientlogf(" found headerextra \"%s\", %d bytes%s", hx_name(type), len, flags & HX_FLAG_PERSIST ? ", persistent" : ""); // debug info headerextras.add(new headerextra(len, flags, p.subbuf(len).buf)); } } @@ -414,6 +415,13 @@ void flagmapconfigchange() if(execcontext != IEXC_MAPCFG && hdr.flags & MHF_AUTOMAPCONFIG) unsavededits++; } +int mapconfigerror = 0; + +void flagmapconfigerror(int what) +{ + if(execcontext == IEXC_MAPCFG || execcontext == IEXC_MDLCFG) mapconfigerror |= what; +} + void getcurrentmapconfig(vector &f, bool onlysounds) { if(!onlysounds) @@ -757,7 +765,7 @@ extern float Mh; static string lastloadedconfigfile; -bool load_world(char *mname) // still supports all map formats that have existed since the earliest cube betas! +int load_world(char *mname) // still supports all map formats that have existed since the earliest cube betas! { const int sizeof_header = sizeof(header), sizeof_baseheader = sizeof_header - sizeof(int) * 16; stopwatch watch; @@ -775,23 +783,23 @@ bool load_world(char *mname) // still supports all map formats that have if(!validmapname(mname)) { conoutf("\f3Invalid map name. It must only contain letters, digits, '-', '_' and be less than %d characters long", MAXMAPNAMELEN); - return false; + return -1; } stream *f = opengzfile(cgzname, "rb"); - if(!f) { conoutf("\f3could not read map %s", cgzname); return false; } + if(!f) { conoutf("\f3could not read map %s", cgzname); return -2; } if(unsavededits) xmapbackup("load_map_", mname); unsavededits = 0; DEBUG("reading map \"" << cgzname << "\""); header tmp; memset(&tmp, 0, sizeof_header); if(f->read(&tmp, sizeof_baseheader) != sizeof_baseheader || - (strncmp(tmp.head, "CUBE", 4)!=0 && strncmp(tmp.head, "ACMP",4)!=0)) { conoutf("\f3while reading map: header malformatted (1)"); delete f; return false; } + (strncmp(tmp.head, "CUBE", 4)!=0 && strncmp(tmp.head, "ACMP",4)!=0)) { conoutf("\f3while reading map: header malformatted (1)"); delete f; return -3; } lilswap(&tmp.version, 4); // version, headersize, sfactor, numents - if(tmp.version > MAPVERSION) { conoutf("\f3this map requires a newer version of AssaultCube"); delete f; return false; } - if(tmp.sfactorLARGEST_FACTOR || tmp.numents > MAXENTITIES) { conoutf("\f3illegal map size"); delete f; return false; } + if(tmp.version > MAPVERSION) { conoutf("\f3this map requires a newer version of AssaultCube"); delete f; return -4; } + if(tmp.sfactorLARGEST_FACTOR || tmp.numents > MAXENTITIES) { conoutf("\f3illegal map size"); delete f; return -5; } tmp.headersize = fixmapheadersize(tmp.version, tmp.headersize); int restofhead = min(tmp.headersize, sizeof_header) - sizeof_baseheader; - if(f->read(&tmp.waterlevel, restofhead) != restofhead) { conoutf("\f3while reading map: header malformatted (2)"); delete f; return false; } + if(f->read(&tmp.waterlevel, restofhead) != restofhead) { conoutf("\f3while reading map: header malformatted (2)"); delete f; return -6; } DEBUG("version " << tmp.version << " headersize " << tmp.headersize << " headerextrasize " << tmp.headersize - int(sizeof(header)) << " entities " << tmp.numents << " factor " << tmp.sfactor << " revision " << tmp.maprevision); clearheaderextras(); if(tmp.headersize > sizeof_header) @@ -802,7 +810,7 @@ bool load_world(char *mname) // still supports all map formats that have if(extrasize) { // map file actually has extra header data that we want too preserve uchar *extrabuf = new uchar[extrasize]; - if(f->read(extrabuf, extrasize) != extrasize) { conoutf("\f3while reading map: header malformatted (3)"); delete f; return false; } + if(f->read(extrabuf, extrasize) != extrasize) { conoutf("\f3while reading map: header malformatted (3)"); delete f; return -7; } unpackheaderextra(extrabuf, extrasize); delete[] extrabuf; } @@ -813,6 +821,7 @@ bool load_world(char *mname) // still supports all map formats that have rebuildtexlists(); loadingscreen("%s", hdr.maptitle); resetmap(); + int res = 0; if(hdr.version>=4) { lilswap(&hdr.waterlevel, 1); @@ -852,7 +861,7 @@ bool load_world(char *mname) // still supports all map formats that have #define SCALEATTR(x) \ if((ss = abs(entwraparound[e.type][x - 1] / entscale[e.type][x - 1]))) e.attr##x = (int(e.attr##x) % ss + ss) % ss; \ e.attr##x = ov = e.attr##x * entscale[e.type][x - 1]; \ - if(ov != e.attr##x) conoutf("overflow during conversion of attr%d of entity #%d (%s) - pls check before saving the map", x, i, entnames[e.type]); + if(ov != e.attr##x) { conoutf("overflow during conversion of attr%d of entity #%d (%s) - pls check before saving the map", x, i, entnames[e.type]); res |= LWW_ENTATTROVERFLOW; } SCALEATTR(1); SCALEATTR(2); SCALEATTR(3); @@ -889,13 +898,14 @@ bool load_world(char *mname) // still supports all map formats that have if(q.len < cubicsize) break; } ucharbuf uf(rawcubes.getbuf(), rawcubes.length()); - rldecodecubes(uf, world, cubicsize, hdr.version, false); // decode file + res |= rldecodecubes(uf, world, cubicsize, hdr.version, false) ? 0 : LWW_DECODEERR; // decode file c2skeepalive(); // calculate map statistics servsqr *smallworld = createservworld(world, cubicsize); int we = calcmapdims(clmapdims, smallworld, ssize); if(we) conoutf("world error %d", we); + res |= LWW_WORLDERROR * (iabs(we) & 0xf); delete[] smallworld; @@ -930,6 +940,7 @@ bool load_world(char *mname) // still supports all map formats that have calclight(); conoutf("read map %s rev %d (%d milliseconds)", cgzname, hdr.maprevision, watch.elapsed()); conoutf("%s", hdr.maptitle); + mapconfigerror = 0; pushscontext(IEXC_MAPCFG); // untrusted altogether per_idents = false; neverpersist = true; @@ -975,18 +986,18 @@ bool load_world(char *mname) // still supports all map formats that have c2skeepalive(); - loadskymap(true); + res |= loadskymap(true) ? 0 : LWW_MISSINGMEDIA * 1; watch.start(); - loopi(256) if(texuse[i]) lookupworldtexture(i, false); + loopi(256) if(texuse[i] && lookupworldtexture(i, false) == notexture) res |= LWW_MISSINGMEDIA * 2; clientlogf("loaded textures (%d milliseconds)", texloadtime+watch.elapsed()); c2skeepalive(); watch.start(); - preload_mapmodels(false); + res |= preload_mapmodels(false) ? 0 : LWW_MISSINGMEDIA * 4; clientlogf("loaded mapmodels (%d milliseconds)", mdlloadtime+watch.elapsed()); c2skeepalive(); watch.start(); - audiomgr.preloadmapsounds(false); + res |= audiomgr.preloadmapsounds(false) ? 0 : LWW_MISSINGMEDIA * 8; // also set, if soundvol == 0, because no sounds are loaded (or checked) in that case clientlogf("loaded mapsounds (%d milliseconds)", audioloadtime+watch.elapsed()); c2skeepalive(); @@ -1006,15 +1017,15 @@ bool load_world(char *mname) // still supports all map formats that have defformatstring(startmillis)("%d", millis_()); alias("gametimestart", startmillis, true); startmap(mname); - return true; + res |= mapconfigerror; + if(res) conoutf("\f3unresolved problems occured during load_world(), warning: 0x%x", res); + return res; // negative: error (no map loaded), zero: no problem, positive: some problems (bitmask value) } -#ifdef _DEBUG COMMANDF(loadmap, "s", (char *mapname) { - if(!multiplayer()) load_world(mapname); + intret(!multiplayer() ? load_world(mapname) : -42); }); -#endif // support reading and writing binary data in config files