This is the first of several savefile-related changes to follow later. This one is groundwork for those later changes. Remove internal compression schemes (RLECOMP and ZEROCOMP) and discard the savefile_info struct that was primarily used to convey which internal compression schemes had been in use. Relocate some struct definitions into appropriate header files for use by code to come in later changes. Remove the two struct size-related fields from version_info and from the nmakedefs_s. Instead, include a series of bytes near the beginning of the savefile, representing the size of each struct or base data type that impacts the historical savefile content. Those are referred to as the "critical bytes". (Related note: the "you" struct required two bytes, low and high, due to its size). Compare those critical bytes in a savefile against the NetHack build that is reading the savefile. This allows mismatch detection early in the savefile-reading process, and a clean exit, rather than proceeding to read nonsensical values from the file. Include some feedback on what the first mismatch was when encountering one. For arrays stored in the savefile, use loop-logic in the core to write/read the array elements one at a time, rather than in a single blob. This will be required for changes to follow later. (impacts artiexist[], artidisco[], svd.dungeons[], svl.level_info[], svl.level.locations[][], msrooms[] field of mapseen, svb.bases[], svb.disco[] objects[], svm.mvitals[], svs.spl_book[], svd.doors[], go.oracle_loc[], utrack[], wgrowtime[]) This also adds data model to the long version information. This invalidates existing save and bones files due to the changes in the information at the start of the file.
179 lines
5.9 KiB
C
179 lines
5.9 KiB
C
/* NetHack 3.7 date.c $NHDT-Date: 1655402414 2022/06/16 18:00:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.3 $ */
|
|
/* Copyright (c) Michael Allison, 2021. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "config.h"
|
|
#include "hacklib.h"
|
|
#ifdef Snprintf
|
|
#undef Snprintf
|
|
#endif
|
|
#define Snprintf(str, size, ...) \
|
|
nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__)
|
|
|
|
/* these are in extern.h but we don't include hack.h */
|
|
void populate_nomakedefs(struct version_info *) NONNULLARG1;
|
|
void free_nomakedefs(void);
|
|
|
|
extern unsigned long md_ignored_features(void);
|
|
extern char *mdlib_version_string(char *, const char *);
|
|
extern char *version_id_string(char *, size_t, const char *);
|
|
extern char *bannerc_string(char *, size_t, const char *);
|
|
|
|
/* nomakedefs_populated: flag for whether 'nomakedefs' should be freed */
|
|
static int nomakedefs_populated = 0;
|
|
|
|
struct nomakedefs_s nomakedefs = {
|
|
/* https://groups.google.com/forum/#!original/
|
|
comp.sources.games/91SfKYg_xzI/dGnR3JnspFkJ */
|
|
"Tue, 28-Jul-87 13:18:57 EDT",
|
|
"Version 1.0, built Jul 28 13:18:57 1987.",
|
|
(const char *) 0, /* git_sha */
|
|
(const char *) 0, /* git_branch */
|
|
(const char *) 0, /* git_prefix */
|
|
"1.0.0-0",
|
|
"NetHack Version 1.0.0-0 - last build Tue Jul 28 13:18:57 1987.",
|
|
0x01010000UL,
|
|
0x00000000UL,
|
|
0x00000000UL,
|
|
0x00000000UL,
|
|
554476737UL,
|
|
};
|
|
|
|
#if defined(__DATE__) && defined(__TIME__)
|
|
|
|
#define extract_field(t,s,n,z) \
|
|
do { \
|
|
for (i = 0; i < n; ++i) \
|
|
t[i] = s[i + z]; \
|
|
t[i] = '\0'; \
|
|
} while (0)
|
|
|
|
void
|
|
populate_nomakedefs(struct version_info *version)
|
|
{
|
|
int i;
|
|
char tmpbuf1[BUFSZ], tmpbuf2[BUFSZ], *strp;
|
|
const char *mth[] = {
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
struct tm t = {0};
|
|
time_t timeresult;
|
|
|
|
/*
|
|
* In a cross-compiled environment, you can't execute
|
|
* the target binaries during the build, so we can't
|
|
* use makedefs to write the values of the build
|
|
* date and time to a file for retrieval. Not for
|
|
* information meaningful to the target execution
|
|
* environment.
|
|
*
|
|
* How can we capture the build date/time of the target
|
|
* binaries in such a situation? We need to rely on the
|
|
* cross-compiler itself to do it for us during the
|
|
* cross-compile.
|
|
*
|
|
* To that end, we are going to make use of the
|
|
* following pre-defined preprocessor macros for this:
|
|
* gcc, msvc, clang __DATE__ "Feb 12 1996"
|
|
* gcc, msvc, clang __TIME__ "23:59:01"
|
|
*
|
|
*/
|
|
|
|
Snprintf(tmpbuf1, sizeof tmpbuf1, "%s %s", __DATE__, __TIME__);
|
|
/* "Feb 12 1996 23:59:01"
|
|
01234567890123456789 */
|
|
if ((int) strlen(tmpbuf1) == 20) {
|
|
extract_field(tmpbuf2, tmpbuf1, 4, 7); /* year */
|
|
t.tm_year = atoi(tmpbuf2) - 1900;
|
|
extract_field(tmpbuf2, tmpbuf1, 3, 0); /* mon */
|
|
for (i = 0; i < SIZE(mth); ++i)
|
|
if (!case_insensitive_comp(tmpbuf2, mth[i])) {
|
|
t.tm_mon = i;
|
|
break;
|
|
}
|
|
extract_field(tmpbuf2, tmpbuf1, 2, 4); /* mday */
|
|
strp = tmpbuf2;
|
|
if (*strp == ' ')
|
|
strp++;
|
|
t.tm_mday = atoi(strp);
|
|
extract_field(tmpbuf2, tmpbuf1, 2, 12); /* hour */
|
|
t.tm_hour = atoi(tmpbuf2);
|
|
extract_field(tmpbuf2, tmpbuf1, 2, 15); /* min */
|
|
t.tm_min = atoi(tmpbuf2);
|
|
extract_field(tmpbuf2, tmpbuf1, 2, 18); /* sec */
|
|
t.tm_sec = atoi(tmpbuf2);
|
|
timeresult = mktime(&t);
|
|
nomakedefs.build_time = (unsigned long) timeresult;
|
|
nomakedefs.build_date = dupstr(tmpbuf1);
|
|
}
|
|
|
|
nomakedefs.version_number = version->incarnation;
|
|
nomakedefs.version_features = version->feature_set;
|
|
nomakedefs.ignored_features = md_ignored_features();
|
|
nomakedefs.version_sanity1 = version->entity_count;
|
|
nomakedefs.version_string = dupstr(mdlib_version_string(tmpbuf2, "."));
|
|
nomakedefs.version_id = dupstr(
|
|
version_id_string(tmpbuf2, sizeof tmpbuf2, nomakedefs.build_date));
|
|
nomakedefs.copyright_banner_c = dupstr(
|
|
bannerc_string(tmpbuf2, sizeof tmpbuf2, nomakedefs.build_date));
|
|
#ifdef NETHACK_GIT_SHA
|
|
nomakedefs.git_sha = dupstr(NETHACK_GIT_SHA);
|
|
#endif
|
|
#ifdef NETHACK_GIT_BRANCH
|
|
nomakedefs.git_branch = dupstr(NETHACK_GIT_BRANCH);
|
|
#endif
|
|
#ifdef NETHACK_GIT_PREFIX
|
|
nomakedefs.git_prefix = dupstr(NETHACK_GIT_PREFIX);
|
|
#endif
|
|
|
|
nomakedefs_populated = 1;
|
|
return;
|
|
}
|
|
|
|
void
|
|
free_nomakedefs(void)
|
|
{
|
|
/* can't just free non-Null values because they're initialized at
|
|
compile-time with static strings and won't have dynamic values
|
|
unless populate_nomakedefs() has been called */
|
|
if (!nomakedefs_populated)
|
|
return;
|
|
|
|
if (nomakedefs.build_date)
|
|
free((genericptr_t) nomakedefs.build_date),
|
|
nomakedefs.build_date = 0;
|
|
if (nomakedefs.version_string)
|
|
free((genericptr_t) nomakedefs.version_string),
|
|
nomakedefs.version_string = 0;
|
|
if (nomakedefs.version_id)
|
|
free((genericptr_t) nomakedefs.version_id),
|
|
nomakedefs.version_id = 0;
|
|
if (nomakedefs.copyright_banner_c)
|
|
free((genericptr_t) nomakedefs.copyright_banner_c),
|
|
nomakedefs.copyright_banner_c = 0;
|
|
#ifdef NETHACK_GIT_SHA
|
|
if (nomakedefs.git_sha)
|
|
free((genericptr_t) nomakedefs.git_sha),
|
|
nomakedefs.git_sha = 0;
|
|
#endif
|
|
#ifdef NETHACK_GIT_BRANCH
|
|
if (nomakedefs.git_branch)
|
|
free((genericptr_t) nomakedefs.git_branch),
|
|
nomakedefs.git_branch = 0;
|
|
#endif
|
|
#ifdef NETHACK_GIT_PREFIX
|
|
if (nomakedefs.git_prefix)
|
|
free((genericptr_t) nomakedefs.git_prefix),
|
|
nomakedefs.git_prefix = 0;
|
|
#endif
|
|
|
|
/* values are Null now; dynamic vs static doesn't really matter anymore */
|
|
nomakedefs_populated = 0;
|
|
return;
|
|
}
|
|
|
|
#endif /* __DATE__ && __TIME__ */
|
|
|
|
|
|
/*date.c*/
|