diff --git a/config/menus.cfg b/config/menus.cfg index 355ce069..222de52e 100644 --- a/config/menus.cfg +++ b/config/menus.cfg @@ -24,7 +24,7 @@ const __genmapitems [ n = (listlen $arg1) loop i $n [ __listmapname = (at $arg1 $i) - menuitemmapload $__listmapname + menuitemmapload $__listmapname [map @__listmapname] next = (+ $i 1) if (= $next $n) [ next = 0 ] [] nextmap = (at $arg1 $next) diff --git a/source/src/menus.cpp b/source/src/menus.cpp index cdeaf5d1..81f51abc 100644 --- a/source/src/menus.cpp +++ b/source/src/menus.cpp @@ -10,57 +10,6 @@ static int menurighttabwidth = 888; // width of sliders and text input fields (i vector menustack; COMMANDF(curmenu, "", () {result(curmenu ? curmenu->name : "");} ); -VARP(browsefiledesc, 0, 1, 1); - -char *getfiledesc(const char *dir, const char *name, const char *ext) -{ - if(!browsefiledesc || !dir || !name || !ext) return NULL; - defformatstring(fn)("%s/%s.%s", dir, name, ext); - path(fn); - string text, demodescalias; - if(!strcmp(ext, "dmo")) - { - stream *f = opengzfile(fn, "rb"); - if(!f) return NULL; - demoheader hdr; - if(f->read(&hdr, sizeof(demoheader))!=sizeof(demoheader) || memcmp(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic))) { delete f; return NULL; } - delete f; - lilswap(&hdr.version, 1); - lilswap(&hdr.protocol, 1); - const char *tag = "(incompatible file) "; - if(hdr.version == DEMO_VERSION) - { - if(hdr.protocol == PROTOCOL_VERSION) tag = ""; - else if(hdr.protocol == -PROTOCOL_VERSION) tag = "(recorded on modded server) "; - } - formatstring(text)("%s%s", tag, hdr.desc); - text[DHDR_DESCCHARS - 1] = '\0'; - formatstring(demodescalias)("demodesc_%s", name); - const char *customdesc = getalias(demodescalias); - if(customdesc) - { - int textlen = strlen(text); - concatformatstring(text, " \n\f4(Description: \f0%s\f4)", customdesc); - ASSERT(MAXSTRLEN > 2 * DHDR_DESCCHARS); - text[textlen + DHDR_DESCCHARS - 1] = '\0'; - } - return newstring(text); - } - else if(!strcmp(ext, "cgz")) - { - stream *f = opengzfile(fn, "rb"); - if(!f) return NULL; - header hdr; - if(f->read(&hdr, sizeof(header))!=sizeof(header) || (strncmp(hdr.head, "CUBE", 4) && strncmp(hdr.head, "ACMP",4))) { delete f; return NULL; } - delete f; - lilswap(&hdr.version, 1); - // hdr.maprevision, hdr.maptitle ... hdr.version, hdr.headersize, - formatstring(text)("%s%s", (hdr.version>MAPVERSION) ? "(incompatible file) " : "", hdr.maptitle); - text[DHDR_DESCCHARS - 1] = '\0'; - return newstring(text); - } - return NULL; -} inline gmenu *setcurmenu(gmenu *newcurmenu) // only change curmenu through here! { @@ -365,25 +314,24 @@ struct mitemimage : mitemimagemanual DELETEA(desc); } }; -VARP(maploaditemlength, 0, 46, 255); -struct mitemmaploadmanual : mitemmanual + +VARP(browsefiledesc, 0, 1, 1); + +struct mitemmapload : mitemmanual { - const char *filename; - string maptitle; + const char *filename, *maptitle; Texture *image; - mitemmaploadmanual(gmenu *parent, const char *filename, const char *altfontname, char *text, char *action, char *hoveraction, color *bgcolor, const char *desc = NULL) : mitemmanual(parent, text, action, NULL, NULL, NULL), filename(filename) - { - image = NULL; - maptitle[0] = '\0'; - } + mitemmapload(gmenu *parent, const char *filename, char *text, char *action, char *hoveraction, const char *desc) : mitemmanual(parent, text, action, hoveraction, NULL, desc), filename(filename), maptitle(NULL), image(NULL) {} - virtual ~mitemmaploadmanual() {} - - virtual int width() + virtual ~mitemmapload() { - if(image && *text != '\t') return (FONTH*image->xs)/image->ys + FONTH/2 + mitemmanual::width(); - return mitemmanual::width(); + delstring(filename); + DELETEA(maptitle); + DELETEA(text); + DELETEA(action); + DELETEA(hoveraction); + DELETEA(desc); } virtual void key(int code, bool isdown, int unicode) @@ -398,22 +346,17 @@ struct mitemmaploadmanual : mitemmanual draw_text(text, x, y); if(isselection()) { - if(!image) - { + if(!image && !maptitle) + { // load map description and preview picture silent_texture_load = true; - if(filename) - { // load map description and preview picture - const char *cgzpath = "packages" PATHDIVS "maps"; - char *d = getfiledesc(cgzpath, behindpath(filename), "cgz"); // check for regular map - if(!d) d = getfiledesc((cgzpath = "packages" PATHDIVS "maps" PATHDIVS "official"), behindpath(filename), "cgz"); // check for official map - if(d) - { - filtertext(maptitle, d, FTXT__MAPMSG); - delstring(d); - } - defformatstring(p2p)("%s/preview/%s.jpg", cgzpath, filename); - if(!hidebigmenuimages) image = textureload(p2p, 3); + const char *cgzpath = "packages" PATHDIVS "maps"; + if(browsefiledesc) + { // checking maptitles is slow and may be disabled + maptitle = getfiledesc(cgzpath, behindpath(filename), "cgz"); // check for regular map + if(!maptitle) maptitle = getfiledesc((cgzpath = "packages" PATHDIVS "maps" PATHDIVS "official"), behindpath(filename), "cgz"); // check for official map } + defformatstring(p2p)("%s/preview/%s.jpg", cgzpath, filename); + if(!hidebigmenuimages) image = textureload(p2p, 3); if(!image || image == notexture) image = textureload("packages/misc/nopreview.jpg", 3); silent_texture_load = false; } @@ -424,28 +367,11 @@ struct mitemmaploadmanual : mitemmanual framedquadtexture(image->id, xp, yp, xs, ys, FONTH); draw_text(text, xp + xs/2 - text_width(text)/2, yp + ys); } - if(maptitle[0]) draw_text(maptitle, x / 6, VIRTH - FONTH/2, 0xFF, 0xFF, 0xFF, 0xFF, -1, (x * 3) / 4); + if(maptitle && *maptitle) draw_text(maptitle, x / 6, VIRTH - FONTH/2, 0xFF, 0xFF, 0xFF, 0xFF, -1, (x * 3) / 4); } } }; -struct mitemmapload : mitemmaploadmanual -{ - mitemmapload(gmenu *parent, const char *filename, char *text, char *action, char *hoveraction, color *bgcolor, const char *desc = NULL) : mitemmaploadmanual(parent, filename, NULL, text, action, hoveraction, bgcolor, desc) {} -// mitemimage (gmenu *parent, const char *filename, char *text, char *action, char *hoveraction, color *bgcolor, const char *desc = NULL) : mitemimagemanual (parent, filename, NULL, text, action, hoveraction, bgcolor, desc) {} -// mitemmapload(gmenu *parent, const char *filename, char *text, char *action, char *hoveraction, color *bgcolor, const char *desc = NULL) : mitemmaploadmanual(parent, filename, NULL, text, action, hoveraction, bgcolor, desc) {} -// mitemmanual (gmenu *parent, char *text, char *action, char *hoveraction, color *bgcolor, const char *desc) : mitem(parent, bgcolor), text(text), action(action), hoveraction(hoveraction), desc(desc) {} - - virtual ~mitemmapload() - { - DELETEA(filename); - //DELETEA(maptitle); - //DELETEA(mapstats); - DELETEA(text); - DELETEA(action); - } -}; - // text input item struct mitemtextinput : mitemtext @@ -855,15 +781,14 @@ void *addmenu(const char *name, const char *title, bool allowinput, void (__cdec m->menusel = 0; } m->title = title; - m->mdl = NULL; + DELSTRING(m->mdl); m->allowinput = allowinput; m->inited = false; m->hotkeys = hotkeys; m->refreshfunc = refreshfunc; m->keyfunc = keyfunc; - m->usefont = NULL; + DELSTRING(m->usefont); m->allowblink = false; - m->dirlist = NULL; m->forwardkeys = forwardkeys; lastmenu = m; return m; @@ -1009,15 +934,12 @@ void menuitemaltfont(char *altfont, char *text, char *action, char *hoveraction, } COMMAND(menuitemaltfont, "sssss"); -void menuitemmapload(char *name, char *text) +void menuitemmapload(char *name, char *action, char *hoveraction, char *desc) { if(!lastmenu) return; - string caction; - if(!text || text[0]=='\0') formatstring(caction)("map %s", name); - else formatstring(caction)("%s", text); - lastmenu->items.add(new mitemmapload(lastmenu, newstring(name), newstring(name), newstring(caction), NULL, NULL, NULL)); + lastmenu->items.add(new mitemmapload(lastmenu, newstring(name), newstring(name), *action ? newstring(action) : NULL, *hoveraction ? newstring(hoveraction) : NULL, *desc ? newstring(desc) : NULL)); } -COMMAND(menuitemmapload, "ss"); +COMMAND(menuitemmapload, "ssss"); void menuitemtextinput(char *text, char *value, char *action, char *hoveraction, int *maxchars, int *maskinput) { @@ -1083,19 +1005,29 @@ COMMAND(menumdl, "sssii"); void menudirlist(char *dir, char *ext, char *action, int *image, char *searchfile) { - if(!lastmenu) return; - if(!action || !action[0]) return; - gmenu *menu = lastmenu; - if(menu->dirlist) delete menu->dirlist; - mdirlist *d = menu->dirlist = new mdirlist; + if(!lastmenu || !*action) return; + if(lastmenu->dirlist) delete lastmenu->dirlist; + mdirlist *d = lastmenu->dirlist = new mdirlist; d->dir = newstring(dir); d->ext = ext[0] ? newstring(ext): NULL; d->action = action[0] ? newstring(action) : NULL; d->image = *image!=0; d->searchfile = searchfile[0] ? newstring(searchfile) : NULL; + d->subdiraction = NULL; + d->subdirdots = false; } COMMAND(menudirlist, "sssis"); +void menudirlistsub(char *action, int *dots) +{ + if(!lastmenu || !lastmenu->dirlist || !*action) return; + mdirlist *d = lastmenu->dirlist; + d->subdirdots = *dots != 0; + DELETEA(d->subdiraction); + d->subdiraction = *action ? newstring(action) : NULL; +} +COMMAND(menudirlistsub, "si"); + void chmenutexture(char *menu, char *texname, char *title) { gmenu *m = *menu ? menus.access(menu) : NULL; @@ -1348,13 +1280,27 @@ void gmenu::init() if(dirlist && dirlist->dir && dirlist->ext) { items.deletecontents(); - vector files; - listfiles(dirlist->dir, dirlist->ext, files); + + if(dirlist->subdiraction) + { + vector subdirs; + if(dirlist->subdirdots) subdirs.add(newstring("..")); + listsubdirs(dirlist->dir, subdirs, stringsort); + loopv(subdirs) + { + defformatstring(cdir)("\fs\f1[%s]\fr", subdirs[i]); + defformatstring(act)("%s %s", dirlist->subdiraction, escapestring(subdirs[i])); + items.add(new mitemtext(this, newstring(cdir), newstring(act), NULL, NULL, NULL)); + } + } + defformatstring(sortorderalias)("menufilesort_%s", dirlist->ext); int sortorderindex = 0; const char *customsortorder = getalias(sortorderalias); if(customsortorder) sortorderindex = getlistindex(customsortorder, menufilesortorders, true, 0); - files.sort(menufilesortcmp[sortorderindex]); + + vector files; + listfiles(dirlist->dir, dirlist->ext, files, menufilesortcmp[sortorderindex]); string searchfileuc; if(dirlist->searchfile) @@ -1366,10 +1312,7 @@ void gmenu::init() loopv(files) { - char *f = files[i]; - if(!f || !f[0]) continue; - char *d = strcmp(dirlist->ext, "cgz") || dirlist->searchfile ? getfiledesc(dirlist->dir, f, dirlist->ext) : NULL; - defformatstring(jpgname)("%s/preview/%s.jpg", dirlist->dir, f); + char *f = files[i], *d = (strcmp(dirlist->ext, "cgz") || dirlist->searchfile) && browsefiledesc ? getfiledesc(dirlist->dir, f, dirlist->ext) : NULL; bool filefound = false; if(dirlist->searchfile) { @@ -1397,7 +1340,7 @@ void gmenu::init() { if(!dirlist->searchfile || filefound) { - items.add(new mitemmapload(this, newstring(f), newstring(f), newstring(dirlist->action), NULL, NULL, NULL)); + items.add(new mitemmapload(this, newstring(f), newstring(f), newstring(dirlist->action), NULL, NULL)); } DELSTRING(d); } diff --git a/source/src/protos.h b/source/src/protos.h index a9455f3e..d0165971 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -209,14 +209,11 @@ struct mitem struct mdirlist { - char *dir, *ext, *action, *searchfile; - bool image; + char *dir, *ext, *action, *searchfile, *subdiraction; + bool image, subdirdots; ~mdirlist() { - DELETEA(dir); - DELETEA(ext); - DELETEA(action); - DELETEA(searchfile); + DELETEA(dir); DELETEA(ext); DELETEA(action); DELETEA(searchfile); DELETEA(subdiraction); } }; @@ -849,6 +846,7 @@ extern const char *setnames(const char *name); extern void save_world(char *mname, bool skipoptimise = false, bool addcomfort = false); extern int _ignoreillegalpaths; extern int load_world(char *mname); +extern char *getfiledesc(const char *dir, const char *name, const char *ext); 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); diff --git a/source/src/stream.cpp b/source/src/stream.cpp index 6955fea5..e0a43b06 100644 --- a/source/src/stream.cpp +++ b/source/src/stream.cpp @@ -279,7 +279,7 @@ bool listsubdir(const char *dir, vector &subdirs) if(Find != INVALID_HANDLE_VALUE) { do { - if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) subdirs.add(newstring(FindFileData.cFileName)); + if((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && FindFileData.cFileName[0] != '.') subdirs.add(newstring(FindFileData.cFileName)); } while(FindNextFile(Find, &FindFileData)); FindClose(Find); return true; @@ -294,12 +294,12 @@ bool listsubdir(const char *dir, vector &subdirs) while(!readdir_r(d, &b, &de) && de != NULL) { #ifdef _DIRENT_HAVE_D_TYPE - if(de->d_type == DT_DIR) subdirs.add(newstring(de->d_name)); + if(de->d_type == DT_DIR && de->d_name[0] != '.') subdirs.add(newstring(de->d_name)); #else struct stat s; int dl = (int)strlen(pathname); concatformatstring(pathname, "/%s", de->d_name); - if(!lstat(pathname, &s) && S_ISDIR(s.st_mode)) subdirs.add(newstring(de->d_name)); + if(!lstat(pathname, &s) && S_ISDIR(s.st_mode) && de->d_name[0] != '.') subdirs.add(newstring(de->d_name)); pathname[dl] = '\0'; #endif } diff --git a/source/src/tools.h b/source/src/tools.h index ca92f93d..0e7e655c 100644 --- a/source/src/tools.h +++ b/source/src/tools.h @@ -954,6 +954,7 @@ extern char *loadfile(const char *fn, int *size, const char *mode = NULL); extern int streamcopy(stream *dest, stream *source, int maxlen = INT_MAX); extern void filerotate(const char *basename, const char *ext, int keepold, const char *oldformat = NULL); extern const char *stream_capabilities(); +extern void listsubdirs(const char *dir, vector &subdirs, int (__cdecl *sf)(const char **, const char **)); extern bool listdir(const char *dir, const char *ext, vector &files); extern void listfiles(const char *dir, const char *ext, vector &files, int (__cdecl *sf)(const char **, const char **) = NULL); extern void listfilesrecursive(const char *dir, vector &files, int level = 0); diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index 33dd4776..4bff3596 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -1085,6 +1085,68 @@ COMMANDF(loadmap, "s", (char *mapname) intret(!multiplayer("loadmap") ? load_world(mapname) : -42); }); +char *getfiledesc(const char *dir, const char *name, const char *ext) // extract demo and map descriptions +{ + if(!dir || !name || !ext) return NULL; + defformatstring(fn)("%s/%s.%s", dir, name, ext); + path(fn); + string text, demodescalias; + if(!strcmp(ext, "dmo")) + { + stream *f = opengzfile(fn, "rb"); + if(!f) return NULL; + demoheader hdr; + if(f->read(&hdr, sizeof(demoheader))!=sizeof(demoheader) || memcmp(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic))) { delete f; return NULL; } + delete f; + lilswap(&hdr.version, 1); + lilswap(&hdr.protocol, 1); + const char *tag = "(incompatible file) "; + if(hdr.version == DEMO_VERSION) + { + if(hdr.protocol == PROTOCOL_VERSION) tag = ""; + else if(hdr.protocol == -PROTOCOL_VERSION) tag = "(recorded on modded server) "; + } + formatstring(text)("%s%s", tag, hdr.desc); + text[DHDR_DESCCHARS - 1] = '\0'; + formatstring(demodescalias)("demodesc_%s", name); + const char *customdesc = getalias(demodescalias); + if(customdesc) + { + int textlen = strlen(text); + concatformatstring(text, " \n\f4(Description: \f0%s\f4)", customdesc); + ASSERT(MAXSTRLEN > 2 * DHDR_DESCCHARS); + text[textlen + DHDR_DESCCHARS - 1] = '\0'; + } + return newstring(text); + } + else if(!strcmp(ext, "cgz")) + { + stream *f = opengzfile(fn, "rb"); + if(!f) return NULL; + header hdr; + if(f->read(&hdr, sizeof(header))!=sizeof(header) || (strncmp(hdr.head, "CUBE", 4) && strncmp(hdr.head, "ACMP",4))) { delete f; return NULL; } + delete f; + lilswap(&hdr.version, 1); + filtertext(hdr.maptitle, hdr.maptitle, FTXT__MAPMSG, 127); + formatstring(text)("%s%s", (hdr.version>MAPVERSION) ? "(incompatible file) " : "", hdr.maptitle); + text[DHDR_DESCCHARS - 1] = '\0'; + return newstring(text); + } + return NULL; +} + +void enumfiles(char *dir, char *ext) +{ + vector res; + vector files; + if(!strcasecmp(ext, "dir")) listsubdirs(dir, files, stringsort); + else listfiles(dir, *ext ? ext : NULL, files, stringsort); + loopv(files) cvecprintf(res, "%s\n", escapestring(files[i])); + files.deletearrays(); + resultcharvector(res, -1); +} +COMMAND(enumfiles, "ss"); + // support reading and writing binary data in config files static bool hexbinenabled = false;