From 309afb2eb2ec507f680fcac1a4aa20df578c6456 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 25 Apr 2026 14:48:49 -0400 Subject: [PATCH] add support for a unique game identifier A 37 character field for holding a unique identifier is added to the save file, as well as to an ancestor field in bones files. Since it requires installation of libuuid package on Linux, it requires an exlicit WANT_NHUUID=1 on the Make command line there. Without the libuuid support, the saved nhuuid is empty, which should be harmless. This also moves the save and restore of gm.moves higher up so that it already has a value the first time it is used against a relative saved timestamp. Invalidates savefiles and bones files due to new fields. I need to get this committed before I can test out the macOS portion, so there may be some build issues there briefly (hopefully), and follow-up commits to resolve them. Increments EDITLEVEL. --- include/decl.h | 2 ++ include/extern.h | 4 +++ include/patchlevel.h | 2 +- src/allmain.c | 1 + src/bones.c | 4 +++ src/decl.c | 5 ++++ src/files.c | 1 + src/restore.c | 3 +- src/save.c | 4 ++- sys/share/pcsys.c | 24 +++++++++++++++ sys/unix/hints/include/multiw-2.370 | 4 +++ sys/unix/hints/linux.370 | 4 +++ sys/unix/unixmain.c | 38 ++++++++++++++++++++++++ sys/vms/vmsmain.c | 24 +++++++++++++++ sys/windows/vs/NetHack/NetHack.vcxproj | 2 +- sys/windows/vs/NetHackW/NetHackW.vcxproj | 2 +- sys/windows/windsys.c | 31 +++++++++++++++++++ 17 files changed, 150 insertions(+), 5 deletions(-) diff --git a/include/decl.h b/include/decl.h index 7a34be6f1..1ea39d6f9 100644 --- a/include/decl.h +++ b/include/decl.h @@ -1174,6 +1174,8 @@ struct instance_globals_saved_m { struct instance_globals_saved_n { /* dungeon.c */ int n_dgns; /* number of dungeons (also used in mklev.c and do.c) */ + /* files.c */ + char nhuuid[37]; /* mkroom.c */ int nroom; /* region.c */ diff --git a/include/extern.h b/include/extern.h index c5cac33cf..d17497325 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1128,6 +1128,8 @@ extern boolean Death_quote(char *, int) NONNULLARG1; extern void livelog_add(long ll_type, const char *) NONNULLARG2; ATTRNORETURN extern void do_deferred_showpaths(int) NORETURN; extern boolean contains_directory(const char *); +extern void get_nhuuid(void); +extern void free_nhuuid(void); /* ### fountain.c ### */ @@ -4031,6 +4033,8 @@ extern char *get_port_id(char *); #ifdef RUNTIME_PASTEBUF_SUPPORT extern void port_insert_pastebuf(char *); #endif +extern void get_nhuuid(void); +extern void free_nhuuid(void); #endif /* !MAKEDEFS_C && !MDLIB_C */ diff --git a/include/patchlevel.h b/include/patchlevel.h index 75e1d3311..d9f25fea4 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 140 +#define EDITLEVEL 141 /* * Development status possibilities. diff --git a/src/allmain.c b/src/allmain.c index 4ac564c87..530840a1a 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -780,6 +780,7 @@ newgame(void) svc.context.next_attrib_check = 600L; /* arbitrary first setting */ svc.context.tribute.enabled = TRUE; /* turn on 3.6 tributes */ svc.context.tribute.tributesz = sizeof(struct tribute_info); + get_nhuuid(); for (i = LOW_PM; i < NUMMONS; i++) svm.mvitals[i].mvflags = mons[i].geno & G_NOCORPSE; diff --git a/src/bones.c b/src/bones.c index 5afb0cbcd..2a3358678 100644 --- a/src/bones.c +++ b/src/bones.c @@ -611,6 +611,7 @@ savebones(int how, time_t when, struct obj *corpse) nhfp->mode = WRITING; store_version(nhfp); + Sfo_char(nhfp, &svn.nhuuid[0], "ancestor-nhuuid", sizeof svn.nhuuid); /* if a bones pool digit is in use, it precedes the bonesid string and isn't recorded in the file */ Sfo_char(nhfp, &c, "bones_count", 1); @@ -632,6 +633,7 @@ getbones(void) NHFILE *nhfp = (NHFILE *) 0; char c = 0, *bonesid, oldbonesid[40] = { 0 }; /* was [10]; more should be safer */ + char ancestor_nhuuid[SIZE(svn.nhuuid)]; #ifndef SFCTOOL if (discover) /* save bones files for real games */ @@ -670,6 +672,8 @@ getbones(void) return 0; } } + Sfi_char(nhfp, &ancestor_nhuuid[0], "ancestor-nhuuid", + sizeof ancestor_nhuuid); Sfi_char(nhfp, &c, "bones_count", 1); /* length incl. '\0' */ if ((unsigned) c <= sizeof oldbonesid) { Sfi_char(nhfp, oldbonesid, "bonesid", (int) c); diff --git a/src/decl.c b/src/decl.c index 14a92abb3..43d7143b9 100644 --- a/src/decl.c +++ b/src/decl.c @@ -929,6 +929,11 @@ static const struct instance_globals_saved_m init_svm = { static const struct instance_globals_saved_n init_svn = { /* dungeon.c */ 0, /* n_dgns */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0 + }, /* nhuuid */ /* mkroom.c */ 0, /* nroom */ /* region.c */ diff --git a/src/files.c b/src/files.c index ae45f6c07..74f600ef2 100644 --- a/src/files.c +++ b/src/files.c @@ -1529,6 +1529,7 @@ free_saved_games(char **saved) free((genericptr_t) saved); } } + #endif /* !SFCTOOL */ /* ---------- END SAVE FILE HANDLING ----------- */ diff --git a/src/restore.c b/src/restore.c index 83e2ca546..35ebaee10 100644 --- a/src/restore.c +++ b/src/restore.c @@ -537,6 +537,8 @@ restgamestate(NHFILE *nhfp) #endif Sfi_ulong(nhfp, &uid, "gamestate-uid"); + Sfi_char(nhfp, &svn.nhuuid[0], "nhuuid", sizeof svn.nhuuid); + Sfi_long(nhfp, &svm.moves, "gamestate-moves"); #ifndef SFCTOOL if (SYSOPT_CHECK_SAVE_UID && uid != (unsigned long) getuid()) { /* strange ... */ @@ -676,7 +678,6 @@ restgamestate(NHFILE *nhfp) restore_dungeon(nhfp); restlevchn(nhfp); - Sfi_long(nhfp, &svm.moves, "gamestate-moves"); /* hero_seq isn't saved and restored because it can be recalculated */ gh.hero_seq = svm.moves << 3; /* normally handled in moveloop() */ Sfi_q_score(nhfp, &svq.quest_status, "gamestate-quest_status"); diff --git a/src/save.c b/src/save.c index a93ae35a4..b6d09f432 100644 --- a/src/save.c +++ b/src/save.c @@ -292,6 +292,8 @@ savegamestate(NHFILE *nhfp) program_state.saving++; /* caller should/did already set this... */ uid = (unsigned long) getuid(); Sfo_ulong(nhfp, &uid, "gamestate-uid"); + Sfo_char(nhfp, &svn.nhuuid[0], "nhuuid", sizeof svn.nhuuid); + Sfo_long(nhfp, &svm.moves, "gamestate-moves"); moves_to_relative_time(&svc.context.seer_turn); moves_to_relative_time(&svc.context.digging.lastdigtime); Sfo_context_info(nhfp, &svc.context, "gamestate-context"); @@ -322,7 +324,6 @@ savegamestate(NHFILE *nhfp) save_dungeon(nhfp, (boolean) !!update_file(nhfp), (boolean) !!release_data(nhfp)); savelevchn(nhfp); - Sfo_long(nhfp, &svm.moves, "gamestate-moves"); Sfo_q_score(nhfp, &svq.quest_status, "gamestate-quest_status"); for (i = 0; i < (MAXSPELL + 1); ++i) { Sfo_spell(nhfp, &svs.spl_book[i], "gamestate-spl_book"); @@ -1177,6 +1178,7 @@ freedynamicdata(void) release_runtime_info(); /* build-time options and version stuff */ free_convert_filenames(); #endif /* FREE_ALL_MEMORY */ + free_nhuuid(); if (VIA_WINDOWPORT()) status_finish(); diff --git a/sys/share/pcsys.c b/sys/share/pcsys.c index 07cab8e42..c2f3aa2dd 100644 --- a/sys/share/pcsys.c +++ b/sys/share/pcsys.c @@ -143,6 +143,30 @@ append_slash(char *name) return; } +void +get_nhuuid(void) +{ + unsigned char stmp[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + if (svn.nhuuid[0]) + return; + + /* FIXME: fill in a useful valie UUID somehow */ + Snprintf(svn.nhuuid, sizeof svn.nhuuid, "%s", (char *) stmp); +} + +void +free_nhuuid(void) +{ + int i; + + for (i = 0; i < SIZE(svn.nhuuid); i++) { + svn.nhuuid[i] = 0; + } +} + void getreturn(const char *str) { diff --git a/sys/unix/hints/include/multiw-2.370 b/sys/unix/hints/include/multiw-2.370 index e1cf1633d..ca732f6c8 100644 --- a/sys/unix/hints/include/multiw-2.370 +++ b/sys/unix/hints/include/multiw-2.370 @@ -168,6 +168,10 @@ CPLUSPLUS_NEED_DEPSUPPRESS = 1 endif # WANT_WIN_QT6 endif # WANT_WIN_QT +ifdef WANT_NHUUID +USE_UUIDLIB=1 +endif + ifeq "$(GIT)" "1" ifndef GITSUBMODULES GITSUBMODULES=1 diff --git a/sys/unix/hints/linux.370 b/sys/unix/hints/linux.370 index 8b402350c..ea239260e 100755 --- a/sys/unix/hints/linux.370 +++ b/sys/unix/hints/linux.370 @@ -227,6 +227,10 @@ ifdef MAKEFILE_SRC ifdef CURSESLIB WINLIB += $(CURSESLIB) endif #CURSESLIB +ifdef WANT_NHUUID +CFLAGS+= -DNHUUID +LIBS+=-luuid +endif #NHUUID endif #MAKEFILE_SRC ifdef WANT_WIN_X11 diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index ad2cef7de..90577fde0 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -15,6 +15,13 @@ #include #endif +#ifdef NHUUID +/* for uuid */ +#ifdef LINUX +#include +#endif +#endif /* NHUUID */ + #if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX) #if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__)) #if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX) @@ -833,5 +840,36 @@ sys_random_seed(void) } return seed; } +void +get_nhuuid(void) +{ + char struuid[37] = { 0 }; +#if defined(LINUX) && defined(NHUUID) + uuid_t binuuid; +#endif +#if defined(MACOS) && defined(NHUUID) + extern char *get_mac_uuid(char **); /* sys/unix/macuuid.m */ +#endif + if (svn.nhuuid[0]) + return; + +#if defined(MACOS) && defined(NHUUID) + get_mac_uuid(&struuid[0]); +#elif defined(LINUX) && defined(NHUUID) + uuid_generate_random(binuuid); + uuid_unparse(binuuid, struuid); +#endif /* MACOS || LINUX */ + Snprintf(svn.nhuuid, sizeof svn.nhuuid, "%s", &struuid[0]); +} + +void +free_nhuuid(void) +{ + int i; + + for (i = 0; i < SIZE(svn.nhuuid); i++) { + svn.nhuuid[i] = 0; + } +} /*unixmain.c*/ diff --git a/sys/vms/vmsmain.c b/sys/vms/vmsmain.c index f540d3190..d69c4de4d 100644 --- a/sys/vms/vmsmain.c +++ b/sys/vms/vmsmain.c @@ -513,4 +513,28 @@ sys_random_seed(void) return seed; } +void +get_nhuuid(void) +{ + unsigned char stmp[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + if (svn.nhuuid[0]) + return; + + /* FIXME: fill in a useful valie UUID somehow */ + Snprintf(svn.nhuuid, sizeof svn.nhuuid, "%s", (char *) stmp); +} + +void +free_nhuuid(void) +{ + int i; + + for (i = 0; i < SIZE(svn.nhuuid); i++) { + svn.nhuuid[i] = 0; + } +} + /*vmsmain.c*/ diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index c6f2df87c..fc2604af1 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -78,7 +78,7 @@ stdclatest - hacklib.lib;lualib.lib;kernel32.lib;dbghelp.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;UserEnv.lib;bcrypt.lib;%(AdditionalDependencies) + hacklib.lib;lualib.lib;kernel32.lib;dbghelp.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;UserEnv.lib;Rpcrt4.lib;bcrypt.lib;%(AdditionalDependencies) $(SndWavDir);$(SysWindDir);%(AdditionalIncludeDirectories) diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index cc3ec2563..6b7ecc097 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -100,7 +100,7 @@ Windows - hacklib.lib;lualib.lib;dbghelp.lib;comctl32.lib;winmm.lib;UserEnv.lib;bcrypt.lib;%(AdditionalDependencies) + hacklib.lib;lualib.lib;dbghelp.lib;comctl32.lib;winmm.lib;UserEnv.lib;Rpcrt4.lib;bcrypt.lib;%(AdditionalDependencies) $(WinWin32Dir)NethackW.exe.manifest;%(AdditionalManifestFiles) diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index 83e63d608..8826c33e4 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -28,6 +28,7 @@ #include #ifdef WIN32 +#include #include #include @@ -530,6 +531,36 @@ nethack_exit(int code) exit(code); } +void +get_nhuuid(void) +{ + UUID binuuid; + unsigned char *stmp; + RPC_STATUS rpcstatus; + + if (svn.nhuuid[0]) + return; + + rpcstatus = UuidCreate(&binuuid); + if (rpcstatus == RPC_S_OK) { + rpcstatus = UuidToStringA(&binuuid, &stmp); + if (rpcstatus == RPC_S_OK) { + Snprintf(svn.nhuuid, sizeof svn.nhuuid, "%s", (char *) stmp); + RpcStringFree(&stmp); + } + } +} + +void +free_nhuuid(void) +{ + int i; + + for (i = 0; i < SIZE(svn.nhuuid); i++) { + svn.nhuuid[i] = 0; + } +} + #ifdef WIN32CON #undef kbhit #include