Files
nethack/src/version.c
T
nhmall 2a912fe001 SELECTSAVE update
In SELECTSAVE implementations, out of date savefiles in the
tree were triggering error messages to the user during the
building of the pick list. The file with the error never
ended up on the pick list, or got removed, so the error was
perpetual on every SELECTSAVE startup.

This passes the UTD_QUIETLY flag down the the small set of
callers involved, so that when it was received by uptodate(),
it went about its verification work quietly.
2026-05-30 11:32:37 -04:00

875 lines
29 KiB
C

/* NetHack 5.0 version.c $NHDT-Date: 1737622664 2025/01/23 00:57:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.105 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Michael Allison, 2018. */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
#include "dlb.h"
#ifndef MINIMAL_FOR_RECOVER
#ifndef SFCTOOL
#ifndef OPTIONS_AT_RUNTIME
#define OPTIONS_AT_RUNTIME
#endif
staticfn void insert_rtoption(char *) NONNULLARG1;
/* fill buffer with short version (so caller can avoid including date.h)
* buf cannot be NULL */
char *
version_string(char *buf, size_t bufsz)
{
Snprintf(buf, bufsz, "%s",
((nomakedefs.version_string && nomakedefs.version_string[0])
? nomakedefs.version_string
/* in case we try to write a paniclog entry after releasing
the 'nomakedefs' data */
: mdlib_version_string(buf, ".")));
return buf;
}
/* fill and return the given buffer with the long nethack version string */
char *
getversionstring(char *buf, size_t bufsz)
{
Strcpy(buf, nomakedefs.version_id);
{
int c = 0;
#if defined(RUNTIME_PORT_ID)
char tmpbuf[BUFSZ], *tmp;
#endif
char *p = eos(buf);
boolean dotoff = (p > buf && p[-1] == '.');
if (dotoff)
--p;
Strcpy(p, " (");
#if defined(RUNTIME_PORT_ID)
tmp = get_port_id(tmpbuf);
if (tmp)
Snprintf(eos(buf), (bufsz - strlen(buf)) - 1,
"%s%s", c++ ? "," : "", tmp);
#endif
if (nomakedefs.git_sha)
Snprintf(eos(buf), (bufsz - strlen(buf)) - 1,
"%s%s", c++ ? "," : "", nomakedefs.git_sha);
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
if (nomakedefs.git_branch)
Snprintf(eos(buf), (bufsz - strlen(buf)) - 1,
"%sbranch:%s",
c++ ? "," : "", nomakedefs.git_branch);
#endif
if (nomakedefs.git_prefix)
Snprintf(eos(buf), (bufsz - strlen(buf)) - 1,
"%sprefix:%s",
c++ ? "," : "", nomakedefs.git_prefix);
if (c)
Snprintf(eos(buf), (bufsz - strlen(buf)) - 1,
"%s", ")");
else /* if nothing has been added, strip " (" back off */
*p = '\0';
if (dotoff)
Snprintf(eos(buf), (bufsz - strlen(buf)) - 1,
"%s", ".");
}
return buf;
}
/* version info that could be displayed on status lines;
"<game name> <git branch name> <x.y.z version number>";
if game name is a prefix of--or same as--branch name, it is omitted
"<git branch name> <x.y.z version number>";
after release--or if branch info is unavailable--it will be
"<game name> <x.y.z version number>";
game name or branch name or both can be requested via flags */
char *
status_version(char *buf, size_t bufsz, boolean indent)
{
const char *name = NULL, *altname = NULL, *indentation;
unsigned vflags = flags.versinfo;
boolean shownum = ((vflags & VI_NUMBER) != 0),
showname = ((vflags & VI_NAME) != 0),
showbranch = ((vflags & VI_BRANCH) != 0);
/* game's name {variants should use own name, not "NetHack"} */
if (showname) {
#ifdef VERS_GAME_NAME /* can be set to override default (base of filename) */
name = VERS_GAME_NAME;
#else
name = nh_basename(gh.hname, FALSE); /* hname is from xxxmain.c */
#endif
if (!name || !*name) /* shouldn't happen */
showname = FALSE;
}
/* git branch name, if available */
if (showbranch) {
#if 1 /*#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)*/
altname = nomakedefs.git_branch;
#endif
if (!altname || !*altname)
showbranch = FALSE;
}
if (showname && showbranch) {
if (!strncmpi(name, altname, strlen(name)))
showname = FALSE;
#if 0
/* note: it's possible for branch name to be a prefix of game name
but that's unlikely enough that we won't bother with it; having
branch "nethack-5.0" be a superset of game "nethack" seems like
including both is redundant, but having branch "net" be a subset
of game "nethack" doesn't feel that way; optimizing "net" out
seems like it would be a mistake */
else if (!strncmpi(altname, name, strlen(altname)))
showbranch = FALSE;
#endif
} else if (!showname && !showbranch) {
/* flags.versinfo could be set to only 'branch' but it might not
be available */
shownum = TRUE;
}
*buf = '\0';
indentation = indent ? " " : "";
if (showname) {
Snprintf(eos(buf), bufsz - strlen(buf), "%s%s", indentation, name);
indentation = " "; /* forced separator rather than optional indent */
}
if (showbranch) {
Snprintf(eos(buf), bufsz - strlen(buf), "%s%s", indentation, altname);
indentation = " ";
}
if (shownum) {
/* x.y.z version number */
Snprintf(eos(buf), bufsz - strlen(buf), "%s%s", indentation,
(nomakedefs.version_string && nomakedefs.version_string[0])
? nomakedefs.version_string
: mdlib_version_string(buf, "."));
}
return buf;
}
/* the #versionshort command */
int
doversion(void)
{
char buf[BUFSZ];
if (iflags.menu_requested)
return doextversion();
pline("%s", getversionstring(buf, sizeof buf));
return ECMD_OK;
}
/* the '#version' command; also a choice for '?' */
int
doextversion(void)
{
int rtcontext = 0;
const char *rtbuf;
dlb *f = (dlb *) 0;
char buf[BUFSZ], *p = 0;
winid win = create_nhwindow(NHW_TEXT);
boolean use_dlb = TRUE,
done_rt = FALSE,
done_dlb = FALSE,
prolog;
/* lua_info[] moved to util/mdlib.c and rendered via do_runtime_info() */
#if defined(OPTIONS_AT_RUNTIME)
use_dlb = FALSE;
#else
done_rt = TRUE;
#endif
/* instead of using ``display_file(OPTIONS_USED,TRUE)'' we handle
the file manually so we can include dynamic version info */
(void) getversionstring(buf, sizeof buf);
/* if extra text (git info) is present, put it on separate line
but don't wrap on (x86) */
if (strlen(buf) >= COLNO)
p = strrchr(buf, '(');
if (p && p > buf && p[-1] == ' ' && p[1] != 'x')
p[-1] = '\0';
else
p = 0;
putstr(win, 0, buf);
if (p) {
*--p = ' ';
putstr(win, 0, p);
}
if (use_dlb) {
f = dlb_fopen(OPTIONS_USED, "r");
if (!f) {
putstr(win, 0, "");
Sprintf(buf, "[Configuration '%s' not available?]", OPTIONS_USED);
putstr(win, 0, buf);
done_dlb = TRUE;
}
}
/*
* already inserted above:
* + outdented program name and version plus build date and time
* dat/options; display contents with lines prefixed by '-' deleted:
* - blank-line
* - indented program name and version
* blank-line
* outdented feature header
* - blank-line
* indented feature list
* spread over multiple lines
* blank-line
* outdented windowing header
* - blank-line
* indented windowing choices with
* optional second line for default
* - blank-line
* - EOF
*/
prolog = TRUE; /* to skip indented program name */
for (;;) {
if (use_dlb && !done_dlb) {
if (!dlb_fgets(buf, BUFSZ, f)) {
done_dlb = TRUE;
continue;
}
} else if (!done_rt) {
if (!(rtbuf = do_runtime_info(&rtcontext))) {
done_rt = TRUE;
continue;
}
(void) strncpy(buf, rtbuf, BUFSZ - 1);
buf[BUFSZ - 1] = '\0';
} else {
break;
}
(void) strip_newline(buf);
if (strchr(buf, '\t') != 0)
(void) tabexpand(buf);
if (*buf && *buf != ' ') {
/* found outdented header; insert a separator since we'll
have skipped corresponding blank line inside the file */
putstr(win, 0, "");
prolog = FALSE;
}
/* skip blank lines and prolog (progame name plus version) */
if (prolog || !*buf)
continue;
if (strchr(buf, ':'))
insert_rtoption(buf);
if (*buf)
putstr(win, 0, buf);
}
if (use_dlb)
(void) dlb_fclose(f);
display_nhwindow(win, FALSE);
destroy_nhwindow(win);
return ECMD_OK;
}
void
early_version_info(boolean pastebuf)
{
char buf1[BUFSZ], buf2[BUFSZ];
char *buf, *tmp;
Snprintf(buf1, sizeof buf1, "test");
/* this is early enough that we have to do our own line-splitting */
getversionstring(buf1, sizeof buf1);
tmp = strstri(buf1, " ("); /* split at start of version info */
if (tmp) {
/* retain one buffer so that it all goes into the paste buffer */
*tmp++ = '\0';
Snprintf(buf2, sizeof (buf2),"%s\n%s", buf1, tmp);
buf = buf2;
} else {
buf = buf1;
}
raw_printf("%s", buf);
if (pastebuf) {
#if defined(RUNTIME_PASTEBUF_SUPPORT) && !defined(LIBNH)
/*
* Call a platform/port-specific routine to insert the
* version information into a paste buffer. Useful for
* easy inclusion in bug reports.
*/
port_insert_pastebuf(buf);
#else
raw_printf("%s", "Paste buffer copy is not available.\n");
#endif
}
}
extern const char regex_id[];
/*
* makedefs should put the first token into dat/options; we'll substitute
* the second value for it. The token must contain at least one colon
* so that we can spot it, and should not contain spaces so that makedefs
* won't split it across lines. Ideally the length should be close to
* that of the substituted value since we don't do phrase-splitting/line-
* wrapping when displaying it.
*/
static struct rt_opt {
const char *token, *value;
} rt_opts[] = {
{ ":PATMATCH:", regex_id },
{ ":LUAVERSION:", (const char *) gl.lua_ver },
{ ":LUACOPYRIGHT:", (const char *) gl.lua_copyright },
};
/*
* 3.6.0
* Some optional stuff is no longer available to makedefs because
* it depends which of several object files got linked into the
* game image, so we insert those options here.
*/
staticfn void
insert_rtoption(char *buf)
{
int i;
if (!gl.lua_ver[0])
get_lua_version();
for (i = 0; i < SIZE(rt_opts); ++i) {
if (strstri(buf, rt_opts[i].token) && *rt_opts[i].value) {
(void) strsubst(buf, rt_opts[i].token, rt_opts[i].value);
}
/* we don't break out of the loop after a match; there might be
other matches on the same line */
}
}
#ifdef MICRO
boolean
comp_times(long filetime)
{
/* BUILD_TIME is constant but might have L suffix rather than UL;
'filetime' is historically signed but ought to have been unsigned */
return ((unsigned long) filetime < (unsigned long) nomakedefs.build_time);
}
#endif
#endif /* !SFCTOOL */
#ifdef SFCTOOL
#ifdef wait_synch
#undef wait_synch
#endif
#define wait_synch()
#endif /* SFCTOOL */
boolean
check_version(
struct version_info *version_data,
const char *filename,
boolean complain,
unsigned long utdflags)
{
if (!filename) {
#ifdef EXTRA_SANITY_CHECKS
if (complain)
impossible("check_version() called with"
" 'complain'=True but 'filename'=Null");
#endif
complain = FALSE; /* 'complain' requires 'filename' for pline("%s") */
}
if ((version_data->feature_set & SFCTOOL_BIT) != 0) {
gc.converted_savefile_loaded = TRUE;
version_data->feature_set &= ~(SFCTOOL_BIT);
}
if (
#ifdef VERSION_COMPATIBILITY /* patchlevel.h */
version_data->incarnation < VERSION_COMPATIBILITY
|| version_data->incarnation > nomakedefs.version_number
#else
version_data->incarnation != nomakedefs.version_number
#endif
) {
#ifndef SFCTOOL
if (complain) {
pline("Version mismatch for file \"%s\".", filename);
if (WIN_MESSAGE != WIN_ERR)
display_nhwindow(WIN_MESSAGE, TRUE);
}
#endif
return FALSE;
} else if (
(version_data->feature_set & ~nomakedefs.ignored_features)
!= (nomakedefs.version_features & ~nomakedefs.ignored_features)
|| ((utdflags & UTD_SKIP_SANITY1) == 0
&& version_data->entity_count != nomakedefs.version_sanity1)
) {
#ifndef SFCTOOL
if (complain) {
pline("Configuration incompatibility for file \"%s\".", filename);
display_nhwindow(WIN_MESSAGE, TRUE);
}
#endif
return FALSE;
}
return TRUE;
}
#ifndef SFCTOOL
#ifdef AMIGA
const char amiga_version_string[] = AMIGA_VERSION_STRING;
#endif
unsigned long
get_feature_notice_ver(char *str)
{
char buf[BUFSZ];
int ver_maj, ver_min, patch;
char *istr[3];
int j = 0;
if (!str)
return 0L;
str = strcpy(buf, str);
istr[j] = str;
while (*str) {
if (*str == '.') {
*str++ = '\0';
j++;
istr[j] = str;
if (j == 2)
break;
} else if (strchr("0123456789", *str) != 0) {
str++;
} else
return 0L;
}
if (j != 2)
return 0L;
ver_maj = atoi(istr[0]);
ver_min = atoi(istr[1]);
patch = atoi(istr[2]);
return FEATURE_NOTICE_VER(ver_maj, ver_min, patch);
/* macro from hack.h */
}
unsigned long
get_current_feature_ver(void)
{
return FEATURE_NOTICE_VER(VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
}
/*ARGUSED*/
const char *
copyright_banner_line(int indx)
{
#ifdef COPYRIGHT_BANNER_A
if (indx == 1)
return COPYRIGHT_BANNER_A;
#endif
#ifdef COPYRIGHT_BANNER_B
if (indx == 2)
return COPYRIGHT_BANNER_B;
#endif
if (indx == 3)
return nomakedefs.copyright_banner_c;
#ifdef COPYRIGHT_BANNER_D
if (indx == 4)
return COPYRIGHT_BANNER_D;
#endif
return "";
}
/* called by argcheck(allmain.c) from early_options(sys/xxx/xxxmain.c) */
void
dump_version_info(void)
{
char buf[BUFSZ];
const char *hname = gh.hname ? gh.hname : "nethack";
if (strlen(hname) > 33)
hname = eos(nhStr(hname)) - 33; /* discard const for eos() */
runtime_info_init();
Snprintf(buf, sizeof buf, "%-12.33s %08lx %08lx %08lx",
hname,
nomakedefs.version_number,
(nomakedefs.version_features & ~nomakedefs.ignored_features),
nomakedefs.version_sanity1);
raw_print(buf);
release_runtime_info();
return;
}
void
store_version(NHFILE *nhfp)
{
struct version_info version_data = {
0UL,
0UL,
0UL,
};
/* actual version number */
version_data.incarnation = nomakedefs.version_number;
/* bitmask of config settings */
version_data.feature_set = nomakedefs.version_features;
/* # of monsters and objects */
version_data.entity_count = nomakedefs.version_sanity1;
/* bwrite() before bufon() uses plain write() */
if (nhfp->structlevel)
bufoff(nhfp->fd);
store_critical_bytes(nhfp);
Sfo_version_info(nhfp, (struct version_info *) &version_data,
"version_info");
if (nhfp->structlevel)
bufon(nhfp->fd);
return;
}
#endif /* !SFCTOOL */
#endif /* MINIMAL_FOR_RECOVER */
struct critical_sizes_with_names {
uchar ucsize;
const char *nm;
};
struct critical_sizes_with_names critical_sizes[] = {
{ 0, "unused" },
/* simple types, that don't have subfields */
{ (uchar) sizeof(short), "short" },
{ (uchar) sizeof(int), "int" },
{ (uchar) sizeof(long), "long" },
{ (uchar) sizeof(long long), "long long" },
{ (uchar) sizeof(genericptr_t), "genericptr_t" },
{ (uchar) sizeof(aligntyp), "aligntyp" },
{ (uchar) sizeof(boolean), "boolean" },
{ (uchar) sizeof(coordxy), "coordxy" },
{ (uchar) sizeof(int16), "int16" },
{ (uchar) sizeof(int32), "int32" },
{ (uchar) sizeof(int64), "int64" },
{ (uchar) sizeof(schar), "schar" },
{ (uchar) sizeof(size_t), "size_t" },
{ (uchar) sizeof(uchar), "uchar" },
{ (uchar) sizeof(uint16), "uint16" },
{ (uchar) sizeof(uint32), "uint32" },
{ (uchar) sizeof(uint64), "uint64" },
{ (uchar) sizeof(ulong), "ulong" },
{ (uchar) sizeof(unsigned), "unsigned" },
{ (uchar) sizeof(ushort), "ushort" },
{ (uchar) sizeof(xint16), "xint16" },
{ (uchar) sizeof(xint8), "xint8" },
/* complex - they break down into one or more simple types */
{ (uchar) sizeof(struct arti_info), "struct arti_info" },
{ (uchar) sizeof(struct nhrect), "struct nhrect" },
{ (uchar) sizeof(struct branch), "struct branch" },
{ (uchar) sizeof(struct bubble), "struct bubble" },
{ (uchar) sizeof(struct cemetery), "struct cemetery" },
{ (uchar) sizeof(struct context_info), "struct context_info" },
{ (uchar) sizeof(struct nhcoord), "struct nhcoord" },
{ (uchar) sizeof(struct damage), "struct damage" },
{ (uchar) sizeof(struct dest_area), "struct dest_area" },
{ (uchar) sizeof(struct dgn_topology), "struct dgn_topology" },
{ (uchar) sizeof(struct dungeon), "struct dungeon" },
{ (uchar) sizeof(struct d_level), "struct d_level" },
{ (uchar) sizeof(struct ebones), "struct ebones" },
{ (uchar) sizeof(struct edog), "struct edog" },
{ (uchar) sizeof(struct egd), "struct egd" },
{ (uchar) sizeof(struct emin), "struct emin" },
{ (uchar) sizeof(struct engr), "struct engr" },
{ (uchar) sizeof(struct epri), "struct epri" },
{ (uchar) sizeof(struct eshk), "struct eshk" },
{ (uchar) sizeof(struct fe), "struct fe" },
{ (uchar) sizeof(struct flag), "struct flag" },
{ (uchar) sizeof(struct fruit), "struct fruit" },
{ (uchar) sizeof(struct gamelog_line), "struct gamelog_line" },
{ (uchar) sizeof(struct kinfo), "struct kinfo" },
{ (uchar) sizeof(struct levelflags), "struct levelflags" },
{ (uchar) sizeof(struct ls_t), "struct ls_t" },
{ (uchar) sizeof(struct linfo), "struct linfo" },
{ (uchar) sizeof(struct mapseen_feat), "struct mapseen_feat" },
{ (uchar) sizeof(struct mapseen_flags), "struct mapseen_flags" },
{ (uchar) sizeof(struct mapseen_rooms), "struct mapseen_rooms" },
{ (uchar) sizeof(struct mextra), "struct mextra" },
{ (uchar) sizeof(struct mkroom), "struct mkroom" },
{ (uchar) sizeof(struct monst), "struct monst" },
{ (uchar) sizeof(struct mvitals), "struct mvitals" },
{ (uchar) sizeof(struct obj), "struct obj" },
{ (uchar) sizeof(struct objclass), "struct objclass" },
{ (uchar) sizeof(struct oextra), "struct oextra" },
{ (uchar) sizeof(struct q_score), "struct q_score" },
{ (uchar) sizeof(struct rm), "struct rm" }, /* [61] */
{ (uchar) sizeof(struct spell), "struct spell" },
{ (uchar) sizeof(struct stairway), "struct stairway" },
{ (uchar) sizeof(struct s_level), "struct s_level" },
{ (uchar) sizeof(struct trap), "struct trap" },
{ (uchar) sizeof(struct version_info), "struct version_info" },
{ (uchar) sizeof(anything), "anything" },
/* struct you requires 2 bytes */
{ (uchar) ((sizeof(struct you) & 0x00FF)), "you_LO" },
{ (uchar) ((sizeof(struct you) & 0xFF00) >> 8), "you_HI" },
#ifdef SF_INCLUDE_SUBSTRUCTS
/*
* the ones below are substructures of the ones
* above, so there is no need to check these directly.
*/
{ (uchar) sizeof(struct attribs), "struct attribs" },
{ (uchar) sizeof(struct dig_info), "struct dig_info" },
{ (uchar) sizeof(struct tin_info), "struct tin_info" },
{ (uchar) sizeof(struct book_info), "struct book_info" },
{ (uchar) sizeof(struct takeoff_info), "struct takeoff_info" },
{ (uchar) sizeof(struct victual_info), "struct victual_info" },
{ (uchar) sizeof(struct engrave_info), "struct engrave_info" },
{ (uchar) sizeof(struct warntype_info), "struct warntype_info" },
{ (uchar) sizeof(struct polearm_info), "struct polearm_info" },
{ (uchar) sizeof(struct obj_split), "struct obj_split" },
{ (uchar) sizeof(struct tribute_info), "struct tribute_info" },
{ (uchar) sizeof(struct novel_tracking), "struct novel_tracking" },
{ (uchar) sizeof(struct achievement_tracking),
"struct achievement_tracking" },
{ (uchar) sizeof(struct d_flags), "struct d_flags" },
{ (uchar) sizeof(struct mapseen), "struct mapseen" },
{ (uchar) sizeof(struct fakecorridor), "struct fakecorridor" },
{ (uchar) sizeof(struct bill_x), "struct bill_x" },
{ (uchar) sizeof(union vptrs), "union vptrs" },
{ (uchar) sizeof(struct prop), "struct prop" },
{ (uchar) sizeof(struct skills), "struct skills" },
{ (uchar) sizeof(union vlaunchinfo), "union vlaunchinfo" },
{ (uchar) sizeof(struct u_have), "struct u_have" },
{ (uchar) sizeof(struct u_event), "struct u_event" },
{ (uchar) sizeof(struct u_realtime), "struct u_realtime" },
{ (uchar) sizeof(struct u_conduct), "struct u_conduct" },
{ (uchar) sizeof(struct u_roleplay), "struct u_roleplay" },
#endif /* SF_INCLUDE_SUBSTRUCTS */
/* 10 for future expansion without changing array size */
{ 0, "" },
{ 0, "" },
{ 0, "" },
{ 0, "" },
{ 0, "" },
{ 0, "" },
{ 0, "" },
{ 0, "" },
{ 0, "" },
{ (uchar) SAVEFILE_REVISION_LEVEL, "savefile_revision_level" },
};
uchar cscbuf[SIZE(critical_sizes)];
int
get_critical_size_count(void)
{
return SIZE(critical_sizes);
}
#ifndef MINIMAL_FOR_RECOVER
void
store_critical_bytes(NHFILE *nhfp)
{
int i, cnt;
char indicate = 'u', csc_count = (char) SIZE(critical_sizes);
/* int cmc = 0; */
if (nhfp->mode & WRITING) {
indicate = (nhfp->structlevel) ? 'h'
: (nhfp->fnidx == exportascii)
? 'a'
: '?';
Sfo_char(nhfp, &indicate, "indicate-format", 1);
Sfo_char(nhfp, &csc_count, "count-critical_sizes", 1);
cnt = (int) csc_count;
for (i = 0; i < cnt; ++i) {
Sfo_uchar(nhfp, &critical_sizes[i].ucsize, "critical_sizes");
}
}
}
/* this used to be based on file date and somewhat OS-dependent,
* but now examines the initial part of the file's contents.
*
* returns:
*
* SF_UPTODATE (0) everything matched and looks good
* SF_OUTDATED (1) savefile is outdated
* SF_CRITICAL_BYTE_COUNT_MISMATCH (2) critical size count mismatch
* SF_DM_IL32LLP64_ON_ILP32LL64 (3) Windows x64 savefile on x86
* SF_DM_I32LP64_ON_ILP32LL64 (4) Unix 64 savefile on x86
* SF_DM_ILP32LL64_ON_I32LP64 (5) x86 savefile on Unix 64
* SF_DM_ILP32LL64_ON_IL32LLP64 (6) x86 savefile on Windows x64
* SF_DM_I32LP64_ON_IL32LLP64 (7) Unix 64 savefile on Windows x64
* SF_DM_IL32LLP64_ON_I32LP64 (8) Windows x64 savefile on Unix 64
* SF_DM_MISMATCH (9) some other mismatch
*/
int
uptodate(NHFILE *nhfp, const char *name, unsigned long utdflags)
{
#ifdef SFCTOOL
extern struct version_info vers_info;
#else
struct version_info vers_info;
#endif
char indicator;
int sfstatus = 0, idx_1st_mismatch = 0;
boolean quietly = (utdflags & UTD_QUIETLY) != 0;
boolean verbose = name ? TRUE : FALSE;
Sfi_char(nhfp, &indicator, "indicate-format", 1);
if ((sfstatus = compare_critical_bytes(nhfp, &idx_1st_mismatch, utdflags))
!= SF_UPTODATE) {
if (sfstatus > 0 && idx_1st_mismatch) {
if (!quietly)
raw_printf("comparison of critical bytes mismatched at %d (%s).",
critical_sizes[idx_1st_mismatch].ucsize,
critical_sizes[idx_1st_mismatch].nm);
}
}
Sfi_version_info(nhfp, &vers_info, "version_info");
if (!check_version(&vers_info, name, verbose, utdflags)) {
if (verbose) {
if ((utdflags & UTD_WITHOUT_WAITSYNCH_PERFILE) == 0) {
wait_synch();
}
}
return SF_OUTDATED;
}
return sfstatus;
}
/*
* returns:
*
* SF_UPTODATE (0) everything matched and looks good
* SF_OUTDATED (1) savefile is outdated
* SF_CRITICAL_BYTE_COUNT_MISMATCH (2) critical size count mismatch
* SF_DM_IL32LLP64_ON_ILP32LL64 (3) Windows x64 savefile on x86
* SF_DM_I32LP64_ON_ILP32LL64 (4) Unix 64 savefile on x86
* SF_DM_ILP32LL64_ON_I32LP64 (5) x86 savefile on Unix 64
* SF_DM_ILP32LL64_ON_IL32LLP64 (6) x86 savefile on Windows x64
* SF_DM_I32LP64_ON_IL32LLP64 (7) Unix 64 savefile on Windows x64
* SF_DM_IL32LLP64_ON_I32LP64 (8) Windows x64 savefile on Unix 64
* SF_DM_MISMATCH (9) some other mismatch
*/
int
compare_critical_bytes(NHFILE *nhfp, int *idx_1st_mismatch, unsigned long utdflags)
{
char active_csc_count = (char) SIZE(critical_sizes),
file_csc_count;
int i, cnt = (int) active_csc_count,
dmmismatch = SF_DM_MISMATCH;
boolean quietly = (utdflags & UTD_QUIETLY) != 0;
Sfi_char(nhfp, &file_csc_count, "count-critical_sizes", 1);
if (file_csc_count > cnt) {
if (!quietly)
raw_printf("critical byte counts do not match"
", file:%d, critical_sizes:%d.",
file_csc_count, SIZE(critical_sizes));
return SF_CRITICAL_BYTE_COUNT_MISMATCH;
}
for (i = 0; i < (int) file_csc_count; ++i) {
Sfi_uchar(nhfp, &cscbuf[i], "critical_sizes");
}
for (i = 1; i < cnt; ++i) {
#ifndef SFCTOOL
if (cscbuf[i] != critical_sizes[i].ucsize && i == cnt - 1) {
/* SAVEFILE_REVISION_LEVEL mismatch; attempt to deal with it */
int file_rev_level = cscbuf[i];
if (revision_increment(file_rev_level,
file_csc_count,
cscbuf))
continue;
}
#endif
if (cscbuf[i] != critical_sizes[i].ucsize) {
const char *dm = datamodel(0), *dmfile;
dmfile = what_datamodel_is_this(0,
cscbuf[1], /* short */
cscbuf[2], /* int */
cscbuf[3], /* long */
cscbuf[4], /* long long */
cscbuf[5]); /* ptr */
if (!strcmp(dmfile, "IL32LLP64") && !strcmp(dm, "ILP32LL64")) {
/* Windows x64 savefile on x86 */
dmmismatch = SF_DM_IL32LLP64_ON_ILP32LL64;
} else if (!strcmp(dmfile, "I32LP64")
&& !strcmp(dm, "ILP32LL64")) {
/* Unix 64 savefile on x86*/
dmmismatch = SF_DM_I32LP64_ON_ILP32LL64;
} else if (!strcmp(dmfile, "ILP32LL64")
&& !strcmp(dm, "I32LP64")) {
/* x86 savefile on Unix 64 */
dmmismatch = SF_DM_ILP32LL64_ON_I32LP64;
} else if (!strcmp(dmfile, "ILP32LL64")
&& !strcmp(dm, "IL32LLP64")) {
/* x86 savefile on Windows x64 */
dmmismatch = SF_DM_ILP32LL64_ON_IL32LLP64;
} else if (!strcmp(dmfile, "I32LP64")
&& !strcmp(dm, "IL32LLP64")) {
/* Unix 64 savefile on Windows x64 */
dmmismatch = SF_DM_I32LP64_ON_IL32LLP64;
} else if (!strcmp(dmfile, "IL32LLP64")
&& !strcmp(dm, "I32LP64")) {
/* Windows x64 savefile on Unix 64 */
dmmismatch = SF_DM_IL32LLP64_ON_I32LP64;
}
if (idx_1st_mismatch)
*idx_1st_mismatch = i;
return dmmismatch;
}
}
return SF_UPTODATE; /* everything matched */
}
/*
* returns:
*
* SF_UPTODATE (0) everything matched and looks good
* SF_OUTDATED (1) savefile is outdated
* SF_CRITICAL_BYTE_COUNT_MISMATCH (2) critical size count mismatch
* SF_DM_IL32LLP64_ON_ILP32LL64 (3) Windows x64 savefile on x86
* SF_DM_I32LP64_ON_ILP32LL64 (4) Unix 64 savefile on x86
* SF_DM_ILP32LL64_ON_I32LP64 (5) x86 savefile on Unix 64
* SF_DM_ILP32LL64_ON_IL32LLP64 (6) x86 savefile on Windows x64
* SF_DM_I32LP64_ON_IL32LLP64 (7) Unix 64 savefile on Windows x64
* SF_DM_IL32LLP64_ON_I32LP64 (8) Windows x64 savefile on Unix 64
* SF_DM_MISMATCH (9) some other mismatch
*/
int
validate(NHFILE *nhfp, const char *name, boolean without_waitsynch_perfile,
int additional_utd_flags)
{
unsigned long utdflags = 0L;
int validsf = 0;
if (additional_utd_flags)
utdflags |= additional_utd_flags;
#ifdef SFCTOOL
utdflags |= UTD_QUIETLY;
#endif
if (nhfp->structlevel)
utdflags |= UTD_CHECKSIZES;
if (without_waitsynch_perfile)
utdflags |= UTD_WITHOUT_WAITSYNCH_PERFILE;
if (nhfp->fieldlevel)
utdflags |= UTD_CHECKFIELDCOUNTS | UTD_SKIP_SANITY1;
validsf = uptodate(nhfp, name, utdflags);
return validsf;
}
#endif /* MINIMAL_FOR_RECOVER */
/*version.c*/