diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index b30fbb712..13910d97b 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2710,6 +2710,12 @@ livelog/#chronicle for saving-grace: if saving-grace prevents hero's death, enlightenment/attribute disclosure for saving-grace: include a line for have or haven't been saved (game in progress) or did or didn't get saved (game over) via saving-grace +'selectsaved' lists "name-role-race-gend-algn" instead of just "name" in the + menu of save files available to be restored +'selectsaved' prefixes "name-role-race-gend-algn" with "- " for normal play, + "X " for explore mode, or "D " for debug mode if any of the games + shown in its menu weren't saved during normal play; if they're all + normal play, the prefix is suppressed Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index 91fb732a5..046045e7c 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2618,7 +2618,7 @@ extern int dorecover(NHFILE *) NONNULLARG1; extern void restcemetery(NHFILE *, struct cemetery **) NONNULLARG12; extern void trickery(char *) NO_NNARGS; extern void getlev(NHFILE *, int, xint8) NONNULLARG1; -extern void get_plname_from_file(NHFILE *, char *) NONNULLARG12; +extern void get_plname_from_file(NHFILE *, char *, boolean) NONNULLARG12; #ifdef SELECTSAVED extern int restore_menu(winid); #endif diff --git a/include/global.h b/include/global.h index 0643ca13d..94e95370f 100644 --- a/include/global.h +++ b/include/global.h @@ -431,6 +431,8 @@ extern struct nomakedefs_s nomakedefs; #define PL_CSIZ 32 /* sizeof pl_character */ #define PL_FSIZ 32 /* fruit name */ #define PL_PSIZ 63 /* player-given names for pets, other monsters, objects */ +/* room for "name-role-race-gend-algn" plus 1 character playmode code */ +#define PL_NSIZ_PLUS (PL_NSIZ + 4 * (1 + 4) + 1) /* 53 */ #define MAXDUNGEON 16 /* current maximum number of dungeons */ #define MAXLEVEL 32 /* max number of levels in one dungeon */ diff --git a/include/patchlevel.h b/include/patchlevel.h index fb3d53212..1f9ae25b6 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 107 +#define EDITLEVEL 108 /* * Development status possibilities. diff --git a/src/files.c b/src/files.c index 2a98a2f5c..e4834e2ae 100644 --- a/src/files.c +++ b/src/files.c @@ -719,11 +719,7 @@ clearlocks(void) staticfn int QSORTCALLBACK strcmp_wrap(const void *p, const void *q) { -#if defined(UNIX) && defined(QT_GRAPHICS) - return strncasecmp(*(char **) p, *(char **) q, 16); -#else - return strncmpi(*(char **) p, *(char **) q, 16); -#endif + return strcmp(*(char **) p, *(char **) q); } #endif @@ -989,7 +985,7 @@ set_savefile_name(boolean regularize_it) if (strlen(gs.SAVEF) < (SAVESIZE - 1)) (void) strncat(gs.SAVEF, svp.plname, (SAVESIZE - strlen(gs.SAVEF))); #endif -#if defined(MICRO) && !defined(VMS) && !defined(WIN32) && !defined(MSDOS) +#if defined(MICRO) && !defined(WIN32) && !defined(MSDOS) if (strlen(gs.SAVEP) < (SAVESIZE - 1)) Strcpy(gs.SAVEF, gs.SAVEP); else @@ -1252,9 +1248,12 @@ check_panic_save(void) #if defined(SELECTSAVED) char * -plname_from_file(const char *filename, boolean without_wait_synch_per_file) +plname_from_file( + const char *filename, + boolean without_wait_synch_per_file) { - NHFILE *nhfp = (NHFILE *) 0; + NHFILE *nhfp; + unsigned ln; char *result = 0; Strcpy(gs.SAVEF, filename); @@ -1271,60 +1270,32 @@ plname_from_file(const char *filename, boolean without_wait_synch_per_file) nh_uncompress(gs.SAVEF); if ((nhfp = open_savefile()) != 0) { if (validate(nhfp, filename, without_wait_synch_per_file) == 0) { - char tplname[PL_NSIZ]; - - get_plname_from_file(nhfp, tplname); - result = dupstr(tplname); + /* room for "name+role+race+gend+algn X" where the space before + X is actually NUL and X is playmode: one of '-', 'X', or 'D' */ + ln = (unsigned) PL_NSIZ_PLUS; + result = memset((genericptr_t) alloc(ln), '\0', ln); + get_plname_from_file(nhfp, result, FALSE); } close_nhfile(nhfp); } nh_compress(gs.SAVEF); - - return result; -#if 0 -/* --------- obsolete - used to be ifndef STORE_PLNAME_IN_FILE ----*/ -#if defined(UNIX) && defined(QT_GRAPHICS) - /* Name not stored in save file, so we have to extract it from - the filename, which loses information - (eg. "/", "_", and "." characters are lost. */ - int k; - int uid; - char name[64]; /* more than PL_NSIZ */ -#ifdef COMPRESS_EXTENSION -#define EXTSTR COMPRESS_EXTENSION -#else -#define EXTSTR "" -#endif - - if (sscanf(filename, "%*[^/]/%d%63[^.]" EXTSTR, &uid, name) == 2) { -#undef EXTSTR - /* "_" most likely means " ", which certainly looks nicer */ - for (k = 0; name[k]; k++) - if (name[k] == '_') - name[k] = ' '; - return dupstr(name); - } else -#endif /* UNIX && QT_GRAPHICS */ - { - return 0; - } -/* --------- end of obsolete code ----*/ -#endif /* 0 - WAS STORE_PLNAME_IN_FILE*/ + return result; /* file's plname[]+playmode value */ } #endif /* defined(SELECTSAVED) */ #define SUPPRESS_WAITSYNCH_PERFILE TRUE #define ALLOW_WAITSYNCH_PERFILE FALSE +/* get list of saved games owned by current user */ char ** get_saved_games(void) { + char **result = NULL; #if defined(SELECTSAVED) #if defined(WIN32) || defined(UNIX) int n; #endif int j = 0; - char **result = 0; #ifdef WIN32 { @@ -1351,8 +1322,8 @@ get_saved_games(void) } if (n > 0) { - files = (char **) alloc((n + 1) * sizeof(char *)); /* at most */ - (void) memset((genericptr_t) files, 0, (n + 1) * sizeof(char *)); + files = (char **) alloc((n + 1) * sizeof (char *)); /* at most */ + (void) memset((genericptr_t) files, 0, (n + 1) * sizeof (char *)); if (findfirst((char *) fq_save)) { i = 0; do { @@ -1362,8 +1333,8 @@ get_saved_games(void) } if (n > 0) { - result = (char **) alloc((n + 1) * sizeof(char *)); /* at most */ - (void) memset((genericptr_t) result, 0, (n + 1) * sizeof(char *)); + result = (char **) alloc((n + 1) * sizeof (char *)); /* at most */ + (void) memset((genericptr_t) result, 0, (n + 1) * sizeof (char *)); for(i = 0; i < n; i++) { char *r; r = plname_from_file(files[i], SUPPRESS_WAITSYNCH_PERFILE); @@ -1390,7 +1361,7 @@ get_saved_games(void) if (count_failures) wait_synch(); } -#endif +#endif /* WIN32 */ #ifdef UNIX /* posixly correct version */ int myuid = getuid(); @@ -1409,7 +1380,7 @@ get_saved_games(void) (void) memset((genericptr_t) result, 0, (n + 1) * sizeof(char *)); for (i = 0, j = 0; i < n; i++) { int uid; - char name[64]; /* more than PL_NSIZ */ + char name[64]; /* more than PL_NSIZ+1 */ struct dirent *entry = readdir(dir); if (!entry) @@ -1430,7 +1401,7 @@ get_saved_games(void) closedir(dir); } } -#endif +#endif /* UNIX */ #ifdef VMS Strcpy(svp.plname, "*"); set_savefile_name(FALSE); @@ -1440,14 +1411,14 @@ get_saved_games(void) if (j > 0) { if (j > 1) qsort(result, j, sizeof (char *), strcmp_wrap); - result[j] = 0; - return result; + result[j] = (char *) NULL; } else if (result) { /* could happen if save files are obsolete */ free_saved_games(result); + result = (char **) NULL; } #endif /* SELECTSAVED */ - return 0; + return result; } #undef SUPPRESS_WAITSYNCH_PERFILE #undef ALLOW_WAITSYNCH_PERFILE @@ -1456,10 +1427,10 @@ void free_saved_games(char **saved) { if (saved) { - int i = 0; + int i; - while (saved[i]) - free((genericptr_t) saved[i++]); + for (i = 0; saved[i]; ++i) + free((genericptr_t) saved[i]); free((genericptr_t) saved); } } @@ -4260,7 +4231,7 @@ recover_savefile(void) int processed[256]; char savename[SAVESIZE], errbuf[BUFSZ], indicator; struct savefile_info sfi; - char tmpplbuf[PL_NSIZ]; + char tmpplbuf[PL_NSIZ_PLUS]; const char *savewrite_failure = (const char *) 0; for (lev = 0; lev < 256; lev++) @@ -4307,7 +4278,7 @@ recover_savefile(void) != sizeof version_data) || (read(gnhfp->fd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi) || (read(gnhfp->fd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz) - != sizeof pltmpsiz) || (pltmpsiz > PL_NSIZ) + != sizeof pltmpsiz) || (pltmpsiz > PL_NSIZ_PLUS) || (read(gnhfp->fd, (genericptr_t) &tmpplbuf, pltmpsiz) != pltmpsiz)) { raw_printf("\nError reading %s -- can't recover.\n", gl.lock); diff --git a/src/restore.c b/src/restore.c index 63c038124..434cd5aaa 100644 --- a/src/restore.c +++ b/src/restore.c @@ -740,7 +740,7 @@ dorecover(NHFILE *nhfp) /* suppress map display if some part of the code tries to update that */ program_state.restoring = REST_GSTATE; - get_plname_from_file(nhfp, svp.plname); + get_plname_from_file(nhfp, svp.plname, TRUE); getlev(nhfp, 0, (xint8) 0); if (!restgamestate(nhfp)) { NHFILE tnhfp; @@ -828,7 +828,7 @@ dorecover(NHFILE *nhfp) restoreinfo.mread_flags = 0; rewind_nhfile(nhfp); /* return to beginning of file */ (void) validate(nhfp, (char *) 0, FALSE); - get_plname_from_file(nhfp, svp.plname); + get_plname_from_file(nhfp, svp.plname, TRUE); /* not 0 nor REST_GSTATE nor REST_LEVELS */ program_state.restoring = REST_CURRENT_LEVEL; @@ -1245,15 +1245,33 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) program_state.in_getlev = FALSE; } +/* "name-role-race-gend-algn" occurs very early in a save file; sometimes we + want the whole thing, other times just "name" (for svp.plname[]) */ void -get_plname_from_file(NHFILE *nhfp, char *plbuf) +get_plname_from_file( + NHFILE *nhfp, + char *outbuf, /* size must be at least [PL_NSIZ_PLUS] even if name_only */ + boolean name_only) /* True: just name; False: name-role-race-gend-algn */ { + char plbuf[PL_NSIZ_PLUS]; int pltmpsiz = 0; + plbuf[0] = '\0'; if (nhfp->structlevel) { - (void) read(nhfp->fd, (genericptr_t) &pltmpsiz, sizeof(pltmpsiz)); + (void) read(nhfp->fd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz); + /* pltmpsiz should now be PL_NSIZ_PLUS */ (void) read(nhfp->fd, (genericptr_t) plbuf, pltmpsiz); + /* plbuf[PL_NSIZ_PLUS-2] should be '\0'; + plbuf[PL_NSIZ_PLUS-1] should be '-' or 'X' or 'D' */ } + /* "-race-role-gend-algn" is already present except that it has been + hidden by replacing the initial dash with NUL; if we want that + information, replace the NUL with a dash */ + if (!name_only) + *eos(plbuf) = '-'; + /* not simple strcpy(); playmode is in the last slot and could (probably + will) be preceded by NULs */ + (void) memcpy((genericptr_t) outbuf, (genericptr_t) plbuf, PL_NSIZ_PLUS); return; } @@ -1418,7 +1436,8 @@ restore_menu( { winid tmpwin; anything any; - char **saved; + char **saved, *next, mode, menutext[BUFSZ]; + boolean all_normal; menu_item *chosen_game = (menu_item *) 0; int k, clet, ch = 0; /* ch: 0 => new game */ int clr = NO_COLOR; @@ -1438,19 +1457,35 @@ restore_menu( add_menu_str(tmpwin, ""); } add_menu_str(tmpwin, "Select one of your saved games"); + /* if all the save files have a playmode of '-' then we'll just list + their character name-role-race-gend-algn values, but if any are + 'X' or 'D', we'll list playmode along with name-role-&c values + for every entry; first, figure out if they're all normal play */ + for (all_normal = TRUE, k = 0; all_normal && saved[k]; ++k) { + next = saved[k]; + mode = next[PL_NSIZ_PLUS - 1]; /* fixed last char, beyond '\0' */ + if (mode != '-') + all_normal = FALSE; + } for (k = 0; saved[k]; ++k) { any.a_int = k + 1; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, saved[k], MENU_ITEMFLAGS_NONE); + next = saved[k]; + mode = next[PL_NSIZ_PLUS - 1]; + if (all_normal) + Sprintf(menutext, "%.*s", PL_NSIZ_PLUS - 1, next); + else + Sprintf(menutext, "%c %.*s", mode, PL_NSIZ_PLUS - 1, next); + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, + menutext, MENU_ITEMFLAGS_SKIPMENUCOLORS); } clet = (k <= 'n' - 'a') ? 'n' : 0; /* new game */ any.a_int = -1; /* not >= 0 */ - add_menu(tmpwin, &nul_glyphinfo, &any, clet, 0, ATR_NONE, - clr, "Start a new character", MENU_ITEMFLAGS_NONE); + add_menu(tmpwin, &nul_glyphinfo, &any, clet, 0, ATR_NONE, clr, + "Start a new character", MENU_ITEMFLAGS_NONE); clet = (k + 1 <= 'q' - 'a') ? 'q' : 0; /* quit */ any.a_int = -2; - add_menu(tmpwin, &nul_glyphinfo, &any, clet, 0, ATR_NONE, - clr, "Never mind (quit)", MENU_ITEMFLAGS_SELECTED); + add_menu(tmpwin, &nul_glyphinfo, &any, clet, 0, ATR_NONE, clr, + "Never mind (quit)", MENU_ITEMFLAGS_SELECTED); /* no prompt on end_menu, as we've done our own at the top */ end_menu(tmpwin, (char *) 0); if (select_menu(tmpwin, PICK_ONE, &chosen_game) > 0) { diff --git a/src/save.c b/src/save.c index 0f194ba34..09a0dedc4 100644 --- a/src/save.c +++ b/src/save.c @@ -1071,16 +1071,35 @@ savelevchn(NHFILE *nhfp) svs.sp_levchn = 0; } +/* write "name-role-race-gend-algn" into save file for menu-based restore; + the first dash is actually stored as '\0' instead of '-' */ void store_plname_in_file(NHFILE *nhfp) { - int plsiztmp = PL_NSIZ; + char hero[PL_NSIZ_PLUS]; /* [PL_NSIZ + 4*4 + 1] */ + int plsiztmp = (int) sizeof hero; + + (void) memset((genericptr_t) hero, '\0', sizeof hero); + /* augment svp.plname[]; the gender and alignment values reflect those + in effect at time of saving rather than at start of game */ + Snprintf(hero, sizeof hero, "%s-%.3s-%.3s-%.3s-%.3s", + svp.plname, gu.urole.filecode, + gu.urace.filecode, genders[flags.female].filecode, + aligns[1 - u.ualign.type].filecode); + /* replace "-role-race..." with "\0role-race..." so that we can include + or exclude the role-&c suffix easily, without worrying about whether + plname contains any dashes; but don't rely on snprintf() for this */ + hero[strlen(svp.plname)] = '\0'; + /* insert playmode into final slot of hero[]; + 'D','X','-' are the same characters as are used for paniclog entries */ + assert(hero[PL_NSIZ_PLUS - 1 - 1] == '\0'); + hero[PL_NSIZ_PLUS - 1] = wizard ? 'D' : discover ? 'X' : '-'; if (nhfp->structlevel) { bufoff(nhfp->fd); /* bwrite() before bufon() uses plain write() */ bwrite(nhfp->fd, (genericptr_t) &plsiztmp, sizeof plsiztmp); - bwrite(nhfp->fd, (genericptr_t) svp.plname, plsiztmp); + bwrite(nhfp->fd, (genericptr_t) hero, plsiztmp); bufon(nhfp->fd); } return; diff --git a/sys/vms/vmsunix.c b/sys/vms/vmsunix.c index 69e5a3929..f2c1bb0df 100644 --- a/sys/vms/vmsunix.c +++ b/sys/vms/vmsunix.c @@ -603,8 +603,9 @@ vmscond lib$find_file_end(void **); /* collect a list of character names from all save files for this player */ int -vms_get_saved_games(const char *savetemplate, /* wildcarded save file name in native VMS format */ - char ***outarray) +vms_get_saved_games( + const char *savetemplate, /* wildcarded save name in native VMS format */ + char ***outarray) { struct dsc in, out; unsigned short l;