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
+