diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index d1a05d564..1c0682aff 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -5,7 +5,7 @@ .ds vr "NetHack 3.4 .ds f0 "\*(vr .ds f1 -.ds f2 "October 29, 2004 +.ds f2 "January 9, 2005 .mt A Guide to the Mazes of Menace (Guidebook for NetHack) @@ -2244,6 +2244,9 @@ when the hero reaches the scroll_margin. .lp scroll_margin NetHack should scroll the display when the hero or cursor is this number of cells away from the edge of the window. +.lp selectsaved +NetHack should display a menu of existing saved games for the player to +choose from at game startup, if it can. Not all ports support this option. .lp softkeyboard Display an onscreen keyboard. Handhelds are most likely to support this option. .lp splash_screen diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index a08aae20d..f21a70678 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -27,7 +27,7 @@ \begin{document} % % input file: guidebook.mn -% $Revision: 1.87 $ $Date: 2004/11/06 02:42:03 $ +% $Revision: 1.88 $ $Date: 2005/01/02 17:10:47 $ % %.ds h0 " %.ds h1 %.ds h2 \% @@ -40,7 +40,7 @@ %.au \author{Eric S. Raymond\\ (Extensively edited and expanded for 3.5)} -\date{October 29, 2004} +\date{January 9, 2005} \maketitle @@ -2759,6 +2759,10 @@ when the hero reaches the scroll\_margin. NetHack should scroll the display when the hero or cursor is this number of cells away from the edge of the window. %.lp +\item[\ib{selectsaved}] +NetHack should display a menu of existing saved games for the player to +choose from at game startup, if it can. Not all ports support this option. +%.lp \item[\ib{softkeyboard}] Display an onscreen keyboard. Handhelds are most likely to support this option. %.lp diff --git a/doc/fixes35.0 b/doc/fixes35.0 index def7cdfa6..4dba3a675 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -117,6 +117,7 @@ shapeshifted vampire will transform back to vampire form after you defeat it and continue to fight in its native form container lknown flag for locked/unlocked/broken awareness container cknown flag for container content awareness +plname is stored in the save file on all platforms now Platform- and/or Interface-Specific New Features @@ -126,6 +127,8 @@ win32gui: menu option to add/remove windows captions win32gui: support for saving/restoring message history win32gui: added menu options "Copy ASCII Screenshot To Clipboard" and "Save ASCII Screenshot To File" +win32tty: support for 'selectsaved' option for menu of existing save files + to choose from at game startup tty: add window port routines for saving/restoring message history diff --git a/doc/window.doc b/doc/window.doc index 7f8106cf3..a37449584 100644 --- a/doc/window.doc +++ b/doc/window.doc @@ -653,6 +653,7 @@ to support: | fullscreen | WC2_FULLSCREEN | wc2_fullscreen |boolean | | softkeyboard | WC2_SOFTKEYBOARD | wc2_softkeyboard |boolean | | wraptext | WC2_WRAPTEXT | wc2_wraptext |boolean | + | selectsaved | WC2_SELECTSAVED | wc2_selectsaved |boolean | +--------------------+--------------------+--------------------+--------+ align_message -- where to place message window (top, bottom, left, right) @@ -678,6 +679,7 @@ player_selection -- dialog or prompts for choosing character. popup_dialog -- port should pop up dialog boxes for input. preload_tiles -- port should preload tiles into memory. +saveselection -- if port can display a menu of the user's saved games do so. scroll_amount -- scroll this amount when scroll_margin is reached. scroll_margin -- port should scroll the display when the hero or cursor is this number of cells away from the edge of the window. diff --git a/include/extern.h b/include/extern.h index 26c91adaa..d35e1f5c0 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1748,6 +1748,7 @@ E void FDECL(inven_inuse, (BOOLEAN_P)); E int FDECL(dorecover, (int)); E void FDECL(trickery, (char *)); E void FDECL(getlev, (int,int,XCHAR_P,BOOLEAN_P)); +E void FDECL(get_plname_from_file, (int, char *)); E void NDECL(minit); E boolean FDECL(lookup_id_mapping, (unsigned, unsigned *)); #ifdef ZEROCOMP @@ -1834,6 +1835,7 @@ E void FDECL(bflush, (int)); E void FDECL(bwrite, (int,genericptr_t,unsigned int)); E void FDECL(bclose, (int)); E void FDECL(savefruitchn, (int,int)); +E void FDECL(store_plname_in_file, (int)); E void NDECL(free_dungeons); E void NDECL(freedynamicdata); diff --git a/include/flag.h b/include/flag.h index 01f77d58a..465d0295a 100644 --- a/include/flag.h +++ b/include/flag.h @@ -259,6 +259,7 @@ struct instance_flags { boolean wc2_fullscreen; /* run fullscreen */ boolean wc2_softkeyboard; /* use software keyboard */ boolean wc2_wraptext; /* wrap text */ + boolean wc2_selectsaved; /* display a menu of user's saved games */ boolean cmdassist; /* provide detailed assistance for some commands */ boolean clicklook; /* allow right-clicking for look */ boolean obsolete; /* obsolete options can point at this, it isn't used */ diff --git a/include/ntconf.h b/include/ntconf.h index d96f0c177..21eff4f6e 100644 --- a/include/ntconf.h +++ b/include/ntconf.h @@ -24,6 +24,11 @@ #define SELF_RECOVER /* Allow the game itself to recover from an aborted game */ #define USER_SOUNDS + +#ifdef WIN32CON +#define SELECTSAVED /* Provide menu of saved games to choose from at start */ +#endif + /* * ----------------------------------------------------------------- * The remaining code shouldn't need modification. diff --git a/include/patchlevel.h b/include/patchlevel.h index 8b5c8ecb1..cf3b55852 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -13,7 +13,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 18 +#define EDITLEVEL 19 #define COPYRIGHT_BANNER_A \ "NetHack, Copyright 1985-2005" diff --git a/include/winprocs.h b/include/winprocs.h index 334e169be..ce8169a78 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -203,7 +203,8 @@ extern NEARDATA struct window_procs windowprocs; #define WC2_SOFTKEYBOARD 0x02L /* 02 software keyboard */ #define WC2_WRAPTEXT 0x04L /* 03 wrap long lines of text */ #define WC2_HILITE_STATUS 0x08L /* 04 hilite fields in status */ - /* 28 free bits */ +#define WC2_SELECTSAVED 0x10L /* 05 saved game selection menu */ + /* 27 free bits */ #define ALIGN_LEFT 1 #define ALIGN_RIGHT 2 diff --git a/src/files.c b/src/files.c index c8e4250c2..713ea6c06 100644 --- a/src/files.c +++ b/src/files.c @@ -148,6 +148,10 @@ extern char *sounddir; extern int n_dgns; /* from dungeon.c */ #if defined(UNIX) && defined(QT_GRAPHICS) +#define SELECTSAVED +#endif + +#ifdef SELECTSAVED STATIC_DCL int FDECL(strcmp_wrap, (const void *, const void *)); #endif STATIC_DCL char *FDECL(set_bonesfile_name, (char *,d_level*)); @@ -543,13 +547,17 @@ clearlocks() #endif } -#if defined(UNIX) && defined(QT_GRAPHICS) +#if defined(SELECTSAVED) STATIC_OVL int strcmp_wrap(p, q) 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 } #endif @@ -954,25 +962,24 @@ restore_saved_game() return fd; } -#if defined(UNIX) && defined(QT_GRAPHICS) +#if defined(SELECTSAVED) /*ARGSUSED*/ static char* plname_from_file(filename) const char* filename; { -#ifdef STORE_PLNAME_IN_FILE int fd; char* result = 0; Strcpy(SAVEF,filename); -#ifdef COMPRESS_EXTENSION +# ifdef COMPRESS_EXTENSION SAVEF[strlen(SAVEF)-strlen(COMPRESS_EXTENSION)] = '\0'; -#endif +# endif uncompress(SAVEF); if ((fd = open_savefile()) >= 0) { if (uptodate(fd, filename)) { char tplname[PL_NSIZ]; - mread(fd, (genericptr_t) tplname, PL_NSIZ); + get_plname_from_file(fd, tplname); result = strdup(tplname); } (void) close(fd); @@ -980,19 +987,20 @@ const char* filename; compress(SAVEF); return result; -#else -# if defined(UNIX) && defined(QT_GRAPHICS) +# 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 +# ifdef COMPRESS_EXTENSION #define EXTSTR COMPRESS_EXTENSION -#else +# else #define EXTSTR "" -#endif +# endif if ( sscanf( filename, "%*[^/]/%d%63[^.]" EXTSTR, &uid, name ) == 2 ) { #undef EXTSTR /* "_" most likely means " ", which certainly looks nicer */ @@ -1001,18 +1009,52 @@ const char* filename; name[k]=' '; return strdup(name); } else -# endif +# endif /* UNIX && QT_GRAPHICS */ { return 0; } -#endif +/* --------- end of obsolete code ----*/ +# endif /* 0 - WAS STORE_PLNAME_IN_FILE*/ } -#endif /* defined(UNIX) && defined(QT_GRAPHICS) */ +#endif /* defined(SELECTSAVED) */ char** get_saved_games() { -#if defined(UNIX) && defined(QT_GRAPHICS) +#if defined(SELECTSAVED) + int n, j; + char **result; +# ifdef WIN32CON + char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ]; + char *foundfile; + const char *fq_save; + + Sprintf(fnamebuf, "%s-", get_username(0)); + (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.", + '%', fnamebuf, encodedfnamebuf, BUFSZ); + Sprintf(SAVEF, "%s*.NetHack-saved-game", encodedfnamebuf); + fq_save = fqname(SAVEF, SAVEPREFIX, 0); + + foundfile = foundfile_buffer(); + if (findfirst((char *)fq_save)) { + n = 0; + do { + ++n; + } while (findnext()); + } + if (n > 0) { + result = (char**)alloc((n+1)*sizeof(char*)); /* at most */ + if (findfirst((char *)fq_save)) { + j = n = 0; + do { + char *r; + r = plname_from_file(foundfile); + if (r) + result[j++] = r; + ++n; + } while (findnext()); +# endif +# if defined(UNIX) && defined(QT_GRAPHICS) /* posixly correct version */ int myuid=getuid(); DIR *dir; @@ -1045,6 +1087,7 @@ get_saved_games() } } closedir(dir); +# endif qsort(result, j, sizeof(char *), strcmp_wrap); result[j++] = 0; return result; diff --git a/src/options.c b/src/options.c index de5aa1cd7..f0f29dbc5 100644 --- a/src/options.c +++ b/src/options.c @@ -171,6 +171,7 @@ static struct Bool_Opt #else {"sanity_check", (boolean *)0, FALSE, SET_IN_FILE}, #endif + {"selectsaved", &iflags.wc2_selectsaved, TRUE, DISP_IN_GAME}, /*WC*/ #ifdef EXP_ON_BOTL {"showexp", &flags.showexp, FALSE, SET_IN_GAME}, #else diff --git a/src/restore.c b/src/restore.c index fe9620103..cd09df5fa 100644 --- a/src/restore.c +++ b/src/restore.c @@ -573,11 +573,8 @@ register int fd; int rtmp; struct obj *otmp; -#ifdef STORE_PLNAME_IN_FILE - mread(fd, (genericptr_t) plname, PL_NSIZ); -#endif - restoring = TRUE; + get_plname_from_file(fd, plname); getlev(fd, 0, (xchar)0, FALSE); if (!restgamestate(fd, &stuckid, &steedid)) { display_nhwindow(WIN_MESSAGE, TRUE); @@ -656,9 +653,8 @@ register int fd; (void) lseek(fd, (off_t)0, 0); #endif (void) uptodate(fd, (char *)0); /* skip version info */ -#ifdef STORE_PLNAME_IN_FILE - mread(fd, (genericptr_t) plname, PL_NSIZ); -#endif + get_plname_from_file(fd, plname); + getlev(fd, 0, (xchar)0, FALSE); (void) close(fd); @@ -931,6 +927,17 @@ boolean ghostly; clear_id_mapping(); } +void +get_plname_from_file(fd, plbuf) +int fd; +char *plbuf; +{ + int rlen, pltmpsiz = 0; + rlen = read(fd, (genericptr_t) &pltmpsiz, sizeof(pltmpsiz)); + rlen = read(fd, (genericptr_t) plbuf, pltmpsiz); + return; +} + STATIC_OVL void restore_msghistory(fd) register int fd; diff --git a/src/save.c b/src/save.c index b5deeec11..62d2325ef 100644 --- a/src/save.c +++ b/src/save.c @@ -209,9 +209,7 @@ dosave0() #endif /* MFLOPPY */ store_version(fd); -#ifdef STORE_PLNAME_IN_FILE - bwrite(fd, (genericptr_t) plname, PL_NSIZ); -#endif + store_plname_in_file(fd); ustuck_id = (u.ustuck ? u.ustuck->m_id : 0); #ifdef STEED usteed_id = (u.usteed ? u.usteed->m_id : 0); @@ -407,9 +405,8 @@ savestateinlock() (void) write(fd, (genericptr_t) &currlev, sizeof(currlev)); save_savefile_name(fd); store_version(fd); -#ifdef STORE_PLNAME_IN_FILE - bwrite(fd, (genericptr_t) plname, PL_NSIZ); -#endif + store_plname_in_file(fd); + ustuck_id = (u.ustuck ? u.ustuck->m_id : 0); #ifdef STEED usteed_id = (u.usteed ? u.usteed->m_id : 0); @@ -1006,6 +1003,19 @@ register int fd, mode; ffruit = 0; } +void +store_plname_in_file(fd) +int fd; +{ + int plsiztmp = PL_NSIZ; + bufoff(fd); + /* bwrite() before bufon() uses plain write() */ + bwrite(fd, (genericptr_t) &plsiztmp, sizeof(plsiztmp)); + bwrite(fd, (genericptr_t) plname, plsiztmp); + bufon(fd); + return; +} + STATIC_OVL void save_msghistory(fd, mode) register int fd, mode; diff --git a/win/tty/wintty.c b/win/tty/wintty.c index cadb0489e..2725527da 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -53,6 +53,9 @@ struct window_procs tty_procs = { WC_MOUSE_SUPPORT| #endif WC_COLOR|WC_HILITE_PET|WC_INVERSE|WC_EIGHT_BIT_IN, +#if defined(SELECTSAVED) && defined(WIN32CON) + WC2_SELECTSAVED| +#endif 0L, tty_init_nhwindows, tty_player_selection, @@ -676,6 +679,62 @@ tty_askname() { static char who_are_you[] = "Who are you? "; register int c, ct, tryct = 0; +#ifdef SELECTSAVED +# if defined(WIN32CON) + int ch = -2; + char** saved = (char **)0; + + if (iflags.wc2_selectsaved) + saved = get_saved_games(); + if (saved && *saved) { + int k, clet = 'a'; + winid tmpwin; + anything any; + menu_item *chosen_game = (menu_item *)0; + + ch = -1; + saved = get_saved_games(); + tty_clear_nhwindow(BASE_WINDOW); + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any.a_int = 0; /* no selection */ + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, + ATR_NONE, COPYRIGHT_BANNER_A, MENU_UNSELECTED); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, + ATR_NONE, COPYRIGHT_BANNER_B, MENU_UNSELECTED); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, + ATR_NONE, COPYRIGHT_BANNER_C, MENU_UNSELECTED); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, + ATR_NONE, "", MENU_UNSELECTED); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, + ATR_NONE, "Select one of your saved games", MENU_UNSELECTED); + for (k = 0; saved[k]; ++k) { + if (clet == 'z' + 1) clet = 'A'; + if (clet == 'Z') break; + any.a_int = k + 1; + add_menu(tmpwin, NO_GLYPH, &any, clet++, 0, + ATR_NONE, saved[k], MENU_UNSELECTED); + } + any.a_int = -2; + add_menu(tmpwin, NO_GLYPH, &any, clet, 0, + ATR_NONE, "Start a new character", MENU_UNSELECTED); + /* 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) { + ch = chosen_game->item.a_int; + if (ch > 0) { + ch--; + strcpy(plname,saved[ch]); + } + free((genericptr_t)chosen_game); + } + destroy_nhwindow(tmpwin); + } + free_saved_games(saved); + if (ch >= 0) return; + if (ch == -1) bail("Until next time then..."); +# endif /* WIN32CON */ +#endif /* SELECTSAVED */ tty_putstr(BASE_WINDOW, 0, ""); do { @@ -1555,8 +1614,10 @@ tty_display_nhwindow(window, blocking) } else clear_screen(); ttyDisplay->toplin = 0; - } else - tty_clear_nhwindow(WIN_MESSAGE); + } else { + if (WIN_MESSAGE != WIN_ERR) + tty_clear_nhwindow(WIN_MESSAGE); + } if (cw->data || !cw->maxrow) process_text_window(window, cw);