diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index a28a71e3d..403ddf9d1 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -2086,6 +2086,10 @@ of specifying your role. Normally only the first letter of the value is examined; `r' is an exception with ``Rogue'', ``Ranger'', and ``random'' values. If you prefix a `!' or ``no'' to the value, you can exclude that role from being picked randomly. +.lp rlecomp +When writing out a save file, perform run length compression of level +structures. Not all ports support run length compression. It has no +effect on reading an existing save file. .lp runmode Controls the amount of screen updating for the map window when engaged in multi-turn movement (running via shift+direction or control+direction @@ -2173,6 +2177,10 @@ Provide more commentary during the game (default on). Select which windowing system to use, such as ``tty'' or ``X11'' (default depends on version). Cannot be set with the `O' command. +.lp zerocomp +When writing out a save file, perform zero-comp compression of the +contents. Not all ports support zero-comp compression. It has no effect +on reading an existing save file. .hn 2 Window Port Customization options .pg diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 4fb7e528f..36eb28a59 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -27,7 +27,7 @@ \begin{document} % % input file: guidebook.mn -% $Revision: 1.89 $ $Date: 2005/01/09 21:40:02 $ +% $Revision: 1.90 $ $Date: 2005/01/16 03:57:39 $ % %.ds h0 " %.ds h1 %.ds h2 \% @@ -2554,6 +2554,11 @@ value is examined; `r' is an exception with ``{\tt Rogue}'', {\tt Ranger}'', and ``{\tt random}'' values. If you prefix `{\tt !}' or ``{\tt no}'' to the value, you can exclude that role from being picked randomly. %.lp +\item[\ib{rlecomp}] +When writing out a save file, perform run length compression of level +structures. Not all ports support run length compression. It has no +effect on reading an existing save file. +%.lp \item[\ib{runmode}] Controls the amount of screen updating for the map window when engaged in multi-turn movement (running via {\tt shift}+direction @@ -2658,6 +2663,11 @@ Provide more commentary during the game (default on). Select which windowing system to use, such as ``{\tt tty}'' or ``{\tt X11}'' (default depends on version). Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{zerocomp}] +When writing out a save file, perform zero-comp compression of the +contents. Not all ports support zero-comp compression. It has no effect +on reading an existing save file. \elist %.hn 2 diff --git a/include/config.h b/include/config.h index 2b61a1aa7..1351c0ddc 100644 --- a/include/config.h +++ b/include/config.h @@ -159,18 +159,22 @@ /* * If COMPRESS is defined, it should contain the full path name of your - * 'compress' program. Defining INTERNAL_COMP causes NetHack to do - * simpler byte-stream compression internally. Both COMPRESS and - * INTERNAL_COMP create smaller bones/level/save files, but require - * additional code and time. Currently, only UNIX fully implements - * COMPRESS; other ports should be able to uncompress save files a - * la unixmain.c if so inclined. + * 'compress' program. + * * If you define COMPRESS, you must also define COMPRESS_EXTENSION * as the extension your compressor appends to filenames after - * compression. + * compression. Currently, only UNIX fully implements + * COMPRESS; other ports should be able to uncompress save files a + * la unixmain.c if so inclined. + * + * Defining ZLIB_COMP builds in support for zlib compression. If you + * define ZLIB_COMP, you must link with a zlib library. + * + * COMPRESS and ZLIB_COMP are mutually exclusive. + * */ - -#ifdef UNIX + +#if defined(UNIX) && !defined(ZLIB_COMP) && !defined(COMPRESS) /* path and file name extension for compression program */ #define COMPRESS "/usr/bin/compress" /* Lempel-Ziv compression */ #define COMPRESS_EXTENSION ".Z" /* compress's extension */ @@ -180,9 +184,41 @@ #endif #ifndef COMPRESS -# define INTERNAL_COMP /* control use of NetHack's compression routines */ +# define ZLIB_COMP /* ZLIB for compression */ #endif +/* + * Internal Compression Options + * + * Internal compression options RLECOMP and ZEROCOMP alter the data + * that gets written to the save file by NetHack, in contrast + * to COMPRESS or ZLIB_COMP which compress the entire file after + * the NetHack data is written out. + * + * Defining RLECOMP builds in support for internal run-length + * compression of level structures. If RLECOMP support is included + * it can be toggled on/off at runtime via the config file option + * rlecomp. + * + * Defining ZEROCOMP builds in support for internal zero-comp + * compression of data. If ZEROCOMP support is included it can still + * be toggled on/off at runtime via the config file option zerocomp. + * + * RLECOMP and ZEROCOMP support can be included even if + * COMPRESS or ZLIB_COMP support is included. One reason for doing + * so would be to provide savefile read compatibility with a savefile + * where those options were in effect. With RLECOMP and/or ZEROCOMP + * defined, NetHack can read an rlecomp or zerocomp savefile in, yet + * re-save without them. + * + * Using any compression option will create smaller bones/level/save + * files at the cost of additional code and time. + */ + +/* # define INTERNAL_COMP */ /* Forces both ZEROCOMP and RLECOMP */ +/* # define ZEROCOMP */ /* Support ZEROCOMP compression */ +/* # define RLECOMP */ /* Support RLECOMP compression */ + /* * Data librarian. Defining DLB places most of the support files into * a tar-like file, thus making a neater installation. See *conf.h diff --git a/include/decl.h b/include/decl.h index ecfa12f53..35e99bb77 100644 --- a/include/decl.h +++ b/include/decl.h @@ -381,6 +381,8 @@ E char *fqn_prefix[PREFIX_COUNT]; E char *fqn_prefix_names[PREFIX_COUNT]; #endif +E NEARDATA struct savefile_info sfcap, sfrestinfo, sfsaveinfo; + #ifdef AUTOPICKUP_EXCEPTIONS struct autopickup_exception { char *pattern; diff --git a/include/extern.h b/include/extern.h index dac2fa9bf..34b3e2643 100644 --- a/include/extern.h +++ b/include/extern.h @@ -673,8 +673,8 @@ E int NDECL(create_savefile); E int NDECL(open_savefile); E int NDECL(delete_savefile); E int NDECL(restore_saved_game); -E void FDECL(compress, (const char *)); -E void FDECL(uncompress, (const char *)); +E void FDECL(nh_compress, (const char *)); +E void FDECL(nh_uncompress, (const char *)); E boolean FDECL(lock_file, (const char *,int,int)); E void FDECL(unlock_file, (const char *)); #ifdef USER_SOUNDS @@ -1751,14 +1751,14 @@ 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 -E int FDECL(mread, (int,genericptr_t,unsigned int)); -#else E void FDECL(mread, (int,genericptr_t,unsigned int)); -#endif #ifndef GOLDOBJ E void FDECL(put_gold_back, (struct obj **,long *)); #endif +E int FDECL(validate, (int,const char *)); +E void NDECL(reset_restpref); +E void FDECL(set_restpref, (const char *)); +E void FDECL(set_savepref, (const char *)); /* ### rip.c ### */ @@ -1835,10 +1835,15 @@ E void FDECL(bufoff, (int)); E void FDECL(bflush, (int)); E void FDECL(bwrite, (int,genericptr_t,unsigned int)); E void FDECL(bclose, (int)); +E void FDECL(def_bclose, (int)); +#if defined(ZEROCOMP) +E void FDECL(zerocomp_bclose, (int)); +#endif E void FDECL(savefruitchn, (int,int)); E void FDECL(store_plname_in_file, (int)); E void NDECL(free_dungeons); E void NDECL(freedynamicdata); +E void FDECL(store_savefileinfo, (int)); /* ### shk.c ### */ diff --git a/include/flag.h b/include/flag.h index 465d0295a..f690cdbca 100644 --- a/include/flag.h +++ b/include/flag.h @@ -164,6 +164,8 @@ struct instance_flags { boolean menu_tab_sep; /* Use tabs to separate option menu fields */ boolean menu_requested; /* Flag for overloaded use of 'm' prefix * on some non-move commands */ + boolean zerocomp; /* write zero-compressed save files */ + boolean rlecomp; /* run-length comp of levels when writing savefile */ uchar num_pad_mode; int menu_headings; /* ATR for menu headings */ int purge_monsters; /* # of dead monsters still on fmon list */ diff --git a/include/global.h b/include/global.h index 8c26e7059..f69b867f2 100644 --- a/include/global.h +++ b/include/global.h @@ -305,6 +305,20 @@ struct version_info { unsigned long struct_sizes2; /* size of more key structs */ }; +struct savefile_info { + unsigned long sfi1; /* compression etc. */ + unsigned long sfi2; /* miscellaneous */ + unsigned long sfi3; /* thirdparty */ +}; +#ifdef NHSTDC +#define SFI1_EXTERNALCOMP (1UL) +#define SFI1_RLECOMP (1UL << 1) +#define SFI1_ZEROCOMP (1UL << 2) +#else +#define SFI1_EXTERNALCOMP (1L) +#define SFI1_RLECOMP (1L << 1) +#define SFI1_ZEROCOMP (1L << 2) +#endif /* * Configurable internal parameters. diff --git a/src/bones.c b/src/bones.c index 69e51997f..44e2e5362 100644 --- a/src/bones.c +++ b/src/bones.c @@ -427,12 +427,14 @@ getbones() fd = open_bonesfile(&u.uz, &bonesid); if (fd < 0) return(0); - if ((ok = uptodate(fd, bones)) == 0) { + if (validate(fd, bones) != 0) { #ifdef WIZARD if (!wizard) #endif pline("Discarding unuseable bones; no need to panic..."); + ok = FALSE; } else { + ok = TRUE; #ifdef WIZARD if(wizard) { if(yn("Get bones?") == 'n') { diff --git a/src/decl.c b/src/decl.c index 72ae782e6..cdde3333c 100644 --- a/src/decl.c +++ b/src/decl.c @@ -275,6 +275,52 @@ char *fqn_prefix_names[PREFIX_COUNT] = { "hackdir", "leveldir", "savedir", "lockdir", "configdir", "troubledir" }; #endif +NEARDATA struct savefile_info sfcap = { +#ifdef NHSTDC + 0x00000000UL +#else + 0x00000000L +#endif +#if defined(COMPRESS) || defined(ZLIB_COMP) + | SFI1_EXTERNALCOMP +#endif +#if defined(ZEROCOMP) + | SFI1_ZEROCOMP +#endif +#if defined(RLECOMP) + | SFI1_RLECOMP +#endif + , +#ifdef NHSTDC + 0x00000000UL, 0x00000000UL +#else + 0x00000000L, 0x00000000L +#endif +}; + +NEARDATA struct savefile_info sfrestinfo, sfsaveinfo = { +#ifdef NHSTDC + 0x00000000UL +#else + 0x00000000L +#endif +#if defined(COMPRESS) || defined(ZLIB_COMP) + | SFI1_EXTERNALCOMP +#endif +#if defined(ZEROCOMP) + | SFI1_ZEROCOMP +#endif +#if defined(RLECOMP) + | SFI1_RLECOMP +#endif + , +#ifdef NHSTDC + 0x00000000UL, 0x00000000UL +#else + 0x00000000L, 0x00000000L +#endif +}; + /* dummy routine used to force linkage */ void decl_init() diff --git a/src/files.c b/src/files.c index fcc1a2e54..7254b6489 100644 --- a/src/files.c +++ b/src/files.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)files.c 3.5 2004/11/22 */ +/* SCCS Id: @(#)files.c 3.5 2005/01/04 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -32,6 +32,13 @@ const extern int errno; #endif +#ifdef ZLIB_COMP /* RLC 09 Mar 1999: Support internal ZLIB */ +#include "zlib.h" +# ifndef COMPRESS_EXTENSION +#define COMPRESS_EXTENSION ".gz" +# endif +#endif + #if defined(UNIX) && defined(QT_GRAPHICS) #include #include @@ -158,6 +165,8 @@ STATIC_DCL char *FDECL(set_bonesfile_name, (char *,d_level*)); STATIC_DCL char *NDECL(set_bonestemp_name); #ifdef COMPRESS STATIC_DCL void FDECL(redirect, (const char *,const char *,FILE *,BOOLEAN_P)); +#endif +#if defined(COMPRESS) || defined(ZLIB_COMP) STATIC_DCL void FDECL(docompress_file, (const char *,BOOLEAN_P)); #endif STATIC_DCL char *FDECL(make_lockname, (const char *,char *)); @@ -770,7 +779,7 @@ char **bonesid; *bonesid = set_bonesfile_name(bones, lev); fq_bones = fqname(bones, BONESPREFIX, 0); - uncompress(fq_bones); /* no effect if nonexistent */ + nh_uncompress(fq_bones); /* no effect if nonexistent */ #ifdef MAC fd = macopen(fq_bones, O_RDONLY | O_BINARY, BONE_TYPE); #else @@ -794,7 +803,7 @@ d_level *lev; void compress_bonesfile() { - compress(fqname(bones, BONESPREFIX, 0)); + nh_compress(fqname(bones, BONESPREFIX, 0)); } /* ---------- END BONES FILE HANDLING ----------- */ @@ -945,6 +954,7 @@ restore_saved_game() const char *fq_save; int fd; + reset_restpref(); set_savefile_name(); #ifdef MFLOPPY if (!saveDiskPrompt(1)) @@ -952,10 +962,10 @@ restore_saved_game() #endif /* MFLOPPY */ fq_save = fqname(SAVEF, SAVEPREFIX, 0); - uncompress(fq_save); + nh_uncompress(fq_save); if ((fd = open_savefile()) < 0) return fd; - if (!uptodate(fd, fq_save)) { + if (validate(fd, fq_save) != 0) { (void) close(fd), fd = -1; (void) delete_savefile(); } @@ -975,16 +985,16 @@ const char* filename; # ifdef COMPRESS_EXTENSION SAVEF[strlen(SAVEF)-strlen(COMPRESS_EXTENSION)] = '\0'; # endif - uncompress(SAVEF); + nh_uncompress(SAVEF); if ((fd = open_savefile()) >= 0) { - if (uptodate(fd, filename)) { + if (validate(fd, filename)==0) { char tplname[PL_NSIZ]; get_plname_from_file(fd, tplname); result = strdup(tplname); } (void) close(fd); } - compress(SAVEF); + nh_compress(SAVEF); return result; # if 0 @@ -1033,6 +1043,9 @@ get_saved_games() (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.", '%', fnamebuf, encodedfnamebuf, BUFSZ); Sprintf(SAVEF, "%s*.NetHack-saved-game", encodedfnamebuf); +#if defined(ZLIB_COMP) + Strcat(SAVEF, COMPRESS_EXTENSION); +#endif fq_save = fqname(SAVEF, SAVEPREFIX, 0); n = 0; @@ -1278,10 +1291,10 @@ boolean uncomp; /* compress file */ void -compress(filename) +nh_compress(filename) const char *filename; { -#ifndef COMPRESS +#if !defined(COMPRESS) && !defined(ZLIB_COMP) #if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) # pragma unused(filename) #endif @@ -1293,10 +1306,10 @@ const char *filename; /* uncompress file if it exists */ void -uncompress(filename) +nh_uncompress(filename) const char *filename; { -#ifndef COMPRESS +#if !defined(COMPRESS) && !defined(ZLIB_COMP) #if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) # pragma unused(filename) #endif @@ -1305,6 +1318,174 @@ const char *filename; #endif } +#ifdef ZLIB_COMP /* RLC 09 Mar 1999: Support internal ZLIB */ +static int +make_compressed_name(filename,cfn) +const char *filename; +char *cfn; +{ +#ifndef SHORT_FILENAMES + /* Assume free-form filename */ + + strcpy(cfn, filename); + strcat(cfn, COMPRESS_EXTENSION); + return TRUE; +#else + /* Accomodates 8.3 restriction, but otherwise assumes free-form filenames. + This may need to be revised for some systems. If a name cannot be + generated, this function may return FALSE and the file will then remain + uncompressed. */ + unsigned len; + + /* Assume DOS style filename */ + + strcpy(cfn, filename); + len = strlen(cfn); + if (len>4 && stricmp(cfn+len-4,".sav")==0) { + /* Save file; change .SAV extension to .SAZ */ + /* EMX/GCC (for OS/2) seems to miscompile cfn[len-1] */ + + cfn[strlen(cfn)-1]='z'; + return TRUE; + } else if (strnicmp(cfn,"bones",5)==0) { + /* Bones file; change BONES prefix to BONEZ */ + + cfn[4]='z'; + return TRUE; + } else { + /* Don't know how to convert this filename */ + + return FALSE; + } +#endif /* !MSDOS */ +} + +STATIC_OVL void +docompress_file(filename, uncomp) +const char *filename; +boolean uncomp; +{ + gzFile *compressedfile; + FILE *uncompressedfile; + char cfn[256]; + char buf[1024]; + unsigned len, len2; + + if (!make_compressed_name(filename, cfn)) { + /* Can't generate a name for the compressed file + due to 8.3 restriction */ + return; + } + + if (!uncomp) { + /* Open the input and output files */ + /* Note that gzopen takes "wb" as its mode, even on systems where + fopen takes "r" and "w" */ + + uncompressedfile = fopen(filename, RDBMODE); + if (uncompressedfile == NULL) { + perror(filename); + return; + } + compressedfile = gzopen(cfn, "wb"); + if (compressedfile == NULL) { + if (errno == 0) { + pline("zlib failed to allocate memory"); + } else { + perror(filename); + } + fclose(uncompressedfile); + return; + } + + /* Copy from the uncompressed to the compressed file */ + + while (1) { + len = fread(buf, 1, sizeof(buf), uncompressedfile); + if (ferror(uncompressedfile)) { + pline("Failure reading uncompressed file"); + pline("Can't compress %s.", filename); + fclose(uncompressedfile); + gzclose(compressedfile); + (void)unlink(cfn); + return; + } + if (len == 0) break; /* End of file */ + + len2 = gzwrite(compressedfile, buf, len); + if (len2 == 0) { + pline("Failure writing compressed file"); + pline("Can't compress %s.", filename); + fclose(uncompressedfile); + gzclose(compressedfile); + (void)unlink(cfn); + return; + } + } + + fclose(uncompressedfile); + gzclose(compressedfile); + + /* Delete the file left behind */ + + (void)unlink(filename); + + } else { /* uncomp */ + + /* Open the input and output files */ + /* Note that gzopen takes "rb" as its mode, even on systems where + fopen takes "r" and "w" */ + + compressedfile = gzopen(cfn, "rb"); + if (compressedfile == NULL) { + if (errno == 0) { + pline("zlib failed to allocate memory"); + } else if (errno != ENOENT) { + perror(filename); + } + return; + } + uncompressedfile = fopen(filename, WRBMODE); + if (uncompressedfile == NULL) { + perror(filename); + gzclose(compressedfile); + return; + } + + /* Copy from the compressed to the uncompressed file */ + + while (1) { + len = gzread(compressedfile, buf, sizeof(buf)); + if (len == (unsigned)-1) { + pline("Failure reading compressed file"); + pline("Can't uncompress %s.", filename); + fclose(uncompressedfile); + gzclose(compressedfile); + (void)unlink(filename); + return; + } + if (len == 0) break; /* End of file */ + + fwrite(buf, 1, len, uncompressedfile); + if (ferror(uncompressedfile)) { + pline("Failure writing uncompressed file"); + pline("Can't uncompress %s.", filename); + fclose(uncompressedfile); + gzclose(compressedfile); + (void)unlink(filename); + return; + } + } + + fclose(uncompressedfile); + gzclose(compressedfile); + + /* Delete the file left behind */ + (void)unlink(cfn); + } +} +#endif /* RLC 09 Mar 1999: End ZLIB patch */ + /* ---------- END FILE COMPRESSION HANDLING ----------- */ diff --git a/src/options.c b/src/options.c index f8d1ccdb8..ed118ea27 100644 --- a/src/options.c +++ b/src/options.c @@ -165,6 +165,15 @@ static struct Bool_Opt {"rawio", (boolean *)0, FALSE, SET_IN_FILE}, #endif {"rest_on_space", &flags.rest_on_space, FALSE, SET_IN_GAME}, +#ifdef RLECOMP + {"rlecomp", &iflags.rlecomp, +# if defined(COMPRESS) || defined(ZLIB_COMP) + FALSE, +# else + TRUE, +# endif + DISP_IN_GAME}, +#endif {"safe_pet", &flags.safe_dog, TRUE, SET_IN_GAME}, #ifdef WIZARD {"sanity_check", &iflags.sanity_check, FALSE, SET_IN_GAME}, @@ -206,6 +215,15 @@ static struct Bool_Opt #endif {"verbose", &flags.verbose, TRUE, SET_IN_GAME}, {"wraptext", &iflags.wc2_wraptext, FALSE, SET_IN_GAME}, +#ifdef ZEROCOMP + {"zerocomp", &iflags.zerocomp, +# if defined(COMPRESS) || defined(ZLIB_COMP) + FALSE, +# else + TRUE, +# endif + DISP_IN_GAME}, +#endif {(char *)0, (boolean *)0, FALSE, 0} }; @@ -515,6 +533,23 @@ initoptions() if (boolopt[i].addr) *(boolopt[i].addr) = boolopt[i].initvalue; } +#if defined(COMPRESS) || defined(ZLIB_COMP) + set_savepref("externalcomp"); + set_restpref("externalcomp"); +# ifdef RLECOMP + set_savepref("!rlecomp"); + set_restpref("!rlecomp"); +# endif +#else +# ifdef ZEROCOMP + set_savepref("zerocomp"); + set_restpref("zerocomp"); +# endif +# ifdef RLECOMP + set_savepref("rlecomp"); + set_restpref("rlecomp"); +# endif +#endif #ifdef SYSFLAGS Strcpy(sysflags.sysflagsid, "sysflags"); sysflags.sysflagsid[9] = (char)sizeof(struct sysflag); @@ -2328,7 +2363,22 @@ goodfruit: # endif } #endif /* TERMLIB || ASCIIGRAPH || MAC_GRAPHICS_ENV */ - +#ifdef RLECOMP + if ((boolopt[i].addr) == &iflags.rlecomp) { + if (*boolopt[i].addr) + set_savepref("rlecomp"); + else + set_savepref("!rlecomp"); + } +#endif +#ifdef ZEROCOMP + if ((boolopt[i].addr) == &iflags.zerocomp) { + if (*boolopt[i].addr) + set_savepref("zerocomp"); + else + set_savepref("externalcomp"); + } +#endif /* only do processing below if setting with doset() */ if (initial) return; diff --git a/src/restore.c b/src/restore.c index cd09df5fa..7d36d8e7f 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)restore.c 3.5 2003/09/06 */ +/* SCCS Id: @(#)restore.c 3.5 2005/01/04 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -16,8 +16,14 @@ extern void FDECL(substitute_tiles, (d_level *)); /* from tile.c */ #endif #ifdef ZEROCOMP -static int NDECL(mgetc); +STATIC_DCL void NDECL(zerocomp_minit); +STATIC_DCL void FDECL(zerocomp_mread, (int,genericptr_t,unsigned int)); +STATIC_DCL int NDECL(zerocomp_mgetc); #endif + +STATIC_DCL void NDECL(def_minit); +STATIC_DCL void FDECL(def_mread, (int,genericptr_t,unsigned int)); + STATIC_DCL void NDECL(find_lev_obj); STATIC_DCL void FDECL(restlevchn, (int)); STATIC_DCL void FDECL(restdamage, (int,BOOLEAN_P)); @@ -34,6 +40,27 @@ STATIC_DCL void FDECL(reset_oattached_mids, (BOOLEAN_P)); #ifndef GOLDOBJ STATIC_DCL struct obj *FDECL(gold_in, (struct obj *)); #endif +STATIC_DCL void FDECL(rest_levl, (int,BOOLEAN_P)); + +static struct restore_procs { + const char *name; + int mread_flags; + void NDECL((*restore_minit)); + void FDECL((*restore_mread), (int,genericptr_t,unsigned int)); + void FDECL((*restore_bclose), (int)); +} restoreprocs = { +#if !defined(ZEROCOMP) || (defined(COMPRESS) || defined(ZLIB_COMP)) + "externalcomp", 0, + def_minit, + def_mread, + def_bclose, +#else + "zerocomp", 0, + zerocomp_minit, + zerocomp_mread, + zerocomp_bclose, +#endif +}; /* * Save a mapping of IDs from ghost levels to the current level. This @@ -547,7 +574,7 @@ xchar ltmp; playwoRAMdisk(); /* Rewind save file and try again */ (void) lseek(fd, (off_t)0, 0); - (void) uptodate(fd, (char *)0); /* skip version */ + (void) validate(fd, (char *)0); /* skip version etc */ return dorecover(fd); /* 0 or 1 */ } else { # endif @@ -624,12 +651,10 @@ register int fd; if (strncmpi("X11", windowprocs.name, 3)) putstr(WIN_MAP, 0, "Restoring:"); #endif + restoreprocs.mread_flags = 1; /* return despite error */ while(1) { -#ifdef ZEROCOMP - if(mread(fd, (genericptr_t) <mp, sizeof ltmp) < 0) -#else - if(read(fd, (genericptr_t) <mp, sizeof ltmp) != sizeof ltmp) -#endif + mread(fd, (genericptr_t) <mp, sizeof ltmp); + if (restoreprocs.mread_flags == -1) break; getlev(fd, 0, ltmp, FALSE); #ifdef MICRO @@ -646,18 +671,24 @@ register int fd; rtmp = restlevelfile(fd, ltmp); if (rtmp < 2) return(rtmp); /* dorecover called recursively */ } + restoreprocs.mread_flags = 0; #ifdef BSD (void) lseek(fd, 0L, 0); #else (void) lseek(fd, (off_t)0, 0); #endif - (void) uptodate(fd, (char *)0); /* skip version info */ + (void) validate(fd, (char *)0); /* skip version and savefile info */ get_plname_from_file(fd, plname); getlev(fd, 0, (xchar)0, FALSE); (void) close(fd); + /* Now set the restore settings to match the + * settings used by the save file output routines + */ + reset_restpref(); + if (!wizard && !discover) (void) delete_savefile(); #ifdef REINCARNATION @@ -700,6 +731,41 @@ register int fd; return(1); } +STATIC_OVL void +rest_levl(fd, rlecomp) +int fd; +boolean rlecomp; +{ +#ifdef RLECOMP + short i, j; + uchar len; + struct rm r; + + if (rlecomp) { +# if defined(MAC) + /* Suppress warning about used before set */ + (void) memset((genericptr_t) &r, 0, sizeof(r)); +# endif + i = 0; j = 0; len = 0; + while(i < ROWNO) { + while(j < COLNO) { + if(len > 0) { + levl[j][i] = r; + len -= 1; + j += 1; + } else { + mread(fd, (genericptr_t)&len, sizeof(uchar)); + mread(fd, (genericptr_t)&r, sizeof(struct rm)); + } + } + j = 0; + i += 1; + } + } else +#endif /* RLECOMP */ + mread(fd, (genericptr_t) levl, sizeof(levl)); +} + void trickery(reason) char *reason; @@ -760,37 +826,7 @@ boolean ghostly; #endif trickery(trickbuf); } - -#ifdef RLECOMP - { - short i, j; - uchar len; - struct rm r; - -#if defined(MAC) - /* Suppress warning about used before set */ - (void) memset((genericptr_t) &r, 0, sizeof(r)); -#endif - i = 0; j = 0; len = 0; - while(i < ROWNO) { - while(j < COLNO) { - if(len > 0) { - levl[j][i] = r; - len -= 1; - j += 1; - } else { - mread(fd, (genericptr_t)&len, sizeof(uchar)); - mread(fd, (genericptr_t)&r, sizeof(struct rm)); - } - } - j = 0; - i += 1; - } - } -#else - mread(fd, (genericptr_t) levl, sizeof(levl)); -#endif /* RLECOMP */ - + rest_levl(fd, (boolean)((sfrestinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP)); mread(fd, (genericptr_t)&omoves, sizeof(omoves)); mread(fd, (genericptr_t)&upstair, sizeof(stairway)); mread(fd, (genericptr_t)&dnstair, sizeof(stairway)); @@ -1051,6 +1087,145 @@ boolean ghostly; } +void +minit() +{ + (*restoreprocs.restore_minit)(); + return; +} + +void +mread(fd, buf, len) +register int fd; +register genericptr_t buf; +register unsigned int len; +{ + (*restoreprocs.restore_mread)(fd, buf, len); + return; +} + +/* examine the version info and the savefile_info data + that immediately follows it. + Return 0 if it passed the checks. + Return 1 if it failed the version check. + Return 2 if it failed the savefile feature check. + Return -1 if it failed for some unknown reason. + */ +int +validate(fd, name) +int fd; +const char *name; +{ + int rlen; + struct savefile_info sfi; + unsigned long compatible; + boolean verbose = name ? TRUE : FALSE, reslt = FALSE; + + if (!(reslt = uptodate(fd, name))) return 1; + + rlen = read(fd, (genericptr_t) &sfi, sizeof sfi); + minit(); /* ZEROCOMP */ + if (rlen == 0) { + if (verbose) { + pline("File \"%s\" is empty during save file feature check?", name); + wait_synch(); + } + return -1; + } + + compatible = (sfi.sfi1 & sfcap.sfi1); + + if ((sfi.sfi1 & SFI1_ZEROCOMP) == SFI1_ZEROCOMP) { + if ((compatible & SFI1_ZEROCOMP) != SFI1_ZEROCOMP) { + if (verbose) { + pline("File \"%s\" has incompatible ZEROCOMP compression.", name); + wait_synch(); + } + return 2; + } else if ((sfrestinfo.sfi1 & SFI1_ZEROCOMP) != SFI1_ZEROCOMP) { + set_restpref("zerocomp"); + } + } + + if ((sfi.sfi1 & SFI1_EXTERNALCOMP) == SFI1_EXTERNALCOMP) { + if ((compatible & SFI1_EXTERNALCOMP) != SFI1_EXTERNALCOMP) { + if (verbose) { + pline("File \"%s\" lacks required internal compression.", name); + wait_synch(); + } + return 2; + } else if ((sfrestinfo.sfi1 & SFI1_EXTERNALCOMP) != SFI1_EXTERNALCOMP) { + set_restpref("externalcomp"); + } + } + + /* RLECOMP check must be last, after ZEROCOMP or INTERNALCOMP adjustments */ + if ((sfi.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP) { + if ((compatible & SFI1_RLECOMP) != SFI1_RLECOMP) { + if (verbose) { + pline("File \"%s\" has incompatible run-length compression.", name); + wait_synch(); + } + return 2; + } else if ((sfrestinfo.sfi1 & SFI1_RLECOMP) != SFI1_RLECOMP) { + set_restpref("rlecomp"); + } + } + /* savefile does not have RLECOMP level location compression, so adjust */ + else set_restpref("!rlecomp"); + + return 0; +} + +void +reset_restpref() +{ +#ifdef ZEROCOMP + if (iflags.zerocomp) + set_restpref("zerocomp"); + else +#endif + set_restpref("externalcomp"); +#ifdef RLECOMP + if (iflags.rlecomp) + set_restpref("rlecomp"); + else +#endif + set_restpref("!rlecomp"); +} + +void +set_restpref(suitename) +const char *suitename; +{ + if (!strcmpi(suitename, "externalcomp")) { + restoreprocs.name = "externalcomp"; + restoreprocs.restore_mread = def_mread; + restoreprocs.restore_minit = def_minit; + sfrestinfo.sfi1 |= SFI1_EXTERNALCOMP; + sfrestinfo.sfi1 &= ~SFI1_ZEROCOMP; + def_minit(); + } + if (!strcmpi(suitename, "!rlecomp")) { + sfrestinfo.sfi1 &= ~SFI1_RLECOMP; + } +#ifdef ZEROCOMP + if (!strcmpi(suitename, "zerocomp")) { + restoreprocs.name = "zerocomp"; + restoreprocs.restore_mread = zerocomp_mread; + restoreprocs.restore_minit = zerocomp_minit; + sfrestinfo.sfi1 |= SFI1_ZEROCOMP; + sfrestinfo.sfi1 &= ~SFI1_EXTERNALCOMP; + zerocomp_minit(); + } +#endif +#ifdef RLECOMP + if (!strcmpi(suitename, "rlecomp")) { + sfrestinfo.sfi1 |= SFI1_RLECOMP; + } +#endif +} + #ifdef ZEROCOMP #define RLESC '\0' /* Leading character for run of RLESC's */ @@ -1063,8 +1238,8 @@ static NEARDATA unsigned short inbufsz = 0; static NEARDATA short inrunlength = -1; static NEARDATA int mreadfd; -static int -mgetc() +STATIC_OVL int +zerocomp_mgetc() { if (inbufp >= inbufsz) { inbufsz = read(mreadfd, (genericptr_t)inbuf, sizeof inbuf); @@ -1079,16 +1254,16 @@ mgetc() return inbuf[inbufp++]; } -void -minit() +STATIC_OVL void +zerocomp_minit() { inbufsz = 0; inbufp = 0; inrunlength = -1; } -int -mread(fd, buf, len) +STATIC_OVL void +zerocomp_mread(fd, buf, len) int fd; genericptr_t buf; register unsigned len; @@ -1101,27 +1276,28 @@ register unsigned len; inrunlength--; *(*((char **)&buf))++ = '\0'; } else { - register short ch = mgetc(); - if (ch < 0) return -1; /*readlen;*/ + register short ch = zerocomp_mgetc(); + if (ch < 0) { + restoreprocs.mread_flags = -1; + return; + } if ((*(*(char **)&buf)++ = (char)ch) == RLESC) { - inrunlength = mgetc(); + inrunlength = zerocomp_mgetc(); } } /*readlen++;*/ } - return 0; /*readlen;*/ } +#endif /* ZEROCOMP */ -#else /* ZEROCOMP */ - -void -minit() +STATIC_OVL void +def_minit() { return; } -void -mread(fd, buf, len) +STATIC_OVL void +def_mread(fd, buf, len) register int fd; register genericptr_t buf; register unsigned int len; @@ -1135,6 +1311,10 @@ register unsigned int len; rlen = read(fd, buf, (unsigned) len); if((unsigned)rlen != len){ #endif + if (restoreprocs.mread_flags == 1) { /* means "return anyway" */ + restoreprocs.mread_flags = -1; + return; + } else { pline("Read %d instead of %u bytes.", rlen, len); if(restoring) { (void) close(fd); @@ -1142,9 +1322,10 @@ register unsigned int len; error("Error restoring old game."); } panic("Error reading level file."); + } } } -#endif /* ZEROCOMP */ + #ifndef GOLDOBJ /* * Takes all of the gold objects out of the invent or diff --git a/src/save.c b/src/save.c index a5519b807..9477eacee 100644 --- a/src/save.c +++ b/src/save.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)save.c 3.5 2003/11/14 */ +/* SCCS Id: @(#)save.c 3.5 2005/01/04 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -22,9 +22,6 @@ static int count_only; int dotcnt, dotrow; /* also used in restore */ #endif -#ifdef ZEROCOMP -STATIC_DCL void FDECL(bputc, (int)); -#endif STATIC_DCL void FDECL(savelevchn, (int,int)); STATIC_DCL void FDECL(savedamage, (int,int)); STATIC_DCL void FDECL(saveobjchn, (int,struct obj *,int)); @@ -37,6 +34,44 @@ STATIC_DCL void FDECL(savelev0, (int,XCHAR_P,int)); STATIC_DCL boolean NDECL(swapout_oldest); STATIC_DCL void FDECL(copyfile, (char *,char *)); #endif /* MFLOPPY */ +STATIC_DCL void FDECL(savelevl, (int fd, BOOLEAN_P)); +STATIC_DCL void FDECL(def_bufon, (int)); +STATIC_DCL void FDECL(def_bufoff, (int)); +STATIC_DCL void FDECL(def_bflush, (int)); +STATIC_DCL void FDECL(def_bwrite, (int,genericptr_t,unsigned int)); +#ifdef ZEROCOMP +STATIC_DCL void FDECL(zerocomp_bufon, (int)); +STATIC_DCL void FDECL(zerocomp_bufoff, (int)); +STATIC_DCL void FDECL(zerocomp_bflush, (int)); +STATIC_DCL void FDECL(zerocomp_bwrite, (int,genericptr_t,unsigned int)); +STATIC_DCL void FDECL(zerocomp_bputc, (int)); +#endif + +static struct save_procs { + const char *name; + void FDECL((*save_bufon), (int)); + void FDECL((*save_bufoff), (int)); + void FDECL((*save_bflush), (int)); + void FDECL((*save_bwrite), (int,genericptr_t,unsigned int)); + void FDECL((*save_bclose), (int)); +} saveprocs = { +#if !defined(ZEROCOMP) || (defined(COMPRESS) || defined(ZLIB_COMP)) + "externalcomp", + def_bufon, + def_bufoff, + def_bflush, + def_bwrite, + def_bclose, +#else + "zerocomp", + zerocomp_bufon, + zerocomp_bufoff, + zerocomp_bflush, + zerocomp_bwrite, + zerocomp_bclose, +#endif +}; + #ifdef GCC_WARN static long nulls[10]; #else @@ -140,14 +175,14 @@ dosave0() #endif HUP if (iflags.window_inited) { - uncompress(fq_save); + nh_uncompress(fq_save); fd = open_savefile(); if (fd > 0) { (void) close(fd); clear_nhwindow(WIN_MESSAGE); There("seems to be an old save file."); if (yn("Overwrite the old file?") == 'n') { - compress(fq_save); + nh_compress(fq_save); return 0; } } @@ -209,6 +244,7 @@ dosave0() #endif /* MFLOPPY */ store_version(fd); + store_savefileinfo(fd); store_plname_in_file(fd); ustuck_id = (u.ustuck ? u.ustuck->m_id : 0); #ifdef STEED @@ -269,7 +305,7 @@ dosave0() /* get rid of current level --jgm */ delete_levelfile(ledger_no(&u.uz)); delete_levelfile(0); - compress(fq_save); + nh_compress(fq_save); return(1); } @@ -405,6 +441,7 @@ savestateinlock() (void) write(fd, (genericptr_t) &currlev, sizeof(currlev)); save_savefile_name(fd); store_version(fd); + store_savefileinfo(fd); store_plname_in_file(fd); ustuck_id = (u.ustuck ? u.ustuck->m_id : 0); @@ -489,54 +526,7 @@ int mode; #else bwrite(fd,(genericptr_t) &lev,sizeof(lev)); #endif -#ifdef RLECOMP - { - /* perform run-length encoding of rm structs */ - struct rm *prm, *rgrm; - int x, y; - uchar match; - - rgrm = &levl[0][0]; /* start matching at first rm */ - match = 0; - - for (y = 0; y < ROWNO; y++) { - for (x = 0; x < COLNO; x++) { - prm = &levl[x][y]; - if (prm->glyph == rgrm->glyph - && prm->typ == rgrm->typ - && prm->seenv == rgrm->seenv - && prm->horizontal == rgrm->horizontal - && prm->flags == rgrm->flags - && prm->lit == rgrm->lit - && prm->waslit == rgrm->waslit - && prm->roomno == rgrm->roomno - && prm->edge == rgrm->edge) { - match++; - if (match > 254) { - match = 254; /* undo this match */ - goto writeout; - } - } else { - /* the run has been broken, - * write out run-length encoding */ - writeout: - bwrite(fd, (genericptr_t)&match, sizeof(uchar)); - bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm)); - /* start encoding again. we have at least 1 rm - * in the next run, viz. this one. */ - match = 1; - rgrm = prm; - } - } - } - if (match > 0) { - bwrite(fd, (genericptr_t)&match, sizeof(uchar)); - bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm)); - } - } -#else - bwrite(fd,(genericptr_t) levl,sizeof(levl)); -#endif /* RLECOMP */ + savelevl(fd, (boolean)((sfsaveinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP)); bwrite(fd,(genericptr_t) &monstermoves,sizeof(monstermoves)); bwrite(fd,(genericptr_t) &upstair,sizeof(stairway)); @@ -575,6 +565,202 @@ int mode; if (mode != FREE_SAVE) bflush(fd); } +STATIC_OVL void +savelevl(fd, rlecomp) +int fd; +boolean rlecomp; +{ +#ifdef RLECOMP + struct rm *prm, *rgrm; + int x, y; + uchar match; + + if (rlecomp) { + /* perform run-length encoding of rm structs */ + + rgrm = &levl[0][0]; /* start matching at first rm */ + match = 0; + + for (y = 0; y < ROWNO; y++) { + for (x = 0; x < COLNO; x++) { + prm = &levl[x][y]; + if (prm->glyph == rgrm->glyph + && prm->typ == rgrm->typ + && prm->seenv == rgrm->seenv + && prm->horizontal == rgrm->horizontal + && prm->flags == rgrm->flags + && prm->lit == rgrm->lit + && prm->waslit == rgrm->waslit + && prm->roomno == rgrm->roomno + && prm->edge == rgrm->edge) { + match++; + if (match > 254) { + match = 254; /* undo this match */ + goto writeout; + } + } else { + /* the run has been broken, + * write out run-length encoding */ + writeout: + bwrite(fd, (genericptr_t)&match, sizeof(uchar)); + bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm)); + /* start encoding again. we have at least 1 rm + * in the next run, viz. this one. */ + match = 1; + rgrm = prm; + } + } + } + if (match > 0) { + bwrite(fd, (genericptr_t)&match, sizeof(uchar)); + bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm)); + } + } else +#endif /* RLECOMP */ + bwrite(fd,(genericptr_t) levl,sizeof(levl)); +} + +/*ARGSUSED*/ +void +bufon(fd) +int fd; +{ + (*saveprocs.save_bufon)(fd); + return; +} + +/*ARGSUSED*/ +void +bufoff(fd) +int fd; +{ + (*saveprocs.save_bufoff)(fd); + return; +} + +void +bflush(fd) /* flush run and buffer */ +register int fd; +{ + (*saveprocs.save_bflush)(fd); + return; +} + +void +bwrite(fd, loc, num) +int fd; +genericptr_t loc; +register unsigned num; +{ + (*saveprocs.save_bwrite)(fd, loc, num); + return; +} + +void +bclose(fd) +int fd; +{ + (*saveprocs.save_bclose)(fd); + return; +} + +static int bw_fd = -1; +static FILE *bw_FILE = 0; +static boolean buffering = FALSE; + +STATIC_OVL void +def_bufon(fd) + int fd; +{ +#ifdef UNIX + if(bw_fd != fd) { + if(bw_fd >= 0) + panic("double buffering unexpected"); + bw_fd = fd; + if((bw_FILE = fdopen(fd, "w")) == 0) + panic("buffering of file %d failed", fd); + } +#endif + buffering = TRUE; +} + +STATIC_OVL void +def_bufoff(fd) +int fd; +{ + def_bflush(fd); + buffering = FALSE; +} + +STATIC_OVL void +def_bflush(fd) + int fd; +{ +#ifdef UNIX + if(fd == bw_fd) { + if(fflush(bw_FILE) == EOF) + panic("flush of savefile failed!"); + } +#endif + return; +} + +STATIC_OVL void +def_bwrite(fd,loc,num) +register int fd; +register genericptr_t loc; +register unsigned num; +{ + boolean failed; + +#ifdef MFLOPPY + bytes_counted += num; + if (count_only) return; +#endif + +#ifdef UNIX + if (buffering) { + if(fd != bw_fd) + panic("unbuffered write to fd %d (!= %d)", fd, bw_fd); + + failed = (fwrite(loc, (int)num, 1, bw_FILE) != 1); + } else +#endif /* UNIX */ + { +/* lint wants the 3rd arg of write to be an int; lint -p an unsigned */ +#if defined(BSD) || defined(ULTRIX) || defined(WIN32) + failed = (write(fd, loc, (int)num) != (int)num); +#else /* e.g. SYSV, __TURBOC__ */ + failed = (write(fd, loc, num) != num); +#endif + } + + if (failed) { +#if defined(UNIX) || defined(VMS) || defined(__EMX__) + if (program_state.done_hup) + terminate(EXIT_FAILURE); + else +#endif + panic("cannot write %u bytes to file #%d", num, fd); + } +} + +void +def_bclose(fd) + int fd; +{ + bufoff(fd); +#ifdef UNIX + if (fd == bw_fd) { + (void) fclose(bw_FILE); + bw_fd = -1; + bw_FILE = 0; + } else +#endif + (void) close(fd); + return; +} + #ifdef ZEROCOMP /* The runs of zero-run compression are flushed after the game state or a * level is written out. This adds a couple bytes to a save file, where @@ -585,7 +771,7 @@ int mode; */ #define RLESC '\0' /* Leading character for run of LRESC's */ -#define flushoutrun(ln) (bputc(RLESC), bputc(ln), ln = -1) +#define flushoutrun(ln) (zerocomp_bputc(RLESC), zerocomp_bputc(ln), ln = -1) #ifndef ZEROCOMP_BUFSIZ # define ZEROCOMP_BUFSIZ BUFSZ @@ -602,7 +788,7 @@ static NEARDATA boolean compressing = FALSE; }*/ STATIC_OVL void -bputc(c) +zerocomp_bputc(c) int c; { #ifdef MFLOPPY @@ -619,7 +805,7 @@ int c; /*ARGSUSED*/ void -bufon(fd) +STATIC_OVL zerocomp_bufon(fd) int fd; { compressing = TRUE; @@ -627,8 +813,8 @@ int fd; } /*ARGSUSED*/ -void -bufoff(fd) +STATIC_OVL void +zerocomp_bufoff(fd) int fd; { if (outbufp) { @@ -640,8 +826,8 @@ int fd; return; } -void -bflush(fd) /* flush run and buffer */ +STATIC_OVL void +zerocomp_bflush(fd) /* flush run and buffer */ register int fd; { bwritefd = fd; @@ -659,14 +845,14 @@ register int fd; terminate(EXIT_FAILURE); else #endif - bclose(fd); /* panic (outbufp != 0) */ + zerocomp_bclose(fd); /* panic (outbufp != 0) */ } outbufp = 0; } } -void -bwrite(fd, loc, num) +STATIC_OVL void +zerocomp_bwrite(fd, loc, num) int fd; genericptr_t loc; register unsigned num; @@ -697,119 +883,20 @@ register unsigned num; if (outrunlength >= 0) { /* flush run */ flushoutrun(outrunlength); } - bputc(*bp); + zerocomp_bputc(*bp); } } } } void -bclose(fd) +zerocomp_bclose(fd) int fd; { - bufoff(fd); + zerocomp_bufoff(fd); (void) close(fd); return; } - -#else /* ZEROCOMP */ - -static int bw_fd = -1; -static FILE *bw_FILE = 0; -static boolean buffering = FALSE; - -void -bufon(fd) - int fd; -{ -#ifdef UNIX - if(bw_fd != fd) { - if(bw_fd >= 0) - panic("double buffering unexpected"); - bw_fd = fd; - if((bw_FILE = fdopen(fd, "w")) == 0) - panic("buffering of file %d failed", fd); - } -#endif - buffering = TRUE; -} - -void -bufoff(fd) -int fd; -{ - bflush(fd); - buffering = FALSE; -} - -void -bflush(fd) - int fd; -{ -#ifdef UNIX - if(fd == bw_fd) { - if(fflush(bw_FILE) == EOF) - panic("flush of savefile failed!"); - } -#endif - return; -} - -void -bwrite(fd,loc,num) -register int fd; -register genericptr_t loc; -register unsigned num; -{ - boolean failed; - -#ifdef MFLOPPY - bytes_counted += num; - if (count_only) return; -#endif - -#ifdef UNIX - if (buffering) { - if(fd != bw_fd) - panic("unbuffered write to fd %d (!= %d)", fd, bw_fd); - - failed = (fwrite(loc, (int)num, 1, bw_FILE) != 1); - } else -#endif /* UNIX */ - { -/* lint wants the 3rd arg of write to be an int; lint -p an unsigned */ -#if defined(BSD) || defined(ULTRIX) - failed = (write(fd, loc, (int)num) != (int)num); -#else /* e.g. SYSV, __TURBOC__ */ - failed = (write(fd, loc, num) != num); -#endif - } - - if (failed) { -#if defined(UNIX) || defined(VMS) || defined(__EMX__) - if (program_state.done_hup) - terminate(EXIT_FAILURE); - else -#endif - panic("cannot write %u bytes to file #%d", num, fd); - } -} - -void -bclose(fd) - int fd; -{ - bufoff(fd); -#ifdef UNIX - if (fd == bw_fd) { - (void) fclose(bw_FILE); - bw_fd = -1; - bw_FILE = 0; - } else -#endif - (void) close(fd); - return; -} #endif /* ZEROCOMP */ STATIC_OVL void @@ -1048,6 +1135,65 @@ register int fd, mode; pline("Stored %d messages into savefile.", msgcount); #endif } + +void +store_savefileinfo(fd) +int fd; +{ + /* sfcap (decl.c) describes the savefile feature capabilities + * that are supported by this port/platform build. + * + * sfsaveinfo (decl.c) describes the savefile info that actually + * gets written into the savefile, and is used to determine the + * save file being written. + + * sfrestinfo (decl.c) describes the savefile info that is + * being used to read the information from an existing savefile. + * + */ + + bufoff(fd); + /* bwrite() before bufon() uses plain write() */ + bwrite(fd,(genericptr_t)&sfsaveinfo, (unsigned)(sizeof sfsaveinfo)); + bufon(fd); + return; +} + +void +set_savepref(suitename) +const char *suitename; +{ + if (!strcmpi(suitename, "externalcomp")) { + saveprocs.name = "externalcomp"; + saveprocs.save_bufon = def_bufon; + saveprocs.save_bufoff = def_bufoff; + saveprocs.save_bflush = def_bflush; + saveprocs.save_bwrite = def_bwrite; + saveprocs.save_bclose = def_bclose; + sfsaveinfo.sfi1 |= SFI1_EXTERNALCOMP; + sfsaveinfo.sfi1 &= ~SFI1_ZEROCOMP; + } + if (!strcmpi(suitename, "!rlecomp")) { + sfsaveinfo.sfi1 &= ~SFI1_RLECOMP; + } +#ifdef ZEROCOMP + if (!strcmpi(suitename, "zerocomp")) { + saveprocs.name = "zerocomp"; + saveprocs.save_bufon = zerocomp_bufon; + saveprocs.save_bufoff = zerocomp_bufoff; + saveprocs.save_bflush = zerocomp_bflush; + saveprocs.save_bwrite = zerocomp_bwrite; + saveprocs.save_bclose = zerocomp_bclose; + sfsaveinfo.sfi1 |= SFI1_ZEROCOMP; + sfsaveinfo.sfi1 &= ~SFI1_EXTERNALCOMP; + } +#endif +#ifdef RLECOMP + if (!strcmpi(suitename, "rlecomp")) { + sfsaveinfo.sfi1 |= SFI1_RLECOMP; + } +#endif +} /* also called by prscore(); this probably belongs in dungeon.c... */ void diff --git a/sys/be/bemain.c b/sys/be/bemain.c index d277fcdca..4c077be4f 100644 --- a/sys/be/bemain.c +++ b/sys/be/bemain.c @@ -106,7 +106,7 @@ int MAIN(int argc, char **argv) if(yn("Do you want to keep the save file?") == 'n') (void) delete_savefile(); else { - compress(fqname(SAVEF, SAVEPREFIX, 0)); + nh_compress(fqname(SAVEF, SAVEPREFIX, 0)); } } diff --git a/sys/mac/macmain.c b/sys/mac/macmain.c index fe5705130..5d91a139e 100644 --- a/sys/mac/macmain.c +++ b/sys/mac/macmain.c @@ -117,7 +117,7 @@ main (void) if(yn("Do you want to keep the save file?") == 'n') (void) delete_savefile(); else { - compress(fqname(SAVEF, SAVEPREFIX, 0)); + nh_compress(fqname(SAVEF, SAVEPREFIX, 0)); } } } diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index eca498128..b345a8fba 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -423,9 +423,12 @@ char *argv[]; update_inventory(); if (discover || wizard) { - if(yn("Do you want to keep the save file?") == 'n'){ + if(yn("Do you want to keep the save file?") == 'n') (void) delete_savefile(); + else { + nh_compress(fqname(SAVEF, SAVEPREFIX, 0)); } + } context.move = 0; diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 537b26529..427256428 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -282,6 +282,9 @@ WINLIB = $(WINTTYLIB) # IRIX 4.0.x needs -lsun if NIS (YP) is being used for passwd file lookup # LIBS = -lsun # +# If ZLIB_COMP is defined in config.h this is necessary to link with zlib. +# LIBS = -lz +# LIBS = # make NetHack diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 4ec7a6021..11f7a9319 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -287,7 +287,7 @@ char *argv[]; (void) delete_savefile(); else { (void) chmod(fq_save,FCMASK); /* back to readable */ - compress(fq_save); + nh_compress(fq_save); } } context.move = 0; diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index 5e0c7652c..b8908bbc2 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -1,5 +1,5 @@ # SCCS Id: @(#)Makefile.msc 3.5 $Date$ -# Copyright (c) NetHack PC Development Team 1993-2003 +# Copyright (c) NetHack PC Development Team 1993-2005 # # NetHack 3.5.x Makefile for MS Visual C++ V6.x and above and MS NMAKE # @@ -127,6 +127,15 @@ RANDOM = $(OBJ)\random.o PRECOMPHEAD = N # set to Y if you want to use precomp. headers +# +# If you defined ZLIB_COMP in include/config.h and you need +# to link with the zlib.lib library, uncomment the line below. +# If necessary, prefix explicit path information to the file name +# otherwise it assumes the NetHack src directory. +# + +#ZLIB = zlib.lib + #=============================================== #======= End of Modification Section =========== #=============================================== @@ -578,7 +587,7 @@ $(NHRES): $(NTSYS)\console.rc $(NTSYS)\NetHack.ico $(GAMEFILE) : $(ALLOBJ) $(NHRES) $(O)gamedir.tag $(WINDLLS) @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) @echo Linking.... - $(link) $(LFLAGS) user32.lib winmm.lib -out:$@ @<<$(GAME).lnk + $(link) $(LFLAGS) $(ZLIB) user32.lib winmm.lib -out:$@ @<<$(GAME).lnk $(ALLOBJ:^ =^ ) $(NHRES) << diff --git a/util/makedefs.c b/util/makedefs.c index 5e5f1ffa6..4b04f0908 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)makedefs.c 3.5 2002/08/14 */ +/* SCCS Id: @(#)makedefs.c 3.5 2005/01/04 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* Copyright (c) M. Stephenson, 1990, 1991. */ /* Copyright (c) Dean Luick, 1990. */ @@ -411,11 +411,24 @@ do_rumors() /* * Use this to explicitly mask out features during version checks. + * + * ZEROCOMP, RLECOMP, and ZLIB_COMP describe compression features + * that the port/plaform which wrote the savefile was capable of + * dealing with. Don't reject a savefile just because the port + * reading the savefile doesn't match on all/some of them. + * The actual compression features used to produce the savefile are + * recorded in the savefile_info structure immediately following the + * version_info, and that is what needs to be checked against the + * feature set of the port that is reading the savefile back in. + * That check is done in src/restore.c now. + * */ #define IGNORED_FEATURES ( 0L \ | (1L << 12) /* GOLDOBJ */ \ | (1L << 20) /* EXP_ON_BOTL */ \ | (1L << 21) /* SCORE_ON_BOTL */ \ + | (1L << 27) /* ZEROCOMP */ \ + | (1L << 28) /* RLECOMP */ \ ) static void @@ -478,7 +491,9 @@ make_version() #ifdef SCORE_ON_BOTL | (1L << 21) #endif - /* data format [COMPRESS excluded] (27..31) */ + /* data format (27..31) + * External compression methods such as COMPRESS and ZLIB_COMP + * do not affect the contents and are thus excluded from here */ #ifdef ZEROCOMP | (1L << 27) #endif @@ -653,6 +668,9 @@ static const char *build_opts[] = { #ifdef COMPRESS "data file compression", #endif +#ifdef ZLIB_COMP + "ZLIB data file compression", +#endif #ifdef DLB "data librarian", #endif @@ -783,6 +801,9 @@ static const char *build_opts[] = { #endif #ifdef ZEROCOMP "zero-compressed save files", +#endif +#ifdef RLECOMP + "run-length compression of levl array in save files", #endif save_bones_compat_buf, "basic NetHack features"