diff --git a/config/scripts.cfg b/config/scripts.cfg index 0230c04a..e2a43f8a 100644 --- a/config/scripts.cfg +++ b/config/scripts.cfg @@ -84,6 +84,7 @@ looplist [ ] [ cmd options ] [ listcomplete $cmd (listoptions $options) ] listcomplete getvarrange "min max default" +looplist [ addzipmod zipmodremove zipmodgetdesc ] cmd [complete $cmd "mods" zip] // set up list of skymaps push skies "" diff --git a/source/src/protocol.h b/source/src/protocol.h index 3da9cb80..b16d3120 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -175,4 +175,4 @@ enum #define FTXT__GLOBALNAME (FTXT_NOWHITE | FTXT_NOCOLOR | FTXT_FILLBLANKS) #define FTXT__MEDIAFILENAME (FTXT_FILENAME) #define FTXT__MEDIAFILEPATH (FTXT_FILENAME | FTXT_ALLOWSLASH) - +#define FTXT__ZIPDESC (FTXT_NOWHITE | FTXT_ALLOWBLANKS | FTXT_TABTOBLANK | FTXT_NOCOLOR | FTXT_SAFECS) diff --git a/source/src/zip.cpp b/source/src/zip.cpp index 5959ac02..a906dac8 100644 --- a/source/src/zip.cpp +++ b/source/src/zip.cpp @@ -477,8 +477,6 @@ void listmemfiles() COMMAND(listmemfiles, ""); #endif -#endif - // manage zipped mod packages in subdirectory "mods/" including description and preview images // zip packages are restricted to contain files below "packages/" only - unless the zip filename starts with "###", which additionally allows files below "config/" @@ -488,8 +486,6 @@ ziparchive *findzip(const char *name) return NULL; } -#ifndef STANDALONE - const char *fixzipname(char *name) // check, if valid path + filename, based on mods/, return NULL if invalid { string tmp; @@ -556,6 +552,155 @@ void addzipmod(char *name) } COMMAND(addzipmod, "s"); +void zipmodremove(char *name) +{ + const char *pname = fixzipname(name); + ziparchive *exists = pname ? findzip(name) : NULL; + if(!exists) + { + conoutf("zip %s is not loaded", name); + return; + } + if(exists->openfiles) + { + conoutf("zip %s has %d open files", name, exists->openfiles); + return; + } + conoutf("removed zip %s", exists->name); + archives.removeobj(exists); + rebuildzipfilehashtable(); + delete exists; +} +COMMAND(zipmodremove, "s"); + +void zipmodclear() +{ + loopvrev(archives) + { + ziparchive *a = archives[i]; + if(a->openfiles) + { + conoutf("zip %s has %d open files", a->name, a->openfiles); + continue; + } + delete archives.remove(i); + } + rebuildzipfilehashtable(); +} +COMMAND(zipmodclear, ""); + +void zipmodlist(char *which) // "active", "inactive", "all"(default) +{ + vector res; + if(!strcasecmp(which, "active")) + { + loopv(archives) cvecprintf(res, "%s ", archives[i]->name); + } + else + { + bool inactive = !strcasecmp(which, "inactive"); + vector files; + listfilesrecursive("mods", files); + files.sort(stringsort); + loopvrev(files) if(files.inrange(i + 1) && !strcmp(files[i], files[i + 1])) delstring(files.remove(i + 1)); // remove doubles + loopv(files) + { + char *f = files[i]; + if(!strncmp(f, "mods/", 5) && !strncmp(f + strlen(f) - 4, ".zip", 4)) + { + f += 5; + f[strlen(f) - 4] = '\0'; + if(fixzipname(f) && (!inactive || !findzip(f))) cvecprintf(res, "%s ", f); + } + } + files.deletearrays(); + } + if(res.length()) res.last() = '\0'; + else res.add('\0'); + result(res.getbuf()); +} +COMMAND(zipmodlist, "s"); + +const char *desctxt = "desc.txt", *previewjpg = "preview.jpg", *modrev = "revision_"; // special files in zip containing description or preview + +void zipmodgetdesc(char *name) // open zip, read desc.txt and mount preview picture +{ + const char *pname = fixzipname(name); + stream *f = pname ? openfile(pname, "rb") : NULL, *zf = NULL; + vector files; + vector res; + void *mz = zipmanualopen(f, files); + string tmp; + if(!f || !mz) conoutf("failed to read/open zip file %s", pname); + else + { + loopv(files) + { + if(!strcmp(files[i], desctxt)) + { + if((zf = zipmanualstream(mz, i))) loopk(11) + { + zf->getline(tmp, MAXSTRLEN / 2); + filtertext(tmp, tmp, FTXT__ZIPDESC); + if(*tmp) cvecprintf(res, "%s\n", escapestring(tmp)); + else break; + } + } + else if(!strcmp(files[i], previewjpg)) + { + vector *v = new vector; + zf = openvecfile(v); + int got = zipmanualread(mz, i, zf, 128 * 1024); // max. file size for preview.jpg is 128k + formatstring(tmp)("packages/modpreviews/%s.jpg", name); + addmemfile(tmp, v->getbuf(), got); + if(got == 128 * 1024) conoutf("\f3preview.jpg in zip file %s exceeds maximum file size: file truncated", pname); + } + DELETEP(zf); + } + zipmanualclose(mz); + } + if(res.length()) res.last() = '\0'; + else res.add('\0'); + result(res.getbuf()); +} +COMMAND(zipmodgetdesc, "s"); + +void zipmodgetfiles(char *name) // open zip, read list of files +{ + const char *pname = fixzipname(name); + stream *f = pname ? openfile(pname, "rb") : NULL; + vector files; + vector res; + void *mz = zipmanualopen(f, files); + if(!f || !mz) conoutf("failed to read/open zip file %s", pname); + else + { + loopv(files) cvecprintf(res, "%s\n", escapestring(files[i])); + zipmanualclose(mz); + } + if(res.length()) res.last() = '\0'; + else res.add('\0'); + result(res.getbuf()); +} +COMMAND(zipmodgetfiles, "s"); + +void zipmodgetrevision(char *name) // open zip, read list of files +{ + const char *pname = fixzipname(name); + stream *f = pname ? openfile(pname, "rb") : NULL; + vector files; + int res = 0, modrevlen = (int)strlen(modrev); + void *mz = zipmanualopen(f, files); + if(!f || !mz) conoutf("failed to read/open zip file %s", pname); + else + { + loopv(files) if(!strncasecmp(files[i], modrev, modrevlen)) res = atoi(files[i] + modrevlen); + zipmanualclose(mz); + } + intret(res); +} +COMMAND(zipmodgetrevision, "s"); + void writezipmodconfig(stream *f) { f->printf("zipcachemaxsize %d\n", zipcachemaxsize);