From 9256ee374b0274079373177d81cf696c060974ca Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sun, 8 Nov 2009 00:53:19 +0000 Subject: [PATCH] support for loading from zip files git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@4717 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/codeblocks/AssaultCube.cbp | 4 +- source/src/Makefile | 7 +- source/src/audiomanager.cpp | 4 +- source/src/bot/bot_waypoint.cpp | 56 +-- source/src/clients2c.cpp | 6 +- source/src/command.cpp | 32 +- source/src/console.cpp | 4 +- source/src/docs.cpp | 52 +- source/src/entity.h | 2 +- source/src/main.cpp | 44 +- source/src/md2.h | 24 +- source/src/md3.h | 42 +- source/src/menus.cpp | 18 +- source/src/oggstream.cpp | 35 +- source/src/openal.cpp | 14 +- source/src/packetqueue.cpp | 18 +- source/src/protos.h | 9 +- source/src/server.cpp | 79 ++-- source/src/serverbrowser.cpp | 12 +- source/src/sound.h | 2 +- source/src/stream.cpp | 755 ++++++++++++++++++++++++++++++ source/src/texture.cpp | 102 +++- source/src/tools.cpp | 363 +------------- source/src/tools.h | 89 +++- source/src/vertmodel.h | 36 +- source/src/worldio.cpp | 164 +++---- source/src/zip.cpp | 557 ++++++++++++++++++++++ 27 files changed, 1852 insertions(+), 678 deletions(-) create mode 100644 source/src/stream.cpp create mode 100644 source/src/zip.cpp diff --git a/source/codeblocks/AssaultCube.cbp b/source/codeblocks/AssaultCube.cbp index d91dbaf3..56b1dfc7 100644 --- a/source/codeblocks/AssaultCube.cbp +++ b/source/codeblocks/AssaultCube.cbp @@ -41,7 +41,7 @@ - + @@ -111,6 +111,7 @@ + @@ -128,6 +129,7 @@ + diff --git a/source/src/Makefile b/source/src/Makefile index 27581ac8..646d8c46 100644 --- a/source/src/Makefile +++ b/source/src/Makefile @@ -57,6 +57,7 @@ CLIENT_OBJS= \ sound.o \ soundlocation.o \ soundscheduler.o \ + stream.o \ texture.o \ tools.o \ water.o \ @@ -67,6 +68,7 @@ CLIENT_OBJS= \ worldlight.o \ worldocull.o \ worldrender.o \ + zip.o \ bot/bot.o \ bot/botmanager.o \ bot/bot_ai.o \ @@ -89,9 +91,10 @@ endif SERVER_OBJS= \ log-standalone.o \ - serverms-standalone.o \ - server-standalone.o \ protocol-standalone.o \ + server-standalone.o \ + serverms-standalone.o \ + stream-standalone.o \ tools-standalone.o \ wizard-standalone.o diff --git a/source/src/audiomanager.cpp b/source/src/audiomanager.cpp index 72d1d031..88c00d88 100644 --- a/source/src/audiomanager.cpp +++ b/source/src/audiomanager.cpp @@ -360,9 +360,9 @@ int audiomanager::soundmuted(int n) return gamesounds.inrange(n) && gamesounds[n].muted ? 1 : 0; } -void audiomanager::writesoundconfig(FILE *f) +void audiomanager::writesoundconfig(stream *f) { - loopv(gamesounds) if(gamesounds[i].muted) fprintf(f, "mutesound %d\n", i); + loopv(gamesounds) if(gamesounds[i].muted) f->printf("mutesound %d\n", i); } diff --git a/source/src/bot/bot_waypoint.cpp b/source/src/bot/bot_waypoint.cpp index cd0e6d97..73dcdb90 100644 --- a/source/src/bot/bot_waypoint.cpp +++ b/source/src/bot/bot_waypoint.cpp @@ -86,7 +86,7 @@ TLinkedList::node_s *testx; // returns true if waypoints succesfull loaded bool CWaypointClass::LoadWaypoints() { - FILE *bfp; + stream *bfp; char szWPFileName[64]; char filename[256]; waypoint_header_s header; @@ -106,7 +106,7 @@ bool CWaypointClass::LoadWaypoints() // if file exists, read the waypoint structure from it if (bfp != NULL) { - fread(&header, sizeof(header), 1, bfp); + bfp->read(&header, sizeof(header)); header.szFileType[sizeof(header.szFileType)-1] = 0; if (strcmp(header.szFileType, "cube_bot") == 0) @@ -128,7 +128,7 @@ bool CWaypointClass::LoadWaypoints() m_iWaypointCount = header.iWPCount; for (i=0; i < header.iWPCount; i++) { - fread(&WPs[i], sizeof(WPs[0]), 1, bfp); + bfp->read(&WPs[i], sizeof(WPs[0])); WPs[i].ConnectedWPs.Reset(); // We get garbage when we read this from // a file, so just clear it @@ -146,7 +146,7 @@ bool CWaypointClass::LoadWaypoints() for (i=0; i < m_iWaypointCount; i++) { // read the number of paths from this node... - fread(&num, sizeof(num), 1, bfp); + bfp->read(&num, sizeof(num)); // See which waypoint the current is node_s *pCurrent = GetWaypointFromVec(WPs[i].v_origin); @@ -159,7 +159,7 @@ bool CWaypointClass::LoadWaypoints() for (j=0; j < num; j++) { - fread(&path_index, sizeof(path_index), 1, bfp); + bfp->read(&path_index, sizeof(path_index)); // See which waypoint this is node_s *pTo = GetWaypointFromVec(WPs[path_index].v_origin); @@ -184,10 +184,10 @@ bool CWaypointClass::LoadWaypoints() for (i=0; i < header.iWPCount; i++) { - fread(&from, sizeof(from), 1, bfp); // Read origin - fread(&flags, sizeof(flags), 1, bfp); // Read waypoint flags - fread(&triggernr, sizeof(triggernr), 1, bfp); // Read trigger nr - fread(&yaw, sizeof(yaw), 1, bfp); // Read target yaw + bfp->read(&from, sizeof(from)); // Read origin + bfp->read(&flags, sizeof(flags)); // Read waypoint flags + bfp->read(&triggernr, sizeof(triggernr)); // Read trigger nr + bfp->read(&yaw, sizeof(yaw)); // Read target yaw node_s *pNode = new node_s(from, flags, triggernr, yaw); @@ -205,8 +205,8 @@ bool CWaypointClass::LoadWaypoints() for (i=0; i < header.iWPCount; i++) { // read the number of paths from this node... - fread(&num, sizeof(num), 1, bfp); - fread(&from, sizeof(from), 1, bfp); + bfp->read(&num, sizeof(num)); + bfp->read(&from, sizeof(from)); if (!num) continue; @@ -217,13 +217,13 @@ bool CWaypointClass::LoadWaypoints() { conoutf("Error: NULL path in waypoint file"); - for(j=0;jread(&to, sizeof(to)); // Read rest of block continue; } for (j=0; j < num; j++) { - fread(&to, sizeof(to), 1, bfp); + bfp->read(&to, sizeof(to)); node_s *p = GetWaypointFromVec(to); if (p) AddPath(pCurrent, p); @@ -233,13 +233,13 @@ bool CWaypointClass::LoadWaypoints() else if (header.iFileVersion > WAYPOINT_VERSION) { conoutf("Error: Waypoint file is newer then current, upgrade cube bot."); - fclose(bfp); + delete bfp; return false; } else { conoutf("Error: Unknown waypoint version for cube bot"); - fclose(bfp); + delete bfp; return false; } } @@ -247,7 +247,7 @@ bool CWaypointClass::LoadWaypoints() { conoutf("Waypoints aren't for map %s but for map %s", m_szMapName, header.szMapName); - fclose(bfp); + delete bfp; return false; } } @@ -256,11 +256,11 @@ bool CWaypointClass::LoadWaypoints() conoutf("Waypoint file isn't for cube bot"); //conoutf("Header FileType: %s", int(header.szFileType)); - fclose(bfp); + delete bfp; return false; } - fclose(bfp); + delete bfp; //RouteInit(); } @@ -307,7 +307,7 @@ void CWaypointClass::SaveWaypoints() BotManager.MakeBotFileName(mapname, "waypoints", NULL, filename); - FILE *bfp = openfile(filename, "wb"); + stream *bfp = openfile(filename, "wb"); if (!bfp) { @@ -317,7 +317,7 @@ void CWaypointClass::SaveWaypoints() } // write the waypoint header to the file... - fwrite(&header, sizeof(header), 1, bfp); + bfp->write(&header, sizeof(header)); // write the waypoint data to the file... loopi(MAX_MAP_GRIDS) @@ -327,10 +327,10 @@ void CWaypointClass::SaveWaypoints() TLinkedList::node_s *p = m_Waypoints[i][j].GetFirst(); while(p) { - fwrite(&p->Entry->v_origin, sizeof(p->Entry->v_origin), 1, bfp); // Write origin - fwrite(&p->Entry->iFlags, sizeof(p->Entry->iFlags), 1, bfp); // Write waypoint flags - fwrite(&p->Entry->sTriggerNr, sizeof(p->Entry->sTriggerNr), 1, bfp); // Write trigger nr - fwrite(&p->Entry->sYaw, sizeof(p->Entry->sYaw), 1, bfp); // Write target yaw + bfp->write(&p->Entry->v_origin, sizeof(p->Entry->v_origin)); // Write origin + bfp->write(&p->Entry->iFlags, sizeof(p->Entry->iFlags)); // Write waypoint flags + bfp->write(&p->Entry->sTriggerNr, sizeof(p->Entry->sTriggerNr)); // Write trigger nr + bfp->write(&p->Entry->sYaw, sizeof(p->Entry->sYaw)); // Write target yaw p = p->next; } @@ -350,13 +350,13 @@ void CWaypointClass::SaveWaypoints() pPath = p->Entry->ConnectedWPs.GetFirst(); num = p->Entry->ConnectedWPs.NodeCount(); - fwrite(&num, sizeof(num), 1, bfp); // write the count - fwrite(&p->Entry->v_origin, sizeof(p->Entry->v_origin), 1, bfp); // write the origin of this path + bfp->write(&num, sizeof(num)); // write the count + bfp->write(&p->Entry->v_origin, sizeof(p->Entry->v_origin)); // write the origin of this path // now write out each path... while (pPath != NULL) { - fwrite(&pPath->Entry->v_origin, sizeof(pPath->Entry->v_origin), 1, bfp); + bfp->write(&pPath->Entry->v_origin, sizeof(pPath->Entry->v_origin)); pPath = pPath->next; // go to next node in linked list } p = p->next; @@ -364,7 +364,7 @@ void CWaypointClass::SaveWaypoints() } } - fclose(bfp); + delete bfp; } bool CWaypointClass::LoadWPExpFile() diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 71eb16b6..0b188d10 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -1003,15 +1003,15 @@ void receivefile(uchar *data, int len) break; } path(fname); - FILE *demo = openfile(fname, "wb"); + stream *demo = openrawfile(fname, "wb"); if(!demo) { conoutf(_("failed writing to \"%s\""), fname); return; } conoutf(_("received demo \"%s\""), fname); - fwrite(&p.buf[p.len], 1, demosize, demo); - fclose(demo); + demo->write(&p.buf[p.len], demosize); + delete demo; break; } diff --git a/source/src/command.cpp b/source/src/command.cpp index ec91d21b..9d03cff8 100644 --- a/source/src/command.cpp +++ b/source/src/command.cpp @@ -866,43 +866,43 @@ int rndn(int a) { return a>0 ? rnd(a) : 0; } COMMANDN(rnd, rndn, ARG_1EXP); void writecfg() { - FILE *f = openfile(path("config/saved.cfg", true), "w"); + stream *f = openfile(path("config/saved.cfg", true), "w"); if(!f) return; - fprintf(f, "// automatically written on exit, DO NOT MODIFY\n// delete this file to have defaults.cfg overwrite these settings\n// modify settings in game, or put settings in autoexec.cfg to override anything\n\n"); - fprintf(f, "name %s\n", player1->name); + f->printf("// automatically written on exit, DO NOT MODIFY\n// delete this file to have defaults.cfg overwrite these settings\n// modify settings in game, or put settings in autoexec.cfg to override anything\n\n"); + f->printf("name %s\n", player1->name); extern const char *crosshairnames[CROSSHAIR_NUM]; extern Texture *crosshairs[CROSSHAIR_NUM]; loopi(CROSSHAIR_NUM) if(crosshairs[i] && crosshairs[i]!=notexture) { const char *fname = crosshairs[i]->name+strlen("packages/misc/crosshairs/"); - if(i==CROSSHAIR_DEFAULT) fprintf(f, "loadcrosshair %s\n", fname); - else fprintf(f, "loadcrosshair %s %s\n", fname, crosshairnames[i]); + if(i==CROSSHAIR_DEFAULT) f->printf("loadcrosshair %s\n", fname); + else f->printf("loadcrosshair %s %s\n", fname, crosshairnames[i]); } extern int lowfps, highfps; - fprintf(f, "fpsrange %d %d\n", lowfps, highfps); - fprintf(f, "\n"); + f->printf("fpsrange %d %d\n", lowfps, highfps); + f->printf("\n"); audiomgr.writesoundconfig(f); - fprintf(f, "\n"); + f->printf("\n"); enumerate(*idents, ident, id, if(!id.persist) continue; switch(id.type) { - case ID_VAR: fprintf(f, "%s %d\n", id.name, *id.storage.i); break; - case ID_FVAR: fprintf(f, "%s %s\n", id.name, floatstr(*id.storage.f)); break; - case ID_SVAR: fprintf(f, "%s [%s]\n", id.name, *id.storage.s); break; + case ID_VAR: f->printf("%s %d\n", id.name, *id.storage.i); break; + case ID_FVAR: f->printf("%s %s\n", id.name, floatstr(*id.storage.f)); break; + case ID_SVAR: f->printf("%s [%s]\n", id.name, *id.storage.s); break; } ); - fprintf(f, "\n"); + f->printf("\n"); writebinds(f); - fprintf(f, "\n"); + f->printf("\n"); enumerate(*idents, ident, id, if(id.type==ID_ALIAS && id.persist && id.action[0]) { - fprintf(f, "alias \"%s\" [%s]\n", id.name, id.action); + f->printf("alias \"%s\" [%s]\n", id.name, id.action); } ); - fprintf(f, "\n"); - fclose(f); + f->printf("\n"); + delete f; } COMMAND(writecfg, ARG_NONE); diff --git a/source/src/console.cpp b/source/src/console.cpp index e76247b7..98acc778 100644 --- a/source/src/console.cpp +++ b/source/src/console.cpp @@ -452,11 +452,11 @@ char *getcurcommand() return saycommandon ? cmdline.buf : NULL; } -void writebinds(FILE *f) +void writebinds(stream *f) { loopv(keyms) { - if(*keyms[i].action) fprintf(f, "bind \"%s\" [%s]\n", keyms[i].name, keyms[i].action); + if(*keyms[i].action) f->printf("bind \"%s\" [%s]\n", keyms[i].name, keyms[i].action); } } diff --git a/source/src/docs.cpp b/source/src/docs.cpp index fd63c391..27425c66 100644 --- a/source/src/docs.cpp +++ b/source/src/docs.cpp @@ -283,7 +283,7 @@ char *xmlstringenc(char *d, const char *s, size_t len) void docwriteref(int allidents, const char *ref, const char *schemalocation, const char *transformation) { - FILE *f = openfile(path(allidents < 0 ? "docs/autogenerated_base_reference.xml" : "docs/autogenerated_todo_reference.xml", true), "w"); + stream *f = openfile(path(allidents < 0 ? "docs/autogenerated_base_reference.xml" : "docs/autogenerated_todo_reference.xml", true), "w"); if(!f) return; char desc[] = "TODO: Description"; @@ -294,14 +294,14 @@ void docwriteref(int allidents, const char *ref, const char *schemalocation, con if(allidents < 0) { - fprintf(f, "\n"); - fprintf(f, "\n", transformation && strlen(transformation) ? transformation : "transformations/cuberef2xhtml.xslt"); - fprintf(f, "\n", ref && strlen(ref) ? ref : "Unnamed Reference", schemalocation && strlen(schemalocation) ? schemalocation : "http://cubers.net/Schemas/CubeRef schemas/cuberef.xsd"); - fprintf(f, "\t%s\n", desc); - fprintf(f, "\t\n"); - fprintf(f, "\t\t
\n"); - fprintf(f, "\t\t\t%s\n", desc); - fprintf(f, "\t\t\t\n"); + f->printf("\n"); + f->printf("\n", transformation && strlen(transformation) ? transformation : "transformations/cuberef2xhtml.xslt"); + f->printf("\n", ref && strlen(ref) ? ref : "Unnamed Reference", schemalocation && strlen(schemalocation) ? schemalocation : "http://cubers.net/Schemas/CubeRef schemas/cuberef.xsd"); + f->printf("\t%s\n", desc); + f->printf("\t\n"); + f->printf("\t\t
\n"); + f->printf("\t\t\t%s\n", desc); + f->printf("\t\t\t\n"); } static const int bases[] = { ARG_1INT, ARG_1STR, ARG_1EXP, ARG_1EXPF, ARG_1EST }; @@ -310,12 +310,12 @@ void docwriteref(int allidents, const char *ref, const char *schemalocation, con { ident *id = idents->access(inames[i]); if(!id || id->type != ID_COMMAND) continue; - fprintf(f, "\t\t\t\t\n", xmlstringenc(name, id->name, MAXSTRLEN)); - fprintf(f, "\t\t\t\t\t%s\n", desc); + f->printf("\t\t\t\t\n", xmlstringenc(name, id->name, MAXSTRLEN)); + f->printf("\t\t\t\t\t%s\n", desc); if(id->narg != ARG_NONE && id->narg != ARG_DOWN && id->narg != ARG_IVAL && id->narg != ARG_FVAL && id->narg != ARG_SVAL) { - fprintf(f, "\t\t\t\t\t\n"); - if(id->narg == ARG_CONC || id->narg == ARG_CONCW || id->narg == ARG_VARI) fprintf(f, "\t\t\t\t\t\t\n"); + f->printf("\t\t\t\t\t\n"); + if(id->narg == ARG_CONC || id->narg == ARG_CONCW || id->narg == ARG_VARI) f->printf("\t\t\t\t\t\t\n"); else { int base = -1; @@ -323,42 +323,42 @@ void docwriteref(int allidents, const char *ref, const char *schemalocation, con { if(id->narg < bases[j]) { if(j) base = bases[j-1]; break; } } - if(base >= 0) loopj(id->narg-base+1) fprintf(f, "\t\t\t\t\t\t\n", (char)(*"A")+j); + if(base >= 0) loopj(id->narg-base+1) f->printf("\t\t\t\t\t\t\n", (char)(*"A")+j); } - fprintf(f, "\t\t\t\t\t\n"); + f->printf("\t\t\t\t\t\n"); } - fprintf(f, "\t\t\t\t\n"); + f->printf("\t\t\t\t\n"); } loopv(inames) { ident *id = idents->access(inames[i]); if(!id || (id->type != ID_VAR && id->type != ID_FVAR)) continue; - fprintf(f, "\t\t\t\t\n", xmlstringenc(name, id->name, MAXSTRLEN)); - fprintf(f, "\t\t\t\t\tTODO\n"); + f->printf("\t\t\t\t\n", xmlstringenc(name, id->name, MAXSTRLEN)); + f->printf("\t\t\t\t\tTODO\n"); switch(id->type) { case ID_VAR: - fprintf(f, "\t\t\t\t\t\n", id->minval>id->maxval ? "" : "token=\"N\"", id->minval, id->maxval, *id->storage.i, id->minval>id->maxval ? "readOnly=\"true\"" : ""); + f->printf("\t\t\t\t\t\n", id->minval>id->maxval ? "" : "token=\"N\"", id->minval, id->maxval, *id->storage.i, id->minval>id->maxval ? "readOnly=\"true\"" : ""); break; case ID_FVAR: - fprintf(f, "\t\t\t\t\t\n", id->minvalf>id->maxvalf ? "" : "token=\"N\"", + f->printf("\t\t\t\t\t\n", id->minvalf>id->maxvalf ? "" : "token=\"N\"", floatstr(id->minvalf), floatstr(id->maxvalf), floatstr(*id->storage.f), id->minvalf>id->maxvalf ? "readOnly=\"true\"" : ""); break; } - fprintf(f, "\t\t\t\t\n"); + f->printf("\t\t\t\t\n"); } loopv(inames) { ident *id = idents->access(inames[i]); if(!id || id->type != ID_ALIAS) continue; - fprintf(f, "\t\t\t\t\n", xmlstringenc(name, id->name, MAXSTRLEN)); - fprintf(f, "\t\t\t\t\t%s\n", desc); - fprintf(f, "\t\t\t\t\n"); + f->printf("\t\t\t\t\n", xmlstringenc(name, id->name, MAXSTRLEN)); + f->printf("\t\t\t\t\t%s\n", desc); + f->printf("\t\t\t\t\n"); } - if(allidents < 0) fprintf(f, "\t\t\t\n\t\t
\n\t
\n
\n"); - fclose(f); + if(allidents < 0) f->printf("\t\t\t
\n\t\t
\n\t
\n
\n"); + delete f; } void docwritebaseref(char *ref, char *schemalocation, char *transformation) diff --git a/source/src/entity.h b/source/src/entity.h index 5d988d9a..fef25521 100644 --- a/source/src/entity.h +++ b/source/src/entity.h @@ -28,7 +28,7 @@ struct persistent_entity // map entity persistent_entity() {} }; -struct entity : public persistent_entity +struct entity : persistent_entity { bool spawned; //the only dynamic state of a map entity entity(short x, short y, short z, uchar type, short attr1, uchar attr2, uchar attr3, uchar attr4) : persistent_entity(x, y, z, type, attr1, attr2, attr3, attr4), spawned(false) {} diff --git a/source/src/main.cpp b/source/src/main.cpp index 77b6e8ae..8796896a 100644 --- a/source/src/main.cpp +++ b/source/src/main.cpp @@ -113,22 +113,22 @@ VARF(fullscreen, 0, 1, 1, setfullscreen(fullscreen!=0)); void writeinitcfg() { if(!restoredinits) return; - FILE *f = openfile(path("config/init.cfg", true), "w"); + stream *f = openfile(path("config/init.cfg", true), "w"); if(!f) return; - fprintf(f, "// automatically written on exit, DO NOT MODIFY\n// modify settings in game\n"); + f->printf("// automatically written on exit, DO NOT MODIFY\n// modify settings in game\n"); extern int fullscreen; - fprintf(f, "fullscreen %d\n", fullscreen); - fprintf(f, "scr_w %d\n", scr_w); - fprintf(f, "scr_h %d\n", scr_h); - fprintf(f, "colorbits %d\n", colorbits); - fprintf(f, "depthbits %d\n", depthbits); - fprintf(f, "stencilbits %d\n", stencilbits); - fprintf(f, "fsaa %d\n", fsaa); - fprintf(f, "vsync %d\n", vsync); + f->printf("fullscreen %d\n", fullscreen); + f->printf("scr_w %d\n", scr_w); + f->printf("scr_h %d\n", scr_h); + f->printf("colorbits %d\n", colorbits); + f->printf("depthbits %d\n", depthbits); + f->printf("stencilbits %d\n", stencilbits); + f->printf("fsaa %d\n", fsaa); + f->printf("vsync %d\n", vsync); extern int audio, soundchannels; - fprintf(f, "audio %d\n", audio > 0 ? 1 : 0); - fprintf(f, "soundchannels %d\n", soundchannels); - fclose(f); + f->printf("audio %d\n", audio > 0 ? 1 : 0); + f->printf("soundchannels %d\n", soundchannels); + delete f; } #if 0 @@ -206,16 +206,15 @@ void jpeg_screenshot(const char *imagepath) void bmp_screenshot(const char *imagepath) { - SDL_Surface *image = SDL_CreateRGBSurface(SDL_SWSURFACE, screen->w, screen->h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0); + SDL_Surface *image = creatergbsurface(screen->w, screen->h); if(!image) return; - uchar *tmp = new uchar[screen->w*screen->h*3]; + uchar *tmp = new uchar[screen->w*screen->h*3*2]; glPixelStorei(GL_PACK_ALIGNMENT, 1); glReadPixels(0, 0, screen->w, screen->h, GL_RGB, GL_UNSIGNED_BYTE, tmp); uchar *dst = (uchar *)image->pixels; loopi(screen->h) { memcpy(dst, &tmp[3*screen->w*(screen->h-i-1)], 3*screen->w); - endianswap(dst, 3, screen->w); dst += image->pitch; } delete[] tmp; @@ -700,21 +699,18 @@ int main(int argc, char **argv) persistidents = true; static char resdata[] = { 112, 97, 99, 107, 97, 103, 101, 115, 47, 116, 101, 120, 116, 117, 114, 101, 115, 47, 107, 117, 114, 116, 47, 107, 108, 105, 116, 101, 50, 46, 106, 112, 103, 0 }; - gzFile f = gzopen(resdata, "rb9"); + stream *f = opengzfile(resdata, "rb"); if(f) { - int n; - gzread(f, &n, sizeof(int)); - endianswap(&n, sizeof(int), 1); + int n = f->getlil(); loopi(n) { string s; - gzread(f, s, sizeof(string)); - enet_uint32 c; - gzread(f, &c, sizeof(enet_uint32)); + f->read(s, sizeof(string)); + enet_uint32 c = f->getlil(); setresdata(s, c); } - gzclose(f); + delete f; } initing = INIT_LOAD; diff --git a/source/src/md2.h b/source/src/md2.h index 035ed351..1c70122f 100644 --- a/source/src/md2.h +++ b/source/src/md2.h @@ -81,16 +81,16 @@ struct md2 : vertmodel { if(filename) return true; - FILE *file = openfile(path, "rb"); + stream *file = openfile(path, "rb"); if(!file) return false; md2_header header; - fread(&header, sizeof(md2_header), 1, file); - endianswap(&header, sizeof(int), sizeof(md2_header)/sizeof(int)); + file->read(&header, sizeof(md2_header)); + lilswap((int *)&header, sizeof(md2_header)/sizeof(int)); if(header.magic!=844121161 || header.version!=8) { - fclose(file); + delete file; return false; } @@ -101,9 +101,9 @@ struct md2 : vertmodel m.owner = this; int *glcommands = new int[header.numglcommands]; - fseek(file, header.offsetglcommands, SEEK_SET); - int numglcommands = (int)fread(glcommands, sizeof(int), header.numglcommands, file); - endianswap(glcommands, sizeof(int), numglcommands); + file->seek(header.offsetglcommands, SEEK_SET); + int numglcommands = (int)file->read(glcommands, sizeof(int)*header.numglcommands)/sizeof(int); + lilswap(glcommands, numglcommands); if(numglcommands < header.numglcommands) memset(&glcommands[numglcommands], 0, (header.numglcommands-numglcommands)*sizeof(int)); vector tcgen; @@ -127,11 +127,11 @@ struct md2 : vertmodel loopi(header.numframes) { md2_frame frame; - fseek(file, frame_offset, SEEK_SET); - fread(&frame, sizeof(md2_frame), 1, file); - endianswap(&frame, sizeof(float), 6); + file->seek(frame_offset, SEEK_SET); + file->read(&frame, sizeof(md2_frame)); + lilswap((float *)&frame, 6); - fread(tmpverts, sizeof(md2_vertex), header.numvertices, file); + file->read(tmpverts, sizeof(md2_vertex)*header.numvertices); loopj(m.numverts) { const md2_vertex &v = tmpverts[vgen[j]]; @@ -143,7 +143,7 @@ struct md2 : vertmodel } delete[] tmpverts; - fclose(file); + delete file; filename = newstring(path); return true; diff --git a/source/src/md3.h b/source/src/md3.h index 8e648c27..b3f54a73 100644 --- a/source/src/md3.h +++ b/source/src/md3.h @@ -53,15 +53,15 @@ struct md3 : vertmodel { if(filename) return true; - FILE *f = openfile(path, "rb"); + stream *f = openfile(path, "rb"); if(!f) return false; md3header header; - fread(&header, sizeof(md3header), 1, f); - endianswap(&header.version, sizeof(int), 1); - endianswap(&header.flags, sizeof(int), 9); + f->read(&header, sizeof(md3header)); + lilswap(&header.version, 1); + lilswap(&header.flags, 9); if(strncmp(header.id, "IDP3", 4) != 0 || header.version != 15) // header check { - fclose(f); + delete f; conoutf("md3: corrupted header"); return false; } @@ -71,13 +71,13 @@ struct md3 : vertmodel if(numtags) { tags = new tag[numframes*numtags]; - fseek(f, header.ofs_tags, SEEK_SET); + f->seek(header.ofs_tags, SEEK_SET); md3tag tag; loopi(header.numframes*header.numtags) { - fread(&tag, sizeof(md3tag), 1, f); - endianswap(&tag.pos, sizeof(float), 12); + f->read(&tag, sizeof(md3tag)); + lilswap((float *)&tag.pos, 12); if(tag.name[0] && iseek(mesh_offset, SEEK_SET); + f->read(&mheader, sizeof(md3meshheader)); + lilswap(&mheader.flags, 10); if(mheader.numtriangles <= 0) { @@ -109,28 +109,28 @@ struct md3 : vertmodel m.numtris = mheader.numtriangles; m.tris = new tri[m.numtris]; - fseek(f, mesh_offset + mheader.ofs_triangles, SEEK_SET); + f->seek(mesh_offset + mheader.ofs_triangles, SEEK_SET); loopj(mheader.numtriangles) { md3triangle tri; - fread(&tri, sizeof(md3triangle), 1, f); // read the triangles - endianswap(&tri, sizeof(int), 3); + f->read(&tri, sizeof(md3triangle)); // read the triangles + lilswap((int *)&tri, 3); loopk(3) m.tris[j].vert[k] = (ushort)tri.vertexindices[k]; } m.numverts = mheader.numvertices; m.tcverts = new tcvert[m.numverts]; - fseek(f, mesh_offset + mheader.ofs_uv , SEEK_SET); - fread(m.tcverts, 2*sizeof(float), m.numverts, f); // read the UV data - endianswap(m.tcverts, sizeof(float), 2*m.numverts); + f->seek(mesh_offset + mheader.ofs_uv , SEEK_SET); + f->read(m.tcverts, 2*sizeof(float)*m.numverts); // read the UV data + lilswap((float *)m.tcverts, sizeof(float)*2*m.numverts); m.verts = new vec[numframes*m.numverts + 1]; - fseek(f, mesh_offset + mheader.ofs_vertices, SEEK_SET); + f->seek(mesh_offset + mheader.ofs_vertices, SEEK_SET); loopj(numframes*mheader.numvertices) { md3vertex v; - fread(&v, sizeof(md3vertex), 1, f); // read the vertices - endianswap(&v, sizeof(short), 4); + f->read(&v, sizeof(md3vertex)); // read the vertices + lilswap((short *)&v, 4); m.verts[j].x = v.vertex[1]/64.0f; m.verts[j].y = v.vertex[0]/64.0f; @@ -139,7 +139,7 @@ struct md3 : vertmodel mesh_offset += mheader.meshsize; } - fclose(f); + delete f; filename = newstring(path); return true; diff --git a/source/src/menus.cpp b/source/src/menus.cpp index 1f82f170..5f70e24d 100644 --- a/source/src/menus.cpp +++ b/source/src/menus.cpp @@ -966,13 +966,13 @@ char *getfiledesc(const char *dir, const char *name, const char *ext) string text; if(!strcmp(ext, "dmo")) { - gzFile f = opengzfile(fn, "rb9"); + stream *f = opengzfile(fn, "rb"); if(!f) return NULL; demoheader hdr; - if(gzread(f, &hdr, sizeof(demoheader))!=sizeof(demoheader) || memcmp(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic))) { gzclose(f); return NULL; } - gzclose(f); - endianswap(&hdr.version, sizeof(int), 1); - endianswap(&hdr.protocol, sizeof(int), 1); + 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) { @@ -985,12 +985,12 @@ char *getfiledesc(const char *dir, const char *name, const char *ext) } else if(!strcmp(ext, "cgz")) { - gzFile f = opengzfile(fn, "rb9"); + stream *f = opengzfile(fn, "rb"); if(!f) return NULL; header hdr; - if(gzread(f, &hdr, sizeof(header))!=sizeof(header) || (strncmp(hdr.head, "CUBE", 4) && strncmp(hdr.head, "ACMP",4))) { gzclose(f); return NULL; } - gzclose(f); - endianswap(&hdr.version, sizeof(int), 4); + 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); formatstring(text)("%s%s", (hdr.version>MAPVERSION) ? "(incompatible file) " : "", hdr.maptitle); text[DHDR_DESCCHARS - 1] = '\0'; return newstring(text); diff --git a/source/src/oggstream.cpp b/source/src/oggstream.cpp index 71683674..c37b4263 100644 --- a/source/src/oggstream.cpp +++ b/source/src/oggstream.cpp @@ -6,18 +6,33 @@ // ogg compat -static int oggseek(FILE *f, ogg_int64_t off, int whence) +static size_t oggcallbackread(void *ptr, size_t size, size_t nmemb, void *datasource) { - return f ? fseek(f, (long)off, whence) : -1; + stream *s = (stream *)datasource; + return s ? s->read(ptr, size*nmemb)/size : 0; } -ov_callbacks oggcallbacks = +static int oggcallbackseek(void *datasource, ogg_int64_t offset, int whence) { - (size_t (*)(void *, size_t, size_t, void *)) fread, - (int (*)(void *, ogg_int64_t, int)) oggseek, - (int (*)(void *)) fclose, - (long (*)(void *)) ftell -}; + stream *s = (stream *)datasource; + return s && s->seek(long(offset), whence) ? 0 : -1; +} + +static int oggcallbackclose(void *datasource) +{ + stream *s = (stream *)datasource; + if(!s) return -1; + delete s; + return 0; +} + +static long oggcallbacktell(void *datasource) +{ + stream *s = (stream *)datasource; + return s ? s->tell() : -1; +} + +ov_callbacks oggcallbacks = { oggcallbackread, oggcallbackseek, oggcallbackclose, oggcallbacktell }; // ogg audio streaming @@ -101,13 +116,13 @@ bool oggstream::open(const char *f) loopi(sizeof(exts)/sizeof(exts[0])) { formatstring(filepath)("packages/audio/songs/%s%s", f, exts[i]); - FILE *file = fopen(findfile(path(filepath), "rb"), "rb"); + ::stream *file = openfile(path(filepath), "rb"); if(!file) continue; isopen = !ov_open_callbacks(file, &oggfile, NULL, 0, oggcallbacks); if(!isopen) { - fclose(file); + delete file; continue; } diff --git a/source/src/openal.cpp b/source/src/openal.cpp index 0ae99fb4..a80368a2 100644 --- a/source/src/openal.cpp +++ b/source/src/openal.cpp @@ -267,14 +267,12 @@ bool sbuffer::load() loopi(sizeof(exts)/sizeof(exts[0])) { formatstring(filepath)("packages/audio/sounds/%s%s", name, exts[i]); - const char *file = findfile(path(filepath), "rb"); - size_t len = strlen(filepath); + stream *f = openfile(path(filepath), "rb"); + if(!f) continue; + size_t len = strlen(filepath); if(len >= 4 && !strcasecmp(filepath + len - 4, ".ogg")) { - FILE *f = fopen(file, "rb"); - if(!f) continue; - OggVorbis_File oggfile; if(!ov_open_callbacks(f, &oggfile, NULL, 0, oggcallbacks)) { @@ -297,7 +295,7 @@ bool sbuffer::load() } else { - fclose(f); + delete f; continue; } } @@ -307,7 +305,7 @@ bool sbuffer::load() uint32_t wavlen; uint8_t *wavbuf; - if(!SDL_LoadWAV(file, &wavspec, &wavbuf, &wavlen)) + if(!SDL_LoadWAV_RW(f->rwops(), 1, &wavspec, &wavbuf, &wavlen)) { SDL_ClearError(); continue; @@ -326,12 +324,14 @@ bool sbuffer::load() break; default: SDL_FreeWAV(wavbuf); + delete f; unload(); return false; } alBufferData(id, format, wavbuf, wavlen, wavspec.freq); SDL_FreeWAV(wavbuf); + delete f; if(ALERR) break; } diff --git a/source/src/packetqueue.cpp b/source/src/packetqueue.cpp index 545682e6..01fe4fba 100644 --- a/source/src/packetqueue.cpp +++ b/source/src/packetqueue.cpp @@ -23,30 +23,30 @@ bool packetqueue::flushtolog(const char *logfile) { if(packets.empty()) return false; - FILE *f = NULL; + stream *f = NULL; if(logfile && logfile[0]) f = openfile(logfile, "w"); if(!f) return false; // header - fprintf(f, "AC PACKET LOG %11s\n\n", numtime()); + f->printf("AC PACKET LOG %11s\n\n", numtime()); // serialize each packet loopv(packets) { ENetPacket *p = packets[i]; - fputs("ENET PACKET\n", f); - fprintf(f, "flags == %d\n", p->flags); - fprintf(f, "referenceCount == %d\n", (int)p->referenceCount); - fprintf(f, "dataLength == %d\n", (int)p->dataLength); - fputs("data == \n", f); + f->printf("ENET PACKET\n"); + f->printf("flags == %d\n", p->flags); + f->printf("referenceCount == %d\n", (int)p->referenceCount); + f->printf("dataLength == %d\n", (int)p->dataLength); + f->printf("data == \n"); // print whole buffer char-wise loopj(p->dataLength) { - fprintf(f, "%16d\n", p->data[j]); + f->printf("%16d\n", p->data[j]); } } - fclose(f); + delete f; clear(); return true; } diff --git a/source/src/protos.h b/source/src/protos.h index b13f936b..c52c6411 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -76,7 +76,7 @@ extern int rendercommand(int x, int y, int w); extern void renderconsole(); extern char *getcurcommand(); extern char *addreleaseaction(const char *s); -extern void writebinds(FILE *f); +extern void writebinds(stream *f); extern void pasteconsole(char *dst); extern void conoutf(const char *s, ...); extern void clientlogf(const char *s, ...); @@ -292,6 +292,11 @@ extern Texture *notexture, *noworldtexture; extern void scaletexture(uchar *src, uint sw, uint sh, uint bpp, uchar *dst, uint dw, uint dh); extern void createtexture(int tnum, int w, int h, void *pixels, int clamp, bool mipmap, bool canreduce, GLenum format); +extern SDL_Surface *wrapsurface(void *data, int width, int height, int bpp); +extern SDL_Surface *creatergbsurface(int width, int height); +extern SDL_Surface *creatergbasurface(int width, int height); +extern SDL_Surface *forcergbsurface(SDL_Surface *os); +extern SDL_Surface *forcergbasurface(SDL_Surface *os); extern Texture *textureload(const char *name, int clamp = 0, bool mipmap = true, bool canreduce = false); extern Texture *lookuptexture(int tex, Texture *failtex = notexture); extern bool reloadtexture(Texture &t); @@ -620,7 +625,7 @@ extern void detachsounds(playerent *owner); extern void updateaudio(); extern void preloadmapsound(entity &e); extern void preloadmapsounds(); -extern void writesoundconfig(FILE *f); +extern void writesoundconfig(stream *f); */ // rendermodel diff --git a/source/src/server.cpp b/source/src/server.cpp index a3958962..b49d785c 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -382,8 +382,7 @@ void sendspawn(client *c) } // demo -FILE *demotmp = NULL; -gzFile demorecord = NULL, demoplayback = NULL; +stream *demotmp = NULL, *demorecord = NULL, *demoplayback = NULL; bool recordpackets = false; int nextplayback = 0; @@ -391,9 +390,9 @@ void writedemo(int chan, void *data, int len) { if(!demorecord) return; int stamp[3] = { gamemillis, chan, len }; - endianswap(stamp, sizeof(int), 3); - gzwrite(demorecord, stamp, sizeof(stamp)); - gzwrite(demorecord, data, len); + lilswap(stamp, 3); + demorecord->write(stamp, sizeof(stamp)); + demorecord->write(data, len); } void recordpacket(int chan, void *data, int len) @@ -410,26 +409,22 @@ void enddemorecord() { if(!demorecord) return; - gzclose(demorecord); + delete demorecord; recordpackets = false; demorecord = NULL; -#ifdef WIN32 - demotmp = fopen(path("demos/demorecord", true), "rb"); -#endif if(!demotmp) return; if(gamemillis < DEMO_MINTIME) { - fclose(demotmp); + delete demotmp; demotmp = NULL; logline(ACLOG_INFO, "Demo discarded."); return; } - fseek(demotmp, 0, SEEK_END); - int len = ftell(demotmp); - rewind(demotmp); + int len = demotmp->size(); + demotmp->seek(0, SEEK_SET); if(demofiles.length() >= scl.maxdemos) { delete[] demofiles[0].data; @@ -445,18 +440,18 @@ void enddemorecord() logline(ACLOG_INFO, "Demo \"%s\" recorded.", d.info); d.data = new uchar[len]; d.len = len; - fread(d.data, 1, len, demotmp); - fclose(demotmp); + demotmp->read(d.data, len); + delete demotmp; demotmp = NULL; if(scl.demopath[0]) { formatstring(msg)("%s%s.dmo", scl.demopath, d.file); path(msg); - FILE *demo = openfile(msg, "wb"); + stream *demo = openfile(msg, "wb"); if(demo) { - int wlen = (int) fwrite(d.data, 1, d.len, demo); - fclose(demo); + int wlen = (int) demo->write(d.data, d.len); + delete demo; logline(ACLOG_INFO, "demo written to file \"%s\" (%d bytes)", msg, wlen); } else @@ -470,22 +465,16 @@ void setupdemorecord() { if(numlocalclients() || !m_mp(gamemode) || gamemode == GMODE_COOPEDIT) return; -#ifdef WIN32 - gzFile f = gzopen(path("demos/demorecord", true), "wb9"); - if(!f) return; -#else - demotmp = tmpfile(); + demotmp = opentempfile("demos/demorecord", "w+b"); if(!demotmp) return; - setvbuf(demotmp, NULL, _IONBF, 0); - gzFile f = gzdopen(_dup(_fileno(demotmp)), "wb9"); + stream *f = opengzfile(NULL, "wb", demotmp); if(!f) { - fclose(demotmp); + delete demotmp; demotmp = NULL; return; } -#endif sendservmsg("recording demo"); logline(ACLOG_INFO, "Demo recording started."); @@ -497,8 +486,8 @@ void setupdemorecord() memcpy(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic)); hdr.version = DEMO_VERSION; hdr.protocol = SERVER_PROTOCOL_VERSION; - endianswap(&hdr.version, sizeof(int), 1); - endianswap(&hdr.protocol, sizeof(int), 1); + lilswap(&hdr.version, 1); + lilswap(&hdr.protocol, 1); memset(hdr.desc, 0, DHDR_DESCCHARS); defformatstring(desc)("%s, %s, %s %s", modestr(gamemode, false), behindpath(smapname), asctime(), servdesc_current); if(strlen(desc) > DHDR_DESCCHARS) @@ -514,7 +503,7 @@ void setupdemorecord() if(strlen(hdr.plist) + strlen(ci->name) < DHDR_PLISTCHARS - 2) { strcat(hdr.plist, bl); strcat(hdr.plist, ci->name); } bl = " "; } - gzwrite(demorecord, &hdr, sizeof(demoheader)); + demorecord->write(&hdr, sizeof(demoheader)); ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); ucharbuf p(packet->data, packet->dataLength); @@ -607,7 +596,7 @@ void senddemo(int cn, int num) void enddemoplayback() { if(!demoplayback) return; - gzclose(demoplayback); + delete demoplayback; demoplayback = NULL; loopv(clients) sendf(i, 1, "ri3", SV_DEMOPLAYBACK, 0, i); @@ -624,20 +613,20 @@ void setupdemoplayback() msg[0] = '\0'; defformatstring(file)("demos/%s.dmo", smapname); path(file); - demoplayback = opengzfile(file, "rb9"); + demoplayback = opengzfile(file, "rb"); if(!demoplayback) formatstring(msg)("could not read demo \"%s\"", file); - else if(gzread(demoplayback, &hdr, sizeof(demoheader))!=sizeof(demoheader) || memcmp(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic))) + else if(demoplayback->read(&hdr, sizeof(demoheader))!=sizeof(demoheader) || memcmp(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic))) formatstring(msg)("\"%s\" is not a demo file", file); else { - endianswap(&hdr.version, sizeof(int), 1); - endianswap(&hdr.protocol, sizeof(int), 1); + lilswap(&hdr.version, 1); + lilswap(&hdr.protocol, 1); if(hdr.version!=DEMO_VERSION) formatstring(msg)("demo \"%s\" requires an %s version of AssaultCube", file, hdr.versionread(&nextplayback, sizeof(nextplayback))!=sizeof(nextplayback)) { enddemoplayback(); return; } - endianswap(&nextplayback, sizeof(nextplayback), 1); + lilswap(&nextplayback, 1); } void readdemo() @@ -661,16 +650,16 @@ void readdemo() while(gamemillis>=nextplayback) { int chan, len; - if(gzread(demoplayback, &chan, sizeof(chan))!=sizeof(chan) || - gzread(demoplayback, &len, sizeof(len))!=sizeof(len)) + if(demoplayback->read(&chan, sizeof(chan))!=sizeof(chan) || + demoplayback->read(&len, sizeof(len))!=sizeof(len)) { enddemoplayback(); return; } - endianswap(&chan, sizeof(chan), 1); - endianswap(&len, sizeof(len), 1); + lilswap(&chan, 1); + lilswap(&len, 1); ENetPacket *packet = enet_packet_create(NULL, len, 0); - if(!packet || gzread(demoplayback, packet->data, len)!=len) + if(!packet || demoplayback->read(packet->data, len)!=len) { if(packet) enet_packet_destroy(packet); enddemoplayback(); @@ -678,12 +667,12 @@ void readdemo() } sendpacket(-1, chan, packet); if(!packet->referenceCount) enet_packet_destroy(packet); - if(gzread(demoplayback, &nextplayback, sizeof(nextplayback))!=sizeof(nextplayback)) + if(demoplayback->read(&nextplayback, sizeof(nextplayback))!=sizeof(nextplayback)) { enddemoplayback(); return; } - endianswap(&nextplayback, sizeof(nextplayback), 1); + lilswap(&nextplayback, 1); } } diff --git a/source/src/serverbrowser.cpp b/source/src/serverbrowser.cpp index 67f09520..1f13f6ae 100644 --- a/source/src/serverbrowser.cpp +++ b/source/src/serverbrowser.cpp @@ -1304,14 +1304,14 @@ COMMAND(updatefrommaster, ARG_1INT); void writeservercfg() { - FILE *f = openfile(path("config/servers.cfg", true), "w"); + stream *f = openfile(path("config/servers.cfg", true), "w"); if(!f) return; - fprintf(f, "// servers connected to are added here automatically\n"); + f->printf("// servers connected to are added here automatically\n"); loopvrev(servers) { - fprintf(f, "\naddserver %s %d", servers[i]->name, servers[i]->port); - if(servers[i]->msweight) fprintf(f, " %d", servers[i]->msweight); + f->printf("\naddserver %s %d", servers[i]->name, servers[i]->port); + if(servers[i]->msweight) f->printf(" %d", servers[i]->msweight); } - fprintf(f, "\n"); - fclose(f); + f->printf("\n"); + delete f; } diff --git a/source/src/sound.h b/source/src/sound.h index 1b64d663..2240f925 100644 --- a/source/src/sound.h +++ b/source/src/sound.h @@ -415,7 +415,7 @@ public: void updateaudio(); // misc - void writesoundconfig(FILE *f); + void writesoundconfig(stream *f); }; void alclearerr(); diff --git a/source/src/stream.cpp b/source/src/stream.cpp new file mode 100644 index 00000000..2c5d92e3 --- /dev/null +++ b/source/src/stream.cpp @@ -0,0 +1,755 @@ +#include "cube.h" + +///////////////////////// file system /////////////////////// + +#ifndef WIN32 +#include +#include +#include +#include +#endif + +string homedir = ""; +vector packagedirs; + +char *makerelpath(const char *dir, const char *file, const char *prefix, const char *cmd) +{ + static string tmp; + if(prefix) copystring(tmp, prefix); + else tmp[0] = '\0'; + if(file[0]=='<') + { + const char *end = strrchr(file, '>'); + if(end) + { + size_t len = strlen(tmp); + copystring(&tmp[len], file, min(sizeof(tmp)-len, size_t(end+2-file))); + file = end+1; + } + } + if(cmd) concatstring(tmp, cmd); + defformatstring(pname)("%s/%s", dir, file); + concatstring(tmp, pname); + return tmp; +} + + +char *path(char *s) +{ + for(char *curpart = s;;) + { + char *endpart = strchr(curpart, '&'); + if(endpart) *endpart = '\0'; + if(curpart[0]=='<') + { + char *file = strrchr(curpart, '>'); + if(!file) return s; + curpart = file+1; + } + for(char *t = curpart; (t = strpbrk(t, "/\\")); *t++ = PATHDIV); + for(char *prevdir = NULL, *curdir = s;;) + { + prevdir = curdir[0]==PATHDIV ? curdir+1 : curdir; + curdir = strchr(prevdir, PATHDIV); + if(!curdir) break; + if(prevdir+1==curdir && prevdir[0]=='.') + { + memmove(prevdir, curdir+1, strlen(curdir+1)+1); + curdir = prevdir; + } + else if(curdir[1]=='.' && curdir[2]=='.' && curdir[3]==PATHDIV) + { + if(prevdir+2==curdir && prevdir[0]=='.' && prevdir[1]=='.') continue; + memmove(prevdir, curdir+4, strlen(curdir+4)+1); + curdir = prevdir; + } + } + if(endpart) + { + *endpart = '&'; + curpart = endpart+1; + } + else break; + } + return s; +} + +char *path(const char *s, bool copy) +{ + static string tmp; + copystring(tmp, s); + path(tmp); + return tmp; +} + +const char *parentdir(const char *directory) +{ + const char *p = directory + strlen(directory); + while(p > directory && *p != '/' && *p != '\\') p--; + static string parent; + size_t len = p-directory+1; + copystring(parent, directory, len); + return parent; +} + +const char *behindpath(const char *s) +{ + const char *t = s; + for( ; (s = strpbrk(s, "/\\")); t = ++s); + return t; +} + +bool fileexists(const char *path, const char *mode) +{ + bool exists = true; + if(mode[0]=='w' || mode[0]=='a') path = parentdir(path); +#ifdef WIN32 + if(GetFileAttributes(path) == INVALID_FILE_ATTRIBUTES) exists = false; +#else + if(access(path, R_OK | (mode[0]=='w' || mode[0]=='a' ? W_OK : 0)) == -1) exists = false; +#endif + return exists; +} + +bool createdir(const char *path) +{ + size_t len = strlen(path); + if(path[len-1]==PATHDIV) + { + static string strip; + path = copystring(strip, path, len); + } +#ifdef WIN32 + return CreateDirectory(path, NULL)!=0; +#else + return mkdir(path, 0777)==0; +#endif +} + +size_t fixpackagedir(char *dir) +{ + path(dir); + size_t len = strlen(dir); + if(len > 0 && dir[len-1] != PATHDIV) + { + dir[len] = PATHDIV; + dir[len+1] = '\0'; + } + return len; +} + +#ifdef WIN32 +char *getregszvalue(HKEY root, const char *keystr, const char *query) +{ + HKEY key; + if(RegOpenKeyEx(HKEY_CURRENT_USER, keystr, 0, KEY_READ, &key)==ERROR_SUCCESS) + { + DWORD type = 0, len = 0; + if(RegQueryValueEx(key, query, 0, &type, 0, &len)==ERROR_SUCCESS && type==REG_SZ) + { + char *val = new char[len]; + long result = RegQueryValueEx(key, query, 0, &type, (uchar *)val, &len); + if(result==ERROR_SUCCESS) + { + RegCloseKey(key); + val[len-1] = '\0'; + return val; + } + delete[] val; + } + RegCloseKey(key); + } + return NULL; +} +#endif + +void sethomedir(const char *dir) +{ + string tmpdir; + copystring(tmpdir, dir); + +#ifdef WIN32 + const char substitute[] = "?MYDOCUMENTS?"; + if(!strncmp(dir, substitute, strlen(substitute))) + { + const char *regpath = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; + char *mydocuments = getregszvalue(HKEY_CURRENT_USER, regpath, "Personal"); + if(mydocuments) + { + formatstring(tmpdir)("%s%s", mydocuments, dir+strlen(substitute)); + delete[] mydocuments; + } + else + { + printf("failed to retrieve 'Personal' path from '%s'\n", regpath); + } + } +#endif + +#ifndef STANDALONE + clientlogf("Using home directory: %s", tmpdir); +#endif + + if(fixpackagedir(tmpdir) > 0) + { + copystring(homedir, tmpdir); + createdir(homedir); + } +} + +void addpackagedir(const char *dir) +{ +#ifndef STANDALONE + clientlogf("Adding package directory: %s", dir); +#endif + + string pdir; + copystring(pdir, dir); + if(fixpackagedir(pdir) > 0) packagedirs.add(newstring(pdir)); +} + +const char *findfile(const char *filename, const char *mode) +{ + static string s; + if(homedir[0]) + { + formatstring(s)("%s%s", homedir, filename); + if(fileexists(s, mode)) return s; + if(mode[0]=='w' || mode[0]=='a') + { + string dirs; + copystring(dirs, s); + char *dir = strchr(dirs[0]==PATHDIV ? dirs+1 : dirs, PATHDIV); + while(dir) + { + *dir = '\0'; + if(!fileexists(dirs, "r") && !createdir(dirs)) return s; + *dir = PATHDIV; + dir = strchr(dir+1, PATHDIV); + } + return s; + } + } + if(mode[0]=='w' || mode[0]=='a') return filename; + loopv(packagedirs) + { + formatstring(s)("%s%s", packagedirs[i], filename); + if(fileexists(s, mode)) return s; + } + return filename; +} + +bool listdir(const char *dir, const char *ext, vector &files) +{ + int extsize = ext ? (int)strlen(ext)+1 : 0; + #if defined(WIN32) + defformatstring(pathname)("%s\\*.%s", dir, ext ? ext : "*"); + WIN32_FIND_DATA FindFileData; + HANDLE Find = FindFirstFile(path(pathname), &FindFileData); + if(Find != INVALID_HANDLE_VALUE) + { + do { + files.add(newstring(FindFileData.cFileName, (int)strlen(FindFileData.cFileName) - extsize)); + } while(FindNextFile(Find, &FindFileData)); + return true; + } + #else + string pathname; + copystring(pathname, dir); + DIR *d = opendir(path(pathname)); + if(d) + { + struct dirent *de; + while((de = readdir(d)) != NULL) + { + if(!ext) files.add(newstring(de->d_name)); + else + { + int namelength = (int)strlen(de->d_name) - extsize; + if(namelength > 0 && de->d_name[namelength] == '.' && strncmp(de->d_name+namelength+1, ext, extsize-1)==0) + files.add(newstring(de->d_name, namelength)); + } + } + closedir(d); + return true; + } + #endif + else return false; +} + +int listfiles(const char *dir, const char *ext, vector &files) +{ + int dirs = 0; + if(listdir(dir, ext, files)) dirs++; + string s; + if(homedir[0]) + { + formatstring(s)("%s%s", homedir, dir); + if(listdir(s, ext, files)) dirs++; + } + loopv(packagedirs) + { + formatstring(s)("%s%s", packagedirs[i], dir); + if(listdir(s, ext, files)) dirs++; + } +#ifndef STANDALONE + dirs += listzipfiles(dir, ext, files); +#endif + return dirs; +} + +bool delfile(const char *path) +{ + return !remove(path); +} + +#ifndef STANDALONE +static int rwopsseek(SDL_RWops *rw, int offset, int whence) +{ + stream *f = (stream *)rw->hidden.unknown.data1; + if((!offset && whence==SEEK_CUR) || f->seek(offset, whence)) return f->tell(); + return -1; +} + +static int rwopsread(SDL_RWops *rw, void *buf, int size, int nmemb) +{ + stream *f = (stream *)rw->hidden.unknown.data1; + return f->read(buf, size*nmemb)/size; +} + +static int rwopswrite(SDL_RWops *rw, const void *buf, int size, int nmemb) +{ + stream *f = (stream *)rw->hidden.unknown.data1; + return f->write(buf, size*nmemb)/size; +} + +static int rwopsclose(SDL_RWops *rw) +{ + return 0; +} + +SDL_RWops *stream::rwops() +{ + SDL_RWops *rw = SDL_AllocRW(); + if(!rw) return NULL; + rw->hidden.unknown.data1 = this; + rw->seek = rwopsseek; + rw->read = rwopsread; + rw->write = rwopswrite; + rw->close = rwopsclose; + return rw; +} +#endif + +long stream::size() +{ + long pos = tell(), endpos; + if(pos < 0 || !seek(0, SEEK_END)) return -1; + endpos = tell(); + return pos == endpos || seek(pos, SEEK_SET) ? endpos : -1; +} + +bool stream::getline(char *str, int len) +{ + loopi(len-1) + { + if(read(&str[i], 1) != 1) { str[i] = '\0'; return i > 0; } + else if(str[i] == '\n') { str[i+1] = '\0'; return true; } + } + if(len > 0) str[len-1] = '\0'; + return true; +} + +struct filestream : stream +{ + FILE *file; + + filestream() : file(NULL) {} + ~filestream() { close(); } + + bool open(const char *name, const char *mode) + { + if(file) return false; + file = fopen(name, mode); + return file!=NULL; + } + + bool opentemp(const char *name, const char *mode) + { + if(file) return false; +#ifdef WIN32 + file = fopen(name, mode); +#else + file = tmpfile(); +#endif + return file!=NULL; + } + + void close() + { + if(file) { fclose(file); file = NULL; } + } + + bool end() { return feof(file)!=0; } + long tell() { return ftell(file); } + bool seek(long offset, int whence) { return fseek(file, offset, whence) >= 0; } + int read(void *buf, int len) { return fread(buf, 1, len, file); } + int write(const void *buf, int len) { return fwrite(buf, 1, len, file); } + int getchar() { return fgetc(file); } + bool putchar(int c) { return fputc(c, file)!=EOF; } + bool getline(char *str, int len) { return fgets(str, len, file)!=NULL; } + bool putstring(const char *str) { return fputs(str, file)!=EOF; } + + int printf(const char *fmt, ...) + { + va_list v; + va_start(v, fmt); + int result = vfprintf(file, fmt, v); + va_end(v); + return result; + } +}; + +#ifndef STANDALONE +VAR(dbggz, 0, 0, 1); +#endif + +struct gzstream : stream +{ + enum + { + MAGIC1 = 0x1F, + MAGIC2 = 0x8B, + BUFSIZE = 16384, + OS_UNIX = 0x03 + }; + + enum + { + F_ASCII = 0x01, + F_CRC = 0x02, + F_EXTRA = 0x04, + F_NAME = 0x08, + F_COMMENT = 0x10, + F_RESERVED = 0xE0 + }; + + stream *file; + z_stream zfile; + uchar *buf; + bool reading, writing, autoclose; + uint crc; + int headersize; + + gzstream() : file(NULL), buf(NULL), reading(false), writing(false), autoclose(false), crc(0), headersize(0) + { + zfile.zalloc = NULL; + zfile.zfree = NULL; + zfile.opaque = NULL; + zfile.next_in = zfile.next_out = NULL; + zfile.avail_in = zfile.avail_out = 0; + } + + ~gzstream() + { + close(); + } + + void writeheader() + { + uchar header[] = { MAGIC1, MAGIC2, Z_DEFLATED, 0, 0, 0, 0, 0, 0, OS_UNIX }; + file->write(header, sizeof(header)); + } + + void readbuf(int size = BUFSIZE) + { + if(!zfile.avail_in) zfile.next_in = (Bytef *)buf; + size = min(size, int(&buf[BUFSIZE] - &zfile.next_in[zfile.avail_in])); + int n = file->read(zfile.next_in + zfile.avail_in, size); + if(n > 0) zfile.avail_in += n; + } + + int readbyte(int size = BUFSIZE) + { + if(!zfile.avail_in) readbuf(size); + if(!zfile.avail_in) return 0; + zfile.avail_in--; + return *(uchar *)zfile.next_in++; + } + + void skipbytes(int n) + { + while(n > 0 && zfile.avail_in > 0) + { + int skipped = min(n, (int)zfile.avail_in); + zfile.avail_in -= skipped; + zfile.next_in += skipped; + n -= skipped; + } + if(n <= 0) return; + file->seek(n, SEEK_CUR); + } + + bool checkheader() + { + readbuf(10); + if(readbyte() != MAGIC1 || readbyte() != MAGIC2 || readbyte() != Z_DEFLATED) return false; + int flags = readbyte(); + if(flags & F_RESERVED) return false; + skipbytes(6); + if(flags & F_EXTRA) + { + int len = readbyte(512); + len |= readbyte(512)<<8; + skipbytes(len); + } + if(flags & F_NAME) while(readbyte(512)); + if(flags & F_COMMENT) while(readbyte(512)); + if(flags & F_CRC) skipbytes(2); + headersize = file->tell() - zfile.avail_in; + return zfile.avail_in > 0 || !file->end(); + } + + bool open(stream *f, const char *mode, bool needclose, int level) + { + if(file) return false; + for(; *mode; *mode++) + { + if(*mode=='r') { reading = true; break; } + else if(*mode=='w') { writing = true; break; } + } + if(reading) + { + if(inflateInit2(&zfile, -MAX_WBITS) != Z_OK) reading = false; + } + else if(writing && deflateInit2(&zfile, level, Z_DEFLATED, -MAX_WBITS, min(MAX_MEM_LEVEL, 8), Z_DEFAULT_STRATEGY) != Z_OK) writing = false; + if(!reading && !writing) return false; + + autoclose = needclose; + file = f; + crc = crc32(0, NULL, 0); + buf = new uchar[BUFSIZE]; + + if(reading) + { + if(!checkheader()) { stopreading(); return false; } + } + else if(writing) writeheader(); + return true; + } + + uint getcrc() { return crc; } + + void finishreading() + { + if(!reading) return; +#ifndef STANDALONE + if(dbggz) + { + uint checkcrc = 0, checksize = 0; + loopi(4) checkcrc |= uint(readbyte()) << (i*8); + loopi(4) checksize |= uint(readbyte()) << (i*8); + if(checkcrc != crc) + conoutf("gzip crc check failed: read %X, calculated %X", checkcrc, crc); + if(checksize != zfile.total_out) + conoutf("gzip size check failed: read %d, calculated %d", checksize, zfile.total_out); + } +#endif + } + + void stopreading() + { + if(!reading) return; + inflateEnd(&zfile); + reading = false; + } + + void finishwriting() + { + if(!writing) return; + for(;;) + { + int err = zfile.avail_out > 0 ? deflate(&zfile, Z_FINISH) : Z_OK; + if(err != Z_OK && err != Z_STREAM_END) break; + flush(); + if(err == Z_STREAM_END) break; + } + uchar trailer[8] = + { + crc&0xFF, (crc>>8)&0xFF, (crc>>16)&0xFF, (crc>>24)&0xFF, + zfile.total_in&0xFF, (zfile.total_in>>8)&0xFF, (zfile.total_in>>16)&0xFF, (zfile.total_in>>24)&0xFF + }; + file->write(trailer, sizeof(trailer)); + } + + void stopwriting() + { + if(!writing) return; + deflateEnd(&zfile); + writing = false; + } + + void close() + { + if(reading) finishreading(); + stopreading(); + if(writing) finishwriting(); + stopwriting(); + DELETEA(buf); + if(autoclose) DELETEP(file); + } + + bool end() { return !reading && !writing; } + long tell() { return reading ? zfile.total_out : (writing ? zfile.total_in : -1); } + + bool seek(long offset, int whence) + { + if(writing || !reading || whence == SEEK_END) return false; + + if(whence == SEEK_CUR) offset += zfile.total_out; + + if(offset >= (int)zfile.total_out) offset -= zfile.total_out; + else if(offset < 0 || !file->seek(headersize, SEEK_SET)) return false; + else + { + if(zfile.next_in && zfile.total_in <= uint(zfile.next_in - buf)) + { + zfile.avail_in += zfile.total_in; + zfile.next_in -= zfile.total_in; + } + else + { + zfile.avail_in = 0; + zfile.next_in = NULL; + } + inflateReset(&zfile); + crc = crc32(0, NULL, 0); + } + + uchar skip[512]; + while(offset > 0) + { + int skipped = min(offset, (long)sizeof(skip)); + if(read(skip, skipped) != skipped) { stopreading(); return false; } + offset -= skipped; + } + + return true; + } + + int read(void *buf, int len) + { + if(!reading || !buf || !len) return 0; + zfile.next_out = (Bytef *)buf; + zfile.avail_out = len; + while(zfile.avail_out > 0) + { + if(!zfile.avail_in) + { + readbuf(BUFSIZE); + if(!zfile.avail_in) { stopreading(); break; } + } + int err = inflate(&zfile, Z_NO_FLUSH); + if(err == Z_STREAM_END) { crc = crc32(crc, (Bytef *)buf, len - zfile.avail_out); finishreading(); stopreading(); return len - zfile.avail_out; } + else if(err != Z_OK) { stopreading(); break; } + } + crc = crc32(crc, (Bytef *)buf, len - zfile.avail_out); + return len - zfile.avail_out; + } + + bool flush() + { + if(zfile.next_out && zfile.avail_out < BUFSIZE) + { + if(file->write(buf, BUFSIZE - zfile.avail_out) != int(BUFSIZE - zfile.avail_out)) + return false; + } + zfile.next_out = buf; + zfile.avail_out = BUFSIZE; + return true; + } + + int write(const void *buf, int len) + { + if(!writing || !buf || !len) return 0; + zfile.next_in = (Bytef *)buf; + zfile.avail_in = len; + while(zfile.avail_in > 0) + { + if(!zfile.avail_out && !flush()) { stopwriting(); break; } + int err = deflate(&zfile, Z_NO_FLUSH); + if(err != Z_OK) { stopwriting(); break; } + } + crc = crc32(crc, (Bytef *)buf, len - zfile.avail_in); + return len - zfile.avail_in; + } +}; + + +stream *openrawfile(const char *filename, const char *mode) +{ + const char *found = findfile(filename, mode); +#ifndef STANDALONE + if(mode && (mode[0]=='w' || mode[0]=='a')) conoutf("writing to file: %s", found); +#endif + if(!found) return NULL; + filestream *file = new filestream; + if(!file->open(found, mode)) { delete file; return NULL; } + return file; +} + +stream *openfile(const char *filename, const char *mode) +{ +#ifndef STANDALONE + stream *s = openzipfile(filename, mode); + if(s) return s; +#endif + return openrawfile(filename, mode); +} + +int getfilesize(const char *filename) +{ + stream *f = openfile(filename, "rb"); + if(!f) return -1; + int len = f->size(); + delete f; + return len; +} + +stream *opentempfile(const char *name, const char *mode) +{ + const char *found = findfile(name, mode); + filestream *file = new filestream; + if(!file->opentemp(found ? found : name, mode)) { delete file; return NULL; } + return file; +} + +stream *opengzfile(const char *filename, const char *mode, stream *file, int level) +{ + stream *source = file ? file : openfile(filename, mode); + if(!source) return NULL; + gzstream *gz = new gzstream; + if(!gz->open(source, mode, !file, level)) { if(!file) delete source; return NULL; } + return gz; +} + +char *loadfile(const char *fn, int *size, const char *mode) +{ + stream *f = openfile(fn, mode ? mode : "rb"); + if(!f) return NULL; + int len = f->size(); + if(len<=0) { delete f; return NULL; } + char *buf = new char[len+1]; + if(!buf) { delete f; return NULL; } + buf[len] = 0; + int rlen = f->read(buf, len); + delete f; + if(len!=rlen && (!mode || strchr(mode, 'b'))) + { + delete[] buf; + return NULL; + } + if(size!=NULL) *size = len; + return buf; +} + diff --git a/source/src/texture.cpp b/source/src/texture.cpp index 8f595ac9..c55e9ce2 100644 --- a/source/src/texture.cpp +++ b/source/src/texture.cpp @@ -151,6 +151,92 @@ void createtexture(int tnum, int w, int h, void *pixels, int clamp, bool mipmap, uploadtexture(GL_TEXTURE_2D, format, tw, th, format, GL_UNSIGNED_BYTE, pixels, w, h, mipmap); } +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define RGBAMASKS 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff +#define RGBMASKS 0xff0000, 0x00ff00, 0x0000ff, 0 +#else +#define RGBAMASKS 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 +#define RGBMASKS 0x0000ff, 0x00ff00, 0xff0000, 0 +#endif + +SDL_Surface *wrapsurface(void *data, int width, int height, int bpp) +{ + switch(bpp) + { + case 3: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBMASKS); + case 4: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBAMASKS); + } + return NULL; +} + +SDL_Surface *creatergbsurface(int width, int height) +{ + return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, RGBMASKS); +} + +SDL_Surface *creatergbasurface(int width, int height) +{ + return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, RGBAMASKS); +} + +SDL_Surface *forcergbsurface(SDL_Surface *os) +{ + SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 24, RGBMASKS); + if(ns) SDL_BlitSurface(os, NULL, ns, NULL); + SDL_FreeSurface(os); + return ns; +} + +SDL_Surface *forcergbasurface(SDL_Surface *os) +{ + SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 32, RGBAMASKS); + if(ns) + { + SDL_SetAlpha(os, 0, 0); + SDL_BlitSurface(os, NULL, ns, NULL); + } + SDL_FreeSurface(os); + return ns; +} + +bool checkgrayscale(SDL_Surface *s) +{ + // gray scale images have 256 levels, no colorkey, and the palette is a ramp + if(s->format->palette) + { + if(s->format->palette->ncolors != 256 || s->format->colorkey) return false; + const SDL_Color *colors = s->format->palette->colors; + loopi(256) if(colors[i].r != i || colors[i].g != i || colors[i].b != i) return false; + } + return true; +} + +SDL_Surface *fixsurfaceformat(SDL_Surface *s) +{ + if(!s) return NULL; + if(!s->pixels || min(s->w, s->h) <= 0 || s->format->BytesPerPixel <= 0) + { + SDL_FreeSurface(s); + return NULL; + } + static const uint rgbmasks[] = { RGBMASKS }, rgbamasks[] = { RGBAMASKS }; + switch(s->format->BytesPerPixel) + { + case 1: + if(!checkgrayscale(s)) return s->format->colorkey ? forcergbasurface(s) : forcergbsurface(s); + break; + case 3: + if(s->format->Rmask != rgbmasks[0] || s->format->Gmask != rgbmasks[1] || s->format->Bmask != rgbmasks[2]) + return forcergbsurface(s); + break; + case 4: + if(s->format->Rmask != rgbamasks[0] || s->format->Gmask != rgbamasks[1] || s->format->Bmask != rgbamasks[2] || s->format->Amask != rgbamasks[3]) + return forcergbasurface(s); + break; + } + return s; +} + GLenum texformat(int bpp) { switch(bpp) @@ -188,8 +274,22 @@ GLuint loadsurface(const char *texname, int &xs, int &ys, int &bpp, int clamp = file++; } - SDL_Surface *s = IMG_Load(findfile(file, "rb")); + SDL_Surface *s = NULL; + stream *z = openzipfile(file, "rb"); + if(z) + { + SDL_RWops *rw = z->rwops(); + if(rw) + { + s = IMG_Load_RW(rw, 0); + SDL_FreeRW(rw); + } + delete z; + } + if(!s) s = IMG_Load(findfile(file, "rb")); if(!s) { conoutf("couldn't load texture %s", texname); return 0; } + s = fixsurfaceformat(s); + GLenum format = texformat(s->format->BitsPerPixel); if(!format) { diff --git a/source/src/tools.cpp b/source/src/tools.cpp index 4dbc9efb..997bb799 100644 --- a/source/src/tools.cpp +++ b/source/src/tools.cpp @@ -2,43 +2,6 @@ #include "cube.h" -///////////////////////// file system /////////////////////// - -#ifndef WIN32 -#include -#include -#include -#include -#endif - -string homedir = ""; -vector packagedirs; - -#ifdef WIN32 -char *getregszvalue(HKEY root, const char *keystr, const char *query) -{ - HKEY key; - if(RegOpenKeyEx(HKEY_CURRENT_USER, keystr, 0, KEY_READ, &key)==ERROR_SUCCESS) - { - DWORD type = 0, len = 0; - if(RegQueryValueEx(key, query, 0, &type, 0, &len)==ERROR_SUCCESS && type==REG_SZ) - { - char *val = new char[len]; - long result = RegQueryValueEx(key, query, 0, &type, (uchar *)val, &len); - if(result==ERROR_SUCCESS) - { - RegCloseKey(key); - val[len-1] = '\0'; - return val; - } - delete[] val; - } - RegCloseKey(key); - } - return NULL; -} -#endif - const char *timestring(bool local, const char *fmt) { static string asciitime; @@ -61,277 +24,6 @@ const char *numtime() return numt; } -char *path(char *s) -{ - for(char *t = s; (t = strpbrk(t, "/\\")); *t++ = PATHDIV); - for(char *prevdir = NULL, *curdir = s;;) - { - prevdir = curdir[0]==PATHDIV ? curdir+1 : curdir; - curdir = strchr(prevdir, PATHDIV); - if(!curdir) break; - if(prevdir+1==curdir && prevdir[0]=='.') - { - memmove(prevdir, curdir+1, strlen(curdir+1)+1); - curdir = prevdir; - } - else if(curdir[1]=='.' && curdir[2]=='.' && curdir[3]==PATHDIV) - { - if(prevdir+2==curdir && prevdir[0]=='.' && prevdir[1]=='.') continue; - memmove(prevdir, curdir+4, strlen(curdir+4)+1); - curdir = prevdir; - } - } - return s; -} - -char *path(const char *s, bool copy) -{ - static string tmp; - copystring(tmp, s); - path(tmp); - return tmp; -} - -const char *behindpath(const char *s) -{ - const char *t = s; - for( ; (s = strpbrk(s, "/\\")); t = ++s); - return t; -} - -const char *parentdir(const char *directory) -{ - const char *p = strrchr(directory, '/'); - if(!p) p = strrchr(directory, '\\'); - if(!p) p = directory; - static string parent; - size_t len = p-directory+1; - copystring(parent, directory, len); - return parent; -} - -bool fileexists(const char *path, const char *mode) -{ - bool exists = true; - if(mode[0]=='w' || mode[0]=='a') path = parentdir(path); -#ifdef WIN32 - if(GetFileAttributes(path) == INVALID_FILE_ATTRIBUTES) exists = false; -#else - if(access(path, R_OK | (mode[0]=='w' || mode[0]=='a' ? W_OK : 0)) == -1) exists = false; -#endif - return exists; -} - -bool createdir(const char *path) -{ - size_t len = strlen(path); - if(path[len-1]==PATHDIV) - { - static string strip; - path = copystring(strip, path, len); - } -#ifdef WIN32 - return CreateDirectory(path, NULL)!=0; -#else - return mkdir(path, 0777)==0; -#endif -} - -static void fixdir(char *dir) -{ - path(dir); - size_t len = strlen(dir); - if(dir[len-1]!=PATHDIV) - { - dir[len] = PATHDIV; - dir[len+1] = '\0'; - } -} - -void sethomedir(const char *dir) -{ - string tmpdir; - copystring(tmpdir, dir); - -#ifdef WIN32 - const char substitute[] = "?MYDOCUMENTS?"; - if(!strncmp(dir, substitute, strlen(substitute))) - { - const char *regpath = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; - char *mydocuments = getregszvalue(HKEY_CURRENT_USER, regpath, "Personal"); - if(mydocuments) - { - formatstring(tmpdir)("%s%s", mydocuments, dir+strlen(substitute)); - delete[] mydocuments; - } - else - { - printf("failed to retrieve 'Personal' path from '%s'\n", regpath); - } - } -#endif - -#ifndef STANDALONE - clientlogf("Using home directory: %s", tmpdir); -#endif - fixdir(copystring(homedir, tmpdir)); - createdir(homedir); -} - -void addpackagedir(const char *dir) -{ -#ifndef STANDALONE - clientlogf("Adding package directory: %s", dir); -#endif - fixdir(packagedirs.add(newstringbuf(dir))); -} - -const char *findfile(const char *filename, const char *mode) -{ - static string s; - if(homedir[0]) - { - formatstring(s)("%s%s", homedir, filename); - if(fileexists(s, mode)) return s; - if(mode[0]=='w' || mode[0]=='a') - { - string dirs; - copystring(dirs, s); - char *dir = strchr(dirs[0]==PATHDIV ? dirs+1 : dirs, PATHDIV); - while(dir) - { - *dir = '\0'; - if(!fileexists(dirs, "r") && !createdir(dirs)) return s; - *dir = PATHDIV; - dir = strchr(dir+1, PATHDIV); - } - return s; - } - } - if(mode[0]=='w' || mode[0]=='a') return filename; - loopv(packagedirs) - { - formatstring(s)("%s%s", packagedirs[i], filename); - if(fileexists(s, mode)) return s; - } - return filename; -} - -int getfilesize(const char *filename) -{ - const char *found = findfile(filename, "rb"); - if(!found) return -1; - FILE *fp = fopen(found, "rb"); - if(!fp) return -1; - fseek(fp, 0, SEEK_END); - int len = ftell(fp); - fclose(fp); - return len; -} - -FILE *openfile(const char *filename, const char *mode) -{ - const char *found = findfile(filename, mode); -#ifndef STANDALONE - if(mode && (mode[0]=='w' || mode[0]=='a')) conoutf("writing to file: %s", found); -#endif - if(!found) return NULL; - return fopen(found, mode); -} - -gzFile opengzfile(const char *filename, const char *mode) -{ - const char *found = findfile(filename, mode); -#ifndef STANDALONE - if(mode && (mode[0]=='w' || mode[0]=='a')) conoutf("writing to file: %s", found); -#endif - if(!found) return NULL; - return gzopen(found, mode); -} - -char *loadfile(const char *fn, int *size, const char *mode) -{ - FILE *f = openfile(fn, mode ? mode : "rb"); - if(!f) return NULL; - fseek(f, 0, SEEK_END); - int len = ftell(f); - if(len<=0) { fclose(f); return NULL; } - fseek(f, 0, SEEK_SET); - char *buf = new char[len+1]; - if(!buf) { fclose(f); return NULL; } - buf[len] = 0; - int rlen = (int)fread(buf, 1, len, f); - fclose(f); - if(len!=rlen && (!mode || strchr(mode, 'b'))) - { - delete[] buf; - return NULL; - } - if(size!=NULL) *size = rlen; - return buf; -} - -bool listdir(const char *dir, const char *ext, vector &files) -{ - int extsize = ext ? (int)strlen(ext)+1 : 0; - #if defined(WIN32) - defformatstring(pathname)("%s\\*.%s", dir, ext ? ext : "*"); - WIN32_FIND_DATA FindFileData; - HANDLE Find = FindFirstFile(path(pathname), &FindFileData); - if(Find != INVALID_HANDLE_VALUE) - { - do { - files.add(newstring(FindFileData.cFileName, (int)strlen(FindFileData.cFileName) - extsize)); - } while(FindNextFile(Find, &FindFileData)); - return true; - } - #else - string pathname; - copystring(pathname, dir); - DIR *d = opendir(path(pathname)); - if(d) - { - struct dirent *de; - while((de = readdir(d)) != NULL) - { - if(!ext) files.add(newstring(de->d_name)); - else - { - int namelength = (int)strlen(de->d_name) - extsize; - if(namelength > 0 && de->d_name[namelength] == '.' && strncmp(de->d_name+namelength+1, ext, extsize-1)==0) - files.add(newstring(de->d_name, namelength)); - } - } - closedir(d); - return true; - } - #endif - else return false; -} - -int listfiles(const char *dir, const char *ext, vector &files) -{ - int dirs = 0; - if(listdir(dir, ext, files)) dirs++; - string s; - if(homedir[0]) - { - formatstring(s)("%s%s", homedir, dir); - if(listdir(s, ext, files)) dirs++; - } - loopv(packagedirs) - { - formatstring(s)("%s%s", packagedirs[i], dir); - if(listdir(s, ext, files)) dirs++; - } - return dirs; -} - -bool delfile(const char *path) -{ - return !remove(path); -} - extern char *maplayout; extern int maplayout_factor; @@ -346,17 +38,17 @@ mapstats *loadmapstats(const char *filename, bool getlayout) loopi(3) s.spawns[i] = 0; loopi(2) s.flags[i] = 0; - gzFile f = opengzfile(filename, "rb9"); + stream *f = opengzfile(filename, "rb"); if(!f) return NULL; memset(&s.hdr, 0, sizeof(header)); - if(gzread(f, &s.hdr, sizeof(header)-sizeof(int)*16-sizeof(char)*128)!=sizeof(header)-sizeof(int)*16-sizeof(char)*128 || (strncmp(s.hdr.head, "CUBE", 4) && strncmp(s.hdr.head, "ACMP",4))) { gzclose(f); return NULL; } - endianswap(&s.hdr.version, sizeof(int), 4); - if(s.hdr.version>MAPVERSION || s.hdr.numents > MAXENTITIES || (s.hdr.version>=4 && gzread(f, &s.hdr.waterlevel, sizeof(int)*16)!=sizeof(int)*16)) { gzclose(f); return NULL; } - if(s.hdr.version>=7 && gzread(f, &s.hdr.mediareq, sizeof(char)*128)!=sizeof(char)*128) { gzclose(f); return NULL; } + if(f->read(&s.hdr, sizeof(header)-sizeof(int)*16-sizeof(char)*128)!=sizeof(header)-sizeof(int)*16-sizeof(char)*128 || (strncmp(s.hdr.head, "CUBE", 4) && strncmp(s.hdr.head, "ACMP",4))) { gzclose(f); return NULL; } + lilswap(&s.hdr.version, 4); + if(s.hdr.version>MAPVERSION || s.hdr.numents > MAXENTITIES || (s.hdr.version>=4 && f->read(&s.hdr.waterlevel, sizeof(int)*16)!=sizeof(int)*16)) { delete f; return NULL; } + if(s.hdr.version>=7 && f->read(&s.hdr.mediareq, sizeof(char)*128)!=sizeof(char)*128) { delete f; return NULL; } if(s.hdr.version>=4) { - endianswap(&s.hdr.waterlevel, sizeof(int), 1); - endianswap(&s.hdr.maprevision, sizeof(int), 1); + lilswap(&s.hdr.waterlevel, 1); + lilswap(&s.hdr.maprevision, 1); } else s.hdr.waterlevel = -100000; if(s.hdr.version<7) @@ -368,8 +60,8 @@ mapstats *loadmapstats(const char *filename, bool getlayout) entposs = new short[s.hdr.numents * 3]; loopi(s.hdr.numents) { - gzread(f, &e, sizeof(persistent_entity)); - endianswap(&e, sizeof(short), 4); + f->read(&e, sizeof(persistent_entity)); + lilswap((short *)&e, 4); TRANSFORMOLDENTITIES(s.hdr) if(e.type == PLAYERSTART && (e.attr2 == 0 || e.attr2 == 1 || e.attr2 == 100)) s.spawns[e.attr2 == 100 ? 2 : e.attr2]++; if(e.type == CTF_FLAG && (e.attr2 == 0 || e.attr2 == 1)) { s.flags[e.attr2]++; s.flagents[e.attr2] = i; } @@ -392,12 +84,12 @@ mapstats *loadmapstats(const char *filename, bool getlayout) loopk(layoutsize) { char *c = maplayout + k; - int type = gzgetc(f); + int type = f->getchar(); switch(type) { case 255: { - int n = gzgetc(f); + int n = f->getchar(); if(!t || n < 0) { fail = true; break; } memset(c, *t, n); k += n - 1; @@ -406,21 +98,21 @@ mapstats *loadmapstats(const char *filename, bool getlayout) case 254: // only in MAPVERSION<=2 if(!t) { fail = true; break; } *c = *t; - gzgetc(f); gzgetc(f); + f->getchar(); f->getchar(); break; default: if(type<0 || type>=MAXTYPE) { fail = true; break; } - floor = gzgetc(f); - ceil = gzgetc(f); + floor = f->getchar(); + ceil = f->getchar(); if(floor >= ceil && ceil > -128) floor = ceil - 1; // for pre 12_13 if(type == FHF) floor = -128; - gzgetc(f); gzgetc(f); - if(s.hdr.version>=2) gzgetc(f); - if(s.hdr.version>=5) gzgetc(f); + f->getchar(); f->getchar(); + if(s.hdr.version>=2) f->getchar(); + if(s.hdr.version>=5) f->getchar(); case SOLID: *c = type == SOLID ? 127 : floor; - gzgetc(f); gzgetc(f); - if(s.hdr.version<=2) { gzgetc(f); gzgetc(f); } + f->getchar(); f->getchar(); + if(s.hdr.version<=2) { f->getchar(); f->getchar(); } break; } if(fail) break; @@ -429,7 +121,7 @@ mapstats *loadmapstats(const char *filename, bool getlayout) if(fail) DELETEA(maplayout); } } - gzclose(f); + delete f; s.hasffaspawns = s.spawns[2] > 0; s.hasteamspawns = s.spawns[0] > 0 && s.spawns[1] > 0; s.hasflags = s.flags[0] > 0 && s.flags[1] > 0; @@ -540,22 +232,9 @@ enet_uint32 adler(unsigned char *data, size_t len) return b; } -void endianswap(void *memory, int stride, int length) // little endian as storage format -{ - static const int littleendian = 1; - if(!*(const char *)&littleendian) loop(w, length) loop(i, stride/2) - { - uchar *p = (uchar *)memory+w*stride; - uchar t = p[i]; - p[i] = p[stride-i-1]; - p[stride-i-1] = t; - } -} - bool isbigendian() { - long one = 1; - return !(*((char *)(&one))); + return !*(const uchar *)&islittleendian; } void strtoupper(char *t, const char *s) diff --git a/source/src/tools.h b/source/src/tools.h index 3b58276d..b1350ce3 100644 --- a/source/src/tools.h +++ b/source/src/tools.h @@ -131,11 +131,11 @@ struct stringformatter //#define s_sprintfdlv(d,last,fmt) string d; { va_list ap; va_start(ap, last); formatstring(d, fmt, ap); va_end(ap); } //#define s_sprintfdv(d,fmt) s_sprintfdlv(d,fmt,fmt) -#define loopv(v) if(false) {} else for(int i = 0; i<(v).length(); i++) -#define loopvj(v) if(false) {} else for(int j = 0; j<(v).length(); j++) -#define loopvk(v) if(false) {} else for(int k = 0; k<(v).length(); k++) -#define loopvrev(v) if(false) {} else for(int i = (v).length()-1; i>=0; i--) -#define loopvjrev(v) if(false) {} else for(int j = (v).length()-1; i>=0; i--) +#define loopv(v) for(int i = 0; i<(v).length(); i++) +#define loopvj(v) for(int j = 0; j<(v).length(); j++) +#define loopvk(v) for(int k = 0; k<(v).length(); k++) +#define loopvrev(v) for(int i = (v).length()-1; i>=0; i--) +#define loopvjrev(v) for(int j = (v).length()-1; i>=0; i--) template struct databuf @@ -653,6 +653,76 @@ inline const char *_gettext(const char *msgid) #define _(s) _gettext(s) +const int islittleendian = 1; +#ifdef SDL_BYTEORDER +#define endianswap16 SDL_Swap16 +#define endianswap32 SDL_Swap32 +#else +inline ushort endianswap16(ushort n) { return (n<<8) | (n>>8); } +inline uint endianswap32(uint n) { return (n<<24) | (n>>24) | ((n>>8)&0xFF00) | ((n<<8)&0xFF0000); } +#endif +template inline T endianswap(T n) { union { T t; uint i; } conv; conv.t = n; conv.i = endianswap32(conv.i); return conv.t; } +template<> inline ushort endianswap(ushort n) { return endianswap16(n); } +template<> inline short endianswap(short n) { return endianswap16(n); } +template<> inline uint endianswap(uint n) { return endianswap32(n); } +template<> inline int endianswap(int n) { return endianswap32(n); } +template inline void endianswap(T *buf, int len) { for(T *end = &buf[len]; buf < end; buf++) *buf = endianswap(*buf); } +template inline T endiansame(T n) { return n; } +template inline void endiansame(T *buf, int len) {} +#ifdef SDL_BYTEORDER +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define lilswap endiansame +#define bigswap endianswap +#else +#define lilswap endianswap +#define bigswap endiansame +#endif +#else +template inline T lilswap(T n) { return *(const uchar *)&islittleendian ? n : endianswap(n); } +template inline void lilswap(T *buf, int len) { if(!*(const uchar *)&islittleendian) endianswap(buf, len); } +template inline T bigswap(T n) { return *(const uchar *)&islittleendian ? endianswap(n) : n; } +template inline void bigswap(T *buf, int len) { if(*(const uchar *)&islittleendian) endianswap(buf, len); } +#endif + +/* workaround for some C platforms that have these two functions as macros - not used anywhere */ +#ifdef getchar +#undef getchar +#endif +#ifdef putchar +#undef putchar +#endif + +struct stream +{ + virtual ~stream() {} + virtual void close() = 0; + virtual bool end() = 0; + virtual long tell() { return -1; } + virtual bool seek(long offset, int whence = SEEK_SET) { return false; } + virtual long size(); + virtual int read(void *buf, int len) { return 0; } + virtual int write(const void *buf, int len) { return 0; } + virtual int getchar() { uchar c; return read(&c, 1) == 1 ? c : -1; } + virtual bool putchar(int n) { uchar c = n; return write(&c, 1) == 1; } + virtual bool getline(char *str, int len); + virtual bool putstring(const char *str) { int len = strlen(str); return write(str, len) == len; } + virtual bool putline(const char *str) { return putstring(str) && putchar('\n'); } + virtual int printf(const char *fmt, ...) { return -1; } + virtual uint getcrc() { return 0; } + + template bool put(T n) { return write(&n, sizeof(n)) == sizeof(n); } + template bool putlil(T n) { return put(lilswap(n)); } + template bool putbig(T n) { return put(bigswap(n)); } + + template T get() { T n; return read(&n, sizeof(n)) == sizeof(n) ? n : 0; } + template T getlil() { return lilswap(get()); } + template T getbig() { return bigswap(get()); } + +#ifndef STANDALONE + SDL_RWops *rwops(); +#endif +}; + extern const char *timestring(bool local = false, const char *fmt = NULL); extern const char *asctime(); extern const char *numtime(); @@ -662,15 +732,20 @@ extern const char *behindpath(const char *s); extern const char *parentdir(const char *directory); extern bool fileexists(const char *path, const char *mode); extern bool createdir(const char *path); +extern size_t fixpackagedir(char *dir); extern void sethomedir(const char *dir); extern void addpackagedir(const char *dir); extern const char *findfile(const char *filename, const char *mode); extern int getfilesize(const char *filename); -extern FILE *openfile(const char *filename, const char *mode); -extern gzFile opengzfile(const char *filename, const char *mode); +extern stream *openrawfile(const char *filename, const char *mode); +extern stream *openzipfile(const char *filename, const char *mode); +extern stream *openfile(const char *filename, const char *mode); +extern stream *opentempfile(const char *filename, const char *mode); +extern stream *opengzfile(const char *filename, const char *mode, stream *file = NULL, int level = Z_BEST_COMPRESSION); extern char *loadfile(const char *fn, int *size, const char *mode = NULL); extern bool listdir(const char *dir, const char *ext, vector &files); extern int listfiles(const char *dir, const char *ext, vector &files); +extern int listzipfiles(const char *dir, const char *ext, vector &files); extern bool delfile(const char *path); extern struct mapstats *loadmapstats(const char *filename, bool getlayout); extern bool cmpb(void *b, int n, enet_uint32 c); diff --git a/source/src/vertmodel.h b/source/src/vertmodel.h index 8334f7b3..b58faad3 100644 --- a/source/src/vertmodel.h +++ b/source/src/vertmodel.h @@ -986,7 +986,7 @@ struct vertmodel : model #undef FILTER } - void genshadow(int aasize, int frame, gzFile f) + void genshadow(int aasize, int frame, stream *f) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -1010,7 +1010,7 @@ struct vertmodel : model int texsize = min(aasize, 1<write(&pixels[texsize*texsize], texsize*texsize); createtexture(shadows[frame], texsize, texsize, &pixels[texsize*texsize], 3, true, false, GL_ALPHA); delete[] pixels; @@ -1037,7 +1037,7 @@ struct vertmodel : model int aasize = 1<<(dynshadowsize + aadynshadow); while(aasize > screen->w || aasize > screen->h) aasize /= 2; - gzFile f = filename ? opengzfile(filename, "wb9") : NULL; + stream *f = filename ? opengzfile(filename, "wb9") : NULL; if(f) { shadowheader hdr; @@ -1045,11 +1045,10 @@ struct vertmodel : model hdr.frames = numframes; hdr.height = height; hdr.rad = rad; - endianswap(&hdr.size, sizeof(ushort), 1); - endianswap(&hdr.frames, sizeof(ushort), 1); - endianswap(&hdr.height, sizeof(float), 1); - endianswap(&hdr.rad, sizeof(float), 1); - gzwrite(f, &hdr, sizeof(shadowheader)); + f->putlil(hdr.size); + f->putlil(hdr.frames); + f->putlil(hdr.height); + f->putlil(hdr.rad); } glViewport(0, 0, aasize, aasize); @@ -1073,23 +1072,23 @@ struct vertmodel : model glEnable(GL_FOG); glViewport(0, 0, screen->w, screen->h); - if(f) gzclose(f); + if(f) delete f; } bool loadshadows(const char *filename) { - gzFile f = opengzfile(filename, "rb9"); + stream *f = opengzfile(filename, "rb9"); if(!f) return false; shadowheader hdr; - if(gzread(f, &hdr, sizeof(shadowheader))!=sizeof(shadowheader)) { gzclose(f); return false; } - endianswap(&hdr.size, sizeof(ushort), 1); - endianswap(&hdr.frames, sizeof(ushort), 1); - if(hdr.size!=(1<read(&hdr, sizeof(shadowheader))!=sizeof(shadowheader)) { delete f; return false; } + lilswap(&hdr.size, 1); + lilswap(&hdr.frames, 1); + if(hdr.size!=(1<read(buf, hdr.size*hdr.size*hdr.frames)!=hdr.size*hdr.size*hdr.frames) { delete f; return false; } shadowrad = hdr.rad; shadows = new GLuint[hdr.frames]; @@ -1098,8 +1097,7 @@ struct vertmodel : model loopi(hdr.frames) createtexture(shadows[i], hdr.size, hdr.size, &buf[i*hdr.size*hdr.size], 3, true, false, GL_ALPHA); delete[] buf; - - gzclose(f); + delete f; return true; } diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index 062f90f9..534fff3f 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -112,10 +112,10 @@ void writemap(char *name, int msize, uchar *mdata) { setnames(name); backup(cgzname, bakname); - FILE *f = openfile(cgzname, "wb"); + stream *f = openfile(cgzname, "wb"); if(!f) { conoutf("\f3could not write map to %s", cgzname); return; } - fwrite(mdata, 1, msize, f); - fclose(f); + f->write(mdata, msize); + delete f; conoutf("wrote map %s as file %s", name, cgzname); } @@ -138,11 +138,11 @@ void writecfggz(char *name, int size, int sizegz, uchar *data) uLongf rawsize = size; if(rawcfg && uncompress(rawcfg, &rawsize, data, sizegz) == Z_OK && rawsize - size == 0) { - FILE *f = openfile(mcfname, "w"); + stream *f = openfile(mcfname, "w"); if(f) { - fwrite(rawcfg, 1, size, f); - fclose(f); + f->write(rawcfg, size); + delete f; conoutf("wrote map config to %s", mcfname); } else @@ -355,7 +355,7 @@ void save_world(char *mname) // MediaPacks (flowtron) 20091007 checkmapdependencies(true); copystring(hdr.mediareq, reqmpak, 128); - gzFile f = opengzfile(cgzname, "wb9"); + stream *f = opengzfile(cgzname, "wb"); if(!f) { conoutf("could not write map to %s", cgzname); return; } // TODO (flowtron) : check map dependencies and set the appropriate value (NULL, "-1" or "A,B,C,...") in the header // MediaPack hdr.version = MAPVERSION; @@ -367,25 +367,25 @@ void save_world(char *mname) hdr.numents = MAXENTITIES; } header tmp = hdr; - endianswap(&tmp.version, sizeof(int), 4); - endianswap(&tmp.waterlevel, sizeof(int), 1); + lilswap(&tmp.version, 4); + lilswap(&tmp.waterlevel, 1); tmp.maprevision += advancemaprevision; - endianswap(&tmp.maprevision, sizeof(int), 1); - gzwrite(f, &tmp, sizeof(header)); + lilswap(&tmp.maprevision, 1); + f->write(&tmp, sizeof(header)); int ne = hdr.numents; loopv(ents) { if(ents[i].type!=NOTUSED) { if(!ne--) break; - entity tmp = ents[i]; - endianswap(&tmp, sizeof(short), 4); - gzwrite(f, &tmp, sizeof(persistent_entity)); + persistent_entity tmp = ents[i]; + lilswap((short *)&tmp, 4); + f->write(&tmp, sizeof(persistent_entity)); } } sqr *t = NULL; int sc = 0; - #define spurge while(sc) { gzputc(f, 255); if(sc>255) { gzputc(f, 255); sc -= 255; } else { gzputc(f, sc); sc = 0; } } + #define spurge while(sc) { f->putchar(255); if(sc>255) { f->putchar(255); sc -= 255; } else { f->putchar(sc); sc = 0; } } loopk(cubicsize) { sqr *s = &world[k]; @@ -405,9 +405,9 @@ void save_world(char *mname) else { spurge; - gzputc(f, s->type); - gzputc(f, s->wtex); - gzputc(f, s->vdelta); + f->putchar(s->type); + f->putchar(s->wtex); + f->putchar(s->vdelta); } } else @@ -419,21 +419,21 @@ void save_world(char *mname) else { spurge; - gzputc(f, s->type); - gzputc(f, s->floor); - gzputc(f, s->ceil); - gzputc(f, s->wtex); - gzputc(f, s->ftex); - gzputc(f, s->ctex); - gzputc(f, s->vdelta); - gzputc(f, s->utex); - gzputc(f, s->tag); + f->putchar(s->type); + f->putchar(s->floor); + f->putchar(s->ceil); + f->putchar(s->wtex); + f->putchar(s->ftex); + f->putchar(s->ctex); + f->putchar(s->vdelta); + f->putchar(s->utex); + f->putchar(s->tag); } } t = s; } spurge; - gzclose(f); + delete f; conoutf("wrote map file %s", cgzname); } @@ -455,25 +455,25 @@ bool load_world(char *mname) // still supports all map formats that have copystring(mcfname, omcfname); } else maploaded = getfilesize(cgzname); - gzFile f = opengzfile(cgzname, "rb9"); + stream *f = opengzfile(cgzname, "rb"); if(!f) { conoutf("\f3could not read map %s", cgzname); return false; } header tmp; memset(&tmp, 0, sizeof(header)); - if(gzread( f, &tmp, sizeof(header)-sizeof(int)*16-sizeof(char)*128)!=sizeof(header)-sizeof(int)*16-sizeof(char)*128) { conoutf("\f3while reading map: header malformatted"); gzclose(f); return false; } - endianswap(&tmp.version, sizeof(int), 4); - if(strncmp(tmp.head, "CUBE", 4)!=0 && strncmp(tmp.head, "ACMP",4)!=0) { conoutf("\f3while reading map: header malformatted"); gzclose(f); return false; } - if(tmp.version>MAPVERSION) { conoutf("\f3this map requires a newer version of cube"); gzclose(f); return false; } - if(tmp.sfactorLARGEST_FACTOR || tmp.numents > MAXENTITIES) { conoutf("\f3illegal map size"); gzclose(f); return false; } - if(tmp.version>=4 && gzread(f, &tmp.waterlevel, sizeof(int)*16)!=sizeof(int)*16) { conoutf("\f3while reading map: header malformatted"); gzclose(f); return false; } - if(tmp.version>=7 && gzread(f, &tmp.mediareq, sizeof(char)*128)!=sizeof(char)*128) { conoutf("\f3while reading map: header malformatted"); gzclose(f); return false; } + if(f->read(&tmp, sizeof(header)-sizeof(int)*16-sizeof(char)*128)!=sizeof(header)-sizeof(int)*16-sizeof(char)*128) { conoutf("\f3while reading map: header malformatted"); delete f; return false; } + lilswap(&tmp.version, 4); + if(strncmp(tmp.head, "CUBE", 4)!=0 && strncmp(tmp.head, "ACMP",4)!=0) { conoutf("\f3while reading map: header malformatted"); delete f; return false; } + if(tmp.version>MAPVERSION) { conoutf("\f3this map requires a newer version of cube"); delete f; return false; } + if(tmp.sfactorLARGEST_FACTOR || tmp.numents > MAXENTITIES) { conoutf("\f3illegal map size"); delete f; return false; } + if(tmp.version>=4 && f->read(&tmp.waterlevel, sizeof(int)*16)!=sizeof(int)*16) { conoutf("\f3while reading map: header malformatted"); delete f; return false; } + if(tmp.version>=7 && f->read(&tmp.mediareq, sizeof(char)*128)!=sizeof(char)*128) { conoutf("\f3while reading map: header malformatted"); delete f; return false; } hdr = tmp; loadingscreen("%s", hdr.maptitle); resetmap(); if(hdr.version>=4) { - endianswap(&hdr.waterlevel, sizeof(int), 1); + lilswap(&hdr.waterlevel, 1); if(!hdr.watercolor[3]) setwatercolor(); - endianswap(&hdr.maprevision, sizeof(int), 1); + lilswap(&hdr.maprevision, 1); curmaprevision = hdr.maprevision; } else @@ -493,8 +493,8 @@ bool load_world(char *mname) // still supports all map formats that have loopi(hdr.numents) { entity &e = ents.add(); - gzread(f, &e, sizeof(persistent_entity)); - endianswap(&e, sizeof(short), 4); + f->read(&e, sizeof(persistent_entity)); + lilswap((short *)&e, 4); e.spawned = false; TRANSFORMOLDENTITIES(hdr) if(e.type == PLAYERSTART && (e.attr2 == 0 || e.attr2 == 1 || e.attr2 == 100)) @@ -516,7 +516,7 @@ bool load_world(char *mname) // still supports all map formats that have loopk(cubicsize) { sqr *s = &world[k]; - int type = f ? gzgetc(f) : -1; + int type = f ? f->getchar() : -1; switch(type) { case -1: @@ -524,7 +524,7 @@ bool load_world(char *mname) // still supports all map formats that have if(f) { conoutf("while reading map at %d: type %d out of range", k, type); - gzclose(f); + delete f; f = NULL; } s->type = SOLID; @@ -539,26 +539,26 @@ bool load_world(char *mname) // still supports all map formats that have } case 255: { - if(!t) { gzclose(f); f = NULL; k--; continue; } - int n = gzgetc(f); + if(!t) { delete f; f = NULL; k--; continue; } + int n = f->getchar(); for(int i = 0; ir = s->g = s->b = gzgetc(f); - gzgetc(f); + s->r = s->g = s->b = f->getchar(); + f->getchar(); break; } case SOLID: { s->type = SOLID; - s->wtex = gzgetc(f); - s->vdelta = gzgetc(f); - if(hdr.version<=2) { gzgetc(f); gzgetc(f); } + s->wtex = f->getchar(); + s->vdelta = f->getchar(); + if(hdr.version<=2) { f->getchar(); f->getchar(); } s->ftex = DEFAULT_FLOOR; s->ctex = DEFAULT_CEIL; s->utex = s->wtex; @@ -572,22 +572,22 @@ bool load_world(char *mname) // still supports all map formats that have if(type<0 || type>=MAXTYPE) { conoutf("while reading map at %d: type %d out of range", k, type); - gzclose(f); + delete f; f = NULL; k--; continue; } s->type = type; - s->floor = gzgetc(f); - s->ceil = gzgetc(f); + s->floor = f->getchar(); + s->ceil = f->getchar(); if(s->floor>=s->ceil) s->floor = s->ceil-1; // for pre 12_13 - s->wtex = gzgetc(f); - s->ftex = gzgetc(f); - s->ctex = gzgetc(f); - if(hdr.version<=2) { gzgetc(f); gzgetc(f); } - s->vdelta = gzgetc(f); - s->utex = (hdr.version>=2) ? gzgetc(f) : s->wtex; - s->tag = (hdr.version>=5) ? gzgetc(f) : 0; + s->wtex = f->getchar(); + s->ftex = f->getchar(); + s->ctex = f->getchar(); + if(hdr.version<=2) { f->getchar(); f->getchar(); } + s->vdelta = f->getchar(); + s->utex = (hdr.version>=2) ? f->getchar() : s->wtex; + s->tag = (hdr.version>=5) ? f->getchar() : 0; } } s->defer = 0; @@ -595,7 +595,7 @@ bool load_world(char *mname) // still supports all map formats that have texuse[s->wtex] = 1; if(!SOLID(s)) texuse[s->utex] = texuse[s->ftex] = texuse[s->ctex] = 1; } - if(f) gzclose(f); + if(f) delete f; c2skeepalive(); calclight(); conoutf("read map %s rev %d (%d milliseconds)", cgzname, hdr.maprevision, watch.stop()); @@ -653,23 +653,23 @@ void listmapdependencies(char *mapname) // print map dependencies to file } if(multiplayer()) return; - FILE *f = openfile(MAPDEPFILENAME, "a"); + stream *f = openfile(MAPDEPFILENAME, "a"); if(!f) { conoutf("\f3could not append to %s", MAPDEPFILENAME); return; } if(!mapname || !*mapname) { // print summary vector allres; enumeratek(sumpaths, const char *, key, allres.add(key)); allres.sort(stringsort); - fprintf(f, "used ressources total:\n"); - loopv(allres) fprintf(f, " used %6d times: \"%s\"\n", *sumpaths.access(allres[i]), allres[i]); + f->printf("used ressources total:\n"); + loopv(allres) f->printf(" used %6d times: \"%s\"\n", *sumpaths.access(allres[i]), allres[i]); enumeratek(sumpaths, const char *, key, delete key); sumpaths.clear(); - fprintf(f, " %d files used\n\n\n", allres.length()); + f->printf(" %d files used\n\n\n", allres.length()); } else if(load_world(mapname)) { // print map deps filtertext(fullname, hdr.maptitle, 1); - fprintf(f, "-- --:-- -- -- --\n map: %s\n %s\nspawns: FFA %d\tCLA %2d RVSF %2d\tflags: %d+%d\n size: %d : %8d\trevision: %d\n-- --:-- -- -- --\n", + f->printf("-- --:-- -- -- --\n map: %s\n %s\nspawns: FFA %d\tCLA %2d RVSF %2d\tflags: %d+%d\n size: %d : %8d\trevision: %d\n-- --:-- -- -- --\n", cgzname, fullname, numspawn[2], numspawn[0], numspawn[1], numflagspawn[0], numflagspawn[1], @@ -680,11 +680,11 @@ void listmapdependencies(char *mapname) // print map dependencies to file { extern Texture *sky[]; Texture *t = sky[i]; - if(t == notexture) fprintf(f, " sky texture %d doesn't exist\n", i); + if(t == notexture) f->printf(" sky texture %d doesn't exist\n", i); else { ADDFILENAME("%s", t->name, 1); - fprintf(f, " sky texture %d, \"%s\"\n", i, fullname); + f->printf(" sky texture %d, \"%s\"\n", i, fullname); } } int texuse[256] = { 0 }; @@ -706,45 +706,45 @@ void listmapdependencies(char *mapname) // print map dependencies to file loopi(256) if(texuse[i]) { Texture *t = lookuptexture(i); - if(t == notexture) fprintf(f, " texture slot %3d doesn't exist (used %d times)\n", i, texuse[i]); + if(t == notexture) f->printf(" texture slot %3d doesn't exist (used %d times)\n", i, texuse[i]); else { used++; ADDFILENAME("%s", t->name, texuse[i]); - fprintf(f, " texture slot %3d used %5d times, \"%s\"\n", i, texuse[i], fullname); + f->printf(" texture slot %3d used %5d times, \"%s\"\n", i, texuse[i], fullname); } } - fprintf(f, " used: %d texture slots\n", used); + f->printf(" used: %d texture slots\n", used); // mapmodels int mmuse[256] = { 0 }; used = 0; loopv(ents) if(ents[i].type == MAPMODEL) mmuse[ents[i].attr2]++; loopi(256) if(mmuse[i]) { - if(!mapmodels.inrange(i)) fprintf(f, " mapmodel slot %3d doesn't exist (used %d times)\n", i, mmuse[i]); + if(!mapmodels.inrange(i)) f->printf(" mapmodel slot %3d doesn't exist (used %d times)\n", i, mmuse[i]); else { used++; ADDFILENAME("packages/models/%s", mapmodels[i].name, mmuse[i]); - fprintf(f, " mapmodel slot %3d used %4d times, \"%s\"\n", i, mmuse[i], fullname); + f->printf(" mapmodel slot %3d used %4d times, \"%s\"\n", i, mmuse[i], fullname); } } - fprintf(f, " used: %d mapmodel slots\n", used); + f->printf(" used: %d mapmodel slots\n", used); // sounds int msuse[128] = { 0 }; used = 0; loopv(ents) if(ents[i].type == SOUND && ents[i].attr1 >= 0) msuse[ents[i].attr1]++; loopi(128) if(msuse[i]) { - if(!mapsounds.inrange(i)) fprintf(f, " mapsound slot %3d doesn't exist (used %d times)\n", i, msuse[i]); + if(!mapsounds.inrange(i)) f->printf(" mapsound slot %3d doesn't exist (used %d times)\n", i, msuse[i]); else if(mapsounds[i].buf && mapsounds[i].buf->name) { used++; ADDFILENAME("packages/audio/sounds/%s", mapsounds[i].buf->name, msuse[i]); - fprintf(f, " mapsound slot %3d used %4d times, \"%s\"\n", i, msuse[i], fullname); + f->printf(" mapsound slot %3d used %4d times, \"%s\"\n", i, msuse[i], fullname); } } - fprintf(f, " used: %d mapsound slots\n", used); + f->printf(" used: %d mapsound slots\n", used); int usedm = 0; allmods[0] = '\0'; @@ -752,13 +752,13 @@ void listmapdependencies(char *mapname) // print map dependencies to file pushscontext(IEXC_MAPCFG); // untrusted altogether persistidents = false; execfile(mcfname); - fprintf(f, " used: %s%s %d total packages (%s.cfg)\n", allmods, usedm ? ", " : "", usedm, mapname); + f->printf(" used: %s%s %d total packages (%s.cfg)\n", allmods, usedm ? ", " : "", usedm, mapname); if(usedm) conoutf(" used: %s (%s.cfg) in %s", allmods, mapname, mapname); persistidents = true; popscontext(); - fprintf(f, "\n\n"); + f->printf("\n\n"); } - fclose(f); + delete f; } COMMAND(listmapdependencies, ARG_1STR); @@ -767,8 +767,8 @@ void listmapdependencies_all(int sure) { if(sure != 42 || multiplayer()) return; - FILE *f = openfile(MAPDEPFILENAME, "w"); - fclose(f); + stream *f = openfile(MAPDEPFILENAME, "w"); + delete f; cvector files; listfiles("packages/maps", "cgz", files); listfiles("packages/maps/official", "cgz", files); diff --git a/source/src/zip.cpp b/source/src/zip.cpp new file mode 100644 index 00000000..584b734f --- /dev/null +++ b/source/src/zip.cpp @@ -0,0 +1,557 @@ +#include "cube.h" + +enum +{ + ZIP_LOCAL_FILE_SIGNATURE = 0x04034B50, + ZIP_LOCAL_FILE_SIZE = 30, + ZIP_FILE_SIGNATURE = 0x02014B50, + ZIP_FILE_SIZE = 46, + ZIP_DIRECTORY_SIGNATURE = 0x06054B50, + ZIP_DIRECTORY_SIZE = 22 +}; + +struct ziplocalfileheader +{ + uint signature; + ushort version, flags, compression, modtime, moddate; + uint crc32, compressedsize, uncompressedsize; + ushort namelength, extralength; +}; + +struct zipfileheader +{ + uint signature; + ushort version, needversion, flags, compression, modtime, moddate; + uint crc32, compressedsize, uncompressedsize; + ushort namelength, extralength, commentlength, disknumber, internalattribs; + uint externalattribs, offset; +}; + +struct zipdirectoryheader +{ + uint signature; + ushort disknumber, directorydisk, diskentries, entries; + uint size, offset; + ushort commentlength; +}; + +struct zipfile +{ + char *name; + uint header, offset, size, compressedsize; + + zipfile() : name(NULL), header(0), offset(~0U), size(0), compressedsize(0) + { + } + ~zipfile() + { + DELETEA(name); + } +}; + +struct zipstream; + +struct ziparchive +{ + char *name; + FILE *data; + hashtable files; + int openfiles; + zipstream *owner; + + ziparchive() : name(NULL), data(NULL), files(512), openfiles(0), owner(NULL) + { + } + ~ziparchive() + { + DELETEA(name); + if(data) { fclose(data); data = NULL; } + } +}; + +static bool findzipdirectory(FILE *f, zipdirectoryheader &hdr) +{ + if(fseek(f, 0, SEEK_END) < 0) return false; + + uchar buf[1024], *src = NULL; + int len = 0, offset = ftell(f), end = max(offset - 0xFFFF - ZIP_DIRECTORY_SIZE, 0); + const uint signature = lilswap(ZIP_DIRECTORY_SIGNATURE); + + while(offset > end) + { + int carry = min(len, ZIP_DIRECTORY_SIZE-1), next = min((int)sizeof(buf) - carry, offset - end); + offset -= next; + memmove(&buf[next], buf, carry); + if(next + carry < ZIP_DIRECTORY_SIZE || fseek(f, offset, SEEK_SET) < 0 || (int)fread(buf, 1, next, f) != next) return false; + len = next + carry; + uchar *search = &buf[next-1]; + for(; search >= buf; search--) if(*(uint *)search == signature) break; + if(search >= buf) { src = search; break; } + } + + if(&buf[len] - src < ZIP_DIRECTORY_SIZE) return false; + + hdr.signature = lilswap(*(uint *)src); src += 4; + hdr.disknumber = lilswap(*(ushort *)src); src += 2; + hdr.directorydisk = lilswap(*(ushort *)src); src += 2; + hdr.diskentries = lilswap(*(ushort *)src); src += 2; + hdr.entries = lilswap(*(ushort *)src); src += 2; + hdr.size = lilswap(*(uint *)src); src += 4; + hdr.offset = lilswap(*(uint *)src); src += 4; + hdr.commentlength = lilswap(*(ushort *)src); src += 2; + + if(hdr.signature != ZIP_DIRECTORY_SIGNATURE || hdr.disknumber != hdr.directorydisk || hdr.diskentries != hdr.entries) return false; + + return true; +} + +#ifndef STANDALONE +VAR(dbgzip, 0, 0, 1); +#endif + +static bool readzipdirectory(const char *archname, FILE *f, int entries, int offset, int size, vector &files) +{ + uchar *buf = new uchar[size], *src = buf; + if(fseek(f, offset, SEEK_SET) < 0 || (int)fread(buf, 1, size, f) != size) { delete[] buf; return false; } + loopi(entries) + { + if(src + ZIP_FILE_SIZE > &buf[size]) break; + + zipfileheader hdr; + hdr.signature = lilswap(*(uint *)src); src += 4; + hdr.version = lilswap(*(ushort *)src); src += 2; + hdr.needversion = lilswap(*(ushort *)src); src += 2; + hdr.flags = lilswap(*(ushort *)src); src += 2; + hdr.compression = lilswap(*(ushort *)src); src += 2; + hdr.modtime = lilswap(*(ushort *)src); src += 2; + hdr.moddate = lilswap(*(ushort *)src); src += 2; + hdr.crc32 = lilswap(*(uint *)src); src += 4; + hdr.compressedsize = lilswap(*(uint *)src); src += 4; + hdr.uncompressedsize = lilswap(*(uint *)src); src += 4; + hdr.namelength = lilswap(*(ushort *)src); src += 2; + hdr.extralength = lilswap(*(ushort *)src); src += 2; + hdr.commentlength = lilswap(*(ushort *)src); src += 2; + hdr.disknumber = lilswap(*(ushort *)src); src += 2; + hdr.internalattribs = lilswap(*(ushort *)src); src += 2; + hdr.externalattribs = lilswap(*(uint *)src); src += 4; + hdr.offset = lilswap(*(uint *)src); src += 4; + if(hdr.signature != ZIP_FILE_SIGNATURE) break; + if(!hdr.namelength || !hdr.uncompressedsize || (hdr.compression && (hdr.compression != Z_DEFLATED || !hdr.compressedsize))) + { + src += hdr.namelength + hdr.extralength + hdr.commentlength; + continue; + } + if(src + hdr.namelength > &buf[size]) break; + + string pname; + int namelen = min((int)hdr.namelength, (int)sizeof(pname)-1); + memcpy(pname, src, namelen); + pname[namelen] = '\0'; + path(pname); + char *name = newstring(pname); + + zipfile &f = files.add(); + f.name = name; + f.header = hdr.offset; + f.size = hdr.uncompressedsize; + f.compressedsize = hdr.compression ? hdr.compressedsize : 0; +#ifndef STANDALONE + if(dbgzip) conoutf("file %s, size %d, compress %d, flags %x", archname, name, hdr.uncompressedsize, hdr.compression, hdr.flags); +#endif + + src += hdr.namelength + hdr.extralength + hdr.commentlength; + } + delete[] buf; + + return files.length() > 0; +} + +static bool readlocalfileheader(FILE *f, ziplocalfileheader &h, uint offset) +{ + fseek(f, offset, SEEK_SET); + uchar buf[ZIP_LOCAL_FILE_SIZE]; + if(fread(buf, 1, ZIP_LOCAL_FILE_SIZE, f) != ZIP_LOCAL_FILE_SIZE) + return false; + uchar *src = buf; + h.signature = lilswap(*(uint *)src); src += 4; + h.version = lilswap(*(ushort *)src); src += 2; + h.flags = lilswap(*(ushort *)src); src += 2; + h.compression = lilswap(*(ushort *)src); src += 2; + h.modtime = lilswap(*(ushort *)src); src += 2; + h.moddate = lilswap(*(ushort *)src); src += 2; + h.crc32 = lilswap(*(uint *)src); src += 4; + h.compressedsize = lilswap(*(uint *)src); src += 4; + h.uncompressedsize = lilswap(*(uint *)src); src += 4; + h.namelength = lilswap(*(ushort *)src); src += 2; + h.extralength = lilswap(*(ushort *)src); src += 2; + if(h.signature != ZIP_LOCAL_FILE_SIGNATURE) return false; + // h.uncompressedsize or h.compressedsize may be zero - so don't validate + return true; +} + +static vector archives; + +ziparchive *findzip(const char *name) +{ + loopv(archives) if(!strcmp(name, archives[i]->name)) return archives[i]; + return NULL; +} + +static bool checkprefix(vector &files, const char *prefix, int prefixlen) +{ + loopv(files) + { + if(!strncmp(files[i].name, prefix, prefixlen)) return false; + } + return true; +} + +static void mountzip(ziparchive &arch, vector &files, const char *mountdir, const char *stripdir) +{ + string packagesdir = "packages/"; + path(packagesdir); + int striplen = stripdir ? strlen(stripdir) : 0; + if(!mountdir && !stripdir) loopv(files) + { + zipfile &f = files[i]; + const char *foundpackages = strstr(f.name, packagesdir); + if(foundpackages) + { + if(foundpackages > f.name) + { + stripdir = f.name; + striplen = foundpackages - f.name; + } + break; + } + const char *foundcgz = strstr(f.name, ".cgz"); + if(foundcgz) + { + const char *cgzdir = foundcgz; + while(--cgzdir >= f.name && *cgzdir != PATHDIV); + if(cgzdir < f.name || checkprefix(files, f.name, cgzdir + 1 - f.name)) + { + if(cgzdir >= f.name) + { + stripdir = f.name; + striplen = cgzdir + 1 - f.name; + } + if(!mountdir) mountdir = "packages/maps/"; + break; + } + } + } + string mdir = "", fname; + if(mountdir) + { + copystring(mdir, mountdir); + if(fixpackagedir(mdir) <= 1) mdir[0] = '\0'; + } + loopv(files) + { + zipfile &f = files[i]; + formatstring(fname)("%s%s", mdir, striplen && !strncmp(f.name, stripdir, striplen) ? &f.name[striplen] : f.name); + if(arch.files.access(fname)) continue; + char *mname = newstring(fname); + zipfile &mf = arch.files[mname]; + mf = f; + mf.name = mname; + } +} + +bool addzip(const char *name, const char *mount = NULL, const char *strip = NULL) +{ + string pname; + copystring(pname, name); + path(pname); + int plen = strlen(pname); + if(plen < 4 || !strchr(&pname[plen-4], '.')) concatstring(pname, ".zip"); + + ziparchive *exists = findzip(pname); + if(exists) + { + conoutf("already added zip %s", pname); + return true; + } + + FILE *f = fopen(findfile(pname, "rb"), "rb"); + if(!f) + { + conoutf("could not open file %s", pname); + return false; + } + zipdirectoryheader h; + vector files; + if(!findzipdirectory(f, h) || !readzipdirectory(pname, f, h.entries, h.offset, h.size, files)) + { + conoutf("could not read directory in zip %s", pname); + fclose(f); + return false; + } + + ziparchive *arch = new ziparchive; + arch->name = newstring(pname); + arch->data = f; + mountzip(*arch, files, mount, strip); + archives.add(arch); + + conoutf("added zip %s", pname); + return true; +} + +bool removezip(const char *name) +{ + const char *pname = path(name, true); + ziparchive *exists = findzip(pname); + if(!exists) + { + conoutf("zip %s is not loaded", pname); + return false; + } + if(exists->openfiles) + { + conoutf("zip %s has open files", pname); + return false; + } + conoutf("removed zip %s", exists->name); + archives.removeobj(exists); + delete exists; + return true; +} + +struct zipstream : stream +{ + enum + { + BUFSIZE = 16384 + }; + + ziparchive *arch; + zipfile *info; + z_stream zfile; + uchar *buf; + int reading; + + zipstream() : arch(NULL), info(NULL), buf(NULL), reading(-1) + { + zfile.zalloc = NULL; + zfile.zfree = NULL; + zfile.opaque = NULL; + zfile.next_in = zfile.next_out = NULL; + zfile.avail_in = zfile.avail_out = 0; + } + + ~zipstream() + { + close(); + } + + void readbuf(uint size = BUFSIZE) + { + if(!zfile.avail_in) zfile.next_in = (Bytef *)buf; + size = min(size, uint(&buf[BUFSIZE] - &zfile.next_in[zfile.avail_in])); + if(arch->owner != this) + { + arch->owner = NULL; + if(fseek(arch->data, reading, SEEK_SET) >= 0) arch->owner = this; + else return; + } + else if(ftell(arch->data) != reading) *(int *)0 = 0; + uint remaining = info->offset + info->compressedsize - reading; + int n = arch->owner == this ? fread(zfile.next_in + zfile.avail_in, 1, min(size, remaining), arch->data) : 0; + zfile.avail_in += n; + reading += n; + } + + bool open(ziparchive *a, zipfile *f) + { + if(f->offset == ~0U) + { + ziplocalfileheader h; + a->owner = NULL; + if(!readlocalfileheader(a->data, h, f->header)) return false; + f->offset = f->header + ZIP_LOCAL_FILE_SIZE + h.namelength + h.extralength; + } + + if(f->compressedsize && inflateInit2(&zfile, -MAX_WBITS) != Z_OK) return false; + + a->openfiles++; + arch = a; + info = f; + reading = f->offset; + if(f->compressedsize) buf = new uchar[BUFSIZE]; + return true; + } + + void stopreading() + { + if(reading < 0) return; +#ifndef STANDALONE + if(dbgzip) conoutf(info->compressedsize ? "%s: zfile.total_out %d, info->size %d" : "%s: reading %d, info->size %d", info->name, info->compressedsize ? zfile.total_out : reading - info->offset, info->size); +#endif + if(info->compressedsize) inflateEnd(&zfile); + reading = -1; + } + + void close() + { + stopreading(); + DELETEA(buf); + if(arch) { arch->owner = NULL; arch->openfiles--; arch = NULL; } + } + + long size() { return info->size; } + bool end() { return reading < 0; } + long tell() { return reading >= 0 ? (info->compressedsize ? zfile.total_out : reading - info->offset) : -1; } + + bool seek(long pos, int whence) + { + if(reading < 0) return false; + if(!info->compressedsize) + { + switch(whence) + { + case SEEK_END: pos += info->offset + info->size; break; + case SEEK_CUR: pos += reading; break; + case SEEK_SET: pos += info->offset; break; + default: return false; + } + pos = clamp(pos, long(info->offset), long(info->offset + info->size)); + arch->owner = NULL; + if(fseek(arch->data, pos, SEEK_SET) < 0) return false; + arch->owner = this; + reading = pos; + return true; + } + + switch(whence) + { + case SEEK_END: pos += info->size; break; + case SEEK_CUR: pos += zfile.total_out; break; + case SEEK_SET: break; + default: return false; + } + + if(pos >= (long)info->size) + { + reading = info->offset + info->compressedsize; + zfile.next_in += zfile.avail_in; + zfile.avail_in = 0; + zfile.total_in = info->compressedsize; + arch->owner = NULL; + return true; + } + + if(pos < 0) return false; + if(pos >= (long)zfile.total_out) pos -= zfile.total_out; + else + { + if(zfile.next_in && zfile.total_in <= uint(zfile.next_in - buf)) + { + zfile.avail_in += zfile.total_in; + zfile.next_in -= zfile.total_in; + } + else + { + arch->owner = NULL; + zfile.avail_in = 0; + zfile.next_in = NULL; + reading = info->offset; + } + inflateReset(&zfile); + } + + uchar skip[512]; + while(pos > 0) + { + int skipped = min(pos, (long)sizeof(skip)); + if(read(skip, skipped) != skipped) { stopreading(); return false; } + pos -= skipped; + } + + return true; + } + + int read(void *buf, int len) + { + if(reading < 0 || !buf || !len) return 0; + if(!info->compressedsize) + { + if(arch->owner != this) + { + arch->owner = NULL; + if(fseek(arch->data, reading, SEEK_SET) < 0) { stopreading(); return 0; } + arch->owner = this; + } + + int n = fread(buf, 1, min(len, int(info->size + info->offset - reading)), arch->data); + reading += n; + if(n < len) stopreading(); + return n; + } + + zfile.next_out = (Bytef *)buf; + zfile.avail_out = len; + while(zfile.avail_out > 0) + { + if(!zfile.avail_in) readbuf(BUFSIZE); + int err = inflate(&zfile, Z_NO_FLUSH); + if(err != Z_OK) + { +#ifndef STANDALONE + if(err != Z_STREAM_END && dbgzip) conoutf("inflate error: %s", err); +#endif + stopreading(); + break; + } + } + return len - zfile.avail_out; + } +}; + +stream *openzipfile(const char *name, const char *mode) +{ + for(; *mode; mode++) if(*mode=='w' || *mode=='a') return NULL; + loopvrev(archives) + { + ziparchive *arch = archives[i]; + zipfile *f = arch->files.access(name); + if(!f) continue; + zipstream *s = new zipstream; + if(s->open(arch, f)) return s; + delete s; + } + return NULL; +} + +int listzipfiles(const char *dir, const char *ext, vector &files) +{ + int extsize = ext ? (int)strlen(ext)+1 : 0, dirsize = strlen(dir), dirs = 0; + loopvrev(archives) + { + ziparchive *arch = archives[i]; + int oldsize = files.length(); + enumerate(arch->files, zipfile, f, + { + if(strncmp(f.name, dir, dirsize)) continue; + const char *name = f.name + dirsize; + if(name[0] == PATHDIV) name++; + if(strchr(name, PATHDIV)) continue; + if(!ext) files.add(newstring(name)); + else + { + int namelength = (int)strlen(name) - extsize; + if(namelength > 0 && name[namelength] == '.' && strncmp(name+namelength+1, ext, extsize-1)==0) + files.add(newstring(name, namelength)); + } + }); + if(files.length() > oldsize) dirs++; + } + return dirs; +} + +#ifndef STANDALONE +COMMANDF(addzip, ARG_3STR, (const char *name, const char *mount, const char *strip) { addzip(name, mount[0] ? mount : NULL, strip[0] ? strip : NULL); }); +COMMANDF(removezip, ARG_1STR, (const char *name) { removezip(name); }); +#endif +