'selectsaved' extension

Instead of a menu listing
 a - hero1
 b - hero2
 n - New game
 q - Quit
show
 a - hero1-role1-race1-gend1-algn1
 b - hero2-role2-race2-gend2-algn2
 n - New game
 q - Quit
or
 a - - hero1-role1-race1-gend1-algn1
 b - X hero2-role2-race2-gend2-algn2
 c - D wizard-role3-race3-gend3-algn3
 n - New game
 q - Quit
when any game in the list wasn't saved during normal play.  (Those
are sorted by character name; the playmode is just coincidence.)

The dash for 'normal' doesn't look great but -/X/D are codes used in
entries written to paniclog.  The whole playmode prefix doesn't look
particularly good but I suspect that most players relying on restore
via menu won't see it.

It should work when the character name has dashes in it but that
hasn't been properly tested.

The gender and alignment suffices reflect their value at the time of
save rather than at the start of the game.  That might be considered
a bug but it was easiest.

Increments EDITLEVEL; existing save and bones files are invalidated.
This commit is contained in:
PatR
2024-10-10 23:14:25 -07:00
parent ac3031932f
commit 1f36b98b8f
8 changed files with 110 additions and 76 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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 */

View File

@@ -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.

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;