diff options
| author | xolatile | 2025-08-04 22:53:42 +0200 |
|---|---|---|
| committer | xolatile | 2025-08-04 22:53:42 +0200 |
| commit | d309df4ce4d8ad0ed995a8e1c4267412a7782021 (patch) | |
| tree | 999ca8d785ecc1681e5eb7538ce2e6a18d244fa5 /src/engine/worldio.cpp | |
| parent | 29d613d9cb65a0faa7e3f80e75bea0b6d910cb9a (diff) | |
| download | xolatile-badassbug-d309df4ce4d8ad0ed995a8e1c4267412a7782021.tar.xz xolatile-badassbug-d309df4ce4d8ad0ed995a8e1c4267412a7782021.tar.zst | |
Bunch of small changes...
Diffstat (limited to 'src/engine/worldio.cpp')
| -rw-r--r-- | src/engine/worldio.cpp | 2307 |
1 files changed, 1151 insertions, 1156 deletions
diff --git a/src/engine/worldio.cpp b/src/engine/worldio.cpp index d1fd52f..34823fa 100644 --- a/src/engine/worldio.cpp +++ b/src/engine/worldio.cpp @@ -4,169 +4,169 @@ void validmapname(char *dst, const char *src, const char *prefix = NULL, const char *alt = "untitled", size_t maxlen = 100) { - if(prefix) while(*prefix) *dst++ = *prefix++; - const char *start = dst; - if(src) loopi(maxlen) - { - char c = *src++; - if(iscubealnum(c) || c == '_' || c == '-' || c == '/' || c == '\\') *dst++ = c; - else break; - } - if(dst > start) *dst = '\0'; - else if(dst != alt) copystring(dst, alt, maxlen); + if(prefix) while(*prefix) *dst++ = *prefix++; + const char *start = dst; + if(src) loopi(maxlen) + { + char c = *src++; + if(iscubealnum(c) || c == '_' || c == '-' || c == '/' || c == '\\') *dst++ = c; + else break; + } + if(dst > start) *dst = '\0'; + else if(dst != alt) copystring(dst, alt, maxlen); } void fixmapname(char *name) { - validmapname(name, name, NULL, ""); + validmapname(name, name, NULL, ""); } void getmapfilenames(const char *fname, const char *cname, char *pakname, char *mapname, char *cfgname) { - if(!cname) cname = fname; - string name; - validmapname(name, cname); - char *slash = strpbrk(name, "/\\"); - if(slash) - { - copystring(pakname, name, slash-name+1); - copystring(cfgname, slash+1, MAXSTRLEN); - } - else - { - copystring(pakname, "maps", MAXSTRLEN); - copystring(cfgname, name, MAXSTRLEN); - } - validmapname(mapname, fname, strpbrk(fname, "/\\") ? NULL : "maps/"); + if(!cname) cname = fname; + string name; + validmapname(name, cname); + char *slash = strpbrk(name, "/\\"); + if(slash) + { + copystring(pakname, name, slash-name+1); + copystring(cfgname, slash+1, MAXSTRLEN); + } + else + { + copystring(pakname, "maps", MAXSTRLEN); + copystring(cfgname, name, MAXSTRLEN); + } + validmapname(mapname, fname, strpbrk(fname, "/\\") ? NULL : "maps/"); } static void fixent(entity &e, int version) { - if(version <= 10 && e.type >= 7) e.type++; - if(version <= 12 && e.type >= 8) e.type++; - if(version <= 14 && e.type >= ET_MAPMODEL && e.type <= 16) - { - if(e.type == 16) e.type = ET_MAPMODEL; - else e.type++; - } - if(version <= 20 && e.type >= ET_ENVMAP) e.type++; - if(version <= 21 && e.type >= ET_PARTICLES) e.type++; - if(version <= 22 && e.type >= ET_SOUND) e.type++; - if(version <= 23 && e.type >= ET_SPOTLIGHT) e.type++; - if(version <= 30 && (e.type == ET_MAPMODEL || e.type == ET_PLAYERSTART)) e.attr1 = (int(e.attr1)+180)%360; - if(version <= 31 && e.type == ET_MAPMODEL) { int yaw = (int(e.attr1)%360 + 360)%360 + 7; e.attr1 = yaw - yaw%15; } + if(version <= 10 && e.type >= 7) e.type++; + if(version <= 12 && e.type >= 8) e.type++; + if(version <= 14 && e.type >= ET_MAPMODEL && e.type <= 16) + { + if(e.type == 16) e.type = ET_MAPMODEL; + else e.type++; + } + if(version <= 20 && e.type >= ET_ENVMAP) e.type++; + if(version <= 21 && e.type >= ET_PARTICLES) e.type++; + if(version <= 22 && e.type >= ET_SOUND) e.type++; + if(version <= 23 && e.type >= ET_SPOTLIGHT) e.type++; + if(version <= 30 && (e.type == ET_MAPMODEL || e.type == ET_PLAYERSTART)) e.attr1 = (int(e.attr1)+180)%360; + if(version <= 31 && e.type == ET_MAPMODEL) { int yaw = (int(e.attr1)%360 + 360)%360 + 7; e.attr1 = yaw - yaw%15; } } bool loadents(const char *fname, vector<entity> &ents, uint *crc) { - string pakname, mapname, mcfgname, ogzname; - getmapfilenames(fname, NULL, pakname, mapname, mcfgname); - formatstring(ogzname, "packages/%s.ogz", mapname); - path(ogzname); - stream *f = opengzfile(ogzname, "rb"); - if(!f) return false; - octaheader hdr; - if(f->read(&hdr, 7*sizeof(int)) != 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } - lilswap(&hdr.version, 6); - if(memcmp(hdr.magic, "OCTA", 4) || hdr.worldsize <= 0|| hdr.numents < 0) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } - if(hdr.version>MAPVERSION) { conoutf(CON_ERROR, "map %s requires a newer version of Cube 2: Sauerbraten", ogzname); delete f; return false; } - compatheader chdr; - if(hdr.version <= 28) - { - if(f->read(&chdr.lightprecision, sizeof(chdr) - 7*sizeof(int)) != sizeof(chdr) - 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } - } - else - { - int extra = 0; - if(hdr.version <= 29) extra++; - if(f->read(&hdr.blendmap, sizeof(hdr) - (7+extra)*sizeof(int)) != sizeof(hdr) - (7+extra)*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } - } - - if(hdr.version <= 28) - { - lilswap(&chdr.lightprecision, 3); - hdr.blendmap = chdr.blendmap; - hdr.numvars = 0; - hdr.numvslots = 0; - } - else - { - lilswap(&hdr.blendmap, 2); - if(hdr.version <= 29) hdr.numvslots = 0; - else lilswap(&hdr.numvslots, 1); - } - - loopi(hdr.numvars) - { - int type = f->getchar(), ilen = f->getlil<ushort>(); - f->seek(ilen, SEEK_CUR); - switch(type) - { - case ID_VAR: f->getlil<int>(); break; - case ID_FVAR: f->getlil<float>(); break; - case ID_SVAR: { int slen = f->getlil<ushort>(); f->seek(slen, SEEK_CUR); break; } - } - } - - string gametype; - copystring(gametype, "fps"); - bool samegame = true; - int eif = 0; - if(hdr.version>=16) - { - int len = f->getchar(); - f->read(gametype, len+1); - } - if(strcmp(gametype, game::gameident())) - { - samegame = false; - conoutf(CON_WARN, "WARNING: loading map from %s game, ignoring entities except for lights/mapmodels", gametype); - } - if(hdr.version>=16) - { - eif = f->getlil<ushort>(); - int extrasize = f->getlil<ushort>(); - f->seek(extrasize, SEEK_CUR); - } - - if(hdr.version<14) - { - f->seek(256, SEEK_CUR); - } - else - { - ushort nummru = f->getlil<ushort>(); - f->seek(nummru*sizeof(ushort), SEEK_CUR); - } - - loopi(min(hdr.numents, MAXENTS)) - { - entity &e = ents.add(); - f->read(&e, sizeof(entity)); - lilswap(&e.o.x, 3); - lilswap(&e.attr1, 5); - fixent(e, hdr.version); - if(eif > 0) f->seek(eif, SEEK_CUR); - if(samegame) - { - entities::readent(e, NULL, hdr.version); - } - else if(e.type>=ET_GAMESPECIFIC || hdr.version<=14) - { - ents.pop(); - continue; - } - } - - if(crc) - { - f->seek(0, SEEK_END); - *crc = f->getcrc(); - } - - delete f; - - return true; + string pakname, mapname, mcfgname, ogzname; + getmapfilenames(fname, NULL, pakname, mapname, mcfgname); + formatstring(ogzname, "packages/%s.ogz", mapname); + path(ogzname); + stream *f = opengzfile(ogzname, "rb"); + if(!f) return false; + octaheader hdr; + if(f->read(&hdr, 7*sizeof(int)) != 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } + lilswap(&hdr.version, 6); + if(memcmp(hdr.magic, "OCTA", 4) || hdr.worldsize <= 0|| hdr.numents < 0) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } + if(hdr.version>MAPVERSION) { conoutf(CON_ERROR, "map %s requires a newer version of Cube 2: Sauerbraten", ogzname); delete f; return false; } + compatheader chdr; + if(hdr.version <= 28) + { + if(f->read(&chdr.lightprecision, sizeof(chdr) - 7*sizeof(int)) != sizeof(chdr) - 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } + } + else + { + int extra = 0; + if(hdr.version <= 29) extra++; + if(f->read(&hdr.blendmap, sizeof(hdr) - (7+extra)*sizeof(int)) != sizeof(hdr) - (7+extra)*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } + } + + if(hdr.version <= 28) + { + lilswap(&chdr.lightprecision, 3); + hdr.blendmap = chdr.blendmap; + hdr.numvars = 0; + hdr.numvslots = 0; + } + else + { + lilswap(&hdr.blendmap, 2); + if(hdr.version <= 29) hdr.numvslots = 0; + else lilswap(&hdr.numvslots, 1); + } + + loopi(hdr.numvars) + { + int type = f->getchar(), ilen = f->getlil<ushort>(); + f->seek(ilen, SEEK_CUR); + switch(type) + { + case ID_VAR: f->getlil<int>(); break; + case ID_FVAR: f->getlil<float>(); break; + case ID_SVAR: { int slen = f->getlil<ushort>(); f->seek(slen, SEEK_CUR); break; } + } + } + + string gametype; + copystring(gametype, "fps"); + bool samegame = true; + int eif = 0; + if(hdr.version>=16) + { + int len = f->getchar(); + f->read(gametype, len+1); + } + if(strcmp(gametype, game::gameident())) + { + samegame = false; + conoutf(CON_WARN, "WARNING: loading map from %s game, ignoring entities except for lights/mapmodels", gametype); + } + if(hdr.version>=16) + { + eif = f->getlil<ushort>(); + int extrasize = f->getlil<ushort>(); + f->seek(extrasize, SEEK_CUR); + } + + if(hdr.version<14) + { + f->seek(256, SEEK_CUR); + } + else + { + ushort nummru = f->getlil<ushort>(); + f->seek(nummru*sizeof(ushort), SEEK_CUR); + } + + loopi(min(hdr.numents, MAXENTS)) + { + entity &e = ents.add(); + f->read(&e, sizeof(entity)); + lilswap(&e.o.x, 3); + lilswap(&e.attr1, 5); + fixent(e, hdr.version); + if(eif > 0) f->seek(eif, SEEK_CUR); + if(samegame) + { + entities::readent(e, NULL, hdr.version); + } + else if(e.type>=ET_GAMESPECIFIC || hdr.version<=14) + { + ents.pop(); + continue; + } + } + + if(crc) + { + f->seek(0, SEEK_END); + *crc = f->getcrc(); + } + + delete f; + + return true; } #ifndef STANDALONE @@ -176,39 +176,39 @@ VARP(savebak, 0, 2, 2); void setmapfilenames(const char *fname, const char *cname = NULL) { - string pakname, mapname, mcfgname; - getmapfilenames(fname, cname, pakname, mapname, mcfgname); - - formatstring(ogzname, "packages/%s.ogz", mapname); - if(savebak==1) formatstring(bakname, "packages/%s.BAK", mapname); - else formatstring(bakname, "packages/%s_%d.BAK", mapname, totalmillis); - formatstring(cfgname, "packages/%s/%s.cfg", pakname, mcfgname); - formatstring(picname, "packages/%s.png", mapname); - - path(ogzname); - path(bakname); - path(cfgname); - path(picname); + string pakname, mapname, mcfgname; + getmapfilenames(fname, cname, pakname, mapname, mcfgname); + + formatstring(ogzname, "packages/%s.ogz", mapname); + if(savebak==1) formatstring(bakname, "packages/%s.BAK", mapname); + else formatstring(bakname, "packages/%s_%d.BAK", mapname, totalmillis); + formatstring(cfgname, "packages/%s/%s.cfg", pakname, mcfgname); + formatstring(picname, "packages/%s.png", mapname); + + path(ogzname); + path(bakname); + path(cfgname); + path(picname); } void mapcfgname() { - const char *mname = game::getclientmap(); - string pakname, mapname, mcfgname; - getmapfilenames(mname, NULL, pakname, mapname, mcfgname); - defformatstring(cfgname, "packages/%s/%s.cfg", pakname, mcfgname); - path(cfgname); - result(cfgname); + const char *mname = game::getclientmap(); + string pakname, mapname, mcfgname; + getmapfilenames(mname, NULL, pakname, mapname, mcfgname); + defformatstring(cfgname, "packages/%s/%s.cfg", pakname, mcfgname); + path(cfgname); + result(cfgname); } COMMAND(mapcfgname, ""); void backup(char *name, char *backupname) { - string backupfile; - copystring(backupfile, findfile(backupname, "wb")); - remove(backupfile); - rename(findfile(name, "wb"), backupfile); + string backupfile; + copystring(backupfile, findfile(backupname, "wb")); + remove(backupfile); + rename(findfile(name, "wb"), backupfile); } enum { OCTSAV_CHILDREN = 0, OCTSAV_EMPTY, OCTSAV_SOLID, OCTSAV_NORMAL, OCTSAV_LODCUBE }; @@ -217,773 +217,773 @@ static int savemapprogress = 0; void savec(cube *c, const ivec &o, int size, stream *f, bool nolms) { - if((savemapprogress++&0xFFF)==0) renderprogress(float(savemapprogress)/allocnodes, "saving octree..."); - - loopi(8) - { - ivec co(i, o, size); - if(c[i].children) - { - f->putchar(OCTSAV_CHILDREN); - savec(c[i].children, co, size>>1, f, nolms); - } - else - { - int oflags = 0, surfmask = 0, totalverts = 0; - if(c[i].material!=MAT_AIR) oflags |= 0x40; - if(isempty(c[i])) f->putchar(oflags | OCTSAV_EMPTY); - else - { - if(!nolms) - { - if(c[i].merged) oflags |= 0x80; - if(c[i].ext) loopj(6) - { - const surfaceinfo &surf = c[i].ext->surfaces[j]; - if(!surf.used()) continue; - oflags |= 0x20; - surfmask |= 1<<j; - totalverts += surf.totalverts(); - } - } - - if(isentirelysolid(c[i])) f->putchar(oflags | OCTSAV_SOLID); - else - { - f->putchar(oflags | OCTSAV_NORMAL); - f->write(c[i].edges, 12); - } - } - - loopj(6) f->putlil<ushort>(c[i].texture[j]); - - if(oflags&0x40) f->putlil<ushort>(c[i].material); - if(oflags&0x80) f->putchar(c[i].merged); - if(oflags&0x20) - { - f->putchar(surfmask); - f->putchar(totalverts); - loopj(6) if(surfmask&(1<<j)) - { - surfaceinfo surf = c[i].ext->surfaces[j]; - vertinfo *verts = c[i].ext->verts() + surf.verts; - int layerverts = surf.numverts&MAXFACEVERTS, numverts = surf.totalverts(), - vertmask = 0, vertorder = 0, uvorder = 0, - dim = dimension(j), vc = C[dim], vr = R[dim]; - if(numverts) - { - if(c[i].merged&(1<<j)) - { - vertmask |= 0x04; - if(layerverts == 4) - { - ivec v[4] = { verts[0].getxyz(), verts[1].getxyz(), verts[2].getxyz(), verts[3].getxyz() }; - loopk(4) - { - const ivec &v0 = v[k], &v1 = v[(k+1)&3], &v2 = v[(k+2)&3], &v3 = v[(k+3)&3]; - if(v1[vc] == v0[vc] && v1[vr] == v2[vr] && v3[vc] == v2[vc] && v3[vr] == v0[vr]) - { - vertmask |= 0x01; - vertorder = k; - break; - } - } - } - } - else - { - int vis = visibletris(c[i], j, co, size); - if(vis&4 || faceconvexity(c[i], j) < 0) vertmask |= 0x01; - if(layerverts < 4 && vis&2) vertmask |= 0x02; - } - bool matchnorm = true; - loopk(numverts) - { - const vertinfo &v = verts[k]; - if(v.u || v.v) vertmask |= 0x40; - if(v.norm) { vertmask |= 0x80; if(v.norm != verts[0].norm) matchnorm = false; } - } - if(matchnorm) vertmask |= 0x08; - if(vertmask&0x40 && layerverts == 4) - { - loopk(4) - { - const vertinfo &v0 = verts[k], &v1 = verts[(k+1)&3], &v2 = verts[(k+2)&3], &v3 = verts[(k+3)&3]; - if(v1.u == v0.u && v1.v == v2.v && v3.u == v2.u && v3.v == v0.v) - { - if(surf.numverts&LAYER_DUP) - { - const vertinfo &b0 = verts[4+k], &b1 = verts[4+((k+1)&3)], &b2 = verts[4+((k+2)&3)], &b3 = verts[4+((k+3)&3)]; - if(b1.u != b0.u || b1.v != b2.v || b3.u != b2.u || b3.v != b0.v) - continue; - } - uvorder = k; - vertmask |= 0x02 | (((k+4-vertorder)&3)<<4); - break; - } - } - } - } - surf.verts = vertmask; - f->write(&surf, sizeof(surfaceinfo)); - bool hasxyz = (vertmask&0x04)!=0, hasuv = (vertmask&0x40)!=0, hasnorm = (vertmask&0x80)!=0; - if(layerverts == 4) - { - if(hasxyz && vertmask&0x01) - { - ivec v0 = verts[vertorder].getxyz(), v2 = verts[(vertorder+2)&3].getxyz(); - f->putlil<ushort>(v0[vc]); f->putlil<ushort>(v0[vr]); - f->putlil<ushort>(v2[vc]); f->putlil<ushort>(v2[vr]); - hasxyz = false; - } - if(hasuv && vertmask&0x02) - { - const vertinfo &v0 = verts[uvorder], &v2 = verts[(uvorder+2)&3]; - f->putlil<ushort>(v0.u); f->putlil<ushort>(v0.v); - f->putlil<ushort>(v2.u); f->putlil<ushort>(v2.v); - if(surf.numverts&LAYER_DUP) - { - const vertinfo &b0 = verts[4+uvorder], &b2 = verts[4+((uvorder+2)&3)]; - f->putlil<ushort>(b0.u); f->putlil<ushort>(b0.v); - f->putlil<ushort>(b2.u); f->putlil<ushort>(b2.v); - } - hasuv = false; - } - } - if(hasnorm && vertmask&0x08) { f->putlil<ushort>(verts[0].norm); hasnorm = false; } - if(hasxyz || hasuv || hasnorm) loopk(layerverts) - { - const vertinfo &v = verts[(k+vertorder)%layerverts]; - if(hasxyz) - { - ivec xyz = v.getxyz(); - f->putlil<ushort>(xyz[vc]); f->putlil<ushort>(xyz[vr]); - } - if(hasuv) { f->putlil<ushort>(v.u); f->putlil<ushort>(v.v); } - if(hasnorm) f->putlil<ushort>(v.norm); - } - if(surf.numverts&LAYER_DUP) loopk(layerverts) - { - const vertinfo &v = verts[layerverts + (k+vertorder)%layerverts]; - if(hasuv) { f->putlil<ushort>(v.u); f->putlil<ushort>(v.v); } - } - } - } - } - } + if((savemapprogress++&0xFFF)==0) renderprogress(float(savemapprogress)/allocnodes, "saving octree..."); + + loopi(8) + { + ivec co(i, o, size); + if(c[i].children) + { + f->putchar(OCTSAV_CHILDREN); + savec(c[i].children, co, size>>1, f, nolms); + } + else + { + int oflags = 0, surfmask = 0, totalverts = 0; + if(c[i].material!=MAT_AIR) oflags |= 0x40; + if(isempty(c[i])) f->putchar(oflags | OCTSAV_EMPTY); + else + { + if(!nolms) + { + if(c[i].merged) oflags |= 0x80; + if(c[i].ext) loopj(6) + { + const surfaceinfo &surf = c[i].ext->surfaces[j]; + if(!surf.used()) continue; + oflags |= 0x20; + surfmask |= 1<<j; + totalverts += surf.totalverts(); + } + } + + if(isentirelysolid(c[i])) f->putchar(oflags | OCTSAV_SOLID); + else + { + f->putchar(oflags | OCTSAV_NORMAL); + f->write(c[i].edges, 12); + } + } + + loopj(6) f->putlil<ushort>(c[i].texture[j]); + + if(oflags&0x40) f->putlil<ushort>(c[i].material); + if(oflags&0x80) f->putchar(c[i].merged); + if(oflags&0x20) + { + f->putchar(surfmask); + f->putchar(totalverts); + loopj(6) if(surfmask&(1<<j)) + { + surfaceinfo surf = c[i].ext->surfaces[j]; + vertinfo *verts = c[i].ext->verts() + surf.verts; + int layerverts = surf.numverts&MAXFACEVERTS, numverts = surf.totalverts(), + vertmask = 0, vertorder = 0, uvorder = 0, + dim = dimension(j), vc = C[dim], vr = R[dim]; + if(numverts) + { + if(c[i].merged&(1<<j)) + { + vertmask |= 0x04; + if(layerverts == 4) + { + ivec v[4] = { verts[0].getxyz(), verts[1].getxyz(), verts[2].getxyz(), verts[3].getxyz() }; + loopk(4) + { + const ivec &v0 = v[k], &v1 = v[(k+1)&3], &v2 = v[(k+2)&3], &v3 = v[(k+3)&3]; + if(v1[vc] == v0[vc] && v1[vr] == v2[vr] && v3[vc] == v2[vc] && v3[vr] == v0[vr]) + { + vertmask |= 0x01; + vertorder = k; + break; + } + } + } + } + else + { + int vis = visibletris(c[i], j, co, size); + if(vis&4 || faceconvexity(c[i], j) < 0) vertmask |= 0x01; + if(layerverts < 4 && vis&2) vertmask |= 0x02; + } + bool matchnorm = true; + loopk(numverts) + { + const vertinfo &v = verts[k]; + if(v.u || v.v) vertmask |= 0x40; + if(v.norm) { vertmask |= 0x80; if(v.norm != verts[0].norm) matchnorm = false; } + } + if(matchnorm) vertmask |= 0x08; + if(vertmask&0x40 && layerverts == 4) + { + loopk(4) + { + const vertinfo &v0 = verts[k], &v1 = verts[(k+1)&3], &v2 = verts[(k+2)&3], &v3 = verts[(k+3)&3]; + if(v1.u == v0.u && v1.v == v2.v && v3.u == v2.u && v3.v == v0.v) + { + if(surf.numverts&LAYER_DUP) + { + const vertinfo &b0 = verts[4+k], &b1 = verts[4+((k+1)&3)], &b2 = verts[4+((k+2)&3)], &b3 = verts[4+((k+3)&3)]; + if(b1.u != b0.u || b1.v != b2.v || b3.u != b2.u || b3.v != b0.v) + continue; + } + uvorder = k; + vertmask |= 0x02 | (((k+4-vertorder)&3)<<4); + break; + } + } + } + } + surf.verts = vertmask; + f->write(&surf, sizeof(surfaceinfo)); + bool hasxyz = (vertmask&0x04)!=0, hasuv = (vertmask&0x40)!=0, hasnorm = (vertmask&0x80)!=0; + if(layerverts == 4) + { + if(hasxyz && vertmask&0x01) + { + ivec v0 = verts[vertorder].getxyz(), v2 = verts[(vertorder+2)&3].getxyz(); + f->putlil<ushort>(v0[vc]); f->putlil<ushort>(v0[vr]); + f->putlil<ushort>(v2[vc]); f->putlil<ushort>(v2[vr]); + hasxyz = false; + } + if(hasuv && vertmask&0x02) + { + const vertinfo &v0 = verts[uvorder], &v2 = verts[(uvorder+2)&3]; + f->putlil<ushort>(v0.u); f->putlil<ushort>(v0.v); + f->putlil<ushort>(v2.u); f->putlil<ushort>(v2.v); + if(surf.numverts&LAYER_DUP) + { + const vertinfo &b0 = verts[4+uvorder], &b2 = verts[4+((uvorder+2)&3)]; + f->putlil<ushort>(b0.u); f->putlil<ushort>(b0.v); + f->putlil<ushort>(b2.u); f->putlil<ushort>(b2.v); + } + hasuv = false; + } + } + if(hasnorm && vertmask&0x08) { f->putlil<ushort>(verts[0].norm); hasnorm = false; } + if(hasxyz || hasuv || hasnorm) loopk(layerverts) + { + const vertinfo &v = verts[(k+vertorder)%layerverts]; + if(hasxyz) + { + ivec xyz = v.getxyz(); + f->putlil<ushort>(xyz[vc]); f->putlil<ushort>(xyz[vr]); + } + if(hasuv) { f->putlil<ushort>(v.u); f->putlil<ushort>(v.v); } + if(hasnorm) f->putlil<ushort>(v.norm); + } + if(surf.numverts&LAYER_DUP) loopk(layerverts) + { + const vertinfo &v = verts[layerverts + (k+vertorder)%layerverts]; + if(hasuv) { f->putlil<ushort>(v.u); f->putlil<ushort>(v.v); } + } + } + } + } + } } struct surfacecompat { - uchar texcoords[8]; - uchar w, h; - ushort x, y; - uchar lmid, layer; + uchar texcoords[8]; + uchar w, h; + ushort x, y; + uchar lmid, layer; }; struct normalscompat { - bvec normals[4]; + bvec normals[4]; }; struct mergecompat { - ushort u1, u2, v1, v2; + ushort u1, u2, v1, v2; }; cube *loadchildren(stream *f, const ivec &co, int size, bool &failed); void convertoldsurfaces(cube &c, const ivec &co, int size, surfacecompat *srcsurfs, int hassurfs, normalscompat *normals, int hasnorms, mergecompat *merges, int hasmerges) { - surfaceinfo dstsurfs[6]; - vertinfo verts[6*2*MAXFACEVERTS]; - int totalverts = 0, numsurfs = 6; - memset(dstsurfs, 0, sizeof(dstsurfs)); - loopi(6) if((hassurfs|hasnorms|hasmerges)&(1<<i)) - { - surfaceinfo &dst = dstsurfs[i]; - vertinfo *curverts = NULL; - int numverts = 0; - surfacecompat *src = NULL, *blend = NULL; - if(hassurfs&(1<<i)) - { - src = &srcsurfs[i]; - if(src->layer&2) - { - blend = &srcsurfs[numsurfs++]; - dst.lmid[0] = src->lmid; - dst.lmid[1] = blend->lmid; - dst.numverts |= LAYER_BLEND; - if(blend->lmid >= LMID_RESERVED && (src->x != blend->x || src->y != blend->y || src->w != blend->w || src->h != blend->h || memcmp(src->texcoords, blend->texcoords, sizeof(src->texcoords)))) - dst.numverts |= LAYER_DUP; - } - else if(src->layer == 1) { dst.lmid[1] = src->lmid; dst.numverts |= LAYER_BOTTOM; } - else { dst.lmid[0] = src->lmid; dst.numverts |= LAYER_TOP; } - } - else dst.numverts |= LAYER_TOP; - bool uselms = hassurfs&(1<<i) && (dst.lmid[0] >= LMID_RESERVED || dst.lmid[1] >= LMID_RESERVED || dst.numverts&~LAYER_TOP), - usemerges = hasmerges&(1<<i) && merges[i].u1 < merges[i].u2 && merges[i].v1 < merges[i].v2, - usenorms = hasnorms&(1<<i) && normals[i].normals[0] != bvec(128, 128, 128); - if(uselms || usemerges || usenorms) - { - ivec v[4], pos[4], e1, e2, e3, n, vo = ivec(co).mask(0xFFF).shl(3); - genfaceverts(c, i, v); - n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0])); - if(usemerges) - { - const mergecompat &m = merges[i]; - int offset = -n.dot(v[0].mul(size).add(vo)), - dim = dimension(i), vc = C[dim], vr = R[dim]; - loopk(4) - { - const ivec &coords = facecoords[i][k]; - int cc = coords[vc] ? m.u2 : m.u1, - rc = coords[vr] ? m.v2 : m.v1, - dc = n[dim] ? -(offset + n[vc]*cc + n[vr]*rc)/n[dim] : vo[dim]; - ivec &mv = pos[k]; - mv[vc] = cc; - mv[vr] = rc; - mv[dim] = dc; - } - } - else - { - int convex = (e3 = v[0]).sub(v[3]).dot(n), vis = 3; - if(!convex) - { - if(ivec().cross(e3, e2).iszero()) { if(!n.iszero()) vis = 1; } - else if(n.iszero()) vis = 2; - } - int order = convex < 0 ? 1 : 0; - pos[0] = v[order].mul(size).add(vo); - pos[1] = vis&1 ? v[order+1].mul(size).add(vo) : pos[0]; - pos[2] = v[order+2].mul(size).add(vo); - pos[3] = vis&2 ? v[(order+3)&3].mul(size).add(vo) : pos[0]; - } - curverts = verts + totalverts; - loopk(4) - { - if(k > 0 && (pos[k] == pos[0] || pos[k] == pos[k-1])) continue; - vertinfo &dv = curverts[numverts++]; - dv.setxyz(pos[k]); - if(uselms) - { - float u = src->x + (src->texcoords[k*2] / 255.0f) * (src->w - 1), - v = src->y + (src->texcoords[k*2+1] / 255.0f) * (src->h - 1); - dv.u = ushort(floor(clamp((u) * float(USHRT_MAX+1)/LM_PACKW + 0.5f, 0.0f, float(USHRT_MAX)))); - dv.v = ushort(floor(clamp((v) * float(USHRT_MAX+1)/LM_PACKH + 0.5f, 0.0f, float(USHRT_MAX)))); - } - else dv.u = dv.v = 0; - dv.norm = usenorms && normals[i].normals[k] != bvec(128, 128, 128) ? encodenormal(normals[i].normals[k].tonormal().normalize()) : 0; - } - dst.verts = totalverts; - dst.numverts |= numverts; - totalverts += numverts; - if(dst.numverts&LAYER_DUP) loopk(4) - { - if(k > 0 && (pos[k] == pos[0] || pos[k] == pos[k-1])) continue; - vertinfo &bv = verts[totalverts++]; - bv.setxyz(pos[k]); - bv.u = ushort(floor(clamp((blend->x + (blend->texcoords[k*2] / 255.0f) * (blend->w - 1)) * float(USHRT_MAX+1)/LM_PACKW, 0.0f, float(USHRT_MAX)))); - bv.v = ushort(floor(clamp((blend->y + (blend->texcoords[k*2+1] / 255.0f) * (blend->h - 1)) * float(USHRT_MAX+1)/LM_PACKH, 0.0f, float(USHRT_MAX)))); - bv.norm = usenorms && normals[i].normals[k] != bvec(128, 128, 128) ? encodenormal(normals[i].normals[k].tonormal().normalize()) : 0; - } - } - } - setsurfaces(c, dstsurfs, verts, totalverts); + surfaceinfo dstsurfs[6]; + vertinfo verts[6*2*MAXFACEVERTS]; + int totalverts = 0, numsurfs = 6; + memset(dstsurfs, 0, sizeof(dstsurfs)); + loopi(6) if((hassurfs|hasnorms|hasmerges)&(1<<i)) + { + surfaceinfo &dst = dstsurfs[i]; + vertinfo *curverts = NULL; + int numverts = 0; + surfacecompat *src = NULL, *blend = NULL; + if(hassurfs&(1<<i)) + { + src = &srcsurfs[i]; + if(src->layer&2) + { + blend = &srcsurfs[numsurfs++]; + dst.lmid[0] = src->lmid; + dst.lmid[1] = blend->lmid; + dst.numverts |= LAYER_BLEND; + if(blend->lmid >= LMID_RESERVED && (src->x != blend->x || src->y != blend->y || src->w != blend->w || src->h != blend->h || memcmp(src->texcoords, blend->texcoords, sizeof(src->texcoords)))) + dst.numverts |= LAYER_DUP; + } + else if(src->layer == 1) { dst.lmid[1] = src->lmid; dst.numverts |= LAYER_BOTTOM; } + else { dst.lmid[0] = src->lmid; dst.numverts |= LAYER_TOP; } + } + else dst.numverts |= LAYER_TOP; + bool uselms = hassurfs&(1<<i) && (dst.lmid[0] >= LMID_RESERVED || dst.lmid[1] >= LMID_RESERVED || dst.numverts&~LAYER_TOP), + usemerges = hasmerges&(1<<i) && merges[i].u1 < merges[i].u2 && merges[i].v1 < merges[i].v2, + usenorms = hasnorms&(1<<i) && normals[i].normals[0] != bvec(128, 128, 128); + if(uselms || usemerges || usenorms) + { + ivec v[4], pos[4], e1, e2, e3, n, vo = ivec(co).mask(0xFFF).shl(3); + genfaceverts(c, i, v); + n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0])); + if(usemerges) + { + const mergecompat &m = merges[i]; + int offset = -n.dot(v[0].mul(size).add(vo)), + dim = dimension(i), vc = C[dim], vr = R[dim]; + loopk(4) + { + const ivec &coords = facecoords[i][k]; + int cc = coords[vc] ? m.u2 : m.u1, + rc = coords[vr] ? m.v2 : m.v1, + dc = n[dim] ? -(offset + n[vc]*cc + n[vr]*rc)/n[dim] : vo[dim]; + ivec &mv = pos[k]; + mv[vc] = cc; + mv[vr] = rc; + mv[dim] = dc; + } + } + else + { + int convex = (e3 = v[0]).sub(v[3]).dot(n), vis = 3; + if(!convex) + { + if(ivec().cross(e3, e2).iszero()) { if(!n.iszero()) vis = 1; } + else if(n.iszero()) vis = 2; + } + int order = convex < 0 ? 1 : 0; + pos[0] = v[order].mul(size).add(vo); + pos[1] = vis&1 ? v[order+1].mul(size).add(vo) : pos[0]; + pos[2] = v[order+2].mul(size).add(vo); + pos[3] = vis&2 ? v[(order+3)&3].mul(size).add(vo) : pos[0]; + } + curverts = verts + totalverts; + loopk(4) + { + if(k > 0 && (pos[k] == pos[0] || pos[k] == pos[k-1])) continue; + vertinfo &dv = curverts[numverts++]; + dv.setxyz(pos[k]); + if(uselms) + { + float u = src->x + (src->texcoords[k*2] / 255.0f) * (src->w - 1), + v = src->y + (src->texcoords[k*2+1] / 255.0f) * (src->h - 1); + dv.u = ushort(floor(clamp((u) * float(USHRT_MAX+1)/LM_PACKW + 0.5f, 0.0f, float(USHRT_MAX)))); + dv.v = ushort(floor(clamp((v) * float(USHRT_MAX+1)/LM_PACKH + 0.5f, 0.0f, float(USHRT_MAX)))); + } + else dv.u = dv.v = 0; + dv.norm = usenorms && normals[i].normals[k] != bvec(128, 128, 128) ? encodenormal(normals[i].normals[k].tonormal().normalize()) : 0; + } + dst.verts = totalverts; + dst.numverts |= numverts; + totalverts += numverts; + if(dst.numverts&LAYER_DUP) loopk(4) + { + if(k > 0 && (pos[k] == pos[0] || pos[k] == pos[k-1])) continue; + vertinfo &bv = verts[totalverts++]; + bv.setxyz(pos[k]); + bv.u = ushort(floor(clamp((blend->x + (blend->texcoords[k*2] / 255.0f) * (blend->w - 1)) * float(USHRT_MAX+1)/LM_PACKW, 0.0f, float(USHRT_MAX)))); + bv.v = ushort(floor(clamp((blend->y + (blend->texcoords[k*2+1] / 255.0f) * (blend->h - 1)) * float(USHRT_MAX+1)/LM_PACKH, 0.0f, float(USHRT_MAX)))); + bv.norm = usenorms && normals[i].normals[k] != bvec(128, 128, 128) ? encodenormal(normals[i].normals[k].tonormal().normalize()) : 0; + } + } + } + setsurfaces(c, dstsurfs, verts, totalverts); } static inline int convertoldmaterial(int mat) { - return ((mat&7)<<MATF_VOLUME_SHIFT) | (((mat>>3)&3)<<MATF_CLIP_SHIFT) | (((mat>>5)&7)<<MATF_FLAG_SHIFT); + return ((mat&7)<<MATF_VOLUME_SHIFT) | (((mat>>3)&3)<<MATF_CLIP_SHIFT) | (((mat>>5)&7)<<MATF_FLAG_SHIFT); } void loadc(stream *f, cube &c, const ivec &co, int size, bool &failed) { - bool haschildren = false; - int octsav = f->getchar(); - switch(octsav&0x7) - { - case OCTSAV_CHILDREN: - c.children = loadchildren(f, co, size>>1, failed); - return; - - case OCTSAV_LODCUBE: haschildren = true; break; - case OCTSAV_EMPTY: emptyfaces(c); break; - case OCTSAV_SOLID: solidfaces(c); break; - case OCTSAV_NORMAL: f->read(c.edges, 12); break; - default: failed = true; return; - } - loopi(6) c.texture[i] = mapversion<14 ? f->getchar() : f->getlil<ushort>(); - if(mapversion < 7) f->seek(3, SEEK_CUR); - else if(mapversion <= 31) - { - uchar mask = f->getchar(); - if(mask & 0x80) - { - int mat = f->getchar(); - if(mapversion < 27) - { - static const ushort matconv[] = { MAT_AIR, MAT_WATER, MAT_CLIP, MAT_GLASS|MAT_CLIP, MAT_NOCLIP, MAT_LAVA|MAT_DEATH, MAT_GAMECLIP, MAT_DEATH }; - c.material = size_t(mat) < sizeof(matconv)/sizeof(matconv[0]) ? (int) matconv[mat] : (int) MAT_AIR; - } - else c.material = convertoldmaterial(mat); - } - surfacecompat surfaces[12]; - normalscompat normals[6]; - mergecompat merges[6]; - int hassurfs = 0, hasnorms = 0, hasmerges = 0; - if(mask & 0x3F) - { - int numsurfs = 6; - loopi(numsurfs) - { - if(i >= 6 || mask & (1 << i)) - { - f->read(&surfaces[i], sizeof(surfacecompat)); - lilswap(&surfaces[i].x, 2); - if(mapversion < 10) ++surfaces[i].lmid; - if(mapversion < 18) - { - if(surfaces[i].lmid >= LMID_AMBIENT1) ++surfaces[i].lmid; - if(surfaces[i].lmid >= LMID_BRIGHT1) ++surfaces[i].lmid; - } - if(mapversion < 19) - { - if(surfaces[i].lmid >= LMID_DARK) surfaces[i].lmid += 2; - } - if(i < 6) - { - if(mask & 0x40) { hasnorms |= 1<<i; f->read(&normals[i], sizeof(normalscompat)); } - if(surfaces[i].layer != 0 || surfaces[i].lmid != LMID_AMBIENT) - hassurfs |= 1<<i; - if(surfaces[i].layer&2) numsurfs++; - } - } - } - } - if(mapversion <= 8) edgespan2vectorcube(c); - if(mapversion <= 11) - { - swap(c.faces[0], c.faces[2]); - swap(c.texture[0], c.texture[4]); - swap(c.texture[1], c.texture[5]); - if(hassurfs&0x33) - { - swap(surfaces[0], surfaces[4]); - swap(surfaces[1], surfaces[5]); - hassurfs = (hassurfs&~0x33) | ((hassurfs&0x30)>>4) | ((hassurfs&0x03)<<4); - } - } - if(mapversion >= 20) - { - if(octsav&0x80) - { - int merged = f->getchar(); - c.merged = merged&0x3F; - if(merged&0x80) - { - int mask = f->getchar(); - if(mask) - { - hasmerges = mask&0x3F; - loopi(6) if(mask&(1<<i)) - { - mergecompat *m = &merges[i]; - f->read(m, sizeof(mergecompat)); - lilswap(&m->u1, 4); - if(mapversion <= 25) - { - int uorigin = m->u1 & 0xE000, vorigin = m->v1 & 0xE000; - m->u1 = (m->u1 - uorigin) << 2; - m->u2 = (m->u2 - uorigin) << 2; - m->v1 = (m->v1 - vorigin) << 2; - m->v2 = (m->v2 - vorigin) << 2; - } - } - } - } - } - } - if(hassurfs || hasnorms || hasmerges) - convertoldsurfaces(c, co, size, surfaces, hassurfs, normals, hasnorms, merges, hasmerges); - } - else - { - if(octsav&0x40) - { - if(mapversion <= 32) - { - int mat = f->getchar(); - c.material = convertoldmaterial(mat); - } - else c.material = f->getlil<ushort>(); - } - if(octsav&0x80) c.merged = f->getchar(); - if(octsav&0x20) - { - int surfmask, totalverts; - surfmask = f->getchar(); - totalverts = max(f->getchar(), 0); - newcubeext(c, totalverts, false); - memset(c.ext->surfaces, 0, sizeof(c.ext->surfaces)); - memset(c.ext->verts(), 0, totalverts*sizeof(vertinfo)); - int offset = 0; - loopi(6) if(surfmask&(1<<i)) - { - surfaceinfo &surf = c.ext->surfaces[i]; - f->read(&surf, sizeof(surfaceinfo)); - int vertmask = surf.verts, numverts = surf.totalverts(); - if(!numverts) { surf.verts = 0; continue; } - surf.verts = offset; - vertinfo *verts = c.ext->verts() + offset; - offset += numverts; - ivec v[4], n, vo = ivec(co).mask(0xFFF).shl(3); - int layerverts = surf.numverts&MAXFACEVERTS, dim = dimension(i), vc = C[dim], vr = R[dim], bias = 0; - genfaceverts(c, i, v); - bool hasxyz = (vertmask&0x04)!=0, hasuv = (vertmask&0x40)!=0, hasnorm = (vertmask&0x80)!=0; - if(hasxyz) - { - ivec e1, e2, e3; - n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0])); - if(n.iszero()) n.cross(e2, (e3 = v[3]).sub(v[0])); - bias = -n.dot(ivec(v[0]).mul(size).add(vo)); - } - else - { - int vis = layerverts < 4 ? (vertmask&0x02 ? 2 : 1) : 3, order = vertmask&0x01 ? 1 : 0, k = 0; - verts[k++].setxyz(v[order].mul(size).add(vo)); - if(vis&1) verts[k++].setxyz(v[order+1].mul(size).add(vo)); - verts[k++].setxyz(v[order+2].mul(size).add(vo)); - if(vis&2) verts[k++].setxyz(v[(order+3)&3].mul(size).add(vo)); - } - if(layerverts == 4) - { - if(hasxyz && vertmask&0x01) - { - ushort c1 = f->getlil<ushort>(), r1 = f->getlil<ushort>(), c2 = f->getlil<ushort>(), r2 = f->getlil<ushort>(); - ivec xyz; - xyz[vc] = c1; xyz[vr] = r1; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim]; - verts[0].setxyz(xyz); - xyz[vc] = c1; xyz[vr] = r2; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim]; - verts[1].setxyz(xyz); - xyz[vc] = c2; xyz[vr] = r2; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim]; - verts[2].setxyz(xyz); - xyz[vc] = c2; xyz[vr] = r1; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim]; - verts[3].setxyz(xyz); - hasxyz = false; - } - if(hasuv && vertmask&0x02) - { - int uvorder = (vertmask&0x30)>>4; - vertinfo &v0 = verts[uvorder], &v1 = verts[(uvorder+1)&3], &v2 = verts[(uvorder+2)&3], &v3 = verts[(uvorder+3)&3]; - v0.u = f->getlil<ushort>(); v0.v = f->getlil<ushort>(); - v2.u = f->getlil<ushort>(); v2.v = f->getlil<ushort>(); - v1.u = v0.u; v1.v = v2.v; - v3.u = v2.u; v3.v = v0.v; - if(surf.numverts&LAYER_DUP) - { - vertinfo &b0 = verts[4+uvorder], &b1 = verts[4+((uvorder+1)&3)], &b2 = verts[4+((uvorder+2)&3)], &b3 = verts[4+((uvorder+3)&3)]; - b0.u = f->getlil<ushort>(); b0.v = f->getlil<ushort>(); - b2.u = f->getlil<ushort>(); b2.v = f->getlil<ushort>(); - b1.u = b0.u; b1.v = b2.v; - b3.u = b2.u; b3.v = b0.v; - } - hasuv = false; - } - } - if(hasnorm && vertmask&0x08) - { - ushort norm = f->getlil<ushort>(); - loopk(layerverts) verts[k].norm = norm; - hasnorm = false; - } - if(hasxyz || hasuv || hasnorm) loopk(layerverts) - { - vertinfo &v = verts[k]; - if(hasxyz) - { - ivec xyz; - xyz[vc] = f->getlil<ushort>(); xyz[vr] = f->getlil<ushort>(); - xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim]; - v.setxyz(xyz); - } - if(hasuv) { v.u = f->getlil<ushort>(); v.v = f->getlil<ushort>(); } - if(hasnorm) v.norm = f->getlil<ushort>(); - } - if(surf.numverts&LAYER_DUP) loopk(layerverts) - { - vertinfo &v = verts[k+layerverts], &t = verts[k]; - v.setxyz(t.x, t.y, t.z); - if(hasuv) { v.u = f->getlil<ushort>(); v.v = f->getlil<ushort>(); } - v.norm = t.norm; - } - } - } - } - - c.children = (haschildren ? loadchildren(f, co, size>>1, failed) : NULL); + bool haschildren = false; + int octsav = f->getchar(); + switch(octsav&0x7) + { + case OCTSAV_CHILDREN: + c.children = loadchildren(f, co, size>>1, failed); + return; + + case OCTSAV_LODCUBE: haschildren = true; break; + case OCTSAV_EMPTY: emptyfaces(c); break; + case OCTSAV_SOLID: solidfaces(c); break; + case OCTSAV_NORMAL: f->read(c.edges, 12); break; + default: failed = true; return; + } + loopi(6) c.texture[i] = mapversion<14 ? f->getchar() : f->getlil<ushort>(); + if(mapversion < 7) f->seek(3, SEEK_CUR); + else if(mapversion <= 31) + { + uchar mask = f->getchar(); + if(mask & 0x80) + { + int mat = f->getchar(); + if(mapversion < 27) + { + static const ushort matconv[] = { MAT_AIR, MAT_WATER, MAT_CLIP, MAT_GLASS|MAT_CLIP, MAT_NOCLIP, MAT_LAVA|MAT_DEATH, MAT_GAMECLIP, MAT_DEATH }; + c.material = size_t(mat) < sizeof(matconv)/sizeof(matconv[0]) ? (int) matconv[mat] : (int) MAT_AIR; + } + else c.material = convertoldmaterial(mat); + } + surfacecompat surfaces[12]; + normalscompat normals[6]; + mergecompat merges[6]; + int hassurfs = 0, hasnorms = 0, hasmerges = 0; + if(mask & 0x3F) + { + int numsurfs = 6; + loopi(numsurfs) + { + if(i >= 6 || mask & (1 << i)) + { + f->read(&surfaces[i], sizeof(surfacecompat)); + lilswap(&surfaces[i].x, 2); + if(mapversion < 10) ++surfaces[i].lmid; + if(mapversion < 18) + { + if(surfaces[i].lmid >= LMID_AMBIENT1) ++surfaces[i].lmid; + if(surfaces[i].lmid >= LMID_BRIGHT1) ++surfaces[i].lmid; + } + if(mapversion < 19) + { + if(surfaces[i].lmid >= LMID_DARK) surfaces[i].lmid += 2; + } + if(i < 6) + { + if(mask & 0x40) { hasnorms |= 1<<i; f->read(&normals[i], sizeof(normalscompat)); } + if(surfaces[i].layer != 0 || surfaces[i].lmid != LMID_AMBIENT) + hassurfs |= 1<<i; + if(surfaces[i].layer&2) numsurfs++; + } + } + } + } + if(mapversion <= 8) edgespan2vectorcube(c); + if(mapversion <= 11) + { + swap(c.faces[0], c.faces[2]); + swap(c.texture[0], c.texture[4]); + swap(c.texture[1], c.texture[5]); + if(hassurfs&0x33) + { + swap(surfaces[0], surfaces[4]); + swap(surfaces[1], surfaces[5]); + hassurfs = (hassurfs&~0x33) | ((hassurfs&0x30)>>4) | ((hassurfs&0x03)<<4); + } + } + if(mapversion >= 20) + { + if(octsav&0x80) + { + int merged = f->getchar(); + c.merged = merged&0x3F; + if(merged&0x80) + { + int mask = f->getchar(); + if(mask) + { + hasmerges = mask&0x3F; + loopi(6) if(mask&(1<<i)) + { + mergecompat *m = &merges[i]; + f->read(m, sizeof(mergecompat)); + lilswap(&m->u1, 4); + if(mapversion <= 25) + { + int uorigin = m->u1 & 0xE000, vorigin = m->v1 & 0xE000; + m->u1 = (m->u1 - uorigin) << 2; + m->u2 = (m->u2 - uorigin) << 2; + m->v1 = (m->v1 - vorigin) << 2; + m->v2 = (m->v2 - vorigin) << 2; + } + } + } + } + } + } + if(hassurfs || hasnorms || hasmerges) + convertoldsurfaces(c, co, size, surfaces, hassurfs, normals, hasnorms, merges, hasmerges); + } + else + { + if(octsav&0x40) + { + if(mapversion <= 32) + { + int mat = f->getchar(); + c.material = convertoldmaterial(mat); + } + else c.material = f->getlil<ushort>(); + } + if(octsav&0x80) c.merged = f->getchar(); + if(octsav&0x20) + { + int surfmask, totalverts; + surfmask = f->getchar(); + totalverts = max(f->getchar(), 0); + newcubeext(c, totalverts, false); + memset(c.ext->surfaces, 0, sizeof(c.ext->surfaces)); + memset(c.ext->verts(), 0, totalverts*sizeof(vertinfo)); + int offset = 0; + loopi(6) if(surfmask&(1<<i)) + { + surfaceinfo &surf = c.ext->surfaces[i]; + f->read(&surf, sizeof(surfaceinfo)); + int vertmask = surf.verts, numverts = surf.totalverts(); + if(!numverts) { surf.verts = 0; continue; } + surf.verts = offset; + vertinfo *verts = c.ext->verts() + offset; + offset += numverts; + ivec v[4], n, vo = ivec(co).mask(0xFFF).shl(3); + int layerverts = surf.numverts&MAXFACEVERTS, dim = dimension(i), vc = C[dim], vr = R[dim], bias = 0; + genfaceverts(c, i, v); + bool hasxyz = (vertmask&0x04)!=0, hasuv = (vertmask&0x40)!=0, hasnorm = (vertmask&0x80)!=0; + if(hasxyz) + { + ivec e1, e2, e3; + n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0])); + if(n.iszero()) n.cross(e2, (e3 = v[3]).sub(v[0])); + bias = -n.dot(ivec(v[0]).mul(size).add(vo)); + } + else + { + int vis = layerverts < 4 ? (vertmask&0x02 ? 2 : 1) : 3, order = vertmask&0x01 ? 1 : 0, k = 0; + verts[k++].setxyz(v[order].mul(size).add(vo)); + if(vis&1) verts[k++].setxyz(v[order+1].mul(size).add(vo)); + verts[k++].setxyz(v[order+2].mul(size).add(vo)); + if(vis&2) verts[k++].setxyz(v[(order+3)&3].mul(size).add(vo)); + } + if(layerverts == 4) + { + if(hasxyz && vertmask&0x01) + { + ushort c1 = f->getlil<ushort>(), r1 = f->getlil<ushort>(), c2 = f->getlil<ushort>(), r2 = f->getlil<ushort>(); + ivec xyz; + xyz[vc] = c1; xyz[vr] = r1; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim]; + verts[0].setxyz(xyz); + xyz[vc] = c1; xyz[vr] = r2; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim]; + verts[1].setxyz(xyz); + xyz[vc] = c2; xyz[vr] = r2; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim]; + verts[2].setxyz(xyz); + xyz[vc] = c2; xyz[vr] = r1; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim]; + verts[3].setxyz(xyz); + hasxyz = false; + } + if(hasuv && vertmask&0x02) + { + int uvorder = (vertmask&0x30)>>4; + vertinfo &v0 = verts[uvorder], &v1 = verts[(uvorder+1)&3], &v2 = verts[(uvorder+2)&3], &v3 = verts[(uvorder+3)&3]; + v0.u = f->getlil<ushort>(); v0.v = f->getlil<ushort>(); + v2.u = f->getlil<ushort>(); v2.v = f->getlil<ushort>(); + v1.u = v0.u; v1.v = v2.v; + v3.u = v2.u; v3.v = v0.v; + if(surf.numverts&LAYER_DUP) + { + vertinfo &b0 = verts[4+uvorder], &b1 = verts[4+((uvorder+1)&3)], &b2 = verts[4+((uvorder+2)&3)], &b3 = verts[4+((uvorder+3)&3)]; + b0.u = f->getlil<ushort>(); b0.v = f->getlil<ushort>(); + b2.u = f->getlil<ushort>(); b2.v = f->getlil<ushort>(); + b1.u = b0.u; b1.v = b2.v; + b3.u = b2.u; b3.v = b0.v; + } + hasuv = false; + } + } + if(hasnorm && vertmask&0x08) + { + ushort norm = f->getlil<ushort>(); + loopk(layerverts) verts[k].norm = norm; + hasnorm = false; + } + if(hasxyz || hasuv || hasnorm) loopk(layerverts) + { + vertinfo &v = verts[k]; + if(hasxyz) + { + ivec xyz; + xyz[vc] = f->getlil<ushort>(); xyz[vr] = f->getlil<ushort>(); + xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim]; + v.setxyz(xyz); + } + if(hasuv) { v.u = f->getlil<ushort>(); v.v = f->getlil<ushort>(); } + if(hasnorm) v.norm = f->getlil<ushort>(); + } + if(surf.numverts&LAYER_DUP) loopk(layerverts) + { + vertinfo &v = verts[k+layerverts], &t = verts[k]; + v.setxyz(t.x, t.y, t.z); + if(hasuv) { v.u = f->getlil<ushort>(); v.v = f->getlil<ushort>(); } + v.norm = t.norm; + } + } + } + } + + c.children = (haschildren ? loadchildren(f, co, size>>1, failed) : NULL); } cube *loadchildren(stream *f, const ivec &co, int size, bool &failed) { - cube *c = newcubes(); - loopi(8) - { - loadc(f, c[i], ivec(i, co, size), size, failed); - if(failed) break; - } - return c; + cube *c = newcubes(); + loopi(8) + { + loadc(f, c[i], ivec(i, co, size), size, failed); + if(failed) break; + } + return c; } void savevslot(stream *f, VSlot &vs, int prev) { - f->putlil<int>(vs.changed); - f->putlil<int>(prev); - if(vs.changed & (1<<VSLOT_SHPARAM)) - { - f->putlil<ushort>(vs.params.length()); - loopv(vs.params) - { - SlotShaderParam &p = vs.params[i]; - f->putlil<ushort>(strlen(p.name)); - f->write(p.name, strlen(p.name)); - loopk(4) f->putlil<float>(p.val[k]); - } - } - if(vs.changed & (1<<VSLOT_SCALE)) f->putlil<float>(vs.scale); - if(vs.changed & (1<<VSLOT_ROTATION)) f->putlil<int>(vs.rotation); - if(vs.changed & (1<<VSLOT_OFFSET)) - { - f->putlil<int>(vs.offset.x); - f->putlil<int>(vs.offset.y); - } - if(vs.changed & (1<<VSLOT_SCROLL)) - { - f->putlil<float>(vs.scroll.x); - f->putlil<float>(vs.scroll.y); - } - if(vs.changed & (1<<VSLOT_LAYER)) f->putlil<int>(vs.layer); - if(vs.changed & (1<<VSLOT_ALPHA)) - { - f->putlil<float>(vs.alphafront); - f->putlil<float>(vs.alphaback); - } - if(vs.changed & (1<<VSLOT_COLOR)) - { - loopk(3) f->putlil<float>(vs.colorscale[k]); - } + f->putlil<int>(vs.changed); + f->putlil<int>(prev); + if(vs.changed & (1<<VSLOT_SHPARAM)) + { + f->putlil<ushort>(vs.params.length()); + loopv(vs.params) + { + SlotShaderParam &p = vs.params[i]; + f->putlil<ushort>(strlen(p.name)); + f->write(p.name, strlen(p.name)); + loopk(4) f->putlil<float>(p.val[k]); + } + } + if(vs.changed & (1<<VSLOT_SCALE)) f->putlil<float>(vs.scale); + if(vs.changed & (1<<VSLOT_ROTATION)) f->putlil<int>(vs.rotation); + if(vs.changed & (1<<VSLOT_OFFSET)) + { + f->putlil<int>(vs.offset.x); + f->putlil<int>(vs.offset.y); + } + if(vs.changed & (1<<VSLOT_SCROLL)) + { + f->putlil<float>(vs.scroll.x); + f->putlil<float>(vs.scroll.y); + } + if(vs.changed & (1<<VSLOT_LAYER)) f->putlil<int>(vs.layer); + if(vs.changed & (1<<VSLOT_ALPHA)) + { + f->putlil<float>(vs.alphafront); + f->putlil<float>(vs.alphaback); + } + if(vs.changed & (1<<VSLOT_COLOR)) + { + loopk(3) f->putlil<float>(vs.colorscale[k]); + } } void savevslots(stream *f, int numvslots) { - if(vslots.empty()) return; - int *prev = new int[numvslots]; - for(int i=0;i<numvslots;++i)prev[i]=-1; - loopi(numvslots) - { - VSlot *vs = vslots[i]; - if(vs->changed) continue; - for(;;) - { - VSlot *cur = vs; - do vs = vs->next; while(vs && vs->index >= numvslots); - if(!vs) break; - prev[vs->index] = cur->index; - } - } - int lastroot = 0; - loopi(numvslots) - { - VSlot &vs = *vslots[i]; - if(!vs.changed) continue; - if(lastroot < i) f->putlil<int>(-(i - lastroot)); - savevslot(f, vs, prev[i]); - lastroot = i+1; - } - if(lastroot < numvslots) f->putlil<int>(-(numvslots - lastroot)); - delete[] prev; + if(vslots.empty()) return; + int *prev = new int[numvslots]; + for(int i=0;i<numvslots;++i)prev[i]=-1; + loopi(numvslots) + { + VSlot *vs = vslots[i]; + if(vs->changed) continue; + for(;;) + { + VSlot *cur = vs; + do vs = vs->next; while(vs && vs->index >= numvslots); + if(!vs) break; + prev[vs->index] = cur->index; + } + } + int lastroot = 0; + loopi(numvslots) + { + VSlot &vs = *vslots[i]; + if(!vs.changed) continue; + if(lastroot < i) f->putlil<int>(-(i - lastroot)); + savevslot(f, vs, prev[i]); + lastroot = i+1; + } + if(lastroot < numvslots) f->putlil<int>(-(numvslots - lastroot)); + delete[] prev; } void loadvslot(stream *f, VSlot &vs, int changed) { - vs.changed = changed; - if(vs.changed & (1<<VSLOT_SHPARAM)) - { - int numparams = f->getlil<ushort>(); - string name; - loopi(numparams) - { - SlotShaderParam &p = vs.params.add(); - int nlen = f->getlil<ushort>(); - f->read(name, min(nlen, MAXSTRLEN-1)); - name[min(nlen, MAXSTRLEN-1)] = '\0'; - if(nlen >= MAXSTRLEN) f->seek(nlen - (MAXSTRLEN-1), SEEK_CUR); - p.name = getshaderparamname(name); - p.loc = -1; - loopk(4) p.val[k] = f->getlil<float>(); - } - } - if(vs.changed & (1<<VSLOT_SCALE)) vs.scale = f->getlil<float>(); - if(vs.changed & (1<<VSLOT_ROTATION)) vs.rotation = clamp(f->getlil<int>(), 0, 7); - if(vs.changed & (1<<VSLOT_OFFSET)) - { - vs.offset.x = f->getlil<int>(); - vs.offset.y = f->getlil<int>(); - } - if(vs.changed & (1<<VSLOT_SCROLL)) - { - vs.scroll.x = f->getlil<float>(); - vs.scroll.y = f->getlil<float>(); - } - if(vs.changed & (1<<VSLOT_LAYER)) vs.layer = f->getlil<int>(); - if(vs.changed & (1<<VSLOT_ALPHA)) - { - vs.alphafront = f->getlil<float>(); - vs.alphaback = f->getlil<float>(); - } - if(vs.changed & (1<<VSLOT_COLOR)) - { - loopk(3) vs.colorscale[k] = f->getlil<float>(); - } + vs.changed = changed; + if(vs.changed & (1<<VSLOT_SHPARAM)) + { + int numparams = f->getlil<ushort>(); + string name; + loopi(numparams) + { + SlotShaderParam &p = vs.params.add(); + int nlen = f->getlil<ushort>(); + f->read(name, min(nlen, MAXSTRLEN-1)); + name[min(nlen, MAXSTRLEN-1)] = '\0'; + if(nlen >= MAXSTRLEN) f->seek(nlen - (MAXSTRLEN-1), SEEK_CUR); + p.name = getshaderparamname(name); + p.loc = -1; + loopk(4) p.val[k] = f->getlil<float>(); + } + } + if(vs.changed & (1<<VSLOT_SCALE)) vs.scale = f->getlil<float>(); + if(vs.changed & (1<<VSLOT_ROTATION)) vs.rotation = clamp(f->getlil<int>(), 0, 7); + if(vs.changed & (1<<VSLOT_OFFSET)) + { + vs.offset.x = f->getlil<int>(); + vs.offset.y = f->getlil<int>(); + } + if(vs.changed & (1<<VSLOT_SCROLL)) + { + vs.scroll.x = f->getlil<float>(); + vs.scroll.y = f->getlil<float>(); + } + if(vs.changed & (1<<VSLOT_LAYER)) vs.layer = f->getlil<int>(); + if(vs.changed & (1<<VSLOT_ALPHA)) + { + vs.alphafront = f->getlil<float>(); + vs.alphaback = f->getlil<float>(); + } + if(vs.changed & (1<<VSLOT_COLOR)) + { + loopk(3) vs.colorscale[k] = f->getlil<float>(); + } } void loadvslots(stream *f, int numvslots) { - int *prev = new (false) int[numvslots]; - if(!prev) return; - for(int i=0;i<numvslots;++i)prev[i]=-1; - while(numvslots > 0) - { - int changed = f->getlil<int>(); - if(changed < 0) - { - loopi(-changed) vslots.add(new VSlot(NULL, vslots.length())); - numvslots += changed; - } - else - { - prev[vslots.length()] = f->getlil<int>(); - loadvslot(f, *vslots.add(new VSlot(NULL, vslots.length())), changed); - numvslots--; - } - } - loopv(vslots) if(vslots.inrange(prev[i])) vslots[prev[i]]->next = vslots[i]; - delete[] prev; + int *prev = new (false) int[numvslots]; + if(!prev) return; + for(int i=0;i<numvslots;++i)prev[i]=-1; + while(numvslots > 0) + { + int changed = f->getlil<int>(); + if(changed < 0) + { + loopi(-changed) vslots.add(new VSlot(NULL, vslots.length())); + numvslots += changed; + } + else + { + prev[vslots.length()] = f->getlil<int>(); + loadvslot(f, *vslots.add(new VSlot(NULL, vslots.length())), changed); + numvslots--; + } + } + loopv(vslots) if(vslots.inrange(prev[i])) vslots[prev[i]]->next = vslots[i]; + delete[] prev; } bool save_world(const char *mname, bool nolms) { - if(!*mname) mname = game::getclientmap(); - setmapfilenames(mname); - if(savebak) backup(ogzname, bakname); - stream *f = opengzfile(ogzname, "wb"); - if(!f) { conoutf(CON_WARN, "could not write map to %s", ogzname); return false; } - - int numvslots = vslots.length(); - if(!nolms && !multiplayer(false)) - { - numvslots = compactvslots(); - allchanged(); - } - - savemapprogress = 0; - renderprogress(0, "saving map..."); - - octaheader hdr; - memcpy(hdr.magic, "OCTA", 4); - hdr.version = MAPVERSION; - hdr.headersize = sizeof(hdr); - hdr.worldsize = worldsize; - hdr.numents = 0; - const vector<extentity *> &ents = entities::getents(); - loopv(ents) if(ents[i]->type!=ET_EMPTY || nolms) hdr.numents++; - hdr.numpvs = 0; - hdr.lightmaps = nolms ? 0 : lightmaps.length(); - hdr.blendmap = shouldsaveblendmap(); - hdr.numvars = 0; - hdr.numvslots = numvslots; - enumerate(idents, ident, id, - { - if((id.type == ID_VAR || id.type == ID_FVAR || id.type == ID_SVAR) && id.flags&IDF_OVERRIDE && !(id.flags&IDF_READONLY) && id.flags&IDF_OVERRIDDEN) hdr.numvars++; - }); - lilswap(&hdr.version, 9); - f->write(&hdr, sizeof(hdr)); - - enumerate(idents, ident, id, - { - if((id.type!=ID_VAR && id.type!=ID_FVAR && id.type!=ID_SVAR) || !(id.flags&IDF_OVERRIDE) || id.flags&IDF_READONLY || !(id.flags&IDF_OVERRIDDEN)) continue; - f->putchar(id.type); - f->putlil<ushort>(strlen(id.name)); - f->write(id.name, strlen(id.name)); - switch(id.type) - { - case ID_VAR: - f->putlil<int>(*id.storage.i); - break; - - case ID_FVAR: - f->putlil<float>(*id.storage.f); - break; - - case ID_SVAR: - f->putlil<ushort>(strlen(*id.storage.s)); - f->write(*id.storage.s, strlen(*id.storage.s)); - break; - } - }); - - f->putchar((int)strlen(game::gameident())); - f->write(game::gameident(), (int)strlen(game::gameident())+1); - f->putlil<ushort>(entities::extraentinfosize()); - vector<char> extras; - game::writegamedata(extras); - f->putlil<ushort>(extras.length()); - f->write(extras.getbuf(), extras.length()); - - f->putlil<ushort>(texmru.length()); - loopv(texmru) f->putlil<ushort>(texmru[i]); - char *ebuf = new char[entities::extraentinfosize()]; - loopv(ents) - { - if(ents[i]->type!=ET_EMPTY || nolms) - { - entity tmp = *ents[i]; - lilswap(&tmp.o.x, 3); - lilswap(&tmp.attr1, 5); - f->write(&tmp, sizeof(entity)); - entities::writeent(*ents[i], ebuf); - if(entities::extraentinfosize()) f->write(ebuf, entities::extraentinfosize()); - } - } - delete[] ebuf; - - savevslots(f, numvslots); - - renderprogress(0, "saving octree..."); - savec(worldroot, ivec(0, 0, 0), worldsize>>1, f, nolms); - - if(!nolms) - { - if(lightmaps.length()) renderprogress(0, "saving lightmaps..."); - loopv(lightmaps) - { - LightMap &lm = lightmaps[i]; - f->putchar(lm.type | (lm.unlitx>=0 ? 0x80 : 0)); - if(lm.unlitx>=0) - { - f->putlil<ushort>(ushort(lm.unlitx)); - f->putlil<ushort>(ushort(lm.unlity)); - } - f->write(lm.data, lm.bpp*LM_PACKW*LM_PACKH); - renderprogress(float(i+1)/lightmaps.length(), "saving lightmaps..."); - } - } - if(shouldsaveblendmap()) { renderprogress(0, "saving blendmap..."); saveblendmap(f); } - - delete f; - conoutf("wrote map file %s", ogzname); - return true; + if(!*mname) mname = game::getclientmap(); + setmapfilenames(mname); + if(savebak) backup(ogzname, bakname); + stream *f = opengzfile(ogzname, "wb"); + if(!f) { conoutf(CON_WARN, "could not write map to %s", ogzname); return false; } + + int numvslots = vslots.length(); + if(!nolms && !multiplayer(false)) + { + numvslots = compactvslots(); + allchanged(); + } + + savemapprogress = 0; + renderprogress(0, "saving map..."); + + octaheader hdr; + memcpy(hdr.magic, "OCTA", 4); + hdr.version = MAPVERSION; + hdr.headersize = sizeof(hdr); + hdr.worldsize = worldsize; + hdr.numents = 0; + const vector<extentity *> &ents = entities::getents(); + loopv(ents) if(ents[i]->type!=ET_EMPTY || nolms) hdr.numents++; + hdr.numpvs = 0; + hdr.lightmaps = nolms ? 0 : lightmaps.length(); + hdr.blendmap = shouldsaveblendmap(); + hdr.numvars = 0; + hdr.numvslots = numvslots; + enumerate(idents, ident, id, + { + if((id.type == ID_VAR || id.type == ID_FVAR || id.type == ID_SVAR) && id.flags&IDF_OVERRIDE && !(id.flags&IDF_READONLY) && id.flags&IDF_OVERRIDDEN) hdr.numvars++; + }); + lilswap(&hdr.version, 9); + f->write(&hdr, sizeof(hdr)); + + enumerate(idents, ident, id, + { + if((id.type!=ID_VAR && id.type!=ID_FVAR && id.type!=ID_SVAR) || !(id.flags&IDF_OVERRIDE) || id.flags&IDF_READONLY || !(id.flags&IDF_OVERRIDDEN)) continue; + f->putchar(id.type); + f->putlil<ushort>(strlen(id.name)); + f->write(id.name, strlen(id.name)); + switch(id.type) + { + case ID_VAR: + f->putlil<int>(*id.storage.i); + break; + + case ID_FVAR: + f->putlil<float>(*id.storage.f); + break; + + case ID_SVAR: + f->putlil<ushort>(strlen(*id.storage.s)); + f->write(*id.storage.s, strlen(*id.storage.s)); + break; + } + }); + + f->putchar((int)strlen(game::gameident())); + f->write(game::gameident(), (int)strlen(game::gameident())+1); + f->putlil<ushort>(entities::extraentinfosize()); + vector<char> extras; + game::writegamedata(extras); + f->putlil<ushort>(extras.length()); + f->write(extras.getbuf(), extras.length()); + + f->putlil<ushort>(texmru.length()); + loopv(texmru) f->putlil<ushort>(texmru[i]); + char *ebuf = new char[entities::extraentinfosize()]; + loopv(ents) + { + if(ents[i]->type!=ET_EMPTY || nolms) + { + entity tmp = *ents[i]; + lilswap(&tmp.o.x, 3); + lilswap(&tmp.attr1, 5); + f->write(&tmp, sizeof(entity)); + entities::writeent(*ents[i], ebuf); + if(entities::extraentinfosize()) f->write(ebuf, entities::extraentinfosize()); + } + } + delete[] ebuf; + + savevslots(f, numvslots); + + renderprogress(0, "saving octree..."); + savec(worldroot, ivec(0, 0, 0), worldsize>>1, f, nolms); + + if(!nolms) + { + if(lightmaps.length()) renderprogress(0, "saving lightmaps..."); + loopv(lightmaps) + { + LightMap &lm = lightmaps[i]; + f->putchar(lm.type | (lm.unlitx>=0 ? 0x80 : 0)); + if(lm.unlitx>=0) + { + f->putlil<ushort>(ushort(lm.unlitx)); + f->putlil<ushort>(ushort(lm.unlity)); + } + f->write(lm.data, lm.bpp*LM_PACKW*LM_PACKH); + renderprogress(float(i+1)/lightmaps.length(), "saving lightmaps..."); + } + } + if(shouldsaveblendmap()) { renderprogress(0, "saving blendmap..."); saveblendmap(f); } + + delete f; + conoutf("wrote map file %s", ogzname); + return true; } static uint mapcrc = 0; @@ -991,276 +991,271 @@ static uint mapcrc = 0; uint getmapcrc() { return mapcrc; } void clearmapcrc() { mapcrc = 0; } -bool load_world(const char *mname, const char *cname) // still supports all map formats that have existed since the earliest cube betas! +bool load_world(const char *mname, const char *cname) // still supports all map formats that have existed since the earliest cube betas! { - int loadingstart = SDL_GetTicks(); - setmapfilenames(mname, cname); - stream *f = opengzfile(ogzname, "rb"); - if(!f) { conoutf(CON_ERROR, "could not read map %s", ogzname); return false; } - octaheader hdr; - if(f->read(&hdr, 7*sizeof(int)) != 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } - lilswap(&hdr.version, 6); - if(memcmp(hdr.magic, "OCTA", 4) || hdr.worldsize <= 0|| hdr.numents < 0) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } - if(hdr.version>MAPVERSION) { conoutf(CON_ERROR, "map %s requires a newer version of Cube 2: Sauerbraten", ogzname); delete f; return false; } - compatheader chdr; - if(hdr.version <= 28) - { - if(f->read(&chdr.lightprecision, sizeof(chdr) - 7*sizeof(int)) != sizeof(chdr) - 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } - } - else - { - int extra = 0; - if(hdr.version <= 29) extra++; - if(f->read(&hdr.blendmap, sizeof(hdr) - (7+extra)*sizeof(int)) != sizeof(hdr) - (7+extra)*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } - } - - resetmap(); - - Texture *mapshot = textureload(picname, 3, true, false); - renderbackground("loading...", mapshot, mname, game::getmapinfo()); - - game::loadingmap(cname ? cname : mname); - - setvar("mapversion", hdr.version, true, false); - - if(hdr.version <= 28) - { - lilswap(&chdr.lightprecision, 3); - if(chdr.lightprecision) setvar("lightprecision", chdr.lightprecision); - if(chdr.lighterror) setvar("lighterror", chdr.lighterror); - if(chdr.bumperror) setvar("bumperror", chdr.bumperror); - setvar("lightlod", chdr.lightlod); - if(chdr.ambient) setvar("ambient", chdr.ambient); - setvar("skylight", (int(chdr.skylight[0])<<16) | (int(chdr.skylight[1])<<8) | int(chdr.skylight[2])); - setvar("watercolour", (int(chdr.watercolour[0])<<16) | (int(chdr.watercolour[1])<<8) | int(chdr.watercolour[2]), true); - setvar("waterfallcolour", (int(chdr.waterfallcolour[0])<<16) | (int(chdr.waterfallcolour[1])<<8) | int(chdr.waterfallcolour[2])); - setvar("lavacolour", (int(chdr.lavacolour[0])<<16) | (int(chdr.lavacolour[1])<<8) | int(chdr.lavacolour[2])); - setvar("fullbright", 0, true); - if(chdr.lerpsubdivsize || chdr.lerpangle) setvar("lerpangle", chdr.lerpangle); - if(chdr.lerpsubdivsize) - { - setvar("lerpsubdiv", chdr.lerpsubdiv); - setvar("lerpsubdivsize", chdr.lerpsubdivsize); - } - setsvar("maptitle", chdr.maptitle); - hdr.blendmap = chdr.blendmap; - hdr.numvars = 0; - hdr.numvslots = 0; - } - else - { - lilswap(&hdr.blendmap, 2); - if(hdr.version <= 29) hdr.numvslots = 0; - else lilswap(&hdr.numvslots, 1); - } - - renderprogress(0, "clearing world..."); - - freeocta(worldroot); - worldroot = NULL; - - int worldscale = 0; - while(1<<worldscale < hdr.worldsize) worldscale++; - setvar("mapsize", 1<<worldscale, true, false); - setvar("mapscale", worldscale, true, false); - - renderprogress(0, "loading vars..."); - - loopi(hdr.numvars) - { - int type = f->getchar(), ilen = f->getlil<ushort>(); - string name; - f->read(name, min(ilen, MAXSTRLEN-1)); - name[min(ilen, MAXSTRLEN-1)] = '\0'; - if(ilen >= MAXSTRLEN) f->seek(ilen - (MAXSTRLEN-1), SEEK_CUR); - ident *id = getident(name); - bool exists = id && id->type == type && id->flags&IDF_OVERRIDE; - switch(type) - { - case ID_VAR: - { - int val = f->getlil<int>(); - if(exists && id->minval <= id->maxval) setvar(name, val); - break; - } - - case ID_FVAR: - { - float val = f->getlil<float>(); - if(exists && id->minvalf <= id->maxvalf) setfvar(name, val); - break; - } - - case ID_SVAR: - { - int slen = f->getlil<ushort>(); - string val; - f->read(val, min(slen, MAXSTRLEN-1)); - val[min(slen, MAXSTRLEN-1)] = '\0'; - if(slen >= MAXSTRLEN) f->seek(slen - (MAXSTRLEN-1), SEEK_CUR); - if(exists) setsvar(name, val); - break; - } - } - } - - string gametype; - copystring(gametype, "fps"); - bool samegame = true; - int eif = 0; - if(hdr.version>=16) - { - int len = f->getchar(); - f->read(gametype, len+1); - } - if(strcmp(gametype, game::gameident())!=0) - { - samegame = false; - conoutf(CON_WARN, "WARNING: loading map from %s game, ignoring entities except for lights/mapmodels", gametype); - } - if(hdr.version>=16) - { - eif = f->getlil<ushort>(); - int extrasize = f->getlil<ushort>(); - vector<char> extras; - f->read(extras.pad(extrasize), extrasize); - if(samegame) game::readgamedata(extras); - } - - texmru.shrink(0); - if(hdr.version<14) - { - uchar oldtl[256]; - f->read(oldtl, sizeof(oldtl)); - loopi(256) texmru.add(oldtl[i]); - } - else - { - ushort nummru = f->getlil<ushort>(); - loopi(nummru) texmru.add(f->getlil<ushort>()); - } - - renderprogress(0, "loading entities..."); - - vector<extentity *> &ents = entities::getents(); - int einfosize = entities::extraentinfosize(); - char *ebuf = einfosize > 0 ? new char[einfosize] : NULL; - loopi(min(hdr.numents, MAXENTS)) - { - extentity &e = *entities::newentity(); - ents.add(&e); - f->read(&e, sizeof(entity)); - lilswap(&e.o.x, 3); - lilswap(&e.attr1, 5); - fixent(e, hdr.version); - if(samegame) - { - if(einfosize > 0) f->read(ebuf, einfosize); - entities::readent(e, ebuf, mapversion); - } - else - { - if(eif > 0) f->seek(eif, SEEK_CUR); - if(e.type>=ET_GAMESPECIFIC || hdr.version<=14) - { - entities::deleteentity(ents.pop()); - continue; - } - } - if(!insideworld(e.o)) - { - if(e.type != ET_LIGHT && e.type != ET_SPOTLIGHT) - { - conoutf(CON_WARN, "warning: ent outside of world: enttype[%s] index %d (%f, %f, %f)", entities::entname(e.type), i, e.o.x, e.o.y, e.o.z); - } - } - if(hdr.version <= 14 && e.type == ET_MAPMODEL) - { - e.o.z += e.attr3; - if(e.attr4) conoutf(CON_WARN, "warning: mapmodel ent (index %d) uses texture slot %d", i, e.attr4); - e.attr3 = e.attr4 = 0; - } - } - if(ebuf) delete[] ebuf; - - if(hdr.numents > MAXENTS) - { - conoutf(CON_WARN, "warning: map has %d entities", hdr.numents); - f->seek((hdr.numents-MAXENTS)*(samegame ? sizeof(entity) + einfosize : eif), SEEK_CUR); - } - - renderprogress(0, "loading slots..."); - loadvslots(f, hdr.numvslots); - - renderprogress(0, "loading octree..."); - bool failed = false; - worldroot = loadchildren(f, ivec(0, 0, 0), hdr.worldsize>>1, failed); - if(failed) conoutf(CON_ERROR, "garbage in map"); - - renderprogress(0, "validating..."); - validatec(worldroot, hdr.worldsize>>1); - - if(!failed) - { - if(hdr.version >= 7) loopi(hdr.lightmaps) - { - renderprogress(i/(float)hdr.lightmaps, "loading lightmaps..."); - LightMap &lm = lightmaps.add(); - if(hdr.version >= 17) - { - int type = f->getchar(); - lm.type = type&0x7F; - if(hdr.version >= 20 && type&0x80) - { - lm.unlitx = f->getlil<ushort>(); - lm.unlity = f->getlil<ushort>(); - } - } - if(lm.type&LM_ALPHA && (lm.type&LM_TYPE)!=LM_BUMPMAP1) lm.bpp = 4; - lm.data = new uchar[lm.bpp*LM_PACKW*LM_PACKH]; - f->read(lm.data, lm.bpp * LM_PACKW * LM_PACKH); - lm.finalize(); - } - - if(hdr.version >= 28 && hdr.blendmap) loadblendmap(f, hdr.blendmap); - } - - mapcrc = f->getcrc(); - delete f; - - conoutf("read map %s (%.1f seconds)", ogzname, (SDL_GetTicks()-loadingstart)/1000.0f); - - clearmainmenu(); - - identflags |= IDF_OVERRIDDEN; - execfile("data/default_map.cfg", false); - execfile(cfgname, false); - identflags &= ~IDF_OVERRIDDEN; - - extern void fixlightmapnormals(); - if(hdr.version <= 25) fixlightmapnormals(); - extern void fixrotatedlightmaps(); - if(hdr.version <= 31) fixrotatedlightmaps(); - - preloadusedmapmodels(true); - - game::preload(); - flushpreloadedmodels(); - - preloadmapsounds(); - - entitiesinoctanodes(); - attachentities(); - initlights(); - allchanged(true); - - renderbackground("loading...", mapshot, mname, game::getmapinfo()); - - if(maptitle[0] && strcmp(maptitle, "Untitled Map by Unknown")) conoutf(CON_ECHO, "%s", maptitle); - - startmap(cname ? cname : mname); - - return true; + int loadingstart = SDL_GetTicks(); + setmapfilenames(mname, cname); + stream *f = opengzfile(ogzname, "rb"); + if(!f) { conoutf(CON_ERROR, "could not read map %s", ogzname); return false; } + octaheader hdr; + if(f->read(&hdr, 7*sizeof(int)) != 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } + lilswap(&hdr.version, 6); + if(memcmp(hdr.magic, "OCTA", 4) || hdr.worldsize <= 0|| hdr.numents < 0) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } + if(hdr.version>MAPVERSION) { conoutf(CON_ERROR, "map %s requires a newer version of Cube 2: Sauerbraten", ogzname); delete f; return false; } + compatheader chdr; + if(hdr.version <= 28) + { + if(f->read(&chdr.lightprecision, sizeof(chdr) - 7*sizeof(int)) != sizeof(chdr) - 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } + } + else + { + int extra = 0; + if(hdr.version <= 29) extra++; + if(f->read(&hdr.blendmap, sizeof(hdr) - (7+extra)*sizeof(int)) != sizeof(hdr) - (7+extra)*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; } + } + + resetmap(); + + Texture *mapshot = textureload(picname, 3, true, false); + renderbackground("loading...", mapshot, mname, game::getmapinfo()); + + game::loadingmap(cname ? cname : mname); + + setvar("mapversion", hdr.version, true, false); + + if(hdr.version <= 28) + { + lilswap(&chdr.lightprecision, 3); + if(chdr.lightprecision) setvar("lightprecision", chdr.lightprecision); + if(chdr.lighterror) setvar("lighterror", chdr.lighterror); + if(chdr.bumperror) setvar("bumperror", chdr.bumperror); + setvar("lightlod", chdr.lightlod); + if(chdr.ambient) setvar("ambient", chdr.ambient); + setvar("skylight", (int(chdr.skylight[0])<<16) | (int(chdr.skylight[1])<<8) | int(chdr.skylight[2])); + setvar("watercolour", (int(chdr.watercolour[0])<<16) | (int(chdr.watercolour[1])<<8) | int(chdr.watercolour[2]), true); + setvar("waterfallcolour", (int(chdr.waterfallcolour[0])<<16) | (int(chdr.waterfallcolour[1])<<8) | int(chdr.waterfallcolour[2])); + setvar("lavacolour", (int(chdr.lavacolour[0])<<16) | (int(chdr.lavacolour[1])<<8) | int(chdr.lavacolour[2])); + setvar("fullbright", 0, true); + if(chdr.lerpsubdivsize || chdr.lerpangle) setvar("lerpangle", chdr.lerpangle); + if(chdr.lerpsubdivsize) + { + setvar("lerpsubdiv", chdr.lerpsubdiv); + setvar("lerpsubdivsize", chdr.lerpsubdivsize); + } + setsvar("maptitle", chdr.maptitle); + hdr.blendmap = chdr.blendmap; + hdr.numvars = 0; + hdr.numvslots = 0; + } + else + { + lilswap(&hdr.blendmap, 2); + if(hdr.version <= 29) hdr.numvslots = 0; + else lilswap(&hdr.numvslots, 1); + } + + renderprogress(0, "clearing world..."); + + freeocta(worldroot); + worldroot = NULL; + + int worldscale = 0; + while(1<<worldscale < hdr.worldsize) worldscale++; + setvar("mapsize", 1<<worldscale, true, false); + setvar("mapscale", worldscale, true, false); + + renderprogress(0, "loading vars..."); + + loopi(hdr.numvars) + { + int type = f->getchar(), ilen = f->getlil<ushort>(); + string name; + f->read(name, min(ilen, MAXSTRLEN-1)); + name[min(ilen, MAXSTRLEN-1)] = '\0'; + if(ilen >= MAXSTRLEN) f->seek(ilen - (MAXSTRLEN-1), SEEK_CUR); + ident *id = getident(name); + bool exists = id && id->type == type && id->flags&IDF_OVERRIDE; + switch(type) + { + case ID_VAR: + { + int val = f->getlil<int>(); + if(exists && id->minval <= id->maxval) setvar(name, val); + break; + } + + case ID_FVAR: + { + float val = f->getlil<float>(); + if(exists && id->minvalf <= id->maxvalf) setfvar(name, val); + break; + } + + case ID_SVAR: + { + int slen = f->getlil<ushort>(); + string val; + f->read(val, min(slen, MAXSTRLEN-1)); + val[min(slen, MAXSTRLEN-1)] = '\0'; + if(slen >= MAXSTRLEN) f->seek(slen - (MAXSTRLEN-1), SEEK_CUR); + if(exists) setsvar(name, val); + break; + } + } + } + + string gametype; + copystring(gametype, "fps"); + bool samegame = true; + int eif = 0; + if(hdr.version>=16) + { + int len = f->getchar(); + f->read(gametype, len+1); + } + if(strcmp(gametype, game::gameident())!=0) + { + samegame = false; + conoutf(CON_WARN, "WARNING: loading map from %s game, ignoring entities except for lights/mapmodels", gametype); + } + if(hdr.version>=16) + { + eif = f->getlil<ushort>(); + int extrasize = f->getlil<ushort>(); + vector<char> extras; + f->read(extras.pad(extrasize), extrasize); + if(samegame) game::readgamedata(extras); + } + + texmru.shrink(0); + if(hdr.version<14) + { + uchar oldtl[256]; + f->read(oldtl, sizeof(oldtl)); + loopi(256) texmru.add(oldtl[i]); + } + else + { + ushort nummru = f->getlil<ushort>(); + loopi(nummru) texmru.add(f->getlil<ushort>()); + } + + renderprogress(0, "loading entities..."); + + vector<extentity *> &ents = entities::getents(); + int einfosize = entities::extraentinfosize(); + char *ebuf = einfosize > 0 ? new char[einfosize] : NULL; + loopi(min(hdr.numents, MAXENTS)) + { + extentity &e = *entities::newentity(); + ents.add(&e); + f->read(&e, sizeof(entity)); + lilswap(&e.o.x, 3); + lilswap(&e.attr1, 5); + fixent(e, hdr.version); + if(samegame) + { + if(einfosize > 0) f->read(ebuf, einfosize); + entities::readent(e, ebuf, mapversion); + } + else + { + if(eif > 0) f->seek(eif, SEEK_CUR); + if(e.type>=ET_GAMESPECIFIC || hdr.version<=14) + { + entities::deleteentity(ents.pop()); + continue; + } + } + if(!insideworld(e.o)) + { + if(e.type != ET_LIGHT && e.type != ET_SPOTLIGHT) + { + conoutf(CON_WARN, "warning: ent outside of world: enttype[%s] index %d (%f, %f, %f)", entities::entname(e.type), i, e.o.x, e.o.y, e.o.z); + } + } + if(hdr.version <= 14 && e.type == ET_MAPMODEL) + { + e.o.z += e.attr3; + if(e.attr4) conoutf(CON_WARN, "warning: mapmodel ent (index %d) uses texture slot %d", i, e.attr4); + e.attr3 = e.attr4 = 0; + } + } + if(ebuf) delete[] ebuf; + + if(hdr.numents > MAXENTS) + { + conoutf(CON_WARN, "warning: map has %d entities", hdr.numents); + f->seek((hdr.numents-MAXENTS)*(samegame ? sizeof(entity) + einfosize : eif), SEEK_CUR); + } + + renderprogress(0, "loading slots..."); + loadvslots(f, hdr.numvslots); + + renderprogress(0, "loading octree..."); + bool failed = false; + worldroot = loadchildren(f, ivec(0, 0, 0), hdr.worldsize>>1, failed); + if(failed) conoutf(CON_ERROR, "garbage in map"); + + renderprogress(0, "validating..."); + validatec(worldroot, hdr.worldsize>>1); + + if(!failed) + { + if(hdr.version >= 7) loopi(hdr.lightmaps) + { + renderprogress(i/(float)hdr.lightmaps, "loading lightmaps..."); + LightMap &lm = lightmaps.add(); + if(hdr.version >= 17) + { + int type = f->getchar(); + lm.type = type&0x7F; + if(hdr.version >= 20 && type&0x80) + { + lm.unlitx = f->getlil<ushort>(); + lm.unlity = f->getlil<ushort>(); + } + } + if(lm.type&LM_ALPHA && (lm.type&LM_TYPE)!=LM_BUMPMAP1) lm.bpp = 4; + lm.data = new uchar[lm.bpp*LM_PACKW*LM_PACKH]; + f->read(lm.data, lm.bpp * LM_PACKW * LM_PACKH); + lm.finalize(); + } + + if(hdr.version >= 28 && hdr.blendmap) loadblendmap(f, hdr.blendmap); + } + + mapcrc = f->getcrc(); + delete f; + + conoutf("read map %s (%.1f seconds)", ogzname, (SDL_GetTicks()-loadingstart)/1000.0f); + + clearmainmenu(); + + identflags |= IDF_OVERRIDDEN; + execfile("data/default_map.cfg", false); + execfile(cfgname, false); + identflags &= ~IDF_OVERRIDDEN; + + preloadusedmapmodels(true); + + game::preload(); + flushpreloadedmodels(); + + preloadmapsounds(); + + entitiesinoctanodes(); + attachentities(); + initlights(); + allchanged(true); + + renderbackground("loading...", mapshot, mname, game::getmapinfo()); + + if(maptitle[0] && strcmp(maptitle, "Untitled Map by Unknown")) conoutf(CON_ECHO, "%s", maptitle); + + startmap(cname ? cname : mname); + + return true; } void savecurrentmap() { save_world(game::getclientmap()); } |
