From 0e7e7ba3b64e6bccfcca85d4120f2f60f1fe05c3 Mon Sep 17 00:00:00 2001 From: James Haley Date: Thu, 10 Feb 2011 20:45:00 +0000 Subject: Restarted work on hub save code. Brought in multiple filepath handling routines from Eternity. Subversion-branch: /branches/strife-branch Subversion-revision: 2252 --- src/strife/m_saves.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 318 insertions(+), 5 deletions(-) (limited to 'src/strife/m_saves.c') diff --git a/src/strife/m_saves.c b/src/strife/m_saves.c index f845598e..6b99a7a8 100644 --- a/src/strife/m_saves.c +++ b/src/strife/m_saves.c @@ -37,14 +37,28 @@ #else #error Need an include for dirent.h! #endif +#include +#include +#include #include "z_zone.h" #include "i_system.h" #include "d_player.h" #include "deh_str.h" +#include "doomstat.h" #include "m_misc.h" +#include "m_saves.h" #include "p_dialog.h" +// +// File Paths +// +// Strife maintains multiple file paths related to savegames. +// +char *savepath; +char *savepath2; +char *loadpath; + // // ClearTmp // @@ -52,6 +66,32 @@ // void ClearTmp(void) { + DIR *sp2dir = NULL; + struct dirent *f = NULL; + + if(savepath2 == NULL) + I_Error("you fucked up savedir man!"); + + if(!(sp2dir = opendir(savepath2))) + I_Error("ClearTmp: Couldn't open dir %s", savepath2); + + while((f = readdir(sp2dir))) + { + char *filepath = NULL; + + // haleyjd: skip "." and ".." without assuming they're the + // first two entries like the original code did. + if(!strcmp(f->d_name, ".") || !strcmp(f->d_name, "..")) + continue; + + // haleyjd: use M_SafeFilePath, not sprintf + filepath = M_SafeFilePath(savepath2, f->d_name); + remove(filepath); + + Z_Free(filepath); + } + + closedir(sp2dir); } // @@ -61,16 +101,50 @@ void ClearTmp(void) // void ClearSlot(void) { + if(savepath == NULL) + I_Error("userdir is fucked up man!"); + + // STRIFE-TODO } // // FromCurr // -// Moving files from one directory to another... -// STRIFE-TODO: figure out exactly what this is for. +// Copying files from savepath2 to savepath // void FromCurr(void) { + DIR *sp2dir = NULL; + struct dirent *f = NULL; + + if(!(sp2dir = opendir(savepath2))) + I_Error("FromCurr: Couldn't open dir %s", savepath2); + + while((f = readdir(sp2dir))) + { + byte *filebuffer = NULL; + int filelen = 0; + char *srcfilename = NULL; + char *dstfilename = NULL; + + // haleyjd: skip "." and ".." without assuming they're the + // first two entries like the original code did. + if(!strcmp(f->d_name, ".") || !strcmp(f->d_name, "..")) + continue; + + // haleyjd: use M_SafeFilePath, NOT sprintf. + srcfilename = M_SafeFilePath(savepath2, f->d_name); + dstfilename = M_SafeFilePath(savepath, f->d_name); + + filelen = M_ReadFile(srcfilename, &filebuffer); + M_WriteFile(dstfilename, filebuffer, filelen); + + Z_Free(filebuffer); + Z_Free(srcfilename); + Z_Free(dstfilename); + } + + closedir(sp2dir); } // @@ -80,6 +154,7 @@ void FromCurr(void) // void sub_1B2F4(void) { + // STRIFE-TODO } // @@ -89,6 +164,27 @@ void sub_1B2F4(void) // void M_SaveMoveMapToHere(void) { + char *mapsave = NULL; + char *heresave = NULL; + char tmpnum[33]; + + // haleyjd: no itoa available... + memset(tmpnum, 0, sizeof(tmpnum)); + sprintf(tmpnum, "%d", gamemap); + + // haleyjd: use M_SafeFilePath, not sprintf + mapsave = M_SafeFilePath(savepath, tmpnum); + heresave = M_SafeFilePath(savepath, "here"); + + // haleyjd: use M_FileExists, not access + if(M_FileExists(mapsave)) + { + remove(heresave); + rename(mapsave, heresave); + } + + Z_Free(mapsave); + Z_Free(heresave); } // @@ -98,6 +194,25 @@ void M_SaveMoveMapToHere(void) // void M_SaveMoveHereToMap(void) { + char *mapsave = NULL; + char *heresave = NULL; + char tmpnum[33]; + + // haleyjd: no itoa available... + memset(tmpnum, 0, sizeof(tmpnum)); + sprintf(tmpnum, "%d", gamemap); + + mapsave = M_SafeFilePath(savepath2, tmpnum); + heresave = M_SafeFilePath(savepath2, "here"); + + if(M_FileExists(heresave)) + { + remove(mapsave); + rename(heresave, mapsave); + } + + Z_Free(mapsave); + Z_Free(heresave); } // @@ -107,10 +222,15 @@ void M_SaveMoveHereToMap(void) // boolean M_SaveMisObj(const char *path) { - char destpath[100]; // WARNING: not large enough for modern file paths! + boolean result; + char *destpath = NULL; + + // haleyjd 20110210: use M_SafeFilePath, not sprintf + destpath = M_SafeFilePath(path, "mis_obj"); + result = M_WriteFile(destpath, mission_objective, OBJECTIVE_LEN); - DEH_snprintf(destpath, sizeof(destpath), "%smis_obj", path); - return M_WriteFile(destpath, mission_objective, OBJECTIVE_LEN); + Z_Free(destpath); + return result; } // @@ -120,6 +240,199 @@ boolean M_SaveMisObj(const char *path) // void M_ReadMisObj(void) { + FILE *f = NULL; + char *srcpath = NULL; + + // haleyjd: use M_SafeFilePath, not sprintf + srcpath = M_SafeFilePath(savepath2, "mis_obj"); + + if((f = fopen(srcpath, "rb"))) + { + fread(mission_objective, 1, 300, f); + fclose(f); + } + + Z_Free(srcpath); +} + +//============================================================================= +// +// Original Routines +// +// haleyjd - None of the below code is derived from Strife itself, but has been +// adapted or created in order to provide secure, portable filepath handling +// for the purposes of savegame support. This is partially needed to allow for +// differences in Choco due to it being multiplatform. The rest exists because +// I cannot stand programming in an impoverished ANSI C environment that +// calls sprintf on fixed-size buffers. :P +// + +// +// M_Calloc +// +// haleyjd 20110210 - original routine +// Because Choco doesn't have Z_Calloc O_o +// +void *M_Calloc(size_t n1, size_t n2) +{ + return (n1 *= n2) ? memset(Z_Malloc(n1, PU_STATIC, NULL), 0, n1) : NULL; +} + +// +// M_StringAlloc +// +// haleyjd: This routine takes any number of strings and a number of extra +// characters, calculates their combined length, and calls Z_Alloca to create +// a temporary buffer of that size. This is extremely useful for allocation of +// file paths, and is used extensively in d_main.c. The pointer returned is +// to a temporary Z_Alloca buffer, which lives until the next main loop +// iteration, so don't cache it. Note that this idiom is not possible with the +// normal non-standard alloca function, which allocates stack space. +// +// [STRIFE] - haleyjd 20110210 +// This routine is taken from the Eternity Engine and adapted to do without +// Z_Alloca. I need secure string concatenation for filepath handling. The +// only difference from use in EE is that the pointer returned in *str must +// be manually freed. +// +int M_StringAlloc(char **str, int numstrs, size_t extra, const char *str1, ...) +{ + va_list args; + size_t len = extra; + + if(numstrs < 1) + I_Error("M_StringAlloc: invalid input\n"); + + len += strlen(str1); + + --numstrs; + + if(numstrs != 0) + { + va_start(args, str1); + + while(numstrs != 0) + { + const char *argstr = va_arg(args, const char *); + + len += strlen(argstr); + + --numstrs; + } + + va_end(args); + } + + ++len; + + *str = (char *)(M_Calloc(1, len)); + + return len; +} + +// +// M_NormalizeSlashes +// +// Remove trailing slashes, translate backslashes to slashes +// The string to normalize is passed and returned in str +// +// killough 11/98: rewritten +// +// [STRIFE] - haleyjd 20110210: Borrowed from Eternity and adapted to respect +// the DIR_SEPARATOR define used by Choco Doom. This routine originated in +// BOOM. +// +void M_NormalizeSlashes(char *str) +{ + char *p; + + // Convert all slashes/backslashes to DIR_SEPARATOR + for(p = str; *p; p++) + { + if((*p == '/' || *p == '\\') && *p != DIR_SEPARATOR) + *p = DIR_SEPARATOR; + } + + // Remove trailing slashes + while(p > str && *--p == DIR_SEPARATOR) + *p = 0; + + // Collapse multiple slashes + for(p = str; (*str++ = *p); ) + if(*p++ == DIR_SEPARATOR) + while(*p == DIR_SEPARATOR) + p++; +} + +// +// M_SafeFilePath +// +// haleyjd 20110210 - original routine. +// This routine performs safe, portable concatenation of a base file path +// with another path component or file name. The returned string is Z_Malloc'd +// and should be freed when it has exhausted its usefulness. +// +char *M_SafeFilePath(const char *basepath, const char *newcomponent) +{ + int newstrlen = 0; + char *newstr = NULL; + + // Always throw in a slash. M_NormalizeSlashes will remove it in the case + // that either basepath or newcomponent includes a redundant slash at the + // end or beginning respectively. + newstrlen = M_StringAlloc(&newstr, 3, 1, basepath, "/", newcomponent); + snprintf(newstr, newstrlen, "%s/%s", basepath, newcomponent); + M_NormalizeSlashes(newstr); + + return newstr; +} + +// +// M_GetFilePath +// +// haleyjd: STRIFE-FIXME: Temporary? +// Code borrowed from Eternity, and modified to return separator char +// +char M_GetFilePath(const char *fn, char *dest, size_t len) +{ + boolean found_slash = false; + char *p; + char sepchar = '\0'; + + memset(dest, 0, len); + + p = dest + len - 1; + + strncpy(dest, fn, len); + + while(p >= dest) + { + if(*p == '/' || *p == '\\') + { + sepchar = *p; + found_slash = true; // mark that the path ended with a slash + *p = '\0'; + break; + } + *p = '\0'; + p--; + } + + // haleyjd: in the case that no slash was ever found, yet the + // path string is empty, we are dealing with a file local to the + // working directory. The proper path to return for such a string is + // not "", but ".", since the format strings add a slash now. When + // the string is empty but a slash WAS found, we really do want to + // return the empty string, since the path is relative to the root. + if(!found_slash && *dest == '\0') + *dest = '.'; + + // if a separator is not found, default to forward, because Windows + // supports that too. + if(sepchar == '\0') + sepchar = '/'; + + return sepchar; } // EOF -- cgit v1.2.3