diff --git a/source/src/command.cpp b/source/src/command.cpp
index 8d1ebe6f..d7e44b66 100644
--- a/source/src/command.cpp
+++ b/source/src/command.cpp
@@ -324,7 +324,7 @@ int rndn(int a) { return a>0 ? rnd(a) : 0; } COMMANDN(rnd, rndn, ARG_1EXP);
void writecfg()
{
s_sprintfd(cfgname)("config%csaved.cfg", PATHDIV);
- FILE *f = fopen(cfgname, "w");
+ FILE *f = openfile(cfgname, "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\nteam %s\nskin %d\n", player1->name, player1->team, player1->nextskin);
diff --git a/source/src/docs.cpp b/source/src/docs.cpp
index 73b00a1c..fc7f7282 100644
--- a/source/src/docs.cpp
+++ b/source/src/docs.cpp
@@ -228,7 +228,7 @@ char *xmlstringenc(char *d, const char *s, size_t len)
void docwritebaseref(char *ref, char *schemalocation, char *transformation)
{
s_sprintfd(outfile)("docs%cautogenerated_base_reference.xml", PATHDIV);
- FILE *f = fopen(outfile, "w");
+ FILE *f = openfile(outfile, "w");
if(!f) return;
char desc[] = "TODO: Description";
diff --git a/source/src/main.cpp b/source/src/main.cpp
index 7846cc4b..312ee1ab 100644
--- a/source/src/main.cpp
+++ b/source/src/main.cpp
@@ -26,6 +26,8 @@ void quit() // normal exit
{
extern void stopreset();
if(demoplayback) stopreset(); else stop();
+ extern void writeinitcfg();
+ writeinitcfg();
writeservercfg();
writecfg();
cleanup(NULL);
@@ -39,22 +41,58 @@ void fatal(char *s, char *o) // failure exit
exit(EXIT_FAILURE);
}
-int scr_w = 1024;
-int scr_h = 768;
+SDL_Surface *screen = NULL;
int VIRTW;
+static bool initing = false, restoredinits = false;
+bool initwarning()
+{
+ if(!initing)
+ {
+ if(restoredinits) conoutf("Please restart AssaultCube for this setting to take effect.");
+ else conoutf("Please restart AssaultCube with the --init command-line option for this setting to take effect.");
+ }
+ return !initing;
+}
+
+VARF(scr_w, 0, 1024, 10000, initwarning());
+VARF(scr_h, 0, 768, 10000, initwarning());
+VARF(colorbits, 0, 0, 32, initwarning());
+VARF(depthbits, 0, 0, 32, initwarning());
+VARF(fsaa, 0, 0, 16, initwarning());
+VARF(vsync, -1, -1, 1, initwarning());
+
+void writeinitcfg()
+{
+ s_sprintfd(fn)("config%cinit.cfg", PATHDIV);
+ FILE *f = openfile(fn, "w");
+ if(!f) return;
+ fprintf(f, "// automatically written on exit, DO NOT MODIFY\n// modify settings in game\n");
+ 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, "fsaa %d\n", fsaa);
+ fprintf(f, "vsync %d\n", vsync);
+ extern int soundchans, soundfreq, soundbufferlen;
+ fprintf(f, "soundchans %d\n", soundchans);
+ fprintf(f, "soundfreq %d\n", soundfreq);
+ fprintf(f, "soundbufferlen %d\n", soundbufferlen);
+ fclose(f);
+}
+
void screenshot(char *imagepath)
{
- SDL_Surface *image = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0);
+ SDL_Surface *image = SDL_CreateRGBSurface(SDL_SWSURFACE, screen->w, screen->h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0);
if(!image) return;
- uchar *tmp = new uchar[scr_w*scr_h*3];
+ uchar *tmp = new uchar[screen->w*screen->h*3];
glPixelStorei(GL_PACK_ALIGNMENT, 1);
- glReadPixels(0, 0, scr_w, scr_h, GL_RGB, GL_UNSIGNED_BYTE, tmp);
+ glReadPixels(0, 0, screen->w, screen->h, GL_RGB, GL_UNSIGNED_BYTE, tmp);
uchar *dst = (uchar *)image->pixels;
loopi(scr_h)
{
- memcpy(dst, &tmp[3*scr_w*(scr_h-i-1)], 3*scr_w);
- endianswap(dst, 3, scr_w);
+ memcpy(dst, &tmp[3*screen->w*(screen->h-i-1)], 3*screen->w);
+ endianswap(dst, 3, screen->w);
dst += image->pitch;
}
delete[] tmp;
@@ -64,25 +102,20 @@ void screenshot(char *imagepath)
s_sprintf(buf)("screenshots/screenshot_%d.bmp", lastmillis);
imagepath = buf;
}
- SDL_SaveBMP(image, path(imagepath));
+ SDL_SaveBMP(image, findfile(path(imagepath), "wb"));
SDL_FreeSurface(image);
}
COMMAND(screenshot, ARG_1STR);
COMMAND(quit, ARG_NONE);
-SDL_Surface *screen = NULL;
-
-extern void setfullscreen(bool enable);
-
-VARF(fullscreen, 0, 0, 1, setfullscreen(fullscreen!=0));
-
void setfullscreen(bool enable)
{
if(enable == !(screen->flags&SDL_FULLSCREEN))
{
#if defined(WIN32) || defined(__APPLE__)
conoutf("\"fullscreen\" variable not supported on this platform. Use the -t command-line option.");
+ extern int fullscreen;
fullscreen = !enable;
#else
SDL_WM_ToggleFullScreen(screen);
@@ -93,16 +126,26 @@ void setfullscreen(bool enable)
void screenres(int w, int h, int bpp = 0)
{
+#if !defined(WIN32) && !defined(__APPLE__)
+ if(initing)
+ {
+#endif
+ scr_w = w;
+ scr_h = h;
+ if(bpp) colorbits = bpp;
#if defined(WIN32) || defined(__APPLE__)
- conoutf("\"screenres\" command not supported on this platform. Use the -w and -h command-line options.");
+ initwarning();
#else
+ return;
+ }
SDL_Surface *surf = SDL_SetVideoMode(w, h, bpp, SDL_OPENGL|SDL_RESIZABLE|(screen->flags&SDL_FULLSCREEN));
if(!surf) return;
- scr_w = w;
- scr_h = h;
- VIRTW = scr_w*VIRTH/scr_h;
screen = surf;
- glViewport(0, 0, w, h);
+ scr_w = screen->w;
+ scr_h = screen->h;
+ if(bpp) colorbits = bpp;
+ glViewport(0, 0, scr_w, scr_h);
+ VIRTW = scr_w*VIRTH/scr_h;
#endif
}
#if defined(WIN32) || defined(__APPLE__) || !defined(WIN32)
@@ -113,6 +156,8 @@ void setresdata(char *s, enet_uint32 c)
}
#endif
+VARF(fullscreen, 0, 0, 1, setfullscreen(fullscreen!=0));
+
COMMAND(screenres, ARG_3INT);
VARFP(gamma, 30, 100, 300,
@@ -170,23 +215,38 @@ VARF(gamespeed, 10, 100, 1000, if(multiplayer()) gamespeed = 100);
int main(int argc, char **argv)
{
bool dedicated = false;
- int fs = SDL_FULLSCREEN, depth = 0, bpp = 0, fsaa = 0, vsync = -1, par = 0, uprate = 0, maxcl = DEFAULTCLIENTS, scthreshold = -5;
+ int fs = SDL_FULLSCREEN, par = 0, uprate = 0, maxcl = DEFAULTCLIENTS, scthreshold = -5;
char *sdesc = "", *ip = "", *master = NULL, *passwd = "", *maprot = NULL, *adminpwd = NULL, *srvmsg = NULL;
#define initlog(s) puts("init: " s)
initlog("sdl");
+ initing = true;
for(int i = 1; iw; scr_h = modes[0]->h; }
}
- screen = SDL_SetVideoMode(scr_w, scr_h, bpp, SDL_OPENGL|resize|fs);
- if(screen==NULL) fatal("Unable to create OpenGL screen");
+ screen = SDL_SetVideoMode(scr_w, scr_h, colorbits, SDL_OPENGL|resize|fs);
+ if(!screen) fatal("Unable to create OpenGL screen");
+ scr_w = screen->w;
+ scr_h = screen->h;
+
fullscreen = fs!=0;
VIRTW = scr_w*VIRTH/scr_h;
@@ -268,7 +332,7 @@ int main(int argc, char **argv)
if(!setfont("default")) fatal("no default font specified");
initlog("gl");
- gl_init(scr_w, scr_h, bpp, depth, fsaa);
+ gl_init(scr_w, scr_h, colorbits, depthbits, fsaa);
crosshair = textureload("packages/misc/crosshairs/default.png");
if(!crosshair) fatal("could not find core textures (hint: run AssaultCube from the parent of the bin directory)");
@@ -370,7 +434,7 @@ int main(int argc, char **argv)
computeraytable(camera1->o.x, camera1->o.y);
if(frames>4) SDL_GL_SwapBuffers();
extern void updatevol(); updatevol();
- if(frames>3) gl_drawframe(scr_w, scr_h, fpshighfps ? fps/highfps : 1.0f), fps);
+ if(frames>3) gl_drawframe(screen->w, screen->h, fpshighfps ? fps/highfps : 1.0f), fps);
//SDL_Delay(100);
SDL_Event event;
int lasttype = 0, lastbut = 0;
@@ -407,8 +471,8 @@ int main(int argc, char **argv)
#ifdef __APPLE__
if(event.motion.y == 0) break; //let mac users drag windows via the title bar
#endif
- if(event.motion.x == scr_w / 2 && event.motion.y == scr_h / 2) break;
- SDL_WarpMouse(scr_w / 2, scr_h / 2);
+ if(event.motion.x == screen->w / 2 && event.motion.y == screen->h / 2) break;
+ SDL_WarpMouse(screen->w / 2, screen->h / 2);
}
#ifndef WIN32
if((screen->flags&SDL_FULLSCREEN) || grabmouse)
diff --git a/source/src/md2.h b/source/src/md2.h
index 8cfca80a..6230e5d1 100644
--- a/source/src/md2.h
+++ b/source/src/md2.h
@@ -87,7 +87,7 @@ struct md2 : vertmodel
{
if(filename) return true;
- FILE *file = fopen(path, "rb");
+ FILE *file = openfile(path, "rb");
if(!file) return false;
md2_header header;
@@ -210,12 +210,12 @@ struct md2 : vertmodel
parts.add(&mdl);
mdl.model = this;
mdl.index = 0;
- char *pname = parentdir(loadname);
+ const char *pname = parentdir(loadname);
s_sprintfd(name1)("packages/models/%s/tris.md2", loadname);
if(!mdl.load(path(name1)))
{
s_sprintf(name1)("packages/models/%s/tris.md2", pname); // try md2 in parent folder (vert sharing)
- if(!mdl.load(path(name1))) { delete[] pname; return false; }
+ if(!mdl.load(path(name1))) return false;
}
Texture *skin;
loadskin(loadname, pname, skin);
@@ -228,7 +228,6 @@ struct md2 : vertmodel
s_sprintf(name2)("packages/models/%s/md2.cfg", pname);
execfile(name2);
}
- delete[] pname;
loadingmd2 = 0;
loopv(parts) parts[i]->scaleverts(scale/16.0f, vec(translate.x, -translate.y, translate.z));
return loaded = true;
diff --git a/source/src/md3.h b/source/src/md3.h
index bfa1291d..88490e2c 100644
--- a/source/src/md3.h
+++ b/source/src/md3.h
@@ -53,7 +53,7 @@ struct md3 : vertmodel
{
if(filename) return true;
- FILE *f = fopen(path, "rb");
+ FILE *f = openfile(path, "rb");
if(!f) return false;
md3header header;
fread(&header, sizeof(md3header), 1, f);
@@ -182,13 +182,12 @@ struct md3 : vertmodel
if(loaded) return true;
s_sprintf(md3dir)("packages/models/%s", loadname);
- char *pname = parentdir(loadname);
+ const char *pname = parentdir(loadname);
s_sprintfd(cfgname)("packages/models/%s/md3.cfg", loadname);
loadingmd3 = this;
if(execfile(cfgname) && parts.length()) // configured md3, will call the md3* commands below
{
- delete[] pname;
loadingmd3 = NULL;
if(parts.empty()) return false;
loopv(parts) if(!parts[i]->filename) return false;
@@ -204,7 +203,7 @@ struct md3 : vertmodel
if(!mdl.load(path(name1)))
{
s_sprintf(name1)("packages/models/%s/tris.md3", pname); // try md3 in parent folder (vert sharing)
- if(!mdl.load(path(name1))) { delete[] pname; return false; };
+ if(!mdl.load(path(name1))) return false;
};
Texture *skin;
loadskin(loadname, pname, skin);
diff --git a/source/src/protos.h b/source/src/protos.h
index 70443219..c4ec500a 100644
--- a/source/src/protos.h
+++ b/source/src/protos.h
@@ -190,6 +190,7 @@ extern int isoccluded(float vx, float vy, float cx, float cy, float csize);
// main
extern void keyrepeat(bool on);
+extern bool initwarning();
// rendertext
extern bool setfont(char *name);
diff --git a/source/src/savegamedemo.cpp b/source/src/savegamedemo.cpp
index 4382f1ab..25447af8 100644
--- a/source/src/savegamedemo.cpp
+++ b/source/src/savegamedemo.cpp
@@ -44,7 +44,7 @@ void stopifrecording() { if(demorecording) stop(); }
void savestate(char *fn)
{
stop();
- f = gzopen(fn, "wb9");
+ f = opengzfile(fn, "wb9");
if(!f) { conoutf("could not write %s", fn); return; }
gzwrite(f, (void *)"CUBESAVE", 8);
gzputc(f, SDL_BYTEORDER==SDL_LIL_ENDIAN ? 1 : 0);
@@ -73,7 +73,7 @@ void loadstate(char *fn)
{
stop();
if(multiplayer()) return;
- f = gzopen(fn, "rb9");
+ f = opengzfile(fn, "rb9");
if(!f) { conoutf("could not open %s", fn); return; }
string buf;
diff --git a/source/src/server.cpp b/source/src/server.cpp
index 0906174f..0e0afcd4 100644
--- a/source/src/server.cpp
+++ b/source/src/server.cpp
@@ -1305,8 +1305,8 @@ int main(int argc, char **argv)
case 'p': passwd = a; break;
case 'c': maxcl = atoi(a); break;
case 'r': maprot = a; break;
- case 'x' : adminpasswd = a; break;
- case 'o' : srvmsg = a; break;
+ case 'x': adminpasswd = a; break;
+ case 'o': srvmsg = a; break;
case 'k': scthreshold = atoi(a); break;
default: printf("WARNING: unknown commandline option\n");
}
diff --git a/source/src/serverbrowser.cpp b/source/src/serverbrowser.cpp
index b4a38837..d6c81a8c 100644
--- a/source/src/serverbrowser.cpp
+++ b/source/src/serverbrowser.cpp
@@ -473,7 +473,7 @@ COMMAND(updatefrommaster, ARG_NONE);
void writeservercfg()
{
- FILE *f = fopen("config/servers.cfg", "w");
+ FILE *f = openfile("config/servers.cfg", "w");
if(!f) return;
fprintf(f, "// servers connected to are added here automatically\n\n");
loopvrev(servers) fprintf(f, "addserver %s\n", servers[i].name);
diff --git a/source/src/sound.cpp b/source/src/sound.cpp
index 474c6f8f..3feef2e2 100644
--- a/source/src/sound.cpp
+++ b/source/src/sound.cpp
@@ -7,14 +7,11 @@
#define USE_MIXER
//#endif
-VARP(soundvol, 0, 150, 255);
bool nosound = true;
#define MAXCHAN 32
#define SOUNDFREQ 22050
-struct soundloc { vec loc; bool inuse; } soundlocs[MAXCHAN];
-
#ifdef USE_MIXER
#include "SDL_mixer.h"
#define MAXVOL MIX_MAX_VOLUME
@@ -25,14 +22,27 @@ struct soundloc { vec loc; bool inuse; } soundlocs[MAXCHAN];
#define MAXVOL 255
FMUSIC_MODULE *mod = NULL;
FSOUND_STREAM *stream = NULL;
+ int musicchan;
#endif
-int musicvol = 125;
+struct sample
+{
+ char *name;
+ #ifdef USE_MIXER
+ Mix_Chunk *sound;
+ #else
+ FSOUND_SAMPLE *sound;
+ #endif
-void setmusicvol(int vol)
+ sample(const char *name) : name(newstring(name)), sound(NULL) {}
+ ~sample() { DELETEA(name); }
+};
+
+struct soundloc { vec loc; bool inuse; };
+vector soundlocs;
+
+void setmusicvol(int musicvol)
{
- if(musicvol > 255 || musicvol < 0) return;
- musicvol = vol;
if(nosound) return;
#ifdef USE_MIXER
if(mod) Mix_VolumeMusic((musicvol*MAXVOL)/255);
@@ -42,7 +52,8 @@ void setmusicvol(int vol)
#endif
}
-COMMANDN(musicvol, setmusicvol, ARG_1INT);
+VARP(soundvol, 0, 150, 255);
+VARFP(musicvol, 0, 128, 255, setmusicvol(musicvol));
void stopsound()
{
@@ -66,21 +77,22 @@ void stopsound()
}
}
-VAR(soundbufferlen, 128, 1024, 4096);
+VARF(soundchans, 0, 32, 128, initwarning());
+VARF(soundfreq, 0, MIX_DEFAULT_FREQUENCY, 44100, initwarning());
+VARF(soundbufferlen, 128, 1024, 4096, initwarning());
void initsound()
{
- memset(soundlocs, 0, sizeof(soundloc)*MAXCHAN);
#ifdef USE_MIXER
- if(Mix_OpenAudio(SOUNDFREQ, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0)
+ if(Mix_OpenAudio(soundfreq, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0)
{
conoutf("sound init failed (SDL_mixer): %s", (size_t)Mix_GetError());
return;
}
- Mix_AllocateChannels(MAXCHAN);
+ Mix_AllocateChannels(soundchans);
#else
if(FSOUND_GetVersion()=0) { FSOUND_SetVolume(chan, (musicvol*MAXVOL)/255); FSOUND_SetPaused(chan, false); }
+ musicchan = FSOUND_Stream_Play(FSOUND_FREE, stream);
+ if(musicchan>=0) { FSOUND_SetVolume(musicchan, (musicvol*MAXVOL)/255); FSOUND_SetPaused(musicchan, false); }
}
#endif
else
@@ -124,20 +137,12 @@ void music(char *name)
COMMAND(music, ARG_1STR);
-
-#ifdef USE_MIXER
-vector samples;
-#else
-vector samples;
-#endif
-
-cvector snames;
+vector samples;
int registersound(char *name)
{
- loopv(snames) if(strcmp(snames[i], name)==0) return i;
- snames.add(newstring(name));
- samples.add(NULL);
+ loopv(samples) if(!strcmp(samples[i]->name, name)) return i;
+ samples.add(new sample(name));
return samples.length()-1;
}
@@ -183,7 +188,7 @@ void updatechanvol(int chan, vec *loc)
void newsoundloc(int chan, vec *loc)
{
- ASSERT(chan>=0 && chan= soundlocs.length()) soundlocs.add().inuse = false;
soundlocs[chan].loc = *loc;
soundlocs[chan].inuse = true;
}
@@ -191,7 +196,7 @@ void newsoundloc(int chan, vec *loc)
void updatevol()
{
if(nosound) return;
- loopi(MAXCHAN) if(soundlocs[i].inuse)
+ loopv(soundlocs) if(soundlocs[i].inuse)
{
#ifdef USE_MIXER
if(Mix_Playing(i))
@@ -210,36 +215,44 @@ void playsoundc(int n)
playsound(n);
}
-int soundsatonce = 0, lastsoundmillis = 0;
-
void playsound(int n, vec *loc)
{
if(nosound) return;
if(!soundvol) return;
if(n==S_NULL) return;
if(m_botmode) BotManager.LetBotsHear(n, loc);
+
+ static int soundsatonce = 0, lastsoundmillis = 0;
if(lastmillis==lastsoundmillis) soundsatonce++; else soundsatonce = 1;
lastsoundmillis = lastmillis;
if(soundsatonce>5) return; // avoid bursts of sounds with heavy packetloss and in sp
- if(n<0 || n>=samples.length()) { conoutf("unregistered sound: %d", n); return; }
- if(!samples[n])
+ if(!samples.inrange(n)) { conoutf("unregistered sound: %d", n); return; }
+
+ sample &s = *samples[n];
+ if(!s.sound)
{
- s_sprintfd(buf)("packages/audio/sounds/%s.wav", snames[n]);
+ s_sprintfd(buf)("packages/audio/sounds/%s", s.name);
- #ifdef USE_MIXER
- samples[n] = Mix_LoadWAV(path(buf));
- #else
- samples[n] = FSOUND_Sample_Load(n, path(buf), FSOUND_LOOP_OFF, 0, 0);
- #endif
+ loopi(2)
+ {
+ if(i) s_strcat(buf, ".wav");
+ const char *file = findfile(path(buf), "rb");
+ #ifdef USE_MIXER
+ s.sound = Mix_LoadWAV(file);
+ #else
+ s.sound = FSOUND_Sample_Load(n, file, FSOUND_LOOP_OFF, 0, 0);
+ #endif
+ if(s.sound) break;
+ }
- if(!samples[n]) { conoutf("failed to load sample: %s", buf); return; }
+ if(!s.sound) { conoutf("failed to load sample: %s", buf); return; }
}
#ifdef USE_MIXER
- int chan = Mix_PlayChannel(-1, samples[n], 0);
+ int chan = Mix_PlayChannel(-1, s.sound, 0);
#else
- int chan = FSOUND_PlaySoundEx(FSOUND_FREE, samples[n], NULL, true);
+ int chan = FSOUND_PlaySoundEx(FSOUND_FREE, s.sound, NULL, true);
#endif
if(chan<0) return;
if(loc) newsoundloc(chan, loc);
diff --git a/source/src/texture.cpp b/source/src/texture.cpp
index 74ecb020..a61cdc7c 100644
--- a/source/src/texture.cpp
+++ b/source/src/texture.cpp
@@ -36,7 +36,7 @@ void createtexture(int tnum, int w, int h, void *pixels, int clamp, bool mipmap,
GLuint loadsurface(const char *texname, int &xs, int &ys, int clamp)
{
- SDL_Surface *s = IMG_Load(texname);
+ SDL_Surface *s = IMG_Load(findfile(texname, "rb"));
if(!s) { conoutf("couldn't load texture %s", texname); return 0; }
if(s->format->BitsPerPixel!=24 && s->format->BitsPerPixel!=32)
{
diff --git a/source/src/tools.cpp b/source/src/tools.cpp
index 290f152a..67d2c722 100644
--- a/source/src/tools.cpp
+++ b/source/src/tools.cpp
@@ -2,9 +2,16 @@
#include "cube.h"
-//////////////////////////// pool ///////////////////////////
+///////////////////////// file system ///////////////////////
-///////////////////////// misc tools ///////////////////////
+#ifndef WIN32
+#include
+#include
+#include
+#endif
+
+string homedir = "";
+vector packagedirs;
char *path(char *s)
{
@@ -29,26 +36,122 @@ char *path(char *s)
return s;
}
-char *parentdir(char *directory)
+const char *parentdir(const char *directory)
{
- char *p = strrchr(directory, '/');
+ const char *p = strrchr(directory, '/');
+ if(!p) p = strrchr(directory, '\\');
if(!p) p = directory;
+ static string parent;
size_t len = p-directory+1;
- char *parent = new char[len];
s_strncpy(parent, directory, len);
return parent;
}
-char *loadfile(char *fn, int *size)
+bool fileexists(const char *path, const char *mode)
{
- FILE *f = fopen(fn, "rb");
+ 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)
+{
+ int len = strlen(path);
+ if(path[len-1]==PATHDIV)
+ {
+ static string strip;
+ path = s_strncpy(strip, path, len);
+ }
+#ifdef WIN32
+ return CreateDirectory(path, NULL)!=0;
+#else
+ return mkdir(path, 0777)==0;
+#endif
+}
+
+static void fixdir(char *dir)
+{
+ path(dir);
+ int len = strlen(dir);
+ if(dir[len-1]!=PATHDIV)
+ {
+ dir[len] = PATHDIV;
+ dir[len+1] = '\0';
+ }
+}
+
+void sethomedir(const char *dir)
+{
+ fixdir(s_strcpy(homedir, dir));
+}
+
+void addpackagedir(const char *dir)
+{
+ fixdir(packagedirs.add(newstringbuf(dir)));
+}
+
+const char *findfile(const char *filename, const char *mode)
+{
+ static string s;
+ if(homedir[0])
+ {
+ s_sprintf(s)("%s%s", homedir, filename);
+ if(fileexists(s, mode)) return s;
+ if(mode[0]=='w' || mode[0]=='a')
+ {
+ string dirs;
+ s_strcpy(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)
+ {
+ s_sprintf(s)("%s%s", packagedirs[i], filename);
+ if(fileexists(s, mode)) return s;
+ }
+ return filename;
+}
+
+FILE *openfile(const char *filename, const char *mode)
+{
+ const char *found = findfile(filename, mode);
+ if(!found) return NULL;
+ return fopen(found, mode);
+}
+
+#ifndef STANDALONE
+gzFile opengzfile(const char *filename, const char *mode)
+{
+ const char *found = findfile(filename, mode);
+ if(!found) return NULL;
+ return gzopen(found, mode);
+}
+#endif
+
+char *loadfile(const char *fn, int *size)
+{
+ FILE *f = openfile(fn, "rb");
if(!f) return NULL;
fseek(f, 0, SEEK_END);
int len = ftell(f);
- if(len<=0) { fclose(f); return NULL; };
+ if(len<=0) { fclose(f); return NULL; }
fseek(f, 0, SEEK_SET);
char *buf = new char[len+1];
- if(!buf) { fclose(f); return NULL; };
+ if(!buf) { fclose(f); return NULL; }
buf[len] = 0;
size_t rlen = fread(buf, 1, len, f);
fclose(f);
@@ -61,6 +164,8 @@ char *loadfile(char *fn, int *size)
return buf;
}
+///////////////////////// misc tools ///////////////////////
+
bool cmpb(void *b, int n, enet_uint32 c)
{
ENetBuffer buf;
diff --git a/source/src/tools.h b/source/src/tools.h
index 77c1dc09..62eb238d 100644
--- a/source/src/tools.h
+++ b/source/src/tools.h
@@ -101,15 +101,21 @@ struct s_sprintf_f
template void _swap(T &a, T &b) { T t = a; a = b; b = t; }
-
extern char *path(char *s);
-extern char *parentdir(char *directory);
-extern char *loadfile(char *fn, int *size);
+extern const char *parentdir(const char *directory);
+extern bool fileexists(const char *path, const char *mode);
+extern bool createdir(const char *path);
+extern void sethomedir(const char *dir);
+extern void addpackagedir(const char *dir);
+extern const char *findfile(const char *filename, const char *mode);
+extern FILE *openfile(const char *filename, const char *mode);
+#ifndef STANDALONE
+extern gzFile opengzfile(const char *filename, const char *mode);
+#endif
+extern char *loadfile(const char *fn, int *size);
extern bool cmpb(void *b, int n, enet_uint32 c);
extern bool cmpf(char *fn, enet_uint32 c);
extern void endianswap(void *, int, int);
-extern void seedMT(uint seed);
-extern uint randomMT(void);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define CMPB(b, c) (true)
@@ -370,22 +376,22 @@ template struct hashtable
enum { CHUNKSIZE = 16 };
struct chain { T data; K key; chain *next; };
- struct chainchunk { chain chunks[CHUNKSIZE]; chainchunk *next; };
+ struct chainchunk { chain chains[CHUNKSIZE]; chainchunk *next; };
int size;
int numelems;
chain **table;
chain *enumc;
- int chunkremain;
- chainchunk *lastchunk;
+ chainchunk *chunks;
+ chain *unused;
hashtable(int size = 1<<10)
: size(size)
{
numelems = 0;
- chunkremain = 0;
- lastchunk = NULL;
+ chunks = NULL;
+ unused = NULL;
table = new chain *[size];
loopi(size) table[i] = NULL;
}
@@ -397,16 +403,19 @@ template struct hashtable
chain *insert(const K &key, uint h)
{
- if(!chunkremain)
+ if(!unused)
{
chainchunk *chunk = new chainchunk;
- chunk->next = lastchunk;
- lastchunk = chunk;
- chunkremain = CHUNKSIZE;
+ chunk->next = chunks;
+ chunks = chunk;
+ loopi(CHUNKSIZE-1) chunk->chains[i].next = &chunk->chains[i+1];
+ chunk->chains[CHUNKSIZE-1].next = unused;
+ unused = chunk->chains;
}
- chain *c = &lastchunk->chunks[--chunkremain];
+ chain *c = unused;
+ unused = unused->next;
c->key = key;
- c->next = table[h];
+ c->next = table[h];
table[h] = c;
numelems++;
return c;
@@ -436,24 +445,36 @@ template struct hashtable
return find(key, true)->data;
}
+ bool remove(const K &key)
+ {
+ uint h = hthash(key)&(size-1);
+ for(chain **p = &table[h], *c = table[h]; c; p = &c->next, c = c->next)
+ {
+ if(htcmp(key, c->key))
+ {
+ *p = c->next;
+ c->data.~T();
+ c->key.~K();
+ new (&c->data) T();
+ new (&c->key) K();
+ c->next = unused;
+ unused = c->next;
+ numelems--;
+ return true;
+ }
+ }
+ return false;
+ }
+
void clear()
{
- loopi(size)
- {
- /*
- for(chain *c = table[i], *next; c; c = next)
- {
- next = c->next;
- delete c;
- }*/
- table[i] = NULL;
- }
+ loopi(size) table[i] = NULL;
numelems = 0;
- chunkremain = 0;
- for(chainchunk *chunk; lastchunk; lastchunk = chunk)
+ unused = NULL;
+ for(chainchunk *nextchunk; chunks; chunks = nextchunk)
{
- chunk = lastchunk->next;
- delete lastchunk;
+ nextchunk = chunks->next;
+ delete chunks;
}
}
};
diff --git a/source/src/vertmodel.h b/source/src/vertmodel.h
index cdbf6eac..40bc388e 100644
--- a/source/src/vertmodel.h
+++ b/source/src/vertmodel.h
@@ -431,11 +431,11 @@ struct vertmodel : model
shadows = new GLuint[numframes];
glGenTextures(numframes, shadows);
- extern int scr_w, scr_h;
+ extern SDL_Surface *screen;
int aasize = 1<<(dynshadowsize + aadynshadow);
- while(aasize > scr_w || aasize > scr_h) aasize /= 2;
+ while(aasize > screen->w || aasize > screen->h) aasize /= 2;
- gzFile f = filename ? gzopen(filename, "wb9") : NULL;
+ gzFile f = filename ? opengzfile(filename, "wb9") : NULL;
if(f)
{
shadowheader hdr;
@@ -469,14 +469,14 @@ struct vertmodel : model
endgenshadow();
glEnable(GL_FOG);
- glViewport(0, 0, scr_w, scr_h);
+ glViewport(0, 0, screen->w, screen->h);
if(f) gzclose(f);
}
bool loadshadows(const char *filename)
{
- gzFile f = gzopen(filename, "rb9");
+ gzFile f = opengzfile(filename, "rb9");
if(!f) return false;
shadowheader hdr;
if(gzread(f, &hdr, sizeof(shadowheader))!=sizeof(shadowheader)) { gzclose(f); return false; }
diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp
index 903ca427..5343a06f 100644
--- a/source/src/worldio.cpp
+++ b/source/src/worldio.cpp
@@ -106,7 +106,7 @@ void writemap(char *mname, int msize, uchar *mdata)
{
setnames(mname);
backup(cgzname, bakname);
- FILE *f = fopen(cgzname, "wb");
+ FILE *f = openfile(cgzname, "wb");
if(!f) { conoutf("could not write map to %s", cgzname); return; }
fwrite(mdata, 1, msize, f);
fclose(f);
@@ -133,7 +133,7 @@ void save_world(char *mname)
toptimize();
setnames(mname);
backup(cgzname, bakname);
- gzFile f = gzopen(cgzname, "wb9");
+ gzFile f = opengzfile(cgzname, "wb9");
if(!f) { conoutf("could not write map to %s", cgzname); return; }
hdr.version = MAPVERSION;
hdr.numents = 0;
@@ -214,7 +214,7 @@ void load_world(char *mname) // still supports all map formats that have
cleardlights();
pruneundos();
setnames(mname);
- gzFile f = gzopen(cgzname, "rb9");
+ gzFile f = opengzfile(cgzname, "rb9");
if(!f) { conoutf("could not read map %s", cgzname); return; }
loadingscreen();
gzread(f, &hdr, sizeof(header)-sizeof(int)*16);